@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,11 +1,10 @@
1
- import fs from "node:fs/promises";
1
+ import { createRequire } from "node:module";
2
2
  import path from "node:path";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import fs from "node:fs/promises";
4
5
  import { loadConfigFromFile, normalizePath } from "vite";
5
6
  import { parse } from "@babel/parser";
6
- import { createRequire } from "node:module";
7
7
  import fg from "fast-glob";
8
-
9
8
  //#region src/editing/page-ops.ts
10
9
  const PAGE_ID_RE = /^[a-z0-9_-]+$/i;
11
10
  function validatePageName(v) {
@@ -45,8 +44,7 @@ function readMetaTitleInSource(source) {
45
44
  for (const property of properties) {
46
45
  if (property.type !== "ObjectProperty" || property.computed) continue;
47
46
  const key = property.key;
48
- const keyName = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? key.value : void 0;
49
- if (keyName !== "title") continue;
47
+ if ((key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? key.value : void 0) !== "title") continue;
50
48
  const value = property.value;
51
49
  if (value?.type === "StringLiteral" && typeof value.value === "string") return {
52
50
  kind: "found",
@@ -54,8 +52,7 @@ function readMetaTitleInSource(source) {
54
52
  };
55
53
  if (value?.type === "TemplateLiteral") {
56
54
  const expressions = value.expressions ?? [];
57
- const quasis = value.quasis ?? [];
58
- const firstValue = quasis[0]?.value;
55
+ const firstValue = (value.quasis ?? [])[0]?.value;
59
56
  const cooked = firstValue?.cooked;
60
57
  const raw = firstValue?.raw;
61
58
  if (expressions.length === 0 && typeof (cooked ?? raw) === "string") return {
@@ -114,14 +111,14 @@ async function duplicatePageDir(pagesRoot, pageId, desiredId) {
114
111
  error: "invalid newId"
115
112
  };
116
113
  newId = desiredId;
117
- const dstDir$1 = path.resolve(root, newId);
118
- if (!dstDir$1.startsWith(root + path.sep)) return {
114
+ const dstDir = path.resolve(root, newId);
115
+ if (!dstDir.startsWith(root + path.sep)) return {
119
116
  ok: false,
120
117
  status: 400,
121
118
  error: "invalid newId"
122
119
  };
123
120
  try {
124
- await fs.access(dstDir$1);
121
+ await fs.access(dstDir);
125
122
  return {
126
123
  ok: false,
127
124
  status: 409,
@@ -156,8 +153,7 @@ async function duplicatePageDir(pagesRoot, pageId, desiredId) {
156
153
  status: 422,
157
154
  error: "could not update copied page title"
158
155
  };
159
- const title = metaTitle.kind === "found" ? metaTitle.title : pageId;
160
- const updated = updateMetaTitleInSource(source, `${title} (copy)`);
156
+ const updated = updateMetaTitleInSource(source, `${metaTitle.kind === "found" ? metaTitle.title : pageId} (copy)`);
161
157
  if (updated === null) return {
162
158
  ok: false,
163
159
  status: 422,
@@ -248,18 +244,14 @@ function updateMetaTitleInSource(source, title) {
248
244
  return source.slice(0, openBrace + 1) + newBody + source.slice(closeBrace);
249
245
  }
250
246
  const firstIndentMatch = body.match(/\n([ \t]+)\S/);
251
- const indent = firstIndentMatch ? firstIndentMatch[1] : " ";
252
- const trimmedBody = body.replace(/^\s*\n?/, "");
253
- const needsSeparator = trimmedBody.trim().length > 0;
254
- const insertion$1 = `\n${indent}title: ${newLiteral}${needsSeparator ? "," : ""}`;
255
- return source.slice(0, openBrace + 1) + insertion$1 + body + source.slice(closeBrace);
247
+ const insertion = `\n${firstIndentMatch ? firstIndentMatch[1] : " "}title: ${newLiteral}${body.replace(/^\s*\n?/, "").trim().length > 0 ? "," : ""}`;
248
+ return source.slice(0, openBrace + 1) + insertion + body + source.slice(closeBrace);
256
249
  }
257
250
  const exportDefaultIdx = source.search(/export\s+default\b/);
258
251
  if (exportDefaultIdx === -1) return null;
259
252
  const insertion = `export const meta: PageMeta = { title: ${newLiteral} };\n\n`;
260
253
  return source.slice(0, exportDefaultIdx) + insertion + source.slice(exportDefaultIdx);
261
254
  }
262
-
263
255
  //#endregion
264
256
  //#region src/vite/open-pages-plugin.ts
265
257
  const CONFIG_FILE = "open-pages.config.ts";
@@ -310,10 +302,9 @@ async function findPages(userCwd, pagesDir) {
310
302
  absolute: true,
311
303
  onlyFiles: true
312
304
  });
313
- const byId = new Map();
305
+ const byId = /* @__PURE__ */ new Map();
314
306
  for (const file of hits.sort()) {
315
- const rel = path.relative(abs, file);
316
- const [id, name] = rel.split(path.sep);
307
+ const [id, name] = path.relative(abs, file).split(path.sep);
317
308
  const kind = REACT_ENTRY_RE.test(name) ? "react" : "html";
318
309
  const existing = byId.get(id);
319
310
  if (!existing || existing.kind === "html" && kind === "react") byId.set(id, {
@@ -362,9 +353,8 @@ function extractMeta(src) {
362
353
  };
363
354
  }
364
355
  function extractHtmlMeta(src) {
365
- const title = src.match(HTML_TITLE_RE)?.[1]?.trim() ?? null;
366
356
  return {
367
- title: title || null,
357
+ title: (src.match(HTML_TITLE_RE)?.[1]?.trim() ?? null) || null,
368
358
  theme: null,
369
359
  createdAt: null
370
360
  };
@@ -382,7 +372,7 @@ function parseCreatedAtMs(iso) {
382
372
  const ms = Date.parse(iso);
383
373
  return Number.isFinite(ms) ? ms : null;
384
374
  }
385
- const warnedInvalidPageIds = new Set();
375
+ const warnedInvalidPageIds = /* @__PURE__ */ new Set();
386
376
  async function generatePagesModule(entries, isDev) {
387
377
  const scanned = await Promise.all(entries.map(async (entry) => {
388
378
  const importPath = isDev ? `@fs/${normalizePath(entry.file).replace(/^\/+/, "")}` : entry.file;
@@ -425,7 +415,8 @@ if (import.meta.hot) {
425
415
  const importExpr = isDev ? `import(/* @vite-ignore */ import.meta.env.BASE_URL + ${JSON.stringify(`${e.importPath}?t=`)} + pageImportTokens[${JSON.stringify(e.id)}])` : `import(${JSON.stringify(e.importPath)})`;
426
416
  return ` case ${JSON.stringify(e.id)}: return ${importExpr};`;
427
417
  }).join("\n");
428
- const code = `// virtual:open-pages/pages — generated
418
+ return {
419
+ code: `// virtual:open-pages/pages — generated
429
420
  export const pageIds = ${JSON.stringify(valid.map((e) => e.id))};
430
421
  export const pageKinds = ${JSON.stringify(kinds)};
431
422
  export const pageThemes = ${JSON.stringify(themesMap)};
@@ -438,9 +429,7 @@ ${cases}
438
429
  default: throw new Error('Page not found: ' + id);
439
430
  }
440
431
  }
441
- `;
442
- return {
443
- code,
432
+ `,
444
433
  ignored
445
434
  };
446
435
  }
@@ -510,7 +499,7 @@ function openPagesPlugin(opts) {
510
499
  return null;
511
500
  };
512
501
  let pageChangeTimer = null;
513
- const pendingPageChanges = new Set();
502
+ const pendingPageChanges = /* @__PURE__ */ new Set();
514
503
  const queuePageChanged = (server, id) => {
515
504
  pendingPageChanges.add(id);
516
505
  if (pageChangeTimer) clearTimeout(pageChangeTimer);
@@ -592,8 +581,7 @@ function openPagesPlugin(opts) {
592
581
  reloadTimer = setTimeout(async () => {
593
582
  reloadTimer = null;
594
583
  try {
595
- const entries = await findPages(userCwd, pagesDir);
596
- if (pagesSignature(entries) === generatedSignature) return;
584
+ if (pagesSignature(await findPages(userCwd, pagesDir)) === generatedSignature) return;
597
585
  } catch (err) {
598
586
  server.config.logger.error(`[open-pages] failed to rescan pages: ${err}`, { error: err instanceof Error ? err : void 0 });
599
587
  }
@@ -638,8 +626,7 @@ function openPagesPlugin(opts) {
638
626
  if (p === foldersManifestPath) invalidateFolders();
639
627
  });
640
628
  server.middlewares.use("/__page", async (req, res, next) => {
641
- const url = new URL(req.url ?? "/", "http://local");
642
- const m = url.pathname.match(/^\/([^/]+)\/(?:index\.html)?$/);
629
+ const m = new URL(req.url ?? "/", "http://local").pathname.match(/^\/([^/]+)\/(?:index\.html)?$/);
643
630
  if (!m || (req.method ?? "GET") !== "GET") return next();
644
631
  const pageId = decodeURIComponent(m[1]);
645
632
  if (!PAGE_ID_RE.test(pageId)) return next();
@@ -666,12 +653,10 @@ function openPagesPlugin(opts) {
666
653
  async function loadUserConfig(userCwd) {
667
654
  const file = path.join(userCwd, CONFIG_FILE);
668
655
  if (!existsSync(file)) return {};
669
- const loaded = await loadConfigFromFile({
656
+ return (await loadConfigFromFile({
670
657
  command: "serve",
671
658
  mode: "development"
672
- }, file, userCwd, "silent");
673
- return loaded?.config ?? {};
659
+ }, file, userCwd, "silent"))?.config ?? {};
674
660
  }
675
-
676
661
  //#endregion
677
- export { PAGE_ID_RE, duplicatePageDir, extractMeta, findPages, invalidatePagesModule, loadUserConfig, openPagesPlugin, resolvePageEntry, rmPageDir, updateMetaTitleInSource, validatePageName };
662
+ export { openPagesPlugin as a, resolvePageEntry as c, validatePageName as d, loadUserConfig as i, rmPageDir as l, findPages as n, PAGE_ID_RE as o, invalidatePagesModule as r, duplicatePageDir as s, extractMeta as t, updateMetaTitleInSource as u };
@@ -0,0 +1,24 @@
1
+ import { a as printUrls, i as printShortcutsHint, o as shortcutsEnabled, r as printHeader, s as startupDuration, t as createCliLogger } from "./cli/bin.js";
2
+ import { t as createViteConfig } from "./config-D_U_aCQz.js";
3
+ import { mergeConfig, preview as preview$1 } from "vite";
4
+ //#region src/cli/preview.ts
5
+ async function preview(opts = {}) {
6
+ const base = await createViteConfig({ userCwd: process.cwd() });
7
+ const config = mergeConfig(base, {
8
+ customLogger: createCliLogger(),
9
+ preview: {
10
+ ...opts.port !== void 0 ? { port: opts.port } : {},
11
+ ...opts.host !== void 0 ? { host: opts.host } : {},
12
+ ...opts.open !== void 0 ? { open: opts.open } : {}
13
+ }
14
+ });
15
+ const server = await preview$1(config);
16
+ printHeader(`preview · ${startupDuration()}`);
17
+ printUrls(server.resolvedUrls, opts.host);
18
+ if (shortcutsEnabled()) {
19
+ printShortcutsHint();
20
+ server.bindCLIShortcuts();
21
+ }
22
+ }
23
+ //#endregion
24
+ export { preview };
@@ -1,9 +1,8 @@
1
+ import path from "node:path";
1
2
  import chalk from "chalk";
3
+ import { existsSync } from "node:fs";
2
4
  import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
- import path from "node:path";
4
5
  import { createHash } from "node:crypto";
5
- import { existsSync } from "node:fs";
6
-
7
6
  //#region src/cli/sync-ui.ts
8
7
  const UI_MANIFEST = path.join("ui", ".sync-manifest.json");
9
8
  const ENTRIES = [
@@ -129,6 +128,5 @@ function printUiSummary(result, dryRun) {
129
128
  if (result.skipped.length > 0 && result.forced.length === 0) process.stdout.write(chalk.dim("Skipped files differ from what was scaffolded. Re-run with --force to overwrite them.\n"));
130
129
  if (result.addedDeps.length > 0) process.stdout.write(`${chalk.yellow("!")} Added ${result.addedDeps.map((d) => chalk.bold(d)).join(", ")} to package.json — run your package manager's install.\n`);
131
130
  }
132
-
133
131
  //#endregion
134
- export { printUiSummary, syncUi };
132
+ export { printUiSummary, syncUi };
@@ -124,6 +124,7 @@ type Locale = {
124
124
  noMatchingAssetsHint: string;
125
125
  clearFilters: string;
126
126
  searchLogos: string;
127
+ searchFonts: string;
127
128
  upload: string;
128
129
  dropToUpload: string;
129
130
  loading: string;
@@ -161,6 +162,20 @@ type Locale = {
161
162
  logoSearchEmptyHintSuffix: string;
162
163
  logoVariantLight: string;
163
164
  logoVariantDark: string;
165
+ fontSearchTitle: string;
166
+ fontSearchPoweredByPrefix: string;
167
+ fontSearchPlaceholder: string;
168
+ fontSearchPreviewPlaceholder: string;
169
+ fontSearchErrorTitle: string;
170
+ fontSearchErrorBody: string;
171
+ /** template: 'No fonts for "{query}"' */
172
+ fontSearchNoResults: string;
173
+ fontSearchEmpty: string;
174
+ fontSearchEmptyHintPrefix: string;
175
+ fontSearchEmptyHintSuffix: string;
176
+ fontWeightLabel: string;
177
+ fontPreviewSample: string;
178
+ /** template: "Upload failed ({status})" */
164
179
  toastUploadFailed: string;
165
180
  toastReplaced: string;
166
181
  toastUploadedAs: string;
@@ -170,6 +185,7 @@ type Locale = {
170
185
  toastDeleteFailed: string;
171
186
  toastDeleted: string;
172
187
  toastDownloadFailed: string;
188
+ toastFontDownloadFailed: string;
173
189
  toastSearchFailed: string;
174
190
  };
175
191
  commandMenu: {
@@ -196,10 +212,6 @@ type Locale = {
196
212
  dark: string;
197
213
  system: string;
198
214
  };
199
- languageToggle: {
200
- toggleAria: string;
201
- title: string;
202
- };
203
215
  themes: {
204
216
  title: string;
205
217
  noThemesTitle: string;
@@ -215,5 +227,6 @@ type Locale = {
215
227
  expandPromptAria: string;
216
228
  collapsePromptAria: string;
217
229
  };
218
- }; //#endregion
219
- export { Locale, Plural };
230
+ };
231
+ //#endregion
232
+ export { Plural as n, Locale as t };
@@ -1,8 +1,7 @@
1
- import { PKG, detectPackageManager, fetchLatest, formatCommand, installCommandFor, isOutdated, localOpenPagesCommand, runCommand, updateCommandFor } from "./update-package-D9zws9nc.js";
1
+ import { a as installCommandFor, c as localOpenPagesCommand, i as formatCommand, l as runCommand, n as detectPackageManager, r as fetchLatest, s as isOutdated, t as PKG, u as updateCommandFor } from "./update-package-GaNsVdr8.js";
2
+ import path from "node:path";
2
3
  import chalk from "chalk";
3
4
  import { readFile } from "node:fs/promises";
4
- import path from "node:path";
5
-
6
5
  //#region src/cli/update.ts
7
6
  async function installedVersion(cwd) {
8
7
  try {
@@ -35,8 +34,7 @@ async function update(opts) {
35
34
  const pkgFile = path.join(cwd, "package.json");
36
35
  const before = await readFile(pkgFile, "utf8").catch(() => "");
37
36
  await runCommand(localOpenPagesCommand(cwd, ["sync:ui"]), cwd, { stdio: "inherit" });
38
- const after = await readFile(pkgFile, "utf8").catch(() => "");
39
- if (after !== before) {
37
+ if (await readFile(pkgFile, "utf8").catch(() => "") !== before) {
40
38
  const reinstall = installCommandFor(packageManager);
41
39
  process.stdout.write(chalk.dim(`$ ${formatCommand(reinstall)}\n`));
42
40
  await runCommand(reinstall, cwd, { stdio: "inherit" });
@@ -44,6 +42,5 @@ async function update(opts) {
44
42
  }
45
43
  process.stdout.write(`${chalk.green("✓")} Updated ${PKG} to ${target}. Restart \`open-pages dev\` to use it.\n`);
46
44
  }
47
-
48
45
  //#endregion
49
- export { update };
46
+ export { update };
@@ -1,10 +1,9 @@
1
- import fs from "node:fs/promises";
2
1
  import path from "node:path";
3
2
  import { spawn } from "node:child_process";
4
-
3
+ import fs from "node:fs/promises";
5
4
  //#region src/shared/update-package.ts
6
5
  const PKG = "@autono/open-pages";
7
- const CACHE_TTL_MS = 10 * 60 * 1e3;
6
+ const CACHE_TTL_MS = 6e5;
8
7
  const COMMAND_TIMEOUT_MS = 3e5;
9
8
  let cache = null;
10
9
  function parseSemver(v) {
@@ -57,12 +56,39 @@ async function fileExists(file) {
57
56
  return false;
58
57
  }
59
58
  }
59
+ const LOCKFILES = [
60
+ ["pnpm-lock.yaml", "pnpm"],
61
+ ["yarn.lock", "yarn"],
62
+ ["bun.lockb", "bun"],
63
+ ["bun.lock", "bun"],
64
+ ["package-lock.json", "npm"]
65
+ ];
66
+ const PROJECT_ROOT_MARKERS = ["pnpm-workspace.yaml", ".git"];
67
+ async function declaredPackageManager(dir) {
68
+ try {
69
+ const raw = await fs.readFile(path.join(dir, "package.json"), "utf8");
70
+ const declared = JSON.parse(raw).packageManager;
71
+ if (typeof declared !== "string") return null;
72
+ const name = declared.split("@")[0];
73
+ return name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" ? name : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ async function isProjectRoot(dir) {
79
+ for (const marker of PROJECT_ROOT_MARKERS) if (await fileExists(path.join(dir, marker))) return true;
80
+ return false;
81
+ }
60
82
  async function detectPackageManager(cwd) {
61
- if (await fileExists(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
62
- if (await fileExists(path.join(cwd, "yarn.lock"))) return "yarn";
63
- if (await fileExists(path.join(cwd, "bun.lockb"))) return "bun";
64
- if (await fileExists(path.join(cwd, "bun.lock"))) return "bun";
65
- if (await fileExists(path.join(cwd, "package-lock.json"))) return "npm";
83
+ let dir = cwd;
84
+ while (true) {
85
+ const declared = await declaredPackageManager(dir);
86
+ if (declared) return declared;
87
+ for (const [lockfile, pm] of LOCKFILES) if (await fileExists(path.join(dir, lockfile))) return pm;
88
+ const parent = path.dirname(dir);
89
+ if (parent === dir || await isProjectRoot(dir)) break;
90
+ dir = parent;
91
+ }
66
92
  const ua = process.env.npm_config_user_agent ?? "";
67
93
  if (ua.startsWith("pnpm")) return "pnpm";
68
94
  if (ua.startsWith("yarn")) return "yarn";
@@ -71,17 +97,14 @@ async function detectPackageManager(cwd) {
71
97
  }
72
98
  async function updateCommandFor(packageManager, cwd) {
73
99
  switch (packageManager) {
74
- case "pnpm": {
75
- const workspaceRoot = await fileExists(path.join(cwd, "pnpm-workspace.yaml"));
76
- return {
77
- cmd: "pnpm",
78
- args: workspaceRoot ? [
79
- "add",
80
- "--workspace-root",
81
- `${PKG}@latest`
82
- ] : ["add", `${PKG}@latest`]
83
- };
84
- }
100
+ case "pnpm": return {
101
+ cmd: "pnpm",
102
+ args: await fileExists(path.join(cwd, "pnpm-workspace.yaml")) ? [
103
+ "add",
104
+ "--workspace-root",
105
+ `${PKG}@latest`
106
+ ] : ["add", `${PKG}@latest`]
107
+ };
85
108
  case "yarn": return {
86
109
  cmd: "yarn",
87
110
  args: ["add", `${PKG}@latest`]
@@ -132,7 +155,7 @@ async function runCommand(spec, cwd, opts = {}) {
132
155
  let stderr = "";
133
156
  const timer = setTimeout(() => {
134
157
  child.kill();
135
- reject(new Error(`${formatCommand(spec)} timed out`));
158
+ reject(/* @__PURE__ */ new Error(`${formatCommand(spec)} timed out`));
136
159
  }, COMMAND_TIMEOUT_MS);
137
160
  child.stderr?.on("data", (chunk) => {
138
161
  stderr += chunk.toString("utf8");
@@ -153,6 +176,5 @@ async function runCommand(spec, cwd, opts = {}) {
153
176
  });
154
177
  });
155
178
  }
156
-
157
179
  //#endregion
158
- export { PKG, detectPackageManager, fetchLatest, formatCommand, installCommandFor, invalidateLatestCache, isOutdated, localOpenPagesCommand, runCommand, updateCommandFor };
180
+ export { installCommandFor as a, localOpenPagesCommand as c, formatCommand as i, runCommand as l, detectPackageManager as n, invalidateLatestCache as o, fetchLatest as r, isOutdated as s, PKG as t, updateCommandFor as u };
@@ -1,8 +1,25 @@
1
+ import "node:module";
1
2
  import path from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
-
5
+ //#region \0rolldown/runtime.js
6
+ var __defProp = Object.defineProperty;
7
+ var __exportAll = (all, no_symbols) => {
8
+ let target = {};
9
+ for (var name in all) __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true
12
+ });
13
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
14
+ return target;
15
+ };
16
+ //#endregion
5
17
  //#region src/vite/version.ts
18
+ var version_exports = /* @__PURE__ */ __exportAll({
19
+ APP_ROOT: () => APP_ROOT,
20
+ PKG_ROOT: () => PKG_ROOT,
21
+ readCoreVersion: () => readCoreVersion
22
+ });
6
23
  function findPackageRoot(fromFile) {
7
24
  let dir = path.dirname(fromFile);
8
25
  while (dir !== path.dirname(dir)) {
@@ -21,6 +38,5 @@ function readCoreVersion() {
21
38
  return "0.0.0";
22
39
  }
23
40
  }
24
-
25
41
  //#endregion
26
- export { APP_ROOT, PKG_ROOT, readCoreVersion };
42
+ export { version_exports as i, PKG_ROOT as n, readCoreVersion as r, APP_ROOT as t };
@@ -1,14 +1,10 @@
1
- import "../types-BzxkFHhd.js";
2
- import { OpenPagesConfig } from "../config-C-sasGou.js";
1
+ import { t as OpenPagesConfig } from "../config-p-TWNXbv.js";
3
2
  import { InlineConfig } from "vite";
4
-
5
3
  //#region src/vite/config.d.ts
6
4
  type CreateViteConfigOptions = {
7
5
  userCwd: string;
8
6
  config?: OpenPagesConfig;
9
7
  mode?: 'serve' | 'build';
10
8
  };
11
- declare function createViteConfig(opts: CreateViteConfigOptions): Promise<InlineConfig>;
12
-
13
- //#endregion
14
- export { createViteConfig };
9
+ export declare function createViteConfig(opts: CreateViteConfigOptions): Promise<InlineConfig>;
10
+ //#endregion
@@ -1,7 +1,2 @@
1
- import "../design-C13iz9_4.js";
2
- import "../open-pages-plugin-C0poXLsx.js";
3
- import { createViteConfig } from "../config-CBbwLHOo.js";
4
- import "../update-package-D9zws9nc.js";
5
- import "../version-B6AiiGvT.js";
6
-
7
- export { createViteConfig };
1
+ import { t as createViteConfig } from "../config-D_U_aCQz.js";
2
+ export { createViteConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autono/open-pages",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Runtime and CLI for open-pages — live preview of React and HTML pages, click-to-comment inspector, and static export.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,6 +29,7 @@
29
29
  "env.d.ts",
30
30
  "src/app",
31
31
  "src/locale",
32
+ "src/shared",
32
33
  "skills",
33
34
  "README.md",
34
35
  "workspace"
@@ -40,7 +41,7 @@
40
41
  "prepack": "pnpm build"
41
42
  },
42
43
  "engines": {
43
- "node": ">=18"
44
+ "node": "^20.19.0 || >=22.12.0"
44
45
  },
45
46
  "keywords": [
46
47
  "pages",
@@ -71,37 +72,37 @@
71
72
  },
72
73
  "dependencies": {
73
74
  "@babel/parser": "^8.0.4",
74
- "@babel/types": "^8.0.4",
75
- "@base-ui/react": "1.6.0",
76
- "@fontsource-variable/geist": "^5.2.8",
75
+ "@babel/types": "^8.0.6",
76
+ "@base-ui/react": "1.8.0",
77
+ "@fontsource-variable/geist": "^5.3.0",
77
78
  "@tailwindcss/vite": "^4.3.3",
78
- "@vitejs/plugin-react": "^4.3.3",
79
+ "@vitejs/plugin-react": "^6.1.1",
79
80
  "chalk": "^6.0.0",
80
81
  "class-variance-authority": "^0.7.1",
81
82
  "clsx": "^2.1.1",
82
83
  "cmdk": "^1.1.1",
83
84
  "commander": "^15.0.0",
84
- "emoji-picker-react": "^4.19.1",
85
+ "emoji-picker-react": "^4.22.2",
85
86
  "fast-glob": "^3.3.2",
86
- "lucide-react": "^1.25.0",
87
+ "lucide-react": "^1.47.0",
87
88
  "next-themes": "^0.4.6",
88
89
  "react": "^19.2.0",
89
90
  "react-dom": "^19.2.0",
90
- "react-router-dom": "^7.18.1",
91
- "shadcn": "^4.19.0",
92
- "sonner": "^2.0.7",
93
- "tailwind-merge": "^3.5.0",
94
- "tailwindcss": "^4.2.2",
91
+ "react-router-dom": "^7.18.4",
92
+ "shadcn": "^4.21.0",
93
+ "sonner": "^2.0.8",
94
+ "tailwind-merge": "^3.7.0",
95
+ "tailwindcss": "^4.3.3",
95
96
  "tw-animate-css": "^1.4.0",
96
- "use-sync-external-store": "^1.6.0",
97
- "vite": "^6.4.3"
97
+ "use-sync-external-store": "^1.7.0",
98
+ "vite": "^8.3.0"
98
99
  },
99
100
  "devDependencies": {
100
- "@playwright/test": "~1.62.1",
101
- "@types/node": "^22.19.17",
102
- "@types/react": "^19.2.0",
103
- "@types/react-dom": "^19.2.0",
104
- "tsdown": "^0.9.9",
105
- "typescript": "^5.9.3"
101
+ "@playwright/test": "1.63.0",
102
+ "@types/node": "^26.6.2",
103
+ "@types/react": "^19.3.0",
104
+ "@types/react-dom": "^19.3.0",
105
+ "tsdown": "^0.23.0",
106
+ "typescript": "^7.0.2"
106
107
  }
107
108
  }
@@ -56,6 +56,7 @@ Your job: read those markers, perform the described edits, and delete the marker
56
56
  - After all edits, re-read the file and confirm the only remaining markers are ones you reported as skipped.
57
57
  - Confirm the edited JSX is well-formed (balanced tags, no dangling attributes) and that changed `className` strings are literal Tailwind utilities. If the project's `package.json` has typecheck/lint scripts, run them with the project's package manager; scaffolded projects ship neither TypeScript nor a linter — there, rely on the running dev server (or the `build` script) to surface compile errors. Fix any errors you introduced.
58
58
  - For layout changes, mentally check the Mobile viewport (390px): did the edit introduce a fixed width or a grid with no stacking fallback?
59
+ - When a comment changed interactive elements, forms, motion, or layout, run the `web-design-guidelines` skill on the page and fix any regression it reports before you report.
59
60
 
60
61
  7. **Report.**
61
62
  - Summarise: `N applied, M skipped` plus a one-line description of each change (including the page id).
@@ -94,7 +94,11 @@ Read the **`page-authoring`** skill before writing — file contract, `@/ui/*` c
94
94
 
95
95
  ## Step 7 — Self-review
96
96
 
97
- Run the checklist in `page-authoring` ("Self-review before finishing"): every button/input/card/dialog/tab is a `@/ui` component, colors are tokens, nothing under `ui/` changed. Check all three viewports.
97
+ Run the checklist in `page-authoring` ("Self-review before finishing"): every button/input/card/dialog/tab is a `@/ui` component, colors are tokens, nothing under `ui/` changed. Check all three viewports. Pay particular attention to the CTA and motion items: one label per intent, no wrapped button text at desktop, every animation answers "what does this communicate?".
98
+
99
+ ## Step 7b — Guidelines review
100
+
101
+ Run the `web-design-guidelines` skill on `pages/<id>/` (it fetches Vercel's current Web Interface Guidelines and reports `file:line` findings). Fix what it finds in the page files, re-run until clean or until only findings you can justify remain, and mention any you left in the hand-off.
98
102
 
99
103
  ## Step 8 — Hand off to the user
100
104
 
@@ -25,6 +25,7 @@ A theme can be derived from any combination of:
25
25
  - **Image references / brand guidelines** — paths or URLs to screenshots, mood boards, logo files, a brand PDF. You will translate them into OKLCH tokens.
26
26
  - **Free-text description** — prose describing the desired palette, weight, feel.
27
27
  - **An existing page** — `pages/<id>/index.tsx` whose look should become reusable.
28
+ - **A `DESIGN.md`** — a design-system document in the Google-spec shape (YAML frontmatter, then Overview, Colors, Typography, Layout, Elevation, Shapes, Components, Do's and Don'ts). [designmd.supply](https://designmd.supply) generates one from any public domain, so "match our brand" is: run the site through it, drop the file at `themes/<id>.design.md` (or paste the path), and ask for a theme.
28
29
 
29
30
  If the user's original message already specifies the inputs unambiguously, skip the question and proceed. Otherwise call `AskUserQuestion` (multi-select) so they can pick one or more sources, and ask follow-ups (paths, preset code, page id, prose) only as needed.
30
31
 
@@ -34,6 +35,7 @@ If the user's original message already specifies the inputs unambiguously, skip
34
35
  - **Images**: read each path with the `Read` tool (it accepts images). Note dominant colors (write them as hex, then convert to OKLCH), type family feel, corner radius, surface treatment (flat vs. cards vs. borders), light or dark default, and chrome (nav style, footer).
35
36
  - **Text**: extract explicit values (hex codes, font names, "rounded", "sharp", "dense") and resolve vague language into concrete decisions before writing.
36
37
  - **Existing page**: read `pages/<id>/index.tsx` (and `components/`) and pull any raw palette classes or hex values into token roles (the page's `bg-[#0b0b10]` root → `--background`; its CTA fill → `--primary`; its card border → `--border`), plus fonts and radius.
38
+ - **`DESIGN.md`**: read it and map sections onto tokens — **Colors** gives `--background`/`--foreground`, `--primary`, `--secondary`, `--accent`, `--muted`, `--destructive` (primary = the brand's main action color, not its logo color, when they differ); **Typography** gives `--font-sans` / `--font-heading` and the type-scale feel; **Shapes** gives `--radius`; **Elevation** decides flat vs. card-with-shadow treatment in the component notes; **Layout** informs container widths and density in the demo; **Components** and **Do's and Don'ts** become the "Components in this theme" guidance. Colors may arrive as hex, `rgb()`, `hsl()`, `oklch()`, or CSS named colors — parse each by its own syntax, then convert to OKLCH. A single light palette is normal; derive the `.dark` block from it (invert lightness, keep hue and chroma) and say so in the `.md`.
37
39
 
38
40
  Every color ends up as `oklch(L C H)` — the same format as `styles/globals.css`. When inputs disagree (images use blue but the description says green), ask the user which to honor.
39
41
 
@@ -187,7 +187,10 @@ A theme is `themes/<id>.md` (direction and component notes) + `themes/<id>.css`
187
187
  - [ ] One coherent type scale across the page; contrast holds on dark sections.
188
188
  - [ ] Designed repeats are explicit component instances; data lists are a `.map` over a typed const.
189
189
  - [ ] No `window`/`document` access at module top level; effects clean up.
190
+ - [ ] One label per CTA intent across the page ("Get started" in the nav, hero, and footer — not "Get started" / "Sign up free" / "Try it"), and no primary button label wraps at desktop width.
191
+ - [ ] Every animation is motivated: it shows hierarchy, sequence, feedback, or a state change. If you cannot say which in one sentence, remove it. `motion-safe:` / `motion-reduce:` variants respect the user's preference.
190
192
  - [ ] Nothing outside `pages/<id>/` was edited.
193
+ - [ ] Ran the `web-design-guidelines` skill on the page and resolved or justified its findings.
191
194
 
192
195
  ## Anti-patterns
193
196
 
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: web-design-guidelines
3
+ description: Review page code against Vercel's Web Interface Guidelines — accessibility, focus and keyboard handling, forms, motion, layout, typography, and performance. Use when asked to "review my page", "check accessibility", "audit the design", "review UX", or as the review pass at the end of `create-page` and `apply-comments`.
4
+ metadata:
5
+ author: vercel
6
+ version: "1.0.0"
7
+ argument-hint: <page-id or file-or-pattern>
8
+ ---
9
+
10
+ # Web Interface Guidelines
11
+
12
+ Review page files for compliance with Vercel's [Web Interface Guidelines](https://github.com/vercel-labs/web-interface-guidelines) (MIT). Vendored from the [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) `web-design-guidelines` skill and scoped to an open-pages workspace.
13
+
14
+ ## How it works
15
+
16
+ 1. Fetch the latest guidelines from the source URL below.
17
+ 2. Read the files to review.
18
+ 3. Check them against every rule in the fetched guidelines.
19
+ 4. Report findings in the terse `file:line` format the guidelines specify.
20
+
21
+ ## Guidelines source
22
+
23
+ Fetch fresh guidelines before each review:
24
+
25
+ ```
26
+ https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
27
+ ```
28
+
29
+ Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and the output format. If the fetch fails (offline, blocked), say so and fall back to the self-review checklist in the `page-authoring` skill instead of guessing at rules.
30
+
31
+ ## What to review
32
+
33
+ - Given a page id, review the page's entry (`pages/<id>/index.tsx` or `pages/<id>/index.html`), everything under `pages/<id>/components/`, and any `styles.css`, `style.css`, or `main.js` beside the entry.
34
+ - Given a file or glob, review those files.
35
+ - Given nothing, resolve the current page with the `current-page` skill; if that yields nothing, ask which page to review.
36
+
37
+ Never review or report on files under `ui/`, `lib/`, `hooks/`, or `styles/`. Those are the shared shadcn set and are not edited for one page; a finding there is a `create-theme` or upstream matter, not a page fix.
38
+
39
+ ## Applying findings
40
+
41
+ Report first. When the user asks you to fix, or the review runs inside `create-page` or `apply-comments`, apply the fixes to the page files only, following the `page-authoring` skill: keep `@/ui/*` components, keep semantic tokens, keep the type scale. Do not add dependencies to satisfy a rule.