From 216cd0cf34bef3a3c7c4fec67e80bd2881e59cb3 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 24 Mar 2026 23:32:49 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20copilot=20QoL=20batch=20=E2=80=94=20res?= =?UTF-8?q?izable,=20SFTP,=20context,=20errors,=20presets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resizable panel: - Drag handle on left border, pointer events, 320px–1200px range SFTP MCP tools: - sftp_list, sftp_read, sftp_write — full HTTP endpoints + bridge tools - SftpService now Clone for MCP server sharing Active session context: - mcp_get_session_context — last 20 lines of any session's scrollback Error watcher: - Background scanner every 2s across all sessions - 20+ patterns: permission denied, OOM, segfault, disk full, etc. - mcp:error events emitted to frontend - Sessions auto-registered on SSH connect Configurable launch presets: - Settings → AI Copilot section with preset editor - Name + command pairs, stored in settings table as JSON - One-click preset buttons in copilot panel empty state - Defaults: Claude Code, Gemini CLI, Codex CLI - User can set custom commands (e.g. claude --dangerously-skip-permissions) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/ai/CopilotPanel.vue | 60 +++++++++++++++-- src/components/common/SettingsModal.vue | 87 ++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 8 deletions(-) diff --git a/src/components/ai/CopilotPanel.vue b/src/components/ai/CopilotPanel.vue index 6546a76..a77756d 100644 --- a/src/components/ai/CopilotPanel.vue +++ b/src/components/ai/CopilotPanel.vue @@ -54,15 +54,23 @@ - -
+ +

- Select a shell and click Launch to start a local terminal. + Select a shell and click Launch, or use a preset:

+
+ +

- Run claude, - gemini, or - codex here. + Configure presets in Settings → AI Copilot

@@ -100,6 +108,10 @@ function startResize(e: PointerEvent): void { document.addEventListener("pointerup", onUp); } +interface LaunchPreset { name: string; shell: string; command: string; } + +const presets = ref([]); + const shells = ref([]); const selectedShell = ref(""); const connected = ref(false); @@ -121,6 +133,37 @@ async function loadShells(): Promise { } } +async function loadPresets(): Promise { + try { + const raw = await invoke("get_setting", { key: "copilot_presets" }); + if (raw) { + presets.value = JSON.parse(raw); + } else { + // Seed with sensible defaults + presets.value = [ + { name: "Claude Code", shell: "", command: "claude" }, + { name: "Gemini CLI", shell: "", command: "gemini" }, + { name: "Codex CLI", shell: "", command: "codex" }, + ]; + } + } catch { + presets.value = []; + } +} + +async function launchPreset(preset: LaunchPreset): Promise { + const shell = preset.shell || selectedShell.value; + if (!shell) return; + selectedShell.value = shell; + await launch(); + // After shell spawns, send the preset command + if (sessionId && connected.value) { + setTimeout(() => { + invoke("pty_write", { sessionId, data: preset.command + "\n" }).catch(() => {}); + }, 300); + } +} + async function launch(): Promise { if (!selectedShell.value) return; sessionEnded.value = false; @@ -184,7 +227,10 @@ function cleanup(): void { sessionId = ""; } -onMounted(loadShells); +onMounted(() => { + loadShells(); + loadPresets(); +}); onBeforeUnmount(() => { if (connected.value) kill(); diff --git a/src/components/common/SettingsModal.vue b/src/components/common/SettingsModal.vue index d78490b..fc7eb06 100644 --- a/src/components/common/SettingsModal.vue +++ b/src/components/common/SettingsModal.vue @@ -154,6 +154,56 @@ + + +