@powerhousedao/codegen 6.0.2-staging.9 → 6.1.0-staging.0
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/dist/{file-builders-hVuFDKL9.mjs → file-builders-BGuRLZmo.mjs} +72 -329
- package/dist/file-builders-BGuRLZmo.mjs.map +1 -0
- package/dist/index.mjs +142 -80
- package/dist/index.mjs.map +1 -1
- package/dist/src/file-builders/index.mjs +1 -1
- package/dist/src/name-builders/index.mjs +1 -1
- package/dist/src/templates/index.mjs +1 -1
- package/dist/src/utils/index.mjs +1 -1
- package/package.json +3 -3
- package/dist/file-builders-hVuFDKL9.mjs.map +0 -1
|
@@ -2209,52 +2209,18 @@ After doing changes to the code, or after creating a new document model or a new
|
|
|
2209
2209
|
|
|
2210
2210
|
#### Strategy: reaching 100% reducer coverage
|
|
2211
2211
|
|
|
2212
|
-
95% is the enforced floor
|
|
2212
|
+
95% is the enforced floor — push toward 100% when feasible. Statements and lines reach high coverage quickly; the real challenge is **branch coverage** (every \`||\`, \`??\`, \`if\`, \`&&\` is two branches).
|
|
2213
2213
|
|
|
2214
|
-
|
|
2214
|
+
Write a small number of **scenario tests** first — each chaining many operations the way a real consumer would. One "full conversation flow" test that exercises 14 ops is more valuable than 14 isolated unit tests. Then categorize each remaining uncovered branch before writing a test for it:
|
|
2215
2215
|
|
|
2216
|
-
|
|
2216
|
+
1. **Wrong nullability** — type allows \`null\` but the value is always initialized. Fix the type (e.g. \`Int!\` in the schema via \`SET_STATE_SCHEMA\` / \`SET_OPERATION_SCHEMA\`); the fallback branch disappears.
|
|
2217
|
+
2. **Missing validation** — the field is genuinely required for a variant but the reducer silently accepts its absence. Add a named error via \`ADD_OPERATION_ERROR\` and reject; the new branch is reachable and worth testing.
|
|
2218
|
+
3. **Wrong coercion operator** — \`||\` used where \`??\` is needed; falsy-but-valid values (\`0\`, \`false\`, \`""\`) get coerced to the fallback. Fix the operator and add a test with a falsy-but-valid value.
|
|
2219
|
+
4. **Legitimate optionality** — both sides reachable through valid inputs. Fold both into existing scenario tests by varying inputs.
|
|
2217
2220
|
|
|
2218
|
-
|
|
2221
|
+
Then extend scenario tests to hit remaining branches: skip initialization to hit "not yet initialized" branches; chain invalid operations using the operation-index pattern from "Testing Reducer Errors" (never \`.toThrow()\`); use minimal/empty inputs for fallback-to-null branches; vary inputs so both sides of legitimate \`||\` / \`??\` are hit.
|
|
2219
2222
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
Prefer fewer tests that each cover wide ranges of behavior over many isolated unit tests per branch. A single "full conversation flow" test that exercises 14 operations in sequence is more valuable and more maintainable than 14 separate tests.
|
|
2223
|
-
|
|
2224
|
-
##### Phase 3: Categorize every uncovered branch
|
|
2225
|
-
|
|
2226
|
-
Don't write tests to hit uncovered branches yet. First, examine each one and classify it:
|
|
2227
|
-
|
|
2228
|
-
1. **Wrong nullability in the schema** — The type allows \`null\` but the value is always initialized and never null at runtime. The defensive fallback (\`?? 0\`, \`?? defaultValue\`) creates an unreachable branch. No test can meaningfully cover it because the condition cannot occur through any valid operation sequence.
|
|
2229
|
-
2. **Missing validation** — The type is nullable because the schema uses a flattened structure (e.g. a tagged union where fields are optional per variant). Some fields are _required for specific variants_ but the reducer silently accepts their absence. The fallback branch is reachable but only with invalid input that should be rejected.
|
|
2230
|
-
3. **Wrong coercion operator** — \`||\` is used where \`??\` is needed. Values like \`0\`, \`false\`, and \`""\` are valid but \`||\` coerces them to the fallback. This is a bug, not a coverage gap.
|
|
2231
|
-
4. **Legitimate optionality** — The field is genuinely optional. Both branches (value provided / not provided) are reachable through valid inputs. These are the only branches worth covering with tests.
|
|
2232
|
-
|
|
2233
|
-
##### Phase 4: Fix the implementation, don't test around it
|
|
2234
|
-
|
|
2235
|
-
For each category:
|
|
2236
|
-
|
|
2237
|
-
- **Wrong nullability** → Tighten the type definition. Make the field non-nullable at the source (e.g. \`Int!\` instead of \`Int\` in the GraphQL schema). This eliminates the fallback code entirely, removing the untestable branch. Update the source schema via MCP (\`SET_STATE_SCHEMA\` / \`SET_OPERATION_SCHEMA\`) and regenerate — see "Document Model Modification Process" below.
|
|
2238
|
-
- **Missing validation** → Add validation that throws a specific named error for invalid input (define it via \`ADD_OPERATION_ERROR\` — see "Error Handling in Operations" below). This converts a silent fallback into an explicit rejection. The validation branch is now both reachable and worth testing.
|
|
2239
|
-
- **Wrong operator** → Fix \`||\` to \`??\` (or vice versa). Add a test that passes a falsy-but-valid value (\`0\`, \`false\`, \`""\`) and asserts it is preserved.
|
|
2240
|
-
- **Legitimate optionality** → Add test cases that exercise both sides. Often these can be folded into existing scenario tests by varying inputs (e.g. one call provides the field, another omits it).
|
|
2241
|
-
|
|
2242
|
-
##### Phase 5: Extend scenario tests to cover remaining branches
|
|
2243
|
-
|
|
2244
|
-
With the implementation corrected, extend the existing scenario tests to hit newly-testable branches:
|
|
2245
|
-
|
|
2246
|
-
- Add a test that skips initialization to cover "not yet initialized" false branches.
|
|
2247
|
-
- Add error path tests that chain multiple invalid operations in sequence, asserting each error and verifying state is unchanged (use the operation-index pattern from "Testing Reducer Errors" below — never \`.toThrow()\`).
|
|
2248
|
-
- Add a test that uses minimal/empty inputs to cover fallback-to-null branches on optional fields.
|
|
2249
|
-
- Vary inputs across tests so both sides of legitimate \`||\` / \`??\` operators are hit (e.g. one test provides \`stepIndex: 0\`, another omits it).
|
|
2250
|
-
|
|
2251
|
-
##### Phase 6: Verify
|
|
2252
|
-
|
|
2253
|
-
Run \`npm run test:coverage\`. Reducers should be at or near 100% across all four metrics. If any branches remain uncovered, repeat the categorization in Phase 3: is it a type problem, a validation gap, an operator bug, or a legitimate test gap? Do not stop at 95% if a small number of uncovered branches remain — they are usually the cheapest signals of an underlying implementation issue.
|
|
2254
|
-
|
|
2255
|
-
##### The principle
|
|
2256
|
-
|
|
2257
|
-
**Don't test around bad types — fix the types.** When a branch is untestable, the problem is almost never a missing test. It is a type that is too loose, a validation that is missing, or an operator that is wrong. Fix the implementation so that every branch is either reachable and meaningful, or eliminated entirely. Coverage follows naturally from correct types, proper validation, and realistic test scenarios.
|
|
2223
|
+
**Principle: don't test around bad types — fix them.** When a branch is untestable, it's almost always a type that's too loose, missing validation, or a wrong operator. Coverage follows naturally from correct types and realistic scenarios.
|
|
2258
2224
|
|
|
2259
2225
|
## Document editor creation flow
|
|
2260
2226
|
|
|
@@ -2394,90 +2360,26 @@ function str(v: unknown): string {
|
|
|
2394
2360
|
|
|
2395
2361
|
### Drag-and-drop file uploads (optional pattern)
|
|
2396
2362
|
|
|
2397
|
-
Use this
|
|
2398
|
-
|
|
2399
|
-
#### Why the default does not work
|
|
2400
|
-
|
|
2401
|
-
Connect wraps every editor inside a \`DropZoneWrapper\` (from \`@powerhousedao/design-system/connect\`). That outer wrapper:
|
|
2402
|
-
|
|
2403
|
-
1. Only accepts \`.phd\`, \`.phdm\`, and \`.zip\` files (Powerhouse document files).
|
|
2404
|
-
2. Sets \`dataTransfer.dropEffect = "none"\` (the blocked / no-entry cursor) for any other file type.
|
|
2405
|
-
3. Calls \`event.stopPropagation()\` on \`dragover\`, \`dragenter\`, and \`dragleave\` so events do not bubble past it.
|
|
2406
|
-
4. Shows a full-screen overlay when a valid Powerhouse document is dragged in.
|
|
2363
|
+
Use this **only** when your editor needs to accept arbitrary file drops (images, PDFs, CSVs, attachments). Default editors do not need any of this.
|
|
2407
2364
|
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
#### Implementation recipe
|
|
2411
|
-
|
|
2412
|
-
Attach all four drag handlers (\`onDragOver\`, \`onDragEnter\`, \`onDragLeave\`, \`onDrop\`) to your editor's **root \`div\`**. Always gate every handler on \`event.dataTransfer.types.includes("Files")\` so internal Connect drags (e.g. sidebar nodes carrying \`UI_NODE\`) bubble through to DropZone untouched — only intercept file drags.
|
|
2365
|
+
Connect wraps every editor in an outer DropZone that only handles Powerhouse document files (\`.phd\`, \`.phdm\`, \`.zip\`). To accept other files in your editor, use the \`useEditorFileDrop\` hook — it spreads the right handlers and a marker attribute that tells the outer DropZone to leave your subtree alone.
|
|
2413
2366
|
|
|
2414
2367
|
~~~tsx
|
|
2415
|
-
import {
|
|
2416
|
-
|
|
2417
|
-
const ALLOWED_EXTENSIONS = [".png", ".jpg", ".jpeg", ".pdf"]; // adjust per editor
|
|
2418
|
-
|
|
2419
|
-
function filterAcceptedFiles(fileList: FileList): File[] {
|
|
2420
|
-
return Array.from(fileList).filter((file) => {
|
|
2421
|
-
const lower = file.name.toLowerCase();
|
|
2422
|
-
return ALLOWED_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
2423
|
-
});
|
|
2424
|
-
}
|
|
2368
|
+
import { useEditorFileDrop } from "@powerhousedao/reactor-browser";
|
|
2425
2369
|
|
|
2426
2370
|
export default function Editor() {
|
|
2427
|
-
const
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
if (e.dataTransfer.types.includes("Files")) {
|
|
2432
|
-
e.preventDefault(); // signals "drop is allowed here"
|
|
2433
|
-
e.stopPropagation(); // blocks DropZone from setting dropEffect="none"
|
|
2434
|
-
}
|
|
2435
|
-
}, []);
|
|
2436
|
-
|
|
2437
|
-
const onEditorDragEnter = useCallback((e: DragEvent) => {
|
|
2438
|
-
if (e.dataTransfer.types.includes("Files")) {
|
|
2439
|
-
e.stopPropagation();
|
|
2440
|
-
dragDepthRef.current += 1;
|
|
2441
|
-
if (dragDepthRef.current === 1) setIsDragOver(true);
|
|
2442
|
-
}
|
|
2443
|
-
}, []);
|
|
2444
|
-
|
|
2445
|
-
const onEditorDragLeave = useCallback((e: DragEvent) => {
|
|
2446
|
-
if (e.dataTransfer.types.includes("Files")) {
|
|
2447
|
-
e.stopPropagation();
|
|
2448
|
-
dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
|
|
2449
|
-
if (dragDepthRef.current === 0) setIsDragOver(false);
|
|
2450
|
-
}
|
|
2451
|
-
}, []);
|
|
2452
|
-
|
|
2453
|
-
const onEditorDrop = useCallback((e: DragEvent) => {
|
|
2454
|
-
if (e.dataTransfer.types.includes("Files")) {
|
|
2455
|
-
e.preventDefault(); // prevents the browser from opening the file
|
|
2456
|
-
e.stopPropagation();
|
|
2457
|
-
dragDepthRef.current = 0;
|
|
2458
|
-
setIsDragOver(false);
|
|
2459
|
-
|
|
2460
|
-
const accepted = filterAcceptedFiles(e.dataTransfer.files);
|
|
2461
|
-
if (accepted.length === 0) return; // silently ignore rejected files
|
|
2462
|
-
handleFiles(accepted);
|
|
2463
|
-
}
|
|
2464
|
-
}, []);
|
|
2371
|
+
const { dragProps, isDragOver } = useEditorFileDrop({
|
|
2372
|
+
accept: [".png", ".jpg", ".jpeg", ".pdf"],
|
|
2373
|
+
onFiles: (files) => handleFiles(files),
|
|
2374
|
+
});
|
|
2465
2375
|
|
|
2466
2376
|
return (
|
|
2467
|
-
<div
|
|
2468
|
-
onDragOver={onEditorDragOver}
|
|
2469
|
-
onDragEnter={onEditorDragEnter}
|
|
2470
|
-
onDragLeave={onEditorDragLeave}
|
|
2471
|
-
onDrop={onEditorDrop}
|
|
2472
|
-
className="relative"
|
|
2473
|
-
>
|
|
2377
|
+
<div {...dragProps} className="relative">
|
|
2474
2378
|
{isDragOver && (
|
|
2475
2379
|
<div className="pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/40">
|
|
2476
|
-
<
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
</p>
|
|
2480
|
-
</div>
|
|
2380
|
+
<p className="text-foreground text-base font-medium">
|
|
2381
|
+
Drop files to attach
|
|
2382
|
+
</p>
|
|
2481
2383
|
</div>
|
|
2482
2384
|
)}
|
|
2483
2385
|
{/* ... editor content ... */}
|
|
@@ -2486,192 +2388,66 @@ export default function Editor() {
|
|
|
2486
2388
|
}
|
|
2487
2389
|
~~~
|
|
2488
2390
|
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
\`dragenter\` and \`dragleave\` fire every time the cursor crosses **any** child boundary inside the editor. Without a depth counter the overlay flickers on/off as the cursor moves over nested elements. The required pattern:
|
|
2492
|
-
|
|
2493
|
-
- \`dragenter\`: increment depth; show the overlay when depth goes \`0\` → \`1\`.
|
|
2494
|
-
- \`dragleave\`: decrement depth; hide the overlay when depth returns to \`0\`.
|
|
2495
|
-
- \`drop\`: reset depth to \`0\` and hide the overlay unconditionally.
|
|
2496
|
-
|
|
2497
|
-
#### Drop overlay rules
|
|
2498
|
-
|
|
2499
|
-
The overlay \`div\` **MUST** use \`pointer-events-none\`. Without it, the overlay element captures the \`drop\` event and your handler on the root \`div\` will never fire. Use theme tokens (\`bg-background\`, \`text-foreground\`, \`text-primary\`, etc.) so the overlay renders correctly in both light and dark mode.
|
|
2500
|
-
|
|
2501
|
-
#### File-type validation
|
|
2502
|
-
|
|
2503
|
-
Always validate dropped files at the editor level — do not assume the user dropped what you expect. The \`filterAcceptedFiles\` helper above filters by file-name extension (case-insensitive). For stricter validation, additionally check \`file.type\` (MIME type) before processing. Files that fail validation should be **silently ignored** or surfaced via a toast/notification — never throw on unexpected input, since drag-and-drop is a user-initiated action and exceptions will surface as uncaught render errors.
|
|
2504
|
-
|
|
2505
|
-
#### Bridging file handlers in child component contexts
|
|
2391
|
+
The overlay \`div\` **MUST** use \`pointer-events-none\` — otherwise it captures the \`drop\` event and your handler never fires.
|
|
2506
2392
|
|
|
2507
|
-
If your file-upload handler lives inside a **child** component's React context (
|
|
2508
|
-
|
|
2509
|
-
~~~tsx
|
|
2510
|
-
// editor.tsx — expose a ref the child fills in:
|
|
2511
|
-
const addFilesRef = useRef<((files: File[]) => void) | null>(null);
|
|
2512
|
-
|
|
2513
|
-
// inside onEditorDrop, after filtering:
|
|
2514
|
-
addFilesRef.current?.(accepted);
|
|
2515
|
-
|
|
2516
|
-
// pass the ref down to the child:
|
|
2517
|
-
<ChatInputBar addFilesRef={addFilesRef} />;
|
|
2518
|
-
~~~
|
|
2519
|
-
|
|
2520
|
-
~~~tsx
|
|
2521
|
-
// ChatInputBar.tsx — bridge component mounted inside the consumer's context:
|
|
2522
|
-
import type { MutableRefObject } from "react";
|
|
2523
|
-
import { useEffect } from "react";
|
|
2524
|
-
|
|
2525
|
-
function DropBridge({
|
|
2526
|
-
addFilesRef,
|
|
2527
|
-
}: {
|
|
2528
|
-
addFilesRef?: MutableRefObject<((files: File[]) => void) | null>;
|
|
2529
|
-
}) {
|
|
2530
|
-
const { add } = usePromptInputAttachments();
|
|
2531
|
-
useEffect(() => {
|
|
2532
|
-
if (addFilesRef) addFilesRef.current = add;
|
|
2533
|
-
return () => {
|
|
2534
|
-
if (addFilesRef) addFilesRef.current = null;
|
|
2535
|
-
};
|
|
2536
|
-
}, [add, addFilesRef]);
|
|
2537
|
-
return null;
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
<PromptInput onSubmit={handleSubmit} multiple>
|
|
2541
|
-
<DropBridge addFilesRef={addFilesRef} />
|
|
2542
|
-
{/* ... */}
|
|
2543
|
-
</PromptInput>;
|
|
2544
|
-
~~~
|
|
2545
|
-
|
|
2546
|
-
#### Common pitfalls — DO NOT make these mistakes
|
|
2547
|
-
|
|
2548
|
-
1. **Do NOT use \`globalDrop\` together with \`stopPropagation\`.** \`PromptInput\`'s \`globalDrop\` prop attaches drop handlers on \`document\`. \`stopPropagation()\` at the editor root prevents events from ever reaching \`document\`. The two are incompatible — use the ref-bridge pattern instead.
|
|
2549
|
-
2. **Always \`preventDefault()\` on BOTH \`dragover\` AND \`drop\`.** Without \`preventDefault\` on \`dragover\`, the browser signals "drop not allowed" and the \`drop\` event will not fire at all. Without \`preventDefault\` on \`drop\`, the browser navigates away to open the dropped file.
|
|
2550
|
-
3. **Always gate handlers on \`e.dataTransfer.types.includes("Files")\`.** Connect uses non-file drag types internally (e.g. \`UI_NODE\` for sidebar items). Those drags must bubble through to DropZone untouched — only intercept file drags.
|
|
2551
|
-
4. **\`dragenter\` and \`dragleave\` do NOT need \`preventDefault\`.** \`stopPropagation()\` alone is enough on those two; only \`dragover\` and \`drop\` require \`preventDefault\`.
|
|
2552
|
-
5. **The overlay \`div\` MUST have \`pointer-events-none\`.** Otherwise the overlay swallows the \`drop\` event and your drop handler never fires.
|
|
2553
|
-
6. **Reset \`dragDepthRef.current = 0\` inside \`onDrop\`.** Otherwise a subsequent drag will start with stale depth and the overlay logic breaks.
|
|
2393
|
+
If your file-upload handler lives inside a **child** component's React context (e.g. \`usePromptInputAttachments().add\` inside a \`PromptInput\`), use a ref bridge: store the child's add function in a ref from a tiny bridge component, and call \`ref.current?.(files)\` from \`onFiles\`.
|
|
2554
2394
|
|
|
2555
2395
|
### Using shadcn / Vercel AI Elements in editors (optional)
|
|
2556
2396
|
|
|
2557
|
-
Use this
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
~~~
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
"lib": "@/editors/<name>/lib",
|
|
2590
|
-
"hooks": "@/editors/<name>/hooks"
|
|
2591
|
-
},
|
|
2592
|
-
"iconLibrary": "lucide"
|
|
2593
|
-
}
|
|
2594
|
-
~~~
|
|
2595
|
-
|
|
2596
|
-
The \`@/*\` alias inside \`components.json\` is consumed by the shadcn / AI Elements CLIs at install time only — they generate \`@/...\` imports inside the components they produce. You will rewrite those imports to relative paths in Step 7. **Do NOT** add a corresponding \`@/*\` alias to \`tsconfig.json\` — see the "Editor code conventions" section above.
|
|
2597
|
-
|
|
2598
|
-
#### Step 3 — create \`lib/utils.ts\` under your editor
|
|
2599
|
-
|
|
2600
|
-
~~~typescript
|
|
2601
|
-
// editors/<name>/lib/utils.ts
|
|
2602
|
-
import { clsx, type ClassValue } from "clsx";
|
|
2603
|
-
import { twMerge } from "tailwind-merge";
|
|
2604
|
-
|
|
2605
|
-
export function cn(...inputs: ClassValue[]) {
|
|
2606
|
-
return twMerge(clsx(inputs));
|
|
2607
|
-
}
|
|
2608
|
-
~~~
|
|
2609
|
-
|
|
2610
|
-
#### Step 4 — extend \`style.css\` with the shadcn theme variables
|
|
2611
|
-
|
|
2612
|
-
The boilerplate \`style.css\` already imports \`tailwindcss\`, \`@powerhousedao/design-system/theme.css\`, and \`@powerhousedao/connect/style.css\`. **Append** (do not replace) the shadcn additions:
|
|
2613
|
-
|
|
2614
|
-
- \`@import "tw-animate-css";\` (after the existing \`@import "tailwindcss";\`)
|
|
2615
|
-
- \`@custom-variant dark (&:is(.dark *));\`
|
|
2616
|
-
- A \`@theme inline { ... }\` block mapping \`--color-*\` and \`--radius-*\` to the shadcn variables
|
|
2617
|
-
- \`:root { ... }\` and \`.dark { ... }\` blocks with \`oklch(...)\` color values
|
|
2618
|
-
- \`@layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } }\`
|
|
2619
|
-
|
|
2620
|
-
⚠️ **Theme conflict warning**: \`@powerhousedao/design-system/theme.css\` already declares its own theme tokens. Adding shadcn's color variables on top has not been verified for conflicts — after this step, render a Connect view and confirm both the editor and the rest of Connect still look correct in light and dark mode. If the design-system theme breaks, fall back to using \`@powerhousedao/design-system\` and \`@powerhousedao/document-engineering\` primitives instead of shadcn.
|
|
2621
|
-
|
|
2622
|
-
#### Step 5 — install AI Elements (Vercel's CLI, NOT shadcn's)
|
|
2623
|
-
|
|
2624
|
-
Vercel ships its own CLI for AI chat components — use it, **not** \`npx shadcn add\`:
|
|
2625
|
-
|
|
2626
|
-
~~~bash
|
|
2627
|
-
npx ai-elements@latest add conversation message reasoning tool prompt-input code-block
|
|
2628
|
-
~~~
|
|
2629
|
-
|
|
2630
|
-
**Verify each component name exists in the AI Elements registry before adding** — names that aren't in the registry will hard-error and abort the entire install. The registry list is at https://ai-sdk.dev/elements .
|
|
2631
|
-
|
|
2632
|
-
The CLI auto-installs supporting deps: \`ai\` (the Vercel AI SDK, used for types like \`UIMessage\`, \`ToolUIPart\`), \`use-stick-to-bottom\` (auto-scroll for \`Conversation\`), \`streamdown\` plus its plugins (\`@streamdown/cjk\`, \`@streamdown/code\`, \`@streamdown/math\`, \`@streamdown/mermaid\`) for markdown rendering, and \`@radix-ui/react-use-controllable-state\` (for the \`Reasoning\` toggle). These are pulled into your \`package.json\` automatically.
|
|
2633
|
-
|
|
2634
|
-
#### Step 6 — relocate AI Elements files into the editor directory
|
|
2635
|
-
|
|
2636
|
-
The CLI puts AI Elements files at \`components/ai-elements/\` at the **project root**, not inside your editor. Move them:
|
|
2637
|
-
|
|
2638
|
-
~~~
|
|
2639
|
-
components/ai-elements/ → editors/<name>/components/ai-elements/
|
|
2640
|
-
~~~
|
|
2641
|
-
|
|
2642
|
-
After the move, the project-root \`components/\` directory should be empty and can be deleted. UI primitives installed under \`editors/<name>/components/ui/\` are already correctly placed.
|
|
2643
|
-
|
|
2644
|
-
#### Step 7 — rewrite all \`@/...\` imports to relative paths with \`.js\` extensions
|
|
2645
|
-
|
|
2646
|
-
The shadcn / AI Elements CLIs generate imports like:
|
|
2647
|
-
|
|
2648
|
-
~~~typescript
|
|
2649
|
-
import { Button } from "@/editors/<name>/components/ui/button";
|
|
2650
|
-
import { cn } from "@/editors/<name>/lib/utils";
|
|
2651
|
-
~~~
|
|
2397
|
+
Use this **only** when your editor needs UI primitives not covered by \`@powerhousedao/design-system\` or \`@powerhousedao/document-engineering\` — typically chat-style UIs built on Vercel AI Elements (\`Conversation\`, \`Message\`, \`Reasoning\`, \`Tool\`, \`PromptInput\`). Default editors should prefer the design-system / document-engineering primitives and skip this section.
|
|
2398
|
+
|
|
2399
|
+
\`shadcn init\` does not work here (it fails with "could not detect a supported framework"), so configure shadcn manually with the steps below. Substitute \`<name>\` with your editor's name throughout.
|
|
2400
|
+
|
|
2401
|
+
#### Setup steps
|
|
2402
|
+
|
|
2403
|
+
1. **Install deps**: \`pnpm add class-variance-authority clsx tailwind-merge lucide-react tw-animate-css\`
|
|
2404
|
+
|
|
2405
|
+
2. **Create \`components.json\`** at the project root. The \`@/*\` alias here is consumed by the shadcn / AI Elements CLIs at install time only — do **NOT** add a matching \`@/*\` alias to \`tsconfig.json\`:
|
|
2406
|
+
|
|
2407
|
+
~~~json
|
|
2408
|
+
{
|
|
2409
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
2410
|
+
"style": "new-york",
|
|
2411
|
+
"rsc": false,
|
|
2412
|
+
"tsx": true,
|
|
2413
|
+
"tailwind": {
|
|
2414
|
+
"config": "",
|
|
2415
|
+
"css": "style.css",
|
|
2416
|
+
"baseColor": "neutral",
|
|
2417
|
+
"cssVariables": true
|
|
2418
|
+
},
|
|
2419
|
+
"aliases": {
|
|
2420
|
+
"components": "@/editors/<name>/components",
|
|
2421
|
+
"utils": "@/editors/<name>/lib/utils",
|
|
2422
|
+
"ui": "@/editors/<name>/components/ui",
|
|
2423
|
+
"lib": "@/editors/<name>/lib",
|
|
2424
|
+
"hooks": "@/editors/<name>/hooks"
|
|
2425
|
+
},
|
|
2426
|
+
"iconLibrary": "lucide"
|
|
2427
|
+
}
|
|
2428
|
+
~~~
|
|
2652
2429
|
|
|
2653
|
-
|
|
2430
|
+
3. **Create \`editors/<name>/lib/utils.ts\`** exporting the standard \`cn\` helper (\`twMerge(clsx(inputs))\`).
|
|
2654
2431
|
|
|
2655
|
-
-
|
|
2656
|
-
- \`@/editors/<name>/lib/utils\` → \`../../lib/utils.js\`
|
|
2432
|
+
4. **Extend \`style.css\`** (append, do not replace): \`@import "tw-animate-css";\`, \`@custom-variant dark (&:is(.dark *));\`, a \`@theme inline { ... }\` block mapping \`--color-*\`/\`--radius-*\`, \`:root\` and \`.dark\` blocks with \`oklch(...)\` values, and a \`@layer base\` block. ⚠️ The design-system theme already declares its own tokens — verify both the editor and Connect still render correctly in light/dark mode after this step. If they break, fall back to design-system primitives.
|
|
2657
2433
|
|
|
2658
|
-
|
|
2434
|
+
5. **Install AI Elements** using Vercel's CLI (not shadcn's). Verify each name exists at https://ai-sdk.dev/elements first — unknown names abort the install:
|
|
2659
2435
|
|
|
2660
|
-
|
|
2661
|
-
-
|
|
2436
|
+
~~~bash
|
|
2437
|
+
npx ai-elements@latest add conversation message reasoning tool prompt-input code-block
|
|
2438
|
+
~~~
|
|
2662
2439
|
|
|
2663
|
-
|
|
2440
|
+
The CLI auto-installs \`ai\`, \`use-stick-to-bottom\`, \`streamdown\` (+ \`@streamdown/{cjk,code,math,mermaid}\`), and \`@radix-ui/react-use-controllable-state\`.
|
|
2664
2441
|
|
|
2665
|
-
|
|
2442
|
+
6. **Move \`components/ai-elements/\`** from the project root into \`editors/<name>/components/ai-elements/\`, then delete the empty project-root \`components/\`. (Files under \`editors/<name>/components/ui/\` are already in the right place.)
|
|
2666
2443
|
|
|
2667
|
-
|
|
2444
|
+
7. **Rewrite \`@/...\` imports to relative paths with \`.js\` extensions** across every CLI-generated file — \`@/*\` does not resolve under \`nodenext\`, and extensionless relative imports fail too. From an \`ai-elements/\` file: \`@/editors/<name>/components/ui/button\` → \`../ui/button.js\`, \`@/editors/<name>/lib/utils\` → \`../../lib/utils.js\`. From a \`ui/\` file: \`./button.js\` and \`../../lib/utils.js\`. Also add \`.js\` to any extensionless sibling imports (e.g. \`./shimmer\` → \`./shimmer.js\`).
|
|
2668
2445
|
|
|
2669
|
-
|
|
2446
|
+
#### Bridging document-model types to AI Elements
|
|
2670
2447
|
|
|
2671
|
-
|
|
2672
|
-
- For \`ToolHeader\`, use \`type="dynamic-tool"\` with explicit \`toolName\` and \`state\` props.
|
|
2448
|
+
AI Elements use Vercel AI SDK types (\`UIMessage\`, \`ToolUIPart\`, \`DynamicToolUIPart\`); your document model has its own. **Do not convert between them.** Use the plain-prop low-level primitives (\`Message\`, \`MessageContent\`, \`Conversation\`, \`ConversationContent\`, \`Reasoning\`, \`ReasoningTrigger\`, \`ReasoningContent\`, \`Tool\`, \`ToolHeader\`, \`ToolContent\`, \`ToolInput\`, \`ToolOutput\`, \`MessageResponse\`) and write thin wrapper components. For \`ToolHeader\`, pass \`type="dynamic-tool"\` with explicit \`toolName\` and \`state\`.
|
|
2673
2449
|
|
|
2674
|
-
|
|
2450
|
+
Tool-state mapping:
|
|
2675
2451
|
|
|
2676
2452
|
| Document-model state | AI Elements \`ToolPart["state"]\` |
|
|
2677
2453
|
| ---------------------------- | ------------------------------- |
|
|
@@ -2679,54 +2455,21 @@ AI Elements components use Vercel AI SDK types (\`UIMessage\`, \`ToolUIPart\`, \
|
|
|
2679
2455
|
| Tool call with result | \`"output-available"\` |
|
|
2680
2456
|
| Tool call with error result | \`"output-error"\` |
|
|
2681
2457
|
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
When passing a concrete document-model interface into a function or component typed as \`Record<string, unknown> & { id: string; type: ... }\`, TypeScript will complain:
|
|
2458
|
+
If TypeScript complains that your concrete interface is missing an index signature when passed where \`Record<string, unknown> & { ... }\` is expected, add \`[key: string]: unknown;\` to the interface.
|
|
2685
2459
|
|
|
2686
|
-
|
|
2687
|
-
Type 'MyInterface' is not assignable to type 'Record<string, unknown>'.
|
|
2688
|
-
Index signature for type 'string' is missing in type 'MyInterface'.
|
|
2689
|
-
~~~
|
|
2690
|
-
|
|
2691
|
-
**Fix**: add \`[key: string]: unknown;\` to the concrete interface so it satisfies the index signature.
|
|
2692
|
-
|
|
2693
|
-
#### Recommended file structure
|
|
2460
|
+
#### Final file structure
|
|
2694
2461
|
|
|
2695
2462
|
~~~
|
|
2696
2463
|
editors/<name>/
|
|
2697
2464
|
editor.tsx ← main editor (edit codegen output)
|
|
2698
2465
|
module.ts ← DO NOT EDIT (codegen)
|
|
2699
|
-
lib/
|
|
2700
|
-
utils.ts ← cn() helper
|
|
2466
|
+
lib/utils.ts ← cn() helper
|
|
2701
2467
|
components/
|
|
2702
|
-
ai-elements/ ← moved from project root in
|
|
2703
|
-
conversation.tsx
|
|
2704
|
-
message.tsx
|
|
2705
|
-
reasoning.tsx
|
|
2706
|
-
tool.tsx
|
|
2707
|
-
code-block.tsx
|
|
2708
|
-
prompt-input.tsx
|
|
2468
|
+
ai-elements/ ← moved from project root in step 6
|
|
2709
2469
|
ui/ ← shadcn primitives installed by ai-elements CLI
|
|
2710
|
-
|
|
2711
|
-
badge.tsx
|
|
2712
|
-
tooltip.tsx
|
|
2713
|
-
... etc
|
|
2714
|
-
<wrapper components that bridge document-model types to AI Elements primitives>
|
|
2470
|
+
<wrappers bridging document-model types to AI Elements primitives>
|
|
2715
2471
|
~~~
|
|
2716
2472
|
|
|
2717
|
-
#### Quick checklist for a shadcn-using editor
|
|
2718
|
-
|
|
2719
|
-
1. Create the editor document via MCP and confirm its status (see "Phase 1" above).
|
|
2720
|
-
2. Wait for codegen to produce the editor boilerplate.
|
|
2721
|
-
3. \`pnpm add class-variance-authority clsx tailwind-merge lucide-react tw-animate-css\`
|
|
2722
|
-
4. Create \`components.json\` and \`editors/<name>/lib/utils.ts\`.
|
|
2723
|
-
5. Extend \`style.css\` with the shadcn theme additions; verify the design-system theme still renders correctly.
|
|
2724
|
-
6. \`npx ai-elements@latest add <components>\` — verify component names against the registry first.
|
|
2725
|
-
7. Move \`components/ai-elements/\` into \`editors/<name>/components/ai-elements/\`.
|
|
2726
|
-
8. Bulk-rewrite every \`@/...\` import to a relative path with a \`.js\` extension; also add \`.js\` to extensionless relative imports.
|
|
2727
|
-
9. Use the top-level \`document-models/<name>\` barrel for all document-model imports (see "Editor code conventions" above).
|
|
2728
|
-
10. Run \`npm run tsc\` and \`npm run lint:fix\`.
|
|
2729
|
-
|
|
2730
2473
|
## ⚠️ CRITICAL: Generated Files & Modification Rules
|
|
2731
2474
|
|
|
2732
2475
|
### Generated Files Rule
|
|
@@ -7379,7 +7122,7 @@ async function tsMorphGenerateApp({ project, editorDir, editorName, editorId, al
|
|
|
7379
7122
|
await createOrUpdateManifest({ apps: [{
|
|
7380
7123
|
name: editorName,
|
|
7381
7124
|
id: editorId,
|
|
7382
|
-
documentTypes: ["
|
|
7125
|
+
documentTypes: ["powerhouse/document-drive"]
|
|
7383
7126
|
}] }, projectDir);
|
|
7384
7127
|
}
|
|
7385
7128
|
async function makeAppComponent({ project, editorDirPath }) {
|
|
@@ -8796,4 +8539,4 @@ async function makeSubgraphsIndexFile(args) {
|
|
|
8796
8539
|
//#endregion
|
|
8797
8540
|
export { buildStringLiteral as $, cursorMcpTemplate as $n, getModuleExportType as $t, writeModuleFiles as A, pnpmWorkspaceTemplate as An, analyticsFactoryTemplate as At, buildTsMorphProject as B, gitIgnoreTemplate as Bn, documentModelSrcIndexFileTemplate as Bt, writeAllGeneratedProjectFiles as C, subgraphsIndexTemplate as Cn, relationalDbIndexTemplate as Ct, writeGeneratedProcessorsFiles as D, buildPowerhouseConfigTemplate as Dn, factoryBuildersTemplate as Dt, writeGeneratedEditorsFiles as E, ManifestTemplate as En, processorsFactoryTemplate as Et, makeEditorsIndexFile as F, mainTsxTemplate as Fn, makeOperationImportNames as Ft, getObjectLiteral as G, editorsTemplate as Gn, documentModelGenTypesTemplate as Gt, getAllImportModuleSpecifiers as H, geminiSettingsTemplate as Hn, documentModelIndexTemplate as Ht, validateDocumentModelState as I, licenseTemplate as In, makeOperationsImports as It, getStringArrayPropertyElements as J, documentModelsTemplate as Jn, documentModelPhFactoriesFileTemplate as Jt, getObjectProperty as K, upgradeManifestsTemplate as Kn, documentModelSchemaIndexTemplate as Kt, updateVersionedImports as L, indexTsTemplate as Ln, makeTestCaseForOperation as Lt, tsMorphGenerateApp as M, packageJsonTemplate as Mn, upgradeTransitionTemplate as Mt, makeEditorModuleFile as N, npmrcTemplate as Nn, upgradeManifestTemplate as Nt, writeGeneratedProjectRootFiles as O, packageJsonExportsTemplate as On, analyticsProcessorTemplate as Ot, makeEditorsFile as P, mcpTemplate as Pn, documentModelOperationsModuleTestFileTemplate as Pt, buildObjectLiteral as Q, connectEntrypointTemplate as Qn, documentModelOperationModuleActionsFileTemplate as Qt, getInitialStates as R, legacyIndexHtmlTemplate as Rn, documentModelTestFileTemplate as Rt, writeAiConfigFiles as S, tsconfigPathsTemplate as Sn, relationalDbMigrationsTemplate as St, writeGeneratedDocumentModelsFiles as T, readmeTemplate as Tn, processorsIndexTemplate as Tt, getAllImportNames as U, eslintConfigTemplate as Un, documentModelHooksFileTemplate as Ut, getDefaultProjectOptions as V, syncAndPublishWorkflowTemplate as Vn, documentModelModuleFileTemplate as Vt, getBooleanPropertyValue as W, editorsIndexTemplate as Wn, documentModelGenUtilsTemplate as Wt, getVariableDeclarationByTypeName as X, nginxConfTemplate as Xn, documentModelOperationsModuleErrorFileTemplate as Xt, getStringPropertyValue as Y, switchboardEntrypointTemplate as Yn, documentModelOperationsModuleOperationsFileTemplate as Yt, loadDocumentModelInDir as Z, dockerfileTemplate as Zn, documentModelOperationsModuleCreatorsFileTemplate as Zt, getCommandHelpInfo as _, documentEditorModuleFileTemplate as _n, customSubgraphSchemaTemplate as _t, getOrCreateManifestFile as a, getLatestDocumentModelSpecVersionNumber as an, folderTreeFileTemplate as ar, getProcessorMetadata as at, writeCliDocsMarkdownFile as b, vitestConfigTemplate as bn, relationalDbSchemaTemplate as bt, operationHasAttachment as c, getActionType as cn, emptyStateFileTemplate as cr, formatSafe as ct, tsMorphGenerateDocumentModel as d, documentModelDocumentTypeTemplate as dn, createDocumentFileTemplate as dr, getDocumentTypeMetadata as dt, getDocumentModelDirName as en, claudeSettingsLocalTemplate as er, ensureDirectoriesExist as et, generateDocumentModelZodSchemas as f, documentModelDocumentSchemaFileTemplate as fn, documentModelDocumentTypeMetadata as ft, tsMorphGenerateDocumentEditor as g, documentModelRootActionsFileTemplate as gn, customSubgraphResolversTemplate as gt, scalarsValidation as h, documentModelGenActionsFileTemplate as hn, parseConfig as ht, createOrUpdateManifest as i, getLatestDocumentModelSpec as in, driveExplorerNavigationBreadcrumbsFileTemplate as ir, getSubgraphMetadata as it, writeProjectRootFiles as j, exportsTemplate as jn, documentModelUtilsTemplate as jt, writeGeneratedSubgraphsFiles as k, packageJsonScriptsTemplate as kn, analyticsIndexTemplate as kt, operationHasEmptyInput as l, getActionTypeName as ln, driveExplorerFileTemplate as lr, formatSourceFileWithPrettier as lt, scalars as m, documentModelGenControllerFileTemplate as mn, parseArgs as mt, tsMorphGenerateSubgraph as n, getDocumentModelVariableNames as nn, appEditorFileTemplate as nr, getOrCreateSourceFile as nt, pruneManifestSection as o, getActionInputName as on, appFoldersFileTemplate as or, getAppMetadata as ot, generateTypesAndZodSchemasFromGraphql as p, documentModelGenCreatorsFileTemplate as pn, configSpec as pt, getProperyAssignmentByName as q, documentModelsIndexTemplate as qn, documentModelGenReducerFileTemplate as qt, tsMorphGenerateProcessor as r, getEditorVariableNames as rn, appConfigFileTemplate as rr, getPreviousVersionSourceFile as rt, makeModulesIndexFile as s, getActionInputTypeNames as sn, appFilesFileTemplate as sr, getEditorMetadata as st, makeSubgraphsIndexFile as t, getDocumentModelSpecByVersionNumber as tn, agentsTemplate as tr, getOrCreateDirectory as tt, operationHasInput as u, documentModelGenIndexFileTemplate as un, appDriveContentsFileTemplate as ur, runPrettier as ut, getCommandsHelpInfo as v, documentEditorEditorFileTemplate as vn, subgraphLibFileTemplate as vt, writeCIFiles as w, styleTemplate as wn, relationalDbFactoryTemplate as wt, buildBoilerplatePackageJson as x, tsConfigTemplate as xn, relationalDbProcessorTemplate as xt, makeCliDocsFromHelp as y, docsFromCliHelpTemplate as yn, subgraphIndexFileTemplate as yt, DEFAULT_PROJECT_OPTIONS as z, indexHtmlTemplate as zn, documentModelSrcUtilsTemplate as zt };
|
|
8798
8541
|
|
|
8799
|
-
//# sourceMappingURL=file-builders-
|
|
8542
|
+
//# sourceMappingURL=file-builders-BGuRLZmo.mjs.map
|