@open-slide/core 1.0.3 → 1.0.5

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 (47) hide show
  1. package/dist/{build-DqfKmw9h.js → build-CoON6kTb.js} +1 -1
  2. package/dist/cli/bin.js +3 -3
  3. package/dist/{config-CN7J0RDO.js → config-Bxtztw-H.js} +373 -221
  4. package/dist/{config-DweCbRkQ.d.ts → config-D2y1AXaN.d.ts} +3 -0
  5. package/dist/{dev-jWxtWHAG.js → dev-IezNC17X.js} +1 -1
  6. package/dist/index.d.ts +3 -2
  7. package/dist/locale/index.d.ts +24 -0
  8. package/dist/locale/index.js +1189 -0
  9. package/dist/{preview-CSA05Gfm.js → preview-BwYjtENY.js} +1 -1
  10. package/dist/types-BVvl_xup.d.ts +314 -0
  11. package/dist/vite/index.d.ts +2 -1
  12. package/dist/vite/index.js +1 -1
  13. package/package.json +7 -1
  14. package/skills/create-slide/SKILL.md +15 -5
  15. package/src/app/app.tsx +6 -2
  16. package/src/app/components/asset-view.tsx +87 -64
  17. package/src/app/components/click-nav-zones.tsx +4 -2
  18. package/src/app/components/inspector/comment-widget.tsx +9 -7
  19. package/src/app/components/inspector/inspector-panel.tsx +68 -39
  20. package/src/app/components/inspector/inspector-provider.tsx +185 -58
  21. package/src/app/components/inspector/save-bar.tsx +6 -2
  22. package/src/app/components/panel/save-card.tsx +12 -9
  23. package/src/app/components/pdf-progress-toast.tsx +11 -4
  24. package/src/app/components/present/control-bar.tsx +17 -10
  25. package/src/app/components/present/help-overlay.tsx +18 -17
  26. package/src/app/components/present/overview-grid.tsx +6 -4
  27. package/src/app/components/sidebar/folder-item.tsx +16 -7
  28. package/src/app/components/sidebar/icon-picker.tsx +4 -2
  29. package/src/app/components/sidebar/sidebar.tsx +87 -25
  30. package/src/app/components/style-panel/style-panel.tsx +26 -18
  31. package/src/app/components/theme-toggle.tsx +7 -5
  32. package/src/app/components/thumbnail-rail.tsx +4 -2
  33. package/src/app/favicon.ico +0 -0
  34. package/src/app/lib/inspector/use-editor.ts +9 -7
  35. package/src/app/lib/use-locale.ts +20 -0
  36. package/src/app/routes/home.tsx +90 -45
  37. package/src/app/routes/presenter.tsx +45 -25
  38. package/src/app/routes/slide.tsx +37 -24
  39. package/src/app/styles.css +28 -0
  40. package/src/app/virtual.d.ts +4 -0
  41. package/src/locale/en.ts +303 -0
  42. package/src/locale/format.ts +12 -0
  43. package/src/locale/index.ts +6 -0
  44. package/src/locale/ja.ts +307 -0
  45. package/src/locale/types.ts +323 -0
  46. package/src/locale/zh-cn.ts +303 -0
  47. package/src/locale/zh-tw.ts +303 -0
@@ -1,5 +1,5 @@
1
1
  import "./design-C13iz9_4.js";
2
- import { createViteConfig } from "./config-CN7J0RDO.js";
2
+ import { createViteConfig } from "./config-Bxtztw-H.js";
3
3
  import { mergeConfig, preview as preview$1 } from "vite";
4
4
 
5
5
  //#region src/cli/preview.ts
@@ -0,0 +1,314 @@
1
+ //#region src/locale/types.d.ts
2
+ type Plural = {
3
+ one: string;
4
+ other: string;
5
+ };
6
+ type Locale = {
7
+ id: 'en' | 'zh-TW' | 'zh-CN' | 'ja';
8
+ common: {
9
+ cancel: string;
10
+ save: string;
11
+ saving: string;
12
+ saved: string;
13
+ discard: string;
14
+ delete: string;
15
+ rename: string;
16
+ move: string;
17
+ close: string;
18
+ loading: string;
19
+ loadFailed: string;
20
+ failedToLoadSlide: string;
21
+ home: string;
22
+ backToHome: string;
23
+ preview: string;
24
+ add: string;
25
+ done: string;
26
+ tryAgain: string;
27
+ undo: string;
28
+ redo: string;
29
+ light: string;
30
+ dark: string;
31
+ system: string;
32
+ selected: string;
33
+ };
34
+ notFound: {
35
+ eyebrow: string;
36
+ title: string;
37
+ };
38
+ home: {
39
+ appTitle: string;
40
+ draft: string;
41
+ folders: string;
42
+ newFolder: string;
43
+ folderName: string;
44
+ changeIcon: string;
45
+ iconEmojiTab: string;
46
+ iconColorTab: string;
47
+ folderActions: string;
48
+ searchPlaceholder: string;
49
+ clearSearch: string;
50
+ noMatches: string;
51
+ nothingMatchesPrefix: string;
52
+ nothingMatchesSuffix: string;
53
+ noSlidesYet: string;
54
+ createSlideHintPrefix: string;
55
+ createSlideHintMid: string;
56
+ createSlideHintSuffix: string;
57
+ folderEmptyTitle: string;
58
+ folderEmptyHint: string;
59
+ slideActions: string;
60
+ moveToFolder: string;
61
+ renameDialogEyebrow: string;
62
+ renameDialogTitle: string;
63
+ renameDialogDescription: string;
64
+ slideNamePlaceholder: string;
65
+ moveDialogEyebrow: string;
66
+ moveDialogTitle: string;
67
+ moveDialogDescriptionPrefix: string;
68
+ moveDialogDescriptionSuffix: string;
69
+ deleteDialogEyebrow: string;
70
+ deleteDialogTitle: string;
71
+ deleteDialogDescriptionPrefix: string;
72
+ deleteDialogDescriptionMid: string;
73
+ deleteDialogDescriptionSuffix: string;
74
+ /** template: "Created folder “{name}”" */
75
+ toastFolderCreated: string;
76
+ toastFolderCreateFailed: string;
77
+ /** template: "Moved “{slide}” to {folder}" */
78
+ toastSlideMoved: string;
79
+ toastSlideMoveFailed: string;
80
+ /** template: "Deleted folder “{name}”" */
81
+ toastFolderDeleted: string;
82
+ toastFolderDeleteFailed: string;
83
+ pickIcon: string;
84
+ };
85
+ slide: {
86
+ home: string;
87
+ backToHome: string;
88
+ download: string;
89
+ exportAsHtml: string;
90
+ exportAsPdf: string;
91
+ pdfExportFailed: string;
92
+ present: string;
93
+ slidesTab: string;
94
+ assetsTab: string;
95
+ renameSlide: string;
96
+ loadingEyebrow: string;
97
+ emptyEyebrow: string;
98
+ nothingToShow: string;
99
+ emptyHintPrefix: string;
100
+ emptyHintMust: string;
101
+ emptyHintSuffix: string;
102
+ };
103
+ presenter: {
104
+ eyebrow: string;
105
+ notLinked: string;
106
+ nowShowing: string;
107
+ upNext: string;
108
+ lastSlide: string;
109
+ endOfDeck: string;
110
+ speakerNotes: string;
111
+ noNotesPrefix: string;
112
+ noNotesSuffix: string;
113
+ blackScreen: string;
114
+ whiteScreen: string;
115
+ prev: string;
116
+ next: string;
117
+ black: string;
118
+ white: string;
119
+ reset: string;
120
+ resetTimer: string;
121
+ currentTime: string;
122
+ elapsed: string;
123
+ jump: string;
124
+ /** template: "Loading {slideId}…" */
125
+ loadingSlide: string;
126
+ };
127
+ present: {
128
+ prevSlideAria: string;
129
+ nextSlideAria: string;
130
+ overviewAria: string;
131
+ blackoutAria: string;
132
+ whiteoutAria: string;
133
+ laserAria: string;
134
+ presenterAria: string;
135
+ helpAria: string;
136
+ exitAria: string;
137
+ elapsedTime: string;
138
+ helpEyebrow: string;
139
+ helpTitle: string;
140
+ shortcutNext: string;
141
+ shortcutPrev: string;
142
+ shortcutFirstLast: string;
143
+ shortcutJump: string;
144
+ shortcutOverview: string;
145
+ shortcutBlack: string;
146
+ shortcutWhite: string;
147
+ shortcutLaser: string;
148
+ shortcutPresenter: string;
149
+ shortcutToggleHelp: string;
150
+ shortcutCloseExit: string;
151
+ overviewDialogAria: string;
152
+ overviewEyebrow: string;
153
+ /** template: "Go to slide {n}" */
154
+ overviewGoToAria: string;
155
+ nowBadge: string;
156
+ };
157
+ inspector: {
158
+ inspect: string;
159
+ deselect: string;
160
+ contentSection: string;
161
+ typographySection: string;
162
+ colorSection: string;
163
+ textColor: string;
164
+ backgroundColor: string;
165
+ imageSection: string;
166
+ imagePlaceholderSection: string;
167
+ elementTextPlaceholder: string;
168
+ sizeLabel: string;
169
+ weightLabel: string;
170
+ weightLight: string;
171
+ weightRegular: string;
172
+ weightMedium: string;
173
+ weightSemibold: string;
174
+ weightBold: string;
175
+ weightExtrabold: string;
176
+ styleLabel: string;
177
+ boldAria: string;
178
+ italicAria: string;
179
+ lineHeightLabel: string;
180
+ trackingLabel: string;
181
+ alignLabel: string;
182
+ clearAria: string;
183
+ replace: string;
184
+ replaceImageDialogTitle: string;
185
+ /** template: "Pick an asset from {path}." */
186
+ replaceImageDescription: string;
187
+ pickerLoading: string;
188
+ pickerEmpty: string;
189
+ placeholderHintLabel: string;
190
+ noteForAgent: string;
191
+ noteAgentPlaceholder: string;
192
+ noteShortcutHint: string;
193
+ addNote: string;
194
+ /** templates: "{count} unsaved change" / "{count} unsaved changes" */
195
+ unsavedChanges: Plural;
196
+ /** templates: "{count} comment" / "{count} comments" */
197
+ commentsCount: Plural;
198
+ /** template: "line {n}" */
199
+ commentLineLabel: string;
200
+ commentsEmpty: string;
201
+ commentsApplyHintPrefix: string;
202
+ commentsApplyHintSuffix: string;
203
+ commentDeleteAria: string;
204
+ /** Prefix for the toast shown when one or more buffered edits fail to write to disk. */
205
+ saveFailed: string;
206
+ };
207
+ stylePanel: {
208
+ designTokens: string;
209
+ draftBadge: string;
210
+ unsavedTitle: string;
211
+ closePanelAria: string;
212
+ colorsSection: string;
213
+ typographySection: string;
214
+ shapeSection: string;
215
+ backgroundLabel: string;
216
+ textLabel: string;
217
+ accentLabel: string;
218
+ displayFontLabel: string;
219
+ bodyFontLabel: string;
220
+ heroLabel: string;
221
+ bodyLabel: string;
222
+ radiusLabel: string;
223
+ designToggle: string;
224
+ designToggleTitle: string;
225
+ fontPresetCustom: string;
226
+ };
227
+ asset: {
228
+ devOnlyMessage: string;
229
+ sectionAria: string;
230
+ eyebrow: string;
231
+ /** templates: "{count} file" / "{count} files" */
232
+ fileCount: Plural;
233
+ searchLogos: string;
234
+ upload: string;
235
+ dropToUpload: string;
236
+ loading: string;
237
+ noAssetsYet: string;
238
+ noAssetsHintPrefix: string;
239
+ noAssetsHintSuffix: string;
240
+ nameAlreadyExists: string;
241
+ /** template: "Preview {name}" */
242
+ previewAria: string;
243
+ /** template: "Actions for {name}" */
244
+ actionsAria: string;
245
+ previewMenuItem: string;
246
+ renameMenuItem: string;
247
+ deleteMenuItem: string;
248
+ conflictTitle: string;
249
+ /** template: "{name} is already in this slide's assets folder." */
250
+ conflictDescription: string;
251
+ conflictReplace: string;
252
+ conflictRenameCopy: string;
253
+ deleteAssetTitle: string;
254
+ /** template: "Delete {name}? Imports referencing this file in the slide will break." */
255
+ deleteAssetDescription: string;
256
+ noPreview: string;
257
+ importHintComment: string;
258
+ importHintSemi: string;
259
+ logoSearchTitle: string;
260
+ logoSearchPoweredByPrefix: string;
261
+ logoSearchPlaceholder: string;
262
+ logoSearchErrorTitle: string;
263
+ logoSearchErrorBody: string;
264
+ /** template: 'No logos for "{query}"' */
265
+ logoSearchNoResults: string;
266
+ logoSearchEmpty: string;
267
+ logoSearchEmptyHintPrefix: string;
268
+ logoSearchEmptyHintSuffix: string;
269
+ logoVariantLight: string;
270
+ logoVariantDark: string;
271
+ /** template: "Upload failed ({status})" */
272
+ toastUploadFailed: string;
273
+ /** template: "Replaced {name}" */
274
+ toastReplaced: string;
275
+ /** template: "Uploaded as {name}" */
276
+ toastUploadedAs: string;
277
+ /** template: "Uploaded {name}" */
278
+ toastUploaded: string;
279
+ /** template: "Rename failed ({status})" */
280
+ toastRenameFailed: string;
281
+ /** template: "Renamed to {name}" */
282
+ toastRenamed: string;
283
+ /** template: "Delete failed ({status})" */
284
+ toastDeleteFailed: string;
285
+ /** template: "Deleted {name}" */
286
+ toastDeleted: string;
287
+ toastDownloadFailed: string;
288
+ toastSearchFailed: string;
289
+ };
290
+ thumbnailRail: {
291
+ pages: string;
292
+ /** template: "Go to page {n}" */
293
+ goToPageAria: string;
294
+ };
295
+ pdfToast: {
296
+ title: string;
297
+ /** template: "Processing page {current} of {total}" */
298
+ processing: string;
299
+ printing: string;
300
+ done: string;
301
+ };
302
+ themeToggle: {
303
+ toggleAria: string;
304
+ title: string;
305
+ light: string;
306
+ dark: string;
307
+ system: string;
308
+ };
309
+ clickNav: {
310
+ prevAria: string;
311
+ nextAria: string;
312
+ };
313
+ }; //#endregion
314
+ export { Locale, Plural };
@@ -1,4 +1,5 @@
1
- import { OpenSlideConfig } from "../config-DweCbRkQ.js";
1
+ import "../types-BVvl_xup.js";
2
+ import { OpenSlideConfig } from "../config-D2y1AXaN.js";
2
3
  import { InlineConfig } from "vite";
3
4
 
4
5
  //#region src/vite/config.d.ts
@@ -1,4 +1,4 @@
1
1
  import "../design-C13iz9_4.js";
2
- import { createViteConfig } from "../config-CN7J0RDO.js";
2
+ import { createViteConfig } from "../config-Bxtztw-H.js";
3
3
 
4
4
  export { createViteConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-slide/core",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Runtime and CLI for open-slide — write slides in slides/, we handle the rest.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,6 +12,10 @@
12
12
  "types": "./dist/vite/index.d.ts",
13
13
  "import": "./dist/vite/index.js"
14
14
  },
15
+ "./locale": {
16
+ "types": "./dist/locale/index.d.ts",
17
+ "import": "./dist/locale/index.js"
18
+ },
15
19
  "./env": {
16
20
  "types": "./env.d.ts"
17
21
  }
@@ -24,6 +28,7 @@
24
28
  "dist",
25
29
  "env.d.ts",
26
30
  "src/app",
31
+ "src/locale",
27
32
  "skills",
28
33
  "README.md"
29
34
  ],
@@ -47,6 +52,7 @@
47
52
  },
48
53
  "dependencies": {
49
54
  "@babel/parser": "^7.29.2",
55
+ "@babel/types": "^7.29.0",
50
56
  "@fontsource-variable/geist": "^5.2.8",
51
57
  "@tailwindcss/vite": "^4.2.2",
52
58
  "@vitejs/plugin-react": "^4.3.3",
@@ -13,23 +13,33 @@ You only write files under `slides/<id>/`. Never modify `package.json`, `open-sl
13
13
 
14
14
  List files under `themes/`. If any theme markdown files exist (anything other than `README.md`), call `AskUserQuestion` with each theme id as an option plus a final **"no theme — design from scratch"** option.
15
15
 
16
- - If the user picks a theme: read `themes/<id>.md` end-to-end. The theme's palette, typography, layout, and Title/Footer components are now authoritative — copy them directly into the slide. In Step 2, you can skip the **topic & aesthetic** question (the theme already commits to one direction); still ask about page count, text density, and motion since those are independent of theme.
16
+ - If the user picks a theme: read `themes/<id>.md` end-to-end. The theme's palette, typography, layout, and Title/Footer components are now authoritative — copy them directly into the slide. In Step 2, skip the **aesthetic direction** question (the theme already commits to one direction); you still need the topic itself, so confirm it before moving on. Page count, text density, and motion are independent of theme — ask those normally.
17
17
  - If the user picks "no theme", or `themes/` is empty (or contains only `README.md`): proceed to Step 2 unchanged.
18
18
 
19
19
  If you skip the aesthetic question because a theme was picked, restate the theme name in Step 2 so the user can correct course before you start writing.
20
20
 
21
21
  ## Step 2 — Clarify requirements (MUST ask before writing code)
22
22
 
23
- **Before doing anything else, call `AskUserQuestion` to confirm the four key style decisions below.** These shape every downstream choice (layout, type scale, asset needs, motion code), so locking them in up front avoids rework. Only skip a question when the user's original message already gave an unambiguous answer for it — and if you skip, restate your assumption so they can correct it.
23
+ **Before writing any code, lock in the four key style decisions below via `AskUserQuestion`.** They shape every downstream choice (layout, type scale, asset needs, motion code), so locking them in up front avoids rework. Only skip a question when the user's original message already gave an unambiguous answer for it — and if you skip, restate your assumption so they can correct it.
24
24
 
25
- Ask these four in a single `AskUserQuestion` call (multi-question form):
25
+ **Topic comes first.** A meaningful aesthetic recommendation requires knowing what the deck is about. If the user's initial request is thin ("make me a deck", "draft some slides"), make a *separate* `AskUserQuestion` call first to gather topic, audience, and any draft outline. Skip this only if the topic is already clear from the user's message — in which case restate your reading of the topic in the next call so they can correct course.
26
+
27
+ Then ask these four in a single `AskUserQuestion` call (multi-question form):
28
+
29
+ 1. **Aesthetic direction** — propose 3 visual directions tailored to *this* topic. Do **not** pull from a fixed preset list. Each option must combine a vibe word + a concrete visual cue (palette, typography, motif) so the user can picture it; bare labels like "minimal" or "corporate" alone are too vague. The three options should feel meaningfully different from each other — not three flavors of the same idea.
30
+
31
+ How options should shift with topic:
32
+ - *"Intro to Rust for backend engineers"* → **rust-orange technical editorial** (warm rust/charcoal, mono headings, code-grid layout) · **blueprint dev-doc** (cyan grid on near-black, monospace, schematic feel) · **brutalist terminal** (lime-on-black, ASCII rules, no-nonsense)
33
+ - *"Q2 product roadmap for stakeholders"* → **calm corporate clean** (off-white, single accent, generous whitespace) · **confident editorial** (large display serif, tight grid, one bold accent) · **data-forward dashboard** (charts as hero, muted neutrals + status colors)
34
+ - *"Kindergarten parent night"* → **playful crayon** (paper texture, hand-drawn accents, primary colors) · **soft pastel storybook** (peach/mint, rounded type, illustrated icons) · **warm photo-led** (full-bleed kid photos, simple captions)
35
+
36
+ Mark the option that best fits the topic and audience as "(Recommended)" so the user has a sensible default. (`AskUserQuestion` already auto-adds "Other" — don't add a generic catch-all yourself.)
26
37
 
27
- 1. **Topic & aesthetic** — what is this slide about, and what visual direction? Offer options like: minimal editorial, playful, corporate-clean, retro, brutalist, soft/pastel, dark neon. If they have no preference, propose one.
28
38
  2. **Page count** — rough length. Offer brackets: 3–5 (short), 6–10 (standard), 11–20 (deep dive), custom.
29
39
  3. **Text density per page** — how much copy lives on each page? Offer: minimal (one line / big number), light (heading + 2–3 bullets), standard (heading + 4–5 bullets or short paragraph), dense (multi-column / detailed). This directly drives type scale and layout.
30
40
  4. **Motion** — does the user want CSS/React animations and transitions, or a fully static deck? Offer: static (no motion), subtle (fades / entrance only), rich (keyframes, staggered reveals, looping visuals). If animated, plan to use CSS `@keyframes` / inline `style` + `useEffect`; no extra libraries.
31
41
 
32
- After those four, ask follow-ups **only if still unclear**: audience, any drafted outline/content, brand colors, required assets. Don't pad the conversation with questions already answered.
42
+ After those four, ask follow-ups **only if still unclear**: brand colors, required assets. Don't pad the conversation with questions already answered.
33
43
 
34
44
  ## Step 3 — Pick a slide id
35
45
 
package/src/app/app.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import config from 'virtual:open-slide/config';
2
2
  import { BrowserRouter, Route, Routes } from 'react-router-dom';
3
3
  import { Toaster } from './components/ui/sonner';
4
+ import { useLocale } from './lib/use-locale';
4
5
  import { Home } from './routes/home';
5
6
  import { Presenter } from './routes/presenter';
6
7
  import { Slide } from './routes/slide';
@@ -20,11 +21,14 @@ export function App() {
20
21
  }
21
22
 
22
23
  function NotFound() {
24
+ const t = useLocale();
23
25
  return (
24
26
  <div className="grid h-screen place-items-center bg-background px-6 text-center text-foreground">
25
27
  <div>
26
- <p className="folio">404 · not found</p>
27
- <h1 className="mt-2 font-heading text-2xl font-semibold tracking-tight">Page not found</h1>
28
+ <p className="folio">{t.notFound.eyebrow}</p>
29
+ <h1 className="mt-2 font-heading text-2xl font-semibold tracking-tight">
30
+ {t.notFound.title}
31
+ </h1>
28
32
  </div>
29
33
  </div>
30
34
  );