diff --git a/apps/server/src/routes/sidebar.ts b/apps/server/src/routes/sidebar.ts index 4b543db..be79948 100644 --- a/apps/server/src/routes/sidebar.ts +++ b/apps/server/src/routes/sidebar.ts @@ -18,7 +18,7 @@ export function registerSidebarRoutes(app: FastifyInstance, sql: Sql): void { projects.map(async (p) => { const [recent_sessions, countRows] = await Promise.all([ sql` - SELECT id, name, model, updated_at + SELECT id, project_id, name, model, updated_at FROM sessions WHERE project_id = ${p.id} ORDER BY updated_at DESC diff --git a/apps/server/src/schema.sql b/apps/server/src/schema.sql index ee0fc89..66ca449 100644 --- a/apps/server/src/schema.sql +++ b/apps/server/src/schema.sql @@ -46,3 +46,14 @@ CREATE TABLE IF NOT EXISTS settings ( ); INSERT INTO settings (key, value) VALUES ('default_model', '"qwen3.6-35b-a3b-mxfp4"') ON CONFLICT (key) DO NOTHING; + +CREATE TABLE IF NOT EXISTS session_panes ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE, + position INTEGER NOT NULL, + kind TEXT NOT NULL CHECK (kind IN ('chat', 'file_browser')), + state JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + UNIQUE (session_id, position) +); +CREATE INDEX IF NOT EXISTS session_panes_session_idx ON session_panes (session_id); diff --git a/apps/server/src/types/api.ts b/apps/server/src/types/api.ts index 3413f58..73068db 100644 --- a/apps/server/src/types/api.ts +++ b/apps/server/src/types/api.ts @@ -61,6 +61,7 @@ export interface ModelInfo { export interface SidebarSession { id: string; + project_id: string; name: string; model: string; updated_at: string; @@ -76,3 +77,68 @@ export interface SidebarProject { export interface SidebarResponse { projects: SidebarProject[]; } + +export type PaneKind = 'chat' | 'file_browser'; + +export interface FileBrowserPaneState { + open_file?: string | null; + filter?: string; + expanded_dirs?: string[]; +} + +// chat panes have no state for now +export type ChatPaneState = Record; + +export type PaneState = ChatPaneState | FileBrowserPaneState; + +export interface Pane { + id: string; + session_id: string; + position: number; + kind: PaneKind; + state: PaneState; + created_at: string; +} + +export interface PaneCreateRequest { + kind: PaneKind; + position?: number; // optional; if omitted, append at end +} + +export interface PaneUpdateRequest { + state?: PaneState; + position?: number; +} + +// User-stream frames (broadcast on /ws/user channel) +export interface ProjectCreatedFrame { + type: 'project_created'; + project: Project; +} +export interface ProjectDeletedFrame { + type: 'project_deleted'; + project_id: string; +} +export interface SessionCreatedFrame { + type: 'session_created'; + session: Session; + project_id: string; +} +export interface SessionDeletedFrame { + type: 'session_deleted'; + session_id: string; + project_id: string; +} +export interface SessionUpdatedFrame { + type: 'session_updated'; + session_id: string; + project_id: string; + name: string; + updated_at: string; +} +export type UserStreamFrame = + | ProjectCreatedFrame + | ProjectDeletedFrame + | SessionCreatedFrame + | SessionDeletedFrame + | SessionUpdatedFrame;