@decantr/core 1.0.2 → 1.0.4
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/index.d.ts +137 -22
- package/dist/index.js +461 -182
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/schema/scaffold-pack.v1.json +14 -0
- package/schema/section-pack.v1.json +36 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
// src/pipeline.ts
|
|
2
|
-
import { validateEssence, isV3 as isV32, migrateV2ToV3 } from "@decantr/essence-spec";
|
|
3
|
-
import { createResolver } from "@decantr/registry";
|
|
4
|
-
|
|
5
1
|
// src/resolve.ts
|
|
6
|
-
import {
|
|
2
|
+
import { computeDensity, isSectioned, isSimple, isV3 } from "@decantr/essence-spec";
|
|
3
|
+
import { detectWirings, resolvePatternPreset } from "@decantr/registry";
|
|
7
4
|
|
|
8
5
|
// src/utils.ts
|
|
9
6
|
function pascalCase(str) {
|
|
@@ -11,7 +8,6 @@ function pascalCase(str) {
|
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
// src/resolve.ts
|
|
14
|
-
import { resolvePatternPreset, detectWirings } from "@decantr/registry";
|
|
15
11
|
var NAV_ICONS = {
|
|
16
12
|
overview: "layout-dashboard",
|
|
17
13
|
dashboard: "layout-dashboard",
|
|
@@ -58,7 +54,11 @@ function extractLayoutRefs(layout) {
|
|
|
58
54
|
refs.push({ id: item.pattern, explicitPreset: item.preset, alias: item.as });
|
|
59
55
|
} else if (isColumnLayout(item)) {
|
|
60
56
|
for (const col of item.cols) {
|
|
61
|
-
|
|
57
|
+
if (typeof col === "string") {
|
|
58
|
+
refs.push({ id: col });
|
|
59
|
+
} else {
|
|
60
|
+
refs.push({ id: col.pattern, explicitPreset: col.preset, alias: col.as });
|
|
61
|
+
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -249,10 +249,13 @@ async function resolveEssence(essence, resolver) {
|
|
|
249
249
|
registryTheme = themeResult.item;
|
|
250
250
|
}
|
|
251
251
|
const themeSpatial = registryTheme?.spatial;
|
|
252
|
-
const density = computeDensity(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
const density = computeDensity(
|
|
253
|
+
simpleEssence.personality,
|
|
254
|
+
themeSpatial ? {
|
|
255
|
+
density_bias: themeSpatial.density_bias,
|
|
256
|
+
content_gap_shift: themeSpatial.content_gap_shift
|
|
257
|
+
} : void 0
|
|
258
|
+
);
|
|
256
259
|
const themeId = simpleEssence.theme.id;
|
|
257
260
|
const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
|
|
258
261
|
const theme = buildTheme(simpleEssence, isAddon);
|
|
@@ -293,10 +296,13 @@ async function resolveV3Essence(essence, resolver) {
|
|
|
293
296
|
registryTheme = themeResult.item;
|
|
294
297
|
}
|
|
295
298
|
const themeSpatial = registryTheme?.spatial;
|
|
296
|
-
const density = computeDensity(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
299
|
+
const density = computeDensity(
|
|
300
|
+
dna.personality,
|
|
301
|
+
themeSpatial ? {
|
|
302
|
+
density_bias: themeSpatial.density_bias,
|
|
303
|
+
content_gap_shift: themeSpatial.content_gap_shift
|
|
304
|
+
} : void 0
|
|
305
|
+
);
|
|
300
306
|
const densityResult = {
|
|
301
307
|
gap: dna.spacing.content_gap || density.content_gap,
|
|
302
308
|
level: dna.spacing.density || density.level
|
|
@@ -305,10 +311,17 @@ async function resolveV3Essence(essence, resolver) {
|
|
|
305
311
|
const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
|
|
306
312
|
const theme = buildThemeFromV3(essence, isAddon);
|
|
307
313
|
const blueprintPages = blueprint.pages ?? (blueprint.sections ? blueprint.sections.flatMap((s) => s.pages) : [{ id: "home", layout: ["hero"] }]);
|
|
308
|
-
const defaultShell = blueprint.shell ??
|
|
314
|
+
const defaultShell = blueprint.shell ?? blueprint.sections?.[0]?.shell ?? "sidebar-main";
|
|
309
315
|
const structurePages = blueprint.pages ? blueprint.pages.map((page) => blueprintPageToStructurePage(page, defaultShell)) : blueprint.sections ? blueprint.sections.flatMap(
|
|
310
|
-
(section) => section.pages.map(
|
|
311
|
-
|
|
316
|
+
(section) => section.pages.map(
|
|
317
|
+
(page) => blueprintPageToStructurePage(page, section.shell ?? defaultShell)
|
|
318
|
+
)
|
|
319
|
+
) : [
|
|
320
|
+
blueprintPageToStructurePage(
|
|
321
|
+
{ id: "home", layout: ["hero"] },
|
|
322
|
+
defaultShell
|
|
323
|
+
)
|
|
324
|
+
];
|
|
312
325
|
const resolvedPages = await resolvePages(structurePages, resolver, registryTheme);
|
|
313
326
|
const shellType = defaultShell;
|
|
314
327
|
const brand = pascalCase(meta.archetype);
|
|
@@ -390,15 +403,20 @@ function buildPatternNode(patternId, alias, resolved, wiring, theme, density, la
|
|
|
390
403
|
const isStandalone = layout === "hero" || layout === "row";
|
|
391
404
|
const contained = pattern && preset ? shouldWrapInCard(pattern, preset, theme) : false;
|
|
392
405
|
const components = pattern?.components || [];
|
|
406
|
+
const presetName = preset?.preset || "default";
|
|
407
|
+
const presetDescription = pattern?.presets?.[presetName]?.description;
|
|
408
|
+
const interactions = Array.isArray(pattern?.interactions) ? pattern.interactions : void 0;
|
|
393
409
|
const patternMeta = {
|
|
394
410
|
patternId,
|
|
395
|
-
preset:
|
|
411
|
+
preset: presetName,
|
|
396
412
|
alias,
|
|
397
413
|
layout,
|
|
398
414
|
contained,
|
|
399
415
|
standalone: isStandalone,
|
|
400
416
|
code: preset?.code ? { imports: preset.code.imports, example: preset.code.example } : null,
|
|
401
|
-
components
|
|
417
|
+
components,
|
|
418
|
+
...presetDescription ? { presetDescription } : {},
|
|
419
|
+
...interactions && interactions.length > 0 ? { interactions } : {}
|
|
402
420
|
};
|
|
403
421
|
const card = contained && !isStandalone && pattern ? buildCardWrapping(pattern, theme) : null;
|
|
404
422
|
const wireProps = wiring?.props[alias] || wiring?.props[patternId] || null;
|
|
@@ -426,27 +444,32 @@ function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
|
|
|
426
444
|
const resolved = resolvedPatterns.get(alias) || resolvedPatterns.get(item.pattern);
|
|
427
445
|
children.push(buildPatternNode(item.pattern, alias, resolved, wiring, theme, density, layer));
|
|
428
446
|
} else if (isColumnLayout2(item)) {
|
|
429
|
-
const
|
|
447
|
+
const colEntries = item.cols.map((col) => ({
|
|
448
|
+
id: typeof col === "string" ? col : col.pattern,
|
|
449
|
+
alias: typeof col === "string" ? col : col.as ?? col.pattern
|
|
450
|
+
}));
|
|
430
451
|
const breakpoint = item.at || null;
|
|
431
452
|
const spans = item.span || null;
|
|
432
453
|
let normalizedSpans = null;
|
|
433
454
|
if (spans) {
|
|
434
455
|
normalizedSpans = {};
|
|
435
|
-
for (const
|
|
436
|
-
normalizedSpans[
|
|
456
|
+
for (const entry of colEntries) {
|
|
457
|
+
normalizedSpans[entry.id] = spans[entry.id] || spans[entry.alias] || 1;
|
|
437
458
|
}
|
|
438
459
|
}
|
|
439
460
|
const gridChildren = [];
|
|
440
|
-
for (const
|
|
441
|
-
const resolved = resolvedPatterns.get(
|
|
442
|
-
gridChildren.push(
|
|
461
|
+
for (const entry of colEntries) {
|
|
462
|
+
const resolved = resolvedPatterns.get(entry.alias) || resolvedPatterns.get(entry.id);
|
|
463
|
+
gridChildren.push(
|
|
464
|
+
buildPatternNode(entry.id, entry.alias, resolved, wiring, theme, density, layer)
|
|
465
|
+
);
|
|
443
466
|
}
|
|
444
|
-
const totalCols = normalizedSpans ? Object.values(normalizedSpans).reduce((a, b) => a + b, 0) :
|
|
467
|
+
const totalCols = normalizedSpans ? Object.values(normalizedSpans).reduce((a, b) => a + b, 0) : colEntries.length;
|
|
445
468
|
const breakpoints = item.breakpoints?.map((bp) => ({ at: bp.at, cols: bp.cols })) || null;
|
|
446
469
|
const responsive = item.responsive || null;
|
|
447
470
|
const gridNode = {
|
|
448
471
|
type: "grid",
|
|
449
|
-
id: `grid-${
|
|
472
|
+
id: `grid-${colEntries.map((e) => e.id).join("-")}`,
|
|
450
473
|
children: gridChildren,
|
|
451
474
|
cols: totalCols,
|
|
452
475
|
spans: normalizedSpans,
|
|
@@ -460,7 +483,7 @@ function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
|
|
|
460
483
|
}
|
|
461
484
|
}
|
|
462
485
|
const gapAtom = density.gap.startsWith("_") ? density.gap : density.gap.startsWith("gap") ? `_${density.gap}` : `_gap${density.gap}`;
|
|
463
|
-
const surface = page.surface || `_flex _col ${gapAtom}
|
|
486
|
+
const surface = page.surface || `_flex _col ${gapAtom}`;
|
|
464
487
|
return {
|
|
465
488
|
type: "page",
|
|
466
489
|
id: page.id,
|
|
@@ -472,12 +495,63 @@ function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
|
|
|
472
495
|
};
|
|
473
496
|
}
|
|
474
497
|
|
|
498
|
+
// src/ir-helpers.ts
|
|
499
|
+
function walkIR(node, visitor, parent = null) {
|
|
500
|
+
visitor(node, parent);
|
|
501
|
+
for (const child of node.children) {
|
|
502
|
+
walkIR(child, visitor, node);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function findNodes(root, type) {
|
|
506
|
+
const results = [];
|
|
507
|
+
walkIR(root, (node) => {
|
|
508
|
+
if (node.type === type) {
|
|
509
|
+
results.push(node);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
return results;
|
|
513
|
+
}
|
|
514
|
+
function countPatterns(root) {
|
|
515
|
+
return findNodes(root, "pattern").length;
|
|
516
|
+
}
|
|
517
|
+
function validateIR(root) {
|
|
518
|
+
const errors = [];
|
|
519
|
+
walkIR(root, (node, parent) => {
|
|
520
|
+
if (node.type === "grid") {
|
|
521
|
+
const grid = node;
|
|
522
|
+
if (grid.children.length === 0) {
|
|
523
|
+
errors.push(`Grid node "${grid.id}" has no children`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (node.type === "pattern") {
|
|
527
|
+
const pattern = node;
|
|
528
|
+
if (!pattern.pattern) {
|
|
529
|
+
errors.push(`Pattern node "${pattern.id}" is missing pattern meta`);
|
|
530
|
+
}
|
|
531
|
+
if (!pattern.pattern.patternId) {
|
|
532
|
+
errors.push(`Pattern node "${pattern.id}" has no patternId`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (node.type === "page") {
|
|
536
|
+
if (!node.id) {
|
|
537
|
+
errors.push("Page node is missing id");
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
return errors;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/packs.ts
|
|
545
|
+
import { isV3 as isV33, migrateV2ToV3 as migrateV2ToV32 } from "@decantr/essence-spec";
|
|
546
|
+
|
|
475
547
|
// src/pipeline.ts
|
|
548
|
+
import { isV3 as isV32, migrateV2ToV3, validateEssence } from "@decantr/essence-spec";
|
|
549
|
+
import { createResolver } from "@decantr/registry";
|
|
476
550
|
function extractRouting(essence) {
|
|
477
551
|
if (isV32(essence)) {
|
|
478
|
-
return essence.meta.platform.routing || "
|
|
552
|
+
return essence.meta.platform.routing || "history";
|
|
479
553
|
}
|
|
480
|
-
return essence.platform?.routing || "
|
|
554
|
+
return essence.platform?.routing || "history";
|
|
481
555
|
}
|
|
482
556
|
async function runPipeline(essence, options) {
|
|
483
557
|
const validation = validateEssence(essence);
|
|
@@ -538,57 +612,10 @@ async function runPipeline(essence, options) {
|
|
|
538
612
|
store: storeNode,
|
|
539
613
|
features: resolved.features
|
|
540
614
|
};
|
|
541
|
-
return { ir: appNode };
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// src/ir-helpers.ts
|
|
545
|
-
function walkIR(node, visitor, parent = null) {
|
|
546
|
-
visitor(node, parent);
|
|
547
|
-
for (const child of node.children) {
|
|
548
|
-
walkIR(child, visitor, node);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
function findNodes(root, type) {
|
|
552
|
-
const results = [];
|
|
553
|
-
walkIR(root, (node) => {
|
|
554
|
-
if (node.type === type) {
|
|
555
|
-
results.push(node);
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
return results;
|
|
559
|
-
}
|
|
560
|
-
function countPatterns(root) {
|
|
561
|
-
return findNodes(root, "pattern").length;
|
|
562
|
-
}
|
|
563
|
-
function validateIR(root) {
|
|
564
|
-
const errors = [];
|
|
565
|
-
walkIR(root, (node, parent) => {
|
|
566
|
-
if (node.type === "grid") {
|
|
567
|
-
const grid = node;
|
|
568
|
-
if (grid.children.length === 0) {
|
|
569
|
-
errors.push(`Grid node "${grid.id}" has no children`);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
if (node.type === "pattern") {
|
|
573
|
-
const pattern = node;
|
|
574
|
-
if (!pattern.pattern) {
|
|
575
|
-
errors.push(`Pattern node "${pattern.id}" is missing pattern meta`);
|
|
576
|
-
}
|
|
577
|
-
if (!pattern.pattern.patternId) {
|
|
578
|
-
errors.push(`Pattern node "${pattern.id}" has no patternId`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
if (node.type === "page") {
|
|
582
|
-
if (!node.id) {
|
|
583
|
-
errors.push("Page node is missing id");
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
return errors;
|
|
615
|
+
return { ir: appNode, registryTheme: resolved.registryTheme };
|
|
588
616
|
}
|
|
589
617
|
|
|
590
618
|
// src/packs.ts
|
|
591
|
-
import { isV3 as isV33, migrateV2ToV3 as migrateV2ToV32 } from "@decantr/essence-spec";
|
|
592
619
|
var EXECUTION_PACK_SCHEMA_URLS = {
|
|
593
620
|
scaffold: "https://decantr.ai/schemas/scaffold-pack.v1.json",
|
|
594
621
|
section: "https://decantr.ai/schemas/section-pack.v1.json",
|
|
@@ -748,12 +775,14 @@ function summarizeRoutes(appNode) {
|
|
|
748
775
|
return appNode.routes.flatMap((route) => {
|
|
749
776
|
const pageNode = findPageNode(appNode, route.pageId);
|
|
750
777
|
if (!pageNode) return [];
|
|
751
|
-
return [
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
778
|
+
return [
|
|
779
|
+
{
|
|
780
|
+
pageId: pageNode.pageId,
|
|
781
|
+
path: route.path,
|
|
782
|
+
patternIds: collectPatternIds(pageNode),
|
|
783
|
+
shell: route.shell
|
|
784
|
+
}
|
|
785
|
+
];
|
|
757
786
|
});
|
|
758
787
|
}
|
|
759
788
|
function summarizeSectionRoutes(appNode, input) {
|
|
@@ -775,7 +804,9 @@ function collectPagePatterns(page) {
|
|
|
775
804
|
id: patternNode.pattern.patternId,
|
|
776
805
|
alias: patternNode.pattern.alias,
|
|
777
806
|
preset: patternNode.pattern.preset,
|
|
778
|
-
layout: patternNode.pattern.layout
|
|
807
|
+
layout: patternNode.pattern.layout,
|
|
808
|
+
...patternNode.pattern.presetDescription ? { presetDescription: patternNode.pattern.presetDescription } : {},
|
|
809
|
+
...patternNode.pattern.interactions && patternNode.pattern.interactions.length > 0 ? { interactions: patternNode.pattern.interactions } : {}
|
|
779
810
|
});
|
|
780
811
|
});
|
|
781
812
|
return patterns;
|
|
@@ -791,45 +822,127 @@ function renderList(title, entries) {
|
|
|
791
822
|
if (entries.length === 0) return [];
|
|
792
823
|
return [title, ...entries.map((entry) => `- ${entry}`), ""];
|
|
793
824
|
}
|
|
825
|
+
function formatHotkeySemantics(semantics) {
|
|
826
|
+
if (!semantics) return "";
|
|
827
|
+
const parts = [];
|
|
828
|
+
if (typeof semantics.chord_window_ms === "number") {
|
|
829
|
+
parts.push(`chord window ${semantics.chord_window_ms}ms`);
|
|
830
|
+
}
|
|
831
|
+
if (semantics.input_guard === true) parts.push("suppress during text-input focus");
|
|
832
|
+
if (semantics.input_guard === false) parts.push("fire even while typing (explicit)");
|
|
833
|
+
if (semantics.modifier_suppression === true) parts.push("ignore when modifier held");
|
|
834
|
+
if (semantics.modifier_suppression === false) parts.push("allow modifier passthrough (explicit)");
|
|
835
|
+
if (semantics.match_case === true) parts.push("case-sensitive");
|
|
836
|
+
return parts.join("; ");
|
|
837
|
+
}
|
|
838
|
+
function routingImplementationHint(routing) {
|
|
839
|
+
switch (routing) {
|
|
840
|
+
case "hash":
|
|
841
|
+
return "HashRouter from react-router-dom; URLs prefixed with /# (e.g. /#/login). Only for static-only hosts without SPA fallback.";
|
|
842
|
+
case "history":
|
|
843
|
+
return "BrowserRouter from react-router-dom; regular URLs (e.g. /login). Works on Vite dev, Vercel, Netlify, Cloudflare Pages.";
|
|
844
|
+
case "pathname":
|
|
845
|
+
return "pathname-based routing (Next.js App Router file conventions).";
|
|
846
|
+
default:
|
|
847
|
+
return String(routing);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
794
850
|
function renderExecutionPackMarkdown(pack) {
|
|
795
851
|
const lines = [];
|
|
796
852
|
lines.push(`# ${pack.packType.charAt(0).toUpperCase()}${pack.packType.slice(1)} Pack`);
|
|
797
853
|
lines.push("");
|
|
798
854
|
lines.push(`**Objective:** ${pack.objective}`);
|
|
799
|
-
lines.push(
|
|
800
|
-
|
|
855
|
+
lines.push(
|
|
856
|
+
`**Target:** ${pack.target.adapter}${pack.target.framework ? ` (${pack.target.framework})` : ""}`
|
|
857
|
+
);
|
|
858
|
+
lines.push(
|
|
859
|
+
`**Scope:** pages=${pack.scope.pageIds.join(", ") || "none"} | patterns=${pack.scope.patternIds.join(", ") || "none"}`
|
|
860
|
+
);
|
|
801
861
|
lines.push("");
|
|
802
862
|
if (pack.packType === "scaffold") {
|
|
803
863
|
const scaffoldPack = pack;
|
|
804
|
-
const scaffoldShells = [
|
|
864
|
+
const scaffoldShells = [
|
|
865
|
+
...new Set(scaffoldPack.data.routes.map((route) => route.shell).filter(Boolean))
|
|
866
|
+
];
|
|
805
867
|
lines.push("## Scaffold Contract");
|
|
806
868
|
lines.push(`- Shell: ${scaffoldPack.data.shell}`);
|
|
807
869
|
if (scaffoldShells.length > 1) {
|
|
808
870
|
const secondaryShells = scaffoldShells.filter((shell) => shell !== scaffoldPack.data.shell);
|
|
809
|
-
lines.push(
|
|
871
|
+
lines.push(
|
|
872
|
+
`- Shells: ${[`${scaffoldPack.data.shell} (primary)`, ...secondaryShells].join(", ")}`
|
|
873
|
+
);
|
|
810
874
|
}
|
|
811
875
|
lines.push(`- Theme: ${scaffoldPack.data.theme.id} (${scaffoldPack.data.theme.mode})`);
|
|
812
|
-
lines.push(
|
|
876
|
+
lines.push(
|
|
877
|
+
`- Routing: ${scaffoldPack.data.routing} \u2192 ${routingImplementationHint(scaffoldPack.data.routing)}`
|
|
878
|
+
);
|
|
813
879
|
if (scaffoldPack.data.features.length > 0) {
|
|
814
880
|
lines.push(`- Features: ${scaffoldPack.data.features.join(", ")}`);
|
|
815
881
|
}
|
|
816
882
|
if (scaffoldPack.data.navigation?.commandPalette || scaffoldPack.data.navigation?.hotkeys.length) {
|
|
817
883
|
lines.push("- Navigation:");
|
|
818
|
-
|
|
819
|
-
|
|
884
|
+
const cp = scaffoldPack.data.navigation.commandPalette;
|
|
885
|
+
if (cp) {
|
|
886
|
+
if (typeof cp === "object") {
|
|
887
|
+
lines.push(" - Command palette:");
|
|
888
|
+
if (cp.trigger) lines.push(` - Trigger: ${cp.trigger}`);
|
|
889
|
+
if (cp.styling)
|
|
890
|
+
lines.push(` - Styling: ${cp.styling}${cp.width ? ` (width ${cp.width})` : ""}`);
|
|
891
|
+
else if (cp.width) lines.push(` - Width: ${cp.width}`);
|
|
892
|
+
if (cp.placeholder) lines.push(` - Placeholder: "${cp.placeholder}"`);
|
|
893
|
+
if (cp.commands && cp.commands.length > 0) {
|
|
894
|
+
lines.push(` - Commands (${cp.commands.length}):`);
|
|
895
|
+
for (const command of cp.commands) {
|
|
896
|
+
const parts = [];
|
|
897
|
+
if (command.section) parts.push(command.section);
|
|
898
|
+
parts.push(command.label);
|
|
899
|
+
if (command.hotkey) parts.push(`[${command.hotkey}]`);
|
|
900
|
+
if (command.route) parts.push(`\u2192 ${command.route}`);
|
|
901
|
+
lines.push(` - ${command.id}: ${parts.join(" \xB7 ")}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
lines.push(" - command palette required");
|
|
906
|
+
}
|
|
820
907
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
908
|
+
const globalSemantics = scaffoldPack.data.navigation.hotkeySemantics;
|
|
909
|
+
if (scaffoldPack.data.navigation.hotkeys.length > 0) {
|
|
910
|
+
const semanticsSummary = formatHotkeySemantics(globalSemantics);
|
|
911
|
+
lines.push(` - Hotkeys${semanticsSummary ? ` (${semanticsSummary})` : ""}:`);
|
|
912
|
+
for (const hotkey of scaffoldPack.data.navigation.hotkeys) {
|
|
913
|
+
const target = [hotkey.label, hotkey.route].filter(Boolean).join(" \u2014 ");
|
|
914
|
+
const perKeySemantics = formatHotkeySemantics(hotkey.semantics);
|
|
915
|
+
const suffix = perKeySemantics ? ` _(${perKeySemantics})_` : "";
|
|
916
|
+
lines.push(` - ${hotkey.key}${target ? `: ${target}` : ""}${suffix}`);
|
|
917
|
+
}
|
|
824
918
|
}
|
|
825
919
|
}
|
|
826
920
|
lines.push("");
|
|
827
921
|
lines.push("## Route Plan");
|
|
828
922
|
for (const route of scaffoldPack.data.routes) {
|
|
829
923
|
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
830
|
-
lines.push(
|
|
924
|
+
lines.push(
|
|
925
|
+
`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`
|
|
926
|
+
);
|
|
831
927
|
}
|
|
832
928
|
lines.push("");
|
|
929
|
+
if (scaffoldPack.data.themeDecorators && scaffoldPack.data.themeDecorators.length > 0) {
|
|
930
|
+
const escScaffoldCell = (s) => s.replace(/\|/g, "\\|");
|
|
931
|
+
lines.push(`## Required Theme Decorators (${scaffoldPack.data.theme.id})`);
|
|
932
|
+
lines.push("");
|
|
933
|
+
lines.push(
|
|
934
|
+
`These classes carry the active theme's visual identity. Tokens alone give bones; decorators give personality. Generated source MUST apply these across all sections \u2014 without them, every page reads as "themed colors only" with no theme character. Section packs reference this table; the contract is project-wide.`
|
|
935
|
+
);
|
|
936
|
+
lines.push("");
|
|
937
|
+
lines.push("| Class | Intent | Apply to |");
|
|
938
|
+
lines.push("|-------|--------|----------|");
|
|
939
|
+
for (const entry of scaffoldPack.data.themeDecorators) {
|
|
940
|
+
lines.push(
|
|
941
|
+
`| \`.${entry.class}\` | ${escScaffoldCell(entry.intent)} | ${escScaffoldCell(entry.applyTo)} |`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
lines.push("");
|
|
945
|
+
}
|
|
833
946
|
}
|
|
834
947
|
if (pack.packType === "section") {
|
|
835
948
|
const sectionPack = pack;
|
|
@@ -848,9 +961,64 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
848
961
|
lines.push("## Section Routes");
|
|
849
962
|
for (const route of sectionPack.data.routes) {
|
|
850
963
|
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
851
|
-
lines.push(
|
|
964
|
+
lines.push(
|
|
965
|
+
`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`
|
|
966
|
+
);
|
|
852
967
|
}
|
|
853
968
|
lines.push("");
|
|
969
|
+
if (sectionPack.data.navigationItems && sectionPack.data.navigationItems.length > 0) {
|
|
970
|
+
lines.push("## Section Navigation");
|
|
971
|
+
lines.push("");
|
|
972
|
+
lines.push(
|
|
973
|
+
"Render these items in the shell's primary navigation. Exact match on label, route, and icon."
|
|
974
|
+
);
|
|
975
|
+
lines.push("");
|
|
976
|
+
for (const item of sectionPack.data.navigationItems) {
|
|
977
|
+
const parts = [`${item.label} \u2192 ${item.route}`];
|
|
978
|
+
if (item.icon) parts.push(`icon: ${item.icon}`);
|
|
979
|
+
if (item.hotkey) parts.push(`hotkey: ${item.hotkey}`);
|
|
980
|
+
if (item.badge) parts.push(`badge: ${item.badge}`);
|
|
981
|
+
if (item.active_match) parts.push(`active match: \`${item.active_match}\``);
|
|
982
|
+
lines.push(`- ${parts.join(" \xB7 ")}`);
|
|
983
|
+
}
|
|
984
|
+
lines.push("");
|
|
985
|
+
}
|
|
986
|
+
if (sectionPack.data.directives && sectionPack.data.directives.length > 0) {
|
|
987
|
+
lines.push("## Section Directives");
|
|
988
|
+
lines.push("");
|
|
989
|
+
lines.push(
|
|
990
|
+
"Execution-level rules every page in this section must obey. Follow exactly \u2014 these live in the pack contract, not narrative prose."
|
|
991
|
+
);
|
|
992
|
+
lines.push("");
|
|
993
|
+
for (const directive of sectionPack.data.directives) {
|
|
994
|
+
lines.push(`- ${directive}`);
|
|
995
|
+
}
|
|
996
|
+
lines.push("");
|
|
997
|
+
}
|
|
998
|
+
if (sectionPack.data.themeDecorators && sectionPack.data.themeDecorators.length > 0) {
|
|
999
|
+
const escCell = (s) => s.replace(/\|/g, "\\|");
|
|
1000
|
+
lines.push(`## Required Theme Decorators (${sectionPack.data.theme.id})`);
|
|
1001
|
+
lines.push("");
|
|
1002
|
+
lines.push(
|
|
1003
|
+
`These classes carry the active theme's visual identity. Tokens alone give bones; decorators give personality. Generated source MUST apply these \u2014 without them, the page reads as "themed colors only" with no theme character.`
|
|
1004
|
+
);
|
|
1005
|
+
lines.push("");
|
|
1006
|
+
lines.push("| Class | Intent | Apply to |");
|
|
1007
|
+
lines.push("|-------|--------|----------|");
|
|
1008
|
+
for (const entry of sectionPack.data.themeDecorators) {
|
|
1009
|
+
lines.push(
|
|
1010
|
+
`| \`.${entry.class}\` | ${escCell(entry.intent)} | ${escCell(entry.applyTo)} |`
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
lines.push("");
|
|
1014
|
+
} else {
|
|
1015
|
+
lines.push("## Theme Decorators");
|
|
1016
|
+
lines.push("");
|
|
1017
|
+
lines.push(
|
|
1018
|
+
`Theme \`${sectionPack.data.theme.id}\` decorators are documented ONCE in \`scaffold-pack.md\` under "Required Theme Decorators". Apply them across this section's pages \u2014 the contract is the same project-wide. See also DECANTR.md "Decorator Quick Reference" for the same table.`
|
|
1019
|
+
);
|
|
1020
|
+
lines.push("");
|
|
1021
|
+
}
|
|
854
1022
|
}
|
|
855
1023
|
if (pack.packType === "page") {
|
|
856
1024
|
const pagePack = pack;
|
|
@@ -872,7 +1040,18 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
872
1040
|
lines.push("");
|
|
873
1041
|
lines.push("## Page Patterns");
|
|
874
1042
|
for (const pattern of pagePack.data.patterns) {
|
|
875
|
-
lines.push(
|
|
1043
|
+
lines.push(
|
|
1044
|
+
`- ${pattern.alias} -> ${pattern.id} [${pattern.layout}${pattern.preset ? ` | ${pattern.preset}` : ""}]`
|
|
1045
|
+
);
|
|
1046
|
+
if (pattern.presetDescription) {
|
|
1047
|
+
lines.push(` > ${pattern.presetDescription}`);
|
|
1048
|
+
}
|
|
1049
|
+
if (pattern.interactions && pattern.interactions.length > 0) {
|
|
1050
|
+
lines.push(` **Interactions (MUST implement each \u2014 see DECANTR.md "Interaction Requirements"):**`);
|
|
1051
|
+
for (const interaction of pattern.interactions) {
|
|
1052
|
+
lines.push(` - [ ] ${interaction}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
876
1055
|
}
|
|
877
1056
|
lines.push("");
|
|
878
1057
|
if (pagePack.data.wiringSignals.length > 0) {
|
|
@@ -882,6 +1061,16 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
882
1061
|
}
|
|
883
1062
|
lines.push("");
|
|
884
1063
|
}
|
|
1064
|
+
if (pagePack.data.directives && pagePack.data.directives.length > 0) {
|
|
1065
|
+
lines.push("## Page Directives");
|
|
1066
|
+
lines.push("");
|
|
1067
|
+
lines.push("Execution-level rules for this route. Follow exactly.");
|
|
1068
|
+
lines.push("");
|
|
1069
|
+
for (const directive of pagePack.data.directives) {
|
|
1070
|
+
lines.push(`- ${directive}`);
|
|
1071
|
+
}
|
|
1072
|
+
lines.push("");
|
|
1073
|
+
}
|
|
885
1074
|
}
|
|
886
1075
|
if (pack.packType === "mutation") {
|
|
887
1076
|
const mutationPack = pack;
|
|
@@ -889,7 +1078,9 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
889
1078
|
lines.push(`- Operation: ${mutationPack.data.mutationType}`);
|
|
890
1079
|
lines.push(`- Shell: ${mutationPack.data.shell}`);
|
|
891
1080
|
lines.push(`- Theme: ${mutationPack.data.theme.id} (${mutationPack.data.theme.mode})`);
|
|
892
|
-
lines.push(
|
|
1081
|
+
lines.push(
|
|
1082
|
+
`- Routing: ${mutationPack.data.routing} \u2192 ${routingImplementationHint(mutationPack.data.routing)}`
|
|
1083
|
+
);
|
|
893
1084
|
if (mutationPack.data.features.length > 0) {
|
|
894
1085
|
lines.push(`- Features: ${mutationPack.data.features.join(", ")}`);
|
|
895
1086
|
}
|
|
@@ -897,7 +1088,9 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
897
1088
|
lines.push("## Route Topology");
|
|
898
1089
|
for (const route of mutationPack.data.routes) {
|
|
899
1090
|
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
900
|
-
lines.push(
|
|
1091
|
+
lines.push(
|
|
1092
|
+
`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`
|
|
1093
|
+
);
|
|
901
1094
|
}
|
|
902
1095
|
lines.push("");
|
|
903
1096
|
if (mutationPack.data.workflow.length > 0) {
|
|
@@ -922,7 +1115,9 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
922
1115
|
lines.push("## Review Topology");
|
|
923
1116
|
for (const route of reviewPack.data.routes) {
|
|
924
1117
|
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
925
|
-
lines.push(
|
|
1118
|
+
lines.push(
|
|
1119
|
+
`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`
|
|
1120
|
+
);
|
|
926
1121
|
}
|
|
927
1122
|
lines.push("");
|
|
928
1123
|
if (reviewPack.data.focusAreas.length > 0) {
|
|
@@ -940,6 +1135,14 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
940
1135
|
lines.push("");
|
|
941
1136
|
}
|
|
942
1137
|
}
|
|
1138
|
+
if (pack.packType === "page") {
|
|
1139
|
+
lines.push("## Shared Contract");
|
|
1140
|
+
lines.push(
|
|
1141
|
+
"Required setup, allowed vocabulary, success checks, anti-patterns, and token budget are shared across every page pack. The full list lives in the pack JSON sidecar (`page-<id>-pack.json`) and in the pack-manifest. Refer there instead of re-reading the same boilerplate 16 times."
|
|
1142
|
+
);
|
|
1143
|
+
lines.push("");
|
|
1144
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
1145
|
+
}
|
|
943
1146
|
lines.push("## Required Setup");
|
|
944
1147
|
if (pack.requiredSetup.length === 0) {
|
|
945
1148
|
lines.push("- None declared.");
|
|
@@ -954,9 +1157,24 @@ function renderExecutionPackMarkdown(pack) {
|
|
|
954
1157
|
lines.push(...pack.allowedVocabulary.map((entry) => `- ${entry}`));
|
|
955
1158
|
}
|
|
956
1159
|
lines.push("");
|
|
957
|
-
lines.push(
|
|
958
|
-
|
|
959
|
-
|
|
1160
|
+
lines.push(
|
|
1161
|
+
...renderList(
|
|
1162
|
+
"## Success Checks",
|
|
1163
|
+
pack.successChecks.map((entry) => `${entry.label} [${entry.severity}]`)
|
|
1164
|
+
)
|
|
1165
|
+
);
|
|
1166
|
+
lines.push(
|
|
1167
|
+
...renderList(
|
|
1168
|
+
"## Anti-Patterns",
|
|
1169
|
+
pack.antiPatterns.map((entry) => `${entry.summary}: ${entry.guidance}`)
|
|
1170
|
+
)
|
|
1171
|
+
);
|
|
1172
|
+
lines.push(
|
|
1173
|
+
...renderList(
|
|
1174
|
+
"## Examples",
|
|
1175
|
+
pack.examples.map((entry) => `${entry.label} (${entry.language})`)
|
|
1176
|
+
)
|
|
1177
|
+
);
|
|
960
1178
|
lines.push("## Token Budget");
|
|
961
1179
|
lines.push(`- Target: ${pack.tokenBudget.target}`);
|
|
962
1180
|
lines.push(`- Max: ${pack.tokenBudget.max}`);
|
|
@@ -976,35 +1194,45 @@ function listPackSections(essence) {
|
|
|
976
1194
|
const declaredSections = essence.blueprint.sections;
|
|
977
1195
|
if (declaredSections && declaredSections.length > 0) {
|
|
978
1196
|
const routedSectionPages = new Set(
|
|
979
|
-
Object.values(essence.blueprint.routes ?? {}).map(
|
|
1197
|
+
Object.values(essence.blueprint.routes ?? {}).map(
|
|
1198
|
+
(entry) => `${entry.section}:${entry.page}`
|
|
1199
|
+
)
|
|
980
1200
|
);
|
|
981
1201
|
return declaredSections.map((section) => {
|
|
982
|
-
const pageIds = section.pages.filter(
|
|
1202
|
+
const pageIds = section.pages.filter(
|
|
1203
|
+
(page) => routedSectionPages.size === 0 || routedSectionPages.has(`${section.id}:${page.id}`)
|
|
1204
|
+
).map((page) => page.id);
|
|
983
1205
|
return {
|
|
984
1206
|
id: section.id,
|
|
985
1207
|
role: section.role,
|
|
986
1208
|
shell: section.shell,
|
|
987
1209
|
description: section.description,
|
|
988
1210
|
features: section.features,
|
|
989
|
-
pageIds
|
|
1211
|
+
pageIds,
|
|
1212
|
+
...Array.isArray(section.navigation_items) && section.navigation_items.length > 0 ? { navigationItems: section.navigation_items } : {},
|
|
1213
|
+
...Array.isArray(section.directives) && section.directives.length > 0 ? { directives: section.directives } : {}
|
|
990
1214
|
};
|
|
991
1215
|
}).filter((section) => section.pageIds.length > 0);
|
|
992
1216
|
}
|
|
993
1217
|
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
994
|
-
return [
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1218
|
+
return [
|
|
1219
|
+
{
|
|
1220
|
+
id: essence.meta.archetype || "default",
|
|
1221
|
+
role: "primary",
|
|
1222
|
+
shell: essence.blueprint.shell ?? "sidebar-main",
|
|
1223
|
+
description: `${essence.meta.archetype || "Application"} section`,
|
|
1224
|
+
features: essence.blueprint.features || [],
|
|
1225
|
+
pageIds: pages.map((page) => page.id)
|
|
1226
|
+
}
|
|
1227
|
+
];
|
|
1002
1228
|
}
|
|
1003
1229
|
function listPackPages(essence) {
|
|
1004
1230
|
const declaredSections = essence.blueprint.sections;
|
|
1005
1231
|
if (declaredSections && declaredSections.length > 0) {
|
|
1006
1232
|
const routedSectionPages = new Set(
|
|
1007
|
-
Object.values(essence.blueprint.routes ?? {}).map(
|
|
1233
|
+
Object.values(essence.blueprint.routes ?? {}).map(
|
|
1234
|
+
(entry) => `${entry.section}:${entry.page}`
|
|
1235
|
+
)
|
|
1008
1236
|
);
|
|
1009
1237
|
return declaredSections.flatMap(
|
|
1010
1238
|
(section) => section.pages.map((page) => ({
|
|
@@ -1012,8 +1240,11 @@ function listPackPages(essence) {
|
|
|
1012
1240
|
shell: page.shell_override ?? section.shell,
|
|
1013
1241
|
sectionId: section.id,
|
|
1014
1242
|
sectionRole: section.role,
|
|
1015
|
-
features: section.features
|
|
1016
|
-
|
|
1243
|
+
features: section.features,
|
|
1244
|
+
...Array.isArray(page.directives) && page.directives.length > 0 ? { directives: page.directives } : {}
|
|
1245
|
+
})).filter(
|
|
1246
|
+
(page) => routedSectionPages.size === 0 || routedSectionPages.has(`${page.sectionId}:${page.pageId}`)
|
|
1247
|
+
)
|
|
1017
1248
|
);
|
|
1018
1249
|
}
|
|
1019
1250
|
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
@@ -1048,13 +1279,15 @@ function buildScaffoldPack(appNode, options = {}) {
|
|
|
1048
1279
|
"Treat the declared routes as the topology source of truth.",
|
|
1049
1280
|
"Preserve the resolved theme and shell contract unless the task explicitly mutates them."
|
|
1050
1281
|
],
|
|
1051
|
-
allowedVocabulary: [
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1282
|
+
allowedVocabulary: [
|
|
1283
|
+
.../* @__PURE__ */ new Set([
|
|
1284
|
+
appNode.shell.config.type,
|
|
1285
|
+
appNode.theme.id,
|
|
1286
|
+
appNode.theme.mode,
|
|
1287
|
+
...appNode.features,
|
|
1288
|
+
...scopePatternIds
|
|
1289
|
+
])
|
|
1290
|
+
],
|
|
1058
1291
|
examples: options.examples ?? [],
|
|
1059
1292
|
antiPatterns: options.antiPatterns ?? [],
|
|
1060
1293
|
successChecks: options.successChecks ?? DEFAULT_SUCCESS_CHECKS,
|
|
@@ -1069,7 +1302,8 @@ function buildScaffoldPack(appNode, options = {}) {
|
|
|
1069
1302
|
routing: appNode.routing,
|
|
1070
1303
|
features: appNode.features,
|
|
1071
1304
|
navigation: options.navigation,
|
|
1072
|
-
routes
|
|
1305
|
+
routes,
|
|
1306
|
+
...options.themeDecorators && options.themeDecorators.length > 0 ? { themeDecorators: options.themeDecorators } : {}
|
|
1073
1307
|
},
|
|
1074
1308
|
renderedMarkdown: ""
|
|
1075
1309
|
};
|
|
@@ -1098,15 +1332,17 @@ function buildSectionPack(appNode, input, options = {}) {
|
|
|
1098
1332
|
"Use the declared section routes as the source of truth for this slice of the app.",
|
|
1099
1333
|
"Keep the section shell consistent unless the task explicitly changes the shell contract."
|
|
1100
1334
|
],
|
|
1101
|
-
allowedVocabulary: [
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1335
|
+
allowedVocabulary: [
|
|
1336
|
+
.../* @__PURE__ */ new Set([
|
|
1337
|
+
input.id,
|
|
1338
|
+
input.role,
|
|
1339
|
+
input.shell,
|
|
1340
|
+
appNode.theme.id,
|
|
1341
|
+
appNode.theme.mode,
|
|
1342
|
+
...input.features,
|
|
1343
|
+
...scopePatternIds
|
|
1344
|
+
])
|
|
1345
|
+
],
|
|
1110
1346
|
examples: options.examples ?? [],
|
|
1111
1347
|
antiPatterns: options.antiPatterns ?? [],
|
|
1112
1348
|
successChecks: options.successChecks ?? DEFAULT_SECTION_SUCCESS_CHECKS,
|
|
@@ -1122,7 +1358,10 @@ function buildSectionPack(appNode, input, options = {}) {
|
|
|
1122
1358
|
mode: appNode.theme.mode,
|
|
1123
1359
|
shape: appNode.theme.shape
|
|
1124
1360
|
},
|
|
1125
|
-
routes
|
|
1361
|
+
routes,
|
|
1362
|
+
...input.navigationItems && input.navigationItems.length > 0 ? { navigationItems: input.navigationItems } : {},
|
|
1363
|
+
...input.directives && input.directives.length > 0 ? { directives: input.directives } : {},
|
|
1364
|
+
...options.themeDecorators && options.themeDecorators.length > 0 ? { themeDecorators: options.themeDecorators } : {}
|
|
1126
1365
|
},
|
|
1127
1366
|
renderedMarkdown: ""
|
|
1128
1367
|
};
|
|
@@ -1155,16 +1394,20 @@ function buildPagePack(appNode, input, options = {}) {
|
|
|
1155
1394
|
"Keep the compiled route and shell contract stable for this page.",
|
|
1156
1395
|
"Treat the listed page patterns as the primary structure for this route."
|
|
1157
1396
|
],
|
|
1158
|
-
allowedVocabulary: [
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1397
|
+
allowedVocabulary: [
|
|
1398
|
+
...new Set(
|
|
1399
|
+
[
|
|
1400
|
+
input.pageId,
|
|
1401
|
+
input.shell,
|
|
1402
|
+
input.sectionId ?? "",
|
|
1403
|
+
input.sectionRole ?? "",
|
|
1404
|
+
appNode.theme.id,
|
|
1405
|
+
appNode.theme.mode,
|
|
1406
|
+
...input.features,
|
|
1407
|
+
...patterns.flatMap((pattern) => [pattern.id, pattern.alias, pattern.layout])
|
|
1408
|
+
].filter(Boolean)
|
|
1409
|
+
)
|
|
1410
|
+
],
|
|
1168
1411
|
examples: options.examples ?? [],
|
|
1169
1412
|
antiPatterns: options.antiPatterns ?? [],
|
|
1170
1413
|
successChecks: options.successChecks ?? DEFAULT_PAGE_SUCCESS_CHECKS,
|
|
@@ -1183,7 +1426,8 @@ function buildPagePack(appNode, input, options = {}) {
|
|
|
1183
1426
|
shape: appNode.theme.shape
|
|
1184
1427
|
},
|
|
1185
1428
|
wiringSignals: pageNode.wiring?.signals.map((signal) => signal.name) ?? [],
|
|
1186
|
-
patterns
|
|
1429
|
+
patterns,
|
|
1430
|
+
...input.directives && input.directives.length > 0 ? { directives: input.directives } : {}
|
|
1187
1431
|
},
|
|
1188
1432
|
renderedMarkdown: ""
|
|
1189
1433
|
};
|
|
@@ -1221,14 +1465,16 @@ function buildMutationPack(appNode, options) {
|
|
|
1221
1465
|
"Treat the compiled topology as the source of truth until the essence changes.",
|
|
1222
1466
|
"Refresh Decantr context after structural mutations so downstream tasks read current packs."
|
|
1223
1467
|
],
|
|
1224
|
-
allowedVocabulary: [
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1468
|
+
allowedVocabulary: [
|
|
1469
|
+
.../* @__PURE__ */ new Set([
|
|
1470
|
+
options.mutationType,
|
|
1471
|
+
appNode.shell.config.type,
|
|
1472
|
+
appNode.theme.id,
|
|
1473
|
+
appNode.theme.mode,
|
|
1474
|
+
...appNode.features,
|
|
1475
|
+
...scopePatternIds
|
|
1476
|
+
])
|
|
1477
|
+
],
|
|
1232
1478
|
examples: options.examples ?? [],
|
|
1233
1479
|
antiPatterns: options.antiPatterns ?? [],
|
|
1234
1480
|
successChecks: options.successChecks ?? DEFAULT_MUTATION_SUCCESS_CHECKS[options.mutationType],
|
|
@@ -1286,15 +1532,17 @@ function buildReviewPack(appNode, options = {}) {
|
|
|
1286
1532
|
"Read the compiled scaffold and route packs before reviewing code.",
|
|
1287
1533
|
"Use concrete evidence from the workspace instead of purely stylistic intuition."
|
|
1288
1534
|
],
|
|
1289
|
-
allowedVocabulary: [
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1535
|
+
allowedVocabulary: [
|
|
1536
|
+
.../* @__PURE__ */ new Set([
|
|
1537
|
+
reviewType,
|
|
1538
|
+
appNode.shell.config.type,
|
|
1539
|
+
appNode.theme.id,
|
|
1540
|
+
appNode.theme.mode,
|
|
1541
|
+
...appNode.features,
|
|
1542
|
+
...scopePatternIds,
|
|
1543
|
+
...focusAreas
|
|
1544
|
+
])
|
|
1545
|
+
],
|
|
1298
1546
|
examples: options.examples ?? [],
|
|
1299
1547
|
antiPatterns: options.antiPatterns ?? DEFAULT_REVIEW_ANTI_PATTERNS,
|
|
1300
1548
|
successChecks: options.successChecks ?? DEFAULT_REVIEW_SUCCESS_CHECKS,
|
|
@@ -1331,32 +1579,63 @@ async function compileExecutionPackBundle(essence, options = {}) {
|
|
|
1331
1579
|
overridePaths: options.overridePaths,
|
|
1332
1580
|
resolver: options.resolver
|
|
1333
1581
|
});
|
|
1582
|
+
const navMeta = effectiveEssence.meta.navigation;
|
|
1583
|
+
const commandPaletteValue = typeof navMeta?.command_palette === "object" && navMeta.command_palette !== null ? navMeta.command_palette : Boolean(navMeta?.command_palette);
|
|
1584
|
+
const themeDecorators = (() => {
|
|
1585
|
+
const defs = pipeline.registryTheme?.decorator_definitions;
|
|
1586
|
+
if (!defs) return void 0;
|
|
1587
|
+
const entries = [];
|
|
1588
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
1589
|
+
const intent = def.intent || def.description || "";
|
|
1590
|
+
const applyTo = (def.usage || []).join(", ");
|
|
1591
|
+
if (!intent && !applyTo) continue;
|
|
1592
|
+
entries.push({ class: name, intent, applyTo });
|
|
1593
|
+
}
|
|
1594
|
+
return entries.length > 0 ? entries : void 0;
|
|
1595
|
+
})();
|
|
1334
1596
|
const scaffold = buildScaffoldPack(pipeline.ir, {
|
|
1335
1597
|
target: sharedTarget,
|
|
1336
1598
|
navigation: {
|
|
1337
|
-
commandPalette:
|
|
1338
|
-
hotkeys: Array.isArray(
|
|
1339
|
-
hotkey
|
|
1340
|
-
|
|
1599
|
+
commandPalette: commandPaletteValue,
|
|
1600
|
+
hotkeys: Array.isArray(navMeta?.hotkeys) ? navMeta.hotkeys.filter(
|
|
1601
|
+
(hotkey) => Boolean(
|
|
1602
|
+
hotkey && typeof hotkey.key === "string" && typeof hotkey.label === "string"
|
|
1603
|
+
)
|
|
1604
|
+
).map((hotkey) => ({
|
|
1341
1605
|
key: hotkey.key,
|
|
1342
1606
|
route: hotkey.route,
|
|
1343
|
-
label: hotkey.label
|
|
1344
|
-
|
|
1345
|
-
|
|
1607
|
+
label: hotkey.label,
|
|
1608
|
+
...hotkey.semantics ? { semantics: hotkey.semantics } : {}
|
|
1609
|
+
})) : [],
|
|
1610
|
+
...navMeta?.hotkey_semantics ? { hotkeySemantics: navMeta.hotkey_semantics } : {}
|
|
1611
|
+
},
|
|
1612
|
+
...themeDecorators ? { themeDecorators } : {}
|
|
1346
1613
|
});
|
|
1347
1614
|
const review = buildReviewPack(pipeline.ir, {
|
|
1348
1615
|
target: sharedTarget
|
|
1349
1616
|
});
|
|
1350
|
-
const sections = listPackSections(effectiveEssence).map(
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1617
|
+
const sections = listPackSections(effectiveEssence).map(
|
|
1618
|
+
(section) => buildSectionPack(pipeline.ir, section, {
|
|
1619
|
+
target: sharedTarget
|
|
1620
|
+
// themeDecorators intentionally NOT passed to section packs as of 1.7.22.
|
|
1621
|
+
// Section packs render a one-line pointer to scaffold-pack instead of
|
|
1622
|
+
// duplicating the full decorator table — F2 Phase 1 reports flagged
|
|
1623
|
+
// 7-10× duplication across context files. The decorators are still
|
|
1624
|
+
// available on each section pack as section-pack.theme.id allows the
|
|
1625
|
+
// renderer to compose the pointer with the correct theme name.
|
|
1626
|
+
})
|
|
1627
|
+
);
|
|
1628
|
+
const pages = listPackPages(effectiveEssence).map(
|
|
1629
|
+
(page) => buildPagePack(pipeline.ir, page, {
|
|
1630
|
+
target: sharedTarget
|
|
1631
|
+
})
|
|
1632
|
+
);
|
|
1633
|
+
const mutations = ["add-page", "modify"].map(
|
|
1634
|
+
(mutationType) => buildMutationPack(pipeline.ir, {
|
|
1635
|
+
mutationType,
|
|
1636
|
+
target: sharedTarget
|
|
1637
|
+
})
|
|
1638
|
+
);
|
|
1360
1639
|
const manifest = {
|
|
1361
1640
|
$schema: PACK_MANIFEST_SCHEMA_URL,
|
|
1362
1641
|
version: "1.0.0",
|