@decantr/cli 1.7.4 → 1.7.6
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/README.md +84 -0
- package/dist/bin.js +3 -2
- package/dist/{chunk-XKXUVNUQ.js → chunk-H4H3IQJK.js} +561 -122
- package/dist/{chunk-Y5C2O7Z7.js → chunk-KAEQTVAM.js} +1249 -360
- package/dist/chunk-KUDAVJOR.js +46 -0
- package/dist/{heal-VYEGIUAS.js → heal-MURR3RVQ.js} +7 -3
- package/dist/index.js +3 -2
- package/dist/{upgrade-GTUGOUSD.js → upgrade-XNUAON3G.js} +9 -13
- package/package.json +12 -6
- package/src/templates/DECANTR.md.template +31 -3
- package/src/templates/essence-summary.md.template +1 -2
- package/src/templates/task-add-page.md.template +3 -3
- package/src/templates/task-modify.md.template +7 -7
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } fr
|
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { computeSpatialTokens } from "@decantr/essence-spec";
|
|
6
|
+
import { compileExecutionPackBundle } from "@decantr/core";
|
|
6
7
|
|
|
7
8
|
// src/treatments.ts
|
|
8
9
|
function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName) {
|
|
@@ -49,7 +50,7 @@ ${themeBody}
|
|
|
49
50
|
["display", "inline-flex"],
|
|
50
51
|
["align-items", "center"],
|
|
51
52
|
["gap", "0.5em"],
|
|
52
|
-
["padding", "var(--d-interactive-py) var(--d-interactive-px)"],
|
|
53
|
+
["padding", "calc(var(--d-interactive-py) * var(--d-density-scale, 1)) var(--d-interactive-px)"],
|
|
53
54
|
["border", "1px solid var(--d-border)"],
|
|
54
55
|
["border-radius", "var(--d-radius)"],
|
|
55
56
|
["background", "transparent"],
|
|
@@ -98,7 +99,7 @@ ${themeBody}
|
|
|
98
99
|
["border", "1px solid var(--d-border)"],
|
|
99
100
|
["border-radius", "var(--d-radius)"],
|
|
100
101
|
["box-shadow", "var(--d-shadow)"],
|
|
101
|
-
["padding", "var(--d-surface-p)"]
|
|
102
|
+
["padding", "calc(var(--d-surface-p) * var(--d-density-scale, 1))"]
|
|
102
103
|
]);
|
|
103
104
|
emitRule('.d-surface[data-elevation="raised"]', [
|
|
104
105
|
["background", "var(--d-surface-raised)"],
|
|
@@ -124,7 +125,7 @@ ${themeBody}
|
|
|
124
125
|
["font-size", "0.875rem"]
|
|
125
126
|
]);
|
|
126
127
|
emitRule(".d-data-header", [
|
|
127
|
-
["padding", "var(--d-data-py) var(--d-content-gap)"],
|
|
128
|
+
["padding", "calc(var(--d-data-py) * var(--d-density-scale, 1)) var(--d-content-gap)"],
|
|
128
129
|
["font-weight", "500"],
|
|
129
130
|
["color", "var(--d-text-muted)"],
|
|
130
131
|
["border-bottom", "1px solid var(--d-border)"],
|
|
@@ -140,13 +141,13 @@ ${themeBody}
|
|
|
140
141
|
["background", "var(--d-surface)"]
|
|
141
142
|
]);
|
|
142
143
|
emitRule(".d-data-cell", [
|
|
143
|
-
["padding", "var(--d-data-py) var(--d-content-gap)"],
|
|
144
|
+
["padding", "calc(var(--d-data-py) * var(--d-density-scale, 1)) var(--d-content-gap)"],
|
|
144
145
|
["vertical-align", "middle"]
|
|
145
146
|
]);
|
|
146
147
|
emitRule(".d-control", [
|
|
147
148
|
["background", "var(--d-surface)"],
|
|
148
149
|
["color", "var(--d-text)"],
|
|
149
|
-
["padding", "var(--d-control-py) 0.75rem"],
|
|
150
|
+
["padding", "calc(var(--d-control-py) * var(--d-density-scale, 1)) 0.75rem"],
|
|
150
151
|
["border-radius", "var(--d-radius)"],
|
|
151
152
|
["border", "1px solid var(--d-border)"],
|
|
152
153
|
["width", "100%"],
|
|
@@ -170,16 +171,21 @@ ${themeBody}
|
|
|
170
171
|
["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-error) 15%, transparent)"]
|
|
171
172
|
]);
|
|
172
173
|
emitRule(".d-section", [
|
|
173
|
-
["
|
|
174
|
+
["--d-density-scale", "1"],
|
|
175
|
+
["padding", "calc(var(--d-section-py) * var(--d-density-scale)) 0"]
|
|
174
176
|
]);
|
|
177
|
+
lines.push('.d-section[data-density="compact"] {');
|
|
178
|
+
lines.push(" --d-density-scale: 0.65;");
|
|
179
|
+
lines.push("}");
|
|
180
|
+
lines.push("");
|
|
181
|
+
lines.push('.d-section[data-density="spacious"] {');
|
|
182
|
+
lines.push(" --d-density-scale: 1.4;");
|
|
183
|
+
lines.push("}");
|
|
184
|
+
lines.push("");
|
|
175
185
|
lines.push(".d-section + .d-section {");
|
|
176
186
|
lines.push(" border-top: 1px solid transparent;");
|
|
177
187
|
lines.push(" border-image: linear-gradient(to right, transparent, var(--d-border), transparent) 1;");
|
|
178
|
-
lines.push(" margin-top: var(--d-gap-
|
|
179
|
-
lines.push("}");
|
|
180
|
-
lines.push("");
|
|
181
|
-
lines.push('.d-section[data-density="compact"] {');
|
|
182
|
-
lines.push(" padding: calc(var(--d-section-py) * 0.5) 0;");
|
|
188
|
+
lines.push(" margin-top: calc(var(--d-section-gap) * var(--d-density-scale, 1));");
|
|
183
189
|
lines.push("}");
|
|
184
190
|
lines.push("");
|
|
185
191
|
emitRule(".d-annotation", [
|
|
@@ -189,6 +195,7 @@ ${themeBody}
|
|
|
189
195
|
["font-size", "0.75rem"],
|
|
190
196
|
["font-weight", "500"],
|
|
191
197
|
["padding", "0.125rem 0.5rem"],
|
|
198
|
+
["margin-top", "calc(var(--d-annotation-mt) * var(--d-density-scale, 1))"],
|
|
192
199
|
["border-radius", "var(--d-radius-full)"],
|
|
193
200
|
["background", "var(--d-surface)"],
|
|
194
201
|
["color", "var(--d-text-muted)"],
|
|
@@ -216,7 +223,13 @@ ${themeBody}
|
|
|
216
223
|
["text-transform", "uppercase"],
|
|
217
224
|
["letter-spacing", "0.08em"],
|
|
218
225
|
["color", "var(--d-text-muted)"],
|
|
219
|
-
["font-family", "var(--d-font-mono, ui-monospace, monospace)"]
|
|
226
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
227
|
+
["display", "block"],
|
|
228
|
+
["margin-bottom", "calc(var(--d-label-mb) * var(--d-density-scale, 1))"]
|
|
229
|
+
]);
|
|
230
|
+
emitRule(".d-label[data-anchor]", [
|
|
231
|
+
["padding-left", "var(--d-label-px)"],
|
|
232
|
+
["border-left", "2px solid var(--d-accent)"]
|
|
220
233
|
]);
|
|
221
234
|
if (themeOverrideRules.length > 0) {
|
|
222
235
|
lines.push("/* \u2500\u2500 Theme-scoped Treatment Overrides \u2500\u2500 */");
|
|
@@ -275,7 +288,62 @@ function generatePersonalityCSS(personality, themeData) {
|
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
// src/scaffold.ts
|
|
291
|
+
import { API_CONTENT_TYPES } from "@decantr/registry";
|
|
278
292
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
293
|
+
function getPlatformMeta(target) {
|
|
294
|
+
const normalized = (target || "react").toLowerCase();
|
|
295
|
+
const routing = normalized === "nextjs" ? "pathname" : "hash";
|
|
296
|
+
return {
|
|
297
|
+
type: "spa",
|
|
298
|
+
routing
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function getLegacyBlueprintId(meta) {
|
|
302
|
+
return meta.blueprint;
|
|
303
|
+
}
|
|
304
|
+
function isRecord(value) {
|
|
305
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
306
|
+
}
|
|
307
|
+
function getStringArray(value) {
|
|
308
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
309
|
+
}
|
|
310
|
+
function toPatternReferenceObject(ref) {
|
|
311
|
+
return typeof ref === "string" ? { pattern: ref } : ref;
|
|
312
|
+
}
|
|
313
|
+
function collectPatternIdsFromValue(value, ids) {
|
|
314
|
+
if (typeof value === "string") {
|
|
315
|
+
ids.add(value);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!value || typeof value !== "object") return;
|
|
319
|
+
if ("pattern" in value && typeof value.pattern === "string") {
|
|
320
|
+
ids.add(value.pattern);
|
|
321
|
+
}
|
|
322
|
+
if ("cols" in value && Array.isArray(value.cols)) {
|
|
323
|
+
for (const col of value.cols) collectPatternIdsFromValue(col, ids);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function collectPatternIdsFromItems(items) {
|
|
327
|
+
const ids = /* @__PURE__ */ new Set();
|
|
328
|
+
for (const item of items) collectPatternIdsFromValue(item, ids);
|
|
329
|
+
return [...ids];
|
|
330
|
+
}
|
|
331
|
+
function mapRegistryArchetypeToArchetypeData(archetype) {
|
|
332
|
+
return {
|
|
333
|
+
id: archetype.id,
|
|
334
|
+
name: archetype.name,
|
|
335
|
+
role: archetype.role,
|
|
336
|
+
description: archetype.description,
|
|
337
|
+
pages: archetype.pages?.map((page) => ({
|
|
338
|
+
id: page.id,
|
|
339
|
+
shell: page.shell,
|
|
340
|
+
default_layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
341
|
+
patterns: page.patterns?.map(toPatternReferenceObject)
|
|
342
|
+
})),
|
|
343
|
+
features: archetype.features,
|
|
344
|
+
seo_hints: archetype.seo_hints
|
|
345
|
+
};
|
|
346
|
+
}
|
|
279
347
|
function composeArchetypes(composeEntries, archetypeResults) {
|
|
280
348
|
if (composeEntries.length === 0) {
|
|
281
349
|
return {
|
|
@@ -298,7 +366,7 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
298
366
|
for (const page of data.pages) {
|
|
299
367
|
allPages.push({
|
|
300
368
|
id: page.id,
|
|
301
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
369
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
302
370
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
303
371
|
});
|
|
304
372
|
}
|
|
@@ -307,7 +375,7 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
307
375
|
for (const page of data.pages) {
|
|
308
376
|
allPages.push({
|
|
309
377
|
id: `${prefix}-${page.id}`,
|
|
310
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
378
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
311
379
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
312
380
|
});
|
|
313
381
|
}
|
|
@@ -359,7 +427,7 @@ function composeSections(composeEntries, archetypeResults, overrides) {
|
|
|
359
427
|
const overriddenPage = overrides?.pages?.[page.id];
|
|
360
428
|
pages.push({
|
|
361
429
|
id: page.id,
|
|
362
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
430
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
363
431
|
...overriddenPage
|
|
364
432
|
});
|
|
365
433
|
}
|
|
@@ -521,6 +589,24 @@ function generateTopologySection(data, personality) {
|
|
|
521
589
|
return lines.join("\n");
|
|
522
590
|
}
|
|
523
591
|
var CLI_VERSION = "1.0.0";
|
|
592
|
+
function mapRegistryThemeToThemeData(theme) {
|
|
593
|
+
return {
|
|
594
|
+
seed: theme.seed,
|
|
595
|
+
palette: theme.palette,
|
|
596
|
+
tokens: theme.tokens,
|
|
597
|
+
cvd_support: theme.cvd_support,
|
|
598
|
+
typography: theme.typography,
|
|
599
|
+
motion: theme.motion,
|
|
600
|
+
decorators: theme.decorators,
|
|
601
|
+
treatments: theme.treatments,
|
|
602
|
+
spatial: theme.spatial,
|
|
603
|
+
radius: theme.radius,
|
|
604
|
+
shell: theme.shell,
|
|
605
|
+
effects: theme.effects,
|
|
606
|
+
compositions: theme.compositions,
|
|
607
|
+
pattern_preferences: theme.pattern_preferences
|
|
608
|
+
};
|
|
609
|
+
}
|
|
524
610
|
function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
525
611
|
if (!themeData) {
|
|
526
612
|
const spatialLines2 = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
|
|
@@ -671,6 +757,26 @@ body {
|
|
|
671
757
|
min-height: 100dvh;
|
|
672
758
|
}
|
|
673
759
|
|
|
760
|
+
.skip-link {
|
|
761
|
+
position: absolute;
|
|
762
|
+
top: 0.75rem;
|
|
763
|
+
left: 0.75rem;
|
|
764
|
+
z-index: 100;
|
|
765
|
+
padding: 0.5rem 0.75rem;
|
|
766
|
+
border-radius: var(--d-radius);
|
|
767
|
+
background: var(--d-surface-raised);
|
|
768
|
+
color: var(--d-text);
|
|
769
|
+
text-decoration: none;
|
|
770
|
+
border: 1px solid var(--d-border);
|
|
771
|
+
transform: translateY(-150%);
|
|
772
|
+
transition: transform 0.15s ease;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.skip-link:focus,
|
|
776
|
+
.skip-link:focus-visible {
|
|
777
|
+
transform: translateY(0);
|
|
778
|
+
}
|
|
779
|
+
|
|
674
780
|
img, picture, video, canvas, svg {
|
|
675
781
|
display: block;
|
|
676
782
|
max-width: 100%;
|
|
@@ -698,6 +804,15 @@ input, button, textarea, select {
|
|
|
698
804
|
border-width: 0;
|
|
699
805
|
}
|
|
700
806
|
|
|
807
|
+
@media (prefers-reduced-motion: reduce) {
|
|
808
|
+
*, *::before, *::after {
|
|
809
|
+
animation-duration: 0.01ms !important;
|
|
810
|
+
animation-iteration-count: 1 !important;
|
|
811
|
+
transition-duration: 0.01ms !important;
|
|
812
|
+
scroll-behavior: auto !important;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
701
816
|
}
|
|
702
817
|
`;
|
|
703
818
|
}
|
|
@@ -861,10 +976,7 @@ function buildEssenceV3(options, archetypeData, themeHints) {
|
|
|
861
976
|
const meta = {
|
|
862
977
|
archetype: options.archetype || "custom",
|
|
863
978
|
target: options.target,
|
|
864
|
-
platform:
|
|
865
|
-
type: "spa",
|
|
866
|
-
routing: "hash"
|
|
867
|
-
},
|
|
979
|
+
platform: getPlatformMeta(options.target),
|
|
868
980
|
guard: guardModeMap[options.guard] || guardModeMap.guided
|
|
869
981
|
};
|
|
870
982
|
return {
|
|
@@ -894,6 +1006,14 @@ import './styles/treatments.css'; // Treatments + theme decorators
|
|
|
894
1006
|
import './styles/global.css'; // Resets
|
|
895
1007
|
\`\`\`
|
|
896
1008
|
|
|
1009
|
+
### Runtime Rules
|
|
1010
|
+
|
|
1011
|
+
- Use the real \`@decantr/css\` runtime for atoms. If \`package.json\` does not already depend on \`@decantr/css\`, add it before building.
|
|
1012
|
+
- Do **not** create local atom-runtime substitutes such as \`src/lib/css.js\`, \`src/lib/css.ts\`, or hand-written \`src/styles/atoms.css\` files unless the task explicitly asks for a fallback runtime.
|
|
1013
|
+
- Keep atoms in \`css(...)\`, treatments as semantic classes, and theme decorators as additive classes. Do not blur those roles together.
|
|
1014
|
+
- Use \`d-control\` as the default semantic treatment for inputs, selects, and textareas. Theme decorators such as \`carbon-input\` are additive and should only layer on when the section or theme contract explicitly calls for them.
|
|
1015
|
+
- Use loading decorators such as \`carbon-skeleton\` as optional enhancement on top of a structurally correct loading state \u2014 they do not replace the need for a real loading/skeleton branch.
|
|
1016
|
+
|
|
897
1017
|
### Visual Treatments
|
|
898
1018
|
|
|
899
1019
|
Six base treatment classes provide semantic styling. Combine with atoms for layout:
|
|
@@ -1032,16 +1152,26 @@ css('hover:_opacity80')
|
|
|
1032
1152
|
| Atom | CSS |
|
|
1033
1153
|
|------|-----|
|
|
1034
1154
|
| \`_bgprimary\` | \`background:var(--d-primary)\` |
|
|
1155
|
+
| \`_bgaccent\` | \`background:var(--d-accent)\` |
|
|
1156
|
+
| \`_bgsecondary\` | \`background:var(--d-secondary)\` |
|
|
1035
1157
|
| \`_bgsurface\` | \`background:var(--d-surface)\` |
|
|
1036
1158
|
| \`_bgsurface0\`-\`_bgsurface2\` | surface elevation layers |
|
|
1037
1159
|
| \`_bgmuted\` | \`background:var(--d-muted)\` |
|
|
1038
1160
|
| \`_bgbg\` | \`background:var(--d-bg)\` |
|
|
1161
|
+
| \`_bgtransparent\` | \`background:transparent\` |
|
|
1039
1162
|
| \`_bgsuccess\`, \`_bgerror\`, \`_bgwarning\`, \`_bginfo\` | status backgrounds |
|
|
1040
1163
|
| \`_fgprimary\` | \`color:var(--d-primary)\` |
|
|
1164
|
+
| \`_fgaccent\` | \`color:var(--d-accent)\` |
|
|
1165
|
+
| \`_fgsecondary\` | \`color:var(--d-secondary)\` |
|
|
1041
1166
|
| \`_fgtext\` | \`color:var(--d-text)\` |
|
|
1042
1167
|
| \`_fgmuted\` | \`color:var(--d-text-muted)\` |
|
|
1168
|
+
| \`_fgwhite\`, \`_fgblack\`, \`_fginherit\` | absolute/inherited text colors |
|
|
1043
1169
|
| \`_fgsuccess\`, \`_fgerror\`, \`_fgwarning\`, \`_fginfo\` | status text |
|
|
1170
|
+
| \`_bcprimary\` | \`border-color:var(--d-primary)\` |
|
|
1171
|
+
| \`_bcaccent\` | \`border-color:var(--d-accent)\` |
|
|
1044
1172
|
| \`_bcborder\` | \`border-color:var(--d-border)\` |
|
|
1173
|
+
| \`_bcmuted\` | \`border-color:var(--d-muted)\` |
|
|
1174
|
+
| \`_bctransparent\` | \`border-color:transparent\` |
|
|
1045
1175
|
|
|
1046
1176
|
#### Overflow & Whitespace
|
|
1047
1177
|
| Atom | CSS |
|
|
@@ -1138,9 +1268,16 @@ If the essence defines hotkeys or command_palette, implement as keyboard event l
|
|
|
1138
1268
|
Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing strategy:
|
|
1139
1269
|
- \`"hash"\` \u2192 use \`HashRouter\` (e.g., for static hosting, GitHub Pages)
|
|
1140
1270
|
- \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
|
|
1271
|
+
- \`"pathname"\` \u2192 use pathname-based routing (e.g., Next.js App Router or React apps using \`BrowserRouter\`)
|
|
1141
1272
|
|
|
1142
1273
|
Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.
|
|
1143
1274
|
|
|
1275
|
+
### SEO Expectations by Platform
|
|
1276
|
+
|
|
1277
|
+
- For hash-routed SPA scaffolds, focus SEO work on the root document: document title, description, Open Graph/Twitter meta, and any root-level JSON-LD that the contract calls for.
|
|
1278
|
+
- Do **not** invent SSR-only per-route metadata systems for a clearly hash-routed scaffold.
|
|
1279
|
+
- For history/SSR-style projects, per-route metadata can be richer, but it still needs to follow the declared route contract instead of introducing off-contract marketing pages.
|
|
1280
|
+
|
|
1144
1281
|
### Layout Rules
|
|
1145
1282
|
|
|
1146
1283
|
1. **Never nest d-surface inside d-surface.** Inner sections use plain containers with padding atoms.
|
|
@@ -1149,6 +1286,13 @@ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and l
|
|
|
1149
1286
|
4. **d-section spacing is self-contained.** Each d-section owns its padding. The d-section + d-section rule adds a separator. Do NOT add extra margin between adjacent sections.
|
|
1150
1287
|
5. **Responsive nav rules.** Hamburger menus appear ONLY below the shell collapse breakpoint. Full nav shows above it.
|
|
1151
1288
|
|
|
1289
|
+
### Accessibility Defaults
|
|
1290
|
+
|
|
1291
|
+
- If \`dna.accessibility.skip_nav = true\`, add a visible-on-focus skip link such as \`<a href="#main-content" className="skip-link">Skip to content</a>\`.
|
|
1292
|
+
- Pair that skip link with a real main landmark target such as \`<main id="main-content">\`.
|
|
1293
|
+
- Keep keyboard focus visible with \`:focus-visible\` treatments on custom interactive surfaces, not just browser defaults.
|
|
1294
|
+
- Implement shell-level accessibility and routing behaviors as reusable structure or shared helpers, not one-off inline patches. Compact header sizing, responsive sidebar collapse, and skip-nav targets should be consistent across the shell, not re-solved page by page.
|
|
1295
|
+
|
|
1152
1296
|
### Motion Philosophy
|
|
1153
1297
|
|
|
1154
1298
|
Every interaction should feel responsive and polished. Apply motion by default, not as an afterthought:
|
|
@@ -1160,6 +1304,8 @@ Every interaction should feel responsive and polished. Apply motion by default,
|
|
|
1160
1304
|
- **Scroll reveals:** Sections below the fold should fade-in on scroll intersection (IntersectionObserver, once)
|
|
1161
1305
|
- **Reduced motion:** Wrap all animations in \`prefers-reduced-motion\` media query \u2014 skip animation, keep state changes instant
|
|
1162
1306
|
|
|
1307
|
+
Never leave this to implication when \`dna.motion.reduce_motion = true\`. The scaffold should include a reviewed reduced-motion path in project CSS, even when the app initially runs on mock data.
|
|
1308
|
+
|
|
1163
1309
|
### Interactivity Philosophy
|
|
1164
1310
|
|
|
1165
1311
|
Build for wow factor. When a pattern describes a canvas, graph, map, or spatial visualization, implement it as a **fully interactive surface**, not a static illustration:
|
|
@@ -1306,9 +1452,8 @@ function generateTaskContextV3(templateName, essence) {
|
|
|
1306
1452
|
const contentGap = essence.dna.spacing?.content_gap || "_gap4";
|
|
1307
1453
|
const vars = {
|
|
1308
1454
|
TARGET: essence.meta.target || "react",
|
|
1309
|
-
|
|
1455
|
+
THEME_ID: essence.dna.theme.id || "",
|
|
1310
1456
|
THEME_MODE: essence.dna.theme.mode,
|
|
1311
|
-
THEME_RECIPE: essence.dna.theme.id || essence.dna.theme.style || "",
|
|
1312
1457
|
DEFAULT_SHELL: defaultShell,
|
|
1313
1458
|
GUARD_MODE: essence.meta.guard.mode,
|
|
1314
1459
|
LAYOUT: layout,
|
|
@@ -1318,6 +1463,186 @@ function generateTaskContextV3(templateName, essence) {
|
|
|
1318
1463
|
};
|
|
1319
1464
|
return renderTemplate(template, vars);
|
|
1320
1465
|
}
|
|
1466
|
+
function renderPackReferenceList(title, entries, fallback, summaryPath = ".decantr/context/pack-manifest.json") {
|
|
1467
|
+
if (entries.length === 0) {
|
|
1468
|
+
return `### ${title}
|
|
1469
|
+
|
|
1470
|
+
- ${fallback}`;
|
|
1471
|
+
}
|
|
1472
|
+
if (entries.length <= 6) {
|
|
1473
|
+
return `### ${title}
|
|
1474
|
+
|
|
1475
|
+
${entries.map((entry) => `- ${entry}`).join("\n")}`;
|
|
1476
|
+
}
|
|
1477
|
+
return `### ${title}
|
|
1478
|
+
|
|
1479
|
+
- ${entries.length} compiled references available. Use \`${summaryPath}\` to resolve the exact files for this scope.`;
|
|
1480
|
+
}
|
|
1481
|
+
function generateScaffoldTaskContext(essence, scaffoldPack, manifest) {
|
|
1482
|
+
if (!scaffoldPack) {
|
|
1483
|
+
return generateTaskContextV3("task-scaffold.md.template", essence);
|
|
1484
|
+
}
|
|
1485
|
+
const themeShape = scaffoldPack.data.theme.shape || "default";
|
|
1486
|
+
const features = scaffoldPack.data.features.length > 0 ? scaffoldPack.data.features.join(", ") : "none";
|
|
1487
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1488
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1489
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1490
|
+
}).join("\n") : "- No routes declared";
|
|
1491
|
+
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
1492
|
+
const tokenStrategy = scaffoldPack.tokenBudget.strategy.map((item) => `- ${item}`).join("\n");
|
|
1493
|
+
const sectionRefs = manifest?.sections.map(
|
|
1494
|
+
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1495
|
+
) ?? [];
|
|
1496
|
+
const pageRefs = manifest?.pages.map(
|
|
1497
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1498
|
+
) ?? [];
|
|
1499
|
+
return `# Task Context: Scaffolding
|
|
1500
|
+
|
|
1501
|
+
**Enforcement Tier: Creative** \u2014 Guard rules are advisory during initial scaffolding.
|
|
1502
|
+
|
|
1503
|
+
## Primary Compiled Contract
|
|
1504
|
+
|
|
1505
|
+
- Start with \`.decantr/context/scaffold-pack.md\` for the compact route, shell, and theme contract.
|
|
1506
|
+
- Use \`.decantr/context/scaffold.md\` only as secondary detail when the compiled pack is not enough.
|
|
1507
|
+
- Read the route-local page packs before building each page so layout and wiring stay aligned with the compiled plan.
|
|
1508
|
+
|
|
1509
|
+
## Generate This Application
|
|
1510
|
+
|
|
1511
|
+
- Target: \`${scaffoldPack.target.adapter}\` (${scaffoldPack.target.framework || "unknown framework"})
|
|
1512
|
+
- Shell: \`${scaffoldPack.data.shell}\`
|
|
1513
|
+
- Theme: \`${scaffoldPack.data.theme.id}\` (${scaffoldPack.data.theme.mode}, ${themeShape})
|
|
1514
|
+
- Routing: \`${scaffoldPack.data.routing}\`
|
|
1515
|
+
- Features: ${features}
|
|
1516
|
+
|
|
1517
|
+
## Route Plan
|
|
1518
|
+
|
|
1519
|
+
${routePlan}
|
|
1520
|
+
|
|
1521
|
+
${renderPackReferenceList("Section Packs", sectionRefs, "No section packs were generated for this scaffold.")}
|
|
1522
|
+
|
|
1523
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1524
|
+
|
|
1525
|
+
## Success Checks
|
|
1526
|
+
|
|
1527
|
+
${successChecks}
|
|
1528
|
+
|
|
1529
|
+
## Token Budget
|
|
1530
|
+
|
|
1531
|
+
- Target: ${scaffoldPack.tokenBudget.target} tokens
|
|
1532
|
+
- Max: ${scaffoldPack.tokenBudget.max} tokens
|
|
1533
|
+
${tokenStrategy}
|
|
1534
|
+
|
|
1535
|
+
Post-scaffold enforcement mode: **${essence.meta.guard.mode.toUpperCase()}**.
|
|
1536
|
+
|
|
1537
|
+
---
|
|
1538
|
+
|
|
1539
|
+
*Task context generated from Decantr execution packs*`;
|
|
1540
|
+
}
|
|
1541
|
+
function generateAddPageTaskContext(essence, scaffoldPack, manifest) {
|
|
1542
|
+
if (!scaffoldPack) {
|
|
1543
|
+
return generateTaskContextV3("task-add-page.md.template", essence);
|
|
1544
|
+
}
|
|
1545
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1546
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1547
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1548
|
+
}).join("\n") : "- No routes declared";
|
|
1549
|
+
const sectionRefs = manifest?.sections.map(
|
|
1550
|
+
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1551
|
+
) ?? [];
|
|
1552
|
+
const pageRefs = manifest?.pages.map(
|
|
1553
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1554
|
+
) ?? [];
|
|
1555
|
+
return `# Task Context: Adding Pages
|
|
1556
|
+
|
|
1557
|
+
**Enforcement Tier: Guided**
|
|
1558
|
+
|
|
1559
|
+
## Primary Compiled Contract
|
|
1560
|
+
|
|
1561
|
+
- Start with \`.decantr/context/mutation-add-page-pack.md\` for the add-page workflow contract.
|
|
1562
|
+
- Use \`.decantr/context/scaffold-pack.md\` for the current route, shell, and theme contract.
|
|
1563
|
+
- Use \`.decantr/context/pack-manifest.json\` to choose the target section before you add a route.
|
|
1564
|
+
- After updating the essence, run \`npx @decantr/cli refresh\` so the new section/page packs exist before code generation.
|
|
1565
|
+
|
|
1566
|
+
## Current Scaffold Contract
|
|
1567
|
+
|
|
1568
|
+
- Target: \`${scaffoldPack.target.adapter}\` (${scaffoldPack.target.framework || "unknown framework"})
|
|
1569
|
+
- Shell: \`${scaffoldPack.data.shell}\`
|
|
1570
|
+
- Theme: \`${scaffoldPack.data.theme.id}\` (${scaffoldPack.data.theme.mode})
|
|
1571
|
+
- Existing routes: ${scaffoldPack.data.routes.length}
|
|
1572
|
+
|
|
1573
|
+
## Existing Routes
|
|
1574
|
+
|
|
1575
|
+
${routePlan}
|
|
1576
|
+
|
|
1577
|
+
${renderPackReferenceList("Section Packs", sectionRefs, "No section packs were generated for this scaffold.")}
|
|
1578
|
+
|
|
1579
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1580
|
+
|
|
1581
|
+
## Required Workflow
|
|
1582
|
+
|
|
1583
|
+
1. Add the new page to the essence before generating any code.
|
|
1584
|
+
2. Keep the new page inside a declared section and shell contract.
|
|
1585
|
+
3. Refresh derived files so Decantr recompiles the section and page packs.
|
|
1586
|
+
4. Read the relevant section pack and the new page pack before implementation.
|
|
1587
|
+
|
|
1588
|
+
## Guided Checks
|
|
1589
|
+
|
|
1590
|
+
- [error] Theme identity remains \`${scaffoldPack.data.theme.id}\` until the essence changes.
|
|
1591
|
+
- [error] The new page exists in the essence before code generation begins.
|
|
1592
|
+
- [error] New layouts only use registry-backed patterns.
|
|
1593
|
+
- [warn] New routes should fit the current shell and section topology instead of creating off-contract filler pages.
|
|
1594
|
+
|
|
1595
|
+
---
|
|
1596
|
+
|
|
1597
|
+
*Task context generated from Decantr execution packs*`;
|
|
1598
|
+
}
|
|
1599
|
+
function generateModifyTaskContext(essence, scaffoldPack, manifest) {
|
|
1600
|
+
if (!scaffoldPack) {
|
|
1601
|
+
return generateTaskContextV3("task-modify.md.template", essence);
|
|
1602
|
+
}
|
|
1603
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1604
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1605
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1606
|
+
}).join("\n") : "- No routes declared";
|
|
1607
|
+
const pageRefs = manifest?.pages.map(
|
|
1608
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1609
|
+
) ?? [];
|
|
1610
|
+
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
1611
|
+
return `# Task Context: Modifying Code
|
|
1612
|
+
|
|
1613
|
+
**Enforcement Tier: Strict**
|
|
1614
|
+
|
|
1615
|
+
## Primary Compiled Contract
|
|
1616
|
+
|
|
1617
|
+
- Start with \`.decantr/context/mutation-modify-pack.md\` for the strict modification workflow contract.
|
|
1618
|
+
- Start with \`decantr_get_page_context\` or the matching \`.decantr/context/page-*-pack.md\` file for the route you are editing.
|
|
1619
|
+
- Use \`decantr_get_section_context\` when you need the richer section contract behind that route.
|
|
1620
|
+
- If a change would alter route identity, shell identity, theme identity, or pattern contract, update the essence first and then refresh the packs.
|
|
1621
|
+
|
|
1622
|
+
## Current Route Topology
|
|
1623
|
+
|
|
1624
|
+
${routePlan}
|
|
1625
|
+
|
|
1626
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1627
|
+
|
|
1628
|
+
## Strict Workflow
|
|
1629
|
+
|
|
1630
|
+
1. Identify the target page and read its compiled page pack first.
|
|
1631
|
+
2. Compare the planned edit against the compiled route, shell, and pattern contract.
|
|
1632
|
+
3. If the edit changes that contract, stop and update the essence before writing code.
|
|
1633
|
+
4. Run \`npx @decantr/cli validate\` and \`npx @decantr/cli check\` after the modification.
|
|
1634
|
+
|
|
1635
|
+
## Strict Checks
|
|
1636
|
+
|
|
1637
|
+
${successChecks}
|
|
1638
|
+
- [error] The page you modify must already exist in the compiled topology.
|
|
1639
|
+
- [error] Pattern order and shell usage should stay aligned with the page pack unless the essence changes first.
|
|
1640
|
+
- [warn] Use section context only as supporting detail; the page pack is the primary contract for route-local work.
|
|
1641
|
+
|
|
1642
|
+
---
|
|
1643
|
+
|
|
1644
|
+
*Task context generated from Decantr execution packs*`;
|
|
1645
|
+
}
|
|
1321
1646
|
function generateEssenceSummaryV3(essence) {
|
|
1322
1647
|
const template = loadTemplate("essence-summary.md.template");
|
|
1323
1648
|
const blueprint = essence.blueprint;
|
|
@@ -1345,9 +1670,8 @@ ${rows.join("\n")}`;
|
|
|
1345
1670
|
BLUEPRINT: "",
|
|
1346
1671
|
PERSONALITY: (essence.dna.personality || []).join(", "),
|
|
1347
1672
|
TARGET: essence.meta.target ?? "",
|
|
1348
|
-
|
|
1673
|
+
THEME_ID: essence.dna.theme.id ?? "",
|
|
1349
1674
|
THEME_MODE: essence.dna.theme.mode,
|
|
1350
|
-
THEME_RECIPE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
|
|
1351
1675
|
SHAPE: essence.dna.theme.shape ?? "",
|
|
1352
1676
|
PAGES_TABLE: pagesTable,
|
|
1353
1677
|
FEATURES_LIST: featuresList,
|
|
@@ -1399,9 +1723,6 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1399
1723
|
}
|
|
1400
1724
|
writeFileSync(projectJsonPath, JSON.stringify(projectJsonObj, null, 2));
|
|
1401
1725
|
const contextFiles = [];
|
|
1402
|
-
const scaffoldPath = join(contextDir, "task-scaffold.md");
|
|
1403
|
-
writeFileSync(scaffoldPath, generateTaskContextV3("task-scaffold.md.template", essenceV3));
|
|
1404
|
-
contextFiles.push(scaffoldPath);
|
|
1405
1726
|
if (composedSections) {
|
|
1406
1727
|
essenceV3.version = "3.1.0";
|
|
1407
1728
|
essenceV3.blueprint = {
|
|
@@ -1438,7 +1759,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1438
1759
|
function scaffoldMinimal(projectRoot) {
|
|
1439
1760
|
const decantrDir = join(projectRoot, ".decantr");
|
|
1440
1761
|
const customDir = join(decantrDir, "custom");
|
|
1441
|
-
const contentTypes =
|
|
1762
|
+
const contentTypes = API_CONTENT_TYPES;
|
|
1442
1763
|
for (const type of contentTypes) {
|
|
1443
1764
|
mkdirSync(join(customDir, type), { recursive: true });
|
|
1444
1765
|
}
|
|
@@ -1496,10 +1817,7 @@ function scaffoldMinimal(projectRoot) {
|
|
|
1496
1817
|
meta: {
|
|
1497
1818
|
archetype: "custom",
|
|
1498
1819
|
target: "react",
|
|
1499
|
-
platform:
|
|
1500
|
-
type: "spa",
|
|
1501
|
-
routing: "hash"
|
|
1502
|
-
},
|
|
1820
|
+
platform: getPlatformMeta("react"),
|
|
1503
1821
|
guard: {
|
|
1504
1822
|
mode: "guided",
|
|
1505
1823
|
dna_enforcement: "error",
|
|
@@ -1623,6 +1941,76 @@ When available, use these tools:
|
|
|
1623
1941
|
gitignoreUpdated
|
|
1624
1942
|
};
|
|
1625
1943
|
}
|
|
1944
|
+
function writeExecutionPackArtifacts(basePathWithoutExtension, pack) {
|
|
1945
|
+
const markdownPath = `${basePathWithoutExtension}.md`;
|
|
1946
|
+
const jsonPath = `${basePathWithoutExtension}.json`;
|
|
1947
|
+
writeFileSync(markdownPath, pack.renderedMarkdown);
|
|
1948
|
+
writeFileSync(jsonPath, JSON.stringify(pack, null, 2) + "\n");
|
|
1949
|
+
return markdownPath;
|
|
1950
|
+
}
|
|
1951
|
+
function writeExecutionPackBundleArtifacts(contextDir, bundle) {
|
|
1952
|
+
mkdirSync(contextDir, { recursive: true });
|
|
1953
|
+
const outputPaths = [];
|
|
1954
|
+
const scaffoldPackPath = writeExecutionPackArtifacts(join(contextDir, "scaffold-pack"), bundle.scaffold);
|
|
1955
|
+
outputPaths.push(scaffoldPackPath);
|
|
1956
|
+
const reviewPackPath = writeExecutionPackArtifacts(join(contextDir, "review-pack"), bundle.review);
|
|
1957
|
+
outputPaths.push(reviewPackPath);
|
|
1958
|
+
for (const sectionPack of bundle.sections) {
|
|
1959
|
+
const sectionPackPath = writeExecutionPackArtifacts(
|
|
1960
|
+
join(contextDir, `section-${sectionPack.data.sectionId}-pack`),
|
|
1961
|
+
sectionPack
|
|
1962
|
+
);
|
|
1963
|
+
outputPaths.push(sectionPackPath);
|
|
1964
|
+
}
|
|
1965
|
+
for (const pagePack of bundle.pages) {
|
|
1966
|
+
const pagePackPath = writeExecutionPackArtifacts(
|
|
1967
|
+
join(contextDir, `page-${pagePack.data.pageId}-pack`),
|
|
1968
|
+
pagePack
|
|
1969
|
+
);
|
|
1970
|
+
outputPaths.push(pagePackPath);
|
|
1971
|
+
}
|
|
1972
|
+
for (const mutationPack of bundle.mutations) {
|
|
1973
|
+
const mutationPackPath = writeExecutionPackArtifacts(
|
|
1974
|
+
join(contextDir, `mutation-${mutationPack.data.mutationType}-pack`),
|
|
1975
|
+
mutationPack
|
|
1976
|
+
);
|
|
1977
|
+
outputPaths.push(mutationPackPath);
|
|
1978
|
+
}
|
|
1979
|
+
const manifestPath = join(contextDir, "pack-manifest.json");
|
|
1980
|
+
writeFileSync(manifestPath, JSON.stringify(bundle.manifest, null, 2) + "\n");
|
|
1981
|
+
outputPaths.push(manifestPath);
|
|
1982
|
+
return {
|
|
1983
|
+
paths: outputPaths,
|
|
1984
|
+
scaffoldPackPath,
|
|
1985
|
+
reviewPackPath,
|
|
1986
|
+
manifestPath
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
async function generatePackContexts(projectRoot, contextDir, essence) {
|
|
1990
|
+
const emptyResult = {
|
|
1991
|
+
paths: [],
|
|
1992
|
+
scaffoldPack: null,
|
|
1993
|
+
manifest: null
|
|
1994
|
+
};
|
|
1995
|
+
const cacheRoot = join(projectRoot, ".decantr", "cache", "@official");
|
|
1996
|
+
if (!existsSync(cacheRoot)) return emptyResult;
|
|
1997
|
+
const customRoot = join(projectRoot, ".decantr", "custom");
|
|
1998
|
+
const overridePaths = existsSync(customRoot) ? [customRoot] : void 0;
|
|
1999
|
+
try {
|
|
2000
|
+
const bundle = await compileExecutionPackBundle(essence, {
|
|
2001
|
+
contentRoot: cacheRoot,
|
|
2002
|
+
overridePaths
|
|
2003
|
+
});
|
|
2004
|
+
const writtenArtifacts = writeExecutionPackBundleArtifacts(contextDir, bundle);
|
|
2005
|
+
return {
|
|
2006
|
+
paths: writtenArtifacts.paths,
|
|
2007
|
+
scaffoldPack: bundle.scaffold,
|
|
2008
|
+
manifest: bundle.manifest
|
|
2009
|
+
};
|
|
2010
|
+
} catch {
|
|
2011
|
+
return emptyResult;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1626
2014
|
async function resolvePatternSpec(name, registry, prefetched, includeExtendedFields = true) {
|
|
1627
2015
|
if (prefetched && (prefetched.layout_hints || prefetched.visual_brief || !includeExtendedFields)) {
|
|
1628
2016
|
if (!prefetched.components || prefetched.components.length === 0) {
|
|
@@ -1638,32 +2026,7 @@ async function resolvePatternSpec(name, registry, prefetched, includeExtendedFie
|
|
|
1638
2026
|
try {
|
|
1639
2027
|
const patResult = await registry.fetchPattern(name);
|
|
1640
2028
|
if (patResult?.data) {
|
|
1641
|
-
|
|
1642
|
-
const defaultPreset = inner.default_preset || "standard";
|
|
1643
|
-
const preset = inner.presets?.[defaultPreset];
|
|
1644
|
-
let slots = preset?.layout?.slots || {};
|
|
1645
|
-
if (Object.keys(slots).length === 0) {
|
|
1646
|
-
const synthetic = generateSyntheticSlots(name, inner.description || "");
|
|
1647
|
-
if (Object.keys(synthetic).length > 0) slots = synthetic;
|
|
1648
|
-
}
|
|
1649
|
-
const spec = {
|
|
1650
|
-
description: inner.description || prefetched?.description || "",
|
|
1651
|
-
components: inner.components || prefetched?.components || [],
|
|
1652
|
-
slots,
|
|
1653
|
-
layout_hints: inner.layout_hints,
|
|
1654
|
-
...includeExtendedFields ? {
|
|
1655
|
-
visual_brief: inner.visual_brief,
|
|
1656
|
-
composition: inner.composition,
|
|
1657
|
-
motion: inner.motion,
|
|
1658
|
-
responsive: inner.responsive,
|
|
1659
|
-
accessibility: inner.accessibility
|
|
1660
|
-
} : {}
|
|
1661
|
-
};
|
|
1662
|
-
if (!spec.components || spec.components.length === 0) {
|
|
1663
|
-
const syntheticComps2 = generateSyntheticComponents(name, spec.description);
|
|
1664
|
-
if (syntheticComps2.length > 0) spec.components = syntheticComps2;
|
|
1665
|
-
}
|
|
1666
|
-
return spec;
|
|
2029
|
+
return mapRegistryPatternToPatternSpecSummary(patResult.data, prefetched, includeExtendedFields);
|
|
1667
2030
|
}
|
|
1668
2031
|
} catch {
|
|
1669
2032
|
}
|
|
@@ -1705,17 +2068,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1705
2068
|
try {
|
|
1706
2069
|
const bpResult = await registry.fetchBlueprint(storedBlueprintId);
|
|
1707
2070
|
if (bpResult?.data) {
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
projectJsonData.voice = bpData.voice;
|
|
2071
|
+
if (bpResult.data.voice) {
|
|
2072
|
+
storedVoice = bpResult.data.voice;
|
|
2073
|
+
projectJsonData.voice = bpResult.data.voice;
|
|
1712
2074
|
writeFileSync(projectJsonFilePath, JSON.stringify(projectJsonData, null, 2));
|
|
1713
2075
|
}
|
|
1714
2076
|
}
|
|
1715
2077
|
} catch {
|
|
1716
2078
|
}
|
|
1717
2079
|
}
|
|
1718
|
-
const themeName = essence.dna.theme.id ||
|
|
2080
|
+
const themeName = essence.dna.theme.id || "default";
|
|
1719
2081
|
const mode = essence.dna.theme.mode;
|
|
1720
2082
|
const guardMode = essence.meta.guard.mode;
|
|
1721
2083
|
const guardConfig = {
|
|
@@ -1728,29 +2090,13 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1728
2090
|
if (!themeData) try {
|
|
1729
2091
|
const themeResult = await registry.fetchTheme(themeName);
|
|
1730
2092
|
if (themeResult?.data) {
|
|
1731
|
-
|
|
1732
|
-
themeData = {
|
|
1733
|
-
seed: t.seed,
|
|
1734
|
-
palette: t.palette,
|
|
1735
|
-
cvd_support: t.cvd_support,
|
|
1736
|
-
tokens: t.tokens,
|
|
1737
|
-
typography: t.typography,
|
|
1738
|
-
motion: t.motion,
|
|
1739
|
-
decorators: t.decorators,
|
|
1740
|
-
treatments: t.treatments,
|
|
1741
|
-
spatial: t.spatial,
|
|
1742
|
-
radius: t.radius,
|
|
1743
|
-
shell: t.shell,
|
|
1744
|
-
effects: t.effects,
|
|
1745
|
-
compositions: t.compositions,
|
|
1746
|
-
pattern_preferences: t.pattern_preferences
|
|
1747
|
-
};
|
|
2093
|
+
themeData = mapRegistryThemeToThemeData(themeResult.data);
|
|
1748
2094
|
}
|
|
1749
2095
|
} catch {
|
|
1750
2096
|
}
|
|
1751
2097
|
if (!themeData?.seed?.primary) {
|
|
1752
2098
|
try {
|
|
1753
|
-
const apiUrl = registry.
|
|
2099
|
+
const apiUrl = registry.getApiUrl();
|
|
1754
2100
|
const resp = await fetch(`${apiUrl}/themes/@official/${themeName}`);
|
|
1755
2101
|
if (resp.ok) {
|
|
1756
2102
|
const apiData = await resp.json();
|
|
@@ -1832,7 +2178,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1832
2178
|
writeFileSync(decantrMdPath, generateDecantrMdV31({
|
|
1833
2179
|
guardMode,
|
|
1834
2180
|
cssApproach: CSS_APPROACH_CONTENT,
|
|
1835
|
-
blueprintId: storedBlueprintId || essence.meta
|
|
2181
|
+
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
|
|
1836
2182
|
themeName,
|
|
1837
2183
|
themeMode: mode,
|
|
1838
2184
|
themeShape: essence.dna.theme.shape || void 0,
|
|
@@ -1849,15 +2195,25 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1849
2195
|
writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
|
|
1850
2196
|
contextFiles.push(summaryPath);
|
|
1851
2197
|
}
|
|
2198
|
+
const packContexts = await generatePackContexts(projectRoot, contextDir, essence);
|
|
2199
|
+
const scaffoldTaskPath = join(contextDir, "task-scaffold.md");
|
|
2200
|
+
writeFileSync(
|
|
2201
|
+
scaffoldTaskPath,
|
|
2202
|
+
generateScaffoldTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2203
|
+
);
|
|
2204
|
+
contextFiles.push(scaffoldTaskPath);
|
|
1852
2205
|
if (!options?.isInitialScaffold) {
|
|
1853
|
-
const scaffoldTaskPath = join(contextDir, "task-scaffold.md");
|
|
1854
|
-
writeFileSync(scaffoldTaskPath, generateTaskContextV3("task-scaffold.md.template", essence));
|
|
1855
|
-
contextFiles.push(scaffoldTaskPath);
|
|
1856
2206
|
const addPagePath = join(contextDir, "task-add-page.md");
|
|
1857
|
-
writeFileSync(
|
|
2207
|
+
writeFileSync(
|
|
2208
|
+
addPagePath,
|
|
2209
|
+
generateAddPageTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2210
|
+
);
|
|
1858
2211
|
contextFiles.push(addPagePath);
|
|
1859
2212
|
const modifyPath = join(contextDir, "task-modify.md");
|
|
1860
|
-
writeFileSync(
|
|
2213
|
+
writeFileSync(
|
|
2214
|
+
modifyPath,
|
|
2215
|
+
generateModifyTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2216
|
+
);
|
|
1861
2217
|
contextFiles.push(modifyPath);
|
|
1862
2218
|
}
|
|
1863
2219
|
const blueprint = essence.blueprint;
|
|
@@ -1923,16 +2279,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1923
2279
|
try {
|
|
1924
2280
|
const shellResult = await registry.fetchShell(shellId);
|
|
1925
2281
|
if (shellResult?.data) {
|
|
1926
|
-
|
|
1927
|
-
shellInfoCache[shellId] = {
|
|
1928
|
-
description: inner.description || "",
|
|
1929
|
-
regions: inner.config?.regions || [],
|
|
1930
|
-
layout: inner.layout || void 0,
|
|
1931
|
-
guidance: inner.guidance || void 0,
|
|
1932
|
-
atoms: inner.atoms || void 0,
|
|
1933
|
-
config: inner.config || void 0,
|
|
1934
|
-
internal_layout: inner.internal_layout || void 0
|
|
1935
|
-
};
|
|
2282
|
+
shellInfoCache[shellId] = mapRegistryShellToShellInfo(shellResult.data);
|
|
1936
2283
|
}
|
|
1937
2284
|
} catch {
|
|
1938
2285
|
}
|
|
@@ -1961,6 +2308,12 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1961
2308
|
}
|
|
1962
2309
|
}
|
|
1963
2310
|
}
|
|
2311
|
+
const sectionSpatialHints = themeData?.spatial ? {
|
|
2312
|
+
section_padding: themeData.spatial.section_padding ?? void 0,
|
|
2313
|
+
density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
|
|
2314
|
+
content_gap_shift: themeData.spatial.content_gap_shift,
|
|
2315
|
+
label_content_gap: themeData.spatial.label_content_gap ?? void 0
|
|
2316
|
+
} : void 0;
|
|
1964
2317
|
const contextContent = generateSectionContext({
|
|
1965
2318
|
section,
|
|
1966
2319
|
themeTokens: themeTokensCss,
|
|
@@ -1979,7 +2332,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1979
2332
|
shellInfo: shellInfoCache[section.shell],
|
|
1980
2333
|
themeData,
|
|
1981
2334
|
themeMode: mode,
|
|
1982
|
-
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0
|
|
2335
|
+
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2336
|
+
spatialHints: sectionSpatialHints
|
|
1983
2337
|
});
|
|
1984
2338
|
const sectionContextPath = join(contextDir, `section-${section.id}.md`);
|
|
1985
2339
|
writeFileSync(sectionContextPath, contextContent);
|
|
@@ -1988,7 +2342,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1988
2342
|
const routes = blueprint.routes || {};
|
|
1989
2343
|
const scaffoldContent = generateScaffoldContext({
|
|
1990
2344
|
appName: essence.meta.archetype || "Application",
|
|
1991
|
-
blueprintId: storedBlueprintId || essence.meta
|
|
2345
|
+
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || "",
|
|
1992
2346
|
themeName,
|
|
1993
2347
|
personality,
|
|
1994
2348
|
topologyMarkdown,
|
|
@@ -2039,19 +2393,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2039
2393
|
try {
|
|
2040
2394
|
const shellResult = await registry.fetchShell(shell);
|
|
2041
2395
|
if (shellResult?.data) {
|
|
2042
|
-
|
|
2043
|
-
v30ShellInfo = {
|
|
2044
|
-
description: inner.description || "",
|
|
2045
|
-
regions: inner.config?.regions || [],
|
|
2046
|
-
layout: inner.layout || void 0,
|
|
2047
|
-
guidance: inner.guidance || void 0,
|
|
2048
|
-
atoms: inner.atoms || void 0,
|
|
2049
|
-
config: inner.config || void 0,
|
|
2050
|
-
internal_layout: inner.internal_layout || void 0
|
|
2051
|
-
};
|
|
2396
|
+
v30ShellInfo = mapRegistryShellToShellInfo(shellResult.data);
|
|
2052
2397
|
}
|
|
2053
2398
|
} catch {
|
|
2054
2399
|
}
|
|
2400
|
+
const v30SpatialHints = themeData?.spatial ? {
|
|
2401
|
+
section_padding: themeData.spatial.section_padding ?? void 0,
|
|
2402
|
+
density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
|
|
2403
|
+
content_gap_shift: themeData.spatial.content_gap_shift,
|
|
2404
|
+
label_content_gap: themeData.spatial.label_content_gap ?? void 0
|
|
2405
|
+
} : void 0;
|
|
2055
2406
|
const contextContent = generateSectionContext({
|
|
2056
2407
|
section: syntheticSection,
|
|
2057
2408
|
themeTokens: themeTokensCss,
|
|
@@ -2070,12 +2421,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2070
2421
|
shellInfo: v30ShellInfo,
|
|
2071
2422
|
themeData,
|
|
2072
2423
|
themeMode: mode,
|
|
2073
|
-
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0
|
|
2424
|
+
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2425
|
+
spatialHints: v30SpatialHints
|
|
2074
2426
|
});
|
|
2075
2427
|
const sectionContextPath = join(contextDir, `section-${syntheticSection.id}.md`);
|
|
2076
2428
|
writeFileSync(sectionContextPath, contextContent);
|
|
2077
2429
|
contextFiles.push(sectionContextPath);
|
|
2078
2430
|
}
|
|
2431
|
+
if (packContexts.paths.length > 0) {
|
|
2432
|
+
contextFiles.push(...packContexts.paths);
|
|
2433
|
+
}
|
|
2079
2434
|
return {
|
|
2080
2435
|
decantrMdPath,
|
|
2081
2436
|
contextFiles,
|
|
@@ -2169,6 +2524,50 @@ function generateSyntheticComponents(patternId, description) {
|
|
|
2169
2524
|
if (patternId.includes("empty") || patternId.includes("new")) syntheticComponents.push("Icon", "Text", "Button");
|
|
2170
2525
|
return [...new Set(syntheticComponents)];
|
|
2171
2526
|
}
|
|
2527
|
+
function mapRegistryPatternToPatternSpecSummary(pattern, prefetched, includeExtendedFields = true) {
|
|
2528
|
+
const defaultPreset = pattern.default_preset || "standard";
|
|
2529
|
+
const preset = pattern.presets?.[defaultPreset];
|
|
2530
|
+
let slots = preset?.layout?.slots || pattern.default_layout?.slots || prefetched?.slots || {};
|
|
2531
|
+
if (Object.keys(slots).length === 0) {
|
|
2532
|
+
const synthetic = generateSyntheticSlots(pattern.id, pattern.description || prefetched?.description || "");
|
|
2533
|
+
if (Object.keys(synthetic).length > 0) slots = synthetic;
|
|
2534
|
+
}
|
|
2535
|
+
const spec = {
|
|
2536
|
+
description: pattern.description || prefetched?.description || "",
|
|
2537
|
+
components: pattern.components || prefetched?.components || [],
|
|
2538
|
+
slots,
|
|
2539
|
+
layout_hints: pattern.layout_hints,
|
|
2540
|
+
...includeExtendedFields ? {
|
|
2541
|
+
visual_brief: pattern.visual_brief,
|
|
2542
|
+
composition: pattern.composition,
|
|
2543
|
+
motion: pattern.motion,
|
|
2544
|
+
responsive: pattern.responsive,
|
|
2545
|
+
accessibility: pattern.accessibility ? {
|
|
2546
|
+
role: pattern.accessibility.role,
|
|
2547
|
+
keyboard: pattern.accessibility.keyboard,
|
|
2548
|
+
announcements: pattern.accessibility.announcements,
|
|
2549
|
+
focus_management: pattern.accessibility.focus_management
|
|
2550
|
+
} : void 0
|
|
2551
|
+
} : {}
|
|
2552
|
+
};
|
|
2553
|
+
if (!spec.components || spec.components.length === 0) {
|
|
2554
|
+
const syntheticComps = generateSyntheticComponents(pattern.id, spec.description);
|
|
2555
|
+
if (syntheticComps.length > 0) spec.components = syntheticComps;
|
|
2556
|
+
}
|
|
2557
|
+
return spec;
|
|
2558
|
+
}
|
|
2559
|
+
function mapRegistryShellToShellInfo(shell) {
|
|
2560
|
+
const config = isRecord(shell.config) ? shell.config : void 0;
|
|
2561
|
+
return {
|
|
2562
|
+
description: shell.description || "",
|
|
2563
|
+
regions: getStringArray(config?.regions),
|
|
2564
|
+
layout: shell.layout || void 0,
|
|
2565
|
+
guidance: shell.guidance,
|
|
2566
|
+
atoms: shell.atoms,
|
|
2567
|
+
config,
|
|
2568
|
+
internal_layout: isRecord(shell.internal_layout) ? shell.internal_layout : void 0
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2172
2571
|
function generateShellImplementation(shellId, shellInfo) {
|
|
2173
2572
|
const lines = [];
|
|
2174
2573
|
lines.push(`## Shell Implementation (${shellId})`);
|
|
@@ -2177,9 +2576,9 @@ function generateShellImplementation(shellId, shellInfo) {
|
|
|
2177
2576
|
for (const [region, props] of Object.entries(shellInfo.internal_layout)) {
|
|
2178
2577
|
lines.push(`### ${region}`);
|
|
2179
2578
|
lines.push("");
|
|
2180
|
-
if (
|
|
2579
|
+
if (isRecord(props)) {
|
|
2181
2580
|
for (const [key, value] of Object.entries(props)) {
|
|
2182
|
-
if (
|
|
2581
|
+
if (isRecord(value)) {
|
|
2183
2582
|
lines.push(`- **${key}:**`);
|
|
2184
2583
|
for (const [subKey, subValue] of Object.entries(value)) {
|
|
2185
2584
|
lines.push(` - ${subKey}: ${subValue}`);
|
|
@@ -2281,10 +2680,10 @@ function generateQuickStart(input) {
|
|
|
2281
2680
|
lines.push("");
|
|
2282
2681
|
return lines;
|
|
2283
2682
|
}
|
|
2284
|
-
function generateSpacingGuide(density) {
|
|
2683
|
+
function generateSpacingGuide(density, spatialHints) {
|
|
2285
2684
|
const lines = [];
|
|
2286
2685
|
const level = density === "compact" || density === "spacious" ? density : "comfortable";
|
|
2287
|
-
const tokens = computeSpatialTokens(level);
|
|
2686
|
+
const tokens = computeSpatialTokens(level, spatialHints);
|
|
2288
2687
|
lines.push("## Spacing Guide");
|
|
2289
2688
|
lines.push("");
|
|
2290
2689
|
lines.push("| Context | Token | Value | Usage |");
|
|
@@ -2296,11 +2695,15 @@ function generateSpacingGuide(density) {
|
|
|
2296
2695
|
lines.push(`| Interactive H | \`--d-interactive-px\` | \`${tokens["--d-interactive-px"]}\` | Horizontal padding on buttons |`);
|
|
2297
2696
|
lines.push(`| Control | \`--d-control-py\` | \`${tokens["--d-control-py"]}\` | Vertical padding on inputs |`);
|
|
2298
2697
|
lines.push(`| Data row | \`--d-data-py\` | \`${tokens["--d-data-py"]}\` | Vertical padding on table rows |`);
|
|
2698
|
+
lines.push(`| Label gap | \`--d-label-mb\` | \`${tokens["--d-label-mb"]}\` | Gap below d-label section headers |`);
|
|
2699
|
+
lines.push(`| Label indent | \`--d-label-px\` | \`${tokens["--d-label-px"]}\` | Anchor indent for d-label[data-anchor] |`);
|
|
2700
|
+
lines.push(`| Section gap | \`--d-section-gap\` | \`${tokens["--d-section-gap"]}\` | Gap between adjacent d-sections |`);
|
|
2701
|
+
lines.push(`| Annotation gap | \`--d-annotation-mt\` | \`${tokens["--d-annotation-mt"]}\` | Top margin on d-annotation |`);
|
|
2299
2702
|
lines.push("");
|
|
2300
2703
|
return lines;
|
|
2301
2704
|
}
|
|
2302
2705
|
function generateSectionContext(input) {
|
|
2303
|
-
const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo } = input;
|
|
2706
|
+
const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo, spatialHints } = input;
|
|
2304
2707
|
const lines = [];
|
|
2305
2708
|
lines.push(`# Section: ${section.id}`);
|
|
2306
2709
|
lines.push("");
|
|
@@ -2320,16 +2723,36 @@ function generateSectionContext(input) {
|
|
|
2320
2723
|
lines.push(...generateShellImplementation(section.shell, shellInfo));
|
|
2321
2724
|
}
|
|
2322
2725
|
if (shellInfo?.guidance && Object.keys(shellInfo.guidance).length > 0) {
|
|
2726
|
+
const labelTreatment = shellInfo.guidance.section_label_treatment;
|
|
2727
|
+
const sectionDensity = shellInfo.guidance.section_density;
|
|
2728
|
+
if (labelTreatment) {
|
|
2729
|
+
lines.push("## Section Label Treatment");
|
|
2730
|
+
lines.push("");
|
|
2731
|
+
lines.push(`Apply \`${labelTreatment}\` to section headers in this shell.`);
|
|
2732
|
+
lines.push("- Uppercase monospace label typography (d-label base treatment)");
|
|
2733
|
+
if (labelTreatment.includes("[data-anchor]")) {
|
|
2734
|
+
lines.push("- Left accent border anchor (data-anchor variant)");
|
|
2735
|
+
}
|
|
2736
|
+
lines.push("- Density-responsive bottom gap via `--d-label-mb` x `--d-density-scale`");
|
|
2737
|
+
if (sectionDensity) {
|
|
2738
|
+
const scaleMap = { compact: "0.65", comfortable: "1", spacious: "1.4" };
|
|
2739
|
+
lines.push("");
|
|
2740
|
+
lines.push(`Section density: ${sectionDensity} (--d-density-scale: ${scaleMap[sectionDensity] || "1"})`);
|
|
2741
|
+
}
|
|
2742
|
+
lines.push("");
|
|
2743
|
+
}
|
|
2744
|
+
const structuredKeys = /* @__PURE__ */ new Set(["section_label_treatment", "section_density"]);
|
|
2323
2745
|
lines.push(`## Shell Notes (${section.shell})`);
|
|
2324
2746
|
lines.push("");
|
|
2325
2747
|
for (const [key, value] of Object.entries(shellInfo.guidance)) {
|
|
2748
|
+
if (structuredKeys.has(key)) continue;
|
|
2326
2749
|
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2327
2750
|
lines.push(`- **${label}:** ${value}`);
|
|
2328
2751
|
}
|
|
2329
2752
|
lines.push("");
|
|
2330
2753
|
}
|
|
2331
2754
|
const density = section.dna_overrides?.density || "comfortable";
|
|
2332
|
-
lines.push(...generateSpacingGuide(density));
|
|
2755
|
+
lines.push(...generateSpacingGuide(density, spatialHints));
|
|
2333
2756
|
lines.push("---");
|
|
2334
2757
|
lines.push("");
|
|
2335
2758
|
lines.push(`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`);
|
|
@@ -2415,7 +2838,7 @@ function generateSectionContext(input) {
|
|
|
2415
2838
|
}
|
|
2416
2839
|
if (themeHints) {
|
|
2417
2840
|
if (themeHints.preferred && themeHints.preferred.length > 0) {
|
|
2418
|
-
const sectionPatterns = new Set(section.pages.flatMap((p) => p.layout.
|
|
2841
|
+
const sectionPatterns = new Set(section.pages.flatMap((p) => p.layout.flatMap(extractPatternNames)));
|
|
2419
2842
|
const relevant = themeHints.preferred.filter((p) => sectionPatterns.has(p));
|
|
2420
2843
|
if (relevant.length > 0) {
|
|
2421
2844
|
lines.push(`**Preferred:** ${relevant.join(", ")}`);
|
|
@@ -2490,6 +2913,9 @@ function generateSectionContext(input) {
|
|
|
2490
2913
|
if (uniquePatterns.size > 0) {
|
|
2491
2914
|
lines.push("## Pattern Reference");
|
|
2492
2915
|
lines.push("");
|
|
2916
|
+
lines.push("Scaffold-tier rule: implement the core visual structure, states, and required slots first.");
|
|
2917
|
+
lines.push("Treat advanced capabilities such as drag/drop, force-layout, minimaps, or simulated live streaming as optional unless the slot guidance or section contract makes them explicitly required.");
|
|
2918
|
+
lines.push("");
|
|
2493
2919
|
for (const [patternName, spec] of uniquePatterns) {
|
|
2494
2920
|
lines.push(`### ${patternName}`);
|
|
2495
2921
|
lines.push("");
|
|
@@ -2698,9 +3124,9 @@ function generateScaffoldContext(input) {
|
|
|
2698
3124
|
// src/registry.ts
|
|
2699
3125
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync } from "fs";
|
|
2700
3126
|
import { join as join2 } from "path";
|
|
2701
|
-
import { RegistryAPIClient } from "@decantr/registry";
|
|
3127
|
+
import { RegistryAPIClient, API_CONTENT_TYPES as API_CONTENT_TYPES2 } from "@decantr/registry";
|
|
2702
3128
|
var DEFAULT_API_URL = "https://api.decantr.ai/v1";
|
|
2703
|
-
var ALL_CONTENT_TYPES =
|
|
3129
|
+
var ALL_CONTENT_TYPES = API_CONTENT_TYPES2;
|
|
2704
3130
|
function loadFromCache(cacheDir, contentType, id, namespace) {
|
|
2705
3131
|
const nsDir = namespace ? join2(cacheDir, namespace) : cacheDir;
|
|
2706
3132
|
const cachePath = id ? join2(nsDir, contentType, `${id}.json`) : join2(nsDir, contentType, "index.json");
|
|
@@ -2727,13 +3153,16 @@ var RegistryClient = class {
|
|
|
2727
3153
|
constructor(options = {}) {
|
|
2728
3154
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
2729
3155
|
this.cacheDir = options.cacheDir || join2(this.projectRoot, ".decantr", "cache");
|
|
2730
|
-
this.apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
3156
|
+
this.apiUrl = options.apiUrl || process.env.DECANTR_API_URL || DEFAULT_API_URL;
|
|
2731
3157
|
this.offline = options.offline || false;
|
|
2732
3158
|
this.apiClient = new RegistryAPIClient({
|
|
2733
3159
|
baseUrl: this.apiUrl,
|
|
2734
|
-
apiKey: options.apiKey
|
|
3160
|
+
apiKey: options.apiKey || process.env.DECANTR_API_KEY || void 0
|
|
2735
3161
|
});
|
|
2736
3162
|
}
|
|
3163
|
+
getApiUrl() {
|
|
3164
|
+
return this.apiUrl;
|
|
3165
|
+
}
|
|
2737
3166
|
/**
|
|
2738
3167
|
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
2739
3168
|
* Works for ALL content types, not just themes.
|
|
@@ -2773,12 +3202,17 @@ var RegistryClient = class {
|
|
|
2773
3202
|
* Unified fetch for a content list.
|
|
2774
3203
|
* Resolution: API -> Cache. Custom items are merged into the list.
|
|
2775
3204
|
*/
|
|
2776
|
-
async fetchContentList(contentType, namespace) {
|
|
3205
|
+
async fetchContentList(contentType, namespace, sort, recommended, intelligenceSource) {
|
|
2777
3206
|
let apiItems = [];
|
|
2778
3207
|
let source = { type: "cache" };
|
|
2779
3208
|
if (!this.offline) {
|
|
2780
3209
|
try {
|
|
2781
|
-
const apiResult = await this.apiClient.listContent(contentType, {
|
|
3210
|
+
const apiResult = await this.apiClient.listContent(contentType, {
|
|
3211
|
+
namespace,
|
|
3212
|
+
sort,
|
|
3213
|
+
recommended,
|
|
3214
|
+
intelligenceSource
|
|
3215
|
+
});
|
|
2782
3216
|
apiItems = apiResult.items;
|
|
2783
3217
|
source = { type: "api", url: this.apiUrl };
|
|
2784
3218
|
saveToCache(this.cacheDir, contentType, null, apiResult, namespace || "@official");
|
|
@@ -2902,14 +3336,19 @@ async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
|
2902
3336
|
}
|
|
2903
3337
|
|
|
2904
3338
|
export {
|
|
3339
|
+
collectPatternIdsFromItems,
|
|
3340
|
+
mapRegistryArchetypeToArchetypeData,
|
|
2905
3341
|
composeArchetypes,
|
|
2906
3342
|
composeSections,
|
|
2907
3343
|
deriveZones,
|
|
2908
3344
|
deriveTransitions,
|
|
2909
3345
|
generateTopologySection,
|
|
3346
|
+
mapRegistryThemeToThemeData,
|
|
2910
3347
|
scaffoldProject,
|
|
2911
3348
|
scaffoldMinimal,
|
|
3349
|
+
writeExecutionPackBundleArtifacts,
|
|
2912
3350
|
refreshDerivedFiles,
|
|
3351
|
+
mapRegistryPatternToPatternSpecSummary,
|
|
2913
3352
|
RegistryClient,
|
|
2914
3353
|
syncRegistry
|
|
2915
3354
|
};
|