@vpxa/aikit 0.1.332 → 0.1.334

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.
@@ -1,798 +0,0 @@
1
- # AI Kit Viewers
2
-
3
- Self-contained HTML viewers for AI Kit skills. Each viewer is built from React 19 source, bundled with Vite, flattened into a single HTML file with `vite-plugin-singlefile`, and then embedded into scaffold skill definitions.
4
-
5
- This folder is a standalone package. It is not part of the parent pnpm workspace. Install and build it from this directory with `--ignore-workspace`.
6
-
7
- ## Overview
8
-
9
- Use this package when a skill needs a rich interactive artifact that can ship as one HTML asset.
10
-
11
- Current viewers:
12
-
13
- - `c4-viewer.html` -> C4 architecture viewer powered by ReactFlow and ELK layout
14
- - `tour-viewer.html` -> interactive code tour viewer powered by ReactFlow and a BFS tree layout
15
-
16
- Why single-file HTML:
17
-
18
- - AI Kit skill definitions embed assets as template literals in `.mjs` files
19
- - A self-contained HTML file avoids external JS, CSS, font, and image dependencies
20
- - Embedding one file is simpler and more reliable than coordinating asset directories
21
- - Browser rendering is predictable inside skill-driven workflows
22
-
23
- ## Quick Start
24
-
25
- Run these commands from `packages/viewers`.
26
-
27
- ```bash
28
- pnpm install --ignore-workspace
29
- pnpm build
30
- pnpm embed
31
- ```
32
-
33
- Available scripts:
34
-
35
- ```bash
36
- pnpm dev:c4
37
- pnpm dev:tour
38
- pnpm build
39
- pnpm embed
40
- ```
41
-
42
- What each command does:
43
-
44
- - `pnpm install --ignore-workspace` installs this package independently from the monorepo root
45
- - `pnpm dev:c4` serves the C4 entry HTML with `vite.c4.config.ts`
46
- - `pnpm dev:tour` serves the Tour entry HTML with `vite.tour.config.ts`
47
- - `pnpm build` writes single-file artifacts into `dist/`
48
- - `pnpm embed` injects built HTML into scaffold skill definitions
49
-
50
- After `pnpm embed`, rebuild the parent scaffold output from the `aikit-mcp` root if you need refreshed generated artifacts. `embed.mjs` prints that reminder explicitly.
51
-
52
- ## Package Layout
53
-
54
- ```text
55
- viewers/
56
- ├── README.md
57
- ├── package.json
58
- ├── tsconfig.json
59
- ├── pnpm-lock.yaml
60
- ├── .gitignore
61
- ├── c4-viewer.html
62
- ├── tour-viewer.html
63
- ├── vite.c4.config.ts
64
- ├── vite.tour.config.ts
65
- ├── embed.mjs
66
- ├── src/
67
- │ ├── env.d.ts
68
- │ ├── shared/
69
- │ │ ├── theme.css
70
- │ │ ├── types.ts
71
- │ │ ├── ThemeProvider.tsx
72
- │ │ ├── Layout.tsx
73
- │ │ ├── Header.tsx
74
- │ │ ├── Footer.tsx
75
- │ │ ├── Sidebar.tsx
76
- │ │ ├── Card.tsx
77
- │ │ ├── Badge.tsx
78
- │ │ ├── simple-template.css
79
- │ │ └── simple-template.html
80
- │ ├── c4/
81
- │ │ ├── C4Viewer.tsx
82
- │ │ └── main.tsx
83
- │ └── tour/
84
- │ ├── TourViewer.tsx
85
- │ └── main.tsx
86
- └── dist/
87
- ├── c4-viewer.html
88
- └── tour-viewer.html
89
- ```
90
-
91
- Important distinction:
92
-
93
- - Root `*-viewer.html` files are Vite entry templates
94
- - `dist/*.html` files are the final self-contained artifacts
95
- - `embed.mjs` reads from `dist/`, not from the root entry templates
96
- - `package.json` `exports` points at the built viewer HTML assets, which keeps workspace health satisfied without changing the standalone build flow
97
-
98
- ## Architecture
99
-
100
- ## Build Stack
101
-
102
- - React 19 for the viewer UI
103
- - `@xyflow/react` v12 for graph rendering and interaction
104
- - `elkjs` for auto-layout in the C4 viewer
105
- - Vite 8 for dev/build
106
- - `vite-plugin-singlefile` to inline JS and CSS into one HTML document
107
-
108
- ## Runtime Shape
109
-
110
- Each full viewer follows the same structure:
111
-
112
- 1. Root HTML entry provides a `<div id="root"></div>` and optional JSON payload script tag
113
- 2. `src/<viewer>/main.tsx` reads `window.__VIEWER_CONFIG__` first, then falls back to legacy inline JSON
114
- 3. The viewer component wraps its content in `ThemeProvider`
115
- 4. `Layout`, `Header`, and `Footer` provide the shared shell
116
- 5. ReactFlow renders the interactive canvas
117
- 6. Vite emits a single HTML file into `dist/`
118
- 7. `embed.mjs` injects that HTML into the relevant skill definition file
119
-
120
- ## Shared Template System
121
-
122
- `src/shared/` is the design system and shell contract for full viewers.
123
-
124
- Treat it as the source of truth for:
125
-
126
- - theme tokens
127
- - layout variants
128
- - header and footer behavior
129
- - shared TypeScript interfaces
130
- - reusable UI primitives
131
-
132
- Two template tiers live here:
133
-
134
- - Full template: React + ReactFlow viewers such as C4 and Tour
135
- - Simple template: `simple-template.html` + `simple-template.css` for pure HTML/CSS rendering patterns
136
-
137
- Use the full template for interactive graph viewers. Use the simple template only when a viewer does not need React or graph interaction.
138
-
139
- ## Shared Components
140
-
141
- Every new full viewer should use the shared shell components unless there is a strong reason not to.
142
-
143
- ## `ThemeProvider`
144
-
145
- Purpose:
146
-
147
- - owns the light/dark/system theme mode state
148
- - resolves system preference with `matchMedia('(prefers-color-scheme: dark)')`
149
- - persists mode to `localStorage` under `aikit-theme-mode`
150
- - writes the active theme to `document.documentElement.dataset.theme`
151
-
152
- API:
153
-
154
- ```tsx
155
- <ThemeProvider defaultMode="system">...</ThemeProvider>
156
- ```
157
-
158
- Context values:
159
-
160
- ```ts
161
- {
162
- mode: 'light' | 'dark' | 'system';
163
- resolvedMode: 'light' | 'dark';
164
- setMode: (mode: ThemeMode) => void;
165
- toggleMode: () => void;
166
- }
167
- ```
168
-
169
- Rules:
170
-
171
- - Full viewers must be wrapped in `ThemeProvider`
172
- - `defaultMode` should stay `system` unless you have a documented override
173
- - Theme toggle behavior should remain centralized here, not reimplemented in individual viewers
174
-
175
- ## `Layout`
176
-
177
- Purpose:
178
-
179
- - applies the top-level CSS Grid layout class
180
-
181
- Props:
182
-
183
- ```ts
184
- interface LayoutProps {
185
- children: React.ReactNode;
186
- variant?: 'full' | 'sidebar-left' | 'sidebar-right';
187
- className?: string;
188
- style?: React.CSSProperties;
189
- }
190
- ```
191
-
192
- Usage:
193
-
194
- ```tsx
195
- <Layout variant="full">
196
- <Header />
197
- <main />
198
- <Footer />
199
- </Layout>
200
- ```
201
-
202
- ## `Header`
203
-
204
- Purpose:
205
-
206
- - renders the sticky top bar
207
- - shows the viewer title and optional subtitle
208
- - exposes the shared theme toggle button
209
-
210
- Props:
211
-
212
- ```ts
213
- interface HeaderConfig {
214
- title: string;
215
- subtitle?: string;
216
- showThemeToggle?: boolean;
217
- }
218
- ```
219
-
220
- Defaults:
221
-
222
- - `title` defaults to `AI KIT`
223
- - `showThemeToggle` defaults to `true`
224
-
225
- Usage:
226
-
227
- ```tsx
228
- <Header title="My Viewer" subtitle="Context for this artifact" />
229
- ```
230
-
231
- Rule:
232
-
233
- - Pass a viewer-specific title instead of changing the shared default
234
-
235
- ## `Footer`
236
-
237
- Purpose:
238
-
239
- - renders attribution
240
- - optionally shows a timestamp
241
- - optionally shows a legend
242
-
243
- Props:
244
-
245
- ```ts
246
- interface FooterConfig {
247
- showTimestamp?: boolean;
248
- showAttribution?: boolean;
249
- legend?: Array<{ label: string; color: string; icon?: string }>;
250
- }
251
- ```
252
-
253
- Usage:
254
-
255
- ```tsx
256
- <Footer
257
- legend={[
258
- { label: 'system', color: 'var(--c4-system)' },
259
- { label: 'container', color: 'var(--c4-container)' },
260
- ]}
261
- />
262
- ```
263
-
264
- Rule:
265
-
266
- - Leave attribution enabled unless the host explicitly requires a different footer contract
267
-
268
- ## `Sidebar`
269
-
270
- Purpose:
271
-
272
- - provides a collapsible inspector panel
273
- - supports left or right placement
274
- - becomes an overlay on mobile
275
-
276
- Props:
277
-
278
- ```ts
279
- interface SidebarProps {
280
- children: React.ReactNode;
281
- position?: 'left' | 'right';
282
- collapsible?: boolean;
283
- defaultCollapsed?: boolean;
284
- width?: number;
285
- className?: string;
286
- title?: string;
287
- }
288
- ```
289
-
290
- Use this when a viewer needs metadata, filters, or a drill-down panel. Do not build a custom sidebar first.
291
-
292
- ## `Card`
293
-
294
- Purpose:
295
-
296
- - reusable surface for detail panes, metrics, or inspector content
297
- - supports a gradient orb effect and clickable states
298
-
299
- Props:
300
-
301
- ```ts
302
- {
303
- children: React.ReactNode;
304
- className?: string;
305
- gradient?: boolean;
306
- onClick?: () => void;
307
- variant?: 'default' | 'outlined' | 'elevated';
308
- }
309
- ```
310
-
311
- Rule:
312
-
313
- - Use `gradient` for featured blocks only. Do not turn every card into a visual accent.
314
-
315
- ## `Badge`
316
-
317
- Purpose:
318
-
319
- - compact status and category labels
320
-
321
- Props:
322
-
323
- ```ts
324
- {
325
- children: React.ReactNode;
326
- className?: string;
327
- variant?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info';
328
- size?: 'sm' | 'md';
329
- }
330
- ```
331
-
332
- Rule:
333
-
334
- - Prefer semantic variants over custom inline styling
335
-
336
- ## `types.ts`
337
-
338
- Purpose:
339
-
340
- - shared viewer contract for theme, layout, header, footer, C4 data, tour data, and the top-level `ViewerConfig`
341
-
342
- Use existing types first. Extend them only when a new viewer needs a real shared contract.
343
-
344
- ## Theme System
345
-
346
- Theme state is split across React state and CSS custom properties.
347
-
348
- Mechanics:
349
-
350
- - `ThemeProvider` tracks `mode: 'light' | 'dark' | 'system'`
351
- - `resolvedMode` is always `'light'` or `'dark'`
352
- - the provider writes `data-theme="light"` or `data-theme="dark"` on `<html>`
353
- - `theme.css` defines the token sets for `:root` and `[data-theme="dark"]`
354
- - if no explicit mode is active, `@media (prefers-color-scheme: dark)` provides the system fallback
355
-
356
- ### Key Theme Tokens
357
-
358
- Core surface tokens:
359
-
360
- - `--background`
361
- - `--foreground`
362
- - `--card`
363
- - `--card-foreground`
364
- - `--primary`
365
- - `--primary-foreground`
366
- - `--muted`
367
- - `--muted-foreground`
368
- - `--accent`
369
- - `--accent-foreground`
370
- - `--border`
371
-
372
- Supplementary tokens used by shared components:
373
-
374
- - `--sidebar`
375
- - `--sidebar-foreground`
376
- - `--color-success`
377
- - `--color-warning`
378
- - `--color-error`
379
- - `--color-info`
380
- - spacing tokens such as `--space-1` through `--space-24`
381
- - shape and motion tokens such as `--radius`, `--shadow-*`, and `--transition-*`
382
-
383
- C4-specific tokens:
384
-
385
- - `--c4-person`
386
- - `--c4-system`
387
- - `--c4-container`
388
- - `--c4-component`
389
- - `--c4-database`
390
- - `--c4-queue`
391
- - `--c4-external`
392
- - `--c4-boundary-border`
393
-
394
- Conventions:
395
-
396
- - Do not hardcode colors in new viewer UI code
397
- - Read from CSS variables with `var(--token)`
398
- - If you need a new semantic token, add it to `theme.css` instead of scattering literals
399
- - Treat any remaining literal-color spots in older code as legacy, not as a pattern to copy
400
-
401
- Note on palette language:
402
-
403
- - The system is designed around shared CSS custom-property tokens and a Nord-inspired dark palette
404
- - The current implementation stores concrete color values directly in `theme.css`
405
- - If you migrate tokens to `oklch()`, keep the same semantic token names so viewer code stays unchanged
406
-
407
- ## Data Format
408
-
409
- New viewers should prefer `window.__VIEWER_CONFIG__`.
410
-
411
- Top-level shape:
412
-
413
- ```ts
414
- interface ViewerConfig {
415
- type: 'c4' | 'tour' | 'custom';
416
- theme?: Partial<ThemeConfig>;
417
- layout?: Partial<LayoutConfig>;
418
- header?: Partial<HeaderConfig>;
419
- footer?: Partial<FooterConfig>;
420
- data: unknown;
421
- }
422
- ```
423
-
424
- ## Preferred Injection Pattern
425
-
426
- Use this in `src/<name>/main.tsx`:
427
-
428
- ```tsx
429
- const config = (window as any).__VIEWER_CONFIG__;
430
- const dataEl = document.getElementById('<viewer>-data');
431
- const data = config?.data || (dataEl ? JSON.parse(dataEl.textContent || '{}') : {});
432
- ```
433
-
434
- Why this order matters:
435
-
436
- - `window.__VIEWER_CONFIG__` is the current contract for AI Kit-driven rendering
437
- - inline script tags keep local preview and legacy flows working
438
- - the fallback keeps the entry HTML usable as a manual demo and as a development fixture
439
-
440
- ## Current Viewers
441
-
442
- ### C4 Viewer
443
-
444
- Source files:
445
-
446
- - `src/c4/C4Viewer.tsx`
447
- - `src/c4/main.tsx`
448
- - `c4-viewer.html`
449
- - `vite.c4.config.ts`
450
-
451
- Behavior:
452
-
453
- - reads `window.__VIEWER_CONFIG__` when `type === 'c4'`
454
- - falls back to legacy `window.__C4_DATA__` and then `#diagram-data`
455
- - normalizes legacy `elements` -> `nodes` and `relationships` -> `edges`
456
- - uses ELK layered layout for automatic node positioning
457
- - builds a legend for the footer from the node types in the payload
458
-
459
- ReactFlow conventions already used here:
460
-
461
- - `animated: true` on edges
462
- - `proOptions: { hideAttribution: true }`
463
- - includes `<Background />`, `<Controls />`, and `<MiniMap />`
464
- - wraps the canvas in `ReactFlowProvider`
465
-
466
- Data shape:
467
-
468
- ```ts
469
- interface C4ViewerConfig {
470
- title?: string;
471
- description?: string;
472
- layout?: {
473
- direction?: string;
474
- spacing?: number;
475
- layerSpacing?: number;
476
- };
477
- nodes: Array<{
478
- id: string;
479
- type: 'person' | 'system' | 'container' | 'component' | 'database' | 'queue' | 'external' | 'boundary';
480
- label: string;
481
- technology?: string;
482
- description?: string;
483
- }>;
484
- edges: Array<{
485
- source: string;
486
- target: string;
487
- label?: string;
488
- style?: 'sync' | 'async' | 'dashed' | 'dotted' | 'solid';
489
- }>;
490
- }
491
- ```
492
-
493
- Example payload:
494
-
495
- ```html
496
- <script type="application/json" id="diagram-data">
497
- {
498
- "title": "Sample System",
499
- "description": "Replace this JSON with your C4 diagram data",
500
- "layout": { "direction": "DOWN", "spacing": 80, "layerSpacing": 120 },
501
- "nodes": [
502
- { "id": "user", "type": "person", "label": "User", "description": "End user" },
503
- { "id": "web", "type": "system", "label": "Web App", "technology": "React", "description": "Frontend" }
504
- ],
505
- "edges": [
506
- { "source": "user", "target": "web", "label": "Uses" }
507
- ]
508
- }
509
- </script>
510
- ```
511
-
512
- ### Tour Viewer
513
-
514
- Source files:
515
-
516
- - `src/tour/TourViewer.tsx`
517
- - `src/tour/main.tsx`
518
- - `tour-viewer.html`
519
- - `vite.tour.config.ts`
520
-
521
- Behavior:
522
-
523
- - reads `window.__VIEWER_CONFIG__` first
524
- - falls back to `#tour-data`
525
- - computes a BFS layout from `steps` and `dependencies`
526
- - renders a graph above and a detail panel below
527
- - supports active-step selection and previous/next navigation
528
-
529
- ReactFlow conventions already used here:
530
-
531
- - `animated: true` on edges
532
- - `proOptions: { hideAttribution: true }`
533
- - includes `<Background />`, `<Controls />`, and `<MiniMap />`
534
- - wraps the canvas in `ReactFlowProvider`
535
-
536
- Current data shape:
537
-
538
- ```ts
539
- {
540
- title?: string;
541
- description?: string;
542
- estimatedTime?: string;
543
- steps: Array<{
544
- stepNumber: number;
545
- id: string;
546
- title: string;
547
- file: string;
548
- explanation: string;
549
- learnsConcept: string;
550
- duration: string;
551
- }>;
552
- dependencies: Array<{
553
- source: string;
554
- target: string;
555
- }>;
556
- }
557
- ```
558
-
559
- ## How to Create a New Viewer
560
-
561
- Use this process for every new full viewer.
562
-
563
- ### 1. Create the source directory
564
-
565
- ```text
566
- src/<name>/
567
- <Name>Viewer.tsx
568
- main.tsx
569
- ```
570
-
571
- ### 2. Create `<Name>Viewer.tsx`
572
-
573
- Use the shared shell.
574
-
575
- ```tsx
576
- import { ReactFlowProvider, ReactFlow, Background, Controls, MiniMap } from '@xyflow/react';
577
- import '@xyflow/react/dist/style.css';
578
-
579
- import { ThemeProvider } from '../shared/ThemeProvider';
580
- import { Layout } from '../shared/Layout';
581
- import { Header } from '../shared/Header';
582
- import { Footer } from '../shared/Footer';
583
- import '../shared/theme.css';
584
-
585
- interface MyViewerData {
586
- title?: string;
587
- description?: string;
588
- nodes: Array<{ id: string; label: string }>;
589
- edges: Array<{ source: string; target: string }>;
590
- }
591
-
592
- function MyViewerInner({ data }: { data: MyViewerData }) {
593
- return (
594
- <Layout variant="full">
595
- <Header title={data.title || 'My Viewer'} subtitle={data.description} showThemeToggle={true} />
596
- <div style={{ minHeight: 0, height: '100%', overflow: 'hidden' }}>
597
- <ReactFlow
598
- nodes={[]}
599
- edges={[]}
600
- proOptions={{ hideAttribution: true }}
601
- >
602
- <Background color="var(--muted)" gap={24} size={1.5} />
603
- <Controls />
604
- <MiniMap />
605
- </ReactFlow>
606
- </div>
607
- <Footer />
608
- </Layout>
609
- );
610
- }
611
-
612
- export function MyViewer({ data }: { data: MyViewerData }) {
613
- return (
614
- <ThemeProvider>
615
- <ReactFlowProvider>
616
- <MyViewerInner data={data} />
617
- </ReactFlowProvider>
618
- </ThemeProvider>
619
- );
620
- }
621
- ```
622
-
623
- ### 3. Create `main.tsx`
624
-
625
- Use the standard data loader.
626
-
627
- ```tsx
628
- import { createRoot } from 'react-dom/client';
629
- import { MyViewer } from './MyViewer';
630
-
631
- const config = (window as any).__VIEWER_CONFIG__;
632
- const dataEl = document.getElementById('my-viewer-data');
633
- const data = config?.data || (dataEl ? JSON.parse(dataEl.textContent || '{}') : {});
634
-
635
- createRoot(document.getElementById('root')!).render(<MyViewer data={data} />);
636
- ```
637
-
638
- ### 4. Create the root entry HTML template
639
-
640
- Name it `<name>-viewer.html` and keep it minimal.
641
-
642
- ```html
643
- <!DOCTYPE html>
644
- <html lang="en">
645
- <head>
646
- <meta charset="UTF-8" />
647
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
648
- <title>AI KIT - My Viewer</title>
649
- </head>
650
- <body>
651
- <script type="application/json" id="my-viewer-data">
652
- {
653
- "title": "Sample Viewer",
654
- "description": "Replace this JSON with sample viewer data"
655
- }
656
- </script>
657
- <div id="root"></div>
658
- <script type="module" src="./src/my-viewer/main.tsx"></script>
659
- </body>
660
- </html>
661
- ```
662
-
663
- ### 5. Create `vite.<name>.config.ts`
664
-
665
- Copy an existing Vite config and change the input file.
666
-
667
- ```ts
668
- import react from '@vitejs/plugin-react';
669
- import { defineConfig } from 'vite';
670
- import { viteSingleFile } from 'vite-plugin-singlefile';
671
-
672
- export default defineConfig({
673
- plugins: [react(), viteSingleFile()],
674
- root: '.',
675
- build: {
676
- outDir: 'dist',
677
- emptyOutDir: false,
678
- rollupOptions: {
679
- input: 'my-viewer.html',
680
- },
681
- },
682
- });
683
- ```
684
-
685
- ### 6. Update `package.json`
686
-
687
- Add a dev script and include the new Vite config in the build flow.
688
-
689
- Example:
690
-
691
- ```json
692
- {
693
- "scripts": {
694
- "dev:my-viewer": "vite --config vite.my-viewer.config.ts",
695
- "build": "vite build --config vite.c4.config.ts && vite build --config vite.tour.config.ts && vite build --config vite.my-viewer.config.ts"
696
- }
697
- }
698
- ```
699
-
700
- ### 7. Build locally
701
-
702
- ```bash
703
- pnpm build
704
- ```
705
-
706
- Confirm that `dist/my-viewer.html` exists and opens correctly.
707
-
708
- ### 8. Embed if the viewer belongs in a skill asset
709
-
710
- Update `embed.mjs` if the new viewer should be injected into a skill file.
711
-
712
- Current embed targets:
713
-
714
- - `dist/c4-viewer.html` -> `../../definitions/skills/c4-architecture.mjs`
715
- - `dist/tour-viewer.html` -> `../../definitions/skills/docs.mjs`
716
-
717
- Pattern:
718
-
719
- ```js
720
- await updateSkillAsset('my-skill.mjs', 'my-viewer.html', myViewerHtml);
721
- ```
722
-
723
- Then run:
724
-
725
- ```bash
726
- pnpm embed
727
- ```
728
-
729
- ## Build and Deploy Flow
730
-
731
- The end-to-end path is:
732
-
733
- ```text
734
- src/<viewer>/*
735
- -> root <name>-viewer.html entry template
736
- -> vite.<name>.config.ts
737
- -> dist/<name>-viewer.html
738
- -> embed.mjs
739
- -> scaffold/definitions/skills/*.mjs asset arrays
740
- ```
741
-
742
- `embed.mjs` behavior matters:
743
-
744
- - it reads the built HTML from `dist/`
745
- - it escapes backslashes, backticks, and `${` for safe template-literal embedding
746
- - it replaces an existing asset entry if the same file marker is already present
747
- - otherwise it appends a new asset object before the closing `];`
748
-
749
- That replacement strategy is designed to recover cleanly from previous partial embeds.
750
-
751
- ## Conventions for LLM Agents
752
-
753
- Follow these rules when modifying or creating viewers.
754
-
755
- 1. Full viewers must use `ThemeProvider`, `Layout`, `Header`, and `Footer`.
756
- 2. Use shared components before creating new shell primitives.
757
- 3. Do not hardcode colors in new code. Use `var(--token)` from `theme.css`.
758
- 4. Theme state belongs on `<html data-theme="light|dark">` and is resolved by `ThemeProvider`.
759
- 5. Keep `ThemeMode` as `'light' | 'dark' | 'system'` unless a cross-viewer contract change is intentional.
760
- 6. `Header` defaults to `AI KIT`. Pass the viewer-specific title through props.
761
- 7. Root `*-viewer.html` files are entry templates, not build artifacts.
762
- 8. Each viewer gets its own `vite.<name>.config.ts` file.
763
- 9. ReactFlow viewers must include `<Background />`, `<Controls />`, and `<MiniMap />`.
764
- 10. ReactFlow edges should be animated unless the viewer has a documented reason not to animate them.
765
- 11. Set `proOptions: { hideAttribution: true }` on ReactFlow instances.
766
- 12. Keep the package standalone. Use `pnpm install --ignore-workspace` in this folder.
767
- 13. Prefer `window.__VIEWER_CONFIG__` and retain an inline JSON fallback for previewability.
768
- 14. Reuse shared types from `src/shared/types.ts` when possible.
769
- 15. If you add a shared token or component, document it here in the same change.
770
- 16. If you add a viewer intended for a skill, wire it into `embed.mjs` and verify the target skill file.
771
-
772
- ## Common Mistakes
773
-
774
- - Editing `dist/*.html` directly instead of source files
775
- - Treating root `*-viewer.html` as final output
776
- - Running `pnpm install` without `--ignore-workspace`
777
- - Recreating theme logic inside a viewer instead of using `ThemeProvider`
778
- - Adding bespoke layout wrappers instead of using `Layout`
779
- - Copying literal colors from older code instead of using theme tokens
780
- - Forgetting to add a new Vite config to the build script
781
- - Forgetting to update `embed.mjs` for a new skill-bound viewer
782
-
783
- ## Minimal Checklist for a New Viewer
784
-
785
- - Source lives under `src/<name>/`
786
- - Root entry HTML exists as `<name>-viewer.html`
787
- - Vite config exists as `vite.<name>.config.ts`
788
- - Viewer uses `ThemeProvider`, `Layout`, `Header`, and `Footer`
789
- - ReactFlow config includes `Background`, `Controls`, `MiniMap`, animated edges, and hidden attribution
790
- - Data loader prefers `window.__VIEWER_CONFIG__`
791
- - `package.json` scripts are updated
792
- - `pnpm build` succeeds
793
- - `pnpm embed` updated and run if needed
794
- - This README updated if the shared contract changed
795
-
796
- ## Source of Truth
797
-
798
- When this README and the code disagree, trust the code in this folder. Then update the README in the same change.