@open-slide/core 1.1.0 → 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-DSqSio-T.js → build-6BeQ3cxb.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-KdiYeWtK.js → config-AxZ5OE1u.js} +673 -211
- package/dist/{config-C7vMYzFD.d.ts → config-CtT8K4VF.d.ts} +1 -1
- package/dist/{dev-B_GVbr11.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 +96 -20
- package/dist/{preview-D_mxhj7w.js → preview-Cunm-f4i.js} +1 -1
- package/dist/{types-DYgVpIGo.d.ts → types-CRHIeoNq.d.ts} +28 -4
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +1 -1
- package/package.json +1 -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 +64 -20
- package/src/app/components/inspector/inspector-panel.tsx +44 -13
- package/src/app/components/inspector/inspector-provider.tsx +60 -7
- package/src/app/components/notes-drawer.tsx +117 -0
- package/src/app/components/player.tsx +11 -7
- package/src/app/components/present/overview-grid.tsx +2 -2
- package/src/app/components/thumbnail-rail.tsx +119 -24
- package/src/app/components/ui/context-menu.tsx +237 -0
- package/src/app/lib/inspector/use-notes.ts +134 -0
- package/src/app/routes/home.tsx +34 -12
- package/src/app/routes/slide.tsx +209 -74
- package/src/locale/en.ts +26 -4
- package/src/locale/ja.ts +26 -4
- package/src/locale/types.ts +29 -4
- package/src/locale/zh-cn.ts +26 -4
- package/src/locale/zh-tw.ts +26 -4
package/src/app/routes/home.tsx
CHANGED
|
@@ -27,7 +27,8 @@ import type { Folder, FolderIcon, SlideModule } from '../lib/sdk';
|
|
|
27
27
|
import { loadSlide, slideIds } from '../lib/slides';
|
|
28
28
|
|
|
29
29
|
export function Home() {
|
|
30
|
-
const { manifest, create, update, remove, assign, renameSlide, deleteSlide } =
|
|
30
|
+
const { manifest, loading, create, update, remove, assign, renameSlide, deleteSlide } =
|
|
31
|
+
useFolders();
|
|
31
32
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
32
33
|
const selectedId = searchParams.get('f') ?? DRAFT_ID;
|
|
33
34
|
const t = useLocale();
|
|
@@ -168,23 +169,27 @@ export function Home() {
|
|
|
168
169
|
<h1 className="font-heading text-[32px] font-semibold leading-[1.05] tracking-[-0.025em] md:text-[44px]">
|
|
169
170
|
{title}
|
|
170
171
|
</h1>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
{!loading && (
|
|
173
|
+
<span className="folio ml-1 self-end pb-2">
|
|
174
|
+
{(isSearching ? filteredSlides.length : visibleSlides.length)
|
|
175
|
+
.toString()
|
|
176
|
+
.padStart(2, '0')}
|
|
177
|
+
{isSearching && (
|
|
178
|
+
<span className="opacity-40">
|
|
179
|
+
/{visibleSlides.length.toString().padStart(2, '0')}
|
|
180
|
+
</span>
|
|
181
|
+
)}
|
|
182
|
+
</span>
|
|
183
|
+
)}
|
|
181
184
|
<div className="ml-auto w-full md:w-auto">
|
|
182
185
|
<SearchInput value={query} onChange={setQuery} />
|
|
183
186
|
</div>
|
|
184
187
|
</div>
|
|
185
188
|
</header>
|
|
186
189
|
|
|
187
|
-
{
|
|
190
|
+
{loading ? (
|
|
191
|
+
<HomeLoading />
|
|
192
|
+
) : visibleSlides.length === 0 ? (
|
|
188
193
|
<EmptyState isDraft={isDraft} folderName={selectedFolder?.name} />
|
|
189
194
|
) : filteredSlides.length === 0 ? (
|
|
190
195
|
<NoResultsState query={query} onClear={() => setQuery('')} />
|
|
@@ -271,6 +276,23 @@ function SearchInput({ value, onChange }: { value: string; onChange: (value: str
|
|
|
271
276
|
);
|
|
272
277
|
}
|
|
273
278
|
|
|
279
|
+
function HomeLoading() {
|
|
280
|
+
const t = useLocale();
|
|
281
|
+
return (
|
|
282
|
+
<div className="grid place-items-center px-8 py-24 text-muted-foreground">
|
|
283
|
+
<div className="flex flex-col items-center gap-4">
|
|
284
|
+
<div className="relative h-px w-56 overflow-hidden bg-hairline">
|
|
285
|
+
<span
|
|
286
|
+
aria-hidden
|
|
287
|
+
className="line-loader-bar absolute inset-y-[-0.5px] left-0 w-1/4 bg-foreground"
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
<span className="eyebrow text-[11.5px]">{t.slide.loadingEyebrow}</span>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
274
296
|
function NoResultsState({ query, onClear }: { query: string; onClear: () => void }) {
|
|
275
297
|
const t = useLocale();
|
|
276
298
|
return (
|
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
|
|
|
@@ -78,6 +81,33 @@ export function Slide() {
|
|
|
78
81
|
const index = Number.isFinite(rawIndex) ? Math.max(0, Math.min(pageCount - 1, rawIndex)) : 0;
|
|
79
82
|
const view = searchParams.get('view') === 'assets' ? 'assets' : 'slides';
|
|
80
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
|
+
|
|
96
|
+
const goTo = useCallback(
|
|
97
|
+
(i: number) => {
|
|
98
|
+
const clamped = Math.max(0, Math.min(pageCount - 1, i));
|
|
99
|
+
setSearchParams(
|
|
100
|
+
(prev) => {
|
|
101
|
+
const next = new URLSearchParams(prev);
|
|
102
|
+
next.set('p', String(clamped + 1));
|
|
103
|
+
return next;
|
|
104
|
+
},
|
|
105
|
+
{ replace: true },
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
[pageCount, setSearchParams],
|
|
109
|
+
);
|
|
110
|
+
|
|
81
111
|
const reorderPage = useCallback(
|
|
82
112
|
async (from: number, to: number) => {
|
|
83
113
|
if (from === to) return;
|
|
@@ -91,21 +121,14 @@ export function Slide() {
|
|
|
91
121
|
const [movedIdx] = order.splice(from, 1);
|
|
92
122
|
order.splice(to, 0, movedIdx);
|
|
93
123
|
|
|
124
|
+
remapNotesSessionCacheAfterReorder(slideId, order);
|
|
125
|
+
|
|
94
126
|
// Keep the user looking at the same page they were on before the drag.
|
|
95
127
|
let nextIndex = index;
|
|
96
128
|
if (index === from) nextIndex = to;
|
|
97
129
|
else if (from < index && to >= index) nextIndex = index - 1;
|
|
98
130
|
else if (from > index && to <= index) nextIndex = index + 1;
|
|
99
|
-
if (nextIndex !== index)
|
|
100
|
-
setSearchParams(
|
|
101
|
-
(prev) => {
|
|
102
|
-
const params = new URLSearchParams(prev);
|
|
103
|
-
params.set('p', String(nextIndex + 1));
|
|
104
|
-
return params;
|
|
105
|
-
},
|
|
106
|
-
{ replace: true },
|
|
107
|
-
);
|
|
108
|
-
}
|
|
131
|
+
if (nextIndex !== index) goTo(nextIndex);
|
|
109
132
|
|
|
110
133
|
try {
|
|
111
134
|
const res = await fetch(`/__slides/${encodeURIComponent(slideId)}/reorder`, {
|
|
@@ -119,25 +142,81 @@ export function Slide() {
|
|
|
119
142
|
}
|
|
120
143
|
} catch (err) {
|
|
121
144
|
setPages(before);
|
|
145
|
+
const inverse = order.map((_, i) => order.indexOf(i));
|
|
146
|
+
remapNotesSessionCacheAfterReorder(slideId, inverse);
|
|
122
147
|
toast.error(`Reorder failed: ${String((err as Error).message ?? err)}`);
|
|
123
148
|
}
|
|
124
149
|
},
|
|
125
|
-
[pages, index, slideId,
|
|
150
|
+
[pages, index, slideId, goTo],
|
|
126
151
|
);
|
|
127
152
|
|
|
128
|
-
const
|
|
129
|
-
(i: number) => {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
}
|
|
139
177
|
},
|
|
140
|
-
[
|
|
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],
|
|
141
220
|
);
|
|
142
221
|
|
|
143
222
|
useEffect(() => {
|
|
@@ -255,6 +334,7 @@ export function Slide() {
|
|
|
255
334
|
return (
|
|
256
335
|
<HistoryProvider>
|
|
257
336
|
<InspectorProvider slideId={slideId}>
|
|
337
|
+
<SelectionReporter />
|
|
258
338
|
<div className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">
|
|
259
339
|
{/* Editorial toolbar — three zones, hairline separators, mono-folio center */}
|
|
260
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">
|
|
@@ -288,6 +368,7 @@ export function Slide() {
|
|
|
288
368
|
</TabsList>
|
|
289
369
|
</Tabs>
|
|
290
370
|
)}
|
|
371
|
+
{import.meta.env.DEV && <AgentConnectedBadge />}
|
|
291
372
|
</div>
|
|
292
373
|
|
|
293
374
|
{/* Centered title — the rail and mobile pill carry the page count. */}
|
|
@@ -400,57 +481,69 @@ export function Slide() {
|
|
|
400
481
|
</div>
|
|
401
482
|
) : (
|
|
402
483
|
<DesignProvider slideId={slideId}>
|
|
403
|
-
<div className="flex min-h-0 flex-1 flex-col
|
|
404
|
-
<div className="
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
|
439
522
|
pads its bottom for the iOS home indicator / Safari URL bar. */}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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)} />
|
|
451
538
|
</div>
|
|
452
|
-
|
|
453
|
-
|
|
539
|
+
{import.meta.env.DEV && (
|
|
540
|
+
<NotesDrawer
|
|
541
|
+
slideId={slideId}
|
|
542
|
+
index={index}
|
|
543
|
+
total={pageCount}
|
|
544
|
+
initial={slide.notes?.[index]}
|
|
545
|
+
/>
|
|
546
|
+
)}
|
|
454
547
|
</div>
|
|
455
548
|
</DesignProvider>
|
|
456
549
|
)}
|
|
@@ -460,6 +553,48 @@ export function Slide() {
|
|
|
460
553
|
);
|
|
461
554
|
}
|
|
462
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
|
+
|
|
463
598
|
function SlideWheelNavigation({
|
|
464
599
|
targetRef,
|
|
465
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',
|
|
@@ -192,10 +198,10 @@ export const en: Locale = {
|
|
|
192
198
|
cropFitContain: 'Fit',
|
|
193
199
|
cropApply: 'Apply',
|
|
194
200
|
cropResetAria: 'Reset crop',
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
leaveComment: 'Leave a comment',
|
|
202
|
+
commentPlaceholder: 'Describe a change for the agent…',
|
|
203
|
+
commentShortcutHint: '⌘↵ to add',
|
|
204
|
+
addComment: 'Add comment',
|
|
199
205
|
unsavedChanges: {
|
|
200
206
|
one: '{count} unsaved change',
|
|
201
207
|
other: '{count} unsaved changes',
|
|
@@ -288,6 +294,13 @@ export const en: Locale = {
|
|
|
288
294
|
thumbnailRail: {
|
|
289
295
|
pages: 'Pages',
|
|
290
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',
|
|
291
304
|
},
|
|
292
305
|
|
|
293
306
|
pdfToast: {
|
|
@@ -309,4 +322,13 @@ export const en: Locale = {
|
|
|
309
322
|
prevAria: 'Previous page',
|
|
310
323
|
nextAria: 'Next page',
|
|
311
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
|
+
},
|
|
312
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: 'ダウンロード',
|
|
@@ -193,10 +196,13 @@ export const ja: Locale = {
|
|
|
193
196
|
cropFitContain: '全体表示',
|
|
194
197
|
cropApply: '適用',
|
|
195
198
|
cropResetAria: 'トリミングをリセット',
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
agentWatching: 'エージェント監視中',
|
|
200
|
+
agentWatchingTooltip:
|
|
201
|
+
'エージェントは選択中の要素を dev server 経由で把握しています。直接チャットで頼めます。ここにコメントを残すのは、複数の依頼をまとめて出したいときだけで OK。',
|
|
202
|
+
leaveComment: 'コメントを残す',
|
|
203
|
+
commentPlaceholder: 'エージェントに依頼する変更を記述…',
|
|
204
|
+
commentShortcutHint: '⌘↵ で追加',
|
|
205
|
+
addComment: 'コメントを追加',
|
|
200
206
|
unsavedChanges: {
|
|
201
207
|
one: '未保存の変更 {count} 件',
|
|
202
208
|
other: '未保存の変更 {count} 件',
|
|
@@ -292,6 +298,13 @@ export const ja: Locale = {
|
|
|
292
298
|
thumbnailRail: {
|
|
293
299
|
pages: 'ページ',
|
|
294
300
|
goToPageAria: 'ページ {n} へ移動',
|
|
301
|
+
duplicatePage: '複製',
|
|
302
|
+
deletePage: '削除',
|
|
303
|
+
pageActionsAria: 'ページ {n} の操作',
|
|
304
|
+
toastDuplicated: 'ページ {n} を複製しました',
|
|
305
|
+
toastDeleted: 'ページ {n} を削除しました',
|
|
306
|
+
toastDuplicateFailed: 'ページを複製できませんでした',
|
|
307
|
+
toastDeleteFailed: 'ページを削除できませんでした',
|
|
295
308
|
},
|
|
296
309
|
|
|
297
310
|
pdfToast: {
|
|
@@ -313,4 +326,13 @@ export const ja: Locale = {
|
|
|
313
326
|
prevAria: '前のページ',
|
|
314
327
|
nextAria: '次のページ',
|
|
315
328
|
},
|
|
329
|
+
|
|
330
|
+
notesDrawer: {
|
|
331
|
+
toggle: '発表者ノート',
|
|
332
|
+
pageLabel: '{n} / {total} ページ',
|
|
333
|
+
placeholder: 'このスライドの発表者ノートを記入…',
|
|
334
|
+
statusSaving: '保存中…',
|
|
335
|
+
statusSaved: '保存済み',
|
|
336
|
+
statusError: '保存に失敗しました: {msg}',
|
|
337
|
+
},
|
|
316
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;
|
|
@@ -198,10 +202,10 @@ export type Locale = {
|
|
|
198
202
|
cropFitContain: string;
|
|
199
203
|
cropApply: string;
|
|
200
204
|
cropResetAria: string;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
leaveComment: string;
|
|
206
|
+
commentPlaceholder: string;
|
|
207
|
+
commentShortcutHint: string;
|
|
208
|
+
addComment: string;
|
|
205
209
|
/** templates: "{count} unsaved change" / "{count} unsaved changes" */
|
|
206
210
|
unsavedChanges: Plural;
|
|
207
211
|
/** templates: "{count} comment" / "{count} comments" */
|
|
@@ -307,6 +311,16 @@ export type Locale = {
|
|
|
307
311
|
pages: string;
|
|
308
312
|
/** template: "Go to page {n}" */
|
|
309
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;
|
|
310
324
|
};
|
|
311
325
|
|
|
312
326
|
pdfToast: {
|
|
@@ -329,4 +343,15 @@ export type Locale = {
|
|
|
329
343
|
prevAria: string;
|
|
330
344
|
nextAria: string;
|
|
331
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
|
+
};
|
|
332
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: '下载',
|
|
@@ -192,10 +195,13 @@ export const zhCN: Locale = {
|
|
|
192
195
|
cropFitContain: '完整显示',
|
|
193
196
|
cropApply: '应用',
|
|
194
197
|
cropResetAria: '重置裁剪',
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
agentWatching: 'Agent 正在关注',
|
|
199
|
+
agentWatchingTooltip:
|
|
200
|
+
'Agent 已经通过 dev server 看到你选的元素了,直接到聊天请它修改就行。想累积几个再一次问才需要在这里留 comments。',
|
|
201
|
+
leaveComment: '留个 comment',
|
|
202
|
+
commentPlaceholder: '描述你希望代理执行的更改…',
|
|
203
|
+
commentShortcutHint: '⌘↵ 添加',
|
|
204
|
+
addComment: '添加 comment',
|
|
199
205
|
unsavedChanges: {
|
|
200
206
|
one: '{count} 项未保存的更改',
|
|
201
207
|
other: '{count} 项未保存的更改',
|
|
@@ -288,6 +294,13 @@ export const zhCN: Locale = {
|
|
|
288
294
|
thumbnailRail: {
|
|
289
295
|
pages: '页面',
|
|
290
296
|
goToPageAria: '前往第 {n} 页',
|
|
297
|
+
duplicatePage: '复制',
|
|
298
|
+
deletePage: '删除',
|
|
299
|
+
pageActionsAria: '第 {n} 页的操作',
|
|
300
|
+
toastDuplicated: '已复制第 {n} 页',
|
|
301
|
+
toastDeleted: '已删除第 {n} 页',
|
|
302
|
+
toastDuplicateFailed: '无法复制页面',
|
|
303
|
+
toastDeleteFailed: '无法删除页面',
|
|
291
304
|
},
|
|
292
305
|
|
|
293
306
|
pdfToast: {
|
|
@@ -309,4 +322,13 @@ export const zhCN: Locale = {
|
|
|
309
322
|
prevAria: '上一页',
|
|
310
323
|
nextAria: '下一页',
|
|
311
324
|
},
|
|
325
|
+
|
|
326
|
+
notesDrawer: {
|
|
327
|
+
toggle: '演讲备注',
|
|
328
|
+
pageLabel: '第 {n} / {total} 页',
|
|
329
|
+
placeholder: '为这一页撰写演讲备注…',
|
|
330
|
+
statusSaving: '保存中…',
|
|
331
|
+
statusSaved: '已保存',
|
|
332
|
+
statusError: '保存失败:{msg}',
|
|
333
|
+
},
|
|
312
334
|
};
|