@agent-native/toolkit 0.15.1 → 0.16.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 (60) hide show
  1. package/README.md +26 -0
  2. package/agent-native.eject.json +16 -0
  3. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
  4. package/dist/composer/ComposerPlusMenu.js +29 -19
  5. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  6. package/dist/composer/PromptComposer.d.ts +11 -1
  7. package/dist/composer/PromptComposer.d.ts.map +1 -1
  8. package/dist/composer/PromptComposer.js +2 -2
  9. package/dist/composer/PromptComposer.js.map +1 -1
  10. package/dist/composer/TiptapComposer.d.ts +30 -1
  11. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  12. package/dist/composer/TiptapComposer.js +255 -145
  13. package/dist/composer/TiptapComposer.js.map +1 -1
  14. package/dist/composer/TiptapComposer.spec.js +33 -1
  15. package/dist/composer/TiptapComposer.spec.js.map +1 -1
  16. package/dist/composer/index.d.ts +1 -0
  17. package/dist/composer/index.d.ts.map +1 -1
  18. package/dist/composer/index.js +1 -0
  19. package/dist/composer/index.js.map +1 -1
  20. package/dist/composer/model-selection.d.ts +12 -0
  21. package/dist/composer/model-selection.d.ts.map +1 -0
  22. package/dist/composer/model-selection.js +31 -0
  23. package/dist/composer/model-selection.js.map +1 -0
  24. package/dist/composer/model-selection.spec.d.ts +2 -0
  25. package/dist/composer/model-selection.spec.d.ts.map +1 -0
  26. package/dist/composer/model-selection.spec.js +38 -0
  27. package/dist/composer/model-selection.spec.js.map +1 -0
  28. package/dist/data-grid/DataGrid.d.ts +92 -0
  29. package/dist/data-grid/DataGrid.d.ts.map +1 -0
  30. package/dist/data-grid/DataGrid.js +315 -0
  31. package/dist/data-grid/DataGrid.js.map +1 -0
  32. package/dist/data-grid/DataGrid.spec.d.ts +2 -0
  33. package/dist/data-grid/DataGrid.spec.d.ts.map +1 -0
  34. package/dist/data-grid/DataGrid.spec.js +134 -0
  35. package/dist/data-grid/DataGrid.spec.js.map +1 -0
  36. package/dist/data-grid/index.d.ts +2 -0
  37. package/dist/data-grid/index.d.ts.map +1 -0
  38. package/dist/data-grid/index.js +2 -0
  39. package/dist/data-grid/index.js.map +1 -0
  40. package/dist/sharing/SharePrimitives.d.ts +14 -3
  41. package/dist/sharing/SharePrimitives.d.ts.map +1 -1
  42. package/dist/sharing/SharePrimitives.js +11 -3
  43. package/dist/sharing/SharePrimitives.js.map +1 -1
  44. package/dist/sharing/index.d.ts +1 -1
  45. package/dist/sharing/index.d.ts.map +1 -1
  46. package/dist/sharing/index.js +1 -1
  47. package/dist/sharing/index.js.map +1 -1
  48. package/package.json +15 -1
  49. package/src/composer/ComposerPlusMenu.tsx +122 -65
  50. package/src/composer/PromptComposer.tsx +21 -0
  51. package/src/composer/TiptapComposer.spec.ts +46 -0
  52. package/src/composer/TiptapComposer.tsx +721 -349
  53. package/src/composer/index.ts +6 -0
  54. package/src/composer/model-selection.spec.ts +45 -0
  55. package/src/composer/model-selection.ts +51 -0
  56. package/src/data-grid/DataGrid.spec.tsx +243 -0
  57. package/src/data-grid/DataGrid.tsx +754 -0
  58. package/src/data-grid/index.ts +15 -0
  59. package/src/sharing/SharePrimitives.tsx +47 -8
  60. package/src/sharing/index.ts +4 -0
@@ -17,6 +17,12 @@ export {
17
17
  type TiptapComposerProps,
18
18
  type TiptapComposerSubmitOptions,
19
19
  } from "./TiptapComposer.js";
20
+ export {
21
+ isClaudeCodeAgentId,
22
+ isLunaModel,
23
+ resolvePreferredAgentModel,
24
+ type ComposerModelGroupLike,
25
+ } from "./model-selection.js";
20
26
  export {
21
27
  PromptComposer,
22
28
  type PromptComposerProps,
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ isClaudeCodeAgentId,
5
+ isLunaModel,
6
+ resolvePreferredAgentModel,
7
+ } from "./model-selection.js";
8
+
9
+ describe("composer agent model defaults", () => {
10
+ const groups = [
11
+ {
12
+ engine: "builder",
13
+ models: ["gpt-5-6-luna"],
14
+ configured: true,
15
+ },
16
+ {
17
+ engine: "builder",
18
+ models: ["claude-sonnet-5"],
19
+ configured: true,
20
+ },
21
+ ];
22
+
23
+ it("identifies Claude Code and Luna model ids", () => {
24
+ expect(isClaudeCodeAgentId("claude-code")).toBe(true);
25
+ expect(isClaudeCodeAgentId("claude-cli")).toBe(true);
26
+ expect(isClaudeCodeAgentId("codex")).toBe(false);
27
+ expect(isLunaModel("gpt-5.6-luna")).toBe(true);
28
+ expect(isLunaModel("openai/gpt-5.6-sol")).toBe(false);
29
+ });
30
+
31
+ it("defaults Claude Code to Sonnet and other agents to Luna", () => {
32
+ expect(resolvePreferredAgentModel("claude-code", groups)).toEqual({
33
+ engine: "builder",
34
+ model: "claude-sonnet-5",
35
+ });
36
+ expect(resolvePreferredAgentModel("codex", groups)).toEqual({
37
+ engine: "builder",
38
+ model: "gpt-5-6-luna",
39
+ });
40
+ expect(resolvePreferredAgentModel(undefined, groups)).toEqual({
41
+ engine: "builder",
42
+ model: "gpt-5-6-luna",
43
+ });
44
+ });
45
+ });
@@ -0,0 +1,51 @@
1
+ export interface ComposerModelGroupLike {
2
+ engine: string;
3
+ models: readonly string[];
4
+ configured?: boolean;
5
+ }
6
+
7
+ export function isClaudeCodeAgentId(agentId: string | undefined): boolean {
8
+ return agentId === "claude-code" || agentId === "claude-cli";
9
+ }
10
+
11
+ export function isLunaModel(model: string): boolean {
12
+ return model.toLowerCase().includes("luna");
13
+ }
14
+
15
+ export function resolvePreferredAgentModel(
16
+ agentId: string | undefined,
17
+ groups: readonly ComposerModelGroupLike[],
18
+ ): { model: string; engine: string } | undefined {
19
+ const candidates = groups.flatMap((group) =>
20
+ group.models
21
+ .filter((model) => model !== "auto")
22
+ .map((model) => ({
23
+ model,
24
+ engine: group.engine,
25
+ configured: group.configured !== false,
26
+ })),
27
+ );
28
+ const orderedCandidates = [
29
+ ...candidates.filter((candidate) => candidate.configured),
30
+ ...candidates.filter((candidate) => !candidate.configured),
31
+ ];
32
+
33
+ if (isClaudeCodeAgentId(agentId)) {
34
+ const preferred =
35
+ orderedCandidates.find(
36
+ (candidate) =>
37
+ candidate.model.toLowerCase().includes("sonnet") &&
38
+ !isLunaModel(candidate.model),
39
+ ) ?? orderedCandidates.find((candidate) => !isLunaModel(candidate.model));
40
+ return preferred
41
+ ? { model: preferred.model, engine: preferred.engine }
42
+ : undefined;
43
+ }
44
+
45
+ const preferred = orderedCandidates.find((candidate) =>
46
+ isLunaModel(candidate.model),
47
+ );
48
+ return preferred
49
+ ? { model: preferred.model, engine: preferred.engine }
50
+ : undefined;
51
+ }
@@ -0,0 +1,243 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { createRoot, type Root } from "react-dom/client";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import {
8
+ DataGrid,
9
+ dataGridColumnTemplate,
10
+ type DataGridColumn,
11
+ } from "./index.js";
12
+
13
+ interface Row {
14
+ id: string;
15
+ name: string;
16
+ status: string;
17
+ }
18
+
19
+ const columns: readonly DataGridColumn<Row>[] = [
20
+ {
21
+ id: "name",
22
+ label: "Name",
23
+ width: 180,
24
+ getValue: (row) => row.name,
25
+ renderCell: ({ value }) => String(value ?? ""),
26
+ },
27
+ {
28
+ id: "status",
29
+ label: "Status",
30
+ width: 120,
31
+ getValue: (row) => row.status,
32
+ renderCell: ({ value }) => String(value ?? ""),
33
+ renderEditor: ({ value, onCommit }) => (
34
+ <input
35
+ aria-label="Status editor"
36
+ defaultValue={String(value ?? "")}
37
+ onKeyDown={(event) => {
38
+ if (event.key === "Enter") onCommit(event.currentTarget.value);
39
+ }}
40
+ />
41
+ ),
42
+ },
43
+ ];
44
+
45
+ const rows: readonly Row[] = [
46
+ { id: "row-1", name: "First", status: "Open" },
47
+ { id: "row-2", name: "Second", status: "Closed" },
48
+ ];
49
+
50
+ describe("data-grid", () => {
51
+ it("builds a bounded template and includes the selection column only when requested", () => {
52
+ expect(dataGridColumnTemplate(columns, { name: 40 }, "none")).toBe(
53
+ "96px 120px",
54
+ );
55
+ expect(dataGridColumnTemplate(columns, { name: 220 }, "multiple")).toBe(
56
+ "40px 220px 120px",
57
+ );
58
+ });
59
+
60
+ describe("DataGrid", () => {
61
+ let container: HTMLDivElement;
62
+ let root: Root;
63
+
64
+ beforeEach(() => {
65
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
66
+ container = document.createElement("div");
67
+ document.body.appendChild(container);
68
+ root = createRoot(container);
69
+ });
70
+
71
+ afterEach(() => {
72
+ act(() => root.unmount());
73
+ container.remove();
74
+ vi.restoreAllMocks();
75
+ vi.unstubAllGlobals();
76
+ });
77
+
78
+ it("moves the active cell with arrows and commits an editor value", () => {
79
+ const onActiveCellChange = vi.fn();
80
+ const onCellCommit = vi.fn();
81
+
82
+ act(() => {
83
+ root.render(
84
+ <DataGrid
85
+ rows={rows}
86
+ columns={columns}
87
+ getRowId={(row) => row.id}
88
+ onActiveCellChange={onActiveCellChange}
89
+ onCellCommit={onCellCommit}
90
+ />,
91
+ );
92
+ });
93
+
94
+ const firstNameCell = container.querySelector<HTMLElement>(
95
+ '[data-data-grid-row-id="row-1"][data-data-grid-column-id="name"]',
96
+ );
97
+ expect(firstNameCell?.getAttribute("tabindex")).toBe("0");
98
+
99
+ act(() => {
100
+ firstNameCell?.dispatchEvent(
101
+ new KeyboardEvent("keydown", { bubbles: true, key: "ArrowRight" }),
102
+ );
103
+ });
104
+ expect(onActiveCellChange).toHaveBeenCalledWith({
105
+ rowId: "row-1",
106
+ columnId: "status",
107
+ editing: false,
108
+ });
109
+
110
+ const statusCell = container.querySelector<HTMLElement>(
111
+ '[data-data-grid-row-id="row-1"][data-data-grid-column-id="status"]',
112
+ );
113
+ act(() => {
114
+ statusCell?.dispatchEvent(
115
+ new KeyboardEvent("keydown", { bubbles: true, key: "Enter" }),
116
+ );
117
+ });
118
+ expect(
119
+ container.querySelector('input[aria-label="Status editor"]'),
120
+ ).not.toBeNull();
121
+
122
+ const input = container.querySelector<HTMLInputElement>(
123
+ 'input[aria-label="Status editor"]',
124
+ );
125
+ expect(input).not.toBeNull();
126
+ if (input) {
127
+ input.value = "In progress";
128
+ act(() => {
129
+ input.dispatchEvent(
130
+ new KeyboardEvent("keydown", { bubbles: true, key: "Enter" }),
131
+ );
132
+ });
133
+ }
134
+ expect(onCellCommit).toHaveBeenCalledWith({
135
+ row: rows[0],
136
+ rowIndex: 0,
137
+ column: columns[1],
138
+ previousValue: "Open",
139
+ value: "In progress",
140
+ });
141
+ });
142
+
143
+ it("supports controlled multi-row selection", () => {
144
+ const onSelectedRowIdsChange = vi.fn();
145
+
146
+ act(() => {
147
+ root.render(
148
+ <DataGrid
149
+ rows={rows}
150
+ columns={columns}
151
+ getRowId={(row) => row.id}
152
+ selection="multiple"
153
+ onSelectedRowIdsChange={onSelectedRowIdsChange}
154
+ />,
155
+ );
156
+ });
157
+
158
+ const rowCheckbox = container.querySelector<HTMLInputElement>(
159
+ '[aria-label="Select row row-1"]',
160
+ );
161
+ act(() => rowCheckbox?.click());
162
+ expect(onSelectedRowIdsChange).toHaveBeenCalledTimes(1);
163
+ expect(Array.from(onSelectedRowIdsChange.mock.calls[0][0])).toEqual([
164
+ "row-1",
165
+ ]);
166
+ });
167
+
168
+ it("reports pointer-based column resizing through the app callback", () => {
169
+ const onColumnWidthsChange = vi.fn();
170
+
171
+ act(() => {
172
+ root.render(
173
+ <DataGrid
174
+ rows={rows}
175
+ columns={columns}
176
+ getRowId={(row) => row.id}
177
+ onColumnWidthsChange={onColumnWidthsChange}
178
+ />,
179
+ );
180
+ });
181
+
182
+ const resizeHandle = container.querySelector<HTMLButtonElement>(
183
+ '[aria-label="Resize name column"]',
184
+ );
185
+ expect(resizeHandle).not.toBeNull();
186
+
187
+ act(() => {
188
+ resizeHandle?.dispatchEvent(
189
+ new PointerEvent("pointerdown", {
190
+ bubbles: true,
191
+ button: 0,
192
+ clientX: 100,
193
+ }),
194
+ );
195
+ document.dispatchEvent(
196
+ new PointerEvent("pointermove", {
197
+ bubbles: true,
198
+ clientX: 180,
199
+ }),
200
+ );
201
+ document.dispatchEvent(
202
+ new PointerEvent("pointerup", { bubbles: true, clientX: 180 }),
203
+ );
204
+ });
205
+
206
+ expect(onColumnWidthsChange).toHaveBeenLastCalledWith({ name: 260 });
207
+ });
208
+
209
+ it("lets an app own the outer header and body while sharing the grid template", () => {
210
+ const renderHeader = vi.fn(({ gridTemplateColumns }) => (
211
+ <div data-testid="custom-header" style={{ gridTemplateColumns }} />
212
+ ));
213
+ const renderBody = vi.fn(({ gridTemplateColumns }) => (
214
+ <div data-testid="custom-body" style={{ gridTemplateColumns }} />
215
+ ));
216
+
217
+ act(() => {
218
+ root.render(
219
+ <DataGrid
220
+ rows={rows}
221
+ columns={columns}
222
+ getRowId={(row) => row.id}
223
+ renderHeader={renderHeader}
224
+ renderBody={renderBody}
225
+ />,
226
+ );
227
+ });
228
+
229
+ expect(
230
+ container.querySelector("[data-testid=custom-header]"),
231
+ ).not.toBeNull();
232
+ expect(
233
+ container.querySelector("[data-testid=custom-body]"),
234
+ ).not.toBeNull();
235
+ expect(renderHeader).toHaveBeenCalledWith(
236
+ expect.objectContaining({ gridTemplateColumns: "180px 120px" }),
237
+ );
238
+ expect(renderBody).toHaveBeenCalledWith(
239
+ expect.objectContaining({ rows, columns }),
240
+ );
241
+ });
242
+ });
243
+ });