@qxuken/kui 0.1.0-alpha.39 → 0.1.0-alpha.40
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 +89 -0
- package/howto.md +29 -0
- package/index.d.ts +22 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/kui_node.node +0 -0
- package/prebuilds/darwin-x64/kui_node.node +0 -0
- package/prebuilds/linux-arm64/kui_node.node +0 -0
- package/prebuilds/linux-x64/kui_node.node +0 -0
- package/prebuilds/win32-x64/kui_node.node +0 -0
- package/props.md +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,95 @@ listed under both (backlog F61, from the alpha.12 field reports: the list
|
|
|
21
21
|
is what the release knows it broke, and a fix it did not think of as one
|
|
22
22
|
was the first bare bump to break an app in five releases).
|
|
23
23
|
|
|
24
|
+
## 0.1.0-alpha.40 (2026-10-07)
|
|
25
|
+
|
|
26
|
+
**What breaks.**
|
|
27
|
+
|
|
28
|
+
- Rust: `InputEvent` gains `Open(Vec<String>)`, so an exhaustive `match`
|
|
29
|
+
on it needs an arm.
|
|
30
|
+
|
|
31
|
+
C stays at ABI 25 (`kui_input_open` is a new function) and the Node wire
|
|
32
|
+
at v21.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **The documents the OS asks the app to open reach it** (backlog F124,
|
|
37
|
+
from kawoosh). The Finder's Open With, a file dropped on the Dock icon,
|
|
38
|
+
`open -a YourApp file`, a double-click on a document type the bundle's
|
|
39
|
+
`Info.plist` declares (`CFBundleDocumentTypes`): AppKit hands every one
|
|
40
|
+
to the application delegate's `application:openURLs:`, which winit's
|
|
41
|
+
delegate does not answer, so they went nowhere. The macOS runner adds
|
|
42
|
+
it to winit's delegate class once per process, and the app hears
|
|
43
|
+
`{kind:"open", paths}` on the root — file-system paths, one event per
|
|
44
|
+
request, asked for or not. A launch's documents, which AppKit sends
|
|
45
|
+
before any window exists and leaves out of the arguments, wait for the
|
|
46
|
+
main window and arrive on its first turn; while the app runs they
|
|
47
|
+
arrive at once. A delegate class that already answers documents (an
|
|
48
|
+
app's own override) is left alone. Headless and for a host with its
|
|
49
|
+
own window: `InputEvent::Open(paths)`, C `kui_input_open`, Node
|
|
50
|
+
`ctx.openDocuments(paths)` (`OpenMsg` in `CoreMsg`). Windows and Linux
|
|
51
|
+
hand documents over in the process's arguments, and nothing sends it
|
|
52
|
+
there.
|
|
53
|
+
|
|
54
|
+
**What you can delete.** An `NSApplicationDelegate` method added or
|
|
55
|
+
swizzled by hand onto winit's delegate to hear a Finder open, and the
|
|
56
|
+
queue that held a launch's paths until a window existed to show them.
|
|
57
|
+
|
|
58
|
+
### Native verification
|
|
59
|
+
|
|
60
|
+
The by-hand round alpha.6 introduced (backlog R4), on 2026-10-07 — the
|
|
61
|
+
one round after the alpha.39 tag: the documents the OS asks the app to
|
|
62
|
+
open (F124) — with a regression pass over it first: the diff read whole,
|
|
63
|
+
each claim probed before anything changed. The probes ran in a window,
|
|
64
|
+
since the half that can break is AppKit's: a scratch app on the
|
|
65
|
+
release tree's `kui-native`, bare and in an ad-hoc-signed bundle
|
|
66
|
+
declaring `public.data` (`LSHandlerRank` `Alternate`). `open -a` at
|
|
67
|
+
launch handed over both documents in one `open` event, `argv` the
|
|
68
|
+
executable alone; two `open -a` calls into the running instance arrived
|
|
69
|
+
as two events, in order, each with its request's paths in their order.
|
|
70
|
+
And the one thing an added `application:openURLs:` could have started
|
|
71
|
+
that F124's own checks did not look at: AppKit taking the process's
|
|
72
|
+
arguments for documents once the delegate answers them
|
|
73
|
+
(`NSTreatUnknownArgumentsAsOpen`). A binary run from a terminal with a
|
|
74
|
+
path, a subcommand, a missing file and a flag, bare and as the bundle's
|
|
75
|
+
own executable, even with `-NSTreatUnknownArgumentsAsOpen YES`, heard
|
|
76
|
+
no `open` — so an app that reads its arguments and hears `open` too
|
|
77
|
+
does not open a file twice. The non-macOS side (`pump_open_documents`
|
|
78
|
+
without `macos_open`) is clean under clippy for `x86_64-pc-windows-msvc`.
|
|
79
|
+
Nothing was filed. This round ran on the Mac alone; Windows and Linux
|
|
80
|
+
did not run it for this tag.
|
|
81
|
+
|
|
82
|
+
**macOS 27.0.1 on an M3 Pro MacBook Pro, rustc 1.99.0 (the toolchain
|
|
83
|
+
CI runs), Node 25.6.0, nu 0.116.1**, on the release commit's tree.
|
|
84
|
+
`cargo fmt --all --check` and `cargo clippy --workspace --all-targets
|
|
85
|
+
--features kui-core/conformance -- -D warnings` are clean, and so is
|
|
86
|
+
`cargo audit --deny warnings`. `cargo test --workspace --features
|
|
87
|
+
kui-core/conformance`: **1822 tests over 137 suites, 0 failed** (4
|
|
88
|
+
ignored). The C round, `cbuild --run`, passes its five checks; the
|
|
89
|
+
corpus passes its **57 scenes** in four adapters, the `drop` scene now
|
|
90
|
+
ending with two documents opened; the ABI is **25**. Node's `node --test
|
|
91
|
+
test.mjs` under `KUI_CONFORMANCE_REQUIRED=1`: **211 of 211**. `npm run
|
|
92
|
+
gen` leaves no diff, the examples typecheck and their lockfile installs,
|
|
93
|
+
the headless round passes all **35 drives**, the book builds and
|
|
94
|
+
`scripts/book-examples.nu --check` passes.
|
|
95
|
+
|
|
96
|
+
**The windowed round**, `smoke -- --node`, three times over — twice
|
|
97
|
+
before the version bump, once on the release tree: **51 Rust examples
|
|
98
|
+
and the eleven Node examples, each on both bases, 120 frames each, every
|
|
99
|
+
one exiting 0** — 124 windows, eight at a time, in 32.3, 32.5 and
|
|
100
|
+
38.8 s — and `counter`, `host`, `c_panel` and `lua_panel` by hand under
|
|
101
|
+
`KUI_SMOKE_FRAMES=120`, each exiting 0 with nothing on stderr: **128
|
|
102
|
+
windows over five hosts.** The AX audit: **106/106**, the audited window
|
|
103
|
+
raised to the front by its pid first, and no warning on the fixture's
|
|
104
|
+
stderr. F124 itself was seen in kawoosh's window before the merge, built
|
|
105
|
+
against the branch by path.
|
|
106
|
+
|
|
107
|
+
**The bench guard** against the alpha.39 tag: **green**, none of the 8
|
|
108
|
+
guarded rows more than 10% slower — every one between −3.2% and +3.5%
|
|
109
|
+
(the worst guarded run-to-run spread 1.3%) — and no row of the run more
|
|
110
|
+
than 4.7% slower (`frame_1k_paths_animating`, a frame F124 does not
|
|
111
|
+
reach). `README.md`'s table is kept as it was.
|
|
112
|
+
|
|
24
113
|
## 0.1.0-alpha.39 (2026-10-06)
|
|
25
114
|
|
|
26
115
|
**What breaks.**
|
package/howto.md
CHANGED
|
@@ -750,6 +750,35 @@ drive, and `dropTarget()` is what a driver answers the OS with.
|
|
|
750
750
|
[`drop` payload](props.md#events) ·
|
|
751
751
|
[`examples/rust/features/drop.rs`](../examples/rust/features/drop.rs)
|
|
752
752
|
|
|
753
|
+
### How do I hear the documents the OS opens: Finder's Open With, the Dock icon?
|
|
754
|
+
|
|
755
|
+
Handle `{kind:"open", paths}` in `update` (Rust `on_event`, C's event
|
|
756
|
+
callback): it arrives on the root, asked for or not, with the file-system
|
|
757
|
+
paths of the documents the Finder's Open With, a file dropped on the Dock
|
|
758
|
+
icon, `open -a YourApp file.rs` or a double-click handed the app. The
|
|
759
|
+
macOS runner hears them from the application delegate's
|
|
760
|
+
`application:openURLs:` — at launch, where AppKit sends them before the
|
|
761
|
+
window exists and puts none in the arguments, the paths wait for the main
|
|
762
|
+
window and arrive on its first turn; while the app runs, at once. One
|
|
763
|
+
`open` per request, several paths when the user opened several.
|
|
764
|
+
|
|
765
|
+
The app's bundle has to say it opens documents, or the Finder will not
|
|
766
|
+
offer it: an `Info.plist` with `CFBundleDocumentTypes`, one entry per
|
|
767
|
+
kind — `LSItemContentTypes` the UTIs (`public.plain-text`,
|
|
768
|
+
`public.source-code`, `public.data` for anything), `CFBundleTypeRole`
|
|
769
|
+
`Editor` or `Viewer`, and `LSHandlerRank` `Alternate` to be listed in Open
|
|
770
|
+
With without taking the type's double-click from its owner (`Default` or
|
|
771
|
+
`Owner` to take it). Re-register a bundle you rebuilt in place with
|
|
772
|
+
`lsregister -f YourApp.app` if the Finder still has the old list.
|
|
773
|
+
|
|
774
|
+
On Windows and Linux the documents are in the process's arguments (a
|
|
775
|
+
file association, a `.desktop` file's `%F`), and nothing sends `open`.
|
|
776
|
+
Headless, `ctx.openDocuments(paths)` (C `kui_input_open`, Rust
|
|
777
|
+
`InputEvent::Open`) is the drive.
|
|
778
|
+
|
|
779
|
+
[`open` payload](props.md#events) ·
|
|
780
|
+
[alpha.40 `### Added`](CHANGELOG.md#010-alpha40-2026-10-07)
|
|
781
|
+
|
|
753
782
|
### How do I have global shortcuts and a Tab ring at once?
|
|
754
783
|
|
|
755
784
|
Put the keymap on an `onKey` sink that encloses the controls: a focused
|
package/index.d.ts
CHANGED
|
@@ -368,6 +368,18 @@ export type SystemMsg = {
|
|
|
368
368
|
* A font the app loads itself raises none. */
|
|
369
369
|
export type FontsMsg = { kind: 'fonts' };
|
|
370
370
|
|
|
371
|
+
/** The OS asked the app to open these documents (backlog F124): the
|
|
372
|
+
* Finder's Open With, a file dropped on the Dock icon, `open -a`, a
|
|
373
|
+
* double-click on a document type the app's Info.plist declares
|
|
374
|
+
* (`CFBundleDocumentTypes`). Delivered on the root whether or not the app
|
|
375
|
+
* asked — at launch, once the window is open, and while it runs. `paths`
|
|
376
|
+
* are file-system paths. macOS only: Windows and Linux hand documents over
|
|
377
|
+
* in `process.argv`. `ctx.openDocuments(paths)` is the headless drive. */
|
|
378
|
+
export type OpenMsg = {
|
|
379
|
+
kind: 'open';
|
|
380
|
+
paths: string[];
|
|
381
|
+
};
|
|
382
|
+
|
|
371
383
|
/** A declared window opened, or closed — because nothing declares it any
|
|
372
384
|
* more, or because the user closed it. A window the user closed stays
|
|
373
385
|
* closed while it is still declared (a declaration reopens a window only
|
|
@@ -496,7 +508,8 @@ export type CoreMsg =
|
|
|
496
508
|
| SoundMsg
|
|
497
509
|
| AccessMsg
|
|
498
510
|
| ChangeMsg
|
|
499
|
-
| FilesMsg
|
|
511
|
+
| FilesMsg
|
|
512
|
+
| OpenMsg;
|
|
500
513
|
|
|
501
514
|
/** A stock slider's proposal (docs/adr/0034-stock-controls-over-the-roles.md):
|
|
502
515
|
* the core turned a press, a drag, an arrow, a Page key, Home / End or an
|
|
@@ -2161,6 +2174,14 @@ export declare class Ctx {
|
|
|
2161
2174
|
* asked it is dropped.
|
|
2162
2175
|
*/
|
|
2163
2176
|
answerFiles(paths: Array<string>): void
|
|
2177
|
+
/**
|
|
2178
|
+
* The OS asked the app to open these documents (backlog F124) — the
|
|
2179
|
+
* Finder's Open With, a file dropped on the Dock icon, `open -a`: the
|
|
2180
|
+
* app hears `{kind:"open", paths}` on the root whether or not it
|
|
2181
|
+
* asked; none is nothing. A `KuiWindow` on macOS hears it from the
|
|
2182
|
+
* runner; this is the headless drive.
|
|
2183
|
+
*/
|
|
2184
|
+
openDocuments(paths: Array<string>): void
|
|
2164
2185
|
/**
|
|
2165
2186
|
* The dragged files released at (`x`, `y`): the zone there hears
|
|
2166
2187
|
* `{kind:"drop", phase:"drop", paths, x, y, tag}` and no `leave`
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/props.md
CHANGED
|
@@ -202,6 +202,7 @@ one field. The payload shapes:
|
|
|
202
202
|
| hover | `{ kind: "hover", phase: "enter" \| "leave", by: "pointer" \| "content", tag }` | The pointer entered or left an `onHover` node — also when a new frame moved it under a still cursor. `by` says which (backlog DX20): `pointer` when the pointer moved or left the window, `content` when it stayed and what is under it changed — a list scrolled by the wheel or the keys, a row that grew, a float that opened. A picker whose selection follows the pointer ignores `content`, or the rows sliding under a still pointer as the keys scroll the list drag the selection with them. |
|
|
203
203
|
| drop | `{ kind: "drop", phase: "enter" \| "move" \| "leave" \| "drop", paths: string[], x, y, tag }` | Files dragged in from the OS over an `onDrop` node (`docs/adr/0031-a-drop-zone-is-a-row-and-the-files-are-an-event.md`): `enter` when they come over the zone, `move` while they move over it (never twice for one point), `leave` when they go to another zone, to no zone or out of the window, `drop` when they land — and no `leave` after a `drop`. `paths` are the OS paths as strings; `x`/`y` the pointer in logical viewport coordinates, absent on `leave`. The zone is the topmost one under the pointer by paint order; a node inside it is its, and a node that is no zone is looked past (an overlay shown on `enter` does not end the hover). Nothing is re-resolved when a frame lands: only the driver's next report moves the files, so a zone the view stops declaring hears its `leave` then. |
|
|
204
204
|
| files | `{ kind: "files", paths: string[], tag }` | A file dialog's answer (backlog C51): what an Open, Save or folder dialog asked for with `requestFiles` / `request_files` / `kui_request_files` picked — the OS paths, as a `drop` carries them — or no paths when the user cancelled. `tag` is the dialog's own. It reaches whoever asked: the host, or the extension whose fill asked; one dialog is out at a time. |
|
|
205
|
+
| open | `{ kind: "open", paths: string[] }` on the root | The OS asked the app to open documents (backlog F124): the Finder's Open With, a file dropped on the Dock icon, `open -a App file`, a double-click on a document of a type the app's `Info.plist` declares under `CFBundleDocumentTypes`. Delivered to the host on the root whether or not anything asked — unlike `files`, which answers an ask. `paths` are file-system paths as strings; a URL of a scheme the app registers is not one, and is not carried (room is left for a `urls` beside `paths`). The macOS runner sends it at launch — held until the main window has opened, since AppKit hands the documents over before it exists and puts none in the arguments — and while the app runs; a host driving its own window sends it as input (`openDocuments`, `kui_input_open`, `InputEvent::Open`). Windows and Linux pass documents in the process's arguments instead, and nothing sends it there. |
|
|
205
206
|
| layout | `{ kind: "layout", x, y, w, h, parent: { x, y, w, h }, scale, tag }` | The rect layout gave an `onLayout` node (logical px, viewport coords, after scrolling and easing): on its first frame and whenever it changes, never on a frame that left it alone. `scale` is physical px per logical px at the node — `w × scale` by `h × scale` is how many pixels to render for it before `updateImage` (the frame's scale today; where a zoom would compose in). |
|
|
206
207
|
| resize | `{ kind: "resize", width, height, scale }` | The viewport changed size or DPI (logical px, delivered to the host on the root): the window less the devtools' dock while the panel is docked (`docs/adr/0024`), so a dock coming, going or being dragged is a resize too. `KuiWindow.size()` queries the same numbers, before the first frame as well (backlog F43). The first frame establishes the viewport rather than reporting it, so a dock present at launch posts none. |
|
|
207
208
|
| window | `{ kind: "window", phase: "opened" \| "closed" \| "focused" \| "blurred", name, id }` | A declared window opened (the diff queued its `Open`) or closed — because nothing declares it any more, or because the user closed it, in which case it stays closed while still declared: stop declaring `name`, then declare it again to reopen. `id` is what its events carry; the event itself is on the root of whichever window's frame noticed. `focused` and `blurred` are this window gaining and losing the keyboard (backlog DX18) — `env.focused` changing, as the driver reports it — so an app that saves on blur or re-reads the clipboard on return hears it once instead of diffing `env.focused` every frame. |
|
|
@@ -631,7 +632,7 @@ binding is a row with its three other cells, or a red test.
|
|
|
631
632
|
| `Core::set_time` | `kui_set_time` | `Ctx.setTime` | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | The clock the tweens read; a window's runner sets it from the display. |
|
|
632
633
|
| `Core::env` | `kui_env_set` and its four siblings, `ENV_FIELDS`' C column | `Ctx.setEnv` | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | The host facts written in (the `env` field); a `KuiWindow`'s runner writes its own. |
|
|
633
634
|
| `Ui::env` | *none: C is the host, so it writes the facts and has no reading (`ENV_FIELDS`)* | `env` | `env`, the view's argument | The facts read back, `ENV_FIELDS` row for row. |
|
|
634
|
-
| `Core::handle_input` | `kui_input_cursor` … `kui_input_access`, one per `InputEvent` | `Ctx.cursor` … `Ctx.access`, one per `InputEvent`; a `KuiWindow` refuses injection | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | Pointer, wheel, key, text, IME, assistive and OS file-drag input; a wheel gesture's latching is `scroll_gesture` (`kui_input_scroll_gesture`, `Ctx.scrollGesture`, backlog F107); `press` / `release` are a click by label (`kui_input_press`, `Ctx.press`); the file drag is `drag_files` / `drop_files` / `drag_cancel` (ADR 0031); a file dialog's answer is `answer_files` (`kui_input_files`, `Ctx.answerFiles`, backlog C51). |
|
|
635
|
+
| `Core::handle_input` | `kui_input_cursor` … `kui_input_access`, one per `InputEvent` | `Ctx.cursor` … `Ctx.access`, one per `InputEvent`; a `KuiWindow` refuses injection | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | Pointer, wheel, key, text, IME, assistive and OS file-drag input; a wheel gesture's latching is `scroll_gesture` (`kui_input_scroll_gesture`, `Ctx.scrollGesture`, backlog F107); `press` / `release` are a click by label (`kui_input_press`, `Ctx.press`); the file drag is `drag_files` / `drop_files` / `drag_cancel` (ADR 0031); a file dialog's answer is `answer_files` (`kui_input_files`, `Ctx.answerFiles`, backlog C51); the documents the OS asked the app to open are `InputEvent::Open` (`kui_input_open`, `Ctx.openDocuments`, backlog F124). |
|
|
635
636
|
| `Core::modifiers` | `kui_input_modifiers` | `Ctx.modifiers` | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | The modifier state, reported on its own when the OS does (backlog AR22). |
|
|
636
637
|
| `Core::release_held_keys` | `kui_release_held_keys` | `Ctx.setEnv({focused: false})` releases, as losing the keyboard does for every driver (ADR 0020) | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | Lets go of every key the focused sink holds. |
|
|
637
638
|
| `Core::set_subpixel_text` | `kui_set_subpixel_text` | *none: a Node host never paints: the renderer behind `KuiWindow` is the runner's, and a headless `Ctx` has none* | *none: a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's* | LCD subpixel coverage for outline glyphs, for a renderer that blends per channel. |
|