@shapesos/clay 0.10.0 → 0.11.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/dist/artifacts.cjs +741 -0
- package/dist/artifacts.cjs.map +1 -0
- package/dist/artifacts.d.cts +56 -0
- package/dist/artifacts.d.ts +56 -0
- package/dist/artifacts.js +23 -0
- package/dist/blocks.cjs +685 -53
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +2 -0
- package/dist/blocks.d.cts +22 -10
- package/dist/blocks.d.ts +22 -10
- package/dist/blocks.js +15 -5
- package/dist/button.d.cts +2 -2
- package/dist/button.d.ts +2 -2
- package/dist/chat.cjs +728 -296
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +27 -10
- package/dist/chat.d.ts +27 -10
- package/dist/chat.js +7 -3
- package/dist/chunk-BX5TCEPR.js +436 -0
- package/dist/chunk-BX5TCEPR.js.map +1 -0
- package/dist/chunk-JGMN6W72.js +12 -0
- package/dist/chunk-JGMN6W72.js.map +1 -0
- package/dist/{chunk-27GJUWVN.js → chunk-JJUIBBBU.js} +14 -8
- package/dist/chunk-JJUIBBBU.js.map +1 -0
- package/dist/chunk-L35M3OD5.js +99 -0
- package/dist/chunk-L35M3OD5.js.map +1 -0
- package/dist/chunk-MEJESPTZ.js +1 -0
- package/dist/chunk-MEJESPTZ.js.map +1 -0
- package/dist/{chunk-R3BGPOAM.js → chunk-MXOPG747.js} +53 -45
- package/dist/chunk-MXOPG747.js.map +1 -0
- package/dist/{chunk-MLCRDVQ2.js → chunk-OBOXCBDL.js} +13 -5
- package/dist/chunk-OBOXCBDL.js.map +1 -0
- package/dist/{chunk-WS4IPADR.js → chunk-OUW6PUEB.js} +42 -110
- package/dist/chunk-OUW6PUEB.js.map +1 -0
- package/dist/icon.cjs +12 -4
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.d.cts +19 -6
- package/dist/icon.d.ts +19 -6
- package/dist/icon.js +1 -1
- package/dist/index.cjs +794 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +50 -25
- package/dist/lottie.d.cts +2 -2
- package/dist/lottie.d.ts +2 -2
- package/dist/table.cjs +142 -0
- package/dist/table.cjs.map +1 -0
- package/dist/table.d.cts +44 -0
- package/dist/table.d.ts +44 -0
- package/dist/table.js +24 -0
- package/dist/table.js.map +1 -0
- package/dist/text-area.d.cts +2 -2
- package/dist/text-area.d.ts +2 -2
- package/dist/types-3Gzk7cRt.d.cts +121 -0
- package/dist/types-3Gzk7cRt.d.ts +121 -0
- package/dist/types-C9XX-Uhk.d.ts +68 -0
- package/dist/types-DuuRI4ll.d.cts +68 -0
- package/dist/utils.cjs +14 -7
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +18 -1
- package/dist/utils.d.ts +18 -1
- package/dist/utils.js +6 -4
- package/package.json +29 -4
- package/dist/chunk-27GJUWVN.js.map +0 -1
- package/dist/chunk-MLCRDVQ2.js.map +0 -1
- package/dist/chunk-OKPNST44.js +0 -1
- package/dist/chunk-R3BGPOAM.js.map +0 -1
- package/dist/chunk-WS4IPADR.js.map +0 -1
- package/dist/types-Q9aqd9nq.d.cts +0 -34
- package/dist/types-Q9aqd9nq.d.ts +0 -34
- /package/dist/{chunk-OKPNST44.js.map → artifacts.js.map} +0 -0
package/dist/chat.cjs
CHANGED
|
@@ -33,28 +33,173 @@ __export(chat_exports, {
|
|
|
33
33
|
ChatContext: () => ChatContext,
|
|
34
34
|
ChatMessage: () => ChatMessage,
|
|
35
35
|
MESSAGE_ROLE: () => MESSAGE_ROLE,
|
|
36
|
+
copyMessageText: () => copyMessageText,
|
|
36
37
|
useChatContext: () => useChatContext,
|
|
37
38
|
useCopyToClipboard: () => useCopyToClipboard
|
|
38
39
|
});
|
|
39
40
|
module.exports = __toCommonJS(chat_exports);
|
|
40
41
|
|
|
41
42
|
// src/components/chat/chat-message/chat-message.tsx
|
|
43
|
+
var import_react11 = require("react");
|
|
44
|
+
|
|
45
|
+
// src/components/blocks/types.ts
|
|
46
|
+
var blockTypes = {
|
|
47
|
+
TEXT: "TEXT",
|
|
48
|
+
ARTIFACT_REF: "ARTIFACT_REF"
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/components/artifacts/types.ts
|
|
52
|
+
var artifactTypes = {
|
|
53
|
+
TABLE: "TABLE"
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
42
57
|
var import_react7 = require("react");
|
|
43
58
|
|
|
44
|
-
// src/
|
|
45
|
-
var
|
|
46
|
-
var
|
|
47
|
-
|
|
59
|
+
// src/lib/utils.ts
|
|
60
|
+
var import_clsx = require("clsx");
|
|
61
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
62
|
+
function cn(...inputs) {
|
|
63
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
64
|
+
}
|
|
48
65
|
|
|
49
|
-
// src/components/
|
|
66
|
+
// src/components/ai-elements/artifact.tsx
|
|
67
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
68
|
+
var Artifact = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: cn("flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm", className),
|
|
72
|
+
...props
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
var ArtifactHeader = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex items-center justify-between border-b bg-muted/50 px-4 py-3", className), ...props });
|
|
76
|
+
var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
|
|
77
|
+
var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex items-center gap-1", className), ...props });
|
|
78
|
+
var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
|
|
79
|
+
|
|
80
|
+
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
50
81
|
var import_react = require("react");
|
|
51
|
-
var BlockContext = (0, import_react.createContext)(null);
|
|
52
|
-
function useBlockContext() {
|
|
53
|
-
return (0, import_react.useContext)(BlockContext) ?? {};
|
|
54
|
-
}
|
|
55
82
|
|
|
56
|
-
//
|
|
83
|
+
// node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs
|
|
84
|
+
var defaultAttributes = {
|
|
85
|
+
outline: {
|
|
86
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
87
|
+
width: 24,
|
|
88
|
+
height: 24,
|
|
89
|
+
viewBox: "0 0 24 24",
|
|
90
|
+
fill: "none",
|
|
91
|
+
stroke: "currentColor",
|
|
92
|
+
strokeWidth: 2,
|
|
93
|
+
strokeLinecap: "round",
|
|
94
|
+
strokeLinejoin: "round"
|
|
95
|
+
},
|
|
96
|
+
filled: {
|
|
97
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
98
|
+
width: 24,
|
|
99
|
+
height: 24,
|
|
100
|
+
viewBox: "0 0 24 24",
|
|
101
|
+
fill: "currentColor",
|
|
102
|
+
stroke: "none"
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
107
|
+
var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
|
|
108
|
+
const Component = (0, import_react.forwardRef)(
|
|
109
|
+
({ color = "currentColor", size = 24, stroke = 2, title, className, children, ...rest }, ref) => (0, import_react.createElement)(
|
|
110
|
+
"svg",
|
|
111
|
+
{
|
|
112
|
+
ref,
|
|
113
|
+
...defaultAttributes[type],
|
|
114
|
+
width: size,
|
|
115
|
+
height: size,
|
|
116
|
+
className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(" "),
|
|
117
|
+
...type === "filled" ? {
|
|
118
|
+
fill: color
|
|
119
|
+
} : {
|
|
120
|
+
strokeWidth: stroke,
|
|
121
|
+
stroke: color
|
|
122
|
+
},
|
|
123
|
+
...rest
|
|
124
|
+
},
|
|
125
|
+
[
|
|
126
|
+
title && (0, import_react.createElement)("title", { key: "svg-title" }, title),
|
|
127
|
+
...iconNode.map(([tag, attrs]) => (0, import_react.createElement)(tag, attrs)),
|
|
128
|
+
...Array.isArray(children) ? children : [children]
|
|
129
|
+
]
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
Component.displayName = `${iconNamePascal}`;
|
|
133
|
+
return Component;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
|
|
137
|
+
var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
|
|
138
|
+
var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
|
|
139
|
+
|
|
140
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
|
|
141
|
+
var __iconNode2 = [["path", { "d": "M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667l0 -8.666", "key": "svg-0" }], ["path", { "d": "M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1", "key": "svg-1" }]];
|
|
142
|
+
var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
|
|
143
|
+
|
|
144
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
|
|
145
|
+
var __iconNode3 = [["path", { "d": "M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2", "key": "svg-0" }], ["path", { "d": "M7 11l5 5l5 -5", "key": "svg-1" }], ["path", { "d": "M12 4l0 12", "key": "svg-2" }]];
|
|
146
|
+
var IconDownload = createReactComponent("outline", "download", "Download", __iconNode3);
|
|
147
|
+
|
|
148
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
|
|
149
|
+
var __iconNode4 = [["path", { "d": "M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3", "key": "svg-0" }]];
|
|
150
|
+
var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode4);
|
|
151
|
+
|
|
152
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
|
|
153
|
+
var __iconNode5 = [["path", { "d": "M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3", "key": "svg-0" }]];
|
|
154
|
+
var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode5);
|
|
155
|
+
|
|
156
|
+
// src/components/icon-button/icon-button.tsx
|
|
157
|
+
var import_react3 = require("react");
|
|
158
|
+
|
|
159
|
+
// src/components/icon/icon.tsx
|
|
160
|
+
var import_react2 = require("react");
|
|
161
|
+
|
|
162
|
+
// src/components/icon/icon-styles.ts
|
|
57
163
|
var import_styled_components = __toESM(require("styled-components"), 1);
|
|
164
|
+
var STROKE_WIDTH_BY_SIZE = {
|
|
165
|
+
12: 1.4,
|
|
166
|
+
14: 1.4,
|
|
167
|
+
16: 1.8,
|
|
168
|
+
18: 1.8,
|
|
169
|
+
20: 1.8,
|
|
170
|
+
24: 1.8
|
|
171
|
+
};
|
|
172
|
+
var DEFAULT_STROKE_WIDTH = 1.8;
|
|
173
|
+
function getStrokeWidth(size) {
|
|
174
|
+
return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;
|
|
175
|
+
}
|
|
176
|
+
var IconWrapper = import_styled_components.default.span`
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
color: ${({ $color }) => $color ?? "currentColor"};
|
|
181
|
+
flex-shrink: 0;
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
// src/components/icon/icon.tsx
|
|
185
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
186
|
+
var DEFAULT_SIZE = 16;
|
|
187
|
+
var Icon = (0, import_react2.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
|
|
188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
189
|
+
IconWrapper,
|
|
190
|
+
{
|
|
191
|
+
ref,
|
|
192
|
+
className,
|
|
193
|
+
$color: color,
|
|
194
|
+
"aria-label": ariaLabel,
|
|
195
|
+
role: ariaLabel ? "img" : void 0,
|
|
196
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// src/components/icon-button/icon-button-styles.ts
|
|
202
|
+
var import_styled_components2 = __toESM(require("styled-components"), 1);
|
|
58
203
|
|
|
59
204
|
// src/tokens/colors.ts
|
|
60
205
|
var colors = {
|
|
@@ -144,42 +289,521 @@ var colors = {
|
|
|
144
289
|
"red-600": "#DC2626"
|
|
145
290
|
};
|
|
146
291
|
|
|
292
|
+
// src/components/icon-button/icon-button-styles.ts
|
|
293
|
+
var PADDING = {
|
|
294
|
+
short: {
|
|
295
|
+
small: "4px",
|
|
296
|
+
medium: "6px"
|
|
297
|
+
},
|
|
298
|
+
long: {
|
|
299
|
+
small: "4px 6px",
|
|
300
|
+
medium: "6px 10px"
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
var BORDER_RADIUS = {
|
|
304
|
+
small: 6,
|
|
305
|
+
medium: 10
|
|
306
|
+
};
|
|
307
|
+
var Button = import_styled_components2.default.button`
|
|
308
|
+
display: inline-flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
justify-content: center;
|
|
311
|
+
padding: ${({ $size, $variant }) => PADDING[$variant][$size]};
|
|
312
|
+
border: none;
|
|
313
|
+
border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;
|
|
314
|
+
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : "transparent"};
|
|
315
|
+
color: ${colors["brown-100"]};
|
|
316
|
+
cursor: pointer;
|
|
317
|
+
transition: background-color 100ms ease;
|
|
318
|
+
|
|
319
|
+
&:hover {
|
|
320
|
+
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : colors["brown-20"]};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
&:active {
|
|
324
|
+
background: ${colors["brown-40"]};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
&:disabled {
|
|
328
|
+
color: ${colors["brown-50"]};
|
|
329
|
+
background: transparent;
|
|
330
|
+
cursor: not-allowed;
|
|
331
|
+
}
|
|
332
|
+
`;
|
|
333
|
+
|
|
334
|
+
// src/components/icon-button/icon-button.tsx
|
|
335
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
336
|
+
var ICON_SIZE_BY_BUTTON_SIZE = {
|
|
337
|
+
small: 16,
|
|
338
|
+
medium: 20
|
|
339
|
+
};
|
|
340
|
+
var IconButton = (0, import_react3.forwardRef)(function IconButton2({
|
|
341
|
+
icon,
|
|
342
|
+
size = "small",
|
|
343
|
+
variant = "short",
|
|
344
|
+
isSelected = false,
|
|
345
|
+
disabled = false,
|
|
346
|
+
onClick,
|
|
347
|
+
className,
|
|
348
|
+
id,
|
|
349
|
+
as,
|
|
350
|
+
"aria-label": ariaLabel,
|
|
351
|
+
...rest
|
|
352
|
+
}, ref) {
|
|
353
|
+
const isNativeButton = as === void 0 || as === "button";
|
|
354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
355
|
+
Button,
|
|
356
|
+
{
|
|
357
|
+
ref,
|
|
358
|
+
className,
|
|
359
|
+
id,
|
|
360
|
+
as,
|
|
361
|
+
$size: size,
|
|
362
|
+
$variant: variant,
|
|
363
|
+
$isSelected: isSelected,
|
|
364
|
+
disabled,
|
|
365
|
+
onClick: disabled ? void 0 : onClick,
|
|
366
|
+
"aria-label": ariaLabel,
|
|
367
|
+
...isNativeButton ? { type: "button" } : {},
|
|
368
|
+
...rest,
|
|
369
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// src/components/ui/tooltip.tsx
|
|
375
|
+
var React = __toESM(require("react"), 1);
|
|
376
|
+
var TooltipPrimitive = __toESM(require("@radix-ui/react-tooltip"), 1);
|
|
377
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
378
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
379
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
380
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
381
|
+
var TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
382
|
+
TooltipPrimitive.Content,
|
|
383
|
+
{
|
|
384
|
+
ref,
|
|
385
|
+
sideOffset,
|
|
386
|
+
className: cn(
|
|
387
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
|
|
388
|
+
className
|
|
389
|
+
),
|
|
390
|
+
...props
|
|
391
|
+
}
|
|
392
|
+
));
|
|
393
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
394
|
+
|
|
395
|
+
// src/components/artifacts/artifact-download-button/artifact-download-button.tsx
|
|
396
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
397
|
+
function ArtifactDownloadButton({ href }) {
|
|
398
|
+
if (!isSafeDownloadUrl(href)) return null;
|
|
399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
|
|
400
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
401
|
+
IconButton,
|
|
402
|
+
{
|
|
403
|
+
as: "a",
|
|
404
|
+
href,
|
|
405
|
+
download: true,
|
|
406
|
+
rel: "noopener noreferrer",
|
|
407
|
+
icon: IconDownload,
|
|
408
|
+
size: "small",
|
|
409
|
+
"aria-label": "Download artifact"
|
|
410
|
+
}
|
|
411
|
+
) }),
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: "Download" })
|
|
413
|
+
] }) });
|
|
414
|
+
}
|
|
415
|
+
function isSafeDownloadUrl(url) {
|
|
416
|
+
try {
|
|
417
|
+
const parsed = new URL(url);
|
|
418
|
+
return parsed.protocol === "https:" || parsed.protocol === "http:";
|
|
419
|
+
} catch {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/components/ui/table.tsx
|
|
425
|
+
var React2 = __toESM(require("react"), 1);
|
|
426
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
427
|
+
var Table = React2.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("relative w-full overflow-auto", containerClassName), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) }));
|
|
428
|
+
Table.displayName = "Table";
|
|
429
|
+
var TableHeader = React2.forwardRef(
|
|
430
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
|
|
431
|
+
);
|
|
432
|
+
TableHeader.displayName = "TableHeader";
|
|
433
|
+
var TableBody = React2.forwardRef(
|
|
434
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
|
|
435
|
+
);
|
|
436
|
+
TableBody.displayName = "TableBody";
|
|
437
|
+
var TableFooter = React2.forwardRef(
|
|
438
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tfoot", { ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })
|
|
439
|
+
);
|
|
440
|
+
TableFooter.displayName = "TableFooter";
|
|
441
|
+
var TableRow = React2.forwardRef(
|
|
442
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
443
|
+
"tr",
|
|
444
|
+
{
|
|
445
|
+
ref,
|
|
446
|
+
className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
|
|
447
|
+
...props
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
TableRow.displayName = "TableRow";
|
|
452
|
+
var TableHead = React2.forwardRef(
|
|
453
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
454
|
+
"th",
|
|
455
|
+
{
|
|
456
|
+
ref,
|
|
457
|
+
className: cn(
|
|
458
|
+
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
459
|
+
className
|
|
460
|
+
),
|
|
461
|
+
...props
|
|
462
|
+
}
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
TableHead.displayName = "TableHead";
|
|
466
|
+
var TableCell = React2.forwardRef(
|
|
467
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
|
|
468
|
+
);
|
|
469
|
+
TableCell.displayName = "TableCell";
|
|
470
|
+
var TableCaption = React2.forwardRef(
|
|
471
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
|
|
472
|
+
);
|
|
473
|
+
TableCaption.displayName = "TableCaption";
|
|
474
|
+
|
|
475
|
+
// src/components/table/use-scroll-shadow.ts
|
|
476
|
+
var import_react4 = require("react");
|
|
477
|
+
function useScrollShadow() {
|
|
478
|
+
const targetRef = (0, import_react4.useRef)(null);
|
|
479
|
+
const [isScrolled, setIsScrolled] = (0, import_react4.useState)(false);
|
|
480
|
+
(0, import_react4.useEffect)(() => {
|
|
481
|
+
const target = targetRef.current;
|
|
482
|
+
if (!target) return;
|
|
483
|
+
let scrollContainer = target;
|
|
484
|
+
while (scrollContainer) {
|
|
485
|
+
const overflowY = getComputedStyle(scrollContainer).overflowY;
|
|
486
|
+
if (overflowY === "auto" || overflowY === "scroll") break;
|
|
487
|
+
scrollContainer = scrollContainer.parentElement;
|
|
488
|
+
}
|
|
489
|
+
if (!scrollContainer) return;
|
|
490
|
+
setIsScrolled(scrollContainer.scrollTop > 0);
|
|
491
|
+
const handleScroll = () => {
|
|
492
|
+
if (!scrollContainer) return;
|
|
493
|
+
setIsScrolled(scrollContainer.scrollTop > 0);
|
|
494
|
+
};
|
|
495
|
+
scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
|
|
496
|
+
return () => scrollContainer.removeEventListener("scroll", handleScroll);
|
|
497
|
+
}, []);
|
|
498
|
+
return { targetRef, isScrolled };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/components/artifacts/table-artifact/table-artifact-styles.ts
|
|
502
|
+
var import_react5 = require("react");
|
|
503
|
+
function StatusMessage({ $tone, children }) {
|
|
504
|
+
return (0, import_react5.createElement)(
|
|
505
|
+
"div",
|
|
506
|
+
{
|
|
507
|
+
className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
|
|
508
|
+
},
|
|
509
|
+
children
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/components/artifacts/table-artifact/table-artifact-content.constants.ts
|
|
514
|
+
var TABLE_STATUS = {
|
|
515
|
+
LOADING: "loading",
|
|
516
|
+
READY: "ready",
|
|
517
|
+
ERROR: "error",
|
|
518
|
+
UNAVAILABLE: "unavailable"
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// src/components/artifacts/table-artifact/table-artifact-content.utils.ts
|
|
522
|
+
function formatCell(row, column) {
|
|
523
|
+
return row[column.key] ?? "";
|
|
524
|
+
}
|
|
525
|
+
var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
|
|
526
|
+
var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
|
|
527
|
+
var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
|
|
528
|
+
var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
|
|
529
|
+
var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
|
|
530
|
+
|
|
531
|
+
// src/components/artifacts/table-artifact/table-artifact-content.tsx
|
|
532
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
533
|
+
function TableArtifactContent({
|
|
534
|
+
columns,
|
|
535
|
+
data,
|
|
536
|
+
knownRowCount,
|
|
537
|
+
maxShimmerRows,
|
|
538
|
+
maxBodyHeight,
|
|
539
|
+
unavailableMessage
|
|
540
|
+
}) {
|
|
541
|
+
if (data.status === TABLE_STATUS.ERROR) {
|
|
542
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "error", children: "Couldn't load table data." });
|
|
543
|
+
}
|
|
544
|
+
if (data.status === TABLE_STATUS.UNAVAILABLE) {
|
|
545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: unavailableMessage });
|
|
546
|
+
}
|
|
547
|
+
if (data.status === TABLE_STATUS.LOADING) {
|
|
548
|
+
if (typeof knownRowCount === "number" && knownRowCount > 0) {
|
|
549
|
+
const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
|
|
550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
|
|
551
|
+
}
|
|
552
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatusMessage, { $tone: "info", children: "Loading table\u2026" });
|
|
553
|
+
}
|
|
554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length) : renderDataRows(columns, data.rows) });
|
|
555
|
+
}
|
|
556
|
+
function ArtifactTable({ columns, maxBodyHeight, children }) {
|
|
557
|
+
const { targetRef, isScrolled } = useScrollShadow();
|
|
558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Table, { containerClassName: "overflow-visible", children: [
|
|
559
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
560
|
+
TableHeader,
|
|
561
|
+
{
|
|
562
|
+
"data-scrolled": isScrolled ? "true" : void 0,
|
|
563
|
+
className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
|
|
564
|
+
style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
|
|
565
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
|
|
566
|
+
}
|
|
567
|
+
),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableBody, { children })
|
|
569
|
+
] }) });
|
|
570
|
+
}
|
|
571
|
+
function renderShimmerRows(columns, count) {
|
|
572
|
+
return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
|
|
573
|
+
}
|
|
574
|
+
function renderDataRows(columns, rows) {
|
|
575
|
+
return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
|
|
576
|
+
}
|
|
577
|
+
function renderEmptyRow(colSpan) {
|
|
578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: "No rows to display." }) });
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/components/artifacts/table-artifact/use-table-artifact-data.ts
|
|
582
|
+
var import_react6 = require("react");
|
|
583
|
+
|
|
584
|
+
// src/components/artifacts/table-artifact/parse-csv.ts
|
|
585
|
+
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
586
|
+
function parseCsv(text, columns) {
|
|
587
|
+
const result = import_papaparse.default.parse(text, {
|
|
588
|
+
header: true,
|
|
589
|
+
skipEmptyLines: true,
|
|
590
|
+
dynamicTyping: false
|
|
591
|
+
});
|
|
592
|
+
const csvHeaders = new Set(result.meta?.fields ?? []);
|
|
593
|
+
const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
|
|
594
|
+
return result.data.map((rawRow) => {
|
|
595
|
+
const row = {};
|
|
596
|
+
for (const column of presentColumns) {
|
|
597
|
+
const value = rawRow[column.key];
|
|
598
|
+
row[column.key] = value === void 0 || value === "" ? null : value;
|
|
599
|
+
}
|
|
600
|
+
return row;
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/components/artifacts/table-artifact/use-table-artifact-data.ts
|
|
605
|
+
var LOADING = { status: "loading", rows: null, error: null };
|
|
606
|
+
var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
|
|
607
|
+
var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
|
|
608
|
+
function columnsFingerprint(columns) {
|
|
609
|
+
return columns.map((c) => `${c.key}:${c.type ?? "string"}`).join("|");
|
|
610
|
+
}
|
|
611
|
+
function useTableArtifactData(presignedUrl, columns) {
|
|
612
|
+
const colsKey = (0, import_react6.useMemo)(() => columnsFingerprint(columns), [columns]);
|
|
613
|
+
const cacheKey = presignedUrl ? `${presignedUrl}::${colsKey}` : null;
|
|
614
|
+
const [state, setState] = (0, import_react6.useState)(INITIAL_INTERNAL);
|
|
615
|
+
(0, import_react6.useEffect)(() => {
|
|
616
|
+
if (!presignedUrl) return;
|
|
617
|
+
const controller = new AbortController();
|
|
618
|
+
fetch(presignedUrl, {
|
|
619
|
+
signal: controller.signal,
|
|
620
|
+
headers: { Accept: "text/csv" },
|
|
621
|
+
// Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
|
|
622
|
+
// omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
|
|
623
|
+
// session cookies or auth headers.
|
|
624
|
+
credentials: "omit"
|
|
625
|
+
}).then(async (response) => {
|
|
626
|
+
if (!response.ok) throw new Error(`Failed to load table data (${response.status})`);
|
|
627
|
+
return response.text();
|
|
628
|
+
}).then((text) => {
|
|
629
|
+
if (controller.signal.aborted) return;
|
|
630
|
+
try {
|
|
631
|
+
const rows = parseCsv(text, columns);
|
|
632
|
+
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
|
|
633
|
+
} catch (parseError) {
|
|
634
|
+
console.error("[TableArtifact] CSV parse failed", parseError);
|
|
635
|
+
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
|
|
636
|
+
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
637
|
+
}
|
|
638
|
+
}).catch((error) => {
|
|
639
|
+
if (controller.signal.aborted) return;
|
|
640
|
+
console.error("[TableArtifact] Fetch failed", error);
|
|
641
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
642
|
+
setState({ cacheKey: `${presignedUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
643
|
+
});
|
|
644
|
+
return () => {
|
|
645
|
+
controller.abort();
|
|
646
|
+
};
|
|
647
|
+
}, [presignedUrl, colsKey]);
|
|
648
|
+
if (!presignedUrl) return UNAVAILABLE;
|
|
649
|
+
if (state.cacheKey !== cacheKey) return LOADING;
|
|
650
|
+
return state.data;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// src/components/artifacts/table-artifact/validate.ts
|
|
654
|
+
function isValidTableConfig(config) {
|
|
655
|
+
if (!config || typeof config !== "object") return false;
|
|
656
|
+
const candidate = config;
|
|
657
|
+
if (!Array.isArray(candidate.columns)) return false;
|
|
658
|
+
if (candidate.columns.length === 0) return false;
|
|
659
|
+
return candidate.columns.every(isValidTableColumn);
|
|
660
|
+
}
|
|
661
|
+
function isValidTableColumn(column) {
|
|
662
|
+
if (!column || typeof column !== "object") return false;
|
|
663
|
+
const candidate = column;
|
|
664
|
+
if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
|
|
665
|
+
if (typeof candidate.label !== "string") return false;
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
670
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
671
|
+
var TITLE_CLASSES = "text-sm font-semibold text-foreground";
|
|
672
|
+
var TABLE_MAX_BODY_HEIGHT = 480;
|
|
673
|
+
var MAX_SHIMMER_ROWS = 10;
|
|
674
|
+
var MSG_UNAVAILABLE = "Table data is unavailable.";
|
|
675
|
+
function TableArtifact({ artifact }) {
|
|
676
|
+
const validConfig = isValidTableConfig(artifact.config);
|
|
677
|
+
const validColumns = validConfig ? artifact.config.columns : null;
|
|
678
|
+
(0, import_react7.useEffect)(() => {
|
|
679
|
+
if (validConfig) return;
|
|
680
|
+
console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
|
|
681
|
+
}, [validConfig]);
|
|
682
|
+
const data = useTableArtifactData(
|
|
683
|
+
validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
|
|
684
|
+
validColumns ?? []
|
|
685
|
+
);
|
|
686
|
+
const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
|
|
687
|
+
if (!validConfig || !validColumns) {
|
|
688
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
689
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
690
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
|
|
691
|
+
presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl }) }) : null
|
|
692
|
+
] }),
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusMessage, { $tone: "info", children: MSG_UNAVAILABLE }) })
|
|
694
|
+
] });
|
|
695
|
+
}
|
|
696
|
+
return (
|
|
697
|
+
// The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
|
|
698
|
+
// Disable the outer overflow container so only one scroll context exists — this prevents
|
|
699
|
+
// double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
|
|
700
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
701
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
702
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", {}),
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactDownloadButton, { href: presignedUrl ?? "" }) })
|
|
704
|
+
] }),
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
706
|
+
TableArtifactContent,
|
|
707
|
+
{
|
|
708
|
+
columns: validColumns,
|
|
709
|
+
data,
|
|
710
|
+
knownRowCount: artifact.config.stats?.rowCount,
|
|
711
|
+
maxShimmerRows: MAX_SHIMMER_ROWS,
|
|
712
|
+
maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
|
|
713
|
+
unavailableMessage: MSG_UNAVAILABLE
|
|
714
|
+
}
|
|
715
|
+
) })
|
|
716
|
+
] })
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/components/artifacts/table-artifact/to-clipboard-text.ts
|
|
721
|
+
function tableArtifactToClipboardText(artifact) {
|
|
722
|
+
const trimmedTitle = artifact.title?.trim();
|
|
723
|
+
return trimmedTitle || "Table";
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/components/artifacts/artifact-services/table-artifact-service.ts
|
|
727
|
+
var TableArtifactService = {
|
|
728
|
+
type: artifactTypes.TABLE,
|
|
729
|
+
Component: TableArtifact,
|
|
730
|
+
toClipboardText: tableArtifactToClipboardText
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/components/artifacts/artifact-services/index.ts
|
|
734
|
+
var ArtifactServices = {
|
|
735
|
+
[artifactTypes.TABLE]: TableArtifactService
|
|
736
|
+
};
|
|
737
|
+
function artifactToClipboardText(artifact) {
|
|
738
|
+
const entry = ArtifactServices[artifact.type];
|
|
739
|
+
if (!entry) {
|
|
740
|
+
return `[Artifact: ${artifact.title ?? artifact.type}]`;
|
|
741
|
+
}
|
|
742
|
+
return entry.toClipboardText(artifact);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
|
|
746
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
747
|
+
function ArtifactRefBlock({ block }) {
|
|
748
|
+
const { artifact } = block.payload;
|
|
749
|
+
const entry = ArtifactServices[artifact.type];
|
|
750
|
+
if (!entry) return null;
|
|
751
|
+
const { Component } = entry;
|
|
752
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Component, { artifact });
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/components/blocks/block-services/artifact-ref-block-service.ts
|
|
756
|
+
var ArtifactRefBlockService = {
|
|
757
|
+
type: blockTypes.ARTIFACT_REF,
|
|
758
|
+
Component: ArtifactRefBlock,
|
|
759
|
+
/**
|
|
760
|
+
* Delegates to the registered artifact component's `toClipboardText`. Falls back to
|
|
761
|
+
* `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
|
|
762
|
+
* has no registered component, so copy never silently produces an empty string.
|
|
763
|
+
*/
|
|
764
|
+
toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
// src/utils/markdown-to-plain-text.ts
|
|
768
|
+
var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
|
|
769
|
+
var PLACEHOLDER_SUFFIX = "\u200B\u200B";
|
|
770
|
+
var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
|
|
771
|
+
function stripInlineMarks(text) {
|
|
772
|
+
return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
|
|
773
|
+
}
|
|
774
|
+
function markdownToPlainText(markdown) {
|
|
775
|
+
if (!markdown) return "";
|
|
776
|
+
const inlineCodeSnippets = [];
|
|
777
|
+
let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
|
|
778
|
+
inlineCodeSnippets.push(code);
|
|
779
|
+
return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
|
|
780
|
+
}).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
|
|
781
|
+
result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
782
|
+
result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
|
|
783
|
+
return result;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/components/blocks/text-block/text-block.tsx
|
|
787
|
+
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
788
|
+
var import_remark_breaks = __toESM(require("remark-breaks"), 1);
|
|
789
|
+
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
790
|
+
|
|
147
791
|
// src/components/blocks/scrollable-table/scrollable-table-styles.ts
|
|
148
|
-
var
|
|
792
|
+
var import_styled_components3 = __toESM(require("styled-components"), 1);
|
|
793
|
+
var TableContainer = import_styled_components3.default.div`
|
|
149
794
|
position: relative;
|
|
150
795
|
margin-block: 8px 16px;
|
|
151
796
|
`;
|
|
152
|
-
var TableScroll =
|
|
797
|
+
var TableScroll = import_styled_components3.default.div`
|
|
153
798
|
width: 100%;
|
|
154
799
|
overflow-x: auto;
|
|
155
800
|
overflow-y: hidden;
|
|
156
801
|
`;
|
|
157
|
-
var TableActionsWrapper = import_styled_components.default.div`
|
|
158
|
-
display: flex;
|
|
159
|
-
justify-content: flex-start;
|
|
160
|
-
margin-block: 8px 0;
|
|
161
|
-
`;
|
|
162
|
-
var TableActionsDivider = import_styled_components.default.div`
|
|
163
|
-
height: 1px;
|
|
164
|
-
background: ${colors["brown-40"]};
|
|
165
|
-
margin-block: 8px 0;
|
|
166
|
-
`;
|
|
167
802
|
|
|
168
803
|
// src/components/blocks/scrollable-table/scrollable-table.tsx
|
|
169
|
-
var
|
|
170
|
-
function ScrollableTable({ node: _node,
|
|
171
|
-
|
|
172
|
-
if (counter) {
|
|
173
|
-
counter.value += 1;
|
|
174
|
-
}
|
|
175
|
-
const { TableActions } = useBlockContext();
|
|
176
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableContainer, { children: [
|
|
177
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { ...props }) }),
|
|
178
|
-
TableActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
179
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActions, { tableIndex: currentIndex }) }),
|
|
180
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableActionsDivider, {})
|
|
181
|
-
] })
|
|
182
|
-
] });
|
|
804
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
805
|
+
function ScrollableTable({ node: _node, ...props }) {
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("table", { ...props }) }) });
|
|
183
807
|
}
|
|
184
808
|
|
|
185
809
|
// src/components/blocks/text-block/sanitize.ts
|
|
@@ -191,7 +815,7 @@ function sanitize(text) {
|
|
|
191
815
|
}
|
|
192
816
|
|
|
193
817
|
// src/components/blocks/text-block/text-block-styles.ts
|
|
194
|
-
var
|
|
818
|
+
var import_styled_components4 = __toESM(require("styled-components"), 1);
|
|
195
819
|
|
|
196
820
|
// src/tokens/typography.ts
|
|
197
821
|
var fontFamilies = {
|
|
@@ -489,7 +1113,7 @@ function typographyMixin(type) {
|
|
|
489
1113
|
}
|
|
490
1114
|
|
|
491
1115
|
// src/components/blocks/text-block/text-block-styles.ts
|
|
492
|
-
var TextBlockWrapper =
|
|
1116
|
+
var TextBlockWrapper = import_styled_components4.default.div`
|
|
493
1117
|
${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
|
|
494
1118
|
color: ${colors["brown-100"]};
|
|
495
1119
|
word-break: break-word;
|
|
@@ -607,248 +1231,71 @@ var TextBlockWrapper = import_styled_components2.default.div`
|
|
|
607
1231
|
`;
|
|
608
1232
|
|
|
609
1233
|
// src/components/blocks/text-block/text-block.tsx
|
|
610
|
-
var
|
|
1234
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1235
|
+
var markdownComponents = {
|
|
1236
|
+
table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollableTable, { ...tableProps }))
|
|
1237
|
+
};
|
|
611
1238
|
function TextBlock({ block }) {
|
|
612
|
-
|
|
613
|
-
const markdownComponents = {
|
|
614
|
-
table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollableTable, { ...tableProps, counter }))
|
|
615
|
-
};
|
|
616
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
|
|
1239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
|
|
617
1240
|
}
|
|
618
1241
|
|
|
619
|
-
// src/components/blocks/types.ts
|
|
620
|
-
var blockTypes = {
|
|
621
|
-
TEXT: "TEXT"
|
|
622
|
-
};
|
|
623
|
-
|
|
624
1242
|
// src/components/blocks/block-services/text-block-service.ts
|
|
625
1243
|
var TextBlockService = {
|
|
626
1244
|
type: blockTypes.TEXT,
|
|
627
|
-
Component: TextBlock
|
|
1245
|
+
Component: TextBlock,
|
|
1246
|
+
/**
|
|
1247
|
+
* Converts the block's markdown source to plain text so the final clipboard value is clean
|
|
1248
|
+
* when pasted into tools that don't render markdown. Each block type owns its own clipboard
|
|
1249
|
+
* conversion so `useCopyToClipboard` can write text verbatim without a second pass.
|
|
1250
|
+
*/
|
|
1251
|
+
toClipboardText: (block) => markdownToPlainText(block.payload.text)
|
|
628
1252
|
};
|
|
629
1253
|
|
|
630
1254
|
// src/components/blocks/block-services/index.ts
|
|
631
1255
|
var BlockServices = {
|
|
632
|
-
[
|
|
1256
|
+
[blockTypes.TEXT]: TextBlockService,
|
|
1257
|
+
[blockTypes.ARTIFACT_REF]: ArtifactRefBlockService
|
|
633
1258
|
};
|
|
634
1259
|
|
|
635
1260
|
// src/components/blocks/block/block.tsx
|
|
636
|
-
var
|
|
1261
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
637
1262
|
function Block({ block }) {
|
|
638
1263
|
const service = BlockServices[block.type];
|
|
639
1264
|
if (!service) return null;
|
|
640
1265
|
const { Component } = service;
|
|
641
|
-
return /* @__PURE__ */ (0,
|
|
1266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Component, { block });
|
|
642
1267
|
}
|
|
643
1268
|
|
|
644
|
-
// src/components/chat/
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1269
|
+
// src/components/chat/hooks/copy-message-text.ts
|
|
1270
|
+
function copyMessageText(message) {
|
|
1271
|
+
const parts = [];
|
|
1272
|
+
for (const block of message.blocks) {
|
|
1273
|
+
const service = BlockServices[block.type];
|
|
1274
|
+
if (!service) continue;
|
|
1275
|
+
if (!service.toClipboardText) continue;
|
|
1276
|
+
const text = service.toClipboardText(block);
|
|
1277
|
+
if (text) parts.push(text);
|
|
651
1278
|
}
|
|
652
|
-
return
|
|
1279
|
+
if (parts.length === 0) return message.fallbackText;
|
|
1280
|
+
return parts.join("\n\n");
|
|
653
1281
|
}
|
|
654
1282
|
|
|
655
1283
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
656
|
-
var
|
|
657
|
-
|
|
658
|
-
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
659
|
-
var import_react3 = require("react");
|
|
660
|
-
|
|
661
|
-
// node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs
|
|
662
|
-
var defaultAttributes = {
|
|
663
|
-
outline: {
|
|
664
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
665
|
-
width: 24,
|
|
666
|
-
height: 24,
|
|
667
|
-
viewBox: "0 0 24 24",
|
|
668
|
-
fill: "none",
|
|
669
|
-
stroke: "currentColor",
|
|
670
|
-
strokeWidth: 2,
|
|
671
|
-
strokeLinecap: "round",
|
|
672
|
-
strokeLinejoin: "round"
|
|
673
|
-
},
|
|
674
|
-
filled: {
|
|
675
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
676
|
-
width: 24,
|
|
677
|
-
height: 24,
|
|
678
|
-
viewBox: "0 0 24 24",
|
|
679
|
-
fill: "currentColor",
|
|
680
|
-
stroke: "none"
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
685
|
-
var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
|
|
686
|
-
const Component = (0, import_react3.forwardRef)(
|
|
687
|
-
({ color = "currentColor", size = 24, stroke = 2, title, className, children, ...rest }, ref) => (0, import_react3.createElement)(
|
|
688
|
-
"svg",
|
|
689
|
-
{
|
|
690
|
-
ref,
|
|
691
|
-
...defaultAttributes[type],
|
|
692
|
-
width: size,
|
|
693
|
-
height: size,
|
|
694
|
-
className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(" "),
|
|
695
|
-
...type === "filled" ? {
|
|
696
|
-
fill: color
|
|
697
|
-
} : {
|
|
698
|
-
strokeWidth: stroke,
|
|
699
|
-
stroke: color
|
|
700
|
-
},
|
|
701
|
-
...rest
|
|
702
|
-
},
|
|
703
|
-
[
|
|
704
|
-
title && (0, import_react3.createElement)("title", { key: "svg-title" }, title),
|
|
705
|
-
...iconNode.map(([tag, attrs]) => (0, import_react3.createElement)(tag, attrs)),
|
|
706
|
-
...Array.isArray(children) ? children : [children]
|
|
707
|
-
]
|
|
708
|
-
)
|
|
709
|
-
);
|
|
710
|
-
Component.displayName = `${iconNamePascal}`;
|
|
711
|
-
return Component;
|
|
712
|
-
};
|
|
713
|
-
|
|
714
|
-
// node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
|
|
715
|
-
var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
|
|
716
|
-
var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
|
|
717
|
-
|
|
718
|
-
// node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
|
|
719
|
-
var __iconNode2 = [["path", { "d": "M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667l0 -8.666", "key": "svg-0" }], ["path", { "d": "M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1", "key": "svg-1" }]];
|
|
720
|
-
var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
|
|
721
|
-
|
|
722
|
-
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
|
|
723
|
-
var __iconNode3 = [["path", { "d": "M7 13v-8a1 1 0 0 0 -1 -1h-2a1 1 0 0 0 -1 1v7a1 1 0 0 0 1 1h3a4 4 0 0 1 4 4v1a2 2 0 0 0 4 0v-5h3a2 2 0 0 0 2 -2l-1 -5a2 3 0 0 0 -2 -2h-7a3 3 0 0 0 -3 3", "key": "svg-0" }]];
|
|
724
|
-
var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode3);
|
|
725
|
-
|
|
726
|
-
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
|
|
727
|
-
var __iconNode4 = [["path", { "d": "M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3", "key": "svg-0" }]];
|
|
728
|
-
var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode4);
|
|
729
|
-
|
|
730
|
-
// src/components/icon/icon.tsx
|
|
731
|
-
var import_react4 = require("react");
|
|
732
|
-
|
|
733
|
-
// src/components/icon/icon-styles.ts
|
|
734
|
-
var import_styled_components3 = __toESM(require("styled-components"), 1);
|
|
735
|
-
var STROKE_WIDTH_BY_SIZE = {
|
|
736
|
-
12: 1.4,
|
|
737
|
-
14: 1.4,
|
|
738
|
-
16: 1.8,
|
|
739
|
-
18: 1.8,
|
|
740
|
-
20: 1.8,
|
|
741
|
-
24: 1.8
|
|
742
|
-
};
|
|
743
|
-
var DEFAULT_STROKE_WIDTH = 1.8;
|
|
744
|
-
function getStrokeWidth(size) {
|
|
745
|
-
return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;
|
|
746
|
-
}
|
|
747
|
-
var IconWrapper = import_styled_components3.default.span`
|
|
748
|
-
display: inline-flex;
|
|
749
|
-
align-items: center;
|
|
750
|
-
justify-content: center;
|
|
751
|
-
color: ${({ $color }) => $color ?? "currentColor"};
|
|
752
|
-
flex-shrink: 0;
|
|
753
|
-
`;
|
|
754
|
-
|
|
755
|
-
// src/components/icon/icon.tsx
|
|
756
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
757
|
-
var DEFAULT_SIZE = 16;
|
|
758
|
-
var Icon = (0, import_react4.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
|
|
759
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
760
|
-
IconWrapper,
|
|
761
|
-
{
|
|
762
|
-
ref,
|
|
763
|
-
className,
|
|
764
|
-
$color: color,
|
|
765
|
-
"aria-label": ariaLabel,
|
|
766
|
-
role: ariaLabel ? "img" : void 0,
|
|
767
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
|
|
768
|
-
}
|
|
769
|
-
);
|
|
770
|
-
});
|
|
771
|
-
|
|
772
|
-
// src/components/icon-button/icon-button-styles.ts
|
|
773
|
-
var import_styled_components4 = __toESM(require("styled-components"), 1);
|
|
774
|
-
var PADDING = {
|
|
775
|
-
short: {
|
|
776
|
-
small: "4px",
|
|
777
|
-
medium: "6px"
|
|
778
|
-
},
|
|
779
|
-
long: {
|
|
780
|
-
small: "4px 6px",
|
|
781
|
-
medium: "6px 10px"
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
var BORDER_RADIUS = {
|
|
785
|
-
small: 6,
|
|
786
|
-
medium: 10
|
|
787
|
-
};
|
|
788
|
-
var Button = import_styled_components4.default.button`
|
|
789
|
-
display: inline-flex;
|
|
790
|
-
align-items: center;
|
|
791
|
-
justify-content: center;
|
|
792
|
-
padding: ${({ $size, $variant }) => PADDING[$variant][$size]};
|
|
793
|
-
border: none;
|
|
794
|
-
border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;
|
|
795
|
-
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : "transparent"};
|
|
796
|
-
color: ${colors["brown-100"]};
|
|
797
|
-
cursor: pointer;
|
|
798
|
-
transition: background-color 100ms ease;
|
|
799
|
-
|
|
800
|
-
&:hover {
|
|
801
|
-
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : colors["brown-20"]};
|
|
802
|
-
}
|
|
1284
|
+
var import_react10 = require("react");
|
|
803
1285
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
cursor: not-allowed;
|
|
1286
|
+
// src/components/chat/chat-context/chat-context.ts
|
|
1287
|
+
var import_react8 = require("react");
|
|
1288
|
+
var ChatContext = (0, import_react8.createContext)(null);
|
|
1289
|
+
function useChatContext() {
|
|
1290
|
+
const context = (0, import_react8.useContext)(ChatContext);
|
|
1291
|
+
if (!context) {
|
|
1292
|
+
throw new Error("useChatContext must be used within a Chat.Root component");
|
|
812
1293
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
// src/components/icon-button/icon-button.tsx
|
|
816
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
817
|
-
var ICON_SIZE_BY_BUTTON_SIZE = {
|
|
818
|
-
small: 16,
|
|
819
|
-
medium: 20
|
|
820
|
-
};
|
|
821
|
-
function IconButton({
|
|
822
|
-
icon,
|
|
823
|
-
size = "small",
|
|
824
|
-
variant = "short",
|
|
825
|
-
isSelected = false,
|
|
826
|
-
disabled = false,
|
|
827
|
-
onClick,
|
|
828
|
-
className,
|
|
829
|
-
id,
|
|
830
|
-
as,
|
|
831
|
-
"aria-label": ariaLabel
|
|
832
|
-
}) {
|
|
833
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
834
|
-
Button,
|
|
835
|
-
{
|
|
836
|
-
className,
|
|
837
|
-
id,
|
|
838
|
-
as,
|
|
839
|
-
$size: size,
|
|
840
|
-
$variant: variant,
|
|
841
|
-
$isSelected: isSelected,
|
|
842
|
-
disabled,
|
|
843
|
-
onClick: disabled ? void 0 : onClick,
|
|
844
|
-
"aria-label": ariaLabel,
|
|
845
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
|
|
846
|
-
}
|
|
847
|
-
);
|
|
1294
|
+
return context;
|
|
848
1295
|
}
|
|
849
1296
|
|
|
850
1297
|
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
851
|
-
var
|
|
1298
|
+
var import_react9 = require("react");
|
|
852
1299
|
|
|
853
1300
|
// src/utils/clipboard.ts
|
|
854
1301
|
function copyToClipboard(text, options) {
|
|
@@ -858,37 +1305,18 @@ function copyToClipboard(text, options) {
|
|
|
858
1305
|
);
|
|
859
1306
|
}
|
|
860
1307
|
|
|
861
|
-
// src/utils/markdown-to-plain-text.ts
|
|
862
|
-
var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
|
|
863
|
-
var PLACEHOLDER_SUFFIX = "\u200B\u200B";
|
|
864
|
-
var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
|
|
865
|
-
function stripInlineMarks(text) {
|
|
866
|
-
return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
|
|
867
|
-
}
|
|
868
|
-
function markdownToPlainText(markdown) {
|
|
869
|
-
if (!markdown) return "";
|
|
870
|
-
const inlineCodeSnippets = [];
|
|
871
|
-
let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
|
|
872
|
-
inlineCodeSnippets.push(code);
|
|
873
|
-
return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
|
|
874
|
-
}).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
|
|
875
|
-
result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
876
|
-
result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
|
|
877
|
-
return result;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
1308
|
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
881
1309
|
var RESET_DELAY_MS = 2e3;
|
|
882
1310
|
function useCopyToClipboard() {
|
|
883
|
-
const [isCopied, setIsCopied] = (0,
|
|
884
|
-
const timeoutRef = (0,
|
|
885
|
-
(0,
|
|
1311
|
+
const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
|
|
1312
|
+
const timeoutRef = (0, import_react9.useRef)(null);
|
|
1313
|
+
(0, import_react9.useEffect)(() => {
|
|
886
1314
|
return () => {
|
|
887
1315
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
888
1316
|
};
|
|
889
1317
|
}, []);
|
|
890
|
-
const copy = (0,
|
|
891
|
-
copyToClipboard(
|
|
1318
|
+
const copy = (0, import_react9.useCallback)((text) => {
|
|
1319
|
+
copyToClipboard(text, {
|
|
892
1320
|
onSuccess: () => {
|
|
893
1321
|
setIsCopied(true);
|
|
894
1322
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -927,24 +1355,24 @@ var AnimatedAction = import_styled_components5.default.div`
|
|
|
927
1355
|
`;
|
|
928
1356
|
|
|
929
1357
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
930
|
-
var
|
|
1358
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
931
1359
|
function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
|
|
932
1360
|
const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
|
|
933
1361
|
const { isCopied, copy } = useCopyToClipboard();
|
|
934
|
-
const handleCopy = (0,
|
|
1362
|
+
const handleCopy = (0, import_react10.useCallback)(() => {
|
|
935
1363
|
copy(content);
|
|
936
1364
|
onCopyMessage?.(messageId);
|
|
937
1365
|
}, [content, messageId, copy, onCopyMessage]);
|
|
938
|
-
const handleThumbUp = (0,
|
|
1366
|
+
const handleThumbUp = (0, import_react10.useCallback)(() => {
|
|
939
1367
|
onThumbUpClick?.(messageId, isHelpful === true ? null : true);
|
|
940
1368
|
}, [messageId, isHelpful, onThumbUpClick]);
|
|
941
|
-
const handleThumbDown = (0,
|
|
1369
|
+
const handleThumbDown = (0, import_react10.useCallback)(() => {
|
|
942
1370
|
onThumbDownClick?.(messageId, isHelpful === false ? null : false);
|
|
943
1371
|
}, [messageId, isHelpful, onThumbDownClick]);
|
|
944
1372
|
const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
|
|
945
1373
|
const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
|
|
946
|
-
return /* @__PURE__ */ (0,
|
|
947
|
-
/* @__PURE__ */ (0,
|
|
1374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ActionsContainer, { $role: role, className, children: [
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
948
1376
|
IconButton,
|
|
949
1377
|
{
|
|
950
1378
|
icon: isCopied ? IconCheck : IconCopy,
|
|
@@ -952,7 +1380,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
952
1380
|
"aria-label": isCopied ? "Copied" : "Copy message"
|
|
953
1381
|
}
|
|
954
1382
|
),
|
|
955
|
-
hasFeedback && /* @__PURE__ */ (0,
|
|
1383
|
+
hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
956
1384
|
IconButton,
|
|
957
1385
|
{
|
|
958
1386
|
icon: IconThumbUp,
|
|
@@ -961,7 +1389,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
961
1389
|
"aria-label": "Good response"
|
|
962
1390
|
}
|
|
963
1391
|
) }),
|
|
964
|
-
hasFeedback && /* @__PURE__ */ (0,
|
|
1392
|
+
hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
965
1393
|
IconButton,
|
|
966
1394
|
{
|
|
967
1395
|
icon: IconThumbDown,
|
|
@@ -999,26 +1427,28 @@ var MessageBubble = import_styled_components6.default.div`
|
|
|
999
1427
|
padding: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "12px 16px" : "0"};
|
|
1000
1428
|
border-radius: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "16px" : "0"};
|
|
1001
1429
|
max-width: 100%;
|
|
1430
|
+
width: 100%;
|
|
1002
1431
|
min-width: 0;
|
|
1432
|
+
/* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.
|
|
1433
|
+
* Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies
|
|
1434
|
+
* spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer
|
|
1435
|
+
* gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */
|
|
1436
|
+
display: flex;
|
|
1437
|
+
flex-direction: column;
|
|
1438
|
+
gap: 12px;
|
|
1003
1439
|
`;
|
|
1004
1440
|
|
|
1005
1441
|
// src/components/chat/chat-message/chat-message.tsx
|
|
1006
|
-
var
|
|
1442
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1007
1443
|
function ChatMessage({ message }) {
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
() => ({
|
|
1011
|
-
|
|
1012
|
-
}),
|
|
1013
|
-
[ChatTableActions, message.id]
|
|
1014
|
-
);
|
|
1015
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageContainer, { $role: message.role, children: [
|
|
1016
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageBubble, { $role: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BlockContext.Provider, { value: blockContextValue, children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Block, { block }, index)) }) }),
|
|
1017
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1444
|
+
const clipboardText = (0, import_react11.useMemo)(() => copyMessageText(message), [message]);
|
|
1445
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(MessageContainer, { $role: message.role, children: [
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Block, { block }, index)) }),
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1018
1448
|
ChatMessageActions,
|
|
1019
1449
|
{
|
|
1020
1450
|
messageId: message.id,
|
|
1021
|
-
content:
|
|
1451
|
+
content: clipboardText,
|
|
1022
1452
|
role: message.role,
|
|
1023
1453
|
isHelpful: message.isHelpful
|
|
1024
1454
|
}
|
|
@@ -1030,6 +1460,7 @@ function ChatMessage({ message }) {
|
|
|
1030
1460
|
ChatContext,
|
|
1031
1461
|
ChatMessage,
|
|
1032
1462
|
MESSAGE_ROLE,
|
|
1463
|
+
copyMessageText,
|
|
1033
1464
|
useChatContext,
|
|
1034
1465
|
useCopyToClipboard
|
|
1035
1466
|
});
|
|
@@ -1039,6 +1470,7 @@ function ChatMessage({ message }) {
|
|
|
1039
1470
|
@tabler/icons-react/dist/esm/createReactComponent.mjs:
|
|
1040
1471
|
@tabler/icons-react/dist/esm/icons/IconCheck.mjs:
|
|
1041
1472
|
@tabler/icons-react/dist/esm/icons/IconCopy.mjs:
|
|
1473
|
+
@tabler/icons-react/dist/esm/icons/IconDownload.mjs:
|
|
1042
1474
|
@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs:
|
|
1043
1475
|
@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs:
|
|
1044
1476
|
@tabler/icons-react/dist/esm/tabler-icons-react.mjs:
|