@cat-factory/app 0.303.0 → 0.303.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ The SPA source lives under `app/` (the Nuxt srcDir).
|
|
|
15
15
|
- [What it is](#what-it-is)
|
|
16
16
|
- [Tech stack](#tech-stack)
|
|
17
17
|
- [Layout](#layout)
|
|
18
|
+
- [Nuxt UI: agent tooling and overrides](#nuxt-ui-agent-tooling-and-where-the-spa-overrides-the-skill)
|
|
18
19
|
- [Task swimlanes](#task-swimlanes)
|
|
19
20
|
- [Roles (engineer / product manager / designer)](#roles-engineer--product-manager--designer)
|
|
20
21
|
- [Interface modes (basic / advanced)](#interface-modes-basic--advanced)
|
|
@@ -41,7 +42,8 @@ over the WebSocket. How that sync works is written up in
|
|
|
41
42
|
|
|
42
43
|
## Tech stack
|
|
43
44
|
|
|
44
|
-
- **Nuxt 4 / Vue 3** SPA:
|
|
45
|
+
- **Nuxt 4 / Vue 3** SPA: the board is `pages/index.vue`, beside three standalone routes
|
|
46
|
+
(`mcp-authorize.vue`, `mcp-oauth-callback.vue`, `reset-password.vue`).
|
|
45
47
|
- **Pinia** (+ `pinia-plugin-persistedstate`): feature stores.
|
|
46
48
|
- **Vue Flow** (`core`, `background`, `controls`, `node-resizer`): the canvas.
|
|
47
49
|
- **Nuxt UI** + Tailwind: components and styling.
|
|
@@ -50,15 +52,15 @@ over the WebSocket. How that sync works is written up in
|
|
|
50
52
|
|
|
51
53
|
## Layout
|
|
52
54
|
|
|
53
|
-
| Path
|
|
54
|
-
|
|
|
55
|
-
| `app.vue`
|
|
56
|
-
| `pages
|
|
57
|
-
| `components/`
|
|
58
|
-
| `composables/`
|
|
59
|
-
| `stores/`
|
|
60
|
-
| `types/`
|
|
61
|
-
| `utils/`
|
|
55
|
+
| Path | Contents |
|
|
56
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `app.vue` | Root; wraps the page in `AuthGate`. |
|
|
58
|
+
| `pages/` | The board (`index.vue`: sidebar, canvas, toolbar, inspector, focus view, all modals), plus the standalone `mcp-authorize`, `mcp-oauth-callback` and `reset-password` routes. |
|
|
59
|
+
| `components/` | UI grouped by area (see [Key UI surfaces](#key-ui-surfaces)). |
|
|
60
|
+
| `composables/` | `useApi` (typed client), `useWorkspaceStream` (WebSocket sync), `useBlockDrag`, `useBlockQueries`, `useBoardFlow`, `useSemanticZoom`, `useDepLabels`. |
|
|
61
|
+
| `stores/` | Pinia stores, one per feature domain. |
|
|
62
|
+
| `types/` | TypeScript domain unions (`domain.ts`) and wire types mirroring the contracts. |
|
|
63
|
+
| `utils/` | Small pure helpers. |
|
|
62
64
|
|
|
63
65
|
### The board's top overlay region has ONE owner
|
|
64
66
|
|
|
@@ -304,6 +306,64 @@ A **theme** is a Nuxt UI theme editor document (https://ui.nuxt.com/theme): the
|
|
|
304
306
|
|
|
305
307
|
**Colour mode** (system / light / dark) is `@nuxtjs/color-mode`'s, registered by Nuxt UI, with its own persistence; `nuxt.config.ts` follows the system preference and falls back to dark. `AppearanceSwitcher` at the sidebar bottom is the one control for both axes. The pre-JS loading shell (`spa-loading-template.html`) cannot resolve a token (no stylesheet is loaded yet), so the plugin caches the computed colours of the four tokens the shell paints with, per colour mode, in `localStorage` (`utils/theme/bootPalette.ts`), and a two-line inline script in the shell applies the entry for the mode the `<head>` colour-mode script has already put on `<html>`. The hardcoded defaults are the default theme's, used only until a theme has been seen in that mode.
|
|
306
308
|
|
|
309
|
+
## Nuxt UI: agent tooling and where the SPA overrides the skill
|
|
310
|
+
|
|
311
|
+
The SPA runs on Nuxt UI 4. Two first-party sources carry the rules this repo does not restate:
|
|
312
|
+
|
|
313
|
+
- **The vendored [`nuxt-ui` skill](https://github.com/kibertoad/cat-factory/tree/main/.claude/skills/nuxt-ui)** teaches WHEN to reach for which
|
|
314
|
+
component (Modal vs Slideover vs Drawer, Tabs vs NavigationMenu, Toast vs Alert) and HOW to build
|
|
315
|
+
well (the variant-weight table, one solid primary per view, semantic utility roles). It is
|
|
316
|
+
vendored under `.claude/skills/nuxt-ui/`, beside the project's own skills, so the rules version
|
|
317
|
+
with the code and every contributor on Claude Code reads the same text. This is the Claude Code
|
|
318
|
+
surface: `.claude/skills/` auto-loads for a local Claude Code session, and the repo `.mcp.json` is
|
|
319
|
+
offered once for approval on the first session in the repo (re-offered when the file changes); a
|
|
320
|
+
decline is remembered, and `claude mcp reset-project-choices` un-sticks it. Another tool that reads
|
|
321
|
+
`AGENTS.md` (Codex, for one) gets the pointers below but not an auto-loaded skill or an auto-wired
|
|
322
|
+
server. Making the platform's OWN coder agents (Claude Code, Codex, Pi in
|
|
323
|
+
the runner image) apply the skill is a separate, backend change: they stage skills and tool
|
|
324
|
+
servers per run through the agent-capabilities system (ADR 0024 /
|
|
325
|
+
[`custom-agents.md`](https://github.com/kibertoad/cat-factory/blob/main/backend/docs/custom-agents.md)), not from these repo files. The `@nuxt/ui` npm
|
|
326
|
+
package does not ship the skill, so it cannot move with `pnpm update`: it is vendored from the
|
|
327
|
+
`v4` branch of `nuxt/ui` (which tracks the 4.x line) and pinned by convention to the Nuxt UI
|
|
328
|
+
major in [`package.json`](./package.json) (`@nuxt/ui@^4.11`). Refresh it by re-vendoring
|
|
329
|
+
`skills/nuxt-ui/` from that branch (or `npx skills add nuxt/ui`) when the major moves. The
|
|
330
|
+
vendored files are kept byte-for-byte as upstream: `.oxfmtrc.json` exempts them from the
|
|
331
|
+
formatter and `scripts/check-doc-links.mjs` skips them, so a refresh stays a clean diff even when
|
|
332
|
+
upstream ships a link this repo's own doc rules would reject.
|
|
333
|
+
- **The [`nuxt-ui` MCP server](https://github.com/kibertoad/cat-factory/blob/main/.mcp.json)** (`https://ui.nuxt.com/mcp`, declared repo-level)
|
|
334
|
+
answers WHAT a component accepts: props, slots, events, theme files and examples per component.
|
|
335
|
+
The skill defers to it for every API question.
|
|
336
|
+
|
|
337
|
+
The SPA's own rules win where they and the skill disagree. Each entry below links the rule it
|
|
338
|
+
protects, so the two never contradict silently:
|
|
339
|
+
|
|
340
|
+
- **Visible copy is an i18n key, never a literal string.** The skill's examples hard-code labels and
|
|
341
|
+
placeholders (`label="Email"`, `placeholder="you@example.com"`); here every user-facing string
|
|
342
|
+
goes through `@nuxtjs/i18n` and lands in the catalog. [Rule](#internationalization-i18n-authoring).
|
|
343
|
+
- **Schema validation goes through valibot, never zod.** The skill's `forms.md` recipe opens with
|
|
344
|
+
`import * as z from 'zod'` and validates through `z.object({...})`; this package depends on
|
|
345
|
+
`valibot` (and `@toad-contracts/valibot`) and has no `zod`, so a `zod` import fails typecheck and
|
|
346
|
+
"fixing" it pulls in a dependency the repo deliberately lacks (which then has to clear the 24h
|
|
347
|
+
`minimumReleaseAge` gate). Validate with valibot, mirroring the wire contracts
|
|
348
|
+
([`@cat-factory/contracts`](https://github.com/kibertoad/cat-factory/tree/main/backend/packages/contracts)).
|
|
349
|
+
- **A backend-declared form renders through `DescriptorFields.vue`, not a hand-built `UForm`.** The
|
|
350
|
+
skill teaches the hand-rolled `UForm` + `UFormField` shape for every form; when the backend
|
|
351
|
+
declares the fields and the SPA only collects them, that shape is forbidden here.
|
|
352
|
+
[Rule](#a-backend-declared-form-renders-through-descriptorfieldsvue).
|
|
353
|
+
- **Failure toasts go through the funnel, not a bare `toast.add`.** The skill uses `useToast()`
|
|
354
|
+
freely; here a failed backend call is reported with `usePipelineErrorToast().present(error, key)`.
|
|
355
|
+
[Rule](#every-failure-toast-goes-through-one-funnel).
|
|
356
|
+
- **Icon-only buttons carry an accessible name.** Use `common/IconButton.vue`
|
|
357
|
+
([source](./app/components/common/IconButton.vue)), which applies `label` as both `title` and
|
|
358
|
+
`aria-label`, rather than a bare `<UButton icon>`.
|
|
359
|
+
- **Colour goes through theme tokens, never a fixed palette shade.** The skill bans raw Tailwind
|
|
360
|
+
hues; this repo also bans fixed numbered aliases, enforced by
|
|
361
|
+
[`scripts/check-frontend-palette.mjs`](https://github.com/kibertoad/cat-factory/blob/main/scripts/check-frontend-palette.mjs).
|
|
362
|
+
[Rule](#colour-through-theme-tokens-never-a-fixed-palette-shade).
|
|
363
|
+
- **Confirmation dialogs use the shared `useConfirm`.** The skill shows `useOverlay()` per call
|
|
364
|
+
site; here callers `await useConfirm().confirm({...})`
|
|
365
|
+
([source](./app/composables/useConfirm.ts)) against one app-mounted `<ConfirmDialog />`.
|
|
366
|
+
|
|
307
367
|
## Task swimlanes
|
|
308
368
|
|
|
309
369
|
A service frame lays its tasks out in **status lanes**, not at coordinates. Three lanes a reader
|
|
@@ -1357,12 +1417,21 @@ wrong place is invisible until a user cannot find it:
|
|
|
1357
1417
|
|
|
1358
1418
|
## Develop & test
|
|
1359
1419
|
|
|
1420
|
+
This package has no `dev` or `lint` script of its own: it is a layer, consumed through `extends`,
|
|
1421
|
+
and linting is whole-tree from the repo root (CLAUDE.md). The `AGENTS.md` verify checklist carries
|
|
1422
|
+
the same list, plus the install-free frontend guards.
|
|
1423
|
+
|
|
1360
1424
|
```bash
|
|
1425
|
+
# From the repo root:
|
|
1361
1426
|
pnpm install
|
|
1362
|
-
pnpm dev
|
|
1363
|
-
pnpm
|
|
1364
|
-
pnpm
|
|
1365
|
-
|
|
1427
|
+
pnpm dev:frontend # Nuxt dev server via deploy/frontend; set NUXT_PUBLIC_API_BASE to a running Worker
|
|
1428
|
+
pnpm exec turbo run typecheck --filter=@cat-factory/app # typecheck goes through Turbo
|
|
1429
|
+
pnpm lint # oxlint + oxfmt over the whole tree
|
|
1430
|
+
|
|
1431
|
+
# From frontend/app:
|
|
1432
|
+
pnpm exec vitest run <file> # run the spec your change touched
|
|
1433
|
+
pnpm i18n:check # catalog is well-formed
|
|
1434
|
+
pnpm i18n:parity # locales are in parity
|
|
1366
1435
|
```
|
|
1367
1436
|
|
|
1368
1437
|
> Building/deploying the static site is covered in the deployment docs: see the
|
|
@@ -35,8 +35,10 @@ const back = useIntegrationBack(open)
|
|
|
35
35
|
// that trade on somebody's behalf.
|
|
36
36
|
const RECOMMENDED_SLUGS = [
|
|
37
37
|
'anthropic/claude-fable-5.1',
|
|
38
|
+
'anthropic/claude-opus-5.5',
|
|
38
39
|
'anthropic/claude-opus-5',
|
|
39
40
|
'openai/gpt-6-astra',
|
|
41
|
+
'openai/gpt-6-sol',
|
|
40
42
|
'openai/gpt-5.6-sol',
|
|
41
43
|
'openai/gpt-5.6-terra',
|
|
42
44
|
'google/gemini-3.1-pro-preview',
|
|
@@ -44,6 +46,7 @@ const RECOMMENDED_SLUGS = [
|
|
|
44
46
|
'meta/muse-spark-1.3',
|
|
45
47
|
'deepseek/deepseek-v4.1-flash',
|
|
46
48
|
'moonshotai/kimi-k2.7-code',
|
|
49
|
+
'x-ai/grok-4.7',
|
|
47
50
|
'z-ai/glm-5.3',
|
|
48
51
|
'z-ai/glm-5.2',
|
|
49
52
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.303.
|
|
3
|
+
"version": "0.303.1",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@modular-vue/runtime": "^1.4.1",
|
|
27
27
|
"@modular-vue/vue": "^1.4.1",
|
|
28
28
|
"@nuxt/fonts": "^0.14.0",
|
|
29
|
-
"@nuxt/ui": "^4.11.
|
|
29
|
+
"@nuxt/ui": "^4.11.2",
|
|
30
30
|
"@nuxtjs/i18n": "^10.6.0",
|
|
31
31
|
"@pinia/nuxt": "^1.0.2",
|
|
32
32
|
"@toad-contracts/core": "1.0.0",
|