@open-slide/core 1.0.6 → 1.2.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 (37) hide show
  1. package/dist/{build-4wOJF1l4.js → build-6BeQ3cxb.js} +1 -1
  2. package/dist/cli/bin.js +3 -3
  3. package/dist/{config-evLWCV1-.js → config-AxZ5OE1u.js} +772 -201
  4. package/dist/{config-D2y1AXaN.d.ts → config-CtT8K4VF.d.ts} +1 -1
  5. package/dist/{dev-BUr0S-Ij.js → dev-C9eLmUEq.js} +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/locale/index.d.ts +1 -1
  8. package/dist/locale/index.js +136 -24
  9. package/dist/{preview-DP_gIphz.js → preview-Cunm-f4i.js} +1 -1
  10. package/dist/{types-BVvl_xup.d.ts → types-CRHIeoNq.d.ts} +37 -4
  11. package/dist/vite/index.d.ts +2 -2
  12. package/dist/vite/index.js +1 -1
  13. package/package.json +5 -1
  14. package/skills/current-slide/SKILL.md +110 -0
  15. package/skills/slide-authoring/SKILL.md +48 -1
  16. package/src/app/components/inspector/image-crop-dialog.tsx +212 -0
  17. package/src/app/components/inspector/inspect-overlay.tsx +17 -2
  18. package/src/app/components/inspector/inspector-panel.tsx +90 -26
  19. package/src/app/components/inspector/inspector-provider.tsx +136 -1
  20. package/src/app/components/notes-drawer.tsx +117 -0
  21. package/src/app/components/player.tsx +26 -8
  22. package/src/app/components/present/overview-grid.tsx +2 -2
  23. package/src/app/components/present/use-idle.ts +6 -4
  24. package/src/app/components/style-panel/design-provider.tsx +13 -0
  25. package/src/app/components/style-panel/style-panel.tsx +23 -11
  26. package/src/app/components/thumbnail-rail.tsx +317 -55
  27. package/src/app/components/ui/context-menu.tsx +237 -0
  28. package/src/app/lib/design-presets.ts +94 -0
  29. package/src/app/lib/inspector/use-notes.ts +134 -0
  30. package/src/app/routes/home.tsx +34 -12
  31. package/src/app/routes/presenter.tsx +27 -24
  32. package/src/app/routes/slide.tsx +238 -51
  33. package/src/locale/en.ts +35 -4
  34. package/src/locale/ja.ts +35 -4
  35. package/src/locale/types.ts +38 -4
  36. package/src/locale/zh-cn.ts +35 -4
  37. package/src/locale/zh-tw.ts +35 -4
@@ -24,17 +24,20 @@ import {
24
24
  DropdownMenuTrigger,
25
25
  } from '@/components/ui/dropdown-menu';
26
26
  import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
27
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
27
28
  import { useFolders } from '@/lib/folders';
28
- import { useLocale } from '@/lib/use-locale';
29
+ import { format, useLocale } from '@/lib/use-locale';
29
30
  import { useWheelPageNavigation } from '@/lib/use-wheel-page-navigation';
30
31
  import { cn } from '@/lib/utils';
31
32
  import { ClickNavZones } from '../components/click-nav-zones';
33
+ import { NotesDrawer } from '../components/notes-drawer';
32
34
  import { PdfProgressToast } from '../components/pdf-progress-toast';
33
35
  import { Player } from '../components/player';
34
36
  import { SlideCanvas } from '../components/slide-canvas';
35
- import { ThumbnailRail } from '../components/thumbnail-rail';
37
+ import { type ThumbnailActions, ThumbnailRail } from '../components/thumbnail-rail';
36
38
  import { exportSlideAsHtml } from '../lib/export-html';
37
39
  import { exportSlideAsPdf } from '../lib/export-pdf';
40
+ import { remapNotesSessionCacheAfterReorder } from '../lib/inspector/use-notes';
38
41
  import type { SlideModule } from '../lib/sdk';
39
42
  import { loadSlide } from '../lib/slides';
40
43
 
@@ -68,12 +71,28 @@ export function Slide() {
68
71
  };
69
72
  }, [slideId]);
70
73
 
71
- const pages = useMemo(() => slide?.default ?? [], [slide]);
74
+ const modulePages = useMemo(() => slide?.default ?? [], [slide]);
75
+ const [pages, setPages] = useState<typeof modulePages>(modulePages);
76
+ useEffect(() => {
77
+ setPages(modulePages);
78
+ }, [modulePages]);
72
79
  const pageCount = pages.length;
73
80
  const rawIndex = Number(searchParams.get('p') ?? '1') - 1;
74
81
  const index = Number.isFinite(rawIndex) ? Math.max(0, Math.min(pageCount - 1, rawIndex)) : 0;
75
82
  const view = searchParams.get('view') === 'assets' ? 'assets' : 'slides';
76
83
 
84
+ useEffect(() => {
85
+ if (!import.meta.hot) return;
86
+ if (!slideId || !slide || pageCount === 0) return;
87
+ import.meta.hot.send('open-slide:current', {
88
+ slideId,
89
+ pageIndex: index,
90
+ totalPages: pageCount,
91
+ slideTitle: slide.meta?.title ?? slideId,
92
+ view,
93
+ });
94
+ }, [slideId, index, pageCount, slide, view]);
95
+
77
96
  const goTo = useCallback(
78
97
  (i: number) => {
79
98
  const clamped = Math.max(0, Math.min(pageCount - 1, i));
@@ -89,6 +108,117 @@ export function Slide() {
89
108
  [pageCount, setSearchParams],
90
109
  );
91
110
 
111
+ const reorderPage = useCallback(
112
+ async (from: number, to: number) => {
113
+ if (from === to) return;
114
+ const before = pages;
115
+ const nextPages = [...before];
116
+ const [moved] = nextPages.splice(from, 1);
117
+ nextPages.splice(to, 0, moved);
118
+ setPages(nextPages);
119
+
120
+ const order = before.map((_, i) => i);
121
+ const [movedIdx] = order.splice(from, 1);
122
+ order.splice(to, 0, movedIdx);
123
+
124
+ remapNotesSessionCacheAfterReorder(slideId, order);
125
+
126
+ // Keep the user looking at the same page they were on before the drag.
127
+ let nextIndex = index;
128
+ if (index === from) nextIndex = to;
129
+ else if (from < index && to >= index) nextIndex = index - 1;
130
+ else if (from > index && to <= index) nextIndex = index + 1;
131
+ if (nextIndex !== index) goTo(nextIndex);
132
+
133
+ try {
134
+ const res = await fetch(`/__slides/${encodeURIComponent(slideId)}/reorder`, {
135
+ method: 'PUT',
136
+ headers: { 'content-type': 'application/json' },
137
+ body: JSON.stringify({ order }),
138
+ });
139
+ if (!res.ok) {
140
+ const detail = await res.json().catch(() => ({ error: res.statusText }));
141
+ throw new Error(detail.error ?? `HTTP ${res.status}`);
142
+ }
143
+ } catch (err) {
144
+ setPages(before);
145
+ const inverse = order.map((_, i) => order.indexOf(i));
146
+ remapNotesSessionCacheAfterReorder(slideId, inverse);
147
+ toast.error(`Reorder failed: ${String((err as Error).message ?? err)}`);
148
+ }
149
+ },
150
+ [pages, index, slideId, goTo],
151
+ );
152
+
153
+ const duplicatePage = useCallback(
154
+ async (i: number) => {
155
+ const before = pages;
156
+ if (i < 0 || i >= before.length) return;
157
+ const nextPages = [...before];
158
+ nextPages.splice(i + 1, 0, before[i]);
159
+ setPages(nextPages);
160
+ if (index > i) goTo(index + 1);
161
+
162
+ try {
163
+ const res = await fetch(`/__slides/${encodeURIComponent(slideId)}/pages/${i}/duplicate`, {
164
+ method: 'POST',
165
+ });
166
+ if (!res.ok) {
167
+ const detail = await res.json().catch(() => ({ error: res.statusText }));
168
+ throw new Error(detail.error ?? `HTTP ${res.status}`);
169
+ }
170
+ toast.success(format(t.thumbnailRail.toastDuplicated, { n: i + 1 }));
171
+ } catch (err) {
172
+ setPages(before);
173
+ toast.error(
174
+ `${t.thumbnailRail.toastDuplicateFailed}: ${String((err as Error).message ?? err)}`,
175
+ );
176
+ }
177
+ },
178
+ [pages, index, slideId, goTo, t.thumbnailRail],
179
+ );
180
+
181
+ const deletePage = useCallback(
182
+ async (i: number) => {
183
+ const before = pages;
184
+ if (i < 0 || i >= before.length || before.length <= 1) return;
185
+ const nextPages = before.slice(0, i).concat(before.slice(i + 1));
186
+ setPages(nextPages);
187
+ if (index >= i && index > 0) {
188
+ const target = index === i ? Math.min(index, nextPages.length - 1) : index - 1;
189
+ goTo(target);
190
+ }
191
+
192
+ try {
193
+ const res = await fetch(`/__slides/${encodeURIComponent(slideId)}/pages/${i}`, {
194
+ method: 'DELETE',
195
+ });
196
+ if (!res.ok) {
197
+ const detail = await res.json().catch(() => ({ error: res.statusText }));
198
+ throw new Error(detail.error ?? `HTTP ${res.status}`);
199
+ }
200
+ toast.success(format(t.thumbnailRail.toastDeleted, { n: i + 1 }));
201
+ } catch (err) {
202
+ setPages(before);
203
+ toast.error(
204
+ `${t.thumbnailRail.toastDeleteFailed}: ${String((err as Error).message ?? err)}`,
205
+ );
206
+ }
207
+ },
208
+ [pages, index, slideId, goTo, t.thumbnailRail],
209
+ );
210
+
211
+ const thumbnailActions = useMemo<ThumbnailActions | undefined>(
212
+ () =>
213
+ import.meta.env.DEV
214
+ ? {
215
+ onDuplicate: duplicatePage,
216
+ onDelete: deletePage,
217
+ }
218
+ : undefined,
219
+ [duplicatePage, deletePage],
220
+ );
221
+
92
222
  useEffect(() => {
93
223
  if (playing) return;
94
224
  const onKey = (e: KeyboardEvent) => {
@@ -204,6 +334,7 @@ export function Slide() {
204
334
  return (
205
335
  <HistoryProvider>
206
336
  <InspectorProvider slideId={slideId}>
337
+ <SelectionReporter />
207
338
  <div className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">
208
339
  {/* Editorial toolbar — three zones, hairline separators, mono-folio center */}
209
340
  <header className="relative flex h-12 shrink-0 items-center justify-between border-b border-hairline bg-sidebar/85 px-2 backdrop-blur-md md:px-3">
@@ -237,6 +368,7 @@ export function Slide() {
237
368
  </TabsList>
238
369
  </Tabs>
239
370
  )}
371
+ {import.meta.env.DEV && <AgentConnectedBadge />}
240
372
  </div>
241
373
 
242
374
  {/* Centered title — the rail and mobile pill carry the page count. */}
@@ -349,56 +481,69 @@ export function Slide() {
349
481
  </div>
350
482
  ) : (
351
483
  <DesignProvider slideId={slideId}>
352
- <div className="flex min-h-0 flex-1 flex-col md:flex-row">
353
- <div className="hidden w-[16.5rem] shrink-0 md:block">
354
- <ThumbnailRail
355
- pages={pages}
356
- design={slide.design}
357
- current={index}
358
- onSelect={goTo}
359
- />
360
- </div>
361
- <main
362
- ref={slideViewportRef}
363
- data-inspector-root
364
- className="paper relative min-h-0 min-w-0 flex-1 bg-canvas p-2 md:p-10"
365
- >
366
- <SlideWheelNavigation
367
- targetRef={slideViewportRef}
368
- onPrev={() => goTo(index - 1)}
369
- onNext={() => goTo(index + 1)}
370
- canPrev={index > 0}
371
- canNext={index < pageCount - 1}
372
- />
373
- <SlideCanvas design={slide.design}>
374
- <CurrentPage />
375
- </SlideCanvas>
376
- <ClickNavZones
377
- onPrev={() => goTo(index - 1)}
378
- onNext={() => goTo(index + 1)}
379
- canPrev={index > 0}
380
- canNext={index < pageCount - 1}
381
- />
382
- <InspectOverlay />
383
- <SaveBar />
384
- {import.meta.env.DEV && <CommentWidget />}
385
- </main>
386
- {/* Mobile-only horizontal rail. Sits below the canvas and
484
+ <div className="flex min-h-0 flex-1 flex-col">
485
+ <div className="flex min-h-0 flex-1 flex-col md:flex-row">
486
+ <div className="hidden w-[16.5rem] shrink-0 md:block">
487
+ <ThumbnailRail
488
+ pages={pages}
489
+ design={slide.design}
490
+ current={index}
491
+ onSelect={goTo}
492
+ onReorder={import.meta.env.DEV ? reorderPage : undefined}
493
+ actions={thumbnailActions}
494
+ />
495
+ </div>
496
+ <main
497
+ ref={slideViewportRef}
498
+ data-inspector-root
499
+ className="paper relative min-h-0 min-w-0 flex-1 bg-canvas p-2 md:p-10"
500
+ >
501
+ <SlideWheelNavigation
502
+ targetRef={slideViewportRef}
503
+ onPrev={() => goTo(index - 1)}
504
+ onNext={() => goTo(index + 1)}
505
+ canPrev={index > 0}
506
+ canNext={index < pageCount - 1}
507
+ />
508
+ <SlideCanvas design={slide.design}>
509
+ <CurrentPage />
510
+ </SlideCanvas>
511
+ <ClickNavZones
512
+ onPrev={() => goTo(index - 1)}
513
+ onNext={() => goTo(index + 1)}
514
+ canPrev={index > 0}
515
+ canNext={index < pageCount - 1}
516
+ />
517
+ <InspectOverlay />
518
+ <SaveBar />
519
+ {import.meta.env.DEV && <CommentWidget />}
520
+ </main>
521
+ {/* Mobile-only horizontal rail. Sits below the canvas and
387
522
  pads its bottom for the iOS home indicator / Safari URL bar. */}
388
- <div
389
- className="shrink-0 border-t border-hairline md:hidden"
390
- style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}
391
- >
392
- <ThumbnailRail
393
- pages={pages}
394
- design={slide.design}
395
- current={index}
396
- onSelect={goTo}
397
- orientation="horizontal"
398
- />
523
+ <div
524
+ className="shrink-0 border-t border-hairline md:hidden"
525
+ style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}
526
+ >
527
+ <ThumbnailRail
528
+ pages={pages}
529
+ design={slide.design}
530
+ current={index}
531
+ onSelect={goTo}
532
+ orientation="horizontal"
533
+ actions={thumbnailActions}
534
+ />
535
+ </div>
536
+ <InspectorPanel />
537
+ <DesignPanel open={designOpen} onClose={() => setDesignOpen(false)} />
399
538
  </div>
400
- <InspectorPanel />
401
- <DesignPanel open={designOpen} onClose={() => setDesignOpen(false)} />
539
+ {import.meta.env.DEV && (
540
+ <NotesDrawer
541
+ slideId={slideId}
542
+ index={index}
543
+ total={pageCount}
544
+ initial={slide.notes?.[index]}
545
+ />
546
+ )}
402
547
  </div>
403
548
  </DesignProvider>
404
549
  )}
@@ -408,6 +553,48 @@ export function Slide() {
408
553
  );
409
554
  }
410
555
 
556
+ function AgentConnectedBadge() {
557
+ const t = useLocale();
558
+ return (
559
+ <TooltipProvider delayDuration={200}>
560
+ <Tooltip>
561
+ <TooltipTrigger asChild>
562
+ <button
563
+ type="button"
564
+ className="ml-1 flex shrink-0 cursor-help items-center gap-1.5 rounded-[3px] border border-hairline bg-card px-1.5 py-0.5 text-[10.5px] text-foreground/85 outline-none focus-visible:ring-2 focus-visible:ring-ring/30"
565
+ >
566
+ <span aria-hidden className="relative flex size-1.5 items-center justify-center">
567
+ <span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-500 opacity-60" />
568
+ <span className="relative inline-flex size-1.5 rounded-full bg-emerald-500" />
569
+ </span>
570
+ {t.slide.agentConnected}
571
+ </button>
572
+ </TooltipTrigger>
573
+ <TooltipContent side="bottom" align="start" className="max-w-[280px] leading-relaxed">
574
+ {t.slide.agentConnectedTooltip}
575
+ </TooltipContent>
576
+ </Tooltip>
577
+ </TooltipProvider>
578
+ );
579
+ }
580
+
581
+ function SelectionReporter() {
582
+ const { selected } = useInspector();
583
+ useEffect(() => {
584
+ if (!import.meta.hot) return;
585
+ const selection = selected
586
+ ? {
587
+ line: selected.line,
588
+ column: selected.column,
589
+ tagName: selected.anchor.tagName.toLowerCase(),
590
+ text: (selected.anchor.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 120),
591
+ }
592
+ : null;
593
+ import.meta.hot.send('open-slide:current', { selection });
594
+ }, [selected]);
595
+ return null;
596
+ }
597
+
411
598
  function SlideWheelNavigation({
412
599
  targetRef,
413
600
  onPrev,
package/src/locale/en.ts CHANGED
@@ -83,6 +83,9 @@ export const en: Locale = {
83
83
  slide: {
84
84
  home: 'Home',
85
85
  backToHome: 'Back to home',
86
+ agentConnected: 'Agent connected',
87
+ agentConnectedTooltip:
88
+ 'The dev server is publishing your current slide and inspector selection to your agent. Ask "this slide" or "this element" in chat and it will resolve. Disappears in production builds.',
86
89
  download: 'Download',
87
90
  exportAsHtml: 'Export as HTML',
88
91
  exportAsPdf: 'Export as PDF',
@@ -156,6 +159,9 @@ export const en: Locale = {
156
159
  inspector: {
157
160
  inspect: 'Inspect',
158
161
  deselect: 'Deselect',
162
+ agentWatching: 'Agent is watching',
163
+ agentWatchingTooltip:
164
+ 'Your agent already sees the selected element via the dev server — just ask it in chat. Leave comments here only when you want to queue a few before asking.',
159
165
  contentSection: 'Content',
160
166
  typographySection: 'Typography',
161
167
  colorSection: 'Color',
@@ -185,10 +191,17 @@ export const en: Locale = {
185
191
  pickerLoading: 'Loading…',
186
192
  pickerEmpty: "No images in this slide's assets folder yet. Add some from the Assets tab.",
187
193
  placeholderHintLabel: 'Hint:',
188
- noteForAgent: 'Note for the agent',
189
- noteAgentPlaceholder: 'Describe a change for the agent…',
190
- noteShortcutHint: '⌘↵ to send',
191
- addNote: 'Add note',
194
+ crop: 'Crop…',
195
+ cropDialogTitle: 'Crop image',
196
+ cropDialogDescription: 'Drag the frame to choose what stays visible.',
197
+ cropFitCover: 'Fill',
198
+ cropFitContain: 'Fit',
199
+ cropApply: 'Apply',
200
+ cropResetAria: 'Reset crop',
201
+ leaveComment: 'Leave a comment',
202
+ commentPlaceholder: 'Describe a change for the agent…',
203
+ commentShortcutHint: '⌘↵ to add',
204
+ addComment: 'Add comment',
192
205
  unsavedChanges: {
193
206
  one: '{count} unsaved change',
194
207
  other: '{count} unsaved changes',
@@ -224,6 +237,8 @@ export const en: Locale = {
224
237
  designToggle: 'Design',
225
238
  designToggleTitle: 'Design tokens',
226
239
  fontPresetCustom: 'Custom…',
240
+ shuffleAria: 'Shuffle design',
241
+ shuffleTitle: 'Shuffle for inspiration',
227
242
  },
228
243
 
229
244
  asset: {
@@ -279,6 +294,13 @@ export const en: Locale = {
279
294
  thumbnailRail: {
280
295
  pages: 'Pages',
281
296
  goToPageAria: 'Go to page {n}',
297
+ duplicatePage: 'Duplicate',
298
+ deletePage: 'Delete',
299
+ pageActionsAria: 'Page {n} actions',
300
+ toastDuplicated: 'Duplicated page {n}',
301
+ toastDeleted: 'Deleted page {n}',
302
+ toastDuplicateFailed: 'Could not duplicate page',
303
+ toastDeleteFailed: 'Could not delete page',
282
304
  },
283
305
 
284
306
  pdfToast: {
@@ -300,4 +322,13 @@ export const en: Locale = {
300
322
  prevAria: 'Previous page',
301
323
  nextAria: 'Next page',
302
324
  },
325
+
326
+ notesDrawer: {
327
+ toggle: 'Notes',
328
+ pageLabel: 'page {n}/{total}',
329
+ placeholder: 'Write speaker notes for this slide…',
330
+ statusSaving: 'Saving…',
331
+ statusSaved: 'Saved',
332
+ statusError: 'Save failed: {msg}',
333
+ },
303
334
  };
package/src/locale/ja.ts CHANGED
@@ -81,6 +81,9 @@ export const ja: Locale = {
81
81
  },
82
82
 
83
83
  slide: {
84
+ agentConnected: 'エージェント接続中',
85
+ agentConnectedTooltip:
86
+ '現在のスライドと Inspector の選択状態を dev server がエージェントに公開しています。チャットで「このスライド」「この要素」と言えば認識されます。本番ビルドでは表示されません。',
84
87
  home: 'ホーム',
85
88
  backToHome: 'ホームへ戻る',
86
89
  download: 'ダウンロード',
@@ -186,10 +189,20 @@ export const ja: Locale = {
186
189
  pickerEmpty:
187
190
  'このスライドのアセットフォルダにまだ画像がありません。「アセット」タブから追加してください。',
188
191
  placeholderHintLabel: 'ヒント:',
189
- noteForAgent: 'エージェントへのメモ',
190
- noteAgentPlaceholder: 'エージェントに依頼する変更を記述…',
191
- noteShortcutHint: '⌘↵ で送信',
192
- addNote: 'メモを追加',
192
+ crop: 'トリミング…',
193
+ cropDialogTitle: '画像をトリミング',
194
+ cropDialogDescription: '枠をドラッグして表示する範囲を選択します。',
195
+ cropFitCover: '塗りつぶす',
196
+ cropFitContain: '全体表示',
197
+ cropApply: '適用',
198
+ cropResetAria: 'トリミングをリセット',
199
+ agentWatching: 'エージェント監視中',
200
+ agentWatchingTooltip:
201
+ 'エージェントは選択中の要素を dev server 経由で把握しています。直接チャットで頼めます。ここにコメントを残すのは、複数の依頼をまとめて出したいときだけで OK。',
202
+ leaveComment: 'コメントを残す',
203
+ commentPlaceholder: 'エージェントに依頼する変更を記述…',
204
+ commentShortcutHint: '⌘↵ で追加',
205
+ addComment: 'コメントを追加',
193
206
  unsavedChanges: {
194
207
  one: '未保存の変更 {count} 件',
195
208
  other: '未保存の変更 {count} 件',
@@ -226,6 +239,8 @@ export const ja: Locale = {
226
239
  designToggle: 'デザイン',
227
240
  designToggleTitle: 'デザイントークン',
228
241
  fontPresetCustom: 'カスタム…',
242
+ shuffleAria: 'デザインをシャッフル',
243
+ shuffleTitle: 'シャッフルしてインスピレーションを得る',
229
244
  },
230
245
 
231
246
  asset: {
@@ -283,6 +298,13 @@ export const ja: Locale = {
283
298
  thumbnailRail: {
284
299
  pages: 'ページ',
285
300
  goToPageAria: 'ページ {n} へ移動',
301
+ duplicatePage: '複製',
302
+ deletePage: '削除',
303
+ pageActionsAria: 'ページ {n} の操作',
304
+ toastDuplicated: 'ページ {n} を複製しました',
305
+ toastDeleted: 'ページ {n} を削除しました',
306
+ toastDuplicateFailed: 'ページを複製できませんでした',
307
+ toastDeleteFailed: 'ページを削除できませんでした',
286
308
  },
287
309
 
288
310
  pdfToast: {
@@ -304,4 +326,13 @@ export const ja: Locale = {
304
326
  prevAria: '前のページ',
305
327
  nextAria: '次のページ',
306
328
  },
329
+
330
+ notesDrawer: {
331
+ toggle: '発表者ノート',
332
+ pageLabel: '{n} / {total} ページ',
333
+ placeholder: 'このスライドの発表者ノートを記入…',
334
+ statusSaving: '保存中…',
335
+ statusSaved: '保存済み',
336
+ statusError: '保存に失敗しました: {msg}',
337
+ },
307
338
  };
@@ -86,6 +86,8 @@ export type Locale = {
86
86
  slide: {
87
87
  home: string;
88
88
  backToHome: string;
89
+ agentConnected: string;
90
+ agentConnectedTooltip: string;
89
91
  download: string;
90
92
  exportAsHtml: string;
91
93
  exportAsPdf: string;
@@ -161,6 +163,8 @@ export type Locale = {
161
163
  inspector: {
162
164
  inspect: string;
163
165
  deselect: string;
166
+ agentWatching: string;
167
+ agentWatchingTooltip: string;
164
168
  contentSection: string;
165
169
  typographySection: string;
166
170
  colorSection: string;
@@ -191,10 +195,17 @@ export type Locale = {
191
195
  pickerLoading: string;
192
196
  pickerEmpty: string;
193
197
  placeholderHintLabel: string;
194
- noteForAgent: string;
195
- noteAgentPlaceholder: string;
196
- noteShortcutHint: string;
197
- addNote: string;
198
+ crop: string;
199
+ cropDialogTitle: string;
200
+ cropDialogDescription: string;
201
+ cropFitCover: string;
202
+ cropFitContain: string;
203
+ cropApply: string;
204
+ cropResetAria: string;
205
+ leaveComment: string;
206
+ commentPlaceholder: string;
207
+ commentShortcutHint: string;
208
+ addComment: string;
198
209
  /** templates: "{count} unsaved change" / "{count} unsaved changes" */
199
210
  unsavedChanges: Plural;
200
211
  /** templates: "{count} comment" / "{count} comments" */
@@ -228,6 +239,8 @@ export type Locale = {
228
239
  designToggle: string;
229
240
  designToggleTitle: string;
230
241
  fontPresetCustom: string;
242
+ shuffleAria: string;
243
+ shuffleTitle: string;
231
244
  };
232
245
 
233
246
  asset: {
@@ -298,6 +311,16 @@ export type Locale = {
298
311
  pages: string;
299
312
  /** template: "Go to page {n}" */
300
313
  goToPageAria: string;
314
+ duplicatePage: string;
315
+ deletePage: string;
316
+ /** template: "Page {n} actions" */
317
+ pageActionsAria: string;
318
+ /** template: "Duplicated page {n}" */
319
+ toastDuplicated: string;
320
+ /** template: "Deleted page {n}" */
321
+ toastDeleted: string;
322
+ toastDuplicateFailed: string;
323
+ toastDeleteFailed: string;
301
324
  };
302
325
 
303
326
  pdfToast: {
@@ -320,4 +343,15 @@ export type Locale = {
320
343
  prevAria: string;
321
344
  nextAria: string;
322
345
  };
346
+
347
+ notesDrawer: {
348
+ toggle: string;
349
+ /** template: "page {n}/{total}" */
350
+ pageLabel: string;
351
+ placeholder: string;
352
+ statusSaving: string;
353
+ statusSaved: string;
354
+ /** template: "Save failed: {msg}" */
355
+ statusError: string;
356
+ };
323
357
  };
@@ -81,6 +81,9 @@ export const zhCN: Locale = {
81
81
  },
82
82
 
83
83
  slide: {
84
+ agentConnected: 'Agent 已连接',
85
+ agentConnectedTooltip:
86
+ 'Dev server 正在把你目前在哪张 slide、Inspector 选了哪个元素发布给 agent。直接到聊天说"这张 slide"或"这个元素"就行。Production build 不会出现。',
84
87
  home: '首页',
85
88
  backToHome: '返回首页',
86
89
  download: '下载',
@@ -185,10 +188,20 @@ export const zhCN: Locale = {
185
188
  pickerLoading: '加载中…',
186
189
  pickerEmpty: '该幻灯片的素材文件夹中尚无图片。请从「素材」标签页添加。',
187
190
  placeholderHintLabel: '提示:',
188
- noteForAgent: '给代理的备注',
189
- noteAgentPlaceholder: '描述你希望代理执行的更改…',
190
- noteShortcutHint: '⌘↵ 发送',
191
- addNote: '添加备注',
191
+ crop: '裁剪…',
192
+ cropDialogTitle: '裁剪图片',
193
+ cropDialogDescription: '拖动框线决定要保留的可见区域。',
194
+ cropFitCover: '填满',
195
+ cropFitContain: '完整显示',
196
+ cropApply: '应用',
197
+ cropResetAria: '重置裁剪',
198
+ agentWatching: 'Agent 正在关注',
199
+ agentWatchingTooltip:
200
+ 'Agent 已经通过 dev server 看到你选的元素了,直接到聊天请它修改就行。想累积几个再一次问才需要在这里留 comments。',
201
+ leaveComment: '留个 comment',
202
+ commentPlaceholder: '描述你希望代理执行的更改…',
203
+ commentShortcutHint: '⌘↵ 添加',
204
+ addComment: '添加 comment',
192
205
  unsavedChanges: {
193
206
  one: '{count} 项未保存的更改',
194
207
  other: '{count} 项未保存的更改',
@@ -224,6 +237,8 @@ export const zhCN: Locale = {
224
237
  designToggle: '设计',
225
238
  designToggleTitle: '设计样式',
226
239
  fontPresetCustom: '自定义…',
240
+ shuffleAria: '随机设计',
241
+ shuffleTitle: '随机配色获取灵感',
227
242
  },
228
243
 
229
244
  asset: {
@@ -279,6 +294,13 @@ export const zhCN: Locale = {
279
294
  thumbnailRail: {
280
295
  pages: '页面',
281
296
  goToPageAria: '前往第 {n} 页',
297
+ duplicatePage: '复制',
298
+ deletePage: '删除',
299
+ pageActionsAria: '第 {n} 页的操作',
300
+ toastDuplicated: '已复制第 {n} 页',
301
+ toastDeleted: '已删除第 {n} 页',
302
+ toastDuplicateFailed: '无法复制页面',
303
+ toastDeleteFailed: '无法删除页面',
282
304
  },
283
305
 
284
306
  pdfToast: {
@@ -300,4 +322,13 @@ export const zhCN: Locale = {
300
322
  prevAria: '上一页',
301
323
  nextAria: '下一页',
302
324
  },
325
+
326
+ notesDrawer: {
327
+ toggle: '演讲备注',
328
+ pageLabel: '第 {n} / {total} 页',
329
+ placeholder: '为这一页撰写演讲备注…',
330
+ statusSaving: '保存中…',
331
+ statusSaved: '已保存',
332
+ statusError: '保存失败:{msg}',
333
+ },
303
334
  };