@lingxia/html 0.17.0 → 0.19.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.
@@ -9,9 +9,55 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
23
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
49
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
50
+ if (ar || !(i in from)) {
51
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
52
+ ar[i] = from[i];
53
+ }
54
+ }
55
+ return to.concat(ar || Array.prototype.slice.call(from));
56
+ };
12
57
  var LingXiaPage = (function (exports) {
58
+ var _a;
13
59
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
14
- //#region dist/__page_runtime_runtime__.js
60
+ //#region ../lingxia-page-runtime/dist/shared/runtime.js
15
61
  var snapshot = {};
16
62
  var stateInfo = {
17
63
  rev: -1,
@@ -19,7 +65,16 @@ var LingXiaPage = (function (exports) {
19
65
  };
20
66
  var subscribed = false;
21
67
  var subscribeRetryTimer = null;
68
+ var snapshotRetryTimer = null;
69
+ var snapshotRetries = 0;
70
+ /**
71
+ * The host pushes a page's first state at bridge-ready, so the request is only
72
+ * a fallback: a few tries, backing off, then stop. A page with no Logic never
73
+ * answers, and must not be asked for the life of the page.
74
+ */
75
+ var MAX_SNAPSHOT_RETRIES = 3;
22
76
  var initialSnapshotResolved = false;
77
+ var actions = null;
23
78
  var snapshotRequestInFlight = false;
24
79
  var listeners = /* @__PURE__ */ new Set();
25
80
  function notifyListeners() {
@@ -30,8 +85,28 @@ var LingXiaPage = (function (exports) {
30
85
  catch (_a) { }
31
86
  });
32
87
  }
88
+ /**
89
+ * In a dev session the page data is frozen, so a View that writes to it fails
90
+ * at the write instead of drifting from Logic until the next update. Each
91
+ * push is a fresh copy, so nothing else holds these objects.
92
+ */
93
+ function isDevSession() {
94
+ var _a;
95
+ return typeof window !== "undefined" && ((_a = window.__LX_BRIDGE_CFG) === null || _a === void 0 ? void 0 : _a.dev) === true;
96
+ }
97
+ function deepFreeze(value) {
98
+ if (!value || typeof value !== "object" || Object.isFrozen(value))
99
+ return;
100
+ Object.freeze(value);
101
+ for (var _i = 0, _a = Object.keys(value); _i < _a.length; _i++) {
102
+ var key = _a[_i];
103
+ deepFreeze(value[key]);
104
+ }
105
+ }
33
106
  function updateSnapshot(next, info) {
34
107
  snapshot = next && typeof next === "object" ? next : {};
108
+ if (isDevSession())
109
+ deepFreeze(snapshot);
35
110
  stateInfo = info;
36
111
  notifyListeners();
37
112
  }
@@ -43,17 +118,35 @@ var LingXiaPage = (function (exports) {
43
118
  ensurePageBridgeSubscription();
44
119
  }, 10);
45
120
  }
121
+ /**
122
+ * Retry the snapshot request itself. The subscription is already in place, so
123
+ * retrying it (as this used to) returned at once and never asked again; the
124
+ * page then relied on the host's own push at bridge-ready.
125
+ */
126
+ function scheduleSnapshotRetry() {
127
+ if (snapshotRetryTimer !== null || snapshotRetries >= MAX_SNAPSHOT_RETRIES)
128
+ return;
129
+ snapshotRetries += 1;
130
+ snapshotRetryTimer = setTimeout(function () {
131
+ snapshotRetryTimer = null;
132
+ requestInitialSnapshot(window.LingXiaBridge);
133
+ }, 250 * Math.pow(2, (snapshotRetries - 1)));
134
+ }
46
135
  function requestInitialSnapshot(bridge) {
47
136
  var _a;
137
+ if (stateInfo.rev >= 0)
138
+ initialSnapshotResolved = true;
48
139
  if (initialSnapshotResolved || snapshotRequestInFlight)
49
140
  return;
50
- if (!((_a = bridge === null || bridge === void 0 ? void 0 : bridge.raw) === null || _a === void 0 ? void 0 : _a.call))
141
+ if (!((_a = bridge === null || bridge === void 0 ? void 0 : bridge.raw) === null || _a === void 0 ? void 0 : _a.call)) {
142
+ scheduleSnapshotRetry();
51
143
  return;
144
+ }
52
145
  snapshotRequestInFlight = true;
53
146
  bridge.raw.call("state.getSnapshot", { scope: "page" }).then(function () {
54
147
  initialSnapshotResolved = true;
55
148
  }).catch(function () {
56
- scheduleSubscribeRetry();
149
+ scheduleSnapshotRetry();
57
150
  }).finally(function () {
58
151
  snapshotRequestInFlight = false;
59
152
  });
@@ -81,41 +174,68 @@ var LingXiaPage = (function (exports) {
81
174
  listeners.delete(listener);
82
175
  };
83
176
  }
84
- function subscribePageData(callback) {
85
- if (typeof callback !== "function")
86
- return function () { };
177
+ /** Whether the page's first state has arrived. Flips once per document. */
178
+ function isPageReady() {
87
179
  ensurePageBridgeSubscription();
88
- if (stateInfo.rev >= 0)
89
- callback(snapshot, {
90
- rev: stateInfo.rev,
91
- initial: true
92
- });
93
- return subscribePageSnapshot(function () {
94
- callback(snapshot, stateInfo);
180
+ return stateInfo.rev >= 0;
181
+ }
182
+ /**
183
+ * Resolves once the page's first state has arrived — the host pushes it at
184
+ * bridge-ready, before `onLoad`, carrying `Page({ data })`'s defaults — so a
185
+ * page can mount with its data whole. With `timeoutMs`, rejects if Logic has
186
+ * not delivered it by then (it failed to load, or threw first); `null` waits
187
+ * without limit.
188
+ */
189
+ function whenPageReady(options) {
190
+ if (options === void 0) { options = {}; }
191
+ if (isPageReady())
192
+ return Promise.resolve();
193
+ var timeoutMs = options.timeoutMs === void 0 ? 1e4 : options.timeoutMs;
194
+ return new Promise(function (resolve, reject) {
195
+ var timer = null;
196
+ var check = function () {
197
+ if (stateInfo.rev < 0)
198
+ return;
199
+ if (timer !== null)
200
+ clearTimeout(timer);
201
+ listeners.delete(check);
202
+ resolve();
203
+ };
204
+ if (timeoutMs !== null)
205
+ timer = setTimeout(function () {
206
+ listeners.delete(check);
207
+ reject(/* @__PURE__ */ new Error("Page Logic did not deliver the page state within ".concat(timeoutMs, " ms")));
208
+ }, timeoutMs);
209
+ listeners.add(check);
95
210
  });
96
211
  }
97
212
  function getPageSnapshot() {
98
213
  ensurePageBridgeSubscription();
99
214
  return snapshot;
100
215
  }
216
+ /**
217
+ * The page's actions: one object for the whole page, so it is a stable
218
+ * dependency. Built on first use — the page's action names arrive with its
219
+ * bridge metadata, after the page module has evaluated.
220
+ */
101
221
  function getPageActions() {
102
- var actions = {};
222
+ if (actions)
223
+ return actions;
103
224
  var bridge = window.__pageBridge;
104
225
  if (!(bridge === null || bridge === void 0 ? void 0 : bridge.__names))
105
- return actions;
226
+ throw new Error("Page actions are not ready. Read useLxPage() during component setup/render, or getPage() after pageReady(); pass actions into shared helpers.");
227
+ var built = {};
106
228
  for (var _i = 0, _a = bridge.__names; _i < _a.length; _i++) {
107
229
  var name = _a[_i];
108
230
  if (typeof name !== "string")
109
231
  continue;
110
232
  var fn = getOrCreatePageAction(bridge, name);
111
233
  if (typeof fn === "function")
112
- actions[name] = fn;
234
+ built[name] = fn;
113
235
  }
236
+ actions = built;
114
237
  return actions;
115
238
  }
116
- function getPageStateInfo() {
117
- return stateInfo;
118
- }
119
239
  function getOrCreatePageAction(bridge, name) {
120
240
  var existing = bridge[name];
121
241
  if (typeof existing === "function")
@@ -127,7 +247,9 @@ var LingXiaPage = (function (exports) {
127
247
  function resolvePageActionMode(bridge, name) {
128
248
  var _a;
129
249
  var mode = (_a = bridge.__modes) === null || _a === void 0 ? void 0 : _a[name];
130
- return mode === "call" || mode === "stream" ? mode : "notify";
250
+ if (mode === "call" || mode === "stream")
251
+ return mode;
252
+ throw new Error("Invalid bridge mode for page action '".concat(name, "'; regenerate the page with the current CLI"));
131
253
  }
132
254
  function definePageBridgeAction(name, mode) {
133
255
  function action() {
@@ -148,14 +270,14 @@ var LingXiaPage = (function (exports) {
148
270
  return handle;
149
271
  }
150
272
  if (mode === "call") {
151
- var promise = bridge.raw.call(name, payload);
273
+ var promise = callUnaryPageAction(bridge, name, payload);
152
274
  if (promise && typeof promise.catch === "function")
153
275
  promise.catch(function (err) {
154
276
  console.warn("[PageFunc] ".concat(name, " failed:"), err instanceof Error ? err.message : err);
155
277
  });
156
278
  return promise;
157
279
  }
158
- bridge.raw.notify(name, payload);
280
+ throw new Error("Invalid bridge mode for page action '".concat(name, "'"));
159
281
  }
160
282
  Object.assign(action, {
161
283
  __logicFunc: true,
@@ -164,35 +286,463 @@ var LingXiaPage = (function (exports) {
164
286
  });
165
287
  return action;
166
288
  }
289
+ /**
290
+ * The one wire path of a unary action. It has no bridge deadline: an action
291
+ * may wait for a person or poll for minutes, and settles when Logic settles
292
+ * or the document goes away.
293
+ */
294
+ function callUnaryPageAction(bridge_1, name_1, payload_1) {
295
+ return __awaiter(this, arguments, void 0, function (bridge, name, payload, dropWhenGone) {
296
+ var error_1;
297
+ if (dropWhenGone === void 0) { dropWhenGone = true; }
298
+ return __generator(this, function (_a) {
299
+ switch (_a.label) {
300
+ case 0:
301
+ if (!!isPageReady()) return [3 /*break*/, 2];
302
+ return [4 /*yield*/, whenPageReady({ timeoutMs: null })];
303
+ case 1:
304
+ _a.sent();
305
+ _a.label = 2;
306
+ case 2:
307
+ _a.trys.push([2, 4, , 5]);
308
+ return [4 /*yield*/, bridge.raw.call(name, payload, { timeoutMs: 0 })];
309
+ case 3: return [2 /*return*/, _a.sent()];
310
+ case 4:
311
+ error_1 = _a.sent();
312
+ if (dropWhenGone && (error_1 === null || error_1 === void 0 ? void 0 : error_1.code) === "BRIDGE_NOT_READY")
313
+ return [2 /*return*/, new Promise(function () { })];
314
+ throw error_1;
315
+ case 5: return [2 /*return*/];
316
+ }
317
+ });
318
+ });
319
+ }
320
+ /**
321
+ * A DOM or framework event, in this realm or another. A JSON payload never
322
+ * carries methods, so event methods tell the two apart.
323
+ */
324
+ function isEventLike(value) {
325
+ if (typeof Event !== "undefined" && value instanceof Event)
326
+ return true;
327
+ if (!value || typeof value !== "object")
328
+ return false;
329
+ var event = value;
330
+ return typeof event.preventDefault === "function" && typeof event.stopPropagation === "function";
331
+ }
332
+ var PAGE_ACTIONS_NOT_READY = "PAGE_ACTIONS_NOT_READY";
333
+ function pageActionError(code, message) {
334
+ return Object.assign(new Error(message), { code: code });
335
+ }
336
+ /**
337
+ * Automation entry (`PageDriver.action`): invoke one unary action of this
338
+ * page with a JSON payload, as the View would. The payload is passed as is —
339
+ * no DOM-event repackaging.
340
+ */
341
+ function invokePageActionForAutomation(name, payload) {
342
+ var _a;
343
+ if (typeof name !== "string" || name === "")
344
+ return Promise.reject(pageActionError("BRIDGE_MALFORMED_MESSAGE", "Page action name must be a non-empty string"));
345
+ var metadata = window.__pageBridge;
346
+ var bridge = window.LingXiaBridge;
347
+ if (!Array.isArray(metadata === null || metadata === void 0 ? void 0 : metadata.__names) || !(bridge === null || bridge === void 0 ? void 0 : bridge.raw))
348
+ return Promise.reject(pageActionError(PAGE_ACTIONS_NOT_READY, "Page actions are not ready yet"));
349
+ if (!metadata.__names.includes(name)) {
350
+ var known = metadata.__names.length ? metadata.__names.join(", ") : "none";
351
+ return Promise.reject(pageActionError("BRIDGE_METHOD_NOT_FOUND", "'".concat(name, "' is not an action of this page (actions: ").concat(known, ")")));
352
+ }
353
+ if (((_a = metadata.__modes) === null || _a === void 0 ? void 0 : _a[name]) !== "call")
354
+ return Promise.reject(pageActionError("PAGE_ACTION_NOT_UNARY", "Page action '".concat(name, "' is a stream action; only unary actions can be invoked")));
355
+ return callUnaryPageAction(bridge, name, payload, false);
356
+ }
357
+ if (typeof window !== "undefined")
358
+ Object.defineProperty(window, "__lxInvokePageAction", {
359
+ value: invokePageActionForAutomation,
360
+ configurable: true
361
+ });
167
362
  function filterPayload(name, args) {
168
363
  var clean = [];
169
364
  for (var _i = 0, args_1 = args; _i < args_1.length; _i++) {
170
365
  var value = args_1[_i];
171
- if (typeof CustomEvent !== "undefined" && value instanceof CustomEvent) {
172
- clean.push({
173
- type: value.type,
174
- detail: value.detail
175
- });
176
- continue;
177
- }
178
- var maybeEvent = value;
179
- if (maybeEvent && typeof maybeEvent === "object" && typeof maybeEvent.type === "string" && "detail" in maybeEvent) {
180
- clean.push({
181
- type: maybeEvent.type,
182
- detail: maybeEvent.detail
183
- });
366
+ if (isEventLike(value)) {
367
+ var event = value;
368
+ if (typeof event.type === "string" && "detail" in event)
369
+ clean.push({
370
+ type: event.type,
371
+ detail: event.detail
372
+ });
184
373
  continue;
185
374
  }
186
- if (value instanceof Event)
187
- continue;
188
- if (value && typeof value === "object" && "stopPropagation" in value && typeof value.stopPropagation === "function")
189
- continue;
190
375
  clean.push(value);
191
376
  }
192
377
  if (clean.length > 1)
193
378
  throw new Error("Page action '".concat(name, "' accepts at most one payload argument"));
194
379
  return clean[0];
195
380
  }
381
+ //#endregion
382
+ //#region ../lingxia-bridge/dist/es2020/error.js
383
+ var ERROR_STYLES = "\n* { margin: 0; padding: 0; box-sizing: border-box; }\nbody {\n font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n background: #f5f5f7;\n color: #1d1d1f;\n padding: 20px;\n -webkit-font-smoothing: antialiased;\n}\n.lx-error {\n text-align: center;\n max-width: 400px;\n}\n.lx-error-code {\n font-size: 120px;\n font-weight: 700;\n color: #d1d1d6;\n line-height: 1;\n letter-spacing: -4px;\n}\n.lx-error-title {\n font-size: 21px;\n font-weight: 600;\n margin: 16px 0 8px;\n color: #1d1d1f;\n}\n.lx-error-desc {\n font-size: 15px;\n color: #86868b;\n line-height: 1.5;\n}\n.lx-error-path {\n display: inline-block;\n margin-top: 20px;\n padding: 10px 16px;\n background: rgba(0,0,0,0.04);\n border-radius: 8px;\n font-family: 'SF Mono', ui-monospace, Menlo, Monaco, monospace;\n font-size: 13px;\n color: #1d1d1f;\n word-break: break-all;\n max-width: 100%;\n}\n@media (prefers-color-scheme: dark) {\n body { background: #000; color: #f5f5f7; }\n .lx-error-code { color: #3a3a3c; }\n .lx-error-title { color: #f5f5f7; }\n .lx-error-desc { color: #86868b; }\n .lx-error-path { background: rgba(255,255,255,0.08); color: #f5f5f7; }\n}\n";
384
+ function escapeHtml(str) {
385
+ var div = document.createElement("div");
386
+ div.textContent = str;
387
+ return div.innerHTML;
388
+ }
389
+ /**
390
+ * A page whose Logic has not delivered its first state in time — it failed to
391
+ * load, threw before the page could start, or is very slow. Shown over the page
392
+ * instead of a blank screen, naming the page; returns a function that removes
393
+ * it, for when the state does arrive after all.
394
+ */
395
+ function renderPageFault(pagePath, reason) {
396
+ var styleEl = document.createElement("style");
397
+ styleEl.textContent = "\n.lx-page-fault { position: fixed; inset: 0; z-index: 2147483647; overflow: auto;\n background: #fff; color: #1d1d1f; }\n@media (prefers-color-scheme: dark) { .lx-page-fault { background: #000; color: #f5f5f7; } }\n".concat(ERROR_STYLES.replace(/\bbody\b/g, ".lx-page-fault"));
398
+ document.head.appendChild(styleEl);
399
+ var overlay = document.createElement("div");
400
+ overlay.className = "lx-page-fault";
401
+ overlay.setAttribute("role", "alert");
402
+ var container = document.createElement("div");
403
+ container.className = "lx-error";
404
+ var html = "<div class=\"lx-error-code\">Page</div>";
405
+ html += "<h1 class=\"lx-error-title\">".concat(escapeHtml("This page couldn't start"), "</h1>");
406
+ html += "<p class=\"lx-error-desc\">".concat(escapeHtml("Its Logic has not delivered the page state. Check the Logic log for an error."), "</p>");
407
+ if (pagePath)
408
+ html += "<div class=\"lx-error-path\">".concat(escapeHtml(pagePath), "</div>");
409
+ html += "<p class=\"lx-error-desc\" style=\"margin-top:12px\">".concat(escapeHtml(reason), "</p>");
410
+ container.innerHTML = html;
411
+ overlay.appendChild(container);
412
+ document.body.appendChild(overlay);
413
+ return function () {
414
+ overlay.remove();
415
+ styleEl.remove();
416
+ };
417
+ }
418
+ //#endregion
419
+ //#region ../lingxia-bridge/dist/es2020/runtime-env.js
420
+ var BRIDGE_CONFIG = typeof window !== "undefined" && window.__LX_BRIDGE_CFG || {};
421
+ /**
422
+ * One store per document, deliberately on `window`.
423
+ *
424
+ * This module is present twice in a page: once as the global bridge runtime
425
+ * the host injects, once bundled into the page's own JS. Module-local state
426
+ * would give each copy its own value — the host would push the change into
427
+ * whichever copy won the race to install the hook, and the other, which is the
428
+ * one the framework hooks read, would answer with the boot value forever.
429
+ */
430
+ var fallbackStore$1 = {
431
+ value: ((_a = BRIDGE_CONFIG.displayLanguage) === null || _a === void 0 ? void 0 : _a.trim()) || "en-US",
432
+ listeners: /* @__PURE__ */ new Set()
433
+ };
434
+ function store$1() {
435
+ var _a;
436
+ if (typeof window === "undefined")
437
+ return fallbackStore$1;
438
+ if (!window.__lxDisplayLanguage)
439
+ window.__lxDisplayLanguage = {
440
+ value: ((_a = BRIDGE_CONFIG.displayLanguage) === null || _a === void 0 ? void 0 : _a.trim()) || "en-US",
441
+ listeners: /* @__PURE__ */ new Set()
442
+ };
443
+ return window.__lxDisplayLanguage;
444
+ }
445
+ /** Primary subtags of languages written right to left, for engines without `Intl.Locale#getTextInfo`. */
446
+ var RTL_LANGUAGES = /* @__PURE__ */ new Set([
447
+ "ar",
448
+ "ckb",
449
+ "dv",
450
+ "fa",
451
+ "he",
452
+ "ps",
453
+ "sd",
454
+ "ug",
455
+ "ur",
456
+ "yi"
457
+ ]);
458
+ /** The writing direction of a BCP-47 tag. */
459
+ function textDirection(tag) {
460
+ try {
461
+ var locale = new Intl.Locale(tag);
462
+ var info = typeof locale.getTextInfo === "function" ? locale.getTextInfo() : locale.textInfo;
463
+ if ((info === null || info === void 0 ? void 0 : info.direction) === "rtl" || (info === null || info === void 0 ? void 0 : info.direction) === "ltr")
464
+ return info.direction;
465
+ }
466
+ catch (_a) { }
467
+ return RTL_LANGUAGES.has(tag.split(/[-_]/)[0].toLowerCase()) ? "rtl" : "ltr";
468
+ }
469
+ /**
470
+ * `lang` and `dir` on `<html>` follow the display language, so a page never
471
+ * sets either: CSS logical properties and `:dir()` just work.
472
+ */
473
+ function stampDocumentLanguage() {
474
+ if (typeof document !== "undefined" && document.documentElement) {
475
+ var language = store$1().value;
476
+ document.documentElement.lang = language;
477
+ document.documentElement.dir = textDirection(language);
478
+ }
479
+ }
480
+ stampDocumentLanguage();
481
+ /**
482
+ * Host entry point for a language the user changed while this document was
483
+ * open. Bootstrap alone would leave a live page in the language it started in,
484
+ * with the native chrome around it already switched.
485
+ *
486
+ * The host pushes each change once, and again — the current value — when a
487
+ * document's bridge reports ready, since a change made while it loaded found
488
+ * no hook to call. Those two can land in either order, so a push carries the
489
+ * host revision it took effect at and an older one never overwrites a newer
490
+ * one. A push without a revision applies as it always did.
491
+ */
492
+ function applyDisplayLanguage(next, revision) {
493
+ var _a;
494
+ var normalized = typeof next === "string" ? next.trim() : "";
495
+ var current = store$1();
496
+ if (typeof revision === "number" && Number.isFinite(revision)) {
497
+ if (revision <= ((_a = current.revision) !== null && _a !== void 0 ? _a : 0))
498
+ return;
499
+ current.revision = revision;
500
+ }
501
+ if (!normalized || normalized === current.value)
502
+ return;
503
+ current.value = normalized;
504
+ stampDocumentLanguage();
505
+ for (var _i = 0, _b = __spreadArray([], current.listeners, true); _i < _b.length; _i++) {
506
+ var listener = _b[_i];
507
+ listener(normalized);
508
+ }
509
+ }
510
+ if (typeof window !== "undefined" && !window.__lingxiaApplyDisplayLanguage)
511
+ Object.defineProperty(window, "__lingxiaApplyDisplayLanguage", {
512
+ configurable: false,
513
+ enumerable: false,
514
+ value: applyDisplayLanguage
515
+ });
516
+ function getDisplayLanguage() {
517
+ return store$1().value;
518
+ }
519
+ /**
520
+ * Subscribe to host display-language changes. Returns an unsubscribe.
521
+ *
522
+ * Change-only, so that this composes with `useSyncExternalStore`: the listener
523
+ * runs when the language changes, never on subscribe. Read the current value
524
+ * with `getDisplayLanguage()`. Logic's `lx.host.displayLanguage.watch` differs
525
+ * deliberately — it has no render loop to feed, so it delivers immediately.
526
+ */
527
+ function subscribeDisplayLanguage(listener) {
528
+ var listeners = store$1().listeners;
529
+ listeners.add(listener);
530
+ return function () {
531
+ listeners.delete(listener);
532
+ };
533
+ }
534
+ function getPlatformOS() {
535
+ return BRIDGE_CONFIG.os || "unknown";
536
+ }
537
+ /**
538
+ * Which kind of machine this is, mobile or desktop. Read through `isMobile()`
539
+ * and `isDesktop()`; the class itself is host vocabulary, not lxapp API.
540
+ *
541
+ * Fixed for the life of the document. A shipped host is one machine; the
542
+ * Runner re-serves the page when its simulated device changes class, so this
543
+ * never has to change under a page that is already rendering.
544
+ *
545
+ * Hosts from before this config key shipped send nothing, so fall back to the
546
+ * OS: every one of them is the machine it names.
547
+ */
548
+ function hostClass() {
549
+ if (BRIDGE_CONFIG.hostClass === "mobile" || BRIDGE_CONFIG.hostClass === "desktop")
550
+ return BRIDGE_CONFIG.hostClass;
551
+ return BRIDGE_CONFIG.os === "macOS" || BRIDGE_CONFIG.os === "Windows" ? "desktop" : "mobile";
552
+ }
553
+ function isDesktop() {
554
+ return hostClass() === "desktop";
555
+ }
556
+ //#endregion
557
+ //#region ../lingxia-bridge/dist/es2020/surface-context.js
558
+ /** What a host that sends nothing — one older than this API — is taken to say. */
559
+ var UNREPORTED = {
560
+ sizeClass: "compact",
561
+ width: 0,
562
+ height: 0,
563
+ aside: false
564
+ };
565
+ /**
566
+ * One store per document, on `window`, for the same reason as the display
567
+ * language: the bridge module is present twice in a page, and the host must
568
+ * reach the copy the framework hooks read.
569
+ */
570
+ function store() {
571
+ var _a;
572
+ if (typeof window === "undefined")
573
+ return fallbackStore;
574
+ if (!window.__lxSurfaceContext) {
575
+ var config = window.__LX_BRIDGE_CFG;
576
+ window.__lxSurfaceContext = {
577
+ value: (_a = parse(config === null || config === void 0 ? void 0 : config.surfaceContext)) !== null && _a !== void 0 ? _a : UNREPORTED,
578
+ revision: typeof (config === null || config === void 0 ? void 0 : config.surfaceContextRevision) === "number" ? config.surfaceContextRevision : 0,
579
+ listeners: /* @__PURE__ */ new Set()
580
+ };
581
+ }
582
+ return window.__lxSurfaceContext;
583
+ }
584
+ var fallbackStore = {
585
+ value: UNREPORTED,
586
+ revision: 0,
587
+ listeners: /* @__PURE__ */ new Set()
588
+ };
589
+ function parse(next) {
590
+ if (typeof next !== "object" || next === null)
591
+ return null;
592
+ var sizeClass = next.sizeClass, width = next.width, height = next.height, aside = next.aside;
593
+ if (sizeClass !== "compact" && sizeClass !== "regular")
594
+ return null;
595
+ if (typeof width !== "number" || typeof height !== "number")
596
+ return null;
597
+ return {
598
+ sizeClass: sizeClass,
599
+ width: width,
600
+ height: height,
601
+ aside: aside === true
602
+ };
603
+ }
604
+ function same$1(a, b) {
605
+ return a.sizeClass === b.sizeClass && a.width === b.width && a.height === b.height && a.aside === b.aside;
606
+ }
607
+ /**
608
+ * Host entry point: once the page's bridge is up, and on every change. Pushes
609
+ * can run out of order across host threads, so each carries a revision and an
610
+ * older one than the store holds is dropped.
611
+ */
612
+ function applySurfaceContext(next, revision) {
613
+ var context = parse(next);
614
+ var current = store();
615
+ if (!context)
616
+ return;
617
+ if (typeof revision === "number") {
618
+ if (revision <= current.revision)
619
+ return;
620
+ current.revision = revision;
621
+ }
622
+ if (same$1(current.value, context))
623
+ return;
624
+ current.value = context;
625
+ for (var _i = 0, _a = __spreadArray([], current.listeners, true); _i < _a.length; _i++) {
626
+ var listener = _a[_i];
627
+ listener();
628
+ }
629
+ }
630
+ if (typeof window !== "undefined" && !window.__lingxiaApplySurfaceContext)
631
+ Object.defineProperty(window, "__lingxiaApplySurfaceContext", {
632
+ configurable: false,
633
+ enumerable: false,
634
+ value: applySurfaceContext
635
+ });
636
+ /**
637
+ * The lxapp's adaptive context. The host seeds it into every page before any
638
+ * script runs and pushes each change, so there is always a value; a host older
639
+ * than this API reports `compact` with a 0×0 viewport.
640
+ */
641
+ function getSurfaceContext() {
642
+ return store().value;
643
+ }
644
+ /**
645
+ * Follow the adaptive context. Change-only, like `subscribeDisplayLanguage`,
646
+ * so it composes with `useSyncExternalStore`. Returns an unsubscribe.
647
+ */
648
+ function subscribeSurfaceContext(listener) {
649
+ var listeners = store().listeners;
650
+ listeners.add(listener);
651
+ return function () {
652
+ listeners.delete(listener);
653
+ };
654
+ }
655
+ //#endregion
656
+ //#region ../lingxia-bridge/dist/es2020/host.js
657
+ var current = null;
658
+ function read() {
659
+ var surface = getSurfaceContext();
660
+ return {
661
+ sizeClass: surface.sizeClass,
662
+ aside: surface.aside,
663
+ displayLanguage: getDisplayLanguage(),
664
+ formFactor: isDesktop() ? "desktop" : "mobile",
665
+ os: getPlatformOS(),
666
+ runner: BRIDGE_CONFIG.runner === true
667
+ };
668
+ }
669
+ function same(a, b) {
670
+ return a.sizeClass === b.sizeClass && a.aside === b.aside && a.displayLanguage === b.displayLanguage && a.formFactor === b.formFactor && a.os === b.os && a.runner === b.runner;
671
+ }
672
+ /**
673
+ * The host facts for this page. The same object is returned until a field
674
+ * changes, so it composes with `useSyncExternalStore` and with memoization.
675
+ */
676
+ function getHost() {
677
+ var next = read();
678
+ if (current === null || !same(current, next))
679
+ current = next;
680
+ return current;
681
+ }
682
+ /**
683
+ * Follow the host facts. Change-only: the listener runs when a field of
684
+ * `getHost()` changes — not on subscribe, and not when only the viewport's
685
+ * size moves within its size class. Returns an unsubscribe.
686
+ */
687
+ function subscribeHost(listener) {
688
+ var last = getHost();
689
+ var check = function () {
690
+ var next = getHost();
691
+ if (next === last)
692
+ return;
693
+ last = next;
694
+ listener();
695
+ };
696
+ var stopLanguage = subscribeDisplayLanguage(check);
697
+ var stopSurface = subscribeSurfaceContext(check);
698
+ return function () {
699
+ stopLanguage();
700
+ stopSurface();
701
+ };
702
+ }
703
+ //#endregion
704
+ //#region ../lingxia-page-runtime/dist/shared/startup.js
705
+ var pending;
706
+ /** Mount with complete initial data; a delayed startup stays recoverable. */
707
+ function waitForPageState() {
708
+ if (isPageReady())
709
+ return Promise.resolve();
710
+ return pending !== null && pending !== void 0 ? pending : (pending = wait().finally(function () {
711
+ pending = void 0;
712
+ }));
713
+ }
714
+ function wait() {
715
+ return __awaiter(this, void 0, void 0, function () {
716
+ var delay, panel, timer;
717
+ var _a;
718
+ return __generator(this, function (_b) {
719
+ switch (_b.label) {
720
+ case 0:
721
+ delay = 1e4;
722
+ panel = {};
723
+ timer = setTimeout(function () {
724
+ var reason = "Page Logic has not delivered the page state after ".concat(delay, " ms");
725
+ console.error("[LingXia] ".concat(location.pathname, ": ").concat(reason));
726
+ panel.dismiss = renderPageFault(location.pathname, reason);
727
+ }, delay);
728
+ _b.label = 1;
729
+ case 1:
730
+ _b.trys.push([1, , 3, 4]);
731
+ return [4 /*yield*/, whenPageReady({ timeoutMs: null })];
732
+ case 2:
733
+ _b.sent();
734
+ return [3 /*break*/, 4];
735
+ case 3:
736
+ clearTimeout(timer);
737
+ (_a = panel.dismiss) === null || _a === void 0 ? void 0 : _a.call(panel);
738
+ return [7 /*endfinally*/];
739
+ case 4: return [2 /*return*/];
740
+ }
741
+ });
742
+ });
743
+ }
744
+ //#endregion
745
+ //#region ../lingxia-page-runtime/dist/page-chrome.js
196
746
  var initialLayout = Object.freeze({
197
747
  revision: 0,
198
748
  topInset: 0,
@@ -208,24 +758,18 @@ var LingXiaPage = (function (exports) {
208
758
  root === null || root === void 0 ? void 0 : root.style.setProperty("--lx-page-chrome-top-inset", "".concat(layout.topInset, "px"));
209
759
  root === null || root === void 0 ? void 0 : root.style.setProperty("--lx-page-chrome-bottom-inset", "".concat(layout.bottomInset, "px"));
210
760
  root === null || root === void 0 ? void 0 : root.style.setProperty("--lx-page-chrome-capsule-inline-end-inset", "".concat(layout.capsuleInlineEndInset, "px"));
211
- }
212
- /** Read the latest realized page-chrome layout synchronously. */
213
- function getPageChromeLayout() {
214
- var _a, _b;
215
- if (typeof window === "undefined")
216
- return initialLayout;
217
- return (_b = (_a = installPageChromeRuntime()) === null || _a === void 0 ? void 0 : _a.layout) !== null && _b !== void 0 ? _b : initialLayout;
218
- }
219
- /** Subscribe to realized page-chrome layout changes. */
220
- function subscribePageChromeLayout(listener) {
221
- if (typeof window === "undefined")
222
- return function () { };
223
- installPageChromeRuntime();
224
- var handleChange = function (event) {
225
- listener(event.detail);
226
- };
227
- window.addEventListener("lxpagechromechange", handleChange);
228
- return function () { return window.removeEventListener("lxpagechromechange", handleChange); };
761
+ var capsule = layout.capsuleRect;
762
+ for (var _i = 0, _a = [
763
+ "top",
764
+ "right",
765
+ "bottom",
766
+ "left",
767
+ "width",
768
+ "height"
769
+ ]; _i < _a.length; _i++) {
770
+ var edge = _a[_i];
771
+ root === null || root === void 0 ? void 0 : root.style.setProperty("--lx-page-chrome-capsule-".concat(edge), "".concat(capsule ? capsule[edge] : 0, "px"));
772
+ }
229
773
  }
230
774
  /** Ensure browser previews have the same synchronous contract as native pages. */
231
775
  function installPageChromeRuntime() {
@@ -264,12 +808,34 @@ var LingXiaPage = (function (exports) {
264
808
  }
265
809
  installPageChromeRuntime();
266
810
  //#endregion
267
- exports.getActions = getPageActions;
268
- exports.getPageChromeLayout = getPageChromeLayout;
269
- exports.getSnapshot = getPageSnapshot;
270
- exports.getStateInfo = getPageStateInfo;
271
- exports.subscribe = subscribePageData;
272
- exports.subscribePageChromeLayout = subscribePageChromeLayout;
273
- exports.subscribeSnapshot = subscribePageSnapshot;
811
+ //#region dist/page.js
812
+ /**
813
+ * Resolves once the first state arrives. By default a delayed startup shows
814
+ * a fault panel and keeps waiting, just like React/Vue. An explicit timeout
815
+ * rejects instead; `null` waits without the panel. Await before `getPage()`.
816
+ */
817
+ function pageReady(options) {
818
+ return (options === null || options === void 0 ? void 0 : options.timeoutMs) === void 0 ? waitForPageState() : whenPageReady(options);
819
+ }
820
+ /**
821
+ * This page's Logic state and actions — `this.data` and the page's methods.
822
+ * `data` is readonly: Logic owns it (in a dev session a write throws).
823
+ */
824
+ function getPage() {
825
+ return {
826
+ data: getPageSnapshot(),
827
+ actions: getPageActions()
828
+ };
829
+ }
830
+ /** Follow the page's state. Change-only; read it with `getPage()`. Returns an unsubscribe. */
831
+ function subscribePage(listener) {
832
+ return subscribePageSnapshot(listener);
833
+ }
834
+ //#endregion
835
+ exports.getHost = getHost;
836
+ exports.getPage = getPage;
837
+ exports.pageReady = pageReady;
838
+ exports.subscribeHost = subscribeHost;
839
+ exports.subscribePage = subscribePage;
274
840
  return exports;
275
841
  })({});