@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.
- package/dist/{build-6hM9Ga2d.js → build-CCoxDxL-.js} +11 -10
- package/dist/cli/bin.d.ts +1 -1
- package/dist/cli/bin.js +427 -30
- package/dist/{config-CBbwLHOo.js → config-D_U_aCQz.js} +484 -460
- package/dist/{config-C-sasGou.d.ts → config-p-TWNXbv.d.ts} +4 -4
- package/dist/{design-C13iz9_4.js → design-Cpqx7SJs.js} +1 -2
- package/dist/{dev-BO0cHiYy.js → dev-CTrB263u.js} +40 -20
- package/dist/{export-DbVwSmKW.js → export-enKL9R7x.js} +7 -10
- package/dist/index.d.ts +6 -8
- package/dist/index.js +2 -3
- package/dist/locale/index.d.ts +8 -14
- package/dist/locale/index.js +67 -32
- package/dist/{open-pages-plugin-C0poXLsx.js → open-pages-plugin-C8cFhMB9.js} +23 -38
- package/dist/preview-Dmzyd1cf.js +24 -0
- package/dist/{sync-ui-CDydLpY4.js → sync-ui-BzRiAGCK.js} +3 -5
- package/dist/{types-BzxkFHhd.d.ts → types-DDdUFUIS.d.ts} +19 -6
- package/dist/{update-D1U8TVwd.js → update-CWaCKlnl.js} +4 -7
- package/dist/{update-package-D9zws9nc.js → update-package-GaNsVdr8.js} +44 -22
- package/dist/{version-B6AiiGvT.js → version-DZrXLoAD.js} +19 -3
- package/dist/vite/index.d.ts +3 -7
- package/dist/vite/index.js +2 -7
- package/package.json +22 -21
- package/skills/apply-comments/SKILL.md +1 -0
- package/skills/create-page/SKILL.md +5 -1
- package/skills/create-theme/SKILL.md +2 -0
- package/skills/page-authoring/SKILL.md +3 -0
- package/skills/web-design-guidelines/SKILL.md +41 -0
- package/src/app/app.tsx +19 -14
- package/src/app/assets/autono-social.png +0 -0
- package/src/app/assets/autono.svg +7 -0
- package/src/app/components/asset-view.tsx +510 -26
- package/src/app/components/command/command-menu.tsx +5 -1
- package/src/app/components/command/command.tsx +4 -2
- package/src/app/components/command/home-command-menu.tsx +6 -6
- package/src/app/components/icon-tooltip.tsx +24 -15
- package/src/app/components/sidebar/folder-item.tsx +46 -19
- package/src/app/components/sidebar/icon-picker.tsx +1 -1
- package/src/app/components/sidebar/sidebar-footer.tsx +18 -7
- package/src/app/components/sidebar/sidebar.tsx +91 -64
- package/src/app/components/theme-toggle.tsx +4 -5
- package/src/app/components/themes/theme-detail.tsx +6 -3
- package/src/app/components/themes/themes-gallery.tsx +12 -7
- package/src/app/index.html +33 -2
- package/src/app/lib/assets.ts +33 -0
- package/src/app/lib/last-home-location.ts +18 -0
- package/src/app/main.tsx +10 -1
- package/src/app/routes/home-shell.tsx +82 -61
- package/src/app/routes/home.tsx +42 -39
- package/src/app/routes/page.tsx +30 -10
- package/src/app/routes/themes.tsx +6 -8
- package/src/app/styles.css +40 -4
- package/src/app/virtual.d.ts +1 -0
- package/src/locale/en.ts +17 -6
- package/src/locale/ja.ts +18 -6
- package/src/locale/types.ts +17 -4
- package/src/locale/zh-cn.ts +19 -8
- package/src/locale/zh-tw.ts +17 -6
- package/src/shared/update-package.ts +193 -0
- package/workspace/deps.json +1 -1
- package/dist/export-BsK6OCpc.js +0 -4
- package/dist/preview-sOKhzyWB.js +0 -21
- package/dist/sync-Be_hxSOF.js +0 -139
- package/dist/sync-lKEXyPOh.js +0 -3
- package/dist/version-CeffgdfZ.js +0 -3
- package/src/app/components/language-toggle.tsx +0 -46
- package/src/app/favicon.ico +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
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
|
|
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
|
|
118
|
-
if (!dstDir
|
|
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
|
|
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
|
|
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
|
|
252
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
-
};
|
|
219
|
-
|
|
230
|
+
};
|
|
231
|
+
//#endregion
|
|
232
|
+
export { Plural as n, Locale as t };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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 {
|
|
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 {
|
|
42
|
+
export { version_exports as i, PKG_ROOT as n, readCoreVersion as r, APP_ROOT as t };
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import "../
|
|
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
|
package/dist/vite/index.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
|
|
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.
|
|
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": ">=
|
|
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.
|
|
75
|
-
"@base-ui/react": "1.
|
|
76
|
-
"@fontsource-variable/geist": "^5.
|
|
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": "^
|
|
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.
|
|
85
|
+
"emoji-picker-react": "^4.22.2",
|
|
85
86
|
"fast-glob": "^3.3.2",
|
|
86
|
-
"lucide-react": "^1.
|
|
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.
|
|
91
|
-
"shadcn": "^4.
|
|
92
|
-
"sonner": "^2.0.
|
|
93
|
-
"tailwind-merge": "^3.
|
|
94
|
-
"tailwindcss": "^4.
|
|
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.
|
|
97
|
-
"vite": "^
|
|
97
|
+
"use-sync-external-store": "^1.7.0",
|
|
98
|
+
"vite": "^8.3.0"
|
|
98
99
|
},
|
|
99
100
|
"devDependencies": {
|
|
100
|
-
"@playwright/test": "
|
|
101
|
-
"@types/node": "^
|
|
102
|
-
"@types/react": "^19.
|
|
103
|
-
"@types/react-dom": "^19.
|
|
104
|
-
"tsdown": "^0.
|
|
105
|
-
"typescript": "^
|
|
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.
|