@decantr/cli 1.7.13 → 1.7.14
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/bin.js +3 -3
- package/dist/{chunk-YSBFK43A.js → chunk-M2DFCY3N.js} +4017 -3484
- package/dist/chunk-QRQCPD3C.js +135 -0
- package/dist/{chunk-NZ4SGTDS.js → chunk-WINKQSUX.js} +2245 -651
- package/dist/{heal-MURR3RVQ.js → heal-EMT5LYVZ.js} +24 -8
- package/dist/index.js +3 -3
- package/dist/{upgrade-A3LEZKSD.js → upgrade-Q7PHW34P.js} +11 -5
- package/package.json +5 -4
- package/dist/chunk-KUDAVJOR.js +0 -46
|
@@ -1,9 +1,224 @@
|
|
|
1
|
+
// src/registry.ts
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { API_CONTENT_TYPES, RegistryAPIClient } from "@decantr/registry";
|
|
5
|
+
var DEFAULT_API_URL = "https://api.decantr.ai/v1";
|
|
6
|
+
var ALL_CONTENT_TYPES = API_CONTENT_TYPES;
|
|
7
|
+
function loadFromCache(cacheDir, contentType, id, namespace) {
|
|
8
|
+
const nsDir = namespace ? join(cacheDir, namespace) : cacheDir;
|
|
9
|
+
const cachePath = id ? join(nsDir, contentType, `${id}.json`) : join(nsDir, contentType, "index.json");
|
|
10
|
+
if (!existsSync(cachePath)) return null;
|
|
11
|
+
try {
|
|
12
|
+
const data = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
13
|
+
return { data, source: { type: "cache" } };
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function saveToCache(cacheDir, contentType, id, data, namespace = "@official") {
|
|
19
|
+
const dir = join(cacheDir, namespace, contentType);
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
const cachePath = id ? join(dir, `${id}.json`) : join(dir, "index.json");
|
|
22
|
+
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
var RegistryClient = class {
|
|
25
|
+
cacheDir;
|
|
26
|
+
apiUrl;
|
|
27
|
+
offline;
|
|
28
|
+
projectRoot;
|
|
29
|
+
apiClient;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.projectRoot = options.projectRoot || process.cwd();
|
|
32
|
+
this.cacheDir = options.cacheDir || join(this.projectRoot, ".decantr", "cache");
|
|
33
|
+
this.apiUrl = options.apiUrl || process.env.DECANTR_API_URL || DEFAULT_API_URL;
|
|
34
|
+
this.offline = options.offline || false;
|
|
35
|
+
this.apiClient = new RegistryAPIClient({
|
|
36
|
+
baseUrl: this.apiUrl,
|
|
37
|
+
apiKey: options.apiKey || process.env.DECANTR_API_KEY || void 0
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
getApiUrl() {
|
|
41
|
+
return this.apiUrl;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
45
|
+
* Works for ALL content types, not just themes.
|
|
46
|
+
*/
|
|
47
|
+
loadCustomContent(contentType, id) {
|
|
48
|
+
const customPath = join(this.projectRoot, ".decantr", "custom", contentType, `${id}.json`);
|
|
49
|
+
if (!existsSync(customPath)) return null;
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(readFileSync(customPath, "utf-8"));
|
|
52
|
+
return { data, source: { type: "custom", path: customPath } };
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* List all custom content of a given type from .decantr/custom/{type}/
|
|
59
|
+
*/
|
|
60
|
+
listCustomContent(contentType) {
|
|
61
|
+
const dir = join(this.projectRoot, ".decantr", "custom", contentType);
|
|
62
|
+
if (!existsSync(dir)) return [];
|
|
63
|
+
try {
|
|
64
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
65
|
+
const data = JSON.parse(readFileSync(join(dir, f), "utf-8"));
|
|
66
|
+
return { id: data.id || f.replace(".json", ""), ...data };
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Unified fetch for a content list.
|
|
74
|
+
* Resolution: API -> Cache. Custom items are merged into the list.
|
|
75
|
+
*/
|
|
76
|
+
async fetchContentList(contentType, namespace, sort, recommended, intelligenceSource) {
|
|
77
|
+
let apiItems = [];
|
|
78
|
+
let source = { type: "cache" };
|
|
79
|
+
if (!this.offline) {
|
|
80
|
+
try {
|
|
81
|
+
const apiResult = await this.apiClient.listContent(contentType, {
|
|
82
|
+
namespace,
|
|
83
|
+
sort,
|
|
84
|
+
recommended,
|
|
85
|
+
intelligenceSource
|
|
86
|
+
});
|
|
87
|
+
apiItems = apiResult.items;
|
|
88
|
+
source = { type: "api", url: this.apiUrl };
|
|
89
|
+
saveToCache(this.cacheDir, contentType, null, apiResult, namespace || "@official");
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (apiItems.length === 0) {
|
|
94
|
+
const cacheResult = loadFromCache(
|
|
95
|
+
this.cacheDir,
|
|
96
|
+
contentType,
|
|
97
|
+
void 0,
|
|
98
|
+
namespace
|
|
99
|
+
);
|
|
100
|
+
if (cacheResult) {
|
|
101
|
+
apiItems = cacheResult.data.items;
|
|
102
|
+
source = { type: "cache" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const customItems = this.listCustomContent(contentType);
|
|
106
|
+
const allItems = [...customItems, ...apiItems];
|
|
107
|
+
return {
|
|
108
|
+
data: { items: allItems, total: allItems.length },
|
|
109
|
+
source
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Unified fetch for a single content item.
|
|
114
|
+
* Resolution: Custom -> API -> Cache
|
|
115
|
+
*/
|
|
116
|
+
async fetchContentItem(contentType, id, namespace = "@official") {
|
|
117
|
+
const customId = id.startsWith("custom:") ? id.slice(7) : id;
|
|
118
|
+
const customResult = this.loadCustomContent(contentType, customId);
|
|
119
|
+
if (customResult) return customResult;
|
|
120
|
+
if (id.startsWith("custom:")) return null;
|
|
121
|
+
if (!this.offline) {
|
|
122
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
123
|
+
try {
|
|
124
|
+
const data = await this.apiClient.getContent(
|
|
125
|
+
contentType,
|
|
126
|
+
namespace,
|
|
127
|
+
id
|
|
128
|
+
);
|
|
129
|
+
saveToCache(this.cacheDir, contentType, id, data, namespace);
|
|
130
|
+
return { data, source: { type: "api", url: this.apiUrl } };
|
|
131
|
+
} catch (e) {
|
|
132
|
+
if (process.env.DECANTR_DEBUG) {
|
|
133
|
+
console.error(
|
|
134
|
+
` [debug] API fetch ${attempt === 0 ? "failed" : "retry failed"} for ${contentType}/${namespace}/${id}: ${e.message}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (attempt === 0) {
|
|
138
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else if (process.env.DECANTR_DEBUG) {
|
|
143
|
+
console.error(` [debug] Skipping API (offline mode) for ${contentType}/${namespace}/${id}`);
|
|
144
|
+
}
|
|
145
|
+
return loadFromCache(this.cacheDir, contentType, id, namespace);
|
|
146
|
+
}
|
|
147
|
+
// ── Convenience methods (delegate to unified fetch) ──
|
|
148
|
+
async fetchArchetypes() {
|
|
149
|
+
return this.fetchContentList("archetypes");
|
|
150
|
+
}
|
|
151
|
+
async fetchArchetype(id) {
|
|
152
|
+
return this.fetchContentItem("archetypes", id);
|
|
153
|
+
}
|
|
154
|
+
async fetchBlueprints() {
|
|
155
|
+
return this.fetchContentList("blueprints");
|
|
156
|
+
}
|
|
157
|
+
async fetchBlueprint(id) {
|
|
158
|
+
return this.fetchContentItem("blueprints", id);
|
|
159
|
+
}
|
|
160
|
+
async fetchThemes() {
|
|
161
|
+
return this.fetchContentList("themes");
|
|
162
|
+
}
|
|
163
|
+
async fetchTheme(id) {
|
|
164
|
+
return this.fetchContentItem("themes", id);
|
|
165
|
+
}
|
|
166
|
+
async fetchPatterns() {
|
|
167
|
+
return this.fetchContentList("patterns");
|
|
168
|
+
}
|
|
169
|
+
async fetchPattern(id) {
|
|
170
|
+
return this.fetchContentItem("patterns", id);
|
|
171
|
+
}
|
|
172
|
+
async fetchShells() {
|
|
173
|
+
return this.fetchContentList("shells");
|
|
174
|
+
}
|
|
175
|
+
async fetchShell(id) {
|
|
176
|
+
return this.fetchContentItem("shells", id);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if API is available.
|
|
180
|
+
*/
|
|
181
|
+
async checkApiAvailability() {
|
|
182
|
+
if (this.offline) return false;
|
|
183
|
+
return this.apiClient.checkHealth();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
187
|
+
const apiClient = new RegistryAPIClient({ baseUrl: apiUrl });
|
|
188
|
+
const synced = [];
|
|
189
|
+
const failed = [];
|
|
190
|
+
const healthy = await apiClient.checkHealth();
|
|
191
|
+
if (!healthy) {
|
|
192
|
+
return { synced: [], failed: ["API unavailable"] };
|
|
193
|
+
}
|
|
194
|
+
for (const type of ALL_CONTENT_TYPES) {
|
|
195
|
+
try {
|
|
196
|
+
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
197
|
+
saveToCache(cacheDir, type, null, result, "@official");
|
|
198
|
+
for (const item of result.items) {
|
|
199
|
+
const slug = item.slug;
|
|
200
|
+
const data = item.data;
|
|
201
|
+
const innerSlug = data?.id || data?.slug;
|
|
202
|
+
const cacheKey = slug || innerSlug || item.id;
|
|
203
|
+
if (cacheKey) {
|
|
204
|
+
saveToCache(cacheDir, type, cacheKey, item, "@official");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
synced.push(type);
|
|
208
|
+
} catch {
|
|
209
|
+
failed.push(type);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { synced, failed };
|
|
213
|
+
}
|
|
214
|
+
|
|
1
215
|
// src/scaffold.ts
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync
|
|
3
|
-
import {
|
|
216
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
217
|
+
import { dirname, join as join2 } from "path";
|
|
4
218
|
import { fileURLToPath } from "url";
|
|
5
|
-
import { computeSpatialTokens } from "@decantr/essence-spec";
|
|
6
219
|
import { compileExecutionPackBundle } from "@decantr/core";
|
|
220
|
+
import { computeSpatialTokens } from "@decantr/essence-spec";
|
|
221
|
+
import { API_CONTENT_TYPES as API_CONTENT_TYPES2 } from "@decantr/registry";
|
|
7
222
|
|
|
8
223
|
// src/treatments.ts
|
|
9
224
|
function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName, themeDecoratorDefinitions) {
|
|
@@ -51,7 +266,10 @@ ${themeBody}
|
|
|
51
266
|
["display", "inline-flex"],
|
|
52
267
|
["align-items", "center"],
|
|
53
268
|
["gap", "0.5em"],
|
|
54
|
-
[
|
|
269
|
+
[
|
|
270
|
+
"padding",
|
|
271
|
+
"calc(var(--d-interactive-py) * var(--d-density-scale, 1)) var(--d-interactive-px)"
|
|
272
|
+
],
|
|
55
273
|
["border", "1px solid var(--d-border)"],
|
|
56
274
|
["border-radius", "var(--d-radius)"],
|
|
57
275
|
["background", "transparent"],
|
|
@@ -59,7 +277,10 @@ ${themeBody}
|
|
|
59
277
|
["font", "inherit"],
|
|
60
278
|
["cursor", "pointer"],
|
|
61
279
|
["text-decoration", "none"],
|
|
62
|
-
[
|
|
280
|
+
[
|
|
281
|
+
"transition",
|
|
282
|
+
"background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease"
|
|
283
|
+
]
|
|
63
284
|
]);
|
|
64
285
|
emitRule(".d-interactive:hover", [
|
|
65
286
|
["border-color", "var(--d-primary-hover)"],
|
|
@@ -87,14 +308,38 @@ ${themeBody}
|
|
|
87
308
|
["border-color", "transparent"],
|
|
88
309
|
["background", "transparent"]
|
|
89
310
|
]);
|
|
90
|
-
emitRule('.d-interactive[data-variant="ghost"]:hover', [
|
|
91
|
-
["background", "var(--d-surface)"]
|
|
92
|
-
]);
|
|
311
|
+
emitRule('.d-interactive[data-variant="ghost"]:hover', [["background", "var(--d-surface)"]]);
|
|
93
312
|
emitRule('.d-interactive[data-variant="danger"]', [
|
|
94
313
|
["background", "var(--d-error)"],
|
|
95
314
|
["color", "#fff"],
|
|
96
315
|
["border-color", "var(--d-error)"]
|
|
97
316
|
]);
|
|
317
|
+
emitRule('.d-interactive[data-size="sm"]', [
|
|
318
|
+
[
|
|
319
|
+
"padding",
|
|
320
|
+
"calc(var(--d-interactive-py) * 0.5 * var(--d-density-scale, 1)) calc(var(--d-interactive-px) * 0.75)"
|
|
321
|
+
],
|
|
322
|
+
["font-size", "0.875rem"],
|
|
323
|
+
["gap", "0.375em"]
|
|
324
|
+
]);
|
|
325
|
+
emitRule('.d-interactive[data-size="md"]', [
|
|
326
|
+
// Explicit md — same as default. Lets authors opt in without inheriting
|
|
327
|
+
// contextual sizing from an ancestor that may have set data-size.
|
|
328
|
+
[
|
|
329
|
+
"padding",
|
|
330
|
+
"calc(var(--d-interactive-py) * var(--d-density-scale, 1)) var(--d-interactive-px)"
|
|
331
|
+
],
|
|
332
|
+
["font-size", "1rem"],
|
|
333
|
+
["gap", "0.5em"]
|
|
334
|
+
]);
|
|
335
|
+
emitRule('.d-interactive[data-size="lg"]', [
|
|
336
|
+
[
|
|
337
|
+
"padding",
|
|
338
|
+
"calc(var(--d-interactive-py) * 1.5 * var(--d-density-scale, 1)) calc(var(--d-interactive-px) * 1.25)"
|
|
339
|
+
],
|
|
340
|
+
["font-size", "1.125rem"],
|
|
341
|
+
["gap", "0.625em"]
|
|
342
|
+
]);
|
|
98
343
|
emitRule(".d-surface", [
|
|
99
344
|
["background", "var(--d-surface)"],
|
|
100
345
|
["border", "1px solid var(--d-border)"],
|
|
@@ -111,9 +356,7 @@ ${themeBody}
|
|
|
111
356
|
["box-shadow", "var(--d-shadow-lg)"],
|
|
112
357
|
["z-index", "50"]
|
|
113
358
|
]);
|
|
114
|
-
emitRule(".d-surface[data-interactive]", [
|
|
115
|
-
["cursor", "pointer"]
|
|
116
|
-
]);
|
|
359
|
+
emitRule(".d-surface[data-interactive]", [["cursor", "pointer"]]);
|
|
117
360
|
emitRule(".d-surface[data-interactive]:hover", [
|
|
118
361
|
["border-color", "var(--d-primary-hover, var(--d-border))"],
|
|
119
362
|
["box-shadow", "var(--d-shadow-md)"],
|
|
@@ -134,103 +377,958 @@ ${themeBody}
|
|
|
134
377
|
["text-transform", "uppercase"],
|
|
135
378
|
["letter-spacing", "0.05em"]
|
|
136
379
|
]);
|
|
137
|
-
emitRule(".d-data-row", [
|
|
138
|
-
["border-bottom", "1px solid var(--d-border)"],
|
|
139
|
-
["transition", "background 0.1s ease"]
|
|
380
|
+
emitRule(".d-data-row", [
|
|
381
|
+
["border-bottom", "1px solid var(--d-border)"],
|
|
382
|
+
["transition", "background 0.1s ease"]
|
|
383
|
+
]);
|
|
384
|
+
emitRule(".d-data-row:hover", [["background", "var(--d-surface)"]]);
|
|
385
|
+
emitRule(".d-data-cell", [
|
|
386
|
+
["padding", "calc(var(--d-data-py) * var(--d-density-scale, 1)) var(--d-content-gap)"],
|
|
387
|
+
["vertical-align", "middle"]
|
|
388
|
+
]);
|
|
389
|
+
emitRule(".d-control", [
|
|
390
|
+
["background", "var(--d-surface)"],
|
|
391
|
+
["color", "var(--d-text)"],
|
|
392
|
+
["padding", "calc(var(--d-control-py) * var(--d-density-scale, 1)) 0.75rem"],
|
|
393
|
+
["border-radius", "var(--d-radius)"],
|
|
394
|
+
["border", "1px solid var(--d-border)"],
|
|
395
|
+
["width", "100%"],
|
|
396
|
+
["outline", "none"],
|
|
397
|
+
["font", "inherit"],
|
|
398
|
+
["transition", "border-color 0.15s ease, box-shadow 0.15s ease"]
|
|
399
|
+
]);
|
|
400
|
+
emitRule(".d-control:focus", [
|
|
401
|
+
["border-color", "var(--d-primary)"],
|
|
402
|
+
["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-primary) 25%, transparent)"]
|
|
403
|
+
]);
|
|
404
|
+
emitRule(".d-control::placeholder", [["color", "var(--d-text-muted)"]]);
|
|
405
|
+
emitRule(".d-control:disabled", [
|
|
406
|
+
["opacity", "0.5"],
|
|
407
|
+
["cursor", "not-allowed"]
|
|
408
|
+
]);
|
|
409
|
+
emitRule(".d-control[aria-invalid]", [
|
|
410
|
+
["border-color", "var(--d-error)"],
|
|
411
|
+
["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-error) 15%, transparent)"]
|
|
412
|
+
]);
|
|
413
|
+
emitRule(".d-section", [
|
|
414
|
+
["--d-density-scale", "1"],
|
|
415
|
+
["padding", "calc(var(--d-section-py) * var(--d-density-scale)) 0"]
|
|
416
|
+
]);
|
|
417
|
+
lines.push('.d-section[data-density="compact"] {');
|
|
418
|
+
lines.push(" --d-density-scale: 0.65;");
|
|
419
|
+
lines.push("}");
|
|
420
|
+
lines.push("");
|
|
421
|
+
lines.push('.d-section[data-density="spacious"] {');
|
|
422
|
+
lines.push(" --d-density-scale: 1.4;");
|
|
423
|
+
lines.push("}");
|
|
424
|
+
lines.push("");
|
|
425
|
+
lines.push(".d-section + .d-section {");
|
|
426
|
+
lines.push(" border-top: 1px solid transparent;");
|
|
427
|
+
lines.push(
|
|
428
|
+
" border-image: linear-gradient(to right, transparent, var(--d-border), transparent) 1;"
|
|
429
|
+
);
|
|
430
|
+
lines.push(" margin-top: calc(var(--d-section-gap) * var(--d-density-scale, 1));");
|
|
431
|
+
lines.push("}");
|
|
432
|
+
lines.push("");
|
|
433
|
+
emitRule(".d-annotation", [
|
|
434
|
+
["display", "inline-flex"],
|
|
435
|
+
["align-items", "center"],
|
|
436
|
+
["gap", "0.25em"],
|
|
437
|
+
["font-size", "0.75rem"],
|
|
438
|
+
["font-weight", "500"],
|
|
439
|
+
["padding", "0.125rem 0.5rem"],
|
|
440
|
+
["border-radius", "var(--d-radius-full)"],
|
|
441
|
+
["background", "color-mix(in srgb, var(--d-surface-raised) 88%, transparent)"],
|
|
442
|
+
["border", "1px solid color-mix(in srgb, var(--d-border) 72%, transparent)"],
|
|
443
|
+
["color", "var(--d-text-muted)"],
|
|
444
|
+
["white-space", "nowrap"]
|
|
445
|
+
]);
|
|
446
|
+
emitRule('.d-annotation[data-status="success"]', [
|
|
447
|
+
["background", "color-mix(in srgb, var(--d-success) 15%, transparent)"],
|
|
448
|
+
["color", "var(--d-success)"]
|
|
449
|
+
]);
|
|
450
|
+
emitRule('.d-annotation[data-status="error"]', [
|
|
451
|
+
["background", "color-mix(in srgb, var(--d-error) 15%, transparent)"],
|
|
452
|
+
["color", "var(--d-error)"]
|
|
453
|
+
]);
|
|
454
|
+
emitRule('.d-annotation[data-status="warning"]', [
|
|
455
|
+
["background", "color-mix(in srgb, var(--d-warning) 15%, transparent)"],
|
|
456
|
+
["color", "var(--d-warning)"]
|
|
457
|
+
]);
|
|
458
|
+
emitRule('.d-annotation[data-status="info"]', [
|
|
459
|
+
["background", "color-mix(in srgb, var(--d-info) 15%, transparent)"],
|
|
460
|
+
["color", "var(--d-info)"]
|
|
461
|
+
]);
|
|
462
|
+
emitRule(".d-label", [
|
|
463
|
+
["font-size", "0.7rem"],
|
|
464
|
+
["font-weight", "600"],
|
|
465
|
+
["text-transform", "uppercase"],
|
|
466
|
+
["letter-spacing", "0.08em"],
|
|
467
|
+
["color", "var(--d-text-muted)"],
|
|
468
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
469
|
+
["display", "block"],
|
|
470
|
+
["margin-bottom", "calc(var(--d-label-mb) * var(--d-density-scale, 1))"]
|
|
471
|
+
]);
|
|
472
|
+
emitRule(".d-label[data-anchor]", [
|
|
473
|
+
["padding-left", "var(--d-label-px)"],
|
|
474
|
+
["border-left", "2px solid var(--d-accent)"]
|
|
475
|
+
]);
|
|
476
|
+
emitRule(".d-link", [
|
|
477
|
+
["color", "var(--d-text)"],
|
|
478
|
+
["text-decoration", "none"],
|
|
479
|
+
["border-bottom", "1px solid transparent"],
|
|
480
|
+
["transition", "color 0.15s ease, border-color 0.15s ease"],
|
|
481
|
+
["cursor", "pointer"]
|
|
482
|
+
]);
|
|
483
|
+
emitRule(".d-link:hover", [
|
|
484
|
+
["color", "var(--d-primary)"],
|
|
485
|
+
["border-bottom-color", "var(--d-primary)"]
|
|
486
|
+
]);
|
|
487
|
+
emitRule(".d-link:focus-visible", [
|
|
488
|
+
["outline", "2px solid var(--d-primary)"],
|
|
489
|
+
["outline-offset", "2px"],
|
|
490
|
+
["border-radius", "2px"]
|
|
491
|
+
]);
|
|
492
|
+
emitRule('.d-link[data-variant="subtle"]', [["color", "var(--d-text-muted)"]]);
|
|
493
|
+
emitRule('.d-link[data-variant="subtle"]:hover', [
|
|
494
|
+
["color", "var(--d-text)"],
|
|
495
|
+
["border-bottom-color", "var(--d-text)"]
|
|
496
|
+
]);
|
|
497
|
+
emitRule('.d-link[data-variant="strong"]', [
|
|
498
|
+
["color", "var(--d-primary)"],
|
|
499
|
+
["font-weight", "500"]
|
|
500
|
+
]);
|
|
501
|
+
emitRule('.d-link[data-variant="strong"]:hover', [
|
|
502
|
+
["color", "var(--d-primary-hover)"],
|
|
503
|
+
["border-bottom-color", "var(--d-primary-hover)"]
|
|
504
|
+
]);
|
|
505
|
+
emitRule('.d-link[aria-current="page"], .d-link[data-active="true"]', [
|
|
506
|
+
["color", "var(--d-primary)"],
|
|
507
|
+
["font-weight", "500"]
|
|
508
|
+
]);
|
|
509
|
+
emitRule(".d-icon-btn", [
|
|
510
|
+
["display", "inline-flex"],
|
|
511
|
+
["align-items", "center"],
|
|
512
|
+
["justify-content", "center"],
|
|
513
|
+
["width", "2rem"],
|
|
514
|
+
["height", "2rem"],
|
|
515
|
+
["border", "none"],
|
|
516
|
+
["background", "transparent"],
|
|
517
|
+
["color", "var(--d-text-muted)"],
|
|
518
|
+
["border-radius", "var(--d-radius-sm)"],
|
|
519
|
+
["cursor", "pointer"],
|
|
520
|
+
["transition", "background 0.15s ease, color 0.15s ease"]
|
|
521
|
+
]);
|
|
522
|
+
emitRule(".d-icon-btn:hover", [
|
|
523
|
+
["background", "color-mix(in srgb, var(--d-text) 8%, transparent)"],
|
|
524
|
+
["color", "var(--d-text)"]
|
|
525
|
+
]);
|
|
526
|
+
emitRule(".d-icon-btn:focus-visible", [
|
|
527
|
+
["outline", "2px solid var(--d-primary)"],
|
|
528
|
+
["outline-offset", "2px"]
|
|
529
|
+
]);
|
|
530
|
+
emitRule(".d-icon-btn:disabled", [
|
|
531
|
+
["opacity", "0.5"],
|
|
532
|
+
["cursor", "not-allowed"],
|
|
533
|
+
["pointer-events", "none"]
|
|
534
|
+
]);
|
|
535
|
+
emitRule('.d-icon-btn[data-size="sm"]', [
|
|
536
|
+
["width", "1.5rem"],
|
|
537
|
+
["height", "1.5rem"]
|
|
538
|
+
]);
|
|
539
|
+
emitRule('.d-icon-btn[data-size="lg"]', [
|
|
540
|
+
["width", "2.5rem"],
|
|
541
|
+
["height", "2.5rem"]
|
|
542
|
+
]);
|
|
543
|
+
emitRule('.d-icon-btn[data-variant="primary"]', [
|
|
544
|
+
["background", "var(--d-primary)"],
|
|
545
|
+
["color", "#fff"]
|
|
546
|
+
]);
|
|
547
|
+
emitRule('.d-icon-btn[data-variant="primary"]:hover', [["background", "var(--d-primary-hover)"]]);
|
|
548
|
+
emitRule(".d-nav-link", [
|
|
549
|
+
["display", "flex"],
|
|
550
|
+
["align-items", "center"],
|
|
551
|
+
["gap", "0.5rem"],
|
|
552
|
+
["padding", "0.5rem 0.75rem"],
|
|
553
|
+
["border-radius", "var(--d-radius-sm)"],
|
|
554
|
+
["color", "var(--d-text-muted)"],
|
|
555
|
+
["text-decoration", "none"],
|
|
556
|
+
["font-size", "0.875rem"],
|
|
557
|
+
["cursor", "pointer"],
|
|
558
|
+
["transition", "background 0.15s ease, color 0.15s ease"],
|
|
559
|
+
["border-left", "2px solid transparent"]
|
|
560
|
+
]);
|
|
561
|
+
emitRule(".d-nav-link:hover", [
|
|
562
|
+
["color", "var(--d-text)"],
|
|
563
|
+
["background", "color-mix(in srgb, var(--d-text) 6%, transparent)"]
|
|
564
|
+
]);
|
|
565
|
+
emitRule(".d-nav-link:focus-visible", [
|
|
566
|
+
["outline", "2px solid var(--d-primary)"],
|
|
567
|
+
["outline-offset", "2px"]
|
|
568
|
+
]);
|
|
569
|
+
emitRule('.d-nav-link[aria-current="page"], .d-nav-link[data-active="true"]', [
|
|
570
|
+
["color", "var(--d-primary)"],
|
|
571
|
+
["background", "color-mix(in srgb, var(--d-primary) 10%, transparent)"],
|
|
572
|
+
["border-left-color", "var(--d-primary)"],
|
|
573
|
+
["font-weight", "500"]
|
|
574
|
+
]);
|
|
575
|
+
emitRule(".d-step-chip", [
|
|
576
|
+
["display", "inline-flex"],
|
|
577
|
+
["align-items", "center"],
|
|
578
|
+
["justify-content", "center"],
|
|
579
|
+
["width", "2rem"],
|
|
580
|
+
["height", "2rem"],
|
|
581
|
+
["border-radius", "50%"],
|
|
582
|
+
["border", "1.5px solid var(--d-border)"],
|
|
583
|
+
["background", "transparent"],
|
|
584
|
+
["color", "var(--d-text-muted)"],
|
|
585
|
+
["font-size", "0.875rem"],
|
|
586
|
+
["font-weight", "600"],
|
|
587
|
+
["font-variant-numeric", "tabular-nums"],
|
|
588
|
+
["transition", "background 0.15s ease, border-color 0.15s ease, color 0.15s ease"]
|
|
589
|
+
]);
|
|
590
|
+
emitRule('.d-step-chip[data-step-state="active"]', [
|
|
591
|
+
["background", "var(--d-primary)"],
|
|
592
|
+
["border-color", "var(--d-primary)"],
|
|
593
|
+
["color", "#fff"]
|
|
594
|
+
]);
|
|
595
|
+
emitRule('.d-step-chip[data-step-state="done"]', [
|
|
596
|
+
["background", "color-mix(in srgb, var(--d-success) 15%, transparent)"],
|
|
597
|
+
["border-color", "var(--d-success)"],
|
|
598
|
+
["color", "var(--d-success)"]
|
|
599
|
+
]);
|
|
600
|
+
emitRule(".d-agent-node", [
|
|
601
|
+
["display", "flex"],
|
|
602
|
+
["flex-direction", "column"],
|
|
603
|
+
["gap", "0.5rem"],
|
|
604
|
+
["position", "relative"],
|
|
605
|
+
["min-width", "200px"],
|
|
606
|
+
["max-width", "260px"],
|
|
607
|
+
["padding", "0.75rem"],
|
|
608
|
+
["background", "var(--d-surface)"],
|
|
609
|
+
["border", "1px solid var(--d-border)"],
|
|
610
|
+
["border-radius", "var(--d-radius)"],
|
|
611
|
+
["cursor", "pointer"],
|
|
612
|
+
["transition", "border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease"]
|
|
613
|
+
]);
|
|
614
|
+
emitRule(".d-agent-node:hover", [
|
|
615
|
+
["border-color", "var(--d-primary)"],
|
|
616
|
+
["transform", "translateY(-1px)"]
|
|
617
|
+
]);
|
|
618
|
+
emitRule('.d-agent-node[data-status="error"]', [
|
|
619
|
+
["box-shadow", "0 0 12px color-mix(in srgb, var(--d-error) 25%, transparent)"],
|
|
620
|
+
["border-color", "color-mix(in srgb, var(--d-error) 60%, transparent)"]
|
|
621
|
+
]);
|
|
622
|
+
emitRule('.d-agent-node[data-status="active"]', [["border-color", "var(--d-primary)"]]);
|
|
623
|
+
emitRule(".d-port", [
|
|
624
|
+
["position", "absolute"],
|
|
625
|
+
["width", "8px"],
|
|
626
|
+
["height", "8px"],
|
|
627
|
+
["border-radius", "50%"],
|
|
628
|
+
["background", "var(--d-border)"],
|
|
629
|
+
["top", "50%"],
|
|
630
|
+
["transform", "translateY(-50%)"],
|
|
631
|
+
["transition", "background 0.15s ease"]
|
|
632
|
+
]);
|
|
633
|
+
emitRule('.d-port[data-side="left"]', [["left", "-4px"]]);
|
|
634
|
+
emitRule('.d-port[data-side="right"]', [["right", "-4px"]]);
|
|
635
|
+
emitRule('.d-port[data-side="top"]', [
|
|
636
|
+
["top", "-4px"],
|
|
637
|
+
["left", "50%"],
|
|
638
|
+
["transform", "translateX(-50%)"]
|
|
639
|
+
]);
|
|
640
|
+
emitRule('.d-port[data-side="bottom"]', [
|
|
641
|
+
["bottom", "-4px"],
|
|
642
|
+
["top", "auto"],
|
|
643
|
+
["left", "50%"],
|
|
644
|
+
["transform", "translateX(-50%)"]
|
|
645
|
+
]);
|
|
646
|
+
emitRule('.d-port[data-active="true"]', [["background", "var(--d-primary)"]]);
|
|
647
|
+
emitRule(".d-cta-banner", [
|
|
648
|
+
["display", "flex"],
|
|
649
|
+
["flex-direction", "column"],
|
|
650
|
+
["align-items", "center"],
|
|
651
|
+
["justify-content", "center"],
|
|
652
|
+
["gap", "1rem"],
|
|
653
|
+
["padding", "3rem 1.5rem"],
|
|
654
|
+
["border-radius", "var(--d-radius-lg)"],
|
|
655
|
+
["text-align", "center"],
|
|
656
|
+
["position", "relative"],
|
|
657
|
+
["overflow", "hidden"],
|
|
658
|
+
// Default gradient uses theme tokens — themes can override the stops
|
|
659
|
+
// via CSS variables --d-cta-gradient if desired.
|
|
660
|
+
[
|
|
661
|
+
"background",
|
|
662
|
+
"var(--d-cta-gradient, linear-gradient(135deg, color-mix(in srgb, var(--d-primary) 85%, transparent), color-mix(in srgb, var(--d-accent) 80%, transparent)))"
|
|
663
|
+
],
|
|
664
|
+
["color", "var(--d-cta-text, #ffffff)"]
|
|
665
|
+
]);
|
|
666
|
+
emitRule('.d-cta-banner[data-size="compact"]', [
|
|
667
|
+
["padding", "1.5rem 1rem"],
|
|
668
|
+
["gap", "0.5rem"]
|
|
669
|
+
]);
|
|
670
|
+
emitRule('.d-cta-banner[data-size="hero"]', [
|
|
671
|
+
["padding", "5rem 2rem"],
|
|
672
|
+
["gap", "1.5rem"]
|
|
673
|
+
]);
|
|
674
|
+
emitRule('.d-interactive[data-variant="dark"]', [
|
|
675
|
+
["background", "var(--d-cta-pill-bg, #18181b)"],
|
|
676
|
+
["color", "var(--d-cta-pill-text, #ffffff)"],
|
|
677
|
+
["border-color", "transparent"],
|
|
678
|
+
["border-radius", "var(--d-radius-full)"]
|
|
679
|
+
]);
|
|
680
|
+
emitRule('.d-interactive[data-variant="dark"]:hover', [
|
|
681
|
+
["background", "var(--d-cta-pill-bg-hover, #27272a)"]
|
|
682
|
+
]);
|
|
683
|
+
emitRule(".d-divider-top", [["border-top", "1px solid var(--d-border)"]]);
|
|
684
|
+
emitRule(".d-divider-bottom", [["border-bottom", "1px solid var(--d-border)"]]);
|
|
685
|
+
emitRule(".d-divider-left", [["border-left", "1px solid var(--d-border)"]]);
|
|
686
|
+
emitRule(".d-divider-right", [["border-right", "1px solid var(--d-border)"]]);
|
|
687
|
+
emitRule(".d-divider", [
|
|
688
|
+
["border", "0"],
|
|
689
|
+
["border-top", "1px solid var(--d-border)"],
|
|
690
|
+
["margin", "0"],
|
|
691
|
+
["width", "100%"],
|
|
692
|
+
["height", "0"]
|
|
693
|
+
]);
|
|
694
|
+
emitRule(".d-shell", [
|
|
695
|
+
["display", "flex"],
|
|
696
|
+
["flex-direction", "column"],
|
|
697
|
+
["min-height", "100vh"],
|
|
698
|
+
["background", "var(--d-bg)"],
|
|
699
|
+
["color", "var(--d-text)"]
|
|
700
|
+
]);
|
|
701
|
+
emitRule('.d-shell[data-layout="sidebar-main"]', [
|
|
702
|
+
["flex-direction", "row"],
|
|
703
|
+
["height", "100vh"],
|
|
704
|
+
["overflow", "hidden"]
|
|
705
|
+
]);
|
|
706
|
+
emitRule('.d-shell[data-layout="centered"]', [
|
|
707
|
+
["align-items", "center"],
|
|
708
|
+
["justify-content", "center"],
|
|
709
|
+
["padding", "1rem"]
|
|
710
|
+
]);
|
|
711
|
+
emitRule('.d-shell[data-layout="top-nav-footer"]', [
|
|
712
|
+
["flex-direction", "column"],
|
|
713
|
+
["height", "100vh"],
|
|
714
|
+
["overflow", "hidden"]
|
|
715
|
+
]);
|
|
716
|
+
emitRule('.d-shell[data-layout="sidebar-aside"]', [
|
|
717
|
+
["display", "grid"],
|
|
718
|
+
["grid-template-columns", "240px 1fr 320px"],
|
|
719
|
+
["grid-template-rows", "1fr"],
|
|
720
|
+
["height", "100vh"],
|
|
721
|
+
["overflow", "hidden"]
|
|
722
|
+
]);
|
|
723
|
+
emitRule(".d-shell-aside", [
|
|
724
|
+
["display", "flex"],
|
|
725
|
+
["flex-direction", "column"],
|
|
726
|
+
["border-left", "1px solid var(--d-border)"],
|
|
727
|
+
["background", "var(--d-surface)"],
|
|
728
|
+
["overflow-y", "auto"]
|
|
729
|
+
]);
|
|
730
|
+
lines.push("@media (max-width: 767.98px) {");
|
|
731
|
+
lines.push(' .d-shell[data-layout="sidebar-aside"] {');
|
|
732
|
+
lines.push(" grid-template-columns: 1fr;");
|
|
733
|
+
lines.push(" grid-template-rows: auto 1fr;");
|
|
734
|
+
lines.push(" }");
|
|
735
|
+
lines.push(' .d-shell[data-layout="sidebar-aside"] .d-shell-aside {');
|
|
736
|
+
lines.push(" display: none;");
|
|
737
|
+
lines.push(" }");
|
|
738
|
+
lines.push(' .d-shell[data-layout="sidebar-aside"] .d-shell-aside[data-mobile-open="true"] {');
|
|
739
|
+
lines.push(" display: flex;");
|
|
740
|
+
lines.push(" position: fixed;");
|
|
741
|
+
lines.push(" inset: 0 0 0 auto;");
|
|
742
|
+
lines.push(" width: min(320px, 100vw);");
|
|
743
|
+
lines.push(" z-index: 50;");
|
|
744
|
+
lines.push(" }");
|
|
745
|
+
lines.push("}");
|
|
746
|
+
lines.push("");
|
|
747
|
+
emitRule(".d-shell-sidebar", [
|
|
748
|
+
["display", "flex"],
|
|
749
|
+
["flex-direction", "column"],
|
|
750
|
+
["width", "240px"],
|
|
751
|
+
["flex-shrink", "0"],
|
|
752
|
+
["border-right", "1px solid var(--d-border)"],
|
|
753
|
+
["background", "var(--d-surface)"],
|
|
754
|
+
["overflow-y", "auto"],
|
|
755
|
+
["transition", "width 0.2s ease, transform 0.2s ease"]
|
|
756
|
+
]);
|
|
757
|
+
emitRule('.d-shell-sidebar[data-collapsed="true"]', [["width", "64px"]]);
|
|
758
|
+
lines.push("@media (max-width: 767.98px) {");
|
|
759
|
+
lines.push(" .d-shell-sidebar {");
|
|
760
|
+
lines.push(" position: fixed;");
|
|
761
|
+
lines.push(" top: 0;");
|
|
762
|
+
lines.push(" left: 0;");
|
|
763
|
+
lines.push(" bottom: 0;");
|
|
764
|
+
lines.push(" z-index: 50;");
|
|
765
|
+
lines.push(" transform: translateX(-100%);");
|
|
766
|
+
lines.push(" }");
|
|
767
|
+
lines.push(' .d-shell-sidebar[data-mobile-open="true"] {');
|
|
768
|
+
lines.push(" transform: translateX(0);");
|
|
769
|
+
lines.push(" }");
|
|
770
|
+
lines.push("}");
|
|
771
|
+
lines.push("");
|
|
772
|
+
emitRule(".d-shell-main", [
|
|
773
|
+
["display", "flex"],
|
|
774
|
+
["flex-direction", "column"],
|
|
775
|
+
["flex", "1"],
|
|
776
|
+
["min-width", "0"],
|
|
777
|
+
["overflow", "hidden"]
|
|
778
|
+
]);
|
|
779
|
+
emitRule(".d-shell-header", [
|
|
780
|
+
["display", "flex"],
|
|
781
|
+
["align-items", "center"],
|
|
782
|
+
["justify-content", "space-between"],
|
|
783
|
+
["gap", "1rem"],
|
|
784
|
+
["height", "52px"],
|
|
785
|
+
["flex-shrink", "0"],
|
|
786
|
+
["padding", "0 clamp(1rem, 2vw, 1.5rem)"],
|
|
787
|
+
["border-bottom", "1px solid var(--d-border)"],
|
|
788
|
+
["background", "var(--d-bg)"],
|
|
789
|
+
["position", "sticky"],
|
|
790
|
+
["top", "0"],
|
|
791
|
+
["z-index", "10"]
|
|
792
|
+
]);
|
|
793
|
+
emitRule(".d-shell-body", [
|
|
794
|
+
["flex", "1"],
|
|
795
|
+
["min-width", "0"],
|
|
796
|
+
["overflow-y", "auto"],
|
|
797
|
+
["overflow-x", "clip"],
|
|
798
|
+
["padding", "1rem"]
|
|
799
|
+
]);
|
|
800
|
+
emitRule('.d-shell-body[data-padding="compact"]', [["padding", "0.75rem"]]);
|
|
801
|
+
emitRule('.d-shell-body[data-padding="spacious"]', [["padding", "1.5rem"]]);
|
|
802
|
+
emitRule('.d-shell-body[data-padding="none"]', [["padding", "0"]]);
|
|
803
|
+
emitRule(".d-shell-footer", [
|
|
804
|
+
["padding", "1rem clamp(1rem, 2vw, 1.5rem)"],
|
|
805
|
+
["border-top", "1px solid var(--d-border)"],
|
|
806
|
+
["background", "var(--d-surface)"],
|
|
807
|
+
["flex-shrink", "0"]
|
|
808
|
+
]);
|
|
809
|
+
emitRule(".d-shell-centered-card", [
|
|
810
|
+
["width", "100%"],
|
|
811
|
+
["max-width", "28rem"],
|
|
812
|
+
["margin-inline", "auto"]
|
|
813
|
+
]);
|
|
814
|
+
emitRule(".d-modal", [
|
|
815
|
+
["position", "fixed"],
|
|
816
|
+
["inset", "0"],
|
|
817
|
+
["z-index", "100"],
|
|
818
|
+
["display", "flex"],
|
|
819
|
+
["align-items", "center"],
|
|
820
|
+
["justify-content", "center"],
|
|
821
|
+
["padding", "1rem"]
|
|
822
|
+
]);
|
|
823
|
+
emitRule('.d-modal[data-align="top"]', [
|
|
824
|
+
["align-items", "flex-start"],
|
|
825
|
+
["padding-top", "15vh"]
|
|
826
|
+
]);
|
|
827
|
+
emitRule(".d-modal-backdrop", [
|
|
828
|
+
["position", "absolute"],
|
|
829
|
+
["inset", "0"],
|
|
830
|
+
["background", "color-mix(in srgb, var(--d-bg) 70%, transparent)"],
|
|
831
|
+
["backdrop-filter", "blur(8px)"],
|
|
832
|
+
["-webkit-backdrop-filter", "blur(8px)"]
|
|
833
|
+
]);
|
|
834
|
+
emitRule(".d-modal-panel", [
|
|
835
|
+
["position", "relative"],
|
|
836
|
+
["z-index", "1"],
|
|
837
|
+
["width", "100%"],
|
|
838
|
+
["max-width", "32rem"],
|
|
839
|
+
["background", "var(--d-surface-raised)"],
|
|
840
|
+
["border", "1px solid var(--d-border)"],
|
|
841
|
+
["border-radius", "var(--d-radius-lg)"],
|
|
842
|
+
["box-shadow", "var(--d-shadow-lg)"],
|
|
843
|
+
["max-height", "85vh"],
|
|
844
|
+
["display", "flex"],
|
|
845
|
+
["flex-direction", "column"],
|
|
846
|
+
["overflow", "hidden"]
|
|
847
|
+
]);
|
|
848
|
+
emitRule('.d-modal-panel[data-size="sm"]', [["max-width", "24rem"]]);
|
|
849
|
+
emitRule('.d-modal-panel[data-size="lg"]', [["max-width", "48rem"]]);
|
|
850
|
+
emitRule(".d-palette", [
|
|
851
|
+
["width", "100%"],
|
|
852
|
+
["max-width", "40rem"],
|
|
853
|
+
["margin-inline", "auto"],
|
|
854
|
+
["background", "var(--d-surface-raised)"],
|
|
855
|
+
["border", "1px solid var(--d-border)"],
|
|
856
|
+
["border-radius", "var(--d-radius-lg)"],
|
|
857
|
+
["box-shadow", "var(--d-shadow-lg)"],
|
|
858
|
+
["display", "flex"],
|
|
859
|
+
["flex-direction", "column"],
|
|
860
|
+
["overflow", "hidden"],
|
|
861
|
+
["max-height", "60vh"]
|
|
862
|
+
]);
|
|
863
|
+
emitRule(".d-palette-input", [
|
|
864
|
+
["padding", "1rem 1.25rem"],
|
|
865
|
+
["border", "0"],
|
|
866
|
+
["border-bottom", "1px solid var(--d-border)"],
|
|
867
|
+
["background", "transparent"],
|
|
868
|
+
["color", "var(--d-text)"],
|
|
869
|
+
["font-size", "1rem"],
|
|
870
|
+
["outline", "0"],
|
|
871
|
+
["width", "100%"]
|
|
872
|
+
]);
|
|
873
|
+
emitRule(".d-palette-list", [
|
|
874
|
+
["flex", "1"],
|
|
875
|
+
["overflow-y", "auto"],
|
|
876
|
+
["padding", "0.5rem"]
|
|
877
|
+
]);
|
|
878
|
+
emitRule(".d-palette-row", [
|
|
879
|
+
["display", "flex"],
|
|
880
|
+
["align-items", "center"],
|
|
881
|
+
["gap", "0.75rem"],
|
|
882
|
+
["padding", "0.5rem 0.75rem"],
|
|
883
|
+
["border-radius", "var(--d-radius-sm)"],
|
|
884
|
+
["cursor", "pointer"],
|
|
885
|
+
["color", "var(--d-text)"],
|
|
886
|
+
["font-size", "0.875rem"],
|
|
887
|
+
["transition", "background 0.1s ease"]
|
|
888
|
+
]);
|
|
889
|
+
emitRule('.d-palette-row:hover, .d-palette-row[data-active="true"]', [
|
|
890
|
+
["background", "color-mix(in srgb, var(--d-primary) 10%, transparent)"]
|
|
891
|
+
]);
|
|
892
|
+
emitRule(".d-palette-section", [
|
|
893
|
+
["padding", "0.5rem 0.75rem 0.25rem"],
|
|
894
|
+
["font-size", "0.7rem"],
|
|
895
|
+
["font-weight", "600"],
|
|
896
|
+
["text-transform", "uppercase"],
|
|
897
|
+
["letter-spacing", "0.08em"],
|
|
898
|
+
["color", "var(--d-text-muted)"]
|
|
899
|
+
]);
|
|
900
|
+
emitRule(".d-kbd", [
|
|
901
|
+
["display", "inline-flex"],
|
|
902
|
+
["align-items", "center"],
|
|
903
|
+
["justify-content", "center"],
|
|
904
|
+
["min-width", "1.5rem"],
|
|
905
|
+
["padding", "0 0.375rem"],
|
|
906
|
+
["height", "1.375rem"],
|
|
907
|
+
["border", "1px solid var(--d-border)"],
|
|
908
|
+
["border-radius", "var(--d-radius-sm)"],
|
|
909
|
+
["background", "var(--d-surface)"],
|
|
910
|
+
["color", "var(--d-text-muted)"],
|
|
911
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
912
|
+
["font-size", "0.75rem"],
|
|
913
|
+
["font-weight", "500"],
|
|
914
|
+
["line-height", "1"]
|
|
915
|
+
]);
|
|
916
|
+
emitRule(".d-enter-fade", [
|
|
917
|
+
[
|
|
918
|
+
"animation",
|
|
919
|
+
"d-fade-in var(--d-motion-base, 250ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)) both"
|
|
920
|
+
]
|
|
921
|
+
]);
|
|
922
|
+
emitRule(".d-enter-slide-up", [
|
|
923
|
+
[
|
|
924
|
+
"animation",
|
|
925
|
+
"d-slide-up var(--d-motion-base, 250ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)) both"
|
|
926
|
+
]
|
|
927
|
+
]);
|
|
928
|
+
emitRule(".d-enter-scale", [
|
|
929
|
+
[
|
|
930
|
+
"animation",
|
|
931
|
+
"d-scale-in var(--d-motion-base, 250ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)) both"
|
|
932
|
+
]
|
|
933
|
+
]);
|
|
934
|
+
emitRule(".d-stagger-children > *", [
|
|
935
|
+
[
|
|
936
|
+
"animation",
|
|
937
|
+
"d-fade-in var(--d-motion-base, 250ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)) both"
|
|
938
|
+
],
|
|
939
|
+
["animation-delay", "calc(var(--d-stagger-index, 0) * var(--d-motion-stagger, 60ms))"]
|
|
940
|
+
]);
|
|
941
|
+
emitRule(".d-pulse", [
|
|
942
|
+
["animation", "d-pulse 2s var(--d-motion-ease, cubic-bezier(0.4,0,0.2,1)) infinite"]
|
|
943
|
+
]);
|
|
944
|
+
emitRule(".d-pulse-ring", [
|
|
945
|
+
["position", "relative"],
|
|
946
|
+
["animation", "d-pulse-ring 1.5s var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)) infinite"]
|
|
947
|
+
]);
|
|
948
|
+
emitRule(".d-shimmer", [
|
|
949
|
+
[
|
|
950
|
+
"background",
|
|
951
|
+
"linear-gradient(90deg, transparent, var(--d-surface-raised) 50%, transparent)"
|
|
952
|
+
],
|
|
953
|
+
["background-size", "200% 100%"],
|
|
954
|
+
["animation", "d-shimmer 1.5s linear infinite"]
|
|
955
|
+
]);
|
|
956
|
+
emitRule(".d-float", [
|
|
957
|
+
["animation", "d-float 3s var(--d-motion-ease, cubic-bezier(0.4,0,0.2,1)) infinite"]
|
|
958
|
+
]);
|
|
959
|
+
emitRule(".d-glow-hover", [
|
|
960
|
+
[
|
|
961
|
+
"transition",
|
|
962
|
+
"box-shadow var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1))"
|
|
963
|
+
]
|
|
964
|
+
]);
|
|
965
|
+
emitRule(".d-glow-hover:hover", [
|
|
966
|
+
[
|
|
967
|
+
"box-shadow",
|
|
968
|
+
"0 0 24px var(--d-accent-glow, color-mix(in srgb, var(--d-accent) 40%, transparent))"
|
|
969
|
+
]
|
|
970
|
+
]);
|
|
971
|
+
emitRule(".d-scale-hover", [
|
|
972
|
+
[
|
|
973
|
+
"transition",
|
|
974
|
+
"transform var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1))"
|
|
975
|
+
]
|
|
976
|
+
]);
|
|
977
|
+
emitRule(".d-scale-hover:hover", [["transform", "scale(1.02)"]]);
|
|
978
|
+
emitRule(".d-lift-hover", [
|
|
979
|
+
[
|
|
980
|
+
"transition",
|
|
981
|
+
"transform var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)), box-shadow var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1))"
|
|
982
|
+
]
|
|
983
|
+
]);
|
|
984
|
+
emitRule(".d-lift-hover:hover", [
|
|
985
|
+
["transform", "translateY(-2px)"],
|
|
986
|
+
["box-shadow", "var(--d-elevation-3, 0 4px 12px rgba(0,0,0,0.10))"]
|
|
987
|
+
]);
|
|
988
|
+
emitRule(".d-ripple", [
|
|
989
|
+
["position", "relative"],
|
|
990
|
+
["overflow", "hidden"]
|
|
991
|
+
]);
|
|
992
|
+
emitRule(".d-ripple::after", [
|
|
993
|
+
["content", "''"],
|
|
994
|
+
["position", "absolute"],
|
|
995
|
+
["inset", "0"],
|
|
996
|
+
["background", "radial-gradient(circle, currentColor 10%, transparent 10.01%)"],
|
|
997
|
+
["opacity", "0"],
|
|
998
|
+
["transform", "scale(0)"],
|
|
999
|
+
[
|
|
1000
|
+
"transition",
|
|
1001
|
+
"transform var(--d-motion-slow, 400ms), opacity var(--d-motion-slow, 400ms)"
|
|
1002
|
+
],
|
|
1003
|
+
["pointer-events", "none"]
|
|
1004
|
+
]);
|
|
1005
|
+
emitRule(".d-ripple:active::after", [
|
|
1006
|
+
["transform", "scale(1)"],
|
|
1007
|
+
["opacity", "0.15"],
|
|
1008
|
+
["transition", "0s"]
|
|
1009
|
+
]);
|
|
1010
|
+
emitRule(".d-display", [
|
|
1011
|
+
["font-family", "var(--d-font-display, var(--d-font-body, system-ui, sans-serif))"],
|
|
1012
|
+
["font-weight", "var(--d-weight-bold, 700)"],
|
|
1013
|
+
["font-size", "var(--d-text-5xl, 3rem)"],
|
|
1014
|
+
["line-height", "var(--d-leading-tight, 1.1)"],
|
|
1015
|
+
["letter-spacing", "var(--d-tracking-tight, -0.02em)"],
|
|
1016
|
+
["margin", "0"]
|
|
1017
|
+
]);
|
|
1018
|
+
emitRule(".d-headline", [
|
|
1019
|
+
["font-family", "var(--d-font-display, var(--d-font-body, system-ui, sans-serif))"],
|
|
1020
|
+
["font-weight", "var(--d-weight-semibold, 600)"],
|
|
1021
|
+
["font-size", "var(--d-text-3xl, 1.875rem)"],
|
|
1022
|
+
["line-height", "var(--d-leading-snug, 1.25)"],
|
|
1023
|
+
["letter-spacing", "var(--d-tracking-tight, -0.01em)"],
|
|
1024
|
+
["margin", "0"]
|
|
1025
|
+
]);
|
|
1026
|
+
emitRule(".d-title", [
|
|
1027
|
+
["font-family", "var(--d-font-display, var(--d-font-body, system-ui, sans-serif))"],
|
|
1028
|
+
["font-weight", "var(--d-weight-semibold, 600)"],
|
|
1029
|
+
["font-size", "var(--d-text-xl, 1.25rem)"],
|
|
1030
|
+
["line-height", "var(--d-leading-snug, 1.25)"],
|
|
1031
|
+
["margin", "0"]
|
|
1032
|
+
]);
|
|
1033
|
+
emitRule(".d-subtitle", [
|
|
1034
|
+
["font-family", "var(--d-font-body, system-ui, sans-serif)"],
|
|
1035
|
+
["font-weight", "var(--d-weight-regular, 400)"],
|
|
1036
|
+
["font-size", "var(--d-text-lg, 1.125rem)"],
|
|
1037
|
+
["line-height", "var(--d-leading-snug, 1.375)"],
|
|
1038
|
+
["color", "var(--d-text-muted)"],
|
|
1039
|
+
["margin", "0"]
|
|
1040
|
+
]);
|
|
1041
|
+
emitRule(".d-prose", [
|
|
1042
|
+
["font-family", "var(--d-font-body, system-ui, sans-serif)"],
|
|
1043
|
+
["font-size", "var(--d-text-base, 1rem)"],
|
|
1044
|
+
["line-height", "var(--d-leading-relaxed, 1.625)"]
|
|
1045
|
+
]);
|
|
1046
|
+
emitRule(".d-body", [
|
|
1047
|
+
["font-family", "var(--d-font-body, system-ui, sans-serif)"],
|
|
1048
|
+
["font-size", "var(--d-text-base, 1rem)"],
|
|
1049
|
+
["line-height", "var(--d-leading-normal, 1.5)"]
|
|
1050
|
+
]);
|
|
1051
|
+
emitRule(".d-caption", [
|
|
1052
|
+
["font-family", "var(--d-font-body, system-ui, sans-serif)"],
|
|
1053
|
+
["font-size", "var(--d-text-sm, 0.875rem)"],
|
|
1054
|
+
["line-height", "var(--d-leading-normal, 1.5)"],
|
|
1055
|
+
["color", "var(--d-text-muted)"]
|
|
1056
|
+
]);
|
|
1057
|
+
emitRule(".d-eyebrow", [
|
|
1058
|
+
["font-family", "var(--d-font-body, system-ui, sans-serif)"],
|
|
1059
|
+
["font-size", "var(--d-text-xs, 0.75rem)"],
|
|
1060
|
+
["line-height", "1.2"],
|
|
1061
|
+
["font-weight", "var(--d-weight-semibold, 600)"],
|
|
1062
|
+
["letter-spacing", "var(--d-tracking-wider, 0.08em)"],
|
|
1063
|
+
["text-transform", "uppercase"],
|
|
1064
|
+
["color", "var(--d-accent)"]
|
|
1065
|
+
]);
|
|
1066
|
+
emitRule(".d-numeric", [["font-variant-numeric", "tabular-nums"]]);
|
|
1067
|
+
emitRule(".d-mono-text", [
|
|
1068
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
1069
|
+
["font-variant-numeric", "tabular-nums"]
|
|
1070
|
+
]);
|
|
1071
|
+
emitRule(".d-card", [
|
|
1072
|
+
["display", "flex"],
|
|
1073
|
+
["flex-direction", "column"],
|
|
1074
|
+
["gap", "0.75rem"],
|
|
1075
|
+
["padding", "1rem"],
|
|
1076
|
+
["background", "var(--d-surface)"],
|
|
1077
|
+
["border", "1px solid var(--d-border)"],
|
|
1078
|
+
["border-radius", "var(--d-radius)"],
|
|
1079
|
+
["box-shadow", "var(--d-elevation-1, var(--d-shadow-sm))"]
|
|
1080
|
+
]);
|
|
1081
|
+
emitRule(".d-card-header", [
|
|
1082
|
+
["display", "flex"],
|
|
1083
|
+
["align-items", "center"],
|
|
1084
|
+
["justify-content", "space-between"],
|
|
1085
|
+
["gap", "0.5rem"],
|
|
1086
|
+
["padding-bottom", "0.5rem"],
|
|
1087
|
+
["border-bottom", "1px solid var(--d-border)"]
|
|
1088
|
+
]);
|
|
1089
|
+
emitRule(".d-card-body", [
|
|
1090
|
+
["display", "flex"],
|
|
1091
|
+
["flex-direction", "column"],
|
|
1092
|
+
["gap", "0.5rem"],
|
|
1093
|
+
["flex", "1 1 auto"],
|
|
1094
|
+
["min-width", "0"]
|
|
1095
|
+
]);
|
|
1096
|
+
emitRule(".d-card-footer", [
|
|
1097
|
+
["display", "flex"],
|
|
1098
|
+
["align-items", "center"],
|
|
1099
|
+
["justify-content", "flex-end"],
|
|
1100
|
+
["gap", "0.5rem"],
|
|
1101
|
+
["padding-top", "0.5rem"],
|
|
1102
|
+
["border-top", "1px solid var(--d-border)"]
|
|
1103
|
+
]);
|
|
1104
|
+
emitRule('.d-card[data-padding="compact"]', [["padding", "0.625rem"]]);
|
|
1105
|
+
emitRule('.d-card[data-padding="spacious"]', [["padding", "1.5rem"]]);
|
|
1106
|
+
emitRule('.d-card[data-padding="none"]', [["padding", "0"]]);
|
|
1107
|
+
emitRule(".d-card[data-interactive]", [
|
|
1108
|
+
["cursor", "pointer"],
|
|
1109
|
+
[
|
|
1110
|
+
"transition",
|
|
1111
|
+
"box-shadow var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)), border-color var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1))"
|
|
1112
|
+
]
|
|
1113
|
+
]);
|
|
1114
|
+
emitRule(".d-card[data-interactive]:hover", [
|
|
1115
|
+
["box-shadow", "var(--d-elevation-2, var(--d-shadow))"],
|
|
1116
|
+
["border-color", "var(--d-primary)"]
|
|
1117
|
+
]);
|
|
1118
|
+
emitRule('.d-elevate[data-level="0"]', [["box-shadow", "var(--d-elevation-0, none)"]]);
|
|
1119
|
+
emitRule('.d-elevate[data-level="1"]', [
|
|
1120
|
+
["box-shadow", "var(--d-elevation-1, 0 1px 2px rgba(0,0,0,0.06))"]
|
|
1121
|
+
]);
|
|
1122
|
+
emitRule('.d-elevate[data-level="2"]', [
|
|
1123
|
+
["box-shadow", "var(--d-elevation-2, 0 2px 4px rgba(0,0,0,0.08))"]
|
|
1124
|
+
]);
|
|
1125
|
+
emitRule('.d-elevate[data-level="3"]', [
|
|
1126
|
+
["box-shadow", "var(--d-elevation-3, 0 4px 12px rgba(0,0,0,0.10))"]
|
|
1127
|
+
]);
|
|
1128
|
+
emitRule('.d-elevate[data-level="4"]', [
|
|
1129
|
+
["box-shadow", "var(--d-elevation-4, 0 8px 24px rgba(0,0,0,0.14))"]
|
|
1130
|
+
]);
|
|
1131
|
+
emitRule('.d-elevate[data-level="5"]', [
|
|
1132
|
+
["box-shadow", "var(--d-elevation-5, 0 16px 48px rgba(0,0,0,0.18))"]
|
|
1133
|
+
]);
|
|
1134
|
+
emitRule(".d-hotkey-indicator", [
|
|
1135
|
+
["position", "fixed"],
|
|
1136
|
+
["bottom", "1.5rem"],
|
|
1137
|
+
["right", "1.5rem"],
|
|
1138
|
+
["padding", "0.5rem 0.75rem"],
|
|
1139
|
+
["background", "var(--d-surface-raised)"],
|
|
1140
|
+
["border", "1px solid var(--d-border)"],
|
|
1141
|
+
["border-radius", "var(--d-radius)"],
|
|
1142
|
+
["box-shadow", "var(--d-elevation-3, 0 4px 12px rgba(0,0,0,0.10))"],
|
|
1143
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
1144
|
+
["font-size", "0.875rem"],
|
|
1145
|
+
["color", "var(--d-text)"],
|
|
1146
|
+
["opacity", "0"],
|
|
1147
|
+
["transform", "translateY(8px)"],
|
|
1148
|
+
["pointer-events", "none"],
|
|
1149
|
+
["z-index", "60"],
|
|
1150
|
+
[
|
|
1151
|
+
"transition",
|
|
1152
|
+
"opacity var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1)), transform var(--d-motion-fast, 150ms) var(--d-motion-ease-out, cubic-bezier(0,0,0.2,1))"
|
|
1153
|
+
]
|
|
1154
|
+
]);
|
|
1155
|
+
emitRule('.d-hotkey-indicator[data-visible="true"]', [
|
|
1156
|
+
["opacity", "1"],
|
|
1157
|
+
["transform", "translateY(0)"]
|
|
1158
|
+
]);
|
|
1159
|
+
emitRule(".d-hotkey-indicator::before", [
|
|
1160
|
+
["content", "'Chord: '"],
|
|
1161
|
+
["color", "var(--d-text-muted)"],
|
|
1162
|
+
["margin-right", "0.25rem"]
|
|
1163
|
+
]);
|
|
1164
|
+
emitRule(".d-hotkey-indicator::after", [
|
|
1165
|
+
["content", "attr(data-prefix) '\u2026'"],
|
|
1166
|
+
["color", "var(--d-accent)"],
|
|
1167
|
+
["font-weight", "600"]
|
|
1168
|
+
]);
|
|
1169
|
+
emitRule(".d-timeline-rail", [
|
|
1170
|
+
["position", "relative"],
|
|
1171
|
+
["padding-left", "2rem"]
|
|
1172
|
+
]);
|
|
1173
|
+
emitRule(".d-timeline-rail::before", [
|
|
1174
|
+
["content", "''"],
|
|
1175
|
+
["position", "absolute"],
|
|
1176
|
+
["left", "0.5rem"],
|
|
1177
|
+
["top", "0.5rem"],
|
|
1178
|
+
["bottom", "0.5rem"],
|
|
1179
|
+
["width", "2px"],
|
|
1180
|
+
["background", "var(--d-border)"]
|
|
140
1181
|
]);
|
|
141
|
-
emitRule(".d-
|
|
142
|
-
["
|
|
1182
|
+
emitRule(".d-timeline-dot", [
|
|
1183
|
+
["position", "relative"],
|
|
1184
|
+
["margin-bottom", "1rem"]
|
|
143
1185
|
]);
|
|
144
|
-
emitRule(".d-
|
|
145
|
-
["
|
|
146
|
-
["
|
|
1186
|
+
emitRule(".d-timeline-dot::before", [
|
|
1187
|
+
["content", "''"],
|
|
1188
|
+
["position", "absolute"],
|
|
1189
|
+
["left", "-1.5rem"],
|
|
1190
|
+
["top", "0.5rem"],
|
|
1191
|
+
["width", "0.625rem"],
|
|
1192
|
+
["height", "0.625rem"],
|
|
1193
|
+
["border-radius", "50%"],
|
|
1194
|
+
["background", "var(--d-border)"],
|
|
1195
|
+
["border", "2px solid var(--d-bg)"],
|
|
1196
|
+
["box-sizing", "border-box"]
|
|
147
1197
|
]);
|
|
148
|
-
emitRule(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
1198
|
+
emitRule('.d-timeline-dot[data-state="active"]::before', [["background", "var(--d-primary)"]]);
|
|
1199
|
+
emitRule('.d-timeline-dot[data-state="done"]::before', [["background", "var(--d-success)"]]);
|
|
1200
|
+
emitRule('.d-timeline-dot[data-state="error"]::before', [["background", "var(--d-error)"]]);
|
|
1201
|
+
emitRule('.d-timeline-dot[data-state="warning"]::before', [["background", "var(--d-warning)"]]);
|
|
1202
|
+
emitRule(".d-sparkline", [
|
|
1203
|
+
["display", "inline-block"],
|
|
1204
|
+
["vertical-align", "middle"],
|
|
1205
|
+
["height", "var(--d-sparkline-height, 1.5rem)"],
|
|
1206
|
+
["width", "var(--d-sparkline-width, 5rem)"]
|
|
1207
|
+
]);
|
|
1208
|
+
emitRule(".d-sparkline-path", [
|
|
1209
|
+
["fill", "none"],
|
|
1210
|
+
["stroke", "var(--d-primary)"],
|
|
1211
|
+
["stroke-width", "1.5"],
|
|
1212
|
+
["stroke-linecap", "round"],
|
|
1213
|
+
["stroke-linejoin", "round"]
|
|
1214
|
+
]);
|
|
1215
|
+
emitRule(".d-sparkline-area", [
|
|
1216
|
+
["fill", "color-mix(in srgb, var(--d-primary) 15%, transparent)"],
|
|
1217
|
+
["stroke", "none"]
|
|
1218
|
+
]);
|
|
1219
|
+
emitRule('.d-sparkline[data-trend="up"] .d-sparkline-path', [["stroke", "var(--d-success)"]]);
|
|
1220
|
+
emitRule('.d-sparkline[data-trend="up"] .d-sparkline-area', [
|
|
1221
|
+
["fill", "color-mix(in srgb, var(--d-success) 15%, transparent)"]
|
|
1222
|
+
]);
|
|
1223
|
+
emitRule('.d-sparkline[data-trend="down"] .d-sparkline-path', [["stroke", "var(--d-error)"]]);
|
|
1224
|
+
emitRule('.d-sparkline[data-trend="down"] .d-sparkline-area', [
|
|
1225
|
+
["fill", "color-mix(in srgb, var(--d-error) 15%, transparent)"]
|
|
1226
|
+
]);
|
|
1227
|
+
emitRule(".d-intent-radar", [
|
|
1228
|
+
["position", "relative"],
|
|
1229
|
+
["width", "var(--d-radar-size, 200px)"],
|
|
1230
|
+
["height", "var(--d-radar-size, 200px)"]
|
|
1231
|
+
]);
|
|
1232
|
+
emitRule(".d-intent-radar-ring", [
|
|
1233
|
+
["position", "absolute"],
|
|
1234
|
+
["border-radius", "50%"],
|
|
153
1235
|
["border", "1px solid var(--d-border)"],
|
|
154
|
-
["
|
|
155
|
-
["outline", "none"],
|
|
156
|
-
["font", "inherit"],
|
|
157
|
-
["transition", "border-color 0.15s ease, box-shadow 0.15s ease"]
|
|
1236
|
+
["inset", "0"]
|
|
158
1237
|
]);
|
|
159
|
-
emitRule(
|
|
160
|
-
|
|
161
|
-
|
|
1238
|
+
emitRule('.d-intent-radar-ring[data-level="2"]', [["inset", "12.5%"]]);
|
|
1239
|
+
emitRule('.d-intent-radar-ring[data-level="3"]', [["inset", "25%"]]);
|
|
1240
|
+
emitRule('.d-intent-radar-ring[data-level="4"]', [["inset", "37.5%"]]);
|
|
1241
|
+
emitRule('.d-intent-radar-ring[data-level="5"]', [["inset", "50%"]]);
|
|
1242
|
+
emitRule(".d-intent-radar-axis", [
|
|
1243
|
+
["position", "absolute"],
|
|
1244
|
+
["top", "50%"],
|
|
1245
|
+
["left", "50%"],
|
|
1246
|
+
["width", "50%"],
|
|
1247
|
+
["height", "1px"],
|
|
1248
|
+
["background", "var(--d-border)"],
|
|
1249
|
+
["transform-origin", "0 0"],
|
|
1250
|
+
["transform", "rotate(var(--d-radar-axis-angle, 0deg))"]
|
|
162
1251
|
]);
|
|
163
|
-
emitRule(".d-
|
|
164
|
-
["
|
|
1252
|
+
emitRule(".d-waveform", [
|
|
1253
|
+
["display", "block"],
|
|
1254
|
+
["width", "100%"],
|
|
1255
|
+
["height", "var(--d-waveform-height, 3rem)"],
|
|
1256
|
+
["overflow", "hidden"]
|
|
165
1257
|
]);
|
|
166
|
-
emitRule(".d-
|
|
167
|
-
["
|
|
168
|
-
["
|
|
1258
|
+
emitRule(".d-waveform-path", [
|
|
1259
|
+
["fill", "color-mix(in srgb, var(--d-primary) 30%, transparent)"],
|
|
1260
|
+
["stroke", "var(--d-primary)"],
|
|
1261
|
+
["stroke-width", "1"]
|
|
169
1262
|
]);
|
|
170
|
-
emitRule(
|
|
171
|
-
["
|
|
172
|
-
["
|
|
1263
|
+
emitRule('.d-waveform[data-state="active"] .d-waveform-path', [
|
|
1264
|
+
["fill", "color-mix(in srgb, var(--d-success) 30%, transparent)"],
|
|
1265
|
+
["stroke", "var(--d-success)"]
|
|
173
1266
|
]);
|
|
174
|
-
emitRule(".d-
|
|
175
|
-
["
|
|
176
|
-
["
|
|
1267
|
+
emitRule(".d-qr-placeholder", [
|
|
1268
|
+
["display", "block"],
|
|
1269
|
+
["width", "var(--d-qr-size, 8rem)"],
|
|
1270
|
+
["height", "var(--d-qr-size, 8rem)"],
|
|
1271
|
+
[
|
|
1272
|
+
"background",
|
|
1273
|
+
"repeating-linear-gradient(0deg, var(--d-text) 0 4px, transparent 4px 8px), repeating-linear-gradient(90deg, var(--d-text) 0 4px, transparent 4px 8px)"
|
|
1274
|
+
],
|
|
1275
|
+
["background-color", "var(--d-surface)"],
|
|
1276
|
+
["border-radius", "var(--d-radius-sm)"],
|
|
1277
|
+
["border", "8px solid var(--d-surface)"],
|
|
1278
|
+
["outline", "1px solid var(--d-border)"]
|
|
177
1279
|
]);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
lines.push(" border-image: linear-gradient(to right, transparent, var(--d-border), transparent) 1;");
|
|
189
|
-
lines.push(" margin-top: calc(var(--d-section-gap) * var(--d-density-scale, 1));");
|
|
190
|
-
lines.push("}");
|
|
191
|
-
lines.push("");
|
|
192
|
-
emitRule(".d-annotation", [
|
|
1280
|
+
emitRule(".d-conic-ring", [
|
|
1281
|
+
["--d-conic-value", "0.5"],
|
|
1282
|
+
["width", "var(--d-conic-size, 4rem)"],
|
|
1283
|
+
["height", "var(--d-conic-size, 4rem)"],
|
|
1284
|
+
["border-radius", "50%"],
|
|
1285
|
+
[
|
|
1286
|
+
"background",
|
|
1287
|
+
"conic-gradient(var(--d-conic-color, var(--d-primary)) 0deg, var(--d-conic-color, var(--d-primary)) calc(var(--d-conic-value) * 360deg), var(--d-border) calc(var(--d-conic-value) * 360deg), var(--d-border) 360deg)"
|
|
1288
|
+
],
|
|
1289
|
+
["position", "relative"],
|
|
193
1290
|
["display", "inline-flex"],
|
|
194
1291
|
["align-items", "center"],
|
|
195
|
-
["
|
|
196
|
-
["font-size", "0.75rem"],
|
|
197
|
-
["font-weight", "500"],
|
|
198
|
-
["padding", "0.125rem 0.5rem"],
|
|
199
|
-
["border-radius", "var(--d-radius-full)"],
|
|
200
|
-
["background", "color-mix(in srgb, var(--d-surface-raised) 88%, transparent)"],
|
|
201
|
-
["border", "1px solid color-mix(in srgb, var(--d-border) 72%, transparent)"],
|
|
202
|
-
["color", "var(--d-text-muted)"],
|
|
203
|
-
["white-space", "nowrap"]
|
|
204
|
-
]);
|
|
205
|
-
emitRule('.d-annotation[data-status="success"]', [
|
|
206
|
-
["background", "color-mix(in srgb, var(--d-success) 15%, transparent)"],
|
|
207
|
-
["color", "var(--d-success)"]
|
|
1292
|
+
["justify-content", "center"]
|
|
208
1293
|
]);
|
|
209
|
-
emitRule(
|
|
210
|
-
["
|
|
211
|
-
["
|
|
1294
|
+
emitRule(".d-conic-ring::before", [
|
|
1295
|
+
["content", "''"],
|
|
1296
|
+
["position", "absolute"],
|
|
1297
|
+
["inset", "var(--d-conic-thickness, 0.5rem)"],
|
|
1298
|
+
["border-radius", "50%"],
|
|
1299
|
+
["background", "var(--d-bg)"]
|
|
212
1300
|
]);
|
|
213
|
-
emitRule(
|
|
214
|
-
["
|
|
215
|
-
["
|
|
1301
|
+
emitRule(".d-conic-ring > *", [
|
|
1302
|
+
["position", "relative"],
|
|
1303
|
+
["z-index", "1"]
|
|
216
1304
|
]);
|
|
217
|
-
emitRule('.d-
|
|
218
|
-
|
|
219
|
-
|
|
1305
|
+
emitRule('.d-conic-ring[data-state="success"]', [["--d-conic-color", "var(--d-success)"]]);
|
|
1306
|
+
emitRule('.d-conic-ring[data-state="warning"]', [["--d-conic-color", "var(--d-warning)"]]);
|
|
1307
|
+
emitRule('.d-conic-ring[data-state="error"]', [["--d-conic-color", "var(--d-error)"]]);
|
|
1308
|
+
emitRule(".d-heatmap-cell", [
|
|
1309
|
+
["--d-heatmap-intensity", "0"],
|
|
1310
|
+
["display", "inline-block"],
|
|
1311
|
+
["width", "var(--d-heatmap-cell-size, 0.875rem)"],
|
|
1312
|
+
["height", "var(--d-heatmap-cell-size, 0.875rem)"],
|
|
1313
|
+
["border-radius", "2px"],
|
|
1314
|
+
[
|
|
1315
|
+
"background",
|
|
1316
|
+
"color-mix(in srgb, var(--d-primary) calc(var(--d-heatmap-intensity) * 100%), var(--d-surface))"
|
|
1317
|
+
],
|
|
1318
|
+
["border", "1px solid var(--d-border)"],
|
|
1319
|
+
["vertical-align", "middle"]
|
|
220
1320
|
]);
|
|
221
|
-
emitRule(
|
|
222
|
-
[
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
["color", "var(--d-text-muted)"],
|
|
227
|
-
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
228
|
-
["display", "block"],
|
|
229
|
-
["margin-bottom", "calc(var(--d-label-mb) * var(--d-density-scale, 1))"]
|
|
1321
|
+
emitRule('.d-heatmap-cell[data-status="error"]', [
|
|
1322
|
+
[
|
|
1323
|
+
"background",
|
|
1324
|
+
"color-mix(in srgb, var(--d-error) calc(var(--d-heatmap-intensity) * 100%), var(--d-surface))"
|
|
1325
|
+
]
|
|
230
1326
|
]);
|
|
231
|
-
emitRule(
|
|
232
|
-
[
|
|
233
|
-
|
|
1327
|
+
emitRule('.d-heatmap-cell[data-status="success"]', [
|
|
1328
|
+
[
|
|
1329
|
+
"background",
|
|
1330
|
+
"color-mix(in srgb, var(--d-success) calc(var(--d-heatmap-intensity) * 100%), var(--d-surface))"
|
|
1331
|
+
]
|
|
234
1332
|
]);
|
|
235
1333
|
if (themeOverrideRules.length > 0) {
|
|
236
1334
|
lines.push("/* \u2500\u2500 Theme-scoped Treatment Overrides \u2500\u2500 */");
|
|
@@ -247,6 +1345,59 @@ ${themeBody}
|
|
|
247
1345
|
lines.push(" 50% { opacity: 0.5; }");
|
|
248
1346
|
lines.push("}");
|
|
249
1347
|
lines.push("");
|
|
1348
|
+
lines.push("@keyframes d-fade-in {");
|
|
1349
|
+
lines.push(" from { opacity: 0; }");
|
|
1350
|
+
lines.push(" to { opacity: 1; }");
|
|
1351
|
+
lines.push("}");
|
|
1352
|
+
lines.push("");
|
|
1353
|
+
lines.push("@keyframes d-slide-up {");
|
|
1354
|
+
lines.push(" from { opacity: 0; transform: translateY(12px); }");
|
|
1355
|
+
lines.push(" to { opacity: 1; transform: translateY(0); }");
|
|
1356
|
+
lines.push("}");
|
|
1357
|
+
lines.push("");
|
|
1358
|
+
lines.push("@keyframes d-scale-in {");
|
|
1359
|
+
lines.push(" from { opacity: 0; transform: scale(0.96); }");
|
|
1360
|
+
lines.push(" to { opacity: 1; transform: scale(1); }");
|
|
1361
|
+
lines.push("}");
|
|
1362
|
+
lines.push("");
|
|
1363
|
+
lines.push("@keyframes d-pulse {");
|
|
1364
|
+
lines.push(" 0%, 100% { opacity: 1; }");
|
|
1365
|
+
lines.push(" 50% { opacity: 0.5; }");
|
|
1366
|
+
lines.push("}");
|
|
1367
|
+
lines.push("");
|
|
1368
|
+
lines.push("@keyframes d-pulse-ring {");
|
|
1369
|
+
lines.push(
|
|
1370
|
+
" 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--d-primary) 40%, transparent); }"
|
|
1371
|
+
);
|
|
1372
|
+
lines.push(" 100% { box-shadow: 0 0 0 12px transparent; }");
|
|
1373
|
+
lines.push("}");
|
|
1374
|
+
lines.push("");
|
|
1375
|
+
lines.push("@keyframes d-shimmer {");
|
|
1376
|
+
lines.push(" 0% { background-position: -200% 0; }");
|
|
1377
|
+
lines.push(" 100% { background-position: 200% 0; }");
|
|
1378
|
+
lines.push("}");
|
|
1379
|
+
lines.push("");
|
|
1380
|
+
lines.push("@keyframes d-float {");
|
|
1381
|
+
lines.push(" 0%, 100% { transform: translateY(0); }");
|
|
1382
|
+
lines.push(" 50% { transform: translateY(-4px); }");
|
|
1383
|
+
lines.push("}");
|
|
1384
|
+
lines.push("");
|
|
1385
|
+
lines.push("/* Respect user motion preferences \u2014 disable all declarative motion */");
|
|
1386
|
+
lines.push("@media (prefers-reduced-motion: reduce) {");
|
|
1387
|
+
lines.push(
|
|
1388
|
+
" .d-enter-fade, .d-enter-slide-up, .d-enter-scale, .d-stagger-children > *,"
|
|
1389
|
+
);
|
|
1390
|
+
lines.push(" .d-pulse, .d-pulse-ring, .d-shimmer, .d-float {");
|
|
1391
|
+
lines.push(" animation: none !important;");
|
|
1392
|
+
lines.push(" }");
|
|
1393
|
+
lines.push(" .d-glow-hover, .d-scale-hover, .d-lift-hover, .d-ripple::after {");
|
|
1394
|
+
lines.push(" transition: none !important;");
|
|
1395
|
+
lines.push(" }");
|
|
1396
|
+
lines.push(" .d-scale-hover:hover, .d-lift-hover:hover {");
|
|
1397
|
+
lines.push(" transform: none !important;");
|
|
1398
|
+
lines.push(" }");
|
|
1399
|
+
lines.push("}");
|
|
1400
|
+
lines.push("");
|
|
250
1401
|
lines.push("} /* end @layer treatments */");
|
|
251
1402
|
const decoratorRules = [];
|
|
252
1403
|
if (themeDecoratorDefinitions) {
|
|
@@ -305,37 +1456,59 @@ function generatePersonalityCSS(personality, themeData) {
|
|
|
305
1456
|
if (text.includes("neon") || text.includes("glow")) {
|
|
306
1457
|
const glowColor = "var(--d-accent-glow, rgba(0, 212, 255, 0.3))";
|
|
307
1458
|
rules.push(`.neon-glow { box-shadow: 0 0 20px ${glowColor}; }`);
|
|
308
|
-
rules.push(
|
|
1459
|
+
rules.push(
|
|
1460
|
+
`.neon-glow-hover:hover { box-shadow: 0 0 24px ${glowColor}; transition: box-shadow var(--d-duration-hover, 0.15s) var(--d-easing, ease); }`
|
|
1461
|
+
);
|
|
309
1462
|
rules.push(`.neon-text-glow { text-shadow: 0 0 12px ${glowColor}; }`);
|
|
310
|
-
rules.push(
|
|
1463
|
+
rules.push(
|
|
1464
|
+
`.neon-border-glow { border-color: var(--d-accent); box-shadow: 0 0 8px ${glowColor}; }`
|
|
1465
|
+
);
|
|
311
1466
|
}
|
|
312
1467
|
if (text.includes("monospace") || text.includes("mono")) {
|
|
313
|
-
rules.push(
|
|
1468
|
+
rules.push(
|
|
1469
|
+
`.mono-data { font-family: var(--d-font-mono, ui-monospace, monospace); font-variant-numeric: tabular-nums; }`
|
|
1470
|
+
);
|
|
314
1471
|
}
|
|
315
1472
|
if (text.includes("pulse") || text.includes("ring") || text.includes("status")) {
|
|
316
|
-
rules.push(
|
|
1473
|
+
rules.push(
|
|
1474
|
+
`.status-ring { width: 48px; height: 48px; border-radius: 50%; border: 2px solid var(--d-border); display: flex; align-items: center; justify-content: center; position: relative; transition: border-color 0.2s ease, box-shadow 0.2s ease; }`
|
|
1475
|
+
);
|
|
1476
|
+
rules.push(`.status-ring[data-size="sm"] { width: 32px; height: 32px; border-width: 1.5px; }`);
|
|
1477
|
+
rules.push(`.status-ring[data-size="md"] { width: 48px; height: 48px; border-width: 2px; }`);
|
|
1478
|
+
rules.push(`.status-ring[data-size="lg"] { width: 64px; height: 64px; border-width: 2.5px; }`);
|
|
317
1479
|
rules.push(`.status-ring[data-status="active"] { border-color: var(--d-success); }`);
|
|
318
|
-
rules.push(
|
|
1480
|
+
rules.push(
|
|
1481
|
+
`.status-ring[data-status="error"] { border-color: var(--d-error); box-shadow: 0 0 12px color-mix(in srgb, var(--d-error) 25%, transparent); }`
|
|
1482
|
+
);
|
|
319
1483
|
rules.push(`.status-ring[data-status="warning"] { border-color: var(--d-warning); }`);
|
|
320
1484
|
rules.push(`.status-ring[data-status="idle"] { border-color: var(--d-text-muted); }`);
|
|
321
|
-
rules.push(
|
|
322
|
-
|
|
323
|
-
|
|
1485
|
+
rules.push(
|
|
1486
|
+
`.status-ring[data-status="processing"] { border-color: var(--d-primary); animation: pulse-ring 2s ease-in-out infinite; }`
|
|
1487
|
+
);
|
|
1488
|
+
rules.push(
|
|
1489
|
+
`@keyframes pulse-ring { 0% { opacity: 0.6; transform: scale(1); } 100% { opacity: 0; transform: scale(1.3); } }`
|
|
1490
|
+
);
|
|
1491
|
+
rules.push(
|
|
1492
|
+
`.status-ring[data-status="active"]::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--d-success); opacity: 0; animation: pulse-ring 2s ease-out infinite; }`
|
|
1493
|
+
);
|
|
324
1494
|
}
|
|
325
1495
|
if (themeData.motion?.entrance) {
|
|
326
|
-
rules.push(
|
|
327
|
-
|
|
1496
|
+
rules.push(
|
|
1497
|
+
`.entrance-fade { animation: decantr-entrance var(--d-duration-entrance, 0.2s) var(--d-easing, ease-out); }`
|
|
1498
|
+
);
|
|
1499
|
+
rules.push(
|
|
1500
|
+
`@keyframes decantr-entrance { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }`
|
|
1501
|
+
);
|
|
328
1502
|
}
|
|
329
1503
|
if (rules.length === 0) return "";
|
|
330
1504
|
return "\n@layer utilities {\n\n/* \u2500\u2500 Personality-Derived Utilities \u2500\u2500 */\n\n" + rules.join("\n\n") + "\n\n} /* end @layer utilities */\n";
|
|
331
1505
|
}
|
|
332
1506
|
|
|
333
1507
|
// src/scaffold.ts
|
|
334
|
-
import { API_CONTENT_TYPES } from "@decantr/registry";
|
|
335
1508
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
336
1509
|
function getPlatformMeta(target) {
|
|
337
1510
|
const normalized = (target || "react").toLowerCase();
|
|
338
|
-
const routing = normalized === "nextjs" ? "pathname" : "
|
|
1511
|
+
const routing = normalized === "nextjs" ? "pathname" : "history";
|
|
339
1512
|
return {
|
|
340
1513
|
type: "spa",
|
|
341
1514
|
routing
|
|
@@ -372,19 +1545,28 @@ function collectPatternIdsFromItems(items) {
|
|
|
372
1545
|
return [...ids];
|
|
373
1546
|
}
|
|
374
1547
|
function mapRegistryArchetypeToArchetypeData(archetype) {
|
|
1548
|
+
const registryExtras = archetype;
|
|
1549
|
+
const registryNavigation = registryExtras.navigation_items;
|
|
1550
|
+
const registryDirectives = registryExtras.directives;
|
|
375
1551
|
return {
|
|
376
1552
|
id: archetype.id,
|
|
377
1553
|
name: archetype.name,
|
|
378
1554
|
role: archetype.role,
|
|
379
1555
|
description: archetype.description,
|
|
380
|
-
pages: archetype.pages?.map((page) =>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
1556
|
+
pages: archetype.pages?.map((page) => {
|
|
1557
|
+
const pageExtras = page;
|
|
1558
|
+
return {
|
|
1559
|
+
id: page.id,
|
|
1560
|
+
shell: page.shell,
|
|
1561
|
+
default_layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
1562
|
+
patterns: page.patterns?.map(toPatternReferenceObject),
|
|
1563
|
+
...Array.isArray(pageExtras.directives) && pageExtras.directives.length > 0 ? { directives: pageExtras.directives } : {}
|
|
1564
|
+
};
|
|
1565
|
+
}),
|
|
386
1566
|
features: archetype.features,
|
|
387
|
-
seo_hints: archetype.seo_hints
|
|
1567
|
+
seo_hints: archetype.seo_hints,
|
|
1568
|
+
...Array.isArray(registryNavigation) && registryNavigation.length > 0 ? { navigation_items: registryNavigation } : {},
|
|
1569
|
+
...Array.isArray(registryDirectives) && registryDirectives.length > 0 ? { directives: registryDirectives } : {}
|
|
388
1570
|
};
|
|
389
1571
|
}
|
|
390
1572
|
function composeArchetypes(composeEntries, archetypeResults) {
|
|
@@ -409,7 +1591,9 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
409
1591
|
for (const page of data.pages) {
|
|
410
1592
|
allPages.push({
|
|
411
1593
|
id: page.id,
|
|
412
|
-
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1594
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1595
|
+
(item) => resolvePatternAlias(item, page.patterns)
|
|
1596
|
+
),
|
|
413
1597
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
414
1598
|
});
|
|
415
1599
|
}
|
|
@@ -418,7 +1602,9 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
418
1602
|
for (const page of data.pages) {
|
|
419
1603
|
allPages.push({
|
|
420
1604
|
id: `${prefix}-${page.id}`,
|
|
421
|
-
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1605
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1606
|
+
(item) => resolvePatternAlias(item, page.patterns)
|
|
1607
|
+
),
|
|
422
1608
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
423
1609
|
});
|
|
424
1610
|
}
|
|
@@ -439,14 +1625,16 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
439
1625
|
function composeSections(composeEntries, archetypeResults, overrides) {
|
|
440
1626
|
if (composeEntries.length === 0) {
|
|
441
1627
|
return {
|
|
442
|
-
sections: [
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
1628
|
+
sections: [
|
|
1629
|
+
{
|
|
1630
|
+
id: "default",
|
|
1631
|
+
role: "primary",
|
|
1632
|
+
shell: "sidebar-main",
|
|
1633
|
+
features: [],
|
|
1634
|
+
description: "Default section",
|
|
1635
|
+
pages: [{ id: "home", layout: ["hero"] }]
|
|
1636
|
+
}
|
|
1637
|
+
],
|
|
450
1638
|
features: [],
|
|
451
1639
|
defaultShell: "sidebar-main"
|
|
452
1640
|
};
|
|
@@ -470,7 +1658,13 @@ function composeSections(composeEntries, archetypeResults, overrides) {
|
|
|
470
1658
|
const overriddenPage = overrides?.pages?.[page.id];
|
|
471
1659
|
pages.push({
|
|
472
1660
|
id: page.id,
|
|
473
|
-
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1661
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map(
|
|
1662
|
+
(item) => resolvePatternAlias(item, page.patterns)
|
|
1663
|
+
),
|
|
1664
|
+
// Propagate per-page directives from the archetype so cold LLMs
|
|
1665
|
+
// see execution-level rules in the page-pack contract instead of
|
|
1666
|
+
// having to read the section narrative.
|
|
1667
|
+
...Array.isArray(page.directives) && page.directives.length > 0 ? { directives: page.directives } : {},
|
|
474
1668
|
...overriddenPage
|
|
475
1669
|
});
|
|
476
1670
|
}
|
|
@@ -480,7 +1674,14 @@ function composeSections(composeEntries, archetypeResults, overrides) {
|
|
|
480
1674
|
shell: data.pages[0]?.shell || "sidebar-main",
|
|
481
1675
|
features: data.features ?? [],
|
|
482
1676
|
description: data.description ?? "",
|
|
483
|
-
pages
|
|
1677
|
+
pages,
|
|
1678
|
+
// Propagate navigation_items from the archetype so the shell renders
|
|
1679
|
+
// the correct primary-nav list instead of the LLM improvising one.
|
|
1680
|
+
...Array.isArray(data.navigation_items) && data.navigation_items.length > 0 ? { navigation_items: data.navigation_items } : {},
|
|
1681
|
+
// Propagate section-level directives from the archetype into the
|
|
1682
|
+
// essence so the section-pack renderer can surface them in the
|
|
1683
|
+
// pack contract.
|
|
1684
|
+
...Array.isArray(data.directives) && data.directives.length > 0 ? { directives: data.directives } : {}
|
|
484
1685
|
});
|
|
485
1686
|
if (data.features) {
|
|
486
1687
|
allFeatures.push(...data.features);
|
|
@@ -575,7 +1776,12 @@ function deriveTransitions(zones) {
|
|
|
575
1776
|
const hasGateway = roles.has("gateway");
|
|
576
1777
|
const hasPublic = roles.has("public");
|
|
577
1778
|
if (hasPublic && hasGateway) {
|
|
578
|
-
transitions.push({
|
|
1779
|
+
transitions.push({
|
|
1780
|
+
from: "public",
|
|
1781
|
+
to: "gateway",
|
|
1782
|
+
type: "conversion",
|
|
1783
|
+
trigger: gatewayTrigger
|
|
1784
|
+
});
|
|
579
1785
|
}
|
|
580
1786
|
if (hasPublic && hasApp && !hasGateway) {
|
|
581
1787
|
transitions.push({ from: "public", to: "app", type: "conversion", trigger: "navigation" });
|
|
@@ -651,7 +1857,7 @@ function mapRegistryThemeToThemeData(theme) {
|
|
|
651
1857
|
pattern_preferences: theme.pattern_preferences
|
|
652
1858
|
};
|
|
653
1859
|
}
|
|
654
|
-
function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
1860
|
+
function generateTokensCSS(themeData, mode, spatialTokens, options) {
|
|
655
1861
|
if (!themeData) {
|
|
656
1862
|
const spatialLines2 = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
|
|
657
1863
|
return `/* No theme data available */
|
|
@@ -673,19 +1879,34 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
|
673
1879
|
const seed = themeData.seed || {};
|
|
674
1880
|
const palette = themeData.palette || {};
|
|
675
1881
|
const resolvedMode = mode === "auto" ? "dark" : mode;
|
|
1882
|
+
const FALLBACKS = {
|
|
1883
|
+
bg: { light: "#ffffff", dark: "#18181b" },
|
|
1884
|
+
surface: { light: "#f9fafb", dark: "#1f1f23" },
|
|
1885
|
+
"surface-raised": { light: "#ffffff", dark: "#27272a" },
|
|
1886
|
+
border: { light: "#e5e7eb", dark: "#3f3f46" },
|
|
1887
|
+
text: { light: "#111827", dark: "#fafafa" },
|
|
1888
|
+
"text-muted": { light: "#6b7280", dark: "#a1a1aa" },
|
|
1889
|
+
secondary: { light: "#6b7280", dark: "#A1A1AA" }
|
|
1890
|
+
};
|
|
676
1891
|
function buildTokens(tokenMode) {
|
|
1892
|
+
const tokenModeKey = tokenMode === "light" ? "light" : "dark";
|
|
1893
|
+
const pickFb = (key) => {
|
|
1894
|
+
const fallbacks = FALLBACKS[key];
|
|
1895
|
+
if (!fallbacks) return tokenModeKey === "light" ? "#ffffff" : "#18181b";
|
|
1896
|
+
return fallbacks[tokenModeKey];
|
|
1897
|
+
};
|
|
677
1898
|
return {
|
|
678
1899
|
// Seed colors
|
|
679
1900
|
"--d-primary": seed.primary || "#6366f1",
|
|
680
|
-
"--d-secondary": palette.secondary?.[tokenMode] ||
|
|
1901
|
+
"--d-secondary": palette.secondary?.[tokenMode] || pickFb("secondary"),
|
|
681
1902
|
"--d-accent": seed.accent || "#f59e0b",
|
|
682
|
-
// Palette colors (mode-aware)
|
|
683
|
-
"--d-bg": palette.background?.[tokenMode] || "
|
|
684
|
-
"--d-surface": palette.surface?.[tokenMode] || "
|
|
685
|
-
"--d-surface-raised": palette["surface-raised"]?.[tokenMode] || "
|
|
686
|
-
"--d-border": palette.border?.[tokenMode] || "
|
|
687
|
-
"--d-text": palette.text?.[tokenMode] || "
|
|
688
|
-
"--d-text-muted": palette["text-muted"]?.[tokenMode] || "
|
|
1903
|
+
// Palette colors (mode-aware with mode-aware fallbacks)
|
|
1904
|
+
"--d-bg": palette.background?.[tokenMode] || pickFb("bg"),
|
|
1905
|
+
"--d-surface": palette.surface?.[tokenMode] || pickFb("surface"),
|
|
1906
|
+
"--d-surface-raised": palette["surface-raised"]?.[tokenMode] || pickFb("surface-raised"),
|
|
1907
|
+
"--d-border": palette.border?.[tokenMode] || pickFb("border"),
|
|
1908
|
+
"--d-text": palette.text?.[tokenMode] || pickFb("text"),
|
|
1909
|
+
"--d-text-muted": palette["text-muted"]?.[tokenMode] || pickFb("text-muted"),
|
|
689
1910
|
"--d-primary-hover": palette["primary-hover"]?.[tokenMode] || seed.primary || "#6366f1",
|
|
690
1911
|
// Spacing scale
|
|
691
1912
|
"--d-gap-1": "0.25rem",
|
|
@@ -706,11 +1927,62 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
|
706
1927
|
"--d-shadow": tokenMode === "light" ? "0 1px 3px rgba(0,0,0,0.1)" : "0 1px 3px rgba(0,0,0,0.25)",
|
|
707
1928
|
"--d-shadow-md": tokenMode === "light" ? "0 4px 6px rgba(0,0,0,0.1)" : "0 4px 6px rgba(0,0,0,0.3)",
|
|
708
1929
|
"--d-shadow-lg": tokenMode === "light" ? "0 10px 15px rgba(0,0,0,0.1)" : "0 10px 15px rgba(0,0,0,0.4)",
|
|
1930
|
+
// Elevation scale (v2.1 Tier B3). Formal cross-theme depth system.
|
|
1931
|
+
// .d-elevate[data-level="N"] reads these. Dark themes need stronger
|
|
1932
|
+
// alpha to register on dark backgrounds.
|
|
1933
|
+
"--d-elevation-0": "none",
|
|
1934
|
+
"--d-elevation-1": tokenMode === "light" ? "0 1px 2px rgba(0,0,0,0.06)" : "0 1px 2px rgba(0,0,0,0.3)",
|
|
1935
|
+
"--d-elevation-2": tokenMode === "light" ? "0 2px 4px rgba(0,0,0,0.08)" : "0 2px 4px rgba(0,0,0,0.4)",
|
|
1936
|
+
"--d-elevation-3": tokenMode === "light" ? "0 4px 12px rgba(0,0,0,0.10)" : "0 4px 12px rgba(0,0,0,0.5)",
|
|
1937
|
+
"--d-elevation-4": tokenMode === "light" ? "0 8px 24px rgba(0,0,0,0.14)" : "0 8px 24px rgba(0,0,0,0.55)",
|
|
1938
|
+
"--d-elevation-5": tokenMode === "light" ? "0 16px 48px rgba(0,0,0,0.18)" : "0 16px 48px rgba(0,0,0,0.6)",
|
|
709
1939
|
// Status colors
|
|
710
1940
|
"--d-success": themeData.tokens?.base?.success || "#22c55e",
|
|
711
1941
|
"--d-error": themeData.tokens?.base?.danger || "#ef4444",
|
|
712
1942
|
"--d-warning": themeData.tokens?.base?.warning || "#f59e0b",
|
|
713
|
-
"--d-info": "#3b82f6"
|
|
1943
|
+
"--d-info": "#3b82f6",
|
|
1944
|
+
// Motion scale (v2.1 Tier B1). Canonical durations + easings.
|
|
1945
|
+
// d-enter-fade, d-pulse, d-glow-hover, etc. all read these.
|
|
1946
|
+
// Themes can override via theme.motion.* and this picks them up
|
|
1947
|
+
// below. Defaults here ensure treatments work even without theme.
|
|
1948
|
+
"--d-motion-instant": "80ms",
|
|
1949
|
+
"--d-motion-fast": "150ms",
|
|
1950
|
+
"--d-motion-base": "250ms",
|
|
1951
|
+
"--d-motion-slow": "400ms",
|
|
1952
|
+
"--d-motion-slower": "600ms",
|
|
1953
|
+
"--d-motion-stagger": "60ms",
|
|
1954
|
+
"--d-motion-ease": "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
1955
|
+
"--d-motion-ease-out": "cubic-bezier(0, 0, 0.2, 1)",
|
|
1956
|
+
"--d-motion-ease-in": "cubic-bezier(0.4, 0, 1, 1)",
|
|
1957
|
+
"--d-motion-ease-spring": "cubic-bezier(0.34, 1.56, 0.64, 1)",
|
|
1958
|
+
// Typography scale (v2.1 Tier B2). Canonical sizes + weights +
|
|
1959
|
+
// tracking + leading. d-display, d-headline, d-title, d-prose,
|
|
1960
|
+
// d-caption, d-eyebrow read these. Themes override via
|
|
1961
|
+
// theme.typography.* below.
|
|
1962
|
+
"--d-font-body": "ui-sans-serif, system-ui, -apple-system, sans-serif",
|
|
1963
|
+
"--d-font-display": "ui-sans-serif, system-ui, -apple-system, sans-serif",
|
|
1964
|
+
"--d-weight-regular": "400",
|
|
1965
|
+
"--d-weight-medium": "500",
|
|
1966
|
+
"--d-weight-semibold": "600",
|
|
1967
|
+
"--d-weight-bold": "700",
|
|
1968
|
+
"--d-tracking-tight": "-0.02em",
|
|
1969
|
+
"--d-tracking-normal": "0",
|
|
1970
|
+
"--d-tracking-wide": "0.04em",
|
|
1971
|
+
"--d-tracking-wider": "0.08em",
|
|
1972
|
+
"--d-leading-tight": "1.1",
|
|
1973
|
+
"--d-leading-snug": "1.25",
|
|
1974
|
+
"--d-leading-normal": "1.5",
|
|
1975
|
+
"--d-leading-relaxed": "1.625",
|
|
1976
|
+
"--d-text-xs": "0.75rem",
|
|
1977
|
+
"--d-text-sm": "0.875rem",
|
|
1978
|
+
"--d-text-base": "1rem",
|
|
1979
|
+
"--d-text-lg": "1.125rem",
|
|
1980
|
+
"--d-text-xl": "1.25rem",
|
|
1981
|
+
"--d-text-2xl": "1.5rem",
|
|
1982
|
+
"--d-text-3xl": "1.875rem",
|
|
1983
|
+
"--d-text-4xl": "2.25rem",
|
|
1984
|
+
"--d-text-5xl": "3rem",
|
|
1985
|
+
"--d-text-6xl": "4rem"
|
|
714
1986
|
};
|
|
715
1987
|
}
|
|
716
1988
|
const tokens = buildTokens(resolvedMode);
|
|
@@ -729,6 +2001,54 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
|
729
2001
|
if (themeData?.motion?.timing) {
|
|
730
2002
|
tokens["--d-easing"] = themeData.motion.timing;
|
|
731
2003
|
}
|
|
2004
|
+
if (themeData?.typography?.display) {
|
|
2005
|
+
tokens["--d-font-display"] = themeData.typography.display;
|
|
2006
|
+
}
|
|
2007
|
+
if (themeData?.typography?.body) {
|
|
2008
|
+
tokens["--d-font-body"] = themeData.typography.body;
|
|
2009
|
+
}
|
|
2010
|
+
if (themeData?.motion?.durations?.instant) {
|
|
2011
|
+
tokens["--d-motion-instant"] = themeData.motion.durations.instant;
|
|
2012
|
+
}
|
|
2013
|
+
if (themeData?.motion?.durations?.fast) {
|
|
2014
|
+
tokens["--d-motion-fast"] = themeData.motion.durations.fast;
|
|
2015
|
+
}
|
|
2016
|
+
if (themeData?.motion?.durations?.base) {
|
|
2017
|
+
tokens["--d-motion-base"] = themeData.motion.durations.base;
|
|
2018
|
+
}
|
|
2019
|
+
if (themeData?.motion?.durations?.slow) {
|
|
2020
|
+
tokens["--d-motion-slow"] = themeData.motion.durations.slow;
|
|
2021
|
+
}
|
|
2022
|
+
if (themeData?.motion?.durations?.slower) {
|
|
2023
|
+
tokens["--d-motion-slower"] = themeData.motion.durations.slower;
|
|
2024
|
+
}
|
|
2025
|
+
if (themeData?.motion?.durations?.stagger) {
|
|
2026
|
+
tokens["--d-motion-stagger"] = themeData.motion.durations.stagger;
|
|
2027
|
+
}
|
|
2028
|
+
if (themeData?.motion?.easings?.ease) {
|
|
2029
|
+
tokens["--d-motion-ease"] = themeData.motion.easings.ease;
|
|
2030
|
+
}
|
|
2031
|
+
if (themeData?.motion?.easings?.easeOut) {
|
|
2032
|
+
tokens["--d-motion-ease-out"] = themeData.motion.easings.easeOut;
|
|
2033
|
+
}
|
|
2034
|
+
if (themeData?.motion?.easings?.easeIn) {
|
|
2035
|
+
tokens["--d-motion-ease-in"] = themeData.motion.easings.easeIn;
|
|
2036
|
+
}
|
|
2037
|
+
if (themeData?.motion?.easings?.spring) {
|
|
2038
|
+
tokens["--d-motion-ease-spring"] = themeData.motion.easings.spring;
|
|
2039
|
+
}
|
|
2040
|
+
if (themeData?.elevation) {
|
|
2041
|
+
for (let i = 1; i <= 5; i++) {
|
|
2042
|
+
const value = themeData.elevation[String(i)];
|
|
2043
|
+
if (typeof value === "string") {
|
|
2044
|
+
tokens[`--d-elevation-${i}`] = value;
|
|
2045
|
+
} else if (value && typeof value === "object") {
|
|
2046
|
+
const modeKey = resolvedMode === "light" ? "light" : "dark";
|
|
2047
|
+
const modeValue = value[modeKey];
|
|
2048
|
+
if (modeValue) tokens[`--d-elevation-${i}`] = modeValue;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
732
2052
|
const lines = Object.entries(tokens).map(([key, value]) => ` ${key}: ${value};`).join("\n");
|
|
733
2053
|
const spatialLines = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
|
|
734
2054
|
let css = `/* Generated by @decantr/cli */
|
|
@@ -737,21 +2057,26 @@ function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
|
737
2057
|
${lines}${spatialLines}
|
|
738
2058
|
}
|
|
739
2059
|
`;
|
|
2060
|
+
const paletteKeys = [
|
|
2061
|
+
"--d-bg",
|
|
2062
|
+
"--d-surface",
|
|
2063
|
+
"--d-surface-raised",
|
|
2064
|
+
"--d-border",
|
|
2065
|
+
"--d-text",
|
|
2066
|
+
"--d-text-muted",
|
|
2067
|
+
"--d-primary-hover",
|
|
2068
|
+
"--d-shadow-sm",
|
|
2069
|
+
"--d-shadow",
|
|
2070
|
+
"--d-shadow-md",
|
|
2071
|
+
"--d-shadow-lg",
|
|
2072
|
+
"--d-elevation-1",
|
|
2073
|
+
"--d-elevation-2",
|
|
2074
|
+
"--d-elevation-3",
|
|
2075
|
+
"--d-elevation-4",
|
|
2076
|
+
"--d-elevation-5"
|
|
2077
|
+
];
|
|
740
2078
|
if (mode === "auto") {
|
|
741
2079
|
const lightTokens = buildTokens("light");
|
|
742
|
-
const paletteKeys = [
|
|
743
|
-
"--d-bg",
|
|
744
|
-
"--d-surface",
|
|
745
|
-
"--d-surface-raised",
|
|
746
|
-
"--d-border",
|
|
747
|
-
"--d-text",
|
|
748
|
-
"--d-text-muted",
|
|
749
|
-
"--d-primary-hover",
|
|
750
|
-
"--d-shadow-sm",
|
|
751
|
-
"--d-shadow",
|
|
752
|
-
"--d-shadow-md",
|
|
753
|
-
"--d-shadow-lg"
|
|
754
|
-
];
|
|
755
2080
|
const lightLines = Object.entries(lightTokens).filter(([key]) => paletteKeys.includes(key)).map(([key, value]) => ` ${key}: ${value};`).join("\n");
|
|
756
2081
|
css += `
|
|
757
2082
|
@media (prefers-color-scheme: light) {
|
|
@@ -759,6 +2084,22 @@ ${lines}${spatialLines}
|
|
|
759
2084
|
${lightLines}
|
|
760
2085
|
}
|
|
761
2086
|
}
|
|
2087
|
+
`;
|
|
2088
|
+
}
|
|
2089
|
+
if (options?.hasThemeToggle) {
|
|
2090
|
+
const opposite = resolvedMode === "light" ? "dark" : "light";
|
|
2091
|
+
const oppositeTokens = buildTokens(opposite);
|
|
2092
|
+
const oppositeLines = Object.entries(oppositeTokens).filter(([key]) => paletteKeys.includes(key)).map(([key, value]) => ` ${key}: ${value};`).join("\n");
|
|
2093
|
+
css += `
|
|
2094
|
+
/*
|
|
2095
|
+
* Theme-toggle variant. Blueprint declared the \`theme-toggle\` feature,
|
|
2096
|
+
* so the user-facing toggle sets \`data-mode\` on <html>. Apply the
|
|
2097
|
+
* opposite mode's palette when that attribute is set.
|
|
2098
|
+
*/
|
|
2099
|
+
:root[data-mode="${opposite}"],
|
|
2100
|
+
[data-mode="${opposite}"] {
|
|
2101
|
+
${oppositeLines}
|
|
2102
|
+
}
|
|
762
2103
|
`;
|
|
763
2104
|
}
|
|
764
2105
|
css += `}
|
|
@@ -899,13 +2240,13 @@ function extractPatternNames(item) {
|
|
|
899
2240
|
return [];
|
|
900
2241
|
}
|
|
901
2242
|
function loadTemplate(name) {
|
|
902
|
-
const fromDist =
|
|
903
|
-
if (
|
|
904
|
-
return
|
|
2243
|
+
const fromDist = join2(__dirname, "..", "src", "templates", name);
|
|
2244
|
+
if (existsSync2(fromDist)) {
|
|
2245
|
+
return readFileSync2(fromDist, "utf-8");
|
|
905
2246
|
}
|
|
906
|
-
const fromSrc =
|
|
907
|
-
if (
|
|
908
|
-
return
|
|
2247
|
+
const fromSrc = join2(__dirname, "templates", name);
|
|
2248
|
+
if (existsSync2(fromSrc)) {
|
|
2249
|
+
return readFileSync2(fromSrc, "utf-8");
|
|
909
2250
|
}
|
|
910
2251
|
throw new Error(`Template not found: ${name}`);
|
|
911
2252
|
}
|
|
@@ -940,15 +2281,15 @@ function resolvePatternAlias(item, patterns) {
|
|
|
940
2281
|
return item;
|
|
941
2282
|
}
|
|
942
2283
|
function buildEssenceV3(options, archetypeData, themeHints) {
|
|
943
|
-
let pages = [
|
|
944
|
-
{ id: "home", layout: ["hero"] }
|
|
945
|
-
];
|
|
2284
|
+
let pages = [{ id: "home", layout: ["hero"] }];
|
|
946
2285
|
let features = options.features;
|
|
947
2286
|
let defaultShell = options.shell || "sidebar-main";
|
|
948
2287
|
if (archetypeData?.pages) {
|
|
949
2288
|
defaultShell = archetypeData.pages[0]?.shell || defaultShell;
|
|
950
2289
|
pages = archetypeData.pages.map((p) => {
|
|
951
|
-
const resolvedLayout = (p.default_layout?.length ? p.default_layout : ["hero"]).map(
|
|
2290
|
+
const resolvedLayout = (p.default_layout?.length ? p.default_layout : ["hero"]).map(
|
|
2291
|
+
(item) => resolvePatternAlias(item, p.patterns)
|
|
2292
|
+
);
|
|
952
2293
|
return {
|
|
953
2294
|
id: p.id,
|
|
954
2295
|
...p.shell !== defaultShell ? { shell_override: p.shell } : {},
|
|
@@ -970,9 +2311,21 @@ function buildEssenceV3(options, archetypeData, themeHints) {
|
|
|
970
2311
|
sharp: 2
|
|
971
2312
|
};
|
|
972
2313
|
const guardModeMap = {
|
|
973
|
-
strict: {
|
|
974
|
-
|
|
975
|
-
|
|
2314
|
+
strict: {
|
|
2315
|
+
mode: "strict",
|
|
2316
|
+
dna_enforcement: "error",
|
|
2317
|
+
blueprint_enforcement: "warn"
|
|
2318
|
+
},
|
|
2319
|
+
guided: {
|
|
2320
|
+
mode: "guided",
|
|
2321
|
+
dna_enforcement: "error",
|
|
2322
|
+
blueprint_enforcement: "off"
|
|
2323
|
+
},
|
|
2324
|
+
creative: {
|
|
2325
|
+
mode: "creative",
|
|
2326
|
+
dna_enforcement: "off",
|
|
2327
|
+
blueprint_enforcement: "off"
|
|
2328
|
+
}
|
|
976
2329
|
};
|
|
977
2330
|
const dna = {
|
|
978
2331
|
theme: {
|
|
@@ -987,9 +2340,13 @@ function buildEssenceV3(options, archetypeData, themeHints) {
|
|
|
987
2340
|
content_gap: densityLevelMap[options.density] || "_gap4"
|
|
988
2341
|
},
|
|
989
2342
|
typography: {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
2343
|
+
// Coerce: 5 historical themes stored typography.scale as a numeric
|
|
2344
|
+
// ratio (e.g., 1.15) instead of a string enum, which fails the v3
|
|
2345
|
+
// essence schema (`/dna/typography/scale: must be string`). Treat
|
|
2346
|
+
// any non-string as missing and fall back to the canonical 'modular'.
|
|
2347
|
+
scale: typeof themeHints?.typography?.scale === "string" ? themeHints.typography.scale : "modular",
|
|
2348
|
+
heading_weight: typeof themeHints?.typography?.heading_weight === "number" ? themeHints.typography.heading_weight : 600,
|
|
2349
|
+
body_weight: typeof themeHints?.typography?.body_weight === "number" ? themeHints.typography.body_weight : 400
|
|
993
2350
|
},
|
|
994
2351
|
color: {
|
|
995
2352
|
palette: "semantic",
|
|
@@ -1068,16 +2425,179 @@ import './styles/global.css'; // Resets
|
|
|
1068
2425
|
|
|
1069
2426
|
### Visual Treatments
|
|
1070
2427
|
|
|
1071
|
-
|
|
2428
|
+
Decantr ships semantic treatment classes that cover the recurring UI idioms. Combine with atoms for layout \u2014 don't hand-roll equivalent CSS classes.
|
|
2429
|
+
|
|
2430
|
+
**Core treatments (every app uses these):**
|
|
1072
2431
|
|
|
1073
2432
|
| Treatment | Class | Variants / States |
|
|
1074
2433
|
|-----------|-------|-------------------|
|
|
1075
|
-
| **Interactive Surface** | \`d-interactive\` | \`data-variant="primary\\|ghost\\|danger"\`, hover/focus-visible/disabled states |
|
|
2434
|
+
| **Interactive Surface** | \`d-interactive\` | \`data-variant="primary\\|ghost\\|danger"\`, \`data-size="sm\\|md\\|lg"\`, hover/focus-visible/disabled states |
|
|
1076
2435
|
| **Container Surface** | \`d-surface\` | \`data-variant="raised\\|overlay"\`, optional \`data-interactive\` for hover |
|
|
1077
2436
|
| **Data Display** | \`d-data\`, \`d-data-header\`, \`d-data-row\`, \`d-data-cell\` | Row hover highlight |
|
|
1078
2437
|
| **Form Control** | \`d-control\` | Focus ring, placeholder, disabled, error via \`aria-invalid\` |
|
|
1079
2438
|
| **Section Rhythm** | \`d-section\` | Auto-spacing between adjacent sections, density-aware |
|
|
1080
2439
|
| **Inline Annotation** | \`d-annotation\` | \`data-status="success\\|error\\|warning\\|info"\` |
|
|
2440
|
+
| **Section Label** | \`d-label\` | \`data-anchor\` for accent-border section headers |
|
|
2441
|
+
|
|
2442
|
+
**Common UI idioms (use these before hand-rolling):**
|
|
2443
|
+
|
|
2444
|
+
| Treatment | Class | Variants / States |
|
|
2445
|
+
|-----------|-------|-------------------|
|
|
2446
|
+
| **Text Link** | \`d-link\` | \`data-variant="subtle\\|strong"\`, active state via \`aria-current="page"\` or \`data-active="true"\` |
|
|
2447
|
+
| **Icon Button** | \`d-icon-btn\` | \`data-size="sm\\|lg"\`, \`data-variant="primary"\`, hover/focus-visible/disabled |
|
|
2448
|
+
| **Nav Link** | \`d-nav-link\` | Active state via \`aria-current="page"\` or \`data-active="true"\` (accent left-border pill) |
|
|
2449
|
+
| **Stepper Chip** | \`d-step-chip\` | \`data-step-state="pending\\|active\\|done"\` |
|
|
2450
|
+
| **Divider utilities** | \`d-divider-top\`, \`d-divider-bottom\`, \`d-divider-left\`, \`d-divider-right\`, \`d-divider\` | Single-side border rule, or standalone \`<hr className="d-divider">\` |
|
|
2451
|
+
|
|
2452
|
+
**Spatial / graph patterns (for canvases with positioned nodes):**
|
|
2453
|
+
|
|
2454
|
+
| Treatment | Class | Variants / States |
|
|
2455
|
+
|-----------|-------|-------------------|
|
|
2456
|
+
| **Agent Node** | \`d-agent-node\` | Card sized for graph canvases (200-260px wide). \`data-status="active\\|error"\` for highlights (error adds red border-glow shadow, active adds accent border). Pair with absolute positioning on the canvas parent. |
|
|
2457
|
+
| **Connection Port** | \`d-port\` | \`data-side="left\\|right\\|top\\|bottom"\` positions the 8px dot on the node edge. \`data-active="true"\` colors it with accent. Use as a slot inside \`d-agent-node\` so SVG connection paths can anchor to predictable element coordinates via \`getBoundingClientRect\`. |
|
|
2458
|
+
|
|
2459
|
+
**Composite card (structural companion to theme card decorators):**
|
|
2460
|
+
|
|
2461
|
+
| Treatment | Class | Variants / States |
|
|
2462
|
+
|-----------|-------|-------------------|
|
|
2463
|
+
| **Card** | \`d-card\` | \`data-padding="compact\\|spacious\\|none"\`, \`data-interactive\` for hover elevation + border accent |
|
|
2464
|
+
| **Card header** | \`d-card-header\` | Flex row, bottom-bordered; pair with \`d-title\` + \`d-icon-btn\` slots |
|
|
2465
|
+
| **Card body** | \`d-card-body\` | Content region, flex col, \`flex: 1 1 auto\` |
|
|
2466
|
+
| **Card footer** | \`d-card-footer\` | Right-aligned action row, top-bordered |
|
|
2467
|
+
|
|
2468
|
+
Pair \`d-card\` with a theme card decorator (e.g., \`carbon-card\`) for hover glow / gradient border. The composite handles layout; the decorator handles aesthetic polish.
|
|
2469
|
+
|
|
2470
|
+
**Data-viz primitives (do NOT hand-roll inline SVGs for these):**
|
|
2471
|
+
|
|
2472
|
+
| Treatment | Class | Purpose / Variants |
|
|
2473
|
+
|-----------|-------|---------------------|
|
|
2474
|
+
| **Timeline rail** | \`d-timeline-rail\` + \`d-timeline-dot\` | Vertical timeline. Dot has \`data-state="active\\|done\\|error\\|warning"\` controlling color. |
|
|
2475
|
+
| **Sparkline** | \`d-sparkline\` + \`d-sparkline-path\` + \`d-sparkline-area\` | Inline trend SVG. \`data-trend="up\\|down"\` colors stroke + area accent. |
|
|
2476
|
+
| **Intent radar** | \`d-intent-radar\` + \`d-intent-radar-ring[data-level]\` + \`d-intent-radar-axis\` | Concentric ring backdrop (5 levels) for confidence/score wheels. \`--d-radar-axis-angle\` for rotated axes. |
|
|
2477
|
+
| **Waveform** | \`d-waveform\` + \`d-waveform-path\` | Audio/signal waveform path container. \`data-state="active"\` switches to success color. |
|
|
2478
|
+
| **QR placeholder** | \`d-qr-placeholder\` | Pure-CSS QR-code placeholder (repeating gradients). \`--d-qr-size\` for size override. |
|
|
2479
|
+
| **Conic ring** | \`d-conic-ring\` | Gauge/confidence ring. Set \`--d-conic-value\` (0..1) to fill arc. \`data-state="success\\|warning\\|error"\` switches color. \`--d-conic-thickness\` for ring width. |
|
|
2480
|
+
| **Heatmap cell** | \`d-heatmap-cell\` | Single heatmap cell. \`--d-heatmap-intensity\` 0..1 blends primary\u2192surface. \`data-status="error\\|success"\` switches base color. |
|
|
2481
|
+
|
|
2482
|
+
**Banners / prominent CTAs:**
|
|
2483
|
+
|
|
2484
|
+
| Treatment | Class | Variants / States |
|
|
2485
|
+
|-----------|-------|-------------------|
|
|
2486
|
+
| **CTA Banner** | \`d-cta-banner\` | \`data-size="compact\\|hero"\` (default is between). Gradient wash from primary to accent. Theme can override via \`--d-cta-gradient\` / \`--d-cta-text\` CSS vars. |
|
|
2487
|
+
| **Dark-Pill Button** | \`d-interactive\` + \`data-variant="dark"\` | Pill-shaped dark-on-accent CTA for use inside \`d-cta-banner\`. Theme can override via \`--d-cta-pill-bg\` / \`--d-cta-pill-text\`. |
|
|
2488
|
+
|
|
2489
|
+
**Shell layouts (do NOT hand-roll these):**
|
|
2490
|
+
|
|
2491
|
+
| Treatment | Class | Purpose / States |
|
|
2492
|
+
|-----------|-------|------------------|
|
|
2493
|
+
| **Shell root** | \`d-shell\` | Full-viewport root container. \`data-layout="sidebar-main\\|centered\\|top-nav-footer\\|sidebar-aside"\` switches the layout model (default equivalent to top-nav-footer: vertical flex with sticky header). |
|
|
2494
|
+
| **Sidebar** | \`d-shell-sidebar\` | Left 240px nav column. \`data-collapsed="true"\` switches to a 64px rail. Below \`_mdmax:\` auto-becomes an off-canvas drawer \u2014 toggle via \`data-mobile-open="true"\`. |
|
|
2495
|
+
| **Main** | \`d-shell-main\` | Remaining-width column to the right of the sidebar (or the full content area in top-nav shells). Handles scroll internally. |
|
|
2496
|
+
| **Aside** | \`d-shell-aside\` | Right 320px auxiliary panel for inspector / timeline / minimap in \`sidebar-aside\` layouts. Below \`_mdmax:\` hides by default; toggle with \`data-mobile-open="true"\`. |
|
|
2497
|
+
| **Header** | \`d-shell-header\` | 52px sticky top bar with horizontal flex layout. Use inside \`d-shell-main\` (sidebar-main shells) or at the top of \`d-shell\` (top-nav shells). |
|
|
2498
|
+
| **Body** | \`d-shell-body\` | Scrollable main region. \`data-padding="compact\\|spacious\\|none"\` overrides the default 1rem padding. |
|
|
2499
|
+
| **Footer** | \`d-shell-footer\` | Narrow band below the body with top border. |
|
|
2500
|
+
| **Centered card** | \`d-shell-centered-card\` | The content parent inside \`d-shell[data-layout="centered"]\`. Caps width at 28rem. |
|
|
2501
|
+
|
|
2502
|
+
**Shell layout recipes:**
|
|
2503
|
+
- **Auth / confirmation:** \`d-shell[data-layout="centered"] + d-shell-centered-card\`.
|
|
2504
|
+
- **Dashboard with sidebar:** \`d-shell[data-layout="sidebar-main"] + d-shell-sidebar + d-shell-main (> d-shell-header + d-shell-body)\`.
|
|
2505
|
+
- **Dashboard with inspector / timeline / minimap:** \`d-shell[data-layout="sidebar-aside"] + d-shell-sidebar + d-shell-main + d-shell-aside\` (3-column grid; aside collapses off-canvas below md).
|
|
2506
|
+
- **Marketing / public pages:** \`d-shell[data-layout="top-nav-footer"]\` (or bare \`d-shell\`) with \`d-shell-header\` at the top and \`d-shell-body\` + \`d-shell-footer\`.
|
|
2507
|
+
|
|
2508
|
+
Do NOT hand-roll \`.shell-sidebar\`, \`.shell-centered\`, \`.shell-tnf\`, \`.shell-aside\`, \`.sidebar-main-layout\`, or similar class names. They exist as treatments.
|
|
2509
|
+
|
|
2510
|
+
### Theme toggle
|
|
2511
|
+
|
|
2512
|
+
If the blueprint declares the \`theme-toggle\` feature, \`tokens.css\` includes a \`[data-mode="<opposite>"]\` selector block. Flip the visible mode by setting \`data-mode\` on \`<html>\` (or any ancestor):
|
|
2513
|
+
|
|
2514
|
+
\`\`\`tsx
|
|
2515
|
+
// Toggle between the blueprint's primary mode and its opposite.
|
|
2516
|
+
function ThemeToggle() {
|
|
2517
|
+
const toggle = () => {
|
|
2518
|
+
const html = document.documentElement;
|
|
2519
|
+
const current = html.getAttribute('data-mode');
|
|
2520
|
+
html.setAttribute('data-mode', current === 'dark' ? 'light' : 'dark');
|
|
2521
|
+
};
|
|
2522
|
+
return <button className="d-icon-btn" onClick={toggle}><SunMoon /></button>;
|
|
2523
|
+
}
|
|
2524
|
+
\`\`\`
|
|
2525
|
+
|
|
2526
|
+
Do NOT branch component code on the current mode via JS to re-style elements \u2014 the token switch handles it CSS-side.
|
|
2527
|
+
|
|
2528
|
+
**Modal / palette chrome:**
|
|
2529
|
+
|
|
2530
|
+
| Treatment | Class | Purpose / States |
|
|
2531
|
+
|-----------|-------|------------------|
|
|
2532
|
+
| **Modal root** | \`d-modal\` | Fixed-position overlay covering the viewport. \`data-align="top"\` shifts content to top 15vh (common for command palettes). |
|
|
2533
|
+
| **Modal backdrop** | \`d-modal-backdrop\` | Scrim with backdrop-blur. Place as a sibling inside \`d-modal\` with \`onClick\` to close. |
|
|
2534
|
+
| **Modal panel** | \`d-modal-panel\` | The actual dialog content. \`data-size="sm\\|lg"\` adjusts max-width (default 32rem). |
|
|
2535
|
+
| **Command palette** | \`d-palette\` | Specialized modal-panel variant for command palettes \u2014 40rem wide, 60vh max-height. |
|
|
2536
|
+
| **Palette input** | \`d-palette-input\` | Search input at top of palette. |
|
|
2537
|
+
| **Palette list** | \`d-palette-list\` | Scrollable command list. |
|
|
2538
|
+
| **Palette row** | \`d-palette-row\` | Individual command row. \`data-active="true"\` for keyboard-highlighted row. |
|
|
2539
|
+
| **Palette section** | \`d-palette-section\` | Uppercase section label inside palette (e.g., "Navigation"). |
|
|
2540
|
+
| **Keyboard chip** | \`d-kbd\` | Mono-font key hint. Use inside \`<kbd>\` for accessibility. |
|
|
2541
|
+
| **Hotkey indicator** | \`d-hotkey-indicator\` | Corner badge shown while a chord hotkey prefix is armed. Apply \`data-visible={isArmed}\` and \`data-prefix="g"\` when the prefix is pressed; clear on timeout/resolve. Required when \`hotkey_semantics.show_chord_indicator !== false\`. |
|
|
2542
|
+
|
|
2543
|
+
Composition pattern for a command palette (REQUIRED \u2014 palette MUST be wrapped in \`d-modal\` + \`d-modal-backdrop\`, otherwise it renders as a top-level full-width strip):
|
|
2544
|
+
\`\`\`tsx
|
|
2545
|
+
{open && (
|
|
2546
|
+
<div className="d-modal" data-align="top">
|
|
2547
|
+
<div className="d-modal-backdrop" onClick={close} />
|
|
2548
|
+
<div className="d-palette">
|
|
2549
|
+
<input className="d-palette-input" placeholder="Type a command..." />
|
|
2550
|
+
<ul className="d-palette-list">
|
|
2551
|
+
<li className="d-palette-section">Navigation</li>
|
|
2552
|
+
<li className="d-palette-row" data-active={i === selectedIndex}>
|
|
2553
|
+
<Bot /> Go to Agents
|
|
2554
|
+
<kbd className="d-kbd">g a</kbd>
|
|
2555
|
+
</li>
|
|
2556
|
+
</ul>
|
|
2557
|
+
</div>
|
|
2558
|
+
</div>
|
|
2559
|
+
)}
|
|
2560
|
+
\`\`\`
|
|
2561
|
+
|
|
2562
|
+
**Hard rules for the palette:**
|
|
2563
|
+
- The palette MUST be inside \`d-modal\` (positions/centers it as overlay) AND have a \`d-modal-backdrop\` sibling (provides scrim + click-to-close).
|
|
2564
|
+
- Group commands by section using \`d-palette-section\` (Uppercase eyebrow label) \u2014 never render a flat list. The blueprint's \`navigation.command_palette.commands\` already has \`section\` fields; honor them.
|
|
2565
|
+
- Each row should have an icon on the LEFT (Lucide), label in the center, and a \`d-kbd\` shortcut hint on the RIGHT \u2014 even when the command has no hotkey, leave the right slot empty for visual rhythm.
|
|
2566
|
+
|
|
2567
|
+
Composition pattern for an auth page (REQUIRED \u2014 must use \`d-shell[data-layout="centered"]\` + \`d-shell-centered-card\`, not a hand-rolled centering wrapper):
|
|
2568
|
+
\`\`\`tsx
|
|
2569
|
+
<div className="d-shell" data-layout="centered">
|
|
2570
|
+
<div className="d-shell-centered-card">
|
|
2571
|
+
{/* Logo + form go here. Card caps at 28rem and self-centers. */}
|
|
2572
|
+
</div>
|
|
2573
|
+
</div>
|
|
2574
|
+
\`\`\`
|
|
2575
|
+
|
|
2576
|
+
**Hard rule for centered/auth pages:**
|
|
2577
|
+
The \`d-shell-centered-card\` element provides the 28rem-max-width box. Do NOT render auth forms directly as children of \`d-shell\` \u2014 they will span full viewport width. Always wrap the form in \`d-shell-centered-card\`.
|
|
2578
|
+
|
|
2579
|
+
**Guidance for cold scaffolds:**
|
|
2580
|
+
- If your component is an icon-only action trigger, it's a \`d-icon-btn\`, not a stripped-down \`d-interactive\`.
|
|
2581
|
+
- Breadcrumb / footer / inline body-copy links use \`d-link\`.
|
|
2582
|
+
- Sidebar and top-nav route links use \`d-nav-link\`. Match active state by setting \`aria-current="page"\` (preferred \u2014 accessible) or \`data-active="true"\`.
|
|
2583
|
+
- Checkout / onboarding stepper position indicators use \`d-step-chip\`.
|
|
2584
|
+
- Horizontal rules between card sections use \`d-divider-top\` / \`d-divider-bottom\` as a container modifier, or \`<hr className="d-divider">\` as a standalone element.
|
|
2585
|
+
- Do NOT create \`.nav-link\`, \`.icon-btn\`, \`.sidebar-link\`, \`.step-chip\`, \`.divider-top\` (or similar) as custom classes. They exist as treatments.
|
|
2586
|
+
|
|
2587
|
+
### Icons \u2014 use Lucide
|
|
2588
|
+
|
|
2589
|
+
Decantr scaffolds ship with \`lucide-react\` pre-installed. When personality prose says "Lucide icons" (or the section/pattern contract references icon names), import them from there:
|
|
2590
|
+
|
|
2591
|
+
\`\`\`tsx
|
|
2592
|
+
import { Bot, ShoppingBag, Settings, Activity, Gauge, Cpu } from 'lucide-react';
|
|
2593
|
+
|
|
2594
|
+
<Bot className={css('_w5 _h5')} aria-hidden="true" />
|
|
2595
|
+
\`\`\`
|
|
2596
|
+
|
|
2597
|
+
- Tree-shaking keeps the bundle at ~1.5-3 KB per icon used.
|
|
2598
|
+
- Do NOT inline SVGs or import an alternative icon library without an explicit contract directive.
|
|
2599
|
+
- When a navigation item declares an \`icon\` field (see section \`navigation_items\`), the value is the Lucide icon name in kebab-case \u2014 e.g., \`"shopping-bag"\` \u2192 \`import { ShoppingBag } from 'lucide-react'\`.
|
|
2600
|
+
- Default sizing: \`_w5 _h5\` (20px) for inline icons, \`_w4 _h4\` (16px) inside dense chrome, \`_w6 _h6\` (24px) for primary slots.
|
|
1081
2601
|
|
|
1082
2602
|
### Composition
|
|
1083
2603
|
|
|
@@ -1109,6 +2629,42 @@ css('_bgprimary _h:bgprimary/80')
|
|
|
1109
2629
|
- Arbitrary values use square brackets when the standard scale is not enough: \`_w[512px]\`, \`_h[100vh]\`, \`_p[clamp(1rem,3vw,2rem)]\`, \`_z[40]\`.
|
|
1110
2630
|
- When you see bracket atoms in shell or page contracts, treat them as first-class Decantr syntax, not as an error or a cue to fall back to inline styles.
|
|
1111
2631
|
|
|
2632
|
+
### Responsive Breakpoint Atoms
|
|
2633
|
+
|
|
2634
|
+
Decantr ships two families of responsive prefixes. Use them directly inside \`css(...)\` \u2014 no \`matchMedia\` JS needed for simple responsive switches.
|
|
2635
|
+
|
|
2636
|
+
**Mobile-first (min-width):**
|
|
2637
|
+
| Prefix | Breakpoint | Meaning |
|
|
2638
|
+
|--------|-----------|---------|
|
|
2639
|
+
| \`_sm:\` | \u2265 640px | small tablet / large phone landscape and up |
|
|
2640
|
+
| \`_md:\` | \u2265 768px | tablet portrait and up |
|
|
2641
|
+
| \`_lg:\` | \u2265 1024px | tablet landscape / small desktop and up |
|
|
2642
|
+
| \`_xl:\` | \u2265 1280px | desktop and up |
|
|
2643
|
+
|
|
2644
|
+
**Desktop-first (max-width, for "hide below" / "swap at small" expressions):**
|
|
2645
|
+
| Prefix | Breakpoint | Meaning |
|
|
2646
|
+
|--------|-----------|---------|
|
|
2647
|
+
| \`_smmax:\` | < 640px | phone only |
|
|
2648
|
+
| \`_mdmax:\` | < 768px | phone + small tablet |
|
|
2649
|
+
| \`_lgmax:\` | < 1024px | below tablet-landscape |
|
|
2650
|
+
| \`_xlmax:\` | < 1280px | below desktop |
|
|
2651
|
+
|
|
2652
|
+
Pseudo-class stacking works with both (e.g., \`_mdmax:h:bgmuted\`, \`_sm:fv:ring2\`).
|
|
2653
|
+
|
|
2654
|
+
**Example:**
|
|
2655
|
+
\`\`\`
|
|
2656
|
+
// 1-column on phone, 2-column from tablet, 3-column from desktop
|
|
2657
|
+
css('_grid _gc1 _sm:gc2 _lg:gc3')
|
|
2658
|
+
|
|
2659
|
+
// Hide the minimap below tablet portrait
|
|
2660
|
+
css('_block _mdmax:none')
|
|
2661
|
+
|
|
2662
|
+
// Show the hamburger below tablet portrait, hide it above
|
|
2663
|
+
css('_none _mdmax:block')
|
|
2664
|
+
\`\`\`
|
|
2665
|
+
|
|
2666
|
+
Prefer these atoms over \`window.matchMedia\` in JS. Reserve JS responsive checks for cases where the component tree ITSELF must change shape (e.g., rendering a different React component), not just styling.
|
|
2667
|
+
|
|
1112
2668
|
### Atom Reference
|
|
1113
2669
|
|
|
1114
2670
|
#### Display
|
|
@@ -1166,6 +2722,10 @@ css('_bgprimary _h:bgprimary/80')
|
|
|
1166
2722
|
| \`_py{n}\` | \`padding-block:{scale}\` | vertical |
|
|
1167
2723
|
| \`_m{n}\` | \`margin:{scale}\` | same as padding variants |
|
|
1168
2724
|
| \`_mx{n}\`, \`_my{n}\` | inline/block margin | horizontal/vertical |
|
|
2725
|
+
| \`_mauto\` | \`margin:auto\` | center in flex/grid |
|
|
2726
|
+
| \`_mtauto\`, \`_mbauto\` | \`margin-top:auto\` / \`margin-bottom:auto\` | pin to bottom/top of flex column |
|
|
2727
|
+
| \`_mlauto\`, \`_mrauto\` | \`margin-left:auto\` / \`margin-right:auto\` | pin to right/left in a row |
|
|
2728
|
+
| \`_mxauto\`, \`_myauto\` | inline/block margin: auto | center horizontally/vertically |
|
|
1169
2729
|
|
|
1170
2730
|
#### Sizing
|
|
1171
2731
|
| Atom | CSS |
|
|
@@ -1325,10 +2885,13 @@ Missing declared navigation features are contract drift, not optional polish.
|
|
|
1325
2885
|
|
|
1326
2886
|
### Routing
|
|
1327
2887
|
|
|
1328
|
-
Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing strategy
|
|
1329
|
-
|
|
1330
|
-
- \`"history"\` \u2192 use \`BrowserRouter\` (
|
|
1331
|
-
- \`"
|
|
2888
|
+
Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing strategy. The value is also rendered at the top of \`.decantr/context/scaffold-pack.md\` with a mechanical router-name hint \u2014 trust the pack.
|
|
2889
|
+
|
|
2890
|
+
- \`"history"\` (modern SPA default) \u2192 use \`BrowserRouter\` from \`react-router-dom\`. Regular URLs like \`/login\`, \`/agents\`. Works on Vite dev, Vercel, Netlify, Cloudflare Pages, and most modern hosts (SPA fallback is automatic on those platforms).
|
|
2891
|
+
- \`"hash"\` \u2192 use \`HashRouter\` from \`react-router-dom\`. URLs are prefixed with \`/#\` (e.g., \`/#/login\`). Only needed when deploying to a static host without SPA fallback (e.g., vanilla GitHub Pages).
|
|
2892
|
+
- \`"pathname"\` \u2192 framework-native file-based routing (Next.js App Router).
|
|
2893
|
+
|
|
2894
|
+
Do **not** pick a router based on personal preference. Match the declared \`routing\` value exactly \u2014 it's the contract.
|
|
1332
2895
|
|
|
1333
2896
|
Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.
|
|
1334
2897
|
|
|
@@ -1336,7 +2899,7 @@ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and l
|
|
|
1336
2899
|
|
|
1337
2900
|
- 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.
|
|
1338
2901
|
- Do **not** invent SSR-only per-route metadata systems for a clearly hash-routed scaffold.
|
|
1339
|
-
- For history
|
|
2902
|
+
- For history-mode SPAs and SSR-style projects, per-route metadata can be richer (set \`document.title\` and meta tags via a route-level effect on SPA; use framework primitives on SSR), but it still needs to follow the declared route contract instead of introducing off-contract marketing pages.
|
|
1340
2903
|
|
|
1341
2904
|
### Layout Rules
|
|
1342
2905
|
|
|
@@ -1346,6 +2909,20 @@ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and l
|
|
|
1346
2909
|
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.
|
|
1347
2910
|
5. **Responsive nav rules.** Hamburger menus appear ONLY below the shell collapse breakpoint. Full nav shows above it.
|
|
1348
2911
|
|
|
2912
|
+
### Responsive Breakpoints
|
|
2913
|
+
|
|
2914
|
+
The \`@decantr/css\` atom breakpoints are the canonical defaults. See the "Responsive Breakpoint Atoms" section below for the full table. Shell-level guidance:
|
|
2915
|
+
|
|
2916
|
+
- **\`_smmax:\` (< 640px \u2014 phone):** hamburger drawer, single-column stack, full-bleed content. Pattern-level content stacks vertically unless the pattern explicitly declares otherwise.
|
|
2917
|
+
- **\`_mdmax:\` (< 768px \u2014 phone + small tablet):** most patterns should use this as the "stack to a single column / hide secondary chrome" breakpoint. This is the level where \`top-nav-footer\` mid-nav links should collapse to a hamburger.
|
|
2918
|
+
- **\`_lgmax:\` (< 1024px \u2014 below tablet-landscape):** \`sidebar-main\` shells should collapse the persistent sidebar into a drawer here. Do **not** keep the sidebar open below \`_lg:\` \u2014 at 768-1023px it leaves the main canvas too cramped for data-dense mission-control content.
|
|
2919
|
+
- **\`_lg:\` (\u2265 1024px \u2014 tablet-landscape / small desktop):** full \`sidebar-main\` layout; responsive multi-column grids.
|
|
2920
|
+
- **\`_xl:\` (\u2265 1280px \u2014 desktop):** canonical layout.
|
|
2921
|
+
|
|
2922
|
+
Implementation: prefer the \`@decantr/css\` breakpoint atoms (\`_sm:\`, \`_md:\`, \`_lg:\`, \`_xl:\`, \`_smmax:\`, \`_mdmax:\`, \`_lgmax:\`, \`_xlmax:\`) or structured \`responsive\` fields on patterns. Use \`window.matchMedia\` only when the React component tree itself must change shape per viewport (e.g., rendering a different component), not just styling.
|
|
2923
|
+
|
|
2924
|
+
**High-density content patterns** (swarm canvases, trace-waterfall, data tables with 8+ columns) should declare explicit mobile-reflow behavior \u2014 stack vertically, collapse to a list, or define a \`desktop-only\` directive and render a lighter alternative pattern below \`_md:\`. Without this, horizontal overflow on phone viewports is the default failure mode.
|
|
2925
|
+
|
|
1349
2926
|
### Accessibility Defaults
|
|
1350
2927
|
|
|
1351
2928
|
- 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>\`.
|
|
@@ -1353,29 +2930,104 @@ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and l
|
|
|
1353
2930
|
- Keep keyboard focus visible with \`:focus-visible\` treatments on custom interactive surfaces, not just browser defaults.
|
|
1354
2931
|
- 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.
|
|
1355
2932
|
|
|
1356
|
-
### Motion
|
|
1357
|
-
|
|
1358
|
-
Every interaction should feel responsive and polished. Apply motion by default, not as an afterthought:
|
|
2933
|
+
### Motion Treatments
|
|
1359
2934
|
|
|
1360
|
-
|
|
1361
|
-
- **Stagger children:** Lists, grids, and card groups should stagger-animate on mount (50-100ms delay per item)
|
|
1362
|
-
- **Data visualization:** Charts, gauges, progress bars, and counters should animate to their values on mount \u2014 never render static
|
|
1363
|
-
- **Micro-interactions:** All interactive elements (buttons, toggles, cards, nav items) need hover/press transitions. Use the motion tokens (--d-duration-hover, --d-easing) for consistency.
|
|
1364
|
-
- **Scroll reveals:** Sections below the fold should fade-in on scroll intersection (IntersectionObserver, once)
|
|
1365
|
-
- **Reduced motion:** Wrap all animations in \`prefers-reduced-motion\` media query \u2014 skip animation, keep state changes instant
|
|
2935
|
+
**Hard rule:** Every animation MUST use one of the treatments below. Do **not** hand-roll \`@keyframes\` or inline \`transition\` rules \u2014 the treatments ship tuned durations, easings, and \`prefers-reduced-motion\` handling.
|
|
1366
2936
|
|
|
1367
|
-
|
|
2937
|
+
| Treatment | Class | Intent | When to use |
|
|
2938
|
+
|-----------|-------|--------|-------------|
|
|
2939
|
+
| Fade entrance | \`d-enter-fade\` | Soft mount | Cards, sections, modals on mount |
|
|
2940
|
+
| Slide-up entrance | \`d-enter-slide-up\` | Forceful mount | Hero blocks, primary content |
|
|
2941
|
+
| Scale entrance | \`d-enter-scale\` | Spring mount | Dialogs, popovers, callouts |
|
|
2942
|
+
| Stagger children | \`d-stagger-children > *\` | Sequential reveal | Lists, grids \u2014 set \`style={{ '--d-stagger-index': i }}\` on each child |
|
|
2943
|
+
| Status pulse | \`d-pulse\` | Opacity cycle | Live indicators, processing badges |
|
|
2944
|
+
| Ring pulse | \`d-pulse-ring\` | Expanding halo | Notification dots, focus attractors |
|
|
2945
|
+
| Shimmer | \`d-shimmer\` | Loading skeleton | Skeleton screens on surface-raised |
|
|
2946
|
+
| Float | \`d-float\` | Idle vertical drift | Decorative elements, empty-state graphics |
|
|
2947
|
+
| Glow on hover | \`d-glow-hover\` | Accent glow | Primary CTAs, feature cards |
|
|
2948
|
+
| Scale on hover | \`d-scale-hover\` | 1.02\xD7 pop | Clickable cards, tiles |
|
|
2949
|
+
| Lift on hover | \`d-lift-hover\` | Translate + elevate | Product cards (elevation jumps to 3) |
|
|
2950
|
+
| Click ripple | \`d-ripple\` | Material ripple | Buttons inside disclosure surfaces |
|
|
1368
2951
|
|
|
1369
|
-
|
|
2952
|
+
**Motion tokens** (theme-tunable via \`theme.motion.durations\` / \`theme.motion.easings\`):
|
|
1370
2953
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
-
|
|
1374
|
-
|
|
1375
|
-
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
-
|
|
2954
|
+
| Token | Default | Meaning |
|
|
2955
|
+
|-------|---------|---------|
|
|
2956
|
+
| \`--d-motion-instant\` | 80ms | Color swaps, focus rings |
|
|
2957
|
+
| \`--d-motion-fast\` | 150ms | Hover transitions, button press |
|
|
2958
|
+
| \`--d-motion-base\` | 250ms | Entrances, section reveals |
|
|
2959
|
+
| \`--d-motion-slow\` | 400ms | Modals, page transitions |
|
|
2960
|
+
| \`--d-motion-slower\` | 600ms | Hero reveals |
|
|
2961
|
+
| \`--d-motion-stagger\` | 60ms | Per-child stagger delay |
|
|
2962
|
+
| \`--d-motion-ease\` | cubic-bezier(0.4, 0, 0.2, 1) | Balanced ease in/out |
|
|
2963
|
+
| \`--d-motion-ease-out\` | cubic-bezier(0, 0, 0.2, 1) | Decelerate (entrances) |
|
|
2964
|
+
| \`--d-motion-ease-in\` | cubic-bezier(0.4, 0, 1, 1) | Accelerate (exits) |
|
|
2965
|
+
| \`--d-motion-ease-spring\` | cubic-bezier(0.34, 1.56, 0.64, 1) | Bounce overshoot |
|
|
2966
|
+
|
|
2967
|
+
**Reduced motion is handled inside the treatments themselves** \u2014 do NOT wrap each usage in a media query. Do NOT branch React/TS code on \`dna.motion.reduce_motion\`. The treatments' \`@media (prefers-reduced-motion: reduce)\` block hands control to the user's OS preference automatically.
|
|
2968
|
+
|
|
2969
|
+
### Typography Treatments
|
|
2970
|
+
|
|
2971
|
+
**Hard rule:** Every text node with a distinct visual role MUST use one of these treatments. Do **not** set \`font-size\` / \`font-weight\` / \`letter-spacing\` / \`line-height\` via inline styles or hand-rolled classes.
|
|
2972
|
+
|
|
2973
|
+
| Treatment | Class | Role | Default size / weight |
|
|
2974
|
+
|-----------|-------|------|----------------------|
|
|
2975
|
+
| Display | \`d-display\` | Hero headings | 3rem / 700 / tight leading / tight tracking |
|
|
2976
|
+
| Headline | \`d-headline\` | Section H1/H2 | 1.875rem / 600 / snug leading |
|
|
2977
|
+
| Title | \`d-title\` | Card titles, dialog headers | 1.25rem / 600 |
|
|
2978
|
+
| Subtitle | \`d-subtitle\` | Under-title explainer | 1.125rem / 400 / muted |
|
|
2979
|
+
| Prose | \`d-prose\` | Long-form reading copy | 1rem / 1.625 leading |
|
|
2980
|
+
| Body | \`d-body\` | UI body text | 1rem / 1.5 leading |
|
|
2981
|
+
| Caption | \`d-caption\` | Help text, fine print | 0.875rem / muted |
|
|
2982
|
+
| Eyebrow | \`d-eyebrow\` | Category kicker above headline | 0.75rem / 600 / uppercase / wider tracking / accent |
|
|
2983
|
+
| Numeric modifier | \`d-numeric\` | Adds tabular-nums to any text | Mix with other treatments |
|
|
2984
|
+
| Monospace | \`d-mono-text\` | Code, IDs, timestamps, metric values | Mono font + tabular nums |
|
|
2985
|
+
|
|
2986
|
+
### Elevation Scale
|
|
2987
|
+
|
|
2988
|
+
**Hard rule:** When a surface needs a shadow, use \`d-elevate[data-level="1..5"]\`. Do **not** hand-roll \`box-shadow\` values.
|
|
2989
|
+
|
|
2990
|
+
| Level | Token | Typical use |
|
|
2991
|
+
|-------|-------|-------------|
|
|
2992
|
+
| 0 | \`--d-elevation-0\` (none) | Flat surfaces (default) |
|
|
2993
|
+
| 1 | \`--d-elevation-1\` | Subtle \u2014 resting cards |
|
|
2994
|
+
| 2 | \`--d-elevation-2\` | Raised \u2014 default cards |
|
|
2995
|
+
| 3 | \`--d-elevation-3\` | Hover / active |
|
|
2996
|
+
| 4 | \`--d-elevation-4\` | Floating panels, popovers |
|
|
2997
|
+
| 5 | \`--d-elevation-5\` | Modals, overlays |
|
|
2998
|
+
|
|
2999
|
+
Dark themes emit stronger alpha values automatically.
|
|
3000
|
+
|
|
3001
|
+
### Interaction Requirements
|
|
3002
|
+
|
|
3003
|
+
**Hard rule:** Every pattern declares its required interactions in its page-pack \`Interactions\` checklist. **A pattern that declares \`interactions: [...]\` MUST implement each one in source.** \`decantr check --strict\` fails when a declared interaction has no matching treatment or handler in the generated code.
|
|
3004
|
+
|
|
3005
|
+
| Declared interaction | Canonical implementation |
|
|
3006
|
+
|----------------------|-------------------------|
|
|
3007
|
+
| \`animate-on-mount\` | \`d-enter-fade\` / \`d-enter-slide-up\` / \`d-enter-scale\` on the pattern root |
|
|
3008
|
+
| \`stagger-children\` | \`d-stagger-children\` on parent + \`style={{ '--d-stagger-index': i }}\` on each child |
|
|
3009
|
+
| \`status-pulse\` | \`d-pulse\` on the indicator |
|
|
3010
|
+
| \`glow-hover\` | \`d-glow-hover\` on the interactive surface |
|
|
3011
|
+
| \`lift-hover\` | \`d-lift-hover\` on the interactive surface |
|
|
3012
|
+
| \`scale-hover\` | \`d-scale-hover\` on the interactive surface |
|
|
3013
|
+
| \`drag-nodes\` | \`pointerdown\` \u2192 \`pointermove\` with 4px threshold before drag engages. \`cursor: grab\` default, \`cursor: grabbing\` during. |
|
|
3014
|
+
| \`pan-background\` | Pointer handlers on canvas background only (not nodes); translate the viewport transform |
|
|
3015
|
+
| \`zoom-scroll\` | Wheel handler adjusting a \`scale\` transform, clamped [0.25, 4]; show zoom indicator |
|
|
3016
|
+
| \`click-connect\` | Two-click state machine: select a port, click another port to create a connection |
|
|
3017
|
+
| \`inline-edit\` | Replace static text with controlled \`<input>\` on click; commit on blur or Enter |
|
|
3018
|
+
| \`hover-tooltip\` | \`data-tooltip\` attribute + hover handler positioning a popover (mount with \`d-enter-scale\`) |
|
|
3019
|
+
| \`live-simulation\` | \`setInterval\` updating mock state every 2-4 seconds; animate changes with \`d-pulse\` |
|
|
3020
|
+
| \`drag-reorder\` | \`pointerdown\` \u2192 \`pointermove\` with 4px threshold + \`cursor: grab/grabbing\`. Reorder list state on drop. (Same handler shape as \`drag-nodes\`; different state model \u2014 list reorder vs free placement.) |
|
|
3021
|
+
| \`scroll-reveal\` | \`IntersectionObserver\` with \`once: true\` triggering \`d-enter-fade\`/\`d-enter-slide-up\` on entry |
|
|
3022
|
+
| \`real-time-updates\` | \`setInterval\` (2-8s) updating mock state OR \`WebSocket\`/\`EventSource\` for live data; animate changes with \`d-pulse\` on the changed element |
|
|
3023
|
+
| \`float-idle\` | \`d-float\` on decorative elements (illustrations, empty-state graphics) |
|
|
3024
|
+
| \`hover-reveal\` | \`onMouseEnter\`/\`onMouseLeave\` toggling visibility, OR group-hover via the \`:hover\` pseudo on a parent revealing a child (e.g., row actions appear on row hover) |
|
|
3025
|
+
| \`click-select\` | Controlled selection state via \`onClick\`; reflect via \`aria-selected\` / \`aria-pressed\` / \`data-active="true"\` and toggle visual via the matching treatment data-attribute |
|
|
3026
|
+
| \`keyboard-navigation\` | \`onKeyDown\` arrow-key handlers (ArrowUp/Down/Left/Right + Enter/Space). For lists/grids: roving tabindex pattern. Always pair with \`tabIndex={0}\` on focusable items. |
|
|
3027
|
+
| \`focus-trap\` | Tab-key interception inside modal/dialog cycles focus to first/last focusable element; restore focus on close |
|
|
3028
|
+
| \`shimmer-skeleton\` | \`d-shimmer\` on skeleton placeholders during loading |
|
|
3029
|
+
| \`zoom-pinch\` | Touch handlers (\`touchstart\`/\`touchmove\`) tracking pinch distance, OR \`gestureend\` on Safari; same scale transform as \`zoom-scroll\` |
|
|
3030
|
+
| \`ripple-click\` | \`d-ripple\` on the interactive surface |`;
|
|
1379
3031
|
function generateDecantrMdV31(params) {
|
|
1380
3032
|
const template = loadTemplate("DECANTR.md.template");
|
|
1381
3033
|
const body = renderTemplate(template, {
|
|
@@ -1404,7 +3056,9 @@ Start implementation from the shell layouts and shared route structure before fi
|
|
|
1404
3056
|
briefLines.push(`- **Blueprint:** ${params.blueprintId || "custom"}`);
|
|
1405
3057
|
const themeDesc = `${params.themeName || "default"} (${params.themeMode || "dark"} mode${params.themeShape ? `, ${params.themeShape} shape` : ""})`;
|
|
1406
3058
|
briefLines.push(`- **Theme:** ${themeDesc}`);
|
|
1407
|
-
briefLines.push(
|
|
3059
|
+
briefLines.push(
|
|
3060
|
+
`- **Workflow:** ${params.workflowMode === "brownfield-attach" ? "brownfield attach" : "greenfield scaffold"}`
|
|
3061
|
+
);
|
|
1408
3062
|
if (params.personality && params.personality.length > 0) {
|
|
1409
3063
|
briefLines.push(`- **Personality:** ${params.personality.join(". ")}`);
|
|
1410
3064
|
}
|
|
@@ -1423,8 +3077,18 @@ Start implementation from the shell layouts and shared route structure before fi
|
|
|
1423
3077
|
briefLines.push("|-------|--------|---------|");
|
|
1424
3078
|
for (const [name, def] of Object.entries(params.decoratorDefinitions)) {
|
|
1425
3079
|
const intent = def.intent || "";
|
|
1426
|
-
const
|
|
1427
|
-
|
|
3080
|
+
const props = def.suggested_properties ?? def.css ?? {};
|
|
3081
|
+
const base = Object.entries(props).map(([p, v]) => `${p}: ${v}`).join("; ");
|
|
3082
|
+
const hasHover = def.hover_properties && Object.keys(def.hover_properties).length > 0;
|
|
3083
|
+
const hasFocus = def.focus_properties && Object.keys(def.focus_properties).length > 0;
|
|
3084
|
+
const hasActive = def.active_properties && Object.keys(def.active_properties).length > 0;
|
|
3085
|
+
const stateMarkers = [
|
|
3086
|
+
hasHover && ":hover",
|
|
3087
|
+
hasFocus && ":focus-visible",
|
|
3088
|
+
hasActive && ":active"
|
|
3089
|
+
].filter((m) => Boolean(m));
|
|
3090
|
+
const stateSuffix = stateMarkers.length > 0 ? ` _(+ ${stateMarkers.join(", ")})_` : "";
|
|
3091
|
+
briefLines.push(`| \`.${name}\` | ${intent} | ${base}${stateSuffix} |`);
|
|
1428
3092
|
}
|
|
1429
3093
|
briefLines.push("");
|
|
1430
3094
|
} else if (params.decorators && params.decorators.length > 0) {
|
|
@@ -1438,7 +3102,9 @@ Start implementation from the shell layouts and shared route structure before fi
|
|
|
1438
3102
|
}
|
|
1439
3103
|
briefLines.push("## Development Workflow");
|
|
1440
3104
|
briefLines.push("");
|
|
1441
|
-
briefLines.push(
|
|
3105
|
+
briefLines.push(
|
|
3106
|
+
"The essence file (`decantr.essence.json`) is the source of truth for your project's structure. Context files in `.decantr/context/` are derived from it. When you need to add, remove, or modify pages, sections, or features:"
|
|
3107
|
+
);
|
|
1442
3108
|
briefLines.push("");
|
|
1443
3109
|
briefLines.push("**1. Update the essence** (use CLI commands for consistency):");
|
|
1444
3110
|
briefLines.push("- `decantr add page {section}/{page} --route /{path}`");
|
|
@@ -1571,9 +3237,7 @@ function generateScaffoldTaskContext(essence, scaffoldPack, manifest) {
|
|
|
1571
3237
|
const sectionRefs = manifest?.sections.map(
|
|
1572
3238
|
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1573
3239
|
) ?? [];
|
|
1574
|
-
const pageRefs = manifest?.pages.map(
|
|
1575
|
-
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1576
|
-
) ?? [];
|
|
3240
|
+
const pageRefs = manifest?.pages.map((page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``) ?? [];
|
|
1577
3241
|
return `# Task Context: Scaffolding
|
|
1578
3242
|
|
|
1579
3243
|
**Enforcement Tier: Creative** \u2014 Guard rules are advisory during initial scaffolding.
|
|
@@ -1627,9 +3291,7 @@ function generateAddPageTaskContext(essence, scaffoldPack, manifest) {
|
|
|
1627
3291
|
const sectionRefs = manifest?.sections.map(
|
|
1628
3292
|
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1629
3293
|
) ?? [];
|
|
1630
|
-
const pageRefs = manifest?.pages.map(
|
|
1631
|
-
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1632
|
-
) ?? [];
|
|
3294
|
+
const pageRefs = manifest?.pages.map((page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``) ?? [];
|
|
1633
3295
|
return `# Task Context: Adding Pages
|
|
1634
3296
|
|
|
1635
3297
|
**Enforcement Tier: Guided**
|
|
@@ -1682,9 +3344,7 @@ function generateModifyTaskContext(essence, scaffoldPack, manifest) {
|
|
|
1682
3344
|
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1683
3345
|
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1684
3346
|
}).join("\n") : "- No routes declared";
|
|
1685
|
-
const pageRefs = manifest?.pages.map(
|
|
1686
|
-
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1687
|
-
) ?? [];
|
|
3347
|
+
const pageRefs = manifest?.pages.map((page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``) ?? [];
|
|
1688
3348
|
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
1689
3349
|
return `# Task Context: Modifying Code
|
|
1690
3350
|
|
|
@@ -1729,14 +3389,18 @@ function generateEssenceSummaryV3(essence) {
|
|
|
1729
3389
|
let pagesTable;
|
|
1730
3390
|
if (sections.length > 0) {
|
|
1731
3391
|
const rows = sections.flatMap(
|
|
1732
|
-
(s) => s.pages.map(
|
|
3392
|
+
(s) => s.pages.map(
|
|
3393
|
+
(p) => `| ${p.id} | ${s.shell} | ${p.layout.map(serializeLayoutItem).join(", ") || "none"} |`
|
|
3394
|
+
)
|
|
1733
3395
|
);
|
|
1734
3396
|
pagesTable = `| Page | Shell | Layout |
|
|
1735
3397
|
|------|-------|--------|
|
|
1736
3398
|
${rows.join("\n")}`;
|
|
1737
3399
|
} else {
|
|
1738
3400
|
const shell = blueprint.shell ?? "sidebar-main";
|
|
1739
|
-
const rows = flatPages.map(
|
|
3401
|
+
const rows = flatPages.map(
|
|
3402
|
+
(p) => `| ${p.id} | ${shell} | ${p.layout.map(serializeLayoutItem).join(", ") || "none"} |`
|
|
3403
|
+
);
|
|
1740
3404
|
pagesTable = `| Page | Shell | Layout |
|
|
1741
3405
|
|------|-------|--------|
|
|
1742
3406
|
${rows.join("\n")}`;
|
|
@@ -1765,10 +3429,10 @@ ${rows.join("\n")}`;
|
|
|
1765
3429
|
return renderTemplate(template, vars);
|
|
1766
3430
|
}
|
|
1767
3431
|
function updateGitignore(projectRoot) {
|
|
1768
|
-
const gitignorePath =
|
|
3432
|
+
const gitignorePath = join2(projectRoot, ".gitignore");
|
|
1769
3433
|
const cacheEntry = ".decantr/cache/";
|
|
1770
|
-
if (
|
|
1771
|
-
const content =
|
|
3434
|
+
if (existsSync2(gitignorePath)) {
|
|
3435
|
+
const content = readFileSync2(gitignorePath, "utf-8");
|
|
1772
3436
|
if (!content.includes(cacheEntry)) {
|
|
1773
3437
|
appendFileSync(gitignorePath, `
|
|
1774
3438
|
# Decantr cache
|
|
@@ -1778,7 +3442,7 @@ ${cacheEntry}
|
|
|
1778
3442
|
}
|
|
1779
3443
|
return false;
|
|
1780
3444
|
} else {
|
|
1781
|
-
|
|
3445
|
+
writeFileSync2(gitignorePath, `# Decantr cache
|
|
1782
3446
|
${cacheEntry}
|
|
1783
3447
|
`);
|
|
1784
3448
|
return true;
|
|
@@ -1786,20 +3450,20 @@ ${cacheEntry}
|
|
|
1786
3450
|
}
|
|
1787
3451
|
async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
|
|
1788
3452
|
const essenceV3 = buildEssenceV3(options, archetypeData, themeData);
|
|
1789
|
-
const decantrDir =
|
|
1790
|
-
const contextDir =
|
|
1791
|
-
const cacheDir =
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
const essencePath =
|
|
1795
|
-
|
|
1796
|
-
const projectJsonPath =
|
|
3453
|
+
const decantrDir = join2(projectRoot, ".decantr");
|
|
3454
|
+
const contextDir = join2(decantrDir, "context");
|
|
3455
|
+
const cacheDir = join2(decantrDir, "cache");
|
|
3456
|
+
mkdirSync2(contextDir, { recursive: true });
|
|
3457
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
3458
|
+
const essencePath = join2(projectRoot, "decantr.essence.json");
|
|
3459
|
+
writeFileSync2(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
|
|
3460
|
+
const projectJsonPath = join2(decantrDir, "project.json");
|
|
1797
3461
|
const projectJsonStr = generateProjectJson(detected, options, registrySource);
|
|
1798
3462
|
const projectJsonObj = JSON.parse(projectJsonStr);
|
|
1799
3463
|
if (blueprintData?.voice) {
|
|
1800
3464
|
projectJsonObj.voice = blueprintData.voice;
|
|
1801
3465
|
}
|
|
1802
|
-
|
|
3466
|
+
writeFileSync2(projectJsonPath, JSON.stringify(projectJsonObj, null, 2));
|
|
1803
3467
|
const contextFiles = [];
|
|
1804
3468
|
if (composedSections) {
|
|
1805
3469
|
essenceV3.version = "3.1.0";
|
|
@@ -1820,9 +3484,12 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1820
3484
|
if (blueprintData?.navigation) {
|
|
1821
3485
|
essenceV3.meta.navigation = blueprintData.navigation;
|
|
1822
3486
|
}
|
|
1823
|
-
|
|
3487
|
+
writeFileSync2(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
|
|
1824
3488
|
}
|
|
1825
|
-
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, {
|
|
3489
|
+
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, {
|
|
3490
|
+
isInitialScaffold: true,
|
|
3491
|
+
patternSpecs
|
|
3492
|
+
});
|
|
1826
3493
|
contextFiles.push(...refreshResult.contextFiles);
|
|
1827
3494
|
const gitignoreUpdated = updateGitignore(projectRoot);
|
|
1828
3495
|
return {
|
|
@@ -1835,11 +3502,11 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1835
3502
|
};
|
|
1836
3503
|
}
|
|
1837
3504
|
function scaffoldMinimal(projectRoot) {
|
|
1838
|
-
const decantrDir =
|
|
1839
|
-
const customDir =
|
|
1840
|
-
const contentTypes =
|
|
3505
|
+
const decantrDir = join2(projectRoot, ".decantr");
|
|
3506
|
+
const customDir = join2(decantrDir, "custom");
|
|
3507
|
+
const contentTypes = API_CONTENT_TYPES2;
|
|
1841
3508
|
for (const type of contentTypes) {
|
|
1842
|
-
|
|
3509
|
+
mkdirSync2(join2(customDir, type), { recursive: true });
|
|
1843
3510
|
}
|
|
1844
3511
|
const essence = {
|
|
1845
3512
|
version: "3.0.0",
|
|
@@ -1887,9 +3554,7 @@ function scaffoldMinimal(projectRoot) {
|
|
|
1887
3554
|
},
|
|
1888
3555
|
blueprint: {
|
|
1889
3556
|
shell: "sidebar-main",
|
|
1890
|
-
pages: [
|
|
1891
|
-
{ id: "home", layout: ["hero"] }
|
|
1892
|
-
],
|
|
3557
|
+
pages: [{ id: "home", layout: ["hero"] }],
|
|
1893
3558
|
features: []
|
|
1894
3559
|
},
|
|
1895
3560
|
meta: {
|
|
@@ -1903,8 +3568,8 @@ function scaffoldMinimal(projectRoot) {
|
|
|
1903
3568
|
}
|
|
1904
3569
|
}
|
|
1905
3570
|
};
|
|
1906
|
-
const essencePath =
|
|
1907
|
-
|
|
3571
|
+
const essencePath = join2(projectRoot, "decantr.essence.json");
|
|
3572
|
+
writeFileSync2(essencePath, JSON.stringify(essence, null, 2) + "\n");
|
|
1908
3573
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1909
3574
|
const projectJson = {
|
|
1910
3575
|
detected: {
|
|
@@ -1936,9 +3601,9 @@ function scaffoldMinimal(projectRoot) {
|
|
|
1936
3601
|
workflowMode: "greenfield-scaffold"
|
|
1937
3602
|
}
|
|
1938
3603
|
};
|
|
1939
|
-
const projectJsonPath =
|
|
1940
|
-
|
|
1941
|
-
const decantrMdPath =
|
|
3604
|
+
const projectJsonPath = join2(decantrDir, "project.json");
|
|
3605
|
+
writeFileSync2(projectJsonPath, JSON.stringify(projectJson, null, 2));
|
|
3606
|
+
const decantrMdPath = join2(projectRoot, "DECANTR.md");
|
|
1942
3607
|
const decantrMdContent = `# DECANTR.md
|
|
1943
3608
|
|
|
1944
3609
|
> This file was generated by \`decantr init\` in offline/minimal mode.
|
|
@@ -2009,7 +3674,7 @@ When available, use these tools:
|
|
|
2009
3674
|
|
|
2010
3675
|
*Generated by @decantr/cli v${CLI_VERSION}*
|
|
2011
3676
|
`;
|
|
2012
|
-
|
|
3677
|
+
writeFileSync2(decantrMdPath, decantrMdContent);
|
|
2013
3678
|
const gitignoreUpdated = updateGitignore(projectRoot);
|
|
2014
3679
|
return {
|
|
2015
3680
|
essencePath,
|
|
@@ -2023,40 +3688,46 @@ When available, use these tools:
|
|
|
2023
3688
|
function writeExecutionPackArtifacts(basePathWithoutExtension, pack) {
|
|
2024
3689
|
const markdownPath = `${basePathWithoutExtension}.md`;
|
|
2025
3690
|
const jsonPath = `${basePathWithoutExtension}.json`;
|
|
2026
|
-
|
|
2027
|
-
|
|
3691
|
+
writeFileSync2(markdownPath, pack.renderedMarkdown);
|
|
3692
|
+
writeFileSync2(jsonPath, JSON.stringify(pack, null, 2) + "\n");
|
|
2028
3693
|
return markdownPath;
|
|
2029
3694
|
}
|
|
2030
3695
|
function writeExecutionPackBundleArtifacts(contextDir, bundle) {
|
|
2031
|
-
|
|
3696
|
+
mkdirSync2(contextDir, { recursive: true });
|
|
2032
3697
|
const outputPaths = [];
|
|
2033
|
-
const scaffoldPackPath = writeExecutionPackArtifacts(
|
|
3698
|
+
const scaffoldPackPath = writeExecutionPackArtifacts(
|
|
3699
|
+
join2(contextDir, "scaffold-pack"),
|
|
3700
|
+
bundle.scaffold
|
|
3701
|
+
);
|
|
2034
3702
|
outputPaths.push(scaffoldPackPath);
|
|
2035
|
-
const reviewPackPath = writeExecutionPackArtifacts(
|
|
3703
|
+
const reviewPackPath = writeExecutionPackArtifacts(
|
|
3704
|
+
join2(contextDir, "review-pack"),
|
|
3705
|
+
bundle.review
|
|
3706
|
+
);
|
|
2036
3707
|
outputPaths.push(reviewPackPath);
|
|
2037
3708
|
for (const sectionPack of bundle.sections) {
|
|
2038
3709
|
const sectionPackPath = writeExecutionPackArtifacts(
|
|
2039
|
-
|
|
3710
|
+
join2(contextDir, `section-${sectionPack.data.sectionId}-pack`),
|
|
2040
3711
|
sectionPack
|
|
2041
3712
|
);
|
|
2042
3713
|
outputPaths.push(sectionPackPath);
|
|
2043
3714
|
}
|
|
2044
3715
|
for (const pagePack of bundle.pages) {
|
|
2045
3716
|
const pagePackPath = writeExecutionPackArtifacts(
|
|
2046
|
-
|
|
3717
|
+
join2(contextDir, `page-${pagePack.data.pageId}-pack`),
|
|
2047
3718
|
pagePack
|
|
2048
3719
|
);
|
|
2049
3720
|
outputPaths.push(pagePackPath);
|
|
2050
3721
|
}
|
|
2051
3722
|
for (const mutationPack of bundle.mutations) {
|
|
2052
3723
|
const mutationPackPath = writeExecutionPackArtifacts(
|
|
2053
|
-
|
|
3724
|
+
join2(contextDir, `mutation-${mutationPack.data.mutationType}-pack`),
|
|
2054
3725
|
mutationPack
|
|
2055
3726
|
);
|
|
2056
3727
|
outputPaths.push(mutationPackPath);
|
|
2057
3728
|
}
|
|
2058
|
-
const manifestPath =
|
|
2059
|
-
|
|
3729
|
+
const manifestPath = join2(contextDir, "pack-manifest.json");
|
|
3730
|
+
writeFileSync2(manifestPath, JSON.stringify(bundle.manifest, null, 2) + "\n");
|
|
2060
3731
|
outputPaths.push(manifestPath);
|
|
2061
3732
|
return {
|
|
2062
3733
|
paths: outputPaths,
|
|
@@ -2071,10 +3742,10 @@ async function generatePackContexts(projectRoot, contextDir, essence) {
|
|
|
2071
3742
|
scaffoldPack: null,
|
|
2072
3743
|
manifest: null
|
|
2073
3744
|
};
|
|
2074
|
-
const cacheRoot =
|
|
2075
|
-
if (!
|
|
2076
|
-
const customRoot =
|
|
2077
|
-
const overridePaths =
|
|
3745
|
+
const cacheRoot = join2(projectRoot, ".decantr", "cache", "@official");
|
|
3746
|
+
if (!existsSync2(cacheRoot)) return emptyResult;
|
|
3747
|
+
const customRoot = join2(projectRoot, ".decantr", "custom");
|
|
3748
|
+
const overridePaths = existsSync2(customRoot) ? [customRoot] : void 0;
|
|
2078
3749
|
try {
|
|
2079
3750
|
const bundle = await compileExecutionPackBundle(essence, {
|
|
2080
3751
|
contentRoot: cacheRoot,
|
|
@@ -2086,7 +3757,22 @@ async function generatePackContexts(projectRoot, contextDir, essence) {
|
|
|
2086
3757
|
scaffoldPack: bundle.scaffold,
|
|
2087
3758
|
manifest: bundle.manifest
|
|
2088
3759
|
};
|
|
2089
|
-
} catch {
|
|
3760
|
+
} catch (err) {
|
|
3761
|
+
const YELLOW = "\x1B[33m";
|
|
3762
|
+
const DIM = "\x1B[2m";
|
|
3763
|
+
const RESET = "\x1B[0m";
|
|
3764
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3765
|
+
const short = message.length > 240 ? message.slice(0, 220) + "\u2026 (truncated)" : message;
|
|
3766
|
+
console.warn(
|
|
3767
|
+
`${YELLOW}\u26A0 Execution pack compilation failed \u2014 scaffold will ship narrative-only context.${RESET}`
|
|
3768
|
+
);
|
|
3769
|
+
console.warn(`${DIM} Reason: ${short}${RESET}`);
|
|
3770
|
+
console.warn(
|
|
3771
|
+
`${DIM} Cold-scaffolding LLMs won't get scaffold-pack.md / section-*-pack.md / page-*-pack.md.${RESET}`
|
|
3772
|
+
);
|
|
3773
|
+
console.warn(
|
|
3774
|
+
`${DIM} This is a known drift source \u2014 fix the underlying issue and re-run \`decantr refresh\`.${RESET}`
|
|
3775
|
+
);
|
|
2090
3776
|
return emptyResult;
|
|
2091
3777
|
}
|
|
2092
3778
|
}
|
|
@@ -2105,7 +3791,11 @@ async function resolvePatternSpec(name, registry, prefetched, includeExtendedFie
|
|
|
2105
3791
|
try {
|
|
2106
3792
|
const patResult = await registry.fetchPattern(name);
|
|
2107
3793
|
if (patResult?.data) {
|
|
2108
|
-
return mapRegistryPatternToPatternSpecSummary(
|
|
3794
|
+
return mapRegistryPatternToPatternSpecSummary(
|
|
3795
|
+
patResult.data,
|
|
3796
|
+
prefetched,
|
|
3797
|
+
includeExtendedFields
|
|
3798
|
+
);
|
|
2109
3799
|
}
|
|
2110
3800
|
} catch {
|
|
2111
3801
|
}
|
|
@@ -2128,20 +3818,21 @@ async function resolvePatternSpec(name, registry, prefetched, includeExtendedFie
|
|
|
2128
3818
|
return null;
|
|
2129
3819
|
}
|
|
2130
3820
|
async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, options) {
|
|
2131
|
-
const decantrDir =
|
|
2132
|
-
const contextDir =
|
|
2133
|
-
|
|
3821
|
+
const decantrDir = join2(projectRoot, ".decantr");
|
|
3822
|
+
const contextDir = join2(decantrDir, "context");
|
|
3823
|
+
mkdirSync2(contextDir, { recursive: true });
|
|
2134
3824
|
let storedBlueprintId;
|
|
2135
3825
|
let storedVoice;
|
|
2136
3826
|
let storedWorkflowMode;
|
|
2137
|
-
const projectJsonFilePath =
|
|
3827
|
+
const projectJsonFilePath = join2(decantrDir, "project.json");
|
|
2138
3828
|
let projectJsonData = {};
|
|
2139
|
-
if (
|
|
3829
|
+
if (existsSync2(projectJsonFilePath)) {
|
|
2140
3830
|
try {
|
|
2141
|
-
projectJsonData = JSON.parse(
|
|
3831
|
+
projectJsonData = JSON.parse(readFileSync2(projectJsonFilePath, "utf-8"));
|
|
2142
3832
|
if (projectJsonData.blueprintId) storedBlueprintId = projectJsonData.blueprintId;
|
|
2143
3833
|
if (projectJsonData.voice) storedVoice = projectJsonData.voice;
|
|
2144
|
-
if (projectJsonData.initialized?.workflowMode)
|
|
3834
|
+
if (projectJsonData.initialized?.workflowMode)
|
|
3835
|
+
storedWorkflowMode = projectJsonData.initialized.workflowMode;
|
|
2145
3836
|
} catch {
|
|
2146
3837
|
}
|
|
2147
3838
|
}
|
|
@@ -2152,7 +3843,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2152
3843
|
if (bpResult.data.voice) {
|
|
2153
3844
|
storedVoice = bpResult.data.voice;
|
|
2154
3845
|
projectJsonData.voice = bpResult.data.voice;
|
|
2155
|
-
|
|
3846
|
+
writeFileSync2(projectJsonFilePath, JSON.stringify(projectJsonData, null, 2));
|
|
2156
3847
|
}
|
|
2157
3848
|
}
|
|
2158
3849
|
} catch {
|
|
@@ -2168,13 +3859,14 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2168
3859
|
};
|
|
2169
3860
|
const personality = essence.dna.personality || [];
|
|
2170
3861
|
let themeData = prefetchedThemeData;
|
|
2171
|
-
if (!themeData)
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
3862
|
+
if (!themeData)
|
|
3863
|
+
try {
|
|
3864
|
+
const themeResult = await registry.fetchTheme(themeName);
|
|
3865
|
+
if (themeResult?.data) {
|
|
3866
|
+
themeData = mapRegistryThemeToThemeData(themeResult.data);
|
|
3867
|
+
}
|
|
3868
|
+
} catch {
|
|
2175
3869
|
}
|
|
2176
|
-
} catch {
|
|
2177
|
-
}
|
|
2178
3870
|
if (!themeData?.seed?.primary) {
|
|
2179
3871
|
try {
|
|
2180
3872
|
const apiUrl = registry.getApiUrl();
|
|
@@ -2204,20 +3896,60 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2204
3896
|
} catch {
|
|
2205
3897
|
}
|
|
2206
3898
|
}
|
|
2207
|
-
const stylesDir =
|
|
2208
|
-
|
|
3899
|
+
const stylesDir = join2(projectRoot, "src", "styles");
|
|
3900
|
+
mkdirSync2(stylesDir, { recursive: true });
|
|
2209
3901
|
const densityLevel = essence.dna?.spacing?.density || "comfortable";
|
|
2210
|
-
const spatialTokens = computeSpatialTokens(
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
3902
|
+
const spatialTokens = computeSpatialTokens(
|
|
3903
|
+
densityLevel,
|
|
3904
|
+
themeData?.spatial ? {
|
|
3905
|
+
section_padding: themeData.spatial.section_padding ?? void 0,
|
|
3906
|
+
density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
|
|
3907
|
+
content_gap_shift: themeData.spatial.content_gap_shift
|
|
3908
|
+
} : void 0
|
|
3909
|
+
);
|
|
3910
|
+
const tokensPath = join2(stylesDir, "tokens.css");
|
|
2216
3911
|
const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
|
|
2217
|
-
if (hasRealThemeData || !
|
|
2218
|
-
|
|
3912
|
+
if (hasRealThemeData || !existsSync2(tokensPath)) {
|
|
3913
|
+
if (themeData?.palette && mode && mode !== "auto") {
|
|
3914
|
+
const paletteEntries = Object.values(themeData.palette);
|
|
3915
|
+
const modeDefined = paletteEntries.some(
|
|
3916
|
+
(entry) => entry && typeof entry === "object" && entry[mode]
|
|
3917
|
+
);
|
|
3918
|
+
if (!modeDefined) {
|
|
3919
|
+
const supportedModes = Array.from(
|
|
3920
|
+
new Set(
|
|
3921
|
+
paletteEntries.flatMap(
|
|
3922
|
+
(entry) => entry && typeof entry === "object" ? Object.keys(entry) : []
|
|
3923
|
+
)
|
|
3924
|
+
)
|
|
3925
|
+
).sort();
|
|
3926
|
+
const YELLOW = "\x1B[33m";
|
|
3927
|
+
const RESET = "\x1B[0m";
|
|
3928
|
+
console.warn(
|
|
3929
|
+
`${YELLOW}\u26A0 Theme "${themeName}" does not define a "${mode}" palette variant.${RESET}`
|
|
3930
|
+
);
|
|
3931
|
+
console.warn(
|
|
3932
|
+
`${YELLOW} Supported modes in palette: ${supportedModes.join(", ") || "none"}.${RESET}`
|
|
3933
|
+
);
|
|
3934
|
+
console.warn(
|
|
3935
|
+
`${YELLOW} Tokens will use mode-aware defaults so the scaffold still renders a legible "${mode}" UI,`
|
|
3936
|
+
);
|
|
3937
|
+
console.warn(
|
|
3938
|
+
`${YELLOW} but the theme's personality may not land. Consider picking a different theme or adding`
|
|
3939
|
+
);
|
|
3940
|
+
console.warn(
|
|
3941
|
+
`${YELLOW} "${mode}" keys to the theme's palette in decantr-content.${RESET}`
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
const features = essence.blueprint?.features ?? [];
|
|
3946
|
+
const hasThemeToggle = features.includes("theme-toggle") || features.includes("theme_toggle");
|
|
3947
|
+
writeFileSync2(
|
|
3948
|
+
tokensPath,
|
|
3949
|
+
generateTokensCSS(themeData, mode, spatialTokens, { hasThemeToggle })
|
|
3950
|
+
);
|
|
2219
3951
|
}
|
|
2220
|
-
const treatmentsPath =
|
|
3952
|
+
const treatmentsPath = join2(stylesDir, "treatments.css");
|
|
2221
3953
|
let treatmentCSS = generateTreatmentCSS(
|
|
2222
3954
|
spatialTokens,
|
|
2223
3955
|
themeData?.treatments,
|
|
@@ -2227,10 +3959,10 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2227
3959
|
);
|
|
2228
3960
|
const personalityCSS = generatePersonalityCSS(personality || [], themeData || {});
|
|
2229
3961
|
treatmentCSS += personalityCSS;
|
|
2230
|
-
|
|
2231
|
-
const globalPath =
|
|
2232
|
-
if (!
|
|
2233
|
-
|
|
3962
|
+
writeFileSync2(treatmentsPath, treatmentCSS);
|
|
3963
|
+
const globalPath = join2(stylesDir, "global.css");
|
|
3964
|
+
if (!existsSync2(globalPath)) {
|
|
3965
|
+
writeFileSync2(globalPath, generateGlobalCSS(personality, essence));
|
|
2234
3966
|
}
|
|
2235
3967
|
const cssFiles = [tokensPath, treatmentsPath, globalPath];
|
|
2236
3968
|
const earlyDecoratorList = [];
|
|
@@ -2256,44 +3988,47 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2256
3988
|
if (!allFeatures.includes(f)) allFeatures.push(f);
|
|
2257
3989
|
}
|
|
2258
3990
|
}
|
|
2259
|
-
const decantrMdPath =
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
3991
|
+
const decantrMdPath = join2(projectRoot, "DECANTR.md");
|
|
3992
|
+
writeFileSync2(
|
|
3993
|
+
decantrMdPath,
|
|
3994
|
+
generateDecantrMdV31({
|
|
3995
|
+
guardMode,
|
|
3996
|
+
cssApproach: CSS_APPROACH_CONTENT,
|
|
3997
|
+
workflowMode: storedWorkflowMode,
|
|
3998
|
+
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
|
|
3999
|
+
themeName,
|
|
4000
|
+
themeMode: mode,
|
|
4001
|
+
themeShape: essence.dna.theme.shape || void 0,
|
|
4002
|
+
personality,
|
|
4003
|
+
sections: sectionSummaries.length > 0 ? sectionSummaries : void 0,
|
|
4004
|
+
features: allFeatures.length > 0 ? allFeatures : void 0,
|
|
4005
|
+
decorators: earlyDecoratorList.length > 0 ? earlyDecoratorList : void 0,
|
|
4006
|
+
decoratorDefinitions: themeData?.decorator_definitions
|
|
4007
|
+
})
|
|
4008
|
+
);
|
|
2274
4009
|
const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
|
|
2275
4010
|
const contextFiles = [];
|
|
2276
4011
|
if (!hasSections) {
|
|
2277
|
-
const summaryPath =
|
|
2278
|
-
|
|
4012
|
+
const summaryPath = join2(contextDir, "essence-summary.md");
|
|
4013
|
+
writeFileSync2(summaryPath, generateEssenceSummaryV3(essence));
|
|
2279
4014
|
contextFiles.push(summaryPath);
|
|
2280
4015
|
}
|
|
2281
4016
|
const packContexts = await generatePackContexts(projectRoot, contextDir, essence);
|
|
2282
|
-
const scaffoldTaskPath =
|
|
2283
|
-
|
|
4017
|
+
const scaffoldTaskPath = join2(contextDir, "task-scaffold.md");
|
|
4018
|
+
writeFileSync2(
|
|
2284
4019
|
scaffoldTaskPath,
|
|
2285
4020
|
generateScaffoldTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2286
4021
|
);
|
|
2287
4022
|
contextFiles.push(scaffoldTaskPath);
|
|
2288
4023
|
if (!options?.isInitialScaffold) {
|
|
2289
|
-
const addPagePath =
|
|
2290
|
-
|
|
4024
|
+
const addPagePath = join2(contextDir, "task-add-page.md");
|
|
4025
|
+
writeFileSync2(
|
|
2291
4026
|
addPagePath,
|
|
2292
4027
|
generateAddPageTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2293
4028
|
);
|
|
2294
4029
|
contextFiles.push(addPagePath);
|
|
2295
|
-
const modifyPath =
|
|
2296
|
-
|
|
4030
|
+
const modifyPath = join2(contextDir, "task-modify.md");
|
|
4031
|
+
writeFileSync2(
|
|
2297
4032
|
modifyPath,
|
|
2298
4033
|
generateModifyTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2299
4034
|
);
|
|
@@ -2346,7 +4081,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2346
4081
|
}
|
|
2347
4082
|
};
|
|
2348
4083
|
const topologyMarkdown = generateTopologySection(topologyData, personality);
|
|
2349
|
-
const themeTokensCss =
|
|
4084
|
+
const themeTokensCss = existsSync2(tokensPath) ? readFileSync2(tokensPath, "utf-8") : "";
|
|
2350
4085
|
const decoratorList = [];
|
|
2351
4086
|
if (themeData?.decorators) {
|
|
2352
4087
|
for (const [name, desc] of Object.entries(themeData.decorators)) {
|
|
@@ -2418,8 +4153,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2418
4153
|
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2419
4154
|
spatialHints: sectionSpatialHints
|
|
2420
4155
|
});
|
|
2421
|
-
const sectionContextPath =
|
|
2422
|
-
|
|
4156
|
+
const sectionContextPath = join2(contextDir, `section-${section.id}.md`);
|
|
4157
|
+
writeFileSync2(sectionContextPath, contextContent);
|
|
2423
4158
|
contextFiles.push(sectionContextPath);
|
|
2424
4159
|
}
|
|
2425
4160
|
const routes = blueprint.routes || {};
|
|
@@ -2436,8 +4171,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2436
4171
|
navigation: essence.meta.navigation,
|
|
2437
4172
|
voice: storedVoice
|
|
2438
4173
|
});
|
|
2439
|
-
const scaffoldMdPath =
|
|
2440
|
-
|
|
4174
|
+
const scaffoldMdPath = join2(contextDir, "scaffold.md");
|
|
4175
|
+
writeFileSync2(scaffoldMdPath, scaffoldContent);
|
|
2441
4176
|
contextFiles.push(scaffoldMdPath);
|
|
2442
4177
|
} else {
|
|
2443
4178
|
const pages = blueprint.pages || [{ id: "home", layout: ["hero"] }];
|
|
@@ -2465,7 +4200,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2465
4200
|
}
|
|
2466
4201
|
}
|
|
2467
4202
|
}
|
|
2468
|
-
const themeTokensCss =
|
|
4203
|
+
const themeTokensCss = existsSync2(tokensPath) ? readFileSync2(tokensPath, "utf-8") : "";
|
|
2469
4204
|
const decoratorList = [];
|
|
2470
4205
|
if (themeData?.decorators) {
|
|
2471
4206
|
for (const [name, desc] of Object.entries(themeData.decorators)) {
|
|
@@ -2507,8 +4242,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2507
4242
|
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2508
4243
|
spatialHints: v30SpatialHints
|
|
2509
4244
|
});
|
|
2510
|
-
const sectionContextPath =
|
|
2511
|
-
|
|
4245
|
+
const sectionContextPath = join2(contextDir, `section-${syntheticSection.id}.md`);
|
|
4246
|
+
writeFileSync2(sectionContextPath, contextContent);
|
|
2512
4247
|
contextFiles.push(sectionContextPath);
|
|
2513
4248
|
}
|
|
2514
4249
|
if (packContexts.paths.length > 0) {
|
|
@@ -2594,17 +4329,26 @@ function generateSyntheticComponents(patternId, description) {
|
|
|
2594
4329
|
if (patternId.includes("pricing")) syntheticComponents.push("Card", "Button", "Badge");
|
|
2595
4330
|
if (patternId.includes("testimonial")) syntheticComponents.push("Card", "Avatar", "Text");
|
|
2596
4331
|
if (patternId.includes("cta")) syntheticComponents.push("Button", "Text");
|
|
2597
|
-
if (patternId.includes("form") || patternId.includes("contact"))
|
|
4332
|
+
if (patternId.includes("form") || patternId.includes("contact"))
|
|
4333
|
+
syntheticComponents.push("Input", "Textarea", "Button", "Label");
|
|
2598
4334
|
if (patternId.includes("team")) syntheticComponents.push("Card", "Avatar", "Text");
|
|
2599
|
-
if (patternId.includes("settings") || patternId.includes("security"))
|
|
2600
|
-
|
|
2601
|
-
if (patternId.includes("
|
|
2602
|
-
|
|
2603
|
-
if (patternId.includes("
|
|
2604
|
-
|
|
4335
|
+
if (patternId.includes("settings") || patternId.includes("security"))
|
|
4336
|
+
syntheticComponents.push("Card", "Toggle", "Input", "Button");
|
|
4337
|
+
if (patternId.includes("message") || patternId.includes("chat"))
|
|
4338
|
+
syntheticComponents.push("Avatar", "Text", "CodeBlock");
|
|
4339
|
+
if (patternId.includes("input") && desc.includes("chat"))
|
|
4340
|
+
syntheticComponents.push("Textarea", "Button", "Icon");
|
|
4341
|
+
if (patternId.includes("header") && desc.includes("chat"))
|
|
4342
|
+
syntheticComponents.push("Button", "Icon", "Text");
|
|
4343
|
+
if (patternId.includes("content") || patternId.includes("legal"))
|
|
4344
|
+
syntheticComponents.push("Heading", "Text", "List");
|
|
4345
|
+
if (patternId.includes("how-it-works") || patternId.includes("steps"))
|
|
4346
|
+
syntheticComponents.push("Card", "Icon", "Text", "Badge");
|
|
2605
4347
|
if (patternId.includes("values")) syntheticComponents.push("Card", "Icon", "Text");
|
|
2606
|
-
if (patternId.includes("story") || patternId.includes("about"))
|
|
2607
|
-
|
|
4348
|
+
if (patternId.includes("story") || patternId.includes("about"))
|
|
4349
|
+
syntheticComponents.push("Text", "Image");
|
|
4350
|
+
if (patternId.includes("empty") || patternId.includes("new"))
|
|
4351
|
+
syntheticComponents.push("Icon", "Text", "Button");
|
|
2608
4352
|
return [...new Set(syntheticComponents)];
|
|
2609
4353
|
}
|
|
2610
4354
|
function mapRegistryPatternToPatternSpecSummary(pattern, prefetched, includeExtendedFields = true) {
|
|
@@ -2612,7 +4356,10 @@ function mapRegistryPatternToPatternSpecSummary(pattern, prefetched, includeExte
|
|
|
2612
4356
|
const preset = pattern.presets?.[defaultPreset];
|
|
2613
4357
|
let slots = preset?.layout?.slots || pattern.default_layout?.slots || prefetched?.slots || {};
|
|
2614
4358
|
if (Object.keys(slots).length === 0) {
|
|
2615
|
-
const synthetic = generateSyntheticSlots(
|
|
4359
|
+
const synthetic = generateSyntheticSlots(
|
|
4360
|
+
pattern.id,
|
|
4361
|
+
pattern.description || prefetched?.description || ""
|
|
4362
|
+
);
|
|
2616
4363
|
if (Object.keys(synthetic).length > 0) slots = synthetic;
|
|
2617
4364
|
}
|
|
2618
4365
|
const spec = {
|
|
@@ -2677,9 +4424,15 @@ function generateShellImplementation(shellId, shellInfo) {
|
|
|
2677
4424
|
}
|
|
2678
4425
|
lines.push("### Anti-patterns");
|
|
2679
4426
|
lines.push("");
|
|
2680
|
-
lines.push(
|
|
2681
|
-
|
|
2682
|
-
|
|
4427
|
+
lines.push(
|
|
4428
|
+
"- Do NOT nest `overflow-y-auto` inside another `overflow-y-auto` \u2014 one scroll container per region."
|
|
4429
|
+
);
|
|
4430
|
+
lines.push(
|
|
4431
|
+
"- Do NOT apply `d-surface` to shell frame regions (sidebar, header). Use `var(--d-surface)` or `var(--d-bg)` directly."
|
|
4432
|
+
);
|
|
4433
|
+
lines.push(
|
|
4434
|
+
"- Do NOT add wrapper `<div>` elements around shell regions \u2014 the grid areas handle placement."
|
|
4435
|
+
);
|
|
2683
4436
|
lines.push("");
|
|
2684
4437
|
} else {
|
|
2685
4438
|
if (shellInfo.layout) {
|
|
@@ -2766,31 +4519,70 @@ function generateQuickStart(input) {
|
|
|
2766
4519
|
function generateSpacingGuide(density, spatialHints) {
|
|
2767
4520
|
const lines = [];
|
|
2768
4521
|
const level = density === "compact" || density === "spacious" ? density : "comfortable";
|
|
2769
|
-
const tokens = computeSpatialTokens(
|
|
4522
|
+
const tokens = computeSpatialTokens(
|
|
4523
|
+
level,
|
|
4524
|
+
spatialHints
|
|
4525
|
+
);
|
|
2770
4526
|
lines.push("## Spacing Guide");
|
|
2771
4527
|
lines.push("");
|
|
2772
4528
|
lines.push("| Context | Token | Value | Usage |");
|
|
2773
4529
|
lines.push("|---------|-------|-------|-------|");
|
|
2774
|
-
lines.push(
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
lines.push(
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
lines.push(
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
lines.push(
|
|
2784
|
-
|
|
4530
|
+
lines.push(
|
|
4531
|
+
`| Content gap | \`--d-content-gap\` | \`${tokens["--d-content-gap"]}\` | Gap between sibling elements |`
|
|
4532
|
+
);
|
|
4533
|
+
lines.push(
|
|
4534
|
+
`| Section padding | \`--d-section-py\` | \`${tokens["--d-section-py"]}\` | Vertical padding on d-section |`
|
|
4535
|
+
);
|
|
4536
|
+
lines.push(
|
|
4537
|
+
`| Surface padding | \`--d-surface-p\` | \`${tokens["--d-surface-p"]}\` | Inner padding for d-surface |`
|
|
4538
|
+
);
|
|
4539
|
+
lines.push(
|
|
4540
|
+
`| Interactive V | \`--d-interactive-py\` | \`${tokens["--d-interactive-py"]}\` | Vertical padding on buttons |`
|
|
4541
|
+
);
|
|
4542
|
+
lines.push(
|
|
4543
|
+
`| Interactive H | \`--d-interactive-px\` | \`${tokens["--d-interactive-px"]}\` | Horizontal padding on buttons |`
|
|
4544
|
+
);
|
|
4545
|
+
lines.push(
|
|
4546
|
+
`| Control | \`--d-control-py\` | \`${tokens["--d-control-py"]}\` | Vertical padding on inputs |`
|
|
4547
|
+
);
|
|
4548
|
+
lines.push(
|
|
4549
|
+
`| Data row | \`--d-data-py\` | \`${tokens["--d-data-py"]}\` | Vertical padding on table rows |`
|
|
4550
|
+
);
|
|
4551
|
+
lines.push(
|
|
4552
|
+
`| Label gap | \`--d-label-mb\` | \`${tokens["--d-label-mb"]}\` | Gap below d-label section headers |`
|
|
4553
|
+
);
|
|
4554
|
+
lines.push(
|
|
4555
|
+
`| Label indent | \`--d-label-px\` | \`${tokens["--d-label-px"]}\` | Anchor indent for d-label[data-anchor] |`
|
|
4556
|
+
);
|
|
4557
|
+
lines.push(
|
|
4558
|
+
`| Section gap | \`--d-section-gap\` | \`${tokens["--d-section-gap"]}\` | Gap between adjacent d-sections |`
|
|
4559
|
+
);
|
|
4560
|
+
lines.push(
|
|
4561
|
+
`| Annotation gap | \`--d-annotation-mt\` | \`${tokens["--d-annotation-mt"]}\` | Top margin on d-annotation |`
|
|
4562
|
+
);
|
|
2785
4563
|
lines.push("");
|
|
2786
4564
|
return lines;
|
|
2787
4565
|
}
|
|
2788
4566
|
function generateSectionContext(input) {
|
|
2789
|
-
const {
|
|
4567
|
+
const {
|
|
4568
|
+
section,
|
|
4569
|
+
decorators,
|
|
4570
|
+
guardConfig,
|
|
4571
|
+
personality,
|
|
4572
|
+
themeName,
|
|
4573
|
+
zoneContext,
|
|
4574
|
+
patternSpecs,
|
|
4575
|
+
themeHints,
|
|
4576
|
+
constraints,
|
|
4577
|
+
shellInfo,
|
|
4578
|
+
spatialHints
|
|
4579
|
+
} = input;
|
|
2790
4580
|
const lines = [];
|
|
2791
4581
|
lines.push(`# Section: ${section.id}`);
|
|
2792
4582
|
lines.push("");
|
|
2793
|
-
lines.push(
|
|
4583
|
+
lines.push(
|
|
4584
|
+
`**Role:** ${section.role} | **Shell:** ${section.shell} | **Archetype:** ${section.id}`
|
|
4585
|
+
);
|
|
2794
4586
|
lines.push(`**Description:** ${section.description}`);
|
|
2795
4587
|
if (section.dna_overrides) {
|
|
2796
4588
|
const parts = [];
|
|
@@ -2818,9 +4610,15 @@ function generateSectionContext(input) {
|
|
|
2818
4610
|
}
|
|
2819
4611
|
lines.push("- Density-responsive bottom gap via `--d-label-mb` x `--d-density-scale`");
|
|
2820
4612
|
if (sectionDensity) {
|
|
2821
|
-
const scaleMap = {
|
|
4613
|
+
const scaleMap = {
|
|
4614
|
+
compact: "0.65",
|
|
4615
|
+
comfortable: "1",
|
|
4616
|
+
spacious: "1.4"
|
|
4617
|
+
};
|
|
2822
4618
|
lines.push("");
|
|
2823
|
-
lines.push(
|
|
4619
|
+
lines.push(
|
|
4620
|
+
`Section density: ${sectionDensity} (--d-density-scale: ${scaleMap[sectionDensity] || "1"})`
|
|
4621
|
+
);
|
|
2824
4622
|
}
|
|
2825
4623
|
lines.push("");
|
|
2826
4624
|
}
|
|
@@ -2834,94 +4632,54 @@ function generateSectionContext(input) {
|
|
|
2834
4632
|
}
|
|
2835
4633
|
lines.push("");
|
|
2836
4634
|
}
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
lines.push("
|
|
4635
|
+
const sectionDensityOverride = section.dna_overrides?.density;
|
|
4636
|
+
const effectiveDensity = sectionDensityOverride || "comfortable";
|
|
4637
|
+
lines.push("## Theme Reference");
|
|
2840
4638
|
lines.push("");
|
|
2841
|
-
lines.push(
|
|
2842
|
-
|
|
2843
|
-
|
|
4639
|
+
lines.push(
|
|
4640
|
+
`**Theme:** ${themeName} (${input.themeMode || "dark"}) \xB7 **Density:** ${effectiveDensity}${sectionDensityOverride ? " _(DNA override)_" : ""}`
|
|
4641
|
+
);
|
|
2844
4642
|
lines.push("");
|
|
2845
|
-
lines.push(
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
"
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
"text-muted": "Secondary text, placeholders, labels",
|
|
2854
|
-
primary: "Brand color, key interactive, selected states",
|
|
2855
|
-
"primary-hover": "Hover state for primary elements",
|
|
2856
|
-
secondary: "Secondary brand color, supporting elements",
|
|
2857
|
-
"accent-glow": "Ambient glow effect for accent-colored elements"
|
|
2858
|
-
};
|
|
2859
|
-
const paletteToTokenName = {
|
|
2860
|
-
"background": "bg"
|
|
2861
|
-
};
|
|
2862
|
-
const addedTokens = /* @__PURE__ */ new Set();
|
|
2863
|
-
if (input.themeData?.palette) {
|
|
2864
|
-
const modeKey = input.themeMode || "dark";
|
|
2865
|
-
for (const [name, values] of Object.entries(input.themeData.palette)) {
|
|
2866
|
-
if (!addedTokens.has(name)) {
|
|
2867
|
-
addedTokens.add(name);
|
|
2868
|
-
const tokenName = paletteToTokenName[name] || name;
|
|
2869
|
-
const val = values[modeKey] || values.dark || values.light || Object.values(values)[0];
|
|
2870
|
-
lines.push(`| \`--d-${tokenName}\` | \`${val}\` | ${semanticRoles[name] || ""} |`);
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
if (input.themeData?.seed?.accent && !addedTokens.has("accent")) {
|
|
2875
|
-
addedTokens.add("accent");
|
|
2876
|
-
lines.push(`| \`--d-accent\` | \`${input.themeData.seed.accent}\` | CTAs, links, active states, glow effects |`);
|
|
2877
|
-
}
|
|
2878
|
-
if (!addedTokens.has("accent-glow")) {
|
|
2879
|
-
const accentGlowVal = input.themeData?.palette?.["accent-glow"]?.[input.themeMode || "dark"] || input.themeData?.tokens?.base?.["accent-glow"];
|
|
2880
|
-
if (accentGlowVal) {
|
|
2881
|
-
addedTokens.add("accent-glow");
|
|
2882
|
-
lines.push(`| \`--d-accent-glow\` | \`${accentGlowVal}\` | Ambient glow effect around accent elements |`);
|
|
2883
|
-
}
|
|
4643
|
+
lines.push(
|
|
4644
|
+
"Full palette tokens, spacing-guide table, and decorator reference live in `DECANTR.md` (project root). These values are identical across sections in this scaffold unless a DNA override above changes density."
|
|
4645
|
+
);
|
|
4646
|
+
if (sectionDensityOverride) {
|
|
4647
|
+
lines.push("");
|
|
4648
|
+
lines.push("Because this section overrides density, the spacing guide is emitted below:");
|
|
4649
|
+
lines.push("");
|
|
4650
|
+
lines.push(...generateSpacingGuide(effectiveDensity, spatialHints));
|
|
2884
4651
|
}
|
|
2885
4652
|
lines.push("");
|
|
2886
|
-
lines.push("
|
|
4653
|
+
lines.push("---");
|
|
4654
|
+
lines.push("");
|
|
4655
|
+
lines.push(
|
|
4656
|
+
`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`
|
|
4657
|
+
);
|
|
2887
4658
|
lines.push("");
|
|
2888
|
-
lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
|
|
2889
4659
|
const decoratorDefs = input.themeData?.decorator_definitions;
|
|
2890
4660
|
if (decoratorDefs && Object.keys(decoratorDefs).length > 0) {
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
const
|
|
2897
|
-
|
|
2898
|
-
const pairsWith = def.pairs_with || "";
|
|
2899
|
-
lines.push(`| \`.${name}\` | ${intent} | ${cssProps} | ${pairsWith} |`);
|
|
2900
|
-
}
|
|
2901
|
-
lines.push("");
|
|
2902
|
-
lines.push("**Decorator usage guide:**");
|
|
2903
|
-
for (const [name, def] of Object.entries(decoratorDefs)) {
|
|
2904
|
-
if (def.usage && def.usage.length > 0) {
|
|
2905
|
-
lines.push(`- \`.${name}\`: ${def.usage.join(", ")}`);
|
|
4661
|
+
const usageEntries = Object.entries(decoratorDefs).filter(
|
|
4662
|
+
([, def]) => def.usage && def.usage.length > 0
|
|
4663
|
+
);
|
|
4664
|
+
if (usageEntries.length > 0) {
|
|
4665
|
+
lines.push("**Section decorators (usage hints):**");
|
|
4666
|
+
for (const [name, def] of usageEntries) {
|
|
4667
|
+
lines.push(`- \`.${name}\`: ${(def.usage || []).join(", ")}`);
|
|
2906
4668
|
}
|
|
4669
|
+
lines.push("");
|
|
2907
4670
|
}
|
|
2908
|
-
lines.push("");
|
|
2909
4671
|
} else if (decorators.length > 0) {
|
|
2910
|
-
lines.push("**
|
|
2911
|
-
lines.push("");
|
|
2912
|
-
lines.push("| Class | Usage |");
|
|
2913
|
-
lines.push("|-------|-------|");
|
|
4672
|
+
lines.push("**Section decorators:**");
|
|
2914
4673
|
for (const d of decorators) {
|
|
2915
|
-
lines.push(
|
|
4674
|
+
lines.push(`- \`.${d.name}\` \u2014 ${d.description}`);
|
|
2916
4675
|
}
|
|
2917
4676
|
lines.push("");
|
|
2918
|
-
} else {
|
|
2919
|
-
lines.push("No theme decorators defined.");
|
|
2920
|
-
lines.push("");
|
|
2921
4677
|
}
|
|
2922
4678
|
if (themeHints) {
|
|
2923
4679
|
if (themeHints.preferred && themeHints.preferred.length > 0) {
|
|
2924
|
-
const sectionPatterns = new Set(
|
|
4680
|
+
const sectionPatterns = new Set(
|
|
4681
|
+
section.pages.flatMap((p) => p.layout.flatMap(extractPatternNames))
|
|
4682
|
+
);
|
|
2925
4683
|
const relevant = themeHints.preferred.filter((p) => sectionPatterns.has(p));
|
|
2926
4684
|
if (relevant.length > 0) {
|
|
2927
4685
|
lines.push(`**Preferred:** ${relevant.join(", ")}`);
|
|
@@ -2937,7 +4695,9 @@ function generateSectionContext(input) {
|
|
|
2937
4695
|
}
|
|
2938
4696
|
const themePrefix = themeName.split("-")[0] || themeName;
|
|
2939
4697
|
lines.push("");
|
|
2940
|
-
lines.push(
|
|
4698
|
+
lines.push(
|
|
4699
|
+
`Usage: \`className={css('_flex _col _gap4') + ' d-surface ${themePrefix}-glass'}\` \u2014 atoms via css(), treatments and theme decorators as plain class strings.`
|
|
4700
|
+
);
|
|
2941
4701
|
lines.push("");
|
|
2942
4702
|
lines.push("---");
|
|
2943
4703
|
lines.push("");
|
|
@@ -2963,11 +4723,15 @@ function generateSectionContext(input) {
|
|
|
2963
4723
|
const pLower = personalityText.toLowerCase();
|
|
2964
4724
|
const utils = [];
|
|
2965
4725
|
if (pLower.includes("neon") || pLower.includes("glow"))
|
|
2966
|
-
utils.push(
|
|
4726
|
+
utils.push(
|
|
4727
|
+
"`neon-glow`, `neon-glow-hover`, `neon-text-glow`, `neon-border-glow` \u2014 Apply to elements needing accent emphasis"
|
|
4728
|
+
);
|
|
2967
4729
|
if (pLower.includes("mono") || pLower.includes("monospace"))
|
|
2968
4730
|
utils.push("`mono-data` \u2014 Monospace + tabular-nums for metrics, IDs, timestamps");
|
|
2969
4731
|
if (pLower.includes("pulse") || pLower.includes("ring") || pLower.includes("status"))
|
|
2970
|
-
utils.push(
|
|
4732
|
+
utils.push(
|
|
4733
|
+
'`status-ring` with `data-status="active|idle|error|processing"` \u2014 Color-coded status with pulse animation'
|
|
4734
|
+
);
|
|
2971
4735
|
if (utils.length > 0) {
|
|
2972
4736
|
lines.push("**Personality utilities available in treatments.css:**");
|
|
2973
4737
|
for (const u of utils) lines.push(`- ${u}`);
|
|
@@ -2978,7 +4742,9 @@ function generateSectionContext(input) {
|
|
|
2978
4742
|
lines.push("## Constraints");
|
|
2979
4743
|
lines.push("");
|
|
2980
4744
|
for (const [key, value] of Object.entries(constraints)) {
|
|
2981
|
-
lines.push(
|
|
4745
|
+
lines.push(
|
|
4746
|
+
`- **${key}:** ${typeof value === "object" && value !== null ? JSON.stringify(value) : value}`
|
|
4747
|
+
);
|
|
2982
4748
|
}
|
|
2983
4749
|
lines.push("");
|
|
2984
4750
|
lines.push("---");
|
|
@@ -2996,8 +4762,12 @@ function generateSectionContext(input) {
|
|
|
2996
4762
|
if (uniquePatterns.size > 0) {
|
|
2997
4763
|
lines.push("## Pattern Reference");
|
|
2998
4764
|
lines.push("");
|
|
2999
|
-
lines.push(
|
|
3000
|
-
|
|
4765
|
+
lines.push(
|
|
4766
|
+
"Scaffold-tier rule: implement the core visual structure, states, and required slots first."
|
|
4767
|
+
);
|
|
4768
|
+
lines.push(
|
|
4769
|
+
"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."
|
|
4770
|
+
);
|
|
3001
4771
|
lines.push("");
|
|
3002
4772
|
for (const [patternName, spec] of uniquePatterns) {
|
|
3003
4773
|
lines.push(`### ${patternName}`);
|
|
@@ -3032,12 +4802,17 @@ function generateSectionContext(input) {
|
|
|
3032
4802
|
if (spec.motion) {
|
|
3033
4803
|
const entries = [];
|
|
3034
4804
|
const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3035
|
-
if (isObj(spec.motion.micro))
|
|
4805
|
+
if (isObj(spec.motion.micro))
|
|
4806
|
+
for (const [k, v] of Object.entries(spec.motion.micro)) entries.push([k, v]);
|
|
3036
4807
|
else if (typeof spec.motion.micro === "string") entries.push(["micro", spec.motion.micro]);
|
|
3037
|
-
if (isObj(spec.motion.transitions))
|
|
3038
|
-
|
|
3039
|
-
if (
|
|
3040
|
-
|
|
4808
|
+
if (isObj(spec.motion.transitions))
|
|
4809
|
+
for (const [k, v] of Object.entries(spec.motion.transitions)) entries.push([k, v]);
|
|
4810
|
+
else if (typeof spec.motion.transitions === "string")
|
|
4811
|
+
entries.push(["transitions", spec.motion.transitions]);
|
|
4812
|
+
if (isObj(spec.motion.ambient))
|
|
4813
|
+
for (const [k, v] of Object.entries(spec.motion.ambient)) entries.push([k, v]);
|
|
4814
|
+
else if (typeof spec.motion.ambient === "string")
|
|
4815
|
+
entries.push(["ambient", spec.motion.ambient]);
|
|
3041
4816
|
if (entries.length > 0) {
|
|
3042
4817
|
lines.push("**Motion:**");
|
|
3043
4818
|
lines.push("| Interaction | Animation |");
|
|
@@ -3049,16 +4824,21 @@ function generateSectionContext(input) {
|
|
|
3049
4824
|
if (spec.responsive) {
|
|
3050
4825
|
lines.push("**Responsive:**");
|
|
3051
4826
|
if (spec.responsive.mobile) lines.push(`- **Mobile (<640px):** ${spec.responsive.mobile}`);
|
|
3052
|
-
if (spec.responsive.tablet)
|
|
3053
|
-
|
|
4827
|
+
if (spec.responsive.tablet)
|
|
4828
|
+
lines.push(`- **Tablet (640-1024px):** ${spec.responsive.tablet}`);
|
|
4829
|
+
if (spec.responsive.desktop)
|
|
4830
|
+
lines.push(`- **Desktop (>1024px):** ${spec.responsive.desktop}`);
|
|
3054
4831
|
lines.push("");
|
|
3055
4832
|
}
|
|
3056
4833
|
if (spec.accessibility) {
|
|
3057
4834
|
lines.push("**Accessibility:**");
|
|
3058
4835
|
if (spec.accessibility.role) lines.push(`- Role: \`${spec.accessibility.role}\``);
|
|
3059
|
-
if (Array.isArray(spec.accessibility.keyboard) && spec.accessibility.keyboard.length)
|
|
3060
|
-
|
|
3061
|
-
if (spec.accessibility.
|
|
4836
|
+
if (Array.isArray(spec.accessibility.keyboard) && spec.accessibility.keyboard.length)
|
|
4837
|
+
lines.push(`- Keyboard: ${spec.accessibility.keyboard.join("; ")}`);
|
|
4838
|
+
if (Array.isArray(spec.accessibility.announcements) && spec.accessibility.announcements.length)
|
|
4839
|
+
lines.push(`- Announcements: ${spec.accessibility.announcements.join("; ")}`);
|
|
4840
|
+
if (spec.accessibility.focus_management)
|
|
4841
|
+
lines.push(`- Focus: ${spec.accessibility.focus_management}`);
|
|
3062
4842
|
lines.push("");
|
|
3063
4843
|
}
|
|
3064
4844
|
lines.push("");
|
|
@@ -3090,7 +4870,18 @@ function generateSectionContext(input) {
|
|
|
3090
4870
|
return lines.join("\n");
|
|
3091
4871
|
}
|
|
3092
4872
|
function generateScaffoldContext(input) {
|
|
3093
|
-
const {
|
|
4873
|
+
const {
|
|
4874
|
+
appName,
|
|
4875
|
+
blueprintId,
|
|
4876
|
+
themeName,
|
|
4877
|
+
personality,
|
|
4878
|
+
topologyMarkdown,
|
|
4879
|
+
sections,
|
|
4880
|
+
routes,
|
|
4881
|
+
constraints,
|
|
4882
|
+
seo,
|
|
4883
|
+
navigation
|
|
4884
|
+
} = input;
|
|
3094
4885
|
const lines = [];
|
|
3095
4886
|
lines.push(`# Scaffold: ${appName}`);
|
|
3096
4887
|
lines.push("");
|
|
@@ -3103,7 +4894,8 @@ function generateScaffoldContext(input) {
|
|
|
3103
4894
|
lines.push("## Voice & Copy");
|
|
3104
4895
|
lines.push("");
|
|
3105
4896
|
if (input.voice.tone) lines.push(`**Tone:** ${input.voice.tone}`);
|
|
3106
|
-
if (input.voice.cta_verbs?.length)
|
|
4897
|
+
if (input.voice.cta_verbs?.length)
|
|
4898
|
+
lines.push(`**CTA verbs:** ${input.voice.cta_verbs.join(", ")}`);
|
|
3107
4899
|
if (input.voice.avoid?.length) lines.push(`**Avoid:** ${input.voice.avoid.join(", ")}`);
|
|
3108
4900
|
if (input.voice.empty_states) lines.push(`**Empty states:** ${input.voice.empty_states}`);
|
|
3109
4901
|
if (input.voice.errors) lines.push(`**Errors:** ${input.voice.errors}`);
|
|
@@ -3115,10 +4907,18 @@ function generateScaffoldContext(input) {
|
|
|
3115
4907
|
lines.push("");
|
|
3116
4908
|
lines.push("For local development and showcases, wire all zone transitions with mock data:");
|
|
3117
4909
|
lines.push("");
|
|
3118
|
-
lines.push(
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
lines.push(
|
|
4910
|
+
lines.push(
|
|
4911
|
+
"- **Auth bypass:** Auth pages should accept any input and redirect to the primary section's default route"
|
|
4912
|
+
);
|
|
4913
|
+
lines.push(
|
|
4914
|
+
"- **Route guards:** Check a simple localStorage flag (e.g., `decantr_authenticated`). Login sets it \u2192 redirect to app zone entry. Logout clears it \u2192 redirect to public/gateway zone."
|
|
4915
|
+
);
|
|
4916
|
+
lines.push(
|
|
4917
|
+
"- **Mock data on every page:** All pages should render with simulated data on first load \u2014 never show empty states during development"
|
|
4918
|
+
);
|
|
4919
|
+
lines.push(
|
|
4920
|
+
"- **Zone transitions:** CTA links on marketing pages should route to the gateway (login/register). Successful auth should route to the primary section default page."
|
|
4921
|
+
);
|
|
3122
4922
|
lines.push("");
|
|
3123
4923
|
lines.push(topologyMarkdown);
|
|
3124
4924
|
lines.push("");
|
|
@@ -3175,7 +4975,9 @@ function generateScaffoldContext(input) {
|
|
|
3175
4975
|
lines.push("## Design Constraints");
|
|
3176
4976
|
lines.push("");
|
|
3177
4977
|
for (const [key, value] of Object.entries(constraints)) {
|
|
3178
|
-
lines.push(
|
|
4978
|
+
lines.push(
|
|
4979
|
+
`- **${key}:** ${typeof value === "object" && value !== null ? JSON.stringify(value) : value}`
|
|
4980
|
+
);
|
|
3179
4981
|
}
|
|
3180
4982
|
lines.push("");
|
|
3181
4983
|
}
|
|
@@ -3195,7 +4997,9 @@ function generateScaffoldContext(input) {
|
|
|
3195
4997
|
lines.push("");
|
|
3196
4998
|
if (navigation.command_palette) {
|
|
3197
4999
|
lines.push("- Command palette: enabled");
|
|
3198
|
-
lines.push(
|
|
5000
|
+
lines.push(
|
|
5001
|
+
"- Requirement: implement a real keyboard-triggered command palette, not just placeholder UI text."
|
|
5002
|
+
);
|
|
3199
5003
|
}
|
|
3200
5004
|
if (navigation.hotkeys && navigation.hotkeys.length > 0) {
|
|
3201
5005
|
lines.push(`- Hotkeys: ${navigation.hotkeys.length} configured`);
|
|
@@ -3205,229 +5009,21 @@ function generateScaffoldContext(input) {
|
|
|
3205
5009
|
lines.push(` - \`${hotkey.key}\`${target ? `: ${target}` : ""}`);
|
|
3206
5010
|
}
|
|
3207
5011
|
}
|
|
3208
|
-
lines.push(
|
|
3209
|
-
|
|
5012
|
+
lines.push(
|
|
5013
|
+
"- Requirement: implement these bindings as real keyboard shortcuts, not as decorative text."
|
|
5014
|
+
);
|
|
5015
|
+
lines.push(
|
|
5016
|
+
"- Presentation rule: do not append hotkey text to persistent nav labels, breadcrumbs, or page titles unless the shell or route contract explicitly requests visible shortcut hints."
|
|
5017
|
+
);
|
|
3210
5018
|
}
|
|
3211
5019
|
lines.push("");
|
|
3212
5020
|
}
|
|
3213
5021
|
return lines.join("\n");
|
|
3214
5022
|
}
|
|
3215
5023
|
|
|
3216
|
-
// src/registry.ts
|
|
3217
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync } from "fs";
|
|
3218
|
-
import { join as join2 } from "path";
|
|
3219
|
-
import { RegistryAPIClient, API_CONTENT_TYPES as API_CONTENT_TYPES2 } from "@decantr/registry";
|
|
3220
|
-
var DEFAULT_API_URL = "https://api.decantr.ai/v1";
|
|
3221
|
-
var ALL_CONTENT_TYPES = API_CONTENT_TYPES2;
|
|
3222
|
-
function loadFromCache(cacheDir, contentType, id, namespace) {
|
|
3223
|
-
const nsDir = namespace ? join2(cacheDir, namespace) : cacheDir;
|
|
3224
|
-
const cachePath = id ? join2(nsDir, contentType, `${id}.json`) : join2(nsDir, contentType, "index.json");
|
|
3225
|
-
if (!existsSync2(cachePath)) return null;
|
|
3226
|
-
try {
|
|
3227
|
-
const data = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
3228
|
-
return { data, source: { type: "cache" } };
|
|
3229
|
-
} catch {
|
|
3230
|
-
return null;
|
|
3231
|
-
}
|
|
3232
|
-
}
|
|
3233
|
-
function saveToCache(cacheDir, contentType, id, data, namespace = "@official") {
|
|
3234
|
-
const dir = join2(cacheDir, namespace, contentType);
|
|
3235
|
-
mkdirSync2(dir, { recursive: true });
|
|
3236
|
-
const cachePath = id ? join2(dir, `${id}.json`) : join2(dir, "index.json");
|
|
3237
|
-
writeFileSync2(cachePath, JSON.stringify(data, null, 2));
|
|
3238
|
-
}
|
|
3239
|
-
var RegistryClient = class {
|
|
3240
|
-
cacheDir;
|
|
3241
|
-
apiUrl;
|
|
3242
|
-
offline;
|
|
3243
|
-
projectRoot;
|
|
3244
|
-
apiClient;
|
|
3245
|
-
constructor(options = {}) {
|
|
3246
|
-
this.projectRoot = options.projectRoot || process.cwd();
|
|
3247
|
-
this.cacheDir = options.cacheDir || join2(this.projectRoot, ".decantr", "cache");
|
|
3248
|
-
this.apiUrl = options.apiUrl || process.env.DECANTR_API_URL || DEFAULT_API_URL;
|
|
3249
|
-
this.offline = options.offline || false;
|
|
3250
|
-
this.apiClient = new RegistryAPIClient({
|
|
3251
|
-
baseUrl: this.apiUrl,
|
|
3252
|
-
apiKey: options.apiKey || process.env.DECANTR_API_KEY || void 0
|
|
3253
|
-
});
|
|
3254
|
-
}
|
|
3255
|
-
getApiUrl() {
|
|
3256
|
-
return this.apiUrl;
|
|
3257
|
-
}
|
|
3258
|
-
/**
|
|
3259
|
-
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
3260
|
-
* Works for ALL content types, not just themes.
|
|
3261
|
-
*/
|
|
3262
|
-
loadCustomContent(contentType, id) {
|
|
3263
|
-
const customPath = join2(
|
|
3264
|
-
this.projectRoot,
|
|
3265
|
-
".decantr",
|
|
3266
|
-
"custom",
|
|
3267
|
-
contentType,
|
|
3268
|
-
`${id}.json`
|
|
3269
|
-
);
|
|
3270
|
-
if (!existsSync2(customPath)) return null;
|
|
3271
|
-
try {
|
|
3272
|
-
const data = JSON.parse(readFileSync2(customPath, "utf-8"));
|
|
3273
|
-
return { data, source: { type: "custom", path: customPath } };
|
|
3274
|
-
} catch {
|
|
3275
|
-
return null;
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
/**
|
|
3279
|
-
* List all custom content of a given type from .decantr/custom/{type}/
|
|
3280
|
-
*/
|
|
3281
|
-
listCustomContent(contentType) {
|
|
3282
|
-
const dir = join2(this.projectRoot, ".decantr", "custom", contentType);
|
|
3283
|
-
if (!existsSync2(dir)) return [];
|
|
3284
|
-
try {
|
|
3285
|
-
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
3286
|
-
const data = JSON.parse(readFileSync2(join2(dir, f), "utf-8"));
|
|
3287
|
-
return { id: data.id || f.replace(".json", ""), ...data };
|
|
3288
|
-
});
|
|
3289
|
-
} catch {
|
|
3290
|
-
return [];
|
|
3291
|
-
}
|
|
3292
|
-
}
|
|
3293
|
-
/**
|
|
3294
|
-
* Unified fetch for a content list.
|
|
3295
|
-
* Resolution: API -> Cache. Custom items are merged into the list.
|
|
3296
|
-
*/
|
|
3297
|
-
async fetchContentList(contentType, namespace, sort, recommended, intelligenceSource) {
|
|
3298
|
-
let apiItems = [];
|
|
3299
|
-
let source = { type: "cache" };
|
|
3300
|
-
if (!this.offline) {
|
|
3301
|
-
try {
|
|
3302
|
-
const apiResult = await this.apiClient.listContent(contentType, {
|
|
3303
|
-
namespace,
|
|
3304
|
-
sort,
|
|
3305
|
-
recommended,
|
|
3306
|
-
intelligenceSource
|
|
3307
|
-
});
|
|
3308
|
-
apiItems = apiResult.items;
|
|
3309
|
-
source = { type: "api", url: this.apiUrl };
|
|
3310
|
-
saveToCache(this.cacheDir, contentType, null, apiResult, namespace || "@official");
|
|
3311
|
-
} catch {
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
if (apiItems.length === 0) {
|
|
3315
|
-
const cacheResult = loadFromCache(
|
|
3316
|
-
this.cacheDir,
|
|
3317
|
-
contentType,
|
|
3318
|
-
void 0,
|
|
3319
|
-
namespace
|
|
3320
|
-
);
|
|
3321
|
-
if (cacheResult) {
|
|
3322
|
-
apiItems = cacheResult.data.items;
|
|
3323
|
-
source = { type: "cache" };
|
|
3324
|
-
}
|
|
3325
|
-
}
|
|
3326
|
-
const customItems = this.listCustomContent(contentType);
|
|
3327
|
-
const allItems = [...customItems, ...apiItems];
|
|
3328
|
-
return {
|
|
3329
|
-
data: { items: allItems, total: allItems.length },
|
|
3330
|
-
source
|
|
3331
|
-
};
|
|
3332
|
-
}
|
|
3333
|
-
/**
|
|
3334
|
-
* Unified fetch for a single content item.
|
|
3335
|
-
* Resolution: Custom -> API -> Cache
|
|
3336
|
-
*/
|
|
3337
|
-
async fetchContentItem(contentType, id, namespace = "@official") {
|
|
3338
|
-
const customId = id.startsWith("custom:") ? id.slice(7) : id;
|
|
3339
|
-
const customResult = this.loadCustomContent(contentType, customId);
|
|
3340
|
-
if (customResult) return customResult;
|
|
3341
|
-
if (id.startsWith("custom:")) return null;
|
|
3342
|
-
if (!this.offline) {
|
|
3343
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
3344
|
-
try {
|
|
3345
|
-
const data = await this.apiClient.getContent(contentType, namespace, id);
|
|
3346
|
-
saveToCache(this.cacheDir, contentType, id, data, namespace);
|
|
3347
|
-
return { data, source: { type: "api", url: this.apiUrl } };
|
|
3348
|
-
} catch (e) {
|
|
3349
|
-
if (process.env.DECANTR_DEBUG) {
|
|
3350
|
-
console.error(` [debug] API fetch ${attempt === 0 ? "failed" : "retry failed"} for ${contentType}/${namespace}/${id}: ${e.message}`);
|
|
3351
|
-
}
|
|
3352
|
-
if (attempt === 0) {
|
|
3353
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
3354
|
-
}
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
|
-
} else if (process.env.DECANTR_DEBUG) {
|
|
3358
|
-
console.error(` [debug] Skipping API (offline mode) for ${contentType}/${namespace}/${id}`);
|
|
3359
|
-
}
|
|
3360
|
-
return loadFromCache(this.cacheDir, contentType, id, namespace);
|
|
3361
|
-
}
|
|
3362
|
-
// ── Convenience methods (delegate to unified fetch) ──
|
|
3363
|
-
async fetchArchetypes() {
|
|
3364
|
-
return this.fetchContentList("archetypes");
|
|
3365
|
-
}
|
|
3366
|
-
async fetchArchetype(id) {
|
|
3367
|
-
return this.fetchContentItem("archetypes", id);
|
|
3368
|
-
}
|
|
3369
|
-
async fetchBlueprints() {
|
|
3370
|
-
return this.fetchContentList("blueprints");
|
|
3371
|
-
}
|
|
3372
|
-
async fetchBlueprint(id) {
|
|
3373
|
-
return this.fetchContentItem("blueprints", id);
|
|
3374
|
-
}
|
|
3375
|
-
async fetchThemes() {
|
|
3376
|
-
return this.fetchContentList("themes");
|
|
3377
|
-
}
|
|
3378
|
-
async fetchTheme(id) {
|
|
3379
|
-
return this.fetchContentItem("themes", id);
|
|
3380
|
-
}
|
|
3381
|
-
async fetchPatterns() {
|
|
3382
|
-
return this.fetchContentList("patterns");
|
|
3383
|
-
}
|
|
3384
|
-
async fetchPattern(id) {
|
|
3385
|
-
return this.fetchContentItem("patterns", id);
|
|
3386
|
-
}
|
|
3387
|
-
async fetchShells() {
|
|
3388
|
-
return this.fetchContentList("shells");
|
|
3389
|
-
}
|
|
3390
|
-
async fetchShell(id) {
|
|
3391
|
-
return this.fetchContentItem("shells", id);
|
|
3392
|
-
}
|
|
3393
|
-
/**
|
|
3394
|
-
* Check if API is available.
|
|
3395
|
-
*/
|
|
3396
|
-
async checkApiAvailability() {
|
|
3397
|
-
if (this.offline) return false;
|
|
3398
|
-
return this.apiClient.checkHealth();
|
|
3399
|
-
}
|
|
3400
|
-
};
|
|
3401
|
-
async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
3402
|
-
const apiClient = new RegistryAPIClient({ baseUrl: apiUrl });
|
|
3403
|
-
const synced = [];
|
|
3404
|
-
const failed = [];
|
|
3405
|
-
const healthy = await apiClient.checkHealth();
|
|
3406
|
-
if (!healthy) {
|
|
3407
|
-
return { synced: [], failed: ["API unavailable"] };
|
|
3408
|
-
}
|
|
3409
|
-
for (const type of ALL_CONTENT_TYPES) {
|
|
3410
|
-
try {
|
|
3411
|
-
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
3412
|
-
saveToCache(cacheDir, type, null, result, "@official");
|
|
3413
|
-
for (const item of result.items) {
|
|
3414
|
-
const slug = item.slug;
|
|
3415
|
-
const data = item.data;
|
|
3416
|
-
const innerSlug = data?.id || data?.slug;
|
|
3417
|
-
const cacheKey = slug || innerSlug || item.id;
|
|
3418
|
-
if (cacheKey) {
|
|
3419
|
-
saveToCache(cacheDir, type, cacheKey, item, "@official");
|
|
3420
|
-
}
|
|
3421
|
-
}
|
|
3422
|
-
synced.push(type);
|
|
3423
|
-
} catch {
|
|
3424
|
-
failed.push(type);
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
return { synced, failed };
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
5024
|
export {
|
|
5025
|
+
RegistryClient,
|
|
5026
|
+
syncRegistry,
|
|
3431
5027
|
collectPatternIdsFromItems,
|
|
3432
5028
|
mapRegistryArchetypeToArchetypeData,
|
|
3433
5029
|
composeArchetypes,
|
|
@@ -3440,7 +5036,5 @@ export {
|
|
|
3440
5036
|
scaffoldMinimal,
|
|
3441
5037
|
writeExecutionPackBundleArtifacts,
|
|
3442
5038
|
refreshDerivedFiles,
|
|
3443
|
-
mapRegistryPatternToPatternSpecSummary
|
|
3444
|
-
RegistryClient,
|
|
3445
|
-
syncRegistry
|
|
5039
|
+
mapRegistryPatternToPatternSpecSummary
|
|
3446
5040
|
};
|