@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.
- package/dist/{build-4wOJF1l4.js → build-6BeQ3cxb.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-evLWCV1-.js → config-AxZ5OE1u.js} +772 -201
- package/dist/{config-D2y1AXaN.d.ts → config-CtT8K4VF.d.ts} +1 -1
- package/dist/{dev-BUr0S-Ij.js → dev-C9eLmUEq.js} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/locale/index.d.ts +1 -1
- package/dist/locale/index.js +136 -24
- package/dist/{preview-DP_gIphz.js → preview-Cunm-f4i.js} +1 -1
- package/dist/{types-BVvl_xup.d.ts → types-CRHIeoNq.d.ts} +37 -4
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +1 -1
- package/package.json +5 -1
- package/skills/current-slide/SKILL.md +110 -0
- package/skills/slide-authoring/SKILL.md +48 -1
- package/src/app/components/inspector/image-crop-dialog.tsx +212 -0
- package/src/app/components/inspector/inspect-overlay.tsx +17 -2
- package/src/app/components/inspector/inspector-panel.tsx +90 -26
- package/src/app/components/inspector/inspector-provider.tsx +136 -1
- package/src/app/components/notes-drawer.tsx +117 -0
- package/src/app/components/player.tsx +26 -8
- package/src/app/components/present/overview-grid.tsx +2 -2
- package/src/app/components/present/use-idle.ts +6 -4
- package/src/app/components/style-panel/design-provider.tsx +13 -0
- package/src/app/components/style-panel/style-panel.tsx +23 -11
- package/src/app/components/thumbnail-rail.tsx +317 -55
- package/src/app/components/ui/context-menu.tsx +237 -0
- package/src/app/lib/design-presets.ts +94 -0
- package/src/app/lib/inspector/use-notes.ts +134 -0
- package/src/app/routes/home.tsx +34 -12
- package/src/app/routes/presenter.tsx +27 -24
- package/src/app/routes/slide.tsx +238 -51
- package/src/locale/en.ts +35 -4
- package/src/locale/ja.ts +35 -4
- package/src/locale/types.ts +38 -4
- package/src/locale/zh-cn.ts +35 -4
- package/src/locale/zh-tw.ts +35 -4
package/src/app/routes/slide.tsx
CHANGED
|
@@ -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
|
|
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
|
|
353
|
-
<div className="
|
|
354
|
-
<
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
401
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
};
|
package/src/locale/types.ts
CHANGED
|
@@ -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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
};
|
package/src/locale/zh-cn.ts
CHANGED
|
@@ -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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
};
|