gesso-framework 0.4.2 → 0.5.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/CHANGELOG.md +106 -0
- package/README.md +2 -0
- package/dist/ChannelProtocol-ByNoHujM.d.ts +247 -0
- package/dist/{FunctionComponent-CgwLKE5d.d.ts → FunctionComponent-fYMdePtH.d.ts} +3 -247
- package/dist/agent/index.d.ts +317 -0
- package/dist/agent/index.js +184 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/app-BvlIO1G9.js +326 -0
- package/dist/app-BvlIO1G9.js.map +1 -0
- package/dist/{index-C9FAI_Kt.d.ts → index-BDM_gzzZ.d.ts} +377 -268
- package/dist/index.d.ts +5 -3
- package/dist/index.js +971 -89
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +1 -1
- package/dist/{persisted-CsTPnjkc.js → persisted-pix1fS1D.js} +6 -1
- package/dist/{persisted-CsTPnjkc.js.map → persisted-pix1fS1D.js.map} +1 -1
- package/dist/remote-xxij8zXe.js +600 -0
- package/dist/remote-xxij8zXe.js.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/ui-U39HjNFA.d.ts +517 -0
- package/dist/webmcp-CJVFvAHe.js +77 -0
- package/dist/webmcp-CJVFvAHe.js.map +1 -0
- package/dist/worker/index.d.ts +4 -2
- package/dist/worker/index.js +1 -1
- package/package.json +12 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { $ as structurallyEqual, A as printPropValue, B as channel, C as captureConsole, D as formatNodePath, E as formatAge, F as applyPatch, G as mutate, H as viewKeys, I as applyPatches, J as select, K as Resource, L as diffProjection, M as provide, N as findUnplainPath, O as formatNodeReport, P as requirePlainData, Q as equalityOf, R as isChannelClientMessage, S as workerHandle, T as describeStream, U as debounced, V as defineChannel, W as throttled, X as computed, Y as ComputedCell, Z as derive, _ as isHubMessage, a as undoable, at as isOutputTarget, b as portHandle, c as classifyStorageError, ct as trackRead, d as storageReadValue, et as InternalState, f as pick, g as APPLICATION_WORKER, h as serveChannels, i as OpfsStorage, it as into, j as ProvidedChannel, k as formatStream, l as storageErrorMessage, lt as withBodyOf, m as serve, n as persisted, nt as InputCell, o as UndoStack, ot as output, p as pickKeys, q as resource, r as IndexedDbStorage, rt as input, s as MemoryStorage, st as outputTargetOf, t as PersistedState, tt as internalState, u as storageReadFailure, v as isPortErrorMessage, w as isConsoleEntryMessage, x as servePorts, y as isPortHandshake, z as isChannelHostMessage } from "./persisted-
|
|
1
|
+
import { $ as structurallyEqual, A as printPropValue, B as channel, C as captureConsole, D as formatNodePath, E as formatAge, F as applyPatch, G as mutate, H as viewKeys, I as applyPatches, J as select, K as Resource, L as diffProjection, M as provide, N as findUnplainPath, O as formatNodeReport, P as requirePlainData, Q as equalityOf, R as isChannelClientMessage, S as workerHandle, T as describeStream, U as debounced, V as defineChannel, W as throttled, X as computed, Y as ComputedCell, Z as derive, _ as isHubMessage, a as undoable, at as isOutputTarget, b as portHandle, c as classifyStorageError, ct as trackRead, d as storageReadValue, et as InternalState, f as pick, g as APPLICATION_WORKER, h as serveChannels, i as OpfsStorage, it as into, j as ProvidedChannel, k as formatStream, l as storageErrorMessage, lt as withBodyOf, m as serve, n as persisted, nt as InputCell, o as UndoStack, ot as output, p as pickKeys, q as resource, r as IndexedDbStorage, rt as input, s as MemoryStorage, st as outputTargetOf, t as PersistedState, tt as internalState, u as storageReadFailure, v as isPortErrorMessage, w as isConsoleEntryMessage, x as servePorts, y as isPortHandshake, z as isChannelHostMessage } from "./persisted-pix1fS1D.js";
|
|
2
2
|
import { a as Each, i as show, n as getComponentMetadata, o as each, r as Show, t as createComponent } from "./createComponent-3n1ZSql8.js";
|
|
3
|
+
import { c as channelSchema, l as describeChannel, t as AGENT_PORT } from "./remote-xxij8zXe.js";
|
|
4
|
+
import { n as serveApplicationAgent } from "./app-BvlIO1G9.js";
|
|
3
5
|
import { BehaviorSubject, EMPTY, Observable, Subject, Subscription, distinctUntilChanged, map } from "rxjs";
|
|
4
|
-
import { AnimationDriver, Box, Canvas2DRenderer, CanvasPlatformSurface, CanvasTextMeasurer, Constraints, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, EnvironmentNotifier, FocusNotifier, IconRasterizer, LayoutEngine, LayoutInspector, LayoutNotifier, MARK_PREFIX, Stack, TEXT_RUN_ID_SEPARATOR, UI_ROLES, UI_SEMANTIC_STATES, UiAnimationFrameClock, UiEditingController, UiEnvironmentKeys, UiEventType, UiFindController, UiFocusManager, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInsetRegistry, UiKeyboardController, UiNodeType, UiPlatformAdapter, 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, capturePointer, createCanvasSurface, createTween, createWebGPUSurface, defaultMotion, describeOverrides, diffSemantics, dragSessionFor, easings, formatExplanation, insetsEqual, isComponentLikeElement, isObservable, isWebGPUAvailable, linear, linkOf, markInstant, markNow, measure, measureSpan, motion, noInsets, observeViewportInsets, percent, performanceMarksEnabled, pointerDeviceOf, prepareInputSurface, propertyEffects, recordsEqual, registerFontStack, resolveCursor, scrollRange, scrollbarThumb, semanticsInertAbove, setPerformanceMarks, setPerformanceMarks as setPerformanceMarks$1, textRunOfRecordId, themeTokens, touchActionFor, wheelDeltaYOf } from "gesso-core";
|
|
6
|
+
import { AnimationDriver, Box, Canvas2DRenderer, CanvasPlatformSurface, CanvasTextMeasurer, Constraints, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, EnvironmentNotifier, FocusNotifier, IconRasterizer, LayoutEngine, LayoutInspector, LayoutNotifier, MARK_PREFIX, Stack, TEXT_RUN_ID_SEPARATOR, UI_ROLES, UI_SEMANTIC_STATES, UiAnimationFrameClock, UiEditingController, UiEnvironmentKeys, UiEventType, UiFindController, UiFocusManager, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInsetRegistry, UiKeyboardController, UiNodeType, UiPlatformAdapter, 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, capturePointer, createCanvasSurface, createTween, createWebGPUSurface, defaultMotion, describeOverrides, diffSemantics, dragSessionFor, dropIndexShifts, easings, formatExplanation, insetsEqual, isComponentLikeElement, isObservable, isWebGPUAvailable, linear, linkOf, markInstant, markNow, measure, measureSpan, motion, noInsets, observeViewportInsets, percent, performanceMarksEnabled, pointerDeviceOf, prepareInputSurface, propertyEffects, recordsEqual, registerFontStack, resolveCursor, rewalkSemantics, scrollRange, scrollbarThumb, semanticsInertAbove, semanticsMemory, setPerformanceMarks, setPerformanceMarks as setPerformanceMarks$1, textRunOfRecordId, themeTokens, touchActionFor, wheelDeltaYOf } from "gesso-core";
|
|
5
7
|
//#region src/Component.ts
|
|
6
8
|
/**
|
|
7
9
|
* Base class for all Gesso framework components.
|
|
@@ -759,6 +761,7 @@ function createChannelRegistry(registrations, onError) {
|
|
|
759
761
|
}
|
|
760
762
|
return {
|
|
761
763
|
registry,
|
|
764
|
+
workers: handles,
|
|
762
765
|
dispose: () => {
|
|
763
766
|
for (const channel of local) channel.dispose();
|
|
764
767
|
local.length = 0;
|
|
@@ -1143,12 +1146,12 @@ var ComponentHost = class {
|
|
|
1143
1146
|
return;
|
|
1144
1147
|
}
|
|
1145
1148
|
if (isObservable(provided)) {
|
|
1146
|
-
const subscription = provided.subscribe((value) => cell
|
|
1149
|
+
const subscription = provided.subscribe((value) => pushInput(cell, value));
|
|
1147
1150
|
this.inputSubscriptions.set(inputName, subscription);
|
|
1148
1151
|
this.subscriptions.add(subscription);
|
|
1149
1152
|
return;
|
|
1150
1153
|
}
|
|
1151
|
-
cell
|
|
1154
|
+
pushInput(cell, provided);
|
|
1152
1155
|
}
|
|
1153
1156
|
validateInputs() {
|
|
1154
1157
|
const metadata = getComponentMetadata(this.element.component);
|
|
@@ -1173,6 +1176,27 @@ var ComponentHost = class {
|
|
|
1173
1176
|
}
|
|
1174
1177
|
}
|
|
1175
1178
|
};
|
|
1179
|
+
/**
|
|
1180
|
+
* Hands an input cell a value from its parent, unless it already holds
|
|
1181
|
+
* that very value.
|
|
1182
|
+
*
|
|
1183
|
+
* A parent that re-renders hands every child its props again, and a
|
|
1184
|
+
* prop that is an Observable built in the parent's render is a new
|
|
1185
|
+
* source each time, which re-subscribes and replays the current value.
|
|
1186
|
+
* Pushed through regardless, that replay re-ran every binding derived
|
|
1187
|
+
* from the input. In the issue tracker's editor, inserting one block
|
|
1188
|
+
* re-rendered the list of 2,868, each replay rebuilt its block's text
|
|
1189
|
+
* runs as a new array, and every editable on the page was re-measured:
|
|
1190
|
+
* 12,912 nodes for one Enter. Values that cross into a component are
|
|
1191
|
+
* treated as immutable everywhere else in the framework (bindings hold
|
|
1192
|
+
* on to them, patches share structure), so the same reference is the
|
|
1193
|
+
* same value and there is nothing to tell the child.
|
|
1194
|
+
*/
|
|
1195
|
+
function pushInput(cell, value) {
|
|
1196
|
+
const current = cell.getValue();
|
|
1197
|
+
if (current !== void 0 && Object.is(current, value)) return;
|
|
1198
|
+
cell.next(value);
|
|
1199
|
+
}
|
|
1176
1200
|
//#endregion
|
|
1177
1201
|
//#region src/ComponentHostResolver.ts
|
|
1178
1202
|
/**
|
|
@@ -1704,6 +1728,7 @@ function filesDuringDrag(event) {
|
|
|
1704
1728
|
var ShellService = class {
|
|
1705
1729
|
handler = null;
|
|
1706
1730
|
scheme = internalState("light");
|
|
1731
|
+
contrastState = internalState("standard");
|
|
1707
1732
|
insets = internalState(noInsets);
|
|
1708
1733
|
isFullscreen = internalState(false);
|
|
1709
1734
|
/** Popups asked for and not yet answered, by the id sent with each. */
|
|
@@ -1712,6 +1737,9 @@ var ShellService = class {
|
|
|
1712
1737
|
/** Storage requests asked for and not yet answered, by the id sent with each. */
|
|
1713
1738
|
stores = /* @__PURE__ */ new Map();
|
|
1714
1739
|
nextStorageId = 1;
|
|
1740
|
+
/** Clipboard writes asked for and not yet answered, by the id sent with each. */
|
|
1741
|
+
copies = /* @__PURE__ */ new Map();
|
|
1742
|
+
nextCopyId = 1;
|
|
1715
1743
|
/** File requests asked for and not yet answered, by the id sent with each. */
|
|
1716
1744
|
files = /* @__PURE__ */ new Map();
|
|
1717
1745
|
nextFileId = 1;
|
|
@@ -1744,6 +1772,18 @@ var ShellService = class {
|
|
|
1744
1772
|
* read it beside a channel's view; its setter stays private here.
|
|
1745
1773
|
*/
|
|
1746
1774
|
colorScheme = this.scheme;
|
|
1775
|
+
/**
|
|
1776
|
+
* The contrast the platform is asking for: `high` when the person has
|
|
1777
|
+
* turned up contrast or turned on a contrast theme, `standard`
|
|
1778
|
+
* otherwise. Reported by the shell like `colorScheme`, and as with it,
|
|
1779
|
+
* what it means is the application's: `withContrast(theme, contrast)`
|
|
1780
|
+
* is the theme that answers it.
|
|
1781
|
+
*
|
|
1782
|
+
* const theme = combineLatest([shell.colorScheme, shell.contrast]).pipe(
|
|
1783
|
+
* map(([scheme, contrast]) => withContrast(scheme === 'dark' ? darkTheme : lightTheme, contrast))
|
|
1784
|
+
* );
|
|
1785
|
+
*/
|
|
1786
|
+
contrast = this.contrastState;
|
|
1747
1787
|
/** The current appearance, for code that needs it without subscribing. */
|
|
1748
1788
|
get currentColorScheme() {
|
|
1749
1789
|
return this.scheme.value;
|
|
@@ -1783,6 +1823,10 @@ var ShellService = class {
|
|
|
1783
1823
|
applyColorScheme(scheme) {
|
|
1784
1824
|
if (this.scheme.value !== scheme) this.scheme.value = scheme;
|
|
1785
1825
|
}
|
|
1826
|
+
/** Called by the runtime when the shell reports the contrast; not for applications. */
|
|
1827
|
+
applyContrast(contrast) {
|
|
1828
|
+
if (this.contrastState.value !== contrast) this.contrastState.value = contrast;
|
|
1829
|
+
}
|
|
1786
1830
|
/**
|
|
1787
1831
|
* Called by the runtime when the shell reports the platform's insets.
|
|
1788
1832
|
*
|
|
@@ -1793,12 +1837,44 @@ var ShellService = class {
|
|
|
1793
1837
|
applyViewportInsets(insets) {
|
|
1794
1838
|
if (!insetsEqual(this.insets.value, insets)) this.insets.value = insets;
|
|
1795
1839
|
}
|
|
1796
|
-
/**
|
|
1840
|
+
/**
|
|
1841
|
+
* Puts text on the system clipboard, and answers whether it got there.
|
|
1842
|
+
*
|
|
1843
|
+
* Most callers ignore the answer, and may: the text is on its way the
|
|
1844
|
+
* moment this returns. It is for an application that tells the person
|
|
1845
|
+
* what happened, a "Copied" toast after a menu command, say, which
|
|
1846
|
+
* would be a lie on the occasions the browser refused. It does refuse:
|
|
1847
|
+
* the clipboard wants a focused document, and some browsers a fresh
|
|
1848
|
+
* gesture, and a key pressed in a render worker reaches the window's
|
|
1849
|
+
* clipboard a message later than it reached the canvas.
|
|
1850
|
+
*
|
|
1851
|
+
* With no shell installed the answer is false, as `openPopup`'s is,
|
|
1852
|
+
* rather than a promise that never settles.
|
|
1853
|
+
*/
|
|
1797
1854
|
copyText(text) {
|
|
1798
|
-
this.handler
|
|
1855
|
+
const handler = this.handler;
|
|
1856
|
+
if (handler === void 0 || handler === null) return Promise.resolve(false);
|
|
1857
|
+
const id = this.nextCopyId++;
|
|
1858
|
+
const settled = new Promise((resolve) => {
|
|
1859
|
+
this.copies.set(id, resolve);
|
|
1860
|
+
});
|
|
1861
|
+
handler({
|
|
1799
1862
|
type: "clipboard",
|
|
1863
|
+
id,
|
|
1800
1864
|
text
|
|
1801
1865
|
});
|
|
1866
|
+
return settled;
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Called by the runtime when the shell reports whether a clipboard
|
|
1870
|
+
* write landed. Not for applications. An unknown id is ignored, on
|
|
1871
|
+
* `settlePopup`'s terms.
|
|
1872
|
+
*/
|
|
1873
|
+
settleClipboard(id, copied) {
|
|
1874
|
+
const resolve = this.copies.get(id);
|
|
1875
|
+
if (resolve === void 0) return;
|
|
1876
|
+
this.copies.delete(id);
|
|
1877
|
+
resolve(copied);
|
|
1802
1878
|
}
|
|
1803
1879
|
/** Opens a URL in the user's browser, in a new tab or window. */
|
|
1804
1880
|
openUrl(url) {
|
|
@@ -2591,7 +2667,7 @@ var EditingProxy = class {
|
|
|
2591
2667
|
* the reason the field's semantics live here rather than on a second
|
|
2592
2668
|
* element beside it.
|
|
2593
2669
|
*/
|
|
2594
|
-
describe(record) {
|
|
2670
|
+
describe(record, activeDescendant) {
|
|
2595
2671
|
if (this.disposed) return;
|
|
2596
2672
|
const textarea = this.textarea;
|
|
2597
2673
|
if (record === null) {
|
|
@@ -2603,7 +2679,9 @@ var EditingProxy = class {
|
|
|
2603
2679
|
"aria-required",
|
|
2604
2680
|
"aria-invalid",
|
|
2605
2681
|
"aria-readonly",
|
|
2606
|
-
"aria-disabled"
|
|
2682
|
+
"aria-disabled",
|
|
2683
|
+
"aria-expanded",
|
|
2684
|
+
"aria-activedescendant"
|
|
2607
2685
|
]) textarea.removeAttribute(attribute);
|
|
2608
2686
|
return;
|
|
2609
2687
|
}
|
|
@@ -2616,6 +2694,8 @@ var EditingProxy = class {
|
|
|
2616
2694
|
setOrClear(textarea, "aria-invalid", states.has("invalid") ? "true" : void 0);
|
|
2617
2695
|
setOrClear(textarea, "aria-readonly", states.has("readonly") ? "true" : void 0);
|
|
2618
2696
|
setOrClear(textarea, "aria-disabled", record.disabled === true ? "true" : void 0);
|
|
2697
|
+
setOrClear(textarea, "aria-expanded", states.has("expanded") ? "true" : states.has("collapsed") ? "false" : void 0);
|
|
2698
|
+
setOrClear(textarea, "aria-activedescendant", activeDescendant);
|
|
2619
2699
|
}
|
|
2620
2700
|
dispose() {
|
|
2621
2701
|
if (this.disposed) return;
|
|
@@ -2678,7 +2758,8 @@ var EditingProxy = class {
|
|
|
2678
2758
|
const clipboard = event.clipboardData;
|
|
2679
2759
|
event.preventDefault();
|
|
2680
2760
|
const text = clipboard?.getData("text/plain") ?? "";
|
|
2681
|
-
|
|
2761
|
+
const html = clipboard?.getData("text/html") ?? "";
|
|
2762
|
+
if (text.length > 0 || html.length > 0) this.sink.paste(text, html.length > 0 ? html : null);
|
|
2682
2763
|
};
|
|
2683
2764
|
/**
|
|
2684
2765
|
* A paste aimed at the canvas rather than at the field.
|
|
@@ -2703,6 +2784,7 @@ var EditingProxy = class {
|
|
|
2703
2784
|
if (clipboard === null || state === null) return;
|
|
2704
2785
|
event.preventDefault();
|
|
2705
2786
|
clipboard.setData("text/plain", state.text.slice(state.selectionStart, state.selectionEnd));
|
|
2787
|
+
if (state.html !== void 0) clipboard.setData("text/html", state.html);
|
|
2706
2788
|
};
|
|
2707
2789
|
const onCut = (event) => {
|
|
2708
2790
|
const state = this.state;
|
|
@@ -2757,17 +2839,15 @@ var EditingProxy = class {
|
|
|
2757
2839
|
}
|
|
2758
2840
|
};
|
|
2759
2841
|
/**
|
|
2760
|
-
* Writes text to the system clipboard from the main thread
|
|
2761
|
-
* API needs a secure context and, in
|
|
2762
|
-
* gesture; the `execCommand` fallback
|
|
2842
|
+
* Writes text to the system clipboard from the main thread, and answers
|
|
2843
|
+
* whether it got there. The async API needs a secure context and, in
|
|
2844
|
+
* some browsers, a recent user gesture; the `execCommand` fallback
|
|
2845
|
+
* covers the rest. False means both refused. It never rejects.
|
|
2763
2846
|
*/
|
|
2764
2847
|
function writeClipboard(text, doc = document) {
|
|
2765
2848
|
const clipboard = typeof navigator !== "undefined" ? navigator.clipboard : void 0;
|
|
2766
|
-
if (clipboard !== void 0 && typeof clipboard.writeText === "function")
|
|
2767
|
-
|
|
2768
|
-
return;
|
|
2769
|
-
}
|
|
2770
|
-
copyWithExecCommand(text, doc);
|
|
2849
|
+
if (clipboard !== void 0 && typeof clipboard.writeText === "function") return clipboard.writeText(text).then(() => true, () => copyWithExecCommand(text, doc));
|
|
2850
|
+
return Promise.resolve(copyWithExecCommand(text, doc));
|
|
2771
2851
|
}
|
|
2772
2852
|
function copyWithExecCommand(text, doc) {
|
|
2773
2853
|
const previous = doc.activeElement;
|
|
@@ -2778,7 +2858,9 @@ function copyWithExecCommand(text, doc) {
|
|
|
2778
2858
|
doc.body.appendChild(scratch);
|
|
2779
2859
|
scratch.select();
|
|
2780
2860
|
try {
|
|
2781
|
-
doc.execCommand("copy");
|
|
2861
|
+
return doc.execCommand("copy");
|
|
2862
|
+
} catch {
|
|
2863
|
+
return false;
|
|
2782
2864
|
} finally {
|
|
2783
2865
|
scratch.remove();
|
|
2784
2866
|
previous?.focus?.({ preventScroll: true });
|
|
@@ -2802,7 +2884,9 @@ const STATE_ATTRIBUTES = {
|
|
|
2802
2884
|
invalid: ["aria-invalid", "true"],
|
|
2803
2885
|
required: ["aria-required", "true"],
|
|
2804
2886
|
readonly: ["aria-readonly", "true"],
|
|
2805
|
-
modal: ["aria-modal", "true"]
|
|
2887
|
+
modal: ["aria-modal", "true"],
|
|
2888
|
+
multiselectable: ["aria-multiselectable", "true"],
|
|
2889
|
+
current: ["aria-current", "true"]
|
|
2806
2890
|
};
|
|
2807
2891
|
/**
|
|
2808
2892
|
* Roles whose checked state is required rather than optional.
|
|
@@ -2848,8 +2932,12 @@ const RECORD_ATTRIBUTES = [
|
|
|
2848
2932
|
"aria-invalid",
|
|
2849
2933
|
"aria-required",
|
|
2850
2934
|
"aria-readonly",
|
|
2851
|
-
"aria-modal"
|
|
2935
|
+
"aria-modal",
|
|
2936
|
+
"aria-multiselectable",
|
|
2937
|
+
"aria-current",
|
|
2938
|
+
"aria-activedescendant"
|
|
2852
2939
|
];
|
|
2940
|
+
let mirrors = 0;
|
|
2853
2941
|
/**
|
|
2854
2942
|
* Roles whose accessible name comes from what the element contains
|
|
2855
2943
|
* rather than from an `aria-label`, plus prose, which has no role at
|
|
@@ -2915,6 +3003,12 @@ var SemanticsMirror = class {
|
|
|
2915
3003
|
applying = false;
|
|
2916
3004
|
focusedId = null;
|
|
2917
3005
|
disposed = false;
|
|
3006
|
+
/**
|
|
3007
|
+
* Prefixes every element's DOM id, which `aria-activedescendant`
|
|
3008
|
+
* refers to. Per mirror, because two apps on one page have nodes with
|
|
3009
|
+
* the same ids.
|
|
3010
|
+
*/
|
|
3011
|
+
idPrefix = `gesso-${mirrors++}-`;
|
|
2918
3012
|
constructor(canvas, sink, editing = null) {
|
|
2919
3013
|
this.canvas = canvas;
|
|
2920
3014
|
this.sink = sink;
|
|
@@ -2930,7 +3024,7 @@ var SemanticsMirror = class {
|
|
|
2930
3024
|
width: "0px",
|
|
2931
3025
|
height: "0px",
|
|
2932
3026
|
pointerEvents: "none",
|
|
2933
|
-
overflow: "
|
|
3027
|
+
overflow: "clip",
|
|
2934
3028
|
margin: "0",
|
|
2935
3029
|
padding: "0",
|
|
2936
3030
|
border: "0",
|
|
@@ -2997,11 +3091,17 @@ var SemanticsMirror = class {
|
|
|
2997
3091
|
record
|
|
2998
3092
|
});
|
|
2999
3093
|
this.ids.set(element, record.id);
|
|
3094
|
+
element.id = this.idPrefix + record.id;
|
|
3000
3095
|
} else existing.record = record;
|
|
3001
3096
|
this.describe(element, record);
|
|
3002
3097
|
this.place(element, record);
|
|
3003
3098
|
if (existing?.box !== void 0) this.position(existing);
|
|
3004
|
-
if (record.id === this.focusedId) this.
|
|
3099
|
+
if (record.id === this.focusedId) this.describeEditing(this.editing?.active === true ? record : null);
|
|
3100
|
+
}
|
|
3101
|
+
/** Hands the proxy the focused editable's record, with its active descendant's DOM id. */
|
|
3102
|
+
describeEditing(record) {
|
|
3103
|
+
const active = record?.activeDescendant;
|
|
3104
|
+
this.editing?.describe(record, active === void 0 ? void 0 : this.idPrefix + active);
|
|
3005
3105
|
}
|
|
3006
3106
|
createElement() {
|
|
3007
3107
|
const element = this.doc.createElement("div");
|
|
@@ -3016,7 +3116,7 @@ var SemanticsMirror = class {
|
|
|
3016
3116
|
padding: "0",
|
|
3017
3117
|
border: "0",
|
|
3018
3118
|
outline: "none",
|
|
3019
|
-
overflow: "
|
|
3119
|
+
overflow: "clip",
|
|
3020
3120
|
pointerEvents: "none",
|
|
3021
3121
|
color: "transparent"
|
|
3022
3122
|
});
|
|
@@ -3047,6 +3147,7 @@ var SemanticsMirror = class {
|
|
|
3047
3147
|
setNumber(element, "aria-setsize", record.setSize);
|
|
3048
3148
|
setNumber(element, "aria-level", record.level);
|
|
3049
3149
|
if (record.valueText !== void 0) element.setAttribute("aria-valuetext", record.valueText);
|
|
3150
|
+
if (record.activeDescendant !== void 0) element.setAttribute("aria-activedescendant", this.idPrefix + record.activeDescendant);
|
|
3050
3151
|
}
|
|
3051
3152
|
/**
|
|
3052
3153
|
* Puts an element under its parent, at the index the record gives.
|
|
@@ -3146,7 +3247,7 @@ var SemanticsMirror = class {
|
|
|
3146
3247
|
this.focusedId = id;
|
|
3147
3248
|
const entry = id === null ? void 0 : this.entries.get(id);
|
|
3148
3249
|
if (this.editing !== null && this.editing.active) {
|
|
3149
|
-
this.
|
|
3250
|
+
this.describeEditing(entry?.record ?? null);
|
|
3150
3251
|
this.applying = true;
|
|
3151
3252
|
try {
|
|
3152
3253
|
this.editing.focus();
|
|
@@ -3155,7 +3256,7 @@ var SemanticsMirror = class {
|
|
|
3155
3256
|
}
|
|
3156
3257
|
return;
|
|
3157
3258
|
}
|
|
3158
|
-
this.
|
|
3259
|
+
this.describeEditing(null);
|
|
3159
3260
|
this.applying = true;
|
|
3160
3261
|
try {
|
|
3161
3262
|
if (entry !== void 0) entry.element.focus({ preventScroll: true });
|
|
@@ -3184,9 +3285,10 @@ var SemanticsMirror = class {
|
|
|
3184
3285
|
const onKeyUp = (event) => this.sink.keyUp?.(event);
|
|
3185
3286
|
const onPaste = (event) => {
|
|
3186
3287
|
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
3187
|
-
|
|
3288
|
+
const html = event.clipboardData?.getData("text/html") ?? "";
|
|
3289
|
+
if (text.length === 0 && html.length === 0) return;
|
|
3188
3290
|
event.preventDefault();
|
|
3189
|
-
this.sink.paste?.(text);
|
|
3291
|
+
this.sink.paste?.(text, html.length > 0 ? html : null);
|
|
3190
3292
|
};
|
|
3191
3293
|
this.container.addEventListener("click", onClick);
|
|
3192
3294
|
this.container.addEventListener("focusin", onFocusIn);
|
|
@@ -3402,6 +3504,49 @@ function observeColorScheme(onChange) {
|
|
|
3402
3504
|
return observeMediaQuery("(prefers-color-scheme: dark)", (dark) => onChange(dark ? "dark" : "light"));
|
|
3403
3505
|
}
|
|
3404
3506
|
//#endregion
|
|
3507
|
+
//#region src/app/contrast.ts
|
|
3508
|
+
/**
|
|
3509
|
+
* Watches the platform's contrast preference.
|
|
3510
|
+
*
|
|
3511
|
+
* `high` when the person has asked for more contrast
|
|
3512
|
+
* (`prefers-contrast: more`, macOS's Increase contrast and its
|
|
3513
|
+
* counterparts) or forced colours are on (Windows' contrast themes). A
|
|
3514
|
+
* canvas gets no forced colours from the browser, which only repaints
|
|
3515
|
+
* the DOM, so the application has to hear about it and draw its own:
|
|
3516
|
+
* `withContrast(theme, contrast)` is the theme that does.
|
|
3517
|
+
*
|
|
3518
|
+
* Shell-side, reported once at once and again on change, for the reason
|
|
3519
|
+
* `observeColorScheme` gives.
|
|
3520
|
+
*/
|
|
3521
|
+
function observeContrast(onChange) {
|
|
3522
|
+
let more = false;
|
|
3523
|
+
let forced = false;
|
|
3524
|
+
let last = null;
|
|
3525
|
+
let ready = false;
|
|
3526
|
+
const report = () => {
|
|
3527
|
+
if (!ready) return;
|
|
3528
|
+
const contrast = more || forced ? "high" : "standard";
|
|
3529
|
+
if (contrast !== last) {
|
|
3530
|
+
last = contrast;
|
|
3531
|
+
onChange(contrast);
|
|
3532
|
+
}
|
|
3533
|
+
};
|
|
3534
|
+
const stopMore = observeMediaQuery("(prefers-contrast: more)", (matches) => {
|
|
3535
|
+
more = matches;
|
|
3536
|
+
report();
|
|
3537
|
+
});
|
|
3538
|
+
const stopForced = observeMediaQuery("(forced-colors: active)", (matches) => {
|
|
3539
|
+
forced = matches;
|
|
3540
|
+
report();
|
|
3541
|
+
});
|
|
3542
|
+
ready = true;
|
|
3543
|
+
report();
|
|
3544
|
+
return () => {
|
|
3545
|
+
stopMore();
|
|
3546
|
+
stopForced();
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
//#endregion
|
|
3405
3550
|
//#region src/app/reducedMotion.ts
|
|
3406
3551
|
/**
|
|
3407
3552
|
* Watches the platform's reduced-motion preference.
|
|
@@ -3807,6 +3952,15 @@ var WorkerApp = class {
|
|
|
3807
3952
|
get appLogic() {
|
|
3808
3953
|
return this.appLogicWorker === void 0 ? void 0 : portHandle(this.appLogicWorker);
|
|
3809
3954
|
}
|
|
3955
|
+
/**
|
|
3956
|
+
* Opens a port to whatever the render worker serves under `key`, or
|
|
3957
|
+
* returns undefined before `mount` has started it. The development
|
|
3958
|
+
* agent bridge asks for `gesso:agent` this way; nothing else in the
|
|
3959
|
+
* shell talks to the render worker except through its protocol.
|
|
3960
|
+
*/
|
|
3961
|
+
openRenderPort(key) {
|
|
3962
|
+
return this.renderWorker === void 0 ? void 0 : portHandle(this.renderWorker).open(key);
|
|
3963
|
+
}
|
|
3810
3964
|
mount(host) {
|
|
3811
3965
|
const element = resolveHost(host);
|
|
3812
3966
|
this.host = element;
|
|
@@ -3889,9 +4043,10 @@ var WorkerApp = class {
|
|
|
3889
4043
|
type: "compositionEnd",
|
|
3890
4044
|
text
|
|
3891
4045
|
}),
|
|
3892
|
-
paste: (text) => this.post({
|
|
4046
|
+
paste: (text, html) => this.post({
|
|
3893
4047
|
type: "paste",
|
|
3894
|
-
text
|
|
4048
|
+
text,
|
|
4049
|
+
...html === null ? {} : { html }
|
|
3895
4050
|
}),
|
|
3896
4051
|
blur: () => this.post({ type: "blur" }),
|
|
3897
4052
|
keyDown: (event) => this.forwardKeyDown(event, true),
|
|
@@ -3904,13 +4059,21 @@ var WorkerApp = class {
|
|
|
3904
4059
|
}),
|
|
3905
4060
|
keyDown: (event) => this.forwardKeyDown(event),
|
|
3906
4061
|
keyUp: (event) => this.forwardKeyUp(event),
|
|
3907
|
-
paste: (text) => this.post({
|
|
4062
|
+
paste: (text, html) => this.post({
|
|
3908
4063
|
type: "paste",
|
|
3909
|
-
text
|
|
4064
|
+
text,
|
|
4065
|
+
...html === null ? {} : { html }
|
|
3910
4066
|
})
|
|
3911
4067
|
}, this.proxy);
|
|
4068
|
+
const webmcp = this.options.webmcp;
|
|
4069
|
+
if (webmcp !== void 0 && webmcp !== false) import("./webmcp-CJVFvAHe.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, webmcp === true ? {} : webmcp)).then((disconnect) => {
|
|
4070
|
+
if (this.renderWorker === worker) this.disconnectWebMcp = disconnect;
|
|
4071
|
+
else disconnect();
|
|
4072
|
+
}, (error) => console.warn("[gesso] WebMCP could not register the channels:", error));
|
|
3912
4073
|
return () => this.dispose();
|
|
3913
4074
|
}
|
|
4075
|
+
/** Removes the WebMCP tools `webmcp` registered, if it did. */
|
|
4076
|
+
disconnectWebMcp = null;
|
|
3914
4077
|
/**
|
|
3915
4078
|
* An error the browser raised *at the worker object*, which is not
|
|
3916
4079
|
* the same thing as the worker reporting one.
|
|
@@ -4075,6 +4238,8 @@ var WorkerApp = class {
|
|
|
4075
4238
|
this.frameHandle = requestAnimationFrame(step);
|
|
4076
4239
|
}
|
|
4077
4240
|
dispose() {
|
|
4241
|
+
this.disconnectWebMcp?.();
|
|
4242
|
+
this.disconnectWebMcp = null;
|
|
4078
4243
|
this.ready = false;
|
|
4079
4244
|
this.setFrameLoop(false);
|
|
4080
4245
|
this.detachColorScheme?.();
|
|
@@ -4136,6 +4301,7 @@ var WorkerApp = class {
|
|
|
4136
4301
|
nodes: message.nodes,
|
|
4137
4302
|
measured: message.measured,
|
|
4138
4303
|
relayoutRoots: message.relayoutRoots,
|
|
4304
|
+
layoutPasses: message.layoutPasses,
|
|
4139
4305
|
at: message.at,
|
|
4140
4306
|
inputLatencyMs: message.inputLatencyMs,
|
|
4141
4307
|
phases: message.phases,
|
|
@@ -4182,7 +4348,11 @@ var WorkerApp = class {
|
|
|
4182
4348
|
return;
|
|
4183
4349
|
}
|
|
4184
4350
|
if (message.type === "clipboard") {
|
|
4185
|
-
writeClipboard(message.text)
|
|
4351
|
+
writeClipboard(message.text).then((copied) => this.post({
|
|
4352
|
+
type: "clipboardResult",
|
|
4353
|
+
id: message.id,
|
|
4354
|
+
copied
|
|
4355
|
+
}));
|
|
4186
4356
|
return;
|
|
4187
4357
|
}
|
|
4188
4358
|
if (message.type === "openUrl") {
|
|
@@ -4522,6 +4692,10 @@ var WorkerApp = class {
|
|
|
4522
4692
|
reduced
|
|
4523
4693
|
});
|
|
4524
4694
|
});
|
|
4695
|
+
const detachContrast = observeContrast((contrast) => this.post({
|
|
4696
|
+
type: "contrast",
|
|
4697
|
+
contrast
|
|
4698
|
+
}));
|
|
4525
4699
|
const onPointerMove = (event) => {
|
|
4526
4700
|
const { x, y } = toLocal(event.clientX, event.clientY);
|
|
4527
4701
|
const move = {
|
|
@@ -4606,6 +4780,21 @@ var WorkerApp = class {
|
|
|
4606
4780
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
4607
4781
|
canvas.addEventListener("keydown", onKeyDown);
|
|
4608
4782
|
canvas.addEventListener("keyup", onKeyUp);
|
|
4783
|
+
const aimedAtPage = (target) => target === null || target === document || target === document.body || target === document.documentElement;
|
|
4784
|
+
const onPageKeyDown = (event) => {
|
|
4785
|
+
if (aimedAtPage(event.target)) {
|
|
4786
|
+
canvas.focus({ preventScroll: true });
|
|
4787
|
+
this.forwardKeyDown(event);
|
|
4788
|
+
}
|
|
4789
|
+
};
|
|
4790
|
+
const onPageKeyUp = (event) => {
|
|
4791
|
+
if (aimedAtPage(event.target)) this.forwardKeyUp(event);
|
|
4792
|
+
};
|
|
4793
|
+
const pageKeys = this.options.pageKeys === true;
|
|
4794
|
+
if (pageKeys) {
|
|
4795
|
+
document.addEventListener("keydown", onPageKeyDown);
|
|
4796
|
+
document.addEventListener("keyup", onPageKeyUp);
|
|
4797
|
+
}
|
|
4609
4798
|
window.addEventListener("scroll", onCanvasMayHaveMoved, {
|
|
4610
4799
|
capture: true,
|
|
4611
4800
|
passive: true
|
|
@@ -4613,6 +4802,7 @@ var WorkerApp = class {
|
|
|
4613
4802
|
window.addEventListener("resize", onCanvasMayHaveMoved);
|
|
4614
4803
|
return () => {
|
|
4615
4804
|
detachReducedMotion();
|
|
4805
|
+
detachContrast();
|
|
4616
4806
|
detachFullscreen();
|
|
4617
4807
|
detachFileDrop();
|
|
4618
4808
|
canvas.removeEventListener("mousedown", onMouseDown);
|
|
@@ -4624,6 +4814,10 @@ var WorkerApp = class {
|
|
|
4624
4814
|
canvas.removeEventListener("wheel", onWheel);
|
|
4625
4815
|
canvas.removeEventListener("keydown", onKeyDown);
|
|
4626
4816
|
canvas.removeEventListener("keyup", onKeyUp);
|
|
4817
|
+
if (pageKeys) {
|
|
4818
|
+
document.removeEventListener("keydown", onPageKeyDown);
|
|
4819
|
+
document.removeEventListener("keyup", onPageKeyUp);
|
|
4820
|
+
}
|
|
4627
4821
|
window.removeEventListener("scroll", onCanvasMayHaveMoved, { capture: true });
|
|
4628
4822
|
window.removeEventListener("resize", onCanvasMayHaveMoved);
|
|
4629
4823
|
this.dropPendingMove();
|
|
@@ -5097,10 +5291,19 @@ function parseUrl(url) {
|
|
|
5097
5291
|
}
|
|
5098
5292
|
/** Joins a path and a query back into the url the history stores. */
|
|
5099
5293
|
function formatUrl(path, query = {}) {
|
|
5100
|
-
const pairs = Object.entries(query).map(([key, value]) => `${
|
|
5294
|
+
const pairs = Object.entries(query).map(([key, value]) => `${encodeQueryPart(key)}=${encodeQueryPart(value)}`);
|
|
5101
5295
|
const normalized = `/${pathSegments(path).join("/")}`;
|
|
5102
5296
|
return pairs.length === 0 ? normalized : `${normalized}?${pairs.join("&")}`;
|
|
5103
5297
|
}
|
|
5298
|
+
/**
|
|
5299
|
+
* Encodes a query key or value, leaving the characters a query may hold
|
|
5300
|
+
* as they are (`,` `:` `@` `/`), so a list of values reads as one:
|
|
5301
|
+
* `status=todo,done`, not `status=todo%2Cdone`. What would change how
|
|
5302
|
+
* the query parses (`&`, `=`, `+`, `#`) is still encoded.
|
|
5303
|
+
*/
|
|
5304
|
+
function encodeQueryPart(text) {
|
|
5305
|
+
return encodeURIComponent(text).replace(/%2C/gi, ",").replace(/%3A/gi, ":").replace(/%40/gi, "@").replace(/%2F/gi, "/");
|
|
5306
|
+
}
|
|
5104
5307
|
function splitOnce(text, separator) {
|
|
5105
5308
|
const index = text.indexOf(separator);
|
|
5106
5309
|
return index === -1 ? [text, ""] : [text.slice(0, index), text.slice(index + separator.length)];
|
|
@@ -5208,7 +5411,7 @@ var RouterService = class {
|
|
|
5208
5411
|
*/
|
|
5209
5412
|
params(route) {
|
|
5210
5413
|
const match = this.match.value;
|
|
5211
|
-
if (match === null || !match.chain
|
|
5414
|
+
if (match === null || !inChain(match.chain, route)) return null;
|
|
5212
5415
|
return match.params;
|
|
5213
5416
|
}
|
|
5214
5417
|
/** The same, as a cell, for binding a screen's title or fields. */
|
|
@@ -5300,7 +5503,7 @@ var RouterService = class {
|
|
|
5300
5503
|
isActive(route) {
|
|
5301
5504
|
return computed(() => {
|
|
5302
5505
|
const match = this.match.value;
|
|
5303
|
-
return match !== null && match.chain
|
|
5506
|
+
return match !== null && inChain(match.chain, route);
|
|
5304
5507
|
});
|
|
5305
5508
|
}
|
|
5306
5509
|
/**
|
|
@@ -5431,6 +5634,23 @@ function sameParams(a, b) {
|
|
|
5431
5634
|
const keys = Object.keys(left);
|
|
5432
5635
|
return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
|
|
5433
5636
|
}
|
|
5637
|
+
/**
|
|
5638
|
+
* Whether a route is in a matched chain: the same object, or a route
|
|
5639
|
+
* declared with the same path.
|
|
5640
|
+
*
|
|
5641
|
+
* Identity alone was the original test, and it is right for a table
|
|
5642
|
+
* loaded once. It fails silently under a dev server's hot replacement:
|
|
5643
|
+
* a route table that imports its screens, with screens that import the
|
|
5644
|
+
* table to read their params, is an import cycle, and after a hot edit
|
|
5645
|
+
* Vite can serve the two sides as `routes.tsx` and `routes.tsx?t=…`.
|
|
5646
|
+
* The table then loads twice, the router holds one copy's route objects
|
|
5647
|
+
* and the screens hold the other's, and every `params(route)` answers
|
|
5648
|
+
* null, so a team page quietly showed the default team. Matching is by
|
|
5649
|
+
* path already, so two routes with one path are one route to the url.
|
|
5650
|
+
*/
|
|
5651
|
+
function inChain(chain, route) {
|
|
5652
|
+
return chain.some((link) => link === route || link.path === route.path);
|
|
5653
|
+
}
|
|
5434
5654
|
//#endregion
|
|
5435
5655
|
//#region src/app/FindService.ts
|
|
5436
5656
|
/**
|
|
@@ -5559,6 +5779,48 @@ var EditingService = class {
|
|
|
5559
5779
|
if (node === null || this.controller === null) return null;
|
|
5560
5780
|
return this.controller.caretRectOf(node);
|
|
5561
5781
|
}
|
|
5782
|
+
/**
|
|
5783
|
+
* Selects from `anchor` to `focus` and focuses the field the focus is
|
|
5784
|
+
* in: within one field, or across the fields of an editing group (see
|
|
5785
|
+
* `UiEditingGroup`). What an editor does after a command over a
|
|
5786
|
+
* selection (making it bold, say) to leave it selected, since its
|
|
5787
|
+
* fields can only select their own text.
|
|
5788
|
+
*
|
|
5789
|
+
* False when the two ends aren't editables of one group, or before
|
|
5790
|
+
* the runtime has wired the controller.
|
|
5791
|
+
*/
|
|
5792
|
+
select(anchor, focus) {
|
|
5793
|
+
return this.controller?.select(anchor, focus) ?? false;
|
|
5794
|
+
}
|
|
5795
|
+
};
|
|
5796
|
+
//#endregion
|
|
5797
|
+
//#region src/app/ScrollService.ts
|
|
5798
|
+
/**
|
|
5799
|
+
* Scrolling a node into view, for a component that moves a highlight
|
|
5800
|
+
* rather than focus.
|
|
5801
|
+
*
|
|
5802
|
+
* Focus moved from the keyboard already brings its node into view. A
|
|
5803
|
+
* highlight doesn't move focus: a combobox's arrows walk its list while
|
|
5804
|
+
* the caret stays in the field, and a grid's cursor can do the same.
|
|
5805
|
+
* The list has to follow the highlight all the same, and only the
|
|
5806
|
+
* runtime knows where the scroll containers above a node are and how
|
|
5807
|
+
* far each has to move. This is the asking.
|
|
5808
|
+
*/
|
|
5809
|
+
var ScrollService = class {
|
|
5810
|
+
scroller = null;
|
|
5811
|
+
/** Installed by the runtime; without one nothing scrolls. */
|
|
5812
|
+
setScroller(scroller) {
|
|
5813
|
+
this.scroller = scroller;
|
|
5814
|
+
}
|
|
5815
|
+
/**
|
|
5816
|
+
* Scrolls every container above `node` just enough that it's inside
|
|
5817
|
+
* the container's viewport, `padding` pixels from the nearest edge.
|
|
5818
|
+
* Nothing moves when it's visible already. Before the node has been
|
|
5819
|
+
* laid out there is nowhere to scroll it to, and nothing happens.
|
|
5820
|
+
*/
|
|
5821
|
+
scrollIntoView(node, padding = 8) {
|
|
5822
|
+
this.scroller?.(node, padding);
|
|
5823
|
+
}
|
|
5562
5824
|
};
|
|
5563
5825
|
//#endregion
|
|
5564
5826
|
//#region src/app/FocusService.ts
|
|
@@ -5586,6 +5848,14 @@ var FocusService = class {
|
|
|
5586
5848
|
focused = internalState(null);
|
|
5587
5849
|
/** Whether focus is confined to a subtree by an open trap. */
|
|
5588
5850
|
trapped = internalState(false);
|
|
5851
|
+
/**
|
|
5852
|
+
* Whether the focus held is focus the keyboard can see: reached by Tab
|
|
5853
|
+
* or an arrow key, or by code with no pointer since, rather than by a
|
|
5854
|
+
* press. CSS's `:focus-visible`, for something other than a ring that
|
|
5855
|
+
* should answer the keyboard and not the click that happened to focus
|
|
5856
|
+
* the same control: a tooltip, say.
|
|
5857
|
+
*/
|
|
5858
|
+
focusVisible = internalState(false);
|
|
5589
5859
|
manager = null;
|
|
5590
5860
|
detach = null;
|
|
5591
5861
|
/** Actions taken before the runtime installed a manager, in order. */
|
|
@@ -5601,6 +5871,7 @@ var FocusService = class {
|
|
|
5601
5871
|
}
|
|
5602
5872
|
const focusChange = manager.onFocusChange((node) => {
|
|
5603
5873
|
this.focused.value = node;
|
|
5874
|
+
this.focusVisible.value = manager.focusVisible;
|
|
5604
5875
|
});
|
|
5605
5876
|
const scopeChange = manager.onScopeChange(() => {
|
|
5606
5877
|
this.trapped.value = manager.trapped;
|
|
@@ -5614,9 +5885,15 @@ var FocusService = class {
|
|
|
5614
5885
|
for (const action of pending) action(manager);
|
|
5615
5886
|
this.sync(manager);
|
|
5616
5887
|
}
|
|
5617
|
-
/**
|
|
5618
|
-
|
|
5619
|
-
|
|
5888
|
+
/**
|
|
5889
|
+
* Gives the node keyboard focus. Non-focusable nodes are ignored.
|
|
5890
|
+
*
|
|
5891
|
+
* The node is scrolled into view, unless `options.preventScroll` asks
|
|
5892
|
+
* for the page to stay where it is, as `element.focus({ preventScroll:
|
|
5893
|
+
* true })` does in a browser.
|
|
5894
|
+
*/
|
|
5895
|
+
focus(node, options) {
|
|
5896
|
+
this.run((manager) => manager.focus(node, "program", options));
|
|
5620
5897
|
}
|
|
5621
5898
|
/** Drops focus without moving it anywhere. */
|
|
5622
5899
|
blur() {
|
|
@@ -5656,6 +5933,7 @@ var FocusService = class {
|
|
|
5656
5933
|
}
|
|
5657
5934
|
sync(manager) {
|
|
5658
5935
|
this.focused.value = manager.focusedNode;
|
|
5936
|
+
this.focusVisible.value = manager.focusVisible;
|
|
5659
5937
|
this.trapped.value = manager.trapped;
|
|
5660
5938
|
}
|
|
5661
5939
|
};
|
|
@@ -6570,7 +6848,20 @@ var GessoRuntime = class {
|
|
|
6570
6848
|
sharedElements = new UiSharedElements();
|
|
6571
6849
|
focusNotifier = new FocusNotifier();
|
|
6572
6850
|
environmentNotifier = new EnvironmentNotifier();
|
|
6851
|
+
/**
|
|
6852
|
+
* The semantics tree: every record by id, and each record's children
|
|
6853
|
+
* in order.
|
|
6854
|
+
*
|
|
6855
|
+
* A record's own `index` is only kept current for the records that
|
|
6856
|
+
* are sent; the order is `semanticsChildren`. Renumbering every
|
|
6857
|
+
* record after an insertion cost a pass over the whole tree on every
|
|
6858
|
+
* structural change, for indices nothing would read until a record
|
|
6859
|
+
* was next sent or the tree was asked for (`semanticsTree`).
|
|
6860
|
+
*/
|
|
6573
6861
|
semantics = /* @__PURE__ */ new Map();
|
|
6862
|
+
semanticsChildren = /* @__PURE__ */ new Map();
|
|
6863
|
+
/** The tree in document order with every index current, built when asked for. */
|
|
6864
|
+
semanticsOrdered = null;
|
|
6574
6865
|
semanticsListener = null;
|
|
6575
6866
|
/**
|
|
6576
6867
|
* The box last reported for each mirrored node, so a frame that
|
|
@@ -6578,6 +6869,8 @@ var GessoRuntime = class {
|
|
|
6578
6869
|
* them. Only populated while a listener is attached.
|
|
6579
6870
|
*/
|
|
6580
6871
|
semanticsBoxes = /* @__PURE__ */ new Map();
|
|
6872
|
+
/** What each semantics walk leaves for the next; see `SemanticsMemory`. */
|
|
6873
|
+
semanticsMemory = semanticsMemory();
|
|
6581
6874
|
lastFocusedId = null;
|
|
6582
6875
|
/**
|
|
6583
6876
|
* Where the caret was when `reload` replaced the tree, put back on
|
|
@@ -6591,6 +6884,14 @@ var GessoRuntime = class {
|
|
|
6591
6884
|
focusAfterReload = null;
|
|
6592
6885
|
/** Whether `focusAfterReload` is waiting to be applied. */
|
|
6593
6886
|
restoringFocus = false;
|
|
6887
|
+
/**
|
|
6888
|
+
* Reveals asked for while the layout listeners run, kept until the
|
|
6889
|
+
* frame's boxes have settled; null outside `settleLayout`, where a
|
|
6890
|
+
* reveal is applied at once.
|
|
6891
|
+
*/
|
|
6892
|
+
deferredReveals = null;
|
|
6893
|
+
/** Whether a frame has run out of layout passes and been warned about; see `MAX_LAYOUT_PASSES`. */
|
|
6894
|
+
warnedLayoutPasses = false;
|
|
6594
6895
|
/** A frame changed semantics while nothing was listening; see `semanticsTree`. */
|
|
6595
6896
|
semanticsStale = false;
|
|
6596
6897
|
lastEditingState = null;
|
|
@@ -6678,8 +6979,8 @@ var GessoRuntime = class {
|
|
|
6678
6979
|
focus: {
|
|
6679
6980
|
isFocused: (node) => this.focusNotifier.isFocused(node),
|
|
6680
6981
|
isFocusVisible: (node) => this.focusNotifier.isFocusVisible(node),
|
|
6681
|
-
focus: (node) => {
|
|
6682
|
-
this.focusManager.focus(node);
|
|
6982
|
+
focus: (node, options) => {
|
|
6983
|
+
this.focusManager.focus(node, "program", options);
|
|
6683
6984
|
},
|
|
6684
6985
|
onFocusChange: (node, listener) => this.focusNotifier.add(node, listener)
|
|
6685
6986
|
},
|
|
@@ -6719,6 +7020,7 @@ var GessoRuntime = class {
|
|
|
6719
7020
|
if (!this.services.has(FindService)) this.services.register(FindService);
|
|
6720
7021
|
if (!this.services.has(FocusService)) this.services.register(FocusService);
|
|
6721
7022
|
if (!this.services.has(EditingService)) this.services.register(EditingService);
|
|
7023
|
+
if (!this.services.has(ScrollService)) this.services.register(ScrollService);
|
|
6722
7024
|
if (!this.services.has(FrameService)) this.services.register(FrameService);
|
|
6723
7025
|
if (!this.services.has(MediaService)) this.services.register(MediaService);
|
|
6724
7026
|
if (!this.services.has(FontService)) this.services.register(FontService);
|
|
@@ -6793,8 +7095,9 @@ var GessoRuntime = class {
|
|
|
6793
7095
|
this.input = this.createInput();
|
|
6794
7096
|
this.services.get(FocusService).setManager(this.input.focus);
|
|
6795
7097
|
this.services.get(EditingService).setController(this.input.editing);
|
|
6796
|
-
this.
|
|
6797
|
-
|
|
7098
|
+
this.services.get(ScrollService).setScroller((node, padding) => this.scrollIntoView(node, padding));
|
|
7099
|
+
this.input.focus.onFocusChange((node, source, options) => {
|
|
7100
|
+
if (node !== null && source !== "pointer" && options.preventScroll !== true) this.scrollIntoView(node);
|
|
6798
7101
|
});
|
|
6799
7102
|
if (options.width !== void 0 && options.height !== void 0) this.resize(options.width, options.height, this.pixelRatio);
|
|
6800
7103
|
}
|
|
@@ -7278,6 +7581,10 @@ var GessoRuntime = class {
|
|
|
7278
7581
|
setColorScheme(scheme) {
|
|
7279
7582
|
this.services.get(ShellService).applyColorScheme(scheme);
|
|
7280
7583
|
}
|
|
7584
|
+
/** The contrast the platform asks for, passed straight to `ShellService`; see `ShellService.contrast`. */
|
|
7585
|
+
setContrast(contrast) {
|
|
7586
|
+
this.services.get(ShellService).applyContrast(contrast);
|
|
7587
|
+
}
|
|
7281
7588
|
/**
|
|
7282
7589
|
* The platform's own insets, as the shell reports them: the safe area
|
|
7283
7590
|
* under a notch or a home indicator and the strip a soft keyboard
|
|
@@ -7342,6 +7649,13 @@ var GessoRuntime = class {
|
|
|
7342
7649
|
this.services.get(ShellService).settlePopup(id, opened);
|
|
7343
7650
|
}
|
|
7344
7651
|
/**
|
|
7652
|
+
* Reports whether a clipboard write landed, settling the promise
|
|
7653
|
+
* `ShellService.copyText` returned.
|
|
7654
|
+
*/
|
|
7655
|
+
settleClipboard(id, copied) {
|
|
7656
|
+
this.services.get(ShellService).settleClipboard(id, copied);
|
|
7657
|
+
}
|
|
7658
|
+
/**
|
|
7345
7659
|
* Reports what the shell found in `localStorage`, settling the
|
|
7346
7660
|
* promise `ShellService.requestStorage` returned.
|
|
7347
7661
|
*
|
|
@@ -7562,8 +7876,20 @@ var GessoRuntime = class {
|
|
|
7562
7876
|
* Scrolls every scroll container above `node` just enough that the
|
|
7563
7877
|
* node is inside its viewport, `padding` pixels from the nearest edge.
|
|
7564
7878
|
* Nothing moves when it is already visible.
|
|
7879
|
+
*
|
|
7880
|
+
* Asked for from a layout listener — `autoFocus` taking focus is the
|
|
7881
|
+
* usual one — it waits until the frame's layout has settled, so it
|
|
7882
|
+
* reveals where the node ends up rather than where the first pass
|
|
7883
|
+
* put it (see `settleLayout`).
|
|
7565
7884
|
*/
|
|
7566
7885
|
scrollIntoView(node, padding = 8) {
|
|
7886
|
+
if (this.deferredReveals !== null) {
|
|
7887
|
+
this.deferredReveals.push({
|
|
7888
|
+
node,
|
|
7889
|
+
padding
|
|
7890
|
+
});
|
|
7891
|
+
return;
|
|
7892
|
+
}
|
|
7567
7893
|
for (const adjustment of this.engine.revealAdjustments(node, padding)) {
|
|
7568
7894
|
this.smoothScroller.stop(adjustment.container);
|
|
7569
7895
|
adjustment.container.setProperty("scrollX", adjustment.scrollX);
|
|
@@ -7792,7 +8118,7 @@ var GessoRuntime = class {
|
|
|
7792
8118
|
editing,
|
|
7793
8119
|
selection
|
|
7794
8120
|
}),
|
|
7795
|
-
wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => this.appRoot ?? root),
|
|
8121
|
+
wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => this.appRoot ?? root, void 0, { pace: true }),
|
|
7796
8122
|
keyboard: new UiKeyboardController(this.dispatcher, focus, () => this.appRoot ?? this.layoutRoot(), {
|
|
7797
8123
|
editing,
|
|
7798
8124
|
selection,
|
|
@@ -7824,7 +8150,7 @@ var GessoRuntime = class {
|
|
|
7824
8150
|
if (listener !== null) this.semanticsTree();
|
|
7825
8151
|
if (listener === null || this.semantics.size === 0) return;
|
|
7826
8152
|
listener({
|
|
7827
|
-
patches: [...this.
|
|
8153
|
+
patches: [...this.orderedSemantics().values()].map((node) => ({
|
|
7828
8154
|
op: "add",
|
|
7829
8155
|
node
|
|
7830
8156
|
})),
|
|
@@ -7833,6 +8159,14 @@ var GessoRuntime = class {
|
|
|
7833
8159
|
});
|
|
7834
8160
|
}
|
|
7835
8161
|
/**
|
|
8162
|
+
* The id of the node that holds focus, or null. The same id the
|
|
8163
|
+
* semantics tree keys its records by, so a reader of the tree can
|
|
8164
|
+
* say which control a key press would reach.
|
|
8165
|
+
*/
|
|
8166
|
+
focusedNodeId() {
|
|
8167
|
+
return this.focusManager.focusedNode?.id ?? null;
|
|
8168
|
+
}
|
|
8169
|
+
/**
|
|
7836
8170
|
* The semantics tree as of the last frame that changed it.
|
|
7837
8171
|
*
|
|
7838
8172
|
* Rebuilt here when no mirror was listening and a frame marked it
|
|
@@ -7841,10 +8175,59 @@ var GessoRuntime = class {
|
|
|
7841
8175
|
*/
|
|
7842
8176
|
semanticsTree() {
|
|
7843
8177
|
if (this.semanticsStale) {
|
|
7844
|
-
this.
|
|
8178
|
+
this.storeSemantics(buildSemanticsTree(this.layoutRoot(), this.semanticsMemory));
|
|
7845
8179
|
this.semanticsStale = false;
|
|
7846
8180
|
}
|
|
7847
|
-
return this.
|
|
8181
|
+
return this.orderedSemantics();
|
|
8182
|
+
}
|
|
8183
|
+
/** Replaces the tree with one built in full, which is in document order. */
|
|
8184
|
+
storeSemantics(tree) {
|
|
8185
|
+
this.semantics = tree;
|
|
8186
|
+
this.semanticsOrdered = tree;
|
|
8187
|
+
this.semanticsChildren = /* @__PURE__ */ new Map();
|
|
8188
|
+
for (const record of tree.values()) {
|
|
8189
|
+
let list = this.semanticsChildren.get(record.parent);
|
|
8190
|
+
if (list === void 0) this.semanticsChildren.set(record.parent, list = []);
|
|
8191
|
+
list.push(record.id);
|
|
8192
|
+
}
|
|
8193
|
+
}
|
|
8194
|
+
/**
|
|
8195
|
+
* The tree in document order, every index current: a walk down
|
|
8196
|
+
* `semanticsChildren`, done when someone asks rather than per frame.
|
|
8197
|
+
* Records whose index had gone out of date are brought up to date in
|
|
8198
|
+
* the store as well.
|
|
8199
|
+
*/
|
|
8200
|
+
orderedSemantics() {
|
|
8201
|
+
if (this.semanticsOrdered !== null) return this.semanticsOrdered;
|
|
8202
|
+
const ordered = /* @__PURE__ */ new Map();
|
|
8203
|
+
const stack = [];
|
|
8204
|
+
const push = (parent) => {
|
|
8205
|
+
const list = this.semanticsChildren.get(parent) ?? [];
|
|
8206
|
+
const indices = semanticIndices(list);
|
|
8207
|
+
for (let i = list.length - 1; i >= 0; i--) stack.push([list[i], indices[i]]);
|
|
8208
|
+
};
|
|
8209
|
+
push(null);
|
|
8210
|
+
while (stack.length > 0) {
|
|
8211
|
+
const [id, index] = stack.pop();
|
|
8212
|
+
let record = this.semantics.get(id);
|
|
8213
|
+
if (record.index !== index) {
|
|
8214
|
+
record = {
|
|
8215
|
+
...record,
|
|
8216
|
+
index
|
|
8217
|
+
};
|
|
8218
|
+
this.semantics.set(id, record);
|
|
8219
|
+
}
|
|
8220
|
+
ordered.set(id, record);
|
|
8221
|
+
push(id);
|
|
8222
|
+
}
|
|
8223
|
+
this.semanticsOrdered = ordered;
|
|
8224
|
+
return ordered;
|
|
8225
|
+
}
|
|
8226
|
+
/** A record's index among its siblings now, which its own `index` may not say. */
|
|
8227
|
+
semanticsIndexOf(record) {
|
|
8228
|
+
const list = this.semanticsChildren.get(record.parent) ?? [];
|
|
8229
|
+
const at = list.indexOf(record.id);
|
|
8230
|
+
return at < 0 ? record.index : semanticIndices(list)[at];
|
|
7848
8231
|
}
|
|
7849
8232
|
/**
|
|
7850
8233
|
* Something an assistive technology did to a mirrored element,
|
|
@@ -7925,7 +8308,7 @@ var GessoRuntime = class {
|
|
|
7925
8308
|
const listener = this.semanticsListener;
|
|
7926
8309
|
if (rebuild) {
|
|
7927
8310
|
if (listener === null) this.semanticsStale = true;
|
|
7928
|
-
else patches = this.rescopeSemantics(frame) ?? this.rebuildSemantics();
|
|
8311
|
+
else patches = this.rescopeSemantics(frame) ?? this.restructureSemantics(frame) ?? this.rebuildSemantics();
|
|
7929
8312
|
}
|
|
7930
8313
|
if (listener === null) return;
|
|
7931
8314
|
const boxes = moved || patches.length > 0 ? this.collectSemanticsBoxes() : EMPTY_BOXES;
|
|
@@ -7944,9 +8327,10 @@ var GessoRuntime = class {
|
|
|
7944
8327
|
}
|
|
7945
8328
|
/** Walks the whole tree and diffs it against the last one. */
|
|
7946
8329
|
rebuildSemantics() {
|
|
7947
|
-
const
|
|
7948
|
-
const
|
|
7949
|
-
|
|
8330
|
+
const previous = this.orderedSemantics();
|
|
8331
|
+
const next = buildSemanticsTree(this.layoutRoot(), this.semanticsMemory);
|
|
8332
|
+
const patches = dropIndexShifts(previous, next, diffSemantics(previous, next));
|
|
8333
|
+
this.storeSemantics(next);
|
|
7950
8334
|
this.semanticsStale = false;
|
|
7951
8335
|
return patches;
|
|
7952
8336
|
}
|
|
@@ -8002,7 +8386,7 @@ var GessoRuntime = class {
|
|
|
8002
8386
|
const updates = [];
|
|
8003
8387
|
for (const owner of owners) {
|
|
8004
8388
|
const previous = this.semantics.get(owner.id);
|
|
8005
|
-
const subtree = buildSemanticsSubtree(owner, previous.parent, previous
|
|
8389
|
+
const subtree = buildSemanticsSubtree(owner, previous.parent, this.semanticsIndexOf(previous), semanticsInertAbove(owner), this.semanticsMemory);
|
|
8006
8390
|
if (subtree === null) return null;
|
|
8007
8391
|
let covered = 0;
|
|
8008
8392
|
for (const id of this.semanticIdsUnder(owner)) {
|
|
@@ -8010,13 +8394,18 @@ var GessoRuntime = class {
|
|
|
8010
8394
|
if (next === void 0) return null;
|
|
8011
8395
|
covered++;
|
|
8012
8396
|
const before = this.semantics.get(id);
|
|
8013
|
-
if (!recordsEqual(
|
|
8397
|
+
if (!recordsEqual({
|
|
8398
|
+
...before,
|
|
8399
|
+
index: next.index
|
|
8400
|
+
}, next)) updates.push(next);
|
|
8401
|
+
else if (before.index !== next.index) this.semantics.set(id, next);
|
|
8014
8402
|
}
|
|
8015
8403
|
if (covered !== subtree.size) return null;
|
|
8016
8404
|
}
|
|
8017
8405
|
const patches = [];
|
|
8018
8406
|
for (const record of updates) {
|
|
8019
8407
|
this.semantics.set(record.id, record);
|
|
8408
|
+
this.semanticsOrdered?.set(record.id, record);
|
|
8020
8409
|
patches.push({
|
|
8021
8410
|
op: "update",
|
|
8022
8411
|
node: record
|
|
@@ -8025,6 +8414,163 @@ var GessoRuntime = class {
|
|
|
8025
8414
|
return patches;
|
|
8026
8415
|
}
|
|
8027
8416
|
/**
|
|
8417
|
+
* The patches for a frame that changed the shape of the tree, without
|
|
8418
|
+
* walking all of it, or null when the whole tree has to be rebuilt.
|
|
8419
|
+
*
|
|
8420
|
+
* Any frame that added or removed a child used to rebuild the tree
|
|
8421
|
+
* from the root, describing every node again. In a 5,000-line editor
|
|
8422
|
+
* that was 11 ms of every Enter, for one new paragraph. Now the walk
|
|
8423
|
+
* starts at the nearest record above each change, as
|
|
8424
|
+
* `rescopeSemantics` does for a change of meaning, and inside it a
|
|
8425
|
+
* subtree nothing touched keeps its records: renumbered if its
|
|
8426
|
+
* siblings moved, never described again (see
|
|
8427
|
+
* `rebuildSemanticsSubtree`). What is described is the path down to
|
|
8428
|
+
* each change, whatever is new, and everything under a node whose
|
|
8429
|
+
* own meaning changed.
|
|
8430
|
+
*
|
|
8431
|
+
* The result is spliced into `this.semantics` in place, since the map
|
|
8432
|
+
* is in document order and a record's subtree is a contiguous run of
|
|
8433
|
+
* it, so adds still arrive in document order. Removals come first.
|
|
8434
|
+
*/
|
|
8435
|
+
restructureSemantics(frame) {
|
|
8436
|
+
if (this.semantics.size === 0 || this.semanticsStale) return null;
|
|
8437
|
+
const dirty = [];
|
|
8438
|
+
/** Nodes whose own meaning may have changed: everything under them is described afresh. */
|
|
8439
|
+
const meaning = /* @__PURE__ */ new Set();
|
|
8440
|
+
let structural = false;
|
|
8441
|
+
for (const [node, flags] of frame.entries()) {
|
|
8442
|
+
const children = (flags & DirtyFlags.Children) !== 0;
|
|
8443
|
+
const semantics = (flags & DirtyFlags.Semantics) !== 0;
|
|
8444
|
+
if (!children && !semantics) continue;
|
|
8445
|
+
if (!this.attached(node)) continue;
|
|
8446
|
+
structural ||= children;
|
|
8447
|
+
dirty.push(node);
|
|
8448
|
+
if (semantics) meaning.add(node);
|
|
8449
|
+
}
|
|
8450
|
+
if (!structural) return null;
|
|
8451
|
+
const owners = /* @__PURE__ */ new Set();
|
|
8452
|
+
for (const node of dirty) {
|
|
8453
|
+
const owner = this.semanticsOwnerOf(node);
|
|
8454
|
+
if (owner === null) return null;
|
|
8455
|
+
owners.add(owner);
|
|
8456
|
+
}
|
|
8457
|
+
for (const owner of owners) for (let above = owner.parent; above !== null; above = above.parent) if (owners.has(above)) {
|
|
8458
|
+
owners.delete(owner);
|
|
8459
|
+
break;
|
|
8460
|
+
}
|
|
8461
|
+
if (owners.size > SCOPED_SEMANTICS_OWNERS) return null;
|
|
8462
|
+
const path = /* @__PURE__ */ new Set();
|
|
8463
|
+
for (const node of dirty) for (let current = node; current !== null && !path.has(current); current = current.parent) {
|
|
8464
|
+
path.add(current);
|
|
8465
|
+
if (owners.has(current)) break;
|
|
8466
|
+
}
|
|
8467
|
+
const fresh = /* @__PURE__ */ new Map();
|
|
8468
|
+
const underChangedMeaning = (node) => {
|
|
8469
|
+
const known = fresh.get(node);
|
|
8470
|
+
if (known !== void 0) return known;
|
|
8471
|
+
const answer = meaning.has(node) || !owners.has(node) && node.parent !== null && underChangedMeaning(node.parent);
|
|
8472
|
+
fresh.set(node, answer);
|
|
8473
|
+
return answer;
|
|
8474
|
+
};
|
|
8475
|
+
const unchanged = (node) => !path.has(node) && (meaning.size === 0 || !underChangedMeaning(node));
|
|
8476
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8477
|
+
const reuse = {
|
|
8478
|
+
previous: this.semantics,
|
|
8479
|
+
childrenOf: (parent) => this.semanticsChildren.get(parent) ?? [],
|
|
8480
|
+
positionOf: (parent, id) => {
|
|
8481
|
+
let index = positions.get(parent);
|
|
8482
|
+
if (index === void 0) {
|
|
8483
|
+
index = new Map((this.semanticsChildren.get(parent) ?? []).map((child, i) => [child, i]));
|
|
8484
|
+
positions.set(parent, index);
|
|
8485
|
+
}
|
|
8486
|
+
return index.get(id) ?? -1;
|
|
8487
|
+
},
|
|
8488
|
+
memory: this.semanticsMemory,
|
|
8489
|
+
unchanged
|
|
8490
|
+
};
|
|
8491
|
+
const walks = [];
|
|
8492
|
+
for (const owner of owners) {
|
|
8493
|
+
const before = this.semantics.get(owner.id);
|
|
8494
|
+
if (before === void 0) return null;
|
|
8495
|
+
const walked = rewalkSemantics(owner, before.parent, this.semanticsIndexOf(before), semanticsInertAbove(owner), reuse);
|
|
8496
|
+
if (walked === null) return null;
|
|
8497
|
+
walks.push(walked);
|
|
8498
|
+
}
|
|
8499
|
+
const present = /* @__PURE__ */ new Set();
|
|
8500
|
+
for (const walked of walks) for (const list of walked.children.values()) for (const id of list) present.add(id);
|
|
8501
|
+
const removals = [];
|
|
8502
|
+
const remove = (id) => {
|
|
8503
|
+
removals.push({
|
|
8504
|
+
op: "remove",
|
|
8505
|
+
id
|
|
8506
|
+
});
|
|
8507
|
+
for (const child of this.semanticsChildren.get(id) ?? []) remove(child);
|
|
8508
|
+
this.semanticsChildren.delete(id);
|
|
8509
|
+
this.semantics.delete(id);
|
|
8510
|
+
};
|
|
8511
|
+
const formerly = /* @__PURE__ */ new Map();
|
|
8512
|
+
for (const walked of walks) for (const parent of walked.children.keys()) formerly.set(parent, this.semanticsChildren.get(parent) ?? []);
|
|
8513
|
+
for (const list of formerly.values()) for (const id of list) if (!present.has(id) && this.semantics.has(id)) remove(id);
|
|
8514
|
+
const reordered = /* @__PURE__ */ new Set();
|
|
8515
|
+
for (const walked of walks) for (const [parent, list] of walked.children) {
|
|
8516
|
+
const old = formerly.get(parent) ?? [];
|
|
8517
|
+
if (old.length === 0) continue;
|
|
8518
|
+
const was = new Map(old.map((id, i) => [id, i]));
|
|
8519
|
+
let last = -1;
|
|
8520
|
+
for (const id of list) {
|
|
8521
|
+
const at = was.get(id);
|
|
8522
|
+
if (at === void 0) continue;
|
|
8523
|
+
if (at < last) {
|
|
8524
|
+
reordered.add(parent);
|
|
8525
|
+
break;
|
|
8526
|
+
}
|
|
8527
|
+
last = at;
|
|
8528
|
+
}
|
|
8529
|
+
}
|
|
8530
|
+
const changes = [];
|
|
8531
|
+
for (const walked of walks) {
|
|
8532
|
+
for (const { id, parent, index } of walked.placed) {
|
|
8533
|
+
const record = walked.records.get(id);
|
|
8534
|
+
const before = this.semantics.get(id);
|
|
8535
|
+
if (record !== void 0) {
|
|
8536
|
+
if (before === void 0) changes.push({
|
|
8537
|
+
op: "add",
|
|
8538
|
+
node: record
|
|
8539
|
+
});
|
|
8540
|
+
else if (before.parent !== record.parent || reordered.has(parent) || !recordsEqual({
|
|
8541
|
+
...before,
|
|
8542
|
+
index: record.index
|
|
8543
|
+
}, record)) changes.push({
|
|
8544
|
+
op: "update",
|
|
8545
|
+
node: record
|
|
8546
|
+
});
|
|
8547
|
+
this.semantics.set(id, record);
|
|
8548
|
+
} else if (before === void 0) return null;
|
|
8549
|
+
else if (before.parent !== parent || reordered.has(parent)) {
|
|
8550
|
+
const moved = {
|
|
8551
|
+
...before,
|
|
8552
|
+
parent,
|
|
8553
|
+
index
|
|
8554
|
+
};
|
|
8555
|
+
changes.push({
|
|
8556
|
+
op: "update",
|
|
8557
|
+
node: moved
|
|
8558
|
+
});
|
|
8559
|
+
this.semantics.set(id, moved);
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
for (const [parent, list] of walked.children) this.semanticsChildren.set(parent, list);
|
|
8563
|
+
}
|
|
8564
|
+
this.semanticsOrdered = null;
|
|
8565
|
+
return [...removals, ...changes];
|
|
8566
|
+
}
|
|
8567
|
+
/** Whether a node is still in the tree being laid out. */
|
|
8568
|
+
attached(node) {
|
|
8569
|
+
const root = this.layoutRoot();
|
|
8570
|
+
for (let current = node; current !== null; current = current.parent) if (current === root) return true;
|
|
8571
|
+
return false;
|
|
8572
|
+
}
|
|
8573
|
+
/**
|
|
8028
8574
|
* The nearest node at or above `node` that holds a semantics record,
|
|
8029
8575
|
* or null when nothing above it does.
|
|
8030
8576
|
*/
|
|
@@ -8085,22 +8631,75 @@ var GessoRuntime = class {
|
|
|
8085
8631
|
*
|
|
8086
8632
|
* Ids that have left the tree are dropped here rather than tracked,
|
|
8087
8633
|
* since a removal patch has already told the mirror about them.
|
|
8634
|
+
*
|
|
8635
|
+
* **Found by walking what is on screen, not by checking everything.**
|
|
8636
|
+
* Bounding what was sent still computed every node's box to find out
|
|
8637
|
+
* whether it was on screen: a 5,000-line document has three thousand
|
|
8638
|
+
* fields, and the sweep cost 4 to 24 ms of every keystroke. The walk
|
|
8639
|
+
* goes down from the root carrying the scroll and sticky offsets that
|
|
8640
|
+
* `visibleBox` adds up per node, and passes over any subtree whose
|
|
8641
|
+
* bounds (the hit tester's, see `subtreeBoundsFor`) are off screen.
|
|
8642
|
+
* A node it passes over is one this skipped before anyway.
|
|
8088
8643
|
*/
|
|
8089
8644
|
collectSemanticsBoxes() {
|
|
8090
8645
|
const changed = [];
|
|
8091
|
-
const focused = this.focusManager.focusedNode
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
if (node === void 0 || this.engine.recordFor(node) === void 0) continue;
|
|
8095
|
-
const box = this.engine.visibleBox(node);
|
|
8646
|
+
const focused = this.focusManager.focusedNode;
|
|
8647
|
+
let focusedSeen = false;
|
|
8648
|
+
const offer = (id, box) => {
|
|
8096
8649
|
const last = this.semanticsBoxes.get(id);
|
|
8097
|
-
if (last !== void 0 && boxesEqual(last, box))
|
|
8098
|
-
if (id !== focused && !this.onScreen(box)) continue;
|
|
8650
|
+
if (last !== void 0 && boxesEqual(last, box)) return;
|
|
8099
8651
|
this.semanticsBoxes.set(id, box);
|
|
8100
8652
|
changed.push({
|
|
8101
8653
|
id,
|
|
8102
8654
|
box
|
|
8103
8655
|
});
|
|
8656
|
+
};
|
|
8657
|
+
const root = this.root;
|
|
8658
|
+
if (root !== void 0) {
|
|
8659
|
+
this.engine.subtreeBoundsFor(root);
|
|
8660
|
+
const stack = [[
|
|
8661
|
+
root,
|
|
8662
|
+
0,
|
|
8663
|
+
0
|
|
8664
|
+
]];
|
|
8665
|
+
while (stack.length > 0) {
|
|
8666
|
+
const [node, dx, dy] = stack.pop();
|
|
8667
|
+
const rec = this.engine.recordFor(node);
|
|
8668
|
+
let childX = dx;
|
|
8669
|
+
let childY = dy;
|
|
8670
|
+
if (rec !== void 0) {
|
|
8671
|
+
const x = dx + rec.stickyOffsetX;
|
|
8672
|
+
const y = dy + rec.stickyOffsetY;
|
|
8673
|
+
if (!rec.boundsUnbounded && node !== focused && !this.onScreen({
|
|
8674
|
+
x: rec.boundsMinX + x,
|
|
8675
|
+
y: rec.boundsMinY + y,
|
|
8676
|
+
width: rec.boundsMaxX - rec.boundsMinX,
|
|
8677
|
+
height: rec.boundsMaxY - rec.boundsMinY
|
|
8678
|
+
})) continue;
|
|
8679
|
+
if (this.semantics.has(node.id)) {
|
|
8680
|
+
const box = {
|
|
8681
|
+
x: rec.x + x,
|
|
8682
|
+
y: rec.y + y,
|
|
8683
|
+
width: rec.width,
|
|
8684
|
+
height: rec.height
|
|
8685
|
+
};
|
|
8686
|
+
if (node === focused) {
|
|
8687
|
+
focusedSeen = true;
|
|
8688
|
+
offer(node.id, box);
|
|
8689
|
+
} else if (this.onScreen(box)) offer(node.id, box);
|
|
8690
|
+
}
|
|
8691
|
+
childX = x - (rec.scrollable ? rec.scrollX : 0);
|
|
8692
|
+
childY = y - (rec.scrollable ? rec.scrollY : 0);
|
|
8693
|
+
}
|
|
8694
|
+
for (let child = node.lastChild; child !== null; child = child.previousSibling) stack.push([
|
|
8695
|
+
child,
|
|
8696
|
+
childX,
|
|
8697
|
+
childY
|
|
8698
|
+
]);
|
|
8699
|
+
}
|
|
8700
|
+
}
|
|
8701
|
+
if (focused !== null && focused !== void 0 && !focusedSeen && this.semantics.has(focused.id)) {
|
|
8702
|
+
if (this.engine.recordFor(focused) !== void 0) offer(focused.id, this.engine.visibleBox(focused));
|
|
8104
8703
|
}
|
|
8105
8704
|
if (this.semanticsBoxes.size > this.semantics.size) {
|
|
8106
8705
|
for (const id of this.semanticsBoxes.keys()) if (!this.semantics.has(id)) this.semanticsBoxes.delete(id);
|
|
@@ -8143,13 +8742,18 @@ var GessoRuntime = class {
|
|
|
8143
8742
|
* every pass, so writing the raw offset here is enough.
|
|
8144
8743
|
*/
|
|
8145
8744
|
createScrollSink() {
|
|
8745
|
+
const going = (node, record, axis) => {
|
|
8746
|
+
const written = node.getProperty(axis === "x" ? "scrollX" : "scrollY");
|
|
8747
|
+
const offset = typeof written === "number" ? written : axis === "x" ? record.scrollX : record.scrollY;
|
|
8748
|
+
return Math.min(Math.max(offset, 0), scrollRange(record, axis));
|
|
8749
|
+
};
|
|
8146
8750
|
return {
|
|
8147
8751
|
containerState: (node) => {
|
|
8148
8752
|
const record = this.engine.recordFor(node);
|
|
8149
8753
|
if (record === void 0) return;
|
|
8150
8754
|
return {
|
|
8151
|
-
scrollX: record
|
|
8152
|
-
scrollY: record
|
|
8755
|
+
scrollX: going(node, record, "x"),
|
|
8756
|
+
scrollY: going(node, record, "y"),
|
|
8153
8757
|
maxScrollX: scrollRange(record, "x"),
|
|
8154
8758
|
maxScrollY: scrollRange(record, "y"),
|
|
8155
8759
|
horizontal: node.getProperty("direction") === "row" || node.type === UiNodeType.Row,
|
|
@@ -8161,13 +8765,13 @@ var GessoRuntime = class {
|
|
|
8161
8765
|
const record = this.engine.recordFor(node);
|
|
8162
8766
|
if (record === void 0) return;
|
|
8163
8767
|
if (behavior === "smooth") {
|
|
8164
|
-
if (dx !== 0) this.smoothScroller.scrollBy(node, "scrollX", dx, record
|
|
8165
|
-
if (dy !== 0) this.smoothScroller.scrollBy(node, "scrollY", dy, record
|
|
8768
|
+
if (dx !== 0) this.smoothScroller.scrollBy(node, "scrollX", dx, going(node, record, "x"));
|
|
8769
|
+
if (dy !== 0) this.smoothScroller.scrollBy(node, "scrollY", dy, going(node, record, "y"));
|
|
8166
8770
|
return;
|
|
8167
8771
|
}
|
|
8168
8772
|
this.smoothScroller.stop(node);
|
|
8169
|
-
if (dx !== 0) node.setProperty("scrollX", record
|
|
8170
|
-
if (dy !== 0) node.setProperty("scrollY", record
|
|
8773
|
+
if (dx !== 0) node.setProperty("scrollX", going(node, record, "x") + dx);
|
|
8774
|
+
if (dy !== 0) node.setProperty("scrollY", going(node, record, "y") + dy);
|
|
8171
8775
|
this.graph.markDirty(node, DirtyFlags.Transform);
|
|
8172
8776
|
},
|
|
8173
8777
|
scrollContainers: () => this.engine.scrollContainers(),
|
|
@@ -8207,6 +8811,7 @@ var GessoRuntime = class {
|
|
|
8207
8811
|
for (const replica of this.replicas) replica.flush();
|
|
8208
8812
|
});
|
|
8209
8813
|
this.phaseTimings.environment = this.timePhase(() => this.graph.hasEnvironmentDirty(), () => this.graph.processEnvironmentDirty());
|
|
8814
|
+
if (this.input.wheel.advance(time)) this.scheduler.wake();
|
|
8210
8815
|
this.phaseTimings.virtualize = this.timePhase(() => this.hasVirtualWindows(), () => this.updateVirtualWindows());
|
|
8211
8816
|
}
|
|
8212
8817
|
hasVirtualWindows() {
|
|
@@ -8306,29 +8911,131 @@ var GessoRuntime = class {
|
|
|
8306
8911
|
if (rec === void 0) return 0;
|
|
8307
8912
|
return column ? rec.measuredHeight + rec.marginTop + rec.marginBottom : rec.measuredWidth + rec.marginLeft + rec.marginRight;
|
|
8308
8913
|
}
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8914
|
+
/**
|
|
8915
|
+
* Tells the layout listeners what moved, and lays out again for as
|
|
8916
|
+
* long as what they do in answer needs it, so the frame paints the
|
|
8917
|
+
* layout they settle on rather than the one they were told about.
|
|
8918
|
+
*
|
|
8919
|
+
* The browser's answer to the same problem. A `ResizeObserver`
|
|
8920
|
+
* callback runs after layout and before paint, and when it changes
|
|
8921
|
+
* layout the browser lays out again before it paints rather than
|
|
8922
|
+
* showing the stale boxes for a frame. The listeners here are
|
|
8923
|
+
* `breakpoint`, `sizeContainer` (and so `Responsive`),
|
|
8924
|
+
* `scrollPosition`, `autoFocus` and anything else on `host.onLayout`.
|
|
8925
|
+
* Before this a page whose breakpoint gave it wide padding was
|
|
8926
|
+
* painted with its narrow padding on the frame it appeared, and
|
|
8927
|
+
* jumped 16 px on the next.
|
|
8928
|
+
*
|
|
8929
|
+
* A pass after the first lays out only what the listeners dirtied,
|
|
8930
|
+
* the same incremental pass any frame runs, and then tells only the
|
|
8931
|
+
* listeners whose boxes it changed. A frame whose listeners wrote
|
|
8932
|
+
* nothing that lays out, which is almost every frame, runs no second
|
|
8933
|
+
* pass: it pays one look at the dirty set.
|
|
8934
|
+
*
|
|
8935
|
+
* **Bounded.** Listeners that keep moving each other's boxes would
|
|
8936
|
+
* otherwise never let the frame paint. ResizeObserver bounds its loop
|
|
8937
|
+
* by tree depth; this one is bounded by count, `MAX_LAYOUT_PASSES` in
|
|
8938
|
+
* all, which is easier to reason about and more than a real chain
|
|
8939
|
+
* needs (a breakpoint inside a `Responsive` inside a `Responsive` is
|
|
8940
|
+
* four passes). Past it the frame paints what it has, the rest waits
|
|
8941
|
+
* for the next frame as it always used to, and a warning says so
|
|
8942
|
+
* once, as the browser's "ResizeObserver loop completed with
|
|
8943
|
+
* undelivered notifications" does.
|
|
8944
|
+
*
|
|
8945
|
+
* **Reveals wait for the end.** `autoFocus` takes focus from a layout
|
|
8946
|
+
* listener, on its node's first layout, and focus reveals the node:
|
|
8947
|
+
* revealed from the first pass's boxes, it scrolled to where the node
|
|
8948
|
+
* was before a breakpoint moved it. A reveal asked for while the
|
|
8949
|
+
* listeners run is applied once they are quiet, from final boxes, and
|
|
8950
|
+
* its scroll laid out like any other write. So is the focus a hot
|
|
8951
|
+
* reload restores, after the listeners rather than before because
|
|
8952
|
+
* `autoFocus` is one of them and the restore has to be the last word:
|
|
8953
|
+
* every node in a subtree a reload replaced has its first layout on
|
|
8954
|
+
* this frame.
|
|
8955
|
+
*/
|
|
8956
|
+
settleLayout(frame, root, laidOut) {
|
|
8957
|
+
const stats = this.engine.stats;
|
|
8958
|
+
const settled = {
|
|
8959
|
+
frame,
|
|
8960
|
+
passes: laidOut ? 1 : 0,
|
|
8961
|
+
measured: laidOut ? stats.measured : 0,
|
|
8962
|
+
relayoutRoots: laidOut && !stats.fullLayout ? stats.relayoutRoots : 0
|
|
8963
|
+
};
|
|
8964
|
+
let fromRoot = laidOut && stats.fullLayout;
|
|
8965
|
+
const read = (node) => {
|
|
8321
8966
|
const record = this.engine.recordFor(node);
|
|
8322
8967
|
return {
|
|
8323
8968
|
box: this.engine.visibleBox(node),
|
|
8324
8969
|
scrollX: record?.scrollX ?? 0,
|
|
8325
8970
|
scrollY: record?.scrollY ?? 0
|
|
8326
8971
|
};
|
|
8327
|
-
}
|
|
8972
|
+
};
|
|
8973
|
+
this.deferredReveals = [];
|
|
8974
|
+
try {
|
|
8975
|
+
for (;;) {
|
|
8976
|
+
if (!this.layoutNotifier.isEmpty()) this.layoutNotifier.notify(read);
|
|
8977
|
+
if (!this.layoutPending()) {
|
|
8978
|
+
this.finishSettling(settled.passes > 0);
|
|
8979
|
+
if (!this.layoutPending()) break;
|
|
8980
|
+
}
|
|
8981
|
+
if (settled.passes >= MAX_LAYOUT_PASSES) {
|
|
8982
|
+
this.warnLayoutPasses();
|
|
8983
|
+
this.finishSettling(true);
|
|
8984
|
+
break;
|
|
8985
|
+
}
|
|
8986
|
+
this.phaseTimings.environment += this.timePhase(() => this.graph.hasEnvironmentDirty(), () => this.graph.processEnvironmentDirty());
|
|
8987
|
+
const more = this.scheduler.recollect(frame);
|
|
8988
|
+
settled.frame = settled.frame.merged(more);
|
|
8989
|
+
this.phaseTimings.layout += this.timePhase(() => true, () => this.engine.layoutForFrame(more, this.constraints, root));
|
|
8990
|
+
settled.passes++;
|
|
8991
|
+
settled.measured += stats.measured;
|
|
8992
|
+
settled.relayoutRoots += stats.fullLayout ? 0 : stats.relayoutRoots;
|
|
8993
|
+
fromRoot ||= stats.fullLayout;
|
|
8994
|
+
}
|
|
8995
|
+
} finally {
|
|
8996
|
+
this.deferredReveals = null;
|
|
8997
|
+
}
|
|
8998
|
+
if (fromRoot) settled.relayoutRoots = 0;
|
|
8999
|
+
return settled;
|
|
9000
|
+
}
|
|
9001
|
+
/**
|
|
9002
|
+
* Whether something written since the frame was collected has to be
|
|
9003
|
+
* laid out before the frame paints: the same flags that make a frame
|
|
9004
|
+
* lay out, or an inherited value still to be handed down.
|
|
9005
|
+
*/
|
|
9006
|
+
layoutPending() {
|
|
9007
|
+
return this.graph.hasEnvironmentDirty() || this.graph.getDirtyNodes().anyFlags(LAYOUT_DIRT);
|
|
9008
|
+
}
|
|
9009
|
+
/** Applies what waits for settled boxes: a reload's focus, then the reveals asked for. */
|
|
9010
|
+
finishSettling(laidOut) {
|
|
9011
|
+
const reveals = this.deferredReveals ?? [];
|
|
9012
|
+
this.deferredReveals = null;
|
|
8328
9013
|
if (this.restoringFocus && laidOut) {
|
|
8329
9014
|
this.restoringFocus = false;
|
|
8330
9015
|
this.restoreFocusAfterReload();
|
|
8331
9016
|
}
|
|
9017
|
+
for (const { node, padding } of reveals) if (this.engine.recordFor(node) !== void 0) this.scrollIntoView(node, padding);
|
|
9018
|
+
this.deferredReveals = [];
|
|
9019
|
+
}
|
|
9020
|
+
warnLayoutPasses() {
|
|
9021
|
+
if (this.warnedLayoutPasses) return;
|
|
9022
|
+
this.warnedLayoutPasses = true;
|
|
9023
|
+
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.`);
|
|
9024
|
+
}
|
|
9025
|
+
handleFrame(collected) {
|
|
9026
|
+
const root = this.root;
|
|
9027
|
+
if (root === void 0) return;
|
|
9028
|
+
const started = now();
|
|
9029
|
+
this.focusManager.settleScope();
|
|
9030
|
+
const firstPass = frameNeedsLayout(collected);
|
|
9031
|
+
this.phaseTimings.layout = this.timePhase(() => firstPass, () => this.engine.layoutForFrame(collected, this.constraints, root));
|
|
9032
|
+
const settled = this.settleLayout(collected, root, firstPass);
|
|
9033
|
+
const frame = settled.frame;
|
|
9034
|
+
const laidOut = settled.passes > 0;
|
|
9035
|
+
if (laidOut) {
|
|
9036
|
+
this.laidOutOnce = true;
|
|
9037
|
+
this.inspector.recordLayout(started);
|
|
9038
|
+
}
|
|
8332
9039
|
this.sendEditingState();
|
|
8333
9040
|
const rebuildSemantics = frameNeedsSemantics(frame);
|
|
8334
9041
|
this.phaseTimings.semantics = this.timePhase(() => rebuildSemantics || this.semanticsListener !== null && (laidOut || this.focusManager.focusedNode?.id !== this.lastFocusedId), () => this.updateSemantics(frame, rebuildSemantics, laidOut));
|
|
@@ -8362,8 +9069,9 @@ var GessoRuntime = class {
|
|
|
8362
9069
|
frame: frame.id,
|
|
8363
9070
|
durationMs: elapsed,
|
|
8364
9071
|
nodes: frame.size,
|
|
8365
|
-
measured:
|
|
8366
|
-
relayoutRoots:
|
|
9072
|
+
measured: settled.measured,
|
|
9073
|
+
relayoutRoots: settled.relayoutRoots,
|
|
9074
|
+
layoutPasses: settled.passes,
|
|
8367
9075
|
at: finished,
|
|
8368
9076
|
inputLatencyMs: this.inputLatency.take(epochAt(finished)),
|
|
8369
9077
|
phases: this.phaseTimings,
|
|
@@ -8589,7 +9297,7 @@ function timeOrigin() {
|
|
|
8589
9297
|
}
|
|
8590
9298
|
function editingStatesEqual(a, b) {
|
|
8591
9299
|
if (a === null || b === null) return a === b;
|
|
8592
|
-
return a.text === b.text && a.selectionStart === b.selectionStart && a.selectionEnd === b.selectionEnd && a.multiline === b.multiline && a.composing === b.composing && a.caret.x === b.caret.x && a.caret.y === b.caret.y && a.caret.width === b.caret.width && a.caret.height === b.caret.height;
|
|
9300
|
+
return a.text === b.text && a.selectionStart === b.selectionStart && a.selectionEnd === b.selectionEnd && a.multiline === b.multiline && a.composing === b.composing && a.html === b.html && a.caret.x === b.caret.x && a.caret.y === b.caret.y && a.caret.width === b.caret.width && a.caret.height === b.caret.height;
|
|
8593
9301
|
}
|
|
8594
9302
|
/** A canvas for the text measurer when the draw canvas is not a 2D one. */
|
|
8595
9303
|
function createMeasureCanvas() {
|
|
@@ -8609,6 +9317,14 @@ function emptyGpuTimings() {
|
|
|
8609
9317
|
encode: 0
|
|
8610
9318
|
};
|
|
8611
9319
|
}
|
|
9320
|
+
/**
|
|
9321
|
+
* The most layout passes one frame runs before it paints; see
|
|
9322
|
+
* `settleLayout`. One for the frame and up to seven more for what its
|
|
9323
|
+
* layout listeners change.
|
|
9324
|
+
*/
|
|
9325
|
+
const MAX_LAYOUT_PASSES = 8;
|
|
9326
|
+
/** Dirt that makes a frame lay out; `frameNeedsLayout` asks the same of a collected frame. */
|
|
9327
|
+
const LAYOUT_DIRT = DirtyFlags.Layout | DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Transform;
|
|
8612
9328
|
function emptyPhaseTimings() {
|
|
8613
9329
|
return {
|
|
8614
9330
|
ticks: 0,
|
|
@@ -8628,7 +9344,7 @@ function emptyPhaseTimings() {
|
|
|
8628
9344
|
* spending an immeasurable amount of time deciding to do nothing.
|
|
8629
9345
|
*/
|
|
8630
9346
|
function frameNeedsLayout(frame) {
|
|
8631
|
-
return frame.anyFlags(
|
|
9347
|
+
return frame.anyFlags(LAYOUT_DIRT);
|
|
8632
9348
|
}
|
|
8633
9349
|
/**
|
|
8634
9350
|
* Semantics follow the properties that carry them and the shape of the
|
|
@@ -8648,6 +9364,18 @@ function frameNeedsLayout(frame) {
|
|
|
8648
9364
|
*/
|
|
8649
9365
|
const SCOPED_SEMANTICS_OWNERS = 32;
|
|
8650
9366
|
/**
|
|
9367
|
+
* The index each id in a parent's list of children has. A paragraph's
|
|
9368
|
+
* text runs are numbered among themselves and its other children among
|
|
9369
|
+
* themselves, as `buildSemanticsTree` numbers them.
|
|
9370
|
+
*/
|
|
9371
|
+
function semanticIndices(list) {
|
|
9372
|
+
const out = [];
|
|
9373
|
+
let runs = 0;
|
|
9374
|
+
let others = 0;
|
|
9375
|
+
for (const id of list) out.push(id.includes(TEXT_RUN_ID_SEPARATOR) ? runs++ : others++);
|
|
9376
|
+
return out;
|
|
9377
|
+
}
|
|
9378
|
+
/**
|
|
8651
9379
|
* How far outside the canvas a mirrored box is still worth reporting.
|
|
8652
9380
|
*
|
|
8653
9381
|
* One screenful of slack, so a fling that covers a lot of ground in
|
|
@@ -8689,6 +9417,14 @@ function frameNeedsSemantics(frame) {
|
|
|
8689
9417
|
*/
|
|
8690
9418
|
var GessoApp = class {
|
|
8691
9419
|
runtime;
|
|
9420
|
+
/**
|
|
9421
|
+
* The clock the runtime was given, its callback, and whether a frame
|
|
9422
|
+
* is waiting on it: what `uiHost().flush` needs to run that frame now,
|
|
9423
|
+
* since a background tab gets no animation frames to run it.
|
|
9424
|
+
*/
|
|
9425
|
+
clock;
|
|
9426
|
+
frameCallback;
|
|
9427
|
+
framePending = false;
|
|
8692
9428
|
canvas;
|
|
8693
9429
|
host;
|
|
8694
9430
|
inputEnabled;
|
|
@@ -8709,6 +9445,7 @@ var GessoApp = class {
|
|
|
8709
9445
|
detachFullscreen = null;
|
|
8710
9446
|
fullscreen = false;
|
|
8711
9447
|
detachReducedMotion = null;
|
|
9448
|
+
detachContrast = null;
|
|
8712
9449
|
/** Stops watching `prefers-color-scheme`; null while overridden. */
|
|
8713
9450
|
detachColorScheme = null;
|
|
8714
9451
|
/** Stops watching `visualViewport` for the safe area and the keyboard. */
|
|
@@ -8731,7 +9468,24 @@ var GessoApp = class {
|
|
|
8731
9468
|
routes: options.routes,
|
|
8732
9469
|
media: options.media,
|
|
8733
9470
|
fonts: options.fonts,
|
|
8734
|
-
clock:
|
|
9471
|
+
clock: (callback) => {
|
|
9472
|
+
const clock = (options.clock ?? ((onFrame) => new UiAnimationFrameClock(onFrame)))((time) => {
|
|
9473
|
+
this.framePending = false;
|
|
9474
|
+
callback(time);
|
|
9475
|
+
});
|
|
9476
|
+
this.clock = clock;
|
|
9477
|
+
this.frameCallback = callback;
|
|
9478
|
+
return {
|
|
9479
|
+
requestFrame: () => {
|
|
9480
|
+
this.framePending = true;
|
|
9481
|
+
clock.requestFrame();
|
|
9482
|
+
},
|
|
9483
|
+
cancelFrame: () => {
|
|
9484
|
+
this.framePending = false;
|
|
9485
|
+
clock.cancelFrame();
|
|
9486
|
+
}
|
|
9487
|
+
};
|
|
9488
|
+
},
|
|
8735
9489
|
dpr: devicePixelRatio()
|
|
8736
9490
|
});
|
|
8737
9491
|
const input = this.runtime.input;
|
|
@@ -8741,6 +9495,30 @@ var GessoApp = class {
|
|
|
8741
9495
|
keyboardController: input.keyboard
|
|
8742
9496
|
});
|
|
8743
9497
|
}
|
|
9498
|
+
/**
|
|
9499
|
+
* The running app, as the agent's screen tools need it: its semantics
|
|
9500
|
+
* tree and focus, the mirror's actions, keys, and a way to run a
|
|
9501
|
+
* pending frame now. See `uiSurface` in `gesso-framework/agent`.
|
|
9502
|
+
*/
|
|
9503
|
+
uiHost() {
|
|
9504
|
+
const runtime = this.runtime;
|
|
9505
|
+
return {
|
|
9506
|
+
semanticsTree: () => runtime.semanticsTree(),
|
|
9507
|
+
focusedNodeId: () => runtime.focusedNodeId(),
|
|
9508
|
+
applySemanticsAction: (action) => runtime.applySemanticsAction(action),
|
|
9509
|
+
key: (key, modifiers) => {
|
|
9510
|
+
runtime.input.keyboard.keyDown(key, modifiers);
|
|
9511
|
+
runtime.input.keyboard.keyUp(key, modifiers);
|
|
9512
|
+
},
|
|
9513
|
+
flush: () => {
|
|
9514
|
+
if (this.framePending && this.clock !== void 0 && this.frameCallback !== void 0) {
|
|
9515
|
+
this.framePending = false;
|
|
9516
|
+
this.clock.cancelFrame();
|
|
9517
|
+
this.frameCallback(performance.now());
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9520
|
+
};
|
|
9521
|
+
}
|
|
8744
9522
|
/** The runtime services a component in this app can inject. */
|
|
8745
9523
|
get services() {
|
|
8746
9524
|
return this.runtime.services;
|
|
@@ -8877,6 +9655,8 @@ var GessoApp = class {
|
|
|
8877
9655
|
this.detachFullscreen = null;
|
|
8878
9656
|
this.detachReducedMotion?.();
|
|
8879
9657
|
this.detachReducedMotion = null;
|
|
9658
|
+
this.detachContrast?.();
|
|
9659
|
+
this.detachContrast = null;
|
|
8880
9660
|
this.detachColorScheme?.();
|
|
8881
9661
|
this.detachColorScheme = null;
|
|
8882
9662
|
this.detachViewportInsets?.();
|
|
@@ -8901,7 +9681,7 @@ var GessoApp = class {
|
|
|
8901
9681
|
compositionStart: () => editing.compositionStart(),
|
|
8902
9682
|
compositionUpdate: (text, caret) => editing.compositionUpdate(text, caret),
|
|
8903
9683
|
compositionEnd: (text) => editing.compositionEnd(text),
|
|
8904
|
-
paste: (text) => editing.paste(text),
|
|
9684
|
+
paste: (text, html) => editing.paste(text, html),
|
|
8905
9685
|
blur: () => this.runtime.input.focus.blur()
|
|
8906
9686
|
});
|
|
8907
9687
|
this.runtime.setTextInputSource("proxy");
|
|
@@ -8934,6 +9714,7 @@ var GessoApp = class {
|
|
|
8934
9714
|
this.runtime.setFullscreen(isDocumentFullscreen(canvas));
|
|
8935
9715
|
}
|
|
8936
9716
|
this.detachReducedMotion = observeReducedMotion((reduced) => this.runtime.setReducedMotion(reduced));
|
|
9717
|
+
this.detachContrast = observeContrast((contrast) => this.runtime.setContrast(contrast));
|
|
8937
9718
|
this.setColorScheme(this.colorSchemePreference);
|
|
8938
9719
|
}
|
|
8939
9720
|
/**
|
|
@@ -8949,7 +9730,7 @@ var GessoApp = class {
|
|
|
8949
9730
|
if (!this.accessibilityEnabled) return;
|
|
8950
9731
|
const mirror = new SemanticsMirror(canvas, {
|
|
8951
9732
|
action: (action) => this.runtime.applySemanticsAction(action),
|
|
8952
|
-
paste: (text) => this.runtime.input.editing.paste(text)
|
|
9733
|
+
paste: (text, html) => this.runtime.input.editing.paste(text, html)
|
|
8953
9734
|
}, this.proxy);
|
|
8954
9735
|
this.mirror = mirror;
|
|
8955
9736
|
this.runtime.onSemantics((update) => mirror.apply(update));
|
|
@@ -8994,10 +9775,11 @@ var GessoApp = class {
|
|
|
8994
9775
|
if (request.type === "popup") this.runtime.settlePopup(request.id, false);
|
|
8995
9776
|
else if (request.type === "storage") this.runtime.settleStorage(request.id, shellStorageDenied());
|
|
8996
9777
|
else if (request.type === "file") this.runtime.settleFile(request.id, shellFilesUnsupported("There is no document to reach files through."));
|
|
9778
|
+
else if (request.type === "clipboard") this.runtime.settleClipboard(request.id, false);
|
|
8997
9779
|
return;
|
|
8998
9780
|
}
|
|
8999
9781
|
if (request.type === "clipboard") {
|
|
9000
|
-
writeClipboard(request.text, this.canvas.ownerDocument);
|
|
9782
|
+
writeClipboard(request.text, this.canvas.ownerDocument).then((copied) => this.runtime.settleClipboard(request.id, copied));
|
|
9001
9783
|
return;
|
|
9002
9784
|
}
|
|
9003
9785
|
if (request.type === "fullscreen") {
|
|
@@ -9070,6 +9852,10 @@ var GessoAppBuilder = class {
|
|
|
9070
9852
|
mediaOptions;
|
|
9071
9853
|
fontDeclarations;
|
|
9072
9854
|
app;
|
|
9855
|
+
/** The mounted app's channel registry, for the workers an agent port asks. */
|
|
9856
|
+
channelHandle;
|
|
9857
|
+
webmcpChoice = false;
|
|
9858
|
+
disconnectWebMcp = null;
|
|
9073
9859
|
colorSchemePreference = "auto";
|
|
9074
9860
|
constructor(root) {
|
|
9075
9861
|
this.root = root;
|
|
@@ -9260,13 +10046,61 @@ var GessoAppBuilder = class {
|
|
|
9260
10046
|
if (this.errorListener !== void 0) app.onError(this.errorListener);
|
|
9261
10047
|
if (this.devtoolsListener !== null) app.onDevtools(this.devtoolsListener);
|
|
9262
10048
|
this.app = app;
|
|
10049
|
+
this.channelHandle = channels;
|
|
9263
10050
|
app.mount();
|
|
10051
|
+
this.connectWebMcp(app);
|
|
9264
10052
|
return () => {
|
|
10053
|
+
this.disconnectWebMcp?.();
|
|
10054
|
+
this.disconnectWebMcp = null;
|
|
9265
10055
|
this.app = void 0;
|
|
10056
|
+
this.channelHandle = void 0;
|
|
9266
10057
|
app.dispose();
|
|
9267
10058
|
channels.dispose();
|
|
9268
10059
|
};
|
|
9269
10060
|
}
|
|
10061
|
+
/**
|
|
10062
|
+
* Offer the app's channels and screen to an AI agent in the browser,
|
|
10063
|
+
* through WebMCP (default false). The single-thread form of
|
|
10064
|
+
* `createApp({ webmcp })`: registered once `mountSync` has run,
|
|
10065
|
+
* removed when the app is unmounted. Call before `mountSync`.
|
|
10066
|
+
*/
|
|
10067
|
+
useWebMcp(choice = true) {
|
|
10068
|
+
this.webmcpChoice = choice;
|
|
10069
|
+
return this;
|
|
10070
|
+
}
|
|
10071
|
+
/**
|
|
10072
|
+
* Opens a port to what this app serves under `key`, or returns
|
|
10073
|
+
* undefined before `mountSync`.
|
|
10074
|
+
*
|
|
10075
|
+
* The same question `WorkerApp.openRenderPort` answers, asked of the
|
|
10076
|
+
* page instead of a render worker, because in this configuration the
|
|
10077
|
+
* page is the thread that draws. It serves one thing, `gesso:agent`:
|
|
10078
|
+
* the channels fed here, the workers behind them, and the screen.
|
|
10079
|
+
* The agent code is loaded the first time one is opened.
|
|
10080
|
+
*/
|
|
10081
|
+
openRenderPort(key) {
|
|
10082
|
+
const app = this.app;
|
|
10083
|
+
if (app === void 0 || key !== "gesso:agent") return;
|
|
10084
|
+
const pair = new MessageChannel();
|
|
10085
|
+
const registrations = this.channelRegistrations;
|
|
10086
|
+
import("./app-BvlIO1G9.js").then((n) => n.t).then((agent) => agent.serveApplicationAgent(pair.port2, {
|
|
10087
|
+
channels: () => registrations.filter((registration) => registration.source !== void 0).map((registration) => ({
|
|
10088
|
+
token: registration.token,
|
|
10089
|
+
source: registration.source
|
|
10090
|
+
})),
|
|
10091
|
+
workers: () => this.channelHandle?.workers ?? [],
|
|
10092
|
+
ui: () => this.app === app ? app.uiHost() : void 0
|
|
10093
|
+
}));
|
|
10094
|
+
return pair.port1;
|
|
10095
|
+
}
|
|
10096
|
+
connectWebMcp(app) {
|
|
10097
|
+
const choice = this.webmcpChoice;
|
|
10098
|
+
if (choice === false) return;
|
|
10099
|
+
import("./webmcp-CJVFvAHe.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, choice === true ? {} : choice)).then((disconnect) => {
|
|
10100
|
+
if (this.app === app) this.disconnectWebMcp = disconnect;
|
|
10101
|
+
else disconnect();
|
|
10102
|
+
}, (error) => console.warn("[gesso] WebMCP could not register the channels:", error));
|
|
10103
|
+
}
|
|
9270
10104
|
};
|
|
9271
10105
|
function requireElement(selector) {
|
|
9272
10106
|
const element = document.querySelector(selector);
|
|
@@ -9818,6 +10652,11 @@ var RenderWorkerApp = class {
|
|
|
9818
10652
|
this.root = typeof root === "function" ? createComponent(root) : root;
|
|
9819
10653
|
this.host = host;
|
|
9820
10654
|
this.host.onmessage = (event) => this.receive(event.data);
|
|
10655
|
+
servePorts((key, port) => {
|
|
10656
|
+
if (key !== "gesso:agent") return false;
|
|
10657
|
+
this.serveAgent(port);
|
|
10658
|
+
return true;
|
|
10659
|
+
}, () => [AGENT_PORT], host);
|
|
9821
10660
|
this.host.addEventListener("error", (event) => {
|
|
9822
10661
|
const error = event.error;
|
|
9823
10662
|
this.reportUncaught(error ?? event.message ?? "Unknown error", error === void 0 ? locationOf(event) : void 0);
|
|
@@ -9995,6 +10834,41 @@ var RenderWorkerApp = class {
|
|
|
9995
10834
|
* console is the worker global's, and this class is what owns the
|
|
9996
10835
|
* global.
|
|
9997
10836
|
*/
|
|
10837
|
+
/**
|
|
10838
|
+
* Answers an agent port with every channel this application can
|
|
10839
|
+
* reach: the ones fed from this thread, and whatever each worker
|
|
10840
|
+
* behind them serves, asked over a port of its own. Imported rather
|
|
10841
|
+
* than loaded on demand: a worker built as an IIFE, which is Vite's
|
|
10842
|
+
* default, cannot split off a chunk, and a dynamic import here failed
|
|
10843
|
+
* every such build. Nothing runs until an agent asks.
|
|
10844
|
+
*/
|
|
10845
|
+
serveAgent(port) {
|
|
10846
|
+
serveApplicationAgent(port, {
|
|
10847
|
+
channels: () => this.channelRegistrations.filter((registration) => registration.source !== void 0).map((registration) => ({
|
|
10848
|
+
token: registration.token,
|
|
10849
|
+
source: registration.source
|
|
10850
|
+
})),
|
|
10851
|
+
workers: () => {
|
|
10852
|
+
const workers = new Set(this.channels?.workers ?? []);
|
|
10853
|
+
if (this.appLogicWorker !== void 0) workers.add(this.appLogicWorker);
|
|
10854
|
+
return workers;
|
|
10855
|
+
},
|
|
10856
|
+
ui: () => {
|
|
10857
|
+
const runtime = this.runtime;
|
|
10858
|
+
if (runtime === void 0) return;
|
|
10859
|
+
return {
|
|
10860
|
+
semanticsTree: () => runtime.semanticsTree(),
|
|
10861
|
+
focusedNodeId: () => runtime.focusedNodeId(),
|
|
10862
|
+
applySemanticsAction: (action) => runtime.applySemanticsAction(action),
|
|
10863
|
+
key: (key, modifiers) => {
|
|
10864
|
+
runtime.input.keyboard.keyDown(key, modifiers);
|
|
10865
|
+
runtime.input.keyboard.keyUp(key, modifiers);
|
|
10866
|
+
},
|
|
10867
|
+
flush: () => this.clock?.tick(performance.now())
|
|
10868
|
+
};
|
|
10869
|
+
}
|
|
10870
|
+
});
|
|
10871
|
+
}
|
|
9998
10872
|
setConsoleForwarding(enabled) {
|
|
9999
10873
|
this.restoreConsole?.();
|
|
10000
10874
|
this.restoreConsole = null;
|
|
@@ -10048,7 +10922,7 @@ var RenderWorkerApp = class {
|
|
|
10048
10922
|
runtime.input.pointer.pointerCancel(message.pointer);
|
|
10049
10923
|
break;
|
|
10050
10924
|
case "wheel":
|
|
10051
|
-
runtime.input.wheel.wheel(message.x, message.y, message.deltaX, message.deltaY, message.modifiers, message.deltaMode, message.wheelDeltaY);
|
|
10925
|
+
runtime.input.wheel.wheel(message.x, message.y, message.deltaX, message.deltaY, message.modifiers, message.deltaMode, message.wheelDeltaY, message.at === void 0 ? void 0 : message.at - performance.timeOrigin);
|
|
10052
10926
|
break;
|
|
10053
10927
|
case "keyDown":
|
|
10054
10928
|
runtime.input.keyboard.keyDown(message.key, message.modifiers, message.textFollows);
|
|
@@ -10069,7 +10943,7 @@ var RenderWorkerApp = class {
|
|
|
10069
10943
|
runtime.input.editing.compositionEnd(message.text);
|
|
10070
10944
|
break;
|
|
10071
10945
|
case "paste":
|
|
10072
|
-
runtime.input.editing.paste(message.text);
|
|
10946
|
+
runtime.input.editing.paste(message.text, message.html ?? null);
|
|
10073
10947
|
break;
|
|
10074
10948
|
case "blur":
|
|
10075
10949
|
runtime.input.focus.blur();
|
|
@@ -10080,6 +10954,9 @@ var RenderWorkerApp = class {
|
|
|
10080
10954
|
case "colorScheme":
|
|
10081
10955
|
runtime.setColorScheme(message.scheme);
|
|
10082
10956
|
break;
|
|
10957
|
+
case "contrast":
|
|
10958
|
+
runtime.setContrast(message.contrast);
|
|
10959
|
+
break;
|
|
10083
10960
|
case "viewportInsets":
|
|
10084
10961
|
runtime.setViewportInsets(message.insets);
|
|
10085
10962
|
break;
|
|
@@ -10089,6 +10966,9 @@ var RenderWorkerApp = class {
|
|
|
10089
10966
|
case "popupResult":
|
|
10090
10967
|
runtime.settlePopup(message.id, message.opened);
|
|
10091
10968
|
break;
|
|
10969
|
+
case "clipboardResult":
|
|
10970
|
+
runtime.settleClipboard(message.id, message.copied);
|
|
10971
|
+
break;
|
|
10092
10972
|
case "storageResult":
|
|
10093
10973
|
runtime.settleStorage(message.id, message.result);
|
|
10094
10974
|
break;
|
|
@@ -10232,6 +11112,7 @@ var RenderWorkerApp = class {
|
|
|
10232
11112
|
this.runtime.onShellRequest((request) => {
|
|
10233
11113
|
if (request.type === "clipboard") this.host.postMessage({
|
|
10234
11114
|
type: "clipboard",
|
|
11115
|
+
id: request.id,
|
|
10235
11116
|
text: request.text
|
|
10236
11117
|
});
|
|
10237
11118
|
else if (request.type === "openUrl") this.host.postMessage({
|
|
@@ -10310,6 +11191,7 @@ var RenderWorkerApp = class {
|
|
|
10310
11191
|
nodes: metrics.nodes,
|
|
10311
11192
|
measured: metrics.measured,
|
|
10312
11193
|
relayoutRoots: metrics.relayoutRoots,
|
|
11194
|
+
layoutPasses: metrics.layoutPasses,
|
|
10313
11195
|
at: metrics.at,
|
|
10314
11196
|
inputLatencyMs: metrics.inputLatencyMs,
|
|
10315
11197
|
phases: metrics.phases,
|
|
@@ -10329,6 +11211,6 @@ function locationOf(event) {
|
|
|
10329
11211
|
return `${event.filename}:${line}:${column}`;
|
|
10330
11212
|
}
|
|
10331
11213
|
//#endregion
|
|
10332
|
-
export { APPLICATION_WORKER, AnimationService, AudioService, AudioSink, BoundsCell, Channel, ChannelRegistry, ChannelReplica, Component, ComponentHost, ComponentHostResolver, ComputedCell, Define, Each, EditingProxy, EditingService, FanCell, FanOut, FindService, FocusService, FontService, FrameService, GessoApp, GessoAppBuilder, GessoRuntime, IndexedDbStorage, Inject, Input, InputCell, InternalState, MARK_PREFIX, MediaService, MemoryStorage, OpfsStorage, Output, OverlayLayer, OverlayService, PersistedState, Presence, ProvidedChannel, RenderWorkerApp, Resource, RouteState, RouterOutlet, RouterService, SemanticsMirror, ServiceRegistry, ShellService, ShellStorage, Show, TextService, ThemeTokenCell, UI_FRAME_PHASES, UI_ROLES, UI_SEMANTIC_STATES, UndoStack, WorkerApp, applyPatch, applyPatches, audioClock, bind, bounds, buildPath, channel, classifyStorageError, computed, controlled, createApp, createChannelRegistry, createComponent, createShellHistory, createSyncApp, debounced, defineChannel, derive, describeStream, diffProjection, each, fanOut, findUnplainPath, formatAge, formatNodePath, formatNodeReport, formatStream, formatUrl, input, internalState, into, isChannelClientMessage, isChannelHostMessage, isClassComponent, isComponentElement, isHubMessage, isOutputTarget, isPortErrorMessage, isPortHandshake, markInstant, markNow, measureSpan, mutate, observeColorScheme, observeMediaQuery, observeReducedMotion, output, parseUrl, performShellStorage, performanceMarksEnabled, persisted, pick, pickKeys, portHandle, printPropValue, provide, registerUndoShortcuts, renderRoot, requirePlainData, resource, route, select, serve, serveChannels, servePorts, setPerformanceMarks, shellStorageDenied, show, storageErrorMessage, storageReadFailure, storageReadValue, structurallyEqual, themeTokenCell, throttled, to, treeText, undoable, viewKeys, workerHandle, writeClipboard };
|
|
11214
|
+
export { APPLICATION_WORKER, AnimationService, AudioService, AudioSink, BoundsCell, Channel, ChannelRegistry, ChannelReplica, Component, ComponentHost, ComponentHostResolver, ComputedCell, Define, Each, EditingProxy, EditingService, FanCell, FanOut, FindService, FocusService, FontService, FrameService, GessoApp, GessoAppBuilder, GessoRuntime, IndexedDbStorage, Inject, Input, InputCell, InternalState, MARK_PREFIX, MediaService, MemoryStorage, OpfsStorage, Output, OverlayLayer, OverlayService, PersistedState, Presence, ProvidedChannel, RenderWorkerApp, Resource, RouteState, RouterOutlet, RouterService, ScrollService, SemanticsMirror, ServiceRegistry, ShellService, ShellStorage, Show, TextService, ThemeTokenCell, UI_FRAME_PHASES, UI_ROLES, UI_SEMANTIC_STATES, UndoStack, WorkerApp, applyPatch, applyPatches, audioClock, bind, bounds, buildPath, channel, channelSchema, classifyStorageError, computed, controlled, createApp, createChannelRegistry, createComponent, createShellHistory, createSyncApp, debounced, defineChannel, derive, describeChannel, describeStream, diffProjection, each, fanOut, findUnplainPath, formatAge, formatNodePath, formatNodeReport, formatStream, formatUrl, input, internalState, into, isChannelClientMessage, isChannelHostMessage, isClassComponent, isComponentElement, isHubMessage, isOutputTarget, isPortErrorMessage, isPortHandshake, markInstant, markNow, measureSpan, mutate, observeColorScheme, observeMediaQuery, observeReducedMotion, output, parseUrl, performShellStorage, performanceMarksEnabled, persisted, pick, pickKeys, portHandle, printPropValue, provide, registerUndoShortcuts, renderRoot, requirePlainData, resource, route, select, serve, serveChannels, servePorts, setPerformanceMarks, shellStorageDenied, show, storageErrorMessage, storageReadFailure, storageReadValue, structurallyEqual, themeTokenCell, throttled, to, treeText, undoable, viewKeys, workerHandle, writeClipboard };
|
|
10333
11215
|
|
|
10334
11216
|
//# sourceMappingURL=index.js.map
|