@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,6 +1,6 @@
1
1
  import { useNavigate, useParams } from 'react-router-dom';
2
2
  import { useLocale } from '~/lib/use-locale';
3
- import { FolderIconChip } from '../components/sidebar/folder-item';
3
+ import { SystemViewIcon } from '../components/sidebar/folder-item';
4
4
  import { ThemeDetail } from '../components/themes/theme-detail';
5
5
  import { ThemesGallery } from '../components/themes/themes-gallery';
6
6
  import { themes as themeRegistry } from '../lib/themes';
@@ -10,15 +10,13 @@ export function ThemesGalleryPage() {
10
10
  const t = useLocale();
11
11
  return (
12
12
  <>
13
- <header className="mb-8 md:mb-12">
14
- <div className="flex flex-wrap items-center gap-3">
15
- <FolderIconChip icon={{ type: 'emoji', value: '🎨' }} className="size-7 text-2xl" />
16
- <h1 className="font-heading text-[32px] font-semibold leading-[1.05] tracking-[-0.025em] md:text-[44px]">
13
+ <header className="mb-6 md:mb-8">
14
+ <div className="flex flex-wrap items-center gap-2.5">
15
+ <SystemViewIcon kind="themes" className="text-muted-foreground" />
16
+ <h1 className="font-heading text-[19px] font-semibold leading-none tracking-[-0.015em] md:text-[21px]">
17
17
  {t.themes.title}
18
18
  </h1>
19
- <span className="folio ml-1 self-end pb-2">
20
- {themeRegistry.length.toString().padStart(2, '0')}
21
- </span>
19
+ <span className="folio ml-0.5">{themeRegistry.length.toString().padStart(2, '0')}</span>
22
20
  </div>
23
21
  </header>
24
22
  <ThemesGallery onOpen={(id) => navigate(`/themes/${encodeURIComponent(id)}`)} />
@@ -58,6 +58,10 @@
58
58
  --radius-3xl: calc(var(--radius) * 2.2);
59
59
  --radius-4xl: calc(var(--radius) * 2.6);
60
60
 
61
+ /* House motion curve — one strong ease-out for every chrome entrance,
62
+ instead of per-file cubic-bezier literals. */
63
+ --ease-swift: cubic-bezier(0.22, 1, 0.36, 1);
64
+
61
65
  --shadow-edge: 0 0 0 0.5px oklch(0 0 0 / 0.06), 0 1px 0 oklch(0 0 0 / 0.025);
62
66
  --shadow-floating:
63
67
  0 0 0 0.5px oklch(0 0 0 / 0.08), 0 1px 1px oklch(0 0 0 / 0.04),
@@ -68,6 +72,9 @@
68
72
  }
69
73
 
70
74
  :root {
75
+ /* Runtime copy of the @theme token for inline style/JS consumers. */
76
+ --ease-swift: cubic-bezier(0.22, 1, 0.36, 1);
77
+
71
78
  /* Pure neutral ramp — zero chroma, flat surfaces, hairline borders.
72
79
  Near-white background with white cards floating on a slightly
73
80
  recessed canvas. */
@@ -110,7 +117,9 @@
110
117
  /* Tight, considered radius — drops the trendy 10px squish for 6px crisp. */
111
118
  --radius: 0.375rem;
112
119
 
113
- --sidebar: oklch(0.98 0 0);
120
+ /* Chrome ground — matches --canvas so sidebar, rails, and panels sit on
121
+ one continuous surface; content floats above it as elevated cards. */
122
+ --sidebar: oklch(0.975 0 0);
114
123
  --sidebar-foreground: oklch(0.185 0 0);
115
124
  --sidebar-primary: oklch(0.185 0 0);
116
125
  --sidebar-primary-foreground: oklch(0.985 0 0);
@@ -121,9 +130,11 @@
121
130
  }
122
131
 
123
132
  .dark {
124
- --background: oklch(0.145 0 0);
133
+ /* Lifted well above the near-black chrome ground (--sidebar 0.1) so the
134
+ elevated content cards read at a glance on dim displays. */
135
+ --background: oklch(0.175 0 0);
125
136
  --foreground: oklch(0.93 0 0);
126
- --canvas: oklch(0.125 0 0);
137
+ --canvas: oklch(0.1 0 0);
127
138
 
128
139
  --card: oklch(0.19 0 0);
129
140
  --card-foreground: oklch(0.93 0 0);
@@ -155,7 +166,7 @@
155
166
  --chart-4: oklch(0.7 0.13 160);
156
167
  --chart-5: oklch(0.65 0 0);
157
168
 
158
- --sidebar: oklch(0.17 0 0);
169
+ --sidebar: oklch(0.1 0 0);
159
170
  --sidebar-foreground: oklch(0.93 0 0);
160
171
  --sidebar-primary: oklch(0.925 0 0);
161
172
  --sidebar-primary-foreground: oklch(0.17 0 0);
@@ -225,6 +236,31 @@
225
236
  }
226
237
  }
227
238
 
239
+ /*
240
+ * Grid entrance — a quiet rise used by card grids. Pair with a small
241
+ * per-item animation-delay for a capped stagger.
242
+ */
243
+ @keyframes osd-rise-in {
244
+ from {
245
+ opacity: 0;
246
+ transform: translateY(4px);
247
+ }
248
+ to {
249
+ opacity: 1;
250
+ transform: none;
251
+ }
252
+ }
253
+
254
+ .rise-in {
255
+ animation: osd-rise-in 350ms var(--ease-swift) both;
256
+ }
257
+
258
+ @media (prefers-reduced-motion: reduce) {
259
+ .rise-in {
260
+ animation: none;
261
+ }
262
+ }
263
+
228
264
  /*
229
265
  * Comment input cue — kept from the original design but retoned to match
230
266
  * the new palette (warm vermillion + amber instead of the AI-classic
@@ -53,4 +53,5 @@ declare module 'virtual:open-pages/themes' {
53
53
  default: PageComponent;
54
54
  design?: DesignSystem;
55
55
  }>;
56
+ export function demoImportUrl(id: string): string;
56
57
  }
package/src/locale/en.ts CHANGED
@@ -18,7 +18,7 @@ export const en: Locale = {
18
18
  title: 'Page not found',
19
19
  },
20
20
  home: {
21
- appTitle: 'open-pages',
21
+ appTitle: 'Autono',
22
22
  draft: 'Draft',
23
23
  duplicate: 'Duplicate',
24
24
  themes: 'Themes',
@@ -28,7 +28,7 @@ export const en: Locale = {
28
28
  menu: 'Menu',
29
29
  newFolder: 'New folder',
30
30
  folderName: 'Folder name',
31
- updateAvailable: 'open-pages {version} is available',
31
+ updateAvailable: 'Autono {version} is available',
32
32
  updatePackage: 'Update',
33
33
  updatingPackage: 'Updating',
34
34
  updatePackageDone: 'Updated. Restart the dev server to use the new version.',
@@ -124,6 +124,7 @@ export const en: Locale = {
124
124
  noMatchingAssetsHint: 'Try adjusting your search or filters.',
125
125
  clearFilters: 'Clear filters',
126
126
  searchLogos: 'Search logos',
127
+ searchFonts: 'Search fonts',
127
128
  upload: 'Upload',
128
129
  dropToUpload: 'Drop to upload',
129
130
  loading: 'Loading…',
@@ -161,6 +162,18 @@ export const en: Locale = {
161
162
  logoSearchEmptyHintSuffix: '.',
162
163
  logoVariantLight: 'Light',
163
164
  logoVariantDark: 'Dark',
165
+ fontSearchTitle: 'Search Google Fonts',
166
+ fontSearchPoweredByPrefix: 'Powered by ',
167
+ fontSearchPlaceholder: 'Search font families…',
168
+ fontSearchPreviewPlaceholder: 'Preview text',
169
+ fontSearchErrorTitle: "Couldn't reach Google Fonts",
170
+ fontSearchErrorBody: 'Check your connection and try again.',
171
+ fontSearchNoResults: 'No fonts for "{query}"',
172
+ fontSearchEmpty: 'No fonts available',
173
+ fontSearchEmptyHintPrefix: 'Try a different family name, or browse the full catalog at ',
174
+ fontSearchEmptyHintSuffix: '.',
175
+ fontWeightLabel: 'Weight',
176
+ fontPreviewSample: 'The quick brown fox jumps over the lazy dog 0123456789',
164
177
  toastUploadFailed: 'Upload failed ({status})',
165
178
  toastReplaced: 'Replaced {name}',
166
179
  toastUploadedAs: 'Uploaded as {name}',
@@ -170,6 +183,7 @@ export const en: Locale = {
170
183
  toastDeleteFailed: 'Delete failed ({status})',
171
184
  toastDeleted: 'Deleted {name}',
172
185
  toastDownloadFailed: 'Failed to download logo',
186
+ toastFontDownloadFailed: 'Failed to download font',
173
187
  toastSearchFailed: 'Search failed',
174
188
  },
175
189
  commandMenu: {
@@ -196,10 +210,7 @@ export const en: Locale = {
196
210
  dark: 'Dark',
197
211
  system: 'System',
198
212
  },
199
- languageToggle: {
200
- toggleAria: 'Change language',
201
- title: 'Language',
202
- },
213
+
203
214
  themes: {
204
215
  title: 'Themes',
205
216
  noThemesTitle: 'No themes yet',
package/src/locale/ja.ts CHANGED
@@ -18,7 +18,7 @@ export const ja: Locale = {
18
18
  title: 'ページが見つかりません',
19
19
  },
20
20
  home: {
21
- appTitle: 'open-pages',
21
+ appTitle: 'Autono',
22
22
  draft: '下書き',
23
23
  duplicate: '複製',
24
24
  themes: 'テーマ',
@@ -28,7 +28,7 @@ export const ja: Locale = {
28
28
  menu: 'メニュー',
29
29
  newFolder: '新規フォルダ',
30
30
  folderName: 'フォルダ名',
31
- updateAvailable: 'open-pages {version} が利用可能です',
31
+ updateAvailable: 'Autono {version} が利用可能です',
32
32
  updatePackage: '更新',
33
33
  updatingPackage: '更新中',
34
34
  updatePackageDone: '更新しました。新しいバージョンを使うには dev server を再起動してください。',
@@ -124,6 +124,7 @@ export const ja: Locale = {
124
124
  noMatchingAssetsHint: '検索条件またはフィルターを調整してください。',
125
125
  clearFilters: 'フィルターをクリア',
126
126
  searchLogos: 'ロゴを検索',
127
+ searchFonts: 'フォントを検索',
127
128
  upload: 'アップロード',
128
129
  dropToUpload: 'ドロップでアップロード',
129
130
  loading: '読み込み中…',
@@ -163,6 +164,19 @@ export const ja: Locale = {
163
164
  logoSearchEmptyHintSuffix: '。',
164
165
  logoVariantLight: 'ライト',
165
166
  logoVariantDark: 'ダーク',
167
+ fontSearchTitle: 'Google Fonts を検索',
168
+ fontSearchPoweredByPrefix: 'Powered by ',
169
+ fontSearchPlaceholder: 'フォントファミリーで検索…',
170
+ fontSearchPreviewPlaceholder: 'プレビューテキスト',
171
+ fontSearchErrorTitle: 'Google Fonts に接続できません',
172
+ fontSearchErrorBody: '接続を確認して再試行してください。',
173
+ fontSearchNoResults: '「{query}」のフォントは見つかりません',
174
+ fontSearchEmpty: '利用可能なフォントはありません',
175
+ fontSearchEmptyHintPrefix:
176
+ '別のファミリー名を試すか、次のサイトでカタログ全体を閲覧してください: ',
177
+ fontSearchEmptyHintSuffix: '。',
178
+ fontWeightLabel: 'ウェイト',
179
+ fontPreviewSample: 'いろはにほへと 永遠 ABCabc 0123456789',
166
180
  toastUploadFailed: 'アップロードに失敗しました ({status})',
167
181
  toastReplaced: '{name} を置き換えました',
168
182
  toastUploadedAs: '{name} としてアップロードしました',
@@ -172,6 +186,7 @@ export const ja: Locale = {
172
186
  toastDeleteFailed: '削除に失敗しました ({status})',
173
187
  toastDeleted: '{name} を削除しました',
174
188
  toastDownloadFailed: 'ロゴのダウンロードに失敗しました',
189
+ toastFontDownloadFailed: 'フォントのダウンロードに失敗しました',
175
190
  toastSearchFailed: '検索に失敗しました',
176
191
  },
177
192
  commandMenu: {
@@ -198,10 +213,7 @@ export const ja: Locale = {
198
213
  dark: 'ダーク',
199
214
  system: 'システム',
200
215
  },
201
- languageToggle: {
202
- toggleAria: '言語を切り替え',
203
- title: '言語',
204
- },
216
+
205
217
  themes: {
206
218
  title: 'テーマ',
207
219
  noThemesTitle: 'テーマがまだありません',
@@ -121,6 +121,7 @@ export type Locale = {
121
121
  noMatchingAssetsHint: string;
122
122
  clearFilters: string;
123
123
  searchLogos: string;
124
+ searchFonts: string;
124
125
  upload: string;
125
126
  dropToUpload: string;
126
127
  loading: string;
@@ -158,6 +159,20 @@ export type Locale = {
158
159
  logoSearchEmptyHintSuffix: string;
159
160
  logoVariantLight: string;
160
161
  logoVariantDark: string;
162
+ fontSearchTitle: string;
163
+ fontSearchPoweredByPrefix: string;
164
+ fontSearchPlaceholder: string;
165
+ fontSearchPreviewPlaceholder: string;
166
+ fontSearchErrorTitle: string;
167
+ fontSearchErrorBody: string;
168
+ /** template: 'No fonts for "{query}"' */
169
+ fontSearchNoResults: string;
170
+ fontSearchEmpty: string;
171
+ fontSearchEmptyHintPrefix: string;
172
+ fontSearchEmptyHintSuffix: string;
173
+ fontWeightLabel: string;
174
+ fontPreviewSample: string;
175
+ /** template: "Upload failed ({status})" */
161
176
  toastUploadFailed: string;
162
177
  toastReplaced: string;
163
178
  toastUploadedAs: string;
@@ -167,6 +182,7 @@ export type Locale = {
167
182
  toastDeleteFailed: string;
168
183
  toastDeleted: string;
169
184
  toastDownloadFailed: string;
185
+ toastFontDownloadFailed: string;
170
186
  toastSearchFailed: string;
171
187
  };
172
188
  commandMenu: {
@@ -193,10 +209,7 @@ export type Locale = {
193
209
  dark: string;
194
210
  system: string;
195
211
  };
196
- languageToggle: {
197
- toggleAria: string;
198
- title: string;
199
- };
212
+
200
213
  themes: {
201
214
  title: string;
202
215
  noThemesTitle: string;
@@ -18,7 +18,7 @@ export const zhCN: Locale = {
18
18
  title: '未找到页面',
19
19
  },
20
20
  home: {
21
- appTitle: 'open-pages',
21
+ appTitle: 'Autono',
22
22
  draft: '草稿',
23
23
  duplicate: '复制',
24
24
  themes: '主题',
@@ -26,9 +26,9 @@ export const zhCN: Locale = {
26
26
  folders: '頁面夹',
27
27
  pages: '幻灯片',
28
28
  menu: '菜单',
29
- newFolder: '新建頁面夹',
30
- folderName: '頁面夹名称',
31
- updateAvailable: 'open-pages {version} 已发布',
29
+ newFolder: '新建文件夹',
30
+ folderName: '文件夹名称',
31
+ updateAvailable: 'Autono {version} 已发布',
32
32
  updatePackage: '更新',
33
33
  updatingPackage: '更新中',
34
34
  updatePackageDone: '已更新。请重启 dev server 以使用新版本。',
@@ -124,6 +124,7 @@ export const zhCN: Locale = {
124
124
  noMatchingAssetsHint: '请调整搜索或筛选条件。',
125
125
  clearFilters: '清除筛选',
126
126
  searchLogos: '搜索 Logo',
127
+ searchFonts: '搜索字体',
127
128
  upload: '上传',
128
129
  dropToUpload: '拖入即可上传',
129
130
  loading: '加载中…',
@@ -161,6 +162,18 @@ export const zhCN: Locale = {
161
162
  logoSearchEmptyHintSuffix: '。',
162
163
  logoVariantLight: '浅色',
163
164
  logoVariantDark: '深色',
165
+ fontSearchTitle: '搜索 Google Fonts',
166
+ fontSearchPoweredByPrefix: '由 ',
167
+ fontSearchPlaceholder: '搜索字体家族…',
168
+ fontSearchPreviewPlaceholder: '预览文字',
169
+ fontSearchErrorTitle: '无法连接至 Google Fonts',
170
+ fontSearchErrorBody: '请检查网络连接后重试。',
171
+ fontSearchNoResults: '找不到「{query}」的字体',
172
+ fontSearchEmpty: '无可用字体',
173
+ fontSearchEmptyHintPrefix: '试试其他字体家族名,或在以下网站浏览完整目录: ',
174
+ fontSearchEmptyHintSuffix: '。',
175
+ fontWeightLabel: '字重',
176
+ fontPreviewSample: '永和九年 岁在癸丑 ABCabc 0123456789',
164
177
  toastUploadFailed: '上传失败 ({status})',
165
178
  toastReplaced: '已替换 {name}',
166
179
  toastUploadedAs: '已以 {name} 上传',
@@ -170,6 +183,7 @@ export const zhCN: Locale = {
170
183
  toastDeleteFailed: '删除失败 ({status})',
171
184
  toastDeleted: '已删除 {name}',
172
185
  toastDownloadFailed: '下载 Logo 失败',
186
+ toastFontDownloadFailed: '下载字体失败',
173
187
  toastSearchFailed: '搜索失败',
174
188
  },
175
189
  commandMenu: {
@@ -196,10 +210,7 @@ export const zhCN: Locale = {
196
210
  dark: '深色',
197
211
  system: '系统',
198
212
  },
199
- languageToggle: {
200
- toggleAria: '切换语言',
201
- title: '语言',
202
- },
213
+
203
214
  themes: {
204
215
  title: '主题',
205
216
  noThemesTitle: '尚无主题',
@@ -18,7 +18,7 @@ export const zhTW: Locale = {
18
18
  title: '找不到頁面',
19
19
  },
20
20
  home: {
21
- appTitle: 'open-pages',
21
+ appTitle: 'Autono',
22
22
  draft: '草稿',
23
23
  duplicate: '複製',
24
24
  themes: '主題',
@@ -28,7 +28,7 @@ export const zhTW: Locale = {
28
28
  menu: '選單',
29
29
  newFolder: '新增資料夾',
30
30
  folderName: '資料夾名稱',
31
- updateAvailable: 'open-pages {version} 已發布',
31
+ updateAvailable: 'Autono {version} 已發布',
32
32
  updatePackage: '更新',
33
33
  updatingPackage: '更新中',
34
34
  updatePackageDone: '已更新。請重新啟動 dev server 以使用新版本。',
@@ -124,6 +124,7 @@ export const zhTW: Locale = {
124
124
  noMatchingAssetsHint: '請調整搜尋或篩選條件。',
125
125
  clearFilters: '清除篩選',
126
126
  searchLogos: '搜尋 Logo',
127
+ searchFonts: '搜尋字型',
127
128
  upload: '上傳',
128
129
  dropToUpload: '拖入即可上傳',
129
130
  loading: '載入中…',
@@ -161,6 +162,18 @@ export const zhTW: Locale = {
161
162
  logoSearchEmptyHintSuffix: '。',
162
163
  logoVariantLight: '淺色',
163
164
  logoVariantDark: '深色',
165
+ fontSearchTitle: '搜尋 Google Fonts',
166
+ fontSearchPoweredByPrefix: '由 ',
167
+ fontSearchPlaceholder: '搜尋字型家族…',
168
+ fontSearchPreviewPlaceholder: '預覽文字',
169
+ fontSearchErrorTitle: '無法連線至 Google Fonts',
170
+ fontSearchErrorBody: '請檢查網路連線後重試。',
171
+ fontSearchNoResults: '找不到「{query}」的字型',
172
+ fontSearchEmpty: '沒有可用的字型',
173
+ fontSearchEmptyHintPrefix: '試試其他字型家族名稱,或在以下網站瀏覽完整目錄: ',
174
+ fontSearchEmptyHintSuffix: '。',
175
+ fontWeightLabel: '字重',
176
+ fontPreviewSample: '永和九年 歲在癸丑 ABCabc 0123456789',
164
177
  toastUploadFailed: '上傳失敗 ({status})',
165
178
  toastReplaced: '已取代 {name}',
166
179
  toastUploadedAs: '已以 {name} 上傳',
@@ -170,6 +183,7 @@ export const zhTW: Locale = {
170
183
  toastDeleteFailed: '刪除失敗 ({status})',
171
184
  toastDeleted: '已刪除 {name}',
172
185
  toastDownloadFailed: '下載 Logo 失敗',
186
+ toastFontDownloadFailed: '下載字型失敗',
173
187
  toastSearchFailed: '搜尋失敗',
174
188
  },
175
189
  commandMenu: {
@@ -196,10 +210,7 @@ export const zhTW: Locale = {
196
210
  dark: '深色',
197
211
  system: '系統',
198
212
  },
199
- languageToggle: {
200
- toggleAria: '切換語言',
201
- title: '語言',
202
- },
213
+
203
214
  themes: {
204
215
  title: '主題',
205
216
  noThemesTitle: '尚無主題',
@@ -0,0 +1,193 @@
1
+ import { type StdioOptions, spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ export const PKG = '@autono/open-pages';
6
+ const CACHE_TTL_MS = 10 * 60 * 1000;
7
+ const COMMAND_TIMEOUT_MS = 300_000;
8
+
9
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
10
+ export type CommandSpec = { cmd: string; args: string[]; shell?: boolean };
11
+
12
+ let cache: { at: number; latest: string | null } | null = null;
13
+
14
+ function parseSemver(v: string): [number, number, number] | null {
15
+ const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
16
+ if (!m) return null;
17
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
18
+ }
19
+
20
+ export function isOutdated(current: string, latest: string): boolean {
21
+ const a = parseSemver(current);
22
+ const b = parseSemver(latest);
23
+ if (!a || !b) return false;
24
+ for (let i = 0; i < 3; i++) {
25
+ if (b[i] > a[i]) return true;
26
+ if (b[i] < a[i]) return false;
27
+ }
28
+ return false;
29
+ }
30
+
31
+ /** npm `latest` dist-tag, cached for 10 minutes. Network failures yield null. */
32
+ export async function fetchLatest(now = Date.now()): Promise<string | null> {
33
+ if (cache && now - cache.at < CACHE_TTL_MS) return cache.latest;
34
+ try {
35
+ const res = await fetch(`https://registry.npmjs.org/${PKG}/latest`, {
36
+ signal: AbortSignal.timeout(3000),
37
+ headers: { accept: 'application/json' },
38
+ });
39
+ if (!res.ok) throw new Error(`registry ${res.status}`);
40
+ const body = (await res.json()) as { version?: unknown };
41
+ const latest = typeof body.version === 'string' ? body.version : null;
42
+ cache = { at: now, latest };
43
+ return latest;
44
+ } catch {
45
+ return cache?.latest ?? null;
46
+ }
47
+ }
48
+
49
+ export function invalidateLatestCache(): void {
50
+ cache = null;
51
+ }
52
+
53
+ async function fileExists(file: string): Promise<boolean> {
54
+ try {
55
+ await fs.access(file);
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ // Lockfiles identify the workspace's package manager; npm_config_user_agent
63
+ // only identifies the process driving this run (e.g. `npx` in a pnpm repo).
64
+ const LOCKFILES: ReadonlyArray<readonly [string, PackageManager]> = [
65
+ ['pnpm-lock.yaml', 'pnpm'],
66
+ ['yarn.lock', 'yarn'],
67
+ ['bun.lockb', 'bun'],
68
+ ['bun.lock', 'bun'],
69
+ ['package-lock.json', 'npm'],
70
+ ];
71
+
72
+ const PROJECT_ROOT_MARKERS = ['pnpm-workspace.yaml', '.git'];
73
+
74
+ async function declaredPackageManager(dir: string): Promise<PackageManager | null> {
75
+ try {
76
+ const raw = await fs.readFile(path.join(dir, 'package.json'), 'utf8');
77
+ const declared = (JSON.parse(raw) as { packageManager?: unknown }).packageManager;
78
+ if (typeof declared !== 'string') return null;
79
+ const name = declared.split('@')[0];
80
+ return name === 'npm' || name === 'pnpm' || name === 'yarn' || name === 'bun' ? name : null;
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+
86
+ async function isProjectRoot(dir: string): Promise<boolean> {
87
+ for (const marker of PROJECT_ROOT_MARKERS) {
88
+ if (await fileExists(path.join(dir, marker))) return true;
89
+ }
90
+ return false;
91
+ }
92
+
93
+ export async function detectPackageManager(cwd: string): Promise<PackageManager> {
94
+ // Workspace members have no lockfile of their own; it lives at the root.
95
+ // The walk stops at the project root so a lockfile in an unrelated
96
+ // ancestor directory never picks the manager.
97
+ let dir = cwd;
98
+ while (true) {
99
+ const declared = await declaredPackageManager(dir);
100
+ if (declared) return declared;
101
+ for (const [lockfile, pm] of LOCKFILES) {
102
+ if (await fileExists(path.join(dir, lockfile))) return pm;
103
+ }
104
+ const parent = path.dirname(dir);
105
+ if (parent === dir || (await isProjectRoot(dir))) break;
106
+ dir = parent;
107
+ }
108
+
109
+ const ua = process.env.npm_config_user_agent ?? '';
110
+ if (ua.startsWith('pnpm')) return 'pnpm';
111
+ if (ua.startsWith('yarn')) return 'yarn';
112
+ if (ua.startsWith('bun')) return 'bun';
113
+ return 'npm';
114
+ }
115
+
116
+ export async function updateCommandFor(
117
+ packageManager: PackageManager,
118
+ cwd: string,
119
+ ): Promise<CommandSpec> {
120
+ switch (packageManager) {
121
+ case 'pnpm': {
122
+ // pnpm rejects plain `add` at a workspace root with ERR_PNPM_ADDING_TO_ROOT.
123
+ const workspaceRoot = await fileExists(path.join(cwd, 'pnpm-workspace.yaml'));
124
+ return {
125
+ cmd: 'pnpm',
126
+ args: workspaceRoot
127
+ ? ['add', '--workspace-root', `${PKG}@latest`]
128
+ : ['add', `${PKG}@latest`],
129
+ };
130
+ }
131
+ case 'yarn':
132
+ return { cmd: 'yarn', args: ['add', `${PKG}@latest`] };
133
+ case 'bun':
134
+ return { cmd: 'bun', args: ['add', `${PKG}@latest`] };
135
+ case 'npm':
136
+ return { cmd: 'npm', args: ['install', `${PKG}@latest`] };
137
+ }
138
+ }
139
+
140
+ /**
141
+ * The freshly installed package's own CLI, so skills come from the new
142
+ * version rather than whatever process is driving the update.
143
+ */
144
+ export function installCommandFor(packageManager: PackageManager): CommandSpec {
145
+ return { cmd: packageManager, args: ['install'] };
146
+ }
147
+
148
+ export function localOpenPagesCommand(cwd: string, args: string[]): CommandSpec {
149
+ const bin = path.join(cwd, 'node_modules', ...PKG.split('/'), 'bin.js');
150
+ return { cmd: process.execPath, args: [bin, ...args], shell: false };
151
+ }
152
+
153
+ export function formatCommand(spec: CommandSpec): string {
154
+ return [spec.cmd, ...spec.args].join(' ');
155
+ }
156
+
157
+ export async function runCommand(
158
+ spec: CommandSpec,
159
+ cwd: string,
160
+ opts: { stdio?: StdioOptions } = {},
161
+ ): Promise<void> {
162
+ await new Promise<void>((resolve, reject) => {
163
+ const child = spawn(spec.cmd, spec.args, {
164
+ cwd,
165
+ env: process.env,
166
+ shell: spec.shell ?? process.platform === 'win32',
167
+ stdio: opts.stdio ?? ['ignore', 'ignore', 'pipe'],
168
+ });
169
+ let stderr = '';
170
+ const timer = setTimeout(() => {
171
+ child.kill();
172
+ reject(new Error(`${formatCommand(spec)} timed out`));
173
+ }, COMMAND_TIMEOUT_MS);
174
+
175
+ child.stderr?.on('data', (chunk: Buffer) => {
176
+ stderr += chunk.toString('utf8');
177
+ if (stderr.length > 2000) stderr = stderr.slice(-2000);
178
+ });
179
+ child.on('error', (err) => {
180
+ clearTimeout(timer);
181
+ reject(err);
182
+ });
183
+ child.on('close', (code) => {
184
+ clearTimeout(timer);
185
+ if (code === 0) {
186
+ resolve();
187
+ return;
188
+ }
189
+ const detail = stderr.trim();
190
+ reject(new Error(detail || `${formatCommand(spec)} exited with code ${code ?? 'unknown'}`));
191
+ });
192
+ });
193
+ }
@@ -3,7 +3,7 @@
3
3
  "react-dom": "^19.2.0",
4
4
  "@base-ui/react": "^1.7.0",
5
5
  "@fontsource-variable/geist": "^5.3.0",
6
- "@shadcn/react": "^0.3.0",
6
+ "@shadcn/react": "^0.3.1",
7
7
  "class-variance-authority": "^0.7.1",
8
8
  "clsx": "^2.1.1",
9
9
  "cmdk": "^1.1.1",
@@ -1,4 +0,0 @@
1
- import "./open-pages-plugin-C0poXLsx.js";
2
- import { buildPage, exportPages } from "./export-DbVwSmKW.js";
3
-
4
- export { exportPages };
@@ -1,21 +0,0 @@
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
- import { mergeConfig, preview as preview$1 } from "vite";
7
-
8
- //#region src/cli/preview.ts
9
- async function preview(opts = {}) {
10
- const base = await createViteConfig({ userCwd: process.cwd() });
11
- const config = mergeConfig(base, { preview: {
12
- ...opts.port !== void 0 ? { port: opts.port } : {},
13
- ...opts.host !== void 0 ? { host: opts.host } : {},
14
- ...opts.open !== void 0 ? { open: opts.open } : {}
15
- } });
16
- const server = await preview$1(config);
17
- server.printUrls();
18
- }
19
-
20
- //#endregion
21
- export { preview };