@open-slide/core 1.3.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-_276DMmJ.js → build-DZhbjQpQ.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-D9cZ1A0X.d.ts → config-BQdTMho4.d.ts} +2 -1
- package/dist/{config-BAwKWNtW.js → config-iKjqaX08.js} +2528 -1640
- package/dist/{dev-BoqeVXVq.js → dev-BjLGk5nN.js} +1 -1
- package/dist/{en-CDKzoZvf.js → en-DDGqyNaW.js} +27 -4
- 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 +82 -13
- package/dist/{preview-BLPxspc9.js → preview-jwLWHWkQ.js} +1 -1
- package/dist/{types-JYG1cmwC.d.ts → types-Dpr8nbih.d.ts} +27 -1
- 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 +19 -4
- package/src/app/app.tsx +2 -0
- package/src/app/components/asset-view.tsx +111 -18
- package/src/app/components/inspector/inspect-overlay.tsx +49 -3
- package/src/app/components/inspector/inspector-panel.tsx +267 -25
- package/src/app/components/inspector/inspector-provider.tsx +390 -49
- package/src/app/components/panel/panel-shell.tsx +5 -3
- package/src/app/components/player.tsx +25 -5
- package/src/app/components/present/control-bar.tsx +12 -0
- package/src/app/components/present/laser-pointer.tsx +3 -4
- package/src/app/components/present/progress-bar.tsx +4 -4
- package/src/app/components/sidebar/folder-item.tsx +14 -3
- package/src/app/components/sidebar/sidebar.tsx +10 -0
- package/src/app/lib/assets.ts +21 -0
- package/src/app/lib/export-pdf.ts +6 -0
- package/src/app/lib/inspector/use-editor.ts +9 -1
- package/src/app/lib/sdk.ts +2 -0
- package/src/app/lib/slides.ts +9 -0
- package/src/app/lib/use-slide-module.ts +48 -0
- package/src/app/routes/assets.tsx +9 -0
- package/src/app/routes/home-shell.tsx +23 -2
- package/src/app/routes/home.tsx +101 -3
- package/src/app/routes/presenter.tsx +2 -20
- package/src/app/routes/slide.tsx +117 -39
- package/src/app/virtual.d.ts +1 -0
- package/src/locale/en.ts +28 -5
- package/src/locale/ja.ts +28 -5
- package/src/locale/types.ts +27 -1
- package/src/locale/zh-cn.ts +28 -6
- package/src/locale/zh-tw.ts +28 -6
|
@@ -35,6 +35,7 @@ const en = {
|
|
|
35
35
|
appTitle: "open-slide",
|
|
36
36
|
draft: "Draft",
|
|
37
37
|
themes: "Themes",
|
|
38
|
+
assets: "Assets",
|
|
38
39
|
folders: "Folders",
|
|
39
40
|
newFolder: "New folder",
|
|
40
41
|
folderName: "Folder name",
|
|
@@ -44,6 +45,11 @@ const en = {
|
|
|
44
45
|
folderActions: "Folder actions",
|
|
45
46
|
searchPlaceholder: "Search slides",
|
|
46
47
|
clearSearch: "Clear search",
|
|
48
|
+
sortLabel: "Sort",
|
|
49
|
+
sortByCreatedDesc: "Newest",
|
|
50
|
+
sortByCreatedAsc: "Oldest",
|
|
51
|
+
sortByTitleAsc: "A–Z",
|
|
52
|
+
sortByTitleDesc: "Z–A",
|
|
47
53
|
noMatches: "No matches",
|
|
48
54
|
nothingMatchesPrefix: "Nothing matches ",
|
|
49
55
|
nothingMatchesSuffix: " in this folder.",
|
|
@@ -79,14 +85,22 @@ const en = {
|
|
|
79
85
|
home: "Home",
|
|
80
86
|
backToHome: "Back to home",
|
|
81
87
|
agentConnected: "Agent connected",
|
|
82
|
-
agentConnectedTooltip: "The
|
|
88
|
+
agentConnectedTooltip: "The current slide and inspector selection are synced to your agent in real time.",
|
|
83
89
|
agentDisconnected: "Agent disconnected",
|
|
84
90
|
agentDisconnectedTooltip: "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.",
|
|
85
91
|
download: "Download",
|
|
92
|
+
copyLink: "Copy link",
|
|
93
|
+
toastCopyLinkSuccess: "Link copied to clipboard",
|
|
94
|
+
toastCopyLinkFailed: "Failed to copy link",
|
|
86
95
|
exportAsHtml: "Export as HTML",
|
|
87
96
|
exportAsPdf: "Export as PDF",
|
|
88
97
|
pdfExportFailed: "PDF export failed",
|
|
98
|
+
pdfExportSafariUnsupported: "Export as PDF is not supported on Safari. Please try a Chromium-based browser instead.",
|
|
89
99
|
present: "Present",
|
|
100
|
+
presentMenuAria: "Present options",
|
|
101
|
+
presentInWindow: "Play",
|
|
102
|
+
presentFullscreen: "Fullscreen",
|
|
103
|
+
presentPresenter: "Presenter mode",
|
|
90
104
|
slidesTab: "Slides",
|
|
91
105
|
assetsTab: "Assets",
|
|
92
106
|
renameSlide: "Rename slide",
|
|
@@ -128,6 +142,8 @@ const en = {
|
|
|
128
142
|
whiteoutAria: "White screen (W)",
|
|
129
143
|
laserAria: "Laser pointer (L)",
|
|
130
144
|
presenterAria: "Presenter view (P)",
|
|
145
|
+
enterFullscreenAria: "Enter fullscreen",
|
|
146
|
+
exitFullscreenAria: "Exit fullscreen",
|
|
131
147
|
helpAria: "Keyboard shortcuts (?)",
|
|
132
148
|
exitAria: "Exit (Esc)",
|
|
133
149
|
elapsedTime: "Elapsed time",
|
|
@@ -153,7 +169,7 @@ const en = {
|
|
|
153
169
|
inspect: "Inspect",
|
|
154
170
|
deselect: "Deselect",
|
|
155
171
|
agentWatching: "Agent is watching",
|
|
156
|
-
agentWatchingTooltip: "
|
|
172
|
+
agentWatchingTooltip: "The selected element is synced to your agent in real time.",
|
|
157
173
|
agentNotWatching: "Agent not watching",
|
|
158
174
|
agentNotWatchingTooltip: "Lost connection to the dev server, so your agent can no longer see the selected element. Restart the dev server to restore the connection.",
|
|
159
175
|
contentSection: "Content",
|
|
@@ -194,7 +210,7 @@ const en = {
|
|
|
194
210
|
cropResetAria: "Reset crop",
|
|
195
211
|
leaveComment: "Leave a comment",
|
|
196
212
|
commentPlaceholder: "Describe a change for the agent…",
|
|
197
|
-
commentShortcutHint: "⌘↵ to add",
|
|
213
|
+
commentShortcutHint: "⌘/ to focus · ⌘↵ to add",
|
|
198
214
|
addComment: "Add comment",
|
|
199
215
|
unsavedChanges: {
|
|
200
216
|
one: "{count} unsaved change",
|
|
@@ -237,6 +253,8 @@ const en = {
|
|
|
237
253
|
devOnlyMessage: "Asset management is only available in dev mode.",
|
|
238
254
|
sectionAria: "Slide assets",
|
|
239
255
|
eyebrow: "Assets",
|
|
256
|
+
scopeSlide: "This slide",
|
|
257
|
+
scopeGlobal: "Global",
|
|
240
258
|
fileCount: {
|
|
241
259
|
one: "{count} file",
|
|
242
260
|
other: "{count} files"
|
|
@@ -255,11 +273,16 @@ const en = {
|
|
|
255
273
|
renameMenuItem: "Rename",
|
|
256
274
|
deleteMenuItem: "Delete",
|
|
257
275
|
conflictTitle: "File already exists",
|
|
258
|
-
conflictDescription: "{name} is already in
|
|
276
|
+
conflictDescription: "{name} is already in the assets folder.",
|
|
259
277
|
conflictReplace: "Replace",
|
|
260
278
|
conflictRenameCopy: "Rename copy",
|
|
261
279
|
deleteAssetTitle: "Delete asset",
|
|
262
280
|
deleteAssetDescription: "Delete {name}? Imports referencing this file in the slide will break.",
|
|
281
|
+
deleteAssetInUseDescription: "{name} is used in {count} place(s) across {slides} slide(s).",
|
|
282
|
+
deleteAssetInUseHint: "Deleting will revert these usages back to image placeholders.",
|
|
283
|
+
deleteAndRevert: "Delete & revert",
|
|
284
|
+
toastRevertFailed: "Couldn't revert usage in {slideId}",
|
|
285
|
+
toastDeletedWithRevert: "Deleted {name} and reverted {count} usage(s)",
|
|
263
286
|
noPreview: "No preview available",
|
|
264
287
|
importHintComment: "import asset from ",
|
|
265
288
|
importHintSemi: ";",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Locale, Plural } from "./types-
|
|
2
|
-
import { OpenSlideConfig } from "./config-
|
|
1
|
+
import { Locale, Plural } from "./types-Dpr8nbih.js";
|
|
2
|
+
import { OpenSlideConfig } from "./config-BQdTMho4.js";
|
|
3
3
|
import { CSSProperties, ComponentType, HTMLAttributes } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|
|
@@ -51,6 +51,8 @@ type Page = ComponentType;
|
|
|
51
51
|
type SlideMeta = {
|
|
52
52
|
title?: string;
|
|
53
53
|
theme?: string;
|
|
54
|
+
/** ISO 8601 timestamp. Set once at scaffold time; used to sort the slide list. */
|
|
55
|
+
createdAt?: string;
|
|
54
56
|
};
|
|
55
57
|
type SlideModule = {
|
|
56
58
|
default: Page[];
|
package/dist/index.js
CHANGED
package/dist/locale/index.d.ts
CHANGED
package/dist/locale/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { en } from "../en-
|
|
1
|
+
import { en } from "../en-DDGqyNaW.js";
|
|
2
2
|
|
|
3
3
|
//#region src/locale/format.ts
|
|
4
4
|
function format(template, vars) {
|
|
@@ -49,6 +49,7 @@ const ja = {
|
|
|
49
49
|
appTitle: "open-slide",
|
|
50
50
|
draft: "下書き",
|
|
51
51
|
themes: "テーマ",
|
|
52
|
+
assets: "アセット",
|
|
52
53
|
folders: "フォルダ",
|
|
53
54
|
newFolder: "新規フォルダ",
|
|
54
55
|
folderName: "フォルダ名",
|
|
@@ -58,6 +59,11 @@ const ja = {
|
|
|
58
59
|
folderActions: "フォルダ操作",
|
|
59
60
|
searchPlaceholder: "スライドを検索",
|
|
60
61
|
clearSearch: "検索をクリア",
|
|
62
|
+
sortLabel: "並べ替え",
|
|
63
|
+
sortByCreatedDesc: "新しい順",
|
|
64
|
+
sortByCreatedAsc: "古い順",
|
|
65
|
+
sortByTitleAsc: "A–Z",
|
|
66
|
+
sortByTitleDesc: "Z–A",
|
|
61
67
|
noMatches: "一致なし",
|
|
62
68
|
nothingMatchesPrefix: "このフォルダ内に ",
|
|
63
69
|
nothingMatchesSuffix: " に一致する項目はありません。",
|
|
@@ -91,16 +97,24 @@ const ja = {
|
|
|
91
97
|
},
|
|
92
98
|
slide: {
|
|
93
99
|
agentConnected: "エージェント接続中",
|
|
94
|
-
agentConnectedTooltip: "現在のスライドと Inspector
|
|
100
|
+
agentConnectedTooltip: "現在のスライドと Inspector の選択はエージェントにリアルタイムで同期されています。",
|
|
95
101
|
agentDisconnected: "エージェント切断",
|
|
96
102
|
agentDisconnectedTooltip: "dev server との接続が切れたため、現在のスライドや Inspector の選択がエージェントに届かなくなっています。dev server を再起動して接続を復旧してください。",
|
|
97
103
|
home: "ホーム",
|
|
98
104
|
backToHome: "ホームへ戻る",
|
|
99
105
|
download: "ダウンロード",
|
|
106
|
+
copyLink: "リンクをコピー",
|
|
107
|
+
toastCopyLinkSuccess: "リンクをクリップボードにコピーしました",
|
|
108
|
+
toastCopyLinkFailed: "リンクのコピーに失敗しました",
|
|
100
109
|
exportAsHtml: "HTML として書き出し",
|
|
101
110
|
exportAsPdf: "PDF として書き出し",
|
|
102
111
|
pdfExportFailed: "PDF の書き出しに失敗しました",
|
|
112
|
+
pdfExportSafariUnsupported: "PDF の書き出しは現在 Safari では対応していません。Chromium ベースのブラウザでお試しください。",
|
|
103
113
|
present: "発表",
|
|
114
|
+
presentMenuAria: "発表オプション",
|
|
115
|
+
presentInWindow: "再生",
|
|
116
|
+
presentFullscreen: "フルスクリーン再生",
|
|
117
|
+
presentPresenter: "発表者モード",
|
|
104
118
|
slidesTab: "スライド",
|
|
105
119
|
assetsTab: "アセット",
|
|
106
120
|
renameSlide: "スライドの名前を変更",
|
|
@@ -142,6 +156,8 @@ const ja = {
|
|
|
142
156
|
whiteoutAria: "白い画面 (W)",
|
|
143
157
|
laserAria: "レーザーポインタ (L)",
|
|
144
158
|
presenterAria: "発表者ビュー (P)",
|
|
159
|
+
enterFullscreenAria: "フルスクリーンへ",
|
|
160
|
+
exitFullscreenAria: "フルスクリーン解除",
|
|
145
161
|
helpAria: "キーボードショートカット (?)",
|
|
146
162
|
exitAria: "終了 (Esc)",
|
|
147
163
|
elapsedTime: "経過時間",
|
|
@@ -203,12 +219,12 @@ const ja = {
|
|
|
203
219
|
cropApply: "適用",
|
|
204
220
|
cropResetAria: "トリミングをリセット",
|
|
205
221
|
agentWatching: "エージェント監視中",
|
|
206
|
-
agentWatchingTooltip: "
|
|
222
|
+
agentWatchingTooltip: "選択中の要素はエージェントにリアルタイムで同期されています。",
|
|
207
223
|
agentNotWatching: "エージェント未接続",
|
|
208
224
|
agentNotWatchingTooltip: "dev server との接続が切れたため、選択中の要素がエージェントに見えなくなっています。dev server を再起動して接続を復旧してください。",
|
|
209
225
|
leaveComment: "コメントを残す",
|
|
210
226
|
commentPlaceholder: "エージェントに依頼する変更を記述…",
|
|
211
|
-
commentShortcutHint: "⌘↵ で追加",
|
|
227
|
+
commentShortcutHint: "⌘/ でフォーカス · ⌘↵ で追加",
|
|
212
228
|
addComment: "コメントを追加",
|
|
213
229
|
unsavedChanges: {
|
|
214
230
|
one: "未保存の変更 {count} 件",
|
|
@@ -251,6 +267,8 @@ const ja = {
|
|
|
251
267
|
devOnlyMessage: "アセット管理は開発モードでのみ利用できます。",
|
|
252
268
|
sectionAria: "スライドのアセット",
|
|
253
269
|
eyebrow: "アセット",
|
|
270
|
+
scopeSlide: "このスライド",
|
|
271
|
+
scopeGlobal: "グローバル",
|
|
254
272
|
fileCount: {
|
|
255
273
|
one: "ファイル {count} 件",
|
|
256
274
|
other: "ファイル {count} 件"
|
|
@@ -269,11 +287,16 @@ const ja = {
|
|
|
269
287
|
renameMenuItem: "名前を変更",
|
|
270
288
|
deleteMenuItem: "削除",
|
|
271
289
|
conflictTitle: "ファイルがすでに存在します",
|
|
272
|
-
conflictDescription: "{name}
|
|
290
|
+
conflictDescription: "{name} はすでにアセットフォルダにあります。",
|
|
273
291
|
conflictReplace: "置き換え",
|
|
274
292
|
conflictRenameCopy: "コピーをリネーム",
|
|
275
293
|
deleteAssetTitle: "アセットを削除",
|
|
276
294
|
deleteAssetDescription: "{name} を削除しますか?スライド内でこのファイルを参照しているインポートは壊れます。",
|
|
295
|
+
deleteAssetInUseDescription: "{name} は {slides} 枚のスライドで {count} 箇所使用されています。",
|
|
296
|
+
deleteAssetInUseHint: "削除すると、これらの使用箇所は画像プレースホルダーに戻ります。",
|
|
297
|
+
deleteAndRevert: "削除して戻す",
|
|
298
|
+
toastRevertFailed: "{slideId} の使用箇所を戻せませんでした",
|
|
299
|
+
toastDeletedWithRevert: "{name} を削除し、{count} 箇所をプレースホルダーに戻しました",
|
|
277
300
|
noPreview: "プレビューはありません",
|
|
278
301
|
importHintComment: "import asset from ",
|
|
279
302
|
importHintSemi: ";",
|
|
@@ -399,6 +422,7 @@ const zhCN = {
|
|
|
399
422
|
appTitle: "open-slide",
|
|
400
423
|
draft: "草稿",
|
|
401
424
|
themes: "主题",
|
|
425
|
+
assets: "素材",
|
|
402
426
|
folders: "文件夹",
|
|
403
427
|
newFolder: "新建文件夹",
|
|
404
428
|
folderName: "文件夹名称",
|
|
@@ -408,6 +432,11 @@ const zhCN = {
|
|
|
408
432
|
folderActions: "文件夹操作",
|
|
409
433
|
searchPlaceholder: "搜索幻灯片",
|
|
410
434
|
clearSearch: "清除搜索",
|
|
435
|
+
sortLabel: "排序",
|
|
436
|
+
sortByCreatedDesc: "最新",
|
|
437
|
+
sortByCreatedAsc: "最旧",
|
|
438
|
+
sortByTitleAsc: "A–Z",
|
|
439
|
+
sortByTitleDesc: "Z–A",
|
|
411
440
|
noMatches: "没有匹配项",
|
|
412
441
|
nothingMatchesPrefix: "该文件夹中没有匹配 ",
|
|
413
442
|
nothingMatchesSuffix: " 的内容。",
|
|
@@ -441,16 +470,24 @@ const zhCN = {
|
|
|
441
470
|
},
|
|
442
471
|
slide: {
|
|
443
472
|
agentConnected: "Agent 已连接",
|
|
444
|
-
agentConnectedTooltip: "
|
|
473
|
+
agentConnectedTooltip: "目前的 slide 与 Inspector 选择会即时同步给 agent。",
|
|
445
474
|
agentDisconnected: "Agent 已断开",
|
|
446
475
|
agentDisconnectedTooltip: "已和 dev server 断开连接,agent 没办法再看到你目前的 slide 或 Inspector 选择。请重新启动 dev server 来恢复连接。",
|
|
447
476
|
home: "首页",
|
|
448
477
|
backToHome: "返回首页",
|
|
449
478
|
download: "下载",
|
|
479
|
+
copyLink: "复制链接",
|
|
480
|
+
toastCopyLinkSuccess: "已复制链接到剪贴板",
|
|
481
|
+
toastCopyLinkFailed: "复制链接失败",
|
|
450
482
|
exportAsHtml: "导出为 HTML",
|
|
451
483
|
exportAsPdf: "导出为 PDF",
|
|
452
484
|
pdfExportFailed: "PDF 导出失败",
|
|
485
|
+
pdfExportSafariUnsupported: "导出 PDF 目前不支持 Safari 设备,请尝试使用基于 Chromium 的浏览器替代。",
|
|
453
486
|
present: "演示",
|
|
487
|
+
presentMenuAria: "演示选项",
|
|
488
|
+
presentInWindow: "播放",
|
|
489
|
+
presentFullscreen: "全屏播放",
|
|
490
|
+
presentPresenter: "演讲者模式",
|
|
454
491
|
slidesTab: "幻灯片",
|
|
455
492
|
assetsTab: "素材",
|
|
456
493
|
renameSlide: "重命名幻灯片",
|
|
@@ -492,6 +529,8 @@ const zhCN = {
|
|
|
492
529
|
whiteoutAria: "白屏 (W)",
|
|
493
530
|
laserAria: "激光笔 (L)",
|
|
494
531
|
presenterAria: "演讲者视图 (P)",
|
|
532
|
+
enterFullscreenAria: "进入全屏",
|
|
533
|
+
exitFullscreenAria: "退出全屏",
|
|
495
534
|
helpAria: "键盘快捷键 (?)",
|
|
496
535
|
exitAria: "退出 (Esc)",
|
|
497
536
|
elapsedTime: "已用时",
|
|
@@ -553,12 +592,12 @@ const zhCN = {
|
|
|
553
592
|
cropApply: "应用",
|
|
554
593
|
cropResetAria: "重置裁剪",
|
|
555
594
|
agentWatching: "Agent 正在关注",
|
|
556
|
-
agentWatchingTooltip: "
|
|
595
|
+
agentWatchingTooltip: "选取的元素会即时同步给 agent。",
|
|
557
596
|
agentNotWatching: "Agent 没在关注",
|
|
558
597
|
agentNotWatchingTooltip: "已和 dev server 断开连接,agent 看不到你选的元素了。请重新启动 dev server 来恢复连接。",
|
|
559
598
|
leaveComment: "留个 comment",
|
|
560
599
|
commentPlaceholder: "描述你希望代理执行的更改…",
|
|
561
|
-
commentShortcutHint: "⌘↵ 添加",
|
|
600
|
+
commentShortcutHint: "⌘/ 聚焦 · ⌘↵ 添加",
|
|
562
601
|
addComment: "添加 comment",
|
|
563
602
|
unsavedChanges: {
|
|
564
603
|
one: "{count} 项未保存的更改",
|
|
@@ -601,6 +640,8 @@ const zhCN = {
|
|
|
601
640
|
devOnlyMessage: "素材管理仅在开发模式下可用。",
|
|
602
641
|
sectionAria: "幻灯片素材",
|
|
603
642
|
eyebrow: "素材",
|
|
643
|
+
scopeSlide: "当前幻灯片",
|
|
644
|
+
scopeGlobal: "全局",
|
|
604
645
|
fileCount: {
|
|
605
646
|
one: "{count} 个文件",
|
|
606
647
|
other: "{count} 个文件"
|
|
@@ -619,11 +660,16 @@ const zhCN = {
|
|
|
619
660
|
renameMenuItem: "重命名",
|
|
620
661
|
deleteMenuItem: "删除",
|
|
621
662
|
conflictTitle: "文件已存在",
|
|
622
|
-
conflictDescription: "{name}
|
|
663
|
+
conflictDescription: "{name} 已在素材文件夹中。",
|
|
623
664
|
conflictReplace: "替换",
|
|
624
665
|
conflictRenameCopy: "重命名副本",
|
|
625
666
|
deleteAssetTitle: "删除素材",
|
|
626
667
|
deleteAssetDescription: "要删除 {name} 吗?幻灯片中引用此文件的导入将失效。",
|
|
668
|
+
deleteAssetInUseDescription: "{name} 在 {slides} 个幻灯片中被使用了 {count} 次。",
|
|
669
|
+
deleteAssetInUseHint: "删除后这些使用处会自动还原为图片占位符。",
|
|
670
|
+
deleteAndRevert: "删除并还原",
|
|
671
|
+
toastRevertFailed: "无法还原 {slideId} 中的使用",
|
|
672
|
+
toastDeletedWithRevert: "已删除 {name} 并还原 {count} 个使用处",
|
|
627
673
|
noPreview: "无预览",
|
|
628
674
|
importHintComment: "import asset from ",
|
|
629
675
|
importHintSemi: ";",
|
|
@@ -749,6 +795,7 @@ const zhTW = {
|
|
|
749
795
|
appTitle: "open-slide",
|
|
750
796
|
draft: "草稿",
|
|
751
797
|
themes: "主題",
|
|
798
|
+
assets: "素材",
|
|
752
799
|
folders: "資料夾",
|
|
753
800
|
newFolder: "新增資料夾",
|
|
754
801
|
folderName: "資料夾名稱",
|
|
@@ -758,6 +805,11 @@ const zhTW = {
|
|
|
758
805
|
folderActions: "資料夾操作",
|
|
759
806
|
searchPlaceholder: "搜尋投影片",
|
|
760
807
|
clearSearch: "清除搜尋",
|
|
808
|
+
sortLabel: "排序",
|
|
809
|
+
sortByCreatedDesc: "最新",
|
|
810
|
+
sortByCreatedAsc: "最舊",
|
|
811
|
+
sortByTitleAsc: "A–Z",
|
|
812
|
+
sortByTitleDesc: "Z–A",
|
|
761
813
|
noMatches: "沒有相符結果",
|
|
762
814
|
nothingMatchesPrefix: "此資料夾中沒有相符 ",
|
|
763
815
|
nothingMatchesSuffix: " 的項目。",
|
|
@@ -791,16 +843,24 @@ const zhTW = {
|
|
|
791
843
|
},
|
|
792
844
|
slide: {
|
|
793
845
|
agentConnected: "Agent 已連線",
|
|
794
|
-
agentConnectedTooltip: "
|
|
846
|
+
agentConnectedTooltip: "目前的 slide 與 Inspector 選擇會即時同步給 agent。",
|
|
795
847
|
agentDisconnected: "Agent 已斷線",
|
|
796
848
|
agentDisconnectedTooltip: "已和 dev server 斷線,agent 沒辦法再看到你目前的 slide 或 Inspector 選擇。請重新啟動 dev server 來恢復連線。",
|
|
797
849
|
home: "首頁",
|
|
798
850
|
backToHome: "返回首頁",
|
|
799
851
|
download: "下載",
|
|
852
|
+
copyLink: "複製連結",
|
|
853
|
+
toastCopyLinkSuccess: "已複製連結到剪貼簿",
|
|
854
|
+
toastCopyLinkFailed: "複製連結失敗",
|
|
800
855
|
exportAsHtml: "匯出為 HTML",
|
|
801
856
|
exportAsPdf: "匯出為 PDF",
|
|
802
857
|
pdfExportFailed: "PDF 匯出失敗",
|
|
858
|
+
pdfExportSafariUnsupported: "匯出 PDF 目前不支援 Safari 裝置,請嘗試用 Chromium 基底瀏覽器替代。",
|
|
803
859
|
present: "簡報",
|
|
860
|
+
presentMenuAria: "簡報選項",
|
|
861
|
+
presentInWindow: "播放",
|
|
862
|
+
presentFullscreen: "全螢幕播放",
|
|
863
|
+
presentPresenter: "簡報者模式",
|
|
804
864
|
slidesTab: "投影片",
|
|
805
865
|
assetsTab: "素材",
|
|
806
866
|
renameSlide: "重新命名投影片",
|
|
@@ -842,6 +902,8 @@ const zhTW = {
|
|
|
842
902
|
whiteoutAria: "白屏 (W)",
|
|
843
903
|
laserAria: "雷射筆 (L)",
|
|
844
904
|
presenterAria: "主講人檢視 (P)",
|
|
905
|
+
enterFullscreenAria: "進入全螢幕",
|
|
906
|
+
exitFullscreenAria: "退出全螢幕",
|
|
845
907
|
helpAria: "鍵盤快速鍵 (?)",
|
|
846
908
|
exitAria: "離開 (Esc)",
|
|
847
909
|
elapsedTime: "已耗時",
|
|
@@ -903,12 +965,12 @@ const zhTW = {
|
|
|
903
965
|
cropApply: "套用",
|
|
904
966
|
cropResetAria: "重設裁切",
|
|
905
967
|
agentWatching: "Agent 正在關注",
|
|
906
|
-
agentWatchingTooltip: "
|
|
968
|
+
agentWatchingTooltip: "選取的元素會即時同步給 agent。",
|
|
907
969
|
agentNotWatching: "Agent 沒在關注",
|
|
908
970
|
agentNotWatchingTooltip: "已和 dev server 斷線,agent 看不到你選的元素了。請重新啟動 dev server 來恢復連線。",
|
|
909
971
|
leaveComment: "留個 comment",
|
|
910
972
|
commentPlaceholder: "描述你希望代理進行的修改…",
|
|
911
|
-
commentShortcutHint: "⌘↵ 新增",
|
|
973
|
+
commentShortcutHint: "⌘/ 聚焦 · ⌘↵ 新增",
|
|
912
974
|
addComment: "新增 comment",
|
|
913
975
|
unsavedChanges: {
|
|
914
976
|
one: "{count} 項未儲存的變更",
|
|
@@ -951,6 +1013,8 @@ const zhTW = {
|
|
|
951
1013
|
devOnlyMessage: "素材管理僅在開發模式下可用。",
|
|
952
1014
|
sectionAria: "投影片素材",
|
|
953
1015
|
eyebrow: "素材",
|
|
1016
|
+
scopeSlide: "此投影片",
|
|
1017
|
+
scopeGlobal: "全域",
|
|
954
1018
|
fileCount: {
|
|
955
1019
|
one: "{count} 個檔案",
|
|
956
1020
|
other: "{count} 個檔案"
|
|
@@ -969,11 +1033,16 @@ const zhTW = {
|
|
|
969
1033
|
renameMenuItem: "重新命名",
|
|
970
1034
|
deleteMenuItem: "刪除",
|
|
971
1035
|
conflictTitle: "檔案已存在",
|
|
972
|
-
conflictDescription: "{name}
|
|
1036
|
+
conflictDescription: "{name} 已在素材資料夾中。",
|
|
973
1037
|
conflictReplace: "取代",
|
|
974
1038
|
conflictRenameCopy: "重新命名副本",
|
|
975
1039
|
deleteAssetTitle: "刪除素材",
|
|
976
1040
|
deleteAssetDescription: "要刪除 {name} 嗎?投影片中引用此檔案的匯入將失效。",
|
|
1041
|
+
deleteAssetInUseDescription: "{name} 在 {slides} 個投影片中被使用了 {count} 次。",
|
|
1042
|
+
deleteAssetInUseHint: "刪除後這些使用處會自動還原為圖片占位符。",
|
|
1043
|
+
deleteAndRevert: "刪除並還原",
|
|
1044
|
+
toastRevertFailed: "無法還原 {slideId} 中的使用",
|
|
1045
|
+
toastDeletedWithRevert: "已刪除 {name} 並還原 {count} 個使用處",
|
|
977
1046
|
noPreview: "無預覽",
|
|
978
1047
|
importHintComment: "import asset from ",
|
|
979
1048
|
importHintSemi: ";",
|
|
@@ -39,6 +39,7 @@ type Locale = {
|
|
|
39
39
|
appTitle: string;
|
|
40
40
|
draft: string;
|
|
41
41
|
themes: string;
|
|
42
|
+
assets: string;
|
|
42
43
|
folders: string;
|
|
43
44
|
newFolder: string;
|
|
44
45
|
folderName: string;
|
|
@@ -48,6 +49,11 @@ type Locale = {
|
|
|
48
49
|
folderActions: string;
|
|
49
50
|
searchPlaceholder: string;
|
|
50
51
|
clearSearch: string;
|
|
52
|
+
sortLabel: string;
|
|
53
|
+
sortByCreatedDesc: string;
|
|
54
|
+
sortByCreatedAsc: string;
|
|
55
|
+
sortByTitleAsc: string;
|
|
56
|
+
sortByTitleDesc: string;
|
|
51
57
|
noMatches: string;
|
|
52
58
|
nothingMatchesPrefix: string;
|
|
53
59
|
nothingMatchesSuffix: string;
|
|
@@ -90,10 +96,18 @@ type Locale = {
|
|
|
90
96
|
agentDisconnected: string;
|
|
91
97
|
agentDisconnectedTooltip: string;
|
|
92
98
|
download: string;
|
|
99
|
+
copyLink: string;
|
|
100
|
+
toastCopyLinkSuccess: string;
|
|
101
|
+
toastCopyLinkFailed: string;
|
|
93
102
|
exportAsHtml: string;
|
|
94
103
|
exportAsPdf: string;
|
|
95
104
|
pdfExportFailed: string;
|
|
105
|
+
pdfExportSafariUnsupported: string;
|
|
96
106
|
present: string;
|
|
107
|
+
presentMenuAria: string;
|
|
108
|
+
presentInWindow: string;
|
|
109
|
+
presentFullscreen: string;
|
|
110
|
+
presentPresenter: string;
|
|
97
111
|
slidesTab: string;
|
|
98
112
|
assetsTab: string;
|
|
99
113
|
renameSlide: string;
|
|
@@ -136,6 +150,8 @@ type Locale = {
|
|
|
136
150
|
whiteoutAria: string;
|
|
137
151
|
laserAria: string;
|
|
138
152
|
presenterAria: string;
|
|
153
|
+
enterFullscreenAria: string;
|
|
154
|
+
exitFullscreenAria: string;
|
|
139
155
|
helpAria: string;
|
|
140
156
|
exitAria: string;
|
|
141
157
|
elapsedTime: string;
|
|
@@ -245,6 +261,8 @@ type Locale = {
|
|
|
245
261
|
devOnlyMessage: string;
|
|
246
262
|
sectionAria: string;
|
|
247
263
|
eyebrow: string;
|
|
264
|
+
scopeSlide: string;
|
|
265
|
+
scopeGlobal: string;
|
|
248
266
|
/** templates: "{count} file" / "{count} files" */
|
|
249
267
|
fileCount: Plural;
|
|
250
268
|
searchLogos: string;
|
|
@@ -263,13 +281,21 @@ type Locale = {
|
|
|
263
281
|
renameMenuItem: string;
|
|
264
282
|
deleteMenuItem: string;
|
|
265
283
|
conflictTitle: string;
|
|
266
|
-
/** template: "{name} is already in
|
|
284
|
+
/** template: "{name} is already in the assets folder." */
|
|
267
285
|
conflictDescription: string;
|
|
268
286
|
conflictReplace: string;
|
|
269
287
|
conflictRenameCopy: string;
|
|
270
288
|
deleteAssetTitle: string;
|
|
271
289
|
/** template: "Delete {name}? Imports referencing this file in the slide will break." */
|
|
272
290
|
deleteAssetDescription: string;
|
|
291
|
+
/** template: "{name} is used in {count} place across {slides} slide." (singular/plural via {count}/{slides}) */
|
|
292
|
+
deleteAssetInUseDescription: string;
|
|
293
|
+
deleteAssetInUseHint: string;
|
|
294
|
+
deleteAndRevert: string;
|
|
295
|
+
/** template: "Couldn't revert usage in {slideId}." */
|
|
296
|
+
toastRevertFailed: string;
|
|
297
|
+
/** template: "Deleted {name} and reverted {count} usage." */
|
|
298
|
+
toastDeletedWithRevert: string;
|
|
273
299
|
noPreview: string;
|
|
274
300
|
importHintComment: string;
|
|
275
301
|
importHintSemi: string;
|
package/dist/vite/index.d.ts
CHANGED
package/dist/vite/index.js
CHANGED
package/package.json
CHANGED
|
@@ -31,7 +31,10 @@ import type { Page, SlideMeta } from '@open-slide/core';
|
|
|
31
31
|
const Cover: Page = () => <div>…</div>;
|
|
32
32
|
const Body: Page = () => <div>…</div>;
|
|
33
33
|
|
|
34
|
-
export const meta: SlideMeta = {
|
|
34
|
+
export const meta: SlideMeta = {
|
|
35
|
+
title: 'My slide',
|
|
36
|
+
createdAt: '2026-05-16T12:00:00Z',
|
|
37
|
+
};
|
|
35
38
|
export default [Cover, Body] satisfies Page[];
|
|
36
39
|
```
|
|
37
40
|
|
|
@@ -39,6 +42,7 @@ export default [Cover, Body] satisfies Page[];
|
|
|
39
42
|
- `meta.title` (optional) shows in the slide header. Default is the folder name.
|
|
40
43
|
- The slide id is the kebab-case folder name. Pick something short and descriptive (`q2-roadmap`, `team-offsite-2026`).
|
|
41
44
|
- `meta.theme` (optional) marks the slide as built from a theme under `themes/`. The id must match a `<id>.md` basename. Surfaces a back-link chip on the slide card and lists the slide on `/themes/<id>`. Omit if the slide isn't derived from a registered theme.
|
|
45
|
+
- `meta.createdAt` is an **ISO 8601 string literal** (e.g. `'2026-05-16T12:00:00Z'`) set once when the slide is scaffolded. The home page uses it for the default "newest first" sort. Always include it on new slides — **immediately before writing the file, run `node -e "console.log(new Date().toISOString())"` via Bash and paste the exact output** as the value. Don't type a timestamp from memory — you will get the date or time wrong. Must be a plain string literal (no `new Date(...)` or imports in the slide itself) — the framework reads it via a regex at build time, not by evaluating the module.
|
|
42
46
|
|
|
43
47
|
## Editing an existing slide
|
|
44
48
|
|
|
@@ -236,13 +240,16 @@ const Content: Page = () => (
|
|
|
236
240
|
</div>
|
|
237
241
|
);
|
|
238
242
|
|
|
239
|
-
export const meta: SlideMeta = {
|
|
243
|
+
export const meta: SlideMeta = {
|
|
244
|
+
title: 'The Big Idea',
|
|
245
|
+
createdAt: '2026-05-16T12:00:00Z',
|
|
246
|
+
};
|
|
240
247
|
export default [Cover, Content] satisfies Page[];
|
|
241
248
|
```
|
|
242
249
|
|
|
243
250
|
## Assets
|
|
244
251
|
|
|
245
|
-
|
|
252
|
+
**Slide-local assets** live under `slides/<id>/assets/` — anything one-off to a single slide. Import them as ES modules:
|
|
246
253
|
|
|
247
254
|
```tsx
|
|
248
255
|
import hero from './assets/hero.jpg';
|
|
@@ -256,6 +263,14 @@ For URL-only access:
|
|
|
256
263
|
const videoUrl = new URL('./assets/intro.mp4', import.meta.url).href;
|
|
257
264
|
```
|
|
258
265
|
|
|
266
|
+
**Global assets** — anything reused across decks or themes (company logos, presenter avatars, recurring icons) — live in the project root `assets/` folder. Import them via the `@assets` alias:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import logo from '@assets/logos/acme.svg';
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A `themes/*.md` file may name an asset path in its prose (e.g. "use `@assets/logos/acme.svg` in the title slot"); the slide imports it explicitly.
|
|
273
|
+
|
|
259
274
|
Skip the `assets/` folder entirely for pure-text slides.
|
|
260
275
|
|
|
261
276
|
## Image placeholders
|
|
@@ -339,7 +354,7 @@ This applies whenever the *visual element* repeats, not whenever the *data* does
|
|
|
339
354
|
- [ ] Slide declares a top-level `export const design: DesignSystem = { … }` and references the values via `var(--osd-X)` (use `design.X` only when you need a JS number for arithmetic). Only omit the `design` const for a one-off slide whose palette is intentionally locked.
|
|
340
355
|
- [ ] One idea per page.
|
|
341
356
|
- [ ] Visually repeated elements (cards, tiles, logo rows) are rendered as explicit `<Component />` instances, not via `array.map` over a data list.
|
|
342
|
-
- [ ] All imported assets exist on disk under `slides/<id>/assets
|
|
357
|
+
- [ ] All imported assets exist on disk — slide-local under `slides/<id>/assets/`, or global under `assets/` (imported via `@assets/...`).
|
|
343
358
|
- [ ] Every `<ImagePlaceholder>` corresponds to a real image the user must supply — not decorative filler. If it could be replaced by typography or layout, it should be.
|
|
344
359
|
- [ ] Nothing outside `slides/<id>/` was edited.
|
|
345
360
|
|
package/src/app/app.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import config from 'virtual:open-slide/config';
|
|
|
2
2
|
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
3
3
|
import { Toaster } from './components/ui/sonner';
|
|
4
4
|
import { useLocale } from './lib/use-locale';
|
|
5
|
+
import { AssetsPage } from './routes/assets';
|
|
5
6
|
import { Home } from './routes/home';
|
|
6
7
|
import { HomeShell } from './routes/home-shell';
|
|
7
8
|
import { Presenter } from './routes/presenter';
|
|
@@ -17,6 +18,7 @@ export function App() {
|
|
|
17
18
|
<Route path="/" element={<Home />} />
|
|
18
19
|
<Route path="/themes" element={<ThemesGalleryPage />} />
|
|
19
20
|
<Route path="/themes/:themeId" element={<ThemeDetailPage />} />
|
|
21
|
+
<Route path="/assets" element={<AssetsPage />} />
|
|
20
22
|
</Route>
|
|
21
23
|
) : (
|
|
22
24
|
<Route path="/" element={<NotFound />} />
|