@vestra-player/sdk 1.5.42 → 1.5.44
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/CHANGELOG.md +20 -0
- package/dist/api-requests.js +1 -0
- package/dist/host-capabilities.js +2 -0
- package/dist/player-runtime.d.ts +1 -0
- package/dist/player-runtime.js +1 -0
- package/dist/plugin/patch.d.ts +101 -0
- package/dist/plugin/patch.js +337 -0
- package/dist/plugin/runtime.d.ts +6 -0
- package/dist/plugin/runtime.js +11 -0
- package/dist/plugin/services.js +24 -2
- package/dist/plugin/settings.js +2 -2
- package/dist/plugin/testing.d.ts +65 -1
- package/dist/plugin/testing.js +238 -15
- package/dist/plugin/types.d.ts +192 -1
- package/dist/plugin/ui.d.ts +2 -2
- package/dist/plugin/ui.js +217 -17
- package/dist/wire.d.ts +73 -7
- package/dist/wire.js +3 -3
- package/llms-full.txt +305 -6
- package/llms.txt +23 -23
- package/package.json +1 -1
- package/slint/vestra/markdown.slint +17 -11
- package/slint/vestra/widgets.slint +770 -1
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @vestra-player/sdk 1.5.
|
|
1
|
+
# @vestra-player/sdk 1.5.44: full reference
|
|
2
2
|
|
|
3
3
|
> Everything needed to write a Vestra Player plugin (plugin API `vestra/1`): the guides, every public declaration of `@vestra-player/sdk` and `@vestra-player/sdk/testing` with its documentation, and the complete source of the example plugins. Generated from the SDK's sources by scripts/llms.mjs; the short index is llms.txt.
|
|
4
4
|
|
|
@@ -2281,6 +2281,7 @@ The table lists the API whose documentation names a player version, newest last:
|
|
|
2281
2281
|
| 0.5.16 | UpscalingReasonCode, UpscalingReason, UpscalingState, VideoUpscaling |
|
|
2282
2282
|
| 0.5.20 | VideoShaders |
|
|
2283
2283
|
| 0.5.21 | Scope.when, When, Bindings.onChange |
|
|
2284
|
+
| 0.5.25 | UpscalingReason.cause, UpscalingReason.plugins, UpscalingDecodingCause, UpscalingPlugin, UpscalingVideoSize, UpscalingState.video, UpscalingState.area, UpscalingState.gpu, UpscalingState.hwdecCurrent, UpscalingState.inChain, VideoUpscaling.onChange |
|
|
2284
2285
|
|
|
2285
2286
|
The Slint library carries no version tags: `Theme.danger` needs player 0.3.0; `@vestra/icons.slint` and the palette `vestra-light` need 0.4.0 (Theme).
|
|
2286
2287
|
|
|
@@ -2318,7 +2319,7 @@ A plugin lists what it may do in the manifest's `capabilities` (Manifest). A cal
|
|
|
2318
2319
|
| `ui.theme` | Appearance.set, Appearance.setLanguage | — | Change the player's palette | |
|
|
2319
2320
|
| `secrets` | Secrets.get, Secrets.set, Secrets.delete | — | Store sign-in details in the system keychain | yes |
|
|
2320
2321
|
| `rendering.manage` | Rendering.setPreference, Rendering.restartPlayer | — | Choose GPU or software rendering and restart the player | yes |
|
|
2321
|
-
| `video.upscaling` | VideoUpscaling.state, VideoUpscaling.set | — | Upscale the video with the GPU's video upscaler | |
|
|
2322
|
+
| `video.upscaling` | VideoUpscaling.state, VideoUpscaling.set, VideoUpscaling.onChange | — | Upscale the video with the GPU's video upscaler | |
|
|
2322
2323
|
|
|
2323
2324
|
**Also needs** applies to every member of the row: declare those capabilities as well. A few members or options need one more, documented at the member:
|
|
2324
2325
|
|
|
@@ -3241,17 +3242,21 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
|
|
|
3241
3242
|
| `checked` | in `bool` | | Icon buttons: the "on" state, drawn in the accent colour. |
|
|
3242
3243
|
| `clicked` | callback `()` | | |
|
|
3243
3244
|
|
|
3244
|
-
**`Slider`**: 2px line slider. Bar style (default): 8px round thumb, a white core in a 2px accent ring, gradient fill. Panel style (`panel: true`): 14×12 thumb, gradient fill. The track is `Theme.line` in both.
|
|
3245
|
+
**`Slider`**: 2px line slider. Bar style (default): 8px round thumb, a white core in a 2px accent ring, gradient fill. Panel style (`panel: true`): 14×12 thumb, gradient fill. The track is `Theme.line` in both. Keyboard (focused by Tab or `focus()`; a click does not take the focus, so the bar's volume slider leaves the arrows to seeking), as std-widgets' Slider: Left/Down and Right/Up move by `step`, Page Down/Up by 10 steps, Home/End to the ends; `changed` follows every move and `released` the key's release. Screen readers can increment, decrement or set the value.
|
|
3245
3246
|
|
|
3246
3247
|
| Member | Kind | Default | Note |
|
|
3247
3248
|
| --- | --- | --- | --- |
|
|
3248
3249
|
| `value` | in-out `float` | | |
|
|
3249
3250
|
| `minimum` | in `float` | `0` | |
|
|
3250
3251
|
| `maximum` | in `float` | `1` | |
|
|
3252
|
+
| `step` | in `float` | `(root.maximum - root.minimum) / 20` | One key press: a twentieth of the range unless set. |
|
|
3251
3253
|
| `enabled` | in `bool` | `true` | |
|
|
3252
3254
|
| `panel` | in `bool` | | |
|
|
3255
|
+
| `focusable` | in `bool` | `true` | false: Tab skips the slider (the bottom bar's seek and volume sliders, so Tab never moves the arrows from seeking to the slider); `focus()` still works. |
|
|
3256
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3253
3257
|
| `changed` | callback `(float)` | | |
|
|
3254
3258
|
| `released` | callback `(float)` | | |
|
|
3259
|
+
| `set-value` | function `(v: float)` | | Clamped to the range; `changed` only when the value moves. |
|
|
3255
3260
|
|
|
3256
3261
|
**`UrlInputRow`**: Link input: a multi-line input and [Open] [Cancel]. Enter or the confirm button accepts (Shift+Enter starts a new line), Esc or Cancel cancels. No validation here: the plugin checks the address.
|
|
3257
3262
|
|
|
@@ -3267,6 +3272,93 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
|
|
|
3267
3272
|
| `focus-input` | function `()` | | |
|
|
3268
3273
|
| `select-all` | function `()` | | |
|
|
3269
3274
|
|
|
3275
|
+
**`Check`**: Check box with a label that wraps onto more lines when the row is narrower than the label (the box stays beside the first line). A click or Space flips `checked`, then `toggled` reports the new state, as std-widgets' CheckBox: bind it two-way (`checked <=> Bridge.on`), or set it again from the plugin's data in a `changed` handler, since the click replaces a one-way binding. Plugin-decided (`self-toggle: false`, like Radio): a click or Space does not flip the box; `toggled` reports the state asked for (`!checked`) and the plugin sets `checked` when it agrees, for example after a confirmation, so a one-way binding (`checked: Bridge.on`) stays. Both modes also call `clicked`. Default style: a 13px box, accent when checked. Panel style (`panel: true`, the picture and audio workbenches' switches): a 12px box on `bg-bar` with a 6px `playing` square.
|
|
3276
|
+
|
|
3277
|
+
| Member | Kind | Default | Note |
|
|
3278
|
+
| --- | --- | --- | --- |
|
|
3279
|
+
| `text` | in `string` | | |
|
|
3280
|
+
| `checked` | in-out `bool` | | |
|
|
3281
|
+
| `enabled` | in `bool` | `true` | |
|
|
3282
|
+
| `panel` | in `bool` | | |
|
|
3283
|
+
| `self-toggle` | in `bool` | `true` | false: the plugin owns `checked` (see above). |
|
|
3284
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3285
|
+
| `toggled` | callback `(bool)` | | The state after the click (self-toggle) or the state asked for. |
|
|
3286
|
+
| `clicked` | callback `()` | | |
|
|
3287
|
+
| `toggle` | function `()` | | What a click does; also Space and the screen reader's default action. |
|
|
3288
|
+
|
|
3289
|
+
**`RadioMark`**: The 12px radio dot alone, for rows a plugin lays out itself, such as the palette list with its previews. `hover` lights the ring; `focused` draws the focus ring.
|
|
3290
|
+
|
|
3291
|
+
| Member | Kind | Default | Note |
|
|
3292
|
+
| --- | --- | --- | --- |
|
|
3293
|
+
| `checked` | in `bool` | | |
|
|
3294
|
+
| `hover` | in `bool` | | |
|
|
3295
|
+
| `enabled` | in `bool` | `true` | |
|
|
3296
|
+
| `focused` | in `bool` | | |
|
|
3297
|
+
|
|
3298
|
+
**`Radio`**: One radio choice: the mark, 4px, an 11px label. The plugin owns the state: `checked` is what it shows, a click or Space asks with `clicked`. For a set of choices from one list use RadioGroup, which also takes the arrow keys. (`focusable` and `focused` are RadioGroup's: its radios are one Tab stop, the group's.)
|
|
3299
|
+
|
|
3300
|
+
| Member | Kind | Default | Note |
|
|
3301
|
+
| --- | --- | --- | --- |
|
|
3302
|
+
| `text` | in `string` | | |
|
|
3303
|
+
| `checked` | in `bool` | | |
|
|
3304
|
+
| `enabled` | in `bool` | `true` | |
|
|
3305
|
+
| `focusable` | in `bool` | `true` | |
|
|
3306
|
+
| `focused` | in `bool` | | |
|
|
3307
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3308
|
+
| `clicked` | callback `()` | | |
|
|
3309
|
+
|
|
3310
|
+
**`RadioGroup`**: Radio choices from a list of labels, in a row (12px apart) or a column (`vertical`). One Tab stop; a click, or the arrow keys while the group has the focus (they wrap around), pick one: `current-index` changes and `selected` reports it, as std-widgets' RadioGroup.
|
|
3311
|
+
|
|
3312
|
+
| Member | Kind | Default | Note |
|
|
3313
|
+
| --- | --- | --- | --- |
|
|
3314
|
+
| `model` | in `[string]` | | |
|
|
3315
|
+
| `current-index` | in-out `int` | `-1` | |
|
|
3316
|
+
| `vertical` | in `bool` | | |
|
|
3317
|
+
| `enabled` | in `bool` | `true` | |
|
|
3318
|
+
| `spacing` | in `length` | `root.vertical ? 0px : 12px` | |
|
|
3319
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3320
|
+
| `selected` | callback `(int)` | | |
|
|
3321
|
+
| `select` | function `(index: int)` | | |
|
|
3322
|
+
|
|
3323
|
+
**`Tab`**: One page tab: at least 74px, 12px label, dimmed unless `selected`; the selected tab has a 28px gradient stroke under its label, the others a rounded plate on hover. `emphasis` draws an unselected label in the accent colour (something waits there). Tabs lays them out; use Tab alone only for a strip with other content.
|
|
3324
|
+
|
|
3325
|
+
| Member | Kind | Default | Note |
|
|
3326
|
+
| --- | --- | --- | --- |
|
|
3327
|
+
| `text` | in `string` | | |
|
|
3328
|
+
| `selected` | in `bool` | | |
|
|
3329
|
+
| `emphasis` | in `bool` | | |
|
|
3330
|
+
| `focused` | in `bool` | | |
|
|
3331
|
+
| `clicked` | callback `()` | | |
|
|
3332
|
+
|
|
3333
|
+
**`Tabs`**: A 31px tab strip on `bg-bar` (the picture, audio and properties windows): one Tab per label, left-aligned. A click, or Left/Right/Home/End while the strip has the focus, picks a tab: `current-index` changes and `selected` reports it. The pages are the plugin's (show the one for current-index). Its min-width is the tabs' width, for a window that must fit them.
|
|
3334
|
+
|
|
3335
|
+
| Member | Kind | Default | Note |
|
|
3336
|
+
| --- | --- | --- | --- |
|
|
3337
|
+
| `model` | in `[string]` | | |
|
|
3338
|
+
| `current-index` | in-out `int` | `0` | |
|
|
3339
|
+
| `emphasis` | in `[bool]` | | Per tab (same order as `model`, may be shorter): Tab's `emphasis`, the accent label of a tab where something waits ("Updates (2)"). |
|
|
3340
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3341
|
+
| `selected` | callback `(int)` | | |
|
|
3342
|
+
| `select` | function `(index: int)` | | |
|
|
3343
|
+
|
|
3344
|
+
**`Choice`**: Compact 21px drop-down: the current label (11px, elided) and a small V; a click opens the list below it (Slint keeps the popup inside the window, moving it up when there is no room below). The list is a ListView (rows are created only while visible, so long lists such as output devices or plugin ids stay cheap) of at most `max-visible-rows` 20px rows; it is at least `popup-min-width` wide. Slint moves the popup into the window but does not shorten it: in a window shorter than the list, set `window-height: root.height` (the window's height) and the list is cut to fit, scrolling to reach every row. A Choice in a mounted panel or settings page cannot see its window's height; keep `max-visible-rows` small there. Keyboard: closed and focused (Tab or `focus()`), Up/Down/Home/End change the choice, Space, Enter, F4 or Alt+Down open the list; open, the arrows, Page Up/Down and Home/End move the lit row, Enter or Space picks it, Escape or a click outside closes the list without a change. A pick sets `current-index` and `selected` reports it (only a change), as std-widgets' ComboBox; the pick replaces a one-way binding, so bind `current-index` two-way or set it again in a `changed` handler. The labels are what the list shows: give translated text (@tr in the .slint file, or text values `plugin.i18n.text` from the plugin).
|
|
3345
|
+
|
|
3346
|
+
| Member | Kind | Default | Note |
|
|
3347
|
+
| --- | --- | --- | --- |
|
|
3348
|
+
| `model` | in `[string]` | | |
|
|
3349
|
+
| `current-index` | in-out `int` | `0` | |
|
|
3350
|
+
| `enabled` | in `bool` | `true` | |
|
|
3351
|
+
| `max-visible-rows` | in `int` | `10` | |
|
|
3352
|
+
| `popup-min-width` | in `length` | `0px` | |
|
|
3353
|
+
| `window-height` | in `length` | `0px` | The window's height, when known: the list is never taller. |
|
|
3354
|
+
| `has-focus` | out `bool` | `keys.has-focus` | |
|
|
3355
|
+
| `open` | out `bool` | `popup.is-open` | |
|
|
3356
|
+
| `current-text` | out `string` | `root.current-index >= 0 && root.current-index < root.model.length ? root.model[root.current-index] : ""` | |
|
|
3357
|
+
| `selected` | callback `(int)` | | |
|
|
3358
|
+
| `select` | function `(index: int)` | | |
|
|
3359
|
+
| `show-list` | function `()` | | |
|
|
3360
|
+
| `close-list` | function `()` | | |
|
|
3361
|
+
|
|
3270
3362
|
A component's public functions are called from your own Slint code, through an element id: `row.focus-input()` puts the keyboard focus into `UrlInputRow`'s input (call it when the window opens, for example from `init`), `row.select-all()` selects its text.
|
|
3271
3363
|
|
|
3272
3364
|
#### Markdown
|
|
@@ -3282,7 +3374,7 @@ A component's public functions are called from your own Slint code, through an e
|
|
|
3282
3374
|
| `link-clicked` | callback `(string)` | | |
|
|
3283
3375
|
| `copy-code` | callback `(string)` | | |
|
|
3284
3376
|
|
|
3285
|
-
**`MarkdownView`**: A scrolling Markdown document.
|
|
3377
|
+
**`MarkdownView`**: A scrolling Markdown document: a `ListView` of its blocks, so only the blocks in view exist (a long document no longer builds every block). It draws as `MarkdownBlocks` in a column with `inset` padding would: `inset` above the first block, below the last and at both sides, 8px between.
|
|
3286
3378
|
|
|
3287
3379
|
| Member | Kind | Default | Note |
|
|
3288
3380
|
| --- | --- | --- | --- |
|
|
@@ -3654,6 +3746,8 @@ It rejects when nothing is bound to the key or the command it would run is busy.
|
|
|
3654
3746
|
| TestHost.app | What `plugin.app` reports. |
|
|
3655
3747
|
| TestHost.rendering | The player's rendering choice behind `plugin.rendering` (player 0.5.13). |
|
|
3656
3748
|
| TestHost.upscaling | The GPU's video upscaler behind `plugin.video.upscaling` (player 0.5.16), answered as the player would: - `state()` and `set(true)` set `checked` (the player's one GPU check; a plugin that should not wake the GPU while it starts can assert it is still `false`) and report `available` from `reason` (`null`: it works), this plugin's `enabled`, and `requested` with `factor` while enabled, available and `factor` is above 1. |
|
|
3749
|
+
| TestHost.upscalingDecoding() | Make the upscaler's reason `decoding` with `cause`, with the player's message (player 0.5.25), and set `upscaling.hwdecCurrent` to match (`d3d11va-copy`, `no`, `nvdec`). |
|
|
3750
|
+
| TestHost.upscalingChanged() | Send `video.upscaling.changed` with the state `TestHost.upscaling` describes now (without `enabled`), as the player does when it changes (player 0.5.25); a plugin listening with `plugin.video.upscaling.onChange` receives it. |
|
|
3657
3751
|
| TestHost.chapters | What `player.chapters()` lists while media is ready. |
|
|
3658
3752
|
| TestHost.dataDirectory | `plugin.dataDirectory`: a fresh empty temporary directory, removed with the host on `stop()`. |
|
|
3659
3753
|
| TestHost.manifestId | The manifest id of the plugin under test (set by `startPlugin`; `"test.plugin"` by default). |
|
|
@@ -4324,8 +4418,9 @@ plugin.video.shaders(id) (player 0.5.20) returns a VideoShaders handle: a list o
|
|
|
4324
4418
|
plugin.video.upscaling (capability `video.upscaling`, player 0.5.16) turns on the GPU's AI upscaler, NVIDIA RTX Video Super Resolution on Windows. It is not a handle: `set(true)` is the plugin's request, and the player keeps the upscaler last in the filter chain, after every plugin's chain, while any plugin has it on and it can work, scaled to the area the video is shown in. Like render options it acts at display time only.
|
|
4325
4419
|
|
|
4326
4420
|
- **Ask when needed.** The player checks the GPU at the first `state()` or `set(true)` and keeps the answer; the check wakes a sleeping discrete GPU, so ask on the first pick of the item, not while the plugin starts. `set(false)` never checks.
|
|
4327
|
-
- **It needs GPU rendering on the upscaler's GPU, with the video decoded there.** A filter chain that needs the frames in memory switches decoding to copy-back, and the upscaler waits (`decoding`) until it is gone.
|
|
4421
|
+
- **It needs GPU rendering on the upscaler's GPU, with the video decoded there.** A filter chain that needs the frames in memory switches decoding to copy-back, and the upscaler waits (`decoding`) until it is gone. From player 0.5.25 the reason's `cause` says which decoding stands in the way (`copy-back`, `hwdec-off`, `software`, `other-hwdec`), and with `copy-back` its `plugins` say whose filters, so a window can ask the user to turn them off there.
|
|
4328
4422
|
- **No sign that it runs.** `requested` says the player asked the driver; on Windows the user must also turn on Super Resolution in the NVIDIA app, which no program can read.
|
|
4423
|
+
- **Watch it while it runs.** From player 0.5.25 the state also has the video's and the area's size, the rendering GPU, the decoder and `inChain`, and onChange sends it as it changes while some plugin has upscaling on. A listener keeps the plugin running, so listen only while a window shows it.
|
|
4329
4424
|
|
|
4330
4425
|
## Explanation: Windows, panels, settings pages, dialogs and the OSD
|
|
4331
4426
|
|
|
@@ -4541,6 +4636,13 @@ Update passes run after every handler the SDK calls, after player and other stat
|
|
|
4541
4636
|
|
|
4542
4637
|
The rule to remember: **state you change after an `await` inside a handler appears only when the handler finishes**, unless something else triggers a pass. Call `plugin.update()` right after changing it (see Keep a slider responsive while dragging).
|
|
4543
4638
|
|
|
4639
|
+
##### Arrays: only the changed rows are sent
|
|
4640
|
+
|
|
4641
|
+
From player 0.6.0 and SDK 1.5.44, a changed array (a list's rows) is sent as the rows that changed: rows set, inserted, removed or moved, matched by a `key`, `id`, `rowId` or `row-id` field when every row has a unique one, else by position. The player writes those rows into the list Slint already shows, so selecting one row of a long list costs one row, not the whole list. Nothing changes in your code, and the TestHost's `props` still hold the whole array. Two consequences:
|
|
4642
|
+
|
|
4643
|
+
- **Your Slint code must not write into the rows of an array property** (`rows[i].selected = true` in a handler). A whole write used to overwrite such a change on the next update; a row patch touches only the rows whose value changed in your plugin, so the edited row keeps the Slint-side value until your value for that row changes. Send the event to the plugin with a callback and change the row there.
|
|
4644
|
+
- **`set()` with an unchanged array sends the whole array**, as `set()` always sends: use it to pull a list back to your rows. A bound array whose rows did not change sends nothing.
|
|
4645
|
+
|
|
4544
4646
|
##### Two-way controls
|
|
4545
4647
|
|
|
4546
4648
|
A control whose value both the user and the plugin change (a slider, a switch, a text field, a combo box) needs a **two-way binding to an `in-out` property**:
|
|
@@ -5708,6 +5810,8 @@ Every SDK release's CHANGELOG entry opens with who must change and what happens
|
|
|
5708
5810
|
|
|
5709
5811
|
| Release | Who must change | Guide |
|
|
5710
5812
|
| --- | --- | --- |
|
|
5813
|
+
| [1.5.44](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1544) | **Nobody.** No changes to the TypeScript API, `apiVersion` stays `vestra/1`, no new capability. | — |
|
|
5814
|
+
| [1.5.43](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1543) | **Nobody.** Additions only, `apiVersion` stays `vestra/1`, no new capability. | — |
|
|
5711
5815
|
| [1.5.42](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1542) | **Nobody.** Documentation only: no API changes, `apiVersion` stays `vestra/1`, no player version needed. | — |
|
|
5712
5816
|
| [1.5.41](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1541) | **Nobody.** No API changes, `apiVersion` stays `vestra/1`, no player version needed. | — |
|
|
5713
5817
|
| [1.5.40](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1540) | **Nobody.** Additions only, `apiVersion` stays `vestra/1`. | — |
|
|
@@ -11839,7 +11943,7 @@ in the order the player checks them (the first that holds is reported):
|
|
|
11839
11943
|
| `gpu-unsupported` | The driver does not offer upscaling on this GPU (Windows: not an RTX GPU), or the check failed. |
|
|
11840
11944
|
| `rendering` | The player renders in software, or GPU rendering is still starting; upscaling needs GPU rendering (Rendering). |
|
|
11841
11945
|
| `adapter` | GPU rendering runs on another GPU than the one that can upscale (a laptop with two GPUs; Windows: choose the high-performance GPU for Vestra in Settings ▸ System ▸ Display ▸ Graphics). |
|
|
11842
|
-
| `decoding` | The video is not decoded on the GPU that renders it: copy-back or software decoding, for example while a VideoFilter needs the frames in memory. |
|
|
11946
|
+
| `decoding` | The video is not decoded on the GPU that renders it: copy-back or software decoding, for example while a VideoFilter needs the frames in memory. UpscalingReason.cause says which, and UpscalingReason.plugins whose filters (newer players; see there). |
|
|
11843
11947
|
| `unchecked` | The player has not checked the GPU yet. Only in the answer to `set(false)`, which never starts the check; `state()` and `set(true)` always check first. |
|
|
11844
11948
|
|
|
11845
11949
|
`decoding` and `rendering` while GPU rendering is still starting can pass
|
|
@@ -11883,6 +11987,16 @@ interface UpscalingReason {
|
|
|
11883
11987
|
readonly code: UpscalingReasonCode;
|
|
11884
11988
|
/** The reason as English text for people, for example with the GPU's name and what to change; never branch on it. */
|
|
11885
11989
|
readonly message: string;
|
|
11990
|
+
/**
|
|
11991
|
+
* With code `decoding` only: why the video is not decoded on the GPU that renders it (UpscalingDecodingCause). Absent with every other code, and from players before 0.5.25.
|
|
11992
|
+
* @player 0.5.25; older players send `decoding` without a cause.
|
|
11993
|
+
*/
|
|
11994
|
+
readonly cause?: UpscalingDecodingCause;
|
|
11995
|
+
/**
|
|
11996
|
+
* With cause `copy-back` only: the plugins whose video filters need the frames in memory, which is why the player switched to copy-back decoding, in the order the plugins started (each plugin once). Name them to the user, who can turn the filters off there. Empty when the copy-back decoding is not one the player chose for a filter (the user's own `hwdec` setting). Absent with any other cause, and from players before 0.5.25.
|
|
11997
|
+
* @player 0.5.25
|
|
11998
|
+
*/
|
|
11999
|
+
readonly plugins?: readonly UpscalingPlugin[];
|
|
11886
12000
|
}
|
|
11887
12001
|
```
|
|
11888
12002
|
|
|
@@ -11897,6 +12011,78 @@ const { available, reason } = await plugin.video.upscaling.state();
|
|
|
11897
12011
|
if (!available && reason) console.log(`${reason.code}: ${reason.message}`);
|
|
11898
12012
|
```
|
|
11899
12013
|
|
|
12014
|
+
### UpscalingDecodingCause
|
|
12015
|
+
|
|
12016
|
+
Why the video is not decoded on the GPU that renders it: the
|
|
12017
|
+
UpscalingReason.cause of a `decoding` reason (player 0.5.25). A
|
|
12018
|
+
newer player may add causes: show UpscalingReason.message for the
|
|
12019
|
+
ones you do not handle.
|
|
12020
|
+
|
|
12021
|
+
| Cause | Meaning |
|
|
12022
|
+
| --- | --- |
|
|
12023
|
+
| `copy-back` | Decoded on the GPU and copied back to memory, because a plugin's video filter needs the frames there (UpscalingReason.plugins names it), or because of the user's `hwdec` setting. |
|
|
12024
|
+
| `hwdec-off` | Hardware decoding is turned off (mpv's `hwdec` is `no`). |
|
|
12025
|
+
| `software` | This video is decoded in software: a cover picture, a format the GPU does not decode, or a hardware decoder that failed. It passes with the next video. |
|
|
12026
|
+
| `other-hwdec` | This video is decoded with another hardware decoder than the one the upscaler needs. |
|
|
12027
|
+
|
|
12028
|
+
- @player 0.5.25
|
|
12029
|
+
|
|
12030
|
+
```ts
|
|
12031
|
+
type UpscalingDecodingCause = "copy-back" | "hwdec-off" | "software" | "other-hwdec" | (string & {});
|
|
12032
|
+
```
|
|
12033
|
+
|
|
12034
|
+
Example: Name the plugins whose filters block upscaling
|
|
12035
|
+
|
|
12036
|
+
A plugin's upscaling window explains a `decoding` reason by its cause,
|
|
12037
|
+
and for `copy-back` names the plugins whose filters stand in the way, so
|
|
12038
|
+
the user can turn them off there (a plugin cannot run another plugin's
|
|
12039
|
+
commands).
|
|
12040
|
+
```ts
|
|
12041
|
+
const { reason } = await plugin.video.upscaling.state();
|
|
12042
|
+
if (reason?.code === "decoding" && reason.cause === "copy-back") {
|
|
12043
|
+
const names = (reason.plugins ?? []).map((p) => p.name).join(", ");
|
|
12044
|
+
// Ask the user to reset those filters; the manifest names are not translated.
|
|
12045
|
+
console.log(names ? `Turn off the video filters of ${names}` : reason.message);
|
|
12046
|
+
} else if (reason) console.log(reason.message);
|
|
12047
|
+
```
|
|
12048
|
+
|
|
12049
|
+
### UpscalingPlugin
|
|
12050
|
+
|
|
12051
|
+
A plugin named in an UpscalingReason: its manifest id and its
|
|
12052
|
+
name for people, as its manifest gives it (not translated).
|
|
12053
|
+
|
|
12054
|
+
- @player 0.5.25
|
|
12055
|
+
|
|
12056
|
+
```ts
|
|
12057
|
+
interface UpscalingPlugin {
|
|
12058
|
+
/** The plugin's manifest id, for example `vestra.video`. */
|
|
12059
|
+
readonly id: string;
|
|
12060
|
+
/** The manifest's `name` as is (its id without one), for people. */
|
|
12061
|
+
readonly name: string;
|
|
12062
|
+
}
|
|
12063
|
+
```
|
|
12064
|
+
|
|
12065
|
+
### UpscalingVideoSize
|
|
12066
|
+
|
|
12067
|
+
The video's size as the upscaler gets it (UpscalingState.video):
|
|
12068
|
+
the decoder's frame size, width and height swapped for the file's
|
|
12069
|
+
quarter-turn rotation metadata.
|
|
12070
|
+
|
|
12071
|
+
- @player 0.5.25
|
|
12072
|
+
|
|
12073
|
+
```ts
|
|
12074
|
+
interface UpscalingVideoSize {
|
|
12075
|
+
/** The frames' stored width in pixels. */
|
|
12076
|
+
readonly width: number;
|
|
12077
|
+
/** The frames' stored height in pixels. */
|
|
12078
|
+
readonly height: number;
|
|
12079
|
+
/** The width the video is meant to be shown at (non-square pixels). */
|
|
12080
|
+
readonly displayWidth: number;
|
|
12081
|
+
/** The height the video is meant to be shown at. */
|
|
12082
|
+
readonly displayHeight: number;
|
|
12083
|
+
}
|
|
12084
|
+
```
|
|
12085
|
+
|
|
11900
12086
|
### UpscalingState
|
|
11901
12087
|
|
|
11902
12088
|
What VideoUpscaling.state and VideoUpscaling.set report:
|
|
@@ -11928,6 +12114,36 @@ interface UpscalingState {
|
|
|
11928
12114
|
* The scale factor the upscaler was asked for (the area the video is shown in over the video's size, in hundredths, above 1); `null` while nothing is requested.
|
|
11929
12115
|
*/
|
|
11930
12116
|
readonly factor: number | null;
|
|
12117
|
+
/**
|
|
12118
|
+
* The video's size as the upscaler gets it; `null` while no video decodes. With UpscalingState.area it explains the factor. Absent from players before 0.5.25.
|
|
12119
|
+
* @player 0.5.25
|
|
12120
|
+
*/
|
|
12121
|
+
readonly video?: UpscalingVideoSize | null;
|
|
12122
|
+
/**
|
|
12123
|
+
* The area the video is shown in, in pixels (the upscaler scales the video to fit it); `null` while the player shows no video area. Absent from players before 0.5.25.
|
|
12124
|
+
* @player 0.5.25
|
|
12125
|
+
*/
|
|
12126
|
+
readonly area?: {
|
|
12127
|
+
/** Width in physical pixels. */
|
|
12128
|
+
readonly width: number;
|
|
12129
|
+
/** Height in physical pixels. */
|
|
12130
|
+
readonly height: number;
|
|
12131
|
+
} | null;
|
|
12132
|
+
/**
|
|
12133
|
+
* The name of the GPU that renders, for example `NVIDIA GeForce RTX 4070 Laptop GPU`; `null` in software rendering and where the player does not know it (macOS, Linux). Absent from players before 0.5.25.
|
|
12134
|
+
* @player 0.5.25
|
|
12135
|
+
*/
|
|
12136
|
+
readonly gpu?: string | null;
|
|
12137
|
+
/**
|
|
12138
|
+
* The hardware decoder in use for this video, as mpv names it (`d3d11va`, `d3d11va-copy`; `no` for software decoding); `null` while no video decodes. For people and logs: branch on UpscalingReason.cause. Absent from players before 0.5.25.
|
|
12139
|
+
* @player 0.5.25
|
|
12140
|
+
*/
|
|
12141
|
+
readonly hwdecCurrent?: string | null;
|
|
12142
|
+
/**
|
|
12143
|
+
* The upscaler is in the player's video filter chain now, read from the chain itself (`requested` is what the player asked for; they differ only for a moment, while the chain changes). Still not a sign that the GPU upscales: the driver gives none (see the remarks). Absent from players before 0.5.25.
|
|
12144
|
+
* @player 0.5.25
|
|
12145
|
+
*/
|
|
12146
|
+
readonly inChain?: boolean;
|
|
11931
12147
|
}
|
|
11932
12148
|
```
|
|
11933
12149
|
|
|
@@ -12036,6 +12252,46 @@ interface VideoUpscaling {
|
|
|
12036
12252
|
* ```
|
|
12037
12253
|
*/
|
|
12038
12254
|
set(enabled: boolean): Promise<UpscalingState>;
|
|
12255
|
+
/**
|
|
12256
|
+
* Call `listener` with the new state whenever it changes while some plugin has upscaling on: the factor, whether the upscaler is in the chain, the reason, the video's and the area's size, the decoder. A window that shows how upscaling runs listens instead of polling VideoUpscaling.state.
|
|
12257
|
+
*
|
|
12258
|
+
* The player looks about five times a second while upscaling is on for
|
|
12259
|
+
* any plugin (a window resize sends at most that many), and sends a
|
|
12260
|
+
* change only when something differs; after the last plugin turns it
|
|
12261
|
+
* off one more change says so, then nothing until it is on again: read
|
|
12262
|
+
* VideoUpscaling.state when you need the state then. Nothing is
|
|
12263
|
+
* sent before the player's GPU check ran (the first `state()` or
|
|
12264
|
+
* `set(true)` of any plugin). `enabled` is this plugin's own request, as
|
|
12265
|
+
* in `state()`. The message of a `rendering` reason can be shorter than
|
|
12266
|
+
* the one `state()` gives (it may leave out what to do).
|
|
12267
|
+
*
|
|
12268
|
+
* **A listener keeps the plugin running.** `video.upscaling.changed` is
|
|
12269
|
+
* not an event the player wakes a stopped plugin for: while the plugin
|
|
12270
|
+
* listens, the player does not stop it while idle (lazy activation), even
|
|
12271
|
+
* with upscaling off. Listen only while a window that shows the state is
|
|
12272
|
+
* open, and dispose of the listener when it closes. Players before 0.5.25 do not know the event: the listener
|
|
12273
|
+
* never fires and the SDK logs that the player refused the subscription.
|
|
12274
|
+
* @capability video.upscaling
|
|
12275
|
+
* @param listener - Receives the new state.
|
|
12276
|
+
* @returns Stops listening; also when the scope that added it is disposed.
|
|
12277
|
+
* @throws capability (synchronously) The manifest does not declare `video.upscaling`.
|
|
12278
|
+
* @player 0.5.25
|
|
12279
|
+
* @example Show how upscaling runs while a window is open
|
|
12280
|
+
* The plugin's window shows the factor, the sizes and the GPU while it
|
|
12281
|
+
* is open; the listener goes when the window closes, so the plugin can
|
|
12282
|
+
* be stopped while idle again.
|
|
12283
|
+
* ```ts
|
|
12284
|
+
* // When the window opens:
|
|
12285
|
+
* const watch = plugin.video.upscaling.onChange((state) => {
|
|
12286
|
+
* const video = state.video ? `${state.video.width}×${state.video.height}` : "no video";
|
|
12287
|
+
* const area = state.area ? `${state.area.width}×${state.area.height}` : "—";
|
|
12288
|
+
* console.log(`${video} → ${area}, ×${state.factor ?? 1}, ${state.gpu ?? "no GPU"}`);
|
|
12289
|
+
* });
|
|
12290
|
+
* // When it closes: stop listening (it keeps the plugin running).
|
|
12291
|
+
* await watch.dispose();
|
|
12292
|
+
* ```
|
|
12293
|
+
*/
|
|
12294
|
+
onChange(listener: (state: UpscalingState) => void): Disposable;
|
|
12039
12295
|
}
|
|
12040
12296
|
```
|
|
12041
12297
|
|
|
@@ -16227,7 +16483,50 @@ class TestHost implements Transport {
|
|
|
16227
16483
|
checked: boolean;
|
|
16228
16484
|
/** GPU rendering is still starting: a `rendering` reason then lets `set(true)` keep the request. */
|
|
16229
16485
|
renderingStarting: boolean;
|
|
16486
|
+
/** The video's size the state reports (player 0.5.25); `null`: no video. */
|
|
16487
|
+
video: UpscalingVideoSize | null;
|
|
16488
|
+
/** The video area the state reports (player 0.5.25); `null`: none. */
|
|
16489
|
+
area: {
|
|
16490
|
+
/** Width in physical pixels. */
|
|
16491
|
+
width: number;
|
|
16492
|
+
/** Height in physical pixels. */
|
|
16493
|
+
height: number;
|
|
16494
|
+
} | null;
|
|
16495
|
+
/** The rendering GPU's name the state reports (player 0.5.25). */
|
|
16496
|
+
gpu: string | null;
|
|
16497
|
+
/** mpv's decoder the state reports (player 0.5.25); `null`: no video. */
|
|
16498
|
+
hwdecCurrent: string | null;
|
|
16230
16499
|
};
|
|
16500
|
+
/**
|
|
16501
|
+
* Make the upscaler's reason `decoding` with `cause`, with the player's message (player 0.5.25), and set `upscaling.hwdecCurrent` to match (`d3d11va-copy`, `no`, `nvdec`). `plugins` are named for `copy-back` only, as the player does. Returns the reason; call TestHost.upscalingChanged to tell a listening plugin.
|
|
16502
|
+
* @capability None.
|
|
16503
|
+
* @example Test the window's advice for each cause
|
|
16504
|
+
* The plugin's window explains why upscaling waits. The test fakes the
|
|
16505
|
+
* copy-back cause that Picture Adjustments' filters cause and checks
|
|
16506
|
+
* that the window names the plugin.
|
|
16507
|
+
* ```ts
|
|
16508
|
+
* host.upscalingDecoding("copy-back", [{ id: "vestra.video", name: "Picture Adjustments" }]);
|
|
16509
|
+
* host.upscalingChanged();
|
|
16510
|
+
* await host.settle();
|
|
16511
|
+
* assert.match(String(host.window("upscale").props.blocker), /Picture Adjustments/);
|
|
16512
|
+
* ```
|
|
16513
|
+
*/
|
|
16514
|
+
upscalingDecoding(cause: UpscalingDecodingCause, plugins?: readonly UpscalingPlugin[]): UpscalingReason;
|
|
16515
|
+
/**
|
|
16516
|
+
* Send `video.upscaling.changed` with the state TestHost.upscaling describes now (without `enabled`), as the player does when it changes (player 0.5.25); a plugin listening with `plugin.video.upscaling.onChange` receives it. Nothing reaches a plugin that does not listen.
|
|
16517
|
+
* @capability None.
|
|
16518
|
+
* @example Show the new factor in the plugin's window
|
|
16519
|
+
* The window resized: the test changes the area and the factor, sends
|
|
16520
|
+
* the change, and checks that the plugin's window shows the new factor.
|
|
16521
|
+
* ```ts
|
|
16522
|
+
* host.upscaling.area = { width: 2560, height: 1440 };
|
|
16523
|
+
* host.upscaling.factor = 1.33; // 1080p in a 1440p area
|
|
16524
|
+
* host.upscalingChanged(); // as the player after the resize settled
|
|
16525
|
+
* await host.settle();
|
|
16526
|
+
* assert.equal(host.window("upscale").props.factor, "×1.33");
|
|
16527
|
+
* ```
|
|
16528
|
+
*/
|
|
16529
|
+
upscalingChanged(): void;
|
|
16231
16530
|
/** What `player.chapters()` lists while media is ready. */
|
|
16232
16531
|
chapters: {
|
|
16233
16532
|
/** The chapter title, `null` without one. */
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vestra Player SDK
|
|
2
2
|
|
|
3
|
-
> @vestra-player/sdk 1.5.
|
|
3
|
+
> @vestra-player/sdk 1.5.44: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process (the player's built-in plugins share one). Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n>: a line of llms-full.txt (next to this file), which has every page, declaration and example.
|
|
4
4
|
|
|
5
5
|
**Rules**
|
|
6
6
|
|
|
@@ -33,7 +33,7 @@ my-plugin/
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits: L2216–
|
|
36
|
+
Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits: L2216–2289.
|
|
37
37
|
|
|
38
38
|
**main.ts**
|
|
39
39
|
|
|
@@ -77,10 +77,10 @@ export default async function activate(plugin: Plugin) {
|
|
|
77
77
|
- `ui.theme`: Appearance.set/setLanguage
|
|
78
78
|
- `secrets`: Secrets.get/set/delete
|
|
79
79
|
- `rendering.manage`: Rendering.setPreference/restartPlayer
|
|
80
|
-
- `video.upscaling`: VideoUpscaling.state/set
|
|
81
|
-
- No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme, packages.list and rendering.state. Details:
|
|
80
|
+
- `video.upscaling`: VideoUpscaling.state/set/onChange
|
|
81
|
+
- No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme, packages.list and rendering.state. Details: L2290–2350.
|
|
82
82
|
|
|
83
|
-
**Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`, `host` an in-memory player (TestHost): act (`host.command(id).run()`, `host.press(key)`), `await host.settle()`, assert on `host.osd`, `host.window(id).props`… Guide: L1861–2019; TestHost:
|
|
83
|
+
**Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`, `host` an in-memory player (TestHost): act (`host.command(id).run()`, `host.press(key)`), `await host.settle()`, assert on `host.osd`, `host.window(id).props`… Guide: L1861–2019; TestHost: L15463.
|
|
84
84
|
|
|
85
85
|
**Command line**
|
|
86
86
|
|
|
@@ -108,40 +108,40 @@ export default async function activate(plugin: Plugin) {
|
|
|
108
108
|
- `limit`: A player-wide limit was reached, a property value is over the limits of PropValue
|
|
109
109
|
- `reserved`: The id is `vestra` or starts with `vestra.`
|
|
110
110
|
- `versionMismatch`: The package needs a newer player
|
|
111
|
-
- Others: PluginError, llms-full.txt
|
|
111
|
+
- Others: PluginError, llms-full.txt L5945.
|
|
112
112
|
|
|
113
113
|
## Reference
|
|
114
114
|
|
|
115
115
|
- [llms-full.txt](https://vestrasys.github.io/vestra-player-sdk/llms-full.txt), also node_modules/@vestra-player/sdk/llms-full.txt; pages: [site](https://vestrasys.github.io/vestra-player-sdk/).
|
|
116
116
|
- Tutorials: Getting started L9–134; Add a window L135–213; Add settings and translations L214–367; Test, check, pack and publish L368–424; Write your own blur kernel L425–685
|
|
117
117
|
- How-to: Control playback L686–854; Store a plugin's data L855–961; Load subtitles from a plugin L962–1074; Keep a slider responsive while dragging L1075–1262; Remember a window's state between runs L1263–1414; Offer a GPU rendering switch L1415–1540; Keep a command's state without keeping the plugin running L1541–1697; Translate a plugin L1698–1860; Test a plugin L1861–2019; Publish a plugin L2020–2121; Troubleshooting L2122–2215
|
|
118
|
-
- Reference: Manifest L2216–
|
|
119
|
-
- Explanation: How a plugin runs
|
|
120
|
-
- Guidelines: Plugin rules
|
|
121
|
-
- Upgrading: Migrate a plugin to English source text
|
|
122
|
-
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html):
|
|
123
|
-
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html):
|
|
118
|
+
- Reference: Manifest L2216–2289; Capabilities L2290–2350; Limits L2351–2496; Commands, menus and bindings L2497–3022; Platforms L3023–3053; Theme L3054–3403; Command line L3404–3461; Error codes L3462–3509; Translations L3510–3602; Test host L3603–3789
|
|
119
|
+
- Explanation: How a plugin runs L3790–3988; Commands, menus and key bindings L3989–4143; Playback state, controls and sources L4144–4303; Video and audio processing L4304–4424; Windows, panels, settings pages, dialogs and the OSD L4425–4687; GPU and software rendering L4688–4704; The player's Rust core L4705–5046; How the player runs and talks to plugins L5047–5386
|
|
120
|
+
- Guidelines: Plugin rules L5387–5398; Interface guidelines L5399–5515; Menu guidelines L5516–5574
|
|
121
|
+
- Upgrading: Migrate a plugin to English source text L5575–5724; Migrating off package rollback (1.5.16, removed in 1.5.17) L5725–5802; Who must change, release by release L5803–5867
|
|
122
|
+
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L5868–15136
|
|
123
|
+
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L15137–18004
|
|
124
124
|
|
|
125
125
|
## API
|
|
126
126
|
|
|
127
127
|
Where each `plugin.<module>` is declared:
|
|
128
128
|
|
|
129
|
-
- Plugin
|
|
130
|
-
- Commands
|
|
129
|
+
- Plugin L6284, Scope L6020, PluginError L5945, Disposable L5883
|
|
130
|
+
- Commands L6871; When L6469; Windows L7808; Panels L9254; Dialogs L9924; Player L10665; video: VideoFilter L11545, VideoView L11645, RenderOptions L11732, VideoShaders L11810, VideoUpscaling L12168; audio: AudioFilter L11590; Subtitles L12404; media: MediaCapture L12808; Resolvers L13038; Packages L13440; Osd L11410; Host L11252; Clipboard L13769; Power L13856; Tools L13915; Storage L14067; System L14266; Secrets L14346; window: MainWindow L9571; App L9751; Rendering L14609; Bindings L13668; Ui L7160; Timeline L9483; Drops L8501; Appearance L8582; I18n L8815; Settings L9098.
|
|
131
131
|
|
|
132
132
|
## Examples
|
|
133
133
|
|
|
134
134
|
Tested plugins in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples):
|
|
135
135
|
|
|
136
|
-
- hello: The README Quick start: one menu command and an on-screen message.
|
|
137
|
-
- blur: A tool window with one slider that blurs the playing video.
|
|
138
|
-
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone.
|
|
139
|
-
- bookmark: A tool window bound to the playback state: remember the position and jump back to it.
|
|
140
|
-
- panel-menu: A sidebar panel with its own right-click menu.
|
|
141
|
-
- menus: Radio menu items, separator groups and a menu item whose title changes.
|
|
142
|
-
- theme-widgets: A tool window drawn with the player's theme and widgets.
|
|
143
|
-
- markdown: A window that shows a Markdown document.
|
|
144
|
-
- i18n: A plugin in the player's language: English source text, a Chinese translation.
|
|
136
|
+
- hello: The README Quick start: one menu command and an on-screen message. L18439–18534
|
|
137
|
+
- blur: A tool window with one slider that blurs the playing video. L18535–18802
|
|
138
|
+
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L18803–19141
|
|
139
|
+
- bookmark: A tool window bound to the playback state: remember the position and jump back to it. L19142–19334
|
|
140
|
+
- panel-menu: A sidebar panel with its own right-click menu. L19335–19549
|
|
141
|
+
- menus: Radio menu items, separator groups and a menu item whose title changes. L19550–19715
|
|
142
|
+
- theme-widgets: A tool window drawn with the player's theme and widgets. L19716–19885
|
|
143
|
+
- markdown: A window that shows a Markdown document. L19886–20088
|
|
144
|
+
- i18n: A plugin in the player's language: English source text, a Chinese translation. L20089–20262
|
|
145
145
|
|
|
146
146
|
## Optional
|
|
147
147
|
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// row (a chat message: a `[MarkdownBlock]` field of the row's struct).
|
|
10
10
|
// `code-copy: true` adds a Copy button to code blocks that reports
|
|
11
11
|
// `copy-code(code)`; the plugin copies it (`plugin.clipboard`).
|
|
12
|
-
import {
|
|
12
|
+
import { ListView } from "std-widgets.slint";
|
|
13
13
|
import { Theme } from "theme.slint";
|
|
14
14
|
import { Icon } from "icons.slint";
|
|
15
15
|
|
|
@@ -234,7 +234,10 @@ export component MarkdownBlocks inherits Rectangle {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
// A scrolling Markdown document
|
|
237
|
+
// A scrolling Markdown document: a `ListView` of its blocks, so only the
|
|
238
|
+
// blocks in view exist (a long document no longer builds every block). It
|
|
239
|
+
// draws as `MarkdownBlocks` in a column with `inset` padding would: `inset`
|
|
240
|
+
// above the first block, below the last and at both sides, 8px between.
|
|
238
241
|
export component MarkdownView inherits Rectangle {
|
|
239
242
|
in property <[MarkdownBlock]> blocks;
|
|
240
243
|
// Space around the document.
|
|
@@ -243,17 +246,20 @@ export component MarkdownView inherits Rectangle {
|
|
|
243
246
|
in property <bool> code-copy: false;
|
|
244
247
|
callback link-clicked(string);
|
|
245
248
|
callback copy-code(string);
|
|
246
|
-
|
|
249
|
+
list := ListView {
|
|
247
250
|
width: 100%;
|
|
248
251
|
height: 100%;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
for block[i] in root.blocks: Rectangle {
|
|
253
|
+
property <length> top: i == 0 ? root.inset : 0px;
|
|
254
|
+
height: self.top + view.preferred-height
|
|
255
|
+
+ (i == root.blocks.length - 1 ? root.inset : 8px);
|
|
256
|
+
view := MarkdownBlockView {
|
|
257
|
+
x: root.inset;
|
|
258
|
+
y: parent.top;
|
|
259
|
+
width: max(0px, parent.width - 2 * root.inset);
|
|
260
|
+
height: self.preferred-height;
|
|
261
|
+
block: block;
|
|
262
|
+
available: self.width;
|
|
257
263
|
code-copy: root.code-copy;
|
|
258
264
|
link-clicked(url) => { root.link-clicked(url); }
|
|
259
265
|
copy-code(code) => { root.copy-code(code); }
|