@glassly/miniapp 0.3.5 → 0.3.6-beta.851

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 (62) hide show
  1. package/README.md +16 -2
  2. package/dist/display/index.d.ts +2 -2
  3. package/dist/display/index.d.ts.map +1 -1
  4. package/dist/display/index.js +1 -1
  5. package/dist/display/index.js.map +1 -1
  6. package/dist/display/scene/types.d.ts +13 -0
  7. package/dist/display/scene/types.d.ts.map +1 -1
  8. package/dist/display/scene/types.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/manifest.d.ts +2 -2
  12. package/dist/manifest.d.ts.map +1 -1
  13. package/dist/manifest.js +2 -2
  14. package/dist/manifest.js.map +1 -1
  15. package/dist/mirror/canvas.d.ts.map +1 -1
  16. package/dist/mirror/canvas.js +17 -1
  17. package/dist/mirror/canvas.js.map +1 -1
  18. package/dist/mirror/geometry.d.ts +11 -0
  19. package/dist/mirror/geometry.d.ts.map +1 -1
  20. package/dist/mirror/geometry.js +27 -0
  21. package/dist/mirror/geometry.js.map +1 -1
  22. package/dist/modules/ai.d.ts +1 -1
  23. package/dist/modules/ai.d.ts.map +1 -1
  24. package/dist/modules/events.d.ts +17 -1
  25. package/dist/modules/events.d.ts.map +1 -1
  26. package/dist/modules/events.js.map +1 -1
  27. package/dist/modules/keys.d.ts +1 -1
  28. package/dist/modules/keys.d.ts.map +1 -1
  29. package/dist/modules/memory.d.ts +39 -0
  30. package/dist/modules/memory.d.ts.map +1 -1
  31. package/dist/modules/memory.js +19 -0
  32. package/dist/modules/memory.js.map +1 -1
  33. package/dist/modules/miniapps.d.ts +1 -0
  34. package/dist/modules/miniapps.d.ts.map +1 -1
  35. package/dist/modules/miniapps.js.map +1 -1
  36. package/dist/modules/widgets.d.ts +74 -12
  37. package/dist/modules/widgets.d.ts.map +1 -1
  38. package/dist/modules/widgets.js +45 -9
  39. package/dist/modules/widgets.js.map +1 -1
  40. package/dist/protocol.d.ts +8 -4
  41. package/dist/protocol.d.ts.map +1 -1
  42. package/dist/protocol.js +7 -3
  43. package/dist/protocol.js.map +1 -1
  44. package/dist/session.d.ts +1 -1
  45. package/dist/session.d.ts.map +1 -1
  46. package/dist/session.js +6 -1
  47. package/dist/session.js.map +1 -1
  48. package/package.json +2 -2
  49. package/src/display/index.ts +2 -1
  50. package/src/display/scene/types.ts +14 -0
  51. package/src/index.ts +5 -0
  52. package/src/manifest.ts +2 -2
  53. package/src/mirror/canvas.ts +17 -1
  54. package/src/mirror/geometry.ts +35 -1
  55. package/src/modules/ai.ts +1 -1
  56. package/src/modules/events.ts +18 -0
  57. package/src/modules/keys.ts +1 -1
  58. package/src/modules/memory.ts +56 -0
  59. package/src/modules/miniapps.ts +1 -0
  60. package/src/modules/widgets.ts +93 -16
  61. package/src/protocol.ts +7 -3
  62. package/src/session.ts +7 -2
@@ -6,9 +6,14 @@
6
6
  * The host hands it a box; the provider pushes a frame for that box with
7
7
  * `update()` whenever its state changes and takes the wearer's input
8
8
  * through `onEvent` while the widget is active: a tap on the selected
9
- * card activates it, a double tap hands input back. A provider runs in
10
- * widget mode while a host has its widget placed: no card in the app
11
- * switcher, no frame of its own on the glasses, no touch input of its own.
9
+ * card activates it, a double tap hands input back. A widget that lists
10
+ * the `EX` size is expanded by that tap instead while the wearer leaves
11
+ * expansion on (the miniapp's settings page, or `setExpandable`): the
12
+ * host gives it the full screen, the provider draws it with
13
+ * `update(elements, {view: "expanded"})`, and a double tap collapses it
14
+ * back to the card. A provider runs in widget mode while a host has its
15
+ * widget placed: no card in the app switcher, no frame of its own on the
16
+ * glasses, no touch input of its own.
12
17
  * - Host (HOST_WIDGETS): `list()` the declared widgets, `place()` one in a
13
18
  * box, hear its frames through `onFrame()`, forward input with `send()`
14
19
  * and `unplace()` it. A provider with nothing placed stops.
@@ -21,7 +26,10 @@ import type {RenderElement} from "./display"
21
26
  import {MiniappErrorCode, MiniappRequestType} from "../protocol"
22
27
  import {MiniappSession} from "../session"
23
28
 
24
- export type WidgetSize = "XS" | "SM" | "MD" | "LG"
29
+ export type WidgetSize = "XS" | "XSM" | "SM" | "MD" | "LG"
30
+
31
+ /** Which surface a frame draws: the card in its box, or the full-screen expanded view. */
32
+ export type WidgetView = "card" | "expanded"
25
33
 
26
34
  /** The box a widget draws into: its size name and its pixel extent. Frames use box-local coordinates. */
27
35
  export interface WidgetBox {
@@ -30,16 +38,35 @@ export interface WidgetBox {
30
38
  height: number
31
39
  }
32
40
 
41
+ /** The full-screen box an expanded widget draws into, in its own local coordinates. */
42
+ export interface WidgetExpandedBox {
43
+ width: number
44
+ height: number
45
+ }
46
+
33
47
  /** The wearer's input, forwarded by the host while the widget is active. */
34
48
  export type WidgetEvent =
35
49
  /** The wearer tapped the selected widget: it owns every swipe and tap until it is deactivated. */
36
50
  | {type: "activate"}
37
51
  /** The wearer double-tapped out, or the dashboard closed: show no selection. */
38
52
  | {type: "deactivate"}
39
- /** A swipe while active. */
40
- | {type: "swipe"; direction: "left" | "right" | "up" | "down"}
41
- /** A tap while active. */
53
+ /**
54
+ * The wearer tapped the selected widget and the host gave it the full
55
+ * screen: draw the expanded view for this box with `update(elements,
56
+ * {view: "expanded"})`. Like activate, it owns every swipe and tap until
57
+ * collapsed. Only sent to widgets that list `EX` with expansion on.
58
+ */
59
+ | {type: "expand"; width: number; height: number}
60
+ /** The wearer double-tapped back to the card, or the dashboard closed: back to the card, show no selection. */
61
+ | {type: "collapse"}
62
+ /** A swipe while active or expanded. `input2d` is set when it came from a 2D controller such as a Neural Band. */
63
+ | {type: "swipe"; direction: "left" | "right" | "up" | "down"; input2d?: boolean}
64
+ /** A tap while active or expanded. */
42
65
  | {type: "tap"}
66
+ /** The wearer pressed and held while active or expanded. */
67
+ | {type: "hold"}
68
+ /** The held press ended. Some controllers report a hold without a release. */
69
+ | {type: "release"}
43
70
  /** The host is about to show the widget: refresh what it draws, then return. */
44
71
  | {type: "beforeShow"}
45
72
 
@@ -57,10 +84,23 @@ export interface WidgetHandle {
57
84
  readonly id: string
58
85
  /** The box the host assigned, or null while unplaced. */
59
86
  readonly box: WidgetBox | null
60
- /** Push the frame for the current box. Coalesced by the host; drawn while the dashboard shows. */
61
- update(elements: RenderElement[]): void
87
+ /** The full-screen box while the host has the widget expanded, else null. */
88
+ readonly expanded: WidgetExpandedBox | null
89
+ /**
90
+ * Push the frame for the current box, or with `{view: "expanded"}` for the
91
+ * expanded box. Coalesced by the host; drawn while the dashboard shows.
92
+ */
93
+ update(elements: RenderElement[], opts?: {view?: WidgetView}): void
62
94
  /** Stop serving the widget. Calls still arrive until the host unplaces it. */
63
95
  release(): void
96
+ /** Whether a tap expands the widget; false when it does not list `EX`. */
97
+ getExpandable(): Promise<boolean>
98
+ /**
99
+ * Turn tap-to-expand on or off, the same choice the wearer makes on the
100
+ * miniapp's settings page; resolves with the setting now in force. Rejects
101
+ * for a widget that does not list `EX`.
102
+ */
103
+ setExpandable(expandable: boolean): Promise<boolean>
64
104
  }
65
105
 
66
106
  /** One declared widget, as a host lists it. */
@@ -71,6 +111,8 @@ export interface WidgetInfo {
71
111
  name: string
72
112
  sizes: WidgetSize[]
73
113
  defaultSize: WidgetSize
114
+ /** A tap on the selected widget expands it to the full screen instead of activating the card. */
115
+ expandable: boolean
74
116
  /** The provider's background context is up (in widget mode or opened by the wearer). */
75
117
  running: boolean
76
118
  /** The provider can run on the connected glasses. */
@@ -81,6 +123,7 @@ export interface WidgetFrame {
81
123
  packageName: string
82
124
  widgetId: string
83
125
  elements: RenderElement[]
126
+ view: WidgetView
84
127
  }
85
128
 
86
129
  export interface SendOptions {
@@ -106,6 +149,7 @@ const widgetKey = (packageName: string, widgetId: string): string => `${packageN
106
149
 
107
150
  class Handle implements WidgetHandle {
108
151
  box: WidgetBox | null = null
152
+ expanded: WidgetExpandedBox | null = null
109
153
 
110
154
  constructor(
111
155
  readonly id: string,
@@ -113,13 +157,21 @@ class Handle implements WidgetHandle {
113
157
  private readonly module: WidgetsModule,
114
158
  ) {}
115
159
 
116
- update(elements: RenderElement[]): void {
117
- this.module._update(this.id, elements)
160
+ update(elements: RenderElement[], opts?: {view?: WidgetView}): void {
161
+ this.module._update(this.id, elements, opts?.view ?? "card")
118
162
  }
119
163
 
120
164
  release(): void {
121
165
  this.module._release(this.id, this)
122
166
  }
167
+
168
+ getExpandable(): Promise<boolean> {
169
+ return this.module._expandable(this.id)
170
+ }
171
+
172
+ setExpandable(expandable: boolean): Promise<boolean> {
173
+ return this.module._expandable(this.id, expandable)
174
+ }
123
175
  }
124
176
 
125
177
  export class WidgetsModule {
@@ -154,8 +206,23 @@ export class WidgetsModule {
154
206
  }
155
207
 
156
208
  /** @internal */
157
- _update(widgetId: string, elements: RenderElement[]): void {
158
- this.session.sendOneShot({type: MiniappRequestType.WIDGET_UPDATE, widgetId, elements})
209
+ _update(widgetId: string, elements: RenderElement[], view: WidgetView): void {
210
+ this.session.sendOneShot({
211
+ type: MiniappRequestType.WIDGET_UPDATE,
212
+ widgetId,
213
+ elements,
214
+ ...(view === "expanded" ? {view} : {}),
215
+ })
216
+ }
217
+
218
+ /** @internal */
219
+ async _expandable(widgetId: string, expandable?: boolean): Promise<boolean> {
220
+ const result = await this.session.sendRequest<{expandable?: boolean}>({
221
+ type: MiniappRequestType.WIDGET_EXPANDABLE,
222
+ widgetId,
223
+ ...(expandable === undefined ? {} : {expandable}),
224
+ })
225
+ return result?.expandable === true
159
226
  }
160
227
 
161
228
  /** @internal */
@@ -200,10 +267,13 @@ export class WidgetsModule {
200
267
  try {
201
268
  if (kind === "box") {
202
269
  handle.box = box
270
+ if (!box) handle.expanded = null
203
271
  handle.hooks.onBox?.(box)
204
272
  this.reply(callId, true, null)
205
273
  return
206
274
  }
275
+ if (event?.type === "expand") handle.expanded = {width: event.width, height: event.height}
276
+ else if (event?.type === "collapse") handle.expanded = null
207
277
  const result = event && handle.hooks.onEvent ? await handle.hooks.onEvent(event) : undefined
208
278
  this.reply(callId, true, result ?? null)
209
279
  } catch (err) {
@@ -237,16 +307,23 @@ export class WidgetsModule {
237
307
  /**
238
308
  * Place a widget in a box. Starts the provider in widget mode when it is
239
309
  * not running, hands it the box, and resolves with the provider's last
240
- * frame for the widget, or null when it has not drawn yet. Placing again
241
- * with a new box resizes.
310
+ * card frame for the widget, or null when it has not drawn yet. A cached
311
+ * expanded frame is delivered to the `onFrame` listeners (with `view:
312
+ * "expanded"`) before it resolves. Placing again with a new box resizes.
242
313
  */
243
314
  async place(packageName: string, widgetId: string, box: WidgetBox): Promise<RenderElement[] | null> {
244
- const result = await this.session.sendRequest<{frame: RenderElement[] | null}>({
315
+ const result = await this.session.sendRequest<{
316
+ frame: RenderElement[] | null
317
+ expandedFrame?: RenderElement[] | null
318
+ }>({
245
319
  type: MiniappRequestType.WIDGET_PLACE,
246
320
  packageName,
247
321
  widgetId,
248
322
  box,
249
323
  })
324
+ if (Array.isArray(result?.expandedFrame)) {
325
+ this._deliverFrame({packageName, widgetId, elements: result.expandedFrame, view: "expanded"})
326
+ }
250
327
  return result?.frame ?? null
251
328
  }
252
329
 
package/src/protocol.ts CHANGED
@@ -183,6 +183,8 @@ export enum MiniappRequestType {
183
183
  MEMORY_QUERY = "miniapp_memory_query",
184
184
  MEMORY_WINDOW = "miniapp_memory_window",
185
185
  MEMORY_EXPORT_TEXT = "miniapp_memory_export_text",
186
+ MEMORY_EXPORT_FILE = "miniapp_memory_export_file",
187
+ MEMORY_IMPORT_FILE = "miniapp_memory_import_file",
186
188
  MEMORY_SUMMARY_ADD = "miniapp_memory_summary_add",
187
189
  MEMORY_SUMMARY_LIST = "miniapp_memory_summary_list",
188
190
  MEMORY_PENDING_SUMMARY_HOUR = "miniapp_memory_pending_summary_hour",
@@ -321,18 +323,20 @@ export enum MiniappRequestType {
321
323
  // A provider declares widgets in its manifest and serves them through
322
324
  // session.widgets.provide; a host (HOST_WIDGETS) places them and forwards
323
325
  // the wearer's input. The host routes every call; neither side names the other.
324
- /** Provider → host, one-shot: the latest frame for one of its widgets. */
326
+ /** Provider → host, one-shot: the latest frame for one of its widgets; `view: "expanded"` for the expanded view. */
325
327
  WIDGET_UPDATE = "miniapp_widget_update",
326
328
  /** Provider → host: the result of a delivered WIDGET_CALL, correlated by callId. */
327
329
  WIDGET_RESULT = "miniapp_widget_result",
328
330
  /** Every widget the installed miniapps declare. HOST_WIDGETS. */
329
331
  WIDGETS_LIST = "miniapp_widgets_list",
330
- /** Place a widget in a box: starts the provider in widget mode and hands it the box. HOST_WIDGETS. */
332
+ /** Place a widget in a box: starts the provider in widget mode and hands it the box. Answers {frame, expandedFrame}. HOST_WIDGETS. */
331
333
  WIDGET_PLACE = "miniapp_widget_place",
332
334
  /** Take a widget off: the provider stops once nothing of it is placed. HOST_WIDGETS. */
333
335
  WIDGET_UNPLACE = "miniapp_widget_unplace",
334
336
  /** Forward an input event to a placed widget and wait for its answer. HOST_WIDGETS. */
335
337
  WIDGET_SEND = "miniapp_widget_send",
338
+ /** Provider: read, or set with `expandable`, whether a tap expands one of its `EX` widgets. Answers {expandable}. */
339
+ WIDGET_EXPANDABLE = "miniapp_widget_expandable",
336
340
  }
337
341
 
338
342
  // ============================================================================
@@ -410,7 +414,7 @@ export enum MiniappResponseType {
410
414
  * expects a WIDGET_RESULT keyed by callId.
411
415
  */
412
416
  WIDGET_CALL = "miniapp_widget_call",
413
- /** Host → widget host: a provider pushed a new frame for a widget this app placed. */
417
+ /** Host → widget host: a provider pushed a new frame for a widget this app placed; `view: "expanded"` for the expanded view. */
414
418
  WIDGET_FRAME = "miniapp_widget_frame",
415
419
 
416
420
  /**
package/src/session.ts CHANGED
@@ -338,7 +338,7 @@ export class MiniappSession<TChannels extends object = any> {
338
338
  /**
339
339
  * User-supplied provider API keys (Settings > API Keys). Requires the
340
340
  * API_KEYS manifest permission; only an allowlisted provider set is
341
- * readable (anthropic / gemini / elevenlabs / mapbox — never soniox).
341
+ * readable (anthropic / gemini / openai / deepseek / elevenlabs / mapbox — never soniox).
342
342
  */
343
343
  /**
344
344
  * Platform LLM completions. Requires the AI manifest permission. The
@@ -957,7 +957,12 @@ export class MiniappSession<TChannels extends object = any> {
957
957
  const packageName = payload.packageName as string | undefined
958
958
  const widgetId = payload.widgetId as string | undefined
959
959
  if (!packageName || !widgetId || !Array.isArray(payload.elements)) return
960
- this.widgets._deliverFrame({packageName, widgetId, elements: payload.elements} as WidgetFrame)
960
+ this.widgets._deliverFrame({
961
+ packageName,
962
+ widgetId,
963
+ elements: payload.elements,
964
+ view: payload.view === "expanded" ? "expanded" : "card",
965
+ } as WidgetFrame)
961
966
  return
962
967
  }
963
968