feat(v2): add SSH management, ctx integration, themes, detached mode, auto-updater

SSH session management:
- SshSession struct + ssh_sessions SQLite table in session.rs
- CRUD Tauri commands (ssh_session_list/save/delete) in lib.rs
- SshDialog.svelte (create/edit modal), SshSessionList.svelte (sidebar)
- SSH pane routes to TerminalPane with shell=/usr/bin/ssh + args

ctx context database integration:
- ctx.rs: read-only CtxDb (SQLITE_OPEN_READ_ONLY for ~/.claude-context/context.db)
- 5 Tauri commands (ctx_list_projects/get_context/get_shared/get_summaries/search)
- ContextPane.svelte with project selector, tabs, search
- ctx-bridge.ts adapter

Catppuccin theme flavors (Latte/Frappe/Macchiato/Mocha):
- themes.ts: all 4 palette definitions + buildXtermTheme/applyCssVariables
- theme.svelte.ts: reactive store with SQLite persistence
- SettingsDialog flavor dropdown, TerminalPane theme-aware

Detached pane mode (pop-out windows):
- detach.ts: isDetachedMode/getDetachedConfig from URL params
- App.svelte: conditional rendering of single pane without chrome

Other additions:
- Shiki syntax highlighting (highlight.ts, lazy singleton, 13 languages)
- Tauri auto-updater plugin (tauri-plugin-updater + updater.ts)
- AgentPane markdown rendering with Shiki code highlighting
- New deps: shiki, @tauri-apps/plugin-updater, tauri-plugin-updater
This commit is contained in:
Hibryda 2026-03-06 14:50:00 +01:00
parent 4f2614186d
commit 4db7ccff60
28 changed files with 2992 additions and 51 deletions

View file

@ -8,6 +8,7 @@
setPreset,
type LayoutPreset,
} from '../../stores/layout.svelte';
import SshSessionList from '../SSH/SshSessionList.svelte';
let panes = $derived(getPanes());
let preset = $derived(getActivePreset());
@ -34,6 +35,20 @@
});
}
function openContext() {
const existing = panes.find(p => p.type === 'context');
if (existing) {
focusPane(existing.id);
return;
}
const id = crypto.randomUUID();
addPane({
id,
type: 'context',
title: 'Context',
});
}
let fileInputEl: HTMLInputElement | undefined = $state();
function openMarkdown() {
@ -62,6 +77,7 @@
<div class="header">
<h2>Sessions</h2>
<div class="header-buttons">
<button class="new-btn" onclick={openContext} title="Context manager">C</button>
<button class="new-btn" onclick={openMarkdown} title="Open markdown file">M</button>
<button class="new-btn" onclick={newAgent} title="New agent (Ctrl+Shift+N)">A</button>
<button class="new-btn" onclick={newTerminal} title="New terminal (Ctrl+N)">+</button>
@ -96,7 +112,7 @@
{#each panes as pane (pane.id)}
<li class="pane-item" class:focused={pane.focused}>
<button class="pane-btn" onclick={() => focusPane(pane.id)}>
<span class="pane-icon">{pane.type === 'terminal' ? '>' : pane.type === 'agent' ? '*' : pane.type === 'markdown' ? 'M' : '#'}</span>
<span class="pane-icon">{pane.type === 'terminal' ? '>' : pane.type === 'agent' ? '*' : pane.type === 'markdown' ? 'M' : pane.type === 'ssh' ? '@' : pane.type === 'context' ? 'C' : '#'}</span>
<span class="pane-name">{pane.title}</span>
</button>
<button class="remove-btn" onclick={() => removePane(pane.id)}>&times;</button>
@ -104,9 +120,18 @@
{/each}
</ul>
{/if}
<div class="divider"></div>
<SshSessionList />
</div>
<style>
.divider {
height: 1px;
background: var(--border);
margin: 4px 0;
}
.session-list {
padding: 12px;
display: flex;