@glassly/engine 0.3.5-beta.788 → 0.3.5-beta.795
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/engine.d.ts +1 -0
- package/build/engine.d.ts.map +1 -1
- package/build/facades/miniapps.d.ts +2 -0
- package/build/facades/miniapps.d.ts.map +1 -1
- package/build/facades/miniapps.js +2 -0
- package/build/facades/miniapps.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AppRegistry.d.ts +4 -1
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +16 -2
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/GlassesTouchInput.d.ts.map +1 -1
- package/build/services/GlassesTouchInput.js +5 -4
- package/build/services/GlassesTouchInput.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +2 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +27 -3
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/manifestActions.d.ts +4 -2
- package/build/services/manifestActions.d.ts.map +1 -1
- package/build/services/manifestActions.js +8 -3
- package/build/services/manifestActions.js.map +1 -1
- package/build/stores/apps.d.ts +6 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +16 -1
- package/build/stores/apps.js.map +1 -1
- package/build/types/applet.d.ts +3 -1
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js.map +1 -1
- package/package.json +7 -7
- package/src/facades/miniapps.ts +3 -0
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/AppRegistry.ts +18 -3
- package/src/services/GlassesTouchInput.ts +5 -4
- package/src/services/LocalMiniappRuntime.ts +28 -3
- package/src/services/manifestActions.ts +8 -3
- package/src/stores/apps.ts +23 -1
- package/src/types/applet.ts +3 -1
|
@@ -22,7 +22,7 @@ import {unzip} from "react-native-zip-archive"
|
|
|
22
22
|
import semver from "semver"
|
|
23
23
|
import {AsyncResult, Result, result as Res} from "typesafe-ts"
|
|
24
24
|
|
|
25
|
-
import type {AppletPermission, AppPermissionType, AppletType, ClientApp} from "../types/applet"
|
|
25
|
+
import type {AppletPermission, AppPermissionType, AppletType, ClientApp, DeclaredWidget} from "../types/applet"
|
|
26
26
|
import {HardwareRequirement, HardwareRequirementLevel, HardwareType} from "../types"
|
|
27
27
|
import {configuredDevHost} from "../utils/configuredDevHost"
|
|
28
28
|
import {storage} from "../utils/storage/storage"
|
|
@@ -776,7 +776,7 @@ class AppRegistry {
|
|
|
776
776
|
|
|
777
777
|
// Declared actions (for session.miniapps.list + invoke gating).
|
|
778
778
|
const actions = normalizeManifestActions(manifest?.actions)
|
|
779
|
-
const widgets = normalizeManifestWidgets(manifest?.widgets)
|
|
779
|
+
const widgets = withWidgetExpandChoices(lmaInfo.packageName, normalizeManifestWidgets(manifest?.widgets))
|
|
780
780
|
|
|
781
781
|
// Dev miniapps live in the same lmas/ tree as installed ones, but
|
|
782
782
|
// their version directory name starts with "dev-".
|
|
@@ -864,7 +864,9 @@ class AppRegistry {
|
|
|
864
864
|
permissions,
|
|
865
865
|
hardwareRequirements,
|
|
866
866
|
actions: normalizeManifestActions(rec.actions),
|
|
867
|
-
...(normalizeManifestWidgets(rec.widgets).length > 0
|
|
867
|
+
...(normalizeManifestWidgets(rec.widgets).length > 0
|
|
868
|
+
? {widgets: withWidgetExpandChoices(rec.packageName, normalizeManifestWidgets(rec.widgets))}
|
|
869
|
+
: {}),
|
|
868
870
|
isMiniappDev: true,
|
|
869
871
|
devUrl: rec.devUrl,
|
|
870
872
|
devPort: rec.devPort,
|
|
@@ -948,6 +950,19 @@ export function saveAlwaysOnOverride(packageName: string, status: boolean): void
|
|
|
948
950
|
storage.save(`${packageName}_always_on`, status)
|
|
949
951
|
}
|
|
950
952
|
|
|
953
|
+
/** Applies the wearer's tap-to-expand choice to each expandable widget; expansion is on until they turn it off. */
|
|
954
|
+
export function withWidgetExpandChoices(packageName: string, widgets: DeclaredWidget[]): DeclaredWidget[] {
|
|
955
|
+
return widgets.map((widget) => {
|
|
956
|
+
if (!widget.expandable) return widget
|
|
957
|
+
const res = storage.load<boolean>(`${packageName}_widget_expand_${widget.id}`)
|
|
958
|
+
return {...widget, expandOnTap: res.is_ok() ? res.value !== false : true}
|
|
959
|
+
})
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
export function saveWidgetExpandChoice(packageName: string, widgetId: string, expandOnTap: boolean): void {
|
|
963
|
+
storage.save(`${packageName}_widget_expand_${widgetId}`, expandOnTap)
|
|
964
|
+
}
|
|
965
|
+
|
|
951
966
|
export function getLocalAppScreenshot(packageName: string): string | undefined {
|
|
952
967
|
const key = `${packageName}_screenshot`
|
|
953
968
|
const res = storage.load<string>(key)
|
|
@@ -107,12 +107,13 @@ export function dispatchTouchEvent(event: TouchEvent): void {
|
|
|
107
107
|
function routeTouchEvent(event: TouchEvent): void {
|
|
108
108
|
const gesture = event.gestureName
|
|
109
109
|
const claimant = localMiniappRuntime.gestureClaimant(gesture)
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
|
|
110
|
+
const doubleTap = gesture === "double_tap"
|
|
111
|
+
// The Neural Band's middle finger (index 1) shares the tap names. Either
|
|
112
|
+
// finger leaves the app on screen; the firmware dashboard is the index finger's.
|
|
113
|
+
const osDoubleTap = doubleTap && event.index !== 1
|
|
113
114
|
const osHeadUp = gesture === "head_up"
|
|
114
115
|
|
|
115
|
-
if (
|
|
116
|
+
if (doubleTap) {
|
|
116
117
|
// The app being left opts out of OS exit by registering double tap (the
|
|
117
118
|
// terminal's back-to-menu). The latest claimant still receives it. Otherwise
|
|
118
119
|
// the tap means "leave the app". The controller shows the bounded OS
|
|
@@ -1824,6 +1824,9 @@ class LocalMiniappRuntime {
|
|
|
1824
1824
|
case MiniappRequestType.WIDGET_RESULT:
|
|
1825
1825
|
this.handleWidgetResult(packageName, payload)
|
|
1826
1826
|
break
|
|
1827
|
+
case MiniappRequestType.WIDGET_EXPANDABLE:
|
|
1828
|
+
this.handleWidgetExpandable(packageName, payload, requestId)
|
|
1829
|
+
break
|
|
1827
1830
|
case MiniappRequestType.SYSTEM_EXIT:
|
|
1828
1831
|
void this.handleSystemExit(packageName, requestId)
|
|
1829
1832
|
break
|
|
@@ -6063,6 +6066,8 @@ class LocalMiniappRuntime {
|
|
|
6063
6066
|
private async startInteropApp(packageName: string): Promise<boolean> {
|
|
6064
6067
|
const app = this.interopApps().find((a) => a.packageName === packageName)
|
|
6065
6068
|
if (!app) return false
|
|
6069
|
+
// Running only for its widget: start() promotes it to a normal run, as opening it on the phone does.
|
|
6070
|
+
if (app.widgetOnly) return useAppStatusStore.getState().start(app, {skipNavigation: true})
|
|
6066
6071
|
if (app.local) {
|
|
6067
6072
|
await miniappLauncher.ensureConnected(packageName)
|
|
6068
6073
|
return true
|
|
@@ -6124,7 +6129,8 @@ class LocalMiniappRuntime {
|
|
|
6124
6129
|
packageName: app.packageName,
|
|
6125
6130
|
name: app.name,
|
|
6126
6131
|
version: app.version ?? "",
|
|
6127
|
-
running
|
|
6132
|
+
// A provider running only for a placed widget is not a run the wearer started.
|
|
6133
|
+
running: app.running && !app.widgetOnly,
|
|
6128
6134
|
hidden: app.hidden === true,
|
|
6129
6135
|
system: isSystemMiniapp(app),
|
|
6130
6136
|
compatibility,
|
|
@@ -6368,7 +6374,7 @@ class LocalMiniappRuntime {
|
|
|
6368
6374
|
name: widget.name,
|
|
6369
6375
|
sizes: widget.sizes,
|
|
6370
6376
|
defaultSize: widget.defaultSize,
|
|
6371
|
-
expandable: widget.
|
|
6377
|
+
expandable: widget.expandOnTap,
|
|
6372
6378
|
running: app.running,
|
|
6373
6379
|
compatible: app.compatibility?.isCompatible !== false,
|
|
6374
6380
|
})
|
|
@@ -6577,6 +6583,25 @@ class LocalMiniappRuntime {
|
|
|
6577
6583
|
}
|
|
6578
6584
|
}
|
|
6579
6585
|
|
|
6586
|
+
/** Provider: read, or set with `expandable`, whether a tap expands one of its own `EX` widgets. */
|
|
6587
|
+
private handleWidgetExpandable(packageName: string, payload: Record<string, unknown>, requestId?: string): void {
|
|
6588
|
+
const widgetId = payload.widgetId as string | undefined
|
|
6589
|
+
const app = this.interopApps().find((a) => a.packageName === packageName)
|
|
6590
|
+
const widget = app && widgetId ? this.declaredWidget(app, widgetId) : undefined
|
|
6591
|
+
if (!widget?.expandable) {
|
|
6592
|
+
this.sendResult(packageName, requestId, false, undefined, {
|
|
6593
|
+
code: MiniappErrorCode.WIDGET_NOT_FOUND,
|
|
6594
|
+
message: `${packageName} has no widget "${widgetId ?? "(missing widgetId)"}" that lists EX`,
|
|
6595
|
+
})
|
|
6596
|
+
return
|
|
6597
|
+
}
|
|
6598
|
+
const expandable =
|
|
6599
|
+
typeof payload.expandable === "boolean"
|
|
6600
|
+
? useAppStatusStore.getState().setWidgetExpand(packageName, widget.id, payload.expandable)
|
|
6601
|
+
: widget.expandOnTap
|
|
6602
|
+
this.sendResult(packageName, requestId, true, {expandable})
|
|
6603
|
+
}
|
|
6604
|
+
|
|
6580
6605
|
/** Provider → host, one-shot: cache the frame for its view and push it to every host holding the widget. */
|
|
6581
6606
|
private handleWidgetUpdate(providerPackageName: string, payload: Record<string, unknown>): void {
|
|
6582
6607
|
const widgetId = payload.widgetId as string | undefined
|
|
@@ -6588,7 +6613,7 @@ class LocalMiniappRuntime {
|
|
|
6588
6613
|
const view: WidgetView = payload.view === "expanded" ? "expanded" : "card"
|
|
6589
6614
|
if (view === "expanded" && !widget.expandable) {
|
|
6590
6615
|
console.warn(
|
|
6591
|
-
`${LOG_TAG}: dropping expanded frame from ${providerPackageName}/${widgetId}: not
|
|
6616
|
+
`${LOG_TAG}: dropping expanded frame from ${providerPackageName}/${widgetId}: it does not list EX`,
|
|
6592
6617
|
)
|
|
6593
6618
|
return
|
|
6594
6619
|
}
|
|
@@ -84,9 +84,11 @@ const isWidgetSize = (value: unknown): value is DeclaredWidgetSize => (WIDGET_SI
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Normalize manifest widgets for installed and dev-sideloaded bundles: an
|
|
87
|
-
* entry without an id, a name or a usable size is dropped, repeats of a
|
|
87
|
+
* entry without an id, a name or a usable card size is dropped, repeats of a
|
|
88
88
|
* size are folded, and a default size the widget does not offer becomes
|
|
89
|
-
* its first size.
|
|
89
|
+
* its first size. Listing `EX` makes a widget expandable; it is not a card
|
|
90
|
+
* size. Taps expand it until the wearer turns that off (AppRegistry applies
|
|
91
|
+
* the choice).
|
|
90
92
|
*/
|
|
91
93
|
export function normalizeManifestWidgets(raw: unknown): DeclaredWidget[] {
|
|
92
94
|
if (!Array.isArray(raw)) return []
|
|
@@ -103,12 +105,15 @@ export function normalizeManifestWidgets(raw: unknown): DeclaredWidget[] {
|
|
|
103
105
|
const sizes = Array.isArray(w.sizes) ? Array.from(new Set(w.sizes.filter(isWidgetSize))) : []
|
|
104
106
|
if (sizes.length === 0) continue
|
|
105
107
|
seen.add(w.id)
|
|
108
|
+
// `expandable: true` is how bundles from before `EX` said the same.
|
|
109
|
+
const expandable = (Array.isArray(w.sizes) && w.sizes.includes("EX")) || w.expandable === true
|
|
106
110
|
out.push({
|
|
107
111
|
id: w.id,
|
|
108
112
|
name: w.name,
|
|
109
113
|
sizes,
|
|
110
114
|
defaultSize: isWidgetSize(w.defaultSize) && sizes.includes(w.defaultSize) ? w.defaultSize : sizes[0],
|
|
111
|
-
expandable
|
|
115
|
+
expandable,
|
|
116
|
+
expandOnTap: expandable,
|
|
112
117
|
})
|
|
113
118
|
}
|
|
114
119
|
return out
|
package/src/stores/apps.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {DeviceTypes} from "../types/enums"
|
|
|
19
19
|
import {getModelCapabilities} from "../types/hardware"
|
|
20
20
|
import {HardwareCompatibility, type CompatibilityResult} from "../utils/hardware/hardware"
|
|
21
21
|
import {storage} from "../utils/storage/storage"
|
|
22
|
-
import appRegistry, {saveAlwaysOnOverride} from "../services/AppRegistry"
|
|
22
|
+
import appRegistry, {saveAlwaysOnOverride, saveWidgetExpandChoice} from "../services/AppRegistry"
|
|
23
23
|
import localDisplayManager from "../services/LocalDisplayManager"
|
|
24
24
|
import {islandNotifications} from "../services/NotificationsEmitter"
|
|
25
25
|
import sttModelManager from "../services/STTModelManager"
|
|
@@ -103,6 +103,12 @@ interface AppStatusState {
|
|
|
103
103
|
* off leaves it running. Persists the wearer's choice beside the hidden flag.
|
|
104
104
|
*/
|
|
105
105
|
setAlwaysOn: (packageName: string, status: boolean) => Promise<void>
|
|
106
|
+
/**
|
|
107
|
+
* Whether a tap on one of the miniapp's dashboard widgets expands it to the
|
|
108
|
+
* full screen. Only widgets that list `EX` expand; false for the rest.
|
|
109
|
+
* Persists the choice and answers the setting now in force.
|
|
110
|
+
*/
|
|
111
|
+
setWidgetExpand: (packageName: string, widgetId: string, expandOnTap: boolean) => boolean
|
|
106
112
|
/** Replace the whole apps array. Hosts use this when their extra source changes. */
|
|
107
113
|
setApps: (apps: ClientApp[]) => void
|
|
108
114
|
}
|
|
@@ -587,6 +593,22 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
|
|
|
587
593
|
if (status && !app.running) await get().start(app, {skipNavigation: true})
|
|
588
594
|
},
|
|
589
595
|
|
|
596
|
+
setWidgetExpand: (packageName, widgetId, expandOnTap) => {
|
|
597
|
+
const app = get().apps.find((a) => a.packageName === packageName)
|
|
598
|
+
const widget = app?.widgets?.find((w) => w.id === widgetId)
|
|
599
|
+
if (!widget?.expandable) return false
|
|
600
|
+
if (widget.expandOnTap === expandOnTap) return expandOnTap
|
|
601
|
+
saveWidgetExpandChoice(packageName, widgetId, expandOnTap)
|
|
602
|
+
set((s) => ({
|
|
603
|
+
apps: s.apps.map((a) =>
|
|
604
|
+
a.packageName === packageName
|
|
605
|
+
? {...a, widgets: a.widgets?.map((w) => (w.id === widgetId ? {...w, expandOnTap} : w))}
|
|
606
|
+
: a,
|
|
607
|
+
),
|
|
608
|
+
}))
|
|
609
|
+
return expandOnTap
|
|
610
|
+
},
|
|
611
|
+
|
|
590
612
|
setApps: (apps) => set({apps}),
|
|
591
613
|
}))
|
|
592
614
|
|
package/src/types/applet.ts
CHANGED
|
@@ -97,8 +97,10 @@ export interface DeclaredWidget {
|
|
|
97
97
|
name: string
|
|
98
98
|
sizes: DeclaredWidgetSize[]
|
|
99
99
|
defaultSize: DeclaredWidgetSize
|
|
100
|
-
/**
|
|
100
|
+
/** Lists the `EX` size: a tap on the selected widget can expand it to the full screen. */
|
|
101
101
|
expandable: boolean
|
|
102
|
+
/** A tap expands it rather than activating the card: expandable, and the wearer left expansion on. */
|
|
103
|
+
expandOnTap: boolean
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
export interface ClientApp extends AppletInterface {
|