@monoes/monodesign 1.2.11 → 1.2.12
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/cli/engine/browser/injected/index.mjs +16 -18
- package/cli/engine/cli/main.mjs +3 -3
- package/cli/engine/detect-antipatterns-browser.js +67 -47
- package/cli/engine/engines/regex/detect-text.mjs +6 -6
- package/cli/engine/engines/static-html/css-cascade.mjs +6 -5
- package/cli/engine/engines/static-html/detect-html.mjs +6 -6
- package/cli/engine/fix/index.mjs +3 -3
- package/cli/engine/node/file-system.mjs +1 -1
- package/cli/engine/registry/antipatterns.mjs +1 -1
- package/cli/engine/rules/checks.mjs +24 -25
- package/cli/engine/shared/color.mjs +2 -2
- package/cli/engine/shared/constants.mjs +1 -1
- package/cli/engine/shared/inline-ignores.mjs +2 -2
- package/cli/lib/monodesign-config.mjs +5 -5
- package/package.json +11 -10
- package/skill/scripts/context.mjs +4 -4
- package/skill/scripts/critique-storage.mjs +1 -1
- package/skill/scripts/detect-csp.mjs +1 -6
- package/skill/scripts/hook-admin.mjs +6 -6
- package/skill/scripts/hook-before-edit.mjs +4 -4
- package/skill/scripts/hook-lib.mjs +20 -11
- package/skill/scripts/hook.mjs +1 -1
- package/skill/scripts/lib/design-parser.mjs +9 -42
- package/skill/scripts/lib/is-generated.mjs +1 -1
- package/skill/scripts/lib/monodesign-paths.mjs +1 -1
- package/skill/scripts/live/event-validation.mjs +13 -13
- package/skill/scripts/live/manual-apply.mjs +4 -4
- package/skill/scripts/live/manual-edits-buffer.mjs +1 -1
- package/skill/scripts/live/session-store.mjs +5 -5
- package/skill/scripts/live/svelte-component.mjs +13 -13
- package/skill/scripts/live/sveltekit-adapter.mjs +4 -4
- package/skill/scripts/live/ui-core.mjs +1 -1
- package/skill/scripts/live-accept.mjs +24 -24
- package/skill/scripts/live-browser-dom.js +8 -7
- package/skill/scripts/live-browser-session.js +8 -7
- package/skill/scripts/live-browser.js +405 -432
- package/skill/scripts/live-commit-manual-edits.mjs +6 -6
- package/skill/scripts/live-copy-edit-agent.mjs +2 -2
- package/skill/scripts/live-discard-manual-edits.mjs +1 -1
- package/skill/scripts/live-inject.mjs +7 -7
- package/skill/scripts/live-insert.mjs +11 -11
- package/skill/scripts/live-manual-edit-evidence.mjs +4 -4
- package/skill/scripts/live-poll.mjs +5 -5
- package/skill/scripts/live-server.mjs +10 -10
- package/skill/scripts/live-wrap.mjs +31 -41
- package/skill/scripts/live.mjs +5 -5
- package/skill/scripts/palette.mjs +3 -1
- package/skill/scripts/pin.mjs +1 -1
|
@@ -73,6 +73,15 @@ export const SENSITIVE_PATH = new RegExp([
|
|
|
73
73
|
// Hard-skip regex for generated, lock, minified, and build-output paths.
|
|
74
74
|
export const GENERATED_PATH = /(?:\.generated\.[a-z]+$|\.d\.ts$|\.min\.[a-z]+$|[/\\]node_modules[/\\]|[/\\](?:dist|build|out|\.next|\.cache|coverage)[/\\]|[/\\]?[^/\\]+\.lock(?:\.json)?$)/i;
|
|
75
75
|
|
|
76
|
+
// GENERATED_PATH checked against the path inside the project, so a project
|
|
77
|
+
// that lives under e.g. ~/.cache/ or /srv/build/ is still scanned. Paths
|
|
78
|
+
// outside the project root fall back to the absolute path.
|
|
79
|
+
export function isGeneratedPath(filePath, projectRoot) {
|
|
80
|
+
const rel = path.relative(path.resolve(projectRoot), path.resolve(filePath));
|
|
81
|
+
const inside = rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
82
|
+
return GENERATED_PATH.test(inside ? path.sep + rel : filePath);
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
export const TRUTHY = /^(1|true|yes|on)$/i;
|
|
77
86
|
|
|
78
87
|
export const DEFAULT_CONFIG = Object.freeze({
|
|
@@ -184,7 +193,7 @@ export function resolveCacheCwd(primaryFile, sessionCwd) {
|
|
|
184
193
|
export function resolveProjectPlatform(cwd) {
|
|
185
194
|
try {
|
|
186
195
|
const ctx = loadContext(cwd);
|
|
187
|
-
return extractPlatform(ctx
|
|
196
|
+
return extractPlatform(ctx?.product);
|
|
188
197
|
} catch {
|
|
189
198
|
return null;
|
|
190
199
|
}
|
|
@@ -239,7 +248,7 @@ function applyDetectorConfigSource(config, raw) {
|
|
|
239
248
|
if (raw.designSystem && typeof raw.designSystem === 'object' && !Array.isArray(raw.designSystem)) {
|
|
240
249
|
config.designSystem = {
|
|
241
250
|
...config.designSystem,
|
|
242
|
-
enabled: raw.designSystem.enabled
|
|
251
|
+
enabled: raw.designSystem.enabled !== false,
|
|
243
252
|
};
|
|
244
253
|
}
|
|
245
254
|
if (Array.isArray(raw.ignoreRules)) {
|
|
@@ -302,10 +311,10 @@ export function matchConfiguredExtension(filePath, extensions) {
|
|
|
302
311
|
|
|
303
312
|
function applyConfigSource(config, raw) {
|
|
304
313
|
if (!raw || typeof raw !== 'object') return config;
|
|
305
|
-
if (Object.
|
|
306
|
-
config.enabled = raw.enabled
|
|
314
|
+
if (Object.hasOwn(raw, 'enabled')) {
|
|
315
|
+
config.enabled = raw.enabled !== false;
|
|
307
316
|
}
|
|
308
|
-
if (Object.
|
|
317
|
+
if (Object.hasOwn(raw, 'quiet')) {
|
|
309
318
|
config.quiet = raw.quiet === true;
|
|
310
319
|
}
|
|
311
320
|
if (typeof raw.auditLog === 'string' && raw.auditLog.trim()) {
|
|
@@ -399,7 +408,7 @@ function splitColorArgs(body) {
|
|
|
399
408
|
if (text.includes(',')) {
|
|
400
409
|
const parts = text.split(',').map((part) => part.trim()).filter(Boolean);
|
|
401
410
|
const last = parts[parts.length - 1];
|
|
402
|
-
if (last
|
|
411
|
+
if (last?.includes('/')) {
|
|
403
412
|
const split = last.split('/').map((part) => part.trim()).filter(Boolean);
|
|
404
413
|
return [...parts.slice(0, -1), ...split];
|
|
405
414
|
}
|
|
@@ -944,7 +953,7 @@ function clampGroupedToBudget(header, lines, footer, maxChars) {
|
|
|
944
953
|
footer,
|
|
945
954
|
].join('\n');
|
|
946
955
|
|
|
947
|
-
|
|
956
|
+
const working = lines.slice();
|
|
948
957
|
let omitted = false;
|
|
949
958
|
let assembled = assemble(working, omitted);
|
|
950
959
|
while (assembled.length > maxChars && working.length > 1) {
|
|
@@ -967,7 +976,7 @@ function clampToBudget(header, lines, more, footer, maxChars) {
|
|
|
967
976
|
return blocks.join('\n');
|
|
968
977
|
};
|
|
969
978
|
|
|
970
|
-
|
|
979
|
+
const working = lines.slice();
|
|
971
980
|
let moreText = more;
|
|
972
981
|
let assembled = assemble(working, moreText);
|
|
973
982
|
while (assembled.length > maxChars && working.length > 1) {
|
|
@@ -1351,7 +1360,7 @@ export function writeAuditLog(env, entry, cwd = process.cwd()) {
|
|
|
1351
1360
|
expanded = path.resolve(baseCwd, target);
|
|
1352
1361
|
}
|
|
1353
1362
|
fs.mkdirSync(path.dirname(expanded), { recursive: true });
|
|
1354
|
-
const line = JSON.stringify({ ts: new Date().toISOString(), ...entry })
|
|
1363
|
+
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`;
|
|
1355
1364
|
fs.appendFileSync(expanded, line);
|
|
1356
1365
|
return true;
|
|
1357
1366
|
} catch {
|
|
@@ -1573,7 +1582,7 @@ export async function runHook({ stdinJson, env = {}, cwd = process.cwd(), now =
|
|
|
1573
1582
|
lastSkip = 'sensitive';
|
|
1574
1583
|
continue;
|
|
1575
1584
|
}
|
|
1576
|
-
if (
|
|
1585
|
+
if (isGeneratedPath(filePath, projectCwd)) {
|
|
1577
1586
|
lastSkip = 'generated';
|
|
1578
1587
|
continue;
|
|
1579
1588
|
}
|
|
@@ -1756,7 +1765,7 @@ export async function runHook({ stdinJson, env = {}, cwd = process.cwd(), now =
|
|
|
1756
1765
|
return {
|
|
1757
1766
|
exitCode: 0,
|
|
1758
1767
|
stdout: '',
|
|
1759
|
-
audit: { ...audit, error: String(err
|
|
1768
|
+
audit: { ...audit, error: String(err?.message ? err.message : err) },
|
|
1760
1769
|
};
|
|
1761
1770
|
}
|
|
1762
1771
|
}
|
package/skill/scripts/hook.mjs
CHANGED
|
@@ -51,7 +51,7 @@ main().catch((err) => {
|
|
|
51
51
|
writeAuditLog(process.env, {
|
|
52
52
|
ts: new Date().toISOString(),
|
|
53
53
|
event: 'PostToolUse',
|
|
54
|
-
error: String(err
|
|
54
|
+
error: String(err?.message ? err.message : err),
|
|
55
55
|
});
|
|
56
56
|
} catch { /* swallow */ }
|
|
57
57
|
if (process.env.MONODESIGN_HOOK_DEBUG) {
|
|
@@ -130,9 +130,6 @@ function parseScalar(raw) {
|
|
|
130
130
|
|
|
131
131
|
const HEX_RE = /#[0-9a-fA-F]{3,8}\b/g;
|
|
132
132
|
const OKLCH_RE = /oklch\([^)]+\)/gi;
|
|
133
|
-
const RGBA_RE = /rgba?\([^)]+\)/gi;
|
|
134
|
-
const BOX_SHADOW_RE = /(?:box-shadow:\s*)?((?:-?\d[\w\d\s\-.,/()#%]*)+)/;
|
|
135
|
-
const NAMED_RULE_RE = /\*\*(The [^*]+?Rule)\.\*\*\s*(.+)/;
|
|
136
133
|
|
|
137
134
|
// ---------- Section splitting ----------
|
|
138
135
|
|
|
@@ -246,7 +243,7 @@ function collectBullets(lines) {
|
|
|
246
243
|
}
|
|
247
244
|
// continuation of a bullet (indented line)
|
|
248
245
|
if (current && raw.match(/^\s{2,}\S/)) {
|
|
249
|
-
current +=
|
|
246
|
+
current += ` ${raw.trim()}`;
|
|
250
247
|
continue;
|
|
251
248
|
}
|
|
252
249
|
// blank line ends a bullet
|
|
@@ -415,7 +412,7 @@ function parseColorBullet(bullet) {
|
|
|
415
412
|
|
|
416
413
|
// Case 1 (Monodesign): **Name** (value-with-maybe-nested-parens): description
|
|
417
414
|
const bold = text.match(/^\*\*(.+?)\*\*\s*(.*)$/);
|
|
418
|
-
if (bold
|
|
415
|
+
if (bold?.[2].startsWith('(')) {
|
|
419
416
|
const value = extractParenGroup(bold[2]);
|
|
420
417
|
if (value !== null) {
|
|
421
418
|
const after = bold[2].slice(value.length + 2).trimStart();
|
|
@@ -485,36 +482,6 @@ function detectFormat(v) {
|
|
|
485
482
|
return 'unknown';
|
|
486
483
|
}
|
|
487
484
|
|
|
488
|
-
function scanInlineColors(lines) {
|
|
489
|
-
const out = [];
|
|
490
|
-
for (const line of lines) {
|
|
491
|
-
if (!/^\s*[-*]\s/.test(line)) continue;
|
|
492
|
-
const trimmed = line.replace(/^\s*[-*]\s+/, '');
|
|
493
|
-
const color = parseColorBullet(trimmed);
|
|
494
|
-
if (color) out.push(color);
|
|
495
|
-
}
|
|
496
|
-
return out;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function parseStitchInlineGroups(lines) {
|
|
500
|
-
// Stitch writes: `* **Primary (`#00478d` to `#005eb8`):** Use for "..."`
|
|
501
|
-
// Each bullet IS its own role. Group them under the spoken role name.
|
|
502
|
-
const out = [];
|
|
503
|
-
for (const line of lines) {
|
|
504
|
-
if (!/^\s*[-*]\s/.test(line)) continue;
|
|
505
|
-
const trimmed = line.replace(/^\s*[-*]\s+/, '').trim();
|
|
506
|
-
const m = trimmed.match(
|
|
507
|
-
/^\*\*([A-Z][a-zA-Z]+)\s*\(([^)]+)\):\*\*\s*(.*)$/
|
|
508
|
-
);
|
|
509
|
-
if (m) {
|
|
510
|
-
const role = m[1];
|
|
511
|
-
const color = buildColor(role, m[2], m[3]);
|
|
512
|
-
out.push({ role, colors: [color] });
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return out;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
485
|
function extractTypography(section) {
|
|
519
486
|
if (!section) return null;
|
|
520
487
|
const text = section.lines.join('\n');
|
|
@@ -611,7 +578,7 @@ function extractElevation(section) {
|
|
|
611
578
|
const shadows = [];
|
|
612
579
|
const seen = new Set();
|
|
613
580
|
const dedupe = (entry) => {
|
|
614
|
-
const key =
|
|
581
|
+
const key = `${entry.name || ''}::${entry.value}`;
|
|
615
582
|
if (seen.has(key)) return;
|
|
616
583
|
seen.add(key);
|
|
617
584
|
shadows.push(entry);
|
|
@@ -660,7 +627,7 @@ function extractInlineShadows(text) {
|
|
|
660
627
|
.trim();
|
|
661
628
|
if (stripped) {
|
|
662
629
|
name =
|
|
663
|
-
stripped.charAt(0).toUpperCase() + stripped.slice(1)
|
|
630
|
+
`${stripped.charAt(0).toUpperCase() + stripped.slice(1)} shadow`;
|
|
664
631
|
}
|
|
665
632
|
}
|
|
666
633
|
out.push({
|
|
@@ -830,11 +797,11 @@ export function parseDesignMd(md) {
|
|
|
830
797
|
schemaVersion: 2,
|
|
831
798
|
title,
|
|
832
799
|
frontmatter,
|
|
833
|
-
overview: extractOverview(sections
|
|
834
|
-
colors: extractColors(sections
|
|
835
|
-
typography: extractTypography(sections
|
|
836
|
-
elevation: extractElevation(sections
|
|
837
|
-
components: extractComponents(sections
|
|
800
|
+
overview: extractOverview(sections.Overview),
|
|
801
|
+
colors: extractColors(sections.Colors),
|
|
802
|
+
typography: extractTypography(sections.Typography),
|
|
803
|
+
elevation: extractElevation(sections.Elevation),
|
|
804
|
+
components: extractComponents(sections.Components),
|
|
838
805
|
dosDonts: extractDosDonts(sections["Do's and Don'ts"]),
|
|
839
806
|
};
|
|
840
807
|
}
|
|
@@ -46,7 +46,7 @@ function isGitIgnored(absPath, cwd) {
|
|
|
46
46
|
stdio: 'ignore',
|
|
47
47
|
});
|
|
48
48
|
return true; // exit 0 = ignored
|
|
49
|
-
} catch
|
|
49
|
+
} catch {
|
|
50
50
|
// Exit code 1 = not ignored. Exit code 128 = not a git repo or other error.
|
|
51
51
|
// In both cases, treat as "not known to be ignored."
|
|
52
52
|
return false;
|
|
@@ -43,7 +43,7 @@ export function getLegacyLiveConfigPath(scriptsDir) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function resolveLiveConfigPath({ cwd = process.cwd(), scriptsDir, env = process.env, targetPath } = {}) {
|
|
46
|
-
if (env.MONODESIGN_LIVE_CONFIG
|
|
46
|
+
if (env.MONODESIGN_LIVE_CONFIG?.trim()) {
|
|
47
47
|
const configured = env.MONODESIGN_LIVE_CONFIG.trim();
|
|
48
48
|
return path.isAbsolute(configured) ? configured : path.resolve(cwd, configured);
|
|
49
49
|
}
|
|
@@ -62,30 +62,30 @@ function validateInsertGenerate(msg) {
|
|
|
62
62
|
|
|
63
63
|
function validateReplaceGenerate(msg) {
|
|
64
64
|
if (!msg.action || !VISUAL_ACTIONS.includes(msg.action)) return 'generate: invalid action';
|
|
65
|
-
if (!msg.element
|
|
65
|
+
if (!msg.element?.outerHTML) return 'generate: missing element context';
|
|
66
66
|
return validateAnnotationFields(msg);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function validateManualEditEvent(msg, label) {
|
|
70
|
-
if (!isValidId(msg.id)) return label
|
|
71
|
-
if (!msg.pageUrl || typeof msg.pageUrl !== 'string') return label
|
|
72
|
-
if (!msg.element || typeof msg.element !== 'object') return label
|
|
73
|
-
if (!Array.isArray(msg.ops) || msg.ops.length === 0) return label
|
|
74
|
-
if (msg.ops.length > 100) return label
|
|
70
|
+
if (!isValidId(msg.id)) return `${label}: missing or malformed id`;
|
|
71
|
+
if (!msg.pageUrl || typeof msg.pageUrl !== 'string') return `${label}: missing pageUrl`;
|
|
72
|
+
if (!msg.element || typeof msg.element !== 'object') return `${label}: missing element`;
|
|
73
|
+
if (!Array.isArray(msg.ops) || msg.ops.length === 0) return `${label}: ops must be non-empty array`;
|
|
74
|
+
if (msg.ops.length > 100) return `${label}: too many ops (max 100)`;
|
|
75
75
|
for (const op of msg.ops) {
|
|
76
|
-
if (typeof op.ref !== 'string') return label
|
|
77
|
-
if (typeof op.tag !== 'string') return label
|
|
78
|
-
if (typeof op.originalText !== 'string') return label
|
|
76
|
+
if (typeof op.ref !== 'string') return `${label}: op.ref required`;
|
|
77
|
+
if (typeof op.tag !== 'string') return `${label}: op.tag required`;
|
|
78
|
+
if (typeof op.originalText !== 'string') return `${label}: op.originalText required`;
|
|
79
79
|
if (op.deleted !== true && typeof op.newText !== 'string') {
|
|
80
|
-
return label
|
|
80
|
+
return `${label}: text op requires newText`;
|
|
81
81
|
}
|
|
82
82
|
if (typeof op.newText === 'string') {
|
|
83
83
|
if (op.deleted !== true && op.newText.trim().length === 0) {
|
|
84
|
-
return label
|
|
84
|
+
return `${label}: newText cannot be empty`;
|
|
85
85
|
}
|
|
86
86
|
const forbidden = validateManualEditText(op.newText);
|
|
87
87
|
if (forbidden) {
|
|
88
|
-
return label
|
|
88
|
+
return `${label}: newText cannot contain ${forbidden.join(' ')} (plain text only; ask the AI to insert markup)`;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -132,6 +132,6 @@ export function validateEvent(msg) {
|
|
|
132
132
|
if (msg.pageUrl !== undefined && typeof msg.pageUrl !== 'string') return 'steer: pageUrl must be string';
|
|
133
133
|
return null;
|
|
134
134
|
default:
|
|
135
|
-
return
|
|
135
|
+
return `Unknown event type: ${msg.type}`;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -326,7 +326,7 @@ export function writeManualApplyEvidence(eventId, batch, cwd = process.cwd()) {
|
|
|
326
326
|
const dir = manualApplyEvidenceDir(cwd);
|
|
327
327
|
fs.mkdirSync(dir, { recursive: true });
|
|
328
328
|
const evidencePath = path.join(dir, `${eventId}.json`);
|
|
329
|
-
fs.writeFileSync(evidencePath, JSON.stringify(batch, null, 2)
|
|
329
|
+
fs.writeFileSync(evidencePath, `${JSON.stringify(batch, null, 2)}\n`, 'utf-8');
|
|
330
330
|
return evidencePath;
|
|
331
331
|
}
|
|
332
332
|
|
|
@@ -495,7 +495,7 @@ function invalidManualApplyResult(reason, eventId, extra = {}) {
|
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
export function validateManualApplyResultMessage(msg, deferred) {
|
|
498
|
-
|
|
498
|
+
const data = msg?.data;
|
|
499
499
|
const eventId = msg?.id || deferred?.event?.id || 'EVENT_ID';
|
|
500
500
|
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
501
501
|
return invalidManualApplyResult('missing_result_data', eventId);
|
|
@@ -743,7 +743,7 @@ export function writeManualApplyTransaction({ cwd = process.cwd(), pageUrl = nul
|
|
|
743
743
|
}),
|
|
744
744
|
};
|
|
745
745
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
746
|
-
fs.writeFileSync(`${file}.tmp`, JSON.stringify(transaction, null, 2)
|
|
746
|
+
fs.writeFileSync(`${file}.tmp`, `${JSON.stringify(transaction, null, 2)}\n`, 'utf-8');
|
|
747
747
|
fs.renameSync(`${file}.tmp`, file);
|
|
748
748
|
return transaction;
|
|
749
749
|
}
|
|
@@ -935,5 +935,5 @@ export function compactManualLogText(value, max = 200) {
|
|
|
935
935
|
if (typeof value !== 'string') return undefined;
|
|
936
936
|
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
937
937
|
if (normalized.length <= max) return normalized;
|
|
938
|
-
return normalized.slice(0, max)
|
|
938
|
+
return `${normalized.slice(0, max)}... [truncated ${normalized.length - max} chars]`;
|
|
939
939
|
}
|
|
@@ -41,7 +41,7 @@ function readBufferInternal(cwd, { strict }) {
|
|
|
41
41
|
return { version: BUFFER_VERSION, entries: parsed.entries };
|
|
42
42
|
} catch (err) {
|
|
43
43
|
if (strict && err?.code !== 'ENOENT') {
|
|
44
|
-
throw new Error(
|
|
44
|
+
throw new Error(`manual_edit_buffer_unreadable: ${err.message || String(err)}`);
|
|
45
45
|
}
|
|
46
46
|
return { version: BUFFER_VERSION, entries: [] };
|
|
47
47
|
}
|
|
@@ -51,7 +51,7 @@ export function createLiveSessionStore({ cwd = process.cwd(), sessionId } = {})
|
|
|
51
51
|
ts: new Date().toISOString(),
|
|
52
52
|
event: normalized,
|
|
53
53
|
};
|
|
54
|
-
fs.appendFileSync(journalPath, JSON.stringify(entry)
|
|
54
|
+
fs.appendFileSync(journalPath, `${JSON.stringify(entry)}\n`);
|
|
55
55
|
const next = applyEvent(prior.snapshot, entry, prior.diagnostics);
|
|
56
56
|
snapshotCache.set(normalized.id, { snapshot: next, diagnostics: next.diagnostics || [], nextSeq: seq + 1 });
|
|
57
57
|
writeSnapshot(snapshotPath, next);
|
|
@@ -92,15 +92,15 @@ function normalizeEvent(event, fallbackId) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function getJournalPath(rootDir, id) {
|
|
95
|
-
return path.join(rootDir, safeSessionId(id)
|
|
95
|
+
return path.join(rootDir, `${safeSessionId(id)}.jsonl`);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
function getSnapshotPath(rootDir, id) {
|
|
99
|
-
return path.join(rootDir, safeSessionId(id)
|
|
99
|
+
return path.join(rootDir, `${safeSessionId(id)}.snapshot.json`);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function safeSessionId(id) {
|
|
103
|
-
if (!/^[A-Za-z0-9_-]{1,128}$/.test(id)) throw new Error(
|
|
103
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(id)) throw new Error(`invalid session id: ${id}`);
|
|
104
104
|
return id;
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -289,5 +289,5 @@ function upsertArtifact(artifacts, artifact) {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
function writeSnapshot(snapshotPath, snapshot) {
|
|
292
|
-
fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2)
|
|
292
|
+
fs.writeFileSync(snapshotPath, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
293
293
|
}
|
|
@@ -73,7 +73,7 @@ export function buildPropContract(expressions) {
|
|
|
73
73
|
|
|
74
74
|
function derivePropName(expr, index) {
|
|
75
75
|
const tail = expr.match(/(?:\.|\[)(\w+)\s*\]?$/);
|
|
76
|
-
if (tail
|
|
76
|
+
if (tail?.[1] && /^[A-Za-z_$][\w$]*$/.test(tail[1])) {
|
|
77
77
|
return tail[1];
|
|
78
78
|
}
|
|
79
79
|
return `prop${index}`;
|
|
@@ -166,7 +166,7 @@ export function scaffoldSvelteComponentSession({
|
|
|
166
166
|
runtimeModule: `/${SVELTE_RUNTIME_FILE}`,
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
-
fs.writeFileSync(path.join(dir, 'manifest.json'), JSON.stringify(manifest, null, 2)
|
|
169
|
+
fs.writeFileSync(path.join(dir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
|
|
170
170
|
|
|
171
171
|
for (let n = 1; n <= count; n++) {
|
|
172
172
|
const variantFile = path.join(dir, `v${n}.svelte`);
|
|
@@ -216,7 +216,7 @@ export function scaffoldSvelteComponentInsertSession({
|
|
|
216
216
|
runtimeModule: `/${SVELTE_RUNTIME_FILE}`,
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
-
fs.writeFileSync(path.join(dir, 'manifest.json'), JSON.stringify(manifest, null, 2)
|
|
219
|
+
fs.writeFileSync(path.join(dir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
|
|
220
220
|
|
|
221
221
|
for (let n = 1; n <= count; n++) {
|
|
222
222
|
const variantFile = path.join(dir, `v${n}.svelte`);
|
|
@@ -270,14 +270,14 @@ export function resolveSourceFile(sourceFile, cwd = process.cwd()) {
|
|
|
270
270
|
throw new Error('Svelte-component source file escapes project root');
|
|
271
271
|
}
|
|
272
272
|
if (!fs.existsSync(full)) {
|
|
273
|
-
throw new Error(
|
|
273
|
+
throw new Error(`Svelte-component source file not found: ${sourceFile}`);
|
|
274
274
|
}
|
|
275
275
|
return full;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
function appendCssToSvelteStyle(lines, cssLines) {
|
|
279
279
|
const closeIdx = findLastStyleCloseLine(lines);
|
|
280
|
-
const prepared = ['', ...cssLines.map((line) => (line.trim() === '' ? '' :
|
|
280
|
+
const prepared = ['', ...cssLines.map((line) => (line.trim() === '' ? '' : ` ${line.trimStart()}`))];
|
|
281
281
|
if (closeIdx === -1) {
|
|
282
282
|
return [...lines, '', '<style>', ...prepared.slice(1), '</style>'];
|
|
283
283
|
}
|
|
@@ -436,7 +436,7 @@ function rewriteAcceptedSvelteSelectorPart(selector, variantNum, paramValues, ro
|
|
|
436
436
|
function rewriteParamSelectors(selector, paramValues) {
|
|
437
437
|
let keep = true;
|
|
438
438
|
const next = selector.replace(/\[data-p-([A-Za-z0-9_-]+)(?:=(["'])(.*?)\2)?\]/g, (_match, key, _quote, expected) => {
|
|
439
|
-
if (!paramValues || !Object.
|
|
439
|
+
if (!paramValues || !Object.hasOwn(paramValues, key)) return '';
|
|
440
440
|
const actual = paramValues[key];
|
|
441
441
|
if (expected != null && String(actual) !== String(expected)) {
|
|
442
442
|
keep = false;
|
|
@@ -537,7 +537,7 @@ export function inlineSvelteComponentAccept(manifest, variantNum, paramValues =
|
|
|
537
537
|
const start = Number(manifest.sourceStartLine) - 1;
|
|
538
538
|
const end = Number(manifest.sourceEndLine) - 1;
|
|
539
539
|
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start || end >= sourceLines.length) {
|
|
540
|
-
return { handled: false, error:
|
|
540
|
+
return { handled: false, error: `Invalid source line range for ${manifest.sourceFile}`, ...resultBase };
|
|
541
541
|
}
|
|
542
542
|
|
|
543
543
|
const indent = sourceLines[start].match(/^(\s*)/)?.[1] || '';
|
|
@@ -561,7 +561,7 @@ export function inlineSvelteComponentAccept(manifest, variantNum, paramValues =
|
|
|
561
561
|
try {
|
|
562
562
|
fs.writeFileSync(sourceFile, newLines.join('\n'), 'utf-8');
|
|
563
563
|
} catch (err) {
|
|
564
|
-
return { handled: false, error:
|
|
564
|
+
return { handled: false, error: `Failed to write Svelte source: ${err.message}`, ...resultBase };
|
|
565
565
|
}
|
|
566
566
|
removeSvelteComponentSession(manifest.id, cwd);
|
|
567
567
|
|
|
@@ -596,7 +596,7 @@ function inlineSvelteComponentInsertAccept({
|
|
|
596
596
|
const sourceLines = sourceContent.split('\n');
|
|
597
597
|
const insertIndex = Number(manifest.insertLine) - 1;
|
|
598
598
|
if (!Number.isInteger(insertIndex) || insertIndex < 0 || insertIndex > sourceLines.length) {
|
|
599
|
-
return { handled: false, error:
|
|
599
|
+
return { handled: false, error: `Invalid insert line for ${manifest.sourceFile}`, ...resultBase };
|
|
600
600
|
}
|
|
601
601
|
|
|
602
602
|
const nearbyLine = sourceLines[insertIndex] ?? sourceLines[insertIndex - 1] ?? '';
|
|
@@ -621,7 +621,7 @@ function inlineSvelteComponentInsertAccept({
|
|
|
621
621
|
try {
|
|
622
622
|
fs.writeFileSync(sourceFile, newLines.join('\n'), 'utf-8');
|
|
623
623
|
} catch (err) {
|
|
624
|
-
return { handled: false, error:
|
|
624
|
+
return { handled: false, error: `Failed to write Svelte source: ${err.message}`, ...resultBase };
|
|
625
625
|
}
|
|
626
626
|
removeSvelteComponentSession(manifest.id, cwd);
|
|
627
627
|
|
|
@@ -675,7 +675,7 @@ function mergeOriginalTopLevelAttrs(markup, originalMarkup) {
|
|
|
675
675
|
const nextOpen = variantOpen.prefix
|
|
676
676
|
+ variantOpen.tag
|
|
677
677
|
+ attrs
|
|
678
|
-
+ additions.map((attr) =>
|
|
678
|
+
+ additions.map((attr) => ` ${attr.trim()}`).join('')
|
|
679
679
|
+ variantOpen.close;
|
|
680
680
|
return markup.slice(0, variantOpen.index) + nextOpen + markup.slice(variantOpen.index + variantOpen.raw.length);
|
|
681
681
|
}
|
|
@@ -761,7 +761,7 @@ export function writeDeferredAccept(entry, cwd = process.cwd()) {
|
|
|
761
761
|
const data = readDeferredAccepts(cwd);
|
|
762
762
|
data.accepts = (data.accepts || []).filter((item) => item.id !== entry.id);
|
|
763
763
|
data.accepts.push({ ...entry, createdAt: new Date().toISOString() });
|
|
764
|
-
fs.writeFileSync(file, JSON.stringify(data, null, 2)
|
|
764
|
+
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf-8');
|
|
765
765
|
}
|
|
766
766
|
|
|
767
767
|
export function applyDeferredSvelteComponentAccepts(cwd = process.cwd()) {
|
|
@@ -792,7 +792,7 @@ export function applyDeferredSvelteComponentAccepts(cwd = process.cwd()) {
|
|
|
792
792
|
}
|
|
793
793
|
}
|
|
794
794
|
if (remaining.length > 0) {
|
|
795
|
-
fs.writeFileSync(file, JSON.stringify({ accepts: remaining }, null, 2)
|
|
795
|
+
fs.writeFileSync(file, `${JSON.stringify({ accepts: remaining }, null, 2)}\n`, 'utf-8');
|
|
796
796
|
} else {
|
|
797
797
|
try { fs.rmSync(file, { force: true }); } catch {}
|
|
798
798
|
}
|
|
@@ -103,9 +103,9 @@ export function patchSvelteLayout(content) {
|
|
|
103
103
|
const scriptMatch = out.match(/<script(?:\s[^>]*)?>/i);
|
|
104
104
|
if (scriptMatch) {
|
|
105
105
|
const insertAt = scriptMatch.index + scriptMatch[0].length;
|
|
106
|
-
out = out.slice(0, insertAt)
|
|
106
|
+
out = `${out.slice(0, insertAt)}\n ${SVELTE_ROOT_IMPORT}${out.slice(insertAt)}`;
|
|
107
107
|
} else {
|
|
108
|
-
out = `<script>\n ${SVELTE_ROOT_IMPORT}\n</script>\n\n
|
|
108
|
+
out = `<script>\n ${SVELTE_ROOT_IMPORT}\n</script>\n\n${out}`;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -117,7 +117,7 @@ export function patchSvelteLayout(content) {
|
|
|
117
117
|
if (match) {
|
|
118
118
|
out = out.slice(0, match.index) + block + out.slice(match.index);
|
|
119
119
|
} else {
|
|
120
|
-
out = out.replace(/\s*$/,
|
|
120
|
+
out = out.replace(/\s*$/, `\n\n${block}`);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -134,7 +134,7 @@ export function unpatchSvelteLayout(content) {
|
|
|
134
134
|
'g',
|
|
135
135
|
);
|
|
136
136
|
out = out.replace(blockRe, '$1');
|
|
137
|
-
out = out.replace(new RegExp(
|
|
137
|
+
out = out.replace(new RegExp(`^\\s*${escapeRegExp(SVELTE_ROOT_IMPORT)}\\s*\\n?`, 'gm'), '');
|
|
138
138
|
out = out.replace(/<script>\s*<\/script>\s*\n?/g, '');
|
|
139
139
|
return out.replace(/\n{3,}/g, '\n\n');
|
|
140
140
|
}
|
|
@@ -140,7 +140,7 @@ export function getLiveUiElementById(id, env = globalThis) {
|
|
|
140
140
|
if (found) return found;
|
|
141
141
|
}
|
|
142
142
|
if (root?.querySelector) {
|
|
143
|
-
const found = root.querySelector(
|
|
143
|
+
const found = root.querySelector(`#${escapeCssIdent(id)}`);
|
|
144
144
|
if (found) return found;
|
|
145
145
|
}
|
|
146
146
|
return doc?.getElementById?.(id) || null;
|