gesso-framework 0.5.0 → 0.6.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 +47 -0
- package/dist/{index-BDM_gzzZ.d.ts → index-DidpPgDT.d.ts} +117 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +265 -43
- package/dist/index.js.map +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { a as Each, i as show, n as getComponentMetadata, o as each, r as Show,
|
|
|
3
3
|
import { c as channelSchema, l as describeChannel, t as AGENT_PORT } from "./remote-xxij8zXe.js";
|
|
4
4
|
import { n as serveApplicationAgent } from "./app-BvlIO1G9.js";
|
|
5
5
|
import { BehaviorSubject, EMPTY, Observable, Subject, Subscription, distinctUntilChanged, map } from "rxjs";
|
|
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";
|
|
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, defineModifier, describeOverrides, diffSemantics, dragSessionFor, dropIndexShifts, easings, fade, 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";
|
|
7
7
|
//#region src/Component.ts
|
|
8
8
|
/**
|
|
9
9
|
* Base class for all Gesso framework components.
|
|
@@ -587,15 +587,28 @@ var ChannelReplica = class {
|
|
|
587
587
|
get send() {
|
|
588
588
|
return this.commandProxy;
|
|
589
589
|
}
|
|
590
|
-
viewProxy = new Proxy({}, {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
590
|
+
viewProxy = new Proxy({}, {
|
|
591
|
+
get: (_target, property) => {
|
|
592
|
+
if (typeof property !== "string") return;
|
|
593
|
+
const cell = this.cells.get(property);
|
|
594
|
+
if (cell === void 0) {
|
|
595
|
+
const names = [...this.cells.keys()].sort().join(", ");
|
|
596
|
+
throw new Error(`'${property}' is not a view key on channel '${this.token.name}'. Declared keys: ${names.length > 0 ? names : "(none)"}.`);
|
|
597
|
+
}
|
|
598
|
+
return cell;
|
|
599
|
+
},
|
|
600
|
+
has: (_target, property) => typeof property === "string" && this.cells.has(property),
|
|
601
|
+
ownKeys: () => [...this.cells.keys()],
|
|
602
|
+
getOwnPropertyDescriptor: (_target, property) => {
|
|
603
|
+
const cell = typeof property === "string" ? this.cells.get(property) : void 0;
|
|
604
|
+
return cell === void 0 ? void 0 : {
|
|
605
|
+
value: cell,
|
|
606
|
+
enumerable: true,
|
|
607
|
+
configurable: true,
|
|
608
|
+
writable: false
|
|
609
|
+
};
|
|
596
610
|
}
|
|
597
|
-
|
|
598
|
-
} });
|
|
611
|
+
});
|
|
599
612
|
createCommandProxy() {
|
|
600
613
|
return new Proxy({}, { get: (_target, property) => {
|
|
601
614
|
if (typeof property !== "string") return;
|
|
@@ -841,19 +854,28 @@ let OverlayLayer = class OverlayLayer extends Component {
|
|
|
841
854
|
renderEntries(entries) {
|
|
842
855
|
const elements = [];
|
|
843
856
|
for (const entry of entries) {
|
|
844
|
-
if (entry.dismissOnOutsidePress)
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
857
|
+
if (entry.dismissOnOutsidePress === true || entry.modal === true) {
|
|
858
|
+
const dismiss = entry.dismissOnOutsidePress === true;
|
|
859
|
+
const environment = entry.environment ?? entry.anchor ?? null;
|
|
860
|
+
elements.push(Box({
|
|
861
|
+
key: `${entry.id}\0backdrop`,
|
|
862
|
+
position: "absolute",
|
|
863
|
+
inset: 0,
|
|
864
|
+
zIndex: entry.zIndex,
|
|
865
|
+
...inheritedFrom(environment),
|
|
866
|
+
modifiers: followEnvironment(environment),
|
|
867
|
+
...dismiss ? {
|
|
868
|
+
onPointerDown: () => this.overlays.close(entry.id),
|
|
869
|
+
onWheel: () => this.overlays.close(entry.id)
|
|
870
|
+
} : {}
|
|
871
|
+
}, ...entry.scrim ?? entry.modal === true ? [scrim()] : []));
|
|
872
|
+
}
|
|
852
873
|
elements.push(Box({
|
|
853
874
|
key: entry.id,
|
|
854
875
|
position: "absolute",
|
|
855
876
|
anchor: entry.anchor ?? void 0,
|
|
856
877
|
anchorPoint: entry.anchor == null ? entry.point : void 0,
|
|
878
|
+
anchorRect: entry.anchor == null ? void 0 : entry.anchorRect,
|
|
857
879
|
placement: entry.placement,
|
|
858
880
|
anchorOffset: entry.offset,
|
|
859
881
|
top: entry.top,
|
|
@@ -862,7 +884,8 @@ let OverlayLayer = class OverlayLayer extends Component {
|
|
|
862
884
|
left: entry.left,
|
|
863
885
|
zIndex: entry.zIndex,
|
|
864
886
|
...centering(entry),
|
|
865
|
-
...inheritedFrom(entry.environment ?? entry.anchor ?? null)
|
|
887
|
+
...inheritedFrom(entry.environment ?? entry.anchor ?? null),
|
|
888
|
+
modifiers: followEnvironment(entry.environment ?? entry.anchor ?? null)
|
|
866
889
|
}, entry.content));
|
|
867
890
|
}
|
|
868
891
|
return elements;
|
|
@@ -871,6 +894,28 @@ let OverlayLayer = class OverlayLayer extends Component {
|
|
|
871
894
|
__decorate([Inject(OverlayService)], OverlayLayer.prototype, "overlays", void 0);
|
|
872
895
|
OverlayLayer = __decorate([Define("gesso-overlay-layer")], OverlayLayer);
|
|
873
896
|
/**
|
|
897
|
+
* The dim over the page, inside the backdrop rather than on it.
|
|
898
|
+
*
|
|
899
|
+
* A node at opacity 0 takes no presses, so a backdrop fading in would
|
|
900
|
+
* let a press through to the page on the frame it opens. The fade is
|
|
901
|
+
* this child's; the backdrop under it is solid to input from the start,
|
|
902
|
+
* and the child takes none of its own. It comes in at the pace a
|
|
903
|
+
* `Dialog` does, and under reduced motion it is simply there.
|
|
904
|
+
*/
|
|
905
|
+
function scrim() {
|
|
906
|
+
return Box({
|
|
907
|
+
position: "absolute",
|
|
908
|
+
inset: 0,
|
|
909
|
+
hitTestable: false,
|
|
910
|
+
backgroundColor: "scrim",
|
|
911
|
+
modifiers: [motion({
|
|
912
|
+
initial: fade,
|
|
913
|
+
duration: "slow",
|
|
914
|
+
easing: "decelerate"
|
|
915
|
+
})]
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
874
919
|
* `center` as the layout engine takes it.
|
|
875
920
|
*
|
|
876
921
|
* Both edges of the axis are pinned, which stretches the entry's box
|
|
@@ -905,11 +950,7 @@ function centering(entry) {
|
|
|
905
950
|
}
|
|
906
951
|
/**
|
|
907
952
|
* The scoped values an entry's content should keep, re-provided on the
|
|
908
|
-
* box that holds it.
|
|
909
|
-
*
|
|
910
|
-
* Read once, when the entry opens. An overlay that outlives a theme
|
|
911
|
-
* change re-opens; nothing here watches, because an entry's content is
|
|
912
|
-
* built once too.
|
|
953
|
+
* box that holds it, as they are when the entry opens.
|
|
913
954
|
*/
|
|
914
955
|
function inheritedFrom(node) {
|
|
915
956
|
const environment = node?.environment;
|
|
@@ -917,9 +958,33 @@ function inheritedFrom(node) {
|
|
|
917
958
|
return {
|
|
918
959
|
theme: environment.get(UiEnvironmentKeys.theme),
|
|
919
960
|
textStyle: environment.get(UiEnvironmentKeys.textStyle),
|
|
961
|
+
color: environment.get(UiEnvironmentKeys.color),
|
|
920
962
|
contentColor: environment.get(UiEnvironmentKeys.contentColor)
|
|
921
963
|
};
|
|
922
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* The same values again whenever they change where the entry was
|
|
967
|
+
* declared, for as long as it is open.
|
|
968
|
+
*
|
|
969
|
+
* Reading them once let a dialog open while the page went dark (the
|
|
970
|
+
* system's setting changed, or the theme a person chose arrived from
|
|
971
|
+
* another worker a moment after they opened it) stay light over a dark
|
|
972
|
+
* page until it was closed.
|
|
973
|
+
*/
|
|
974
|
+
const followEnvironmentKind = defineModifier({
|
|
975
|
+
name: "overlayEnvironment",
|
|
976
|
+
attach(host, from) {
|
|
977
|
+
host.onEnvironment(() => {
|
|
978
|
+
host.set("theme", host.environment(UiEnvironmentKeys.theme, from));
|
|
979
|
+
host.set("textStyle", host.environment(UiEnvironmentKeys.textStyle, from));
|
|
980
|
+
host.set("color", host.environment(UiEnvironmentKeys.color, from));
|
|
981
|
+
host.set("contentColor", host.environment(UiEnvironmentKeys.contentColor, from));
|
|
982
|
+
}, from);
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
function followEnvironment(node) {
|
|
986
|
+
return node === null ? [] : [followEnvironmentKind(node)];
|
|
987
|
+
}
|
|
923
988
|
//#endregion
|
|
924
989
|
//#region src/ComponentElement.ts
|
|
925
990
|
function isComponentElement(value) {
|
|
@@ -1884,6 +1949,38 @@ var ShellService = class {
|
|
|
1884
1949
|
});
|
|
1885
1950
|
}
|
|
1886
1951
|
/**
|
|
1952
|
+
* Replaces the page with `url`, leaving the application.
|
|
1953
|
+
*
|
|
1954
|
+
* Not the router's `navigate`, which moves between screens *inside*
|
|
1955
|
+
* the app and keeps it running: this unloads the page, and whatever
|
|
1956
|
+
* the app held in memory goes with it. It is for the places a page
|
|
1957
|
+
* has to be handed to somebody else's server and wait to be sent
|
|
1958
|
+
* back — an OAuth sign-in that goes to `/api/auth/login`, is
|
|
1959
|
+
* redirected to an identity provider and returns with a session, or
|
|
1960
|
+
* a sign-out at `/api/auth/logout` that clears a cookie only the
|
|
1961
|
+
* server can clear. It is also where a sign-in falls back to when
|
|
1962
|
+
* `openPopup` reports the popup blocked.
|
|
1963
|
+
*
|
|
1964
|
+
* A request rather than a call because `window.location` belongs to
|
|
1965
|
+
* the main thread and a render worker cannot reach it. A relative
|
|
1966
|
+
* url resolves against the page, so `/api/auth/login` means the
|
|
1967
|
+
* app's own server. The shell refuses anything that is not http,
|
|
1968
|
+
* https or the page's own scheme — a `javascript:` url, above all,
|
|
1969
|
+
* which would run in the page rather than go anywhere — so a url
|
|
1970
|
+
* built from data cannot be turned into script.
|
|
1971
|
+
*
|
|
1972
|
+
* Nothing comes back, and there is nothing to wait for: if the
|
|
1973
|
+
* redirect happens, the code that would have read the answer is
|
|
1974
|
+
* gone. Leaving the page is not cancellable from here either, so a
|
|
1975
|
+
* component with unsaved work should settle it before asking.
|
|
1976
|
+
*/
|
|
1977
|
+
redirect(url) {
|
|
1978
|
+
this.handler?.({
|
|
1979
|
+
type: "redirect",
|
|
1980
|
+
url
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1887
1984
|
* Whether the application's surface is filling the screen.
|
|
1888
1985
|
*
|
|
1889
1986
|
* Read rather than assumed: the person can leave fullscreen with
|
|
@@ -1925,8 +2022,8 @@ var ShellService = class {
|
|
|
1925
2022
|
* The answer is a promise, the one place in the framework where a
|
|
1926
2023
|
* shell request has a reply, because there is nothing useful an
|
|
1927
2024
|
* application can do with a popup it cannot see the fate of. A sign-in
|
|
1928
|
-
* flow that is blocked falls back to a full-page redirect
|
|
1929
|
-
* only choose that if it is told.
|
|
2025
|
+
* flow that is blocked falls back to a full-page redirect — `redirect`,
|
|
2026
|
+
* below — and it can only choose that if it is told.
|
|
1930
2027
|
*
|
|
1931
2028
|
* Two browser rules shape the contract and both were measured in
|
|
1932
2029
|
* Chrome before this existed:
|
|
@@ -2463,6 +2560,89 @@ function browserFilesHost(view) {
|
|
|
2463
2560
|
};
|
|
2464
2561
|
}
|
|
2465
2562
|
//#endregion
|
|
2563
|
+
//#region src/app/shellRedirect.ts
|
|
2564
|
+
/**
|
|
2565
|
+
* The shell's half of `ShellService.redirect`: the page is replaced
|
|
2566
|
+
* with a url a component asked for.
|
|
2567
|
+
*
|
|
2568
|
+
* Both hosts answer the request — `WorkerApp` when it arrives as a
|
|
2569
|
+
* message, `GessoApp` when the runtime hands it over on the same
|
|
2570
|
+
* thread — and both go through here, so the one rule that matters is
|
|
2571
|
+
* written once.
|
|
2572
|
+
*
|
|
2573
|
+
* That rule is the scheme. `location.assign('javascript:…')` does not
|
|
2574
|
+
* go anywhere; it runs the script in the page, with everything the
|
|
2575
|
+
* page can reach. A component builds the url it redirects to, often
|
|
2576
|
+
* from data — a `returnTo` from the address bar, a link from the
|
|
2577
|
+
* server — and a shell that assigned whatever it was given would make
|
|
2578
|
+
* every such url a way to run script. So a url is resolved against
|
|
2579
|
+
* the page first, the way the browser would resolve it, and only an
|
|
2580
|
+
* http or https result, or one in the page's own scheme, is followed.
|
|
2581
|
+
* The page's own scheme is there for a page that is not served over
|
|
2582
|
+
* http at all — an Electrobun window loads from `views://` — where a
|
|
2583
|
+
* relative url resolves to that scheme and is no less the app's own.
|
|
2584
|
+
*/
|
|
2585
|
+
/** Schemes a redirect may always go to, whatever the page was served from. */
|
|
2586
|
+
const WEB_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
2587
|
+
/**
|
|
2588
|
+
* Schemes refused even when the page itself has one of them.
|
|
2589
|
+
*
|
|
2590
|
+
* A page served from `data:` or `blob:` is unusual but real — a
|
|
2591
|
+
* preview, a sandboxed frame — and "the page's own scheme" must not
|
|
2592
|
+
* then admit a `data:` url built from data, which is script by
|
|
2593
|
+
* another name.
|
|
2594
|
+
*/
|
|
2595
|
+
const REFUSED_SCHEMES = /* @__PURE__ */ new Set([
|
|
2596
|
+
"javascript:",
|
|
2597
|
+
"data:",
|
|
2598
|
+
"blob:",
|
|
2599
|
+
"about:"
|
|
2600
|
+
]);
|
|
2601
|
+
/**
|
|
2602
|
+
* The absolute url `url` names, resolved against `base` (the page's
|
|
2603
|
+
* address), or null when it is not one a redirect may follow.
|
|
2604
|
+
*
|
|
2605
|
+
* Null as well for a url that does not parse, or a base that does
|
|
2606
|
+
* not: a redirect nobody can resolve is refused rather than guessed.
|
|
2607
|
+
*/
|
|
2608
|
+
function redirectTarget(url, base) {
|
|
2609
|
+
let page;
|
|
2610
|
+
let target;
|
|
2611
|
+
try {
|
|
2612
|
+
page = new URL(base);
|
|
2613
|
+
target = new URL(url, page);
|
|
2614
|
+
} catch {
|
|
2615
|
+
return null;
|
|
2616
|
+
}
|
|
2617
|
+
if (WEB_SCHEMES.has(target.protocol)) return target.href;
|
|
2618
|
+
if (target.protocol === page.protocol && !REFUSED_SCHEMES.has(target.protocol)) return target.href;
|
|
2619
|
+
return null;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Follows a redirect, or refuses it, and says which.
|
|
2623
|
+
*
|
|
2624
|
+
* The url is checked before the handler is called as well as before
|
|
2625
|
+
* the default: a desktop host's `onRedirect` navigates a webview, and
|
|
2626
|
+
* a `javascript:` url is no safer there. The handler is given the
|
|
2627
|
+
* resolved, absolute url, so it never has to know what page the
|
|
2628
|
+
* request came from.
|
|
2629
|
+
*
|
|
2630
|
+
* A refusal is a warning on the console and nothing else. There is no
|
|
2631
|
+
* one to report it to — the request carries no answer — and throwing
|
|
2632
|
+
* on the main thread would take down a message handler over a url
|
|
2633
|
+
* that was simply ignored.
|
|
2634
|
+
*/
|
|
2635
|
+
function redirectWith(handler, url, location) {
|
|
2636
|
+
const target = redirectTarget(url, location.href);
|
|
2637
|
+
if (target === null) {
|
|
2638
|
+
console.warn(`[gesso] ShellService.redirect refused ${JSON.stringify(url)}: only an http, https or relative url can replace the page.`);
|
|
2639
|
+
return false;
|
|
2640
|
+
}
|
|
2641
|
+
if (handler !== void 0) handler(target);
|
|
2642
|
+
else location.assign(target);
|
|
2643
|
+
return true;
|
|
2644
|
+
}
|
|
2645
|
+
//#endregion
|
|
2466
2646
|
//#region src/app/EditingProxy.ts
|
|
2467
2647
|
/** Composition-owned input types: the composition events carry these. */
|
|
2468
2648
|
const COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -2667,7 +2847,7 @@ var EditingProxy = class {
|
|
|
2667
2847
|
* the reason the field's semantics live here rather than on a second
|
|
2668
2848
|
* element beside it.
|
|
2669
2849
|
*/
|
|
2670
|
-
describe(record, activeDescendant) {
|
|
2850
|
+
describe(record, activeDescendant, controls) {
|
|
2671
2851
|
if (this.disposed) return;
|
|
2672
2852
|
const textarea = this.textarea;
|
|
2673
2853
|
if (record === null) {
|
|
@@ -2681,7 +2861,9 @@ var EditingProxy = class {
|
|
|
2681
2861
|
"aria-readonly",
|
|
2682
2862
|
"aria-disabled",
|
|
2683
2863
|
"aria-expanded",
|
|
2684
|
-
"aria-activedescendant"
|
|
2864
|
+
"aria-activedescendant",
|
|
2865
|
+
"aria-controls",
|
|
2866
|
+
"aria-autocomplete"
|
|
2685
2867
|
]) textarea.removeAttribute(attribute);
|
|
2686
2868
|
return;
|
|
2687
2869
|
}
|
|
@@ -2696,6 +2878,8 @@ var EditingProxy = class {
|
|
|
2696
2878
|
setOrClear(textarea, "aria-disabled", record.disabled === true ? "true" : void 0);
|
|
2697
2879
|
setOrClear(textarea, "aria-expanded", states.has("expanded") ? "true" : states.has("collapsed") ? "false" : void 0);
|
|
2698
2880
|
setOrClear(textarea, "aria-activedescendant", activeDescendant);
|
|
2881
|
+
setOrClear(textarea, "aria-controls", controls);
|
|
2882
|
+
setOrClear(textarea, "aria-autocomplete", record.autocomplete);
|
|
2699
2883
|
}
|
|
2700
2884
|
dispose() {
|
|
2701
2885
|
if (this.disposed) return;
|
|
@@ -2935,7 +3119,9 @@ const RECORD_ATTRIBUTES = [
|
|
|
2935
3119
|
"aria-modal",
|
|
2936
3120
|
"aria-multiselectable",
|
|
2937
3121
|
"aria-current",
|
|
2938
|
-
"aria-activedescendant"
|
|
3122
|
+
"aria-activedescendant",
|
|
3123
|
+
"aria-controls",
|
|
3124
|
+
"aria-autocomplete"
|
|
2939
3125
|
];
|
|
2940
3126
|
let mirrors = 0;
|
|
2941
3127
|
/**
|
|
@@ -3098,10 +3284,11 @@ var SemanticsMirror = class {
|
|
|
3098
3284
|
if (existing?.box !== void 0) this.position(existing);
|
|
3099
3285
|
if (record.id === this.focusedId) this.describeEditing(this.editing?.active === true ? record : null);
|
|
3100
3286
|
}
|
|
3101
|
-
/** Hands the proxy the focused editable's record, with its active descendant
|
|
3287
|
+
/** Hands the proxy the focused editable's record, with the DOM ids of its active descendant and what it controls. */
|
|
3102
3288
|
describeEditing(record) {
|
|
3103
3289
|
const active = record?.activeDescendant;
|
|
3104
|
-
|
|
3290
|
+
const controls = record?.controls;
|
|
3291
|
+
this.editing?.describe(record, active === void 0 ? void 0 : this.idPrefix + active, controls === void 0 ? void 0 : this.idPrefix + controls);
|
|
3105
3292
|
}
|
|
3106
3293
|
createElement() {
|
|
3107
3294
|
const element = this.doc.createElement("div");
|
|
@@ -3148,6 +3335,8 @@ var SemanticsMirror = class {
|
|
|
3148
3335
|
setNumber(element, "aria-level", record.level);
|
|
3149
3336
|
if (record.valueText !== void 0) element.setAttribute("aria-valuetext", record.valueText);
|
|
3150
3337
|
if (record.activeDescendant !== void 0) element.setAttribute("aria-activedescendant", this.idPrefix + record.activeDescendant);
|
|
3338
|
+
if (record.controls !== void 0) element.setAttribute("aria-controls", this.idPrefix + record.controls);
|
|
3339
|
+
if (record.autocomplete !== void 0) element.setAttribute("aria-autocomplete", record.autocomplete);
|
|
3151
3340
|
}
|
|
3152
3341
|
/**
|
|
3153
3342
|
* Puts an element under its parent, at the index the record gives.
|
|
@@ -4359,6 +4548,10 @@ var WorkerApp = class {
|
|
|
4359
4548
|
openUrlWith(this.options.onOpenUrl, message.url);
|
|
4360
4549
|
return;
|
|
4361
4550
|
}
|
|
4551
|
+
if (message.type === "redirect") {
|
|
4552
|
+
redirectWith(this.options.onRedirect, message.url, window.location);
|
|
4553
|
+
return;
|
|
4554
|
+
}
|
|
4362
4555
|
if (message.type === "fullscreen") {
|
|
4363
4556
|
if (this.canvas !== void 0) setElementFullscreen(this.canvas, message.enter);
|
|
4364
4557
|
return;
|
|
@@ -5551,8 +5744,8 @@ var RouterService = class {
|
|
|
5551
5744
|
const settled = match?.url ?? normalize$1(url);
|
|
5552
5745
|
if (options.replace === true) this.history?.replace(settled);
|
|
5553
5746
|
else if (options.push) this.history?.push(settled);
|
|
5554
|
-
this.url.value = settled;
|
|
5555
5747
|
this.match.value = match;
|
|
5748
|
+
this.url.value = settled;
|
|
5556
5749
|
}
|
|
5557
5750
|
/** First declared route whose pattern describes this url wins. */
|
|
5558
5751
|
resolve(url) {
|
|
@@ -5770,14 +5963,19 @@ var EditingService = class {
|
|
|
5770
5963
|
* scroll already applied: a popup placed under the caret adds the
|
|
5771
5964
|
* node's position on screen and nothing else.
|
|
5772
5965
|
*
|
|
5966
|
+
* `offset` asks about a caret at another character than the one the
|
|
5967
|
+
* caret is at: the start of the word being completed, so its list
|
|
5968
|
+
* stays put as the word grows. With an overlay's `anchorRect` and the
|
|
5969
|
+
* field as its anchor, the list follows the field from then on.
|
|
5970
|
+
*
|
|
5773
5971
|
* Null when the node is not an editable, when it has not been laid
|
|
5774
5972
|
* out yet, or before the runtime has wired the controller — all
|
|
5775
5973
|
* three of which are "ask again next frame" rather than errors, and
|
|
5776
5974
|
* all three of which happen during the frame a field first appears.
|
|
5777
5975
|
*/
|
|
5778
|
-
caretRectOf(node) {
|
|
5976
|
+
caretRectOf(node, offset) {
|
|
5779
5977
|
if (node === null || this.controller === null) return null;
|
|
5780
|
-
return this.controller.caretRectOf(node);
|
|
5978
|
+
return this.controller.caretRectOf(node, offset);
|
|
5781
5979
|
}
|
|
5782
5980
|
/**
|
|
5783
5981
|
* Selects from `anchor` to `focus` and focuses the field the focus is
|
|
@@ -6989,7 +7187,7 @@ var GessoRuntime = class {
|
|
|
6989
7187
|
onChange: (node, listener) => this.environmentNotifier.add(node, listener)
|
|
6990
7188
|
},
|
|
6991
7189
|
layout: {
|
|
6992
|
-
box: (node) => this.engine.recordFor(node) === void 0 ? null : this.engine.
|
|
7190
|
+
box: (node) => this.engine.recordFor(node) === void 0 ? null : this.engine.screenBox(node),
|
|
6993
7191
|
flowBox: (node) => this.engine.recordFor(node) === void 0 ? null : this.engine.worldBox(node),
|
|
6994
7192
|
scroll: (node) => {
|
|
6995
7193
|
const record = this.engine.recordFor(node);
|
|
@@ -8067,6 +8265,7 @@ var GessoRuntime = class {
|
|
|
8067
8265
|
const editing = new UiEditingController({
|
|
8068
8266
|
recordFor: (node) => this.engine.recordFor(node),
|
|
8069
8267
|
visibleBox: (node) => this.engine.visibleBox(node),
|
|
8268
|
+
screenBox: (node, part) => this.engine.screenBox(node, part),
|
|
8070
8269
|
toLocal: (node, x, y) => hitTester.toLocal(node, x, y),
|
|
8071
8270
|
measurer: this.textMeasurer,
|
|
8072
8271
|
markDirty: (node, flags) => this.graph.markDirty(node, flags),
|
|
@@ -8076,6 +8275,7 @@ var GessoRuntime = class {
|
|
|
8076
8275
|
const selection = new UiSelectionController({
|
|
8077
8276
|
recordFor: (node) => this.engine.recordFor(node),
|
|
8078
8277
|
visibleBox: (node) => this.engine.visibleBox(node),
|
|
8278
|
+
screenBox: (node) => this.engine.screenBox(node),
|
|
8079
8279
|
measurer: this.textMeasurer,
|
|
8080
8280
|
markDirty: (node, flags) => this.graph.markDirty(node, flags),
|
|
8081
8281
|
root: () => this.layoutRoot(),
|
|
@@ -8124,7 +8324,7 @@ var GessoRuntime = class {
|
|
|
8124
8324
|
selection,
|
|
8125
8325
|
find,
|
|
8126
8326
|
activation: { clickAt: (node) => {
|
|
8127
|
-
const box = this.engine.
|
|
8327
|
+
const box = this.engine.screenBox(node);
|
|
8128
8328
|
return {
|
|
8129
8329
|
x: box.x + box.width / 2,
|
|
8130
8330
|
y: box.y + box.height / 2
|
|
@@ -8266,7 +8466,7 @@ var GessoRuntime = class {
|
|
|
8266
8466
|
return;
|
|
8267
8467
|
}
|
|
8268
8468
|
this.focusManager.focus(node);
|
|
8269
|
-
const box = this.engine.
|
|
8469
|
+
const box = this.engine.screenBox(node);
|
|
8270
8470
|
this.dispatcher.dispatch(new UiPointerEvent(UiEventType.Click, box.x + box.width / 2, box.y + box.height / 2, 1), node);
|
|
8271
8471
|
}
|
|
8272
8472
|
/**
|
|
@@ -8639,7 +8839,9 @@ var GessoRuntime = class {
|
|
|
8639
8839
|
* goes down from the root carrying the scroll and sticky offsets that
|
|
8640
8840
|
* `visibleBox` adds up per node, and passes over any subtree whose
|
|
8641
8841
|
* bounds (the hit tester's, see `subtreeBoundsFor`) are off screen.
|
|
8642
|
-
* A node it passes over is one this skipped before anyway.
|
|
8842
|
+
* A node it passes over is one this skipped before anyway. Below a
|
|
8843
|
+
* `transform` the walk asks `screenBox` instead, which carries the
|
|
8844
|
+
* box through it.
|
|
8643
8845
|
*/
|
|
8644
8846
|
collectSemanticsBoxes() {
|
|
8645
8847
|
const changed = [];
|
|
@@ -8660,14 +8862,25 @@ var GessoRuntime = class {
|
|
|
8660
8862
|
const stack = [[
|
|
8661
8863
|
root,
|
|
8662
8864
|
0,
|
|
8663
|
-
0
|
|
8865
|
+
0,
|
|
8866
|
+
false
|
|
8664
8867
|
]];
|
|
8665
8868
|
while (stack.length > 0) {
|
|
8666
|
-
const [node, dx, dy] = stack.pop();
|
|
8869
|
+
const [node, dx, dy, inherited] = stack.pop();
|
|
8667
8870
|
const rec = this.engine.recordFor(node);
|
|
8668
8871
|
let childX = dx;
|
|
8669
8872
|
let childY = dy;
|
|
8670
|
-
|
|
8873
|
+
const own = node.properties.get("transform");
|
|
8874
|
+
const transformed = inherited || typeof own === "object" && own !== null;
|
|
8875
|
+
if (rec !== void 0 && inherited) {
|
|
8876
|
+
if (this.semantics.has(node.id)) {
|
|
8877
|
+
const box = this.engine.screenBox(node);
|
|
8878
|
+
if (node === focused) {
|
|
8879
|
+
focusedSeen = true;
|
|
8880
|
+
offer(node.id, box);
|
|
8881
|
+
} else if (this.onScreen(box)) offer(node.id, box);
|
|
8882
|
+
}
|
|
8883
|
+
} else if (rec !== void 0) {
|
|
8671
8884
|
const x = dx + rec.stickyOffsetX;
|
|
8672
8885
|
const y = dy + rec.stickyOffsetY;
|
|
8673
8886
|
if (!rec.boundsUnbounded && node !== focused && !this.onScreen({
|
|
@@ -8694,12 +8907,13 @@ var GessoRuntime = class {
|
|
|
8694
8907
|
for (let child = node.lastChild; child !== null; child = child.previousSibling) stack.push([
|
|
8695
8908
|
child,
|
|
8696
8909
|
childX,
|
|
8697
|
-
childY
|
|
8910
|
+
childY,
|
|
8911
|
+
transformed
|
|
8698
8912
|
]);
|
|
8699
8913
|
}
|
|
8700
8914
|
}
|
|
8701
8915
|
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.
|
|
8916
|
+
if (this.engine.recordFor(focused) !== void 0) offer(focused.id, this.engine.screenBox(focused));
|
|
8703
8917
|
}
|
|
8704
8918
|
if (this.semanticsBoxes.size > this.semantics.size) {
|
|
8705
8919
|
for (const id of this.semanticsBoxes.keys()) if (!this.semantics.has(id)) this.semanticsBoxes.delete(id);
|
|
@@ -8965,7 +9179,7 @@ var GessoRuntime = class {
|
|
|
8965
9179
|
const read = (node) => {
|
|
8966
9180
|
const record = this.engine.recordFor(node);
|
|
8967
9181
|
return {
|
|
8968
|
-
box: this.engine.
|
|
9182
|
+
box: this.engine.screenBox(node),
|
|
8969
9183
|
scrollX: record?.scrollX ?? 0,
|
|
8970
9184
|
scrollY: record?.scrollY ?? 0
|
|
8971
9185
|
};
|
|
@@ -9811,6 +10025,10 @@ var GessoApp = class {
|
|
|
9811
10025
|
this.runtime.settlePopup(request.id, opened);
|
|
9812
10026
|
return;
|
|
9813
10027
|
}
|
|
10028
|
+
if (request.type === "redirect") {
|
|
10029
|
+
if (view !== null) redirectWith(void 0, request.url, view.location);
|
|
10030
|
+
return;
|
|
10031
|
+
}
|
|
9814
10032
|
view?.open(request.url, "_blank", "noopener,noreferrer");
|
|
9815
10033
|
}
|
|
9816
10034
|
observeResize() {
|
|
@@ -11119,6 +11337,10 @@ var RenderWorkerApp = class {
|
|
|
11119
11337
|
type: "openUrl",
|
|
11120
11338
|
url: request.url
|
|
11121
11339
|
});
|
|
11340
|
+
else if (request.type === "redirect") this.host.postMessage({
|
|
11341
|
+
type: "redirect",
|
|
11342
|
+
url: request.url
|
|
11343
|
+
});
|
|
11122
11344
|
else if (request.type === "fullscreen") this.host.postMessage({
|
|
11123
11345
|
type: "fullscreen",
|
|
11124
11346
|
enter: request.enter
|