-
-
Notifications
You must be signed in to change notification settings - Fork 0
2.1 architecture
wiki[bot] edited this page Aug 23, 2026
·
4 revisions
src/
├── App.vue / main.ts # app shell + boot wiring
├── api/ # typed REST layer (api-url, config, conversations, storage, queries/)
├── assets/ # static assets
├── components/ # feature areas: app, chat, sysctl, dlq, pproc, widgets, shared, debug
├── composables/ # cross-area composable logic
├── stores/ # Pinia stores (+ helpers/create-socket-provider)
├── types/ utils/ # shared types and pure helpers
└── version.ts # build version constants
-
Components are thin. A
.vuefile binds props/state to the template and forwards events. Logic lives in composables, side-effect-free logic in helpers. -
Composables do the work. Cross-area shared logic in
src/composables/; feature-scoped logic incomponents/<area>/composables/(or per-componentcomposables/folders). Examples:use-submit,use-chat-input,use-chat-think,use-socket-subscription,use-read-tracker,use-action-bar,use-blink,use-frozen-read-snapshot,use-vertical-carousel,use-native-scroll,use-carousel-blend(exchange-list scroll modes). - State is Pinia, server state is Query. Persistent UI/domain state in stores; remote resource fetching via TanStack Query where applicable.
-
DOM refs via
useTemplateRef. Template refs are declared with Vue 3.5'suseTemplateRef('name')inside composables — never destructured from a composable merely to satisfy a template string ref (that pattern is invisible tovue-tsc'snoUnusedLocals).
| Store | Owns |
|---|---|
app |
Shell state: active area/tab, UI toggles, scroll mode (default + per-conversation), media priority, temporary-conversation retention |
conversation |
Conversation list, active conversation, exchange models |
messages |
Incoming API/socket message stream (addMessage) |
session |
Session identity (sessionId lifecycle) |
socket |
Connection lifecycle + callback plumbing to messages/debug
|
debug |
Socket/API debug journal |
dlq |
DLQ counter poll, selected envelopes |
models |
Model catalogue fetched from the harness API |
preprocessing |
Preprocessing settings, push-to-server sync |
theme |
Theme selection + persistence |
The socket provider (stores/helpers/create-socket-provider.helper.ts) is a factory that wires the socket store to message/debug sinks with explicit subscriptions instead of ad-hoc on() calls scattered through components.
-
api-url.ts— base URL composition fromVITE_API_URL; -
config.api.ts— SysCtl/config endpoints (HarnessConfig, provider overrides, preprocessing); -
conversations.api.ts— conversation persistence endpoints; -
playlists.api.ts— server-side playlist persistence endpoints; -
stock-data.api.ts— cached end-of-day market history + coverage endpoints (feeds the D3 stock charts); -
warm-model.api.ts— opt-in model warm-up trigger; -
storage.api.ts— image payload probes/uploads; -
queries/— TanStack Query definitions for remote resources.
Components never call fetch directly — they go through stores/composables which use the API layer.
- Components:
PascalCase.vue; sibling files colocated per component folder (Component.vue,Component.spec.ts,Component.stories.ts,composables/,helpers/,types/,styles/). - Composables:
kebab-case,use-prefix. - Pure functions live in
helpers/and are unit-testable without mounting anything. - Tests: Vitest + Testing Library (
*.spec.ts), colocated. Stories: Storybook (*.stories.ts) for visual states incl. smoke specs.
socket.io-client ─▶ socket store ─(subscriptions)─▶ messages store ─▶ conversation store
│ │
└▶ debug store (journal) exchange components render
use-socket-subscription gives components declarative, auto-cleaned subscriptions; use-frozen-read-snapshot + use-read-tracker handle scroll/read-state during token streaming without layout jank.
pnpm lint (ESLint flat config incl. Vue rules), vue-tsc -b as part of build, depcheck, dependency-cruiser (depcruise), ts-unused-exports. CI enforces all of them (see non-release.ci.yml).