@vestra-player/sdk 1.5.43 → 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/llms-full.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @vestra-player/sdk 1.5.43: full reference
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
 
@@ -3242,17 +3242,21 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
3242
3242
  | `checked` | in `bool` | | Icon buttons: the "on" state, drawn in the accent colour. |
3243
3243
  | `clicked` | callback `()` | | |
3244
3244
 
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.
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.
3246
3246
 
3247
3247
  | Member | Kind | Default | Note |
3248
3248
  | --- | --- | --- | --- |
3249
3249
  | `value` | in-out `float` | | |
3250
3250
  | `minimum` | in `float` | `0` | |
3251
3251
  | `maximum` | in `float` | `1` | |
3252
+ | `step` | in `float` | `(root.maximum - root.minimum) / 20` | One key press: a twentieth of the range unless set. |
3252
3253
  | `enabled` | in `bool` | `true` | |
3253
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` | |
3254
3257
  | `changed` | callback `(float)` | | |
3255
3258
  | `released` | callback `(float)` | | |
3259
+ | `set-value` | function `(v: float)` | | Clamped to the range; `changed` only when the value moves. |
3256
3260
 
3257
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.
3258
3262
 
@@ -3268,6 +3272,93 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
3268
3272
  | `focus-input` | function `()` | | |
3269
3273
  | `select-all` | function `()` | | |
3270
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
+
3271
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.
3272
3363
 
3273
3364
  #### Markdown
@@ -3283,7 +3374,7 @@ A component's public functions are called from your own Slint code, through an e
3283
3374
  | `link-clicked` | callback `(string)` | | |
3284
3375
  | `copy-code` | callback `(string)` | | |
3285
3376
 
3286
- **`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.
3287
3378
 
3288
3379
  | Member | Kind | Default | Note |
3289
3380
  | --- | --- | --- | --- |
@@ -4545,6 +4636,13 @@ Update passes run after every handler the SDK calls, after player and other stat
4545
4636
 
4546
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).
4547
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
+
4548
4646
  ##### Two-way controls
4549
4647
 
4550
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**:
@@ -5712,6 +5810,7 @@ Every SDK release's CHANGELOG entry opens with who must change and what happens
5712
5810
 
5713
5811
  | Release | Who must change | Guide |
5714
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. | — |
5715
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. | — |
5716
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. | — |
5717
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. | — |
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.43: 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.
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
 
@@ -80,7 +80,7 @@ export default async function activate(plugin: Plugin) {
80
80
  - `video.upscaling`: VideoUpscaling.state/set/onChange
81
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: L15364.
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 L5846.
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–2289; Capabilities L2290–2350; Limits L2351–2496; Commands, menus and bindings L2497–3022; Platforms L3023–3053; Theme L3054–3312; Command line L3313–3370; Error codes L3371–3418; Translations L3419–3511; Test host L3512–3698
119
- - Explanation: How a plugin runs L3699–3897; Commands, menus and key bindings L3898–4052; Playback state, controls and sources L4053–4212; Video and audio processing L4213–4333; Windows, panels, settings pages, dialogs and the OSD L4334–4589; GPU and software rendering L4590–4606; The player's Rust core L4607–4948; How the player runs and talks to plugins L4949–5288
120
- - Guidelines: Plugin rules L5289–5300; Interface guidelines L5301–5417; Menu guidelines L5418–5476
121
- - Upgrading: Migrate a plugin to English source text L5477–5626; Migrating off package rollback (1.5.16, removed in 1.5.17) L5627–5704; Who must change, release by release L5705–5768
122
- - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L5769–15037
123
- - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L15038–17905
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 L6185, Scope L5921, PluginError L5846, Disposable L5784
130
- - Commands L6772; When L6370; Windows L7709; Panels L9155; Dialogs L9825; Player L10566; video: VideoFilter L11446, VideoView L11546, RenderOptions L11633, VideoShaders L11711, VideoUpscaling L12069; audio: AudioFilter L11491; Subtitles L12305; media: MediaCapture L12709; Resolvers L12939; Packages L13341; Osd L11311; Host L11153; Clipboard L13670; Power L13757; Tools L13816; Storage L13968; System L14167; Secrets L14247; window: MainWindow L9472; App L9652; Rendering L14510; Bindings L13569; Ui L7061; Timeline L9384; Drops L8402; Appearance L8483; I18n L8716; Settings L8999.
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. L18340–18435
137
- - blur: A tool window with one slider that blurs the playing video. L18436–18703
138
- - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L18704–19042
139
- - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L19043–19235
140
- - panel-menu: A sidebar panel with its own right-click menu. L19236–19450
141
- - menus: Radio menu items, separator groups and a menu item whose title changes. L19451–19616
142
- - theme-widgets: A tool window drawn with the player's theme and widgets. L19617–19786
143
- - markdown: A window that shows a Markdown document. L19787–19989
144
- - i18n: A plugin in the player's language: English source text, a Chinese translation. L19990–20163
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.43",
3
+ "version": "1.5.44",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -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 { ScrollView } from "std-widgets.slint";
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
- scroll := ScrollView {
249
+ list := ListView {
247
250
  width: 100%;
248
251
  height: 100%;
249
- content-width: self.visible-width;
250
- content-height: column.preferred-height;
251
- column := VerticalLayout {
252
- width: scroll.visible-width;
253
- padding: root.inset;
254
- alignment: start;
255
- MarkdownBlocks {
256
- blocks: root.blocks;
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); }