documentation update

This commit is contained in:
Storme-bit
2026-04-18 23:37:32 -07:00
parent 1fc6e8a66d
commit e1375e7d1b
5 changed files with 145 additions and 98 deletions

View File

@@ -82,7 +82,7 @@ src/
├── api/
│ └── orchestration.js # All fetch calls to the orchestration service
├── config/
│ └── constants.js # FALLBACK_MODELS, DEFAULT_MODEL, API_DEFAULTS
│ └── constants.js # FALLBACK_MODELS, DEFAULT_MODEL, API_DEFAULTS, CLIENT_DEFAULTS
├── hooks/
│ ├── useSession.js # Session list, history loading, active session state
│ ├── useChat.js # Message sending, SSE streaming, message state
@@ -93,14 +93,16 @@ src/
├── components/
│ ├── App.jsx # Root component — layout, shared state, view routing
│ ├── Sidebar.jsx # Left sidebar — projects, recent chats, navigation
│ ├── HomeView.jsx # Landing screen — greeting, centred input, quick actions
│ ├── ChatWindow.jsx # Centre panel — message thread and input bar
│ ├── MessageBubble.jsx # Individual message bubble — renders markdown via react-markdown
│ ├── InfoPanel.jsx # Right panel — model selector and session metadata (slide-in)
│ ├── SessionModal.jsx # Modal for session rename, project assignment, delete
│ ├── ProjectModal.jsx # Modal for project create, edit, delete
│ ├── AllChatsView.jsx # Full paginated session list with multi-select bulk delete
│ ├── AllProjectsView.jsx # Project tile grid with create/edit/delete
│ ├── ProjectView.jsx # Individual project — session list, new chat button
│ ├── AllProjectsView.jsx # Project tile grid with create/edit/delete; tile click navigates to ProjectView
│ ├── ProjectView.jsx # Individual project — conversations, new chat input, memory
│ │ # placeholder, user notes, ⋮ edit/delete menu
│ ├── MemoryView.jsx # Paginated, searchable, expandable, deletable episode viewer
│ └── SettingsView.jsx # Settings — Memory limits, Models (inference params, active
│ # model, context window), Service Health, Appearance placeholder
@@ -108,8 +110,6 @@ src/
└── main.jsx # React entry point
```
> `SessionList.jsx` is superseded by `Sidebar.jsx` and kept only as a reference.
## Layout
The app uses a view-based layout. `App.jsx` manages a `view` state string
@@ -120,13 +120,13 @@ panel are persistent across all views.
┌──────────────────┬──────────────────────────────┐
│ Sidebar │ Main Area (view-dependent) │
│ (collapsible) │ │
│ │ chatChatWindow
│ + New Chat │ all-chatsAllChatsView
│ ⊞ View Projects │ all-projects → AllProjectsView│
│ │ projectProjectView
│ PROJECTS ▾ │ settings → SettingsView │
│ [tile] [tile] │ memory → MemoryView
│ All Projects → │
│ │ homeHomeView
│ + New Chat │ chat → ChatWindow
│ ⊞ View Projects │ all-chats → AllChatsView
│ │ all-projects → AllProjectsView│
│ PROJECTS ▾ │ project → ProjectView
│ [tile] [tile] │ settings → SettingsView │
│ All Projects → │ memory → MemoryView
│ │ │
│ RECENT CHATS ▾ │ │
│ Session 1 │ │
@@ -137,24 +137,43 @@ panel are persistent across all views.
└──────────────────┴──────────────────────────────┘
```
The sidebar collapses to a 48px icon rail. The right `InfoPanel` slides in
from the right using `transform: translateX()` — hidden by default, toggled
via the `⊹` button in the `ChatWindow` header.
The sidebar collapses to a 48px icon rail and starts collapsed on the home
view. The right `InfoPanel` slides in from the right using
`transform: translateX()` — hidden by default, toggled via the `⊹` button
in the `ChatWindow` header.
## View Routing
| View | Component | Trigger |
|---|---|---|
| `'chat'` | `ChatWindow` | Default; selecting a session; new chat |
| `'home'` | `HomeView` | Initial load; going back from chat with no history |
| `'chat'` | `ChatWindow` | Selecting a session; new chat; sending from HomeView |
| `'all-chats'` | `AllChatsView` | "All Chats →" or ☰ icon in collapsed rail |
| `'all-projects'` | `AllProjectsView` | "View Projects" button or ⊞ icon |
| `'project'` | `ProjectView` | Clicking a project tile in the sidebar |
| `'project'` | `ProjectView` | Clicking a project tile in sidebar or AllProjectsView |
| `'settings'` | `SettingsView` | Settings button or ⚙ icon |
| `'memory'` | `MemoryView` | "Open →" button in Settings → Memory section |
`activeProject` state in `App.jsx` tracks which project `ProjectView` is
displaying. Set via `onSelectProject` before navigating to `'project'`.
### View History Stack
`App.jsx` maintains a `viewHistory` array. Each `navigate(view)` call pushes
the current view onto the stack. `goBack()` pops the last entry and restores
it. All view components receive `onBack={goBack}` — no component hardcodes
its own back destination. Navigating to `'home'` collapses the sidebar;
leaving `'home'` expands it.
## Home View
`HomeView` is the landing screen shown on initial load and when there are no
active sessions. It displays:
- Time-based greeting ("Morning / Afternoon / Evening, Tim")
- Currently loaded model name (from `modelProps.modelAlias`, stripped of `.gguf`)
- Centred textarea input — sending creates a new session and navigates to chat
- Quick action pills that populate the input without auto-sending
## CSS Architecture
Styles follow a hybrid approach — CSS utility classes for static reusable
@@ -217,6 +236,10 @@ the `uuid` package. New sessions are created locally and auto-registered in
the memory service on the first message. The session list refreshes after
each completed response to surface newly created sessions.
`useSession.selectSession` skips the history fetch for new (`isNew: true`)
sessions — fetching history for an unsaved session would 404 since it doesn't
exist in the backend yet.
### Auto-naming
After the first exchange completes, orchestration fires a secondary inference
@@ -236,46 +259,60 @@ Session rows support rename, project assignment, and delete via:
`SessionModal` handles rename and project assignment together in `settings`
mode, and delete confirmation in `confirm-delete` mode.
### Active Session Clearing on Delete
When the deleted session is the currently active one, `App.jsx` clears the
chat window before refreshing the list:
```js
function handleSessionsChange(deletedSession) {
if (deletedSession?.external_id === activeSession?.external_id) {
selectSession(null);
}
refreshSessions();
}
```
### Key Patterns
- Button nesting: action icons are siblings of row buttons, not children — HTML forbids `<button>` inside `<button>`
- Context menu rendered outside sidebar via React fragment to avoid `overflow: hidden` clipping
- `useContextMenu` dismisses on a `window` click listener
- Dynamic `updateSession` SQL builds `SET` clause from only the fields passed — prevents accidental overwrites
- `AllChatsView` pagination uses `CLIENT_DEFAULTS.PAGE_SIZE` (not `API_DEFAULTS.PAGE_SIZE` which doesn't exist)
## Project Management
All projects are isolated by default (`isolated: 1` hardcoded on create).
The isolated toggle has been removed from `ProjectModal`.
`useProjects` fetches the project list from `GET /projects` on mount and
exposes `refreshProjects` for keeping the sidebar in sync after mutations.
`ProjectModal` handles create, edit, and delete confirmation. Fields: name
(required), description (optional), colour picker, isolated toggle.
(required), description (optional), colour picker.
`ProjectView` shows the project's name, description, isolated badge (if set),
and a filtered session list. The "+ New Chat" button creates a new session,
navigates to `'chat'`, and writes the project assignment after the first message.
Clicking a project tile in `AllProjectsView` calls `onSelectProject` then
navigates to `'project'`.
### ProjectView
`ProjectView` is a full project workspace with:
- Colour accent bar + project title + description
- ⋮ dropdown menu for edit (opens `ProjectModal` pre-filled) and delete
- Conversations list — each session is a clickable row navigating to `'chat'`
- `ChatInput` component below the list (or centred when no sessions exist) for
starting new project-tied conversations without a separate button
- **Project Memory** — placeholder section explaining upcoming auto-summary feature
- **Project Notes** — textarea with Save button; notes saved to `projects.notes`
column in SQLite; save button only appears when content has changed from last
saved value (`savedNotes` state tracks the baseline, not `initialNotes`)
`updateProject` in `orchestration.js` uses a passthrough pattern — spreads
all fields directly into the request body, only transforming `isolated` if
present. This allows partial updates like `{ notes }` without clobbering
other fields.
For memory isolation behaviour, see `memory-isolation.md`.
## Settings
`useSettings` fetches from `GET /settings` on mount and exposes a `saveSetting(key, value)`
helper that issues a `PATCH /settings` with a single key-value pair. The `saving`
boolean is exposed for disabling save buttons during in-flight requests.
`useSettings` fetches from `GET /settings` on mount and exposes a
`saveSetting(key, value)` helper that issues a `PATCH /settings` with a
single key-value pair. The `saving` boolean is exposed for disabling save
buttons during in-flight requests.
`SettingsView` receives `settings`/`saveSetting`/`saving` from a single
`useSettings()` call at the top level and passes them as props to
`ModelsSection` and `ModelsFolderSetting` — avoiding triple fetch on mount.
`modelProps` (context window, loaded model) is fetched once in `App.jsx` and
passed down as a prop, eliminating a duplicate fetch on every settings open.
`SettingsView` is organised into sections:
@@ -284,4 +321,8 @@ boolean is exposed for disabling save buttons during in-flight requests.
active model dropdown, read-only model info panel (file, size, context window,
loaded model from llama-server)
- **About** — service health check panel, version
- **Appearance** — theme (coming soon)
- **Appearance** — theme (coming soon)
An error boundary (`SettingsSectionErrorBoundary`) wraps the Models section —
if the models fetch fails, only that section shows an error with a Retry
button rather than blanking the entire settings view.

View File

@@ -58,7 +58,7 @@ Six core tables:
- **entities** — named things the system learns about (people, places, concepts)
- **relationships** — directional labeled links between entities
- **summaries** — condensed episode groups for efficient context retrieval
- **projects** — named groupings of sessions with `name`, `description`, `colour`, `icon`, `isolated`
- **projects** — named groupings of sessions with `name`, `description`, `colour`, `icon`, `isolated`, `notes`
### Migrations
@@ -70,6 +70,7 @@ try { db.exec(`ALTER TABLE sessions ADD COLUMN name TEXT`); } catch {}
try { db.exec(`ALTER TABLE sessions ADD COLUMN project_id INTEGER REFERENCES projects(id)`); } catch {}
try { db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id)`); } catch {}
try { db.exec(`ALTER TABLE projects ADD COLUMN isolated INTEGER NOT NULL DEFAULT 0`); } catch {}
try { db.exec(`ALTER TABLE projects ADD COLUMN notes TEXT`); } catch {}
```
New migrations are always appended here — never modify the schema file for
@@ -87,22 +88,34 @@ keep the FTS index automatically in sync with the episodes table.
- `foreign_keys = ON` — enforces referential integrity and cascade deletes
- PRAGMAs set via `db.pragma()`, not `db.exec()`
### Dynamic Session Updates
### Dynamic Updates
`updateSession` builds its `SET` clause dynamically from only the fields
passed — prevents partial updates from overwriting fields that weren't
touched:
Both `updateSession` and `updateProject` build their `SET` clause dynamically
from only the fields passed — prevents partial updates from overwriting fields
that weren't touched:
```js
function updateSession(id, { name, projectId } = {}) {
// updateProject example
function updateProject(id, fields = {}) {
const allowed = ['name', 'description', 'colour', 'icon', 'isolated', 'notes'];
const updates = [];
const values = [];
if (name !== undefined) { updates.push('name = ?'); values.push(name ?? null); }
if (projectId !== undefined) { updates.push('project_id = ?'); values.push(projectId ?? null); }
// ...
for (const key of allowed) {
if (fields[key] !== undefined) {
updates.push(`${key} = ?`);
values.push(fields[key] ?? null);
}
}
if (updates.length === 0) return getProject(id);
values.push(id);
db.prepare(`UPDATE projects SET ${updates.join(', ')} WHERE id = ?`).run(...values);
return getProject(id);
}
```
This means saving just `{ notes: "..." }` won't touch `name`, `colour`, or
any other field.
## Qdrant / Semantic Layer
Three Qdrant collections are initialized on service startup: