@assistant-ui/react-generative-ui 0.0.24 → 0.1.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.
- package/README.md +2 -0
- package/dist/a2ui.d.ts +1 -4
- package/dist/a2ui.d.ts.map +1 -1
- package/dist/a2ui.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -10
- package/dist/index.server.d.ts +1 -2
- package/dist/index.server.d.ts.map +1 -1
- package/dist/index.server.js +1 -10
- package/dist/ir.d.ts +1 -111
- package/dist/ir.d.ts.map +1 -1
- package/dist/ir.js +1 -175
- package/dist/slack.d.ts +1 -4
- package/dist/slack.d.ts.map +1 -1
- package/dist/slack.js +1 -4
- package/dist/teams.d.ts +1 -4
- package/dist/teams.d.ts.map +1 -1
- package/dist/teams.js +1 -4
- package/package.json +4 -5
- package/src/a2ui.ts +1 -22
- package/src/index.server.ts +1 -2
- package/src/index.test.ts +75 -0
- package/src/index.ts +1 -2
- package/src/ir.ts +1 -279
- package/src/slack.ts +1 -39
- package/src/teams.ts +1 -35
- package/dist/JSONGenerativeUI.client.d.ts +0 -28
- package/dist/JSONGenerativeUI.client.d.ts.map +0 -1
- package/dist/JSONGenerativeUI.client.js +0 -101
- package/dist/JSONGenerativeUI.client.js.map +0 -1
- package/dist/JSONGenerativeUI.server.d.ts +0 -21
- package/dist/JSONGenerativeUI.server.d.ts.map +0 -1
- package/dist/JSONGenerativeUI.server.js +0 -32
- package/dist/JSONGenerativeUI.server.js.map +0 -1
- package/dist/JSONGenerativeUI.shared.d.ts +0 -63
- package/dist/JSONGenerativeUI.shared.d.ts.map +0 -1
- package/dist/JSONGenerativeUI.shared.js +0 -31
- package/dist/JSONGenerativeUI.shared.js.map +0 -1
- package/dist/RadioGroupScope.d.ts +0 -6
- package/dist/RadioGroupScope.d.ts.map +0 -1
- package/dist/RadioGroupScope.js +0 -25
- package/dist/RadioGroupScope.js.map +0 -1
- package/dist/a2ui/BindingContext.d.ts +0 -11
- package/dist/a2ui/BindingContext.d.ts.map +0 -1
- package/dist/a2ui/BindingContext.js +0 -12
- package/dist/a2ui/BindingContext.js.map +0 -1
- package/dist/a2ui/PresentRenderer.d.ts +0 -11
- package/dist/a2ui/PresentRenderer.d.ts.map +0 -1
- package/dist/a2ui/PresentRenderer.js +0 -39
- package/dist/a2ui/PresentRenderer.js.map +0 -1
- package/dist/a2ui/convert.d.ts +0 -15
- package/dist/a2ui/convert.d.ts.map +0 -1
- package/dist/a2ui/convert.js +0 -728
- package/dist/a2ui/convert.js.map +0 -1
- package/dist/a2ui/dataModel.d.ts +0 -9
- package/dist/a2ui/dataModel.d.ts.map +0 -1
- package/dist/a2ui/dataModel.js +0 -44
- package/dist/a2ui/dataModel.js.map +0 -1
- package/dist/a2ui/reducer.d.ts +0 -8
- package/dist/a2ui/reducer.d.ts.map +0 -1
- package/dist/a2ui/reducer.js +0 -242
- package/dist/a2ui/reducer.js.map +0 -1
- package/dist/a2ui/snapshot.d.ts +0 -3
- package/dist/a2ui/snapshot.d.ts.map +0 -1
- package/dist/a2ui/snapshot.js +0 -33
- package/dist/a2ui/snapshot.js.map +0 -1
- package/dist/a2ui/types.d.ts +0 -88
- package/dist/a2ui/types.d.ts.map +0 -1
- package/dist/a2ui/types.js +0 -6
- package/dist/a2ui/types.js.map +0 -1
- package/dist/a2ui/valueFunctions.d.ts +0 -16
- package/dist/a2ui/valueFunctions.d.ts.map +0 -1
- package/dist/a2ui/valueFunctions.js +0 -332
- package/dist/a2ui/valueFunctions.js.map +0 -1
- package/dist/actionRegistry.d.ts +0 -29
- package/dist/actionRegistry.d.ts.map +0 -1
- package/dist/actionRegistry.js +0 -41
- package/dist/actionRegistry.js.map +0 -1
- package/dist/answeredValues.d.ts +0 -16
- package/dist/answeredValues.d.ts.map +0 -1
- package/dist/answeredValues.js +0 -37
- package/dist/answeredValues.js.map +0 -1
- package/dist/buildPresentParameters.d.ts +0 -21
- package/dist/buildPresentParameters.d.ts.map +0 -1
- package/dist/buildPresentParameters.js +0 -95
- package/dist/buildPresentParameters.js.map +0 -1
- package/dist/constants.d.ts +0 -15
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -19
- package/dist/constants.js.map +0 -1
- package/dist/convert/boundSpec.d.ts +0 -48
- package/dist/convert/boundSpec.d.ts.map +0 -1
- package/dist/convert/boundSpec.js +0 -108
- package/dist/convert/boundSpec.js.map +0 -1
- package/dist/convert/copyBounded.d.ts +0 -25
- package/dist/convert/copyBounded.d.ts.map +0 -1
- package/dist/convert/copyBounded.js +0 -38
- package/dist/convert/copyBounded.js.map +0 -1
- package/dist/convert/isElement.d.ts +0 -3
- package/dist/convert/isElement.d.ts.map +0 -1
- package/dist/convert/isElement.js +0 -6
- package/dist/convert/isElement.js.map +0 -1
- package/dist/convert/takeRun.d.ts +0 -6
- package/dist/convert/takeRun.d.ts.map +0 -1
- package/dist/convert/takeRun.js +0 -20
- package/dist/convert/takeRun.js.map +0 -1
- package/dist/defineGenerativeComponents.d.ts +0 -29
- package/dist/defineGenerativeComponents.d.ts.map +0 -1
- package/dist/defineGenerativeComponents.js +0 -34
- package/dist/defineGenerativeComponents.js.map +0 -1
- package/dist/fieldReferences.d.ts +0 -9
- package/dist/fieldReferences.d.ts.map +0 -1
- package/dist/fieldReferences.js +0 -53
- package/dist/fieldReferences.js.map +0 -1
- package/dist/generativeUIToJSX.d.ts +0 -18
- package/dist/generativeUIToJSX.d.ts.map +0 -1
- package/dist/generativeUIToJSX.js +0 -76
- package/dist/generativeUIToJSX.js.map +0 -1
- package/dist/index.shared.d.ts +0 -15
- package/dist/index.shared.d.ts.map +0 -1
- package/dist/index.shared.js +0 -9
- package/dist/ir.js.map +0 -1
- package/dist/renderGenerativeUI.d.ts +0 -14
- package/dist/renderGenerativeUI.d.ts.map +0 -1
- package/dist/renderGenerativeUI.js +0 -83
- package/dist/renderGenerativeUI.js.map +0 -1
- package/dist/scopeSchema.d.ts +0 -6
- package/dist/scopeSchema.d.ts.map +0 -1
- package/dist/scopeSchema.js +0 -56
- package/dist/scopeSchema.js.map +0 -1
- package/dist/slack/constants.d.ts +0 -93
- package/dist/slack/constants.d.ts.map +0 -1
- package/dist/slack/constants.js +0 -146
- package/dist/slack/constants.js.map +0 -1
- package/dist/slack/decodeBlockAction.d.ts +0 -10
- package/dist/slack/decodeBlockAction.d.ts.map +0 -1
- package/dist/slack/decodeBlockAction.js +0 -129
- package/dist/slack/decodeBlockAction.js.map +0 -1
- package/dist/slack/fromSlackBlocks.d.ts +0 -27
- package/dist/slack/fromSlackBlocks.d.ts.map +0 -1
- package/dist/slack/fromSlackBlocks.js +0 -483
- package/dist/slack/fromSlackBlocks.js.map +0 -1
- package/dist/slack/toSlackBlocks.d.ts +0 -4
- package/dist/slack/toSlackBlocks.d.ts.map +0 -1
- package/dist/slack/toSlackBlocks.js +0 -901
- package/dist/slack/toSlackBlocks.js.map +0 -1
- package/dist/slack/types.d.ts +0 -204
- package/dist/slack/types.d.ts.map +0 -1
- package/dist/slack/types.js +0 -0
- package/dist/teams/constants.d.ts +0 -34
- package/dist/teams/constants.d.ts.map +0 -1
- package/dist/teams/constants.js +0 -68
- package/dist/teams/constants.js.map +0 -1
- package/dist/teams/decodeSubmitData.d.ts +0 -26
- package/dist/teams/decodeSubmitData.d.ts.map +0 -1
- package/dist/teams/decodeSubmitData.js +0 -70
- package/dist/teams/decodeSubmitData.js.map +0 -1
- package/dist/teams/temporalId.d.ts +0 -5
- package/dist/teams/temporalId.d.ts.map +0 -1
- package/dist/teams/temporalId.js +0 -28
- package/dist/teams/temporalId.js.map +0 -1
- package/dist/teams/toAdaptiveCard.d.ts +0 -45
- package/dist/teams/toAdaptiveCard.d.ts.map +0 -1
- package/dist/teams/toAdaptiveCard.js +0 -642
- package/dist/teams/toAdaptiveCard.js.map +0 -1
- package/dist/teams/toTeamsAttachments.d.ts +0 -10
- package/dist/teams/toTeamsAttachments.d.ts.map +0 -1
- package/dist/teams/toTeamsAttachments.js +0 -99
- package/dist/teams/toTeamsAttachments.js.map +0 -1
- package/dist/teams/types.d.ts +0 -224
- package/dist/teams/types.d.ts.map +0 -1
- package/dist/teams/types.js +0 -0
- package/dist/temporal.d.ts +0 -33
- package/dist/temporal.d.ts.map +0 -1
- package/dist/temporal.js +0 -153
- package/dist/temporal.js.map +0 -1
- package/dist/types.d.ts +0 -99
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -0
- package/dist/vocabulary/alert.d.ts +0 -32
- package/dist/vocabulary/alert.d.ts.map +0 -1
- package/dist/vocabulary/alert.js +0 -160
- package/dist/vocabulary/alert.js.map +0 -1
- package/dist/vocabulary/collectFormValues.d.ts +0 -23
- package/dist/vocabulary/collectFormValues.d.ts.map +0 -1
- package/dist/vocabulary/collectFormValues.js +0 -44
- package/dist/vocabulary/collectFormValues.js.map +0 -1
- package/dist/vocabulary/data.d.ts +0 -66
- package/dist/vocabulary/data.d.ts.map +0 -1
- package/dist/vocabulary/data.js +0 -503
- package/dist/vocabulary/data.js.map +0 -1
- package/dist/vocabulary/dispatch.d.ts +0 -9
- package/dist/vocabulary/dispatch.d.ts.map +0 -1
- package/dist/vocabulary/dispatch.js +0 -50
- package/dist/vocabulary/dispatch.js.map +0 -1
- package/dist/vocabulary/fact.d.ts +0 -19
- package/dist/vocabulary/fact.d.ts.map +0 -1
- package/dist/vocabulary/fact.js +0 -51
- package/dist/vocabulary/fact.js.map +0 -1
- package/dist/vocabulary/form.d.ts +0 -11
- package/dist/vocabulary/form.d.ts.map +0 -1
- package/dist/vocabulary/form.js +0 -23
- package/dist/vocabulary/form.js.map +0 -1
- package/dist/vocabulary/formatValue.d.ts +0 -7
- package/dist/vocabulary/formatValue.d.ts.map +0 -1
- package/dist/vocabulary/formatValue.js +0 -76
- package/dist/vocabulary/formatValue.js.map +0 -1
- package/dist/vocabulary/icon.d.ts +0 -41
- package/dist/vocabulary/icon.d.ts.map +0 -1
- package/dist/vocabulary/icon.js +0 -272
- package/dist/vocabulary/icon.js.map +0 -1
- package/dist/vocabulary/index.d.ts +0 -12
- package/dist/vocabulary/index.d.ts.map +0 -1
- package/dist/vocabulary/index.js +0 -36
- package/dist/vocabulary/index.js.map +0 -1
- package/dist/vocabulary/interactive.d.ts +0 -230
- package/dist/vocabulary/interactive.d.ts.map +0 -1
- package/dist/vocabulary/interactive.js +0 -584
- package/dist/vocabulary/interactive.js.map +0 -1
- package/dist/vocabulary/layout.d.ts +0 -79
- package/dist/vocabulary/layout.d.ts.map +0 -1
- package/dist/vocabulary/layout.js +0 -139
- package/dist/vocabulary/layout.js.map +0 -1
- package/dist/vocabulary/list.d.ts +0 -14
- package/dist/vocabulary/list.d.ts.map +0 -1
- package/dist/vocabulary/list.js +0 -50
- package/dist/vocabulary/list.js.map +0 -1
- package/dist/vocabulary/media.d.ts +0 -25
- package/dist/vocabulary/media.d.ts.map +0 -1
- package/dist/vocabulary/media.js +0 -42
- package/dist/vocabulary/media.js.map +0 -1
- package/dist/vocabulary/text.d.ts +0 -57
- package/dist/vocabulary/text.d.ts.map +0 -1
- package/dist/vocabulary/text.js +0 -49
- package/dist/vocabulary/text.js.map +0 -1
- package/dist/vocabulary/toTextContent.d.ts +0 -2
- package/dist/vocabulary/toTextContent.d.ts.map +0 -1
- package/dist/vocabulary/toTextContent.js +0 -6
- package/dist/vocabulary/toTextContent.js.map +0 -1
- package/src/JSONGenerativeUI.client.tsx +0 -168
- package/src/JSONGenerativeUI.server.tsx +0 -39
- package/src/JSONGenerativeUI.shared.ts +0 -90
- package/src/JSONGenerativeUI.test.tsx +0 -1298
- package/src/RadioGroupScope.server.test.tsx +0 -24
- package/src/RadioGroupScope.tsx +0 -34
- package/src/a2ui/BindingContext.ts +0 -26
- package/src/a2ui/PresentRenderer.tsx +0 -83
- package/src/a2ui/convert.test.ts +0 -2923
- package/src/a2ui/convert.ts +0 -1207
- package/src/a2ui/dataModel.ts +0 -69
- package/src/a2ui/reducer.test.ts +0 -422
- package/src/a2ui/reducer.ts +0 -363
- package/src/a2ui/snapshot.test.ts +0 -52
- package/src/a2ui/snapshot.ts +0 -52
- package/src/a2ui/types.ts +0 -114
- package/src/a2ui/valueFunctions.test.ts +0 -271
- package/src/a2ui/valueFunctions.ts +0 -523
- package/src/actionRegistry.test.ts +0 -90
- package/src/actionRegistry.ts +0 -88
- package/src/answeredValues.test.ts +0 -33
- package/src/answeredValues.tsx +0 -70
- package/src/buildPresentParameters.test.ts +0 -246
- package/src/buildPresentParameters.ts +0 -130
- package/src/constants.ts +0 -17
- package/src/convert/boundSpec.test.ts +0 -192
- package/src/convert/boundSpec.ts +0 -134
- package/src/convert/copyBounded.test.ts +0 -131
- package/src/convert/copyBounded.ts +0 -38
- package/src/convert/isElement.ts +0 -6
- package/src/convert/takeRun.test.ts +0 -64
- package/src/convert/takeRun.ts +0 -19
- package/src/defineGenerativeComponents.ts +0 -38
- package/src/fieldReferences.test.ts +0 -66
- package/src/fieldReferences.ts +0 -83
- package/src/generativeUIToJSX.test.ts +0 -268
- package/src/generativeUIToJSX.ts +0 -152
- package/src/index.server.test.ts +0 -106
- package/src/index.shared.ts +0 -61
- package/src/ir.test.ts +0 -203
- package/src/renderGenerativeUI.test.tsx +0 -373
- package/src/renderGenerativeUI.tsx +0 -165
- package/src/scopeSchema.test.ts +0 -72
- package/src/scopeSchema.ts +0 -71
- package/src/slack/a2uiSlack.test.ts +0 -198
- package/src/slack/constants.ts +0 -194
- package/src/slack/datePicker.test.ts +0 -612
- package/src/slack/decodeBlockAction.test.ts +0 -233
- package/src/slack/decodeBlockAction.ts +0 -232
- package/src/slack/fromSlackBlocks.test.ts +0 -1465
- package/src/slack/fromSlackBlocks.ts +0 -789
- package/src/slack/toSlackBlocks.test.ts +0 -3065
- package/src/slack/toSlackBlocks.ts +0 -1818
- package/src/slack/types.ts +0 -264
- package/src/teams/constants.ts +0 -93
- package/src/teams/decodeSubmitData.test.ts +0 -397
- package/src/teams/decodeSubmitData.ts +0 -110
- package/src/teams/temporal.test.ts +0 -766
- package/src/teams/temporalId.ts +0 -56
- package/src/teams/toAdaptiveCard.test.ts +0 -1897
- package/src/teams/toAdaptiveCard.ts +0 -1057
- package/src/teams/toTeamsAttachments.test.ts +0 -74
- package/src/teams/toTeamsAttachments.ts +0 -133
- package/src/teams/types.ts +0 -266
- package/src/temporal.test.ts +0 -337
- package/src/temporal.ts +0 -263
- package/src/types.ts +0 -119
- package/src/vocabulary/alert.test.tsx +0 -427
- package/src/vocabulary/alert.tsx +0 -217
- package/src/vocabulary/collectFormValues.test.ts +0 -389
- package/src/vocabulary/collectFormValues.ts +0 -116
- package/src/vocabulary/data.test.tsx +0 -858
- package/src/vocabulary/data.tsx +0 -819
- package/src/vocabulary/datepicker.dom.test.tsx +0 -1457
- package/src/vocabulary/dispatch.ts +0 -83
- package/src/vocabulary/fact.test.tsx +0 -94
- package/src/vocabulary/fact.tsx +0 -54
- package/src/vocabulary/form.test.tsx +0 -113
- package/src/vocabulary/form.tsx +0 -39
- package/src/vocabulary/formatValue.test.ts +0 -72
- package/src/vocabulary/formatValue.ts +0 -127
- package/src/vocabulary/icon.test.tsx +0 -64
- package/src/vocabulary/icon.tsx +0 -174
- package/src/vocabulary/index.test.ts +0 -65
- package/src/vocabulary/index.ts +0 -33
- package/src/vocabulary/interactive.dispatch.test.tsx +0 -454
- package/src/vocabulary/interactive.dom.test.tsx +0 -1747
- package/src/vocabulary/interactive.test.tsx +0 -377
- package/src/vocabulary/interactive.tsx +0 -1109
- package/src/vocabulary/layout.test.tsx +0 -323
- package/src/vocabulary/layout.tsx +0 -247
- package/src/vocabulary/list.test.tsx +0 -222
- package/src/vocabulary/list.tsx +0 -50
- package/src/vocabulary/media.test.tsx +0 -85
- package/src/vocabulary/media.tsx +0 -56
- package/src/vocabulary/text.test.tsx +0 -102
- package/src/vocabulary/text.tsx +0 -60
- package/src/vocabulary/toTextContent.ts +0 -2
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import type { ReactElement } from "react";
|
|
3
|
-
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
-
import { renderGenerativeUI } from "../renderGenerativeUI";
|
|
5
|
-
import { createActionRegistry } from "../actionRegistry";
|
|
6
|
-
import type { GenerativeUIDispatch } from "../types";
|
|
7
|
-
import { listVocabulary } from "./list";
|
|
8
|
-
|
|
9
|
-
const render = (node: unknown) =>
|
|
10
|
-
renderToStaticMarkup(<>{renderGenerativeUI(node, listVocabulary)}</>);
|
|
11
|
-
|
|
12
|
-
describe("listVocabulary", () => {
|
|
13
|
-
it("ListView renders a ul wrapping its children", () => {
|
|
14
|
-
const html = render({
|
|
15
|
-
$type: "ListView",
|
|
16
|
-
children: [
|
|
17
|
-
{ $type: "ListViewItem", children: "a" },
|
|
18
|
-
{ $type: "ListViewItem", children: "b" },
|
|
19
|
-
],
|
|
20
|
-
});
|
|
21
|
-
expect(html).toContain('<ul data-aui="listview">');
|
|
22
|
-
expect((html.match(/data-aui="listview-item"/g) ?? []).length).toBe(2);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("ListViewItem without $action renders children directly with no trigger wrapper", () => {
|
|
26
|
-
expect(render({ $type: "ListViewItem", children: "row" })).toBe(
|
|
27
|
-
'<li data-aui="listview-item">row</li>',
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("ListViewItem with $action but no dispatch renders children directly (read-only)", () => {
|
|
32
|
-
expect(
|
|
33
|
-
render({
|
|
34
|
-
$type: "ListViewItem",
|
|
35
|
-
$action: { type: "open" },
|
|
36
|
-
children: "row",
|
|
37
|
-
}),
|
|
38
|
-
).toBe('<li data-aui="listview-item">row</li>');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("ListViewItem wraps children in a clickable trigger when $action and dispatch are both present", () => {
|
|
42
|
-
const registry = createActionRegistry({ open: vi.fn() });
|
|
43
|
-
const html = renderToStaticMarkup(
|
|
44
|
-
<>
|
|
45
|
-
{renderGenerativeUI(
|
|
46
|
-
{ $type: "ListViewItem", $action: { type: "open" }, children: "row" },
|
|
47
|
-
listVocabulary,
|
|
48
|
-
{ status: "done", dispatch: registry.dispatch },
|
|
49
|
-
)}
|
|
50
|
-
</>,
|
|
51
|
-
);
|
|
52
|
-
expect(html).toContain('data-aui="listview-item-trigger"');
|
|
53
|
-
expect(html).toContain('role="button"');
|
|
54
|
-
expect(html).toContain('tabindex="0"');
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("listVocabulary $action dispatch", () => {
|
|
59
|
-
const rowOut = (dispatch: GenerativeUIDispatch) =>
|
|
60
|
-
listVocabulary.ListViewItem.render({
|
|
61
|
-
$status: "done",
|
|
62
|
-
$action: { type: "open" },
|
|
63
|
-
$dispatch: dispatch,
|
|
64
|
-
children: "row",
|
|
65
|
-
}) as ReactElement<{ children: ReactElement }>;
|
|
66
|
-
|
|
67
|
-
it("clicking the trigger fires $action with no $input", () => {
|
|
68
|
-
const handler = vi.fn();
|
|
69
|
-
const registry = createActionRegistry({ open: handler });
|
|
70
|
-
const trigger = rowOut(registry.dispatch).props.children as ReactElement;
|
|
71
|
-
const onClick = (trigger.props as { onClick: (e: unknown) => void })
|
|
72
|
-
.onClick;
|
|
73
|
-
onClick({
|
|
74
|
-
target: { closest: () => trigger },
|
|
75
|
-
currentTarget: trigger,
|
|
76
|
-
});
|
|
77
|
-
expect(handler).toHaveBeenCalledWith({ payload: { type: "open" } });
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
type KeyDownEvent = {
|
|
81
|
-
key: string;
|
|
82
|
-
target: { closest: (selector: string) => unknown };
|
|
83
|
-
currentTarget: unknown;
|
|
84
|
-
preventDefault: () => void;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const getOnKeyDown = (dispatch: GenerativeUIDispatch) => {
|
|
88
|
-
const trigger = rowOut(dispatch).props.children;
|
|
89
|
-
return (trigger.props as { onKeyDown: (e: KeyDownEvent) => void })
|
|
90
|
-
.onKeyDown;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
it("Enter and Space keydown both fire $action", () => {
|
|
94
|
-
const handler = vi.fn();
|
|
95
|
-
const registry = createActionRegistry({ open: handler });
|
|
96
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
97
|
-
const currentTarget = {};
|
|
98
|
-
onKeyDown({
|
|
99
|
-
key: "Enter",
|
|
100
|
-
target: { closest: () => currentTarget },
|
|
101
|
-
currentTarget,
|
|
102
|
-
preventDefault: vi.fn(),
|
|
103
|
-
});
|
|
104
|
-
onKeyDown({
|
|
105
|
-
key: " ",
|
|
106
|
-
target: { closest: () => currentTarget },
|
|
107
|
-
currentTarget,
|
|
108
|
-
preventDefault: vi.fn(),
|
|
109
|
-
});
|
|
110
|
-
expect(handler).toHaveBeenCalledTimes(2);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("a keydown for any other key does not fire $action", () => {
|
|
114
|
-
const handler = vi.fn();
|
|
115
|
-
const registry = createActionRegistry({ open: handler });
|
|
116
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
117
|
-
onKeyDown({
|
|
118
|
-
key: "Tab",
|
|
119
|
-
target: { closest: () => null },
|
|
120
|
-
currentTarget: {},
|
|
121
|
-
preventDefault: vi.fn(),
|
|
122
|
-
});
|
|
123
|
-
expect(handler).not.toHaveBeenCalled();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("a click whose target sits inside a nested interactive element does not fire the row action", () => {
|
|
127
|
-
const handler = vi.fn();
|
|
128
|
-
const registry = createActionRegistry({ open: handler });
|
|
129
|
-
const trigger = rowOut(registry.dispatch).props.children as ReactElement;
|
|
130
|
-
const onClick = (trigger.props as { onClick: (e: unknown) => void })
|
|
131
|
-
.onClick;
|
|
132
|
-
const nestedButton = {
|
|
133
|
-
closest: (selector: string) =>
|
|
134
|
-
selector.includes("button") ? nestedButton : null,
|
|
135
|
-
};
|
|
136
|
-
onClick({ target: nestedButton, currentTarget: {} });
|
|
137
|
-
expect(handler).not.toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("a keydown Enter whose target sits inside a nested interactive element does not fire the row action", () => {
|
|
141
|
-
const handler = vi.fn();
|
|
142
|
-
const registry = createActionRegistry({ open: handler });
|
|
143
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
144
|
-
const nestedInput = {
|
|
145
|
-
closest: (selector: string) =>
|
|
146
|
-
selector.includes("input") ? nestedInput : null,
|
|
147
|
-
};
|
|
148
|
-
const preventDefault = vi.fn();
|
|
149
|
-
onKeyDown({
|
|
150
|
-
key: "Enter",
|
|
151
|
-
target: nestedInput,
|
|
152
|
-
currentTarget: {},
|
|
153
|
-
preventDefault,
|
|
154
|
-
});
|
|
155
|
-
expect(handler).not.toHaveBeenCalled();
|
|
156
|
-
expect(preventDefault).not.toHaveBeenCalled();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('a click whose target sits inside a nested role="button" element does not fire the row action', () => {
|
|
160
|
-
const handler = vi.fn();
|
|
161
|
-
const registry = createActionRegistry({ open: handler });
|
|
162
|
-
const trigger = rowOut(registry.dispatch).props.children as ReactElement;
|
|
163
|
-
const onClick = (trigger.props as { onClick: (e: unknown) => void })
|
|
164
|
-
.onClick;
|
|
165
|
-
const nestedRoleButton = {
|
|
166
|
-
closest: (selector: string) =>
|
|
167
|
-
selector.includes('[role="button"]') ? nestedRoleButton : null,
|
|
168
|
-
};
|
|
169
|
-
onClick({ target: nestedRoleButton, currentTarget: {} });
|
|
170
|
-
expect(handler).not.toHaveBeenCalled();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('a keydown Enter whose target sits inside a nested role="button" element (e.g. a nested ListViewItem trigger) does not fire the row action', () => {
|
|
174
|
-
const handler = vi.fn();
|
|
175
|
-
const registry = createActionRegistry({ open: handler });
|
|
176
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
177
|
-
const nestedRoleButton = {
|
|
178
|
-
closest: (selector: string) =>
|
|
179
|
-
selector.includes('[role="button"]') ? nestedRoleButton : null,
|
|
180
|
-
};
|
|
181
|
-
const preventDefault = vi.fn();
|
|
182
|
-
onKeyDown({
|
|
183
|
-
key: "Enter",
|
|
184
|
-
target: nestedRoleButton,
|
|
185
|
-
currentTarget: {},
|
|
186
|
-
preventDefault,
|
|
187
|
-
});
|
|
188
|
-
expect(handler).not.toHaveBeenCalled();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("Space on the trigger itself fires the row action and prevents default", () => {
|
|
192
|
-
const handler = vi.fn();
|
|
193
|
-
const registry = createActionRegistry({ open: handler });
|
|
194
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
195
|
-
const currentTarget = {};
|
|
196
|
-
const preventDefault = vi.fn();
|
|
197
|
-
onKeyDown({
|
|
198
|
-
key: " ",
|
|
199
|
-
target: { closest: () => currentTarget },
|
|
200
|
-
currentTarget,
|
|
201
|
-
preventDefault,
|
|
202
|
-
});
|
|
203
|
-
expect(handler).toHaveBeenCalledWith({ payload: { type: "open" } });
|
|
204
|
-
expect(preventDefault).toHaveBeenCalledTimes(1);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("Enter on the trigger itself fires without calling preventDefault", () => {
|
|
208
|
-
const handler = vi.fn();
|
|
209
|
-
const registry = createActionRegistry({ open: handler });
|
|
210
|
-
const onKeyDown = getOnKeyDown(registry.dispatch);
|
|
211
|
-
const currentTarget = {};
|
|
212
|
-
const preventDefault = vi.fn();
|
|
213
|
-
onKeyDown({
|
|
214
|
-
key: "Enter",
|
|
215
|
-
target: { closest: () => currentTarget },
|
|
216
|
-
currentTarget,
|
|
217
|
-
preventDefault,
|
|
218
|
-
});
|
|
219
|
-
expect(handler).toHaveBeenCalledWith({ payload: { type: "open" } });
|
|
220
|
-
expect(preventDefault).not.toHaveBeenCalled();
|
|
221
|
-
});
|
|
222
|
-
});
|
package/src/vocabulary/list.tsx
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { GenerativeUILibrary } from "../types";
|
|
3
|
-
import { fire } from "./dispatch";
|
|
4
|
-
|
|
5
|
-
const INTERACTIVE_DESCENDANT_SELECTOR =
|
|
6
|
-
'button, a, input, select, textarea, label, [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="switch"], [role="textbox"], [role="combobox"], [role="menuitem"]';
|
|
7
|
-
|
|
8
|
-
export const listVocabulary = {
|
|
9
|
-
ListView: {
|
|
10
|
-
description:
|
|
11
|
-
"A vertical list container. Wrap `ListViewItem` children as rows.",
|
|
12
|
-
properties: z.object({}),
|
|
13
|
-
render: ({ children }) => <ul data-aui="listview">{children}</ul>,
|
|
14
|
-
},
|
|
15
|
-
ListViewItem: {
|
|
16
|
-
description:
|
|
17
|
-
"A row inside a ListView. Carries `$action` to make the whole row clickable and keyboard-activatable.",
|
|
18
|
-
properties: z.object({}),
|
|
19
|
-
render: ({ $action, $dispatch, children }) => {
|
|
20
|
-
if (!$action || !$dispatch) {
|
|
21
|
-
return <li data-aui="listview-item">{children}</li>;
|
|
22
|
-
}
|
|
23
|
-
return (
|
|
24
|
-
<li data-aui="listview-item">
|
|
25
|
-
<div
|
|
26
|
-
role="button"
|
|
27
|
-
tabIndex={0}
|
|
28
|
-
data-aui="listview-item-trigger"
|
|
29
|
-
onClick={(event) => {
|
|
30
|
-
const target = event.target as Element;
|
|
31
|
-
const hit = target.closest(INTERACTIVE_DESCENDANT_SELECTOR);
|
|
32
|
-
if (hit !== null && hit !== event.currentTarget) return;
|
|
33
|
-
fire($action, $dispatch, undefined, event.currentTarget);
|
|
34
|
-
}}
|
|
35
|
-
onKeyDown={(event) => {
|
|
36
|
-
if (event.key !== "Enter" && event.key !== " ") return;
|
|
37
|
-
const target = event.target as Element;
|
|
38
|
-
const hit = target.closest(INTERACTIVE_DESCENDANT_SELECTOR);
|
|
39
|
-
if (hit !== null && hit !== event.currentTarget) return;
|
|
40
|
-
if (event.key === " ") event.preventDefault();
|
|
41
|
-
fire($action, $dispatch, undefined, event.currentTarget);
|
|
42
|
-
}}
|
|
43
|
-
>
|
|
44
|
-
{children}
|
|
45
|
-
</div>
|
|
46
|
-
</li>
|
|
47
|
-
);
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
} satisfies GenerativeUILibrary;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
-
import { renderGenerativeUI } from "../renderGenerativeUI";
|
|
4
|
-
import { mediaVocabulary } from "./media";
|
|
5
|
-
|
|
6
|
-
const render = (node: unknown) =>
|
|
7
|
-
renderToStaticMarkup(<>{renderGenerativeUI(node, mediaVocabulary)}</>);
|
|
8
|
-
|
|
9
|
-
describe("mediaVocabulary", () => {
|
|
10
|
-
it("Image renders an img with src, alt, and size hook", () => {
|
|
11
|
-
const html = render({
|
|
12
|
-
$type: "Image",
|
|
13
|
-
src: "/a.png",
|
|
14
|
-
alt: "alt",
|
|
15
|
-
size: "md",
|
|
16
|
-
});
|
|
17
|
-
expect(html).toContain(
|
|
18
|
-
'<img data-aui="image" data-aui-size="md" src="/a.png" alt="alt"/>',
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("Image renders a numeric size as an inline max-width style, not a dead data-aui-size attribute", () => {
|
|
23
|
-
const html = render({
|
|
24
|
-
$type: "Image",
|
|
25
|
-
src: "/a.png",
|
|
26
|
-
alt: "alt",
|
|
27
|
-
size: 128,
|
|
28
|
-
});
|
|
29
|
-
expect(html).not.toContain("data-aui-size");
|
|
30
|
-
expect(html).toContain('style="max-width:128px"');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("Image renders a numeric size on a round avatar as an inline width/height square", () => {
|
|
34
|
-
const html = render({
|
|
35
|
-
$type: "Image",
|
|
36
|
-
src: "/a.png",
|
|
37
|
-
alt: "avatar",
|
|
38
|
-
size: 64,
|
|
39
|
-
round: true,
|
|
40
|
-
});
|
|
41
|
-
expect(html).not.toContain("data-aui-size");
|
|
42
|
-
expect(html).toContain('style="width:64px;height:64px"');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("Image `alt` is required by the schema (not optional)", () => {
|
|
46
|
-
const schema = mediaVocabulary.Image.properties as unknown as {
|
|
47
|
-
safeParse: (v: unknown) => { success: boolean };
|
|
48
|
-
};
|
|
49
|
-
expect(schema.safeParse({ src: "/a.png" }).success).toBe(false);
|
|
50
|
-
expect(schema.safeParse({ src: "/a.png", alt: "x" }).success).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("Image omits data-aui-round when round is not set", () => {
|
|
54
|
-
const html = render({ $type: "Image", src: "/a.png", alt: "alt" });
|
|
55
|
-
expect(html).not.toContain("data-aui-round");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("Image renders data-aui-round when round is true", () => {
|
|
59
|
-
const html = render({
|
|
60
|
-
$type: "Image",
|
|
61
|
-
src: "/a.png",
|
|
62
|
-
alt: "avatar",
|
|
63
|
-
round: true,
|
|
64
|
-
});
|
|
65
|
-
expect(html).toContain('data-aui-round="true"');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("Image with a missing alt still renders (defensive: schema validation is not runtime enforcement)", () => {
|
|
69
|
-
expect(() =>
|
|
70
|
-
render({ $type: "Image", src: "/a.png" } as never),
|
|
71
|
-
).not.toThrow();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("Divider omits data-aui-flush when flush is false", () => {
|
|
75
|
-
expect(render({ $type: "Divider", flush: false })).toBe(
|
|
76
|
-
'<hr data-aui="divider"/>',
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("Divider renders an hr with a flush hook", () => {
|
|
81
|
-
expect(render({ $type: "Divider", flush: true })).toBe(
|
|
82
|
-
'<hr data-aui="divider" data-aui-flush="true"/>',
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
});
|
package/src/vocabulary/media.tsx
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from "react";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type { GenerativeUILibrary } from "../types";
|
|
4
|
-
import { IMAGE_SIZE_TOKENS } from "../ir";
|
|
5
|
-
|
|
6
|
-
export const mediaVocabulary = {
|
|
7
|
-
Image: {
|
|
8
|
-
description: "An image. Provide `src` and `alt` for accessibility.",
|
|
9
|
-
properties: z.object({
|
|
10
|
-
src: z.string().describe("Image URL."),
|
|
11
|
-
alt: z.string().describe("Alt text for accessibility."),
|
|
12
|
-
size: z
|
|
13
|
-
.union([z.enum(IMAGE_SIZE_TOKENS), z.number()])
|
|
14
|
-
.optional()
|
|
15
|
-
.describe("Size token or pixel value."),
|
|
16
|
-
round: z
|
|
17
|
-
.boolean()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe(
|
|
20
|
-
"Render as a circular avatar (fully rounded, cropped to a square).",
|
|
21
|
-
),
|
|
22
|
-
}),
|
|
23
|
-
render: ({ src, alt, size, round }) => {
|
|
24
|
-
const numericSize = typeof size === "number" ? size : undefined;
|
|
25
|
-
const style: CSSProperties | undefined =
|
|
26
|
-
numericSize === undefined
|
|
27
|
-
? undefined
|
|
28
|
-
: round
|
|
29
|
-
? { width: `${numericSize}px`, height: `${numericSize}px` }
|
|
30
|
-
: { maxWidth: `${numericSize}px` };
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<img
|
|
34
|
-
data-aui="image"
|
|
35
|
-
data-aui-size={numericSize === undefined ? size : undefined}
|
|
36
|
-
data-aui-round={round || undefined}
|
|
37
|
-
src={src}
|
|
38
|
-
alt={alt}
|
|
39
|
-
style={style}
|
|
40
|
-
/>
|
|
41
|
-
);
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
Divider: {
|
|
45
|
-
description: "A horizontal rule between sections.",
|
|
46
|
-
properties: z.object({
|
|
47
|
-
flush: z
|
|
48
|
-
.boolean()
|
|
49
|
-
.optional()
|
|
50
|
-
.describe("Whether the divider spans the full width with no inset."),
|
|
51
|
-
}),
|
|
52
|
-
render: ({ flush }) => (
|
|
53
|
-
<hr data-aui="divider" data-aui-flush={flush || undefined} />
|
|
54
|
-
),
|
|
55
|
-
},
|
|
56
|
-
} satisfies GenerativeUILibrary;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
-
import { renderGenerativeUI } from "../renderGenerativeUI";
|
|
4
|
-
import { textVocabulary } from "./text";
|
|
5
|
-
|
|
6
|
-
const render = (node: unknown) =>
|
|
7
|
-
renderToStaticMarkup(<>{renderGenerativeUI(node, textVocabulary)}</>);
|
|
8
|
-
|
|
9
|
-
describe("textVocabulary", () => {
|
|
10
|
-
it("Header renders an h2 with the text and a size hook", () => {
|
|
11
|
-
expect(render({ $type: "Header", text: "Title", size: "xl" })).toBe(
|
|
12
|
-
'<h2 data-aui="header" data-aui-size="xl">Title</h2>',
|
|
13
|
-
);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("Header defaults size to lg when omitted", () => {
|
|
17
|
-
expect(render({ $type: "Header", text: "Title" })).toBe(
|
|
18
|
-
'<h2 data-aui="header" data-aui-size="lg">Title</h2>',
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("Text renders a span with size/weight/color hooks and the value", () => {
|
|
23
|
-
expect(
|
|
24
|
-
render({
|
|
25
|
-
$type: "Text",
|
|
26
|
-
value: "hi",
|
|
27
|
-
size: "sm",
|
|
28
|
-
weight: "bold",
|
|
29
|
-
color: "secondary",
|
|
30
|
-
}),
|
|
31
|
-
).toBe(
|
|
32
|
-
'<span data-aui="text" data-aui-size="sm" data-aui-weight="bold" data-aui-color="secondary">hi</span>',
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("Text defaults size to md and leaves weight/color undefined", () => {
|
|
37
|
-
expect(render({ $type: "Text", value: "hi" })).toBe(
|
|
38
|
-
'<span data-aui="text" data-aui-size="md">hi</span>',
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("Text renders partial value while streaming (streamProperties: true)", () => {
|
|
43
|
-
expect(
|
|
44
|
-
renderToStaticMarkup(
|
|
45
|
-
<>
|
|
46
|
-
{renderGenerativeUI(
|
|
47
|
-
{ $type: "Text", value: "partial" },
|
|
48
|
-
textVocabulary,
|
|
49
|
-
{ status: "streaming" },
|
|
50
|
-
)}
|
|
51
|
-
</>,
|
|
52
|
-
),
|
|
53
|
-
).toBe('<span data-aui="text" data-aui-size="md">partial</span>');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("Text renders nested children (span-in-span, valid HTML)", () => {
|
|
57
|
-
expect(
|
|
58
|
-
render({
|
|
59
|
-
$type: "Text",
|
|
60
|
-
value: "before",
|
|
61
|
-
children: { $type: "Text", value: "after" },
|
|
62
|
-
}),
|
|
63
|
-
).toBe(
|
|
64
|
-
'<span data-aui="text" data-aui-size="md">before<span data-aui="text" data-aui-size="md">after</span></span>',
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("Caption renders a p with the value", () => {
|
|
69
|
-
expect(render({ $type: "Caption", value: "muted note" })).toBe(
|
|
70
|
-
'<p data-aui="caption">muted note</p>',
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it.each([
|
|
75
|
-
[
|
|
76
|
-
{ $type: "Header", text: { unexpected: true } },
|
|
77
|
-
undefined,
|
|
78
|
-
'<h2 data-aui="header" data-aui-size="lg"></h2>',
|
|
79
|
-
],
|
|
80
|
-
[
|
|
81
|
-
{ $type: "Text", value: { unexpected: true } },
|
|
82
|
-
{ status: "streaming" as const },
|
|
83
|
-
'<span data-aui="text" data-aui-size="md"></span>',
|
|
84
|
-
],
|
|
85
|
-
[
|
|
86
|
-
{ $type: "Caption", value: { unexpected: true } },
|
|
87
|
-
{ status: "streaming" as const },
|
|
88
|
-
'<p data-aui="caption"></p>',
|
|
89
|
-
],
|
|
90
|
-
[
|
|
91
|
-
{ $type: "Text", value: 42 },
|
|
92
|
-
{ status: "streaming" as const },
|
|
93
|
-
'<span data-aui="text" data-aui-size="md">42</span>',
|
|
94
|
-
],
|
|
95
|
-
])("renders only supported text properties", (node, options, expected) => {
|
|
96
|
-
expect(
|
|
97
|
-
renderToStaticMarkup(
|
|
98
|
-
<>{renderGenerativeUI(node, textVocabulary, options)}</>,
|
|
99
|
-
),
|
|
100
|
-
).toBe(expected);
|
|
101
|
-
});
|
|
102
|
-
});
|
package/src/vocabulary/text.tsx
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { GenerativeUILibrary } from "../types";
|
|
3
|
-
import { COLORS, TEXT_SIZES, WEIGHTS } from "../ir";
|
|
4
|
-
import { toTextContent } from "./toTextContent";
|
|
5
|
-
|
|
6
|
-
export const textVocabulary = {
|
|
7
|
-
Header: {
|
|
8
|
-
description: "A section heading. Renders as a bold title.",
|
|
9
|
-
properties: z.object({
|
|
10
|
-
text: z.string().describe("The heading text."),
|
|
11
|
-
size: z
|
|
12
|
-
.enum(TEXT_SIZES)
|
|
13
|
-
.optional()
|
|
14
|
-
.describe("Heading size; defaults to `lg`."),
|
|
15
|
-
}),
|
|
16
|
-
render: ({ text, size, children }) => (
|
|
17
|
-
<h2 data-aui="header" data-aui-size={size ?? "lg"}>
|
|
18
|
-
{toTextContent(text)}
|
|
19
|
-
{children}
|
|
20
|
-
</h2>
|
|
21
|
-
),
|
|
22
|
-
},
|
|
23
|
-
Text: {
|
|
24
|
-
description: "A run of text. Use for paragraphs, labels, or inline copy.",
|
|
25
|
-
properties: z.object({
|
|
26
|
-
value: z.string().describe("The text content."),
|
|
27
|
-
size: z
|
|
28
|
-
.enum(TEXT_SIZES)
|
|
29
|
-
.optional()
|
|
30
|
-
.describe("Text size; defaults to `md`."),
|
|
31
|
-
weight: z.enum(WEIGHTS).optional().describe("Font weight."),
|
|
32
|
-
color: z.enum(COLORS).optional().describe("Foreground color token."),
|
|
33
|
-
}),
|
|
34
|
-
streamProperties: true,
|
|
35
|
-
render: ({ value, size, weight, color, children }) => (
|
|
36
|
-
<span
|
|
37
|
-
data-aui="text"
|
|
38
|
-
data-aui-size={size ?? "md"}
|
|
39
|
-
data-aui-weight={weight}
|
|
40
|
-
data-aui-color={color}
|
|
41
|
-
>
|
|
42
|
-
{toTextContent(value)}
|
|
43
|
-
{children}
|
|
44
|
-
</span>
|
|
45
|
-
),
|
|
46
|
-
},
|
|
47
|
-
Caption: {
|
|
48
|
-
description: "Secondary, de-emphasized text under a primary element.",
|
|
49
|
-
properties: z.object({
|
|
50
|
-
value: z.string().describe("The caption text."),
|
|
51
|
-
}),
|
|
52
|
-
streamProperties: true,
|
|
53
|
-
render: ({ value, children }) => (
|
|
54
|
-
<p data-aui="caption">
|
|
55
|
-
{toTextContent(value)}
|
|
56
|
-
{children}
|
|
57
|
-
</p>
|
|
58
|
-
),
|
|
59
|
-
},
|
|
60
|
-
} satisfies GenerativeUILibrary;
|