generated from nhcarrigan/template
feat: add built-in file editor with syntax highlighting (#79)
## Summary - Add CodeMirror 6 editor with syntax highlighting for 40+ languages - Add file browser sidebar with collapsible directory tree navigation - Add multi-tab support with dirty state indicators and close buttons - Add keyboard shortcuts (Ctrl+E toggle, Ctrl+B file browser, Ctrl+S save, Ctrl+W close tab) - Add editor toggle button to status bar (disabled when not connected) - Editor automatically uses current session's working directory - Add Tauri backend commands for file operations (list_directory, read_file_content, write_file_content) ## Test Plan - [ ] Connect to a session and verify the editor toggle button becomes enabled - [ ] Press Ctrl+E to open the editor and verify file tree shows the session's CWD - [ ] Navigate directories and open files to verify syntax highlighting works - [ ] Edit a file and verify the dirty indicator (*) appears - [ ] Save with Ctrl+S and verify the dirty indicator disappears - [ ] Open multiple files and verify tab switching works - [ ] Close tabs with Ctrl+W or the X button - [ ] Disconnect and verify the editor automatically closes - [ ] Verify keyboard shortcuts are documented in the shortcuts modal Closes #72 ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #79 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #79.
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import "../app.css";
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
// Prevent the default context menu globally
|
||||
// Individual components can show their own context menus by calling event.stopPropagation()
|
||||
function handleContextMenu(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window oncontextmenu={handleContextMenu} />
|
||||
|
||||
<div id="app">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
+78
-3
@@ -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,49 @@
|
||||
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;
|
||||
}
|
||||
|
||||
// Ctrl+N - New file (when editor is visible)
|
||||
// Note: This just emits an event that FileBrowser listens to
|
||||
if (event.ctrlKey && event.key === "n" && get(editorStore.isEditorVisible)) {
|
||||
event.preventDefault();
|
||||
// Dispatch a custom event that FileBrowser will listen to
|
||||
window.dispatchEvent(new CustomEvent("editor-new-file"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleInterrupt() {
|
||||
@@ -330,10 +401,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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user