feat(electrobun): add xterm.js terminal with image addon (Sixel/iTerm2)
- Terminal.svelte component with @xterm/xterm + Canvas + Fit + Image addons - Catppuccin Mocha terminal theme matching main app - Sixel, iTerm2 inline image protocol support via xterm-addon-image - ResizeObserver for responsive terminal sizing - Demo cargo test output in terminal section below agent messages
This commit is contained in:
parent
b79fbf688e
commit
f97ea95373
10 changed files with 241 additions and 23 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte App</title>
|
||||
<script type="module" crossorigin src="/assets/index-C2tlpXVI.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-xWFAud6t.css">
|
||||
<script type="module" crossorigin src="/assets/index-CxMdDiN5.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dim5hfvk.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
"": {
|
||||
"name": "electrobun-svelte",
|
||||
"dependencies": {
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-image": "^0.9.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"electrobun": "latest",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -148,6 +152,14 @@
|
|||
|
||||
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
||||
|
||||
"@xterm/addon-canvas": ["@xterm/addon-canvas@0.7.0", "", { "peerDependencies": { "@xterm/xterm": "^5.0.0" } }, "sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw=="],
|
||||
|
||||
"@xterm/addon-fit": ["@xterm/addon-fit@0.11.0", "", {}, "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g=="],
|
||||
|
||||
"@xterm/addon-image": ["@xterm/addon-image@0.9.0", "", {}, "sha512-oYWA8/QAr5/Emwl1xL7WCoOqeG3IZfpzEz/OVq1j4Oi9934TQmHiyubClikRf0D/jL3JNiNuz/Lsqx0kXQ02BA=="],
|
||||
|
||||
"@xterm/xterm": ["@xterm/xterm@6.0.0", "", {}, "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
"build:canary": "vite build && electrobun build --env=canary"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-image": "^0.9.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"electrobun": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts">
|
||||
import Terminal from './Terminal.svelte';
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────
|
||||
type AgentStatus = 'running' | 'idle' | 'stalled';
|
||||
type MsgRole = 'user' | 'assistant' | 'tool-call' | 'tool-result';
|
||||
|
|
@ -165,16 +167,22 @@
|
|||
role="tabpanel"
|
||||
aria-label="Model"
|
||||
>
|
||||
{#each project.messages as msg (msg.id)}
|
||||
<div class="msg">
|
||||
<span class="msg-role {msg.role.split('-')[0]}">{msg.role}</span>
|
||||
<div
|
||||
class="msg-body"
|
||||
class:tool-call={msg.role === 'tool-call'}
|
||||
class:tool-result={msg.role === 'tool-result'}
|
||||
>{msg.content}</div>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="agent-messages">
|
||||
{#each project.messages as msg (msg.id)}
|
||||
<div class="msg">
|
||||
<span class="msg-role {msg.role.split('-')[0]}">{msg.role}</span>
|
||||
<div
|
||||
class="msg-body"
|
||||
class:tool-call={msg.role === 'tool-call'}
|
||||
class:tool-result={msg.role === 'tool-result'}
|
||||
>{msg.content}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<!-- Terminal section -->
|
||||
<div class="terminal-section">
|
||||
<Terminal />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Docs tab placeholder -->
|
||||
|
|
|
|||
97
ui-electrobun/src/mainview/Terminal.svelte
Normal file
97
ui-electrobun/src/mainview/Terminal.svelte
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { CanvasAddon } from '@xterm/addon-canvas';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { ImageAddon } from '@xterm/addon-image';
|
||||
|
||||
// Catppuccin Mocha terminal theme
|
||||
const THEME = {
|
||||
background: '#1e1e2e',
|
||||
foreground: '#cdd6f4',
|
||||
cursor: '#f5e0dc',
|
||||
cursorAccent: '#1e1e2e',
|
||||
selectionBackground: '#585b7066',
|
||||
black: '#45475a',
|
||||
red: '#f38ba8',
|
||||
green: '#a6e3a1',
|
||||
yellow: '#f9e2af',
|
||||
blue: '#89b4fa',
|
||||
magenta: '#f5c2e7',
|
||||
cyan: '#94e2d5',
|
||||
white: '#bac2de',
|
||||
brightBlack: '#585b70',
|
||||
brightRed: '#f38ba8',
|
||||
brightGreen: '#a6e3a1',
|
||||
brightYellow: '#f9e2af',
|
||||
brightBlue: '#89b4fa',
|
||||
brightMagenta: '#f5c2e7',
|
||||
brightCyan: '#94e2d5',
|
||||
brightWhite: '#a6adc8',
|
||||
};
|
||||
|
||||
let termEl: HTMLDivElement;
|
||||
let term: Terminal;
|
||||
let fitAddon: FitAddon;
|
||||
|
||||
onMount(() => {
|
||||
term = new Terminal({
|
||||
theme: THEME,
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||
fontSize: 13,
|
||||
cursorBlink: true,
|
||||
allowProposedApi: true,
|
||||
scrollback: 5000,
|
||||
});
|
||||
|
||||
fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
term.loadAddon(new CanvasAddon());
|
||||
|
||||
// Image addon — enables Sixel, iTerm2, and Kitty inline image protocols
|
||||
term.loadAddon(new ImageAddon({
|
||||
enableSizeReports: true,
|
||||
sixelSupport: true,
|
||||
sixelScrolling: true,
|
||||
sixelPaletteLimit: 4096,
|
||||
showPlaceholder: true,
|
||||
}));
|
||||
|
||||
term.open(termEl);
|
||||
fitAddon.fit();
|
||||
|
||||
// Demo content with ANSI colors
|
||||
term.writeln('\x1b[1;34m~/code/ai/agent-orchestrator\x1b[0m \x1b[32m$\x1b[0m cargo test --workspace');
|
||||
term.writeln(' \x1b[1;32mCompiling\x1b[0m agor-core v0.1.0');
|
||||
term.writeln(' \x1b[1;32mCompiling\x1b[0m agor-gpui v0.1.0');
|
||||
term.writeln(' \x1b[1;32mRunning\x1b[0m tests/unit.rs');
|
||||
term.writeln('test result: ok. \x1b[32m47 passed\x1b[0m; 0 failed');
|
||||
term.writeln('');
|
||||
term.writeln('\x1b[1;34m~/code/ai/agent-orchestrator\x1b[0m \x1b[32m$\x1b[0m \x1b[5m▊\x1b[0m');
|
||||
|
||||
// Resize on window resize
|
||||
const ro = new ResizeObserver(() => fitAddon.fit());
|
||||
ro.observe(termEl);
|
||||
|
||||
return () => ro.disconnect();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
term?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="terminal-container" bind:this={termEl}></div>
|
||||
|
||||
<style>
|
||||
.terminal-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 10rem;
|
||||
}
|
||||
|
||||
/* xterm.js base styles */
|
||||
:global(.xterm) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -401,3 +401,20 @@ html, body {
|
|||
color: var(--ctp-text);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ── Terminal section ─────────────────────────────────────── */
|
||||
.agent-messages {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.terminal-section {
|
||||
height: 12rem;
|
||||
min-height: 8rem;
|
||||
border-top: 1px solid var(--ctp-surface0);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import "./app.css";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import App from "./App.svelte";
|
||||
import { mount } from "svelte";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue