@glassly/engine 0.3.5-beta.710 → 0.3.5-beta.760

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 (119) hide show
  1. package/build/engine.d.ts +4 -1
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +5 -1
  4. package/build/engine.js.map +1 -1
  5. package/build/facades/ai.d.ts +3 -3
  6. package/build/facades/ai.d.ts.map +1 -1
  7. package/build/facades/ai.js +3 -3
  8. package/build/facades/ai.js.map +1 -1
  9. package/build/facades/glasses.d.ts +5 -1
  10. package/build/facades/glasses.d.ts.map +1 -1
  11. package/build/facades/glasses.js +5 -0
  12. package/build/facades/glasses.js.map +1 -1
  13. package/build/generated/releaseMetadata.js +3 -3
  14. package/build/generated/releaseMetadata.js.map +1 -1
  15. package/build/services/GestureDotsService.d.ts.map +1 -1
  16. package/build/services/GestureDotsService.js +2 -1
  17. package/build/services/GestureDotsService.js.map +1 -1
  18. package/build/services/GlassesTouchInput.d.ts +6 -4
  19. package/build/services/GlassesTouchInput.d.ts.map +1 -1
  20. package/build/services/GlassesTouchInput.js +76 -37
  21. package/build/services/GlassesTouchInput.js.map +1 -1
  22. package/build/services/LocalDisplayManager.d.ts +12 -4
  23. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  24. package/build/services/LocalDisplayManager.js +21 -7
  25. package/build/services/LocalDisplayManager.js.map +1 -1
  26. package/build/services/LocalLlmModelManager.d.ts +3 -1
  27. package/build/services/LocalLlmModelManager.d.ts.map +1 -1
  28. package/build/services/LocalLlmModelManager.js +13 -5
  29. package/build/services/LocalLlmModelManager.js.map +1 -1
  30. package/build/services/LocalLlmPrompt.d.ts +10 -9
  31. package/build/services/LocalLlmPrompt.d.ts.map +1 -1
  32. package/build/services/LocalLlmPrompt.js +43 -32
  33. package/build/services/LocalLlmPrompt.js.map +1 -1
  34. package/build/services/LocalLlmProvider.d.ts.map +1 -1
  35. package/build/services/LocalLlmProvider.js +39 -20
  36. package/build/services/LocalLlmProvider.js.map +1 -1
  37. package/build/services/LocalMiniappRuntime.d.ts +22 -4
  38. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  39. package/build/services/LocalMiniappRuntime.js +108 -134
  40. package/build/services/LocalMiniappRuntime.js.map +1 -1
  41. package/build/services/MiniappExitGesture.d.ts +6 -3
  42. package/build/services/MiniappExitGesture.d.ts.map +1 -1
  43. package/build/services/MiniappExitGesture.js +4 -4
  44. package/build/services/MiniappExitGesture.js.map +1 -1
  45. package/build/services/MiniappWidgetMode.d.ts +6 -4
  46. package/build/services/MiniappWidgetMode.d.ts.map +1 -1
  47. package/build/services/MiniappWidgetMode.js +7 -6
  48. package/build/services/MiniappWidgetMode.js.map +1 -1
  49. package/build/services/SceneRenderer.d.ts.map +1 -1
  50. package/build/services/SceneRenderer.js +3 -1
  51. package/build/services/SceneRenderer.js.map +1 -1
  52. package/build/services/actionInvoke.d.ts +37 -0
  53. package/build/services/actionInvoke.d.ts.map +1 -0
  54. package/build/services/actionInvoke.js +89 -0
  55. package/build/services/actionInvoke.js.map +1 -0
  56. package/build/services/appleWatch/AppleWatchLink.d.ts +42 -0
  57. package/build/services/appleWatch/AppleWatchLink.d.ts.map +1 -0
  58. package/build/services/appleWatch/AppleWatchLink.js +259 -0
  59. package/build/services/appleWatch/AppleWatchLink.js.map +1 -0
  60. package/build/services/controllers.d.ts.map +1 -1
  61. package/build/services/controllers.js +16 -0
  62. package/build/services/controllers.js.map +1 -1
  63. package/build/services/settingsBridge.d.ts +4 -4
  64. package/build/services/settingsBridge.d.ts.map +1 -1
  65. package/build/services/settingsBridge.js +9 -4
  66. package/build/services/settingsBridge.js.map +1 -1
  67. package/build/stores/apps.d.ts.map +1 -1
  68. package/build/stores/apps.js +13 -11
  69. package/build/stores/apps.js.map +1 -1
  70. package/build/stores/bluetoothSettingKeys.d.ts +2 -0
  71. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  72. package/build/stores/bluetoothSettingKeys.js +17 -0
  73. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  74. package/build/stores/settings.d.ts.map +1 -1
  75. package/build/stores/settings.js +32 -2
  76. package/build/stores/settings.js.map +1 -1
  77. package/build/types/capabilities/rayneo-io.d.ts.map +1 -1
  78. package/build/types/capabilities/rayneo-io.js +2 -0
  79. package/build/types/capabilities/rayneo-io.js.map +1 -1
  80. package/build/types/enums.d.ts +2 -1
  81. package/build/types/enums.d.ts.map +1 -1
  82. package/build/types/enums.js +1 -0
  83. package/build/types/enums.js.map +1 -1
  84. package/build/utils/display/scene/index.d.ts +1 -1
  85. package/build/utils/display/scene/index.d.ts.map +1 -1
  86. package/build/utils/display/scene/index.js.map +1 -1
  87. package/build/utils/display/scene/process.d.ts.map +1 -1
  88. package/build/utils/display/scene/process.js +20 -1
  89. package/build/utils/display/scene/process.js.map +1 -1
  90. package/build/utils/display/scene/types.d.ts +1 -1
  91. package/build/utils/display/scene/types.d.ts.map +1 -1
  92. package/build/utils/display/scene/types.js.map +1 -1
  93. package/package.json +7 -7
  94. package/src/engine.ts +5 -1
  95. package/src/facades/ai.ts +3 -3
  96. package/src/facades/glasses.ts +7 -0
  97. package/src/generated/releaseMetadata.ts +3 -3
  98. package/src/services/GestureDotsService.ts +2 -1
  99. package/src/services/GlassesTouchInput.ts +81 -34
  100. package/src/services/LocalDisplayManager.ts +22 -8
  101. package/src/services/LocalLlmModelManager.ts +16 -6
  102. package/src/services/LocalLlmPrompt.ts +56 -37
  103. package/src/services/LocalLlmProvider.ts +49 -22
  104. package/src/services/LocalMiniappRuntime.ts +140 -134
  105. package/src/services/MiniappExitGesture.ts +8 -5
  106. package/src/services/MiniappWidgetMode.ts +9 -7
  107. package/src/services/SceneRenderer.ts +3 -1
  108. package/src/services/actionInvoke.ts +105 -0
  109. package/src/services/appleWatch/AppleWatchLink.ts +297 -0
  110. package/src/services/controllers.ts +17 -0
  111. package/src/services/settingsBridge.ts +9 -4
  112. package/src/stores/apps.ts +12 -10
  113. package/src/stores/bluetoothSettingKeys.ts +20 -0
  114. package/src/stores/settings.ts +34 -2
  115. package/src/types/capabilities/rayneo-io.ts +2 -0
  116. package/src/types/enums.ts +1 -0
  117. package/src/utils/display/scene/index.ts +1 -0
  118. package/src/utils/display/scene/process.ts +17 -1
  119. package/src/utils/display/scene/types.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/scene/process.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAGV,wBAAwB,EACxB,iBAAiB,EAIlB,MAAM,SAAS,CAAA;AAGhB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAsDD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,iBAAiB,EAAE,EACnC,IAAI,EAAE,wBAAwB,EAC9B,OAAO,EAAE,cAAc,GACtB,cAAc,CAgThB"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/scene/process.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAGV,wBAAwB,EACxB,iBAAiB,EAKlB,MAAM,SAAS,CAAA;AAGhB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAmED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,iBAAiB,EAAE,EACnC,IAAI,EAAE,wBAAwB,EAC9B,OAAO,EAAE,cAAc,GACtB,cAAc,CAkThB"}
@@ -45,13 +45,29 @@ function pointsBox(points) {
45
45
  const y = Math.min(...ys);
46
46
  return { x, y, w: Math.max(...xs) - x + 1, h: Math.max(...ys) - y + 1 };
47
47
  }
48
+ function validEdge(e) {
49
+ if (!e || typeof e !== "object")
50
+ return undefined;
51
+ const dx = Math.round(Number(e.dx ?? 0));
52
+ const dy = Math.round(Number(e.dy ?? 0));
53
+ if (!isFinite(dx) || !isFinite(dy) || Math.abs(dx) > 1024 || Math.abs(dy) > 1024)
54
+ return undefined;
55
+ return { dx, dy };
56
+ }
48
57
  function validTransition(t) {
49
58
  if (!t || typeof t !== "object")
50
59
  return undefined;
51
60
  const { durationMs, easing } = t;
52
61
  if (!isFinite(durationMs) || durationMs <= 0)
53
62
  return undefined;
54
- return easing ? { durationMs, easing } : { durationMs };
63
+ const out = easing ? { durationMs, easing } : { durationMs };
64
+ const enter = validEdge(t.enter);
65
+ const exit = validEdge(t.exit);
66
+ if (enter)
67
+ out.enter = enter;
68
+ if (exit)
69
+ out.exit = exit;
70
+ return out;
55
71
  }
56
72
  function boxShrunk(orig, clamped) {
57
73
  return (clamped.x !== Math.floor(orig.x) ||
@@ -143,6 +159,9 @@ export function processScene(input, caps, profile) {
143
159
  let pathEdges = 0;
144
160
  for (const { el, index, box, points } of valid) {
145
161
  const transition = validTransition(el.transition);
162
+ // Leaving elements only exist to tween away; a device that jumps never shows them.
163
+ if (transition?.exit && !caps.animation)
164
+ continue;
146
165
  const transform = {};
147
166
  if (el.rotation !== undefined || el.pivot !== undefined) {
148
167
  const angle = el.rotation ?? 0;
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sourceRoot":"","sources":["../../../../src/utils/display/scene/process.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scene processing — validate → clamp → budget → wrap. Everything here is\n * generic; device variation enters only through the capabilities block and the\n * display profile (data, not code). Spec §5.\n *\n * Never rejects a frame: offending elements are dropped per-element and\n * reported via `dropped` / `degraded` (design doc §3.4.6; spec §4 refines the\n * design doc's image \"reject\" to per-element drop).\n */\n\nimport {compileSvgPath} from \"./svgPath\"\nimport {TextMeasurer} from \"../measurer/TextMeasurer\"\nimport {TextWrapper} from \"../wrapper/TextWrapper\"\nimport type {DisplayProfile} from \"../profiles/types\"\nimport type {DiffableElement} from \"./differ\"\nimport type {\n SceneBox,\n SceneBoxInput,\n SceneDisplayCapabilities,\n SceneElementInput,\n ScenePoint,\n SceneTextStyle,\n SceneTransition,\n} from \"./types\"\nimport {MAX_SCENE_DEPTH, POINT_SHAPE_ARITY, POINT_SHAPE_TYPES, elementContentHash} from \"./types\"\n\nexport interface ProcessedScene {\n elements: DiffableElement[]\n degraded: boolean\n dropped: string[]\n}\n\n/** Reporting id for an element the app may not have named. */\nfunction reportId(el: SceneElementInput, index: number): string {\n return el.id ?? `${el.type}[${index}]`\n}\n\nfunction clampBox(box: SceneBox, width: number, height: number): SceneBox | null {\n const x1 = Math.max(0, Math.floor(box.x))\n const y1 = Math.max(0, Math.floor(box.y))\n const x2 = Math.min(width, Math.floor(box.x) + Math.max(0, Math.floor(box.w)))\n const y2 = Math.min(height, Math.floor(box.y) + Math.max(0, Math.floor(box.h)))\n if (x2 <= x1 || y2 <= y1) return null\n return {x: x1, y: y1, w: x2 - x1, h: y2 - y1}\n}\n\nconst isFinite = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n)\n\n/** Integer points, or null when the count/values are invalid for the type. */\nfunction validPoints(el: SceneElementInput): ScenePoint[] | null {\n const raw = (el as {points?: unknown}).points\n if (!Array.isArray(raw) || !(POINT_SHAPE_ARITY[el.type] ?? []).includes(raw.length)) return null\n const points: ScenePoint[] = []\n for (const p of raw) {\n if (!p || typeof p !== \"object\" || !isFinite((p as ScenePoint).x) || !isFinite((p as ScenePoint).y)) return null\n points.push({x: Math.floor((p as ScenePoint).x), y: Math.floor((p as ScenePoint).y)})\n }\n return points\n}\n\nfunction pointsBox(points: ScenePoint[]): SceneBox {\n const xs = points.map((p) => p.x)\n const ys = points.map((p) => p.y)\n const x = Math.min(...xs)\n const y = Math.min(...ys)\n return {x, y, w: Math.max(...xs) - x + 1, h: Math.max(...ys) - y + 1}\n}\n\nfunction validTransition(t: unknown): SceneTransition | undefined {\n if (!t || typeof t !== \"object\") return undefined\n const {durationMs, easing} = t as SceneTransition\n if (!isFinite(durationMs) || durationMs <= 0) return undefined\n return easing ? {durationMs, easing} : {durationMs}\n}\n\nfunction boxShrunk(orig: SceneBox, clamped: SceneBox): boolean {\n return (\n clamped.x !== Math.floor(orig.x) ||\n clamped.y !== Math.floor(orig.y) ||\n clamped.w !== Math.floor(orig.w) ||\n clamped.h !== Math.floor(orig.h)\n )\n}\n\n/**\n * Line height for box-height→line-count math — ONLY when the profile declares\n * a calibrated `lineHeightPx`. Deriving one from full-canvas numbers proved\n * wrong on hardware (288/8=36px clipped the G2 nav instruction to one line;\n * the real container line height is smaller). Without calibration we return\n * null and the wrap step skips host-side height clipping entirely — text wraps\n * to the box WIDTH and the firmware clips vertically in-box, which is exactly\n * the legacy behavior.\n */\nexport function profileLineHeightPx(profile: DisplayProfile, _canvasHeight: number): number | null {\n return profile.lineHeightPx ?? null\n}\n\n/**\n * Process a raw scene against a device's capabilities + profile. Output is\n * diff-ready (text pre-wrapped, boxes clamped, content hashed) and reflects\n * exactly what will be sent — the diff baseline is post-processed by design\n * (spec §4).\n */\nexport function processScene(\n input: readonly SceneElementInput[],\n caps: SceneDisplayCapabilities,\n profile: DisplayProfile,\n): ProcessedScene {\n const dropped: string[] = []\n let degraded = false\n\n const measurer = new TextMeasurer(profile)\n const wrapper = new TextWrapper(measurer)\n const lineHeight = profileLineHeightPx(profile, caps.height)\n\n // Validate + dedupe explicit ids (first occurrence wins; dupes are dev error).\n const seenIds = new Set<string>()\n const valid: {el: SceneElementInput; index: number; box: SceneBoxInput; points?: ScenePoint[]}[] = []\n input.forEach((raw, index) => {\n if (!raw || typeof raw !== \"object\") {\n dropped.push(reportId(raw ?? ({type: \"text\"} as SceneElementInput), index))\n degraded = true\n return\n }\n let points: ScenePoint[] | undefined\n if (POINT_SHAPE_TYPES.includes(raw.type)) {\n const valid = validPoints(raw)\n if (!valid) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n points = valid\n raw = {...raw, box: pointsBox(valid)} as SceneElementInput\n }\n if (!raw.box) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n // Non-finite box numbers (NaN/±Infinity/non-number) would survive the\n // Math.* clamp below and poison the frame all the way to native ints.\n const b = raw.box\n if (![b.x, b.y, b.w, b.h].every(isFinite)) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n if ((raw.type === \"arc\" || raw.type === \"pie\") && !(isFinite(raw.startDeg) && isFinite(raw.endDeg))) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n // Leading \"~\" is the differ's synthetic-id namespace — an app id there\n // would never match across frames (re-created every push). Normalize by\n // stripping; the rewrite is deterministic, so diffing stays stable.\n const el = raw.id?.startsWith(\"~\") ? {...raw, id: raw.id.replace(/^~+/, \"\") || undefined} : raw\n if (el.id) {\n const key = `${el.type}:${el.id}`\n if (seenIds.has(key)) {\n dropped.push(reportId(el, index))\n degraded = true\n return\n }\n seenIds.add(key)\n }\n valid.push({el, index, box: el.box as SceneBoxInput, points})\n })\n\n // Clamp + per-type limits, then budget in array order.\n let textBudget = caps.maxTextElements\n let imageBudget = caps.maxImageElements\n const out: DiffableElement[] = []\n let pathEdges = 0\n\n for (const {el, index, box, points} of valid) {\n const transition = validTransition((el as {transition?: unknown}).transition)\n const transform: {rotation?: number; pivot?: ScenePoint} = {}\n if (el.rotation !== undefined || el.pivot !== undefined) {\n const angle = el.rotation ?? 0\n const pivot = el.pivot ?? {x: box.x + box.w / 2, y: box.y + box.h / 2}\n if (\n !isFinite(angle) ||\n Math.abs(angle) > 36000 ||\n !isFinite(pivot.x) ||\n !isFinite(pivot.y) ||\n Math.abs(pivot.x) > 32767 ||\n Math.abs(pivot.y) > 32767 ||\n !caps.rotation ||\n el.type === \"image\" ||\n el.type === \"text\"\n ) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n transform.rotation = Math.round(angle * 256) / 256\n transform.pivot = {x: Math.round(pivot.x), y: Math.round(pivot.y)}\n if (transition) transition.durationMs = Math.min(65535, transition.durationMs)\n }\n const transformHash =\n transform.rotation === undefined ? undefined : `${transform.rotation}:${transform.pivot?.x}:${transform.pivot?.y}`\n // Depth is a lens disparity, not content: devices without it draw the\n // element flat instead of dropping it. A non-number is still a dev error.\n let depth: number | undefined\n if (el.depth !== undefined) {\n if (!isFinite(el.depth)) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n const d = Math.max(-MAX_SCENE_DEPTH, Math.min(MAX_SCENE_DEPTH, Math.round(el.depth)))\n if (caps.depth && d !== 0) depth = d\n }\n const plane = depth === undefined ? {} : {depth}\n const clamped = clampBox(box, caps.width, caps.height)\n if (!clamped) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n const shrunk = boxShrunk(box, clamped)\n if (shrunk) degraded = true\n\n if (el.type === \"svg\") {\n try {\n if (!caps.shapes.includes(\"svg\") || textBudget <= 0 || shrunk)\n throw new Error(\"SVG unavailable or out of bounds\")\n const scale = el.scale ?? 1\n if (\n !isFinite(scale) ||\n scale < 0 ||\n scale > 8 ||\n (el.fillRule !== undefined && el.fillRule !== \"evenodd\" && el.fillRule !== \"nonzero\")\n )\n throw new Error(\"Invalid SVG scale or fill rule\")\n const compiled = compileSvgPath(el.d)\n if (pathEdges + compiled.edges > 4096) throw new Error(\"SVG scene exceeds the edge budget\")\n pathEdges += compiled.edges\n textBudget--\n out.push({\n id: el.id,\n type: \"svg\",\n box: clamped,\n d: el.d,\n path: compiled.commands,\n pathScale: Math.round(scale * 256),\n fillRule: el.fillRule ?? \"nonzero\",\n style: el.style,\n ...transform,\n ...plane,\n ...(transition ? {transition} : {}),\n contentHash: elementContentHash({\n type: \"svg\",\n data: `${el.d}:${scale}:${el.fillRule}:${transformHash}`,\n style: el.style,\n depth,\n }),\n })\n } catch {\n dropped.push(reportId(el, index))\n degraded = true\n }\n continue\n }\n\n if (el.type !== \"text\" && el.type !== \"image\" && el.type !== \"rect\") {\n // Vector shapes: only devices that draw them get them; the device clips\n // point shapes itself, so an off-canvas point is not a drop.\n if (!caps.shapes.includes(el.type) || textBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n textBudget--\n const angles = el.type === \"arc\" || el.type === \"pie\" ? {startDeg: el.startDeg, endDeg: el.endDeg} : {}\n out.push({\n id: el.id,\n type: el.type,\n box: clamped,\n style: el.style,\n ...(points ? {points} : {}),\n ...angles,\n ...(transition ? {transition} : {}),\n ...transform,\n ...plane,\n contentHash: elementContentHash({\n type: el.type,\n style: el.style,\n points,\n ...angles,\n data: transformHash,\n depth,\n }),\n })\n continue\n }\n\n if (el.type === \"image\") {\n // Images: a shrunk box can't be honored (we don't crop pixels host-side)\n // and per-image device limits are box-level (the firmware allocates the\n // component from the box). SGCs scale pixels to the box (phone-side\n // scaling — never on glasses).\n if (shrunk) {\n dropped.push(reportId(el, index))\n continue\n }\n if (caps.maxImagePx && (clamped.w > caps.maxImagePx.width || clamped.h > caps.maxImagePx.height)) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n if (imageBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n imageBudget--\n out.push({\n id: el.id,\n type: \"image\",\n box: clamped,\n data: el.data,\n ...(el.style ? {style: el.style} : {}),\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"image\", data: el.data, style: el.style, depth}),\n })\n continue\n }\n\n // text + rect share the text-container budget (design doc §3.4.6).\n if (textBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n textBudget--\n\n if (el.type === \"rect\") {\n out.push({\n id: el.id,\n type: \"rect\",\n box: clamped,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...transform,\n ...plane,\n contentHash: elementContentHash({type: \"rect\", style: el.style, data: transformHash, depth}),\n })\n continue\n }\n\n // Text: wrap on the phone into the (clamped) box. The box then carries\n // pre-wrapped text; firmware in-box wrap is a fallback, not the mechanism.\n // Height clipping only applies with a CALIBRATED line height — otherwise\n // the firmware clips vertically in-box (legacy behavior).\n const style: SceneTextStyle = el.style ?? {}\n if (style.font === \"mono\" || style.font === \"builtin\") {\n // Grid text is laid out by the app (cols × rows) and builtin text by the\n // glasses' own font engine; the host's proportional wrap metrics don't\n // apply to either. Pass lines through and let the device clip.\n const monoText = el.text ?? \"\"\n out.push({\n id: el.id,\n type: \"text\",\n box: clamped,\n text: monoText,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"text\", text: monoText, style: el.style, depth}),\n })\n continue\n }\n const maxLines = lineHeight ? Math.max(1, Math.floor(clamped.h / lineHeight)) : profile.maxLines\n const result = wrapper.wrap(el.text ?? \"\", {\n maxWidthPx: clamped.w,\n maxLines,\n ...(style.breakMode ? {breakMode: style.breakMode} : {}),\n })\n let lines = result.lines\n if (result.truncated) {\n degraded = true\n if (style.overflow === \"ellipsis\" && lines.length > 0) {\n // Trim the last line until it fits WITH the ellipsis appended —\n // otherwise the extra glyph can overflow the clamped box width.\n let last = lines[lines.length - 1]\n while (last.length > 0 && !measurer.fitsInWidth(`${last}…`, clamped.w)) {\n last = last.slice(0, -1)\n }\n // A box narrower than the ellipsis glyph itself gets an empty line —\n // never emit a line wider than the clamped box.\n const ellipsized = last.length > 0 || measurer.fitsInWidth(\"…\", clamped.w) ? `${last}…` : \"\"\n lines = [...lines.slice(0, -1), ellipsized]\n }\n }\n const wrappedText = lines.join(\"\\n\")\n out.push({\n id: el.id,\n type: \"text\",\n box: clamped,\n text: wrappedText,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"text\", text: wrappedText, style: el.style, depth}),\n })\n }\n\n return {elements: out, degraded, dropped}\n}\n"]}
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../../../src/utils/display/scene/process.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scene processing — validate → clamp → budget → wrap. Everything here is\n * generic; device variation enters only through the capabilities block and the\n * display profile (data, not code). Spec §5.\n *\n * Never rejects a frame: offending elements are dropped per-element and\n * reported via `dropped` / `degraded` (design doc §3.4.6; spec §4 refines the\n * design doc's image \"reject\" to per-element drop).\n */\n\nimport {compileSvgPath} from \"./svgPath\"\nimport {TextMeasurer} from \"../measurer/TextMeasurer\"\nimport {TextWrapper} from \"../wrapper/TextWrapper\"\nimport type {DisplayProfile} from \"../profiles/types\"\nimport type {DiffableElement} from \"./differ\"\nimport type {\n SceneBox,\n SceneBoxInput,\n SceneDisplayCapabilities,\n SceneElementInput,\n ScenePoint,\n SceneTextStyle,\n SceneTransition,\n SceneTransitionEdge,\n} from \"./types\"\nimport {MAX_SCENE_DEPTH, POINT_SHAPE_ARITY, POINT_SHAPE_TYPES, elementContentHash} from \"./types\"\n\nexport interface ProcessedScene {\n elements: DiffableElement[]\n degraded: boolean\n dropped: string[]\n}\n\n/** Reporting id for an element the app may not have named. */\nfunction reportId(el: SceneElementInput, index: number): string {\n return el.id ?? `${el.type}[${index}]`\n}\n\nfunction clampBox(box: SceneBox, width: number, height: number): SceneBox | null {\n const x1 = Math.max(0, Math.floor(box.x))\n const y1 = Math.max(0, Math.floor(box.y))\n const x2 = Math.min(width, Math.floor(box.x) + Math.max(0, Math.floor(box.w)))\n const y2 = Math.min(height, Math.floor(box.y) + Math.max(0, Math.floor(box.h)))\n if (x2 <= x1 || y2 <= y1) return null\n return {x: x1, y: y1, w: x2 - x1, h: y2 - y1}\n}\n\nconst isFinite = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n)\n\n/** Integer points, or null when the count/values are invalid for the type. */\nfunction validPoints(el: SceneElementInput): ScenePoint[] | null {\n const raw = (el as {points?: unknown}).points\n if (!Array.isArray(raw) || !(POINT_SHAPE_ARITY[el.type] ?? []).includes(raw.length)) return null\n const points: ScenePoint[] = []\n for (const p of raw) {\n if (!p || typeof p !== \"object\" || !isFinite((p as ScenePoint).x) || !isFinite((p as ScenePoint).y)) return null\n points.push({x: Math.floor((p as ScenePoint).x), y: Math.floor((p as ScenePoint).y)})\n }\n return points\n}\n\nfunction pointsBox(points: ScenePoint[]): SceneBox {\n const xs = points.map((p) => p.x)\n const ys = points.map((p) => p.y)\n const x = Math.min(...xs)\n const y = Math.min(...ys)\n return {x, y, w: Math.max(...xs) - x + 1, h: Math.max(...ys) - y + 1}\n}\n\nfunction validEdge(e: unknown): SceneTransitionEdge | undefined {\n if (!e || typeof e !== \"object\") return undefined\n const dx = Math.round(Number((e as SceneTransitionEdge).dx ?? 0))\n const dy = Math.round(Number((e as SceneTransitionEdge).dy ?? 0))\n if (!isFinite(dx) || !isFinite(dy) || Math.abs(dx) > 1024 || Math.abs(dy) > 1024) return undefined\n return {dx, dy}\n}\n\nfunction validTransition(t: unknown): SceneTransition | undefined {\n if (!t || typeof t !== \"object\") return undefined\n const {durationMs, easing} = t as SceneTransition\n if (!isFinite(durationMs) || durationMs <= 0) return undefined\n const out: SceneTransition = easing ? {durationMs, easing} : {durationMs}\n const enter = validEdge((t as SceneTransition).enter)\n const exit = validEdge((t as SceneTransition).exit)\n if (enter) out.enter = enter\n if (exit) out.exit = exit\n return out\n}\n\nfunction boxShrunk(orig: SceneBox, clamped: SceneBox): boolean {\n return (\n clamped.x !== Math.floor(orig.x) ||\n clamped.y !== Math.floor(orig.y) ||\n clamped.w !== Math.floor(orig.w) ||\n clamped.h !== Math.floor(orig.h)\n )\n}\n\n/**\n * Line height for box-height→line-count math — ONLY when the profile declares\n * a calibrated `lineHeightPx`. Deriving one from full-canvas numbers proved\n * wrong on hardware (288/8=36px clipped the G2 nav instruction to one line;\n * the real container line height is smaller). Without calibration we return\n * null and the wrap step skips host-side height clipping entirely — text wraps\n * to the box WIDTH and the firmware clips vertically in-box, which is exactly\n * the legacy behavior.\n */\nexport function profileLineHeightPx(profile: DisplayProfile, _canvasHeight: number): number | null {\n return profile.lineHeightPx ?? null\n}\n\n/**\n * Process a raw scene against a device's capabilities + profile. Output is\n * diff-ready (text pre-wrapped, boxes clamped, content hashed) and reflects\n * exactly what will be sent — the diff baseline is post-processed by design\n * (spec §4).\n */\nexport function processScene(\n input: readonly SceneElementInput[],\n caps: SceneDisplayCapabilities,\n profile: DisplayProfile,\n): ProcessedScene {\n const dropped: string[] = []\n let degraded = false\n\n const measurer = new TextMeasurer(profile)\n const wrapper = new TextWrapper(measurer)\n const lineHeight = profileLineHeightPx(profile, caps.height)\n\n // Validate + dedupe explicit ids (first occurrence wins; dupes are dev error).\n const seenIds = new Set<string>()\n const valid: {el: SceneElementInput; index: number; box: SceneBoxInput; points?: ScenePoint[]}[] = []\n input.forEach((raw, index) => {\n if (!raw || typeof raw !== \"object\") {\n dropped.push(reportId(raw ?? ({type: \"text\"} as SceneElementInput), index))\n degraded = true\n return\n }\n let points: ScenePoint[] | undefined\n if (POINT_SHAPE_TYPES.includes(raw.type)) {\n const valid = validPoints(raw)\n if (!valid) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n points = valid\n raw = {...raw, box: pointsBox(valid)} as SceneElementInput\n }\n if (!raw.box) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n // Non-finite box numbers (NaN/±Infinity/non-number) would survive the\n // Math.* clamp below and poison the frame all the way to native ints.\n const b = raw.box\n if (![b.x, b.y, b.w, b.h].every(isFinite)) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n if ((raw.type === \"arc\" || raw.type === \"pie\") && !(isFinite(raw.startDeg) && isFinite(raw.endDeg))) {\n dropped.push(reportId(raw, index))\n degraded = true\n return\n }\n // Leading \"~\" is the differ's synthetic-id namespace — an app id there\n // would never match across frames (re-created every push). Normalize by\n // stripping; the rewrite is deterministic, so diffing stays stable.\n const el = raw.id?.startsWith(\"~\") ? {...raw, id: raw.id.replace(/^~+/, \"\") || undefined} : raw\n if (el.id) {\n const key = `${el.type}:${el.id}`\n if (seenIds.has(key)) {\n dropped.push(reportId(el, index))\n degraded = true\n return\n }\n seenIds.add(key)\n }\n valid.push({el, index, box: el.box as SceneBoxInput, points})\n })\n\n // Clamp + per-type limits, then budget in array order.\n let textBudget = caps.maxTextElements\n let imageBudget = caps.maxImageElements\n const out: DiffableElement[] = []\n let pathEdges = 0\n\n for (const {el, index, box, points} of valid) {\n const transition = validTransition((el as {transition?: unknown}).transition)\n // Leaving elements only exist to tween away; a device that jumps never shows them.\n if (transition?.exit && !caps.animation) continue\n const transform: {rotation?: number; pivot?: ScenePoint} = {}\n if (el.rotation !== undefined || el.pivot !== undefined) {\n const angle = el.rotation ?? 0\n const pivot = el.pivot ?? {x: box.x + box.w / 2, y: box.y + box.h / 2}\n if (\n !isFinite(angle) ||\n Math.abs(angle) > 36000 ||\n !isFinite(pivot.x) ||\n !isFinite(pivot.y) ||\n Math.abs(pivot.x) > 32767 ||\n Math.abs(pivot.y) > 32767 ||\n !caps.rotation ||\n el.type === \"image\" ||\n el.type === \"text\"\n ) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n transform.rotation = Math.round(angle * 256) / 256\n transform.pivot = {x: Math.round(pivot.x), y: Math.round(pivot.y)}\n if (transition) transition.durationMs = Math.min(65535, transition.durationMs)\n }\n const transformHash =\n transform.rotation === undefined ? undefined : `${transform.rotation}:${transform.pivot?.x}:${transform.pivot?.y}`\n // Depth is a lens disparity, not content: devices without it draw the\n // element flat instead of dropping it. A non-number is still a dev error.\n let depth: number | undefined\n if (el.depth !== undefined) {\n if (!isFinite(el.depth)) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n const d = Math.max(-MAX_SCENE_DEPTH, Math.min(MAX_SCENE_DEPTH, Math.round(el.depth)))\n if (caps.depth && d !== 0) depth = d\n }\n const plane = depth === undefined ? {} : {depth}\n const clamped = clampBox(box, caps.width, caps.height)\n if (!clamped) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n const shrunk = boxShrunk(box, clamped)\n if (shrunk) degraded = true\n\n if (el.type === \"svg\") {\n try {\n if (!caps.shapes.includes(\"svg\") || textBudget <= 0 || shrunk)\n throw new Error(\"SVG unavailable or out of bounds\")\n const scale = el.scale ?? 1\n if (\n !isFinite(scale) ||\n scale < 0 ||\n scale > 8 ||\n (el.fillRule !== undefined && el.fillRule !== \"evenodd\" && el.fillRule !== \"nonzero\")\n )\n throw new Error(\"Invalid SVG scale or fill rule\")\n const compiled = compileSvgPath(el.d)\n if (pathEdges + compiled.edges > 4096) throw new Error(\"SVG scene exceeds the edge budget\")\n pathEdges += compiled.edges\n textBudget--\n out.push({\n id: el.id,\n type: \"svg\",\n box: clamped,\n d: el.d,\n path: compiled.commands,\n pathScale: Math.round(scale * 256),\n fillRule: el.fillRule ?? \"nonzero\",\n style: el.style,\n ...transform,\n ...plane,\n ...(transition ? {transition} : {}),\n contentHash: elementContentHash({\n type: \"svg\",\n data: `${el.d}:${scale}:${el.fillRule}:${transformHash}`,\n style: el.style,\n depth,\n }),\n })\n } catch {\n dropped.push(reportId(el, index))\n degraded = true\n }\n continue\n }\n\n if (el.type !== \"text\" && el.type !== \"image\" && el.type !== \"rect\") {\n // Vector shapes: only devices that draw them get them; the device clips\n // point shapes itself, so an off-canvas point is not a drop.\n if (!caps.shapes.includes(el.type) || textBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n textBudget--\n const angles = el.type === \"arc\" || el.type === \"pie\" ? {startDeg: el.startDeg, endDeg: el.endDeg} : {}\n out.push({\n id: el.id,\n type: el.type,\n box: clamped,\n style: el.style,\n ...(points ? {points} : {}),\n ...angles,\n ...(transition ? {transition} : {}),\n ...transform,\n ...plane,\n contentHash: elementContentHash({\n type: el.type,\n style: el.style,\n points,\n ...angles,\n data: transformHash,\n depth,\n }),\n })\n continue\n }\n\n if (el.type === \"image\") {\n // Images: a shrunk box can't be honored (we don't crop pixels host-side)\n // and per-image device limits are box-level (the firmware allocates the\n // component from the box). SGCs scale pixels to the box (phone-side\n // scaling — never on glasses).\n if (shrunk) {\n dropped.push(reportId(el, index))\n continue\n }\n if (caps.maxImagePx && (clamped.w > caps.maxImagePx.width || clamped.h > caps.maxImagePx.height)) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n if (imageBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n imageBudget--\n out.push({\n id: el.id,\n type: \"image\",\n box: clamped,\n data: el.data,\n ...(el.style ? {style: el.style} : {}),\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"image\", data: el.data, style: el.style, depth}),\n })\n continue\n }\n\n // text + rect share the text-container budget (design doc §3.4.6).\n if (textBudget <= 0) {\n dropped.push(reportId(el, index))\n degraded = true\n continue\n }\n textBudget--\n\n if (el.type === \"rect\") {\n out.push({\n id: el.id,\n type: \"rect\",\n box: clamped,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...transform,\n ...plane,\n contentHash: elementContentHash({type: \"rect\", style: el.style, data: transformHash, depth}),\n })\n continue\n }\n\n // Text: wrap on the phone into the (clamped) box. The box then carries\n // pre-wrapped text; firmware in-box wrap is a fallback, not the mechanism.\n // Height clipping only applies with a CALIBRATED line height — otherwise\n // the firmware clips vertically in-box (legacy behavior).\n const style: SceneTextStyle = el.style ?? {}\n if (style.font === \"mono\" || style.font === \"builtin\") {\n // Grid text is laid out by the app (cols × rows) and builtin text by the\n // glasses' own font engine; the host's proportional wrap metrics don't\n // apply to either. Pass lines through and let the device clip.\n const monoText = el.text ?? \"\"\n out.push({\n id: el.id,\n type: \"text\",\n box: clamped,\n text: monoText,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"text\", text: monoText, style: el.style, depth}),\n })\n continue\n }\n const maxLines = lineHeight ? Math.max(1, Math.floor(clamped.h / lineHeight)) : profile.maxLines\n const result = wrapper.wrap(el.text ?? \"\", {\n maxWidthPx: clamped.w,\n maxLines,\n ...(style.breakMode ? {breakMode: style.breakMode} : {}),\n })\n let lines = result.lines\n if (result.truncated) {\n degraded = true\n if (style.overflow === \"ellipsis\" && lines.length > 0) {\n // Trim the last line until it fits WITH the ellipsis appended —\n // otherwise the extra glyph can overflow the clamped box width.\n let last = lines[lines.length - 1]\n while (last.length > 0 && !measurer.fitsInWidth(`${last}…`, clamped.w)) {\n last = last.slice(0, -1)\n }\n // A box narrower than the ellipsis glyph itself gets an empty line —\n // never emit a line wider than the clamped box.\n const ellipsized = last.length > 0 || measurer.fitsInWidth(\"…\", clamped.w) ? `${last}…` : \"\"\n lines = [...lines.slice(0, -1), ellipsized]\n }\n }\n const wrappedText = lines.join(\"\\n\")\n out.push({\n id: el.id,\n type: \"text\",\n box: clamped,\n text: wrappedText,\n style: el.style,\n ...(transition ? {transition} : {}),\n ...plane,\n contentHash: elementContentHash({type: \"text\", text: wrappedText, style: el.style, depth}),\n })\n }\n\n return {elements: out, degraded, dropped}\n}\n"]}
@@ -1,3 +1,3 @@
1
- export type { FrameElement, SceneAnchor, SceneBox, SceneBoxInput, SceneBreakMode, SceneChange, SceneDisplayCapabilities, SceneElementInput, SceneElementType, SceneFrame, SceneImageStyle, ScenePoint, SceneProcessResult, SceneRectStyle, SceneShapeStyle, SceneStrokeStyle, SceneTextStyle, SceneTransition, } from "@glassly/miniapp/display";
1
+ export type { FrameElement, SceneAnchor, SceneBox, SceneBoxInput, SceneBreakMode, SceneChange, SceneDisplayCapabilities, SceneElementInput, SceneElementType, SceneFrame, SceneImageStyle, ScenePoint, SceneProcessResult, SceneRectStyle, SceneShapeStyle, SceneStrokeStyle, SceneTextStyle, SceneTransition, SceneTransitionEdge, } from "@glassly/miniapp/display";
2
2
  export { boxesEqual, contentHash, elementContentHash, MAX_SCENE_DEPTH, POINT_SHAPE_ARITY, POINT_SHAPE_TYPES, } from "@glassly/miniapp/display";
3
3
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/scene/types.ts"],"names":[],"mappings":"AACA,YAAY,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,WAAW,EACX,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/scene/types.ts"],"names":[],"mappings":"AACA,YAAY,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,WAAW,EACX,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,0BAA0B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/utils/display/scene/types.ts"],"names":[],"mappings":"AAqBA,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,0BAA0B,CAAA","sourcesContent":["// Moved into the SDK (@glassly/miniapp/display) so miniapps lay text out the way the host measures it; this path re-exports it for the engine.\nexport type {\n FrameElement,\n SceneAnchor,\n SceneBox,\n SceneBoxInput,\n SceneBreakMode,\n SceneChange,\n SceneDisplayCapabilities,\n SceneElementInput,\n SceneElementType,\n SceneFrame,\n SceneImageStyle,\n ScenePoint,\n SceneProcessResult,\n SceneRectStyle,\n SceneShapeStyle,\n SceneStrokeStyle,\n SceneTextStyle,\n SceneTransition,\n} from \"@glassly/miniapp/display\"\nexport {\n boxesEqual,\n contentHash,\n elementContentHash,\n MAX_SCENE_DEPTH,\n POINT_SHAPE_ARITY,\n POINT_SHAPE_TYPES,\n} from \"@glassly/miniapp/display\"\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/utils/display/scene/types.ts"],"names":[],"mappings":"AAsBA,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,0BAA0B,CAAA","sourcesContent":["// Moved into the SDK (@glassly/miniapp/display) so miniapps lay text out the way the host measures it; this path re-exports it for the engine.\nexport type {\n FrameElement,\n SceneAnchor,\n SceneBox,\n SceneBoxInput,\n SceneBreakMode,\n SceneChange,\n SceneDisplayCapabilities,\n SceneElementInput,\n SceneElementType,\n SceneFrame,\n SceneImageStyle,\n ScenePoint,\n SceneProcessResult,\n SceneRectStyle,\n SceneShapeStyle,\n SceneStrokeStyle,\n SceneTextStyle,\n SceneTransition,\n SceneTransitionEdge,\n} from \"@glassly/miniapp/display\"\nexport {\n boxesEqual,\n contentHash,\n elementContentHash,\n MAX_SCENE_DEPTH,\n POINT_SHAPE_ARITY,\n POINT_SHAPE_TYPES,\n} from \"@glassly/miniapp/display\"\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/engine",
3
- "version": "0.3.5-beta.710",
3
+ "version": "0.3.5-beta.760",
4
4
  "description": "Glassly Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -97,17 +97,17 @@
97
97
  "registry": "https://registry.npmjs.org/"
98
98
  },
99
99
  "dependencies": {
100
- "@glassly/bluetooth-sdk": "0.3.5-beta.710",
101
- "@glassly/cloud-client": "0.3.5-beta.710",
102
- "@glassly/cloud-protocol": "0.3.5-beta.710",
103
- "@glassly/crust": "0.3.5-beta.710",
104
- "@glassly/miniapp": "0.3.5-beta.710",
100
+ "@glassly/bluetooth-sdk": "0.3.5-beta.760",
101
+ "@glassly/cloud-client": "0.3.5-beta.760",
102
+ "@glassly/cloud-protocol": "0.3.5-beta.760",
103
+ "@glassly/crust": "0.3.5-beta.760",
104
+ "@glassly/miniapp": "0.3.5-beta.760",
105
105
  "buffer": "^6.0.3",
106
106
  "events": "^3.3.0",
107
107
  "react-native-marked": "8.1.1",
108
108
  "semver": "^7.7.4",
109
109
  "typesafe-ts": "^1.7.1",
110
- "@glassly/llm-providers": "0.3.5-beta.710",
110
+ "@glassly/llm-providers": "0.3.5-beta.760",
111
111
  "@huggingface/jinja": "^0.5.9",
112
112
  "jsonrepair": "^3.12.0"
113
113
  },
package/src/engine.ts CHANGED
@@ -24,6 +24,7 @@ import {startPhoneActivityTracking, stopPhoneActivityTracking} from "./services/
24
24
  import {startPhoneNotificationsSync, stopPhoneNotificationsSync} from "./services/PhoneNotificationsSync"
25
25
  import {startSpeakerIdSync, stopSpeakerIdSync} from "./services/SpeakerIdSync"
26
26
  import {startNeuralBandSettingsSync, stopNeuralBandSettingsSync} from "./services/neuralBand/NeuralBandSettingsSync"
27
+ import {startAppleWatchLink, stopAppleWatchLink} from "./services/appleWatch/AppleWatchLink"
27
28
  import {startCaptionsTesterReportService, stopCaptionsTesterReportService} from "./services/CaptionsTesterReportService"
28
29
  import {startJSCrashloopReportService, stopJSCrashloopReportService} from "./services/JSCrashloopReportService"
29
30
  import {ensureMiniappEngine, stopMiniappEngine} from "./services/MiniappEngine"
@@ -137,6 +138,8 @@ export const engine = {
137
138
  startSpeakerIdSync()
138
139
  // Meta Neural Band: push the air mouse and wearing-hand settings to the band on connect.
139
140
  startNeuralBandSettingsSync()
141
+ // Apple Watch remote (iOS): status and mirror frames out, wrist commands in.
142
+ startAppleWatchLink()
140
143
  // Android internal/e2e: laptop captions tester can broadcast a failure intent;
141
144
  // engine owns turning that into a Cloud report.
142
145
  startCaptionsTesterReportService()
@@ -181,6 +184,7 @@ export const engine = {
181
184
  await safely("phone activity tracking", stopPhoneActivityTracking)
182
185
  await safely("speaker id sync", stopSpeakerIdSync)
183
186
  await safely("neural band settings sync", stopNeuralBandSettingsSync)
187
+ await safely("apple watch link", stopAppleWatchLink)
184
188
  await safely("captions tester report service", stopCaptionsTesterReportService)
185
189
  await safely("js crashloop report service", stopJSCrashloopReportService)
186
190
  await safely("miniapp engine", stopMiniappEngine)
@@ -192,7 +196,7 @@ export const engine = {
192
196
  },
193
197
  glasses,
194
198
  speech,
195
- /** On-device LLM model management (Settings > AI Models). */
199
+ /** On-device LLM model management (Settings > Offline Models). */
196
200
  ai,
197
201
  /** Cloud live-session status + account ops — engine owns the client. */
198
202
  session,
package/src/facades/ai.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  * ai facade — `engine.ai.local`: the on-device LLM model that serves
3
3
  * session.ai calls routed to the phone (forceLocal / localFallback). Thin
4
4
  * passthrough over LocalLlmModelManager and LocalLlmProvider so Settings >
5
- * AI Models can list, pick, download, and delete models without reaching
6
- * into engine internals.
5
+ * Offline Models can list, pick, download, and delete models without
6
+ * reaching into engine internals.
7
7
  */
8
8
  import localLlmModelManager, {
9
9
  LocalLlmModelManager,
@@ -17,7 +17,7 @@ export const ai = {
17
17
  local: {
18
18
  /** The catalog, in dropdown order. */
19
19
  models: (): LocalLlmModelConfig[] => localLlmModelManager.models(),
20
- /** The Settings > AI Models pick. */
20
+ /** The Settings > Offline Models pick. */
21
21
  selectedModelId: (): LocalLlmModelId => localLlmModelManager.selectedModelId(),
22
22
  select: (id: LocalLlmModelId): Promise<void> => localLlmModelManager.select(id),
23
23
  isDownloaded: (id?: LocalLlmModelId): Promise<boolean> => localLlmModelManager.isModelAvailable(id),
@@ -12,6 +12,7 @@
12
12
  // standalone build); jest moduleNameMapper + tsconfig both resolve it.
13
13
  import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
14
14
  import type {
15
+ AppleWatchAvailability,
15
16
  ButtonPressEvent,
16
17
  ConnectOptions,
17
18
  ControllerStatus,
@@ -329,6 +330,12 @@ export const glasses = {
329
330
  setHand: (id: string, hand: "left" | "right"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),
330
331
  },
331
332
 
333
+ // --- Apple Watch (iOS): pairs through the Glassly watch app over WatchConnectivity ---
334
+ appleWatch: {
335
+ /** Whether this iPhone has a paired watch and the Glassly watch app is on it. */
336
+ availability: (): Promise<AppleWatchAvailability> => BluetoothSdk.appleWatchAvailability(),
337
+ },
338
+
332
339
  // --- audio (glasses media-volume, own-app playback hint, live mic level) ---
333
340
  audio: {
334
341
  /** Read the glasses' current media volume. */
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
12
12
  export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
14
  "familyBaseVersion": "0.3.5",
15
- "releaseIdentity": "0.3.5-beta.710",
16
- "releaseSetId": "glassly-0.3.5-beta.710",
17
- "sourceCommit": "dd27c8bbc0536d3479bdb50a288c2d63f2542290",
15
+ "releaseIdentity": "0.3.5-beta.760",
16
+ "releaseSetId": "glassly-0.3.5-beta.760",
17
+ "sourceCommit": "d5e2a8877bc5441b410677c79d96b38b985d5f91",
18
18
  "otaManifestUrl": null,
19
19
  "otaManifestSha256": null
20
20
  })
@@ -132,7 +132,8 @@ class GestureDotsService {
132
132
  * `controllerId` keeps two controllers' fingers apart.
133
133
  */
134
134
  noteGesture(gestureName: string, claimed = false, fingerIndex?: number, controllerId?: string): void {
135
- if (!this.started || !this.enabled()) return
135
+ // A ring's scroll steps ride beside their stroke's one swipe, which already flashed.
136
+ if (!this.started || !this.enabled() || gestureName === "scroll_up" || gestureName === "scroll_down") return
136
137
  this.applyGesture(gestureName, claimed, fingerIndex, controllerId)
137
138
  this.trace("input", {gestureName, claimed})
138
139
  }
@@ -5,6 +5,7 @@ import BluetoothSdk from "@glassly/bluetooth-sdk/internal"
5
5
  import {useAppStatusStore} from "../stores/apps"
6
6
  import {useGlassesStore} from "../stores/glasses"
7
7
  import {BgTimer} from "../utils/timers"
8
+ import {hasInput2d} from "./controllers"
8
9
  import dashboardOpenTrace from "./debug/DashboardOpenTrace"
9
10
  import gestureDotsService from "./GestureDotsService"
10
11
  import {isGlassesConnected} from "./GlassesReadiness"
@@ -17,7 +18,7 @@ import {pagesEligible} from "./pages/pageEligibility"
17
18
 
18
19
  const miniappExitGesture = new MiniappExitGesture({
19
20
  getState: () => useAppStatusStore.getState(),
20
- getVisiblePackageName: () => localDisplayManager.getVisibleMiniappPackageName(),
21
+ getTargetPackageName: () => localDisplayManager.getMiniappToLeave(),
21
22
  beginExitNotice: (packageName, text) => localDisplayManager.beginMiniappExit(packageName, text),
22
23
  finishExitNotice: (packageName) => localDisplayManager.finishMiniappExit(packageName),
23
24
  cancelExitNotice: (packageName) => localDisplayManager.cancelMiniappExit(packageName),
@@ -25,36 +26,85 @@ const miniappExitGesture = new MiniappExitGesture({
25
26
  clearTimeout: (timerId) => BgTimer.clearTimeout(timerId),
26
27
  })
27
28
 
28
- // Raw ring reports include every scroll step, whether the CFW forwards them
29
- // (deviceModel G2) or the ring sends them to the phone itself (deviceModel R1);
30
- // both tag source 2. Rate-limit scroll actions separately from press/release
31
- // recognition, per ring, using native receipt timestamps so a JS backlog does
32
- // not collapse otherwise separate gestures.
33
- const RING_SCROLL_INTERVAL_MS = 100
34
- const lastRingScrollAt = new Map<string, number>()
35
-
36
- function shouldForwardScroll(event: TouchEvent): boolean {
37
- if (event.source !== 2 || (event.gestureName !== "swipe_up" && event.gestureName !== "swipe_down")) return true
38
- const ring = event.controllerId ?? `source:${event.source}`
39
- const last = lastRingScrollAt.get(ring)
29
+ // The ring reports a slide once per step of travel, whether the CFW forwards
30
+ // it (deviceModel G2) or the ring sends it to the phone itself (deviceModel
31
+ // R1); both tag source 2. One press is one swipe: the stroke's first step goes
32
+ // out as swipe_up / swipe_down, and every step, the first included, as
33
+ // scroll_up / scroll_down with its distance and speed for smooth scrolling. A
34
+ // stroke ends at the ring's next touch_down or tap_end, or after
35
+ // RING_STROKE_IDLE_MS without a step when a report was lost. Reversing within
36
+ // a stroke fires no swipe (a finger rolling back at lift-off). Native receipt
37
+ // timestamps keep strokes apart when JS drains a backlog.
38
+ const RING_STROKE_IDLE_MS = 1000
39
+ /** Per ring, when the open stroke's last step arrived. */
40
+ const ringStrokes = new Map<string, number>()
41
+
42
+ function ringKey(event: TouchEvent): string {
43
+ return event.controllerId ?? `source:${event.source}`
44
+ }
45
+
46
+ function isRingSlideStep(event: TouchEvent): boolean {
47
+ return event.source === 2 && (event.gestureName === "swipe_up" || event.gestureName === "swipe_down")
48
+ }
49
+
50
+ function opensRingStroke(event: TouchEvent): boolean {
51
+ const last = ringStrokes.get(ringKey(event))
52
+ ringStrokes.set(ringKey(event), event.timestamp)
40
53
  const elapsed = last === undefined ? null : event.timestamp - last
41
- if (elapsed !== null && elapsed >= 0 && elapsed < RING_SCROLL_INTERVAL_MS) return false
42
- lastRingScrollAt.set(ring, event.timestamp)
43
- return true
54
+ return elapsed === null || elapsed < 0 || elapsed >= RING_STROKE_IDLE_MS
55
+ }
56
+
57
+ function endRingStroke(event: TouchEvent): void {
58
+ if (event.source === 2 && (event.gestureName === "touch_down" || event.gestureName === "tap_end")) {
59
+ ringStrokes.delete(ringKey(event))
60
+ }
61
+ }
62
+
63
+ /** The discrete swipe a stroke opens with: the step's travel belongs to scroll_*. */
64
+ function strokeSwipe(step: TouchEvent): TouchEvent {
65
+ const swipe = {...step}
66
+ delete swipe.distance
67
+ delete swipe.speed
68
+ return swipe
69
+ }
70
+
71
+ /**
72
+ * Miniapps' copy of the touch: `input2d: true` when it came from a controller
73
+ * that swipes in two dimensions (the Neural Band), the field absent otherwise,
74
+ * so a 1D swipe from the temples or the ring stays tellable while a band is
75
+ * connected under the same gesture names.
76
+ */
77
+ function toMiniapps(event: TouchEvent): TouchEvent & {input2d?: true} {
78
+ const id = event.controllerId
79
+ if (id === undefined) return event
80
+ const model = useGlassesStore.getState().controllers?.[id]?.model
81
+ return model !== undefined && hasInput2d(model) ? {...event, input2d: true} : event
44
82
  }
45
83
 
46
84
  /**
47
85
  * Route both real glasses input and phone mirror controls through the same
48
86
  * policy. A claimed gesture goes to the app holding the claim, alone; an
49
87
  * unclaimed one goes to every subscriber and then the engine's default. The
50
- * one rule ahead of the claims: an unclaimed double tap on a miniapp whose
51
- * frame is visibly on the glasses leaves that app, whoever else claimed the
52
- * gesture — the dashboard's claim is for the empty screen. Head position
88
+ * one rule ahead of the claims: an unclaimed double tap leaves the miniapp
89
+ * the wearer is in — the one visibly on the glasses, else the running
90
+ * standard app on an empty screen — whoever else claimed the gesture; the
91
+ * dashboard's claim is for the screen with no app in it. Head position
53
92
  * (head_up / head_down) is a gesture like any other: the dashboard claims it
54
93
  * while it runs, and an unclaimed head_up on glasses that defer the dashboard
55
- * to the phone opens the native one, as it would with no app at all.
94
+ * to the phone opens the native one, as it would with no app at all. A ring
95
+ * slide step becomes one swipe per stroke plus a scroll step (see above).
56
96
  */
57
97
  export function dispatchTouchEvent(event: TouchEvent): void {
98
+ if (isRingSlideStep(event)) {
99
+ if (opensRingStroke(event)) routeTouchEvent(strokeSwipe(event))
100
+ routeTouchEvent({...event, gestureName: event.gestureName === "swipe_up" ? "scroll_up" : "scroll_down"})
101
+ return
102
+ }
103
+ endRingStroke(event)
104
+ routeTouchEvent(event)
105
+ }
106
+
107
+ function routeTouchEvent(event: TouchEvent): void {
58
108
  const gesture = event.gestureName
59
109
  const claimant = localMiniappRuntime.gestureClaimant(gesture)
60
110
  // The Neural Band's middle finger (index 1) shares the tap names but is left
@@ -63,16 +113,16 @@ export function dispatchTouchEvent(event: TouchEvent): void {
63
113
  const osHeadUp = gesture === "head_up"
64
114
 
65
115
  if (osDoubleTap) {
66
- // A visible main miniapp opts out of OS exit by registering double tap (the
116
+ // The app being left opts out of OS exit by registering double tap (the
67
117
  // terminal's back-to-menu). The latest claimant still receives it. Otherwise
68
118
  // the tap means "leave the app". The controller shows the bounded OS
69
- // notice first, then uses the normal
70
- // store stop path (and closes its phone overlay).
71
- const visible = localDisplayManager.getVisibleMiniappPackageName()
119
+ // notice first, then uses the normal store stop path (and closes its
120
+ // phone overlay).
121
+ const leaving = localDisplayManager.getMiniappToLeave()
72
122
  if (
73
- visible !== null &&
74
- !localMiniappRuntime.hasGestureClaim(visible, gesture) &&
75
- miniappExitGesture.tryExitVisibleMiniapp()
123
+ leaving !== null &&
124
+ !localMiniappRuntime.hasGestureClaim(leaving, gesture) &&
125
+ miniappExitGesture.tryExitMiniapp()
76
126
  ) {
77
127
  gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)
78
128
  return
@@ -86,18 +136,15 @@ export function dispatchTouchEvent(event: TouchEvent): void {
86
136
  }
87
137
  // The press may have started before this app claimed its release edge.
88
138
  gestureDotsService.noteGesture(gesture, true, event.index, event.controllerId)
89
- if (!shouldForwardScroll(event)) return
90
- localMiniappRuntime.forwardEvent("touch_event", event, undefined, claimant)
139
+ localMiniappRuntime.forwardEvent("touch_event", toMiniapps(event), undefined, claimant)
91
140
  return
92
141
  }
93
142
 
94
143
  // Gesture-dots debug indicator (any device, gated by its setting). It
95
- // only paints while the screen is otherwise empty. Process even suppressed
96
- // scroll repeats here so their completion still clears a held dot.
144
+ // only paints while the screen is otherwise empty.
97
145
  gestureDotsService.noteGesture(gesture, false, event.index, event.controllerId)
98
146
 
99
- if (!shouldForwardScroll(event)) return
100
- localMiniappRuntime.forwardEvent("touch_event", event)
147
+ localMiniappRuntime.forwardEvent("touch_event", toMiniapps(event))
101
148
 
102
149
  // Nobody claimed a double tap or a head-up: the glasses' native dashboard.
103
150
  // On eligible Glassly CFW glasses the dashboard miniapp claims both while it
@@ -118,7 +165,7 @@ export function dispatchTouchEvent(event: TouchEvent): void {
118
165
 
119
166
  export function resetTouchInput(): void {
120
167
  miniappExitGesture.reset()
121
- lastRingScrollAt.clear()
168
+ ringStrokes.clear()
122
169
  }
123
170
 
124
171
  /** Phone mirror input uses the same dashboard/miniapp policy as real glasses. */
@@ -165,7 +165,7 @@ class LocalDisplayManager {
165
165
  private overlayOwner: string | null = null
166
166
 
167
167
  /** Miniapp whose visible frame is temporarily covered by an OS exit notice. */
168
- private exitingApp: {packageName: string; coveredDisplay: ActiveDisplay} | null = null
168
+ private exitingApp: {packageName: string; coveredDisplay: ActiveDisplay | null} | null = null
169
169
 
170
170
  private expiryTimerId: number | null = null
171
171
 
@@ -309,16 +309,30 @@ class LocalDisplayManager {
309
309
  }
310
310
 
311
311
  /**
312
- * Cover the currently visible miniapp with a persistent system exit notice.
313
- * While it is up, miniapp display traffic is held off so a streaming app
314
- * cannot immediately overwrite the notice. The caller completes the normal
315
- * stop flow and then calls finishMiniappExit(); onUnmount does the same as a
312
+ * The miniapp an unclaimed double tap leaves: the one visibly drawing on
313
+ * the main view, else the core (running standard) app on an empty or
314
+ * system-drawn screen — an app that has drawn nothing yet, or whose frame
315
+ * expired, is still the one the wearer is in. Nothing while a page covers
316
+ * the main view: the page's claims own the tap.
317
+ */
318
+ public getMiniappToLeave(): string | null {
319
+ if (this.overlayOwner !== null) return null
320
+ return this.getVisibleMiniappPackageName() ?? this.coreApp
321
+ }
322
+
323
+ /**
324
+ * Cover the leaving miniapp with a persistent system exit notice. While it
325
+ * is up, miniapp display traffic is held off so a streaming app cannot
326
+ * immediately overwrite the notice. The caller completes the normal stop
327
+ * flow and then calls finishMiniappExit(); onUnmount does the same as a
316
328
  * safety net.
317
329
  */
318
330
  public beginMiniappExit(packageName: string, text: string): boolean {
319
331
  if (this.exitingApp !== null) return this.exitingApp.packageName === packageName
320
- const coveredDisplay = this.currentDisplay
321
- if (!coveredDisplay || this.getVisibleMiniappPackageName() !== packageName) return false
332
+ if (this.getMiniappToLeave() !== packageName) return false
333
+ // Only the app's own visible frame comes back on cancel; a system frame
334
+ // or an empty screen does not.
335
+ const coveredDisplay = this.getVisibleMiniappPackageName() === packageName ? this.currentDisplay : null
322
336
 
323
337
  console.log(`${LOG_TAG}: beginMiniappExit(${packageName})`)
324
338
  this.exitingApp = {packageName, coveredDisplay}
@@ -349,7 +363,7 @@ class LocalDisplayManager {
349
363
  this.exitingApp = null
350
364
 
351
365
  if (this.currentDisplay?.packageName !== SYSTEM_NOTICE_PKG) return
352
- if (covered.expiresAt === null || covered.expiresAt > this.now()) {
366
+ if (covered && (covered.expiresAt === null || covered.expiresAt > this.now())) {
353
367
  this.restoreDisplay(covered)
354
368
  } else {
355
369
  this.currentDisplay = null