@glassly/engine 0.3.7-dev.905 → 0.3.7-dev.909
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.map +1 -1
- package/build/services/JSRouter.js +6 -1
- package/build/services/JSRouter.js.map +1 -1
- package/build/services/SceneRenderer.js +1 -1
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +11 -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 +6 -1
- package/src/services/SceneRenderer.ts +1 -1
- package/src/stores/apps.ts +11 -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
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
|
|
@@ -543,7 +547,12 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
|
|
|
543
547
|
},
|
|
544
548
|
|
|
545
549
|
saveScreenshot: async (packageName: string, screenshot: string) => {
|
|
546
|
-
|
|
550
|
+
const key = `${packageName}_screenshot`
|
|
551
|
+
if (useSettingsStore.getState().getSetting(SETTINGS.persist_miniapp_screenshots.key) !== false) {
|
|
552
|
+
storage.save(key, screenshot)
|
|
553
|
+
} else {
|
|
554
|
+
storage.remove(key)
|
|
555
|
+
}
|
|
547
556
|
set((s) => ({
|
|
548
557
|
apps: s.apps.map((a) => (a.packageName === packageName ? {...a, screenshot} : a)),
|
|
549
558
|
}))
|
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
|
}
|