@autono/open-pages 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/{build-6hM9Ga2d.js → build-CCoxDxL-.js} +11 -10
  2. package/dist/cli/bin.d.ts +1 -1
  3. package/dist/cli/bin.js +427 -30
  4. package/dist/{config-CBbwLHOo.js → config-D_U_aCQz.js} +484 -460
  5. package/dist/{config-C-sasGou.d.ts → config-p-TWNXbv.d.ts} +4 -4
  6. package/dist/{design-C13iz9_4.js → design-Cpqx7SJs.js} +1 -2
  7. package/dist/{dev-BO0cHiYy.js → dev-CTrB263u.js} +40 -20
  8. package/dist/{export-DbVwSmKW.js → export-enKL9R7x.js} +7 -10
  9. package/dist/index.d.ts +6 -8
  10. package/dist/index.js +2 -3
  11. package/dist/locale/index.d.ts +8 -14
  12. package/dist/locale/index.js +67 -32
  13. package/dist/{open-pages-plugin-C0poXLsx.js → open-pages-plugin-C8cFhMB9.js} +23 -38
  14. package/dist/preview-Dmzyd1cf.js +24 -0
  15. package/dist/{sync-ui-CDydLpY4.js → sync-ui-BzRiAGCK.js} +3 -5
  16. package/dist/{types-BzxkFHhd.d.ts → types-DDdUFUIS.d.ts} +19 -6
  17. package/dist/{update-D1U8TVwd.js → update-CWaCKlnl.js} +4 -7
  18. package/dist/{update-package-D9zws9nc.js → update-package-GaNsVdr8.js} +44 -22
  19. package/dist/{version-B6AiiGvT.js → version-DZrXLoAD.js} +19 -3
  20. package/dist/vite/index.d.ts +3 -7
  21. package/dist/vite/index.js +2 -7
  22. package/package.json +22 -21
  23. package/skills/apply-comments/SKILL.md +1 -0
  24. package/skills/create-page/SKILL.md +5 -1
  25. package/skills/create-theme/SKILL.md +2 -0
  26. package/skills/page-authoring/SKILL.md +3 -0
  27. package/skills/web-design-guidelines/SKILL.md +41 -0
  28. package/src/app/app.tsx +19 -14
  29. package/src/app/assets/autono-social.png +0 -0
  30. package/src/app/assets/autono.svg +7 -0
  31. package/src/app/components/asset-view.tsx +510 -26
  32. package/src/app/components/command/command-menu.tsx +5 -1
  33. package/src/app/components/command/command.tsx +4 -2
  34. package/src/app/components/command/home-command-menu.tsx +6 -6
  35. package/src/app/components/icon-tooltip.tsx +24 -15
  36. package/src/app/components/sidebar/folder-item.tsx +46 -19
  37. package/src/app/components/sidebar/icon-picker.tsx +1 -1
  38. package/src/app/components/sidebar/sidebar-footer.tsx +18 -7
  39. package/src/app/components/sidebar/sidebar.tsx +91 -64
  40. package/src/app/components/theme-toggle.tsx +4 -5
  41. package/src/app/components/themes/theme-detail.tsx +6 -3
  42. package/src/app/components/themes/themes-gallery.tsx +12 -7
  43. package/src/app/index.html +33 -2
  44. package/src/app/lib/assets.ts +33 -0
  45. package/src/app/lib/last-home-location.ts +18 -0
  46. package/src/app/main.tsx +10 -1
  47. package/src/app/routes/home-shell.tsx +82 -61
  48. package/src/app/routes/home.tsx +42 -39
  49. package/src/app/routes/page.tsx +30 -10
  50. package/src/app/routes/themes.tsx +6 -8
  51. package/src/app/styles.css +40 -4
  52. package/src/app/virtual.d.ts +1 -0
  53. package/src/locale/en.ts +17 -6
  54. package/src/locale/ja.ts +18 -6
  55. package/src/locale/types.ts +17 -4
  56. package/src/locale/zh-cn.ts +19 -8
  57. package/src/locale/zh-tw.ts +17 -6
  58. package/src/shared/update-package.ts +193 -0
  59. package/workspace/deps.json +1 -1
  60. package/dist/export-BsK6OCpc.js +0 -4
  61. package/dist/preview-sOKhzyWB.js +0 -21
  62. package/dist/sync-Be_hxSOF.js +0 -139
  63. package/dist/sync-lKEXyPOh.js +0 -3
  64. package/dist/version-CeffgdfZ.js +0 -3
  65. package/src/app/components/language-toggle.tsx +0 -46
  66. package/src/app/favicon.ico +0 -0
@@ -1,24 +1,20 @@
1
- import { defaultDesign } from "./design-C13iz9_4.js";
2
- import { PAGE_ID_RE, duplicatePageDir, invalidatePagesModule, loadUserConfig, openPagesPlugin, resolvePageEntry, rmPageDir, updateMetaTitleInSource, validatePageName } from "./open-pages-plugin-C0poXLsx.js";
3
- import { detectPackageManager, fetchLatest, formatCommand, invalidateLatestCache, isOutdated, localOpenPagesCommand, runCommand, updateCommandFor } from "./update-package-D9zws9nc.js";
4
- import { APP_ROOT, PKG_ROOT, readCoreVersion } from "./version-B6AiiGvT.js";
5
- import fs from "node:fs/promises";
1
+ import { n as PKG_ROOT, r as readCoreVersion, t as APP_ROOT } from "./version-DZrXLoAD.js";
2
+ import { n as defaultDesign } from "./design-Cpqx7SJs.js";
3
+ import { c as localOpenPagesCommand, i as formatCommand, l as runCommand, n as detectPackageManager, o as invalidateLatestCache, r as fetchLatest, s as isOutdated, u as updateCommandFor } from "./update-package-GaNsVdr8.js";
4
+ import { a as openPagesPlugin, c as resolvePageEntry, d as validatePageName, i as loadUserConfig, l as rmPageDir, o as PAGE_ID_RE, r as invalidatePagesModule, s as duplicatePageDir, u as updateMetaTitleInSource } from "./open-pages-plugin-C8cFhMB9.js";
6
5
  import path from "node:path";
7
- import { randomUUID } from "node:crypto";
8
6
  import { existsSync } from "node:fs";
7
+ import fs from "node:fs/promises";
8
+ import { randomUUID } from "node:crypto";
9
9
  import tailwindcss from "@tailwindcss/vite";
10
10
  import react from "@vitejs/plugin-react";
11
11
  import { normalizePath, searchForWorkspaceRoot } from "vite";
12
12
  import { parse } from "@babel/parser";
13
- import * as t$3 from "@babel/types";
14
- import * as t$2 from "@babel/types";
15
- import * as t$1 from "@babel/types";
16
13
  import * as t from "@babel/types";
17
14
  import { isJSXElement, isJSXFragment } from "@babel/types";
18
15
  import fg from "fast-glob";
19
-
20
16
  //#region src/editing/babel-walk.ts
21
- const SKIP_KEYS = new Set([
17
+ const SKIP_KEYS = /* @__PURE__ */ new Set([
22
18
  "loc",
23
19
  "start",
24
20
  "end",
@@ -57,19 +53,41 @@ function walkJsx(ast, visit) {
57
53
  function walkAll(ast, visit) {
58
54
  walk(ast, visit, acceptAll);
59
55
  }
60
- function parseSource$1(source) {
56
+ /** Parse with error recovery; recoverable syntax errors are kept in `errors`. */
57
+ function tryParse(source) {
61
58
  try {
62
- const ast = parse(source, {
59
+ return parse(source, {
63
60
  sourceType: "module",
64
61
  plugins: ["typescript", "jsx"],
65
62
  errorRecovery: true
66
63
  });
67
- return ast.errors && ast.errors.length > 0 ? null : ast;
68
64
  } catch {
69
65
  return null;
70
66
  }
71
67
  }
72
-
68
+ /** `tryParse`, but a source with any recovered error is rejected outright. */
69
+ function parseSource$1(source) {
70
+ const ast = tryParse(source);
71
+ if (!ast) return null;
72
+ return ast.errors && ast.errors.length > 0 ? null : ast;
73
+ }
74
+ /** JSX nodes enclosing (line, column), innermost first. */
75
+ function findJsxAncestors$1(ast, line, column) {
76
+ const hits = [];
77
+ walkJsx(ast, (n) => {
78
+ if (!n.loc || !isJSXElement(n) && !isJSXFragment(n)) return;
79
+ const s = n.loc.start;
80
+ const e = n.loc.end;
81
+ const afterStart = line > s.line || line === s.line && column >= s.column;
82
+ const beforeEnd = line < e.line || line === e.line && column < e.column;
83
+ if (afterStart && beforeEnd) hits.push({
84
+ node: n,
85
+ size: (n.end ?? 0) - (n.start ?? 0)
86
+ });
87
+ });
88
+ hits.sort((a, b) => a.size - b.size);
89
+ return hits.map((h) => h.node);
90
+ }
73
91
  //#endregion
74
92
  //#region src/editing/edit-ops.ts
75
93
  function jsString$1(s) {
@@ -87,34 +105,32 @@ function formatJsxAttrValue(value) {
87
105
  return `{${jsString$1(value)}}`;
88
106
  }
89
107
  function jsxAttrName(attr) {
90
- return t$3.isJSXIdentifier(attr.name) ? attr.name.name : null;
108
+ return t.isJSXIdentifier(attr.name) ? attr.name.name : null;
91
109
  }
92
110
  function findJsxAttr(opening, name) {
93
- for (const attr of opening.attributes) if (t$3.isJSXAttribute(attr) && jsxAttrName(attr) === name) return attr;
111
+ for (const attr of opening.attributes) if (t.isJSXAttribute(attr) && jsxAttrName(attr) === name) return attr;
94
112
  return null;
95
113
  }
96
114
  function readJsxStringAttr(opening, name) {
97
- const attr = findJsxAttr(opening, name);
98
- const v = attr?.value;
115
+ const v = findJsxAttr(opening, name)?.value;
99
116
  if (!v) return null;
100
- if (t$3.isStringLiteral(v)) return v.value;
101
- if (t$3.isJSXExpressionContainer(v) && t$3.isStringLiteral(v.expression)) return v.expression.value;
117
+ if (t.isStringLiteral(v)) return v.value;
118
+ if (t.isJSXExpressionContainer(v) && t.isStringLiteral(v.expression)) return v.expression.value;
102
119
  return null;
103
120
  }
104
121
  function readJsxNumberAttr(opening, name) {
105
- const attr = findJsxAttr(opening, name);
106
- const v = attr?.value;
107
- if (!v || !t$3.isJSXExpressionContainer(v)) return null;
108
- if (!t$3.isNumericLiteral(v.expression)) return null;
122
+ const v = findJsxAttr(opening, name)?.value;
123
+ if (!v || !t.isJSXExpressionContainer(v)) return null;
124
+ if (!t.isNumericLiteral(v.expression)) return null;
109
125
  const n = v.expression.value;
110
126
  return Number.isFinite(n) ? n : null;
111
127
  }
112
128
  function findImports$1(ast) {
113
129
  const out = [];
114
130
  for (const node of ast.program.body) {
115
- if (!t$3.isImportDeclaration(node)) continue;
131
+ if (!t.isImportDeclaration(node)) continue;
116
132
  let def = null;
117
- for (const spec of node.specifiers) if (t$3.isImportDefaultSpecifier(spec)) {
133
+ for (const spec of node.specifiers) if (t.isImportDefaultSpecifier(spec)) {
118
134
  def = spec.local.name;
119
135
  break;
120
136
  }
@@ -127,10 +143,10 @@ function findImports$1(ast) {
127
143
  return out;
128
144
  }
129
145
  function collectTopLevelIdentifiers(ast) {
130
- const names = new Set();
146
+ const names = /* @__PURE__ */ new Set();
131
147
  for (const imp of findImports$1(ast)) {
132
148
  if (imp.defaultIdent) names.add(imp.defaultIdent);
133
- for (const spec of imp.node.specifiers) if (!t$3.isImportDefaultSpecifier(spec)) names.add(spec.local.name);
149
+ for (const spec of imp.node.specifiers) if (!t.isImportDefaultSpecifier(spec)) names.add(spec.local.name);
134
150
  }
135
151
  return names;
136
152
  }
@@ -153,10 +169,10 @@ function safeAssetIdentifier(filename, taken) {
153
169
  }
154
170
  return candidate;
155
171
  }
156
- function findJsxAncestors$1(ast, line, column) {
172
+ function findJsxAncestors(ast, line, column) {
157
173
  const hits = [];
158
174
  walkJsx(ast, (n) => {
159
- if (!n.loc || !t$3.isJSXElement(n) && !t$3.isJSXFragment(n)) return;
175
+ if (!n.loc || !t.isJSXElement(n) && !t.isJSXFragment(n)) return;
160
176
  const s = n.loc.start;
161
177
  const e = n.loc.end;
162
178
  const afterStart = line > s.line || line === s.line && column >= s.column;
@@ -172,7 +188,7 @@ function findJsxAncestors$1(ast, line, column) {
172
188
  function findJsxByStart(ast, line, column) {
173
189
  let hit = null;
174
190
  walkJsx(ast, (n) => {
175
- if (!t$3.isJSXElement(n) || !n.loc) return;
191
+ if (!t.isJSXElement(n) || !n.loc) return;
176
192
  const s = n.loc.start;
177
193
  if (s.line === line && s.column === column) {
178
194
  hit = n;
@@ -184,13 +200,13 @@ function findJsxByStart(ast, line, column) {
184
200
  function findInnermostJsxElement(ast, line, column) {
185
201
  const exact = findJsxByStart(ast, line, column);
186
202
  if (exact) return exact;
187
- for (const n of findJsxAncestors$1(ast, line, column)) if (t$3.isJSXElement(n)) return n;
203
+ for (const n of findJsxAncestors(ast, line, column)) if (t.isJSXElement(n)) return n;
188
204
  return null;
189
205
  }
190
206
  function findUniqueElementByText(ast, prevText) {
191
207
  const hits = [];
192
208
  walkJsx(ast, (n) => {
193
- if (!t$3.isJSXElement(n)) return;
209
+ if (!t.isJSXElement(n)) return;
194
210
  const parts = [];
195
211
  collectTextRangeParts(n, parts);
196
212
  if (textRangeContent(parts) !== prevText) return;
@@ -204,8 +220,7 @@ function findUniqueElementByText(ast, prevText) {
204
220
  const best = hits[0];
205
221
  const bestStart = best.node.start ?? 0;
206
222
  const bestEnd = best.node.end ?? 0;
207
- const hasSiblingMatch = hits.slice(1).some(({ node }) => (node.start ?? 0) > bestStart || (node.end ?? 0) < bestEnd);
208
- return hasSiblingMatch ? null : best.node;
223
+ return hits.slice(1).some(({ node }) => (node.start ?? 0) > bestStart || (node.end ?? 0) < bestEnd) ? null : best.node;
209
224
  }
210
225
  function fallbackTextForOps(ops) {
211
226
  for (const op of ops) if ((op.kind === "set-style" || op.kind === "set-text" || op.kind === "set-text-range-style") && op.prevText !== void 0) return op.prevText;
@@ -239,19 +254,19 @@ function buildStyleSplice(source, element, ops) {
239
254
  let hasRawEntry = false;
240
255
  if (existing) {
241
256
  const value = existing.value;
242
- if (!value || !t$3.isJSXExpressionContainer(value)) return { error: "style attribute has unsupported form" };
257
+ if (!value || !t.isJSXExpressionContainer(value)) return { error: "style attribute has unsupported form" };
243
258
  const expr = value.expression;
244
- if (!t$3.isObjectExpression(expr)) {
259
+ if (!t.isObjectExpression(expr)) {
245
260
  if (typeof expr.start !== "number" || typeof expr.end !== "number") return { error: "style value missing source range" };
246
261
  entries.push({
247
262
  kind: "raw",
248
263
  text: `...(${source.slice(expr.start, expr.end)})`
249
264
  });
250
265
  hasRawEntry = true;
251
- } else for (const prop of expr.properties) if (t$3.isObjectProperty(prop) && !prop.computed) {
266
+ } else for (const prop of expr.properties) if (t.isObjectProperty(prop) && !prop.computed) {
252
267
  let keyName = null;
253
- if (t$3.isIdentifier(prop.key)) keyName = prop.key.name;
254
- else if (t$3.isStringLiteral(prop.key)) keyName = prop.key.value;
268
+ if (t.isIdentifier(prop.key)) keyName = prop.key.name;
269
+ else if (t.isStringLiteral(prop.key)) keyName = prop.key.value;
255
270
  if (!keyName) return { error: "style has unsupported key" };
256
271
  const v = prop.value;
257
272
  if (typeof prop.key.start !== "number" || typeof prop.key.end !== "number" || typeof v.start !== "number" || typeof v.end !== "number") return { error: "style value missing source range" };
@@ -298,15 +313,14 @@ function buildStyleSplice(source, element, ops) {
298
313
  text: ""
299
314
  };
300
315
  }
301
- const propsText = entries.map((entry) => entry.kind === "prop" ? `${entry.keyText}: ${entry.valueText}` : entry.text).join(", ");
302
- const newAttr = `style={{ ${propsText} }}`;
316
+ const newAttr = `style={{ ${entries.map((entry) => entry.kind === "prop" ? `${entry.keyText}: ${entry.valueText}` : entry.text).join(", ")} }}`;
303
317
  if (existing) {
304
- const lastAttr$1 = opening.attributes[opening.attributes.length - 1];
305
- if (lastAttr$1 && lastAttr$1 !== existing && typeof lastAttr$1.end === "number") {
306
- const attrsAfterStyle = source.slice(existing.end ?? 0, lastAttr$1.end).replace(/^[ \t]+/, "");
318
+ const lastAttr = opening.attributes[opening.attributes.length - 1];
319
+ if (lastAttr && lastAttr !== existing && typeof lastAttr.end === "number") {
320
+ const attrsAfterStyle = source.slice(existing.end ?? 0, lastAttr.end).replace(/^[ \t]+/, "");
307
321
  return {
308
322
  from: existing.start ?? 0,
309
- to: lastAttr$1.end,
323
+ to: lastAttr.end,
310
324
  text: `${attrsAfterStyle} ${newAttr}`
311
325
  };
312
326
  }
@@ -316,8 +330,7 @@ function buildStyleSplice(source, element, ops) {
316
330
  text: newAttr
317
331
  };
318
332
  }
319
- const lastAttr = opening.attributes[opening.attributes.length - 1];
320
- const at = lastAttr?.end ?? opening.name.end ?? 0;
333
+ const at = opening.attributes[opening.attributes.length - 1]?.end ?? opening.name.end ?? 0;
321
334
  return {
322
335
  from: at,
323
336
  to: at,
@@ -330,7 +343,7 @@ function formatJsxText(value) {
330
343
  }
331
344
  function meaningfulChildren(parent) {
332
345
  return parent.children.filter((c) => {
333
- if (t$3.isJSXText(c)) return c.value.trim() !== "";
346
+ if (t.isJSXText(c)) return c.value.trim() !== "";
334
347
  return true;
335
348
  });
336
349
  }
@@ -397,14 +410,14 @@ function cleanJsxTextWithOffsets(value) {
397
410
  };
398
411
  }
399
412
  function isJsxBrElement(node) {
400
- if (!t$3.isJSXElement(node)) return false;
413
+ if (!t.isJSXElement(node)) return false;
401
414
  const name = node.openingElement.name;
402
- return t$3.isJSXIdentifier(name) && name.name.toLowerCase() === "br";
415
+ return t.isJSXIdentifier(name) && name.name.toLowerCase() === "br";
403
416
  }
404
417
  function collectTextCandidates(element, out) {
405
418
  const meaningful = meaningfulChildren(element);
406
419
  const isSole = meaningful.length === 1;
407
- for (const child of meaningful) if (t$3.isJSXText(child)) {
420
+ for (const child of meaningful) if (t.isJSXText(child)) {
408
421
  const current = child.value.trim();
409
422
  if (!current) continue;
410
423
  out.push({
@@ -415,9 +428,9 @@ function collectTextCandidates(element, out) {
415
428
  text: formatJsxText(v)
416
429
  }
417
430
  });
418
- } else if (t$3.isJSXExpressionContainer(child)) {
431
+ } else if (t.isJSXExpressionContainer(child)) {
419
432
  const expr = child.expression;
420
- if (t$3.isStringLiteral(expr) || t$3.isNumericLiteral(expr)) {
433
+ if (t.isStringLiteral(expr) || t.isNumericLiteral(expr)) {
421
434
  const current = String(expr.value);
422
435
  out.push({
423
436
  current,
@@ -428,7 +441,7 @@ function collectTextCandidates(element, out) {
428
441
  }
429
442
  });
430
443
  }
431
- } else if (t$3.isJSXElement(child) || t$3.isJSXFragment(child)) collectTextCandidates(child, out);
444
+ } else if (t.isJSXElement(child) || t.isJSXFragment(child)) collectTextCandidates(child, out);
432
445
  }
433
446
  function collectTextRangeParts(element, out) {
434
447
  const parts = [];
@@ -436,7 +449,7 @@ function collectTextRangeParts(element, out) {
436
449
  out.push(...normalizeTextRangeParts(parts));
437
450
  }
438
451
  function collectTextRangePartsRaw(element, out) {
439
- for (const child of element.children) if (t$3.isJSXText(child)) {
452
+ for (const child of element.children) if (t.isJSXText(child)) {
440
453
  const { text: current, offsets } = cleanJsxTextWithOffsets(child.value);
441
454
  if (current) out.push({
442
455
  node: child,
@@ -446,9 +459,9 @@ function collectTextRangePartsRaw(element, out) {
446
459
  text: formatJsxText,
447
460
  offsets
448
461
  });
449
- } else if (t$3.isJSXExpressionContainer(child)) {
462
+ } else if (t.isJSXExpressionContainer(child)) {
450
463
  const expression = child.expression;
451
- if (t$3.isStringLiteral(expression) || t$3.isNumericLiteral(expression)) {
464
+ if (t.isStringLiteral(expression) || t.isNumericLiteral(expression)) {
452
465
  const raw = String(expression.value);
453
466
  const current = raw;
454
467
  if (current) out.push({
@@ -464,7 +477,7 @@ function collectTextRangePartsRaw(element, out) {
464
477
  node: child,
465
478
  current: "\n"
466
479
  });
467
- else if (t$3.isJSXElement(child) || t$3.isJSXFragment(child)) collectTextRangePartsRaw(child, out);
480
+ else if (t.isJSXElement(child) || t.isJSXFragment(child)) collectTextRangePartsRaw(child, out);
468
481
  }
469
482
  function normalizeTextRangeParts(parts) {
470
483
  return parts.flatMap((part, index) => {
@@ -627,7 +640,7 @@ function buildTextRangeStyleSplices(ast, source, element, start, end, op, prevTe
627
640
  leafStart = leafEnd;
628
641
  continue;
629
642
  }
630
- if (selectedStart === leafStart && selectedEnd === leafEnd && t$3.isJSXElement(leaf.parent) && leaf.parent !== element && isOnlyMeaningfulChild(leaf.parent, leaf.node)) {
643
+ if (selectedStart === leafStart && selectedEnd === leafEnd && t.isJSXElement(leaf.parent) && leaf.parent !== element && isOnlyMeaningfulChild(leaf.parent, leaf.node)) {
631
644
  const result = buildStyleSplice(source, leaf.parent, [op]);
632
645
  if (result && "error" in result) return result;
633
646
  if (result) splices.push(result);
@@ -643,8 +656,8 @@ function buildTextRangeStyleSplices(ast, source, element, start, end, op, prevTe
643
656
  const before = raw.slice(0, rawStart);
644
657
  const selected = leaf.current.slice(localStart, localEnd);
645
658
  const after = raw.slice(rawEnd);
646
- const beforeText = t$3.isJSXText(leaf.node) ? before : formatOptionalText(before, leaf.text);
647
- const afterText = t$3.isJSXText(leaf.node) ? after : formatOptionalText(after, leaf.text);
659
+ const beforeText = t.isJSXText(leaf.node) ? before : formatOptionalText(before, leaf.text);
660
+ const afterText = t.isJSXText(leaf.node) ? after : formatOptionalText(after, leaf.text);
648
661
  splices.push(spliceRange(leaf.node, `${beforeText}${styleSpanForText(selected, op.key, op.value)}${afterText}`));
649
662
  leafStart = leafEnd;
650
663
  }
@@ -654,8 +667,8 @@ function propPassthroughName(element) {
654
667
  const meaningful = meaningfulChildren(element);
655
668
  if (meaningful.length !== 1) return null;
656
669
  const child = meaningful[0];
657
- if (!t$3.isJSXExpressionContainer(child)) return null;
658
- return t$3.isIdentifier(child.expression) ? child.expression.name : null;
670
+ if (!t.isJSXExpressionContainer(child)) return null;
671
+ return t.isIdentifier(child.expression) ? child.expression.name : null;
659
672
  }
660
673
  function findEnclosingComponent(ast, target) {
661
674
  let best = null;
@@ -677,17 +690,17 @@ function findEnclosingComponent(ast, target) {
677
690
  }
678
691
  };
679
692
  const visitDecl = (decl) => {
680
- if (t$3.isFunctionDeclaration(decl) && decl.id) consider(decl.id.name, decl);
681
- else if (t$3.isVariableDeclaration(decl)) for (const d of decl.declarations) {
682
- if (!t$3.isVariableDeclarator(d) || !t$3.isIdentifier(d.id) || !d.init) continue;
683
- if (t$3.isArrowFunctionExpression(d.init) || t$3.isFunctionExpression(d.init)) consider(d.id.name, d.init);
693
+ if (t.isFunctionDeclaration(decl) && decl.id) consider(decl.id.name, decl);
694
+ else if (t.isVariableDeclaration(decl)) for (const d of decl.declarations) {
695
+ if (!t.isVariableDeclarator(d) || !t.isIdentifier(d.id) || !d.init) continue;
696
+ if (t.isArrowFunctionExpression(d.init) || t.isFunctionExpression(d.init)) consider(d.id.name, d.init);
684
697
  }
685
698
  };
686
699
  for (const decl of ast.program.body) {
687
700
  visitDecl(decl);
688
- if (t$3.isExportNamedDeclaration(decl) || t$3.isExportDefaultDeclaration(decl)) {
701
+ if (t.isExportNamedDeclaration(decl) || t.isExportDefaultDeclaration(decl)) {
689
702
  const inner = decl.declaration;
690
- if (inner && (t$3.isStatement(inner) || t$3.isFunctionDeclaration(inner))) visitDecl(inner);
703
+ if (inner && (t.isStatement(inner) || t.isFunctionDeclaration(inner))) visitDecl(inner);
691
704
  }
692
705
  }
693
706
  return best;
@@ -695,40 +708,40 @@ function findEnclosingComponent(ast, target) {
695
708
  function componentDestructuresProp(fn, propName) {
696
709
  if (fn.params.length === 0) return false;
697
710
  let first = fn.params[0];
698
- if (t$3.isAssignmentPattern(first)) first = first.left;
699
- if (!t$3.isObjectPattern(first)) return false;
711
+ if (t.isAssignmentPattern(first)) first = first.left;
712
+ if (!t.isObjectPattern(first)) return false;
700
713
  for (const prop of first.properties) {
701
- if (!t$3.isObjectProperty(prop)) continue;
702
- if (t$3.isIdentifier(prop.key) && prop.key.name === propName) return true;
703
- if (t$3.isStringLiteral(prop.key) && prop.key.value === propName) return true;
714
+ if (!t.isObjectProperty(prop)) continue;
715
+ if (t.isIdentifier(prop.key) && prop.key.name === propName) return true;
716
+ if (t.isStringLiteral(prop.key) && prop.key.value === propName) return true;
704
717
  }
705
718
  return false;
706
719
  }
707
720
  function collectCallSiteCandidates(ast, componentName) {
708
721
  const out = [];
709
722
  walkJsx(ast, (n) => {
710
- if (!t$3.isJSXElement(n)) return;
723
+ if (!t.isJSXElement(n)) return;
711
724
  const elName = n.openingElement.name;
712
- if (t$3.isJSXIdentifier(elName) && elName.name === componentName) collectTextCandidates(n, out);
725
+ if (t.isJSXIdentifier(elName) && elName.name === componentName) collectTextCandidates(n, out);
713
726
  });
714
727
  return out;
715
728
  }
716
729
  function collectPropCallSiteCandidates(ast, componentName, propName) {
717
730
  const out = [];
718
731
  walkJsx(ast, (n) => {
719
- if (!t$3.isJSXElement(n)) return;
732
+ if (!t.isJSXElement(n)) return;
720
733
  const elName = n.openingElement.name;
721
- if (!t$3.isJSXIdentifier(elName) || elName.name !== componentName) return;
734
+ if (!t.isJSXIdentifier(elName) || elName.name !== componentName) return;
722
735
  const attr = findJsxAttr(n.openingElement, propName);
723
736
  if (!attr?.value) return;
724
737
  const v = attr.value;
725
- if (t$3.isStringLiteral(v)) out.push({
738
+ if (t.isStringLiteral(v)) out.push({
726
739
  current: v.value,
727
740
  splice: (s) => spliceRange(v, formatJsxAttrValue(s))
728
741
  });
729
- else if (t$3.isJSXExpressionContainer(v)) {
742
+ else if (t.isJSXExpressionContainer(v)) {
730
743
  const expr = v.expression;
731
- if (t$3.isStringLiteral(expr) || t$3.isNumericLiteral(expr)) out.push({
744
+ if (t.isStringLiteral(expr) || t.isNumericLiteral(expr)) out.push({
732
745
  current: String(expr.value),
733
746
  splice: (s) => spliceRange(v, formatJsxAttrValue(s))
734
747
  });
@@ -741,17 +754,17 @@ function findEnclosingMapCallback(ast, target) {
741
754
  const targetStart = target.start ?? 0;
742
755
  const targetEnd = target.end ?? 0;
743
756
  walkAll(ast, (node) => {
744
- if (!t$3.isCallExpression(node)) return;
757
+ if (!t.isCallExpression(node)) return;
745
758
  const callee = node.callee;
746
- if (!t$3.isMemberExpression(callee) || callee.computed) return;
747
- if (!t$3.isIdentifier(callee.property)) return;
759
+ if (!t.isMemberExpression(callee) || callee.computed) return;
760
+ if (!t.isIdentifier(callee.property)) return;
748
761
  if (callee.property.name !== "map" && callee.property.name !== "flatMap") return;
749
762
  const fn = node.arguments[0];
750
- if (!fn || !t$3.isArrowFunctionExpression(fn) && !t$3.isFunctionExpression(fn)) return;
763
+ if (!fn || !t.isArrowFunctionExpression(fn) && !t.isFunctionExpression(fn)) return;
751
764
  const fnStart = fn.start ?? 0;
752
765
  const fnEnd = fn.end ?? 0;
753
766
  if (fnStart > targetStart || fnEnd < targetEnd) return;
754
- if (!t$3.isExpression(callee.object)) return;
767
+ if (!t.isExpression(callee.object)) return;
755
768
  const size = fnEnd - fnStart;
756
769
  if (!best || size < best.size) best = {
757
770
  fn,
@@ -768,26 +781,26 @@ function findEnclosingMapCallback(ast, target) {
768
781
  }
769
782
  function resolveArrayLiteralElements(ast, expr) {
770
783
  const dropHoles = (arr) => arr.elements.filter((e) => e != null);
771
- if (t$3.isArrayExpression(expr)) return dropHoles(expr);
772
- if (!t$3.isIdentifier(expr)) return null;
784
+ if (t.isArrayExpression(expr)) return dropHoles(expr);
785
+ if (!t.isIdentifier(expr)) return null;
773
786
  const name = expr.name;
774
787
  const useStart = expr.start ?? 0;
775
788
  let init = null;
776
789
  walkAll(ast, (node) => {
777
- if (!t$3.isVariableDeclarator(node)) return;
778
- if (!t$3.isIdentifier(node.id) || node.id.name !== name) return;
779
- if (!node.init || !t$3.isArrayExpression(node.init)) return;
790
+ if (!t.isVariableDeclarator(node)) return;
791
+ if (!t.isIdentifier(node.id) || node.id.name !== name) return;
792
+ if (!node.init || !t.isArrayExpression(node.init)) return;
780
793
  if ((node.init.start ?? 0) > useStart) return;
781
794
  init = node.init;
782
795
  });
783
796
  return init ? dropHoles(init) : null;
784
797
  }
785
798
  function findObjectProperty(obj, name) {
786
- if (!t$3.isObjectExpression(obj)) return null;
799
+ if (!t.isObjectExpression(obj)) return null;
787
800
  for (const prop of obj.properties) {
788
- if (!t$3.isObjectProperty(prop) || prop.computed) continue;
789
- if (t$3.isIdentifier(prop.key) && prop.key.name === name) return prop;
790
- if (t$3.isStringLiteral(prop.key) && prop.key.value === name) return prop;
801
+ if (!t.isObjectProperty(prop) || prop.computed) continue;
802
+ if (t.isIdentifier(prop.key) && prop.key.name === name) return prop;
803
+ if (t.isStringLiteral(prop.key) && prop.key.value === name) return prop;
791
804
  }
792
805
  return null;
793
806
  }
@@ -795,22 +808,22 @@ function decodeMapPassthrough(element, callbackParam) {
795
808
  const meaningful = meaningfulChildren(element);
796
809
  if (meaningful.length !== 1) return null;
797
810
  const child = meaningful[0];
798
- if (!t$3.isJSXExpressionContainer(child)) return null;
811
+ if (!t.isJSXExpressionContainer(child)) return null;
799
812
  const expr = child.expression;
800
- if (t$3.isMemberExpression(expr)) {
813
+ if (t.isMemberExpression(expr)) {
801
814
  if (expr.computed) return null;
802
- if (!t$3.isIdentifier(expr.object) || !t$3.isIdentifier(expr.property)) return null;
803
- if (!callbackParam || !t$3.isIdentifier(callbackParam)) return null;
815
+ if (!t.isIdentifier(expr.object) || !t.isIdentifier(expr.property)) return null;
816
+ if (!callbackParam || !t.isIdentifier(callbackParam)) return null;
804
817
  if (callbackParam.name !== expr.object.name) return null;
805
818
  return expr.property.name;
806
819
  }
807
- if (t$3.isIdentifier(expr)) {
820
+ if (t.isIdentifier(expr)) {
808
821
  const fieldName = expr.name;
809
- if (!callbackParam || !t$3.isObjectPattern(callbackParam)) return null;
822
+ if (!callbackParam || !t.isObjectPattern(callbackParam)) return null;
810
823
  for (const prop of callbackParam.properties) {
811
- if (!t$3.isObjectProperty(prop) || prop.computed) continue;
812
- if (!t$3.isIdentifier(prop.key) || prop.key.name !== fieldName) continue;
813
- return t$3.isIdentifier(prop.value) && prop.value.name === fieldName ? fieldName : null;
824
+ if (!t.isObjectProperty(prop) || prop.computed) continue;
825
+ if (!t.isIdentifier(prop.key) || prop.key.name !== fieldName) continue;
826
+ return t.isIdentifier(prop.value) && prop.value.name === fieldName ? fieldName : null;
814
827
  }
815
828
  }
816
829
  return null;
@@ -827,11 +840,11 @@ function collectArrayMapCandidates(ast, element) {
827
840
  const prop = findObjectProperty(obj, fieldName);
828
841
  if (!prop) continue;
829
842
  const v = prop.value;
830
- if (t$3.isStringLiteral(v)) out.push({
843
+ if (t.isStringLiteral(v)) out.push({
831
844
  current: v.value,
832
845
  splice: (s) => spliceRange(v, jsString$1(s))
833
846
  });
834
- else if (t$3.isNumericLiteral(v)) out.push({
847
+ else if (t.isNumericLiteral(v)) out.push({
835
848
  current: String(v.value),
836
849
  splice: (s) => spliceRange(v, jsString$1(s))
837
850
  });
@@ -871,18 +884,16 @@ function planAssetImport(ast, assetPath) {
871
884
  identifier: imp.defaultIdent,
872
885
  importSplice: null
873
886
  };
874
- const filename = assetPath.slice(assetPath.lastIndexOf("/") + 1);
875
- const identifier = safeAssetIdentifier(filename, collectTopLevelIdentifiers(ast));
887
+ const identifier = safeAssetIdentifier(assetPath.slice(assetPath.lastIndexOf("/") + 1), collectTopLevelIdentifiers(ast));
876
888
  const importStmt = `import ${identifier} from '${assetPath.replace(/'/g, "\\'")}';\n`;
877
889
  const last = imports[imports.length - 1];
878
890
  const insertAt = last ? last.node.end ?? 0 : 0;
879
- const prefix = last ? "\n" : "";
880
891
  return {
881
892
  identifier,
882
893
  importSplice: {
883
894
  from: insertAt,
884
895
  to: insertAt,
885
- text: prefix + importStmt
896
+ text: (last ? "\n" : "") + importStmt
886
897
  }
887
898
  };
888
899
  }
@@ -893,23 +904,22 @@ function planAssetAttr(ast, element, attr, assetPath) {
893
904
  const opening = element.openingElement;
894
905
  const newAttr = `${attr}={${identifier}}`;
895
906
  const existing = findJsxAttr(opening, attr);
896
- const attrSplice = existing ? {
897
- from: existing.start ?? 0,
898
- to: existing.end ?? 0,
899
- text: newAttr
900
- } : {
901
- from: opening.name.end ?? 0,
902
- to: opening.name.end ?? 0,
903
- text: ` ${newAttr}`
904
- };
905
907
  return {
906
908
  importSplice,
907
- attrSplice
909
+ attrSplice: existing ? {
910
+ from: existing.start ?? 0,
911
+ to: existing.end ?? 0,
912
+ text: newAttr
913
+ } : {
914
+ from: opening.name.end ?? 0,
915
+ to: opening.name.end ?? 0,
916
+ text: ` ${newAttr}`
917
+ }
908
918
  };
909
919
  }
910
920
  function planReplacePlaceholder(ast, element, assetPath) {
911
921
  const opening = element.openingElement;
912
- if (!t$3.isJSXIdentifier(opening.name) || opening.name.name !== "ImagePlaceholder") return { error: "not a placeholder" };
922
+ if (!t.isJSXIdentifier(opening.name) || opening.name.name !== "ImagePlaceholder") return { error: "not a placeholder" };
913
923
  if (!assetPath.startsWith("./assets/") && !assetPath.startsWith("@assets/")) return { error: "asset path must start with ./assets/ or @assets/" };
914
924
  const hint = readJsxStringAttr(opening, "hint") ?? "";
915
925
  const width = readJsxNumberAttr(opening, "width");
@@ -922,10 +932,9 @@ function planReplacePlaceholder(ast, element, assetPath) {
922
932
  else if (width != null) styleParts.push(`height: '100%'`);
923
933
  styleParts.push(`objectFit: 'cover'`);
924
934
  styleParts.push(`objectPosition: '50% 50%'`);
925
- const replacement = `<img src={${identifier}} alt=${jsString$1(hint)} style={{ ${styleParts.join(", ")} }} />`;
926
935
  return {
927
936
  importSplice,
928
- elementSplice: spliceRange(element, replacement)
937
+ elementSplice: spliceRange(element, `<img src={${identifier}} alt=${jsString$1(hint)} style={{ ${styleParts.join(", ")} }} />`)
929
938
  };
930
939
  }
931
940
  function applyEdit(source, line, column, ops) {
@@ -1049,20 +1058,19 @@ function applyEdit(source, line, column, ops) {
1049
1058
  source: next
1050
1059
  };
1051
1060
  }
1052
-
1053
1061
  //#endregion
1054
1062
  //#region src/editing/revert-asset.ts
1055
1063
  function collectImgSrcUses(ast, identifier) {
1056
1064
  const uses = [];
1057
1065
  walkJsx(ast, (n) => {
1058
- if (!t$2.isJSXElement(n)) return;
1066
+ if (!t.isJSXElement(n)) return;
1059
1067
  const opening = n.openingElement;
1060
- if (!t$2.isJSXIdentifier(opening.name) || opening.name.name !== "img") return;
1068
+ if (!t.isJSXIdentifier(opening.name) || opening.name.name !== "img") return;
1061
1069
  const src = findJsxAttr(opening, "src");
1062
1070
  if (!src?.value) return;
1063
- if (!t$2.isJSXExpressionContainer(src.value)) return;
1071
+ if (!t.isJSXExpressionContainer(src.value)) return;
1064
1072
  const expr = src.value.expression;
1065
- if (!t$2.isIdentifier(expr) || expr.name !== identifier) return;
1073
+ if (!t.isIdentifier(expr) || expr.name !== identifier) return;
1066
1074
  uses.push({
1067
1075
  element: n,
1068
1076
  identNode: expr
@@ -1073,17 +1081,16 @@ function collectImgSrcUses(ast, identifier) {
1073
1081
  function readStyleNumericDim(opening, key) {
1074
1082
  const style = findJsxAttr(opening, "style");
1075
1083
  if (!style?.value) return null;
1076
- if (!t$2.isJSXExpressionContainer(style.value)) return null;
1084
+ if (!t.isJSXExpressionContainer(style.value)) return null;
1077
1085
  const obj = style.value.expression;
1078
- if (!t$2.isObjectExpression(obj)) return null;
1086
+ if (!t.isObjectExpression(obj)) return null;
1079
1087
  for (const prop of obj.properties) {
1080
- if (!t$2.isObjectProperty(prop)) continue;
1088
+ if (!t.isObjectProperty(prop)) continue;
1081
1089
  if (prop.computed) continue;
1082
1090
  const k = prop.key;
1083
- const keyName = t$2.isIdentifier(k) ? k.name : t$2.isStringLiteral(k) ? k.value : null;
1084
- if (keyName !== key) continue;
1091
+ if ((t.isIdentifier(k) ? k.name : t.isStringLiteral(k) ? k.value : null) !== key) continue;
1085
1092
  const v = prop.value;
1086
- if (t$2.isNumericLiteral(v) && Number.isFinite(v.value)) return v.value;
1093
+ if (t.isNumericLiteral(v) && Number.isFinite(v.value)) return v.value;
1087
1094
  return null;
1088
1095
  }
1089
1096
  return null;
@@ -1102,31 +1109,29 @@ function planEnsureImagePlaceholderImport(ast) {
1102
1109
  if (imp.source !== "@autono/open-pages") continue;
1103
1110
  const declIsTypeOnly = readKind(imp.node);
1104
1111
  for (const spec of imp.node.specifiers) {
1105
- if (!t$2.isImportSpecifier(spec)) continue;
1112
+ if (!t.isImportSpecifier(spec)) continue;
1106
1113
  const imported = spec.imported;
1107
- const name = t$2.isIdentifier(imported) ? imported.name : imported.value;
1108
- if (name !== "ImagePlaceholder") continue;
1109
- const specIsTypeOnly = readKind(spec) || declIsTypeOnly;
1110
- if (!specIsTypeOnly) return null;
1114
+ if ((t.isIdentifier(imported) ? imported.name : imported.value) !== "ImagePlaceholder") continue;
1115
+ if (!(readKind(spec) || declIsTypeOnly)) return null;
1111
1116
  }
1112
1117
  if (!declIsTypeOnly && !valueImport) valueImport = imp;
1113
1118
  }
1114
1119
  if (valueImport) {
1115
1120
  const node = valueImport.node;
1116
1121
  const lastSpec = node.specifiers[node.specifiers.length - 1];
1117
- if (lastSpec && t$2.isImportSpecifier(lastSpec)) {
1118
- const insertAt$1 = lastSpec.end ?? 0;
1122
+ if (lastSpec && t.isImportSpecifier(lastSpec)) {
1123
+ const insertAt = lastSpec.end ?? 0;
1119
1124
  return {
1120
- from: insertAt$1,
1121
- to: insertAt$1,
1125
+ from: insertAt,
1126
+ to: insertAt,
1122
1127
  text: ", ImagePlaceholder"
1123
1128
  };
1124
1129
  }
1125
- if (lastSpec && t$2.isImportDefaultSpecifier(lastSpec)) {
1126
- const insertAt$1 = lastSpec.end ?? 0;
1130
+ if (lastSpec && t.isImportDefaultSpecifier(lastSpec)) {
1131
+ const insertAt = lastSpec.end ?? 0;
1127
1132
  return {
1128
- from: insertAt$1,
1129
- to: insertAt$1,
1133
+ from: insertAt,
1134
+ to: insertAt,
1130
1135
  text: ", { ImagePlaceholder }"
1131
1136
  };
1132
1137
  }
@@ -1146,28 +1151,27 @@ function planEnsureImagePlaceholderImport(ast) {
1146
1151
  function findAssetUsages(source, assetPath) {
1147
1152
  const ast = parseSource$1(source);
1148
1153
  if (!ast) return 0;
1149
- const imports = findImports$1(ast);
1150
- const target = imports.find((imp) => imp.source === assetPath && imp.defaultIdent);
1154
+ const target = findImports$1(ast).find((imp) => imp.source === assetPath && imp.defaultIdent);
1151
1155
  if (!target?.defaultIdent) return 0;
1152
1156
  return collectImgSrcUses(ast, target.defaultIdent).length;
1153
1157
  }
1154
1158
  function findReferencedAssets(source, assetPaths) {
1155
- const referenced = new Set();
1159
+ const referenced = /* @__PURE__ */ new Set();
1156
1160
  if (assetPaths.length === 0) return referenced;
1157
1161
  const ast = parseSource$1(source);
1158
1162
  if (!ast) return referenced;
1159
1163
  const wanted = new Set(assetPaths);
1160
- const identToPath = new Map();
1161
- const importLocals = new Set();
1164
+ const identToPath = /* @__PURE__ */ new Map();
1165
+ const importLocals = /* @__PURE__ */ new Set();
1162
1166
  for (const imp of findImports$1(ast)) {
1163
1167
  if (!imp.defaultIdent) continue;
1164
1168
  if (!wanted.has(imp.source)) continue;
1165
1169
  identToPath.set(imp.defaultIdent, imp.source);
1166
- for (const spec of imp.node.specifiers) if (t$2.isImportDefaultSpecifier(spec) && spec.local.name === imp.defaultIdent) importLocals.add(spec.local);
1170
+ for (const spec of imp.node.specifiers) if (t.isImportDefaultSpecifier(spec) && spec.local.name === imp.defaultIdent) importLocals.add(spec.local);
1167
1171
  }
1168
1172
  if (identToPath.size === 0) return referenced;
1169
1173
  walkAll(ast, (n) => {
1170
- if (!t$2.isIdentifier(n)) return;
1174
+ if (!t.isIdentifier(n)) return;
1171
1175
  const p = identToPath.get(n.name);
1172
1176
  if (!p) return;
1173
1177
  if (importLocals.has(n)) return;
@@ -1182,15 +1186,14 @@ function applyRevertAsset(source, assetPath) {
1182
1186
  status: 422,
1183
1187
  error: "could not parse source"
1184
1188
  };
1185
- const imports = findImports$1(ast);
1186
- const target = imports.find((imp) => imp.source === assetPath && imp.defaultIdent);
1189
+ const target = findImports$1(ast).find((imp) => imp.source === assetPath && imp.defaultIdent);
1187
1190
  if (!target?.defaultIdent) return {
1188
1191
  ok: true,
1189
1192
  source
1190
1193
  };
1191
1194
  const identifier = target.defaultIdent;
1192
1195
  const importLocal = (() => {
1193
- for (const spec of target.node.specifiers) if (t$2.isImportDefaultSpecifier(spec) && spec.local.name === identifier) return spec.local;
1196
+ for (const spec of target.node.specifiers) if (t.isImportDefaultSpecifier(spec) && spec.local.name === identifier) return spec.local;
1194
1197
  return null;
1195
1198
  })();
1196
1199
  const imgUses = collectImgSrcUses(ast, identifier);
@@ -1198,7 +1201,7 @@ function applyRevertAsset(source, assetPath) {
1198
1201
  if (importLocal) allowed.add(importLocal);
1199
1202
  let foreign = false;
1200
1203
  walkAll(ast, (n) => {
1201
- if (!t$2.isIdentifier(n) || n.name !== identifier) return;
1204
+ if (!t.isIdentifier(n) || n.name !== identifier) return;
1202
1205
  if (!allowed.has(n)) foreign = true;
1203
1206
  });
1204
1207
  if (foreign) return {
@@ -1242,11 +1245,9 @@ function applyRevertAsset(source, assetPath) {
1242
1245
  source: next
1243
1246
  };
1244
1247
  }
1245
-
1246
1248
  //#endregion
1247
1249
  //#region src/files/assets.ts
1248
1250
  const GLOBAL_SCOPE = "@global";
1249
- const ASSET_MAX_BYTES = 25 * 1024 * 1024;
1250
1251
  const ASSET_FORBIDDEN_RE = /[\x00-\x1F\x7F/\\:*?"<>|]/;
1251
1252
  const MIME_BY_EXT = {
1252
1253
  png: "image/png",
@@ -1290,42 +1291,27 @@ function validateAssetName(v) {
1290
1291
  }
1291
1292
  function resolveAssetsDir(pagesRoot, pageId) {
1292
1293
  if (!PAGE_ID_RE.test(pageId)) return null;
1293
- const docDir = path.resolve(pagesRoot, pageId);
1294
- if (!docDir.startsWith(pagesRoot + path.sep)) return null;
1295
- const assetsDir = path.resolve(docDir, "assets");
1296
- if (assetsDir !== path.join(docDir, "assets")) return null;
1297
- return assetsDir;
1298
- }
1299
- function resolveAssetFile(pagesRoot, pageId, filename) {
1300
- const assetsDir = resolveAssetsDir(pagesRoot, pageId);
1301
- if (!assetsDir) return null;
1302
- if (!validateAssetName(filename)) return null;
1303
- const file = path.resolve(assetsDir, filename);
1304
- if (!file.startsWith(assetsDir + path.sep)) return null;
1305
- return file;
1294
+ const pageDir = path.resolve(pagesRoot, pageId);
1295
+ if (!pageDir.startsWith(pagesRoot + path.sep)) return null;
1296
+ return path.join(pageDir, "assets");
1306
1297
  }
1307
1298
  function resolveScopedAssetsDir(pagesRoot, globalAssetsRoot, scope) {
1308
- if (scope === GLOBAL_SCOPE) return globalAssetsRoot;
1299
+ if (scope === "@global") return globalAssetsRoot;
1309
1300
  return resolveAssetsDir(pagesRoot, scope);
1310
1301
  }
1311
1302
  function resolveScopedAssetFile(pagesRoot, globalAssetsRoot, scope, filename) {
1312
- if (scope === GLOBAL_SCOPE) {
1313
- if (!validateAssetName(filename)) return null;
1314
- const file = path.resolve(globalAssetsRoot, filename);
1315
- if (!file.startsWith(globalAssetsRoot + path.sep)) return null;
1316
- return file;
1317
- }
1318
- return resolveAssetFile(pagesRoot, scope, filename);
1303
+ const assetsDir = resolveScopedAssetsDir(pagesRoot, globalAssetsRoot, scope);
1304
+ if (!assetsDir) return null;
1305
+ if (!validateAssetName(filename)) return null;
1306
+ const file = path.resolve(assetsDir, filename);
1307
+ if (!file.startsWith(assetsDir + path.sep)) return null;
1308
+ return file;
1319
1309
  }
1320
-
1321
1310
  //#endregion
1322
1311
  //#region src/http/request-guard.ts
1323
- function firstHeaderValue(value) {
1324
- if (Array.isArray(value)) return value[0] ?? null;
1325
- return value ?? null;
1326
- }
1327
1312
  function headerValue(req, name) {
1328
- return firstHeaderValue(req.headers[name.toLowerCase()])?.trim() ?? null;
1313
+ const raw = req.headers[name.toLowerCase()];
1314
+ return (Array.isArray(raw) ? raw[0] : raw)?.trim() ?? null;
1329
1315
  }
1330
1316
  function firstCommaToken(value) {
1331
1317
  if (!value) return null;
@@ -1347,15 +1333,13 @@ function normalizedOrigin(origin) {
1347
1333
  }
1348
1334
  function validateMutationRequest(req, opts = {}) {
1349
1335
  if (opts.requireJsonBody) {
1350
- const contentType = headerValue(req, "content-type")?.toLowerCase();
1351
- if (!contentType || !contentType.startsWith("application/json")) return {
1336
+ if (!(headerValue(req, "content-type")?.toLowerCase())?.startsWith("application/json")) return {
1352
1337
  ok: false,
1353
1338
  status: 415,
1354
1339
  error: "content-type must be application/json"
1355
1340
  };
1356
1341
  }
1357
- const fetchSite = firstCommaToken(headerValue(req, "sec-fetch-site"))?.toLowerCase();
1358
- if (fetchSite === "cross-site") return {
1342
+ if (firstCommaToken(headerValue(req, "sec-fetch-site"))?.toLowerCase() === "cross-site") return {
1359
1343
  ok: false,
1360
1344
  status: 403,
1361
1345
  error: "cross-site request blocked"
@@ -1379,15 +1363,90 @@ function validateMutationRequest(req, opts = {}) {
1379
1363
  status: 400,
1380
1364
  error: "missing host header"
1381
1365
  };
1382
- const expectedOrigin = `${requestProto(req)}://${host}`.toLowerCase();
1383
- if (actualOrigin !== expectedOrigin) return {
1366
+ if (actualOrigin !== `${requestProto(req)}://${host}`.toLowerCase()) return {
1384
1367
  ok: false,
1385
1368
  status: 403,
1386
1369
  error: "origin mismatch"
1387
1370
  };
1388
1371
  return { ok: true };
1389
1372
  }
1390
-
1373
+ //#endregion
1374
+ //#region src/files/short-id.ts
1375
+ function shortId(prefix) {
1376
+ return `${prefix}-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
1377
+ }
1378
+ //#endregion
1379
+ //#region src/files/folders.ts
1380
+ const FOLDER_ID_RE = /^f-[a-f0-9]{8}$/;
1381
+ const COLOR_RE = /^#[0-9a-fA-F]{6}$/;
1382
+ function foldersManifestPath(pagesRoot) {
1383
+ return path.join(pagesRoot, ".folders.json");
1384
+ }
1385
+ function emptyManifest() {
1386
+ return {
1387
+ folders: [],
1388
+ assignments: {}
1389
+ };
1390
+ }
1391
+ async function readManifest(file) {
1392
+ try {
1393
+ const raw = await fs.readFile(file, "utf8");
1394
+ const parsed = JSON.parse(raw);
1395
+ return {
1396
+ folders: Array.isArray(parsed.folders) ? parsed.folders : [],
1397
+ assignments: parsed.assignments && typeof parsed.assignments === "object" ? parsed.assignments : {}
1398
+ };
1399
+ } catch (err) {
1400
+ if (err.code === "ENOENT") return emptyManifest();
1401
+ throw err;
1402
+ }
1403
+ }
1404
+ async function writeManifest(file, manifest) {
1405
+ await fs.mkdir(path.dirname(file), { recursive: true });
1406
+ await fs.writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
1407
+ }
1408
+ function newFolderId() {
1409
+ return shortId("f");
1410
+ }
1411
+ function validateName(v) {
1412
+ if (typeof v !== "string") return null;
1413
+ const trimmed = v.trim();
1414
+ if (trimmed.length < 1 || trimmed.length > 40) return null;
1415
+ return trimmed;
1416
+ }
1417
+ function validateReorder(v, current) {
1418
+ if (!Array.isArray(v) || v.length !== current.length) return null;
1419
+ const known = new Set(current.map((f) => f.id));
1420
+ const seen = /* @__PURE__ */ new Set();
1421
+ const out = [];
1422
+ for (const id of v) {
1423
+ if (typeof id !== "string" || !FOLDER_ID_RE.test(id)) return null;
1424
+ if (!known.has(id) || seen.has(id)) return null;
1425
+ seen.add(id);
1426
+ out.push(id);
1427
+ }
1428
+ return out;
1429
+ }
1430
+ function validateIcon(v) {
1431
+ if (!v || typeof v !== "object") return null;
1432
+ const icon = v;
1433
+ if (icon.type === "emoji") {
1434
+ if (typeof icon.value !== "string") return null;
1435
+ if (icon.value.length < 1 || icon.value.length > 8) return null;
1436
+ return {
1437
+ type: "emoji",
1438
+ value: icon.value
1439
+ };
1440
+ }
1441
+ if (icon.type === "color") {
1442
+ if (typeof icon.value !== "string" || !COLOR_RE.test(icon.value)) return null;
1443
+ return {
1444
+ type: "color",
1445
+ value: icon.value
1446
+ };
1447
+ }
1448
+ return null;
1449
+ }
1391
1450
  //#endregion
1392
1451
  //#region src/vite/routes/context.ts
1393
1452
  function makeContext(opts) {
@@ -1395,14 +1454,12 @@ function makeContext(opts) {
1395
1454
  const pagesDir = opts.pagesDir ?? "pages";
1396
1455
  const assetsDir = opts.assetsDir ?? "assets";
1397
1456
  const pagesRoot = path.resolve(userCwd, pagesDir);
1398
- const globalAssetsRoot = path.resolve(userCwd, assetsDir);
1399
- const manifestPath = path.join(pagesRoot, ".folders.json");
1400
1457
  return {
1401
1458
  userCwd,
1402
1459
  pagesDir,
1403
1460
  pagesRoot,
1404
- globalAssetsRoot,
1405
- manifestPath,
1461
+ globalAssetsRoot: path.resolve(userCwd, assetsDir),
1462
+ manifestPath: foldersManifestPath(pagesRoot),
1406
1463
  coreVersion: opts.coreVersion
1407
1464
  };
1408
1465
  }
@@ -1437,7 +1494,6 @@ function resolvePagePath(userCwd, pagesDir, pageId) {
1437
1494
  function resolvePageEntryPath(ctx, pageId) {
1438
1495
  return resolvePagePath(ctx.userCwd, ctx.pagesDir, pageId);
1439
1496
  }
1440
-
1441
1497
  //#endregion
1442
1498
  //#region src/vite/routes/assets.ts
1443
1499
  function registerAssetRoutes(server, ctx) {
@@ -1456,8 +1512,7 @@ function registerAssetRoutes(server, ctx) {
1456
1512
  const assetPath = isGlobal ? `@assets/${filename}` : `./assets/${filename}`;
1457
1513
  let pageIds;
1458
1514
  if (isGlobal) try {
1459
- const entries = await fs.readdir(ctx.pagesRoot, { withFileTypes: true });
1460
- pageIds = entries.filter((e) => e.isDirectory() && PAGE_ID_RE.test(e.name)).map((e) => e.name);
1515
+ pageIds = (await fs.readdir(ctx.pagesRoot, { withFileTypes: true })).filter((e) => e.isDirectory() && PAGE_ID_RE.test(e.name)).map((e) => e.name);
1461
1516
  } catch {
1462
1517
  pageIds = [];
1463
1518
  }
@@ -1521,8 +1576,7 @@ function registerAssetRoutes(server, ctx) {
1521
1576
  const isGlobal = pageId === GLOBAL_SCOPE;
1522
1577
  let scanIds;
1523
1578
  if (isGlobal) try {
1524
- const dirs = await fs.readdir(ctx.pagesRoot, { withFileTypes: true });
1525
- scanIds = dirs.filter((e) => e.isDirectory() && PAGE_ID_RE.test(e.name)).map((e) => e.name);
1579
+ scanIds = (await fs.readdir(ctx.pagesRoot, { withFileTypes: true })).filter((e) => e.isDirectory() && PAGE_ID_RE.test(e.name)).map((e) => e.name);
1526
1580
  } catch {
1527
1581
  scanIds = [];
1528
1582
  }
@@ -1568,7 +1622,7 @@ function registerAssetRoutes(server, ctx) {
1568
1622
  const overwrite = url.searchParams.get("overwrite") === "1";
1569
1623
  const lenHeader = req.headers["content-length"];
1570
1624
  const len = typeof lenHeader === "string" ? Number(lenHeader) : NaN;
1571
- if (Number.isFinite(len) && len > ASSET_MAX_BYTES) return json(res, 413, { error: "file too large" });
1625
+ if (Number.isFinite(len) && len > 26214400) return json(res, 413, { error: "file too large" });
1572
1626
  if (!overwrite) try {
1573
1627
  await fs.access(file);
1574
1628
  return json(res, 409, { error: "asset exists" });
@@ -1582,7 +1636,7 @@ function registerAssetRoutes(server, ctx) {
1582
1636
  await new Promise((resolve, reject) => {
1583
1637
  req.on("data", (c) => {
1584
1638
  total += c.length;
1585
- if (total > ASSET_MAX_BYTES) {
1639
+ if (total > 26214400) {
1586
1640
  oversized = true;
1587
1641
  req.destroy();
1588
1642
  return;
@@ -1608,8 +1662,7 @@ function registerAssetRoutes(server, ctx) {
1608
1662
  if (method === "PATCH") {
1609
1663
  const requestCheck = validateMutationRequest(req, { requireJsonBody: true });
1610
1664
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
1611
- const body = await readBody(req);
1612
- const target = validateAssetName(body.name);
1665
+ const target = validateAssetName((await readBody(req)).name);
1613
1666
  if (!target) return json(res, 400, { error: "invalid name" });
1614
1667
  if (target === filename) return json(res, 200, {
1615
1668
  ok: true,
@@ -1650,10 +1703,9 @@ function registerAssetRoutes(server, ctx) {
1650
1703
  }
1651
1704
  });
1652
1705
  }
1653
-
1654
1706
  //#endregion
1655
1707
  //#region src/editing/comments.ts
1656
- const MARKER_RE = /\{\/\*\s*@page-comment\s+id="(c-[a-f0-9]+)"\s+ts="([^"]+)"\s+text="([A-Za-z0-9_-]+={0,2})"\s*\*\/\}/g;
1708
+ const MARKER_RE = /\{\/\*\s*@page-comment\s+id="(c-[a-f0-9]+)"\s+ts="([^"]+)"\s+text="([A-Za-z0-9_-]+={0,2})"\s*\*\/\}/;
1657
1709
  function b64urlEncode(s) {
1658
1710
  return Buffer.from(s, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1659
1711
  }
@@ -1665,9 +1717,7 @@ function parseMarkers(source) {
1665
1717
  const comments = [];
1666
1718
  const lines = source.split("\n");
1667
1719
  for (let i = 0; i < lines.length; i++) {
1668
- const line = lines[i];
1669
- MARKER_RE.lastIndex = 0;
1670
- const m = MARKER_RE.exec(line);
1720
+ const m = lines[i].match(MARKER_RE);
1671
1721
  if (!m) continue;
1672
1722
  const [, id, ts, textB64] = m;
1673
1723
  try {
@@ -1684,7 +1734,7 @@ function parseMarkers(source) {
1684
1734
  return comments;
1685
1735
  }
1686
1736
  function newCommentId() {
1687
- return `c-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
1737
+ return shortId("c");
1688
1738
  }
1689
1739
  function markerDeleteRegex(id) {
1690
1740
  return new RegExp(`\\{\\/\\*\\s*@page-comment\\s+id="${id}"\\s+ts="[^"]+"\\s+text="[A-Za-z0-9_\\-]+={0,2}"\\s*\\*\\/\\}`);
@@ -1700,50 +1750,19 @@ function lineToOffset(source, line) {
1700
1750
  }
1701
1751
  function lineIndent(source, lineNumber) {
1702
1752
  const start = lineToOffset(source, lineNumber);
1703
- const m = source.slice(start, start + 200).match(/^[ \t]*/);
1704
- return m?.[0] ?? "";
1705
- }
1706
- function findJsxAncestors(ast, line, column) {
1707
- const hits = [];
1708
- walkJsx(ast, (n) => {
1709
- if (!n.loc || !t$1.isJSXElement(n) && !t$1.isJSXFragment(n)) return;
1710
- const s = n.loc.start;
1711
- const e = n.loc.end;
1712
- const afterStart = line > s.line || line === s.line && column >= s.column;
1713
- const beforeEnd = line < e.line || line === e.line && column < e.column;
1714
- if (afterStart && beforeEnd) hits.push({
1715
- node: n,
1716
- size: (n.end ?? 0) - (n.start ?? 0)
1717
- });
1718
- });
1719
- hits.sort((a, b) => a.size - b.size);
1720
- return hits.map((h) => h.node);
1753
+ return source.slice(start, start + 200).match(/^[ \t]*/)?.[0] ?? "";
1721
1754
  }
1722
1755
  function planInsertion(source, target) {
1723
- if (t$1.isJSXFragment(target)) {
1724
- const opening = target.openingFragment;
1725
- const startLine = target.loc?.start.line ?? 1;
1726
- return {
1727
- offset: opening.end ?? 0,
1728
- indent: `${lineIndent(source, startLine)} `
1729
- };
1730
- }
1731
- if (t$1.isJSXElement(target)) {
1732
- const opening = target.openingElement;
1733
- if (opening.selfClosing) return null;
1734
- const startLine = target.loc?.start.line ?? 1;
1735
- return {
1736
- offset: opening.end ?? 0,
1737
- indent: `${lineIndent(source, startLine)} `
1738
- };
1739
- }
1740
- return null;
1756
+ if (t.isJSXElement(target) && target.openingElement.selfClosing) return null;
1757
+ return {
1758
+ offset: (t.isJSXFragment(target) ? target.openingFragment : target.openingElement).end ?? 0,
1759
+ indent: `${lineIndent(source, target.loc?.start.line ?? 1)} `
1760
+ };
1741
1761
  }
1742
1762
  function findInsertion(source, line, column) {
1743
1763
  const ast = parseSource$1(source);
1744
1764
  if (!ast) return null;
1745
- const col = column ?? 0;
1746
- const ancestors = findJsxAncestors(ast, line, col);
1765
+ const ancestors = findJsxAncestors$1(ast, line, column ?? 0);
1747
1766
  for (const node of ancestors) {
1748
1767
  const plan = planInsertion(source, node);
1749
1768
  if (plan) return plan;
@@ -1751,11 +1770,8 @@ function findInsertion(source, line, column) {
1751
1770
  return null;
1752
1771
  }
1753
1772
  function offsetToLine(source, offset) {
1754
- let line = 1;
1755
- for (let i = 0; i < offset && i < source.length; i++) if (source[i] === "\n") line++;
1756
- return line;
1773
+ return source.slice(0, offset).split("\n").length;
1757
1774
  }
1758
-
1759
1775
  //#endregion
1760
1776
  //#region src/vite/routes/comments.ts
1761
1777
  function registerCommentRoutes(server, ctx) {
@@ -1764,8 +1780,7 @@ function registerCommentRoutes(server, ctx) {
1764
1780
  const method = req.method ?? "GET";
1765
1781
  try {
1766
1782
  if (method === "GET" && url.pathname === "/") {
1767
- const pageId = url.searchParams.get("pageId") ?? "";
1768
- const file = resolvePageEntryPath(ctx, pageId);
1783
+ const file = resolvePageEntryPath(ctx, url.searchParams.get("pageId") ?? "");
1769
1784
  if (!file) return json(res, 400, { error: "invalid pageId" });
1770
1785
  let source;
1771
1786
  try {
@@ -1779,8 +1794,7 @@ function registerCommentRoutes(server, ctx) {
1779
1794
  const requestCheck = validateMutationRequest(req, { requireJsonBody: true });
1780
1795
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
1781
1796
  const body = await readBody(req);
1782
- const pageId = body.pageId ?? "";
1783
- const file = resolvePageEntryPath(ctx, pageId);
1797
+ const file = resolvePageEntryPath(ctx, body.pageId ?? "");
1784
1798
  if (!file) return json(res, 400, { error: "invalid pageId" });
1785
1799
  if (!body.line || body.line < 1) return json(res, 400, { error: "invalid line" });
1786
1800
  if (!body.text || typeof body.text !== "string") return json(res, 400, { error: "missing text" });
@@ -1793,18 +1807,17 @@ function registerCommentRoutes(server, ctx) {
1793
1807
  const plan = findInsertion(source, body.line, body.column);
1794
1808
  if (!plan) return json(res, 422, { error: `could not find a JSX container around line ${body.line}. Try clicking a different element.` });
1795
1809
  const id = newCommentId();
1796
- const ts = new Date().toISOString();
1810
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
1797
1811
  const payload = b64urlEncode(JSON.stringify({
1798
1812
  note: body.text,
1799
1813
  hint: body.hint
1800
1814
  }));
1801
1815
  const marker = `\n${plan.indent}{/* @page-comment id="${id}" ts="${ts}" text="${payload}" */}`;
1802
- const next$1 = source.slice(0, plan.offset) + marker + source.slice(plan.offset);
1803
- await fs.writeFile(file, next$1, "utf8");
1804
- const markerLine = offsetToLine(next$1, plan.offset + 1);
1816
+ const next = source.slice(0, plan.offset) + marker + source.slice(plan.offset);
1817
+ await fs.writeFile(file, next, "utf8");
1805
1818
  return json(res, 200, {
1806
1819
  id,
1807
- line: markerLine
1820
+ line: offsetToLine(next, plan.offset + 1)
1808
1821
  });
1809
1822
  }
1810
1823
  if (method === "DELETE" && url.pathname.startsWith("/")) {
@@ -1812,8 +1825,7 @@ function registerCommentRoutes(server, ctx) {
1812
1825
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
1813
1826
  const id = url.pathname.slice(1);
1814
1827
  if (!/^c-[a-f0-9]+$/.test(id)) return json(res, 400, { error: "invalid id" });
1815
- const pageId = url.searchParams.get("pageId") ?? "";
1816
- const file = resolvePageEntryPath(ctx, pageId);
1828
+ const file = resolvePageEntryPath(ctx, url.searchParams.get("pageId") ?? "");
1817
1829
  if (!file) return json(res, 400, { error: "invalid pageId" });
1818
1830
  let source;
1819
1831
  try {
@@ -1835,21 +1847,18 @@ function registerCommentRoutes(server, ctx) {
1835
1847
  }
1836
1848
  });
1837
1849
  }
1838
-
1839
1850
  //#endregion
1840
1851
  //#region src/vite/routes/edit.ts
1841
1852
  function registerEditRoutes(server, ctx) {
1842
1853
  server.middlewares.use("/__edit", async (req, res, next) => {
1843
1854
  const url = new URL(req.url ?? "/", "http://local");
1844
- const method = req.method ?? "GET";
1845
- if (method !== "POST") return next();
1855
+ if ((req.method ?? "GET") !== "POST") return next();
1846
1856
  const requestCheck = validateMutationRequest(req, { requireJsonBody: true });
1847
1857
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
1848
1858
  try {
1849
1859
  if (url.pathname === "/") {
1850
1860
  const body = await readBody(req);
1851
- const pageId = body.pageId ?? "";
1852
- const file = resolvePageEntryPath(ctx, pageId);
1861
+ const file = resolvePageEntryPath(ctx, body.pageId ?? "");
1853
1862
  if (!file) return json(res, 400, { error: "invalid pageId" });
1854
1863
  if (!body.line || body.line < 1) return json(res, 400, { error: "invalid line" });
1855
1864
  if (!Array.isArray(body.ops)) return json(res, 400, { error: "missing ops" });
@@ -1893,8 +1902,7 @@ function registerEditRoutes(server, ctx) {
1893
1902
  }
1894
1903
  if (url.pathname === "/batch") {
1895
1904
  const body = await readBody(req);
1896
- const pageId = body.pageId ?? "";
1897
- const file = resolvePageEntryPath(ctx, pageId);
1905
+ const file = resolvePageEntryPath(ctx, body.pageId ?? "");
1898
1906
  if (!file) return json(res, 400, { error: "invalid pageId" });
1899
1907
  if (!Array.isArray(body.edits)) return json(res, 400, { error: "missing edits" });
1900
1908
  let source;
@@ -1936,77 +1944,6 @@ function registerEditRoutes(server, ctx) {
1936
1944
  }
1937
1945
  });
1938
1946
  }
1939
-
1940
- //#endregion
1941
- //#region src/files/folders.ts
1942
- const FOLDER_ID_RE = /^f-[a-f0-9]{8}$/;
1943
- const COLOR_RE = /^#[0-9a-fA-F]{6}$/;
1944
- function emptyManifest() {
1945
- return {
1946
- folders: [],
1947
- assignments: {}
1948
- };
1949
- }
1950
- async function readManifest(file) {
1951
- try {
1952
- const raw = await fs.readFile(file, "utf8");
1953
- const parsed = JSON.parse(raw);
1954
- return {
1955
- folders: Array.isArray(parsed.folders) ? parsed.folders : [],
1956
- assignments: parsed.assignments && typeof parsed.assignments === "object" ? parsed.assignments : {}
1957
- };
1958
- } catch (err) {
1959
- if (err.code === "ENOENT") return emptyManifest();
1960
- throw err;
1961
- }
1962
- }
1963
- async function writeManifest(file, manifest) {
1964
- await fs.mkdir(path.dirname(file), { recursive: true });
1965
- await fs.writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
1966
- }
1967
- function newFolderId() {
1968
- return `f-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
1969
- }
1970
- function validateName(v) {
1971
- if (typeof v !== "string") return null;
1972
- const trimmed = v.trim();
1973
- if (trimmed.length < 1 || trimmed.length > 40) return null;
1974
- return trimmed;
1975
- }
1976
- function validateReorder(v, current) {
1977
- if (!Array.isArray(v) || v.length !== current.length) return null;
1978
- const known = new Set(current.map((f) => f.id));
1979
- const seen = new Set();
1980
- const out = [];
1981
- for (const id of v) {
1982
- if (typeof id !== "string" || !FOLDER_ID_RE.test(id)) return null;
1983
- if (!known.has(id) || seen.has(id)) return null;
1984
- seen.add(id);
1985
- out.push(id);
1986
- }
1987
- return out;
1988
- }
1989
- function validateIcon(v) {
1990
- if (!v || typeof v !== "object") return null;
1991
- const icon = v;
1992
- if (icon.type === "emoji") {
1993
- if (typeof icon.value !== "string") return null;
1994
- if (icon.value.length < 1 || icon.value.length > 8) return null;
1995
- return {
1996
- type: "emoji",
1997
- value: icon.value
1998
- };
1999
- }
2000
- if (icon.type === "color") {
2001
- if (typeof icon.value !== "string" || !COLOR_RE.test(icon.value)) return null;
2002
- return {
2003
- type: "color",
2004
- value: icon.value
2005
- };
2006
- }
2007
- return null;
2008
- }
2009
-
2010
1947
  //#endregion
2011
1948
  //#region src/vite/routes/folders.ts
2012
1949
  function registerFolderRoutes(server, ctx) {
@@ -2014,10 +1951,7 @@ function registerFolderRoutes(server, ctx) {
2014
1951
  const url = new URL(req.url ?? "/", "http://local");
2015
1952
  const method = req.method ?? "GET";
2016
1953
  try {
2017
- if (method === "GET" && url.pathname === "/") {
2018
- const manifest = await readManifest(ctx.manifestPath);
2019
- return json(res, 200, manifest);
2020
- }
1954
+ if (method === "GET" && url.pathname === "/") return json(res, 200, await readManifest(ctx.manifestPath));
2021
1955
  if (method === "POST" && url.pathname === "/") {
2022
1956
  const requestCheck = validateMutationRequest(req, { requireJsonBody: true });
2023
1957
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
@@ -2107,7 +2041,123 @@ function registerFolderRoutes(server, ctx) {
2107
2041
  }
2108
2042
  });
2109
2043
  }
2110
-
2044
+ //#endregion
2045
+ //#region src/vite/routes/gfonts.ts
2046
+ const CATALOG_URL = "https://fonts.google.com/metadata/fonts";
2047
+ const CATALOG_TTL = 36e5;
2048
+ const LEGACY_UA = "Mozilla/5.0";
2049
+ const FONT_EXTS = /* @__PURE__ */ new Set([
2050
+ "ttf",
2051
+ "otf",
2052
+ "woff",
2053
+ "woff2"
2054
+ ]);
2055
+ const FETCH_TIMEOUT_MS = 15e3;
2056
+ const DEFAULT_LIMIT = 30;
2057
+ const MAX_LIMIT = 100;
2058
+ function isGstaticHost(hostname) {
2059
+ const host = hostname.toLowerCase();
2060
+ return host === "gstatic.com" || host.endsWith(".gstatic.com");
2061
+ }
2062
+ function clampLimit(raw) {
2063
+ const n = Math.floor(Number(raw));
2064
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
2065
+ return Math.min(n, MAX_LIMIT);
2066
+ }
2067
+ let catalogCache = null;
2068
+ let catalogInflight = null;
2069
+ async function loadCatalog() {
2070
+ if (catalogCache && Date.now() - catalogCache.at < CATALOG_TTL) return catalogCache.entries;
2071
+ if (catalogInflight) return catalogInflight;
2072
+ catalogInflight = (async () => {
2073
+ const res = await fetch(CATALOG_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
2074
+ if (!res.ok) throw new Error(`metadata ${res.status}`);
2075
+ const text = await res.text();
2076
+ const start = text.indexOf("{");
2077
+ const entries = (JSON.parse(start > 0 ? text.slice(start) : text).familyMetadataList ?? []).map((f) => ({
2078
+ family: f.family,
2079
+ category: f.category ?? "",
2080
+ variants: Object.keys(f.fonts ?? {}),
2081
+ popularity: typeof f.popularity === "number" ? f.popularity : Number.MAX_SAFE_INTEGER
2082
+ }));
2083
+ catalogCache = {
2084
+ at: Date.now(),
2085
+ entries
2086
+ };
2087
+ return entries;
2088
+ })();
2089
+ try {
2090
+ return await catalogInflight;
2091
+ } finally {
2092
+ catalogInflight = null;
2093
+ }
2094
+ }
2095
+ function cssFamilyParam(family) {
2096
+ return encodeURIComponent(family).replace(/%20/g, "+");
2097
+ }
2098
+ async function resolveFontFile(family, variant) {
2099
+ const italic = /i(talic)?$/.test(variant);
2100
+ const weight = variant.replace(/i(talic)?$/, "") || "400";
2101
+ const style = italic ? `${weight}italic` : weight;
2102
+ const cssUrl = `https://fonts.googleapis.com/css?family=${cssFamilyParam(family)}:${style}`;
2103
+ const css = await fetch(cssUrl, {
2104
+ headers: { "user-agent": LEGACY_UA },
2105
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2106
+ });
2107
+ if (!css.ok) return null;
2108
+ const match = (await css.text()).match(/url\((https:\/\/[^)]+)\)/);
2109
+ if (!match) return null;
2110
+ let parsed;
2111
+ try {
2112
+ parsed = new URL(match[1]);
2113
+ } catch {
2114
+ return null;
2115
+ }
2116
+ if (!isGstaticHost(parsed.hostname)) return null;
2117
+ const rawExt = parsed.pathname.split(".").pop()?.toLowerCase() ?? "";
2118
+ const ext = FONT_EXTS.has(rawExt) ? rawExt : "ttf";
2119
+ return {
2120
+ url: parsed.toString(),
2121
+ ext
2122
+ };
2123
+ }
2124
+ function registerGfontsRoutes(server) {
2125
+ server.middlewares.use("/__gfonts", async (req, res, next) => {
2126
+ const reqUrl = new URL(req.url ?? "/", "http://local");
2127
+ if ((req.method ?? "GET") !== "GET") return next();
2128
+ try {
2129
+ if (reqUrl.pathname === "/search") {
2130
+ const q = (reqUrl.searchParams.get("q") ?? "").trim().toLowerCase();
2131
+ const limit = clampLimit(reqUrl.searchParams.get("limit"));
2132
+ let entries = await loadCatalog();
2133
+ if (q) entries = entries.filter((e) => e.family.toLowerCase().includes(q));
2134
+ return json(res, 200, entries.slice().sort((a, b) => a.popularity - b.popularity).slice(0, limit).map((e) => ({
2135
+ family: e.family,
2136
+ category: e.category,
2137
+ variants: e.variants
2138
+ })));
2139
+ }
2140
+ if (reqUrl.pathname === "/download") {
2141
+ const family = reqUrl.searchParams.get("family");
2142
+ const variant = reqUrl.searchParams.get("variant") || "400";
2143
+ if (!family) return json(res, 400, { error: "missing family" });
2144
+ const resolved = await resolveFontFile(family, variant);
2145
+ if (!resolved) return json(res, 404, { error: "font not found" });
2146
+ const upstream = await fetch(resolved.url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
2147
+ if (!upstream.ok) return json(res, 502, { error: `gstatic ${upstream.status}` });
2148
+ res.statusCode = 200;
2149
+ res.setHeader("content-type", upstream.headers.get("content-type") ?? "font/ttf");
2150
+ res.setHeader("x-font-ext", resolved.ext);
2151
+ res.setHeader("cache-control", "no-store");
2152
+ res.end(Buffer.from(await upstream.arrayBuffer()));
2153
+ return;
2154
+ }
2155
+ return next();
2156
+ } catch (err) {
2157
+ json(res, 502, { error: String(err.message ?? err) });
2158
+ }
2159
+ });
2160
+ }
2111
2161
  //#endregion
2112
2162
  //#region src/vite/routes/pages.ts
2113
2163
  function registerPageRoutes(server, ctx) {
@@ -2119,14 +2169,14 @@ function registerPageRoutes(server, ctx) {
2119
2169
  if (duplicateMatch && method === "POST") {
2120
2170
  const requestCheck = validateMutationRequest(req);
2121
2171
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
2122
- const pageId$1 = duplicateMatch[1];
2123
- if (!PAGE_ID_RE.test(pageId$1)) return json(res, 400, { error: "invalid pageId" });
2172
+ const pageId = duplicateMatch[1];
2173
+ if (!PAGE_ID_RE.test(pageId)) return json(res, 400, { error: "invalid pageId" });
2124
2174
  const body = await readBody(req);
2125
2175
  if (body.newId !== void 0 && typeof body.newId !== "string") return json(res, 400, { error: "invalid newId" });
2126
- const duplicated = await duplicatePageDir(ctx.pagesRoot, pageId$1, body.newId);
2176
+ const duplicated = await duplicatePageDir(ctx.pagesRoot, pageId, body.newId);
2127
2177
  if (!duplicated.ok) return json(res, duplicated.status, { error: duplicated.error });
2128
2178
  const manifest = await readManifest(ctx.manifestPath);
2129
- const folderId = manifest.assignments[pageId$1];
2179
+ const folderId = manifest.assignments[pageId];
2130
2180
  if (folderId) {
2131
2181
  manifest.assignments[duplicated.pageId] = folderId;
2132
2182
  await writeManifest(ctx.manifestPath, manifest);
@@ -2168,8 +2218,7 @@ function registerPageRoutes(server, ctx) {
2168
2218
  if (method === "DELETE") {
2169
2219
  const requestCheck = validateMutationRequest(req);
2170
2220
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
2171
- const removed = await rmPageDir(ctx.pagesRoot, pageId);
2172
- if (!removed) return json(res, 404, { error: "page not found" });
2221
+ if (!await rmPageDir(ctx.pagesRoot, pageId)) return json(res, 404, { error: "page not found" });
2173
2222
  const manifest = await readManifest(ctx.manifestPath);
2174
2223
  delete manifest.assignments[pageId];
2175
2224
  await writeManifest(ctx.manifestPath, manifest);
@@ -2182,10 +2231,6 @@ function registerPageRoutes(server, ctx) {
2182
2231
  }
2183
2232
  });
2184
2233
  }
2185
-
2186
- //#endregion
2187
- //#region src/vite/routes/restart.ts
2188
- const RESTART_EXIT_CODE = 52;
2189
2234
  const DEV_SUPERVISED_ENV = "OPEN_PAGES_DEV_SUPERVISED";
2190
2235
  const executionId = randomUUID();
2191
2236
  function isSupervised() {
@@ -2205,18 +2250,16 @@ function registerRestartRoutes(server) {
2205
2250
  const guard = validateMutationRequest(req);
2206
2251
  if (!guard.ok) return json(res, guard.status, { error: guard.error });
2207
2252
  if (!isSupervised()) return json(res, 409, { error: "dev server was not started by the open-pages CLI" });
2208
- res.on("finish", () => process.exit(RESTART_EXIT_CODE));
2253
+ res.on("finish", () => process.exit(52));
2209
2254
  json(res, 200, { restarting: true });
2210
2255
  });
2211
2256
  }
2212
-
2213
2257
  //#endregion
2214
2258
  //#region src/vite/routes/svgl.ts
2215
2259
  function registerSvglRoutes(server) {
2216
2260
  server.middlewares.use("/__svgl", async (req, res, next) => {
2217
2261
  const reqUrl = new URL(req.url ?? "/", "http://local");
2218
- const method = req.method ?? "GET";
2219
- if (method !== "GET") return next();
2262
+ if ((req.method ?? "GET") !== "GET") return next();
2220
2263
  try {
2221
2264
  let target = null;
2222
2265
  if (reqUrl.pathname === "/search") {
@@ -2253,7 +2296,6 @@ function registerSvglRoutes(server) {
2253
2296
  }
2254
2297
  });
2255
2298
  }
2256
-
2257
2299
  //#endregion
2258
2300
  //#region src/vite/routes/update.ts
2259
2301
  let updateInFlight = null;
@@ -2292,18 +2334,15 @@ function registerUpdateRoutes(server, ctx) {
2292
2334
  updateInFlight ??= updatePackage(ctx).finally(() => {
2293
2335
  updateInFlight = null;
2294
2336
  });
2295
- const result = await updateInFlight;
2296
- json(res, 200, result);
2337
+ json(res, 200, await updateInFlight);
2297
2338
  } catch (err) {
2298
2339
  json(res, 500, { error: err instanceof Error ? err.message : "update failed" });
2299
2340
  }
2300
2341
  });
2301
2342
  }
2302
-
2303
2343
  //#endregion
2304
2344
  //#region src/vite/routes/watchers.ts
2305
2345
  function registerWatchers(server, ctx) {
2306
- server.watcher.add(ctx.manifestPath);
2307
2346
  server.watcher.on("change", (p) => {
2308
2347
  if (p === ctx.manifestPath) server.ws.send({
2309
2348
  type: "custom",
@@ -2321,8 +2360,7 @@ function registerWatchers(server, ctx) {
2321
2360
  return;
2322
2361
  }
2323
2362
  if (!p.startsWith(ctx.pagesRoot + path.sep)) return;
2324
- const rel = p.slice(ctx.pagesRoot.length + 1);
2325
- const parts = rel.split(path.sep);
2363
+ const parts = p.slice(ctx.pagesRoot.length + 1).split(path.sep);
2326
2364
  if (parts.length < 3 || parts[1] !== "assets") return;
2327
2365
  const pageId = parts[0];
2328
2366
  if (!PAGE_ID_RE.test(pageId)) return;
@@ -2336,7 +2374,6 @@ function registerWatchers(server, ctx) {
2336
2374
  server.watcher.on("change", onAssetChange);
2337
2375
  server.watcher.on("unlink", onAssetChange);
2338
2376
  }
2339
-
2340
2377
  //#endregion
2341
2378
  //#region src/vite/api-plugin.ts
2342
2379
  function apiPlugin(opts) {
@@ -2351,13 +2388,13 @@ function apiPlugin(opts) {
2351
2388
  registerPageRoutes(server, ctx);
2352
2389
  registerAssetRoutes(server, ctx);
2353
2390
  registerSvglRoutes(server);
2391
+ registerGfontsRoutes(server);
2354
2392
  registerFolderRoutes(server, ctx);
2355
2393
  registerUpdateRoutes(server, ctx);
2356
2394
  registerRestartRoutes(server);
2357
2395
  }
2358
2396
  };
2359
2397
  }
2360
-
2361
2398
  //#endregion
2362
2399
  //#region src/vite/current-plugin.ts
2363
2400
  const TEXT_SNIPPET_MAX = 120;
@@ -2411,7 +2448,7 @@ function currentPlugin(opts) {
2411
2448
  cached = next;
2412
2449
  const body = {
2413
2450
  ...next,
2414
- updatedAt: new Date().toISOString()
2451
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2415
2452
  };
2416
2453
  try {
2417
2454
  await fs.mkdir(outDir, { recursive: true });
@@ -2422,7 +2459,6 @@ function currentPlugin(opts) {
2422
2459
  }
2423
2460
  };
2424
2461
  }
2425
-
2426
2462
  //#endregion
2427
2463
  //#region src/vite/design-plugin.ts
2428
2464
  function parseSource(source) {
@@ -2484,17 +2520,13 @@ function literalToValue(node) {
2484
2520
  }
2485
2521
  case "TemplateLiteral": {
2486
2522
  const quasis = node.quasis;
2487
- const expressions = node.expressions;
2488
- if (expressions.length > 0) throw new Error("template literal has expressions");
2523
+ if (node.expressions.length > 0) throw new Error("template literal has expressions");
2489
2524
  return quasis[0].value.cooked ?? quasis[0].value.raw;
2490
2525
  }
2491
- case "ArrayExpression": {
2492
- const elements = node.elements;
2493
- return elements.map((el) => {
2494
- if (!el) throw new Error("array has hole");
2495
- return literalToValue(el);
2496
- });
2497
- }
2526
+ case "ArrayExpression": return node.elements.map((el) => {
2527
+ if (!el) throw new Error("array has hole");
2528
+ return literalToValue(el);
2529
+ });
2498
2530
  case "ObjectExpression": {
2499
2531
  const properties = node.properties;
2500
2532
  const out = {};
@@ -2544,18 +2576,16 @@ function serializeValue(value, level) {
2544
2576
  if (typeof value === "boolean") return value ? "true" : "false";
2545
2577
  if (Array.isArray(value)) {
2546
2578
  if (value.length === 0) return "[]";
2547
- const inner = value.map((el) => serializeValue(el, level + 1)).join(", ");
2548
- return `[${inner}]`;
2579
+ return `[${value.map((el) => serializeValue(el, level + 1)).join(", ")}]`;
2549
2580
  }
2550
2581
  if (isPlainObject(value)) {
2551
2582
  const entries = Object.entries(value);
2552
2583
  if (entries.length === 0) return "{}";
2553
2584
  const childIndent = indent(level + 1);
2554
- const lines = entries.map(([k, v]) => {
2585
+ return `{\n${entries.map(([k, v]) => {
2555
2586
  const key = isValidIdentifier(k) ? k : jsString(k);
2556
2587
  return `${childIndent}${key}: ${serializeValue(v, level + 1)},`;
2557
- });
2558
- return `{\n${lines.join("\n")}\n${indent(level)}}`;
2588
+ }).join("\n")}\n${indent(level)}}`;
2559
2589
  }
2560
2590
  throw new Error(`unsupported value type ${typeof value}`);
2561
2591
  }
@@ -2590,10 +2620,9 @@ function parseDocDesign(source) {
2590
2620
  error: err.message
2591
2621
  };
2592
2622
  }
2593
- const merged = mergeDesign(defaultDesign, value);
2594
2623
  return {
2595
2624
  ok: true,
2596
- design: merged,
2625
+ design: mergeDesign(defaultDesign, value),
2597
2626
  loc
2598
2627
  };
2599
2628
  }
@@ -2644,52 +2673,44 @@ function ensureDesignSystemImport(source, ast) {
2644
2673
  const imports = findImports(ast);
2645
2674
  const coreImport = imports.find((imp) => imp.source === "@autono/open-pages");
2646
2675
  if (coreImport) {
2647
- const hasDesignSystem = coreImport.specifiers.some((spec) => {
2676
+ if (coreImport.specifiers.some((spec) => {
2648
2677
  if (spec.type !== "ImportSpecifier") return false;
2649
- const imported = spec.imported;
2650
- return imported?.name === "DesignSystem";
2651
- });
2652
- if (hasDesignSystem) return {
2678
+ return spec.imported?.name === "DesignSystem";
2679
+ })) return {
2653
2680
  source,
2654
2681
  offsetShift: 0
2655
2682
  };
2656
2683
  const node = coreImport.node;
2657
- const importText = source.slice(node.start, node.end);
2658
- const braceClose = importText.lastIndexOf("}");
2684
+ const braceClose = source.slice(node.start, node.end).lastIndexOf("}");
2659
2685
  if (braceClose === -1) return {
2660
2686
  source,
2661
2687
  offsetShift: 0
2662
2688
  };
2663
2689
  const absoluteBrace = node.start + braceClose;
2664
2690
  const insertText = coreImport.specifiers.length > 0 ? ", type DesignSystem" : "type DesignSystem";
2665
- const next$1 = `${source.slice(0, absoluteBrace)}${insertText}${source.slice(absoluteBrace)}`;
2666
2691
  return {
2667
- source: next$1,
2692
+ source: `${source.slice(0, absoluteBrace)}${insertText}${source.slice(absoluteBrace)}`,
2668
2693
  offsetShift: insertText.length
2669
2694
  };
2670
2695
  }
2671
2696
  const stmt = `import type { DesignSystem } from '@autono/open-pages';\n`;
2672
2697
  if (imports.length > 0) {
2673
- const last = imports[imports.length - 1];
2674
- const insertAt = last.node.end;
2698
+ const insertAt = imports[imports.length - 1].node.end;
2675
2699
  const trail = source[insertAt] === "\n" ? "" : "\n";
2676
- const next$1 = `${source.slice(0, insertAt)}\n${stmt.slice(0, -1)}${trail}${source.slice(insertAt)}`;
2677
2700
  return {
2678
- source: next$1,
2701
+ source: `${source.slice(0, insertAt)}\n${stmt.slice(0, -1)}${trail}${source.slice(insertAt)}`,
2679
2702
  offsetShift: 1 + stmt.length - (trail ? 0 : 1)
2680
2703
  };
2681
2704
  }
2682
- const next = `${stmt}\n${source}`;
2683
2705
  return {
2684
- source: next,
2706
+ source: `${stmt}\n${source}`,
2685
2707
  offsetShift: stmt.length + 1
2686
2708
  };
2687
2709
  }
2688
2710
  function findInsertionPoint(source, ast) {
2689
2711
  const imports = findImports(ast);
2690
2712
  if (imports.length === 0) return 0;
2691
- const last = imports[imports.length - 1];
2692
- let off = last.node.end;
2713
+ let off = imports[imports.length - 1].node.end;
2693
2714
  while (off < source.length && source[off] !== "\n") off++;
2694
2715
  if (off < source.length) off++;
2695
2716
  return off;
@@ -2712,14 +2733,11 @@ function applyDesignWrite(source, next) {
2712
2733
  error: "could not parse page source"
2713
2734
  };
2714
2735
  const loc = findDesignDecl(ast);
2715
- if (loc) {
2716
- const out$1 = source.slice(0, loc.objectStart) + body + source.slice(loc.objectEnd);
2717
- return {
2718
- ok: true,
2719
- source: out$1,
2720
- created: false
2721
- };
2722
- }
2736
+ if (loc) return {
2737
+ ok: true,
2738
+ source: source.slice(0, loc.objectStart) + body + source.slice(loc.objectEnd),
2739
+ created: false
2740
+ };
2723
2741
  const withImport = ensureDesignSystemImport(source, ast);
2724
2742
  const ast2 = parseSource(withImport.source);
2725
2743
  if (!ast2) return {
@@ -2729,10 +2747,9 @@ function applyDesignWrite(source, next) {
2729
2747
  };
2730
2748
  const insertAt = findInsertionPoint(withImport.source, ast2);
2731
2749
  const block = `\nconst design: DesignSystem = ${body};\n`;
2732
- const out = withImport.source.slice(0, insertAt) + block + withImport.source.slice(insertAt);
2733
2750
  return {
2734
2751
  ok: true,
2735
- source: out,
2752
+ source: withImport.source.slice(0, insertAt) + block + withImport.source.slice(insertAt),
2736
2753
  created: true
2737
2754
  };
2738
2755
  }
@@ -2777,8 +2794,7 @@ function designPlugin(opts) {
2777
2794
  if (method === "PUT" && url.pathname === "/") {
2778
2795
  const requestCheck = validateMutationRequest(req, { requireJsonBody: true });
2779
2796
  if (!requestCheck.ok) return json(res, requestCheck.status, { error: requestCheck.error });
2780
- const body = await readBody(req);
2781
- const patch = body.patch;
2797
+ const patch = (await readBody(req)).patch;
2782
2798
  if (!patch || typeof patch !== "object") return json(res, 400, { error: "missing patch object" });
2783
2799
  let source;
2784
2800
  try {
@@ -2825,10 +2841,9 @@ function designPlugin(opts) {
2825
2841
  }
2826
2842
  };
2827
2843
  }
2828
-
2829
2844
  //#endregion
2830
2845
  //#region src/vite/loc-tags-plugin.ts
2831
- const UNTAGGABLE_COMPONENTS = new Set([
2846
+ const UNTAGGABLE_COMPONENTS = /* @__PURE__ */ new Set([
2832
2847
  "Fragment",
2833
2848
  "StrictMode",
2834
2849
  "Suspense"
@@ -2873,8 +2888,7 @@ function isPageSourceFile(id, docsRootPosix) {
2873
2888
  if (!filePath.startsWith(`${docsRootPosix}/`)) return false;
2874
2889
  if (!filePath.endsWith(".tsx")) return false;
2875
2890
  if (filePath.endsWith(".d.ts") || filePath.endsWith(".test.tsx")) return false;
2876
- const rel = filePath.slice(docsRootPosix.length + 1);
2877
- return rel.includes("/");
2891
+ return filePath.slice(docsRootPosix.length + 1).includes("/");
2878
2892
  }
2879
2893
  function locTagsPlugin(opts) {
2880
2894
  const pagesRoot = path.resolve(opts.userCwd, opts.pagesDir ?? "pages").replace(/\\/g, "/");
@@ -2893,7 +2907,6 @@ function locTagsPlugin(opts) {
2893
2907
  }
2894
2908
  };
2895
2909
  }
2896
-
2897
2910
  //#endregion
2898
2911
  //#region src/vite/themes-plugin.ts
2899
2912
  const THEMES_VMOD = "virtual:open-pages/themes";
@@ -2924,17 +2937,15 @@ function parseFrontmatter(raw, themeId) {
2924
2937
  async function findThemes(userCwd, themesDir) {
2925
2938
  const abs = path.resolve(userCwd, themesDir);
2926
2939
  if (!existsSync(abs)) return [];
2927
- const hits = await fg("*.md", {
2940
+ return (await fg("*.md", {
2928
2941
  cwd: abs,
2929
2942
  absolute: true,
2930
2943
  onlyFiles: true
2931
- });
2932
- return hits.sort();
2944
+ })).sort();
2933
2945
  }
2934
2946
  async function readTheme(mdAbs, themesRoot) {
2935
2947
  const id = path.basename(mdAbs, ".md");
2936
- const raw = await fs.readFile(mdAbs, "utf8");
2937
- const { fm, body } = parseFrontmatter(raw, id);
2948
+ const { fm, body } = parseFrontmatter(await fs.readFile(mdAbs, "utf8"), id);
2938
2949
  const demoCandidates = [
2939
2950
  `${id}.demo.tsx`,
2940
2951
  `${id}.demo.jsx`,
@@ -2960,30 +2971,33 @@ async function readTheme(mdAbs, themesRoot) {
2960
2971
  };
2961
2972
  }
2962
2973
  function generateThemesModule(themes, isDev) {
2963
- const meta = themes.map((t$4) => ({
2964
- id: t$4.id,
2965
- name: t$4.frontmatter.name,
2966
- description: t$4.frontmatter.description,
2967
- body: t$4.body,
2968
- hasDemo: t$4.demoAbs !== null,
2969
- hasCss: t$4.cssAbs !== null
2974
+ const meta = themes.map((t) => ({
2975
+ id: t.id,
2976
+ name: t.frontmatter.name,
2977
+ description: t.frontmatter.description,
2978
+ body: t.body,
2979
+ hasDemo: t.demoAbs !== null,
2980
+ hasCss: t.cssAbs !== null
2970
2981
  }));
2971
- const cases = themes.flatMap((t$4) => {
2972
- const abs = t$4.demoAbs;
2973
- if (!abs) return [];
2974
- const importPath = isDev ? `@fs/${normalizePath(abs).replace(/^\/+/, "")}` : abs;
2982
+ const withDemo = themes.filter((t) => t.demoAbs !== null);
2983
+ const cases = withDemo.map((t) => {
2984
+ const importPath = isDev ? `@fs/${normalizePath(t.demoAbs).replace(/^\/+/, "")}` : t.demoAbs;
2975
2985
  const importExpr = isDev ? `import(/* @vite-ignore */ import.meta.env.BASE_URL + ${JSON.stringify(importPath)})` : `import(${JSON.stringify(importPath)})`;
2976
- return [` case ${JSON.stringify(t$4.id)}: return ${importExpr};`];
2986
+ return ` case ${JSON.stringify(t.id)}: return ${importExpr};`;
2977
2987
  }).join("\n");
2978
- const withCss = themes.filter((t$4) => t$4.cssAbs !== null);
2979
- const cssCases = withCss.map((t$4) => {
2980
- const importPath = isDev ? `@fs/${normalizePath(t$4.cssAbs).replace(/^\/+/, "")}` : t$4.cssAbs;
2988
+ const urlCases = withDemo.map((t) => {
2989
+ const urlExpr = isDev ? `import.meta.env.BASE_URL + ${JSON.stringify(`@fs/${normalizePath(t.demoAbs).replace(/^\/+/, "")}`)}` : "''";
2990
+ return ` case ${JSON.stringify(t.id)}: return ${urlExpr};`;
2991
+ }).join("\n");
2992
+ const withCss = themes.filter((t) => t.cssAbs !== null);
2993
+ const cssCases = withCss.map((t) => {
2994
+ const importPath = isDev ? `@fs/${normalizePath(t.cssAbs).replace(/^\/+/, "")}` : t.cssAbs;
2981
2995
  const importExpr = isDev ? `import(/* @vite-ignore */ import.meta.env.BASE_URL + ${JSON.stringify(importPath)})` : `import(${JSON.stringify(importPath)})`;
2982
- return ` case ${JSON.stringify(t$4.id)}: return ${importExpr};`;
2996
+ return ` case ${JSON.stringify(t.id)}: return ${importExpr};`;
2983
2997
  }).join("\n");
2984
2998
  return `// virtual:open-pages/themes — generated
2985
2999
  export const themes = ${JSON.stringify(meta)};
2986
- export const themeCssIds = ${JSON.stringify(withCss.map((t$4) => t$4.id))};
3000
+ export const themeCssIds = ${JSON.stringify(withCss.map((t) => t.id))};
2987
3001
 
2988
3002
  // Importing a stylesheet through Vite injects it into the document; the
2989
3003
  // frame calls this for a page's meta.theme and for theme demos.
@@ -3000,6 +3014,15 @@ ${cases}
3000
3014
  default: throw new Error('Theme demo not found: ' + id);
3001
3015
  }
3002
3016
  }
3017
+
3018
+ // Dev-server module URL so the render worker can import the same demo the
3019
+ // main thread just loaded. A static build imports through loadThemeDemo.
3020
+ export function demoImportUrl(id) {
3021
+ switch (id) {
3022
+ ${urlCases}
3023
+ default: throw new Error('Theme demo not found: ' + id);
3024
+ }
3025
+ }
3003
3026
  `;
3004
3027
  }
3005
3028
  function themesPlugin(opts) {
@@ -3019,8 +3042,7 @@ function themesPlugin(opts) {
3019
3042
  async load(id) {
3020
3043
  if (id !== resolved(THEMES_VMOD)) return null;
3021
3044
  const files = await findThemes(userCwd, themesDir);
3022
- const themes = await Promise.all(files.map((f) => readTheme(f, themesRoot)));
3023
- return generateThemesModule(themes, isDev);
3045
+ return generateThemesModule(await Promise.all(files.map((f) => readTheme(f, themesRoot))), isDev);
3024
3046
  },
3025
3047
  configureServer(server) {
3026
3048
  const isThemeFile = (p) => {
@@ -3052,7 +3074,6 @@ function themesPlugin(opts) {
3052
3074
  }
3053
3075
  };
3054
3076
  }
3055
-
3056
3077
  //#endregion
3057
3078
  //#region src/vite/config.ts
3058
3079
  const CORE_VERSION = readCoreVersion();
@@ -3104,7 +3125,11 @@ async function createViteConfig(opts) {
3104
3125
  "@assets": assetsAbs
3105
3126
  } },
3106
3127
  optimizeDeps: {
3107
- entries: [path.join(APP_ROOT, "main.tsx"), path.join(APP_ROOT, "frame", "main.tsx")],
3128
+ entries: [
3129
+ path.join(APP_ROOT, "main.tsx"),
3130
+ path.join(APP_ROOT, "frame", "main.tsx"),
3131
+ path.join(pagesAbs, "*", "index.{tsx,jsx,ts,js}")
3132
+ ],
3108
3133
  include: [
3109
3134
  "react",
3110
3135
  "react-dom",
@@ -3120,15 +3145,15 @@ async function createViteConfig(opts) {
3120
3145
  "class-variance-authority",
3121
3146
  "emoji-picker-react"
3122
3147
  ],
3123
- esbuildOptions: {
3124
- target: "es2022",
3148
+ rolldownOptions: {
3149
+ transform: { target: "es2022" },
3125
3150
  plugins: [{
3126
3151
  name: "open-pages:virtual-externals",
3127
- setup(build$1) {
3128
- build$1.onResolve({ filter: /^virtual:open-pages\// }, (args) => ({
3129
- path: args.path,
3152
+ resolveId(id) {
3153
+ return id.startsWith("virtual:open-pages/") ? {
3154
+ id,
3130
3155
  external: true
3131
- }));
3156
+ } : null;
3132
3157
  }
3133
3158
  }]
3134
3159
  }
@@ -3157,6 +3182,5 @@ async function createViteConfig(opts) {
3157
3182
  }
3158
3183
  };
3159
3184
  }
3160
-
3161
3185
  //#endregion
3162
- export { DEV_SUPERVISED_ENV, RESTART_EXIT_CODE, createViteConfig };
3186
+ export { DEV_SUPERVISED_ENV as n, createViteConfig as t };