feat: add built-in file editor with syntax highlighting
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 58s
CI / Lint & Test (pull_request) Successful in 16m9s
CI / Build Linux (pull_request) Successful in 21m21s
CI / Build Windows (cross-compile) (pull_request) Successful in 34m43s

- Add CodeMirror 6 editor with support for 40+ languages
- Add file browser sidebar with directory tree navigation
- Add multi-tab support with dirty state indicators
- Add keyboard shortcuts (Ctrl+E, Ctrl+B, Ctrl+S, Ctrl+W)
- Add editor toggle button to status bar
- Editor uses current session's CWD and requires connection
- Add Tauri commands for file operations (list, read, write)
This commit is contained in:
2026-01-28 16:12:51 -08:00
committed by Naomi Carrigan
parent edc863e020
commit 392243f54f
14 changed files with 2015 additions and 3 deletions
+69 -3
View File
@@ -7,6 +7,7 @@
import { initNotificationSync, cleanupNotificationSync } from "$lib/stores/notifications";
import { conversationsStore } from "$lib/stores/conversations";
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
import { editorStore } from "$lib/stores/editor";
import { getCurrentWindow, LogicalSize } from "@tauri-apps/api/window";
import "$lib/notifications/testNotifications";
import Terminal from "$lib/components/Terminal.svelte";
@@ -14,6 +15,7 @@
import StatusBar from "$lib/components/StatusBar.svelte";
import AnimeGirl from "$lib/components/AnimeGirl.svelte";
import CompactMode from "$lib/components/CompactMode.svelte";
import EditorPanel from "$lib/components/editor/EditorPanel.svelte";
import { characterState } from "$lib/stores/character";
import type { CharacterState } from "$lib/types/states";
import PermissionModal from "$lib/components/PermissionModal.svelte";
@@ -29,6 +31,32 @@
let currentCharacterState: CharacterState = $state("idle");
let compactModeActive = $state(false);
// Editor state
const isEditorVisible = editorStore.isEditorVisible;
let lastInitializedCwd = "";
// Track connection status and CWD for the editor
const connectionStatus = claudeStore.connectionStatus;
const currentWorkingDirectory = claudeStore.currentWorkingDirectory;
// Initialize/update editor file tree when CWD changes while editor is visible
$effect(() => {
const visible = $isEditorVisible;
const cwd = $currentWorkingDirectory;
const connected = $connectionStatus === "connected";
// Only initialize when editor is visible, connected, and CWD is set
if (visible && connected && cwd && cwd !== lastInitializedCwd) {
lastInitializedCwd = cwd;
editorStore.initializeFileTree(cwd);
}
// Hide editor if disconnected
if (!connected && visible) {
editorStore.hideEditor();
}
});
// Window size constants
const COMPACT_WIDTH = 280;
const COMPACT_HEIGHT = 400;
@@ -176,6 +204,40 @@
toggleCompactMode();
return;
}
// Ctrl+E - Toggle editor panel (only when connected)
if (event.ctrlKey && event.key === "e") {
event.preventDefault();
// Only allow opening the editor when connected
if (get(claudeStore.connectionStatus) === "connected") {
editorStore.toggleEditor();
}
return;
}
// Ctrl+B - Toggle file browser (when editor is visible)
if (event.ctrlKey && event.key === "b" && get(editorStore.isEditorVisible)) {
event.preventDefault();
editorStore.toggleFileBrowser();
return;
}
// Ctrl+S - Save current file (when editor is visible)
if (event.ctrlKey && event.key === "s" && get(editorStore.isEditorVisible)) {
event.preventDefault();
editorStore.saveFile();
return;
}
// Ctrl+W - Close current tab (when editor is visible)
if (event.ctrlKey && event.key === "w" && get(editorStore.isEditorVisible)) {
event.preventDefault();
const activeTabId = get(editorStore.activeTabId);
if (activeTabId) {
editorStore.closeTab(activeTabId);
}
return;
}
}
async function handleInterrupt() {
@@ -330,10 +392,14 @@
onmousedown={startResize}
></div>
<!-- Right panel: Terminal and input -->
<!-- Right panel: Terminal/Editor and input -->
<div class="terminal-panel flex-1 flex flex-col min-w-0">
<Terminal />
<InputBar />
{#if $isEditorVisible}
<EditorPanel />
{:else}
<Terminal />
<InputBar />
{/if}
</div>
</main>