@open-slide/core 1.4.0 → 1.5.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-1Rqivz0d.js → build-DZhbjQpQ.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-s0YUbmUe.d.ts → config-BQdTMho4.d.ts} +1 -1
- package/dist/{config-XZJnC_fu.js → config-iKjqaX08.js} +2045 -1631
- package/dist/{dev-0W8gYiSa.js → dev-BjLGk5nN.js} +1 -1
- package/dist/{en-7GU-DHbJ.js → en-DDGqyNaW.js} +14 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/locale/index.d.ts +1 -1
- package/dist/locale/index.js +43 -4
- package/dist/{preview-DT9hJvzM.js → preview-jwLWHWkQ.js} +1 -1
- package/dist/{types-QCpkHkiS.d.ts → types-Dpr8nbih.d.ts} +16 -0
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +1 -1
- package/package.json +1 -1
- package/skills/slide-authoring/SKILL.md +9 -2
- package/src/app/components/asset-view.tsx +75 -9
- package/src/app/components/inspector/inspector-panel.tsx +16 -1
- package/src/app/components/panel/panel-shell.tsx +5 -3
- package/src/app/components/present/laser-pointer.tsx +3 -4
- package/src/app/components/present/progress-bar.tsx +4 -4
- package/src/app/lib/assets.ts +21 -0
- package/src/app/lib/sdk.ts +2 -0
- package/src/app/lib/slides.ts +2 -0
- package/src/app/routes/home.tsx +101 -3
- package/src/app/routes/slide.tsx +45 -0
- package/src/app/virtual.d.ts +1 -0
- package/src/locale/en.ts +14 -1
- package/src/locale/ja.ts +14 -1
- package/src/locale/types.ts +16 -0
- package/src/locale/zh-cn.ts +14 -1
- package/src/locale/zh-tw.ts +14 -1
package/src/app/routes/home.tsx
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ArrowDownAZ,
|
|
3
|
+
ChevronDown,
|
|
4
|
+
Clock,
|
|
2
5
|
FolderInput,
|
|
3
6
|
FolderPlus,
|
|
4
7
|
MoreHorizontal,
|
|
@@ -31,9 +34,38 @@ import { FolderIconChip, SLIDE_DND_MIME } from '../components/sidebar/folder-ite
|
|
|
31
34
|
import { DRAFT_ID } from '../components/sidebar/sidebar';
|
|
32
35
|
import { SlideCanvas } from '../components/slide-canvas';
|
|
33
36
|
import type { Folder, FolderIcon, SlideModule } from '../lib/sdk';
|
|
34
|
-
import { loadSlide } from '../lib/slides';
|
|
37
|
+
import { loadSlide, slideCreatedAt } from '../lib/slides';
|
|
35
38
|
import type { HomeOutletContext } from './home-shell';
|
|
36
39
|
|
|
40
|
+
type SortKey = 'created-desc' | 'created-asc' | 'title-asc' | 'title-desc';
|
|
41
|
+
|
|
42
|
+
const SORT_KEYS: readonly SortKey[] = ['created-desc', 'created-asc', 'title-asc', 'title-desc'];
|
|
43
|
+
|
|
44
|
+
const DEFAULT_SORT: SortKey = 'created-desc';
|
|
45
|
+
const SORT_STORAGE_KEY = 'open-slide:home-sort';
|
|
46
|
+
|
|
47
|
+
function readSortPref(): SortKey {
|
|
48
|
+
if (typeof window === 'undefined') return DEFAULT_SORT;
|
|
49
|
+
try {
|
|
50
|
+
const raw = window.localStorage.getItem(SORT_STORAGE_KEY);
|
|
51
|
+
if (raw && (SORT_KEYS as readonly string[]).includes(raw)) return raw as SortKey;
|
|
52
|
+
} catch {}
|
|
53
|
+
return DEFAULT_SORT;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function useSortPref(): [SortKey, (next: SortKey) => void] {
|
|
57
|
+
const [sortKey, setSortKey] = useState<SortKey>(readSortPref);
|
|
58
|
+
const update = (next: SortKey) => {
|
|
59
|
+
setSortKey(next);
|
|
60
|
+
try {
|
|
61
|
+
window.localStorage.setItem(SORT_STORAGE_KEY, next);
|
|
62
|
+
} catch {}
|
|
63
|
+
};
|
|
64
|
+
return [sortKey, update];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const TITLE_COLLATOR = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true });
|
|
68
|
+
|
|
37
69
|
export function Home() {
|
|
38
70
|
const {
|
|
39
71
|
manifest,
|
|
@@ -58,6 +90,7 @@ export function Home() {
|
|
|
58
90
|
const isDraft = selectedId === DRAFT_ID;
|
|
59
91
|
|
|
60
92
|
const [query, setQuery] = useState('');
|
|
93
|
+
const [sortKey, setSortKey] = useSortPref();
|
|
61
94
|
|
|
62
95
|
const trimmedQuery = query.trim().toLowerCase();
|
|
63
96
|
const filteredSlides = useMemo(() => {
|
|
@@ -68,6 +101,24 @@ export function Home() {
|
|
|
68
101
|
return tl ? tl.includes(trimmedQuery) : false;
|
|
69
102
|
});
|
|
70
103
|
}, [visibleSlides, titleMap, trimmedQuery]);
|
|
104
|
+
const sortedSlides = useMemo(() => {
|
|
105
|
+
const list = filteredSlides.slice();
|
|
106
|
+
const titleOf = (id: string) => titleMap[id] ?? id;
|
|
107
|
+
switch (sortKey) {
|
|
108
|
+
case 'title-asc':
|
|
109
|
+
list.sort((a, b) => TITLE_COLLATOR.compare(titleOf(a), titleOf(b)));
|
|
110
|
+
break;
|
|
111
|
+
case 'title-desc':
|
|
112
|
+
list.sort((a, b) => TITLE_COLLATOR.compare(titleOf(b), titleOf(a)));
|
|
113
|
+
break;
|
|
114
|
+
case 'created-asc':
|
|
115
|
+
list.sort((a, b) => (slideCreatedAt[a] ?? 0) - (slideCreatedAt[b] ?? 0));
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
list.sort((a, b) => (slideCreatedAt[b] ?? 0) - (slideCreatedAt[a] ?? 0));
|
|
119
|
+
}
|
|
120
|
+
return list;
|
|
121
|
+
}, [filteredSlides, sortKey, titleMap]);
|
|
71
122
|
const isSearching = trimmedQuery.length > 0;
|
|
72
123
|
|
|
73
124
|
return (
|
|
@@ -90,7 +141,8 @@ export function Home() {
|
|
|
90
141
|
)}
|
|
91
142
|
</span>
|
|
92
143
|
)}
|
|
93
|
-
<div className="ml-auto w-full md:w-auto">
|
|
144
|
+
<div className="ml-auto flex w-full items-center gap-2 md:w-auto">
|
|
145
|
+
<SortControl value={sortKey} onChange={setSortKey} />
|
|
94
146
|
<SearchInput value={query} onChange={setQuery} />
|
|
95
147
|
</div>
|
|
96
148
|
</div>
|
|
@@ -104,7 +156,7 @@ export function Home() {
|
|
|
104
156
|
<NoResultsState query={query} onClear={() => setQuery('')} />
|
|
105
157
|
) : (
|
|
106
158
|
<ul className="grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] gap-x-6 gap-y-9 md:grid-cols-[repeat(auto-fill,minmax(300px,1fr))]">
|
|
107
|
-
{
|
|
159
|
+
{sortedSlides.map((id) => (
|
|
108
160
|
<li key={id}>
|
|
109
161
|
<SlideCard
|
|
110
162
|
id={id}
|
|
@@ -152,6 +204,52 @@ function SearchInput({ value, onChange }: { value: string; onChange: (value: str
|
|
|
152
204
|
);
|
|
153
205
|
}
|
|
154
206
|
|
|
207
|
+
function SortControl({ value, onChange }: { value: SortKey; onChange: (next: SortKey) => void }) {
|
|
208
|
+
const t = useLocale();
|
|
209
|
+
const labels: Record<SortKey, string> = {
|
|
210
|
+
'created-desc': t.home.sortByCreatedDesc,
|
|
211
|
+
'created-asc': t.home.sortByCreatedAsc,
|
|
212
|
+
'title-asc': t.home.sortByTitleAsc,
|
|
213
|
+
'title-desc': t.home.sortByTitleDesc,
|
|
214
|
+
};
|
|
215
|
+
const FieldIcon = ({ k, className }: { k: SortKey; className?: string }) =>
|
|
216
|
+
k === 'title-asc' || k === 'title-desc' ? (
|
|
217
|
+
<ArrowDownAZ className={className} aria-hidden />
|
|
218
|
+
) : (
|
|
219
|
+
<Clock className={className} aria-hidden />
|
|
220
|
+
);
|
|
221
|
+
return (
|
|
222
|
+
<DropdownMenu>
|
|
223
|
+
<DropdownMenuTrigger asChild>
|
|
224
|
+
<button
|
|
225
|
+
type="button"
|
|
226
|
+
aria-label={`${t.home.sortLabel}: ${labels[value]}`}
|
|
227
|
+
className="flex h-8 items-center gap-1.5 rounded-[6px] border border-border bg-background pl-2 pr-1.5 text-[12.5px] font-medium text-foreground outline-none hover:bg-muted focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30"
|
|
228
|
+
>
|
|
229
|
+
<FieldIcon k={value} className="size-3.5 text-muted-foreground" />
|
|
230
|
+
<span>{labels[value]}</span>
|
|
231
|
+
<ChevronDown className="size-3 text-muted-foreground" aria-hidden />
|
|
232
|
+
</button>
|
|
233
|
+
</DropdownMenuTrigger>
|
|
234
|
+
<DropdownMenuContent align="end" className="min-w-[180px]">
|
|
235
|
+
{SORT_KEYS.map((key) => {
|
|
236
|
+
const active = value === key;
|
|
237
|
+
return (
|
|
238
|
+
<DropdownMenuItem
|
|
239
|
+
key={key}
|
|
240
|
+
onSelect={() => onChange(key)}
|
|
241
|
+
className={cn(active && 'bg-muted text-foreground')}
|
|
242
|
+
>
|
|
243
|
+
<FieldIcon k={key} className="size-3.5 text-muted-foreground" />
|
|
244
|
+
<span>{labels[key]}</span>
|
|
245
|
+
</DropdownMenuItem>
|
|
246
|
+
);
|
|
247
|
+
})}
|
|
248
|
+
</DropdownMenuContent>
|
|
249
|
+
</DropdownMenu>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
155
253
|
function HomeLoading() {
|
|
156
254
|
const t = useLocale();
|
|
157
255
|
return (
|
package/src/app/routes/slide.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import config from 'virtual:open-slide/config';
|
|
2
2
|
import {
|
|
3
|
+
Check,
|
|
3
4
|
ChevronDown,
|
|
4
5
|
ChevronLeft,
|
|
5
6
|
Download,
|
|
6
7
|
FileCode2,
|
|
7
8
|
FileText,
|
|
9
|
+
Link2,
|
|
8
10
|
Loader2,
|
|
9
11
|
Maximize,
|
|
10
12
|
MonitorSpeaker,
|
|
@@ -61,7 +63,15 @@ export function Slide() {
|
|
|
61
63
|
const { slide, error } = useSlideModule(slideId);
|
|
62
64
|
const [playMode, setPlayMode] = useState<'window' | 'fullscreen' | null>(null);
|
|
63
65
|
const [exporting, setExporting] = useState(false);
|
|
66
|
+
const [linkCopied, setLinkCopied] = useState(false);
|
|
67
|
+
const linkCopiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
64
68
|
const [designOpen, setDesignOpen] = useState(false);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
return () => {
|
|
72
|
+
if (linkCopiedTimerRef.current) clearTimeout(linkCopiedTimerRef.current);
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
65
75
|
const { renameSlide } = useFolders();
|
|
66
76
|
const slideViewportRef = useRef<HTMLElement>(null);
|
|
67
77
|
const t = useLocale();
|
|
@@ -375,6 +385,41 @@ export function Slide() {
|
|
|
375
385
|
</div>
|
|
376
386
|
|
|
377
387
|
<div className="flex items-center gap-1">
|
|
388
|
+
{view === 'slides' && (
|
|
389
|
+
<button
|
|
390
|
+
type="button"
|
|
391
|
+
aria-label={t.slide.copyLink}
|
|
392
|
+
title={t.slide.copyLink}
|
|
393
|
+
className={cn(buttonVariants({ variant: 'ghost', size: 'icon-sm' }))}
|
|
394
|
+
onClick={async () => {
|
|
395
|
+
try {
|
|
396
|
+
await navigator.clipboard.writeText(window.location.href);
|
|
397
|
+
toast.success(t.slide.toastCopyLinkSuccess);
|
|
398
|
+
setLinkCopied(true);
|
|
399
|
+
if (linkCopiedTimerRef.current) clearTimeout(linkCopiedTimerRef.current);
|
|
400
|
+
linkCopiedTimerRef.current = setTimeout(() => setLinkCopied(false), 1200);
|
|
401
|
+
} catch (err) {
|
|
402
|
+
console.error('[open-slide] copy link failed', err);
|
|
403
|
+
toast.error(t.slide.toastCopyLinkFailed);
|
|
404
|
+
}
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
<span className="relative grid size-4 place-items-center">
|
|
408
|
+
<Link2
|
|
409
|
+
className={cn(
|
|
410
|
+
'col-start-1 row-start-1 size-4 transition-opacity duration-200',
|
|
411
|
+
linkCopied ? 'opacity-0' : 'opacity-100',
|
|
412
|
+
)}
|
|
413
|
+
/>
|
|
414
|
+
<Check
|
|
415
|
+
className={cn(
|
|
416
|
+
'col-start-1 row-start-1 size-4 transition-opacity duration-200',
|
|
417
|
+
linkCopied ? 'opacity-100' : 'opacity-0',
|
|
418
|
+
)}
|
|
419
|
+
/>
|
|
420
|
+
</span>
|
|
421
|
+
</button>
|
|
422
|
+
)}
|
|
378
423
|
{view === 'slides' && allowHtmlDownload && (
|
|
379
424
|
<DropdownMenu>
|
|
380
425
|
<DropdownMenuTrigger
|
package/src/app/virtual.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ declare module 'virtual:open-slide/slides' {
|
|
|
2
2
|
import type { SlideModule } from './lib/sdk';
|
|
3
3
|
export const slideIds: string[];
|
|
4
4
|
export const slideThemes: Record<string, string>;
|
|
5
|
+
export const slideCreatedAt: Record<string, number>;
|
|
5
6
|
export function loadSlide(id: string): Promise<SlideModule>;
|
|
6
7
|
}
|
|
7
8
|
|
package/src/locale/en.ts
CHANGED
|
@@ -49,6 +49,11 @@ export const en: Locale = {
|
|
|
49
49
|
folderActions: 'Folder actions',
|
|
50
50
|
searchPlaceholder: 'Search slides',
|
|
51
51
|
clearSearch: 'Clear search',
|
|
52
|
+
sortLabel: 'Sort',
|
|
53
|
+
sortByCreatedDesc: 'Newest',
|
|
54
|
+
sortByCreatedAsc: 'Oldest',
|
|
55
|
+
sortByTitleAsc: 'A–Z',
|
|
56
|
+
sortByTitleDesc: 'Z–A',
|
|
52
57
|
noMatches: 'No matches',
|
|
53
58
|
nothingMatchesPrefix: 'Nothing matches ',
|
|
54
59
|
nothingMatchesSuffix: ' in this folder.',
|
|
@@ -91,6 +96,9 @@ export const en: Locale = {
|
|
|
91
96
|
agentDisconnectedTooltip:
|
|
92
97
|
'Lost connection to the dev server, so your agent can no longer see the current slide or inspector selection. Restart the dev server to restore the connection.',
|
|
93
98
|
download: 'Download',
|
|
99
|
+
copyLink: 'Copy link',
|
|
100
|
+
toastCopyLinkSuccess: 'Link copied to clipboard',
|
|
101
|
+
toastCopyLinkFailed: 'Failed to copy link',
|
|
94
102
|
exportAsHtml: 'Export as HTML',
|
|
95
103
|
exportAsPdf: 'Export as PDF',
|
|
96
104
|
pdfExportFailed: 'PDF export failed',
|
|
@@ -214,7 +222,7 @@ export const en: Locale = {
|
|
|
214
222
|
cropResetAria: 'Reset crop',
|
|
215
223
|
leaveComment: 'Leave a comment',
|
|
216
224
|
commentPlaceholder: 'Describe a change for the agent…',
|
|
217
|
-
commentShortcutHint: '⌘↵ to add',
|
|
225
|
+
commentShortcutHint: '⌘/ to focus · ⌘↵ to add',
|
|
218
226
|
addComment: 'Add comment',
|
|
219
227
|
unsavedChanges: {
|
|
220
228
|
one: '{count} unsaved change',
|
|
@@ -281,6 +289,11 @@ export const en: Locale = {
|
|
|
281
289
|
conflictRenameCopy: 'Rename copy',
|
|
282
290
|
deleteAssetTitle: 'Delete asset',
|
|
283
291
|
deleteAssetDescription: 'Delete {name}? Imports referencing this file in the slide will break.',
|
|
292
|
+
deleteAssetInUseDescription: '{name} is used in {count} place(s) across {slides} slide(s).',
|
|
293
|
+
deleteAssetInUseHint: 'Deleting will revert these usages back to image placeholders.',
|
|
294
|
+
deleteAndRevert: 'Delete & revert',
|
|
295
|
+
toastRevertFailed: "Couldn't revert usage in {slideId}",
|
|
296
|
+
toastDeletedWithRevert: 'Deleted {name} and reverted {count} usage(s)',
|
|
284
297
|
noPreview: 'No preview available',
|
|
285
298
|
importHintComment: 'import asset from ',
|
|
286
299
|
importHintSemi: ';',
|
package/src/locale/ja.ts
CHANGED
|
@@ -49,6 +49,11 @@ export const ja: Locale = {
|
|
|
49
49
|
folderActions: 'フォルダ操作',
|
|
50
50
|
searchPlaceholder: 'スライドを検索',
|
|
51
51
|
clearSearch: '検索をクリア',
|
|
52
|
+
sortLabel: '並べ替え',
|
|
53
|
+
sortByCreatedDesc: '新しい順',
|
|
54
|
+
sortByCreatedAsc: '古い順',
|
|
55
|
+
sortByTitleAsc: 'A–Z',
|
|
56
|
+
sortByTitleDesc: 'Z–A',
|
|
52
57
|
noMatches: '一致なし',
|
|
53
58
|
nothingMatchesPrefix: 'このフォルダ内に ',
|
|
54
59
|
nothingMatchesSuffix: ' に一致する項目はありません。',
|
|
@@ -91,6 +96,9 @@ export const ja: Locale = {
|
|
|
91
96
|
home: 'ホーム',
|
|
92
97
|
backToHome: 'ホームへ戻る',
|
|
93
98
|
download: 'ダウンロード',
|
|
99
|
+
copyLink: 'リンクをコピー',
|
|
100
|
+
toastCopyLinkSuccess: 'リンクをクリップボードにコピーしました',
|
|
101
|
+
toastCopyLinkFailed: 'リンクのコピーに失敗しました',
|
|
94
102
|
exportAsHtml: 'HTML として書き出し',
|
|
95
103
|
exportAsPdf: 'PDF として書き出し',
|
|
96
104
|
pdfExportFailed: 'PDF の書き出しに失敗しました',
|
|
@@ -215,7 +223,7 @@ export const ja: Locale = {
|
|
|
215
223
|
'dev server との接続が切れたため、選択中の要素がエージェントに見えなくなっています。dev server を再起動して接続を復旧してください。',
|
|
216
224
|
leaveComment: 'コメントを残す',
|
|
217
225
|
commentPlaceholder: 'エージェントに依頼する変更を記述…',
|
|
218
|
-
commentShortcutHint: '⌘↵ で追加',
|
|
226
|
+
commentShortcutHint: '⌘/ でフォーカス · ⌘↵ で追加',
|
|
219
227
|
addComment: 'コメントを追加',
|
|
220
228
|
unsavedChanges: {
|
|
221
229
|
one: '未保存の変更 {count} 件',
|
|
@@ -284,6 +292,11 @@ export const ja: Locale = {
|
|
|
284
292
|
deleteAssetTitle: 'アセットを削除',
|
|
285
293
|
deleteAssetDescription:
|
|
286
294
|
'{name} を削除しますか?スライド内でこのファイルを参照しているインポートは壊れます。',
|
|
295
|
+
deleteAssetInUseDescription: '{name} は {slides} 枚のスライドで {count} 箇所使用されています。',
|
|
296
|
+
deleteAssetInUseHint: '削除すると、これらの使用箇所は画像プレースホルダーに戻ります。',
|
|
297
|
+
deleteAndRevert: '削除して戻す',
|
|
298
|
+
toastRevertFailed: '{slideId} の使用箇所を戻せませんでした',
|
|
299
|
+
toastDeletedWithRevert: '{name} を削除し、{count} 箇所をプレースホルダーに戻しました',
|
|
287
300
|
noPreview: 'プレビューはありません',
|
|
288
301
|
importHintComment: 'import asset from ',
|
|
289
302
|
importHintSemi: ';',
|
package/src/locale/types.ts
CHANGED
|
@@ -49,6 +49,11 @@ export type Locale = {
|
|
|
49
49
|
folderActions: string;
|
|
50
50
|
searchPlaceholder: string;
|
|
51
51
|
clearSearch: string;
|
|
52
|
+
sortLabel: string;
|
|
53
|
+
sortByCreatedDesc: string;
|
|
54
|
+
sortByCreatedAsc: string;
|
|
55
|
+
sortByTitleAsc: string;
|
|
56
|
+
sortByTitleDesc: string;
|
|
52
57
|
noMatches: string;
|
|
53
58
|
nothingMatchesPrefix: string;
|
|
54
59
|
nothingMatchesSuffix: string;
|
|
@@ -92,6 +97,9 @@ export type Locale = {
|
|
|
92
97
|
agentDisconnected: string;
|
|
93
98
|
agentDisconnectedTooltip: string;
|
|
94
99
|
download: string;
|
|
100
|
+
copyLink: string;
|
|
101
|
+
toastCopyLinkSuccess: string;
|
|
102
|
+
toastCopyLinkFailed: string;
|
|
95
103
|
exportAsHtml: string;
|
|
96
104
|
exportAsPdf: string;
|
|
97
105
|
pdfExportFailed: string;
|
|
@@ -286,6 +294,14 @@ export type Locale = {
|
|
|
286
294
|
deleteAssetTitle: string;
|
|
287
295
|
/** template: "Delete {name}? Imports referencing this file in the slide will break." */
|
|
288
296
|
deleteAssetDescription: string;
|
|
297
|
+
/** template: "{name} is used in {count} place across {slides} slide." (singular/plural via {count}/{slides}) */
|
|
298
|
+
deleteAssetInUseDescription: string;
|
|
299
|
+
deleteAssetInUseHint: string;
|
|
300
|
+
deleteAndRevert: string;
|
|
301
|
+
/** template: "Couldn't revert usage in {slideId}." */
|
|
302
|
+
toastRevertFailed: string;
|
|
303
|
+
/** template: "Deleted {name} and reverted {count} usage." */
|
|
304
|
+
toastDeletedWithRevert: string;
|
|
289
305
|
noPreview: string;
|
|
290
306
|
importHintComment: string;
|
|
291
307
|
importHintSemi: string;
|
package/src/locale/zh-cn.ts
CHANGED
|
@@ -49,6 +49,11 @@ export const zhCN: Locale = {
|
|
|
49
49
|
folderActions: '文件夹操作',
|
|
50
50
|
searchPlaceholder: '搜索幻灯片',
|
|
51
51
|
clearSearch: '清除搜索',
|
|
52
|
+
sortLabel: '排序',
|
|
53
|
+
sortByCreatedDesc: '最新',
|
|
54
|
+
sortByCreatedAsc: '最旧',
|
|
55
|
+
sortByTitleAsc: 'A–Z',
|
|
56
|
+
sortByTitleDesc: 'Z–A',
|
|
52
57
|
noMatches: '没有匹配项',
|
|
53
58
|
nothingMatchesPrefix: '该文件夹中没有匹配 ',
|
|
54
59
|
nothingMatchesSuffix: ' 的内容。',
|
|
@@ -90,6 +95,9 @@ export const zhCN: Locale = {
|
|
|
90
95
|
home: '首页',
|
|
91
96
|
backToHome: '返回首页',
|
|
92
97
|
download: '下载',
|
|
98
|
+
copyLink: '复制链接',
|
|
99
|
+
toastCopyLinkSuccess: '已复制链接到剪贴板',
|
|
100
|
+
toastCopyLinkFailed: '复制链接失败',
|
|
93
101
|
exportAsHtml: '导出为 HTML',
|
|
94
102
|
exportAsPdf: '导出为 PDF',
|
|
95
103
|
pdfExportFailed: 'PDF 导出失败',
|
|
@@ -213,7 +221,7 @@ export const zhCN: Locale = {
|
|
|
213
221
|
'已和 dev server 断开连接,agent 看不到你选的元素了。请重新启动 dev server 来恢复连接。',
|
|
214
222
|
leaveComment: '留个 comment',
|
|
215
223
|
commentPlaceholder: '描述你希望代理执行的更改…',
|
|
216
|
-
commentShortcutHint: '⌘↵ 添加',
|
|
224
|
+
commentShortcutHint: '⌘/ 聚焦 · ⌘↵ 添加',
|
|
217
225
|
addComment: '添加 comment',
|
|
218
226
|
unsavedChanges: {
|
|
219
227
|
one: '{count} 项未保存的更改',
|
|
@@ -280,6 +288,11 @@ export const zhCN: Locale = {
|
|
|
280
288
|
conflictRenameCopy: '重命名副本',
|
|
281
289
|
deleteAssetTitle: '删除素材',
|
|
282
290
|
deleteAssetDescription: '要删除 {name} 吗?幻灯片中引用此文件的导入将失效。',
|
|
291
|
+
deleteAssetInUseDescription: '{name} 在 {slides} 个幻灯片中被使用了 {count} 次。',
|
|
292
|
+
deleteAssetInUseHint: '删除后这些使用处会自动还原为图片占位符。',
|
|
293
|
+
deleteAndRevert: '删除并还原',
|
|
294
|
+
toastRevertFailed: '无法还原 {slideId} 中的使用',
|
|
295
|
+
toastDeletedWithRevert: '已删除 {name} 并还原 {count} 个使用处',
|
|
283
296
|
noPreview: '无预览',
|
|
284
297
|
importHintComment: 'import asset from ',
|
|
285
298
|
importHintSemi: ';',
|
package/src/locale/zh-tw.ts
CHANGED
|
@@ -49,6 +49,11 @@ export const zhTW: Locale = {
|
|
|
49
49
|
folderActions: '資料夾操作',
|
|
50
50
|
searchPlaceholder: '搜尋投影片',
|
|
51
51
|
clearSearch: '清除搜尋',
|
|
52
|
+
sortLabel: '排序',
|
|
53
|
+
sortByCreatedDesc: '最新',
|
|
54
|
+
sortByCreatedAsc: '最舊',
|
|
55
|
+
sortByTitleAsc: 'A–Z',
|
|
56
|
+
sortByTitleDesc: 'Z–A',
|
|
52
57
|
noMatches: '沒有相符結果',
|
|
53
58
|
nothingMatchesPrefix: '此資料夾中沒有相符 ',
|
|
54
59
|
nothingMatchesSuffix: ' 的項目。',
|
|
@@ -90,6 +95,9 @@ export const zhTW: Locale = {
|
|
|
90
95
|
home: '首頁',
|
|
91
96
|
backToHome: '返回首頁',
|
|
92
97
|
download: '下載',
|
|
98
|
+
copyLink: '複製連結',
|
|
99
|
+
toastCopyLinkSuccess: '已複製連結到剪貼簿',
|
|
100
|
+
toastCopyLinkFailed: '複製連結失敗',
|
|
93
101
|
exportAsHtml: '匯出為 HTML',
|
|
94
102
|
exportAsPdf: '匯出為 PDF',
|
|
95
103
|
pdfExportFailed: 'PDF 匯出失敗',
|
|
@@ -213,7 +221,7 @@ export const zhTW: Locale = {
|
|
|
213
221
|
'已和 dev server 斷線,agent 看不到你選的元素了。請重新啟動 dev server 來恢復連線。',
|
|
214
222
|
leaveComment: '留個 comment',
|
|
215
223
|
commentPlaceholder: '描述你希望代理進行的修改…',
|
|
216
|
-
commentShortcutHint: '⌘↵ 新增',
|
|
224
|
+
commentShortcutHint: '⌘/ 聚焦 · ⌘↵ 新增',
|
|
217
225
|
addComment: '新增 comment',
|
|
218
226
|
unsavedChanges: {
|
|
219
227
|
one: '{count} 項未儲存的變更',
|
|
@@ -280,6 +288,11 @@ export const zhTW: Locale = {
|
|
|
280
288
|
conflictRenameCopy: '重新命名副本',
|
|
281
289
|
deleteAssetTitle: '刪除素材',
|
|
282
290
|
deleteAssetDescription: '要刪除 {name} 嗎?投影片中引用此檔案的匯入將失效。',
|
|
291
|
+
deleteAssetInUseDescription: '{name} 在 {slides} 個投影片中被使用了 {count} 次。',
|
|
292
|
+
deleteAssetInUseHint: '刪除後這些使用處會自動還原為圖片占位符。',
|
|
293
|
+
deleteAndRevert: '刪除並還原',
|
|
294
|
+
toastRevertFailed: '無法還原 {slideId} 中的使用',
|
|
295
|
+
toastDeletedWithRevert: '已刪除 {name} 並還原 {count} 個使用處',
|
|
283
296
|
noPreview: '無預覽',
|
|
284
297
|
importHintComment: 'import asset from ',
|
|
285
298
|
importHintSemi: ';',
|