gesso-framework 0.7.0 → 0.8.1
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 +49 -0
- package/dist/{FunctionComponent-Czkex8fp.d.ts → FunctionComponent-BDVrANP0.d.ts} +14 -2
- package/dist/WorkerPorts-m_hLRoPj.js.map +1 -1
- package/dist/agent/index.d.ts +3 -1
- package/dist/agent/index.js +4 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/{computed-_cYcdczA.js → computed-DyW9SPpq.js} +12 -2
- package/dist/computed-DyW9SPpq.js.map +1 -0
- package/dist/{index-BENndtaH.d.ts → index-C08h2Jze.d.ts} +451 -29
- package/dist/index.d.ts +4 -4
- package/dist/index.js +207 -22
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +1 -1
- package/dist/{persisted-8meJmsOh.js → persisted-qEBOHDnj.js} +2 -2
- package/dist/{persisted-8meJmsOh.js.map → persisted-qEBOHDnj.js.map} +1 -1
- package/dist/remote/index.d.ts +2 -2
- package/dist/{render-cydQ10Nd.js → render-DlhL5y0d.js} +810 -190
- package/dist/render-DlhL5y0d.js.map +1 -0
- package/dist/{tools-BFBwuinE.d.ts → tools-Bl8LZ7LR.d.ts} +2 -2
- package/dist/worker/index.d.ts +3 -3
- package/dist/worker/index.js +2 -2
- package/package.json +2 -2
- package/dist/computed-_cYcdczA.js.map +0 -1
- package/dist/render-cydQ10Nd.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as InternalState,
|
|
1
|
+
import { a as InternalState, c as forceInput, d as isOutputTarget, h as withBodyOf, l as input, n as computed, o as internalState, p as outputTargetOf, s as InputCell } from "./computed-DyW9SPpq.js";
|
|
2
2
|
import { f as formatNodePath, g as printPropValue, r as isPortErrorMessage, u as describeStream, v as applyPatches, w as viewKeys, x as isChannelHostMessage } from "./WorkerPorts-m_hLRoPj.js";
|
|
3
3
|
import { n as getComponentMetadata, t as createComponent } from "./createComponent-DJads88q.js";
|
|
4
4
|
import { m as designedTheme } from "./app-De3z58x7.js";
|
|
5
|
-
import { EMPTY, Subject, Subscription, map } from "rxjs";
|
|
6
|
-
import { AnimationDriver, Box, Canvas2DRenderer, CanvasTextMeasurer, Column, Constraints, DefaultImageResolver, DefaultSvgResolver, DefaultVideoResolver, DirtyFlags, EnvironmentNotifier, FocusNotifier, IconRasterizer, LayoutEngine, LayoutInspector, LayoutNotifier, Stack, TEXT_RUN_ID_SEPARATOR, UiEditingController, UiEnvironmentKeys, UiEventType, UiFindController, UiFocusManager, UiFrame, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiInputDispatcher, UiInsetRegistry, UiKeyboardController, UiManualFrameClock, UiNodeType, UiPointerController, UiPointerEvent, UiScheduler, UiSelectionController, UiSharedElements, UiSpring, UiTimerFrameClock, UiTouchScroller, UiVirtualSheet, UiVirtualWindow, UiWheelController, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPURenderer, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, clearOverrideProperty, createCanvasSurface, createTween, createWebGPUSurface, defaultMotion, defineModifier, describeOverrides, diffSemantics, dragSessionFor, dropIndexShifts,
|
|
5
|
+
import { EMPTY, Subject, Subscription, concat, map, of, skip } from "rxjs";
|
|
6
|
+
import { AnimationDriver, Box, Canvas2DRenderer, CanvasTextMeasurer, Column, Constraints, DefaultImageResolver, DefaultSvgResolver, DefaultVideoResolver, DirtyFlags, EnvironmentNotifier, FocusNotifier, IconRasterizer, LayoutEngine, LayoutInspector, LayoutNotifier, Stack, TEXT_RUN_ID_SEPARATOR, Text, UiEditingController, UiEnvironmentKeys, UiEventType, UiFindController, UiFocusManager, UiFrame, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiInputDispatcher, UiInsetRegistry, UiKeyboardController, UiManualFrameClock, UiNodeType, UiPointerController, UiPointerEvent, UiScheduler, UiSelectionController, UiSharedElements, UiSpring, UiTimerFrameClock, UiTouchScroller, UiVirtualSheet, UiVirtualWindow, UiWheelController, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPURenderer, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, clearOverrideProperty, clockOf, createCanvasSurface, createEnvironmentKey, createTween, createWebGPUSurface, defaultMotion, defineModifier, describeOverrides, diffSemantics, dragSessionFor, dropIndexShifts, durationIn, easingIn, fade, findEnvironmentKey, formatExplanation, insetsEqual, isComponentLikeElement, isObservable, isUiElement, isWebGPUAvailable, lightTheme, linear, linkOf, measure, mediaLoadsInFlight, motion, noInsets, propertyEffects, provided, recordsEqual, registerFontStack, resolveCursor, rewalkSemantics, scrollBounds, scrollRange, scrollbarThumb, scrollsFromRight, semanticsInertAbove, semanticsMemory, setPerformanceMarks, springIn, textRunOfRecordId, writeOverrideProperty } from "gesso-core";
|
|
7
7
|
//#region src/Component.ts
|
|
8
8
|
/**
|
|
9
9
|
* Base class for all Gesso framework components.
|
|
@@ -161,6 +161,33 @@ function bounds(label) {
|
|
|
161
161
|
return cell;
|
|
162
162
|
}
|
|
163
163
|
//#endregion
|
|
164
|
+
//#region src/liveInput.ts
|
|
165
|
+
/** Where the component whose body is running keeps its live inputs, while it runs. */
|
|
166
|
+
let target = null;
|
|
167
|
+
/** Runs a component's body with its live inputs going to `into`; see `liveInput`. */
|
|
168
|
+
function withLiveInputs(into, run) {
|
|
169
|
+
const previous = target;
|
|
170
|
+
target = into;
|
|
171
|
+
try {
|
|
172
|
+
return run();
|
|
173
|
+
} finally {
|
|
174
|
+
target = previous;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Says what an input of the component whose body is running holds now,
|
|
179
|
+
* as the component keeps it, and how to read it again: a switch turned
|
|
180
|
+
* on, a field typed in, a list scrolled to 600. The inspector reports it
|
|
181
|
+
* on the component's owner report (`live`), and a design tool saves a
|
|
182
|
+
* moment of a played screen from it, to start the screen there again
|
|
183
|
+
* (`StartingInputs`). `controlled` calls it for its value, so a control
|
|
184
|
+
* written by its contract reports itself; outside a component's body it
|
|
185
|
+
* does nothing.
|
|
186
|
+
*/
|
|
187
|
+
function liveInput(name, read) {
|
|
188
|
+
target?.set(name, read);
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
164
191
|
//#region src/decorators.ts
|
|
165
192
|
/**
|
|
166
193
|
* Declares a class as a Gesso component.
|
|
@@ -455,6 +482,7 @@ function isClassComponent(component) {
|
|
|
455
482
|
* declared the entry.
|
|
456
483
|
*/
|
|
457
484
|
function physicalPlacement(placement, rtl) {
|
|
485
|
+
if (placement === "over") return placement;
|
|
458
486
|
const [side, align] = placement.split("-");
|
|
459
487
|
if (side === "top" || side === "bottom") {
|
|
460
488
|
if (align === void 0 || !rtl) return placement;
|
|
@@ -498,6 +526,149 @@ var OverlayService = class {
|
|
|
498
526
|
return this.entries.value.some((entry) => entry.id === id);
|
|
499
527
|
}
|
|
500
528
|
};
|
|
529
|
+
/**
|
|
530
|
+
* The overlays of the `OverlayScope` a node is in: where an entry
|
|
531
|
+
* opened there goes instead of the runtime's layer. Null outside every
|
|
532
|
+
* scope.
|
|
533
|
+
*/
|
|
534
|
+
const OverlayScopeKey = createEnvironmentKey({
|
|
535
|
+
name: "gesso.overlayScope",
|
|
536
|
+
defaultValue: null
|
|
537
|
+
});
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/app/FocusService.ts
|
|
540
|
+
/**
|
|
541
|
+
* Keyboard focus, as a store components can inject.
|
|
542
|
+
*
|
|
543
|
+
* `UiFocusManager` lives in the runtime and a component cannot reach
|
|
544
|
+
* it — components reach the world through stores, as they do for the
|
|
545
|
+
* clipboard (`ShellService`), overlays (`OverlayService`) and find
|
|
546
|
+
* (`FindService`). Without this a component cannot autofocus a field,
|
|
547
|
+
* trap the keyboard in a dialog, or put the caret in the input that
|
|
548
|
+
* failed validation.
|
|
549
|
+
*
|
|
550
|
+
* Nodes come from a `ref` prop. A tree's refs fire before the runtime
|
|
551
|
+
* installs the manager, so an action taken during the first build is
|
|
552
|
+
* queued and replayed once there is one; that is the same problem
|
|
553
|
+
* `FindService` solves for its query field, and the reason `autoFocus`
|
|
554
|
+
* in a dialog works on the frame it mounts.
|
|
555
|
+
*
|
|
556
|
+
* It must stay on the render thread: its actions take `UiNode`s, which
|
|
557
|
+
* never cross a worker boundary.
|
|
558
|
+
*/
|
|
559
|
+
/** The nodes a trap taken inside is modal only within: each `OverlayScope`'s. */
|
|
560
|
+
const focusBounds = /* @__PURE__ */ new WeakSet();
|
|
561
|
+
/** Makes a node one, for `OverlayScope`. */
|
|
562
|
+
function boundFocus(node) {
|
|
563
|
+
focusBounds.add(node);
|
|
564
|
+
}
|
|
565
|
+
var FocusService = class {
|
|
566
|
+
/** The node holding focus, or null. A control binds its focus ring to this. */
|
|
567
|
+
focused = internalState(null);
|
|
568
|
+
/** Whether focus is confined to a subtree by an open trap. */
|
|
569
|
+
trapped = internalState(false);
|
|
570
|
+
/**
|
|
571
|
+
* Whether the focus held is focus the keyboard can see: reached by Tab
|
|
572
|
+
* or an arrow key, or by code with no pointer since, rather than by a
|
|
573
|
+
* press. CSS's `:focus-visible`, for something other than a ring that
|
|
574
|
+
* should answer the keyboard and not the click that happened to focus
|
|
575
|
+
* the same control: a tooltip, say.
|
|
576
|
+
*/
|
|
577
|
+
focusVisible = internalState(false);
|
|
578
|
+
manager = null;
|
|
579
|
+
detach = null;
|
|
580
|
+
/** Actions taken before the runtime installed a manager, in order. */
|
|
581
|
+
queued = [];
|
|
582
|
+
/** Installed by the runtime; without one every action is queued. */
|
|
583
|
+
setManager(manager) {
|
|
584
|
+
this.detach?.();
|
|
585
|
+
this.detach = null;
|
|
586
|
+
this.manager = manager;
|
|
587
|
+
if (manager === null) {
|
|
588
|
+
this.queued = [];
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const focusChange = manager.onFocusChange((node) => {
|
|
592
|
+
this.focused.value = node;
|
|
593
|
+
this.focusVisible.value = manager.focusVisible;
|
|
594
|
+
this.trapped.value = manager.trapped;
|
|
595
|
+
});
|
|
596
|
+
manager.setFocusBounds((node) => focusBounds.has(node));
|
|
597
|
+
const scopeChange = manager.onScopeChange(() => {
|
|
598
|
+
this.trapped.value = manager.trapped;
|
|
599
|
+
});
|
|
600
|
+
this.detach = () => {
|
|
601
|
+
focusChange();
|
|
602
|
+
scopeChange();
|
|
603
|
+
};
|
|
604
|
+
const pending = this.queued;
|
|
605
|
+
this.queued = [];
|
|
606
|
+
for (const action of pending) action(manager);
|
|
607
|
+
this.sync(manager);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Gives the node keyboard focus. Non-focusable nodes are ignored.
|
|
611
|
+
*
|
|
612
|
+
* The node is scrolled into view, unless `options.preventScroll` asks
|
|
613
|
+
* for the page to stay where it is, as `element.focus({ preventScroll:
|
|
614
|
+
* true })` does in a browser.
|
|
615
|
+
*/
|
|
616
|
+
focus(node, options) {
|
|
617
|
+
this.run((manager) => manager.focus(node, "program", options));
|
|
618
|
+
}
|
|
619
|
+
/** Drops focus without moving it anywhere. */
|
|
620
|
+
blur() {
|
|
621
|
+
this.run((manager) => manager.blur());
|
|
622
|
+
}
|
|
623
|
+
/** Moves focus to the next focusable node, wrapping around. */
|
|
624
|
+
focusNext() {
|
|
625
|
+
this.run((manager) => manager.focusNext());
|
|
626
|
+
}
|
|
627
|
+
/** Moves focus to the previous focusable node, wrapping around. */
|
|
628
|
+
focusPrevious() {
|
|
629
|
+
this.run((manager) => manager.focusPrevious());
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* The tab stops under `within` (the whole tree when not given), in
|
|
633
|
+
* the order Tab visits them: what a design tool numbers to show a
|
|
634
|
+
* screen's keyboard order. Empty before the runtime has a manager.
|
|
635
|
+
*/
|
|
636
|
+
tabStops(within) {
|
|
637
|
+
return this.manager?.tabStops(within) ?? [];
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Confines focus to `scope` until `releaseTrap`, moving it inside if
|
|
641
|
+
* it was elsewhere. Traps nest: a dialog opened over a dialog traps
|
|
642
|
+
* again, and each release restores its own opener.
|
|
643
|
+
*/
|
|
644
|
+
trap(scope) {
|
|
645
|
+
this.run((manager) => manager.pushScope(scope));
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Ends the innermost trap and returns focus to whatever held it when
|
|
649
|
+
* the trap was taken — the button that opened the dialog. Given the
|
|
650
|
+
* node trapped in, only that trap ends, and nothing when it ended
|
|
651
|
+
* already: a trap's subtree removed releases it, and a release after
|
|
652
|
+
* that would otherwise end the trap under it.
|
|
653
|
+
*/
|
|
654
|
+
releaseTrap(scope) {
|
|
655
|
+
this.run((manager) => manager.popScope(scope));
|
|
656
|
+
}
|
|
657
|
+
run(action) {
|
|
658
|
+
const manager = this.manager;
|
|
659
|
+
if (manager === null) {
|
|
660
|
+
this.queued.push(action);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
action(manager);
|
|
664
|
+
this.sync(manager);
|
|
665
|
+
}
|
|
666
|
+
sync(manager) {
|
|
667
|
+
this.focused.value = manager.focusedNode;
|
|
668
|
+
this.focusVisible.value = manager.focusVisible;
|
|
669
|
+
this.trapped.value = manager.trapped;
|
|
670
|
+
}
|
|
671
|
+
};
|
|
501
672
|
//#endregion
|
|
502
673
|
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorate.js
|
|
503
674
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -509,7 +680,12 @@ function __decorate(decorators, target, key, desc) {
|
|
|
509
680
|
//#endregion
|
|
510
681
|
//#region src/overlay/OverlayLayer.ts
|
|
511
682
|
let OverlayLayer = class OverlayLayer extends Component {
|
|
512
|
-
|
|
683
|
+
runtimeOverlays;
|
|
684
|
+
/** The entries to draw, when not the runtime's: an `OverlayScope`'s. */
|
|
685
|
+
service = input(null);
|
|
686
|
+
get overlays() {
|
|
687
|
+
return this.service.value ?? this.runtimeOverlays;
|
|
688
|
+
}
|
|
513
689
|
render() {
|
|
514
690
|
return Box({
|
|
515
691
|
position: "absolute",
|
|
@@ -547,6 +723,7 @@ let OverlayLayer = class OverlayLayer extends Component {
|
|
|
547
723
|
top: entry.top,
|
|
548
724
|
bottom: entry.bottom,
|
|
549
725
|
zIndex: entry.zIndex,
|
|
726
|
+
...entry.placement === "over" && entry.anchor != null ? { hitTestable: false } : {},
|
|
550
727
|
...centering(entry),
|
|
551
728
|
...physicalEdges(entry, readsRightToLeft(environment)),
|
|
552
729
|
...inheritedFrom(environment),
|
|
@@ -556,7 +733,8 @@ let OverlayLayer = class OverlayLayer extends Component {
|
|
|
556
733
|
return elements;
|
|
557
734
|
}
|
|
558
735
|
};
|
|
559
|
-
__decorate([Inject(OverlayService)], OverlayLayer.prototype, "
|
|
736
|
+
__decorate([Inject(OverlayService)], OverlayLayer.prototype, "runtimeOverlays", void 0);
|
|
737
|
+
__decorate([Input()], OverlayLayer.prototype, "service", void 0);
|
|
560
738
|
OverlayLayer = __decorate([Define("gesso-overlay-layer")], OverlayLayer);
|
|
561
739
|
/**
|
|
562
740
|
* The dim over the page, inside the backdrop rather than on it.
|
|
@@ -646,9 +824,31 @@ function inheritedFrom(node) {
|
|
|
646
824
|
theme: environment.get(UiEnvironmentKeys.theme),
|
|
647
825
|
textStyle: environment.get(UiEnvironmentKeys.textStyle),
|
|
648
826
|
color: environment.get(UiEnvironmentKeys.color),
|
|
649
|
-
contentColor: environment.get(UiEnvironmentKeys.contentColor)
|
|
827
|
+
contentColor: environment.get(UiEnvironmentKeys.contentColor),
|
|
828
|
+
provide: providedFrom(environment)
|
|
650
829
|
};
|
|
651
830
|
}
|
|
831
|
+
/** The built-in keys an entry's box carries by props of its own, or leaves to where it is drawn. */
|
|
832
|
+
const CARRIED_BY_PROPS = new Set(Object.values(UiEnvironmentKeys).map((key) => key.name).filter((name) => name !== UiEnvironmentKeys.clock.name && name !== UiEnvironmentKeys.motion.name));
|
|
833
|
+
/**
|
|
834
|
+
* The rest of what the entry was opened under, re-provided: the clock
|
|
835
|
+
* its animations run on, so a dialog opened on a settled frame is at
|
|
836
|
+
* rest there and one opened in a slowed player slows, the motion its
|
|
837
|
+
* names mean, and every key the
|
|
838
|
+
* application provides itself, so a component in a dialog reads with
|
|
839
|
+
* `ctx.environment` what the one that opened it reads.
|
|
840
|
+
*/
|
|
841
|
+
function providedFrom(environment) {
|
|
842
|
+
const out = [];
|
|
843
|
+
const seen = /* @__PURE__ */ new Set();
|
|
844
|
+
for (let at = environment; at !== null; at = at.parent) for (const name of at.providedKeys()) {
|
|
845
|
+
if (seen.has(name) || CARRIED_BY_PROPS.has(name)) continue;
|
|
846
|
+
seen.add(name);
|
|
847
|
+
const key = findEnvironmentKey(name);
|
|
848
|
+
if (key !== void 0) out.push(provided(key, environment.get(key)));
|
|
849
|
+
}
|
|
850
|
+
return out;
|
|
851
|
+
}
|
|
652
852
|
/**
|
|
653
853
|
* The same values again whenever they change where the entry was
|
|
654
854
|
* declared, for as long as it is open.
|
|
@@ -668,6 +868,7 @@ const followEnvironmentKind = defineModifier({
|
|
|
668
868
|
host.set("textStyle", textStyle);
|
|
669
869
|
host.set("color", host.environment(UiEnvironmentKeys.color, from));
|
|
670
870
|
host.set("contentColor", host.environment(UiEnvironmentKeys.contentColor, from));
|
|
871
|
+
if (from.environment !== null) host.set("provide", providedFrom(from.environment));
|
|
671
872
|
if (entry !== null) {
|
|
672
873
|
const edges = physicalEdges(entry, textStyle.textDirection === "rtl");
|
|
673
874
|
host.set("placement", edges.placement);
|
|
@@ -684,6 +885,36 @@ function followEnvironment(node, entry) {
|
|
|
684
885
|
})];
|
|
685
886
|
}
|
|
686
887
|
//#endregion
|
|
888
|
+
//#region src/startingInputsKey.ts
|
|
889
|
+
/**
|
|
890
|
+
* The inputs components under a node start with, by component name,
|
|
891
|
+
* over what their parents pass; see `StartingInputs`. Empty outside one.
|
|
892
|
+
*/
|
|
893
|
+
const StartingInputsKey = createEnvironmentKey({
|
|
894
|
+
name: "gesso.startingInputs",
|
|
895
|
+
defaultValue: {}
|
|
896
|
+
});
|
|
897
|
+
/** How many of each name have been made under each set of starting inputs, for a list's next. */
|
|
898
|
+
const made = /* @__PURE__ */ new WeakMap();
|
|
899
|
+
/** What a component named `name`, made at `anchor`, starts with: null for nothing. */
|
|
900
|
+
function startingInputsFor(anchor, name) {
|
|
901
|
+
for (let at = anchor; at !== null; at = at.parent) if (at.environment !== null) {
|
|
902
|
+
const all = at.environment.get(StartingInputsKey);
|
|
903
|
+
const given = all[name];
|
|
904
|
+
if (given === void 0) return null;
|
|
905
|
+
if (!Array.isArray(given)) return given;
|
|
906
|
+
let counts = made.get(all);
|
|
907
|
+
if (counts === void 0) {
|
|
908
|
+
counts = /* @__PURE__ */ new Map();
|
|
909
|
+
made.set(all, counts);
|
|
910
|
+
}
|
|
911
|
+
const index = counts.get(name) ?? 0;
|
|
912
|
+
counts.set(name, index + 1);
|
|
913
|
+
return given[index] ?? null;
|
|
914
|
+
}
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
//#endregion
|
|
687
918
|
//#region src/ComponentHost.ts
|
|
688
919
|
/**
|
|
689
920
|
* Owns a single component instance and its lifecycle.
|
|
@@ -710,6 +941,7 @@ function followEnvironment(node, entry) {
|
|
|
710
941
|
var ComponentHost = class {
|
|
711
942
|
services;
|
|
712
943
|
channels;
|
|
944
|
+
anchor;
|
|
713
945
|
/** The class instance; undefined for a functional component. */
|
|
714
946
|
instance;
|
|
715
947
|
element;
|
|
@@ -731,14 +963,34 @@ var ComponentHost = class {
|
|
|
731
963
|
inputSubscriptions = /* @__PURE__ */ new Map();
|
|
732
964
|
/** A functional component's cells, by prop name, as they were handed out. */
|
|
733
965
|
functionalCells = /* @__PURE__ */ new Map();
|
|
966
|
+
/**
|
|
967
|
+
* Inputs it is drawn with over its parent's, from a `StartingInputs`
|
|
968
|
+
* above it when it was made: a moment of a screen, a tour's second
|
|
969
|
+
* step. A preview goes over these, and ending one comes back to them.
|
|
970
|
+
*/
|
|
971
|
+
started;
|
|
972
|
+
/** What the parent passed for each started input at birth: a new value from it takes over. */
|
|
973
|
+
startedOver = /* @__PURE__ */ new Map();
|
|
974
|
+
/** A started input the parent passes as a stream: the start, then the stream's changes; one per input, so it is not resubscribed. */
|
|
975
|
+
startedStreams = /* @__PURE__ */ new Map();
|
|
976
|
+
/** What the component says it holds now, by input; see `liveInput`. */
|
|
977
|
+
live = /* @__PURE__ */ new Map();
|
|
978
|
+
/** Inputs a tool draws the component with over its parent's, until it ends them; see `previewInputs`. */
|
|
979
|
+
previewed = null;
|
|
980
|
+
/** A class's input values from before they were previewed, for the ones its parent does not pass. */
|
|
981
|
+
beforePreview = /* @__PURE__ */ new Map();
|
|
734
982
|
mountHooks = [];
|
|
735
983
|
unmountHooks = [];
|
|
736
984
|
/** True only while the component function runs; hooks may register then. */
|
|
737
985
|
rendering = false;
|
|
738
|
-
constructor(element, services = new ServiceRegistry(), channels = new ChannelRegistry()) {
|
|
986
|
+
constructor(element, services = new ServiceRegistry(), channels = new ChannelRegistry(), anchor = null) {
|
|
739
987
|
this.services = services;
|
|
740
988
|
this.channels = channels;
|
|
989
|
+
this.anchor = anchor;
|
|
741
990
|
this.element = element;
|
|
991
|
+
const started = startingInputsFor(anchor, element.tag);
|
|
992
|
+
this.started = started === null ? null : { ...started };
|
|
993
|
+
for (const name of Object.keys(this.started ?? {})) this.startedOver.set(name, element.props[name]);
|
|
742
994
|
if (isClassComponent(element.component)) {
|
|
743
995
|
this.instance = new element.component();
|
|
744
996
|
this.validateInputs();
|
|
@@ -793,14 +1045,68 @@ var ComponentHost = class {
|
|
|
793
1045
|
this.wireInputs();
|
|
794
1046
|
return;
|
|
795
1047
|
}
|
|
796
|
-
for (const [name, cell] of this.functionalCells) this.applyInput(name, cell,
|
|
1048
|
+
for (const [name, cell] of this.functionalCells) this.applyInput(name, cell, this.providedFor(name), true);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Feeds the inputs `values` names from them instead of from the
|
|
1052
|
+
* parent, until a call with null hands them back to the parent: a
|
|
1053
|
+
* design tool showing one of a component's states on the live tree.
|
|
1054
|
+
* A later call replaces the earlier one; a new prop from the parent
|
|
1055
|
+
* for a previewed input waits until the preview ends.
|
|
1056
|
+
*
|
|
1057
|
+
* Each input a preview starts or ends is handed its value even when
|
|
1058
|
+
* it holds that value already, as a new value from the parent is: a
|
|
1059
|
+
* control that owns what it was started at (`controlled`) starts
|
|
1060
|
+
* again from it, rather than keeping what a person turned it to.
|
|
1061
|
+
*/
|
|
1062
|
+
previewInputs(values) {
|
|
1063
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(this.previewed ?? {}), ...Object.keys(values ?? {})]);
|
|
1064
|
+
if (this.instance !== void 0) {
|
|
1065
|
+
const cells = this.instance;
|
|
1066
|
+
const inputs = new Set(getComponentMetadata(this.element.component).inputs);
|
|
1067
|
+
for (const name of Object.keys(values ?? {})) if (inputs.has(name) && !this.beforePreview.has(name)) this.beforePreview.set(name, cells[name].getValue());
|
|
1068
|
+
this.previewed = values;
|
|
1069
|
+
this.wireInputs(names);
|
|
1070
|
+
for (const [name, value] of this.beforePreview) if (values === null || !Object.hasOwn(values, name)) {
|
|
1071
|
+
this.beforePreview.delete(name);
|
|
1072
|
+
if (this.providedFor(name) === void 0) cells[name].next(value);
|
|
1073
|
+
}
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
this.previewed = values;
|
|
1077
|
+
for (const name of names) {
|
|
1078
|
+
const cell = this.functionalCells.get(name);
|
|
1079
|
+
if (cell !== void 0) this.applyInput(name, cell, this.providedFor(name), true, true);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/** What an input is fed from: a preview's value for it, else what it was started with, else the parent's prop. */
|
|
1083
|
+
providedFor(name) {
|
|
1084
|
+
if (this.previewed !== null && Object.hasOwn(this.previewed, name)) return this.previewed[name];
|
|
1085
|
+
const parent = this.element.props[name];
|
|
1086
|
+
if (this.started !== null && Object.hasOwn(this.started, name)) {
|
|
1087
|
+
if (parent !== this.startedOver.get(name)) {
|
|
1088
|
+
delete this.started[name];
|
|
1089
|
+
this.startedStreams.delete(name);
|
|
1090
|
+
return parent;
|
|
1091
|
+
}
|
|
1092
|
+
if (isObservable(parent)) {
|
|
1093
|
+
let stream = this.startedStreams.get(name);
|
|
1094
|
+
if (stream === void 0) {
|
|
1095
|
+
stream = concat(of(this.started[name]), parent.pipe(skip(1)));
|
|
1096
|
+
this.startedStreams.set(name, stream);
|
|
1097
|
+
}
|
|
1098
|
+
return stream;
|
|
1099
|
+
}
|
|
1100
|
+
return this.started[name];
|
|
1101
|
+
}
|
|
1102
|
+
return this.element.props[name];
|
|
797
1103
|
}
|
|
798
1104
|
renderFunction() {
|
|
799
1105
|
const component = this.element.component;
|
|
800
1106
|
const context = this.createContext();
|
|
801
1107
|
this.rendering = true;
|
|
802
1108
|
try {
|
|
803
|
-
return withBodyOf(this.element.tag, () => component(this.createInputRecord(), context));
|
|
1109
|
+
return withLiveInputs(this.live, () => withBodyOf(this.element.tag, () => component(this.createInputRecord(), context)));
|
|
804
1110
|
} finally {
|
|
805
1111
|
this.rendering = false;
|
|
806
1112
|
}
|
|
@@ -820,7 +1126,7 @@ var ComponentHost = class {
|
|
|
820
1126
|
cell = new InputCell(void 0);
|
|
821
1127
|
cell.label = `${this.element.tag}.${name}`;
|
|
822
1128
|
this.functionalCells.set(name, cell);
|
|
823
|
-
this.applyInput(name, cell, this.
|
|
1129
|
+
this.applyInput(name, cell, this.providedFor(name), true);
|
|
824
1130
|
}
|
|
825
1131
|
return cell;
|
|
826
1132
|
};
|
|
@@ -863,18 +1169,25 @@ var ComponentHost = class {
|
|
|
863
1169
|
const cell = bounds(label ?? `${this.element.tag}.bounds`);
|
|
864
1170
|
this.subscriptions.add(() => cell.complete());
|
|
865
1171
|
return cell;
|
|
1172
|
+
},
|
|
1173
|
+
environment: (key) => {
|
|
1174
|
+
for (let at = this.anchor; at !== null; at = at.parent) if (at.environment !== null) return at.environment.get(key);
|
|
1175
|
+
return key.defaultValue;
|
|
866
1176
|
}
|
|
867
1177
|
};
|
|
868
1178
|
}
|
|
869
|
-
wireInputs() {
|
|
1179
|
+
wireInputs(forced = /* @__PURE__ */ new Set()) {
|
|
870
1180
|
const metadata = getComponentMetadata(this.element.component);
|
|
871
|
-
const props = this.element.props;
|
|
872
1181
|
for (const inputName of metadata.inputs) {
|
|
873
1182
|
const cell = this.instance[inputName];
|
|
874
1183
|
cell.label ??= `${metadata.tag}.${inputName}`;
|
|
875
|
-
this.applyInput(inputName, cell,
|
|
1184
|
+
this.applyInput(inputName, cell, this.providedFor(inputName), false, forced.has(inputName));
|
|
876
1185
|
}
|
|
877
1186
|
}
|
|
1187
|
+
/** What the component says each of its inputs holds now; see `liveInput`. */
|
|
1188
|
+
liveInputs() {
|
|
1189
|
+
return new Map([...this.live].map(([name, read]) => [name, read()]));
|
|
1190
|
+
}
|
|
878
1191
|
/**
|
|
879
1192
|
* What the parent most recently supplied for each input, as given:
|
|
880
1193
|
* a value, an element or a stream, whether or not the component has
|
|
@@ -894,8 +1207,8 @@ var ComponentHost = class {
|
|
|
894
1207
|
* fallback)` supplies one — so a prop the parent stops passing is
|
|
895
1208
|
* pushed through as `undefined`, which re-applies the fallback.
|
|
896
1209
|
*/
|
|
897
|
-
applyInput(inputName, cell, provided, resetWhenAbsent) {
|
|
898
|
-
if (this.inputSources.has(inputName) && this.inputSources.get(inputName) === provided) return;
|
|
1210
|
+
applyInput(inputName, cell, provided, resetWhenAbsent, force = false) {
|
|
1211
|
+
if (!force && this.inputSources.has(inputName) && this.inputSources.get(inputName) === provided) return;
|
|
899
1212
|
const previous = this.inputSubscriptions.get(inputName);
|
|
900
1213
|
if (previous !== void 0) {
|
|
901
1214
|
previous.unsubscribe();
|
|
@@ -914,12 +1227,19 @@ var ComponentHost = class {
|
|
|
914
1227
|
return;
|
|
915
1228
|
}
|
|
916
1229
|
if (isObservable(provided)) {
|
|
917
|
-
|
|
1230
|
+
let first = force;
|
|
1231
|
+
const subscription = provided.subscribe((value) => {
|
|
1232
|
+
if (first) {
|
|
1233
|
+
first = false;
|
|
1234
|
+
forceInput(cell, value);
|
|
1235
|
+
} else pushInput(cell, value);
|
|
1236
|
+
});
|
|
918
1237
|
this.inputSubscriptions.set(inputName, subscription);
|
|
919
1238
|
this.subscriptions.add(subscription);
|
|
920
1239
|
return;
|
|
921
1240
|
}
|
|
922
|
-
|
|
1241
|
+
if (force) forceInput(cell, provided);
|
|
1242
|
+
else pushInput(cell, provided);
|
|
923
1243
|
}
|
|
924
1244
|
validateInputs() {
|
|
925
1245
|
const metadata = getComponentMetadata(this.element.component);
|
|
@@ -988,7 +1308,7 @@ var ComponentHostResolver = class {
|
|
|
988
1308
|
this.services = services;
|
|
989
1309
|
this.channels = channels;
|
|
990
1310
|
}
|
|
991
|
-
resolve(element, anchorId) {
|
|
1311
|
+
resolve(element, anchorId, anchor) {
|
|
992
1312
|
let host = this.hosts.get(anchorId);
|
|
993
1313
|
if (host !== void 0 && host.component !== element.component) {
|
|
994
1314
|
host.dispose();
|
|
@@ -996,7 +1316,7 @@ var ComponentHostResolver = class {
|
|
|
996
1316
|
host = void 0;
|
|
997
1317
|
}
|
|
998
1318
|
if (host === void 0) {
|
|
999
|
-
host = new ComponentHost(element, this.services, this.channels);
|
|
1319
|
+
host = new ComponentHost(element, this.services, this.channels, anchor ?? null);
|
|
1000
1320
|
this.hosts.set(anchorId, host);
|
|
1001
1321
|
this.pendingMounts.push(host);
|
|
1002
1322
|
} else host.updateProps(element.props);
|
|
@@ -1145,6 +1465,8 @@ var ShellService = class {
|
|
|
1145
1465
|
handler = null;
|
|
1146
1466
|
/** What `warnOnLeave` last said. */
|
|
1147
1467
|
leaving = false;
|
|
1468
|
+
/** What `preferColorScheme` last asked for; null until it is asked. */
|
|
1469
|
+
preferred = null;
|
|
1148
1470
|
scheme = internalState("light");
|
|
1149
1471
|
contrastState = internalState("standard");
|
|
1150
1472
|
pointerState = internalState("fine");
|
|
@@ -1262,6 +1584,10 @@ var ShellService = class {
|
|
|
1262
1584
|
type: "leaveWarning",
|
|
1263
1585
|
on: true
|
|
1264
1586
|
});
|
|
1587
|
+
if (handler !== null && this.preferred !== null) handler({
|
|
1588
|
+
type: "colorScheme",
|
|
1589
|
+
preference: this.preferred
|
|
1590
|
+
});
|
|
1265
1591
|
}
|
|
1266
1592
|
/**
|
|
1267
1593
|
* Called by the runtime when the shell reports the appearance.
|
|
@@ -1478,6 +1804,33 @@ var ShellService = class {
|
|
|
1478
1804
|
on
|
|
1479
1805
|
});
|
|
1480
1806
|
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Asks the shell to report this appearance from now on: `light` or
|
|
1809
|
+
* `dark` whatever the platform says, or `auto` to follow the platform
|
|
1810
|
+
* again. `colorScheme` then reports the answer, as it does when the
|
|
1811
|
+
* host calls `setColorScheme`, which is the same switch.
|
|
1812
|
+
*
|
|
1813
|
+
* For an application whose appearance setting is its own and stored:
|
|
1814
|
+
* the host reads the stored choice before the first frame and starts
|
|
1815
|
+
* with it (`createApp({ colorScheme })`), and the application, where
|
|
1816
|
+
* the person changes it, says so here. Without this, an application in
|
|
1817
|
+
* a render worker could not take back an override the host started it
|
|
1818
|
+
* with, and `auto` would keep reporting the stored choice instead of
|
|
1819
|
+
* the platform's. An application that stores nothing can combine its
|
|
1820
|
+
* choice with `colorScheme` instead and never call this.
|
|
1821
|
+
*
|
|
1822
|
+
* A request, because the media query is the window's; and a state,
|
|
1823
|
+
* like `warnOnLeave`, so a shell that arrives after it was asked is
|
|
1824
|
+
* told. Asking for the same thing twice sends nothing.
|
|
1825
|
+
*/
|
|
1826
|
+
preferColorScheme(preference) {
|
|
1827
|
+
if (preference === this.preferred) return;
|
|
1828
|
+
this.preferred = preference;
|
|
1829
|
+
this.handler?.({
|
|
1830
|
+
type: "colorScheme",
|
|
1831
|
+
preference
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1481
1834
|
/** Called by the runtime when the shell reports the state. Not for applications. */
|
|
1482
1835
|
applyFullscreen(active) {
|
|
1483
1836
|
if (this.isFullscreen.value !== active) this.isFullscreen.value = active;
|
|
@@ -1728,6 +2081,44 @@ function shellFilesUnsupported(error = "There is no shell to reach files through
|
|
|
1728
2081
|
error
|
|
1729
2082
|
};
|
|
1730
2083
|
}
|
|
2084
|
+
//#endregion
|
|
2085
|
+
//#region src/app/FrameService.ts
|
|
2086
|
+
/**
|
|
2087
|
+
* Every frame the runtime draws, as a component can hear it.
|
|
2088
|
+
*
|
|
2089
|
+
* The shell already receives these as `onFrame`, for an FPS readout or
|
|
2090
|
+
* a profiler on the host thread. A screen that wants to show its own
|
|
2091
|
+
* frame gap or input latency, as a demo of the thread model does,
|
|
2092
|
+
* needs them on this thread, and this is where the runtime puts them.
|
|
2093
|
+
* Injected like any service: `ctx.inject(FrameService).frames`.
|
|
2094
|
+
*/
|
|
2095
|
+
var FrameService = class {
|
|
2096
|
+
subject = new Subject();
|
|
2097
|
+
flusher = null;
|
|
2098
|
+
/** Emits after each frame finishes, with what it cost and what it answered. */
|
|
2099
|
+
frames = this.subject.asObservable();
|
|
2100
|
+
/** Called by the runtime; not for components. */
|
|
2101
|
+
publish(metrics) {
|
|
2102
|
+
this.subject.next(metrics);
|
|
2103
|
+
}
|
|
2104
|
+
/** Called by the shell that drives the frames; not for components. */
|
|
2105
|
+
setFlush(flush) {
|
|
2106
|
+
this.flusher = flush;
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Draws a pending frame now, rather than when the display next asks
|
|
2110
|
+
* for one; nothing when none is pending.
|
|
2111
|
+
*
|
|
2112
|
+
* A tab in the background is sent no frames. A design editor's tool,
|
|
2113
|
+
* called by an agent while the editor's window is behind another,
|
|
2114
|
+
* changes the screen and waits for it to be drawn before reading it:
|
|
2115
|
+
* without this, it waits for a frame that never comes. The agent's
|
|
2116
|
+
* own screen tools (`uiSurface`) do the same after each action.
|
|
2117
|
+
*/
|
|
2118
|
+
flush() {
|
|
2119
|
+
this.flusher?.();
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
1731
2122
|
/** A text node's text as the tree shows it. */
|
|
1732
2123
|
function treeText(text) {
|
|
1733
2124
|
if (typeof text !== "string" || text === "") return;
|
|
@@ -2570,6 +2961,20 @@ var EditingService = class {
|
|
|
2570
2961
|
return this.controller.caretRectOf(node, offset);
|
|
2571
2962
|
}
|
|
2572
2963
|
/**
|
|
2964
|
+
* The caret position nearest a point in a field, the point given as a
|
|
2965
|
+
* pointer event gives it: where a press there would put the caret.
|
|
2966
|
+
*
|
|
2967
|
+
* What a field opened over text the person pointed at uses to start
|
|
2968
|
+
* the caret where they pointed: ask once the field is laid out, then
|
|
2969
|
+
* `select` that offset. Null when the node is not an editable, when it
|
|
2970
|
+
* has not been laid out yet, or before the runtime has wired the
|
|
2971
|
+
* controller: "ask again next frame", as for `caretRectOf`.
|
|
2972
|
+
*/
|
|
2973
|
+
offsetNear(node, x, y) {
|
|
2974
|
+
if (node === null || this.controller === null) return null;
|
|
2975
|
+
return this.controller.offsetNear(node, x, y);
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2573
2978
|
* Selects from `anchor` to `focus` and focuses the field the focus is
|
|
2574
2979
|
* in: within one field, or across the fields of an editing group (see
|
|
2575
2980
|
* `UiEditingGroup`). What an editor does after a command over a
|
|
@@ -2613,124 +3018,6 @@ var ScrollService = class {
|
|
|
2613
3018
|
}
|
|
2614
3019
|
};
|
|
2615
3020
|
//#endregion
|
|
2616
|
-
//#region src/app/FocusService.ts
|
|
2617
|
-
/**
|
|
2618
|
-
* Keyboard focus, as a store components can inject.
|
|
2619
|
-
*
|
|
2620
|
-
* `UiFocusManager` lives in the runtime and a component cannot reach
|
|
2621
|
-
* it — components reach the world through stores, as they do for the
|
|
2622
|
-
* clipboard (`ShellService`), overlays (`OverlayService`) and find
|
|
2623
|
-
* (`FindService`). Without this a component cannot autofocus a field,
|
|
2624
|
-
* trap the keyboard in a dialog, or put the caret in the input that
|
|
2625
|
-
* failed validation.
|
|
2626
|
-
*
|
|
2627
|
-
* Nodes come from a `ref` prop. A tree's refs fire before the runtime
|
|
2628
|
-
* installs the manager, so an action taken during the first build is
|
|
2629
|
-
* queued and replayed once there is one; that is the same problem
|
|
2630
|
-
* `FindService` solves for its query field, and the reason `autoFocus`
|
|
2631
|
-
* in a dialog works on the frame it mounts.
|
|
2632
|
-
*
|
|
2633
|
-
* It must stay on the render thread: its actions take `UiNode`s, which
|
|
2634
|
-
* never cross a worker boundary.
|
|
2635
|
-
*/
|
|
2636
|
-
var FocusService = class {
|
|
2637
|
-
/** The node holding focus, or null. A control binds its focus ring to this. */
|
|
2638
|
-
focused = internalState(null);
|
|
2639
|
-
/** Whether focus is confined to a subtree by an open trap. */
|
|
2640
|
-
trapped = internalState(false);
|
|
2641
|
-
/**
|
|
2642
|
-
* Whether the focus held is focus the keyboard can see: reached by Tab
|
|
2643
|
-
* or an arrow key, or by code with no pointer since, rather than by a
|
|
2644
|
-
* press. CSS's `:focus-visible`, for something other than a ring that
|
|
2645
|
-
* should answer the keyboard and not the click that happened to focus
|
|
2646
|
-
* the same control: a tooltip, say.
|
|
2647
|
-
*/
|
|
2648
|
-
focusVisible = internalState(false);
|
|
2649
|
-
manager = null;
|
|
2650
|
-
detach = null;
|
|
2651
|
-
/** Actions taken before the runtime installed a manager, in order. */
|
|
2652
|
-
queued = [];
|
|
2653
|
-
/** Installed by the runtime; without one every action is queued. */
|
|
2654
|
-
setManager(manager) {
|
|
2655
|
-
this.detach?.();
|
|
2656
|
-
this.detach = null;
|
|
2657
|
-
this.manager = manager;
|
|
2658
|
-
if (manager === null) {
|
|
2659
|
-
this.queued = [];
|
|
2660
|
-
return;
|
|
2661
|
-
}
|
|
2662
|
-
const focusChange = manager.onFocusChange((node) => {
|
|
2663
|
-
this.focused.value = node;
|
|
2664
|
-
this.focusVisible.value = manager.focusVisible;
|
|
2665
|
-
});
|
|
2666
|
-
const scopeChange = manager.onScopeChange(() => {
|
|
2667
|
-
this.trapped.value = manager.trapped;
|
|
2668
|
-
});
|
|
2669
|
-
this.detach = () => {
|
|
2670
|
-
focusChange();
|
|
2671
|
-
scopeChange();
|
|
2672
|
-
};
|
|
2673
|
-
const pending = this.queued;
|
|
2674
|
-
this.queued = [];
|
|
2675
|
-
for (const action of pending) action(manager);
|
|
2676
|
-
this.sync(manager);
|
|
2677
|
-
}
|
|
2678
|
-
/**
|
|
2679
|
-
* Gives the node keyboard focus. Non-focusable nodes are ignored.
|
|
2680
|
-
*
|
|
2681
|
-
* The node is scrolled into view, unless `options.preventScroll` asks
|
|
2682
|
-
* for the page to stay where it is, as `element.focus({ preventScroll:
|
|
2683
|
-
* true })` does in a browser.
|
|
2684
|
-
*/
|
|
2685
|
-
focus(node, options) {
|
|
2686
|
-
this.run((manager) => manager.focus(node, "program", options));
|
|
2687
|
-
}
|
|
2688
|
-
/** Drops focus without moving it anywhere. */
|
|
2689
|
-
blur() {
|
|
2690
|
-
this.run((manager) => manager.blur());
|
|
2691
|
-
}
|
|
2692
|
-
/** Moves focus to the next focusable node, wrapping around. */
|
|
2693
|
-
focusNext() {
|
|
2694
|
-
this.run((manager) => manager.focusNext());
|
|
2695
|
-
}
|
|
2696
|
-
/** Moves focus to the previous focusable node, wrapping around. */
|
|
2697
|
-
focusPrevious() {
|
|
2698
|
-
this.run((manager) => manager.focusPrevious());
|
|
2699
|
-
}
|
|
2700
|
-
/**
|
|
2701
|
-
* Confines focus to `scope` until `releaseTrap`, moving it inside if
|
|
2702
|
-
* it was elsewhere. Traps nest: a dialog opened over a dialog traps
|
|
2703
|
-
* again, and each release restores its own opener.
|
|
2704
|
-
*/
|
|
2705
|
-
trap(scope) {
|
|
2706
|
-
this.run((manager) => manager.pushScope(scope));
|
|
2707
|
-
}
|
|
2708
|
-
/**
|
|
2709
|
-
* Ends the innermost trap and returns focus to whatever held it when
|
|
2710
|
-
* the trap was taken — the button that opened the dialog. Given the
|
|
2711
|
-
* node trapped in, only that trap ends, and nothing when it ended
|
|
2712
|
-
* already: a trap's subtree removed releases it, and a release after
|
|
2713
|
-
* that would otherwise end the trap under it.
|
|
2714
|
-
*/
|
|
2715
|
-
releaseTrap(scope) {
|
|
2716
|
-
this.run((manager) => manager.popScope(scope));
|
|
2717
|
-
}
|
|
2718
|
-
run(action) {
|
|
2719
|
-
const manager = this.manager;
|
|
2720
|
-
if (manager === null) {
|
|
2721
|
-
this.queued.push(action);
|
|
2722
|
-
return;
|
|
2723
|
-
}
|
|
2724
|
-
action(manager);
|
|
2725
|
-
this.sync(manager);
|
|
2726
|
-
}
|
|
2727
|
-
sync(manager) {
|
|
2728
|
-
this.focused.value = manager.focusedNode;
|
|
2729
|
-
this.focusVisible.value = manager.focusVisible;
|
|
2730
|
-
this.trapped.value = manager.trapped;
|
|
2731
|
-
}
|
|
2732
|
-
};
|
|
2733
|
-
//#endregion
|
|
2734
3021
|
//#region src/app/MediaService.ts
|
|
2735
3022
|
/**
|
|
2736
3023
|
* Where an `Image` finds its bitmap, an `Icon` its raster and a
|
|
@@ -3065,6 +3352,16 @@ var AnimationService = class {
|
|
|
3065
3352
|
*/
|
|
3066
3353
|
setMotion(motion) {
|
|
3067
3354
|
this.motionVocabulary = motion;
|
|
3355
|
+
this.motionListener?.(motion);
|
|
3356
|
+
}
|
|
3357
|
+
motionListener = null;
|
|
3358
|
+
/**
|
|
3359
|
+
* Called by the runtime, which provides the vocabulary at its root so
|
|
3360
|
+
* a name in a `transition` or a `motion` state resolves to it too; a
|
|
3361
|
+
* part of the tree may provide its own over it (`UiEnvironmentKeys.motion`).
|
|
3362
|
+
*/
|
|
3363
|
+
setMotionListener(listener) {
|
|
3364
|
+
this.motionListener = listener;
|
|
3068
3365
|
}
|
|
3069
3366
|
get motion() {
|
|
3070
3367
|
return this.motionVocabulary;
|
|
@@ -3115,7 +3412,7 @@ var AnimationService = class {
|
|
|
3115
3412
|
tween.snap();
|
|
3116
3413
|
return tween.values;
|
|
3117
3414
|
}
|
|
3118
|
-
return driver.start(tween);
|
|
3415
|
+
return driver.start(tween, options.clock ?? null);
|
|
3119
3416
|
}
|
|
3120
3417
|
/**
|
|
3121
3418
|
* Pulls a number towards a value on a spring, with no duration.
|
|
@@ -3124,7 +3421,7 @@ var AnimationService = class {
|
|
|
3124
3421
|
* so a flick that changes direction bends rather than restarting.
|
|
3125
3422
|
*/
|
|
3126
3423
|
spring(cell, to, options = {}) {
|
|
3127
|
-
const base =
|
|
3424
|
+
const base = springIn(this.motionVocabulary, options.spring);
|
|
3128
3425
|
const driver = this.driver;
|
|
3129
3426
|
const previous = driver?.animationFor(cell);
|
|
3130
3427
|
const animation = new UiSpring(cell, to, {
|
|
@@ -3143,7 +3440,7 @@ var AnimationService = class {
|
|
|
3143
3440
|
animation.snap();
|
|
3144
3441
|
return animation.values;
|
|
3145
3442
|
}
|
|
3146
|
-
return driver.start(animation);
|
|
3443
|
+
return driver.start(animation, options.clock ?? null);
|
|
3147
3444
|
}
|
|
3148
3445
|
/** Stops whatever is driving a cell, leaving it where it stands. */
|
|
3149
3446
|
stop(cell) {
|
|
@@ -3162,31 +3459,10 @@ var AnimationService = class {
|
|
|
3162
3459
|
return this.driver?.animationFor(cell);
|
|
3163
3460
|
}
|
|
3164
3461
|
resolveDuration(duration) {
|
|
3165
|
-
return
|
|
3462
|
+
return durationIn(this.motionVocabulary, duration);
|
|
3166
3463
|
}
|
|
3167
3464
|
resolveEasing(easing) {
|
|
3168
|
-
|
|
3169
|
-
return this.motionVocabulary.easings[easing] ?? easings.standard;
|
|
3170
|
-
}
|
|
3171
|
-
};
|
|
3172
|
-
//#endregion
|
|
3173
|
-
//#region src/app/FrameService.ts
|
|
3174
|
-
/**
|
|
3175
|
-
* Every frame the runtime draws, as a component can hear it.
|
|
3176
|
-
*
|
|
3177
|
-
* The shell already receives these as `onFrame`, for an FPS readout or
|
|
3178
|
-
* a profiler on the host thread. A screen that wants to show its own
|
|
3179
|
-
* frame gap or input latency, as a demo of the thread model does,
|
|
3180
|
-
* needs them on this thread, and this is where the runtime puts them.
|
|
3181
|
-
* Injected like any service: `ctx.inject(FrameService).frames`.
|
|
3182
|
-
*/
|
|
3183
|
-
var FrameService = class {
|
|
3184
|
-
subject = new Subject();
|
|
3185
|
-
/** Emits after each frame finishes, with what it cost and what it answered. */
|
|
3186
|
-
frames = this.subject.asObservable();
|
|
3187
|
-
/** Called by the runtime; not for components. */
|
|
3188
|
-
publish(metrics) {
|
|
3189
|
-
this.subject.next(metrics);
|
|
3465
|
+
return easingIn(this.motionVocabulary, easing);
|
|
3190
3466
|
}
|
|
3191
3467
|
};
|
|
3192
3468
|
//#endregion
|
|
@@ -3228,6 +3504,17 @@ var InspectionService = class {
|
|
|
3228
3504
|
return this.host.inspect(node);
|
|
3229
3505
|
}
|
|
3230
3506
|
/**
|
|
3507
|
+
* Why the node has the size it has, as the layout decided it: for
|
|
3508
|
+
* each axis, the rule that fixed it (`decidedBy`: its own length, a
|
|
3509
|
+
* flex share, a stretch across its parent, its content, a clamp) and
|
|
3510
|
+
* the sentences that say so. What a design tool needs to tell an
|
|
3511
|
+
* element that fills its parent from one that only happens to be as
|
|
3512
|
+
* wide, which the box alone cannot. Null before the node is laid out.
|
|
3513
|
+
*/
|
|
3514
|
+
explain(node) {
|
|
3515
|
+
return this.host?.explain?.(node) ?? null;
|
|
3516
|
+
}
|
|
3517
|
+
/**
|
|
3231
3518
|
* Where the node is drawn, as `inspect(node).box` reports it, without
|
|
3232
3519
|
* the rest of the report: for an editor that reads the boxes of many
|
|
3233
3520
|
* nodes as its view moves (a minimap of a board of frames), where
|
|
@@ -3238,6 +3525,19 @@ var InspectionService = class {
|
|
|
3238
3525
|
return this.host.box?.(node) ?? this.host.inspect(node).box;
|
|
3239
3526
|
}
|
|
3240
3527
|
/**
|
|
3528
|
+
* Where the node is seen on the screen: `box` with every transform
|
|
3529
|
+
* above it applied, so a node in a frame drawn at half its size is
|
|
3530
|
+
* half its size, where it is drawn. What an overlay laid over the
|
|
3531
|
+
* live tree is placed by (an outline on the component under the
|
|
3532
|
+
* pointer). `box` keeps the untransformed box, which is where the
|
|
3533
|
+
* node is in the layout: what an editor reading a zoomed board in
|
|
3534
|
+
* the board's own coordinates (a minimap) needs.
|
|
3535
|
+
*/
|
|
3536
|
+
screenBox(node) {
|
|
3537
|
+
if (this.host === null) throw new Error("InspectionService has nothing to measure before the runtime has built its tree.");
|
|
3538
|
+
return this.host.screenBox?.(node) ?? this.box(node);
|
|
3539
|
+
}
|
|
3540
|
+
/**
|
|
3241
3541
|
* Draws a node as though it declared `value` for `property`, until
|
|
3242
3542
|
* the preview is ended or the node leaves the tree.
|
|
3243
3543
|
*
|
|
@@ -3255,6 +3555,43 @@ var InspectionService = class {
|
|
|
3255
3555
|
endPreview(node, property) {
|
|
3256
3556
|
this.host?.endPreview?.(node, property);
|
|
3257
3557
|
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Draws a component as though its parent passed `inputs`, over what
|
|
3560
|
+
* the parent did pass, until the preview is ended or the component
|
|
3561
|
+
* leaves the tree. The component is named by its anchor, as
|
|
3562
|
+
* `inspect(node).owners[].anchorId` gives it.
|
|
3563
|
+
*
|
|
3564
|
+
* What a design editor shows when a component on a played screen is
|
|
3565
|
+
* jumped to one of its states (Filled, Error, Disabled): the same
|
|
3566
|
+
* instance, still live, given that state's inputs, and the inputs it
|
|
3567
|
+
* was written with back when the preview ends. A later preview of the
|
|
3568
|
+
* same component replaces this one. The names and values are props',
|
|
3569
|
+
* as JSX would write them. An anchor no component holds is ignored.
|
|
3570
|
+
*/
|
|
3571
|
+
previewInputs(anchorId, inputs) {
|
|
3572
|
+
this.host?.previewInputs?.(anchorId, inputs);
|
|
3573
|
+
}
|
|
3574
|
+
/** Ends a preview of a component's inputs: its parent's are its own again. */
|
|
3575
|
+
endInputPreview(anchorId) {
|
|
3576
|
+
this.host?.previewInputs?.(anchorId, null);
|
|
3577
|
+
}
|
|
3578
|
+
/**
|
|
3579
|
+
* Presses a node, or replaces the text of the field it is, as a
|
|
3580
|
+
* person would: a click at the middle of where it is drawn, which
|
|
3581
|
+
* its handlers and every handler above it get, or the edit a
|
|
3582
|
+
* keyboard makes once it has focus. The path the accessibility
|
|
3583
|
+
* mirror's actions take, for any node in the tree, record or not.
|
|
3584
|
+
*
|
|
3585
|
+
* What a design editor's agent does to play a prototype in its live
|
|
3586
|
+
* frames: a row pressed, an email typed, by a node it found with
|
|
3587
|
+
* `nodesAt` or by walking the tree. False when it did not take: a
|
|
3588
|
+
* node no longer in the tree, or a field that could not have focus
|
|
3589
|
+
* because an open dialog's trap holds it, or takes no text.
|
|
3590
|
+
*/
|
|
3591
|
+
act(node, action, value) {
|
|
3592
|
+
if (this.host?.act === void 0) throw new Error("InspectionService cannot act before the runtime has built its tree.");
|
|
3593
|
+
return this.host.act(node, action, value);
|
|
3594
|
+
}
|
|
3258
3595
|
};
|
|
3259
3596
|
//#endregion
|
|
3260
3597
|
//#region src/app/InputLatency.ts
|
|
@@ -3421,7 +3758,10 @@ var SmoothScroller = class {
|
|
|
3421
3758
|
const { min, max } = this.boundsOf(node, axis);
|
|
3422
3759
|
const target = clamp((pending ?? from) + delta, min, max);
|
|
3423
3760
|
if (pending !== void 0 && target === pending) return;
|
|
3424
|
-
this.animations.spring(cell, target, {
|
|
3761
|
+
this.animations.spring(cell, target, {
|
|
3762
|
+
spring: SCROLL_SPRING,
|
|
3763
|
+
clock: clockOf(node)
|
|
3764
|
+
});
|
|
3425
3765
|
}
|
|
3426
3766
|
/**
|
|
3427
3767
|
* Shifts a running scroll by a coordinate correction.
|
|
@@ -3446,7 +3786,10 @@ var SmoothScroller = class {
|
|
|
3446
3786
|
const pending = this.destinationOf(state.cell);
|
|
3447
3787
|
if (pending === void 0) return;
|
|
3448
3788
|
state.lastWritten = state.cell.value;
|
|
3449
|
-
this.animations.spring(state.cell, pending + delta, {
|
|
3789
|
+
this.animations.spring(state.cell, pending + delta, {
|
|
3790
|
+
spring: SCROLL_SPRING,
|
|
3791
|
+
clock: clockOf(node)
|
|
3792
|
+
});
|
|
3450
3793
|
}
|
|
3451
3794
|
/** Whether this container is being animated right now. */
|
|
3452
3795
|
isScrolling(node) {
|
|
@@ -3654,6 +3997,8 @@ var GessoRuntime = class {
|
|
|
3654
3997
|
lastEarlyFrame = -Infinity;
|
|
3655
3998
|
/** When the last discrete input was routed; see `answerInput`. */
|
|
3656
3999
|
lastDiscreteInput = -Infinity;
|
|
4000
|
+
/** The wheel's steps since the last frame, added together; see `wheel`. */
|
|
4001
|
+
heldWheel = null;
|
|
3657
4002
|
/** The display time the frame being drawn is for; see `FrameMetrics.time`. */
|
|
3658
4003
|
frameTime = null;
|
|
3659
4004
|
/** When the frame's first phase began, before its changes were collected; see `FrameMetrics.durationMs`. */
|
|
@@ -3763,6 +4108,11 @@ var GessoRuntime = class {
|
|
|
3763
4108
|
* them. Only populated while a listener is attached.
|
|
3764
4109
|
*/
|
|
3765
4110
|
semanticsBoxes = /* @__PURE__ */ new Map();
|
|
4111
|
+
/** When a frame last moved something, and when the boxes were last swept; see `updateSemantics`. */
|
|
4112
|
+
semanticsMovedAt = -Infinity;
|
|
4113
|
+
semanticsSweptAt = -Infinity;
|
|
4114
|
+
/** The sweep owed once a run of moving frames stops. */
|
|
4115
|
+
semanticsSweepTimer = null;
|
|
3766
4116
|
/** What each semantics walk leaves for the next; see `SemanticsMemory`. */
|
|
3767
4117
|
semanticsMemory = semanticsMemory();
|
|
3768
4118
|
lastFocusedId = null;
|
|
@@ -3809,6 +4159,10 @@ var GessoRuntime = class {
|
|
|
3809
4159
|
* lays out, whichever comes first; see `resize`.
|
|
3810
4160
|
*/
|
|
3811
4161
|
laidOutOnce = false;
|
|
4162
|
+
/** Frames in a row that threw; see `reportFrameError`. */
|
|
4163
|
+
framesFailed = 0;
|
|
4164
|
+
/** Nodes that declare `holdUnseen`, as frames have found them; see `updateHolds`. */
|
|
4165
|
+
holdable = /* @__PURE__ */ new Set();
|
|
3812
4166
|
constructor(options) {
|
|
3813
4167
|
this.services = options.services ?? new ServiceRegistry();
|
|
3814
4168
|
this.channels = options.channels ?? new ChannelRegistry();
|
|
@@ -3902,7 +4256,8 @@ var GessoRuntime = class {
|
|
|
3902
4256
|
width: this.width,
|
|
3903
4257
|
height: this.height
|
|
3904
4258
|
}),
|
|
3905
|
-
onLayout: (node, listener) => this.layoutNotifier.add(node, listener)
|
|
4259
|
+
onLayout: (node, listener) => this.layoutNotifier.add(node, listener),
|
|
4260
|
+
onFlowLayout: (node, listener) => this.layoutNotifier.addFlow(node, listener)
|
|
3906
4261
|
},
|
|
3907
4262
|
animations: this.animations,
|
|
3908
4263
|
sharedElements: this.sharedElements
|
|
@@ -3926,6 +4281,9 @@ var GessoRuntime = class {
|
|
|
3926
4281
|
if (options.fonts !== void 0) this.services.get(FontService).declare(options.fonts);
|
|
3927
4282
|
if (!this.services.has(AnimationService)) this.services.register(AnimationService);
|
|
3928
4283
|
this.services.get(AnimationService).setDriver(this.animations);
|
|
4284
|
+
this.services.get(AnimationService).setMotionListener((motion) => {
|
|
4285
|
+
if (this.graph.updateNodePropertyNow(this.graph.root, "provide", [provided(UiEnvironmentKeys.motion, motion)], DirtyFlags.Environment)) this.requestRepaint();
|
|
4286
|
+
});
|
|
3929
4287
|
this.services.get(AudioService).setAnimations(this.services.get(AnimationService));
|
|
3930
4288
|
this.smoothScroller = new SmoothScroller(this.graph, this.services.get(AnimationService), (node, axis) => {
|
|
3931
4289
|
const record = this.engine.recordFor(node);
|
|
@@ -3997,6 +4355,7 @@ var GessoRuntime = class {
|
|
|
3997
4355
|
this.services.get(InspectionService).attach({
|
|
3998
4356
|
nodesAt: (x, y) => this.hitTester.hitStack(x, y),
|
|
3999
4357
|
inspect: (node) => this.inspectNode(node),
|
|
4358
|
+
explain: (node) => this.engine.explain(node),
|
|
4000
4359
|
box: (node) => {
|
|
4001
4360
|
const box = this.engine.visibleBox(node);
|
|
4002
4361
|
return {
|
|
@@ -4006,8 +4365,22 @@ var GessoRuntime = class {
|
|
|
4006
4365
|
height: box.height
|
|
4007
4366
|
};
|
|
4008
4367
|
},
|
|
4368
|
+
screenBox: (node) => {
|
|
4369
|
+
const box = this.engine.screenBox(node);
|
|
4370
|
+
return {
|
|
4371
|
+
x: box.x,
|
|
4372
|
+
y: box.y,
|
|
4373
|
+
width: box.width,
|
|
4374
|
+
height: box.height
|
|
4375
|
+
};
|
|
4376
|
+
},
|
|
4009
4377
|
preview: (node, property, value) => this.previewProperty(node, property, value),
|
|
4010
|
-
endPreview: (node, property) => this.endPreviews(node, property)
|
|
4378
|
+
endPreview: (node, property) => this.endPreviews(node, property),
|
|
4379
|
+
previewInputs: (anchorId, inputs) => this.resolver.hostFor(anchorId)?.previewInputs(inputs),
|
|
4380
|
+
act: (node, action, value) => this.graph.getNode(node.id) === node && this.actOn(node, value === void 0 ? { action } : {
|
|
4381
|
+
action,
|
|
4382
|
+
value
|
|
4383
|
+
})
|
|
4011
4384
|
});
|
|
4012
4385
|
this.services.get(FocusService).setManager(this.input.focus);
|
|
4013
4386
|
this.services.get(EditingService).setController(this.input.editing);
|
|
@@ -4070,6 +4443,7 @@ var GessoRuntime = class {
|
|
|
4070
4443
|
this.frameErrorListener = listener;
|
|
4071
4444
|
}
|
|
4072
4445
|
reportFrameError(error) {
|
|
4446
|
+
if (this.framesFailed++ === 0) this.requestRepaint();
|
|
4073
4447
|
const message = error instanceof Error ? error.message : String(error);
|
|
4074
4448
|
const stack = error instanceof Error ? error.stack : void 0;
|
|
4075
4449
|
if (this.frameErrorListener !== null) {
|
|
@@ -4236,6 +4610,51 @@ var GessoRuntime = class {
|
|
|
4236
4610
|
this.lastDiscreteInput = now();
|
|
4237
4611
|
this.drawEarly();
|
|
4238
4612
|
}
|
|
4613
|
+
/**
|
|
4614
|
+
* A step of the wheel, or of a trackpad's two fingers or its pinch,
|
|
4615
|
+
* held until the frame and added to the others that arrive before it.
|
|
4616
|
+
*
|
|
4617
|
+
* A trackpad sends a step on every refresh, and a handler that takes
|
|
4618
|
+
* longer than a refresh over one (a design tool's board zoomed over
|
|
4619
|
+
* hundreds of frames, each of whose names it scales) fell behind for
|
|
4620
|
+
* good: each step waited for the one before it, the frame waited for
|
|
4621
|
+
* them all, and a pinch drew nothing until the fingers stopped, then
|
|
4622
|
+
* jumped. Held, the steps that queue while the handler runs are one
|
|
4623
|
+
* step at the next frame, so the handler runs once a frame however
|
|
4624
|
+
* many arrive, and what it draws follows the fingers a frame at a
|
|
4625
|
+
* time. A step with other modifiers or in another unit is not added
|
|
4626
|
+
* to the held one but follows it, and any other input lets the held
|
|
4627
|
+
* step go first (`releaseWheel`), so nothing is answered out of order.
|
|
4628
|
+
*/
|
|
4629
|
+
wheel(...step) {
|
|
4630
|
+
const held = this.heldWheel;
|
|
4631
|
+
if (held !== null && !sameWheel(held, step)) this.releaseWheel();
|
|
4632
|
+
if (this.heldWheel === null) {
|
|
4633
|
+
this.heldWheel = step;
|
|
4634
|
+
this.scheduler.wake();
|
|
4635
|
+
return;
|
|
4636
|
+
}
|
|
4637
|
+
const [, , deltaX, deltaY, modifiers, deltaMode, wheelDeltaY] = this.heldWheel;
|
|
4638
|
+
const [x, y, moreX, moreY, , , moreWheelY, at] = step;
|
|
4639
|
+
const legacy = wheelDeltaY === void 0 || moreWheelY === void 0 ? moreWheelY : wheelDeltaY + moreWheelY;
|
|
4640
|
+
this.heldWheel = [
|
|
4641
|
+
x,
|
|
4642
|
+
y,
|
|
4643
|
+
deltaX + moreX,
|
|
4644
|
+
deltaY + moreY,
|
|
4645
|
+
modifiers,
|
|
4646
|
+
deltaMode,
|
|
4647
|
+
legacy,
|
|
4648
|
+
at
|
|
4649
|
+
];
|
|
4650
|
+
}
|
|
4651
|
+
/** Hands the held wheel step to the input now, before what follows it; see `wheel`. */
|
|
4652
|
+
releaseWheel() {
|
|
4653
|
+
const held = this.heldWheel;
|
|
4654
|
+
if (held === null) return;
|
|
4655
|
+
this.heldWheel = null;
|
|
4656
|
+
this.input.wheel.wheel(...held);
|
|
4657
|
+
}
|
|
4239
4658
|
/** Draws the pending frame in a task of its own, if the guards in `answerInput` allow. */
|
|
4240
4659
|
drawEarly() {
|
|
4241
4660
|
if (!this.started || !this.visible || this.earlyFrame !== null || !this.scheduler.framePending) return;
|
|
@@ -4436,7 +4855,8 @@ var GessoRuntime = class {
|
|
|
4436
4855
|
source: PREVIEW,
|
|
4437
4856
|
name: "preview",
|
|
4438
4857
|
order: Number.MAX_SAFE_INTEGER,
|
|
4439
|
-
value
|
|
4858
|
+
value,
|
|
4859
|
+
snap: true
|
|
4440
4860
|
}, propertyEffects(property));
|
|
4441
4861
|
}
|
|
4442
4862
|
endPreviews(node, property) {
|
|
@@ -4780,6 +5200,7 @@ var GessoRuntime = class {
|
|
|
4780
5200
|
listens: this.dispatcher.listenerTypes(node),
|
|
4781
5201
|
beneath: this.beneathAtPointer(node),
|
|
4782
5202
|
semantics: this.semanticsOf(node),
|
|
5203
|
+
...this.interactionOf(node),
|
|
4783
5204
|
explanation: formatExplanation(this.engine.explain(node)),
|
|
4784
5205
|
...this.sourceReport(node)
|
|
4785
5206
|
};
|
|
@@ -4827,6 +5248,27 @@ var GessoRuntime = class {
|
|
|
4827
5248
|
const source = this.builder.sourceOf(node);
|
|
4828
5249
|
return source === void 0 ? {} : { source };
|
|
4829
5250
|
}
|
|
5251
|
+
/** The props the node's `interactive` modifiers write while hovered and pressed, later ones over earlier. */
|
|
5252
|
+
interactionOf(node) {
|
|
5253
|
+
let hovered;
|
|
5254
|
+
let pressed;
|
|
5255
|
+
for (const modifier of this.builder.modifiersFor(node)?.modifiers ?? []) {
|
|
5256
|
+
if (modifier.kind.name !== "interactive") continue;
|
|
5257
|
+
const options = modifier.args;
|
|
5258
|
+
if (options.hovered !== void 0) hovered = {
|
|
5259
|
+
...hovered,
|
|
5260
|
+
...options.hovered
|
|
5261
|
+
};
|
|
5262
|
+
if (options.pressed !== void 0) pressed = {
|
|
5263
|
+
...pressed,
|
|
5264
|
+
...options.pressed
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
return hovered === void 0 && pressed === void 0 ? {} : { interaction: {
|
|
5268
|
+
...hovered === void 0 ? {} : { hovered },
|
|
5269
|
+
...pressed === void 0 ? {} : { pressed }
|
|
5270
|
+
} };
|
|
5271
|
+
}
|
|
4830
5272
|
ownersOf(node) {
|
|
4831
5273
|
const owners = [];
|
|
4832
5274
|
for (let current = node; current !== null; current = current.parent) {
|
|
@@ -4838,11 +5280,14 @@ var GessoRuntime = class {
|
|
|
4838
5280
|
const value = ownerInput(supplied);
|
|
4839
5281
|
if (value !== void 0) inputs[name] = value;
|
|
4840
5282
|
}
|
|
5283
|
+
const live = {};
|
|
5284
|
+
for (const [name, value] of host.liveInputs()) if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") live[name] = value;
|
|
4841
5285
|
owners.push({
|
|
4842
5286
|
name: getComponentMetadata(host.component).tag,
|
|
4843
5287
|
anchorId: current.id,
|
|
4844
5288
|
...usedAt === void 0 ? {} : { usedAt },
|
|
4845
|
-
...Object.keys(inputs).length === 0 ? {} : { inputs }
|
|
5289
|
+
...Object.keys(inputs).length === 0 ? {} : { inputs },
|
|
5290
|
+
...Object.keys(live).length === 0 ? {} : { live }
|
|
4846
5291
|
});
|
|
4847
5292
|
}
|
|
4848
5293
|
}
|
|
@@ -4999,7 +5444,9 @@ var GessoRuntime = class {
|
|
|
4999
5444
|
* reconciles rather than recreating, so a component whose class is
|
|
5000
5445
|
* the same object as before keeps its host and its `internalState`;
|
|
5001
5446
|
* one whose module was replaced is a different class, and its host
|
|
5002
|
-
* is disposed and mounted again
|
|
5447
|
+
* is disposed and mounted again with everything it rendered: its body
|
|
5448
|
+
* made new objects for its children, and a child kept from before
|
|
5449
|
+
* would go on holding the old ones. Scroll offsets on containers that
|
|
5003
5450
|
* survive are kept, because the node is kept. Component state in a
|
|
5004
5451
|
* replaced module is lost, which is the honest cost and the reason
|
|
5005
5452
|
* this is a development tool.
|
|
@@ -5051,6 +5498,10 @@ var GessoRuntime = class {
|
|
|
5051
5498
|
clearTimeout(this.rendererTimer);
|
|
5052
5499
|
this.rendererTimer = null;
|
|
5053
5500
|
}
|
|
5501
|
+
if (this.semanticsSweepTimer !== null) {
|
|
5502
|
+
clearTimeout(this.semanticsSweepTimer);
|
|
5503
|
+
this.semanticsSweepTimer = null;
|
|
5504
|
+
}
|
|
5054
5505
|
this.detachViewportInsetEnvironment?.();
|
|
5055
5506
|
this.detachViewportInsetEnvironment = null;
|
|
5056
5507
|
this.viewportInsetWrite?.();
|
|
@@ -5129,7 +5580,10 @@ var GessoRuntime = class {
|
|
|
5129
5580
|
if (isObservable(definition)) throw new Error("Root definition cannot be an Observable. Wrap it in a component or static element.");
|
|
5130
5581
|
if (isComponentLikeElement(definition)) {
|
|
5131
5582
|
if (depth > MAX_ROOT_COMPONENT_DEPTH) throw new Error(`Root component chain exceeded ${MAX_ROOT_COMPONENT_DEPTH} levels without producing an element.`);
|
|
5132
|
-
const
|
|
5583
|
+
const anchorId = `app:component:${depth}`;
|
|
5584
|
+
const before = this.resolver.hostFor(anchorId);
|
|
5585
|
+
if (before !== void 0 && before.component !== definition.component && this.root !== void 0) this.builder.reconcileChildren(this.root, []);
|
|
5586
|
+
const output = this.resolver.resolve(definition, anchorId);
|
|
5133
5587
|
return this.resolveRootElement(output, depth + 1);
|
|
5134
5588
|
}
|
|
5135
5589
|
return definition;
|
|
@@ -5241,7 +5695,7 @@ var GessoRuntime = class {
|
|
|
5241
5695
|
op: "add",
|
|
5242
5696
|
node
|
|
5243
5697
|
})),
|
|
5244
|
-
boxes: this.
|
|
5698
|
+
boxes: this.sweepSemanticsBoxes(),
|
|
5245
5699
|
focused: this.focusManager.focusedNode?.id ?? null
|
|
5246
5700
|
});
|
|
5247
5701
|
}
|
|
@@ -5343,18 +5797,21 @@ var GessoRuntime = class {
|
|
|
5343
5797
|
if (this.applyTextRunAction(action)) return;
|
|
5344
5798
|
const node = this.graph.getNode(action.id);
|
|
5345
5799
|
if (node === void 0 || !this.semantics.has(action.id)) return;
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5800
|
+
this.actOn(node, action);
|
|
5801
|
+
}
|
|
5802
|
+
/**
|
|
5803
|
+
* A semantics action on a node: the mirror's, or `InspectionService.act`'s
|
|
5804
|
+
* on a node with no record of its own. False when it did not take: focus
|
|
5805
|
+
* refused (an open dialog's trap holds it elsewhere), or a field that
|
|
5806
|
+
* takes no text.
|
|
5807
|
+
*/
|
|
5808
|
+
actOn(node, action) {
|
|
5809
|
+
if (action.action === "focus") return this.focusManager.focus(node);
|
|
5810
|
+
if (action.action === "setValue") return this.focusManager.focus(node) && this.input.editing.replaceText(action.value ?? "");
|
|
5355
5811
|
this.focusManager.focus(node);
|
|
5356
5812
|
const box = this.engine.screenBox(node);
|
|
5357
5813
|
this.dispatcher.dispatch(new UiPointerEvent(UiEventType.Click, box.x + box.width / 2, box.y + box.height / 2, 1), node);
|
|
5814
|
+
return true;
|
|
5358
5815
|
}
|
|
5359
5816
|
/**
|
|
5360
5817
|
* An action on an inline link, which is a run and not a node.
|
|
@@ -5398,7 +5855,7 @@ var GessoRuntime = class {
|
|
|
5398
5855
|
else patches = this.rescopeSemantics(frame) ?? this.restructureSemantics(frame) ?? this.rebuildSemantics();
|
|
5399
5856
|
}
|
|
5400
5857
|
if (listener === null) return;
|
|
5401
|
-
const boxes =
|
|
5858
|
+
const boxes = patches.length > 0 ? this.sweepSemanticsBoxes() : moved ? this.movedSemanticsBoxes() : EMPTY_BOXES;
|
|
5402
5859
|
const focused = this.focusManager.focusedNode?.id ?? null;
|
|
5403
5860
|
const focusMoved = focused !== this.lastFocusedId;
|
|
5404
5861
|
this.lastFocusedId = focused;
|
|
@@ -5695,6 +6152,70 @@ var GessoRuntime = class {
|
|
|
5695
6152
|
return ids;
|
|
5696
6153
|
}
|
|
5697
6154
|
/**
|
|
6155
|
+
* The boxes for a frame that moved something but changed no meaning:
|
|
6156
|
+
* all of them when it is the first move after a rest, and otherwise
|
|
6157
|
+
* only the focused node's, with the sweep owed for when the moving
|
|
6158
|
+
* stops (`SEMANTICS_SETTLE_MS`), or for when the boxes have been stale
|
|
6159
|
+
* too long (`SEMANTICS_MOST_STALE_MS`).
|
|
6160
|
+
*
|
|
6161
|
+
* A pan or a zoom moves every box under the camera on every frame. In
|
|
6162
|
+
* Atelier, a board of three dozen screens panned at 15%, the sweep was
|
|
6163
|
+
* 3 to 4 ms of a 4 to 6 ms frame, with frames to 13 ms, past a 120 Hz
|
|
6164
|
+
* display's 8.3; and the main thread wrote four styles on each of 545
|
|
6165
|
+
* elements per frame, which in Chromium was 45% of that thread while
|
|
6166
|
+
* it also had the wheel events to deliver. Nobody reads the rectangle
|
|
6167
|
+
* of an element that is moving under a pan: a screen reader's cursor
|
|
6168
|
+
* is on what it rests on. The focused node is the exception, since its
|
|
6169
|
+
* focus ring is drawn from its element, so it moves every frame.
|
|
6170
|
+
*/
|
|
6171
|
+
movedSemanticsBoxes() {
|
|
6172
|
+
const at = now();
|
|
6173
|
+
const resting = at - this.semanticsMovedAt > SEMANTICS_SETTLE_MS;
|
|
6174
|
+
this.semanticsMovedAt = at;
|
|
6175
|
+
if (resting || at - this.semanticsSweptAt > SEMANTICS_MOST_STALE_MS) return this.sweepSemanticsBoxes();
|
|
6176
|
+
if (this.semanticsSweepTimer === null) this.scheduleSemanticsSweep(SEMANTICS_SETTLE_MS);
|
|
6177
|
+
return this.focusedSemanticsBox();
|
|
6178
|
+
}
|
|
6179
|
+
/** Sweeps the boxes once the moving has stopped, and sends what changed. */
|
|
6180
|
+
scheduleSemanticsSweep(wait) {
|
|
6181
|
+
this.semanticsSweepTimer = setTimeout(() => {
|
|
6182
|
+
this.semanticsSweepTimer = null;
|
|
6183
|
+
const listener = this.semanticsListener;
|
|
6184
|
+
if (listener === null) return;
|
|
6185
|
+
const left = this.semanticsMovedAt + SEMANTICS_SETTLE_MS - now();
|
|
6186
|
+
if (left > 0) {
|
|
6187
|
+
this.scheduleSemanticsSweep(left);
|
|
6188
|
+
return;
|
|
6189
|
+
}
|
|
6190
|
+
const boxes = this.sweepSemanticsBoxes();
|
|
6191
|
+
if (boxes.length > 0) listener({
|
|
6192
|
+
patches: EMPTY_PATCHES,
|
|
6193
|
+
boxes
|
|
6194
|
+
});
|
|
6195
|
+
}, Math.max(1, wait));
|
|
6196
|
+
}
|
|
6197
|
+
sweepSemanticsBoxes() {
|
|
6198
|
+
this.semanticsSweptAt = now();
|
|
6199
|
+
if (this.semanticsSweepTimer !== null) {
|
|
6200
|
+
clearTimeout(this.semanticsSweepTimer);
|
|
6201
|
+
this.semanticsSweepTimer = null;
|
|
6202
|
+
}
|
|
6203
|
+
return this.collectSemanticsBoxes();
|
|
6204
|
+
}
|
|
6205
|
+
/** The focused node's box when it differs from the one last sent, as `collectSemanticsBoxes` would give it. */
|
|
6206
|
+
focusedSemanticsBox() {
|
|
6207
|
+
const focused = this.focusManager.focusedNode;
|
|
6208
|
+
if (focused === null || focused === void 0 || !this.semantics.has(focused.id) || this.engine.recordFor(focused) === void 0) return EMPTY_BOXES;
|
|
6209
|
+
const box = this.engine.screenBox(focused);
|
|
6210
|
+
const last = this.semanticsBoxes.get(focused.id);
|
|
6211
|
+
if (last !== void 0 && boxesEqual(last, box)) return EMPTY_BOXES;
|
|
6212
|
+
this.semanticsBoxes.set(focused.id, box);
|
|
6213
|
+
return [{
|
|
6214
|
+
id: focused.id,
|
|
6215
|
+
box
|
|
6216
|
+
}];
|
|
6217
|
+
}
|
|
6218
|
+
/**
|
|
5698
6219
|
* The mirrored nodes whose box differs from the one last sent, and
|
|
5699
6220
|
* which are on screen.
|
|
5700
6221
|
*
|
|
@@ -5793,6 +6314,7 @@ var GessoRuntime = class {
|
|
|
5793
6314
|
childX = x - (rec.scrollable ? rec.scrollX : 0);
|
|
5794
6315
|
childY = y - (rec.scrollable ? rec.scrollY : 0);
|
|
5795
6316
|
}
|
|
6317
|
+
if (node.hold !== null && !(focused != null && focused.heldBy === node)) continue;
|
|
5796
6318
|
for (let child = node.lastChild; child !== null; child = child.previousSibling) stack.push([
|
|
5797
6319
|
child,
|
|
5798
6320
|
childX,
|
|
@@ -5921,6 +6443,7 @@ var GessoRuntime = class {
|
|
|
5921
6443
|
this.frameBegan = now();
|
|
5922
6444
|
this.phaseTimings = emptyPhaseTimings();
|
|
5923
6445
|
this.frameTime = time;
|
|
6446
|
+
this.releaseWheel();
|
|
5924
6447
|
this.phaseTimings.ticks = this.timePhase(() => this.visible && this.animations.isRunning, () => this.animations.advance(time));
|
|
5925
6448
|
this.scheduleAnimationTick(time);
|
|
5926
6449
|
this.phaseTimings.patches = this.timePhase(() => this.replicas.some((replica) => replica.hasPendingPatches), () => {
|
|
@@ -6092,10 +6615,18 @@ var GessoRuntime = class {
|
|
|
6092
6615
|
scrollY: record?.scrollY ?? 0
|
|
6093
6616
|
};
|
|
6094
6617
|
};
|
|
6618
|
+
const readFlow = (node) => {
|
|
6619
|
+
const record = this.engine.recordFor(node);
|
|
6620
|
+
return {
|
|
6621
|
+
box: record === void 0 ? NO_BOX : this.engine.worldBox(node),
|
|
6622
|
+
scrollX: record?.scrollX ?? 0,
|
|
6623
|
+
scrollY: record?.scrollY ?? 0
|
|
6624
|
+
};
|
|
6625
|
+
};
|
|
6095
6626
|
this.deferredReveals = [];
|
|
6096
6627
|
try {
|
|
6097
6628
|
for (;;) {
|
|
6098
|
-
if (!this.layoutNotifier.isEmpty()) this.layoutNotifier.notify(read);
|
|
6629
|
+
if (!this.layoutNotifier.isEmpty()) this.layoutNotifier.notify(read, readFlow);
|
|
6099
6630
|
if (!this.layoutPending()) {
|
|
6100
6631
|
this.finishSettling(settled.passes > 0);
|
|
6101
6632
|
if (!this.layoutPending()) break;
|
|
@@ -6144,6 +6675,39 @@ var GessoRuntime = class {
|
|
|
6144
6675
|
this.warnedLayoutPasses = true;
|
|
6145
6676
|
console.warn(`Gesso: a frame laid out ${MAX_LAYOUT_PASSES} times and its layout listeners were still changing it, so it was painted unsettled and the rest left for the next frame. Something on \`host.onLayout\` (a breakpoint, a sizeContainer, a Responsive, a modifier of your own) is writing a property that changes the box it is listening to, back and forth. Shown once.`);
|
|
6146
6677
|
}
|
|
6678
|
+
/**
|
|
6679
|
+
* Holds each subtree that declares `holdUnseen` while it cannot be
|
|
6680
|
+
* read, and lets it go on the frame it can (see `UiGraph.hold`).
|
|
6681
|
+
*
|
|
6682
|
+
* Decided after the frame is drawn, from where it was drawn: a subtree
|
|
6683
|
+
* is only ever held once it has been laid out and painted, so what is
|
|
6684
|
+
* kept of it is what it showed, and one let go catches up on the next
|
|
6685
|
+
* frame, which its own marks arm. Only frames that moved or laid
|
|
6686
|
+
* something out can change the answer, so the rest pay nothing.
|
|
6687
|
+
*/
|
|
6688
|
+
updateHolds(frame, laidOut) {
|
|
6689
|
+
let found = false;
|
|
6690
|
+
for (const [node, flags] of frame.entries()) if ((flags & DirtyFlags.Properties) !== 0 && node.properties.get("holdUnseen") !== void 0 && !this.holdable.has(node)) {
|
|
6691
|
+
this.holdable.add(node);
|
|
6692
|
+
found = true;
|
|
6693
|
+
}
|
|
6694
|
+
if (this.holdable.size === 0 || !laidOut && !found) return;
|
|
6695
|
+
for (const node of this.holdable) {
|
|
6696
|
+
const how = node.properties.get("holdUnseen");
|
|
6697
|
+
if (how === void 0 || this.graph.getNode(node.id) !== node) {
|
|
6698
|
+
this.graph.release(node);
|
|
6699
|
+
this.holdable.delete(node);
|
|
6700
|
+
continue;
|
|
6701
|
+
}
|
|
6702
|
+
const rec = this.engine.recordFor(node);
|
|
6703
|
+
if (rec === void 0 || !(rec.width > 0) || !(rec.height > 0)) continue;
|
|
6704
|
+
const box = this.engine.screenBox(node);
|
|
6705
|
+
const margin = how === true ? 0 : how.margin ?? 0;
|
|
6706
|
+
const below = how === true ? 0 : how.belowScale ?? 0;
|
|
6707
|
+
if (box.x + box.width > -margin && box.y + box.height > -margin && box.x < this.width + margin && box.y < this.height + margin && box.width / rec.width >= below) this.graph.release(node);
|
|
6708
|
+
else this.graph.hold(node);
|
|
6709
|
+
}
|
|
6710
|
+
}
|
|
6147
6711
|
handleFrame(collected) {
|
|
6148
6712
|
const root = this.root;
|
|
6149
6713
|
if (root === void 0) return;
|
|
@@ -6175,11 +6739,13 @@ var GessoRuntime = class {
|
|
|
6175
6739
|
overlay: overlay?.shapes,
|
|
6176
6740
|
changes
|
|
6177
6741
|
}));
|
|
6742
|
+
this.updateHolds(frame, laidOut);
|
|
6178
6743
|
if (overlay !== null) {
|
|
6179
6744
|
if (laidOut) this.sendInspection();
|
|
6180
6745
|
this.scheduleInspectorRepaint(overlay.nextChange);
|
|
6181
6746
|
}
|
|
6182
6747
|
this.sendDevtoolsUpdates(frame);
|
|
6748
|
+
this.framesFailed = 0;
|
|
6183
6749
|
const finished = now();
|
|
6184
6750
|
const elapsed = finished - (this.frameBegan ?? started);
|
|
6185
6751
|
this.frameBegan = null;
|
|
@@ -6522,6 +7088,14 @@ function semanticIndices(list) {
|
|
|
6522
7088
|
* one frame still hands the mirror the rows it is about to show.
|
|
6523
7089
|
*/
|
|
6524
7090
|
const SEMANTICS_VIEWPORT_MARGIN = 400;
|
|
7091
|
+
/**
|
|
7092
|
+
* How long a run of moving frames has to pause before the mirrored
|
|
7093
|
+
* boxes are swept again; see `movedSemanticsBoxes`. Longer than the gap
|
|
7094
|
+
* between a trackpad's wheel events, so a pan is one run.
|
|
7095
|
+
*/
|
|
7096
|
+
const SEMANTICS_SETTLE_MS = 120;
|
|
7097
|
+
/** The longest the mirrored boxes go unswept while something keeps moving: an endless animation still gets them. */
|
|
7098
|
+
const SEMANTICS_MOST_STALE_MS = 1e3;
|
|
6525
7099
|
const EMPTY_PATCHES = [];
|
|
6526
7100
|
const EMPTY_BOXES = [];
|
|
6527
7101
|
function boxesEqual(a, b) {
|
|
@@ -6536,6 +7110,19 @@ function boxesEqual(a, b) {
|
|
|
6536
7110
|
function frameChangedTree(frame) {
|
|
6537
7111
|
return frameNeedsSemantics(frame);
|
|
6538
7112
|
}
|
|
7113
|
+
/** The box a node with no record reports. */
|
|
7114
|
+
const NO_BOX = {
|
|
7115
|
+
x: 0,
|
|
7116
|
+
y: 0,
|
|
7117
|
+
width: 0,
|
|
7118
|
+
height: 0
|
|
7119
|
+
};
|
|
7120
|
+
/** Whether two wheel steps can be added together: the same keys held, in the same unit. */
|
|
7121
|
+
function sameWheel(a, b) {
|
|
7122
|
+
const [, , , , keysA, unitA] = a;
|
|
7123
|
+
const [, , , , keysB, unitB] = b;
|
|
7124
|
+
return unitA === unitB && (keysA?.ctrl ?? false) === (keysB?.ctrl ?? false) && (keysA?.shift ?? false) === (keysB?.shift ?? false) && (keysA?.alt ?? false) === (keysB?.alt ?? false) && (keysA?.meta ?? false) === (keysB?.meta ?? false);
|
|
7125
|
+
}
|
|
6539
7126
|
function frameNeedsSemantics(frame) {
|
|
6540
7127
|
return frame.anyFlags(DirtyFlags.Semantics | DirtyFlags.Children);
|
|
6541
7128
|
}
|
|
@@ -6752,25 +7339,58 @@ async function loadModule(module, load) {
|
|
|
6752
7339
|
}
|
|
6753
7340
|
/** The export to draw, or why none can be chosen. */
|
|
6754
7341
|
function chooseExport(exports, wanted, module) {
|
|
6755
|
-
const drawable = (value) => typeof value === "function" || isUiElement(value);
|
|
7342
|
+
const drawable = (value) => typeof value === "function" || isUiElement(value) || examplesOf(value) !== null;
|
|
6756
7343
|
if (wanted !== void 0) {
|
|
6757
|
-
|
|
7344
|
+
const [head, ...rest] = wanted.split(".");
|
|
7345
|
+
let value = exports[head];
|
|
7346
|
+
for (const key of rest) value = typeof value === "object" && value !== null ? value[key] : void 0;
|
|
7347
|
+
if (!drawable(value)) return `${module} has no component or element exported as ${wanted}. It exports ${list(exports, drawable)}.`;
|
|
7348
|
+
const examples = examplesOf(value);
|
|
6758
7349
|
return {
|
|
6759
7350
|
name: wanted,
|
|
6760
|
-
value:
|
|
7351
|
+
value: examples === null ? value : examplesSheet(examples)
|
|
6761
7352
|
};
|
|
6762
7353
|
}
|
|
7354
|
+
const asDrawn = (value) => {
|
|
7355
|
+
const examples = examplesOf(value);
|
|
7356
|
+
return examples === null ? value : examplesSheet(examples);
|
|
7357
|
+
};
|
|
6763
7358
|
if (drawable(exports.default)) return {
|
|
6764
7359
|
name: "default",
|
|
6765
|
-
value: exports.default
|
|
7360
|
+
value: asDrawn(exports.default)
|
|
6766
7361
|
};
|
|
6767
7362
|
const candidates = Object.keys(exports).filter((name) => /^[A-Z]/.test(name) && drawable(exports[name]));
|
|
6768
7363
|
if (candidates.length === 1) return {
|
|
6769
7364
|
name: candidates[0],
|
|
6770
|
-
value: exports[candidates[0]]
|
|
7365
|
+
value: asDrawn(exports[candidates[0]])
|
|
6771
7366
|
};
|
|
6772
7367
|
return candidates.length === 0 ? `${module} exports nothing to draw: no component, and no element.` : `${module} exports ${candidates.join(", ")}; say which with export.`;
|
|
6773
7368
|
}
|
|
7369
|
+
/**
|
|
7370
|
+
* An object of examples, as a component's states are kept for a design
|
|
7371
|
+
* tool (`export const Button = { Primary: <Button … />, … }`): each
|
|
7372
|
+
* element by its name, or null for anything else.
|
|
7373
|
+
*/
|
|
7374
|
+
function examplesOf(value) {
|
|
7375
|
+
if (typeof value !== "object" || value === null || isUiElement(value) || Array.isArray(value)) return null;
|
|
7376
|
+
const entries = Object.entries(value).filter((entry) => isUiElement(entry[1]));
|
|
7377
|
+
return entries.length === 0 ? null : entries;
|
|
7378
|
+
}
|
|
7379
|
+
/** Every example, each under its name, down the frame. */
|
|
7380
|
+
function examplesSheet(examples) {
|
|
7381
|
+
return Column({
|
|
7382
|
+
gap: 24,
|
|
7383
|
+
padding: 16
|
|
7384
|
+
}, ...examples.map(([name, element]) => Column({
|
|
7385
|
+
key: name,
|
|
7386
|
+
gap: 8
|
|
7387
|
+
}, Text({
|
|
7388
|
+
text: name,
|
|
7389
|
+
fontSize: 12,
|
|
7390
|
+
fontWeight: "600",
|
|
7391
|
+
color: "textMuted"
|
|
7392
|
+
}), element)));
|
|
7393
|
+
}
|
|
6774
7394
|
function list(exports, keep) {
|
|
6775
7395
|
const names = Object.keys(exports).filter((name) => keep(exports[name]));
|
|
6776
7396
|
return names.length === 0 ? "nothing that can be drawn" : names.join(", ");
|
|
@@ -6790,6 +7410,6 @@ function failure(text) {
|
|
|
6790
7410
|
};
|
|
6791
7411
|
}
|
|
6792
7412
|
//#endregion
|
|
6793
|
-
export {
|
|
7413
|
+
export { ComponentHostResolver as A, ChannelRegistry as B, ShellService as C, isDiscreteInputMessage as D, epochNow as E, FocusService as F, Input as G, Channel as H, boundFocus as I, BoundsCell as J, Output as K, OverlayScopeKey as L, StartingInputsKey as M, OverlayLayer as N, isInputMessage as O, __decorate as P, OverlayService as R, FrameService as S, epochFromEvent as T, Define as U, ChannelReplica as V, Inject as W, ServiceRegistry as X, bounds as Y, Component as Z, parsePattern as _, UI_FRAME_PHASES as a, AudioService as b, TextService as c, ScrollService as d, EditingService as f, formatUrl as g, buildPath as h, GessoRuntime as i, ComponentHost as j, modifiersFrom as k, FontService as l, RouterService as m, renderFrame as n, InspectionService as o, FindService as p, liveInput as q, isClaimedError as r, AnimationService as s, renderModule as t, MediaService as u, parseUrl as v, shellFilesUnsupported as w, treeText as x, RouteState as y, isClassComponent as z };
|
|
6794
7414
|
|
|
6795
|
-
//# sourceMappingURL=render-
|
|
7415
|
+
//# sourceMappingURL=render-DlhL5y0d.js.map
|