@weasel-js/labkit 1.2.0 → 1.3.0-pre.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.
- package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
- package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
- package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
- package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
- package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
- package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
- package/dist/canvas/index.d.ts +7 -3
- package/dist/canvas/index.js +2 -1
- package/dist/chrome/index.d.ts +86 -0
- package/dist/chrome/index.js +8 -0
- package/dist/chrome/index.js.map +1 -0
- package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
- package/dist/chunk-4TMMVIDM.js.map +1 -0
- package/dist/{chunk-THBG7FQZ.js → chunk-HFDVGF4X.js} +15 -12
- package/dist/chunk-HFDVGF4X.js.map +1 -0
- package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
- package/dist/chunk-NS54R52R.js.map +1 -0
- package/dist/{chunk-G5TJVQQT.js → chunk-O2BULFHX.js} +5 -14
- package/dist/chunk-O2BULFHX.js.map +1 -0
- package/dist/chunk-O7NVSCLN.js +266 -0
- package/dist/chunk-O7NVSCLN.js.map +1 -0
- package/dist/chunk-PO6L3NSH.js +366 -0
- package/dist/chunk-PO6L3NSH.js.map +1 -0
- package/dist/chunk-PQJ5B2U2.js +27 -0
- package/dist/chunk-PQJ5B2U2.js.map +1 -0
- package/dist/chunk-RVNN4CHQ.js +212 -0
- package/dist/chunk-RVNN4CHQ.js.map +1 -0
- package/dist/chunk-TLGRYALP.js +6151 -0
- package/dist/chunk-TLGRYALP.js.map +1 -0
- package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
- package/dist/chunk-V7PRSIRQ.js.map +1 -0
- package/dist/{chunk-BOHF3PQO.js → chunk-Z6HJ5FEM.js} +8111 -5190
- package/dist/chunk-Z6HJ5FEM.js.map +1 -0
- package/dist/chunk-ZZAYVX4X.js +505 -0
- package/dist/chunk-ZZAYVX4X.js.map +1 -0
- package/dist/controls/index.d.ts +19 -5
- package/dist/controls/index.js +3 -1
- package/dist/dragdrop/index.d.ts +4 -2
- package/dist/index.d.ts +246 -106
- package/dist/index.js +673 -559
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +5 -3
- package/dist/layers/index.js +2 -1
- package/dist/passthrough/weasel-canvas.d.ts +69 -13
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +3799 -3672
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +102 -8
- package/dist/primitives/index.js +4 -1
- package/dist/state/index.d.ts +3 -2
- package/dist/state/index.js +2 -2
- package/dist/styles.css +542 -153
- package/dist/surface/index.js +2 -1
- package/dist/ui/layers/index.js +4 -3
- package/dist/undo/index.d.ts +4 -2
- package/package.json +16 -7
- package/src/canvas/AGENTS.md +4 -0
- package/src/canvas/CanvasStack.tsx +6 -2
- package/src/canvas/useLayerScheduler.ts +22 -15
- package/src/canvas/usePanZoom.test.ts +80 -0
- package/src/canvas/usePanZoom.ts +16 -2
- package/src/chrome/ChromeRegions.stories.tsx +106 -0
- package/src/chrome/builtins.test.ts +153 -0
- package/src/chrome/builtins.tsx +205 -0
- package/src/chrome/index.ts +25 -0
- package/src/chrome/merge.test.ts +51 -0
- package/src/chrome/merge.ts +52 -0
- package/src/chrome/regions/PaletteRegion.less +14 -0
- package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
- package/src/chrome/regions/PaletteRegion.tsx +41 -0
- package/src/chrome/regions/SidebarRegion.tsx +56 -0
- package/src/chrome/regions/StatusRegion.tsx +31 -0
- package/src/chrome/regions/TitleBarRegion.tsx +41 -0
- package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
- package/src/chrome/regions/ToolbarRegion.tsx +71 -0
- package/src/chrome/regions/ViewportRegion.less +42 -0
- package/src/chrome/regions/ViewportRegion.tsx +37 -0
- package/src/chrome/regions/regions.test.tsx +81 -0
- package/src/chrome/types.ts +127 -0
- package/src/config/builder.test.ts +87 -0
- package/src/config/builder.ts +224 -0
- package/src/config/fromConfigField.test.ts +93 -0
- package/src/config/fromConfigField.ts +74 -0
- package/src/config/index.ts +22 -0
- package/src/config/resolve.test.ts +120 -0
- package/src/config/resolve.ts +67 -0
- package/src/config/rules.ts +68 -0
- package/src/config/types.ts +108 -0
- package/src/config/useConfigSchema.ts +30 -0
- package/src/config/visible.ts +20 -0
- package/src/controls/ControlPanel.less +4 -58
- package/src/controls/ControlPanel.stories.tsx +86 -0
- package/src/controls/ControlPanel.test.tsx +223 -10
- package/src/controls/ControlPanel.tsx +202 -203
- package/src/dragdrop/DragDropRuntime.tsx +36 -18
- package/src/dragdrop/DragGhost.less +1 -1
- package/src/dragdrop/Palette.less +1 -1
- package/src/index.ts +25 -3
- package/src/instrument/SineWave.smoke.test.tsx +8 -1
- package/src/instrument/defineInstrument.test.ts +38 -1
- package/src/instrument/defineInstrument.ts +31 -4
- package/src/instrument/index.ts +1 -0
- package/src/instrument/types.ts +27 -4
- package/src/instrument/validateConfigSchema.test.ts +13 -3
- package/src/instrument/validateConfigSchema.ts +7 -10
- package/src/lab/Lab.chrome.test.tsx +65 -0
- package/src/lab/Lab.tsx +58 -15
- package/src/lab/LabContext.ts +6 -0
- package/src/lab/LabFullChrome.stories.tsx +308 -0
- package/src/lab/LabHeader.test.tsx +42 -0
- package/src/lab/LabHeader.tsx +57 -0
- package/src/lab/LabPalette.test.tsx +35 -0
- package/src/lab/LabPalette.tsx +34 -0
- package/src/lab/LabShell.less +50 -3
- package/src/lab/Workspace.less +1 -30
- package/src/lab/Workspace.test.tsx +20 -10
- package/src/lab/Workspace.tsx +5 -8
- package/src/lab/index.ts +2 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.less +31 -7
- package/src/layers/LayerList.tsx +17 -4
- package/src/passthrough/weasel-ui.ts +23 -0
- package/src/primitives/DragHandleGlyph.tsx +29 -0
- package/src/primitives/FloatingPanel.less +28 -0
- package/src/primitives/FloatingPanel.stories.tsx +46 -0
- package/src/primitives/FloatingPanel.test.tsx +240 -0
- package/src/primitives/FloatingPanel.tsx +190 -0
- package/src/primitives/FpsMeter.test.tsx +44 -0
- package/src/primitives/FpsMeter.tsx +20 -9
- package/src/primitives/JobProgress.less +69 -0
- package/src/primitives/JobProgress.stories.tsx +48 -0
- package/src/primitives/JobProgress.tsx +65 -0
- package/src/primitives/Legend.less +47 -0
- package/src/primitives/Legend.stories.tsx +32 -0
- package/src/primitives/Legend.test.tsx +57 -0
- package/src/primitives/Legend.tsx +40 -0
- package/src/primitives/Sidebar.less +32 -2
- package/src/primitives/StatusBar.less +7 -0
- package/src/primitives/StatusBar.tsx +10 -3
- package/src/primitives/Toolbar.less +50 -3
- package/src/primitives/Toolbar.test.tsx +67 -1
- package/src/primitives/Toolbar.tsx +72 -6
- package/src/primitives/ZoomControl.less +31 -0
- package/src/primitives/ZoomControl.tsx +76 -0
- package/src/primitives/floating.entry.test.tsx +14 -0
- package/src/primitives/index.ts +10 -2
- package/src/primitives/useRovingTabIndex.test.ts +27 -0
- package/src/primitives/useRovingTabIndex.ts +79 -0
- package/src/state/store.ts +11 -3
- package/src/state/toolSlot.test.ts +36 -0
- package/src/state/types.ts +4 -0
- package/src/styles.less +6 -0
- package/src/surface/useTiledSurface.ts +14 -10
- package/src/test-setup.ts +26 -0
- package/src/theme/base.less +19 -2
- package/src/theme/interstellar.tokens.json +6 -2
- package/src/tools/types.ts +24 -0
- package/src/trial/Trial.config.test.tsx +113 -0
- package/src/trial/Trial.job.test.tsx +13 -15
- package/src/trial/Trial.less +115 -10
- package/src/trial/Trial.stories.tsx +1 -0
- package/src/trial/Trial.test.tsx +67 -96
- package/src/trial/Trial.tsx +80 -39
- package/src/trial/TrialChrome.tsx +99 -80
- package/src/trial/TrialDragContext.ts +10 -0
- package/src/trial/TrialTitleBar.tsx +41 -0
- package/src/trial/index.ts +1 -15
- package/src/trial/trialChrome.borders.test.ts +38 -0
- package/src/ui/layers/LayerStack.less +9 -9
- package/src/ui/layers/LayerStack.tsx +1 -13
- package/src/ui/properties/CurveField.less +2 -2
- package/src/ui/properties/CurveField.test.tsx +11 -11
- package/src/ui/properties/PropertyGroup.less +2 -2
- package/src/ui/properties/PropertyPanel.less +55 -41
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
- package/src/ui/properties/storyLayouts.tsx +2 -2
- package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/chunk-574LJAV4.js.map +0 -1
- package/dist/chunk-5R2ATYPJ.js +0 -201
- package/dist/chunk-5R2ATYPJ.js.map +0 -1
- package/dist/chunk-73KA7WBO.js +0 -5992
- package/dist/chunk-73KA7WBO.js.map +0 -1
- package/dist/chunk-BOHF3PQO.js.map +0 -1
- package/dist/chunk-G5TJVQQT.js.map +0 -1
- package/dist/chunk-PMAU3SEE.js +0 -126
- package/dist/chunk-PMAU3SEE.js.map +0 -1
- package/dist/chunk-SFL7NFKN.js.map +0 -1
- package/dist/chunk-THBG7FQZ.js.map +0 -1
- package/dist/chunk-VUU5UXHE.js.map +0 -1
- package/src/instrument/capabilityDetector.test.ts +0 -64
- package/src/instrument/capabilityDetector.ts +0 -20
- package/src/trial/DefaultSidebar.tsx +0 -29
- package/src/trial/DefaultStatusBar.tsx +0 -19
- package/src/trial/DefaultToolbar.tsx +0 -92
- package/src/trial/slotTypes.ts +0 -54
|
@@ -208,6 +208,24 @@ interface RecordEntryOptions {
|
|
|
208
208
|
selectionBefore?: readonly string[];
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Path data model. Vector-graphics primitive used kit-wide as the canonical
|
|
213
|
+
* shape (replacing per-shape ad-hoc poses). SVG-style command stream so we
|
|
214
|
+
* cover lines, polygons, beziers, and multi-contour shapes (think the inner
|
|
215
|
+
* hole of an "O") under one type.
|
|
216
|
+
*
|
|
217
|
+
* Storage: command codes in a `Uint8Array`, parameters in a `Float32Array`.
|
|
218
|
+
* Each command consumes a fixed number of float coords from the parameter
|
|
219
|
+
* array; the parser walks both arrays in lockstep. This keeps interaction
|
|
220
|
+
* hot loops monomorphic and keeps GC pressure low under heavy edits.
|
|
221
|
+
*
|
|
222
|
+
* Two path subtypes are distinguished at the type level so common-case
|
|
223
|
+
* machinery (selection AABBs, area-select intersection, hit-testing rect
|
|
224
|
+
* silhouettes) can short-circuit on `RectPath` without paying the polygon
|
|
225
|
+
* kernel cost. Polymorphic kernels accept either via the `Path` union and
|
|
226
|
+
* dispatch on `kind`.
|
|
227
|
+
*/
|
|
228
|
+
|
|
211
229
|
/** Fill rule used by polygon path hit-testing and `ctx.fill()`. */
|
|
212
230
|
type PathFillRule = 'nonzero' | 'evenodd';
|
|
213
231
|
/**
|
|
@@ -312,6 +330,20 @@ interface NodeBase<TData, TLayer extends string, TPose> {
|
|
|
312
330
|
pose: TPose;
|
|
313
331
|
data: TData;
|
|
314
332
|
parent: NodeId | null;
|
|
333
|
+
/** Nodes whose poses this node's geometry is computed from. Fixed at add
|
|
334
|
+
* time. Absent or empty means the node's geometry is authored, which is the
|
|
335
|
+
* normal case. */
|
|
336
|
+
dependsOn?: readonly NodeId[];
|
|
337
|
+
/** Computes this node's path from its dependencies' poses, in `dependsOn`
|
|
338
|
+
* order. A dependency that has been removed arrives as `undefined`.
|
|
339
|
+
* Returning `null` means "nothing to draw right now". Re-evaluated when a
|
|
340
|
+
* dependency's world pose changes, never authored. Absolute-pose `Scene`
|
|
341
|
+
* makes that the dependency's own pose, and an ancestor's move reaches it as
|
|
342
|
+
* a `setPose` of its own from the container cascade.
|
|
343
|
+
* `node` is deliberately widened: naming `TData`/`TLayer` here puts them in
|
|
344
|
+
* a contravariant position, making `Scene` invariant in both and breaking
|
|
345
|
+
* assignment kit-wide. The cost is that a `derivePath` casts to read `node.data`. */
|
|
346
|
+
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (TPose | undefined)[]) => Path | null;
|
|
315
347
|
}
|
|
316
348
|
/** A node with no children — a shape, a label, an image. */
|
|
317
349
|
interface LeafNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
|
|
@@ -366,6 +398,11 @@ interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
|
|
|
366
398
|
/** Only meaningful when `kind === 'container'`. Attach a clip-path function
|
|
367
399
|
* to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */
|
|
368
400
|
clipFromPose?: (pose: TPose) => Path | null;
|
|
401
|
+
/** Mirrors `SceneNode.dependsOn`. */
|
|
402
|
+
dependsOn?: readonly NodeId[];
|
|
403
|
+
/** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is
|
|
404
|
+
* looked up from it, never passed in. */
|
|
405
|
+
derivePath?: (node: Node<unknown, string, TPose>, deps: readonly (TPose | undefined)[]) => Path | null;
|
|
369
406
|
}
|
|
370
407
|
/** A custom scene mutation registered with `Scene.registerOp`: how to apply
|
|
371
408
|
* it and how to undo it. The pair is what makes it participate in history. */
|
|
@@ -413,6 +450,57 @@ interface SerializedNode<TData, TLayer extends string, TPose> {
|
|
|
413
450
|
/** Registry key for the container's clip-path factory.
|
|
414
451
|
* Containers only; omitted when the container has no clip. */
|
|
415
452
|
clipFromPoseKey?: string;
|
|
453
|
+
/** Ids this node's geometry derives from. Omitted when it derives from nothing. */
|
|
454
|
+
dependsOn?: readonly string[];
|
|
455
|
+
/** Registry key for the node's `derivePath` function. Omitted when it has none. */
|
|
456
|
+
derivePathKey?: string;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* One node's ephemeral presentation override — what a frame loop wants to say
|
|
460
|
+
* about a node without saying it about the document.
|
|
461
|
+
*
|
|
462
|
+
* Not document content: never recorded in history, never in `toJSON`, and
|
|
463
|
+
* writing one does not bump `Scene.getVersion()`. Hoist one entry per node and
|
|
464
|
+
* mutate it in place on a frame loop; `PoseOverrides.commit()` is what makes a
|
|
465
|
+
* mutation visible.
|
|
466
|
+
*/
|
|
467
|
+
interface PoseOverride<TPose> {
|
|
468
|
+
/** Replaces the node's document pose everywhere the render and hit-test
|
|
469
|
+
* paths read one, including the clip a container derives from its pose.
|
|
470
|
+
* Resolved by `effectivePose` — reading `node.pose` directly is how those
|
|
471
|
+
* paths came to disagree about where a node is. */
|
|
472
|
+
pose?: TPose;
|
|
473
|
+
/** Multiplied into the node's painted alpha, on top of any `alphaFor`. */
|
|
474
|
+
alpha?: number;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* The scene's ephemeral per-node overrides — see {@link PoseOverride}.
|
|
478
|
+
*
|
|
479
|
+
* The intended shape of a frame is: `set` each node once, mutate the entries
|
|
480
|
+
* in place per frame, `commit()` once. `commit` is not optional bookkeeping —
|
|
481
|
+
* the painter memo keys on pose *reference*, so a mutation without a commit
|
|
482
|
+
* paints the previous frame with no error.
|
|
483
|
+
*
|
|
484
|
+
* To promote a frame to document state (dropping a drag, baking an animation),
|
|
485
|
+
* write it once through `Scene.setPose` and `clear` the override.
|
|
486
|
+
*/
|
|
487
|
+
interface PoseOverrides<TPose> {
|
|
488
|
+
/** Store `entry` for `id` **by reference**; the caller keeps mutating it. */
|
|
489
|
+
set(id: NodeId, entry: PoseOverride<TPose>): void;
|
|
490
|
+
get(id: NodeId): PoseOverride<TPose> | undefined;
|
|
491
|
+
has(id: NodeId): boolean;
|
|
492
|
+
/** The overridden ids, as a snapshot array. */
|
|
493
|
+
ids(): readonly NodeId[];
|
|
494
|
+
clear(id: NodeId): void;
|
|
495
|
+
clearAll(): void;
|
|
496
|
+
/** Publish this frame's in-place mutations: invalidate the painter memo for
|
|
497
|
+
* every overridden node, then notify subscribers. */
|
|
498
|
+
commit(): void;
|
|
499
|
+
/** Notified after every write. The canvas uses this to repaint without a
|
|
500
|
+
* scene version bump. */
|
|
501
|
+
subscribe(fn: () => void): () => void;
|
|
502
|
+
/** Monotonic write counter. A snapshot for observers that poll. */
|
|
503
|
+
getGeneration(): number;
|
|
416
504
|
}
|
|
417
505
|
/**
|
|
418
506
|
* The kit-owned scene tree: nodes, layers, and the undo history over both.
|
|
@@ -442,10 +530,19 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
442
530
|
* not a live view, and not yours to mutate. */
|
|
443
531
|
renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];
|
|
444
532
|
add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;
|
|
445
|
-
/** Delete `id
|
|
446
|
-
*
|
|
447
|
-
*
|
|
533
|
+
/** Delete `id`, its **entire subtree**, and **everything that derives from**
|
|
534
|
+
* any of those nodes — a node listing one of them in `dependsOn` goes too,
|
|
535
|
+
* along with its own subtree, transitively. A dependent can live anywhere in
|
|
536
|
+
* the tree, so this deletes nodes the caller never named and may unlink
|
|
537
|
+
* several disjoint subtrees at once. Recorded as one undoable step; `undo()`
|
|
538
|
+
* restores every one of them where it was, child order intact. */
|
|
448
539
|
remove(id: NodeId): void;
|
|
540
|
+
/** {@link remove} over several roots at once, as a **single** undoable step.
|
|
541
|
+
* Ids resolve against the tree as it stands at the call, so an id that
|
|
542
|
+
* another one would cascade away is absorbed rather than removed twice —
|
|
543
|
+
* which is what makes it safe to pass a whole selection. Throws if any id is
|
|
544
|
+
* not in the scene; an empty list does nothing and records no step. */
|
|
545
|
+
removeMany(ids: readonly NodeId[]): void;
|
|
449
546
|
update(id: NodeId, patch: {
|
|
450
547
|
data: TData;
|
|
451
548
|
}): void;
|
|
@@ -481,6 +578,9 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
481
578
|
setLayerVisible(layer: TLayer, visible: boolean): void;
|
|
482
579
|
setLayerLocked(layer: TLayer, locked: boolean): void;
|
|
483
580
|
addLayer(spec: AddLayerSpec<TLayer>): void;
|
|
581
|
+
/** Drop a user layer and every node tagged to it, as one undoable step.
|
|
582
|
+
* Removal cascades, so this also deletes nodes **on other layers** that
|
|
583
|
+
* derive from a node on this one. */
|
|
484
584
|
removeLayer(layer: TLayer): void;
|
|
485
585
|
renameLayer(layer: TLayer, name: string): void;
|
|
486
586
|
moveLayer(layer: TLayer, index: number): void;
|
|
@@ -522,6 +622,11 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
|
|
|
522
622
|
* undo step of its own. */
|
|
523
623
|
getSelection(): readonly NodeId[];
|
|
524
624
|
setSelection(ids: readonly NodeId[]): void;
|
|
625
|
+
/** Per-node pose / alpha overrides the render and hit-test paths read
|
|
626
|
+
* through. Like {@link Scene.getSelection} this is not document content:
|
|
627
|
+
* writes are never recorded, never serialized, and do not bump
|
|
628
|
+
* {@link Scene.getVersion}. See {@link PoseOverrides}. */
|
|
629
|
+
readonly overrides: PoseOverrides<TPose>;
|
|
525
630
|
undo(): boolean;
|
|
526
631
|
redo(): boolean;
|
|
527
632
|
canUndo(): boolean;
|
|
@@ -631,29 +736,11 @@ interface ViewportDims {
|
|
|
631
736
|
}
|
|
632
737
|
|
|
633
738
|
/**
|
|
634
|
-
*
|
|
635
|
-
* `WebGL2RenderingContext.uniformMatrix3fv` byte order so we can pass the
|
|
636
|
-
* array directly without a transpose flag.
|
|
637
|
-
*
|
|
638
|
-
* Layout (column-major):
|
|
639
|
-
* [m00, m10, 0,
|
|
640
|
-
* m01, m11, 0,
|
|
641
|
-
* tx, ty, 1]
|
|
642
|
-
*
|
|
643
|
-
* `apply(m, x, y)` returns `[m * (x, y, 1)] = [m00*x + m01*y + tx,
|
|
644
|
-
* m10*x + m11*y + ty]`.
|
|
645
|
-
*/
|
|
646
|
-
type Mat3 = Float32Array;
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* registerTexture — accepts an image source, assigns an opaque id, stores in
|
|
650
|
-
* a module-level registry. Actual GL upload happens lazily at draw time in
|
|
651
|
-
* drawShader() via GLTextureCache.upload (which is idempotent).
|
|
739
|
+
* A registered texture, named by the registry that holds it.
|
|
652
740
|
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
* Each WeaselRenderer's GLTextureCache does its own dedup via has(id).
|
|
741
|
+
* The handle is here rather than beside the registry because `FillStyle`'s
|
|
742
|
+
* pattern variant names it, and paint is a leaf: the registry lives in the
|
|
743
|
+
* renderer and imports this back.
|
|
657
744
|
*/
|
|
658
745
|
interface TextureHandle {
|
|
659
746
|
readonly id: string;
|
|
@@ -811,6 +898,26 @@ interface GradStop {
|
|
|
811
898
|
* typically use a stencil mask of the stroked path against the geometry.
|
|
812
899
|
*/
|
|
813
900
|
type StrokeAlign = 'center' | 'inner' | 'outer';
|
|
901
|
+
/**
|
|
902
|
+
* The kit's built-in marker vocabulary, open on the string the way
|
|
903
|
+
* `PaintKind` is so a consumer's registered key typechecks.
|
|
904
|
+
*/
|
|
905
|
+
type KitMarkerKey = 'arrow' | 'arrow-open' | 'arrow-concave' | 'diamond' | 'diamond-hollow' | 'circle' | 'square' | 'bar';
|
|
906
|
+
type MarkerKey = KitMarkerKey | (string & {});
|
|
907
|
+
/**
|
|
908
|
+
* A marker on one end (or every interior vertex) of a stroke.
|
|
909
|
+
*
|
|
910
|
+
* `size` reuses `Stroke.width`'s unit system: a bare number scales with the
|
|
911
|
+
* resolved stroke width, `{ px }` pins screen pixels. That is SVG's
|
|
912
|
+
* `markerUnits` in the idiom this codebase already resolves at draw time.
|
|
913
|
+
* Omitted, the marker is one stroke width per unit.
|
|
914
|
+
*/
|
|
915
|
+
type MarkerRef = MarkerKey | {
|
|
916
|
+
key: MarkerKey;
|
|
917
|
+
size?: number | {
|
|
918
|
+
px: number;
|
|
919
|
+
};
|
|
920
|
+
};
|
|
814
921
|
/** Stroke style: a FillStyle plus structural line parameters. */
|
|
815
922
|
interface Stroke {
|
|
816
923
|
paint: FillStyle;
|
|
@@ -822,6 +929,14 @@ interface Stroke {
|
|
|
822
929
|
};
|
|
823
930
|
/** Per `CanvasRenderingContext2D.setLineDash` — empty/omitted = solid. */
|
|
824
931
|
dash?: number[];
|
|
932
|
+
/** Marker at the first vertex of each open subpath, rotated to point back
|
|
933
|
+
* along the line (SVG's `auto-start-reverse`, as the only behavior). */
|
|
934
|
+
markerStart?: MarkerRef;
|
|
935
|
+
/** Marker at every interior authored vertex, on the bisector of the
|
|
936
|
+
* incoming and outgoing directions. Never insets the stroke. */
|
|
937
|
+
markerMid?: MarkerRef;
|
|
938
|
+
/** Marker at the last vertex of each open subpath. */
|
|
939
|
+
markerEnd?: MarkerRef;
|
|
825
940
|
cap?: 'butt' | 'round' | 'square';
|
|
826
941
|
join?: 'miter' | 'round' | 'bevel';
|
|
827
942
|
/**
|
|
@@ -864,17 +979,41 @@ interface Stroke {
|
|
|
864
979
|
varyingWidthJoinThreshold?: number;
|
|
865
980
|
}
|
|
866
981
|
|
|
982
|
+
/**
|
|
983
|
+
* 2D affine matrix utilities. Column-major 9-element Float32Array, matching
|
|
984
|
+
* `WebGL2RenderingContext.uniformMatrix3fv` byte order so we can pass the
|
|
985
|
+
* array directly without a transpose flag.
|
|
986
|
+
*
|
|
987
|
+
* Layout (column-major):
|
|
988
|
+
* [m00, m10, 0,
|
|
989
|
+
* m01, m11, 0,
|
|
990
|
+
* tx, ty, 1]
|
|
991
|
+
*
|
|
992
|
+
* `apply(m, x, y)` returns `[m * (x, y, 1)] = [m00*x + m01*y + tx,
|
|
993
|
+
* m10*x + m11*y + ty]`.
|
|
994
|
+
*/
|
|
995
|
+
type Mat3 = Float32Array;
|
|
996
|
+
|
|
867
997
|
/**
|
|
868
998
|
* Typography for `TextPose` and friends. Every field is optional; consumers
|
|
869
999
|
* pass `{}` or override the few they care about. Defaults live in
|
|
870
1000
|
* `DEFAULT_TEXT_STYLE` and are applied at render/measure time, never written
|
|
871
1001
|
* back to the pose.
|
|
872
1002
|
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
1003
|
+
* Paint is not typography and does not live here. A text node carries its
|
|
1004
|
+
* fill and stroke in `data.fill` / `data.stroke`, the slots every other node
|
|
1005
|
+
* kind uses; `resolveTextStyle` takes them as its second argument and
|
|
1006
|
+
* `StyledRun.fill` / `.stroke` override them per range.
|
|
876
1007
|
*/
|
|
877
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* Horizontal alignment. `start` / `end` resolve against the reading direction;
|
|
1011
|
+
* `left` / `right` are absolute. Same five values as CSS `text-align`, with
|
|
1012
|
+
* the same split between the relative pair and the absolute pair.
|
|
1013
|
+
*/
|
|
1014
|
+
type TextAlign = 'left' | 'center' | 'right' | 'start' | 'end';
|
|
1015
|
+
/** Reading direction, which is what gives `start` / `end` their meaning. */
|
|
1016
|
+
type TextDirection = 'ltr' | 'rtl';
|
|
878
1017
|
/** User-facing text style. All fields optional; defaults applied at render time via `resolveTextStyle`. */
|
|
879
1018
|
interface TextStyle {
|
|
880
1019
|
/** Font size in world units. Default 16. */
|
|
@@ -885,15 +1024,15 @@ interface TextStyle {
|
|
|
885
1024
|
fontWeight?: number | string;
|
|
886
1025
|
/** Default `'normal'`. */
|
|
887
1026
|
fontStyle?: 'normal' | 'italic';
|
|
888
|
-
/** Default `'left'`. */
|
|
889
|
-
align?:
|
|
1027
|
+
/** Default `'left'`. Pass `'start'` to align by reading order instead. */
|
|
1028
|
+
align?: TextAlign;
|
|
1029
|
+
/** Reading direction, resolving `align: 'start' | 'end'`. Default `'ltr'`. */
|
|
1030
|
+
direction?: TextDirection;
|
|
890
1031
|
/** Multiplier applied to `fontSize`. Default 1.2. */
|
|
891
1032
|
lineHeight?: number;
|
|
892
|
-
/** Default `{ fill: 'solid', color: '#000' }`. */
|
|
893
|
-
fill?: FillStyle;
|
|
894
1033
|
/**
|
|
895
|
-
* Caret color used by the edit overlay. Defaults to the
|
|
896
|
-
*
|
|
1034
|
+
* Caret color used by the edit overlay. Defaults to the node's fill when
|
|
1035
|
+
* that fill is solid; falls back to `#000` for non-solid paints.
|
|
897
1036
|
*/
|
|
898
1037
|
caretColor?: string;
|
|
899
1038
|
/**
|
|
@@ -910,19 +1049,8 @@ interface TextStyle {
|
|
|
910
1049
|
underline?: boolean;
|
|
911
1050
|
/** Default `false`. */
|
|
912
1051
|
strikethrough?: boolean;
|
|
913
|
-
/**
|
|
914
|
-
|
|
915
|
-
* outline — there is no such thing as a default text stroke.
|
|
916
|
-
*
|
|
917
|
-
* Only glyphs on the outline tier are stroked: above
|
|
918
|
-
* `textOutlineMinScreenSize` a glyph is a real `PolygonPath`, so it gets
|
|
919
|
-
* the ordinary tessellated ribbon with real joins, caps and miters, in any
|
|
920
|
-
* paint. Below it a glyph is a sampled distance field with no geometry to
|
|
921
|
-
* stroke, and it renders unstroked rather than approximated. `width` is in
|
|
922
|
-
* world units, like every other stroke in the kit — it does not scale with
|
|
923
|
-
* `fontSize`.
|
|
924
|
-
*/
|
|
925
|
-
stroke?: Stroke;
|
|
1052
|
+
/** Default `false`. */
|
|
1053
|
+
overline?: boolean;
|
|
926
1054
|
}
|
|
927
1055
|
|
|
928
1056
|
/**
|
|
@@ -937,10 +1065,15 @@ interface TextStyle {
|
|
|
937
1065
|
* override the node-level value (`letterSpacing: 0` on a run is an override,
|
|
938
1066
|
* not an absence — it zeroes inherited tracking).
|
|
939
1067
|
*
|
|
940
|
-
* `underline` / `strikethrough` are *additive*, like
|
|
941
|
-
* can turn a decoration on but never off, so they
|
|
942
|
-
* `run.x || style.x` and not `run.x ?? style.x`. See the header of
|
|
1068
|
+
* `underline` / `strikethrough` / `overline` are *additive*, like
|
|
1069
|
+
* `bold`/`italic`: a run can turn a decoration on but never off, so they
|
|
1070
|
+
* resolve as `run.x || style.x` and not `run.x ?? style.x`. See the header of
|
|
943
1071
|
* `runs/rangeStyle.ts` for why the model collapses the tri-state.
|
|
1072
|
+
*
|
|
1073
|
+
* `script` is folded the same way the toggles are, into the two primitives it
|
|
1074
|
+
* is a preset over: `baselineShift` and `fontScale`. Both come out as one
|
|
1075
|
+
* world-unit `baselineShift` and a final `fontSize`, so layout never learns
|
|
1076
|
+
* that superscripts exist — it places a run against a baseline and an offset.
|
|
944
1077
|
*/
|
|
945
1078
|
|
|
946
1079
|
/** A run with every style resolved against the node's text style — no
|
|
@@ -961,6 +1094,17 @@ interface ResolvedRun {
|
|
|
961
1094
|
underline: boolean;
|
|
962
1095
|
/** Draw a rule through this run's x-height. Additive over the node style. */
|
|
963
1096
|
strikethrough: boolean;
|
|
1097
|
+
/** Draw a rule above this run's ascent. Additive over the node style. */
|
|
1098
|
+
overline: boolean;
|
|
1099
|
+
/**
|
|
1100
|
+
* How far this run sits off the line's shared baseline, in world units;
|
|
1101
|
+
* positive raises. 0 for ordinary text.
|
|
1102
|
+
*
|
|
1103
|
+
* Already multiplied out against the inherited font size, and already
|
|
1104
|
+
* carrying whatever `script` asked for — layout adds it to a baseline and
|
|
1105
|
+
* asks nothing about where it came from.
|
|
1106
|
+
*/
|
|
1107
|
+
baselineShift: number;
|
|
964
1108
|
}
|
|
965
1109
|
|
|
966
1110
|
/**
|
|
@@ -979,7 +1123,7 @@ type TextVerticalAlign = 'top' | 'center' | 'bottom';
|
|
|
979
1123
|
* calls getProgramSource() and compiles the result. This keeps registerProgram
|
|
980
1124
|
* GL-context-agnostic — identical pattern to registerFont storing ImageBitmap.
|
|
981
1125
|
*
|
|
982
|
-
*
|
|
1126
|
+
* Module-level state = source strings only; compiled GL
|
|
983
1127
|
* programs live on each renderer's programRegistry (Map<id, ShaderProgram>).
|
|
984
1128
|
*
|
|
985
1129
|
* Lifecycle: program sources live for the module lifetime. No unregister in v1.
|
|
@@ -1129,4 +1273,4 @@ interface ShaderDrawCommand {
|
|
|
1129
1273
|
};
|
|
1130
1274
|
}
|
|
1131
1275
|
|
|
1132
|
-
export type { Bounds as B, DrawCommand as D, History as H, Node as N, Op as O,
|
|
1276
|
+
export type { Bounds as B, DrawCommand as D, History as H, Node as N, Op as O, Path as P, Scene as S, View as V, ViewportDims as a, PathDrawCommand as b, NodeId as c };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/** The value types a built-in pref leaf can hold. */
|
|
4
|
+
type ToolPrefKind = 'number' | 'boolean' | 'string' | 'enum' | 'color' | 'paint' | 'object';
|
|
5
|
+
interface ToolPrefBase<K extends string, Value> {
|
|
6
|
+
kind: K;
|
|
7
|
+
/** Human-readable label. */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Longer help text — shown in tooltips / a settings pane. */
|
|
10
|
+
description: string;
|
|
11
|
+
/** Fallback when nothing is persisted. */
|
|
12
|
+
default: Value;
|
|
13
|
+
/** Hide from a host app's settings UI by default. */
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
/** Render full-width with no label row in schema-driven settings UIs
|
|
16
|
+
* (weasel-ui `PrefsForm` honors this for leaves whose control brings
|
|
17
|
+
* its own chrome). */
|
|
18
|
+
block?: boolean;
|
|
19
|
+
/** Glyph naming this leaf in a host UI's icon set (weasel-ui resolves it
|
|
20
|
+
* against `ICON_PATHS`). A plain string because core ships no icon set and
|
|
21
|
+
* cannot depend on one. Read where a leaf's `name` has nowhere to go — a
|
|
22
|
+
* `pair`ed row is labeled by the pair, so its fields have only the glyph
|
|
23
|
+
* to tell them apart. */
|
|
24
|
+
icon?: string;
|
|
25
|
+
/** Row-pairing hint for compact property UIs (weasel-ui
|
|
26
|
+
* `SelectionPanel`): leaves sharing a `pair` id render side-by-side
|
|
27
|
+
* on one row labeled with the `pair` string (e.g. `'Position'` for
|
|
28
|
+
* `pose.x` / `pose.y`). Purely presentational. */
|
|
29
|
+
pair?: string;
|
|
30
|
+
}
|
|
31
|
+
/** How a schema-driven UI should present a number pref. */
|
|
32
|
+
type ToolPrefNumberControl = 'input' | 'slider';
|
|
33
|
+
/** How a schema-driven UI should present a boolean pref. */
|
|
34
|
+
type ToolPrefBooleanControl = 'checkbox' | 'switch';
|
|
35
|
+
/** How a schema-driven UI should present a string pref. */
|
|
36
|
+
type ToolPrefStringControl = 'input' | 'textarea';
|
|
37
|
+
/** How a schema-driven UI should present an enum pref. */
|
|
38
|
+
type ToolPrefEnumControl = 'select' | 'radio' | 'toggle';
|
|
39
|
+
/** Display-unit conversion for number leaves whose stored value uses a
|
|
40
|
+
* canonical unit the user shouldn't see (e.g. radians stored, degrees
|
|
41
|
+
* shown). The stored value stays canonical; UIs convert at the edge. */
|
|
42
|
+
interface ToolPrefNumberUnit {
|
|
43
|
+
toDisplay: (stored: number) => number;
|
|
44
|
+
fromDisplay: (display: number) => number;
|
|
45
|
+
/** Shown after the input, e.g. `'°'`. */
|
|
46
|
+
suffix?: string;
|
|
47
|
+
}
|
|
48
|
+
/** A numeric pref, optionally bounded and stepped, and optionally stored in a
|
|
49
|
+
* different unit from the one shown. */
|
|
50
|
+
interface ToolPrefNumber extends ToolPrefBase<'number', number> {
|
|
51
|
+
min?: number;
|
|
52
|
+
max?: number;
|
|
53
|
+
step?: number;
|
|
54
|
+
control?: ToolPrefNumberControl;
|
|
55
|
+
unit?: ToolPrefNumberUnit;
|
|
56
|
+
}
|
|
57
|
+
/** An on/off pref. */
|
|
58
|
+
interface ToolPrefBoolean extends ToolPrefBase<'boolean', boolean> {
|
|
59
|
+
control?: ToolPrefBooleanControl;
|
|
60
|
+
}
|
|
61
|
+
/** A free-text pref. */
|
|
62
|
+
interface ToolPrefString extends ToolPrefBase<'string', string> {
|
|
63
|
+
control?: ToolPrefStringControl;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Stored-value bridge for an enum leaf whose value is not the option string —
|
|
67
|
+
* the counterpart of {@link ToolPrefNumberUnit}, which does the same for a
|
|
68
|
+
* number stored in a canonical unit.
|
|
69
|
+
*
|
|
70
|
+
* A dash array is the case that needs it: `Stroke.dash` stores lengths, and
|
|
71
|
+
* the thing a person chooses is a style. The presets scale by the stroke's
|
|
72
|
+
* width, so both directions are given the object's other fields — a style is
|
|
73
|
+
* meaningless without the width it is a multiple of.
|
|
74
|
+
*/
|
|
75
|
+
interface ToolPrefEnumEncoding<T extends string = string> {
|
|
76
|
+
/**
|
|
77
|
+
* The option `stored` reads as, or `undefined` for none — which a UI shows
|
|
78
|
+
* the way it shows a mixed selection, by selecting nothing.
|
|
79
|
+
*
|
|
80
|
+
* `siblings` is the object the leaf is a field of, or `undefined` when the
|
|
81
|
+
* node does not hold that object (and for a top-level leaf, which has none).
|
|
82
|
+
*/
|
|
83
|
+
read: (stored: unknown, siblings: Record<string, unknown> | undefined) => T | undefined;
|
|
84
|
+
/** What to store for `option`. `undefined` removes the field. */
|
|
85
|
+
write: (option: T, siblings: Record<string, unknown> | undefined) => unknown;
|
|
86
|
+
}
|
|
87
|
+
/** A pref with a fixed set of labeled choices. */
|
|
88
|
+
interface ToolPrefEnum<T extends string = string> extends ToolPrefBase<'enum', T> {
|
|
89
|
+
/** `short` is the label a segmented control uses when a full one would not
|
|
90
|
+
* fit — a capital or two. `icon` names a glyph in the host UI's set
|
|
91
|
+
* (weasel-ui resolves it against `ICON_PATHS`) and outranks `short` where
|
|
92
|
+
* it resolves. It is a plain string because core ships no icon set and
|
|
93
|
+
* cannot depend on one. The full `label` stays the accessible name, so
|
|
94
|
+
* neither the abbreviation nor the glyph becomes the only thing naming
|
|
95
|
+
* the option.
|
|
96
|
+
*
|
|
97
|
+
* `disabled` marks an option a control reports but cannot author — the
|
|
98
|
+
* value a stored form reads as when it matches nothing offered. Dropping it
|
|
99
|
+
* from the list instead would leave the control selecting nothing and
|
|
100
|
+
* claiming the field is unset. */
|
|
101
|
+
options: readonly {
|
|
102
|
+
value: T;
|
|
103
|
+
label: string;
|
|
104
|
+
short?: string;
|
|
105
|
+
icon?: string;
|
|
106
|
+
disabled?: boolean;
|
|
107
|
+
}[];
|
|
108
|
+
control?: ToolPrefEnumControl;
|
|
109
|
+
encoding?: ToolPrefEnumEncoding<T>;
|
|
110
|
+
}
|
|
111
|
+
/** A single color, stored as a hex string. For a value that may also be a
|
|
112
|
+
* gradient or a pattern, use {@link ToolPrefPaint} instead. */
|
|
113
|
+
interface ToolPrefColor extends ToolPrefBase<'color', string> {
|
|
114
|
+
/** Value is `#rrggbb`, or `#rrggbbaa` when `alpha` is set (UIs then
|
|
115
|
+
* offer an opacity control). */
|
|
116
|
+
alpha?: boolean;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Open leaf: any node with a `kind` outside the built-ins. Schema-driven
|
|
120
|
+
* UIs (weasel-ui `PrefsForm` / `SelectionPanel`) dispatch it to an
|
|
121
|
+
* app-supplied renderer. Deliberately NOT index-signatured so concrete
|
|
122
|
+
* app interfaces stay assignable. Mirrors weasel-ui's `PrefCustom`.
|
|
123
|
+
*/
|
|
124
|
+
type ToolPrefCustom = ToolPrefBase<string, unknown>;
|
|
125
|
+
/**
|
|
126
|
+
* A whole `FillStyle`, not a color inside one. Use it wherever the value is
|
|
127
|
+
* the tagged paint union — a solid color, a pattern, a gradient — rather
|
|
128
|
+
* than a hex string.
|
|
129
|
+
*
|
|
130
|
+
* Addressing `…fill.color` instead reads `undefined` off a gradient (so the
|
|
131
|
+
* control shows its default and claims the text is black) and writes a
|
|
132
|
+
* hybrid `{ fill: 'gradient', stops, color }` that the renderer's structural
|
|
133
|
+
* `'color' in paint` checks then paint flat solid. The union has to be
|
|
134
|
+
* edited as a union.
|
|
135
|
+
*/
|
|
136
|
+
interface ToolPrefPaint extends ToolPrefBase<'paint', unknown> {
|
|
137
|
+
/** Offer an opacity control alongside the color. */
|
|
138
|
+
alpha?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A leaf whose value is one object, with its own fields hanging off it.
|
|
142
|
+
*
|
|
143
|
+
* A compound value — a stroke, a shadow, a pattern spec — could be described
|
|
144
|
+
* as several sibling leaves addressing into it (`data.stroke.width`,
|
|
145
|
+
* `data.stroke.cap`). It shouldn't be: each control would then write one field
|
|
146
|
+
* of a value it can only half see, and writing into something that is not an
|
|
147
|
+
* object yet corrupts it. Here the fields are `children` of one leaf, and
|
|
148
|
+
* every edit commits the parent object whole.
|
|
149
|
+
*
|
|
150
|
+
* `children` paths are relative to the object. They are ordinary leaves, so a
|
|
151
|
+
* field that is itself a union (a stroke's `paint`) declares the kind that
|
|
152
|
+
* edits that union. A child may also be a {@link ToolPrefGroup}, which
|
|
153
|
+
* organises the fields under a heading without contributing to the path —
|
|
154
|
+
* the same rule group keys follow at the top level. A `TextStyle` needs it:
|
|
155
|
+
* its character and paragraph fields belong to one value but read as two
|
|
156
|
+
* lists.
|
|
157
|
+
*/
|
|
158
|
+
interface ToolPrefObject extends ToolPrefBase<'object', unknown> {
|
|
159
|
+
children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
|
|
160
|
+
/**
|
|
161
|
+
* Lift a non-object value into the object form, for a consumer field that
|
|
162
|
+
* may also be held as a scalar. Called before a child edit is applied;
|
|
163
|
+
* without it a scalar-valued leaf shows its children empty and refuses the
|
|
164
|
+
* edit.
|
|
165
|
+
*/
|
|
166
|
+
fromScalar?: (value: unknown) => Record<string, unknown>;
|
|
167
|
+
}
|
|
168
|
+
/** One built-in pref leaf. `ToolPrefLeaf` widens this to include
|
|
169
|
+
* app-defined kinds. */
|
|
170
|
+
type ToolPref = ToolPrefNumber | ToolPrefBoolean | ToolPrefString | ToolPrefEnum | ToolPrefColor | ToolPrefPaint | ToolPrefObject;
|
|
171
|
+
/** Built-in or app-defined leaf. */
|
|
172
|
+
type ToolPrefLeaf = ToolPref | ToolPrefCustom;
|
|
173
|
+
/** Nestable group: branch nodes a tool can use to organize its prefs. */
|
|
174
|
+
interface ToolPrefGroup {
|
|
175
|
+
/** Heading for the group's rows. **Empty means no heading** — for a group
|
|
176
|
+
* that exists to organise, not to name: one whose children are themselves
|
|
177
|
+
* groups carrying the labels a reader needs. Give it a name whenever the
|
|
178
|
+
* name is the referent (a `Border` group over `Top` / `Right` / `Bottom`
|
|
179
|
+
* reads as nothing without it). */
|
|
180
|
+
name: string;
|
|
181
|
+
description?: string;
|
|
182
|
+
children: Record<string, ToolPrefLeaf | ToolPrefGroup>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** What a {@link PrefRenderer} is given for the leaf it is rendering. */
|
|
186
|
+
interface PrefRenderContext {
|
|
187
|
+
/** Dotted path of the leaf within the schema root. */
|
|
188
|
+
path: string;
|
|
189
|
+
/** The schema node. App renderers narrow this to their own kind shape. */
|
|
190
|
+
pref: ToolPrefLeaf;
|
|
191
|
+
/** Current value — `values` at `path`, falling back to `pref.default`. */
|
|
192
|
+
value: unknown;
|
|
193
|
+
setValue: (value: unknown) => void;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Renders the control cell for one preference leaf. Returning `null`
|
|
197
|
+
* collapses the row.
|
|
198
|
+
*/
|
|
199
|
+
type PrefRenderer = (ctx: PrefRenderContext) => ReactNode;
|
|
200
|
+
|
|
201
|
+
export type { PrefRenderer as P, ToolPrefLeaf as T, ToolPrefGroup as a, ToolPref as b, ToolPrefBoolean as c, ToolPrefBooleanControl as d, ToolPrefColor as e, ToolPrefCustom as f, ToolPrefEnum as g, ToolPrefEnumControl as h, ToolPrefEnumEncoding as i, ToolPrefKind as j, ToolPrefNumber as k, ToolPrefNumberControl as l, ToolPrefNumberUnit as m, ToolPrefObject as n, ToolPrefPaint as o, PrefRenderContext as p, ToolPrefString as q, ToolPrefStringControl as r };
|