@lingxia/elements 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/index.d.ts +3 -2
  2. package/dist/index.js +1 -0
  3. package/dist/inline-native/commit.d.ts +39 -0
  4. package/dist/inline-native/commit.js +94 -0
  5. package/dist/inline-native/elements.d.ts +104 -0
  6. package/dist/inline-native/elements.js +749 -0
  7. package/dist/inline-native/errors.d.ts +8 -0
  8. package/dist/inline-native/errors.js +23 -0
  9. package/dist/inline-native/events.d.ts +10 -0
  10. package/dist/inline-native/events.js +16 -0
  11. package/dist/inline-native/geometry.d.ts +78 -0
  12. package/dist/inline-native/geometry.js +40 -0
  13. package/dist/inline-native/identity.d.ts +28 -0
  14. package/dist/inline-native/identity.js +51 -0
  15. package/dist/inline-native/index.d.ts +14 -0
  16. package/dist/inline-native/index.js +19 -0
  17. package/dist/inline-native/layout.d.ts +2 -0
  18. package/dist/inline-native/layout.js +58 -0
  19. package/dist/inline-native/lease.d.ts +13 -0
  20. package/dist/inline-native/lease.js +31 -0
  21. package/dist/inline-native/runtime.d.ts +64 -0
  22. package/dist/inline-native/runtime.js +119 -0
  23. package/dist/inline-native/schema.d.ts +111 -0
  24. package/dist/inline-native/schema.js +210 -0
  25. package/dist/inline-native/structure.d.ts +22 -0
  26. package/dist/inline-native/structure.js +706 -0
  27. package/dist/inline-native/style.d.ts +7 -0
  28. package/dist/inline-native/style.js +119 -0
  29. package/dist/inline-native/types.d.ts +82 -0
  30. package/dist/inline-native/types.js +7 -0
  31. package/dist/inline-native/unwrap.d.ts +10 -0
  32. package/dist/inline-native/unwrap.js +32 -0
  33. package/dist/inline-native/video.d.ts +65 -0
  34. package/dist/inline-native/video.js +69 -0
  35. package/dist/native_component_wrapper_shared.d.ts +3 -2
  36. package/dist/native_component_wrapper_shared.js +6 -3
  37. package/dist/nativecomponent.d.ts +1 -1
  38. package/dist/nativecomponent.js +8 -11
  39. package/dist/navigator.d.ts +2 -2
  40. package/dist/navigator.js +5 -5
  41. package/dist/video-events.d.ts +2 -0
  42. package/dist/video-events.js +18 -0
  43. package/dist/video.d.ts +98 -79
  44. package/dist/video.js +182 -757
  45. package/package.json +2 -1
  46. package/src/index.ts +58 -1
  47. package/src/inline-native/commit.ts +141 -0
  48. package/src/inline-native/elements.ts +829 -0
  49. package/src/inline-native/errors.ts +35 -0
  50. package/src/inline-native/events.ts +31 -0
  51. package/src/inline-native/geometry.ts +86 -0
  52. package/src/inline-native/identity.ts +102 -0
  53. package/src/inline-native/index.ts +103 -0
  54. package/src/inline-native/layout.ts +58 -0
  55. package/src/inline-native/lease.ts +48 -0
  56. package/src/inline-native/runtime.ts +185 -0
  57. package/src/inline-native/schema.ts +247 -0
  58. package/src/inline-native/structure.ts +823 -0
  59. package/src/inline-native/style.ts +111 -0
  60. package/src/inline-native/types.ts +105 -0
  61. package/src/inline-native/unwrap.ts +34 -0
  62. package/src/inline-native/video.ts +125 -0
  63. package/src/native_component_wrapper_shared.ts +8 -6
  64. package/src/nativecomponent.ts +8 -13
  65. package/src/navigator.ts +9 -9
  66. package/src/video-events.ts +16 -0
  67. package/src/video.ts +245 -839
@@ -0,0 +1,8 @@
1
+ import { type NativeError, type RootRef } from "./types.js";
2
+ import type { NativeErrorCode } from "./schema.js";
3
+ export declare function nativeError(code: NativeErrorCode, message: string, options?: {
4
+ scope?: "node" | "root";
5
+ recoverable?: boolean;
6
+ root?: RootRef;
7
+ }): NativeError;
8
+ export declare function isNativeErrorCode(value: unknown): value is NativeErrorCode;
@@ -0,0 +1,23 @@
1
+ import { EMPTY_ROOT_REF } from "./types.js";
2
+ export function nativeError(code, message, options = {}) {
3
+ var _a, _b, _c;
4
+ return {
5
+ code,
6
+ scope: (_a = options.scope) !== null && _a !== void 0 ? _a : "root",
7
+ recoverable: (_b = options.recoverable) !== null && _b !== void 0 ? _b : false,
8
+ root: (_c = options.root) !== null && _c !== void 0 ? _c : EMPTY_ROOT_REF,
9
+ message,
10
+ };
11
+ }
12
+ export function isNativeErrorCode(value) {
13
+ return (value === "NATIVE_ROOT_UNAVAILABLE" ||
14
+ value === "NATIVE_ROOT_INCOMPATIBLE" ||
15
+ value === "NATIVE_ROOT_INVALID_STRUCTURE" ||
16
+ value === "NATIVE_ROOT_FAILED" ||
17
+ value === "NATIVE_ROOT_UNSUPPORTED_LAYOUT" ||
18
+ value === "NATIVE_COMPONENT_INVALID_PROPS" ||
19
+ value === "NATIVE_COMPONENT_MOUNT_FAILED" ||
20
+ value === "NATIVE_COMPONENT_COMMAND_FAILED" ||
21
+ value === "NATIVE_ROOT_UNSUPPORTED_STYLE" ||
22
+ value === "NATIVE_ROOT_DESTROYED");
23
+ }
@@ -0,0 +1,10 @@
1
+ export type IslandHostMessage = {
2
+ event?: string;
3
+ action?: string;
4
+ detail?: unknown;
5
+ };
6
+ export type IslandEventTarget = {
7
+ dispatchEvent(event: Event): boolean;
8
+ };
9
+ /** Dispatch native interaction payloads onto the author element. */
10
+ export declare function applyIslandHostEvent(target: IslandEventTarget, message: IslandHostMessage): boolean;
@@ -0,0 +1,16 @@
1
+ const HOST_EVENTS = new Set(["press", "focus", "blur", "pointerenter", "pointerleave", "pointerwithinchange"]);
2
+ /** Dispatch native interaction payloads onto the author element. */
3
+ export function applyIslandHostEvent(target, message) {
4
+ var _a, _b, _c;
5
+ const name = String((_b = (_a = message.event) !== null && _a !== void 0 ? _a : message.action) !== null && _b !== void 0 ? _b : "").toLowerCase();
6
+ if (!HOST_EVENTS.has(name)) {
7
+ return false;
8
+ }
9
+ const detail = (_c = message.detail) !== null && _c !== void 0 ? _c : {};
10
+ target.dispatchEvent(new CustomEvent(name, {
11
+ detail,
12
+ bubbles: false,
13
+ cancelable: false,
14
+ }));
15
+ return true;
16
+ }
@@ -0,0 +1,78 @@
1
+ import type { IdentifiedRoot } from "./identity.js";
2
+ import type { RootRef } from "./types.js";
3
+ export interface NativeGeometrySnapshotJson {
4
+ action: "geometry.snapshot";
5
+ surfaceInstanceId: string;
6
+ pageInstanceId: string;
7
+ documentInstanceId: string;
8
+ revision: number;
9
+ coordinateSpace: "page-unscrolled-css-px";
10
+ viewportOffset: {
11
+ x: number;
12
+ y: number;
13
+ };
14
+ roots: Array<{
15
+ ref: RootRef;
16
+ basisTreeRevision: number;
17
+ rootOrder: number;
18
+ chainKey: string;
19
+ contentRect: {
20
+ x: number;
21
+ y: number;
22
+ width: number;
23
+ height: number;
24
+ };
25
+ visible: boolean;
26
+ }>;
27
+ nodes: Array<{
28
+ ref: IdentifiedRoot["children"][number]["nodeRef"];
29
+ chainKey: string;
30
+ contentRect: {
31
+ x: number;
32
+ y: number;
33
+ width: number;
34
+ height: number;
35
+ };
36
+ clipStack: unknown[];
37
+ visible: boolean;
38
+ }>;
39
+ chains: Array<{
40
+ chainKey: string;
41
+ ancestors: Array<{
42
+ key: string;
43
+ viewportRect: {
44
+ x: number;
45
+ y: number;
46
+ width: number;
47
+ height: number;
48
+ };
49
+ offsetX: number;
50
+ offsetY: number;
51
+ }>;
52
+ }>;
53
+ }
54
+ export declare function buildGeometrySnapshot(options: {
55
+ identified: IdentifiedRoot;
56
+ basisTreeRevision: number;
57
+ geometryRevision: number;
58
+ rootOrder: number;
59
+ rootRect: {
60
+ x: number;
61
+ y: number;
62
+ width: number;
63
+ height: number;
64
+ };
65
+ nodeRects: Record<string, {
66
+ x: number;
67
+ y: number;
68
+ width: number;
69
+ height: number;
70
+ }>;
71
+ nodeVisibility?: Record<string, boolean>;
72
+ nodeClipStacks?: Record<string, unknown[]>;
73
+ rootVisible?: boolean;
74
+ viewportOffset?: {
75
+ x: number;
76
+ y: number;
77
+ };
78
+ }): NativeGeometrySnapshotJson;
@@ -0,0 +1,40 @@
1
+ export function buildGeometrySnapshot(options) {
2
+ var _a, _b;
3
+ const root = options.identified.rootRef;
4
+ const nodes = [];
5
+ const walk = (list) => {
6
+ var _a, _b, _c, _d, _e;
7
+ for (const node of list) {
8
+ nodes.push({
9
+ ref: node.nodeRef,
10
+ chainKey: "page",
11
+ contentRect: (_a = options.nodeRects[node.nodeRef.nodeKey]) !== null && _a !== void 0 ? _a : options.rootRect,
12
+ clipStack: (_c = (_b = options.nodeClipStacks) === null || _b === void 0 ? void 0 : _b[node.nodeRef.nodeKey]) !== null && _c !== void 0 ? _c : [],
13
+ visible: (_e = (_d = options.nodeVisibility) === null || _d === void 0 ? void 0 : _d[node.nodeRef.nodeKey]) !== null && _e !== void 0 ? _e : true,
14
+ });
15
+ walk(node.children);
16
+ }
17
+ };
18
+ walk(options.identified.children);
19
+ return {
20
+ action: "geometry.snapshot",
21
+ surfaceInstanceId: root.surfaceInstanceId,
22
+ pageInstanceId: root.pageInstanceId,
23
+ documentInstanceId: root.documentInstanceId,
24
+ revision: options.geometryRevision,
25
+ coordinateSpace: "page-unscrolled-css-px",
26
+ viewportOffset: (_a = options.viewportOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 },
27
+ roots: [
28
+ {
29
+ ref: root,
30
+ basisTreeRevision: options.basisTreeRevision,
31
+ rootOrder: options.rootOrder,
32
+ chainKey: "page",
33
+ contentRect: options.rootRect,
34
+ visible: (_b = options.rootVisible) !== null && _b !== void 0 ? _b : true,
35
+ },
36
+ ],
37
+ nodes,
38
+ chains: [{ chainKey: "page", ancestors: [] }],
39
+ };
40
+ }
@@ -0,0 +1,28 @@
1
+ import type { CompiledNativeRoot, CoreNode, NodeRef, RootRef } from "./types.js";
2
+ export interface IdentityRecord {
3
+ nodeKey: string;
4
+ nodeEpoch: number;
5
+ kind: CoreNode["kind"];
6
+ authorId?: string;
7
+ path: string;
8
+ }
9
+ export interface IdentityTable {
10
+ byPath: Record<string, IdentityRecord>;
11
+ byAuthorId: Record<string, IdentityRecord>;
12
+ }
13
+ export interface IdentifiedNode {
14
+ node: CoreNode;
15
+ nodeRef: NodeRef;
16
+ parentRef: NodeRef | null;
17
+ order: number;
18
+ path: string;
19
+ children: IdentifiedNode[];
20
+ }
21
+ export interface IdentifiedRoot {
22
+ rootRef: RootRef;
23
+ children: IdentifiedNode[];
24
+ table: IdentityTable;
25
+ }
26
+ export declare function nextOpaqueKey(prefix: string): string;
27
+ export declare function identifyCompiledRoot(compiled: CompiledNativeRoot, rootRef: RootRef, previous?: IdentifiedRoot | null): IdentifiedRoot;
28
+ export declare function remountIdentity(previous: IdentityRecord): IdentityRecord;
@@ -0,0 +1,51 @@
1
+ let autoKey = 0;
2
+ export function nextOpaqueKey(prefix) {
3
+ autoKey += 1;
4
+ return `${prefix}-${autoKey.toString(36)}`;
5
+ }
6
+ export function identifyCompiledRoot(compiled, rootRef, previous = null) {
7
+ const table = { byPath: Object.create(null), byAuthorId: Object.create(null) };
8
+ const livePrev = previous ? liveNodeKeys(previous) : new Set();
9
+ const children = compiled.children.map((child, index) => identifyNode(child, rootRef, null, index, String(index), previous, livePrev, table));
10
+ return { rootRef, children, table };
11
+ }
12
+ function liveNodeKeys(root) {
13
+ const keys = new Set();
14
+ const walk = (nodes) => {
15
+ for (const node of nodes) {
16
+ keys.add(node.nodeRef.nodeKey);
17
+ walk(node.children);
18
+ }
19
+ };
20
+ walk(root.children);
21
+ return keys;
22
+ }
23
+ function identifyNode(node, rootRef, parentRef, order, path, previous, livePrev, table) {
24
+ const authorId = node.authorId;
25
+ const prev = authorId ? previous === null || previous === void 0 ? void 0 : previous.table.byAuthorId[authorId] : previous === null || previous === void 0 ? void 0 : previous.table.byPath[path];
26
+ let nodeKey;
27
+ let nodeEpoch;
28
+ if (prev && prev.kind === node.kind && prev.authorId === authorId && livePrev.delete(prev.nodeKey)) {
29
+ nodeKey = prev.nodeKey;
30
+ nodeEpoch = prev.nodeEpoch;
31
+ }
32
+ else {
33
+ nodeKey = nextOpaqueKey("n");
34
+ nodeEpoch = 1;
35
+ }
36
+ const record = { nodeKey, nodeEpoch, kind: node.kind, authorId, path };
37
+ table.byPath[path] = record;
38
+ if (authorId) {
39
+ table.byAuthorId[authorId] = record;
40
+ }
41
+ const nodeRef = {
42
+ ...rootRef,
43
+ nodeKey,
44
+ nodeEpoch,
45
+ };
46
+ const children = node.children.map((child, index) => identifyNode(child, rootRef, nodeRef, index, `${path}/${index}`, previous, livePrev, table));
47
+ return { node, nodeRef, parentRef, order, path, children };
48
+ }
49
+ export function remountIdentity(previous) {
50
+ return { ...previous, nodeEpoch: previous.nodeEpoch + 1 };
51
+ }
@@ -0,0 +1,14 @@
1
+ export { INLINE_NATIVE_SCHEMA, NATIVE_ERROR_CODES, PUBLIC_COMPONENT_NAMES, PUBLIC_ELEMENT_TAGS, CORE_KINDS, HOST_FACTORY_KINDS, AUTHOR_COMPONENT_TO_TAG, TAG_TO_AUTHOR_COMPONENT, NATIVE_ACTION_ICONS, POINTER_EVENTS_VALUES, COVER_SCRIM_VALUES, type NativeErrorCode, type PublicComponentName, type PublicElementTag, type CoreKind, type HostFactoryKind, type NativeActionIcon, type NativeStyleProperty, } from "./schema.js";
2
+ export { compileInlineNativeRoot, compileInlineNativeForest, collectAuthorTreeFromElement, normalizeAuthorType, parseBooleanAttr, isFallbackElement, findInlineNativeRoot, } from "./structure.js";
3
+ export { unwrapNativeEventPayload, bindPayloadHandler, isDomEvent } from "./unwrap.js";
4
+ export { applyIslandHostEvent, type IslandHostMessage } from "./events.js";
5
+ export { identifyCompiledRoot, nextOpaqueKey, type IdentifiedRoot, type IdentifiedNode } from "./identity.js";
6
+ export { buildRootCommit, type NativeRootCommitJson } from "./commit.js";
7
+ export { emptyViewLease, viewApplyGrant, viewAcceptLease, viewMarkActive, viewCanShowFallback, type ViewLeaseState, } from "./lease.js";
8
+ export { buildGeometrySnapshot, type NativeGeometrySnapshotJson } from "./geometry.js";
9
+ export { createRootRuntimeState, publishCompiledRoot, applyHostLeaseMessage, type RootRuntimeState, type RootHostMessages, } from "./runtime.js";
10
+ export { VIDEO_COMMANDS, buildVideoCommandRequest, collectVideoResourceUrls, videoCommandUrls, validateControlsSnapshot, type VideoCommand, type VideoCommandRequest, type VideoControlsSemanticSnapshot, } from "./video.js";
11
+ export { nativeError, isNativeErrorCode } from "./errors.js";
12
+ export { EMPTY_ROOT_REF, type RootRef, type NodeRef, type NativeError, type AuthorNode, type AuthorChild, type CoreNode, type CompiledNativeRoot, type CompileInlineNativeResult, type CompileInlineNativeOptions, type PressPayload, type ValuePayload, type FocusPayload, type PointerPayload, type NativeHandler, } from "./types.js";
13
+ export { LxNativeRootElement, LxNativeViewElement, LxNativeCoverElement, LxNativeTextElement, LxNativeButtonElement, registerNativeRootComponent, registerNativeViewComponent, registerNativeCoverComponent, registerNativeTextComponent, registerNativeButtonComponent, registerInlineNativeAuthorComponents, } from "./elements.js";
14
+ export declare function registerInlineNativeComponents(): void;
@@ -0,0 +1,19 @@
1
+ export { INLINE_NATIVE_SCHEMA, NATIVE_ERROR_CODES, PUBLIC_COMPONENT_NAMES, PUBLIC_ELEMENT_TAGS, CORE_KINDS, HOST_FACTORY_KINDS, AUTHOR_COMPONENT_TO_TAG, TAG_TO_AUTHOR_COMPONENT, NATIVE_ACTION_ICONS, POINTER_EVENTS_VALUES, COVER_SCRIM_VALUES, } from "./schema.js";
2
+ export { compileInlineNativeRoot, compileInlineNativeForest, collectAuthorTreeFromElement, normalizeAuthorType, parseBooleanAttr, isFallbackElement, findInlineNativeRoot, } from "./structure.js";
3
+ export { unwrapNativeEventPayload, bindPayloadHandler, isDomEvent } from "./unwrap.js";
4
+ export { applyIslandHostEvent } from "./events.js";
5
+ export { identifyCompiledRoot, nextOpaqueKey } from "./identity.js";
6
+ export { buildRootCommit } from "./commit.js";
7
+ export { emptyViewLease, viewApplyGrant, viewAcceptLease, viewMarkActive, viewCanShowFallback, } from "./lease.js";
8
+ export { buildGeometrySnapshot } from "./geometry.js";
9
+ export { createRootRuntimeState, publishCompiledRoot, applyHostLeaseMessage, } from "./runtime.js";
10
+ export { VIDEO_COMMANDS, buildVideoCommandRequest, collectVideoResourceUrls, videoCommandUrls, validateControlsSnapshot, } from "./video.js";
11
+ export { nativeError, isNativeErrorCode } from "./errors.js";
12
+ export { EMPTY_ROOT_REF, } from "./types.js";
13
+ export { LxNativeRootElement, LxNativeViewElement, LxNativeCoverElement, LxNativeTextElement, LxNativeButtonElement, registerNativeRootComponent, registerNativeViewComponent, registerNativeCoverComponent, registerNativeTextComponent, registerNativeButtonComponent, registerInlineNativeAuthorComponents, } from "./elements.js";
14
+ import { registerInlineNativeAuthorComponents } from "./elements.js";
15
+ import { registerVideoComponent } from "../video.js";
16
+ export function registerInlineNativeComponents() {
17
+ registerInlineNativeAuthorComponents();
18
+ registerVideoComponent();
19
+ }
@@ -0,0 +1,2 @@
1
+ /** ResizeObserver misses position-only changes (siblings, fonts and ancestor layout). */
2
+ export declare function observeNativeLayout(root: HTMLElement, invalidate: () => void): () => void;
@@ -0,0 +1,58 @@
1
+ import { OBSERVED_STYLE_FIELDS } from "./style.js";
2
+ const NODE_SELECTOR = "lx-native-view,lx-native-cover,lx-native-text,lx-native-button,lx-video";
3
+ const subscriptions = new Set();
4
+ let frame = null;
5
+ let observer;
6
+ let stylesDirty = false;
7
+ function sample() {
8
+ frame = null;
9
+ for (const entry of subscriptions) {
10
+ if (!entry.root.isConnected)
11
+ continue;
12
+ const elements = [entry.root, ...Array.from(entry.root.querySelectorAll(NODE_SELECTOR))];
13
+ for (let ancestor = entry.root.parentElement; ancestor; ancestor = ancestor.parentElement)
14
+ elements.push(ancestor);
15
+ const geometry = elements
16
+ .map((element) => {
17
+ const rect = element.getBoundingClientRect();
18
+ const style = getComputedStyle(element);
19
+ // CSSOM, pseudo-classes and media queries can change paint without a DOM mutation.
20
+ const paint = OBSERVED_STYLE_FIELDS.map(field => style[field]).join(",");
21
+ return `${rect.x},${rect.y},${rect.width},${rect.height},${element.clientWidth},${element.clientHeight}:${paint}`;
22
+ }).join(";");
23
+ if (stylesDirty || geometry !== entry.geometry) {
24
+ entry.geometry = geometry;
25
+ entry.invalidate();
26
+ }
27
+ }
28
+ stylesDirty = false;
29
+ if (subscriptions.size)
30
+ frame = requestAnimationFrame(sample);
31
+ }
32
+ /** ResizeObserver misses position-only changes (siblings, fonts and ancestor layout). */
33
+ export function observeNativeLayout(root, invalidate) {
34
+ const entry = { root, invalidate, geometry: "" };
35
+ subscriptions.add(entry);
36
+ if (!observer) {
37
+ observer = new MutationObserver((records) => {
38
+ // Root already observes its own subtree. External changes may alter inherited styles.
39
+ if (records.some((record) => !Array.from(subscriptions).some(({ root }) => root.contains(record.target)))) {
40
+ stylesDirty = true;
41
+ }
42
+ });
43
+ observer.observe(document.documentElement, { attributes: true, childList: true, characterData: true, subtree: true });
44
+ }
45
+ if (frame === null)
46
+ frame = requestAnimationFrame(sample);
47
+ return () => {
48
+ subscriptions.delete(entry);
49
+ if (!subscriptions.size) {
50
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
51
+ observer = undefined;
52
+ if (frame !== null)
53
+ cancelAnimationFrame(frame);
54
+ frame = null;
55
+ stylesDirty = false;
56
+ }
57
+ };
58
+ }
@@ -0,0 +1,13 @@
1
+ export type ViewLeasePhase = "none" | "granted" | "accept-sent" | "active" | "revoked" | "expired";
2
+ export interface ViewLeaseState {
3
+ phase: ViewLeasePhase;
4
+ leaseId?: string;
5
+ sequence: number;
6
+ durationMs: number;
7
+ deadlineTickMs?: number;
8
+ }
9
+ export declare function emptyViewLease(): ViewLeaseState;
10
+ export declare function viewApplyGrant(leaseId: string, sequence: number, durationMs: number, nowMs: number): ViewLeaseState;
11
+ export declare function viewAcceptLease(state: ViewLeaseState): ViewLeaseState | null;
12
+ export declare function viewMarkActive(state: ViewLeaseState): ViewLeaseState;
13
+ export declare function viewCanShowFallback(state: ViewLeaseState, nowMs: number): boolean;
@@ -0,0 +1,31 @@
1
+ export function emptyViewLease() {
2
+ return { phase: "none", sequence: 0, durationMs: 0 };
3
+ }
4
+ export function viewApplyGrant(leaseId, sequence, durationMs, nowMs) {
5
+ return {
6
+ phase: "granted",
7
+ leaseId,
8
+ sequence,
9
+ durationMs,
10
+ deadlineTickMs: nowMs + durationMs,
11
+ };
12
+ }
13
+ export function viewAcceptLease(state) {
14
+ if (state.phase !== "granted" || !state.leaseId)
15
+ return null;
16
+ return { ...state, phase: "accept-sent" };
17
+ }
18
+ export function viewMarkActive(state) {
19
+ return { ...state, phase: "active", deadlineTickMs: undefined };
20
+ }
21
+ export function viewCanShowFallback(state, nowMs) {
22
+ if (state.phase === "none" || state.phase === "granted" || state.phase === "revoked" || state.phase === "expired") {
23
+ return true;
24
+ }
25
+ if (state.phase === "accept-sent") {
26
+ return state.deadlineTickMs !== undefined && nowMs >= state.deadlineTickMs;
27
+ }
28
+ if (state.phase === "active")
29
+ return false;
30
+ return false;
31
+ }
@@ -0,0 +1,64 @@
1
+ import { type NativeRootCommitJson } from "./commit.js";
2
+ import { type NativeGeometrySnapshotJson } from "./geometry.js";
3
+ import { type IdentifiedRoot } from "./identity.js";
4
+ import { type ViewLeaseState } from "./lease.js";
5
+ import type { CompiledNativeRoot, RootRef } from "./types.js";
6
+ export interface RootRuntimeState {
7
+ identified: IdentifiedRoot | null;
8
+ treeRevision: number;
9
+ geometryRevision: number;
10
+ lease: ViewLeaseState;
11
+ }
12
+ export interface RootHostMessages {
13
+ commit?: NativeRootCommitJson;
14
+ geometry: NativeGeometrySnapshotJson;
15
+ leaseAccept?: {
16
+ action: "root.leaseAccept";
17
+ id: string;
18
+ root: RootRef;
19
+ leaseId: string;
20
+ sequence: number;
21
+ };
22
+ ready: boolean;
23
+ }
24
+ export declare function createRootRuntimeState(): RootRuntimeState;
25
+ export declare function publishCompiledRoot(options: {
26
+ compiled: CompiledNativeRoot;
27
+ rootRef: RootRef;
28
+ state: RootRuntimeState;
29
+ rootRect: {
30
+ x: number;
31
+ y: number;
32
+ width: number;
33
+ height: number;
34
+ };
35
+ nodeRects: Record<string, {
36
+ x: number;
37
+ y: number;
38
+ width: number;
39
+ height: number;
40
+ }>;
41
+ nodeVisibility?: Record<string, boolean>;
42
+ nodeClipStacks?: Record<string, unknown[]>;
43
+ rootVisible?: boolean;
44
+ rootOrder?: number;
45
+ viewportOffset?: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ nowMs?: number;
50
+ }): {
51
+ state: RootRuntimeState;
52
+ messages: RootHostMessages;
53
+ };
54
+ export declare function applyHostLeaseMessage(state: RootRuntimeState, message: {
55
+ action?: string;
56
+ leaseId?: string;
57
+ sequence?: number;
58
+ leaseDurationMs?: number;
59
+ }, nowMs: number): {
60
+ state: RootRuntimeState;
61
+ leaseAccept?: RootHostMessages["leaseAccept"];
62
+ ready: boolean;
63
+ };
64
+ export declare function rootFallbackAllowed(state: RootRuntimeState, nowMs: number): boolean;
@@ -0,0 +1,119 @@
1
+ import { buildRootCommit } from "./commit.js";
2
+ import { buildGeometrySnapshot } from "./geometry.js";
3
+ import { identifyCompiledRoot } from "./identity.js";
4
+ import { emptyViewLease, viewAcceptLease, viewApplyGrant, viewCanShowFallback, viewMarkActive, } from "./lease.js";
5
+ export function createRootRuntimeState() {
6
+ return {
7
+ identified: null,
8
+ treeRevision: 0,
9
+ geometryRevision: 0,
10
+ lease: emptyViewLease(),
11
+ };
12
+ }
13
+ export function publishCompiledRoot(options) {
14
+ var _a, _b, _c;
15
+ const identified = identifyCompiledRoot(options.compiled, options.rootRef, options.state.identified);
16
+ const first = options.state.identified === null;
17
+ let treeRevision = options.state.treeRevision;
18
+ let commit;
19
+ if (first) {
20
+ treeRevision = 1;
21
+ commit = buildRootCommit(identified, null, 1);
22
+ }
23
+ else {
24
+ const next = buildRootCommit(identified, options.state.identified, options.state.treeRevision + 1);
25
+ if (next.operations.length > 0) {
26
+ treeRevision = options.state.treeRevision + 1;
27
+ commit = next;
28
+ }
29
+ }
30
+ const geometryRevision = options.state.geometryRevision + 1;
31
+ const nodeRects = mapRectsToNodeKeys(identified, options.nodeRects);
32
+ const nodeVisibility = mapValuesToNodeKeys(identified, (_a = options.nodeVisibility) !== null && _a !== void 0 ? _a : {});
33
+ const nodeClipStacks = mapValuesToNodeKeys(identified, (_b = options.nodeClipStacks) !== null && _b !== void 0 ? _b : {});
34
+ const geometry = buildGeometrySnapshot({
35
+ identified,
36
+ basisTreeRevision: Math.max(treeRevision, 1),
37
+ geometryRevision,
38
+ rootOrder: (_c = options.rootOrder) !== null && _c !== void 0 ? _c : 0,
39
+ rootRect: options.rootRect,
40
+ nodeRects,
41
+ nodeVisibility,
42
+ nodeClipStacks,
43
+ rootVisible: options.rootVisible,
44
+ viewportOffset: options.viewportOffset,
45
+ });
46
+ const state = {
47
+ identified,
48
+ treeRevision,
49
+ geometryRevision,
50
+ lease: options.state.lease,
51
+ };
52
+ return {
53
+ state,
54
+ messages: {
55
+ commit,
56
+ geometry,
57
+ ready: state.lease.phase === "active",
58
+ },
59
+ };
60
+ }
61
+ function mapValuesToNodeKeys(identified, values) {
62
+ const mapped = { ...values };
63
+ const walk = (nodes) => {
64
+ for (const node of nodes) {
65
+ const authorId = node.node.authorId;
66
+ if (authorId && values[authorId] !== undefined) {
67
+ mapped[node.nodeRef.nodeKey] = values[authorId];
68
+ }
69
+ walk(node.children);
70
+ }
71
+ };
72
+ walk(identified.children);
73
+ return mapped;
74
+ }
75
+ export function applyHostLeaseMessage(state, message, nowMs) {
76
+ var _a, _b;
77
+ if (message.action === "root.leaseGranted" && message.leaseId) {
78
+ const granted = viewApplyGrant(message.leaseId, (_a = message.sequence) !== null && _a !== void 0 ? _a : 1, (_b = message.leaseDurationMs) !== null && _b !== void 0 ? _b : 8000, nowMs);
79
+ const accepted = viewAcceptLease(granted);
80
+ if (!accepted || !accepted.leaseId || !state.identified) {
81
+ return { state: { ...state, lease: granted }, ready: false };
82
+ }
83
+ return {
84
+ state: { ...state, lease: accepted },
85
+ leaseAccept: {
86
+ action: "root.leaseAccept",
87
+ id: state.identified.rootRef.rootKey,
88
+ root: state.identified.rootRef,
89
+ leaseId: accepted.leaseId,
90
+ sequence: accepted.sequence,
91
+ },
92
+ ready: false,
93
+ };
94
+ }
95
+ if (message.action === "root.leaseActive") {
96
+ const lease = viewMarkActive(state.lease);
97
+ return { state: { ...state, lease }, ready: lease.phase === "active" };
98
+ }
99
+ return { state, ready: state.lease.phase === "active" };
100
+ }
101
+ export function rootFallbackAllowed(state, nowMs) {
102
+ return viewCanShowFallback(state.lease, nowMs);
103
+ }
104
+ function mapRectsToNodeKeys(identified, rects) {
105
+ const mapped = {
106
+ ...rects,
107
+ };
108
+ const walk = (nodes) => {
109
+ for (const node of nodes) {
110
+ const authorId = node.node.authorId;
111
+ if (authorId && rects[authorId]) {
112
+ mapped[node.nodeRef.nodeKey] = rects[authorId];
113
+ }
114
+ walk(node.children);
115
+ }
116
+ };
117
+ walk(identified.children);
118
+ return mapped;
119
+ }