@superxd-studio/adptr 0.5.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/GENERATIVE.md +101 -0
- package/INTERACTIONS.md +51 -0
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/REUSE.md +170 -0
- package/REVIEW.md +68 -0
- package/THIRD-PARTY-NOTICES.md +28 -0
- package/ai-sdk/index.js +96 -0
- package/assets/ibm-plex-mono-cyrillic-400-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-cyrillic-500-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-cyrillic-ext-400-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-cyrillic-ext-500-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-vietnamese-400-normal.woff2 +0 -0
- package/assets/ibm-plex-mono-vietnamese-500-normal.woff2 +0 -0
- package/assets/instrument-sans-latin-ext-wght-normal.woff2 +0 -0
- package/assets/instrument-sans-latin-wght-normal.woff2 +0 -0
- package/assistant-ui/index.js +139 -0
- package/chunks/callout-DKMHSnjc.js +342 -0
- package/chunks/canvas-BJ15-XxE.js +598 -0
- package/chunks/controller-Cfpg0C7v.js +180 -0
- package/chunks/core-BCqVRLUy.js +312 -0
- package/chunks/text-field-BLtoaZFn.js +41 -0
- package/chunks/types-BGF7r1my.js +115 -0
- package/chunks/ui-9DyNsYXY.js +967 -0
- package/core/index.js +4 -0
- package/custom/index.js +64 -0
- package/examples/ai-sdk-backend/package.json +11 -0
- package/examples/ai-sdk-backend/server.mjs +316 -0
- package/examples/custom-backend/copy.mjs +7 -0
- package/examples/custom-backend/server.mjs +118 -0
- package/examples/generative/specs.json +2094 -0
- package/examples/review-backend/copy.mjs +27 -0
- package/examples/review-backend/package.json +11 -0
- package/examples/review-backend/server.mjs +276 -0
- package/examples/web-ai-sdk/index.html +12 -0
- package/examples/web-ai-sdk/package.json +21 -0
- package/examples/web-ai-sdk/src/copy.ts +1 -0
- package/examples/web-ai-sdk/src/main.tsx +64 -0
- package/examples/web-ai-sdk/tsconfig.json +13 -0
- package/examples/web-generative/index.html +12 -0
- package/examples/web-generative/package.json +23 -0
- package/examples/web-generative/src/copy.ts +42 -0
- package/examples/web-generative/src/main.tsx +74 -0
- package/examples/web-generative/tsconfig.json +14 -0
- package/examples/web-independent/index.html +12 -0
- package/examples/web-independent/package.json +20 -0
- package/examples/web-independent/src/main.tsx +32 -0
- package/examples/web-independent/tsconfig.json +14 -0
- package/examples/web-interactions/index.html +12 -0
- package/examples/web-interactions/package.json +21 -0
- package/examples/web-interactions/src/copy.ts +215 -0
- package/examples/web-interactions/src/examples.tsx +628 -0
- package/examples/web-interactions/src/main.tsx +116 -0
- package/examples/web-interactions/tsconfig.json +14 -0
- package/examples/web-review/index.html +12 -0
- package/examples/web-review/package.json +20 -0
- package/examples/web-review/src/main.tsx +59 -0
- package/examples/web-review/tsconfig.json +14 -0
- package/fonts.css +117 -0
- package/generative/index.js +1560 -0
- package/interactions/index.js +1591 -0
- package/langgraph-review/index.js +36 -0
- package/licenses/ai-sdk.txt +13 -0
- package/licenses/assistant-ui-generative.txt +21 -0
- package/licenses/assistant-ui.txt +23 -0
- package/licenses/ibm-plex-mono.txt +93 -0
- package/licenses/instrument-sans.txt +93 -0
- package/licenses/internationalized-date.txt +201 -0
- package/licenses/langgraph.txt +21 -0
- package/licenses/lucide.txt +43 -0
- package/licenses/react-markdown.txt +21 -0
- package/licenses/remark-gfm.txt +22 -0
- package/licenses/zod.txt +21 -0
- package/package.json +138 -0
- package/review/index.js +31 -0
- package/source/components/avatar.tsx +50 -0
- package/source/components/button.tsx +155 -0
- package/source/components/callout.tsx +65 -0
- package/source/components/canvas/connector.tsx +14 -0
- package/source/components/canvas/copy.ts +34 -0
- package/source/components/canvas/index.tsx +257 -0
- package/source/components/canvas/recorder.ts +36 -0
- package/source/components/date-time-field.tsx +77 -0
- package/source/components/flow-motion.tsx +109 -0
- package/source/components/icons.tsx +275 -0
- package/source/components/responsive-overlay.tsx +91 -0
- package/source/components/scenario-picker/copy.ts +5 -0
- package/source/components/scenario-picker/index.tsx +142 -0
- package/source/components/scroll-area.tsx +59 -0
- package/source/components/search-picker.tsx +127 -0
- package/source/components/segmented-control.tsx +123 -0
- package/source/components/skeleton.tsx +11 -0
- package/source/components/spinner.tsx +19 -0
- package/source/components/superxd-arrow.tsx +22 -0
- package/source/components/switch.tsx +45 -0
- package/source/components/text-field.tsx +81 -0
- package/source/components/thinking.tsx +16 -0
- package/source/components/ui/copy.ts +12 -0
- package/source/components/ui/display.tsx +276 -0
- package/source/components/ui/navigation.tsx +346 -0
- package/source/components/ui/portal-theme.tsx +28 -0
- package/source/components/ui/selection.tsx +336 -0
- package/source/lib/cn.ts +23 -0
- package/source/reusable/ai-sdk/index.ts +114 -0
- package/source/reusable/assistant-ui/index.tsx +266 -0
- package/source/reusable/core/attachments.ts +163 -0
- package/source/reusable/core/index.ts +417 -0
- package/source/reusable/custom/index.ts +90 -0
- package/source/reusable/generative/actions.ts +188 -0
- package/source/reusable/generative/copy.ts +47 -0
- package/source/reusable/generative/data.tsx +126 -0
- package/source/reusable/generative/index.tsx +517 -0
- package/source/reusable/generative/schema.ts +327 -0
- package/source/reusable/generative/timeline.tsx +214 -0
- package/source/reusable/generative/vocabulary.tsx +847 -0
- package/source/reusable/interactions/agent.ts +259 -0
- package/source/reusable/interactions/artifact.ts +236 -0
- package/source/reusable/interactions/context.tsx +38 -0
- package/source/reusable/interactions/copy.ts +117 -0
- package/source/reusable/interactions/feedback.ts +73 -0
- package/source/reusable/interactions/index.ts +11 -0
- package/source/reusable/interactions/presentation.tsx +483 -0
- package/source/reusable/interactions/store.ts +74 -0
- package/source/reusable/interactions/threads.ts +126 -0
- package/source/reusable/interactions/web.tsx +574 -0
- package/source/reusable/langgraph-review/index.ts +73 -0
- package/source/reusable/review/controller.ts +263 -0
- package/source/reusable/review/index.ts +47 -0
- package/source/reusable/review/types.ts +189 -0
- package/source/reusable/ui/index.ts +25 -0
- package/source/reusable/uploads/index.ts +48 -0
- package/source/reusable/web/attachments/copy.ts +23 -0
- package/source/reusable/web/attachments/index.tsx +233 -0
- package/source/reusable/web/copy.ts +22 -0
- package/source/reusable/web/index.tsx +470 -0
- package/source/reusable/web/message/copy.ts +26 -0
- package/source/reusable/web/message/index.tsx +388 -0
- package/source/reusable/web/message/streaming.ts +64 -0
- package/source/reusable/web/message/styles.css +21 -0
- package/source/reusable/web/review/copy.ts +78 -0
- package/source/reusable/web/review/flow.ts +24 -0
- package/source/reusable/web/review/index.tsx +506 -0
- package/source/reusable/web/styles.ts +2 -0
- package/source/styles/library.css +612 -0
- package/source/styles/theme.css +228 -0
- package/styles.css +3 -0
- package/styles.d.ts +1 -0
- package/types/components/avatar.d.ts +14 -0
- package/types/components/button.d.ts +36 -0
- package/types/components/callout.d.ts +19 -0
- package/types/components/canvas/connector.d.ts +2 -0
- package/types/components/canvas/copy.d.ts +34 -0
- package/types/components/canvas/index.d.ts +33 -0
- package/types/components/canvas/recorder.d.ts +10 -0
- package/types/components/date-time-field.d.ts +11 -0
- package/types/components/flow-motion.d.ts +38 -0
- package/types/components/icons.d.ts +138 -0
- package/types/components/responsive-overlay.d.ts +15 -0
- package/types/components/scenario-picker/copy.d.ts +5 -0
- package/types/components/scenario-picker/index.d.ts +14 -0
- package/types/components/scroll-area.d.ts +14 -0
- package/types/components/search-picker.d.ts +27 -0
- package/types/components/segmented-control.d.ts +23 -0
- package/types/components/skeleton.d.ts +4 -0
- package/types/components/spinner.d.ts +4 -0
- package/types/components/superxd-arrow.d.ts +5 -0
- package/types/components/switch.d.ts +8 -0
- package/types/components/text-field.d.ts +19 -0
- package/types/components/thinking.d.ts +8 -0
- package/types/components/ui/copy.d.ts +12 -0
- package/types/components/ui/display.d.ts +50 -0
- package/types/components/ui/navigation.d.ts +63 -0
- package/types/components/ui/portal-theme.d.ts +8 -0
- package/types/components/ui/selection.d.ts +37 -0
- package/types/lib/cn.d.ts +3 -0
- package/types/reusable/ai-sdk/index.d.ts +15 -0
- package/types/reusable/assistant-ui/index.d.ts +12 -0
- package/types/reusable/core/attachments.d.ts +54 -0
- package/types/reusable/core/index.d.ts +107 -0
- package/types/reusable/custom/index.d.ts +14 -0
- package/types/reusable/generative/actions.d.ts +45 -0
- package/types/reusable/generative/copy.d.ts +48 -0
- package/types/reusable/generative/data.d.ts +4 -0
- package/types/reusable/generative/index.d.ts +30 -0
- package/types/reusable/generative/schema.d.ts +303 -0
- package/types/reusable/generative/timeline.d.ts +21 -0
- package/types/reusable/generative/vocabulary.d.ts +34 -0
- package/types/reusable/interactions/agent.d.ts +98 -0
- package/types/reusable/interactions/artifact.d.ts +118 -0
- package/types/reusable/interactions/context.d.ts +130 -0
- package/types/reusable/interactions/copy.d.ts +115 -0
- package/types/reusable/interactions/feedback.d.ts +34 -0
- package/types/reusable/interactions/index.d.ts +10 -0
- package/types/reusable/interactions/presentation.d.ts +97 -0
- package/types/reusable/interactions/store.d.ts +29 -0
- package/types/reusable/interactions/threads.d.ts +38 -0
- package/types/reusable/interactions/web.d.ts +22 -0
- package/types/reusable/langgraph-review/index.d.ts +5 -0
- package/types/reusable/review/controller.d.ts +52 -0
- package/types/reusable/review/index.d.ts +12 -0
- package/types/reusable/review/types.d.ts +61 -0
- package/types/reusable/ui/index.d.ts +19 -0
- package/types/reusable/uploads/index.d.ts +11 -0
- package/types/reusable/web/attachments/copy.d.ts +25 -0
- package/types/reusable/web/attachments/index.d.ts +24 -0
- package/types/reusable/web/copy.d.ts +24 -0
- package/types/reusable/web/index.d.ts +41 -0
- package/types/reusable/web/message/copy.d.ts +27 -0
- package/types/reusable/web/message/index.d.ts +56 -0
- package/types/reusable/web/message/streaming.d.ts +27 -0
- package/types/reusable/web/review/copy.d.ts +72 -0
- package/types/reusable/web/review/flow.d.ts +3 -0
- package/types/reusable/web/review/index.d.ts +38 -0
- package/types/reusable/web/styles.d.ts +2 -0
- package/ui/index.js +4 -0
- package/uploads/index.js +34 -0
- package/web/index.js +931 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { PortalTheme } from "./portal-theme";
|
|
2
|
+
import { createContext, useContext, useId, type ReactNode } from "react";
|
|
3
|
+
import {
|
|
4
|
+
Checkbox as AriaCheckbox,
|
|
5
|
+
CheckboxGroup as AriaCheckboxGroup,
|
|
6
|
+
RadioGroup as AriaRadioGroup,
|
|
7
|
+
Radio,
|
|
8
|
+
Label,
|
|
9
|
+
Text,
|
|
10
|
+
FieldError,
|
|
11
|
+
Select as AriaSelect,
|
|
12
|
+
SelectValue,
|
|
13
|
+
ListBox,
|
|
14
|
+
ListBoxItem,
|
|
15
|
+
Popover,
|
|
16
|
+
Slider as AriaSlider,
|
|
17
|
+
SliderTrack,
|
|
18
|
+
SliderThumb,
|
|
19
|
+
SliderOutput,
|
|
20
|
+
ToggleButton as AriaToggleButton,
|
|
21
|
+
Form as AriaForm,
|
|
22
|
+
type CheckboxProps,
|
|
23
|
+
type CheckboxGroupProps,
|
|
24
|
+
type RadioGroupProps,
|
|
25
|
+
type SelectProps,
|
|
26
|
+
type SliderProps,
|
|
27
|
+
type ToggleButtonProps,
|
|
28
|
+
type FormProps,
|
|
29
|
+
} from "react-aria-components";
|
|
30
|
+
import { TextField as ExistingTextField, type TextFieldProps } from "../text-field";
|
|
31
|
+
import { Button, buttonStyles } from "../button";
|
|
32
|
+
import { IconCheck, IconChevronDown } from "../icons";
|
|
33
|
+
import { cn } from "../../lib/cn";
|
|
34
|
+
import { uiCopy } from "./copy";
|
|
35
|
+
|
|
36
|
+
const field = "flex min-w-0 flex-col gap-2";
|
|
37
|
+
const hint = "type-caption font-normal text-fg-subtle";
|
|
38
|
+
export interface Choice {
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
isDisabled?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function Checkbox({
|
|
46
|
+
label,
|
|
47
|
+
description,
|
|
48
|
+
className,
|
|
49
|
+
...props
|
|
50
|
+
}: Omit<CheckboxProps, "children"> & { label: ReactNode; description?: ReactNode }) {
|
|
51
|
+
const id = useId();
|
|
52
|
+
return (
|
|
53
|
+
<AriaCheckbox
|
|
54
|
+
{...props}
|
|
55
|
+
aria-describedby={description ? `${id}-hint` : props["aria-describedby"]}
|
|
56
|
+
className={cn(
|
|
57
|
+
"group inline-flex min-h-control-md cursor-pointer items-start gap-3 rounded-control py-2 outline-none data-disabled:cursor-default data-disabled:opacity-50 data-focus-visible:focus-ring",
|
|
58
|
+
typeof className === "string" && className,
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
{({ isSelected, isIndeterminate }) => (
|
|
62
|
+
<>
|
|
63
|
+
<span
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-cell border border-line-input bg-surface transition-[background-color,border-color,scale] duration-(--duration-settle) ease-settle group-data-indeterminate:border-brand group-data-indeterminate:bg-brand group-data-indeterminate:text-on-brand group-data-pressed:scale-(--press-depth) group-data-pressed:duration-(--duration-snap) group-data-pressed:ease-snap group-data-selected:border-brand group-data-selected:bg-brand group-data-selected:text-on-brand"
|
|
66
|
+
>
|
|
67
|
+
{/* The box presses in; the tick grows and settles in, like the radio dot */}
|
|
68
|
+
{isIndeterminate ? (
|
|
69
|
+
<span>−</span>
|
|
70
|
+
) : (
|
|
71
|
+
<IconCheck
|
|
72
|
+
className={cn(
|
|
73
|
+
"size-3.5 transition-[scale] duration-(--duration-settle) ease-settle",
|
|
74
|
+
isSelected ? "scale-100" : "scale-0",
|
|
75
|
+
)}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
</span>
|
|
79
|
+
<span className="min-w-0">
|
|
80
|
+
<span className="block type-label text-fg-emphasis">{label}</span>
|
|
81
|
+
{description ? (
|
|
82
|
+
<span id={`${id}-hint`} className={cn("mt-1 block", hint)}>
|
|
83
|
+
{description}
|
|
84
|
+
</span>
|
|
85
|
+
) : null}
|
|
86
|
+
</span>
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
89
|
+
</AriaCheckbox>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
export function CheckboxGroup({
|
|
93
|
+
label,
|
|
94
|
+
description,
|
|
95
|
+
error,
|
|
96
|
+
options,
|
|
97
|
+
className,
|
|
98
|
+
...props
|
|
99
|
+
}: Omit<CheckboxGroupProps, "children"> & {
|
|
100
|
+
label: ReactNode;
|
|
101
|
+
description?: ReactNode;
|
|
102
|
+
error?: ReactNode;
|
|
103
|
+
options: readonly Choice[];
|
|
104
|
+
}) {
|
|
105
|
+
return (
|
|
106
|
+
<AriaCheckboxGroup
|
|
107
|
+
{...props}
|
|
108
|
+
isInvalid={Boolean(error) || props.isInvalid}
|
|
109
|
+
className={cn(field, typeof className === "string" && className)}
|
|
110
|
+
>
|
|
111
|
+
<Label className="type-label text-fg-emphasis">{label}</Label>
|
|
112
|
+
{options.map((option) => (
|
|
113
|
+
<Checkbox
|
|
114
|
+
key={option.id}
|
|
115
|
+
value={option.id}
|
|
116
|
+
label={option.label}
|
|
117
|
+
description={option.description}
|
|
118
|
+
isDisabled={option.isDisabled}
|
|
119
|
+
/>
|
|
120
|
+
))}
|
|
121
|
+
{description && (
|
|
122
|
+
<Text slot="description" className={hint}>
|
|
123
|
+
{description}
|
|
124
|
+
</Text>
|
|
125
|
+
)}
|
|
126
|
+
<FieldError className="type-caption text-error">{error}</FieldError>
|
|
127
|
+
</AriaCheckboxGroup>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
export function RadioGroup({
|
|
131
|
+
label,
|
|
132
|
+
description,
|
|
133
|
+
error,
|
|
134
|
+
options,
|
|
135
|
+
className,
|
|
136
|
+
...props
|
|
137
|
+
}: Omit<RadioGroupProps, "children"> & {
|
|
138
|
+
label: ReactNode;
|
|
139
|
+
description?: ReactNode;
|
|
140
|
+
error?: ReactNode;
|
|
141
|
+
options: readonly Choice[];
|
|
142
|
+
}) {
|
|
143
|
+
return (
|
|
144
|
+
<AriaRadioGroup
|
|
145
|
+
{...props}
|
|
146
|
+
isInvalid={Boolean(error) || props.isInvalid}
|
|
147
|
+
className={cn(field, typeof className === "string" && className)}
|
|
148
|
+
>
|
|
149
|
+
<Label className="type-label text-fg-emphasis">{label}</Label>
|
|
150
|
+
{options.map((option) => (
|
|
151
|
+
<Radio
|
|
152
|
+
key={option.id}
|
|
153
|
+
value={option.id}
|
|
154
|
+
isDisabled={option.isDisabled}
|
|
155
|
+
className="group flex min-h-control-md cursor-pointer items-start gap-3 rounded-control py-2 outline-none data-disabled:opacity-50 data-focus-visible:focus-ring"
|
|
156
|
+
>
|
|
157
|
+
<span
|
|
158
|
+
aria-hidden="true"
|
|
159
|
+
className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-pill border border-line-input bg-surface transition-[border-color,scale] duration-(--duration-settle) ease-settle group-data-pressed:scale-(--press-depth) group-data-pressed:duration-(--duration-snap) group-data-selected:border-brand"
|
|
160
|
+
>
|
|
161
|
+
{/* The dot settles into place: a small spring, from nothing to full size */}
|
|
162
|
+
<span className="size-2.5 scale-0 rounded-pill bg-brand transition-[scale] duration-(--duration-settle) ease-settle group-data-selected:scale-100" />
|
|
163
|
+
</span>
|
|
164
|
+
<span>
|
|
165
|
+
<span className="block type-label text-fg-emphasis">{option.label}</span>
|
|
166
|
+
{option.description && (
|
|
167
|
+
<Text slot="description" className={cn("mt-1 block", hint)}>
|
|
168
|
+
{option.description}
|
|
169
|
+
</Text>
|
|
170
|
+
)}
|
|
171
|
+
</span>
|
|
172
|
+
</Radio>
|
|
173
|
+
))}
|
|
174
|
+
{description && (
|
|
175
|
+
<Text slot="description" className={hint}>
|
|
176
|
+
{description}
|
|
177
|
+
</Text>
|
|
178
|
+
)}
|
|
179
|
+
<FieldError className="type-caption text-error">{error}</FieldError>
|
|
180
|
+
</AriaRadioGroup>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
export function Select({
|
|
184
|
+
label,
|
|
185
|
+
description,
|
|
186
|
+
error,
|
|
187
|
+
options,
|
|
188
|
+
className,
|
|
189
|
+
placeholder = uiCopy.choose,
|
|
190
|
+
...props
|
|
191
|
+
}: Omit<SelectProps<Choice>, "children" | "items"> & {
|
|
192
|
+
label: ReactNode;
|
|
193
|
+
description?: ReactNode;
|
|
194
|
+
error?: ReactNode;
|
|
195
|
+
options: readonly Choice[];
|
|
196
|
+
}) {
|
|
197
|
+
return (
|
|
198
|
+
<PortalTheme>
|
|
199
|
+
{(theme) => (
|
|
200
|
+
<AriaSelect
|
|
201
|
+
{...props}
|
|
202
|
+
placeholder={placeholder}
|
|
203
|
+
isInvalid={Boolean(error) || props.isInvalid}
|
|
204
|
+
disabledKeys={options.filter((o) => o.isDisabled).map((o) => o.id)}
|
|
205
|
+
className={cn(field, typeof className === "string" && className)}
|
|
206
|
+
>
|
|
207
|
+
<Label className="type-label text-fg-emphasis">{label}</Label>
|
|
208
|
+
<Button className="group/trigger w-full justify-between text-left whitespace-normal">
|
|
209
|
+
{/* Only the chosen text, on one line, not the option's whole list layout */}
|
|
210
|
+
<SelectValue className="min-w-0 break-words data-placeholder:text-fg-subtle">
|
|
211
|
+
{({ isPlaceholder, selectedText, defaultChildren }) =>
|
|
212
|
+
isPlaceholder ? defaultChildren : selectedText
|
|
213
|
+
}
|
|
214
|
+
</SelectValue>
|
|
215
|
+
{/* Turns with the settle spring as the list opens, like the accordion */}
|
|
216
|
+
<IconChevronDown className="transition-[rotate] duration-(--duration-settle) ease-settle group-aria-expanded/trigger:rotate-180 motion-reduce:transition-none" />
|
|
217
|
+
</Button>
|
|
218
|
+
{description && (
|
|
219
|
+
<Text slot="description" className={hint}>
|
|
220
|
+
{description}
|
|
221
|
+
</Text>
|
|
222
|
+
)}
|
|
223
|
+
<FieldError className="type-caption text-error">{error}</FieldError>
|
|
224
|
+
<Popover
|
|
225
|
+
style={theme.style}
|
|
226
|
+
placement="bottom start"
|
|
227
|
+
className={cn(
|
|
228
|
+
theme.className,
|
|
229
|
+
"z-50 w-(--trigger-width) max-w-[calc(100vw-2rem)] origin-(--trigger-anchor-point) rounded-surface border border-line-subtle bg-surface p-1 text-fg shadow-popover outline-none data-entering:animate-pop-in data-exiting:animate-pop-out",
|
|
230
|
+
)}
|
|
231
|
+
>
|
|
232
|
+
<ListBox
|
|
233
|
+
items={options}
|
|
234
|
+
className="max-h-72 overflow-y-auto outline-none"
|
|
235
|
+
renderEmptyState={() => (
|
|
236
|
+
<p className="p-3 type-body text-fg-subtle">{uiCopy.empty}</p>
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{(item) => (
|
|
240
|
+
<ListBoxItem
|
|
241
|
+
id={item.id}
|
|
242
|
+
textValue={item.label}
|
|
243
|
+
className="group flex cursor-pointer items-center gap-3 rounded-cell p-3 outline-none data-disabled:opacity-50 data-focused:bg-surface-subtle data-selected:bg-surface-muted"
|
|
244
|
+
>
|
|
245
|
+
<span className="min-w-0 flex-1">
|
|
246
|
+
<Text slot="label" className="block type-label">
|
|
247
|
+
{item.label}
|
|
248
|
+
</Text>
|
|
249
|
+
{item.description && (
|
|
250
|
+
<Text slot="description" className={cn("mt-1 block", hint)}>
|
|
251
|
+
{item.description}
|
|
252
|
+
</Text>
|
|
253
|
+
)}
|
|
254
|
+
</span>
|
|
255
|
+
<IconCheck className="invisible group-data-selected:visible" />
|
|
256
|
+
</ListBoxItem>
|
|
257
|
+
)}
|
|
258
|
+
</ListBox>
|
|
259
|
+
</Popover>
|
|
260
|
+
</AriaSelect>
|
|
261
|
+
)}
|
|
262
|
+
</PortalTheme>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
export function Slider({
|
|
266
|
+
label,
|
|
267
|
+
className,
|
|
268
|
+
...props
|
|
269
|
+
}: Omit<SliderProps, "children"> & { label: ReactNode }) {
|
|
270
|
+
return (
|
|
271
|
+
<AriaSlider
|
|
272
|
+
{...props}
|
|
273
|
+
className={cn(
|
|
274
|
+
"flex w-full min-w-0 flex-col gap-3 data-disabled:opacity-50",
|
|
275
|
+
typeof className === "string" && className,
|
|
276
|
+
)}
|
|
277
|
+
>
|
|
278
|
+
<div className="flex justify-between gap-4">
|
|
279
|
+
<Label className="type-label text-fg-emphasis">{label}</Label>
|
|
280
|
+
<SliderOutput className="type-caption text-fg-subtle numeric" />
|
|
281
|
+
</div>
|
|
282
|
+
<SliderTrack className="relative h-6 w-full rounded-control bg-surface-subtle">
|
|
283
|
+
{({ state }) => (
|
|
284
|
+
<>
|
|
285
|
+
{state.values.map((_, index) => (
|
|
286
|
+
<SliderThumb
|
|
287
|
+
key={index}
|
|
288
|
+
index={index}
|
|
289
|
+
aria-label={
|
|
290
|
+
Array.isArray(props.value) || Array.isArray(props.defaultValue)
|
|
291
|
+
? `${typeof label === "string" ? label : "Value"} ${index + 1}`
|
|
292
|
+
: undefined
|
|
293
|
+
}
|
|
294
|
+
// Grows from the point React Aria centres it on, so it stays centred while dragged
|
|
295
|
+
className="top-1/2 size-6 origin-top-left rounded-pill border border-line-emphasis bg-surface shadow-control transition-[scale,border-color] duration-(--duration-settle) ease-settle outline-none data-dragging:scale-110 data-dragging:border-brand data-focus-visible:focus-ring"
|
|
296
|
+
/>
|
|
297
|
+
))}
|
|
298
|
+
</>
|
|
299
|
+
)}
|
|
300
|
+
</SliderTrack>
|
|
301
|
+
</AriaSlider>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
export function Toggle({ className, ...props }: ToggleButtonProps) {
|
|
305
|
+
return (
|
|
306
|
+
<AriaToggleButton
|
|
307
|
+
{...props}
|
|
308
|
+
className={cn(
|
|
309
|
+
buttonStyles({ size: "sm" }),
|
|
310
|
+
"data-selected:border-brand data-selected:bg-brand data-selected:text-on-brand",
|
|
311
|
+
typeof className === "string" && className,
|
|
312
|
+
)}
|
|
313
|
+
/>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const FormValidation = createContext<"native" | "aria" | undefined>(undefined);
|
|
317
|
+
export function TextField(props: TextFieldProps) {
|
|
318
|
+
const context = useContext(FormValidation);
|
|
319
|
+
return (
|
|
320
|
+
<ExistingTextField
|
|
321
|
+
{...props}
|
|
322
|
+
validationBehavior={props.validationBehavior ?? context ?? "aria"}
|
|
323
|
+
/>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
export function Form({ className, children, validationBehavior = "native", ...props }: FormProps) {
|
|
327
|
+
return (
|
|
328
|
+
<AriaForm
|
|
329
|
+
{...props}
|
|
330
|
+
validationBehavior={validationBehavior}
|
|
331
|
+
className={cn("flex min-w-0 flex-col gap-4", typeof className === "string" && className)}
|
|
332
|
+
>
|
|
333
|
+
<FormValidation.Provider value={validationBehavior}>{children}</FormValidation.Provider>
|
|
334
|
+
</AriaForm>
|
|
335
|
+
);
|
|
336
|
+
}
|
package/source/lib/cn.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
// Teaches tailwind-merge the library's own token names, so a className passed
|
|
5
|
+
// in from outside (say "rounded-none") cleanly replaces the component's own.
|
|
6
|
+
const merge = extendTailwindMerge<"type-role">({
|
|
7
|
+
extend: {
|
|
8
|
+
theme: {
|
|
9
|
+
radius: ["control", "cell", "surface", "pill"],
|
|
10
|
+
shadow: ["control", "popover", "dialog"],
|
|
11
|
+
ease: ["standard", "exit"],
|
|
12
|
+
animate: ["fade-in", "rise-in", "slide-in", "dialog-in", "pulse-soft"],
|
|
13
|
+
},
|
|
14
|
+
classGroups: {
|
|
15
|
+
"type-role": ["type-title", "type-heading", "type-body", "type-label", "type-caption"],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/** Joins class names and resolves Tailwind conflicts (last one wins). */
|
|
21
|
+
export function cn(...inputs: ClassValue[]): string {
|
|
22
|
+
return merge(clsx(inputs));
|
|
23
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { DefaultChatTransport, readUIMessageStream, type UIMessage, type UIMessageChunk } from "ai";
|
|
2
|
+
import {
|
|
3
|
+
ChatInterrupted,
|
|
4
|
+
type ChatAdapter,
|
|
5
|
+
type ChatEvent,
|
|
6
|
+
type ChatMessage,
|
|
7
|
+
} from "../core/index.js";
|
|
8
|
+
|
|
9
|
+
export interface AiSdkChatOptions {
|
|
10
|
+
endpoint: string;
|
|
11
|
+
/** Opt into editing, regeneration and versions. Restart is controlled separately by retry. */
|
|
12
|
+
conversationActions?: boolean;
|
|
13
|
+
/** Allow Restart as a new run. Defaults to true; disable for endpoints without replay safety. */
|
|
14
|
+
retry?: boolean;
|
|
15
|
+
/** Stable conversation identity; requestId remains a distinct per-run identifier. */
|
|
16
|
+
chatId?: string;
|
|
17
|
+
headers?: () => HeadersInit | Promise<HeadersInit>;
|
|
18
|
+
credentials?: RequestCredentials;
|
|
19
|
+
fetch?: typeof globalThis.fetch;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toMessage(message: ChatMessage): UIMessage {
|
|
23
|
+
const retained = message.metadata?.aiSdk as
|
|
24
|
+
{ metadata?: unknown; parts?: UIMessage["parts"] } | undefined;
|
|
25
|
+
return {
|
|
26
|
+
id: message.id,
|
|
27
|
+
role: message.role,
|
|
28
|
+
metadata: { provider: retained?.metadata, superxdFiles: message.files ?? [] },
|
|
29
|
+
parts: [
|
|
30
|
+
...(message.text ? [{ type: "text" as const, text: message.text }] : []),
|
|
31
|
+
...(message.files ?? []).map((file) => ({
|
|
32
|
+
type: "file" as const,
|
|
33
|
+
mediaType: file.mediaType,
|
|
34
|
+
filename: file.name,
|
|
35
|
+
url: file.url,
|
|
36
|
+
})),
|
|
37
|
+
...(retained?.parts ?? []).filter((part) => part.type !== "text" && part.type !== "file"),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** SDK owns protocol parsing only; SuperXD or the host owns the conversation. */
|
|
43
|
+
export function createAiSdkChatAdapter(options: AiSdkChatOptions): ChatAdapter {
|
|
44
|
+
const chatId = options.chatId ?? globalThis.crypto.randomUUID();
|
|
45
|
+
const transport = new DefaultChatTransport<UIMessage>({
|
|
46
|
+
api: options.endpoint,
|
|
47
|
+
headers: async () => new Headers(await options.headers?.()),
|
|
48
|
+
credentials: options.credentials ?? "same-origin",
|
|
49
|
+
fetch: options.fetch,
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
capabilities: {
|
|
53
|
+
send: true,
|
|
54
|
+
cancel: true,
|
|
55
|
+
retry: options.retry ?? true,
|
|
56
|
+
attachments: true,
|
|
57
|
+
...(options.conversationActions ? { edit: true, regenerate: true, branches: true } : {}),
|
|
58
|
+
},
|
|
59
|
+
async *run(request, signal): AsyncGenerator<ChatEvent> {
|
|
60
|
+
const stream = await transport.sendMessages({
|
|
61
|
+
trigger: "submit-message",
|
|
62
|
+
chatId,
|
|
63
|
+
messageId: request.messages.at(-1)?.id,
|
|
64
|
+
messages: request.messages.map(toMessage),
|
|
65
|
+
abortSignal: signal,
|
|
66
|
+
body: { requestId: request.requestId },
|
|
67
|
+
});
|
|
68
|
+
let finished = false;
|
|
69
|
+
let remoteAbort = false;
|
|
70
|
+
const observed = stream.pipeThrough(
|
|
71
|
+
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
72
|
+
transform(chunk, controller) {
|
|
73
|
+
if (JSON.stringify(chunk).length > 200_000) throw new Error("SDK part too large");
|
|
74
|
+
if (chunk.type.startsWith("tool-"))
|
|
75
|
+
throw new Error("Tool capabilities are not supported by this slice");
|
|
76
|
+
if (chunk.type === "finish") finished = true;
|
|
77
|
+
if (chunk.type === "abort") remoteAbort = true;
|
|
78
|
+
controller.enqueue(chunk);
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
let previous = "";
|
|
83
|
+
try {
|
|
84
|
+
for await (const message of readUIMessageStream({
|
|
85
|
+
stream: observed,
|
|
86
|
+
terminateOnError: true,
|
|
87
|
+
})) {
|
|
88
|
+
if (signal.aborted) return;
|
|
89
|
+
const text = message.parts
|
|
90
|
+
.filter((part) => part.type === "text")
|
|
91
|
+
.map((part) => part.text)
|
|
92
|
+
.join("");
|
|
93
|
+
if (text.length > 100_000) throw new Error("Response too large");
|
|
94
|
+
if (text !== previous) {
|
|
95
|
+
yield text.startsWith(previous)
|
|
96
|
+
? { type: "text", text: text.slice(previous.length) }
|
|
97
|
+
: { type: "replace-text", text };
|
|
98
|
+
previous = text;
|
|
99
|
+
}
|
|
100
|
+
const metadata = { aiSdk: { metadata: message.metadata, parts: message.parts } };
|
|
101
|
+
if (JSON.stringify(metadata).length > 200_000) throw new Error("Metadata too large");
|
|
102
|
+
yield { type: "metadata", metadata };
|
|
103
|
+
}
|
|
104
|
+
if (signal.aborted) return;
|
|
105
|
+
if (!finished || remoteAbort) throw new ChatInterrupted();
|
|
106
|
+
yield { type: "finish" };
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (signal.aborted) return;
|
|
109
|
+
if (error instanceof TypeError) throw new ChatInterrupted();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|