@react-x11/components 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +214 -31
  2. package/dist/color-picker/ColorPicker.d.ts.map +1 -1
  3. package/dist/color-picker/ColorPicker.js +43 -33
  4. package/dist/color-picker/ColorPicker.js.map +1 -1
  5. package/dist/internal/scroll.d.ts.map +1 -1
  6. package/dist/internal/scroll.js +32 -15
  7. package/dist/internal/scroll.js.map +1 -1
  8. package/dist/internal/units.d.ts +2 -0
  9. package/dist/internal/units.d.ts.map +1 -0
  10. package/dist/internal/units.js +35 -0
  11. package/dist/internal/units.js.map +1 -0
  12. package/dist/internal/window.d.ts.map +1 -1
  13. package/dist/internal/window.js +4 -2
  14. package/dist/internal/window.js.map +1 -1
  15. package/dist/table/index.d.ts.map +1 -1
  16. package/dist/table/index.js +14 -5
  17. package/dist/table/index.js.map +1 -1
  18. package/dist/terminal/vt/node.d.ts +56 -3
  19. package/dist/terminal/vt/node.d.ts.map +1 -1
  20. package/dist/terminal/vt/node.js +106 -26
  21. package/dist/terminal/vt/node.js.map +1 -1
  22. package/dist/terminal/vt/renderer.d.ts +4 -0
  23. package/dist/terminal/vt/renderer.d.ts.map +1 -1
  24. package/dist/terminal/vt/renderer.js +14 -5
  25. package/dist/terminal/vt/renderer.js.map +1 -1
  26. package/dist/tree/index.d.ts.map +1 -1
  27. package/dist/tree/index.js +6 -4
  28. package/dist/tree/index.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/color-picker/ColorPicker.ts +107 -81
  31. package/src/internal/scroll.ts +34 -15
  32. package/src/internal/units.ts +34 -0
  33. package/src/internal/window.ts +4 -2
  34. package/src/table/index.ts +14 -5
  35. package/src/terminal/vt/node.ts +128 -29
  36. package/src/terminal/vt/renderer.ts +27 -5
  37. package/src/tree/index.ts +6 -4
package/README.md CHANGED
@@ -11,10 +11,10 @@ needs a change to core to exist, and core does not grow to carry it.
11
11
  reference page per component, rendered from [`docs/`](docs/README.md). This
12
12
  README is the tour; that is the detail.
13
13
 
14
- > **Installable now.** react-x11 2.0.0 is on npm, so the peer range this
15
- > package declares resolves and `npm install` just works. The published
16
- > release is `0.1.0`; `master` carries components added since, so use a
17
- > checkout if you want what is not in that release yet.
14
+ > **Installable now.** The published release is `0.7.1`, and the react-x11
15
+ > range it declares resolves off the registry, so `npm install` just works.
16
+ > `master` carries what has landed since, so use a checkout if you want
17
+ > something not in that release yet.
18
18
 
19
19
  ## What is here, and what is in core
20
20
 
@@ -36,10 +36,49 @@ This package carries it when **all** of these hold:
36
36
  So `<box>`, `<text>`, `<window>`, buttons, menus, dialogs and the rest of the
37
37
  widget set are core. Heavier, more specialised things live here.
38
38
 
39
- The line can also fall inside a single feature. `<glarea>` is core — it is a
40
- real X window on a GLX visual, which is renderer internals. A Three.js-shaped
41
- scene graph drawn into it is not: that is composition over a public element,
42
- and it belongs here.
39
+ The line can also fall inside a single feature. `<glarea>` is core — a real
40
+ child surface on a GL visual, created in the commit phase, which is renderer
41
+ internals whichever backend is under it. A Three.js-shaped scene graph drawn
42
+ into it is not: that is composition over a public element, and it belongs
43
+ here.
44
+
45
+ ## Two backends, and the three components that only run on one
46
+
47
+ react-x11 has two backend families now — X11, and a native macOS one that
48
+ speaks to Cocoa with no X server anywhere (`createRoot({ backend })`, or
49
+ `REACT_X11_BACKEND`). **Almost everything in this package is neutral about
50
+ which**, because almost everything here is either composition over core's
51
+ host elements or a registered element that draws through core's 2D context,
52
+ and both are backend contracts rather than X ones. `<Map>`, the vt terminal,
53
+ `<Flow>`, `<Html>`, `<Markdown>`, the charts and the rest render on either.
54
+
55
+ The exceptions are the components built on **XEmbed**, and they are
56
+ exceptions because cross-process window embedding does not exist on macOS at
57
+ all — there is no `<foreign>` to build on, which react-x11's own
58
+ [`docs/macos.md`](https://github.com/sidorares/react-x11/blob/master/docs/macos.md)
59
+ names this package in as much:
60
+
61
+ | Component | On the Cocoa backend |
62
+ | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | `<Terminal>`, embedded backends (xterm, urxvt, alacritty) | No equivalent. Pin `backend="vt"`, which is native and needs no emulator installed anyway. |
64
+ | `<MediaPlayer>` | No equivalent — mpv's and VLC's `--wid` embedding is an X mechanism. |
65
+ | `<TrayHost>` | Reports `status: 'unavailable'` and renders `fallback`; there is no manager selection to take. Putting an icon _in_ a Mac's status bar is the other direction, and is core's `useTray()`. |
66
+
67
+ `<Three>` is the one that looks like it should be on that list and is not: it
68
+ draws through `<glarea>`, which the Cocoa backend implements as GL into a
69
+ CALayer, so the scene graph runs there too — on a third GL path rather than
70
+ none. [Its reference](docs/components/three.md) has the table.
71
+
72
+ One sharp edge worth knowing: `<Terminal backend="auto">` probes `PATH`, and
73
+ a `PATH` probe cannot see which backend the app is running on. On a Mac with
74
+ XQuartz installed it will still choose that xterm, which the Cocoa backend
75
+ then cannot embed. Say `backend="vt"` outright in an app that runs on both.
76
+
77
+ The other X-shaped behaviour to know about is **PRIMARY**: selecting text
78
+ publishing to the X PRIMARY selection, and middle-click pasting it, are what
79
+ X11 desktops do and what this package's document surfaces take part in. On
80
+ macOS there is one pasteboard and no such convention, so what is described
81
+ as PRIMARY below is the X11 backend's half of the story.
43
82
 
44
83
  ## Install
45
84
 
@@ -51,8 +90,14 @@ npm install @react-x11/components react react-x11
51
90
  host element mutates state inside react-x11, so a second copy of the renderer
52
91
  would leave you with an element that lays out correctly and never paints.
53
92
 
54
- Core must be **2.0.0 or newer**: that is the release the subpaths this
55
- package imports (`react-x11/host`, `/node`, `/style`) arrived in.
93
+ Core must be **2.11.0 or newer**. The floor is a running one rather than a
94
+ one-time gate — it moves whenever a component here adopts something core
95
+ just landed, and the last few moves were the Cocoa glyph-run seams
96
+ (`^2.5.0`), the chunked Cocoa stroke `<Map>` wanted (`^2.6.1`), the desktop
97
+ calendar's move into core (`^2.9.1`) and the eyedropper's macOS rung
98
+ (`^2.11.0`). The subpaths this package imports are `react-x11` itself plus
99
+ `/host`, `/node`, `/style`, `/keysyms`, `/ntk`, `/yoga` and
100
+ `/jsx-runtime`.
56
101
 
57
102
  ## Usage
58
103
 
@@ -124,14 +169,18 @@ import type { CodeEditorProps } from '@react-x11/components';
124
169
  | `Code` | `@react-x11/components/code` | A static code block: highlighted, selectable. |
125
170
  | `CodeEditor` | `@react-x11/components/code-editor` | Multiline code editing: highlighting, completion. |
126
171
  | `Flow` | `@react-x11/components/flow` | A directed-graph editor: nodes, edges, pan and zoom. |
172
+ | `Formula` | `@react-x11/components/formula` | TeX mathematics: KaTeX layout, native ink, selectable. |
127
173
  | `Html` | `@react-x11/components/html` | A static HTML + CSS document, selectable, with seams. |
128
174
  | `Map` | `@react-x11/components/maps` | A 2D vector-tile map: pan, zoom, markers, overlays. |
129
175
  | `Markdown` | `@react-x11/components/markdown` | Streaming-friendly GFM with cross-block selection. |
130
176
  | `MediaPlayer` | `@react-x11/components/media-player` | mpv or VLC, embedded, with real transport control. |
177
+ | `QmlView` | `@react-x11/components/qml` | Qt's QML language as an authoring layer. No Qt. |
131
178
  | `ReorderList` … | `@react-x11/components/reorder` | A drag-and-drop list, over core's own drag and drop. |
132
179
  | `Table` | `@react-x11/components/table` | A data table: sortable, virtualized, any row height. |
180
+ | `Tabs` … | `@react-x11/components/tabs` | One visible panel at a time, five strip styles. |
133
181
  | `Terminal` | `@react-x11/components/terminal` | A real terminal: an embedded emulator, or its own. |
134
182
  | `TerminalOutput` | `@react-x11/components/terminal-output` | A captured session, rendered. `<Terminal>`'s static sibling. |
183
+ | `Canvas` … | `@react-x11/components/three` | A three-fiber-shaped 3D scene over either GL backend. |
135
184
  | `Timeline` … | `@react-x11/components/timeline` | A run of events: a mark per step, a line between. |
136
185
  | `TrayHost` | `@react-x11/components/tray-host` | The system tray: applications dock their icons in. |
137
186
  | `Tree` | `@react-x11/components/tree` | A disclosure tree: seams throughout, and virtualized. |
@@ -147,7 +196,7 @@ spawn, watch and hand-back lifecycle both XEmbed wrappers are built on).
147
196
  Selecting text is **core's**, not this package's: a `<box selectable>` is
148
197
  a surface, everything under it that answers for its own text is in the
149
198
  selection, and the drag, the word and block granularities, Ctrl+A, Ctrl+C
150
- and PRIMARY come with it (react-x11#291). `<Markdown>` and `<Code>` set
199
+ and (on X11) PRIMARY come with it (react-x11#291). `<Markdown>` and `<Code>` set
151
200
  that prop and say which parts are chrome; the elements underneath answer
152
201
  `textContent`/`textIndexAt`/`textCaretRect`/`textRangeRects`, which is all
153
202
  an element of your own has to do to join a document.
@@ -218,9 +267,10 @@ What "put a lot of effort into performance" means here, concretely:
218
267
  a per-pixel-column min/max index (a pyramid over the data, built lazily
219
268
  and extended on append), so a million points in a 90px cell cost ~90
220
269
  rectangles. A million points that fall on one pixel render one pixel.
221
- - **Server-side drawing commands by default, pixels when they win.** A
222
- dense line goes out as one batched `FillRectangles` (~8 bytes per pixel
223
- column); a sparse one as a real antialiased path. The one place a pixel
270
+ - **Drawing commands by default, pixels when they win.** A dense line goes
271
+ out as one batched `fillRects` — on X11 that is a `FillRectangles` at
272
+ ~8 bytes per pixel column, and the wire cost `onFrameStats` reports is
273
+ that one; a sparse line goes as a real antialiased path. The one place a pixel
224
274
  push wins — a scatter covering most of the plot — is detected by
225
275
  comparing the actual byte costs, and flips to one composited density
226
276
  image.
@@ -278,8 +328,8 @@ markdown→HTML pass anywhere — and is exported (`parseMarkdown`) with the
278
328
  AST types.
279
329
 
280
330
  **Selection is the point.** Text selects across every block — drag,
281
- double-click a word, triple-click a block, Ctrl+A, Ctrl+C — and a mouse-up
282
- with a selection takes the X11 PRIMARY selection, so middle-click paste
331
+ double-click a word, triple-click a block, Ctrl+A, Ctrl+C — and on X11 a
332
+ mouse-up with a selection takes the PRIMARY selection, so middle-click paste
283
333
  works everywhere. All of that is core's `selectable` (react-x11#291); what
284
334
  this component adds is which parts are chrome, so copied text is clean:
285
335
  list markers stay behind, and the separators come from the layout, which
@@ -372,6 +422,26 @@ explicit `language={…}`) and the look is shared with `<Markdown>`'s fenced
372
422
  blocks, so the two agree in one window. Selection and copy are core's; the
373
423
  line-number gutter is `selectable={false}`, so copied code pastes clean.
374
424
 
425
+ ## Mathematics
426
+
427
+ ```jsx
428
+ import { Formula } from '@react-x11/components/formula';
429
+
430
+ <Formula tex="x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}" display selectable />;
431
+ ```
432
+
433
+ TeX, rendered natively. KaTeX — an optional dependency — parses the source
434
+ into its virtual DOM, and this package's `formula` element lays that tree out
435
+ and draws it through the app's font manager, using KaTeX's own faces. Every
436
+ glyph answers core's four text accessors, so the mathematics is **part of the
437
+ selection** rather than an opaque picture: on its own with `selectable`, or as
438
+ one block inside any `selectable` document. `partial` holds the last tree that
439
+ parsed while more source is still arriving, which is what makes it safe to
440
+ append to a formula a model is still writing. A ` ```math ` fence in a
441
+ document becomes one through `<Markdown>`'s `fences` map, which is opt-in —
442
+ that seam is how `<Markdown>` hosts a component without importing it.
443
+ [The reference](docs/components/formula.md) has the rest.
444
+
375
445
  ## A terminal session you already have: `<TerminalOutput>`
376
446
 
377
447
  The static sibling of `<Terminal>`, exactly as `<Code>` is `<CodeEditor>`'s.
@@ -473,6 +543,40 @@ pipeline beside galleries of the sizes and variants;
473
543
  [the reference](docs/components/timeline.md) has the rest, including why
474
544
  every indicator's chip is opaque.
475
545
 
546
+ ## Tabs
547
+
548
+ One visible panel at a time. The API is
549
+ [Chakra UI's Tabs](https://chakra-ui.com/docs/components/tabs) with its parts
550
+ spelled flat, exactly as `<Timeline>` spells its own:
551
+
552
+ ```jsx
553
+ import {
554
+ Tabs,
555
+ TabsList,
556
+ TabsTrigger,
557
+ TabsContent,
558
+ } from '@react-x11/components/tabs';
559
+
560
+ <Tabs defaultValue="members">
561
+ <TabsList>
562
+ <TabsTrigger value="members">Members</TabsTrigger>
563
+ <TabsTrigger value="projects">Projects</TabsTrigger>
564
+ </TabsList>
565
+ <TabsContent value="members">…</TabsContent>
566
+ <TabsContent value="projects">…</TabsContent>
567
+ </Tabs>;
568
+ ```
569
+
570
+ The vocabulary is Chakra's too — `value`/`defaultValue`/`onValueChange`, five
571
+ strip variants (`line`, `subtle`, `enclosed`, `outline`, `plain`), `size`,
572
+ `orientation`, `activationMode`, `fitted`, `lazyMount` — so a snippet from
573
+ their docs is the same tree with the dots removed. It supersedes core's tabs
574
+ the way `<Tree>` supersedes core's tree: the keyboard and RTL behaviour a
575
+ user has already learnt, without the items-array API. The one prop Chakra has
576
+ no counterpart for is `overflow`: a horizontal strip that runs out of room
577
+ puts the tabs that do not fit in a menu at its end rather than off its own
578
+ edge. [The reference](docs/components/tabs.md) has the rest.
579
+
476
580
  ## A drag-and-drop list
477
581
 
478
582
  `<ReorderList>` is a list the user reorders by dragging its items — or by
@@ -617,8 +721,8 @@ import {
617
721
  ```
618
722
 
619
723
  Editing is the full expected set: selection (keyboard and mouse, word and
620
- line variants), undo/redo with coalescing, X11 clipboard including PRIMARY
621
- and middle-click paste, auto-indent, Tab/Shift+Tab indentation, Ctrl+/
724
+ line variants), undo/redo with coalescing, the system clipboard — including
725
+ PRIMARY and middle-click paste on X11 — auto-indent, Tab/Shift+Tab indentation, Ctrl+/
622
726
  comment toggling, bracket matching, and LSP-shaped `diagnostics` squiggles.
623
727
  Escape then Tab leaves the field. Ctrl+Space asks for completions.
624
728
 
@@ -857,6 +961,71 @@ architecture.
857
961
 
858
962
  [mvt]: https://github.com/mapbox/vector-tile-spec
859
963
 
964
+ ## A 3D scene
965
+
966
+ A [react-three-fiber](https://docs.pmnd.rs/react-three-fiber)-shaped scene
967
+ graph over core's `<glarea>`. There is no three.js and no WebGL underneath —
968
+ the element names, the prop shapes, `attach`, dashed paths, `useFrame` /
969
+ `useThree` and `extend()` follow r3f, and what differs is the pipeline.
970
+
971
+ ```jsx
972
+ import { Canvas, useFrame } from '@react-x11/components/three';
973
+
974
+ <Canvas camera={{ position: [3, 3, 6], fov: 50 }} style={{ flexGrow: 1 }}>
975
+ <ambientLight intensity={0.4} />
976
+ <pointLight position={[5, 6, 6]} />
977
+ <mesh position={[0, 0.5, 0]}>
978
+ <boxGeometry args={[1, 1, 1]} />
979
+ <meshStandardMaterial color="hotpink" />
980
+ </mesh>
981
+ </Canvas>;
982
+ ```
983
+
984
+ This is the worked example at the top of this file of a boundary running
985
+ _through_ a feature: `<glarea>` is a real GL surface created in the commit
986
+ phase, which is renderer internals and stayed in core; the scene graph over it
987
+ is composition, and it is here.
988
+
989
+ Which pipeline draws is the connection's business, not the scene's. **Indirect
990
+ GLX** encodes GL 1.x into the X connection and survives a network hop, at the
991
+ cost of shaders and post-processing — the protocol encodes neither. **Direct**
992
+ rendering (the `x11-dri` addon: DRI3 on Linux, Apple-DRI under XQuartz, CGL
993
+ into a CALayer on the native macOS backend) is OpenGL ES 2 on the GPU, and it
994
+ is where `<shaderMaterial>` and `<effectComposer>` work. The same JSX renders
995
+ on all of them; the two direct-only families throw at creation naming the
996
+ reason rather than showing a blank surface, so a scene that would rather
997
+ degrade can branch. [The reference](docs/components/three.md) is the table,
998
+ including the JSX pragma the intrinsic element names want.
999
+
1000
+ ## QML, without Qt
1001
+
1002
+ Qt's declarative UI language as an authoring layer — the parser, the reactive
1003
+ binding graph and the object model are this package's own, with zero
1004
+ dependencies and no Qt anywhere:
1005
+
1006
+ ```jsx
1007
+ import { QmlView } from '@react-x11/components/qml';
1008
+
1009
+ <QmlView
1010
+ source={`
1011
+ import QtQuick 2.15
1012
+ Rectangle {
1013
+ width: 300; height: 120; color: "#101418"
1014
+ property int count: 0
1015
+ Text { anchors.centerIn: parent; color: "white"; text: "clicks: " + count }
1016
+ MouseArea { anchors.fill: parent; onClicked: count++ }
1017
+ }
1018
+ `}
1019
+ />;
1020
+ ```
1021
+
1022
+ Everything visible is an ordinary `<box>`, `<text>` or `<image>` committed
1023
+ through the renderer, so theming, damage tracking, accessibility and the test
1024
+ harness all apply to QML content without knowing it is QML. It registers no
1025
+ host element; the one import-time side effect is populating the family's own
1026
+ QtQuick type registry, which touches no core state and shakes out with the
1027
+ family. [The reference](docs/components/qml.md) has the rest.
1028
+
860
1029
  ## The user's real calendar
861
1030
 
862
1031
  `<Calendar dayContent>` is the seam the desktop's own events hang off, and
@@ -901,9 +1070,9 @@ handed, so nothing sits between the two — **that string format is the whole
901
1070
  contract between the two packages**, and it is why the grid never had to know
902
1071
  what an event is.
903
1072
 
904
- This package shipped the D-Bus half until 0.4.0, as
1073
+ This package shipped the D-Bus half through 0.6.0, as
905
1074
  `@react-x11/components/desktop-calendar`. It moved to core in react-x11 2.9.1
906
- and the subpath is gone: a calendar is one of the things an app does _outside_
1075
+ and 0.7.0 deleted the subpath: a calendar is one of the things an app does _outside_
907
1076
  its own windows, like notifications, the tray and the file dialog, and every
908
1077
  one of those is a ladder in core with a freedesktop rung and a macOS one — the
909
1078
  macOS rung here reaches EventKit through `@windowkit/appkit`, which only core
@@ -924,6 +1093,12 @@ These two are the same component twice, and they are what core's `<foreign>`
924
1093
  element was added for: a react-x11 app can now **host** another X client
925
1094
  rather than only drawing its own pixels.
926
1095
 
1096
+ **Both are X11-only**, and the heading says why: an X client is what they
1097
+ host. macOS has no cross-process window embedding to build the same thing
1098
+ on, so on the Cocoa backend `<Terminal backend="vt">` is the terminal and
1099
+ `<MediaPlayer>` has no counterpart — see
1100
+ [Two backends](#two-backends-and-the-three-components-that-only-run-on-one).
1101
+
927
1102
  ```jsx
928
1103
  import { Terminal } from '@react-x11/components';
929
1104
 
@@ -993,9 +1168,10 @@ without an xterm in CI.
993
1168
 
994
1169
  One prop changes the terminal from a hosted X client into a native element: a
995
1170
  pty (through a pluggable `PtyHost`), [`@xterm/headless`][xterm-headless] as
996
- the escape-sequence state machine, and a cell-grid renderer that draws with
997
- XRender glyph runs into a retained offscreen surface, scrolls with a
998
- server-side copy, and coalesces onto react-x11's vblank-paced frame clock.
1171
+ the escape-sequence state machine, and a cell-grid renderer that draws glyph
1172
+ runs into a retained offscreen surface, scrolls it in place, and coalesces
1173
+ onto react-x11's frame clock. One renderer, both backends: XRender runs and a
1174
+ server-side copy on X11, CoreText runs into a CG bitmap on macOS.
999
1175
 
1000
1176
  ```jsx
1001
1177
  <Terminal
@@ -1056,8 +1232,8 @@ Keyboard, mouse and selection are what a terminal user expects: xterm-compatible
1056
1232
  key encoding (application cursor/keypad modes, the modifier parameter
1057
1233
  scheme, `Alt` as an ESC prefix), mouse reporting in the tracking mode the
1058
1234
  program asked for (with Shift as the universal "let me select instead"
1059
- override), char/word/line selection that publishes PRIMARY, middle-click
1060
- paste, Ctrl+Shift+C/V, bracketed paste, and OSC 52 clipboard **writes** —
1235
+ override), char/word/line selection that publishes PRIMARY on X11,
1236
+ middle-click paste, Ctrl+Shift+C/V, bracketed paste, and OSC 52 clipboard **writes** —
1061
1237
  never reads, which are answered with nothing whatever a program asks for.
1062
1238
 
1063
1239
  Escape arms one pass-through Tab, so the terminal is not a keyboard trap;
@@ -1125,7 +1301,11 @@ The same protocol as the two above, pointed the other way. `<Terminal>` and
1125
1301
  `<MediaPlayer>` spawn a program into a container they own; a tray is handed
1126
1302
  windows by applications that were already running, and the
1127
1303
  [system tray spec](http://specifications.freedesktop.org/systemtray/latest/)
1128
- is XEmbed's biggest surviving consumer.
1304
+ is XEmbed's biggest surviving consumer. **X11-only**, for the same reason the
1305
+ two above are: on the Cocoa backend there is no manager selection to take, so
1306
+ it reports `status: 'unavailable'` and renders `fallback` — the same posture
1307
+ it has against the headless test server. Putting an icon _in_ a Mac's status
1308
+ bar is the other direction and is core's `useTray()`.
1129
1309
 
1130
1310
  ```jsx
1131
1311
  import { TrayHost } from '@react-x11/components';
@@ -1184,9 +1364,6 @@ rather than inside it.
1184
1364
 
1185
1365
  Candidates to move here:
1186
1366
 
1187
- - The 3D scene graph and a Three.js / react-three-fiber-shaped layer, with
1188
- `<glarea>` itself staying in core.
1189
- - `<Tabs>`, undecided — it may well stay in core.
1190
1367
  - The inline half of MDX — a component in the middle of a sentence, which is
1191
1368
  gated on a `<richtext>` run that can reserve advance width for an embedded
1192
1369
  element. Block-position components and expressions have shipped;
@@ -1196,8 +1373,14 @@ Candidates to move here:
1196
1373
  `dbusmenu.js`, and a complete panel wants both.
1197
1374
 
1198
1375
  `<Table>` above supersedes core's `<Table>` the way `<Tree>` supersedes
1199
- core's tree; whether core's remainder is stripped down or removed outright
1200
- is core's decision, still open — `docs/prd-table.md` records the contract.
1376
+ core's tree, and `<Tabs>` supersedes core's tabs the same way; whether core's
1377
+ remainder in each case is stripped down or removed outright is core's
1378
+ decision, still open — `docs/prd-table.md` records the table's contract.
1379
+
1380
+ The 3D scene graph landed while this list still called it a candidate:
1381
+ `@react-x11/components/three` is the react-three-fiber-shaped layer, and
1382
+ `<glarea>` stayed in core exactly as planned. That is the worked example at
1383
+ the top of this file of a boundary running _through_ a feature.
1201
1384
 
1202
1385
  `<Markdown>` above **replaces** core's ntk-backed `<markdown>` element, and
1203
1386
  `<Html>` now replaces `HtmlView` and core's `<html>` (ntk's document widgets
@@ -1 +1 @@
1
- {"version":3,"file":"ColorPicker.d.ts","sourceRoot":"","sources":["../../src/color-picker/ColorPicker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ColorPicker.d.ts","sourceRoot":"","sources":["../../src/color-picker/ColorPicker.ts"],"names":[],"mappings":"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"}
@@ -296,7 +296,16 @@ export function ColorPicker(props) {
296
296
  const setHsv = (h, sat, v, done = false) => emit(channelsFromHsv(h, sat, v, model.a), done);
297
297
  // --- pointer ------------------------------------------------------------
298
298
  const fractionIn = (node, ev) => {
299
- const box = node?.abs;
299
+ // `getClientRects()`, not `abs`. `abs` is **device** pixels and an
300
+ // event's `x`/`y` are **logical** — react-x11's docs/scale.md — so
301
+ // subtracting one from the other is only right at scale 1. On a retina
302
+ // panel a press at the middle of this box read as a press a quarter of
303
+ // the way in from its corner: the thumb sat near the corner however the
304
+ // pointer moved, and the far side of every axis was unreachable. The
305
+ // client rect is the same box already divided by the node's scale,
306
+ // which is the unit the pointer arrives in, so the arithmetic below is
307
+ // the arithmetic it always was.
308
+ const [box] = node?.getClientRects() ?? [];
300
309
  if (!box?.width || !box.height)
301
310
  return null;
302
311
  return {
@@ -450,21 +459,38 @@ export function ColorPicker(props) {
450
459
  onBlur: () => setFocused(false),
451
460
  onKeyDown: focusable ? handleKey : undefined,
452
461
  });
453
- const renderArea = () => hx('box', {
454
- key: 'area',
455
- ref: areaRef,
456
- ...axisProps('area', 'Saturation and brightness', `saturation ${Math.round(model.s * 100)}%, brightness ${Math.round(model.v * 100)}%`, model.s * 100, 100),
462
+ /**
463
+ * The gradient and its border, as one element *under* the thumb.
464
+ *
465
+ * Core paints a node's children first and its border after them, so a
466
+ * border on the box the thumb lives in is painted across the thumb — the
467
+ * grey line over the white handle. On a sibling below it, the thumb wins,
468
+ * which is the order a handle wants: it is the thing being pointed at.
469
+ *
470
+ * It fixes the geometry too. An absolutely positioned child is laid out
471
+ * from its parent's *padding* box, so a border on the axis box shifted
472
+ * every thumb by its width — a strip handle overhung the top by 1px and
473
+ * the bottom by 3, and sat 1px right of the colour it was naming. Here
474
+ * the thumb's parent has no border, so `left` and `top` mean what they
475
+ * say: the same outer box `fractionIn` measures a press against.
476
+ */
477
+ const framed = (axis, pane) => hx('box', {
478
+ key: 'frame',
457
479
  style: [
458
- s.area,
480
+ s.pane,
459
481
  {
460
482
  borderWidth: theme.borderWidth,
461
- borderColor: cursorShown && active === 'area'
462
- ? theme.borderFocus
463
- : theme.border,
483
+ borderColor: cursorShown && active === axis ? theme.borderFocus : theme.border,
464
484
  },
465
485
  ],
486
+ }, pane);
487
+ const renderArea = () => hx('box', {
488
+ key: 'area',
489
+ ref: areaRef,
490
+ ...axisProps('area', 'Saturation and brightness', `saturation ${Math.round(model.s * 100)}%, brightness ${Math.round(model.v * 100)}%`, model.s * 100, 100),
491
+ style: s.area,
466
492
  ...track('area', areaTo),
467
- }, hx('canvas', {
493
+ }, framed('area', hx('canvas', {
468
494
  style: s.pane,
469
495
  // Everything the drawing reads: the hue, and the size, which is a
470
496
  // constant here precisely so this key can name it.
@@ -485,7 +511,7 @@ export function ColorPicker(props) {
485
511
  ctx.fillStyle = black;
486
512
  ctx.fillRect(0, 0, width, height);
487
513
  },
488
- }), hx('box', {
514
+ })), hx('box', {
489
515
  style: [
490
516
  s.areaThumb,
491
517
  {
@@ -499,17 +525,9 @@ export function ColorPicker(props) {
499
525
  key: 'hue',
500
526
  ref: hueRef,
501
527
  ...axisProps('hue', 'Hue', `hue ${Math.round(model.h)} degrees`, model.h, 360),
502
- style: [
503
- s.strip,
504
- {
505
- borderWidth: theme.borderWidth,
506
- borderColor: cursorShown && active === 'hue'
507
- ? theme.borderFocus
508
- : theme.border,
509
- },
510
- ],
528
+ style: s.strip,
511
529
  ...track('hue', hueTo),
512
- }, hx('canvas', {
530
+ }, framed('hue', hx('canvas', {
513
531
  style: s.pane,
514
532
  cacheKey: `hue:${CONTENT}x${STRIP_H}`,
515
533
  onDraw: (ctx, { width, height }) => {
@@ -520,7 +538,7 @@ export function ColorPicker(props) {
520
538
  ctx.fillStyle = ramp;
521
539
  ctx.fillRect(0, 0, width, height);
522
540
  },
523
- }), hx('box', {
541
+ })), hx('box', {
524
542
  style: [
525
543
  s.stripThumb,
526
544
  { left: (model.h / 360) * CONTENT - 4, backgroundColor: hue },
@@ -530,17 +548,9 @@ export function ColorPicker(props) {
530
548
  key: 'alpha',
531
549
  ref: alphaRef,
532
550
  ...axisProps('alpha', 'Opacity', `opacity ${Math.round(model.a * 100)}%`, model.a * 100, 100),
533
- style: [
534
- s.strip,
535
- {
536
- borderWidth: theme.borderWidth,
537
- borderColor: cursorShown && active === 'alpha'
538
- ? theme.borderFocus
539
- : theme.border,
540
- },
541
- ],
551
+ style: s.strip,
542
552
  ...track('alpha', alphaTo),
543
- }, hx('canvas', {
553
+ }, framed('alpha', hx('canvas', {
544
554
  style: s.pane,
545
555
  cacheKey: `alpha:${opaqueHex(model)}:${CONTENT}x${STRIP_H}`,
546
556
  onDraw: (ctx, { width, height }) => {
@@ -552,7 +562,7 @@ export function ColorPicker(props) {
552
562
  ctx.fillStyle = ramp;
553
563
  ctx.fillRect(0, 0, width, height);
554
564
  },
555
- }), hx('box', {
565
+ })), hx('box', {
556
566
  style: [
557
567
  s.stripThumb,
558
568
  { left: model.a * CONTENT - 4, backgroundColor: opaqueHex(model) },