+
{#if msg.type === 'init'}
Session started
@@ -241,6 +264,22 @@
{/if}
+ {#if session.sdkSessionId}
+
+ {
+ if (e.key === 'Enter' && followUpPrompt.trim()) {
+ startQuery(followUpPrompt, true);
+ }
+ }}
+ />
+
+
+ {/if}
{:else if session.status === 'error'}
Error: {session.error ?? 'Unknown'}
@@ -250,6 +289,22 @@
{/if}
+ {#if session.sdkSessionId}
+
+ {
+ if (e.key === 'Enter' && followUpPrompt.trim()) {
+ startQuery(followUpPrompt, true);
+ }
+ }}
+ />
+
+
+ {/if}
{/if}
{/if}
@@ -633,4 +688,40 @@
.done-bar { color: var(--ctp-green); }
.error-bar { color: var(--ctp-red); }
+
+ .follow-up {
+ display: flex;
+ gap: 6px;
+ padding: 4px 0 0;
+ }
+
+ .follow-up-input {
+ flex: 1;
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ color: var(--text-primary);
+ font-size: 12px;
+ padding: 4px 8px;
+ font-family: inherit;
+ }
+
+ .follow-up-input:focus {
+ outline: none;
+ border-color: var(--accent);
+ }
+
+ .follow-up-btn {
+ background: var(--accent);
+ color: var(--ctp-crust);
+ border: none;
+ border-radius: 3px;
+ padding: 4px 12px;
+ font-size: 11px;
+ font-weight: 600;
+ cursor: pointer;
+ }
+
+ .follow-up-btn:hover { opacity: 0.9; }
+ .follow-up-btn:disabled { opacity: 0.4; cursor: not-allowed; }
diff --git a/v2/src/lib/components/Agent/AgentTree.svelte b/v2/src/lib/components/Agent/AgentTree.svelte
index 63daf48..80743be 100644
--- a/v2/src/lib/components/Agent/AgentTree.svelte
+++ b/v2/src/lib/components/Agent/AgentTree.svelte
@@ -1,5 +1,5 @@
{#if panes.length === 0}
@@ -91,12 +204,41 @@
{/if}
+
+{#if panes.length > 1 && gridEl}
+ {#each { length: colCount - 1 } as _, ci}
+
+
startColDrag(ci, e)}
+ >
+ {/each}
+ {#each { length: rowCount - 1 } as _, ri}
+
+
startRowDrag(ri, e)}
+ >
+ {/each}
+{/if}
+
diff --git a/v2/src/lib/components/Terminal/TerminalPane.svelte b/v2/src/lib/components/Terminal/TerminalPane.svelte
index 1d24693..6738046 100644
--- a/v2/src/lib/components/Terminal/TerminalPane.svelte
+++ b/v2/src/lib/components/Terminal/TerminalPane.svelte
@@ -4,7 +4,7 @@
import { CanvasAddon } from '@xterm/addon-canvas';
import { FitAddon } from '@xterm/addon-fit';
import { spawnPty, writePty, resizePty, killPty, onPtyData, onPtyExit } from '../../adapters/pty-bridge';
- import { getXtermTheme } from '../../stores/theme.svelte';
+ import { getXtermTheme, onThemeChange } from '../../stores/theme.svelte';
import type { UnlistenFn } from '@tauri-apps/api/event';
import '@xterm/xterm/css/xterm.css';
@@ -24,6 +24,7 @@
let unlistenData: UnlistenFn | null = null;
let unlistenExit: UnlistenFn | null = null;
let resizeObserver: ResizeObserver | null = null;
+ let unsubTheme: (() => void) | null = null;
onMount(async () => {
term = new Terminal({
@@ -59,6 +60,24 @@
onExit?.();
});
+ // Copy/paste via Ctrl+Shift+C/V
+ term.attachCustomKeyEventHandler((e: KeyboardEvent) => {
+ if (e.ctrlKey && e.shiftKey && e.type === 'keydown') {
+ if (e.key === 'C') {
+ const selection = term.getSelection();
+ if (selection) navigator.clipboard.writeText(selection);
+ return false;
+ }
+ if (e.key === 'V') {
+ navigator.clipboard.readText().then(text => {
+ if (text && ptyId) writePty(ptyId, text);
+ });
+ return false;
+ }
+ }
+ return true;
+ });
+
// Forward keyboard input to PTY
term.onData((data) => {
if (ptyId) writePty(ptyId, data);
@@ -80,10 +99,16 @@
}, 100);
});
resizeObserver.observe(terminalEl);
+
+ // Hot-swap theme when flavor changes
+ unsubTheme = onThemeChange(() => {
+ term.options.theme = getXtermTheme();
+ });
});
onDestroy(async () => {
resizeObserver?.disconnect();
+ unsubTheme?.();
unlistenData?.();
unlistenExit?.();
if (ptyId) {
diff --git a/v2/src/lib/stores/theme.svelte.ts b/v2/src/lib/stores/theme.svelte.ts
index 6690ed8..7b1b9c9 100644
--- a/v2/src/lib/stores/theme.svelte.ts
+++ b/v2/src/lib/stores/theme.svelte.ts
@@ -10,6 +10,17 @@ import {
let currentFlavor = $state
('mocha');
+/** Registered theme-change listeners */
+const themeChangeCallbacks = new Set<() => void>();
+
+/** Register a callback invoked after every flavor change. Returns an unsubscribe function. */
+export function onThemeChange(callback: () => void): () => void {
+ themeChangeCallbacks.add(callback);
+ return () => {
+ themeChangeCallbacks.delete(callback);
+ };
+}
+
export function getCurrentFlavor(): CatppuccinFlavor {
return currentFlavor;
}
@@ -22,6 +33,15 @@ export function getXtermTheme(): XtermTheme {
export async function setFlavor(flavor: CatppuccinFlavor): Promise {
currentFlavor = flavor;
applyCssVariables(flavor);
+ // Notify all listeners (e.g. open xterm.js terminals)
+ for (const cb of themeChangeCallbacks) {
+ try {
+ cb();
+ } catch (e) {
+ console.error('Theme change callback error:', e);
+ }
+ }
+
try {
await setSetting('theme', flavor);
} catch (e) {