@agent-native/toolkit 0.8.3 → 0.9.1

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 (99) hide show
  1. package/README.md +42 -3
  2. package/agent-native.eject.json +33 -0
  3. package/dist/chat-history/ChatHistoryRail.d.ts +19 -0
  4. package/dist/chat-history/ChatHistoryRail.d.ts.map +1 -0
  5. package/dist/chat-history/ChatHistoryRail.js +22 -0
  6. package/dist/chat-history/ChatHistoryRail.js.map +1 -0
  7. package/dist/chat-history/ChatHistoryRail.spec.d.ts +2 -0
  8. package/dist/chat-history/ChatHistoryRail.spec.d.ts.map +1 -0
  9. package/dist/chat-history/ChatHistoryRail.spec.js +81 -0
  10. package/dist/chat-history/ChatHistoryRail.spec.js.map +1 -0
  11. package/dist/chat-history/index.d.ts +1 -0
  12. package/dist/chat-history/index.d.ts.map +1 -1
  13. package/dist/chat-history/index.js +1 -0
  14. package/dist/chat-history/index.js.map +1 -1
  15. package/dist/chat-history.css +59 -0
  16. package/dist/composer/RealtimeVoiceMode.d.ts +5 -1
  17. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
  18. package/dist/composer/RealtimeVoiceMode.js +27 -8
  19. package/dist/composer/RealtimeVoiceMode.js.map +1 -1
  20. package/dist/composer/RealtimeVoiceMode.spec.js +51 -11
  21. package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -1
  22. package/dist/composer/VoiceButton.d.ts +2 -0
  23. package/dist/composer/VoiceButton.d.ts.map +1 -1
  24. package/dist/composer/VoiceButton.js +33 -1
  25. package/dist/composer/VoiceButton.js.map +1 -1
  26. package/dist/composer/VoiceButton.spec.js +9 -1
  27. package/dist/composer/VoiceButton.spec.js.map +1 -1
  28. package/dist/composer/realtime-voice-audio-level.d.ts.map +1 -1
  29. package/dist/composer/realtime-voice-audio-level.js +2 -1
  30. package/dist/composer/realtime-voice-audio-level.js.map +1 -1
  31. package/dist/composer/realtime-voice-audio-level.spec.js +7 -0
  32. package/dist/composer/realtime-voice-audio-level.spec.js.map +1 -1
  33. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  34. package/dist/composer/useRealtimeVoiceMode.js +4 -4
  35. package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
  36. package/dist/dashboard/DataTable.d.ts +20 -0
  37. package/dist/dashboard/DataTable.d.ts.map +1 -0
  38. package/dist/dashboard/DataTable.js +87 -0
  39. package/dist/dashboard/DataTable.js.map +1 -0
  40. package/dist/dashboard/DateRangePicker.d.ts +15 -0
  41. package/dist/dashboard/DateRangePicker.d.ts.map +1 -0
  42. package/dist/dashboard/DateRangePicker.js +15 -0
  43. package/dist/dashboard/DateRangePicker.js.map +1 -0
  44. package/dist/dashboard/GenericChartPanel.d.ts +57 -0
  45. package/dist/dashboard/GenericChartPanel.d.ts.map +1 -0
  46. package/dist/dashboard/GenericChartPanel.js +119 -0
  47. package/dist/dashboard/GenericChartPanel.js.map +1 -0
  48. package/dist/dashboard/GenericChartPanel.spec.d.ts +2 -0
  49. package/dist/dashboard/GenericChartPanel.spec.d.ts.map +1 -0
  50. package/dist/dashboard/GenericChartPanel.spec.js +41 -0
  51. package/dist/dashboard/GenericChartPanel.spec.js.map +1 -0
  52. package/dist/dashboard/MetricCard.d.ts +14 -0
  53. package/dist/dashboard/MetricCard.d.ts.map +1 -0
  54. package/dist/dashboard/MetricCard.js +10 -0
  55. package/dist/dashboard/MetricCard.js.map +1 -0
  56. package/dist/dashboard/StatsCard.d.ts +16 -0
  57. package/dist/dashboard/StatsCard.d.ts.map +1 -0
  58. package/dist/dashboard/StatsCard.js +8 -0
  59. package/dist/dashboard/StatsCard.js.map +1 -0
  60. package/dist/dashboard/dashboard-layout.d.ts +49 -0
  61. package/dist/dashboard/dashboard-layout.d.ts.map +1 -0
  62. package/dist/dashboard/dashboard-layout.js +231 -0
  63. package/dist/dashboard/dashboard-layout.js.map +1 -0
  64. package/dist/dashboard/dashboard-layout.spec.d.ts +2 -0
  65. package/dist/dashboard/dashboard-layout.spec.d.ts.map +1 -0
  66. package/dist/dashboard/dashboard-layout.spec.js +52 -0
  67. package/dist/dashboard/dashboard-layout.spec.js.map +1 -0
  68. package/dist/dashboard/index.d.ts +7 -0
  69. package/dist/dashboard/index.d.ts.map +1 -0
  70. package/dist/dashboard/index.js +7 -0
  71. package/dist/dashboard/index.js.map +1 -0
  72. package/dist/index.d.ts +1 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +1 -0
  75. package/dist/index.js.map +1 -1
  76. package/dist/styles.css +35 -0
  77. package/package.json +15 -1
  78. package/src/chat-history/ChatHistoryRail.spec.tsx +138 -0
  79. package/src/chat-history/ChatHistoryRail.tsx +89 -0
  80. package/src/chat-history/index.ts +5 -0
  81. package/src/chat-history.css +59 -0
  82. package/src/composer/RealtimeVoiceMode.spec.tsx +116 -11
  83. package/src/composer/RealtimeVoiceMode.tsx +106 -36
  84. package/src/composer/VoiceButton.spec.ts +17 -1
  85. package/src/composer/VoiceButton.tsx +49 -0
  86. package/src/composer/realtime-voice-audio-level.spec.ts +15 -0
  87. package/src/composer/realtime-voice-audio-level.ts +2 -1
  88. package/src/composer/useRealtimeVoiceMode.tsx +5 -3
  89. package/src/dashboard/DataTable.tsx +273 -0
  90. package/src/dashboard/DateRangePicker.tsx +57 -0
  91. package/src/dashboard/GenericChartPanel.spec.tsx +53 -0
  92. package/src/dashboard/GenericChartPanel.tsx +461 -0
  93. package/src/dashboard/MetricCard.tsx +54 -0
  94. package/src/dashboard/StatsCard.tsx +49 -0
  95. package/src/dashboard/dashboard-layout.spec.ts +79 -0
  96. package/src/dashboard/dashboard-layout.ts +329 -0
  97. package/src/dashboard/index.ts +6 -0
  98. package/src/index.ts +1 -0
  99. package/src/styles.css +35 -0
@@ -0,0 +1,329 @@
1
+ export const MIN_DASHBOARD_COLUMNS = 1;
2
+ export const MAX_DASHBOARD_COLUMNS = 6;
3
+ export const DEFAULT_DASHBOARD_COLUMNS = 2;
4
+
5
+ export interface DashboardLayoutPanel {
6
+ id: string;
7
+ width?: number;
8
+ columns?: number;
9
+ }
10
+
11
+ export type DashboardPanelRow<TPanel extends DashboardLayoutPanel> = {
12
+ key: string;
13
+ panels: TPanel[];
14
+ };
15
+
16
+ export type DashboardPanelGroup<TPanel extends DashboardLayoutPanel> = {
17
+ key: string;
18
+ section: TPanel | null;
19
+ panels: TPanel[];
20
+ rows: DashboardPanelRow<TPanel>[];
21
+ columns: number;
22
+ };
23
+
24
+ export type DashboardDropSlot =
25
+ | { type: "row"; groupKey: string; rowIndex: number }
26
+ | { type: "column"; groupKey: string; rowIndex: number; columnIndex: number };
27
+
28
+ export type DashboardColumnExpansion = {
29
+ columns: number;
30
+ sectionPanelId: string | null;
31
+ };
32
+
33
+ export type DashboardLayoutOptions<TPanel extends DashboardLayoutPanel> = {
34
+ isSection?: (panel: TPanel) => boolean;
35
+ };
36
+
37
+ export function clampDashboardColumns(value: unknown): number {
38
+ if (typeof value !== "number" || !Number.isFinite(value))
39
+ return DEFAULT_DASHBOARD_COLUMNS;
40
+ return Math.min(
41
+ MAX_DASHBOARD_COLUMNS,
42
+ Math.max(MIN_DASHBOARD_COLUMNS, Math.floor(value)),
43
+ );
44
+ }
45
+
46
+ export function clampPanelWidth(value: unknown, gridColumns: number): number {
47
+ if (typeof value !== "number" || !Number.isFinite(value)) return 1;
48
+ return Math.min(
49
+ clampDashboardColumns(gridColumns),
50
+ Math.max(1, Math.floor(value)),
51
+ );
52
+ }
53
+
54
+ export function rebalanceRowWidths<TPanel extends DashboardLayoutPanel>(
55
+ panels: TPanel[],
56
+ columns: number,
57
+ ): TPanel[] {
58
+ if (panels.length === 0) return [];
59
+ const safeColumns = clampDashboardColumns(columns);
60
+ const base = Math.max(1, Math.floor(safeColumns / panels.length));
61
+ const remainder = safeColumns % panels.length;
62
+ return panels.map((panel, index) => ({
63
+ ...panel,
64
+ width: base + (index < remainder ? 1 : 0),
65
+ }));
66
+ }
67
+
68
+ export function buildDashboardRows<TPanel extends DashboardLayoutPanel>(
69
+ panels: TPanel[],
70
+ columns: number,
71
+ ): DashboardPanelRow<TPanel>[] {
72
+ const safeColumns = clampDashboardColumns(columns);
73
+ const rows: DashboardPanelRow<TPanel>[] = [];
74
+ let current: TPanel[] = [];
75
+ let usedColumns = 0;
76
+ const push = () => {
77
+ if (!current.length) return;
78
+ rows.push({
79
+ key: current.map((panel) => panel.id).join(":") || `empty-${rows.length}`,
80
+ panels: current,
81
+ });
82
+ current = [];
83
+ usedColumns = 0;
84
+ };
85
+ for (const panel of panels) {
86
+ const width = clampPanelWidth(panel.width, safeColumns);
87
+ if (
88
+ current.length &&
89
+ (usedColumns + width > safeColumns || current.length >= safeColumns)
90
+ )
91
+ push();
92
+ current.push(panel);
93
+ usedColumns += width;
94
+ if (usedColumns >= safeColumns || current.length >= safeColumns) push();
95
+ }
96
+ push();
97
+ return rows;
98
+ }
99
+
100
+ export function buildDashboardPanelGroups<TPanel extends DashboardLayoutPanel>(
101
+ panels: TPanel[],
102
+ dashboardColumns: number,
103
+ { isSection = () => false }: DashboardLayoutOptions<TPanel> = {},
104
+ ): DashboardPanelGroup<TPanel>[] {
105
+ const defaultColumns = clampDashboardColumns(dashboardColumns);
106
+ const groups: DashboardPanelGroup<TPanel>[] = [];
107
+ let current: Omit<DashboardPanelGroup<TPanel>, "rows"> = {
108
+ key: "intro",
109
+ section: null,
110
+ panels: [],
111
+ columns: defaultColumns,
112
+ };
113
+ const push = () => {
114
+ if (!current.section && !current.panels.length) return;
115
+ groups.push({
116
+ ...current,
117
+ rows: buildDashboardRows(current.panels, current.columns),
118
+ });
119
+ };
120
+ for (const panel of panels) {
121
+ if (isSection(panel)) {
122
+ push();
123
+ current = {
124
+ key: panel.id,
125
+ section: panel,
126
+ panels: [],
127
+ columns: clampDashboardColumns(panel.columns ?? defaultColumns),
128
+ };
129
+ } else {
130
+ current.panels.push(panel);
131
+ }
132
+ }
133
+ push();
134
+ return groups;
135
+ }
136
+
137
+ function flattenGroups<TPanel extends DashboardLayoutPanel>(
138
+ groups: DashboardPanelGroup<TPanel>[],
139
+ ): TPanel[] {
140
+ return groups.flatMap((group) => [
141
+ ...(group.section ? [group.section] : []),
142
+ ...group.rows.flatMap((row) =>
143
+ rebalanceRowWidths(row.panels, group.columns),
144
+ ),
145
+ ]);
146
+ }
147
+
148
+ export function removePanelFromLayout<TPanel extends DashboardLayoutPanel>(
149
+ panels: TPanel[],
150
+ panelId: string,
151
+ dashboardColumns: number,
152
+ options?: DashboardLayoutOptions<TPanel>,
153
+ ): TPanel[] {
154
+ const groups = buildDashboardPanelGroups(panels, dashboardColumns, options);
155
+ return flattenGroups(
156
+ groups
157
+ .map((group) => ({
158
+ ...group,
159
+ section: group.section?.id === panelId ? null : group.section,
160
+ rows: group.rows
161
+ .map((row) => ({
162
+ ...row,
163
+ panels: row.panels.filter((panel) => panel.id !== panelId),
164
+ }))
165
+ .filter((row) => row.panels.length),
166
+ }))
167
+ .filter((group) => group.section || group.rows.length),
168
+ );
169
+ }
170
+
171
+ export function dropSlotId(slot: DashboardDropSlot): string {
172
+ return slot.type === "row"
173
+ ? `dashboard-drop:row:${slot.groupKey}:${slot.rowIndex}`
174
+ : `dashboard-drop:column:${slot.groupKey}:${slot.rowIndex}:${slot.columnIndex}`;
175
+ }
176
+
177
+ export function readDropSlot(value: unknown): DashboardDropSlot | null {
178
+ if (!value || typeof value !== "object") return null;
179
+ const slot = (value as { slot?: unknown }).slot;
180
+ if (!slot || typeof slot !== "object") return null;
181
+ const candidate = slot as Partial<DashboardDropSlot>;
182
+ if (
183
+ candidate.type === "row" &&
184
+ typeof candidate.groupKey === "string" &&
185
+ typeof candidate.rowIndex === "number"
186
+ ) {
187
+ return {
188
+ type: "row",
189
+ groupKey: candidate.groupKey,
190
+ rowIndex: candidate.rowIndex,
191
+ };
192
+ }
193
+ if (
194
+ candidate.type === "column" &&
195
+ typeof candidate.groupKey === "string" &&
196
+ typeof candidate.rowIndex === "number" &&
197
+ typeof candidate.columnIndex === "number"
198
+ ) {
199
+ return {
200
+ type: "column",
201
+ groupKey: candidate.groupKey,
202
+ rowIndex: candidate.rowIndex,
203
+ columnIndex: candidate.columnIndex,
204
+ };
205
+ }
206
+ return null;
207
+ }
208
+
209
+ export function sameDropSlot(
210
+ a: DashboardDropSlot | null,
211
+ b: DashboardDropSlot,
212
+ ): boolean {
213
+ return (
214
+ !!a &&
215
+ a.type === b.type &&
216
+ a.groupKey === b.groupKey &&
217
+ a.rowIndex === b.rowIndex &&
218
+ (a.type === "row" ||
219
+ (b.type === "column" && a.columnIndex === b.columnIndex))
220
+ );
221
+ }
222
+
223
+ function findPanel<TPanel extends DashboardLayoutPanel>(
224
+ groups: DashboardPanelGroup<TPanel>[],
225
+ panelId: string,
226
+ ) {
227
+ for (const group of groups) {
228
+ for (let rowIndex = 0; rowIndex < group.rows.length; rowIndex++) {
229
+ const columnIndex = group.rows[rowIndex].panels.findIndex(
230
+ (panel) => panel.id === panelId,
231
+ );
232
+ if (columnIndex >= 0) return { group, rowIndex, columnIndex };
233
+ }
234
+ }
235
+ return null;
236
+ }
237
+
238
+ export function columnExpansionForDropSlot<TPanel extends DashboardLayoutPanel>(
239
+ groups: DashboardPanelGroup<TPanel>[],
240
+ panelId: string,
241
+ slot: DashboardDropSlot,
242
+ ): DashboardColumnExpansion | null {
243
+ if (slot.type !== "column") return null;
244
+ const group = groups.find((item) => item.key === slot.groupKey);
245
+ const row = group?.rows[slot.rowIndex];
246
+ if (!group || !row) return null;
247
+ const required = row.panels.some((panel) => panel.id === panelId)
248
+ ? row.panels.length
249
+ : row.panels.length + 1;
250
+ return required > group.columns
251
+ ? {
252
+ columns: clampDashboardColumns(required),
253
+ sectionPanelId: group.section?.id ?? null,
254
+ }
255
+ : null;
256
+ }
257
+
258
+ /** Moves a panel by visible row/column slot and rebalances persisted widths. */
259
+ export function movePanelToDropSlot<TPanel extends DashboardLayoutPanel>(
260
+ panels: TPanel[],
261
+ panelId: string,
262
+ slot: DashboardDropSlot,
263
+ dashboardColumns: number,
264
+ options?: DashboardLayoutOptions<TPanel>,
265
+ ): TPanel[] {
266
+ const groups = buildDashboardPanelGroups(panels, dashboardColumns, options);
267
+ const source = findPanel(groups, panelId);
268
+ if (!source) return panels;
269
+ const moving = source.group.rows[source.rowIndex].panels[source.columnIndex];
270
+ const sourceWasSingle =
271
+ source.group.rows[source.rowIndex].panels.length === 1;
272
+ const next = groups.map((group) => ({
273
+ ...group,
274
+ rows: group.rows
275
+ .map((row) => ({
276
+ ...row,
277
+ panels: row.panels.filter((panel) => panel.id !== panelId),
278
+ }))
279
+ .filter((row) => row.panels.length),
280
+ }));
281
+ const target = next.find((group) => group.key === slot.groupKey);
282
+ if (!target) return panels;
283
+ if (slot.type === "row") {
284
+ let rowIndex = slot.rowIndex;
285
+ if (
286
+ source.group.key === target.key &&
287
+ sourceWasSingle &&
288
+ source.rowIndex < rowIndex
289
+ )
290
+ rowIndex--;
291
+ if (
292
+ source.group.key === target.key &&
293
+ sourceWasSingle &&
294
+ source.rowIndex === rowIndex
295
+ )
296
+ return panels;
297
+ target.rows.splice(Math.max(0, rowIndex), 0, {
298
+ key: moving.id,
299
+ panels: [moving],
300
+ });
301
+ } else {
302
+ let rowIndex = slot.rowIndex;
303
+ if (
304
+ source.group.key === target.key &&
305
+ sourceWasSingle &&
306
+ source.rowIndex < rowIndex
307
+ )
308
+ rowIndex--;
309
+ const targetRow = target.rows[rowIndex];
310
+ if (!targetRow) return panels;
311
+ let columnIndex = slot.columnIndex;
312
+ if (
313
+ source.group.key === target.key &&
314
+ source.rowIndex === slot.rowIndex &&
315
+ source.columnIndex < columnIndex
316
+ )
317
+ columnIndex--;
318
+ targetRow.panels.splice(
319
+ Math.max(0, Math.min(columnIndex, targetRow.panels.length)),
320
+ 0,
321
+ moving,
322
+ );
323
+ target.columns = Math.max(
324
+ target.columns,
325
+ clampDashboardColumns(targetRow.panels.length),
326
+ );
327
+ }
328
+ return flattenGroups(next);
329
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./DataTable.js";
2
+ export * from "./DateRangePicker.js";
3
+ export * from "./GenericChartPanel.js";
4
+ export * from "./MetricCard.js";
5
+ export * from "./StatsCard.js";
6
+ export * from "./dashboard-layout.js";
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./app-shell/index.js";
2
2
  export * from "./collab-ui/index.js";
3
+ export * from "./dashboard/index.js";
3
4
  export * from "./hooks/index.js";
4
5
  export * from "./onboarding/index.js";
5
6
  export * from "./provider.js";
package/src/styles.css CHANGED
@@ -19,3 +19,38 @@
19
19
  * compiled `.js` next to it for consumers that only install `dist/`.
20
20
  */
21
21
  @source "./**/*.{js,ts,tsx}";
22
+
23
+ .agent-realtime-voice-working::after {
24
+ position: absolute;
25
+ z-index: 0;
26
+ inset: 0;
27
+ border-radius: inherit;
28
+ pointer-events: none;
29
+ content: "";
30
+ }
31
+
32
+ @media (prefers-reduced-motion: no-preference) {
33
+ .agent-realtime-voice-working::after {
34
+ background: linear-gradient(
35
+ 90deg,
36
+ transparent 0%,
37
+ transparent 36%,
38
+ hsl(var(--foreground) / 0.14) 50%,
39
+ transparent 64%,
40
+ transparent 100%
41
+ );
42
+ background-repeat: repeat;
43
+ background-size: 14rem 100%;
44
+ animation: agent-realtime-voice-shine 2.6s linear infinite;
45
+ }
46
+
47
+ @keyframes agent-realtime-voice-shine {
48
+ from {
49
+ background-position: 0 0;
50
+ }
51
+
52
+ to {
53
+ background-position: 14rem 0;
54
+ }
55
+ }
56
+ }