diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 4612961..8a22af0 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -37,7 +37,7 @@ - Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a singleton that routes sidecar events to the agent store. Handles subagent routing (project-scoped sessions skip layout pane, render in TeamAgentsPanel; detached mode creates layout pane). Session persistence via registerSessionProject() + persistSessionForProject() (saves state + messages to SQLite on complete). - AgentQueryOptions supports `permission_mode` field (flows Rust -> sidecar -> SDK). Defaults to 'bypassPermissions', supports 'default' mode. allowDangerouslySkipPermissions is conditionally set. Also supports: `setting_sources` (defaults to ['user', 'project']), `system_prompt`, `model`, `claude_config_dir` (for multi-account), `additional_directories`. - Claude profiles: claude_list_profiles() reads ~/.config/switcher/profiles/ with profile.toml metadata. Profile set per-project in Settings (project.profile field), passed through ClaudeSession -> AgentPane `profile` prop -> resolved to config_dir for SDK. Profile name shown as info-only in ProjectHeader. -- ProjectBox has project-level tab bar: Model | Docs | Context | Files | SSH | Memory. Three mount strategies: PERSISTED-EAGER (Model, Docs, Context — always mounted, display:flex/none), PERSISTED-LAZY (Files, SSH, Memory — mount on first activation via {#if everActivated} + display:flex/none). Tab type: `'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'`. Model tab = ClaudeSession+TeamAgentsPanel. Docs tab = ProjectFiles (markdown viewer). Context tab = ContextTab.svelte (LLM context window visualization: stats bar, segmented token meter, file references, turn breakdown; reads from agent store via sessionId prop; replaced old ContextPane ctx database viewer). Files tab = FilesTab.svelte (VSCode-style directory tree + CodeMirror 6 editor with 15 language modes, dirty tracking, Ctrl+S save, save-on-blur setting, image display via convertFileSrc, 10MB gate; CodeEditor.svelte wrapper). SSH tab = SshTab.svelte (CRUD for SSH connections, launch spawns terminal tab in Model tab). Memory tab = MemoriesTab.svelte (pluggable via MemoryAdapter interface in memory-adapter.ts). Rust backend: list_directory_children + read_file_content + write_file_content (FileContent tagged union: Text/Binary/TooLarge). Frontend bridge: files-bridge.ts. +- ProjectBox has project-level tab bar: Model | Docs | Context | Files | SSH | Memory. Three mount strategies: PERSISTED-EAGER (Model, Docs, Context — always mounted, display:flex/none), PERSISTED-LAZY (Files, SSH, Memory — mount on first activation via {#if everActivated} + display:flex/none). Tab type: `'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'`. Model tab = ClaudeSession+TeamAgentsPanel. Docs tab = ProjectFiles (markdown viewer). Context tab = ContextTab.svelte (LLM context window visualization: stats bar, segmented token meter, file references, turn breakdown; reads from agent store via sessionId prop; replaced old ContextPane ctx database viewer). Files tab = FilesTab.svelte (VSCode-style directory tree + CodeMirror 6 editor with 15 language modes, dirty tracking, Ctrl+S save, save-on-blur setting, image display via convertFileSrc, 10MB gate; CodeEditor.svelte wrapper; PdfViewer.svelte for PDF files via pdfjs-dist with canvas multi-page rendering + zoom 0.5x–3x; CsvTable.svelte for CSV with RFC 4180 parser, delimiter auto-detect, sortable columns). SSH tab = SshTab.svelte (CRUD for SSH connections, launch spawns terminal tab in Model tab). Memory tab = MemoriesTab.svelte (pluggable via MemoryAdapter interface in memory-adapter.ts). Rust backend: list_directory_children + read_file_content + write_file_content (FileContent tagged union: Text/Binary/TooLarge). Frontend bridge: files-bridge.ts. - ProjectHeader shows CWD (ellipsized from START via `direction: rtl`) + profile name as info-only text on right side. AgentPane no longer has DIR/ACC toolbar — CWD and profile are props from parent. - Skill discovery: claude_list_skills() reads ~/.claude/skills/ (dirs with SKILL.md or .md files). claude_read_skill() reads content. AgentPane `/` prefix triggers autocomplete menu. Skill content injected as prompt via expandSkillPrompt(). - claude-bridge.ts adapter wraps profile/skill Tauri commands (ClaudeProfile, ClaudeSkill interfaces). diff --git a/CHANGELOG.md b/CHANGELOG.md index fed9440..4863fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `claude_read_skill` path traversal: added `canonicalize()` + `starts_with()` validation to prevent reading arbitrary files via crafted skill paths (lib.rs) ### Added +- **PDF viewer** in Files tab: `PdfViewer.svelte` using pdfjs-dist (v5.5.207). Canvas-based multi-page rendering, zoom controls (0.5x–3x, 25% steps), HiDPI-aware via devicePixelRatio. Reads PDF via `convertFileSrc()` — no new Rust commands needed +- **CSV table view** in Files tab: `CsvTable.svelte` with RFC 4180 CSV parser (no external dependency). Auto-detects delimiter (comma, semicolon, tab). Sortable columns (numeric-aware), sticky header, row numbers, text truncation at 20rem +- FilesTab routing update: Binary+pdf → PdfViewer, Text+csv → CsvTable. Updated file icons (📕 PDF, 📊 CSV) - **S-1 Phase 2: Filesystem write detection** — inotify-based real-time file change detection via `ProjectFsWatcher` (fs_watcher.rs). Watches project CWDs recursively, filters .git/node_modules/target, debounces 100ms per-file (fs_watcher.rs, lib.rs) - External write conflict detection: timing heuristic (2s grace window) distinguishes agent writes from external edits. `EXTERNAL_SESSION_ID` sentinel, `recordExternalWrite()`, `getExternalConflictCount()`, `FileConflict.isExternal` flag (conflicts.svelte.ts) - Separate external write badge (orange ⚡) and agent conflict badge (red ⚠) in ProjectHeader (ProjectHeader.svelte) diff --git a/CLAUDE.md b/CLAUDE.md index e9bd684..79f8e1a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,6 +79,8 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth | `v2/src/lib/components/Context/ContextPane.svelte` | ctx database viewer (projects, entries, search) — replaced by ContextTab in ProjectBox | | `v2/src/lib/components/Workspace/ContextTab.svelte` | LLM context window visualization (stats, token meter, file refs, turn breakdown) | | `v2/src/lib/components/Workspace/CodeEditor.svelte` | CodeMirror 6 wrapper (15 languages, Catppuccin theme, save/blur callbacks) | +| `v2/src/lib/components/Workspace/PdfViewer.svelte` | PDF viewer (pdfjs-dist, canvas multi-page, zoom 0.5x–3x, HiDPI) | +| `v2/src/lib/components/Workspace/CsvTable.svelte` | CSV table viewer (RFC 4180 parser, delimiter auto-detect, sortable columns) | | `v2/src/lib/stores/health.svelte.ts` | Project health store (activity state, burn rate, context pressure, file conflicts, attention scoring) | | `v2/src/lib/stores/conflicts.svelte.ts` | File overlap + external write conflict detection (per-project, session-scoped, worktree-aware, dismissible, inotify-backed) | | `v2/src/lib/utils/tool-files.ts` | Shared file path extraction from tool_call inputs (extractFilePaths, extractWritePaths, extractWorktreePath) | @@ -122,7 +124,7 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth - Rust deps (src-tauri): tauri, bterminal-core (path), rusqlite (bundled), dirs, notify, serde, tokio, tokio-tungstenite, futures-util, tracing, tracing-subscriber, opentelemetry, opentelemetry_sdk, opentelemetry-otlp, tracing-opentelemetry, tauri-plugin-updater, tauri-plugin-dialog - Rust deps (bterminal-core): portable-pty, uuid, serde, serde_json, log - Rust deps (bterminal-relay): bterminal-core, tokio, tokio-tungstenite, clap, env_logger, futures-util -- npm deps: @anthropic-ai/claude-agent-sdk, @xterm/xterm, @xterm/addon-canvas, @xterm/addon-fit, @tauri-apps/api, @tauri-apps/plugin-updater, @tauri-apps/plugin-dialog, marked, shiki, vitest (dev) +- npm deps: @anthropic-ai/claude-agent-sdk, @xterm/xterm, @xterm/addon-canvas, @xterm/addon-fit, @tauri-apps/api, @tauri-apps/plugin-updater, @tauri-apps/plugin-dialog, marked, shiki, pdfjs-dist, vitest (dev) - Source: `v2/` directory ## Build / Run diff --git a/TODO.md b/TODO.md index 2b71ad5..28f29ba 100644 --- a/TODO.md +++ b/TODO.md @@ -4,8 +4,6 @@ ### v2/v3 Remaining - [ ] **Register Memora adapter** -- MemoryAdapter interface exists but no concrete adapter registered at app init. Need to create a MemoraAdapter that bridges to Memora MCP/CLI and register it on startup. -- [ ] **Files tab PDF viewer** -- FilesTab marks PDFs as Binary. Add pdfjs-dist Canvas 2D rendering for inline PDF viewing. -- [ ] **Files tab CSV table view** -- CSV files render as plain text. Add structured table rendering with column headers. - [ ] **E2E testing — expand coverage** -- 48 tests passing across 8 describe blocks (WebdriverIO v9.24 + tauri-driver, single spec file, ~23s). Add tests for agent sessions, terminal interaction. - [ ] **Multi-machine real-world testing** -- Test bterminal-relay with 2 machines. - [ ] **Multi-machine TLS/certificate pinning** -- TLS support for bterminal-relay + certificate pinning in RemoteManager. @@ -17,6 +15,7 @@ ## Completed +- [x] **Files tab PDF viewer + CSV table** -- PdfViewer.svelte (pdfjs-dist 5.5.207, canvas multi-page, zoom 0.5x–3x, HiDPI). CsvTable.svelte (RFC 4180 parser, delimiter auto-detect, sortable columns, sticky header). FilesTab routing: Binary+pdf→PdfViewer, Text+csv→CsvTable. | Done: 2026-03-11 - [x] **Filesystem Write Detection (S-1 Phase 2)** -- inotify-based file change detection via notify crate (fs_watcher.rs). Timing heuristic (2s grace) for PID attribution — suppresses agent's own writes, surfaces external edits. EXTERNAL_SESSION_ID sentinel, recordExternalWrite(), externalConflictCount. Orange "ext write" badge + red "agent conflict" badge in ProjectHeader. ProjectBox $effect starts/stops per-project. Inotify watch limit sensing: reads /proc/sys max_user_watches, counts watched dirs, warns at >75% usage with fix command. 202/202 vitest + 42/42 cargo tests. | Done: 2026-03-11 - [x] **Conflict Detection Enhancements (S-1 Phase 1.5)** -- Bash write detection (>, >>, sed -i, tee, cp, mv). Acknowledge/dismiss conflicts (clickable badge). Worktree-aware conflict suppression. useWorktrees ProjectConfig field. 194/194 tests. | Done: 2026-03-11 - [x] **File Overlap Conflict Detection (S-1 Phase 1)** -- conflicts.svelte.ts store tracks per-session Write/Edit file paths. Detects 2+ agents writing same file. Health attention score=70. Toast on conflict. Red badge in ProjectHeader + StatusBar. Shared tool-files.ts utility. 170/170 tests. | Done: 2026-03-10 diff --git a/docs/v3-progress.md b/docs/v3-progress.md index d387e7d..66c7cf7 100644 --- a/docs/v3-progress.md +++ b/docs/v3-progress.md @@ -570,3 +570,29 @@ All editor themes map to the same `--ctp-*` CSS custom property names (26 vars). #### Verification - [x] vitest: 202/202 tests pass (+8 new external write tests) - [x] cargo test: 39/39 pass (+5 new fs_watcher tests) + +### Session: 2026-03-11 — Files Tab: PDF Viewer + CSV Table View + +#### PDF Viewer +- [x] Added pdfjs-dist@5.5.207 dependency (WebKit2GTK has no built-in PDF viewer) +- [x] Created PdfViewer.svelte — canvas-based multi-page renderer +- [x] Zoom controls (0.5x–3x, 25% steps), HiDPI-aware (devicePixelRatio scaling) +- [x] Reads PDF via convertFileSrc() → pdfjs (no new Rust commands needed) +- [x] Page shadow, themed toolbar, error handling + +#### CSV Table View +- [x] Created CsvTable.svelte — RFC 4180 CSV parser (no external dependency) +- [x] Auto-detects delimiter (comma, semicolon, tab) +- [x] Sortable columns (numeric-aware), sticky header, row numbers +- [x] Row hover, text truncation at 20rem, themed via --ctp-* vars + +#### FilesTab Routing +- [x] Binary+pdf → PdfViewer (via isPdfExt check) +- [x] Text+csv → CsvTable (via isCsvLang check) +- [x] Updated file icons: 📕 PDF, 📊 CSV +- [x] Both viewers are read-only + +#### Verification +- [x] vitest: 202/202 tests pass (no regressions) +- [x] Vite build: clean +- [x] cargo check: clean