@glassly/engine 0.3.4-dev.704 → 0.3.5-dev.705

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 (47) hide show
  1. package/build/generated/releaseMetadata.js +4 -4
  2. package/build/generated/releaseMetadata.js.map +1 -1
  3. package/build/index.d.ts +1 -1
  4. package/build/index.d.ts.map +1 -1
  5. package/build/index.js +1 -1
  6. package/build/index.js.map +1 -1
  7. package/build/services/AppRegistry.d.ts +3 -1
  8. package/build/services/AppRegistry.d.ts.map +1 -1
  9. package/build/services/AppRegistry.js +5 -2
  10. package/build/services/AppRegistry.js.map +1 -1
  11. package/build/services/LocalMiniappRuntime.d.ts +32 -0
  12. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  13. package/build/services/LocalMiniappRuntime.js +332 -1
  14. package/build/services/LocalMiniappRuntime.js.map +1 -1
  15. package/build/services/MiniappLauncher.d.ts.map +1 -1
  16. package/build/services/MiniappLauncher.js +2 -0
  17. package/build/services/MiniappLauncher.js.map +1 -1
  18. package/build/services/MiniappWidgetMode.d.ts +24 -0
  19. package/build/services/MiniappWidgetMode.d.ts.map +1 -0
  20. package/build/services/MiniappWidgetMode.js +58 -0
  21. package/build/services/MiniappWidgetMode.js.map +1 -0
  22. package/build/services/manifestActions.d.ts +8 -1
  23. package/build/services/manifestActions.d.ts.map +1 -1
  24. package/build/services/manifestActions.js +29 -0
  25. package/build/services/manifestActions.js.map +1 -1
  26. package/build/services/widgetPlacements.d.ts +43 -0
  27. package/build/services/widgetPlacements.d.ts.map +1 -0
  28. package/build/services/widgetPlacements.js +83 -0
  29. package/build/services/widgetPlacements.js.map +1 -0
  30. package/build/stores/apps.d.ts +3 -1
  31. package/build/stores/apps.d.ts.map +1 -1
  32. package/build/stores/apps.js +37 -11
  33. package/build/stores/apps.js.map +1 -1
  34. package/build/types/applet.d.ts +16 -0
  35. package/build/types/applet.d.ts.map +1 -1
  36. package/build/types/applet.js.map +1 -1
  37. package/package.json +7 -7
  38. package/src/generated/releaseMetadata.ts +4 -4
  39. package/src/index.ts +1 -0
  40. package/src/services/AppRegistry.ts +8 -2
  41. package/src/services/LocalMiniappRuntime.ts +362 -2
  42. package/src/services/MiniappLauncher.ts +3 -0
  43. package/src/services/MiniappWidgetMode.ts +62 -0
  44. package/src/services/manifestActions.ts +29 -1
  45. package/src/services/widgetPlacements.ts +121 -0
  46. package/src/stores/apps.ts +45 -11
  47. package/src/types/applet.ts +18 -0
@@ -26,6 +26,7 @@ import sttModelManager from "../services/STTModelManager"
26
26
  import {miniappLauncher} from "../services/MiniappLauncher"
27
27
  import {isMiniappsPaused, onMiniappsPauseChange} from "../services/MiniappPauseCoordinator"
28
28
  import {miniappRunningRegistry} from "../services/MiniappRunningRegistry"
29
+ import {miniappWidgetMode} from "../services/MiniappWidgetMode"
29
30
  import {defaultHiddenStatus, isGatedByDevTools, isGatedUntilPaired, resolveHiddenStatus} from "./appVisibility"
30
31
  import {SETTINGS, useSettingsStore} from "./settings"
31
32
  import BluetoothSdk from "@glassly/bluetooth-sdk"
@@ -373,16 +374,21 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
373
374
 
374
375
  // If the tapped app is already running, this is a re-open: the nav/foreground
375
376
  // above already drove the animation, and there's nothing to spawn. Short-
376
- // circuit before the spawn pipeline.
377
- if (app.running) {
377
+ // circuit before the spawn pipeline. An app running only for its widget
378
+ // is promoted instead: it keeps its context and takes the foreground.
379
+ if (app.running && !app.widgetOnly) {
378
380
  saveLastOpenTime(packageName)
379
381
  return true
380
382
  }
383
+ if (app.widgetOnly) miniappWidgetMode.setWidgetOnly(packageName, false)
381
384
 
382
385
  // Only-one rules: one standard app in the foreground, one dashboard
383
- // drawing the overlay. Starting another stops the one running.
386
+ // drawing the overlay. Starting another stops the one running. Widget-only
387
+ // peers hold no foreground, so they stay.
384
388
  if (app.type === "standard" || app.type === "dashboard") {
385
- const runningPeers = state.apps.filter((a) => a.running && a.type === app.type && a.packageName !== packageName)
389
+ const runningPeers = state.apps.filter(
390
+ (a) => a.running && !a.widgetOnly && a.type === app.type && a.packageName !== packageName,
391
+ )
386
392
  for (const r of runningPeers) {
387
393
  await get().stop(r.packageName)
388
394
  }
@@ -429,6 +435,22 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
429
435
  return
430
436
  }
431
437
 
438
+ // A placed widget provider is the dashboard's to keep: closing it from
439
+ // the tray takes its card and its frame, and it runs on in widget mode.
440
+ if (app.local && app.running && miniappWidgetMode.isPlaced(packageName)) {
441
+ if (!app.widgetOnly) {
442
+ miniappWidgetMode.setWidgetOnly(packageName, true)
443
+ localDisplayManager.onUnmount(packageName)
444
+ set((s) => ({
445
+ foregroundedPackage: s.foregroundedPackage === packageName ? null : s.foregroundedPackage,
446
+ apps: s.apps.map((a) =>
447
+ a.packageName === packageName ? {...a, screenshot: undefined, foregrounded: false, loading: false} : a,
448
+ ),
449
+ }))
450
+ }
451
+ return
452
+ }
453
+
432
454
  set((s) => ({
433
455
  apps: s.apps.map((a) =>
434
456
  a.packageName === packageName ? {...a, running: false, screenshot: undefined, loading: false} : a,
@@ -494,7 +516,8 @@ export const useAppStatusStore = create<AppStatusState>((set, get) => ({
494
516
  stopAll: () => {
495
517
  return Res.try_async(async () => {
496
518
  // Always On apps stay up; they stop one at a time from the switcher.
497
- const running = get().apps.filter((a) => a.running && !a.alwaysOn)
519
+ // Widget-only apps have no card to close; they are the dashboard's.
520
+ const running = get().apps.filter((a) => a.running && !a.alwaysOn && !a.widgetOnly)
498
521
  for (const a of running) {
499
522
  await get().stop(a.packageName)
500
523
  }
@@ -578,15 +601,17 @@ function projectLocalRunningState(): void {
578
601
  if (!app.local) return app
579
602
  const next = running.has(app.packageName)
580
603
  const paused = next && pausedNow
581
- if (app.running === next && (app.paused ?? false) === paused) return app
604
+ const widgetOnly = next && miniappWidgetMode.isWidgetOnly(app.packageName)
605
+ if (app.running === next && (app.paused ?? false) === paused && (app.widgetOnly ?? false) === widgetOnly) return app
582
606
  changed = true
583
- return {...app, running: next, paused}
607
+ return {...app, running: next, paused, widgetOnly}
584
608
  })
585
609
  if (changed) {
586
610
  useAppStatusStore.setState({apps: updated})
587
611
  }
588
612
  }
589
613
  miniappRunningRegistry.subscribe(projectLocalRunningState)
614
+ miniappWidgetMode.subscribe(projectLocalRunningState)
590
615
  onMiniappsPauseChange(projectLocalRunningState)
591
616
 
592
617
  // The display-manager's core (foreground) slot is DERIVED state: always the
@@ -596,7 +621,7 @@ onMiniappsPauseChange(projectLocalRunningState)
596
621
  // sites, so no launch/stop path can forget to claim or release the slot.
597
622
  // onCoreAppChange is idempotent, so firing on every store write is free.
598
623
  useAppStatusStore.subscribe((state) => {
599
- const core = state.apps.find((a) => a.running && (a.type === "standard" || !a.type))
624
+ const core = state.apps.find((a) => a.running && !a.widgetOnly && (a.type === "standard" || !a.type))
600
625
  localDisplayManager.onCoreAppChange(core?.packageName ?? null)
601
626
  })
602
627
 
@@ -657,10 +682,16 @@ export const useStopAll = () => useAppStatusStore((state) => state.stopAll)
657
682
  export const useInstall = () => useAppStatusStore((state) => state.install)
658
683
  export const useUninstall = () => useAppStatusStore((state) => state.uninstall)
659
684
 
660
- /** Every running app; each one has a card in the app switcher tray. */
685
+ /** Every running app with a card in the app switcher tray: widget-only apps have none. */
661
686
  export const useActiveApps = () => {
662
687
  const apps = useApps()
663
- return useMemo(() => apps.filter((app) => app.running), [apps])
688
+ return useMemo(() => apps.filter((app) => app.running && !app.widgetOnly), [apps])
689
+ }
690
+
691
+ /** Apps running only for a widget the dashboard placed; the switcher's Widgets pill lists them. */
692
+ export const useWidgetApps = () => {
693
+ const apps = useApps()
694
+ return useMemo(() => apps.filter((app) => app.running && app.widgetOnly), [apps])
664
695
  }
665
696
 
666
697
  /** Always On apps whose background task is running, card or no card. */
@@ -690,7 +721,10 @@ export const useBackgroundApps = () => {
690
721
 
691
722
  export const useActiveForegroundApp = () => {
692
723
  const apps = useApps()
693
- return useMemo(() => apps.find((app) => (app.type === "standard" || !app.type) && app.running) ?? null, [apps])
724
+ return useMemo(
725
+ () => apps.find((app) => (app.type === "standard" || !app.type) && app.running && !app.widgetOnly) ?? null,
726
+ [apps],
727
+ )
694
728
  }
695
729
 
696
730
  /**
@@ -89,6 +89,16 @@ export interface DeclaredAction {
89
89
  visibility?: "public" | "system"
90
90
  }
91
91
 
92
+ export type DeclaredWidgetSize = "XS" | "SM" | "MD" | "LG"
93
+
94
+ /** A dashboard widget a miniapp declares in `miniapp.json` and serves through `session.widgets`. */
95
+ export interface DeclaredWidget {
96
+ id: string
97
+ name: string
98
+ sizes: DeclaredWidgetSize[]
99
+ defaultSize: DeclaredWidgetSize
100
+ }
101
+
92
102
  export interface ClientApp extends AppletInterface {
93
103
  offline: boolean
94
104
  offlineRoute: string
@@ -124,4 +134,12 @@ export interface ClientApp extends AppletInterface {
124
134
  isMiniappDev?: boolean
125
135
  /** Manifest-declared actions (for session.miniapps.list + invoke gating). */
126
136
  actions?: DeclaredAction[]
137
+ /** Manifest-declared dashboard widgets. */
138
+ widgets?: DeclaredWidget[]
139
+ /**
140
+ * Running only because a host placed one of its widgets: no card in the
141
+ * app switcher, no frame of its own on the glasses. Opening the app from
142
+ * the home screen promotes it to a normal run.
143
+ */
144
+ widgetOnly?: boolean
127
145
  }