@mihe-dev/js 0.0.0-stage → 0.2.0

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -2
  3. package/dist/audio.d.ts +121 -0
  4. package/dist/audio.d.ts.map +1 -0
  5. package/dist/audio.js +377 -0
  6. package/dist/audio.js.map +1 -0
  7. package/dist/card-view.d.ts +79 -0
  8. package/dist/card-view.d.ts.map +1 -0
  9. package/dist/card-view.js +99 -0
  10. package/dist/card-view.js.map +1 -0
  11. package/dist/chart.d.ts +15 -0
  12. package/dist/chart.d.ts.map +1 -0
  13. package/dist/chart.js +58 -0
  14. package/dist/chart.js.map +1 -0
  15. package/dist/client.d.ts +226 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.js +358 -0
  18. package/dist/client.js.map +1 -0
  19. package/dist/commands.d.ts +70 -0
  20. package/dist/commands.d.ts.map +1 -0
  21. package/dist/commands.js +295 -0
  22. package/dist/commands.js.map +1 -0
  23. package/dist/debug.d.ts +17 -0
  24. package/dist/debug.d.ts.map +1 -0
  25. package/dist/debug.js +29 -0
  26. package/dist/debug.js.map +1 -0
  27. package/dist/declarations.d.ts +160 -0
  28. package/dist/declarations.d.ts.map +1 -0
  29. package/dist/declarations.js +171 -0
  30. package/dist/declarations.js.map +1 -0
  31. package/dist/highlight.d.ts +77 -0
  32. package/dist/highlight.d.ts.map +1 -0
  33. package/dist/highlight.js +233 -0
  34. package/dist/highlight.js.map +1 -0
  35. package/dist/index.d.ts +41 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +32 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mihe.d.ts +364 -0
  40. package/dist/mihe.d.ts.map +1 -0
  41. package/dist/mihe.iife.min.js +217 -0
  42. package/dist/mihe.iife.min.js.map +7 -0
  43. package/dist/mihe.js +758 -0
  44. package/dist/mihe.js.map +1 -0
  45. package/dist/mihe.min.js +217 -0
  46. package/dist/mihe.min.js.map +7 -0
  47. package/dist/store.d.ts +175 -0
  48. package/dist/store.d.ts.map +1 -0
  49. package/dist/store.js +208 -0
  50. package/dist/store.js.map +1 -0
  51. package/dist/voice.d.ts +192 -0
  52. package/dist/voice.d.ts.map +1 -0
  53. package/dist/voice.js +443 -0
  54. package/dist/voice.js.map +1 -0
  55. package/dist/widget/card.d.ts +17 -0
  56. package/dist/widget/card.d.ts.map +1 -0
  57. package/dist/widget/card.js +31 -0
  58. package/dist/widget/card.js.map +1 -0
  59. package/dist/widget/dom.d.ts +24 -0
  60. package/dist/widget/dom.d.ts.map +1 -0
  61. package/dist/widget/dom.js +49 -0
  62. package/dist/widget/dom.js.map +1 -0
  63. package/dist/widget/element.d.ts +88 -0
  64. package/dist/widget/element.d.ts.map +1 -0
  65. package/dist/widget/element.js +285 -0
  66. package/dist/widget/element.js.map +1 -0
  67. package/dist/widget/format.d.ts +24 -0
  68. package/dist/widget/format.d.ts.map +1 -0
  69. package/dist/widget/format.js +64 -0
  70. package/dist/widget/format.js.map +1 -0
  71. package/dist/widget/icons.d.ts +5 -0
  72. package/dist/widget/icons.d.ts.map +1 -0
  73. package/dist/widget/icons.js +19 -0
  74. package/dist/widget/icons.js.map +1 -0
  75. package/dist/widget/launcher.d.ts +13 -0
  76. package/dist/widget/launcher.d.ts.map +1 -0
  77. package/dist/widget/launcher.js +27 -0
  78. package/dist/widget/launcher.js.map +1 -0
  79. package/dist/widget/mark.d.ts +10 -0
  80. package/dist/widget/mark.d.ts.map +1 -0
  81. package/dist/widget/mark.js +20 -0
  82. package/dist/widget/mark.js.map +1 -0
  83. package/dist/widget/orb.d.ts +92 -0
  84. package/dist/widget/orb.d.ts.map +1 -0
  85. package/dist/widget/orb.js +500 -0
  86. package/dist/widget/orb.js.map +1 -0
  87. package/dist/widget/panel.d.ts +40 -0
  88. package/dist/widget/panel.d.ts.map +1 -0
  89. package/dist/widget/panel.js +240 -0
  90. package/dist/widget/panel.js.map +1 -0
  91. package/dist/widget/styles.d.ts +22 -0
  92. package/dist/widget/styles.d.ts.map +1 -0
  93. package/dist/widget/styles.js +192 -0
  94. package/dist/widget/styles.js.map +1 -0
  95. package/package.json +43 -4
@@ -0,0 +1,175 @@
1
+ /**
2
+ * The app's declaration of itself.
3
+ *
4
+ * Spec §5.1 — the host app publishes assistant-readable context rather than
5
+ * the assistant scraping the DOM. This store is where those declarations
6
+ * accumulate: the current screen from `useAssistantScreen`, the components
7
+ * that mounted under it from `useAssistantComponent`, and the action handlers
8
+ * from `useAssistantAction`.
9
+ *
10
+ * Deliberately framework-free and dependency-free. React binds to it through
11
+ * `useSyncExternalStore`, which keeps the store itself testable without a
12
+ * renderer and avoids pulling a state library into a published SDK.
13
+ */
14
+ import type { ChartPoint, ChartSeries, ChartXKind, ComponentContext, ScreenContext, ViewControl } from "@mihe-dev/protocol";
15
+ /** What a screen declares about itself, minus the components. */
16
+ export interface ScreenDeclaration {
17
+ screenId: string;
18
+ route: string;
19
+ title: string;
20
+ description?: string;
21
+ keywords?: string[];
22
+ /**
23
+ * Summary values visible on screen. Keep this small and scalar — counts,
24
+ * totals, statuses. It is context for the assistant, not a data dump, and
25
+ * every byte here is sent on every screen change.
26
+ */
27
+ visibleData?: Record<string, unknown>;
28
+ /**
29
+ * The page's data has not arrived yet. While true, `visibleData` is not
30
+ * sent, and a navigation to this screen is not reported done.
31
+ *
32
+ * Without it, the counts a page declares before its queries resolve are
33
+ * zeros, and an assistant that has just navigated here reads them as the
34
+ * truth: "you have no raw materials", on a page about to show sixteen.
35
+ */
36
+ loading?: boolean;
37
+ }
38
+ /** What a component declares about itself. */
39
+ export interface ComponentDeclaration {
40
+ type: ComponentContext["type"];
41
+ label: string;
42
+ description?: string;
43
+ actions?: string[];
44
+ }
45
+ /**
46
+ * A tab set, or another control that changes what the screen shows
47
+ * (amendment 67). `onChange` is the app's own code, which is what lets this
48
+ * work however the tabs are built.
49
+ */
50
+ export interface ViewControlDeclaration {
51
+ kind: ViewControl["kind"];
52
+ label: string;
53
+ options: {
54
+ value: string;
55
+ label: string;
56
+ }[];
57
+ value?: string;
58
+ onChange: (value: string) => void | Promise<void>;
59
+ }
60
+ /**
61
+ * A table the assistant can read (amendment 67, step 2). The rows stay here,
62
+ * read through `read_table` when a question needs them.
63
+ */
64
+ export interface TableDeclaration {
65
+ label: string;
66
+ columns: {
67
+ key: string;
68
+ label: string;
69
+ }[];
70
+ /** The input name a row's id fills, such as `orderId`. */
71
+ rowIdName?: string;
72
+ /** The screen a row opens. Each row read carries its `open` params. */
73
+ opens?: string;
74
+ /** Rows in the whole list, when the page shows only some. */
75
+ total?: number;
76
+ /** How many rows are loaded, for the screen context. */
77
+ loadedRows: number;
78
+ /** The rows as they are right now, rendered to declared columns only. */
79
+ read: (offset: number, limit: number) => TableRow[];
80
+ /** How the table can be searched past its loaded rows (amendment 67, step 3). */
81
+ query?: TableQueryDeclaration;
82
+ }
83
+ /** One parameter a table search accepts, as the page declared it. */
84
+ export interface QueryParamDeclaration {
85
+ key: string;
86
+ kind: "text" | "enum" | "date";
87
+ /** For `enum`: the values accepted. */
88
+ values: string[];
89
+ description: string;
90
+ }
91
+ /** A table's search: what it takes, and the app's code that runs it. */
92
+ export interface TableQueryDeclaration {
93
+ params: QueryParamDeclaration[];
94
+ /** Runs the app's own search and renders the result to declared columns. */
95
+ run: (params: Record<string, string>) => Promise<{
96
+ rows: TableRow[];
97
+ total?: number;
98
+ }>;
99
+ /** Makes the table on screen show this search, when the page can. */
100
+ show?: (params: Record<string, string>) => void | Promise<void>;
101
+ }
102
+ /**
103
+ * One row as the model sees it: its id, the params that open it when the
104
+ * table opens a screen, and each declared column by label.
105
+ */
106
+ export type TableRow = {
107
+ id: string;
108
+ open?: Record<string, string>;
109
+ } & Record<string, string | number | boolean | null | Record<string, string> | undefined>;
110
+ /** A chart the assistant can explain (amendment 67, step 4). */
111
+ export interface ChartDeclaration {
112
+ title: string;
113
+ xLabel: string;
114
+ xKind: ChartXKind;
115
+ /** Names, units and summaries, as the screen context carries them. */
116
+ series: ChartSeries[];
117
+ /** The points as they are right now, thinned to `limit` per series. */
118
+ read: (series: string | undefined, limit: number) => {
119
+ name: string;
120
+ points: ChartPoint[];
121
+ }[];
122
+ }
123
+ /** A frontend action the assistant may run. */
124
+ export interface ActionHandler {
125
+ name: string;
126
+ handler: (input: Record<string, unknown>) => unknown | Promise<unknown>;
127
+ }
128
+ type Listener = () => void;
129
+ export declare class AssistantStore {
130
+ private screen;
131
+ /** Insertion-ordered, so the context sent to the model is stable. */
132
+ private components;
133
+ private controls;
134
+ private tables;
135
+ private charts;
136
+ /** Values a component on the screen adds to its `visibleData`, by key. */
137
+ private values;
138
+ private actions;
139
+ private listeners;
140
+ /** Cached so `useSyncExternalStore` sees a stable reference between changes. */
141
+ private snapshot;
142
+ private snapshotDirty;
143
+ subscribe: (listener: Listener) => (() => void);
144
+ /**
145
+ * The current screen context, or null before any screen has registered.
146
+ *
147
+ * Returns the same object until something actually changes, so React can
148
+ * bail out of re-rendering and the provider can skip republishing.
149
+ */
150
+ getScreenContext: () => ScreenContext | null;
151
+ setScreen(screen: ScreenDeclaration): void;
152
+ clearScreen(screenId: string): void;
153
+ registerComponent(componentKey: string, meta: ComponentDeclaration): void;
154
+ unregisterComponent(componentKey: string): void;
155
+ registerControl(controlKey: string, control: ViewControlDeclaration): void;
156
+ unregisterControl(controlKey: string): void;
157
+ getControl(controlKey: string): ViewControlDeclaration | undefined;
158
+ registerTable(tableKey: string, table: TableDeclaration): void;
159
+ unregisterTable(tableKey: string): void;
160
+ getTable(tableKey: string): TableDeclaration | undefined;
161
+ setValues(key: string, values: Record<string, unknown>): void;
162
+ clearValues(key: string): void;
163
+ registerChart(chartKey: string, chart: ChartDeclaration): void;
164
+ unregisterChart(chartKey: string): void;
165
+ getChart(chartKey: string): ChartDeclaration | undefined;
166
+ registerAction(action: ActionHandler): void;
167
+ unregisterAction(name: string): void;
168
+ getAction(name: string): ActionHandler | undefined;
169
+ /** Whether the current screen says its data is still loading. */
170
+ isScreenLoading(): boolean;
171
+ hasComponent(componentKey: string): boolean;
172
+ private invalidate;
173
+ }
174
+ export {};
175
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../js/src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAE5B,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC;IACpD,iFAAiF;IACjF,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,4EAA4E;IAC5E,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvF,qEAAqE;IACrE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAC3E,MAAM,EACN,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CACtE,CAAC;AAEF,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,sEAAsE;IACtE,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,uEAAuE;IACvE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE,EAAE,CAAC;CAC/F;AAED,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE;AAED,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAE3B,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAkC;IAChD,qEAAqE;IACrE,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,QAAQ,CAA6C;IAC7D,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAuC;IACrD,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAA8C;IAC5D,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,SAAS,CAAuB;IAExC,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,aAAa,CAAQ;IAE7B,SAAS,GAAI,UAAU,QAAQ,KAAG,CAAC,MAAM,IAAI,CAAC,CAK5C;IAEF;;;;;OAKG;IACH,gBAAgB,QAAO,aAAa,GAAG,IAAI,CAgEzC;IAEF,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAiB1C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAanC,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,IAAI;IAKzE,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI/C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI;IAK1E,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAI3C,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS;IAIlE,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAK9D,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIxD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK7D,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI9B,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAK9D,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIxD,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAW3C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIlD,iEAAiE;IACjE,eAAe,IAAI,OAAO;IAI1B,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI3C,OAAO,CAAC,UAAU;CAInB"}
package/dist/store.js ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * The app's declaration of itself.
3
+ *
4
+ * Spec §5.1 — the host app publishes assistant-readable context rather than
5
+ * the assistant scraping the DOM. This store is where those declarations
6
+ * accumulate: the current screen from `useAssistantScreen`, the components
7
+ * that mounted under it from `useAssistantComponent`, and the action handlers
8
+ * from `useAssistantAction`.
9
+ *
10
+ * Deliberately framework-free and dependency-free. React binds to it through
11
+ * `useSyncExternalStore`, which keeps the store itself testable without a
12
+ * renderer and avoids pulling a state library into a published SDK.
13
+ */
14
+ export class AssistantStore {
15
+ screen = null;
16
+ /** Insertion-ordered, so the context sent to the model is stable. */
17
+ components = new Map();
18
+ controls = new Map();
19
+ tables = new Map();
20
+ charts = new Map();
21
+ /** Values a component on the screen adds to its `visibleData`, by key. */
22
+ values = new Map();
23
+ actions = new Map();
24
+ listeners = new Set();
25
+ /** Cached so `useSyncExternalStore` sees a stable reference between changes. */
26
+ snapshot = null;
27
+ snapshotDirty = true;
28
+ subscribe = (listener) => {
29
+ this.listeners.add(listener);
30
+ return () => {
31
+ this.listeners.delete(listener);
32
+ };
33
+ };
34
+ /**
35
+ * The current screen context, or null before any screen has registered.
36
+ *
37
+ * Returns the same object until something actually changes, so React can
38
+ * bail out of re-rendering and the provider can skip republishing.
39
+ */
40
+ getScreenContext = () => {
41
+ if (!this.snapshotDirty)
42
+ return this.snapshot;
43
+ this.snapshot = this.screen
44
+ ? {
45
+ screenId: this.screen.screenId,
46
+ route: this.screen.route,
47
+ title: this.screen.title,
48
+ description: this.screen.description ?? "",
49
+ keywords: this.screen.keywords ?? [],
50
+ visibleData: (this.screen.loading
51
+ ? {}
52
+ : {
53
+ ...(this.screen.visibleData ?? {}),
54
+ ...Object.fromEntries(this.values),
55
+ }),
56
+ components: [...this.components.entries()].map(([componentKey, meta]) => ({
57
+ componentKey,
58
+ type: meta.type,
59
+ label: meta.label,
60
+ description: meta.description ?? "",
61
+ actions: meta.actions ?? [],
62
+ })),
63
+ controls: [...this.controls.entries()].map(([controlKey, control]) => ({
64
+ controlKey,
65
+ kind: control.kind,
66
+ label: control.label,
67
+ options: control.options.map(({ value, label }) => ({ value, label })),
68
+ ...(control.value === undefined ? {} : { value: control.value }),
69
+ })),
70
+ tables: [...this.tables.entries()].map(([tableKey, table]) => ({
71
+ tableKey,
72
+ label: table.label,
73
+ columns: table.columns.map(({ key, label }) => ({ key, label })),
74
+ loadedRows: table.loadedRows,
75
+ ...(table.rowIdName ? { rowId: table.rowIdName } : {}),
76
+ ...(table.opens ? { opens: table.opens } : {}),
77
+ ...(table.total === undefined ? {} : { totalRows: table.total }),
78
+ ...(table.query
79
+ ? {
80
+ query: {
81
+ params: table.query.params.map(({ key, kind, values, description }) => ({
82
+ key,
83
+ kind,
84
+ values,
85
+ description,
86
+ })),
87
+ canShow: table.query.show !== undefined,
88
+ },
89
+ }
90
+ : {}),
91
+ })),
92
+ charts: [...this.charts.entries()].map(([chartKey, chart]) => ({
93
+ chartKey,
94
+ title: chart.title,
95
+ xLabel: chart.xLabel,
96
+ xKind: chart.xKind,
97
+ series: chart.series,
98
+ })),
99
+ }
100
+ : null;
101
+ this.snapshotDirty = false;
102
+ return this.snapshot;
103
+ };
104
+ setScreen(screen) {
105
+ const previous = this.screen;
106
+ this.screen = screen;
107
+ // Components belong to the screen that registered them. Moving to a new
108
+ // screen clears them, so the assistant is never told it can point at an
109
+ // element from the page the user just left.
110
+ if (previous && previous.screenId !== screen.screenId) {
111
+ this.components.clear();
112
+ this.controls.clear();
113
+ this.tables.clear();
114
+ this.charts.clear();
115
+ this.values.clear();
116
+ }
117
+ this.invalidate();
118
+ }
119
+ clearScreen(screenId) {
120
+ // Ignore a stale unmount: React mounts the next screen before unmounting
121
+ // the previous one, so a late cleanup must not wipe the new screen.
122
+ if (this.screen?.screenId !== screenId)
123
+ return;
124
+ this.screen = null;
125
+ this.components.clear();
126
+ this.controls.clear();
127
+ this.tables.clear();
128
+ this.charts.clear();
129
+ this.values.clear();
130
+ this.invalidate();
131
+ }
132
+ registerComponent(componentKey, meta) {
133
+ this.components.set(componentKey, meta);
134
+ this.invalidate();
135
+ }
136
+ unregisterComponent(componentKey) {
137
+ if (this.components.delete(componentKey))
138
+ this.invalidate();
139
+ }
140
+ registerControl(controlKey, control) {
141
+ this.controls.set(controlKey, control);
142
+ this.invalidate();
143
+ }
144
+ unregisterControl(controlKey) {
145
+ if (this.controls.delete(controlKey))
146
+ this.invalidate();
147
+ }
148
+ getControl(controlKey) {
149
+ return this.controls.get(controlKey);
150
+ }
151
+ registerTable(tableKey, table) {
152
+ this.tables.set(tableKey, table);
153
+ this.invalidate();
154
+ }
155
+ unregisterTable(tableKey) {
156
+ if (this.tables.delete(tableKey))
157
+ this.invalidate();
158
+ }
159
+ getTable(tableKey) {
160
+ return this.tables.get(tableKey);
161
+ }
162
+ setValues(key, values) {
163
+ this.values.set(key, values);
164
+ this.invalidate();
165
+ }
166
+ clearValues(key) {
167
+ if (this.values.delete(key))
168
+ this.invalidate();
169
+ }
170
+ registerChart(chartKey, chart) {
171
+ this.charts.set(chartKey, chart);
172
+ this.invalidate();
173
+ }
174
+ unregisterChart(chartKey) {
175
+ if (this.charts.delete(chartKey))
176
+ this.invalidate();
177
+ }
178
+ getChart(chartKey) {
179
+ return this.charts.get(chartKey);
180
+ }
181
+ registerAction(action) {
182
+ if (this.actions.has(action.name)) {
183
+ console.warn(`[assistant] Two handlers registered for action "${action.name}". ` +
184
+ `The most recent one wins; the earlier one will never run.`);
185
+ }
186
+ this.actions.set(action.name, action);
187
+ // Handlers are not part of the published context, so no invalidate.
188
+ }
189
+ unregisterAction(name) {
190
+ this.actions.delete(name);
191
+ }
192
+ getAction(name) {
193
+ return this.actions.get(name);
194
+ }
195
+ /** Whether the current screen says its data is still loading. */
196
+ isScreenLoading() {
197
+ return this.screen?.loading === true;
198
+ }
199
+ hasComponent(componentKey) {
200
+ return this.components.has(componentKey);
201
+ }
202
+ invalidate() {
203
+ this.snapshotDirty = true;
204
+ for (const listener of this.listeners)
205
+ listener();
206
+ }
207
+ }
208
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../js/src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The voice session (doc 03, phase 5a).
3
+ *
4
+ * A second, short-lived WebSocket carrying audio only. Commands and
5
+ * confirmation cards keep arriving on the chat socket, which is the whole
6
+ * design: voice is an input modality, not a second command channel, so a
7
+ * confirmation card is the same card whether the user typed or spoke and there
8
+ * is exactly one place that draws it.
9
+ *
10
+ * Push-to-talk, deliberately, before hands-free. It has no VAD edge cases, it
11
+ * works on a phone the day it ships, and the button press doubles as the user
12
+ * gesture iOS Safari requires before any audio can start. Hands-free is 5b.
13
+ */
14
+ import { type VoiceEndReason } from "@mihe-dev/protocol";
15
+ export type VoiceState = "idle" | "connecting"
16
+ /** Connected, microphone closed, waiting for the user to press. */
17
+ | "listening"
18
+ /** The button is held down and audio is going up. */
19
+ | "recording"
20
+ /** The utterance is committed and the model has not answered yet. */
21
+ | "thinking" | "speaking" | "error";
22
+ export type VoiceEvent = {
23
+ type: "state";
24
+ state: VoiceState;
25
+ } | {
26
+ type: "transcript";
27
+ role: "user" | "assistant";
28
+ text: string;
29
+ final: boolean;
30
+ } | {
31
+ type: "error";
32
+ code: string;
33
+ message: string;
34
+ /** True when the session survived and the user can simply try again. */
35
+ recoverable: boolean;
36
+ fallbackToChat: boolean;
37
+ }
38
+ /** The duration ceiling is near. The session is still running. */
39
+ | {
40
+ type: "ending_soon";
41
+ secondsRemaining: number;
42
+ }
43
+ /**
44
+ * The session finished the way it was meant to — the minutes ran out, or
45
+ * nobody spoke. Deliberately not an `error`: the chat socket is untouched and
46
+ * the transcript is already in the conversation, so the user has lost
47
+ * nothing and should not be told they have.
48
+ */
49
+ | {
50
+ type: "ended";
51
+ reason: VoiceEndReason;
52
+ message: string;
53
+ };
54
+ export interface VoiceSessionOptions {
55
+ /** Voice socket URL, already carrying the single-use token. */
56
+ url: string;
57
+ /**
58
+ * Hands-free: the microphone stays open, the provider decides when an
59
+ * utterance ends, and the user can talk over the assistant.
60
+ *
61
+ * The trade is echo. Under push-to-talk the microphone is shut except while
62
+ * the button is held, so the assistant physically cannot hear itself; here
63
+ * it can, and browser echo cancellation is the only thing between the two.
64
+ * On a laptop that is usually enough. On a phone with the speaker beside the
65
+ * microphone it may not be, and the failure is the assistant answering its
66
+ * own last sentence.
67
+ */
68
+ handsFree?: boolean;
69
+ onEvent: (event: VoiceEvent) => void;
70
+ /**
71
+ * How loud each side is, 0 to 1, as it happens: the user's voice as it is
72
+ * captured, the assistant's as it plays. Deliberately not a `VoiceEvent`:
73
+ * it fires tens of times a second, and whatever listens should move a ring
74
+ * rather than re-render a component tree.
75
+ */
76
+ onLevel?: (source: "user" | "assistant", level: number) => void;
77
+ /** Injectable for tests. */
78
+ socketFactory?: (url: string) => VoiceSocketLike;
79
+ }
80
+ /** The slice of `WebSocket` a voice session uses. */
81
+ export interface VoiceSocketLike {
82
+ binaryType: string;
83
+ send(data: string | ArrayBuffer): void;
84
+ close(): void;
85
+ onopen: ((event: unknown) => void) | null;
86
+ onclose: ((event: unknown) => void) | null;
87
+ onerror: ((event: unknown) => void) | null;
88
+ onmessage: ((event: {
89
+ data: unknown;
90
+ }) => void) | null;
91
+ }
92
+ export declare class VoiceSession {
93
+ private readonly options;
94
+ private socket;
95
+ private state;
96
+ private mic;
97
+ private speaker;
98
+ /**
99
+ * Audio captured since a held press began, not yet sent.
100
+ *
101
+ * A press on a draggable orb is ambiguous for its first moment: it may be
102
+ * the start of speech or the start of a drag. Holding the frames here means
103
+ * neither answer costs anything. Confirmed, they go up in order and the first
104
+ * word survives. Discarded, they never leave the browser, because the voice
105
+ * socket has no way to take back audio it has been sent.
106
+ */
107
+ private held;
108
+ private stopped;
109
+ /**
110
+ * Milliseconds of audio captured since the last commit.
111
+ *
112
+ * Counted rather than timed from the button press, because those differ:
113
+ * the microphone graph delivers in render quanta, so a press held for
114
+ * exactly the floor produces slightly less audio than the clock suggests.
115
+ * The provider measures the buffer, so we measure the buffer too.
116
+ */
117
+ private utteranceMs;
118
+ /** The assistant's line so far, joined from streamed transcript deltas. */
119
+ private assistantLine;
120
+ /** Reports playback position while speaking, for barge-in truncation. */
121
+ private playbackTimer;
122
+ /**
123
+ * The provider's shortest acceptable utterance, learned at `ready`.
124
+ *
125
+ * Defaults high enough to be safe before the runtime has told us, since
126
+ * guessing low means the first press of a session is the one that fails.
127
+ */
128
+ private minUtteranceMs;
129
+ constructor(options: VoiceSessionOptions);
130
+ get currentState(): VoiceState;
131
+ /**
132
+ * Open the socket and the audio devices.
133
+ *
134
+ * Must be called from a user gesture. Both the microphone permission prompt
135
+ * and iOS Safari's audio unlock require one, and a session started from an
136
+ * effect will connect happily and then be silent in both directions with no
137
+ * error anywhere.
138
+ */
139
+ start(): Promise<void>;
140
+ /**
141
+ * The user pressed the button. Push-to-talk only.
142
+ *
143
+ * With `hold`, capture starts but nothing is sent, and the assistant is not
144
+ * interrupted, until `confirmUtterance`. `discardUtterance` drops it
145
+ * instead. For a control where a press might turn out to be a drag.
146
+ */
147
+ beginUtterance(options?: {
148
+ hold?: boolean;
149
+ }): void;
150
+ /** A held press was speech after all: send what was captured, and carry on. */
151
+ confirmUtterance(): void;
152
+ /** A held press was a drag: drop what was captured, and send nothing. */
153
+ discardUtterance(): void;
154
+ /**
155
+ * Pressing while the assistant is talking is an interruption. Split out so a
156
+ * held press does not cut the assistant off before it is known to be speech.
157
+ */
158
+ private interruptPlayback;
159
+ /** The user released the button. Push-to-talk only. */
160
+ endUtterance(): void;
161
+ stop(): Promise<void>;
162
+ private handleControl;
163
+ private sendAudio;
164
+ private sendControl;
165
+ private setState;
166
+ /**
167
+ * Tell the runtime how much of the current reply has actually been heard,
168
+ * a few times a second, for as long as the assistant is speaking.
169
+ *
170
+ * Push-to-talk does not need this: the interruption *is* a button press, so
171
+ * the position can be measured at exactly that moment. Hands-free has no
172
+ * such moment on this side — the provider notices the user talking, upstream
173
+ * — so the runtime has to already hold a recent figure when it truncates.
174
+ *
175
+ * Without it, truncation falls back to nothing and the model's history keeps
176
+ * the whole reply, including the part the user cut off. It then answers
177
+ * follow-ups about words nobody heard.
178
+ *
179
+ * Four frames a second of small JSON, against fifty of audio.
180
+ */
181
+ private reportPlaybackWhileSpeaking;
182
+ private emit;
183
+ }
184
+ /**
185
+ * Derive the voice socket URL from the chat socket URL.
186
+ *
187
+ * One `url` prop for the whole SDK rather than two: the two sockets are always
188
+ * served by the same runtime, and a second prop would be a second thing to get
189
+ * wrong in every integration for no configurability anyone wants.
190
+ */
191
+ export declare function voiceUrl(chatUrl: string, token: string, handsFree?: boolean): string;
192
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.ts","sourceRoot":"","sources":["../../../js/src/voice.ts"],"names":[],"mappings":"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"}