@vestra-player/sdk 1.5.12 → 1.5.14

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/commands.js CHANGED
@@ -49,6 +49,444 @@ export const MENU_IDS = [
49
49
  "screen",
50
50
  "settings",
51
51
  ];
52
+ /**
53
+ * The submenus the player's built-in plugins declare, by key path, in menu
54
+ * order (built-in menus as in {@link MENU_IDS}, then top-level submenus).
55
+ * Menu paths merge by their English key, so a command whose path starts
56
+ * with the same keys appears in the same submenu, whatever the interface
57
+ * language; the submenu's title is the declaring plugin's translation. The
58
+ * player's own submenus (`Playback ▸ Speed`, `Playback ▸ Seek`) are not
59
+ * listed. The player's tests check that this list agrees with what its
60
+ * built-in plugins declare.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { BUILTIN_SUBMENUS } from "@vestra-player/sdk/testing";
65
+ *
66
+ * const snapshot = BUILTIN_SUBMENUS.find((menu) => menu.path.at(-1) === "Snapshot")!;
67
+ * plugin.commands.add({
68
+ * id: "snapshot-to-clipboard",
69
+ * path: ["@video", ...snapshot.path.slice(1), "Copy Snapshot"],
70
+ * run: copySnapshot,
71
+ * });
72
+ * ```
73
+ *
74
+ * @since 1.5.14
75
+ */
76
+ export const BUILTIN_SUBMENUS = Object.freeze([
77
+ { path: ["Open", "Recently Played"], plugins: ["vestra.history"] },
78
+ { path: ["Playback", "A-B Loop"], plugins: ["vestra.marks"] },
79
+ { path: ["Playback", "A-B Loop", "Loop Count"], plugins: ["vestra.marks"] },
80
+ { path: ["Playback", "Chapters and Bookmarks"], plugins: ["vestra.marks"] },
81
+ { path: ["Playback", "Playlist"], plugins: ["vestra.playlist"] },
82
+ { path: ["Playback", "Playlist", "Repeat"], plugins: ["vestra.playlist"] },
83
+ {
84
+ path: ["Playback", "Playlist", "When Opening a File"],
85
+ plugins: ["vestra.playlist"],
86
+ },
87
+ { path: ["Playback", "Resume Playback"], plugins: ["vestra.history"] },
88
+ {
89
+ path: ["Subtitles", "Online Subtitles"],
90
+ plugins: ["vestra.subtitle-online"],
91
+ },
92
+ { path: ["Subtitles", "Secondary Subtitle"], plugins: ["vestra.subtitles"] },
93
+ {
94
+ path: ["Subtitles", "Secondary Subtitle", "Position"],
95
+ plugins: ["vestra.subtitles"],
96
+ },
97
+ { path: ["Subtitles", "Subtitle Style"], plugins: ["vestra.subtitles"] },
98
+ { path: ["Subtitles", "Subtitle Track"], plugins: ["vestra.subtitles"] },
99
+ { path: ["Subtitles", "Sync"], plugins: ["vestra.subtitles"] },
100
+ { path: ["Video", "Aspect"], plugins: ["vestra.video"] },
101
+ { path: ["Video", "Crop/Stretch"], plugins: ["vestra.video"] },
102
+ { path: ["Video", "Deinterlace"], plugins: ["vestra.video"] },
103
+ { path: ["Video", "Deinterlace", "Output"], plugins: ["vestra.video"] },
104
+ { path: ["Video", "Framing"], plugins: ["vestra.video"] },
105
+ { path: ["Video", "Image Processing"], plugins: ["vestra.video"] },
106
+ { path: ["Video", "Pixel Shaders"], plugins: ["vestra.video"] },
107
+ {
108
+ path: ["Video", "Pixel Shaders", "HDR Tone Mapping"],
109
+ plugins: ["vestra.video"],
110
+ },
111
+ { path: ["Video", "Rotation"], plugins: ["vestra.video"] },
112
+ { path: ["Video", "Snapshot"], plugins: ["vestra.capture"] },
113
+ { path: ["Audio", "Audio Sync"], plugins: ["vestra.audio"] },
114
+ { path: ["Audio", "Audio Track"], plugins: ["vestra.audio"] },
115
+ { path: ["Audio", "Output Device"], plugins: ["vestra.audio"] },
116
+ { path: ["Audio", "Preset"], plugins: ["vestra.audio"] },
117
+ { path: ["Audio", "Processing"], plugins: ["vestra.audio"] },
118
+ { path: ["Screen", "Always on Top"], plugins: ["vestra.window"] },
119
+ { path: ["Screen", "When Playback Starts"], plugins: ["vestra.window"] },
120
+ {
121
+ path: ["Settings", "Appearance"],
122
+ plugins: ["vestra.preferences", "vestra.visualizer"],
123
+ },
124
+ { path: ["Settings", "Language"], plugins: ["vestra.preferences"] },
125
+ { path: ["Copy to Clipboard"], plugins: ["vestra.playback"] },
126
+ { path: ["Emulator"], plugins: ["vestra.nes"] },
127
+ ]);
128
+ /** owner | id | key path, or (title) outside the menu | bindings | panel */
129
+ const BUILTIN_COMMAND_TABLE = `
130
+ vestra.playback | open | Open File… | Ctrl+O, F3
131
+ vestra.playback | open-url | Open ▸ Open URL… | Ctrl+U
132
+ vestra.playlist | playlist.open-folder | Open ▸ Open Folder… | F2
133
+ vestra.subtitles | load | Open ▸ Open Subtitle… | Alt+O
134
+ vestra.audio | audio.load | Open ▸ Load External Audio…
135
+ vestra.history | recent.empty | Open ▸ Recently Played ▸ (No Entries)
136
+ vestra.history | recent.prune | Open ▸ Recently Played ▸ Remove Invalid Entries
137
+ vestra.history | recent.clear | Open ▸ Recently Played ▸ Clear Entries
138
+ vestra.history | recent.enabled | Open ▸ Recently Played ▸ Record Playback History
139
+ vestra.subtitles | load-secondary | Open ▸ Add Secondary Subtitle…
140
+ host | stop | Close | F4
141
+ vestra.playback | reopen | Reopen Current/Last File | Ctrl+Y
142
+ vestra.playback | copy-name | Copy to Clipboard ▸ File Name
143
+ vestra.playback | copy-folder | Copy to Clipboard ▸ Folder Path
144
+ vestra.playback | copy-path | Copy to Clipboard ▸ Full Path
145
+ host | play-pause | Playback ▸ Play/Pause | Space
146
+ host | previous | Playback ▸ Previous | PageUp, Mouse:X1
147
+ host | next | Playback ▸ Next | PageDown, Mouse:X2
148
+ host | frame-back | Playback ▸ Seek ▸ Previous Frame | D
149
+ host | frame-forward | Playback ▸ Seek ▸ Next Frame | F
150
+ vestra.playback | keyframe-back | Playback ▸ Seek ▸ Previous Keyframe | Ctrl+Shift+ArrowLeft
151
+ vestra.playback | keyframe-forward | Playback ▸ Seek ▸ Next Keyframe | Ctrl+Shift+ArrowRight
152
+ host | seek-backward | Playback ▸ Seek ▸ Back 5 s | ArrowLeft
153
+ host | seek-forward | Playback ▸ Seek ▸ Forward 5 s | ArrowRight
154
+ vestra.playback | backward-2 | Playback ▸ Seek ▸ Back 30 s | Ctrl+ArrowLeft
155
+ vestra.playback | forward-2 | Playback ▸ Seek ▸ Forward 30 s | Ctrl+ArrowRight
156
+ vestra.playback | backward-3 | Playback ▸ Seek ▸ Back 60 s | Shift+ArrowLeft
157
+ vestra.playback | forward-3 | Playback ▸ Seek ▸ Forward 60 s | Shift+ArrowRight
158
+ vestra.playback | backward-4 | Playback ▸ Seek ▸ Back 300 s | Ctrl+Alt+ArrowLeft
159
+ vestra.playback | forward-4 | Playback ▸ Seek ▸ Forward 300 s | Ctrl+Alt+ArrowRight
160
+ vestra.subtitles | dialogue-previous | Playback ▸ Seek ▸ Previous Dialogue | Home
161
+ vestra.subtitles | dialogue-next | Playback ▸ Seek ▸ Next Dialogue | End
162
+ vestra.subtitles | dialogue-current | Playback ▸ Seek ▸ Current Subtitle Start | Ctrl+Home
163
+ vestra.playback | jump-start | Playback ▸ Seek ▸ Jump to Start | Backspace
164
+ vestra.playback | time-jump | Playback ▸ Seek ▸ Seek Steps…
165
+ vestra.playback | jump | Playback ▸ Jump to Time… | G
166
+ host | rate-normal | Playback ▸ Speed ▸ Normal | Z
167
+ host | rate-down | Playback ▸ Speed ▸ Slower 0.1 | X
168
+ host | rate-up | Playback ▸ Speed ▸ Faster 0.1 | C
169
+ vestra.playback | rate-05 | Playback ▸ Speed ▸ 0.5×
170
+ vestra.playback | rate-15 | Playback ▸ Speed ▸ 1.5×
171
+ vestra.playback | rate-20 | Playback ▸ Speed ▸ 2×
172
+ vestra.playback | rate-custom | Playback ▸ Speed ▸ Custom…
173
+ vestra.playlist | playlist.add | Playback ▸ Playlist ▸ Add Files…
174
+ vestra.playlist | playlist.link | Playback ▸ Playlist ▸ Add Link…
175
+ vestra.playlist | playlist.add-folder | Playback ▸ Playlist ▸ Add Folder…
176
+ vestra.playlist | playlist.repeat-none | Playback ▸ Playlist ▸ Repeat ▸ Off
177
+ vestra.playlist | playlist.repeat-all | Playback ▸ Playlist ▸ Repeat ▸ All
178
+ vestra.playlist | playlist.repeat-one | Playback ▸ Playlist ▸ Repeat ▸ One
179
+ vestra.playlist | playlist.shuffle | Playback ▸ Playlist ▸ Shuffle
180
+ vestra.playlist | playlist.open-list | Playback ▸ Playlist ▸ Open List File…
181
+ vestra.playlist | playlist.save-list | Playback ▸ Playlist ▸ Save List…
182
+ vestra.playlist | playlist.open-mode-only | Playback ▸ Playlist ▸ When Opening a File ▸ Open Only That File
183
+ vestra.playlist | playlist.open-mode-similar | Playback ▸ Playlist ▸ When Opening a File ▸ Add Similar Files
184
+ vestra.playlist | playlist.open-mode-folder | Playback ▸ Playlist ▸ When Opening a File ▸ Add All Files in the Folder
185
+ vestra.playlist | playlist.fast-advance | Playback ▸ Playlist ▸ Fast Advance to Next
186
+ vestra.playlist | playlist.remember | Playback ▸ Playlist ▸ Remember List
187
+ vestra.playlist | playlist.confirm-clear | Playback ▸ Playlist ▸ Confirm Before Removing All
188
+ vestra.playback | prevent-sleep | Playback ▸ Disable Screen Saver While Playing
189
+ vestra.window | hide-cursor | Playback ▸ Hide Pointer While Playing
190
+ vestra.playback | hover-time | Playback ▸ Show Time on Seek Bar Hover
191
+ vestra.playback | hover-thumbnail | Playback ▸ Show Thumbnail on Seek Bar Hover
192
+ vestra.history | resume.off | Playback ▸ Resume Playback ▸ Don't Resume
193
+ vestra.history | resume.auto | Playback ▸ Resume Playback ▸ Resume Automatically
194
+ vestra.history | resume.ask | Playback ▸ Resume Playback ▸ Ask Every Time
195
+ vestra.history | resume.audio | Playback ▸ Resume Playback ▸ Also Remember Audio Files
196
+ vestra.history | resume.clear | Playback ▸ Resume Playback ▸ Clear All Saved Positions
197
+ vestra.marks | marks.list | Playback ▸ Chapters and Bookmarks ▸ Chapter and Bookmark List… | H
198
+ vestra.marks | marks.add | Playback ▸ Chapters and Bookmarks ▸ Add Bookmark | P
199
+ vestra.marks | marks.previous | Playback ▸ Chapters and Bookmarks ▸ Previous Bookmark or Chapter | Shift+PageUp
200
+ vestra.marks | marks.next | Playback ▸ Chapters and Bookmarks ▸ Next Bookmark or Chapter | Shift+PageDown
201
+ vestra.marks | marks.previous-chapter | Playback ▸ Chapters and Bookmarks ▸ Previous Chapter | Ctrl+PageUp
202
+ vestra.marks | marks.next-chapter | Playback ▸ Chapters and Bookmarks ▸ Next Chapter | Ctrl+PageDown
203
+ vestra.marks | marks.seekbar | Playback ▸ Chapters and Bookmarks ▸ Show Marks on Seek Bar
204
+ vestra.marks | ab.set-a | Playback ▸ A-B Loop ▸ Set Start Point | [
205
+ vestra.marks | ab.clear-a | Playback ▸ A-B Loop ▸ Clear Start Point | {
206
+ vestra.marks | ab.a-back | Playback ▸ A-B Loop ▸ Start Point Back 0.1 s | Ctrl+[
207
+ vestra.marks | ab.a-forward | Playback ▸ A-B Loop ▸ Start Point Forward 0.1 s | Alt+[
208
+ vestra.marks | ab.set-b | Playback ▸ A-B Loop ▸ Set End Point | ]
209
+ vestra.marks | ab.clear-b | Playback ▸ A-B Loop ▸ Clear End Point | }
210
+ vestra.marks | ab.b-back | Playback ▸ A-B Loop ▸ End Point Back 0.1 s | Ctrl+]
211
+ vestra.marks | ab.b-forward | Playback ▸ A-B Loop ▸ End Point Forward 0.1 s | Alt+]
212
+ vestra.marks | ab.back | Playback ▸ A-B Loop ▸ Move Range Back 0.1 s | Ctrl+Alt+[
213
+ vestra.marks | ab.forward | Playback ▸ A-B Loop ▸ Move Range Forward 0.1 s | Ctrl+Alt+]
214
+ vestra.marks | ab.segment | Playback ▸ A-B Loop ▸ Loop Current Chapter or Bookmark Range | Alt+\\
215
+ vestra.marks | ab.toggle | Playback ▸ A-B Loop ▸ A-B Loop On/Off | \\
216
+ vestra.marks | ab.count.endless | Playback ▸ A-B Loop ▸ Loop Count ▸ Endless
217
+ vestra.marks | ab.count.1 | Playback ▸ A-B Loop ▸ Loop Count ▸ Once
218
+ vestra.marks | ab.count.2 | Playback ▸ A-B Loop ▸ Loop Count ▸ 2 Times
219
+ vestra.marks | ab.count.3 | Playback ▸ A-B Loop ▸ Loop Count ▸ 3 Times
220
+ vestra.marks | ab.count.5 | Playback ▸ A-B Loop ▸ Loop Count ▸ 5 Times
221
+ vestra.marks | ab.count.10 | Playback ▸ A-B Loop ▸ Loop Count ▸ 10 Times
222
+ vestra.subtitles | show | Subtitles ▸ Show Subtitles | Alt+H
223
+ vestra.subtitles | track-off | Subtitles ▸ Subtitle Track ▸ Off
224
+ vestra.subtitles | track-cycle | Subtitles ▸ Subtitle Track ▸ Cycle | Alt+L
225
+ vestra.subtitles | second-off | Subtitles ▸ Secondary Subtitle ▸ Off
226
+ vestra.subtitles | second-position-top | Subtitles ▸ Secondary Subtitle ▸ Position ▸ Top
227
+ vestra.subtitles | second-position-aboveMain | Subtitles ▸ Secondary Subtitle ▸ Position ▸ Above Main Subtitle
228
+ vestra.subtitles | bold | Subtitles ▸ Subtitle Style ▸ Bold | Alt+B
229
+ vestra.subtitles | size-up | Subtitles ▸ Subtitle Style ▸ Size + | Alt+PageUp
230
+ vestra.subtitles | size-down | Subtitles ▸ Subtitle Style ▸ Size − | Alt+PageDown
231
+ vestra.subtitles | move-up | Subtitles ▸ Subtitle Style ▸ Move Up | Alt+ArrowUp
232
+ vestra.subtitles | move-down | Subtitles ▸ Subtitle Style ▸ Move Down | Alt+ArrowDown
233
+ vestra.subtitles | move-reset | Subtitles ▸ Subtitle Style ▸ Reset Position | Alt+Home
234
+ vestra.subtitles | shadow | Subtitles ▸ Subtitle Style ▸ Shadow
235
+ vestra.subtitles | box | Subtitles ▸ Subtitle Style ▸ Opaque Box
236
+ vestra.subtitles | ass-original | Subtitles ▸ Original ASS/SSA Styles
237
+ vestra.subtitles | sync-earlier | Subtitles ▸ Sync ▸ Earlier 0.5 s | .
238
+ vestra.subtitles | sync-later | Subtitles ▸ Sync ▸ Later 0.5 s | ,
239
+ vestra.subtitles | sync-reset | Subtitles ▸ Sync ▸ Reset | /
240
+ vestra.subtitles | sync-input | Subtitles ▸ Sync ▸ Adjust Sync…
241
+ vestra.subtitle-online | search | Subtitles ▸ Online Subtitles ▸ Search and Download Subtitles…
242
+ vestra.subtitle-online | settings | Subtitles ▸ Online Subtitles ▸ Online Subtitle Settings…
243
+ vestra.subtitles | reload | Subtitles ▸ Reload Subtitles | Ctrl+Alt+Y
244
+ vestra.subtitles | refresh | Subtitles ▸ Refresh Subtitle Tracks
245
+ vestra.subtitles | tune | Subtitles ▸ Subtitle Tuning…
246
+ vestra.subtitles | settings | Subtitles ▸ Subtitle Settings…
247
+ vestra.video | video.shader.use | Video ▸ Pixel Shaders ▸ Use Shaders | Shift+P
248
+ vestra.video | video.shader.list | Video ▸ Pixel Shaders ▸ Shader List…
249
+ vestra.video | video.shader.folder | Video ▸ Pixel Shaders ▸ Open Shader Folder
250
+ vestra.video | video.shader.reload | Video ▸ Pixel Shaders ▸ Reload Shaders
251
+ vestra.video | video.hdr.auto | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Auto
252
+ vestra.video | video.hdr.off | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Off
253
+ vestra.video | video.hdr.bt.2390 | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ BT.2390
254
+ vestra.video | video.hdr.hable | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Hable
255
+ vestra.video | video.hdr.mobius | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Mobius
256
+ vestra.video | video.hdr.reinhard | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Reinhard
257
+ vestra.video | video.hdr.clip | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Clip
258
+ vestra.video | video.hdr.peak | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Dynamic Peak Detection
259
+ vestra.video | video.hdr.settings | Video ▸ Pixel Shaders ▸ HDR Settings…
260
+ vestra.video | video.deinterlace.cycle | Video ▸ Deinterlace ▸ Cycle | Ctrl+I
261
+ vestra.video | video.deinterlace.auto | Video ▸ Deinterlace ▸ Auto
262
+ vestra.video | video.deinterlace.on | Video ▸ Deinterlace ▸ Always
263
+ vestra.video | video.deinterlace.off | Video ▸ Deinterlace ▸ Never
264
+ vestra.video | video.deinterlace.output.frame | Video ▸ Deinterlace ▸ Output ▸ By Frame
265
+ vestra.video | video.deinterlace.output.field | Video ▸ Deinterlace ▸ Output ▸ By Field (Double Rate)
266
+ vestra.video | video.fit.keep | Video ▸ Aspect ▸ Keep Aspect Ratio
267
+ vestra.video | video.fit.stretch | Video ▸ Aspect ▸ Ignore Aspect Ratio
268
+ vestra.video | video.fit.fill | Video ▸ Aspect ▸ Fill Window (Crop Excess)
269
+ vestra.video | video.aspect.cycle | Video ▸ Aspect ▸ Cycle Aspect Ratio | Shift+F6
270
+ vestra.video | video.aspect.default | Video ▸ Aspect ▸ Default (Source)
271
+ vestra.video | video.aspect.0 | Video ▸ Aspect ▸ 4:3
272
+ vestra.video | video.aspect.1 | Video ▸ Aspect ▸ 16:9
273
+ vestra.video | video.aspect.2 | Video ▸ Aspect ▸ 16:10
274
+ vestra.video | video.aspect.3 | Video ▸ Aspect ▸ 1.85:1
275
+ vestra.video | video.aspect.4 | Video ▸ Aspect ▸ 2.35:1
276
+ vestra.video | video.aspect.custom | Video ▸ Aspect ▸ Custom (x:y)
277
+ vestra.video | video.aspect.edit | Video ▸ Aspect ▸ Custom Aspect Ratio…
278
+ vestra.video | video.reframe.none | Video ▸ Crop/Stretch ▸ None
279
+ vestra.video | video.reframe.pad.0 | Video ▸ Crop/Stretch ▸ Stretch to 4:3
280
+ vestra.video | video.reframe.pad.1 | Video ▸ Crop/Stretch ▸ Stretch to 16:9
281
+ vestra.video | video.reframe.pad.2 | Video ▸ Crop/Stretch ▸ Stretch to 16:10
282
+ vestra.video | video.reframe.pad.3 | Video ▸ Crop/Stretch ▸ Stretch to 1.85:1
283
+ vestra.video | video.reframe.pad.4 | Video ▸ Crop/Stretch ▸ Stretch to 2.35:1
284
+ vestra.video | video.reframe.crop.0 | Video ▸ Crop/Stretch ▸ Crop to 4:3
285
+ vestra.video | video.reframe.crop.1 | Video ▸ Crop/Stretch ▸ Crop to 16:9
286
+ vestra.video | video.reframe.crop.2 | Video ▸ Crop/Stretch ▸ Crop to 16:10
287
+ vestra.video | video.reframe.crop.3 | Video ▸ Crop/Stretch ▸ Crop to 1.85:1
288
+ vestra.video | video.reframe.crop.4 | Video ▸ Crop/Stretch ▸ Crop to 2.35:1
289
+ vestra.video | video.reframe.reset | Video ▸ Crop/Stretch ▸ Reset Position
290
+ vestra.video | video.reframe.left | Video ▸ Crop/Stretch ▸ Move Left
291
+ vestra.video | video.reframe.right | Video ▸ Crop/Stretch ▸ Move Right
292
+ vestra.video | video.reframe.up | Video ▸ Crop/Stretch ▸ Move Up
293
+ vestra.video | video.reframe.down | Video ▸ Crop/Stretch ▸ Move Down
294
+ vestra.video | video.frame.reset | Video ▸ Framing ▸ Reset Framing | Numpad5, Alt+Backspace
295
+ vestra.video | video.frame.zoom-in | Video ▸ Framing ▸ Zoom In | Numpad9, Alt+Plus
296
+ vestra.video | video.frame.zoom-out | Video ▸ Framing ▸ Zoom Out | Numpad1, Alt+-
297
+ vestra.video | video.frame.right | Video ▸ Framing ▸ Pan → | Ctrl+Numpad6, Alt+ArrowRight
298
+ vestra.video | video.frame.left | Video ▸ Framing ▸ Pan ← | Ctrl+Numpad4, Alt+ArrowLeft
299
+ vestra.video | video.frame.up | Video ▸ Framing ▸ Pan ↑ | Ctrl+Numpad8, Alt+ArrowUp
300
+ vestra.video | video.frame.down | Video ▸ Framing ▸ Pan ↓ | Ctrl+Numpad2, Alt+ArrowDown
301
+ vestra.video | video.frame.centre | Video ▸ Framing ▸ Center Frame | Ctrl+Numpad5
302
+ vestra.video | video.flip.vertical | Video ▸ Image Processing ▸ Flip Vertically | Ctrl+P
303
+ vestra.video | video.flip.horizontal | Video ▸ Image Processing ▸ Flip Horizontally | Ctrl+Z
304
+ vestra.video | video.gaussian | Video ▸ Image Processing ▸ Gaussian Blur | Ctrl+B
305
+ vestra.video | video.sharpen | Video ▸ Image Processing ▸ Sharpen | Ctrl+R
306
+ vestra.video | video.edge | Video ▸ Image Processing ▸ Edge Sharpen
307
+ vestra.video | video.deblock | Video ▸ Image Processing ▸ Deblock | Ctrl+H
308
+ vestra.video | video.deband | Video ▸ Image Processing ▸ Deband
309
+ vestra.video | video.denoise.d3 | Video ▸ Image Processing ▸ 3D Denoise | Ctrl+N
310
+ vestra.video | video.denoise.gradual | Video ▸ Image Processing ▸ Gradual Denoise
311
+ vestra.video | video.denoise.temporal | Video ▸ Image Processing ▸ Temporal Denoise | Ctrl+M
312
+ vestra.video | video.frame-blend | Video ▸ Image Processing ▸ Frame Doubling
313
+ vestra.video | video.interpolation | Video ▸ Image Processing ▸ Motion Interpolation
314
+ vestra.video | video.rotate.cycle | Video ▸ Rotation ▸ Cycle | Alt+K
315
+ vestra.video | video.rotate.0 | Video ▸ Rotation ▸ No Rotation
316
+ vestra.video | video.rotate.90 | Video ▸ Rotation ▸ 90°
317
+ vestra.video | video.rotate.180 | Video ▸ Rotation ▸ 180°
318
+ vestra.video | video.rotate.270 | Video ▸ Rotation ▸ 270°
319
+ vestra.capture | capture.save | Video ▸ Snapshot ▸ Save Current Frame | Ctrl+E
320
+ vestra.capture | capture.saveAs | Video ▸ Snapshot ▸ Save Current Frame As…
321
+ vestra.capture | capture.copy | Video ▸ Snapshot ▸ Copy Current Frame | Ctrl+C
322
+ vestra.capture | capture.saveScreen | Video ▸ Snapshot ▸ Save Rendered Frame | Ctrl+Alt+E
323
+ vestra.capture | capture.saveScreenAs | Video ▸ Snapshot ▸ Save Rendered Frame As…
324
+ vestra.capture | capture.copyScreen | Video ▸ Snapshot ▸ Copy Rendered Frame | Ctrl+Alt+C
325
+ vestra.capture | capture.burst | Video ▸ Snapshot ▸ Burst… | Ctrl+G
326
+ vestra.capture | capture.clip | Video ▸ Snapshot ▸ Export Clip…
327
+ vestra.capture | capture.tasks | Video ▸ Snapshot ▸ Export Tasks…
328
+ vestra.capture | capture.format.png | Video ▸ Snapshot ▸ Capture as PNG
329
+ vestra.capture | capture.format.jpeg | Video ▸ Snapshot ▸ Capture as JPEG
330
+ vestra.capture | capture.format.bmp | Video ▸ Snapshot ▸ Capture as BMP
331
+ vestra.capture | capture.screenSubtitles | Video ▸ Snapshot ▸ Include Subtitles in Rendered Frame
332
+ vestra.capture | capture.settings | Video ▸ Snapshot ▸ Snapshot Settings…
333
+ vestra.capture | capture.folder | Video ▸ Snapshot ▸ Snapshot Folder…
334
+ vestra.capture | capture.openFolder | Video ▸ Snapshot ▸ Open Snapshot Folder
335
+ vestra.video | video.picture | Video ▸ Picture Adjustments…
336
+ vestra.video | video.reset | Video ▸ Reset | Q
337
+ vestra.video | video.brightness.down | Video ▸ Brightness -1% | W
338
+ vestra.video | video.brightness.up | Video ▸ Brightness +1% | E
339
+ vestra.video | video.contrast.down | Video ▸ Contrast -1% | R
340
+ vestra.video | video.contrast.up | Video ▸ Contrast +1% | T
341
+ vestra.video | video.saturation.down | Video ▸ Saturation -1% | Y
342
+ vestra.video | video.saturation.up | Video ▸ Saturation +1% | U
343
+ vestra.video | video.hue.down | Video ▸ Hue -1% | I
344
+ vestra.video | video.hue.up | Video ▸ Hue +1% | O
345
+ host | volume-up | Audio ▸ Volume + | ArrowUp, Mouse:WheelUp
346
+ host | volume-down | Audio ▸ Volume − | ArrowDown, Mouse:WheelDown
347
+ host | mute | Audio ▸ Mute | M
348
+ vestra.audio | audio.workbench | Audio ▸ Audio Workbench…
349
+ vestra.audio | audio.track.none | Audio ▸ Audio Track ▸ No Audio Tracks
350
+ vestra.audio | audio.track.cycle | Audio ▸ Audio Track ▸ Cycle Tracks | Alt+A
351
+ vestra.audio | audio.channels.stereo | Audio ▸ Audio Track ▸ Stereo
352
+ vestra.audio | audio.channels.left | Audio ▸ Audio Track ▸ Left Channel
353
+ vestra.audio | audio.channels.right | Audio ▸ Audio Track ▸ Right Channel
354
+ vestra.audio | audio.channels.mono | Audio ▸ Audio Track ▸ Mono Mix
355
+ vestra.audio | audio.channels.swap | Audio ▸ Audio Track ▸ Swap Left and Right
356
+ vestra.audio | audio.device.none | Audio ▸ Output Device ▸ No Output Devices
357
+ vestra.audio | audio.equalizer | Audio ▸ Equalizer | Shift+E
358
+ vestra.audio | audio.preset.0 | Audio ▸ Preset ▸ Flat
359
+ vestra.audio | audio.preset.1 | Audio ▸ Preset ▸ Voice
360
+ vestra.audio | audio.preset.2 | Audio ▸ Preset ▸ Music
361
+ vestra.audio | audio.preset.3 | Audio ▸ Preset ▸ Cinema
362
+ vestra.audio | audio.sync.earlier | Audio ▸ Audio Sync ▸ 50 ms Earlier | >
363
+ vestra.audio | audio.sync.later | Audio ▸ Audio Sync ▸ 50 ms Later | <
364
+ vestra.audio | audio.sync.reset | Audio ▸ Audio Sync ▸ Reset | ?
365
+ vestra.audio | audio.sync.keep.none | Audio ▸ Audio Sync ▸ Don't Keep
366
+ vestra.audio | audio.sync.keep.session | Audio ▸ Audio Sync ▸ Keep for This Run
367
+ vestra.audio | audio.sync.keep.always | Audio ▸ Audio Sync ▸ Always Keep
368
+ vestra.audio | audio.normalize | Audio ▸ Processing ▸ Normalize | Shift+N
369
+ vestra.audio | audio.clarity | Audio ▸ Processing ▸ Crystalizer | Shift+C
370
+ vestra.audio | audio.reset | Audio ▸ Reset
371
+ vestra.visualizer | audio-cover | Audio ▸ Show Album Cover for Audio Only
372
+ vestra.nes | nes.open | Emulator ▸ Open NES ROM…
373
+ vestra.nes | nes.stop | Emulator ▸ Stop NES
374
+ vestra.nes | nes.window | Emulator ▸ NES Tools…
375
+ host | fullscreen | Toggle Fullscreen | Enter, Alt+Enter, F11, Mouse:LeftDoubleClick
376
+ vestra.window | pip | Screen ▸ Picture-in-Picture
377
+ vestra.window | mini | Screen ▸ Mini Size | \`
378
+ vestra.window | size.0.5 | Screen ▸ 0.5× | 1
379
+ vestra.window | size.1 | Screen ▸ 1× | 2
380
+ vestra.window | size.1.5 | Screen ▸ 1.5× | 3
381
+ vestra.window | size.2 | Screen ▸ 2× | 4
382
+ vestra.window | fit | Screen ▸ Fit to Video Aspect Ratio | 8
383
+ vestra.window | auto.off | Screen ▸ When Playback Starts ▸ Don't Resize
384
+ vestra.window | auto.0.5 | Screen ▸ When Playback Starts ▸ 0.5×
385
+ vestra.window | auto.1 | Screen ▸ When Playback Starts ▸ 1×
386
+ vestra.window | auto.1.5 | Screen ▸ When Playback Starts ▸ 1.5×
387
+ vestra.window | auto.2 | Screen ▸ When Playback Starts ▸ 2×
388
+ vestra.window | auto.fit | Screen ▸ When Playback Starts ▸ Fit to Video Aspect Ratio
389
+ vestra.window | auto.once | Screen ▸ When Playback Starts ▸ Only Once
390
+ vestra.window | top.cycle | Screen ▸ Always on Top ▸ Cycle Mode | Ctrl+T
391
+ vestra.window | top.never | Screen ▸ Always on Top ▸ Never
392
+ vestra.window | top.always | Screen ▸ Always on Top ▸ Always
393
+ vestra.window | top.playing | Screen ▸ Always on Top ▸ While Playing
394
+ vestra.window | top.fullscreen | Screen ▸ Always on Top ▸ While Playing in Fullscreen
395
+ vestra.playlist | playlist.visibility | Playlist | F6
396
+ vestra.info | info.toggle | Playback Info | Tab
397
+ vestra.info | info.properties | Playback Properties… | Ctrl+F1
398
+ vestra.workshop | workshop.open | Vestra Player AI…
399
+ vestra.store | open | Plugin Store…
400
+ vestra.preferences | options | Settings ▸ Options… | F5
401
+ vestra.hotkeys | keys | Settings ▸ Keyboard Shortcuts…
402
+ vestra.hotkeys | mouse | Settings ▸ Mouse…
403
+ vestra.hotkeys | ignore-defaults | Settings ▸ Ignore Default Shortcuts
404
+ vestra.plugins | manage | Settings ▸ Plugin Manager…
405
+ vestra.preferences | theme.vestra | Settings ▸ Appearance ▸ Vestra Dark
406
+ vestra.preferences | theme.vestra-light | Settings ▸ Appearance ▸ Vestra Light
407
+ vestra.visualizer | idle-logo | Settings ▸ Appearance ▸ Show Logo When Idle
408
+ vestra.visualizer | choose-logo | Settings ▸ Appearance ▸ Choose Logo Image…
409
+ vestra.visualizer | reset-logo | Settings ▸ Appearance ▸ Restore Default Logo
410
+ vestra.preferences | language.system | Settings ▸ Language ▸ Follow System
411
+ vestra.preferences | language.en | Settings ▸ Language ▸ English
412
+ vestra.preferences | language.zh-Hans | Settings ▸ Language ▸ 简体中文
413
+ vestra.preferences | language.ja | Settings ▸ Language ▸ 日本語
414
+ vestra.preferences | language.ko | Settings ▸ Language ▸ 한국어
415
+ vestra.preferences | language.de | Settings ▸ Language ▸ Deutsch
416
+ vestra.preferences | language.fr | Settings ▸ Language ▸ Français
417
+ vestra.preferences | language.es | Settings ▸ Language ▸ Español
418
+ vestra.info | info.about | About Vestra Player… | F1
419
+ host | quit | Quit | Cmd+Q
420
+ host | exit-fullscreen | (Leave Fullscreen) | Escape
421
+ vestra.info | info.brief | (Brief Info) | Shift+Tab
422
+ vestra.playlist | playlist.play | (Play) | Enter, P | panel
423
+ vestra.playlist | playlist.list-add | (Add Files…) | Ctrl+I | panel
424
+ vestra.playlist | playlist.list-add-folder | (Add Folder…) | Ctrl+O | panel
425
+ vestra.playlist | playlist.list-link | (Add Link…) | Ctrl+U | panel
426
+ vestra.playlist | playlist.remove | (Remove Selected) | Delete, Backspace | panel
427
+ vestra.playlist | playlist.remove-unselected | (Remove Unselected) | Ctrl+Delete | panel
428
+ vestra.playlist | playlist.remove-invalid | (Remove Invalid Items) | Ctrl+T | panel
429
+ vestra.playlist | playlist.clear | (Remove All) | Alt+Delete | panel
430
+ vestra.playlist | playlist.reveal | (Show in Finder) | Ctrl+F | panel
431
+ vestra.playlist | playlist.move-top | (Move to Top) | Ctrl+Home | panel
432
+ vestra.playlist | playlist.move-up | (Move Up) | Ctrl+ArrowUp | panel
433
+ vestra.playlist | playlist.move-down | (Move Down) | Ctrl+ArrowDown | panel
434
+ vestra.playlist | playlist.move-bottom | (Move to Bottom) | Ctrl+End | panel
435
+ vestra.playlist | playlist.sort-ascending | (Ascending) | Ctrl+1 | panel
436
+ vestra.playlist | playlist.sort-descending | (Descending) | Ctrl+2 | panel
437
+ vestra.playlist | playlist.sort-title | (By Title) | Ctrl+3 | panel
438
+ vestra.playlist | playlist.sort-filename | (By File Name) | Ctrl+4 | panel
439
+ vestra.playlist | playlist.sort-extension | (By Extension) | Ctrl+5 | panel
440
+ vestra.playlist | playlist.sort-size | (By Size) | Ctrl+6 | panel
441
+ vestra.playlist | playlist.sort-duration | (By Duration) | Ctrl+7 | panel
442
+ vestra.playlist | playlist.sort-date | (By Date) | Ctrl+8 | panel
443
+ vestra.playlist | playlist.sort-shuffle | (Random Order) | Ctrl+9 | panel
444
+ vestra.playlist | playlist.sort-folder | (By Folder) | Ctrl+0 | panel
445
+ vestra.playlist | playlist.sort-path | (Sort by Path)
446
+ vestra.playlist | playlist.sort-reverse | (Reverse Current Order)
447
+ vestra.playlist | playlist.select-all | (Select All) | Ctrl+A | panel
448
+ vestra.playlist | playlist.search | (Search) | Ctrl+S | panel
449
+ vestra.playlist | playlist.list-open-list | (Open List File…) | F3 | panel
450
+ vestra.playlist | playlist.list-save-list | (Save List…) | F2 | panel
451
+ vestra.playlist | playlist.close | (Close List) | F6 | panel
452
+ vestra.playlist | playlist.settings | (Playlist Settings…) | F5 | panel
453
+ `;
454
+ /**
455
+ * The commands the player and its built-in plugins register when the
456
+ * player starts with nothing open, in menu order (commands outside the main
457
+ * menu last): their key paths, default bindings and owners. Use it to pick
458
+ * a menu place and default keys that do not collide with the player's (the
459
+ * user's own changes come on top; see {@link @vestra-player/sdk!Bindings | Bindings}). Menu items built
460
+ * from content (tracks, recent files, output devices) are not listed; their
461
+ * placeholders (`No Audio Tracks`) are. The player's tests check that this
462
+ * list agrees with what the player and its built-in plugins register.
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * import assert from "node:assert/strict";
467
+ * import { BUILTIN_COMMANDS } from "@vestra-player/sdk/testing";
468
+ *
469
+ * const taken = BUILTIN_COMMANDS.filter((command) => command.focus === null)
470
+ * .flatMap((command) => command.bindings);
471
+ * assert.ok(!taken.includes("Ctrl+K"));
472
+ * ```
473
+ *
474
+ * @since 1.5.14
475
+ */
476
+ export const BUILTIN_COMMANDS = Object.freeze(BUILTIN_COMMAND_TABLE.trim()
477
+ .split("\n")
478
+ .map((line) => {
479
+ const [owner = "", id = "", menu = "", keys = "", focus = ""] = line.split(" | ");
480
+ const path = menu.startsWith("(") ? [] : menu.split(" ▸ ");
481
+ return Object.freeze({
482
+ owner,
483
+ id,
484
+ path: Object.freeze(path),
485
+ title: path.at(-1) ?? menu.slice(1, -1),
486
+ bindings: Object.freeze(keys ? keys.split(", ") : []),
487
+ focus: focus === "panel" ? "panel" : null,
488
+ });
489
+ }));
52
490
  export function commandClient(transport) {
53
491
  return {
54
492
  register: (id, revision, declaration, state) => transport.request("commands.register", {
@@ -648,10 +648,17 @@ export function extract(options) {
648
648
  const findings = extractor.findings.sort((a, b) => a.file === b.file ? a.line - b.line : a.file < b.file ? -1 : 1);
649
649
  return { messages: [...messages.values()], findings };
650
650
  }
651
- const entryOf = (message) => {
651
+ /** `msgstr[n]` lines a plural entry has: the header's `nplurals`, else the language's. */
652
+ const formsOf = (language, header) => {
653
+ const declared = /nplurals\s*=\s*(\d+)/.exec(header ?? "")?.[1];
654
+ if (declared !== undefined && Number(declared) >= 1)
655
+ return Number(declared);
656
+ return ONE_FORM.has(language) ? 1 : 2;
657
+ };
658
+ const entryOf = (message, forms = 2) => {
652
659
  const entry = {
653
660
  id: message.id,
654
- strings: message.plural === undefined ? [""] : ["", ""],
661
+ strings: message.plural === undefined ? [""] : Array(forms).fill(""),
655
662
  fuzzy: false,
656
663
  notes: [...message.notes],
657
664
  references: [...message.references],
@@ -669,7 +676,7 @@ export function template(manifest, messages) {
669
676
  const project = [manifest.id, manifest.version]
670
677
  .filter((part) => typeof part === "string")
671
678
  .join(" ");
672
- return writePo(`Project-Id-Version: ${project}\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n`, messages.map(entryOf));
679
+ return writePo(`Project-Id-Version: ${project}\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n`, messages.map((message) => entryOf(message)));
673
680
  }
674
681
  /** The header of a new `<lang>.po`. */
675
682
  export const newHeader = (language) => `Language: ${language}\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nPlural-Forms: ${pluralForms(language)}\n`;
@@ -687,20 +694,30 @@ export function merge(existing, language, messages) {
687
694
  old.set(keyOf(entry.context, entry.id), entry);
688
695
  const result = [];
689
696
  const present = new Set();
697
+ // A plural entry has exactly nplurals msgstr[n] (one for ja, ko,
698
+ // zh-Hans); extra empty ones are dropped, filled ones kept.
699
+ const forms = formsOf(language, parsed?.header ?? newHeader(language));
690
700
  let added = 0;
691
701
  let translated = 0;
692
702
  for (const message of messages) {
693
703
  const key = keyOf(message.context, message.id);
694
704
  present.add(key);
695
- const entry = entryOf(message);
705
+ const entry = entryOf(message, forms);
696
706
  const before = old.get(key);
707
+ // The translator's own comments stay with their message.
708
+ if (before?.comments?.length)
709
+ entry.comments = [...before.comments];
697
710
  if (before && hasText(before)) {
698
711
  entry.strings = [...before.strings];
699
712
  if (message.plural === undefined)
700
713
  entry.strings = [entry.strings[0]];
701
- else
702
- while (entry.strings.length < 2)
714
+ else {
715
+ while (entry.strings.length < forms)
703
716
  entry.strings.push("");
717
+ while (entry.strings.length > forms &&
718
+ entry.strings[entry.strings.length - 1] === "")
719
+ entry.strings.pop();
720
+ }
704
721
  entry.fuzzy = before.fuzzy;
705
722
  entry.flags = [...before.flags];
706
723
  }
@@ -718,7 +735,7 @@ export function merge(existing, language, messages) {
718
735
  obsolete++;
719
736
  }
720
737
  return {
721
- text: writePo(parsed?.header ?? newHeader(language), result),
738
+ text: writePo(parsed?.header ?? newHeader(language), result, parsed?.headerComments ?? []),
722
739
  total: messages.length,
723
740
  translated,
724
741
  added,
package/dist/i18n.d.ts CHANGED
@@ -25,16 +25,23 @@ export interface PoEntry {
25
25
  flags: string[];
26
26
  /** A `#~` entry: no longer in the source. */
27
27
  obsolete: boolean;
28
+ /** `# ` lines: the translator's own comments, kept as written. */
29
+ comments?: string[];
28
30
  }
29
31
  /** The catalog key: `msgid`, or `msgctxt` U+0004 `msgid`. */
30
32
  export declare const keyOf: (context: string | undefined, id: string) => string;
31
33
  /** Parse a `.po` (or `.pot`) file. Throws with the line number on errors. */
32
34
  export declare function parsePo(text: string): {
33
35
  header: string;
36
+ /** The comment lines before the header entry, as written (`# Reviewed…`, `#, fuzzy`). */
37
+ headerComments: string[];
34
38
  entries: PoEntry[];
35
39
  };
36
- /** Write a `.po` file: the header, then the entries in the given order. */
37
- export declare function writePo(header: string, entries: readonly PoEntry[]): string;
40
+ /**
41
+ * Write a `.po` file: the header (after its comment lines, as written),
42
+ * then the entries in the given order.
43
+ */
44
+ export declare function writePo(header: string, entries: readonly PoEntry[], headerComments?: readonly string[]): string;
38
45
  /** A message of a compiled catalog: a translation, or `[one, other]`. */
39
46
  export type CompiledMessage = string | [string, string];
40
47
  /**
package/dist/i18n.js CHANGED
@@ -23,6 +23,9 @@ const translated = (entry) => !entry.fuzzy && !entry.obsolete && (entry.strings[
23
23
  export function parsePo(text) {
24
24
  const entries = [];
25
25
  let header = "";
26
+ let headerComments = [];
27
+ // Every comment line of the entry being read, as written.
28
+ let raw = [];
26
29
  const blank = () => ({
27
30
  id: "",
28
31
  strings: [],
@@ -39,11 +42,14 @@ export function parsePo(text) {
39
42
  let pending = blank();
40
43
  const finish = () => {
41
44
  if (started) {
42
- if (entry.id === "" && entry.context === undefined)
45
+ if (entry.id === "" && entry.context === undefined) {
43
46
  header = entry.strings[0] ?? "";
47
+ headerComments = raw;
48
+ }
44
49
  else
45
50
  entries.push(entry);
46
51
  }
52
+ raw = [];
47
53
  entry = blank();
48
54
  started = false;
49
55
  field = "none";
@@ -66,8 +72,11 @@ export function parsePo(text) {
66
72
  else if (line.startsWith("#")) {
67
73
  if (started)
68
74
  finish();
75
+ raw.push(lines[index].trimEnd());
69
76
  const body = line.slice(2).trim();
70
- if (line.startsWith("#."))
77
+ if (line === "#" || line.startsWith("# "))
78
+ (pending.comments ??= []).push(line.slice(2));
79
+ else if (line.startsWith("#."))
71
80
  pending.notes.push(body);
72
81
  else if (line.startsWith("#:"))
73
82
  pending.references.push(...body.split(/\s+/).filter(Boolean));
@@ -135,7 +144,7 @@ export function parsePo(text) {
135
144
  field = next;
136
145
  }
137
146
  finish();
138
- return { header, entries };
147
+ return { header, headerComments, entries };
139
148
  }
140
149
  function unquote(text, at) {
141
150
  if (text.length < 2 || !text.startsWith('"') || !text.endsWith('"'))
@@ -172,12 +181,19 @@ function field(keyword, text) {
172
181
  return `${keyword} ${poString(text)}`;
173
182
  return [`${keyword} ""`, ...parts.map(poString)].join("\n");
174
183
  }
175
- /** Write a `.po` file: the header, then the entries in the given order. */
176
- export function writePo(header, entries) {
177
- const blocks = [`msgid ""\n${field("msgstr", header)}`];
184
+ /**
185
+ * Write a `.po` file: the header (after its comment lines, as written),
186
+ * then the entries in the given order.
187
+ */
188
+ export function writePo(header, entries, headerComments = []) {
189
+ const blocks = [
190
+ [...headerComments, `msgid ""`, field("msgstr", header)].join("\n"),
191
+ ];
178
192
  for (const entry of entries) {
179
193
  const prefix = entry.obsolete ? "#~ " : "";
180
194
  const lines = [];
195
+ for (const comment of entry.comments ?? [])
196
+ lines.push(comment === "" ? "#" : `# ${comment}`);
181
197
  for (const note of entry.notes)
182
198
  lines.push(`#. ${note}`);
183
199
  if (entry.references.length && !entry.obsolete)
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The colours of the player's palettes, by `Theme` colour token (without the
3
+ * `Theme.` prefix): `vestra`, the dark default, and `vestra-light` (player
4
+ * 0.4.0). The player sets one of them on every view; a plugin reads the
5
+ * colours through `Theme` in its Slint files and never needs these values.
6
+ * They are here for the documentation (Reference: theme) and for tests that
7
+ * check a design against both palettes.
8
+ *
9
+ * Current values: a later player may adjust them, a token keeps its
10
+ * meaning. The `vestra` values are also the defaults of the SDK's copy of
11
+ * the palette (`slint/palette.slint`); the player's tests check both
12
+ * palettes against its own.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { THEME_PALETTES } from "@vestra-player/sdk/testing";
17
+ *
18
+ * const accent = THEME_PALETTES["vestra-light"].accent; // "#d4128a"
19
+ * ```
20
+ *
21
+ * @since 1.5.14
22
+ */
23
+ export declare const THEME_PALETTES: Readonly<Record<"vestra" | "vestra-light", Readonly<Record<string, string>>>>;