# Data Model & Layout System

This document covers agor's data model (SQLite schemas, project group config), layout system, xterm budget management, and keyboard shortcuts.

---

## SQLite Databases

The backend manages two SQLite databases, both in WAL mode with 5-second busy timeout for concurrent access:

| Database | Location | Purpose |
|----------|----------|---------|
| `sessions.db` | `~/.local/share/agor/` | Sessions, layout, settings, agent state, metrics, anchors |
| `btmsg.db` | `~/.local/share/agor/` | Inter-agent messages, tasks, agents registry, audit log |

WAL checkpoints run every 5 minutes via a background tokio task to prevent unbounded WAL growth.

All queries use **named column access** (`row.get("column_name")`) — never positional indices. Rust structs use `#[serde(rename_all = "camelCase")]` so TypeScript interfaces receive camelCase field names on the wire.

---

## Project Group Config (`~/.config/agor/groups.json`)

Human-editable JSON file defining workspaces. Each group contains up to 5 projects. Loaded at startup by `groups.rs`, not hot-reloaded.

```jsonc
{
  "version": 1,
  "groups": [
    {
      "id": "work-ai",
      "name": "AI Projects",
      "projects": [
        {
          "id": "agor",
          "name": "Agents Orchestrator",
          "identifier": "agor",
          "description": "Terminal emulator with Claude integration",
          "icon": "\uf120",
          "cwd": "/home/user/code/Agents Orchestrator",
          "profile": "default",
          "enabled": true
        }
      ]
    }
  ],
  "activeGroupId": "work-ai"
}
```

### TypeScript Types (`src/lib/types/groups.ts`)

```typescript
export interface ProjectConfig {
  id: string;
  name: string;
  identifier: string;
  description: string;
  icon: string;
  cwd: string;
  profile: string;
  enabled: boolean;
}

export interface GroupConfig {
  id: string;
  name: string;
  projects: ProjectConfig[];  // max 5
}

export interface GroupsFile {
  version: number;
  groups: GroupConfig[];
  activeGroupId: string;
}
```

---

## SQLite Schema (v3 Additions)

Beyond the core `sessions` and `settings` tables, v3 added project-scoped agent persistence:

```sql
ALTER TABLE sessions ADD COLUMN project_id TEXT DEFAULT '';

CREATE TABLE IF NOT EXISTS agent_messages (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    session_id TEXT NOT NULL,
    project_id TEXT NOT NULL,
    sdk_session_id TEXT,
    message_type TEXT NOT NULL,
    content TEXT NOT NULL,
    parent_id TEXT,
    created_at INTEGER NOT NULL,
    FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS project_agent_state (
    project_id TEXT PRIMARY KEY,
    last_session_id TEXT NOT NULL,
    sdk_session_id TEXT,
    status TEXT NOT NULL,
    cost_usd REAL DEFAULT 0,
    input_tokens INTEGER DEFAULT 0,
    output_tokens INTEGER DEFAULT 0,
    last_prompt TEXT,
    updated_at INTEGER NOT NULL
);
```

---

## Layout System

### Project Grid (Flexbox + scroll-snap)

Projects are arranged horizontally in a flex container with CSS scroll-snap for clean project-to-project scrolling:

```css
.project-grid {
  display: flex;
  gap: 4px;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.project-box {
  flex: 0 0 calc((100% - (N-1) * 4px) / N);
  scroll-snap-align: start;
  min-width: 480px;
}
```

N is computed from viewport width: `Math.min(projects.length, Math.max(1, Math.floor(containerWidth / 520)))`

### Project Box Internal Layout

Each project box uses a CSS grid with 4 rows:

```
+-- ProjectHeader (auto) --------------------+
+---------------------+---------------------+
| AgentSession        | TeamAgentsPanel      |
| (flex: 1)           | (240px/overlay)      |
+---------------------+---------------------+
| [Tab1] [Tab2] [+]          TabBar auto     |
+--------------------------------------------+
| Terminal content (xterm or scrollback)      |
+--------------------------------------------+
```

Team panel: inline at >2560px viewport (240px wide), overlay at <2560px. Collapsed when no subagents running.

### Responsive Breakpoints

| Viewport Width | Visible Projects | Team Panel Mode |
|---------------|-----------------|-----------------|
| 5120px+ | 5 | inline 240px |
| 3840px | 4 | inline 200px |
| 2560px | 3 | overlay |
| 1920px | 3 | overlay |
| <1600px | 1 + project tabs | overlay |

---

## xterm.js Budget: 4 Active Instances

WebKit2GTK OOMs at ~5 simultaneous xterm.js instances. The budget system manages this:

| State | xterm.js Instance? | Memory |
|-------|--------------------|--------|
| Active-Focused | Yes | ~20MB |
| Active-Background | Yes (if budget allows) | ~20MB |
| Suspended | No (HTML pre scrollback) | ~200KB |
| Uninitialized | No (placeholder) | 0 |

On focus: serialize least-recent xterm scrollback, destroy it, create new for focused tab, reconnect PTY. Suspend/resume cycle < 50ms.

### Project Accent Colors

Each project slot gets a distinct Catppuccin accent color for visual distinction:

| Slot | Color | CSS Variable |
|------|-------|-------------|
| 1 | Blue | `var(--ctp-blue)` |
| 2 | Green | `var(--ctp-green)` |
| 3 | Mauve | `var(--ctp-mauve)` |
| 4 | Peach | `var(--ctp-peach)` |
| 5 | Pink | `var(--ctp-pink)` |

Applied to border tint and header accent via `var(--accent)` CSS custom property set per ProjectBox.

---

## Adapters (IPC Bridge Layer)

Adapters wrap Tauri `invoke()` calls and `listen()` event subscriptions. They isolate the frontend from IPC details and provide typed TypeScript interfaces.

| Adapter | Backend Module | Purpose |
|---------|---------------|---------|
| `agent-bridge.ts` | sidecar + commands/agent | Agent query/stop/restart |
| `pty-bridge.ts` | pty + commands/pty | Terminal spawn/write/resize |
| `claude-messages.ts` | (frontend-only) | Parse Claude SDK NDJSON -> AgentMessage |
| `codex-messages.ts` | (frontend-only) | Parse Codex ThreadEvents -> AgentMessage |
| `ollama-messages.ts` | (frontend-only) | Parse Ollama chunks -> AgentMessage |
| `message-adapters.ts` | (frontend-only) | Provider registry for message parsers |
| `provider-bridge.ts` | commands/claude | Generic provider bridge (profiles, skills) |
| `btmsg-bridge.ts` | btmsg | Inter-agent messaging |
| `bttask-bridge.ts` | bttask | Task board operations |
| `groups-bridge.ts` | groups | Group config load/save |
| `session-bridge.ts` | session | Session/layout persistence |
| `settings-bridge.ts` | session/settings | Key-value settings |
| `files-bridge.ts` | commands/files | File browser operations |
| `search-bridge.ts` | search | FTS5 search |
| `secrets-bridge.ts` | secrets | System keyring |
| `anchors-bridge.ts` | session/anchors | Session anchor CRUD |
| `remote-bridge.ts` | remote | Remote machine management |
| `ssh-bridge.ts` | session/ssh | SSH session CRUD |
| `ctx-bridge.ts` | ctx | Context database queries |
| `memora-bridge.ts` | memora | Memora database queries |
| `fs-watcher-bridge.ts` | fs_watcher | Filesystem change events |
| `audit-bridge.ts` | btmsg (audit_log) | Audit log queries |
| `telemetry-bridge.ts` | telemetry | Frontend -> Rust tracing |
| `notifications-bridge.ts` | notifications | Desktop notification trigger |
| `plugins-bridge.ts` | plugins | Plugin discovery |

---

## Keyboard Shortcuts

Three-layer shortcut system prevents conflicts between terminal input, workspace navigation, and app-level commands:

| Shortcut | Action | Layer |
|----------|--------|-------|
| Ctrl+K | Command palette | App |
| Ctrl+G | Switch group (palette filtered) | App |
| Ctrl+1..5 | Focus project by index | App |
| Alt+1..4 | Switch sidebar tab + open drawer | App |
| Ctrl+B | Toggle sidebar open/closed | App |
| Ctrl+, | Toggle settings panel | App |
| Escape | Close sidebar drawer | App |
| Ctrl+Shift+F | FTS5 search overlay | App |
| Ctrl+N | New terminal in focused project | Workspace |
| Ctrl+Shift+N | New agent query | Workspace |
| Ctrl+Tab | Next terminal tab | Project |
| Ctrl+W | Close terminal tab | Project |
| Ctrl+Shift+C/V | Copy/paste in terminal | Terminal |

Terminal layer captures raw keys only when focused. App layer has highest priority.
