@lingxia/elements 0.14.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 (69) 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/picker.js +12 -4
  42. package/dist/video-events.d.ts +2 -0
  43. package/dist/video-events.js +18 -0
  44. package/dist/video.d.ts +98 -79
  45. package/dist/video.js +182 -757
  46. package/package.json +2 -1
  47. package/src/index.ts +58 -1
  48. package/src/inline-native/commit.ts +141 -0
  49. package/src/inline-native/elements.ts +829 -0
  50. package/src/inline-native/errors.ts +35 -0
  51. package/src/inline-native/events.ts +31 -0
  52. package/src/inline-native/geometry.ts +86 -0
  53. package/src/inline-native/identity.ts +102 -0
  54. package/src/inline-native/index.ts +103 -0
  55. package/src/inline-native/layout.ts +58 -0
  56. package/src/inline-native/lease.ts +48 -0
  57. package/src/inline-native/runtime.ts +185 -0
  58. package/src/inline-native/schema.ts +247 -0
  59. package/src/inline-native/structure.ts +823 -0
  60. package/src/inline-native/style.ts +111 -0
  61. package/src/inline-native/types.ts +105 -0
  62. package/src/inline-native/unwrap.ts +34 -0
  63. package/src/inline-native/video.ts +125 -0
  64. package/src/native_component_wrapper_shared.ts +8 -6
  65. package/src/nativecomponent.ts +8 -13
  66. package/src/navigator.ts +9 -9
  67. package/src/picker.ts +13 -4
  68. package/src/video-events.ts +16 -0
  69. package/src/video.ts +245 -839
@@ -0,0 +1,7 @@
1
+ import type { AuthorNode } from "./types.js";
2
+ export declare const OBSERVED_STYLE_FIELDS: string[];
3
+ /** Native nodes are flat siblings, so each receives its cumulative alpha. */
4
+ export declare function effectiveOpacity(element: Element): number;
5
+ /** Canvas resolves browser-supported CSS colors into the hosts' sRGB wire format. */
6
+ export declare function normalizeNativeColor(document: Document, value: string): string | undefined;
7
+ export declare function collectNativeStyleIssues(element: Element): NonNullable<AuthorNode["styleIssues"]>;
@@ -0,0 +1,119 @@
1
+ // Computed values catch unsupported styles from classes as well as inline CSS.
2
+ const unsupportedDefaults = {
3
+ transform: ["none"], translate: ["none"], rotate: ["none"], scale: ["none"],
4
+ perspective: ["none"], clipPath: ["none"], maskImage: ["none"],
5
+ boxShadow: ["none"], filter: ["none"], backdropFilter: ["none"],
6
+ mixBlendMode: ["normal"], backgroundImage: ["none"],
7
+ animationName: ["none"], transitionDuration: ["0s"],
8
+ textDecorationLine: ["none"], fontStyle: ["normal"],
9
+ letterSpacing: ["normal", "0px"], textShadow: ["none"],
10
+ };
11
+ const layoutProperties = new Set([
12
+ "transform", "translate", "rotate", "scale", "perspective", "clipPath", "maskImage",
13
+ ]);
14
+ export const OBSERVED_STYLE_FIELDS = [
15
+ ...Object.keys(unsupportedDefaults), "display", "visibility", "opacity", "overflowX", "overflowY",
16
+ "color", "backgroundColor", "accentColor", "fontSize", "fontWeight", "lineHeight", "textAlign", "direction",
17
+ ...["Top", "Right", "Bottom", "Left"].flatMap(side => ["Width", "Style", "Color"].map(suffix => `border${side}${suffix}`)),
18
+ "borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius", "borderBottomRightRadius",
19
+ ];
20
+ /** Native nodes are flat siblings, so each receives its cumulative alpha. */
21
+ export function effectiveOpacity(element) {
22
+ var _a, _b;
23
+ let opacity = 1;
24
+ for (let node = element; node; node = node.parentElement) {
25
+ opacity *= Number((_b = (_a = node.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(node).opacity) !== null && _b !== void 0 ? _b : 1);
26
+ }
27
+ return opacity;
28
+ }
29
+ const colorContexts = new WeakMap();
30
+ const colors = new Map();
31
+ /** Canvas resolves browser-supported CSS colors into the hosts' sRGB wire format. */
32
+ export function normalizeNativeColor(document, value) {
33
+ var _a;
34
+ if (/^rgba?\([\d.,\s]+\)$/.test(value) || /^#[\da-f]{6}([\da-f]{2})?$/i.test(value))
35
+ return value;
36
+ const cached = colors.get(value);
37
+ if (cached)
38
+ return cached;
39
+ let context = colorContexts.get(document);
40
+ if (!context) {
41
+ const canvas = document.createElement("canvas");
42
+ canvas.width = canvas.height = 1;
43
+ context = (_a = canvas.getContext("2d", { willReadFrequently: true, colorSpace: "srgb" })) !== null && _a !== void 0 ? _a : undefined;
44
+ if (!context)
45
+ return undefined;
46
+ colorContexts.set(document, context);
47
+ }
48
+ context.fillStyle = "#010203";
49
+ context.fillStyle = value;
50
+ const first = context.fillStyle;
51
+ context.fillStyle = "#040506";
52
+ context.fillStyle = value;
53
+ if (context.fillStyle !== first)
54
+ return undefined;
55
+ context.clearRect(0, 0, 1, 1);
56
+ context.fillRect(0, 0, 1, 1);
57
+ const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;
58
+ const result = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
59
+ if (colors.size >= 256)
60
+ colors.clear();
61
+ colors.set(value, result);
62
+ return result;
63
+ }
64
+ export function collectNativeStyleIssues(element) {
65
+ var _a, _b;
66
+ const view = (_a = element.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView;
67
+ if (!(view === null || view === void 0 ? void 0 : view.getComputedStyle))
68
+ return [];
69
+ const style = view.getComputedStyle(element);
70
+ const issues = [];
71
+ for (const property of ["color", "backgroundColor", "borderTopColor", "accentColor"]) {
72
+ const value = style[property];
73
+ if (value && value !== "auto" && !normalizeNativeColor(element.ownerDocument, value)) {
74
+ issues.push({ property, value, layout: false });
75
+ }
76
+ }
77
+ for (const [property, defaults] of Object.entries(unsupportedDefaults)) {
78
+ const value = String((_b = style[property]) !== null && _b !== void 0 ? _b : "").trim();
79
+ if (!value || defaults.includes(value))
80
+ continue;
81
+ if (property === "backgroundImage" && isVideoPoster(element, value))
82
+ continue;
83
+ if (property === "transitionDuration" && value.split(",").every((duration) => parseFloat(duration) === 0))
84
+ continue;
85
+ issues.push({ property, value, layout: layoutProperties.has(property) });
86
+ }
87
+ const corners = [style.borderTopLeftRadius, style.borderTopRightRadius,
88
+ style.borderBottomRightRadius, style.borderBottomLeftRadius];
89
+ if (corners.some((value) => value && (value !== corners[0] || !/^\d+(\.\d+)?px$/.test(value)))) {
90
+ issues.push({ property: "borderRadius", value: corners.join(" / "), layout: false });
91
+ }
92
+ for (const suffix of ["Width", "Color", "Style"]) {
93
+ const values = [style[`borderTop${suffix}`], style[`borderRight${suffix}`],
94
+ style[`borderBottom${suffix}`], style[`borderLeft${suffix}`]];
95
+ const hasBorder = [style.borderTopWidth, style.borderRightWidth,
96
+ style.borderBottomWidth, style.borderLeftWidth].some((width) => parseFloat(width) > 0);
97
+ if (hasBorder && (values.some((value) => value !== values[0]) ||
98
+ (suffix === "Style" && values.some((value) => value !== "solid" && value !== "none")))) {
99
+ issues.push({ property: `border${suffix}`, value: values.join(" / "), layout: false });
100
+ }
101
+ }
102
+ return issues;
103
+ }
104
+ function isVideoPoster(element, background) {
105
+ var _a;
106
+ if (element.tagName.toLowerCase() !== "lx-video")
107
+ return false;
108
+ const poster = element.getAttribute("poster");
109
+ const url = (_a = /^url\("(.*)"\)$/.exec(background)) === null || _a === void 0 ? void 0 : _a[1];
110
+ if (!poster || !url)
111
+ return false;
112
+ // LxVideo owns this DOM placeholder; the native backend paints its poster prop.
113
+ try {
114
+ return new URL(url.replace(/\\(["\\])/g, "$1"), element.baseURI).href === new URL(poster, element.baseURI).href;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
@@ -0,0 +1,82 @@
1
+ import type { CoreKind, HostFactoryKind, NativeErrorCode, PublicComponentName } from "./schema.js";
2
+ export interface RootRef {
3
+ surfaceInstanceId: string;
4
+ pageInstanceId: string;
5
+ documentInstanceId: string;
6
+ rootKey: string;
7
+ rootEpoch: number;
8
+ }
9
+ export interface NodeRef extends RootRef {
10
+ nodeKey: string;
11
+ nodeEpoch: number;
12
+ }
13
+ export interface NativeError {
14
+ code: NativeErrorCode;
15
+ scope: "node" | "root";
16
+ recoverable: boolean;
17
+ root: RootRef;
18
+ node?: NodeRef;
19
+ message: string;
20
+ }
21
+ export interface PressPayload {
22
+ source: "pointer" | "keyboard" | "accessibility";
23
+ }
24
+ export interface ValuePayload {
25
+ value: number;
26
+ }
27
+ export interface FocusPayload {
28
+ source: "pointer" | "keyboard" | "accessibility";
29
+ }
30
+ export interface PointerPayload {
31
+ source: "pointer";
32
+ }
33
+ export type NativeHandler<TPayload> = (payload: TPayload) => void;
34
+ export type AuthorChild = AuthorNode | string | number | false | null | undefined | readonly AuthorChild[];
35
+ export interface AuthorNode {
36
+ type: string;
37
+ authorId?: string;
38
+ automationId?: string;
39
+ props?: Record<string, unknown>;
40
+ children?: AuthorChild;
41
+ textContent?: string | number;
42
+ styleIssues?: Array<{
43
+ property: string;
44
+ value: string;
45
+ layout: boolean;
46
+ }>;
47
+ }
48
+ export interface CoreNode {
49
+ kind: HostFactoryKind;
50
+ authorType: PublicComponentName;
51
+ authorId?: string;
52
+ automationId?: string;
53
+ props: Record<string, unknown>;
54
+ children: CoreNode[];
55
+ text?: string;
56
+ }
57
+ export interface CompiledNativeRoot {
58
+ authorType: "LxNativeRoot";
59
+ authorId?: string;
60
+ automationId?: string;
61
+ props: Record<string, unknown>;
62
+ children: CoreNode[];
63
+ kind: Extract<CoreKind, "root">;
64
+ }
65
+ export type CompileInlineNativeResult = {
66
+ ok: true;
67
+ root: CompiledNativeRoot;
68
+ diagnostics: NativeError[];
69
+ } | {
70
+ ok: false;
71
+ error: NativeError;
72
+ diagnostics: NativeError[];
73
+ };
74
+ export interface CompileInlineNativeOptions {
75
+ rootRef?: RootRef;
76
+ /**
77
+ * When true, `controls` defaults to `true` (author contract). Tests and
78
+ * wrappers should leave this on; only a host fixture may override it.
79
+ */
80
+ defaultVideoControls?: boolean;
81
+ }
82
+ export declare const EMPTY_ROOT_REF: RootRef;
@@ -0,0 +1,7 @@
1
+ export const EMPTY_ROOT_REF = {
2
+ surfaceInstanceId: "",
3
+ pageInstanceId: "",
4
+ documentInstanceId: "",
5
+ rootKey: "",
6
+ rootEpoch: 0,
7
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Payload-first unwrap used by React/Vue wrappers. HTML keeps CustomEvent.
3
+ *
4
+ * A real DOM Event (or an event-like `{ type, detail }` used by unit tests)
5
+ * yields `detail`. A bare payload object is returned as-is so wrappers can
6
+ * also be called with already-unwrapped values.
7
+ */
8
+ export declare function unwrapNativeEventPayload<TPayload>(event: unknown): TPayload;
9
+ export declare function isDomEvent(value: unknown): value is Event;
10
+ export declare function bindPayloadHandler<TPayload>(handler: ((payload: TPayload) => void) | undefined): EventListener;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Payload-first unwrap used by React/Vue wrappers. HTML keeps CustomEvent.
3
+ *
4
+ * A real DOM Event (or an event-like `{ type, detail }` used by unit tests)
5
+ * yields `detail`. A bare payload object is returned as-is so wrappers can
6
+ * also be called with already-unwrapped values.
7
+ */
8
+ export function unwrapNativeEventPayload(event) {
9
+ if (isDomEvent(event) || isEventLike(event)) {
10
+ const detail = event.detail;
11
+ return (detail === undefined ? {} : detail);
12
+ }
13
+ return event;
14
+ }
15
+ export function isDomEvent(value) {
16
+ return typeof Event !== "undefined" && value instanceof Event;
17
+ }
18
+ function isEventLike(value) {
19
+ if (!value || typeof value !== "object")
20
+ return false;
21
+ const record = value;
22
+ if (!("detail" in record))
23
+ return false;
24
+ return "type" in record || "target" in record || "currentTarget" in record;
25
+ }
26
+ export function bindPayloadHandler(handler) {
27
+ return (event) => {
28
+ if (typeof handler !== "function")
29
+ return;
30
+ handler(unwrapNativeEventPayload(event));
31
+ };
32
+ }
@@ -0,0 +1,65 @@
1
+ import type { NodeRef } from "./types.js";
2
+ export declare const VIDEO_COMMANDS: readonly ["play", "pause", "stop", "seek", "enterFullscreen", "exitFullscreen", "setStreamSource"];
3
+ export type VideoCommandName = (typeof VIDEO_COMMANDS)[number];
4
+ export type VideoCommand = {
5
+ name: "play";
6
+ } | {
7
+ name: "pause";
8
+ } | {
9
+ name: "stop";
10
+ } | {
11
+ name: "seek";
12
+ seconds: number;
13
+ } | {
14
+ name: "enterFullscreen";
15
+ } | {
16
+ name: "exitFullscreen";
17
+ } | {
18
+ name: "setStreamSource";
19
+ options: Record<string, unknown>;
20
+ };
21
+ export interface VideoCommandRequest {
22
+ action: "video.command";
23
+ owner: NodeRef;
24
+ requestId: string;
25
+ command: VideoCommand;
26
+ }
27
+ export interface VideoControlDescriptorBase {
28
+ controlId: string;
29
+ label: string;
30
+ frame: {
31
+ x: number;
32
+ y: number;
33
+ width: number;
34
+ height: number;
35
+ };
36
+ visible: boolean;
37
+ disabled?: boolean;
38
+ }
39
+ export type VideoControlDescriptor = (VideoControlDescriptorBase & {
40
+ role: "button";
41
+ pressed?: boolean;
42
+ expanded?: boolean;
43
+ actions: ReadonlyArray<"focus" | "blur" | "activate">;
44
+ }) | (VideoControlDescriptorBase & {
45
+ role: "slider";
46
+ value: number;
47
+ min: number;
48
+ max: number;
49
+ actions: ReadonlyArray<"focus" | "blur" | "increment" | "decrement" | "setValue">;
50
+ });
51
+ export interface VideoControlsSemanticSnapshot {
52
+ action: "video.controlsSemanticSnapshot";
53
+ owner: NodeRef;
54
+ revision: number;
55
+ controls: readonly VideoControlDescriptor[];
56
+ }
57
+ export declare function buildVideoCommandRequest(owner: NodeRef, command: VideoCommand, requestId: string): VideoCommandRequest;
58
+ export declare function videoCommandUrls(command: VideoCommand): string[];
59
+ export declare function collectVideoResourceUrls(props: Record<string, unknown>): string[];
60
+ export declare function validateControlsSnapshot(snapshot: VideoControlsSemanticSnapshot, lastRevision: number): {
61
+ ok: true;
62
+ } | {
63
+ ok: false;
64
+ message: string;
65
+ };
@@ -0,0 +1,69 @@
1
+ export const VIDEO_COMMANDS = [
2
+ "play",
3
+ "pause",
4
+ "stop",
5
+ "seek",
6
+ "enterFullscreen",
7
+ "exitFullscreen",
8
+ "setStreamSource",
9
+ ];
10
+ export function buildVideoCommandRequest(owner, command, requestId) {
11
+ return { action: "video.command", owner, requestId, command };
12
+ }
13
+ export function videoCommandUrls(command) {
14
+ var _a;
15
+ if (command.name !== "setStreamSource")
16
+ return [];
17
+ const options = (_a = command.options) !== null && _a !== void 0 ? _a : {};
18
+ const urls = [];
19
+ for (const key of ["url", "src", "uri"]) {
20
+ const value = options[key];
21
+ if (typeof value === "string" && value)
22
+ urls.push(value);
23
+ }
24
+ return urls;
25
+ }
26
+ export function collectVideoResourceUrls(props) {
27
+ const urls = [];
28
+ for (const key of ["src", "poster"]) {
29
+ const value = props[key];
30
+ if (typeof value === "string" && value)
31
+ urls.push(value);
32
+ }
33
+ const watermark = props.watermark;
34
+ if ((watermark === null || watermark === void 0 ? void 0 : watermark.resource) && typeof watermark.resource === "object") {
35
+ const url = watermark.resource.url;
36
+ if (typeof url === "string" && url)
37
+ urls.push(url);
38
+ }
39
+ const qualities = props.qualities;
40
+ if (Array.isArray(qualities)) {
41
+ for (const item of qualities) {
42
+ if (item && typeof item === "object" && typeof item.url === "string") {
43
+ urls.push(item.url);
44
+ }
45
+ }
46
+ }
47
+ return urls;
48
+ }
49
+ export function validateControlsSnapshot(snapshot, lastRevision) {
50
+ if (snapshot.revision <= lastRevision) {
51
+ return { ok: false, message: "controls snapshot revision must increase" };
52
+ }
53
+ const seen = new Set();
54
+ for (const control of snapshot.controls) {
55
+ if (!control.controlId || seen.has(control.controlId)) {
56
+ return { ok: false, message: "controlId must be unique and non-empty" };
57
+ }
58
+ seen.add(control.controlId);
59
+ if (control.role === "slider") {
60
+ if (![control.min, control.max, control.value].every(Number.isFinite)) {
61
+ return { ok: false, message: "slider descriptor values must be finite" };
62
+ }
63
+ if (control.min > control.value || control.value > control.max) {
64
+ return { ok: false, message: "slider descriptor value is out of range" };
65
+ }
66
+ }
67
+ }
68
+ return { ok: true };
69
+ }
@@ -1,4 +1,4 @@
1
- import type { NavigatorEnvVersion, NavigatorOpenType, NavigatorTarget } from "./navigator.js";
1
+ import type { NavigatorChannel, NavigatorOpenType, NavigatorTarget } from "./navigator.js";
2
2
  import type { NavigatorQuery } from "./navigator.js";
3
3
  import type { LxMediaSwiperItem } from "./media_swiper.js";
4
4
  import type { LxVideoQuality } from "./video.js";
@@ -12,7 +12,7 @@ export interface NavigatorNativeAttrOptions {
12
12
  delta?: number;
13
13
  appId?: string;
14
14
  query?: NavigatorQuery;
15
- envVersion?: NavigatorEnvVersion;
15
+ channel?: NavigatorChannel;
16
16
  targetVersion?: string;
17
17
  phoneNumber?: string;
18
18
  hoverClass?: string;
@@ -61,6 +61,7 @@ export declare const VIDEO_DOM_EVENT_MAP: {
61
61
  readonly onWaiting: "waiting";
62
62
  readonly onQualityChange: "qualitychange";
63
63
  readonly onRateChange: "ratechange";
64
+ readonly onVolumeChange: "volumechange";
64
65
  };
65
66
  export interface VideoNativeAttrOptions {
66
67
  id?: string;
@@ -43,8 +43,8 @@ export function buildNavigatorNativeAttrs(options, extraAttrs = {}) {
43
43
  result["app-id"] = options.appId;
44
44
  if (options.query)
45
45
  result.query = JSON.stringify(options.query);
46
- if (options.envVersion)
47
- result["env-version"] = options.envVersion;
46
+ if (options.channel)
47
+ result.channel = options.channel;
48
48
  if (options.targetVersion)
49
49
  result["target-version"] = options.targetVersion;
50
50
  if (options.phoneNumber)
@@ -164,6 +164,7 @@ export const VIDEO_DOM_EVENT_MAP = {
164
164
  onWaiting: "waiting",
165
165
  onQualityChange: "qualitychange",
166
166
  onRateChange: "ratechange",
167
+ onVolumeChange: "volumechange",
167
168
  };
168
169
  export function buildVideoNativeAttrs(options, extraAttrs = {}) {
169
170
  var _a, _b;
@@ -181,7 +182,9 @@ export function buildVideoNativeAttrs(options, extraAttrs = {}) {
181
182
  result.loop = "";
182
183
  if (options.muted)
183
184
  result.muted = "";
184
- if (options.controls)
185
+ if (options.controls === false)
186
+ result.controls = "false";
187
+ else if (options.controls)
185
188
  result.controls = "";
186
189
  if (options.progressBar === false)
187
190
  result["progress-bar"] = "false";
@@ -1,6 +1,6 @@
1
1
  import type { NativeComponentMessage } from './types.js';
2
2
  export type { NativeComponentMessage } from './types.js';
3
- export declare function sendNativeComponentMessage(message: NativeComponentMessage): void;
3
+ export declare function sendNativeComponentMessage(message: NativeComponentMessage): boolean;
4
4
  export declare function registerNativeComponentHandler(id: string, handler: (msg: NativeComponentMessage) => void): () => void;
5
5
  export declare function invalidateNativeComponentLayout(delays?: number[]): void;
6
6
  export declare function addNativeComponentLayoutInvalidationListener(listener: EventListenerOrEventListenerObject): () => void;
@@ -2,6 +2,8 @@ let warnedNoHandler = false;
2
2
  const NATIVE_COMPONENT_LAYOUT_INVALIDATED_EVENT = "lingxia:native-component-layout-invalidated";
3
3
  let pendingLayoutInvalidationFrame = null;
4
4
  const pendingLayoutInvalidationTimers = new Map();
5
+ /// Returns false when the native channel is not up yet and the message was
6
+ /// dropped, so callers that carry state (an island's first commit) can retry.
5
7
  export function sendNativeComponentMessage(message) {
6
8
  var _a, _b;
7
9
  const sender = typeof window !== "undefined"
@@ -12,29 +14,24 @@ export function sendNativeComponentMessage(message) {
12
14
  warnedNoHandler = true;
13
15
  console.warn("[LingXia NativeComponent] message handler not available");
14
16
  }
15
- return;
17
+ return false;
16
18
  }
17
19
  sender(message);
20
+ return true;
18
21
  }
19
22
  export function registerNativeComponentHandler(id, handler) {
20
- var _a, _b, _c, _d, _e;
23
+ var _a;
21
24
  const nativeComponents = typeof window !== "undefined"
22
25
  ? (_a = window.LingXiaBridge) === null || _a === void 0 ? void 0 : _a.nativeComponents
23
26
  : undefined;
24
27
  const registerFn = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.register;
25
28
  if (typeof registerFn === "function") {
26
29
  const unregister = registerFn(id, handler);
27
- const platform = (_b = window.LingXiaBridge) === null || _b === void 0 ? void 0 : _b.platform;
28
- const requiresReadyHandshake = !!(((_c = platform === null || platform === void 0 ? void 0 : platform.isIOS) === null || _c === void 0 ? void 0 : _c.call(platform)) ||
29
- ((_d = platform === null || platform === void 0 ? void 0 : platform.isAndroid) === null || _d === void 0 ? void 0 : _d.call(platform)) ||
30
- ((_e = platform === null || platform === void 0 ? void 0 : platform.isMacOS) === null || _e === void 0 ? void 0 : _e.call(platform)));
31
- const hasHandler = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.hasHandler;
32
30
  const send = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.send;
31
+ // Always announce ready after the leaf handler is registered, including
32
+ // Windows: island video queues play/playing until this id's handshake.
33
33
  if (typeof send === "function") {
34
- const nativeReady = typeof hasHandler === "function" ? hasHandler() : true;
35
- if (requiresReadyHandshake || nativeReady) {
36
- send({ action: "component.ready", id });
37
- }
34
+ send({ action: "component.ready", id });
38
35
  }
39
36
  return unregister;
40
37
  }
@@ -2,7 +2,7 @@ export type NavigatorOpenType = 'navigate' | 'redirect' | 'navigateBack' | 'reLa
2
2
  export type NavigatorTarget = 'self' | 'lxapp' | 'browser';
3
3
  export type NavigatorQueryValue = string | number | boolean | null | undefined;
4
4
  export type NavigatorQuery = Record<string, NavigatorQueryValue>;
5
- export type NavigatorEnvVersion = 'release' | 'preview' | 'developer';
5
+ export type NavigatorChannel = 'release' | 'preview' | 'draft';
6
6
  export interface LxNavigatorEventDetail {
7
7
  success?: boolean;
8
8
  errMsg?: string;
@@ -18,7 +18,7 @@ export type LxNavigatorAttributes = {
18
18
  target?: NavigatorTarget;
19
19
  delta?: number;
20
20
  'app-id'?: string;
21
- 'env-version'?: NavigatorEnvVersion;
21
+ channel?: NavigatorChannel;
22
22
  'target-version'?: string;
23
23
  'phone-number'?: string;
24
24
  'hover-class'?: string;
package/dist/navigator.js CHANGED
@@ -44,7 +44,7 @@ export class LxNavigatorElement extends HTMLElement {
44
44
  "target",
45
45
  "delta",
46
46
  "app-id",
47
- "env-version",
47
+ "channel",
48
48
  "target-version",
49
49
  "phone-number",
50
50
  "hover-class",
@@ -169,7 +169,7 @@ export class LxNavigatorElement extends HTMLElement {
169
169
  const explicitTarget = this.getAttribute('target');
170
170
  const delta = parseInt(this.getAttribute('delta') || '1', 10);
171
171
  const appId = this.getAttribute('app-id');
172
- const envVersion = this.getAttribute('env-version');
172
+ const channel = this.getAttribute('channel');
173
173
  const targetVersion = this.getAttribute('target-version');
174
174
  const phoneNumber = this.getAttribute('phone-number');
175
175
  // Auto-infer target if not explicitly specified
@@ -183,7 +183,7 @@ export class LxNavigatorElement extends HTMLElement {
183
183
  target,
184
184
  delta,
185
185
  appId,
186
- envVersion,
186
+ channel,
187
187
  targetVersion,
188
188
  phoneNumber
189
189
  });
@@ -381,8 +381,8 @@ export class LxNavigatorElement extends HTMLElement {
381
381
  const pageTarget = this.buildPageTarget(options);
382
382
  if (pageTarget)
383
383
  Object.assign(target, pageTarget);
384
- if (options.envVersion)
385
- target.envVersion = options.envVersion;
384
+ if (options.channel)
385
+ target.channel = options.channel;
386
386
  if (options.targetVersion)
387
387
  target.targetVersion = options.targetVersion;
388
388
  return target;
package/dist/picker.js CHANGED
@@ -1,7 +1,15 @@
1
1
  import { sendNativeComponentMessage, registerNativeComponentHandler, addNativeComponentLayoutInvalidationListener } from "./nativecomponent.js";
2
2
  import { ensureComponentId } from "./component.js";
3
3
  import { measureElement } from "./dom.js";
4
- import { isAndroid, isHarmony, isDesktop } from "./platform.js";
4
+ import { isAndroid, isHarmony, isIOS } from "./platform.js";
5
+ // Which hosts register a `picker.native` component. A capability question, not
6
+ // a form-factor one: macOS and Windows have no picker factory, so they draw the
7
+ // web picker even in the Runner's phone frame, where the host class is mobile
8
+ // but the machine underneath is still a Mac or a PC. Harmony has its own embed
9
+ // path and never reaches either branch.
10
+ function hasNativePicker() {
11
+ return isIOS() || isAndroid();
12
+ }
5
13
  const HARMONY_PROPS_PREFIX = "data:application/json,";
6
14
  // Component implementation
7
15
  export class LxPickerElement extends HTMLElement {
@@ -14,7 +22,7 @@ export class LxPickerElement extends HTMLElement {
14
22
  this._pageBindings = {};
15
23
  this.layoutInvalidationListener = {
16
24
  handleEvent: () => {
17
- if (!this.isConnected || isHarmony() || isDesktop())
25
+ if (!this.isConnected || isHarmony() || !hasNativePicker())
18
26
  return;
19
27
  void this.mountPicker();
20
28
  }
@@ -86,7 +94,7 @@ export class LxPickerElement extends HTMLElement {
86
94
  }
87
95
  (_a = this.removeLayoutInvalidationListener) === null || _a === void 0 ? void 0 : _a.call(this);
88
96
  this.removeLayoutInvalidationListener = undefined;
89
- if (this.componentId && !isHarmony() && !isDesktop()) {
97
+ if (this.componentId && !isHarmony() && hasNativePicker()) {
90
98
  sendNativeComponentMessage({
91
99
  action: "component.unmount",
92
100
  id: this.componentId
@@ -241,7 +249,7 @@ export class LxPickerElement extends HTMLElement {
241
249
  this.mounted = true;
242
250
  return;
243
251
  }
244
- if (isDesktop()) {
252
+ if (!hasNativePicker()) {
245
253
  if (this.webCleanup)
246
254
  return;
247
255
  const { renderWebPicker } = await import('./picker-web.js');
@@ -0,0 +1,2 @@
1
+ /** Keep platform spelling differences out of the public DOM/payload contract. */
2
+ export declare function normalizeVideoEventDetail(event: string, payload: unknown): Record<string, unknown>;
@@ -0,0 +1,18 @@
1
+ /** Keep platform spelling differences out of the public DOM/payload contract. */
2
+ export function normalizeVideoEventDetail(event, payload) {
3
+ var _a, _b, _c, _d, _e;
4
+ const detail = payload && typeof payload === "object" ? payload : {};
5
+ if (["playrequest", "play", "playing", "pause", "stop", "ended", "waiting"].includes(event))
6
+ return {};
7
+ if (event === "fullscreenchange") {
8
+ return { ...detail, fullscreen: (_b = (_a = detail.fullscreen) !== null && _a !== void 0 ? _a : detail.fullScreen) !== null && _b !== void 0 ? _b : false };
9
+ }
10
+ if (event === "error") {
11
+ return {
12
+ ...detail,
13
+ code: String((_c = detail.code) !== null && _c !== void 0 ? _c : "NATIVE_COMPONENT_COMMAND_FAILED"),
14
+ message: String((_e = (_d = detail.message) !== null && _d !== void 0 ? _d : detail.errMsg) !== null && _e !== void 0 ? _e : "Native video failed"),
15
+ };
16
+ }
17
+ return detail;
18
+ }