@glassly/engine 0.3.7-beta.904 → 0.3.7-beta.916
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +29 -6
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/DisplayProcessor.d.ts +2 -0
- package/build/services/DisplayProcessor.d.ts.map +1 -1
- package/build/services/DisplayProcessor.js +13 -1
- package/build/services/DisplayProcessor.js.map +1 -1
- package/build/services/JSCrashloopReportService.d.ts.map +1 -1
- package/build/services/JSCrashloopReportService.js +5 -0
- package/build/services/JSCrashloopReportService.js.map +1 -1
- package/build/services/JSRouter.d.ts +2 -0
- package/build/services/JSRouter.d.ts.map +1 -1
- package/build/services/JSRouter.js +15 -1
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +6 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +11 -0
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts +2 -0
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +4 -0
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/SceneRenderer.js +1 -1
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/effectiveCapabilities.js +3 -0
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +14 -2
- package/build/stores/apps.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +10 -0
- package/build/stores/settings.js.map +1 -1
- package/build/types/capabilities/even-realities-g1.d.ts.map +1 -1
- package/build/types/capabilities/even-realities-g1.js +3 -0
- package/build/types/capabilities/even-realities-g1.js.map +1 -1
- package/build/utils/display/index.d.ts +1 -1
- package/build/utils/display/index.d.ts.map +1 -1
- package/build/utils/display/index.js +2 -2
- package/build/utils/display/index.js.map +1 -1
- package/build/utils/display/profiles/index.d.ts +1 -1
- package/build/utils/display/profiles/index.d.ts.map +1 -1
- package/build/utils/display/profiles/index.js +2 -2
- package/build/utils/display/profiles/index.js.map +1 -1
- package/build/utils/display/profiles/rayneo-io.d.ts +1 -1
- package/build/utils/display/profiles/rayneo-io.d.ts.map +1 -1
- package/build/utils/display/profiles/rayneo-io.js +1 -1
- package/build/utils/display/profiles/rayneo-io.js.map +1 -1
- package/build/utils/display/scene/index.d.ts +1 -1
- package/build/utils/display/scene/index.d.ts.map +1 -1
- package/build/utils/display/scene/index.js +1 -1
- package/build/utils/display/scene/index.js.map +1 -1
- package/build/utils/display/scene/process.d.ts +6 -0
- package/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +22 -7
- package/build/utils/display/scene/process.js.map +1 -1
- package/package.json +7 -7
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/AppRegistry.ts +28 -6
- package/src/services/DisplayProcessor.ts +15 -0
- package/src/services/JSCrashloopReportService.ts +5 -0
- package/src/services/JSRouter.ts +14 -1
- package/src/services/LocalMiniappRuntime.ts +11 -0
- package/src/services/MiniappLauncher.ts +5 -0
- package/src/services/SceneRenderer.ts +1 -1
- package/src/services/effectiveCapabilities.ts +3 -0
- package/src/stores/apps.ts +13 -2
- package/src/stores/settings.ts +10 -0
- package/src/types/capabilities/even-realities-g1.ts +4 -0
- package/src/utils/display/index.ts +2 -2
- package/src/utils/display/profiles/index.ts +7 -2
- package/src/utils/display/profiles/rayneo-io.ts +6 -1
- package/src/utils/display/scene/index.ts +1 -1
- package/src/utils/display/scene/process.ts +22 -7
|
@@ -6868,6 +6868,17 @@ class LocalMiniappRuntime {
|
|
|
6868
6868
|
this.sendToMiniapp(packageName, {type: MiniappResponseType.LINK, params})
|
|
6869
6869
|
}
|
|
6870
6870
|
|
|
6871
|
+
/**
|
|
6872
|
+
* Tell a running miniapp the user opened it again and from where. The
|
|
6873
|
+
* CONNECT_ACK only carries the source of the spawn, which is stale for a
|
|
6874
|
+
* background app that keeps running between opens.
|
|
6875
|
+
*/
|
|
6876
|
+
public sendLaunchChange(packageName: string, launchSource: MiniappLaunchSource): void {
|
|
6877
|
+
const existing = this.connectedApps.get(packageName)
|
|
6878
|
+
if (existing) existing.launchSource = launchSource
|
|
6879
|
+
this.sendToMiniapp(packageName, {type: MiniappResponseType.LAUNCH_CHANGE, launchSource})
|
|
6880
|
+
}
|
|
6881
|
+
|
|
6871
6882
|
/**
|
|
6872
6883
|
* Send a VISIBILITY_CHANGE push to a miniapp.
|
|
6873
6884
|
*/
|
|
@@ -91,6 +91,11 @@ class MiniappLauncher {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/** True iff the package's background JS context is currently spawned. */
|
|
94
|
+
/** Tell an already-running miniapp the user opened it again and from where. */
|
|
95
|
+
notifyLaunch(packageName: string, launchSource: MiniappLaunchSource): void {
|
|
96
|
+
this.deps?.router.notifyLaunch(packageName, launchSource)
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
isRunning(packageName: string): boolean {
|
|
95
100
|
return this.deps?.router.registeredPackages().includes(packageName) ?? false
|
|
96
101
|
}
|
|
@@ -250,7 +250,7 @@ class SceneRenderer {
|
|
|
250
250
|
return {kind: "legacy", layout, degraded, dropped}
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
const processed = processScene(elements, caps, displayProcessor.
|
|
253
|
+
const processed = processScene(elements, caps, displayProcessor.getSceneProfile())
|
|
254
254
|
// A stale baseline means the glasses were cleared since the last commit —
|
|
255
255
|
// diff from empty so everything repaints (the retained frame stays valid
|
|
256
256
|
// as the replay source only).
|
|
@@ -68,6 +68,9 @@ function glassesCapabilities(model: DeviceTypes): Capabilities {
|
|
|
68
68
|
...base.display,
|
|
69
69
|
width: canvas.width,
|
|
70
70
|
height: canvas.height,
|
|
71
|
+
// A Glassly CFW that publishes a canvas takes positioned elements, whatever
|
|
72
|
+
// the stock profile says (the G1's stock firmware degrades scenes to text walls).
|
|
73
|
+
canPosition: true,
|
|
71
74
|
maxTextElements: Math.max(
|
|
72
75
|
base.display.maxTextElements ?? 0,
|
|
73
76
|
vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS,
|
package/src/stores/apps.ts
CHANGED
|
@@ -217,6 +217,7 @@ function projectApps(previousState: AppStatusState, localApps: ClientApp[]): Cli
|
|
|
217
217
|
DeviceTypes.NONE
|
|
218
218
|
const capabilities = getModelCapabilities(defaultWearable)
|
|
219
219
|
const devToolsEnabled = useSettingsStore.getState().getSetting(SETTINGS.dev_tools.key) === true
|
|
220
|
+
const persistScreenshots = useSettingsStore.getState().getSetting(SETTINGS.persist_miniapp_screenshots.key) !== false
|
|
220
221
|
const hasPairedGlasses = useSettingsStore.getState().getSetting(SETTINGS.has_paired_glasses.key) === true
|
|
221
222
|
|
|
222
223
|
// Single pass: dedupe → screenshot carry-over → compat → hidden, all into
|
|
@@ -246,7 +247,10 @@ function projectApps(previousState: AppStatusState, localApps: ClientApp[]): Cli
|
|
|
246
247
|
.map((app) => {
|
|
247
248
|
const next: ClientApp = {
|
|
248
249
|
...app,
|
|
249
|
-
|
|
250
|
+
// The registry's screenshot is the one restored from disk; only an
|
|
251
|
+
// in-memory capture carries over when persistence is off.
|
|
252
|
+
screenshot:
|
|
253
|
+
previousByPackage.get(app.packageName)?.screenshot ?? (persistScreenshots ? app.screenshot : undefined),
|
|
250
254
|
compatibility: HardwareCompatibility.checkCompatibility(app.hardwareRequirements, capabilities),
|
|
251
255
|
// A registry source can force an app off the primary home screen.
|
|
252
256
|
// Compose that gate with the user's persisted home-screen layout instead
|
|
@@ -390,6 +394,8 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
|
|
|
390
394
|
// still takes the foreground below.
|
|
391
395
|
if (app.running && !app.widgetOnly) {
|
|
392
396
|
saveLastOpenTime(packageName)
|
|
397
|
+
// The running background only knows the source of its spawn.
|
|
398
|
+
if (app.local) miniappLauncher.notifyLaunch(packageName, opts?.launchSource ?? "phone")
|
|
393
399
|
return true
|
|
394
400
|
}
|
|
395
401
|
|
|
@@ -543,7 +549,12 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
|
|
|
543
549
|
},
|
|
544
550
|
|
|
545
551
|
saveScreenshot: async (packageName: string, screenshot: string) => {
|
|
546
|
-
|
|
552
|
+
const key = `${packageName}_screenshot`
|
|
553
|
+
if (useSettingsStore.getState().getSetting(SETTINGS.persist_miniapp_screenshots.key) !== false) {
|
|
554
|
+
storage.save(key, screenshot)
|
|
555
|
+
} else {
|
|
556
|
+
storage.remove(key)
|
|
557
|
+
}
|
|
547
558
|
set((s) => ({
|
|
548
559
|
apps: s.apps.map((a) => (a.packageName === packageName ? {...a, screenshot} : a)),
|
|
549
560
|
}))
|
package/src/stores/settings.ts
CHANGED
|
@@ -913,6 +913,16 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
913
913
|
saveOnServer: false,
|
|
914
914
|
persist: true,
|
|
915
915
|
},
|
|
916
|
+
// Debug: keep each miniapp's switcher-card screenshot on disk so the card
|
|
917
|
+
// comes back after an app restart. Off keeps captures in memory only, for
|
|
918
|
+
// checking the capture path itself. Device-local, default on.
|
|
919
|
+
persist_miniapp_screenshots: {
|
|
920
|
+
key: "persist_miniapp_screenshots",
|
|
921
|
+
defaultValue: () => true,
|
|
922
|
+
writable: true,
|
|
923
|
+
saveOnServer: false,
|
|
924
|
+
persist: true,
|
|
925
|
+
},
|
|
916
926
|
// Gesture-dots debug indicator: flash a glyph on the glasses when a touch
|
|
917
927
|
// gesture is received while the display is empty. Works on any
|
|
918
928
|
// scene-capable device, not only CFW. Device-local debug knob, default off.
|
|
@@ -68,4 +68,8 @@ export const evenRealitiesG1: Capabilities = {
|
|
|
68
68
|
|
|
69
69
|
// OTA capabilities - G1 firmware updates are not driven by the ASG OTA flow
|
|
70
70
|
hasOta: false,
|
|
71
|
+
|
|
72
|
+
// The app flashes the bundled Glassly CFW over MCUmgr SMP (see
|
|
73
|
+
// bluetooth-sdk/ios/Source/Resources/G1Firmware/README.md).
|
|
74
|
+
hasCustomFirmware: true,
|
|
71
75
|
}
|
|
@@ -70,8 +70,8 @@ export {Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX} from "./profile
|
|
|
70
70
|
// Mentra Nex Profile (a.k.a. Mentra Display)
|
|
71
71
|
export {NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX} from "./profiles"
|
|
72
72
|
|
|
73
|
-
// RayNeo iO
|
|
74
|
-
export {RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX} from "./profiles"
|
|
73
|
+
// RayNeo iO Profiles (stock subtitle page, Glassly CFW scene page)
|
|
74
|
+
export {RAYNEO_IO_PROFILE, RAYNEO_IO_CFW_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX} from "./profiles"
|
|
75
75
|
|
|
76
76
|
// =============================================================================
|
|
77
77
|
// Measurer - Pixel-accurate text measurement
|
|
@@ -27,5 +27,10 @@ export {G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX} from "./g2"
|
|
|
27
27
|
// Mentra Nex Profile (a.k.a. Mentra Display)
|
|
28
28
|
export {NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX} from "./nex"
|
|
29
29
|
|
|
30
|
-
// RayNeo iO
|
|
31
|
-
export {
|
|
30
|
+
// RayNeo iO Profiles (stock subtitle page, Glassly CFW scene page)
|
|
31
|
+
export {
|
|
32
|
+
RAYNEO_IO_PROFILE,
|
|
33
|
+
RAYNEO_IO_CFW_PROFILE,
|
|
34
|
+
RAYNEO_IO_HYPHEN_WIDTH_PX,
|
|
35
|
+
RAYNEO_IO_SPACE_WIDTH_PX,
|
|
36
|
+
} from "./rayneo-io"
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
// 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.
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
RAYNEO_IO_PROFILE,
|
|
4
|
+
RAYNEO_IO_CFW_PROFILE,
|
|
5
|
+
RAYNEO_IO_HYPHEN_WIDTH_PX,
|
|
6
|
+
RAYNEO_IO_SPACE_WIDTH_PX,
|
|
7
|
+
} from "@glassly/miniapp/display"
|
|
@@ -24,7 +24,7 @@ export {resolveAnchors} from "./anchor"
|
|
|
24
24
|
export type {DiffResult, DiffableElement} from "./differ"
|
|
25
25
|
export {diffScene} from "./differ"
|
|
26
26
|
export type {ProcessedScene} from "./process"
|
|
27
|
-
export {processScene, profileLineHeightPx} from "./process"
|
|
27
|
+
export {processScene, profileLineHeightPx, wrapsOnDevice} from "./process"
|
|
28
28
|
export type {DegradedScene} from "./degrade"
|
|
29
29
|
export {degradeScene} from "./degrade"
|
|
30
30
|
export {SceneStore} from "./store"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {compileSvgPath} from "./svgPath"
|
|
12
|
+
import {isUnsupportedScript} from "../measurer/script-detection"
|
|
12
13
|
import {TextMeasurer} from "../measurer/TextMeasurer"
|
|
13
14
|
import {TextWrapper} from "../wrapper/TextWrapper"
|
|
14
15
|
import type {DisplayProfile} from "../profiles/types"
|
|
@@ -47,6 +48,18 @@ function clampBox(box: SceneBox, width: number, height: number): SceneBox | null
|
|
|
47
48
|
|
|
48
49
|
const isFinite = (n: unknown): n is number => typeof n === "number" && Number.isFinite(n)
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Text the host cannot wrap: a script the measurer has no widths or break
|
|
53
|
+
* rules for (Thai has no spaces between words, for one). It is passed through
|
|
54
|
+
* unwrapped and the device's text engine lays it out in the box.
|
|
55
|
+
*/
|
|
56
|
+
export function wrapsOnDevice(text: string): boolean {
|
|
57
|
+
for (const char of text) {
|
|
58
|
+
if (isUnsupportedScript(char)) return true
|
|
59
|
+
}
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
50
63
|
/** Integer points, or null when the count/values are invalid for the type. */
|
|
51
64
|
function validPoints(el: SceneElementInput): ScenePoint[] | null {
|
|
52
65
|
const raw = (el as {points?: unknown}).points
|
|
@@ -372,10 +385,11 @@ export function processScene(
|
|
|
372
385
|
// Height clipping only applies with a CALIBRATED line height — otherwise
|
|
373
386
|
// the firmware clips vertically in-box (legacy behavior).
|
|
374
387
|
const style: SceneTextStyle = el.style ?? {}
|
|
375
|
-
if (style.font === "mono" || style.font === "builtin") {
|
|
376
|
-
// Grid text is laid out by the app (cols × rows)
|
|
377
|
-
// glasses' own font engine
|
|
378
|
-
//
|
|
388
|
+
if (style.font === "mono" || style.font === "builtin" || wrapsOnDevice(el.text ?? "")) {
|
|
389
|
+
// Grid text is laid out by the app (cols × rows), builtin text by the
|
|
390
|
+
// glasses' own font engine and unsupported scripts by the device's text
|
|
391
|
+
// engine; the host's proportional wrap metrics don't apply to any of
|
|
392
|
+
// them. Pass lines through and let the device wrap and clip.
|
|
379
393
|
const monoText = el.text ?? ""
|
|
380
394
|
out.push({
|
|
381
395
|
id: el.id,
|
|
@@ -390,8 +404,9 @@ export function processScene(
|
|
|
390
404
|
continue
|
|
391
405
|
}
|
|
392
406
|
const maxLines = lineHeight ? Math.max(1, Math.floor(clamped.h / lineHeight)) : profile.maxLines
|
|
407
|
+
const maxWidthPx = Math.max(1, clamped.w - 2 * (profile.textInsetPx ?? 0))
|
|
393
408
|
const result = wrapper.wrap(el.text ?? "", {
|
|
394
|
-
maxWidthPx
|
|
409
|
+
maxWidthPx,
|
|
395
410
|
maxLines,
|
|
396
411
|
...(style.breakMode ? {breakMode: style.breakMode} : {}),
|
|
397
412
|
})
|
|
@@ -402,12 +417,12 @@ export function processScene(
|
|
|
402
417
|
// Trim the last line until it fits WITH the ellipsis appended —
|
|
403
418
|
// otherwise the extra glyph can overflow the clamped box width.
|
|
404
419
|
let last = lines[lines.length - 1]
|
|
405
|
-
while (last.length > 0 && !measurer.fitsInWidth(`${last}…`,
|
|
420
|
+
while (last.length > 0 && !measurer.fitsInWidth(`${last}…`, maxWidthPx)) {
|
|
406
421
|
last = last.slice(0, -1)
|
|
407
422
|
}
|
|
408
423
|
// A box narrower than the ellipsis glyph itself gets an empty line —
|
|
409
424
|
// never emit a line wider than the clamped box.
|
|
410
|
-
const ellipsized = last.length > 0 || measurer.fitsInWidth("…",
|
|
425
|
+
const ellipsized = last.length > 0 || measurer.fitsInWidth("…", maxWidthPx) ? `${last}…` : ""
|
|
411
426
|
lines = [...lines.slice(0, -1), ellipsized]
|
|
412
427
|
}
|
|
413
428
|
}
|