@grampro/kit 2.7.1 → 2.7.3
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.
- package/.gbs/skills/gbs-components/SKILL.md +233 -207
- package/.gbs/skills/gbs-components/references/data-grid.md +9 -0
- package/.gbs/skills/gbs-components/references/forms.md +12 -0
- package/.gbs/skills/gbs-components/references/install.md +42 -5
- package/CHANGELOG.md +963 -798
- package/README.md +13 -4
- package/index.cjs +250 -138
- package/package.json +1 -1
- package/source/components/accordion/passport.json +1 -1
- package/source/components/alert/passport.json +1 -1
- package/source/components/avatar/passport.json +1 -1
- package/source/components/badge/passport.json +1 -1
- package/source/components/breadcrumb/passport.json +1 -1
- package/source/components/button/passport.json +1 -1
- package/source/components/card/passport.json +1 -1
- package/source/components/checkbox/passport.json +2 -2
- package/source/components/checkbox/passport.manual.json +1 -1
- package/source/components/checkbox/react/Checkbox.tsx +14 -0
- package/source/components/combobox/passport.json +3 -2
- package/source/components/combobox/passport.manual.json +1 -1
- package/source/components/combobox/react/MultiSelect.tsx +13 -0
- package/source/components/combobox/react/Select.tsx +13 -0
- package/source/components/combobox/react/surface.ts +70 -0
- package/source/components/data-grid/passport.json +6 -6
- package/source/components/data-grid/react/DataGrid.tsx +353 -351
- package/source/components/date-picker/passport.json +3 -2
- package/source/components/date-picker/passport.manual.json +1 -1
- package/source/components/date-picker/react/DatePicker.tsx +16 -2
- package/source/components/date-picker/react/DateRangePicker.tsx +16 -2
- package/source/components/date-picker/react/surface.ts +68 -0
- package/source/components/dialog/passport.json +1 -1
- package/source/components/file-uploader/passport.json +1 -1
- package/source/components/input/passport.json +2 -2
- package/source/components/input/passport.manual.json +1 -1
- package/source/components/input/react/Input.tsx +21 -0
- package/source/components/menu/passport.json +1 -1
- package/source/components/modal/passport.json +1 -1
- package/source/components/number-input/passport.json +2 -2
- package/source/components/number-input/passport.manual.json +1 -1
- package/source/components/number-input/react/NumberInput.tsx +30 -0
- package/source/components/popover/passport.json +1 -1
- package/source/components/progress/passport.json +1 -1
- package/source/components/radio-group/passport.json +2 -2
- package/source/components/radio-group/passport.manual.json +1 -1
- package/source/components/radio-group/react/RadioGroup.tsx +33 -0
- package/source/components/shared/core/agent/field-executors.ts +173 -137
- package/source/components/shared/core/agent/index.ts +21 -0
- package/source/components/shared/core/agent/surface.ts +437 -0
- package/source/components/shared/core/agent/webmcp.ts +332 -328
- package/source/components/shared/core/agent/write.ts +62 -58
- package/source/components/shared/index.ts +7 -0
- package/source/components/shared/react/AgentSurface.tsx +145 -0
- package/source/components/shared/react/agentSurfaceContext.ts +92 -0
- package/source/components/shared/react/useFieldAgent.ts +62 -88
- package/source/components/shared/version.json +4 -4
- package/source/components/shared/version.ts +6 -6
- package/source/components/skeleton/passport.json +1 -1
- package/source/components/spinner/passport.json +1 -1
- package/source/components/switch/passport.json +2 -2
- package/source/components/switch/passport.manual.json +1 -1
- package/source/components/switch/react/Switch.tsx +16 -0
- package/source/components/tabs/passport.json +1 -1
- package/source/components/textarea/passport.json +2 -2
- package/source/components/textarea/passport.manual.json +1 -1
- package/source/components/textarea/react/Textarea.tsx +16 -0
- package/source/components/toaster/passport.json +1 -1
- package/source/components/tooltip/passport.json +1 -1
- package/tools/doctor/baseline.cjs +228 -0
- package/tools/doctor/contract.cjs +252 -0
- package/tools/doctor/format.cjs +160 -0
- package/tools/doctor/index.cjs +246 -0
- package/tools/doctor/usage.cjs +95 -0
- package/tools/jsx-tags.cjs +120 -0
- package/tools/mcp/server.cjs +60 -41
- package/tools/passport/extract.cjs +22 -40
- package/tools/passport/hash.cjs +56 -0
- package/tools/passport/index.cjs +4 -2
- package/tools/passport/typescript.cjs +220 -0
- package/tools/prompt.cjs +205 -0
- package/tools/ui.cjs +67 -0
|
@@ -1,137 +1,173 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* How a field actually performs what the validator allowed.
|
|
3
|
-
*
|
|
4
|
-
* Two kinds of field exist in this library and the difference is not cosmetic:
|
|
5
|
-
*
|
|
6
|
-
* - Input, Textarea, NumberInput, Checkbox, Switch and Radio hand you the
|
|
7
|
-
* DOM element as their ref. They are written through the element.
|
|
8
|
-
* - Combobox and DatePicker hand you a handle object, because their value is
|
|
9
|
-
* not a string in an input — it is a selection, or a date. They are
|
|
10
|
-
* written through that handle.
|
|
11
|
-
*
|
|
12
|
-
* Both end up satisfying the same `OperationExecutor`, which is the point of
|
|
13
|
-
* the executor being a separate thing from the operation. Everything above
|
|
14
|
-
* this file — the contract, the schemas, the validator, the refusals — is
|
|
15
|
-
* identical for both, and neither knows which it is driving.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { writeChecked, writeValue, writable } from "./write";
|
|
19
|
-
import type { FieldCommand, FieldContract, FieldOperationName } from "./field";
|
|
20
|
-
import type { ExecutionContext, RegisteredExecutor } from "./types";
|
|
21
|
-
|
|
22
|
-
export type FieldExecutionContext = ExecutionContext<FieldContract>;
|
|
23
|
-
|
|
24
|
-
/** What a component with a real handle must provide to be driven. */
|
|
25
|
-
export interface FieldHandleTarget {
|
|
26
|
-
setValue(value: unknown): void;
|
|
27
|
-
clear?(): void;
|
|
28
|
-
focus?(): void;
|
|
29
|
-
open?(): void;
|
|
30
|
-
close?(): void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type Registry = Map<string, RegisteredExecutor<FieldContract>>;
|
|
34
|
-
|
|
35
|
-
const define = (
|
|
36
|
-
registry: Registry,
|
|
37
|
-
operation: FieldOperationName,
|
|
38
|
-
execute: (input: FieldCommand, context: FieldExecutionContext) => unknown,
|
|
39
|
-
) => {
|
|
40
|
-
registry.set(operation, { operation, execute } as RegisteredExecutor<FieldContract>);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Executors for a field whose ref is the DOM element.
|
|
45
|
-
*
|
|
46
|
-
* `getElement` is a function rather than the element itself because the ref is
|
|
47
|
-
* not populated on the first render, and an executor registry built once must
|
|
48
|
-
* still work on the tenth.
|
|
49
|
-
*/
|
|
50
|
-
export function domFieldExecutors(
|
|
51
|
-
getElement: () => HTMLInputElement | HTMLTextAreaElement | null,
|
|
52
|
-
options: { readonly checkable?: boolean } = {},
|
|
53
|
-
): Registry {
|
|
54
|
-
const registry: Registry = new Map();
|
|
55
|
-
|
|
56
|
-
const element = () => {
|
|
57
|
-
const found = getElement();
|
|
58
|
-
if (!found) throw new Error("The field is not mounted.");
|
|
59
|
-
return found;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
define(registry, "setValue", (command, context) => {
|
|
63
|
-
const target = element();
|
|
64
|
-
if (context.dryRun) return { wouldWrite: command.value };
|
|
65
|
-
if (!writable(target)) throw new Error("The field is disabled or read-only.");
|
|
66
|
-
if (options.checkable) writeChecked(target as HTMLInputElement, Boolean(command.value));
|
|
67
|
-
else writeValue(target, String(command.value ?? ""));
|
|
68
|
-
return { wrote: command.value };
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
define(registry, "clear", (_command, context) => {
|
|
72
|
-
const target = element();
|
|
73
|
-
if (context.dryRun) return { wouldClear: true };
|
|
74
|
-
if (!writable(target)) throw new Error("The field is disabled or read-only.");
|
|
75
|
-
if (options.checkable) writeChecked(target as HTMLInputElement, false);
|
|
76
|
-
else writeValue(target, "");
|
|
77
|
-
return { cleared: true };
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
define(registry, "focus", (_command, context) => {
|
|
81
|
-
if (context.dryRun) return { wouldFocus: true };
|
|
82
|
-
element().focus();
|
|
83
|
-
return { focused: true };
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return registry;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Executors for a field that exposes a handle object rather than an element. */
|
|
90
|
-
export function handleFieldExecutors(getHandle: () => FieldHandleTarget | null): Registry {
|
|
91
|
-
const registry: Registry = new Map();
|
|
92
|
-
|
|
93
|
-
const handle = () => {
|
|
94
|
-
const found = getHandle();
|
|
95
|
-
if (!found) throw new Error("The field is not mounted.");
|
|
96
|
-
return found;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
define(registry, "setValue", (command, context) => {
|
|
100
|
-
const target = handle();
|
|
101
|
-
if (context.dryRun) return { wouldWrite: command.value };
|
|
102
|
-
target.setValue(command.value);
|
|
103
|
-
return { wrote: command.value };
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
define(registry, "clear", (_command, context) => {
|
|
107
|
-
const target = handle();
|
|
108
|
-
if (context.dryRun) return { wouldClear: true };
|
|
109
|
-
// A handle without `clear` still clears: writing null is the same thing.
|
|
110
|
-
if (target.clear) target.clear();
|
|
111
|
-
else target.setValue(null);
|
|
112
|
-
return { cleared: true };
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
define(registry, "focus", (_command, context) => {
|
|
116
|
-
const target = handle();
|
|
117
|
-
if (context.dryRun) return { wouldFocus: true };
|
|
118
|
-
target.focus?.();
|
|
119
|
-
return { focused: true };
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
define(registry, "open", (_command, context) => {
|
|
123
|
-
const target = handle();
|
|
124
|
-
if (context.dryRun) return { wouldOpen: true };
|
|
125
|
-
target.open?.();
|
|
126
|
-
return { opened: true };
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
define(registry, "close", (_command, context) => {
|
|
130
|
-
const target = handle();
|
|
131
|
-
if (context.dryRun) return { wouldClose: true };
|
|
132
|
-
target.close?.();
|
|
133
|
-
return { closed: true };
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
return registry;
|
|
137
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* How a field actually performs what the validator allowed.
|
|
3
|
+
*
|
|
4
|
+
* Two kinds of field exist in this library and the difference is not cosmetic:
|
|
5
|
+
*
|
|
6
|
+
* - Input, Textarea, NumberInput, Checkbox, Switch and Radio hand you the
|
|
7
|
+
* DOM element as their ref. They are written through the element.
|
|
8
|
+
* - Combobox and DatePicker hand you a handle object, because their value is
|
|
9
|
+
* not a string in an input — it is a selection, or a date. They are
|
|
10
|
+
* written through that handle.
|
|
11
|
+
*
|
|
12
|
+
* Both end up satisfying the same `OperationExecutor`, which is the point of
|
|
13
|
+
* the executor being a separate thing from the operation. Everything above
|
|
14
|
+
* this file — the contract, the schemas, the validator, the refusals — is
|
|
15
|
+
* identical for both, and neither knows which it is driving.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { writeChecked, writeValue, writable } from "./write";
|
|
19
|
+
import type { FieldCommand, FieldContract, FieldOperationName } from "./field";
|
|
20
|
+
import type { ExecutionContext, RegisteredExecutor } from "./types";
|
|
21
|
+
|
|
22
|
+
export type FieldExecutionContext = ExecutionContext<FieldContract>;
|
|
23
|
+
|
|
24
|
+
/** What a component with a real handle must provide to be driven. */
|
|
25
|
+
export interface FieldHandleTarget {
|
|
26
|
+
setValue(value: unknown): void;
|
|
27
|
+
clear?(): void;
|
|
28
|
+
focus?(): void;
|
|
29
|
+
open?(): void;
|
|
30
|
+
close?(): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Registry = Map<string, RegisteredExecutor<FieldContract>>;
|
|
34
|
+
|
|
35
|
+
const define = (
|
|
36
|
+
registry: Registry,
|
|
37
|
+
operation: FieldOperationName,
|
|
38
|
+
execute: (input: FieldCommand, context: FieldExecutionContext) => unknown,
|
|
39
|
+
) => {
|
|
40
|
+
registry.set(operation, { operation, execute } as RegisteredExecutor<FieldContract>);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Executors for a field whose ref is the DOM element.
|
|
45
|
+
*
|
|
46
|
+
* `getElement` is a function rather than the element itself because the ref is
|
|
47
|
+
* not populated on the first render, and an executor registry built once must
|
|
48
|
+
* still work on the tenth.
|
|
49
|
+
*/
|
|
50
|
+
export function domFieldExecutors(
|
|
51
|
+
getElement: () => HTMLInputElement | HTMLTextAreaElement | null,
|
|
52
|
+
options: { readonly checkable?: boolean } = {},
|
|
53
|
+
): Registry {
|
|
54
|
+
const registry: Registry = new Map();
|
|
55
|
+
|
|
56
|
+
const element = () => {
|
|
57
|
+
const found = getElement();
|
|
58
|
+
if (!found) throw new Error("The field is not mounted.");
|
|
59
|
+
return found;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
define(registry, "setValue", (command, context) => {
|
|
63
|
+
const target = element();
|
|
64
|
+
if (context.dryRun) return { wouldWrite: command.value };
|
|
65
|
+
if (!writable(target)) throw new Error("The field is disabled or read-only.");
|
|
66
|
+
if (options.checkable) writeChecked(target as HTMLInputElement, Boolean(command.value));
|
|
67
|
+
else writeValue(target, String(command.value ?? ""));
|
|
68
|
+
return { wrote: command.value };
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
define(registry, "clear", (_command, context) => {
|
|
72
|
+
const target = element();
|
|
73
|
+
if (context.dryRun) return { wouldClear: true };
|
|
74
|
+
if (!writable(target)) throw new Error("The field is disabled or read-only.");
|
|
75
|
+
if (options.checkable) writeChecked(target as HTMLInputElement, false);
|
|
76
|
+
else writeValue(target, "");
|
|
77
|
+
return { cleared: true };
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
define(registry, "focus", (_command, context) => {
|
|
81
|
+
if (context.dryRun) return { wouldFocus: true };
|
|
82
|
+
element().focus();
|
|
83
|
+
return { focused: true };
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return registry;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Executors for a field that exposes a handle object rather than an element. */
|
|
90
|
+
export function handleFieldExecutors(getHandle: () => FieldHandleTarget | null): Registry {
|
|
91
|
+
const registry: Registry = new Map();
|
|
92
|
+
|
|
93
|
+
const handle = () => {
|
|
94
|
+
const found = getHandle();
|
|
95
|
+
if (!found) throw new Error("The field is not mounted.");
|
|
96
|
+
return found;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
define(registry, "setValue", (command, context) => {
|
|
100
|
+
const target = handle();
|
|
101
|
+
if (context.dryRun) return { wouldWrite: command.value };
|
|
102
|
+
target.setValue(command.value);
|
|
103
|
+
return { wrote: command.value };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
define(registry, "clear", (_command, context) => {
|
|
107
|
+
const target = handle();
|
|
108
|
+
if (context.dryRun) return { wouldClear: true };
|
|
109
|
+
// A handle without `clear` still clears: writing null is the same thing.
|
|
110
|
+
if (target.clear) target.clear();
|
|
111
|
+
else target.setValue(null);
|
|
112
|
+
return { cleared: true };
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
define(registry, "focus", (_command, context) => {
|
|
116
|
+
const target = handle();
|
|
117
|
+
if (context.dryRun) return { wouldFocus: true };
|
|
118
|
+
target.focus?.();
|
|
119
|
+
return { focused: true };
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
define(registry, "open", (_command, context) => {
|
|
123
|
+
const target = handle();
|
|
124
|
+
if (context.dryRun) return { wouldOpen: true };
|
|
125
|
+
target.open?.();
|
|
126
|
+
return { opened: true };
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
define(registry, "close", (_command, context) => {
|
|
130
|
+
const target = handle();
|
|
131
|
+
if (context.dryRun) return { wouldClose: true };
|
|
132
|
+
target.close?.();
|
|
133
|
+
return { closed: true };
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return registry;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A handle, as opposed to a DOM node. Checked by capability, not by instanceof. */
|
|
140
|
+
const isHandle = (value: unknown): value is FieldHandleTarget =>
|
|
141
|
+
Boolean(value) && typeof (value as FieldHandleTarget).setValue === "function";
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Executors that decide at call time whether the target is a DOM field or a
|
|
145
|
+
* handle.
|
|
146
|
+
*
|
|
147
|
+
* It cannot be decided earlier: a ref is null on the first render, and
|
|
148
|
+
* deciding then would treat every component as a DOM field for good. Both
|
|
149
|
+
* registries are built, and the choice is made inside `execute`, by which
|
|
150
|
+
* point the target exists.
|
|
151
|
+
*/
|
|
152
|
+
export function autoFieldExecutors(
|
|
153
|
+
getTarget: () => unknown,
|
|
154
|
+
options: { readonly checkable?: boolean; readonly component?: string } = {},
|
|
155
|
+
): Registry {
|
|
156
|
+
const dom = domFieldExecutors(() => getTarget() as HTMLInputElement | HTMLTextAreaElement | null, {
|
|
157
|
+
checkable: options.checkable,
|
|
158
|
+
});
|
|
159
|
+
const handle = handleFieldExecutors(() => getTarget() as FieldHandleTarget | null);
|
|
160
|
+
|
|
161
|
+
const registry: Registry = new Map();
|
|
162
|
+
for (const name of new Set([...dom.keys(), ...handle.keys()])) {
|
|
163
|
+
registry.set(name, {
|
|
164
|
+
operation: name,
|
|
165
|
+
execute: (input, context) => {
|
|
166
|
+
const chosen = isHandle(getTarget()) ? handle.get(name) : dom.get(name);
|
|
167
|
+
if (!chosen) throw new Error(`${options.component ?? "This field"} cannot ${name}.`);
|
|
168
|
+
return chosen.execute(input, context);
|
|
169
|
+
},
|
|
170
|
+
} as RegisteredExecutor<FieldContract>);
|
|
171
|
+
}
|
|
172
|
+
return registry;
|
|
173
|
+
}
|
|
@@ -28,6 +28,8 @@ export { parseQuantity, type ParseQuantityOptions, type Quantity } from "./numbe
|
|
|
28
28
|
export {
|
|
29
29
|
buildToolInputSchema,
|
|
30
30
|
registerAgentTool,
|
|
31
|
+
runProjected,
|
|
32
|
+
toToolResult,
|
|
31
33
|
webmcpAvailable,
|
|
32
34
|
type ModelContext,
|
|
33
35
|
type ProjectableAgent,
|
|
@@ -61,6 +63,7 @@ export {
|
|
|
61
63
|
type FieldOption,
|
|
62
64
|
} from "./field";
|
|
63
65
|
export {
|
|
66
|
+
autoFieldExecutors,
|
|
64
67
|
domFieldExecutors,
|
|
65
68
|
handleFieldExecutors,
|
|
66
69
|
type FieldExecutionContext,
|
|
@@ -72,5 +75,23 @@ export {
|
|
|
72
75
|
type FieldAgentOptions,
|
|
73
76
|
type FieldExecution,
|
|
74
77
|
} from "./field-agent";
|
|
78
|
+
/*
|
|
79
|
+
* A page that describes itself to an agent: every operable component, what it
|
|
80
|
+
* holds and accepts, one tool to operate them through their own validators.
|
|
81
|
+
*/
|
|
82
|
+
export {
|
|
83
|
+
createAgentSurface,
|
|
84
|
+
describeFieldContract,
|
|
85
|
+
projectFieldAgent,
|
|
86
|
+
registerSurfaceTool,
|
|
87
|
+
SURFACE_TOOL_NAME,
|
|
88
|
+
SURFACE_TOOL_SCHEMA,
|
|
89
|
+
SURFACE_VERSION,
|
|
90
|
+
type AgentSurfaceHandle,
|
|
91
|
+
type RegisterSurfaceToolOptions,
|
|
92
|
+
type SurfaceEntry,
|
|
93
|
+
type SurfaceManifest,
|
|
94
|
+
type SurfaceMember,
|
|
95
|
+
} from "./surface";
|
|
75
96
|
/* Browser-only: everything above this line runs on a server too. */
|
|
76
97
|
export { writable, writeChecked, writeValue } from "./write";
|