jules 3dbf2ac175 feat: auth scaffold, notifications inbox, resources CRUD, vitest baseline, typed API client
Auth
- ~/lib/session.ts: Session type + loadSession/signIn/signOut/hasSession,
  reactive useSession hook (mock backend; replace fetch calls with your
  real auth endpoint when ready)
- routes/login.tsx: form with email/password (mock-validated), bounces
  to ?next= on success
- AppShell: redirects to /login when no session; account-menu Sign out
  now actually signs out; live session.name/email used for the appbar
  avatar (falls back to profile)

Notifications
- ~/lib/notifications.ts: persistent inbox with kinds (info/success/
  warning/error), unreadCount, markRead, markAllRead, dismiss,
  dismissAll; seedIfEmpty for a friendly first-run
- AppShell bell: 320px popover with badge, kind dots, per-row open
  (navigates to href) and dismiss; Mark all read + Clear actions
- Hidden NotificationDispatcher in AppShell so the action bus can
  create real notifications via fill notif-title / notif-body /
  notif-kind / notif-href + click notif-create

Data layer
- ~/lib/api.ts: typed apiFetch<T> + api.get/post/put/patch/del,
  auto-attaches the session token, throws structured ApiError, signs
  out on 401
- ~/lib/resources.ts: example domain entity (CRUD) backed by
  localStorage today; each call is a 1:1 swap for api.get/post/put/del
- routes/resources.tsx: real working table — search, add, inline
  status edit, delete; seeded demo rows on first load

Tests
- vitest + jsdom + @testing-library/react + @testing-library/jest-dom
  + vite-tsconfig-paths installed
- vitest.config.ts (jsdom, globals, ~ aliases via tsconfig-paths)
- vitest.setup.ts (RTL cleanup + localStorage clear between tests)
- app/lib/session.test.ts and resources.test.ts as starter coverage
- npm test / npm run test:watch scripts

UI Control catalog
- Login form, resources CRUD, notifications inbox, and the hidden
  notif-bridge ids tagged so the assistant can drive every new surface

Threads
- ThreadMessage now carries optional agentId so per-message authorship
  survives persona switches and handoffs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:59:31 +10:00

crema-app-aifirst-template

Minimal hybrid traditional + AI-first webapp scaffold built on the Crema design system. Sibling to crema-app-template (the bare scaffold) — this template adds the AI assistant surface, the command bus, scripts, and a virtual cursor.

The pitch: most surfaces are normal (sidenav, tables, forms) and an Assistant surface where the LLM can actually drive the UI on the user's behalf — click, navigate, fill, submit — through a documented command bus that scripts and remote control share.

Quick start

npm install
npm run dev

Open http://localhost:5173. The app probes a local LM Studio at http://localhost:1234/v1 and falls back to a MockLLM if it can't reach one — the UI is always usable.

To use the Assistant with a real model:

  1. Run LM Studio (or any OpenAI-compatible local server — Ollama, vLLM, etc.). Enable CORS in its server settings.
  2. Visit /settings and confirm the base URL.
  3. Visit /assistant and click Enable UI Control.
  4. Type "take me to settings" — watch the cursor.

What this template gives you

App shell

app/components/layout/app-shell.tsx. Collapsible left rail (icon-only ↔ expanded with labels, persisted), top appbar (search · scripts · theme · bell · avatar dropdown), mobile sheet at <md. Brand and user identity come from app/lib/identity.ts — swap useBrand() / useUser() for a real session later. Skip-to-main-content link, focus rings, [data-action] slots throughout. The shell is template code, not a lib — fork it, customize it, no need to upgrade an upstream.

AI assistant with UI control

/assistant route. Streams from any OpenAI-compatible LLM via @crema/llm-ui, with mock fallback. Message bubbles render markdown, fenced ```action blocks in the model's reply are extracted and run through the command bus. Status bar, model selector, token meter, retry probe, stop-while-streaming, persistent UI Control toggle.

Command bus + DSL + virtual cursor

Provided by @crema/action-bus. JSON commands dispatch to handlers; producers are LLM tool calls, scripts, and (optional) WebSocket — all funnel through one bus.

window.commandBus.dispatch({ type: "navigate", path: "/resources" })
window.commandBus.listActions()    // every visible [data-action] on screen

Plus a plain-text DSL for humans / LLM:

navigate /resources
wait_for resources-table
click row-acme-corp
expect detail-panel to_contain "Acme"

Hit ⌘⇧P for the script runner dialog. See docs/AI_FIRST.md for the full system tour.

Mightypix theme

Default theme is lib-theme-mightypix — warm cream surfaces, ink-blue accent, Source Serif 4 for assistant prose. Swap by editing one @import line in app/app.css. Per-route alt themes via <AppShell theme="…">. See app/components/layout/THEME_CONTRACT.md.

Sibling-cloning

Crema libs are sibling git repos, not npm packages. Before npm install you need them cloned alongside this template:

your-workspace/
  crema-app-aifirst-template/   ← this repo
  lib-action-bus/
  lib-aifirst-ui/
  lib-chat-ui/
  lib-llm-ui/
  lib-notification-ui/
  lib-theme-mightypix/

Use the crema add CLI from create-crema-app, or clone manually. tsconfig paths and app.css @source lines are pre-wired.

Structure

app/
  routes/                  # 6 routes: Overview, Resources, Activity, Assistant, Library, Settings
  components/
    layout/                # AppShell, Appbar, ThemeToggle, THEME_CONTRACT.md
    assistant/             # MessageBody (markdown + action-block pill)
    scripts-dialog.tsx     # ⌘⇧P script runner UI
    ui/                    # shadcn primitives
  lib/
    identity.ts            # useBrand() / useUser() — swap for real session
    llm-settings.ts        # persisted base URL / context / response cap
    page-meta.ts           # `pageTitle("…")` for route <title>
    utils.ts               # shadcn `cn()`
  app.css                  # active theme @import + tailwind + @source lines
  root.tsx                 # ToastProvider + CommandBusProvider
public/
  scripts/
    demo-tour.script       # sample DSL: tour the rail
    demo-search.script     # sample DSL: focus + fill search
docs/
  AI_FIRST.md              # full system tour

Dev scripts

Command What it does
npm run dev Vite dev server
npm run build Production build
npm run start Serve the built app
npm run typecheck react-router typegen && tsc
bash start.sh / bash stop.sh Run dev server in the background, log to .demo.log

Conventions to know

  • [data-action="<id>"] on every interactive element — turns it into something the LLM, scripts, and tests can target. Naming: nav-*, appbar-*, avatar-*, assistant-*, settings-*, etc.
  • Tokens, not values. bg-card, text-foreground, var(--primary) — never hex.
  • Lib-shaped components live as sibling repos. Edits commit to the lib's own repo. See CLAUDE.md for polyrepo gotchas.

Further reading

What's not in here

  • Real auth — useUser() returns a static stub
  • Real backend — /resources, /activity, /library are placeholder routes
  • Production safety gates around LLM-driven actions — [data-action-danger] and confirmation flows are sketched in docs/AI_FIRST.md but not built

These are deliberate. The template is the framework; domain content is what you add.

Description
Hybrid traditional + AI-first webapp scaffold (sibling of crema-app-template). Pre-wires the Assistant surface, command bus, virtual cursor, and mightypix theme.
Readme 392 KiB
Languages
TypeScript 91.5%
CSS 6.5%
JavaScript 1.8%
Shell 0.2%