feat: design & quality uplift (Suspect Web, Case Archive, world switcher, renderer decomposition) - #123
Open
komod0 wants to merge 25 commits into
Open
feat: design & quality uplift (Suspect Web, Case Archive, world switcher, renderer decomposition)#123komod0 wants to merge 25 commits into
komod0 wants to merge 25 commits into
Conversation
- eslint.config.mjs: enforce eqeqeq/no-var/no-explicit-any/no-console and prefer named exports (Next.js entrypoints excepted); pre-existing violations set to warn for incremental cleanup - add MIT LICENSE (README claimed MIT with no file) - docs: fix stale dir references after the legacy-renderer/training removal, correct test count, archive proposals referencing deleted code - bump deps and add skills-lock.json
- new routes: images/generate, images/[id]/{direct,redevelop}, interrogation,
voice-design, live-music/key, sfx/[world]/[kind]
- per-domain stores (image/cast/sfx/recording) + resolveImageBytes
- live score (Lyria) + reactive audio-event bus and useLiveWire hook
- shared lib/api/responses (apiError/parseJsonBody) and elevenlabs/client to
collapse duplicated route boilerplate
- new aesthetic fonts
…lines) Extract ~25 component renderers into src/components/a2ui/renderers/ and the shared context/binding/motion/validation/registry helpers into src/components/a2ui/internal/. SurfaceRenderer is now the context providers + COMPONENT_MAP assembly. Public exports and behavior unchanged; the existing COMPONENT_MAP registry made the extraction mechanical. Also hardens the generated ModalRenderer (focus trap, autofocus, Escape-to-close).
- new relationshipGraph component end-to-end: schema arm + normalize coercion (suspectWeb/links/source-target synonyms), SUPPORTED_LEGACY_TYPES, adapter handler, catalog schema, RelationshipGraph renderer (deterministic cork-board layout, red-string edges), and playbook guidance - refactor normalizeA2UI (366-line monolith) and walk() (265-line switch) into per-type dispatch; extract liftTitle helper - validateCatalogDev: dev-only non-throwing validation of adapter output against catalog schemas, making the v0.9 catalog load-bearing
- archiveSlice: persisted, capped (24) history of completed generations with oldest-eviction; CaseArchive slide-up film-strip with search and non-destructive restore (reuses the variant snapshot/restore path) - promote world-switching from a dropdown to a first-class desk gesture: WorldGalleryTile living previews, ProfileSelector gallery, on-desk WorldSwitcher triggering the existing cinematic transition - WorldTransition + useWorldSwitch cinematic world-change pipeline; CaseYarn red-string overlay; per-world fallback narration in DetectiveWorkspace
- extend AestheticCopy with logTitle/thinkingLines/chatEmptyLine/ ttsUnavailableLine + dictaphone/interrogation strings; in-character values for all 6 worlds (kills noir-only English leaking into other worlds) - chat: detective avatar instead of generic Bot icon, diegetic per-message timestamp/entry metadata, rotating thinking lines, sample-prompt chips, up/down prompt-history recall - dictaphone: VU needle driven by a real WebAudio AnalyserNode (was Math.random)
…viewport - useFocusTrap + CustomizationPanel dialog semantics (focus trap, Escape, focus restoration) - shared FileUploadField replacing duplicated upload logic in Color/Audio customizers (fixes the drifted 10MB/20MB size limits) - viewport meta + desktop-only notice in layout - customization lab + theme-generator refinements
…e world - KanbanBoard/DataDashboard: literal emoji glyphs (assignee, trend arrows) replaced with theme-tinted lucide icons - SandpackPreview: build a Sandpack theme from the active aesthetic palette instead of hardcoded dark, so ejected previews match the chosen world - noir effects, photo developer, evidence skeleton, globals polish
- events.spec.ts: replace the always-passing expect(true).toBeTruthy() with a real assertion that the stream request fires - replace race-prone waitForTimeout sleeps with deterministic web-first waits; drop CI retries to 0 so flakes surface instead of being masked - new useA2UIStream unit test (previously-untested client SSE consumer) - stream/theme routes adopt shared apiError/parseJsonBody helpers
Both are unused after the legacy-renderer removal; no remaining importers.
The dev catalog-drift validator flagged the adapter emitting Slider without the schema-required min/max/value. Emit the same defaults the renderer falls back to (0..100, value seeded to min) so the flat output satisfies sliderSchema. Verified: zero catalog drift warnings across the full suite.
The on-desk world switcher + archive buttons widened the main column's min-content; a bare 1fr is minmax(auto,1fr) and won't shrink below that, so the board pushed past the reserved sidebar margin and overlapped it. Use minmax(0,1fr) so the column can shrink. Caught by the e2e 'panels do not overlap' spec.
… gallery
- CustomizationPanel: only auto-close on committing to a BUILT-IN world (the
cinematic-on-clean-desk case); selecting/creating a CUSTOM profile keeps the
lab open so you can immediately tune it (was closing → tabs detached → e2e
hangs on upload/tab tests)
- migrate ~30 e2e call sites from the removed 'Select active profile' dropdown
to the new always-visible gallery ('Switch to <world>' buttons)
- rework Test 5.2 to reopen the lab between rapid built-in world switches
Update the two DeskLayout grid-template assertions to match the minmax(0,1fr) main column from the sidebar-overlap fix.
- video: veo-3.1-*-preview rejects `generateAudio` (400 INVALID_ARGUMENT) —
audio is native/prompt-steered. Gate the flag to the veo-3.0 family only
(3.1 generates audio natively); fixes video generation failing outright.
- schema: coerce dataDashboard trend.value emitted as a string ("12", "+5%")
to a number, dropping non-numeric values instead of salvaging the whole
dashboard away.
- schema: drop out-of-enum button `action` values (e.g. "navigate") so the
button still renders instead of being dropped from the surface.
- tests for all three, incl. veo-3.0 still opting into generateAudio.
- grand-hotel no longer reuses noir's jazz loop: music.src now points at a new lazy-generated /api/music/grand-hotel/atmosphere route that renders the world's musicStylePrompt (1920s lobby band) via ElevenLabs Music on first request, caches to disk, and falls back to the noir loop without a key — mirroring the existing lazy SFX pipeline. - cyber-fixer voice: replace the stock 'Adam' (generic-AI sounding) with Matt (natural, hyper-conversational) and retune voiceDirection for a natural voice (was tuned for the robotic one).
…mpt-first tool comment
…rops; gallery Icons row
…eractive fields
Display + interactive fields (text/stat/input/select/checkbox value, button
action) now accept {path}/functionCall bindings instead of being String()-ed to
[object Object] or salvaged out. Adds matchSet (conditional write), array
actions, a reveal wrapper, and a stateImage that edits-on-state via a new
non-destructive /fork route (caches both states).
TypewriterText kept typing progress in internal state seeded to empty, so the
conditional sidebar remount reset it and re-typed completed messages. Only the
streaming message animates now (animate={isStreaming}); completed messages
render instantly and snap to full text if streaming ends mid-type.
…tness
Consolidation audit follow-up: widen every resolved display/value field to
accept {path}/functionCall bindings (heading/paragraph/card title+desc, stat
helper, slider/dateTimeInput value+min/max, kanban/dashboard/graph title, icon
name, audio description/speaker) so a bound node is no longer dropped at
validation. Also: matchSet with an omitted then/else no longer DELETES the
target (undefined = v0.9 delete), and the eject exporter handles reveal
(children inline) + stateImage (base image) instead of dropping them. Plus
dev-timestamps flush-on-close. +regression tests.
…_MAP - Extract the normalization layer into normalize.ts and the shared pure primitives (SUPPORTED_LEGACY_TYPES, isBindingObject) into legacy-constants.ts, a leaf module — one-way DAG (constants ← normalize ← schema), no cycle. schema.ts re-exports them so @/lib/protocol/schema imports are unchanged. - Drop the redundant double-normalize: a2uiInputSchema already runs normalizeA2UI via z.preprocess, so callers pass raw nodes (route + adapter). - Type COMPONENT_MAP as Record<CatalogComponentName|ExtensionComponentName, FC> so a missing/typo'd renderer is a compile error, not a runtime [Unknown: X] (mirrors the adapter's compiler-enforced handlers map). Pure refactor — 1270 tests unchanged, behavior identical.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A large design-and-quality uplift building on the repo audit. Lands the previously-uncommitted media-generation work, a full
SurfaceRendererdecomposition, a new Suspect Web A2UI component, the Case Archive + on-desk world switcher, a per-world copy system, and a batch of polish/quality fixes.Authored as
komod0. Branched fresh offmain.What's included (10 focused commits)
LICENSE, refresh docs after the legacy-renderer/training removal, bump deps.lib/api/responses+elevenlabs/clientto collapse route boilerplate.SurfaceRenderergod-component (2708 → 370 lines) intorenderers/+internal/; harden the generated Modal (focus trap, Escape, autofocus). Behavior & public exports unchanged.normalizeA2UI/walk()into per-type dispatch; dev-only catalog-drift validator makes the v0.9 catalog load-bearing.AestheticCopysystem (kills noir-only English in the other 5 worlds); detective avatar + diegetic message metadata; real-audio VU meter (wasMath.random); prompt-history recall.FileUploadField(fixes drifted upload size limits), viewport meta + desktop notice.waitForTimeoutsleeps with deterministic waits, drop CI retries to 0, adduseA2UIStreamcoverage.Verification
Local full gate green:
prettier✓ ·eslint(0 errors) ✓ ·stylelint✓ ·typecheck✓ · vitest 1223 passing / 107 files ✓ ·next build✓. Zero catalog-drift warnings across the suite.🤖 Generated with Claude Code