@shapesos/clay 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/artifacts.cjs +1467 -0
- package/dist/artifacts.cjs.map +1 -0
- package/dist/artifacts.d.cts +89 -0
- package/dist/artifacts.d.ts +89 -0
- package/dist/artifacts.js +33 -0
- package/dist/blocks.cjs +1418 -68
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +2 -0
- package/dist/blocks.d.cts +36 -11
- package/dist/blocks.d.ts +36 -11
- package/dist/blocks.js +18 -5
- package/dist/button.d.cts +2 -2
- package/dist/button.d.ts +2 -2
- package/dist/chart.cjs +594 -0
- package/dist/chart.cjs.map +1 -0
- package/dist/chart.d.cts +439 -0
- package/dist/chart.d.ts +439 -0
- package/dist/chart.js +32 -0
- package/dist/chart.js.map +1 -0
- package/dist/chat.cjs +1462 -312
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +28 -10
- package/dist/chat.d.ts +28 -10
- package/dist/chat.js +10 -3
- package/dist/{chunk-R3BGPOAM.js → chunk-36CB624W.js} +53 -45
- package/dist/chunk-36CB624W.js.map +1 -0
- package/dist/chunk-4MZZH3WX.js +93 -0
- package/dist/chunk-4MZZH3WX.js.map +1 -0
- package/dist/chunk-AQEJRMRN.js +1 -0
- package/dist/chunk-AQEJRMRN.js.map +1 -0
- package/dist/{chunk-WS4IPADR.js → chunk-FFX3CAOX.js} +41 -109
- package/dist/chunk-FFX3CAOX.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-MLCRDVQ2.js → chunk-OBOXCBDL.js} +13 -5
- package/dist/chunk-OBOXCBDL.js.map +1 -0
- package/dist/chunk-P6GUNIAE.js +11 -0
- package/dist/chunk-P6GUNIAE.js.map +1 -0
- package/dist/chunk-QXGYMDIA.js +477 -0
- package/dist/chunk-QXGYMDIA.js.map +1 -0
- package/dist/chunk-Z5JWF24N.js +719 -0
- package/dist/chunk-Z5JWF24N.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 +1520 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +58 -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 +25 -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-B2aYk82c.d.cts +29 -0
- package/dist/types-B2aYk82c.d.ts +29 -0
- package/dist/types-C0BvwliI.d.cts +332 -0
- package/dist/types-C5bFH4v3.d.ts +332 -0
- package/dist/types-DCutaXjZ.d.cts +83 -0
- package/dist/types-uPfn67Dc.d.ts +83 -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 +41 -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,193 @@ __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
|
|
42
|
-
var
|
|
43
|
+
var import_react15 = require("react");
|
|
43
44
|
|
|
44
|
-
// src/components/blocks/
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// src/components/blocks/types.ts
|
|
46
|
+
var blockTypes = {
|
|
47
|
+
TEXT: "TEXT",
|
|
48
|
+
ARTIFACT_REF: "ARTIFACT_REF"
|
|
49
|
+
};
|
|
48
50
|
|
|
49
|
-
// src/components/
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
// src/components/artifacts/artifact-base.ts
|
|
52
|
+
var artifactTypes = {
|
|
53
|
+
TABLE: "TABLE",
|
|
54
|
+
CHART: "CHART"
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/components/artifacts/chart-artifact/types.ts
|
|
58
|
+
var DEFAULT_CHART_ARTIFACT_LABELS = {
|
|
59
|
+
download: "Download CSV",
|
|
60
|
+
unavailable: "Chart data is unavailable.",
|
|
61
|
+
loadError: "Couldn't load chart data.",
|
|
62
|
+
loading: "Loading chart\u2026",
|
|
63
|
+
empty: "No data to chart.",
|
|
64
|
+
othersCategoryLabels: ["Others", "Other"]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/components/artifacts/table-artifact/types.ts
|
|
68
|
+
var DEFAULT_TABLE_ARTIFACT_LABELS = {
|
|
69
|
+
download: "Download CSV",
|
|
70
|
+
unavailable: "Table data is unavailable.",
|
|
71
|
+
loadError: "Couldn't load table data.",
|
|
72
|
+
loading: "Loading table\u2026",
|
|
73
|
+
empty: "No rows to display."
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/components/artifacts/chart-artifact/chart-artifact.tsx
|
|
77
|
+
var import_react9 = require("react");
|
|
78
|
+
|
|
79
|
+
// src/lib/utils.ts
|
|
80
|
+
var import_clsx = require("clsx");
|
|
81
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
82
|
+
function cn(...inputs) {
|
|
83
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
54
84
|
}
|
|
55
85
|
|
|
56
|
-
// src/components/
|
|
86
|
+
// src/components/ai-elements/artifact.tsx
|
|
87
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
88
|
+
var Artifact = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
89
|
+
"div",
|
|
90
|
+
{
|
|
91
|
+
className: cn("flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm", className),
|
|
92
|
+
...props
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
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 });
|
|
96
|
+
var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
|
|
97
|
+
var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex items-center gap-1", className), ...props });
|
|
98
|
+
var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
|
|
99
|
+
|
|
100
|
+
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
101
|
+
var import_react = require("react");
|
|
102
|
+
|
|
103
|
+
// node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs
|
|
104
|
+
var defaultAttributes = {
|
|
105
|
+
outline: {
|
|
106
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
107
|
+
width: 24,
|
|
108
|
+
height: 24,
|
|
109
|
+
viewBox: "0 0 24 24",
|
|
110
|
+
fill: "none",
|
|
111
|
+
stroke: "currentColor",
|
|
112
|
+
strokeWidth: 2,
|
|
113
|
+
strokeLinecap: "round",
|
|
114
|
+
strokeLinejoin: "round"
|
|
115
|
+
},
|
|
116
|
+
filled: {
|
|
117
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
118
|
+
width: 24,
|
|
119
|
+
height: 24,
|
|
120
|
+
viewBox: "0 0 24 24",
|
|
121
|
+
fill: "currentColor",
|
|
122
|
+
stroke: "none"
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs
|
|
127
|
+
var createReactComponent = (type, iconName, iconNamePascal, iconNode) => {
|
|
128
|
+
const Component = (0, import_react.forwardRef)(
|
|
129
|
+
({ color = "currentColor", size = 24, stroke = 2, title, className, children, ...rest }, ref) => (0, import_react.createElement)(
|
|
130
|
+
"svg",
|
|
131
|
+
{
|
|
132
|
+
ref,
|
|
133
|
+
...defaultAttributes[type],
|
|
134
|
+
width: size,
|
|
135
|
+
height: size,
|
|
136
|
+
className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(" "),
|
|
137
|
+
...type === "filled" ? {
|
|
138
|
+
fill: color
|
|
139
|
+
} : {
|
|
140
|
+
strokeWidth: stroke,
|
|
141
|
+
stroke: color
|
|
142
|
+
},
|
|
143
|
+
...rest
|
|
144
|
+
},
|
|
145
|
+
[
|
|
146
|
+
title && (0, import_react.createElement)("title", { key: "svg-title" }, title),
|
|
147
|
+
...iconNode.map(([tag, attrs]) => (0, import_react.createElement)(tag, attrs)),
|
|
148
|
+
...Array.isArray(children) ? children : [children]
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
Component.displayName = `${iconNamePascal}`;
|
|
153
|
+
return Component;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs
|
|
157
|
+
var __iconNode = [["path", { "d": "M5 12l5 5l10 -10", "key": "svg-0" }]];
|
|
158
|
+
var IconCheck = createReactComponent("outline", "check", "Check", __iconNode);
|
|
159
|
+
|
|
160
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconCopy.mjs
|
|
161
|
+
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" }]];
|
|
162
|
+
var IconCopy = createReactComponent("outline", "copy", "Copy", __iconNode2);
|
|
163
|
+
|
|
164
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconDownload.mjs
|
|
165
|
+
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" }]];
|
|
166
|
+
var IconDownload = createReactComponent("outline", "download", "Download", __iconNode3);
|
|
167
|
+
|
|
168
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs
|
|
169
|
+
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" }]];
|
|
170
|
+
var IconThumbDown = createReactComponent("outline", "thumb-down", "ThumbDown", __iconNode4);
|
|
171
|
+
|
|
172
|
+
// node_modules/@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs
|
|
173
|
+
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" }]];
|
|
174
|
+
var IconThumbUp = createReactComponent("outline", "thumb-up", "ThumbUp", __iconNode5);
|
|
175
|
+
|
|
176
|
+
// src/components/icon-button/icon-button.tsx
|
|
177
|
+
var import_react3 = require("react");
|
|
178
|
+
|
|
179
|
+
// src/components/icon/icon.tsx
|
|
180
|
+
var import_react2 = require("react");
|
|
181
|
+
|
|
182
|
+
// src/components/icon/icon-styles.ts
|
|
57
183
|
var import_styled_components = __toESM(require("styled-components"), 1);
|
|
184
|
+
var STROKE_WIDTH_BY_SIZE = {
|
|
185
|
+
12: 1.4,
|
|
186
|
+
14: 1.4,
|
|
187
|
+
16: 1.8,
|
|
188
|
+
18: 1.8,
|
|
189
|
+
20: 1.8,
|
|
190
|
+
24: 1.8
|
|
191
|
+
};
|
|
192
|
+
var DEFAULT_STROKE_WIDTH = 1.8;
|
|
193
|
+
function getStrokeWidth(size) {
|
|
194
|
+
return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;
|
|
195
|
+
}
|
|
196
|
+
var IconWrapper = import_styled_components.default.span`
|
|
197
|
+
display: inline-flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
justify-content: center;
|
|
200
|
+
color: ${({ $color }) => $color ?? "currentColor"};
|
|
201
|
+
flex-shrink: 0;
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
// src/components/icon/icon.tsx
|
|
205
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
206
|
+
var DEFAULT_SIZE = 16;
|
|
207
|
+
var Icon = (0, import_react2.forwardRef)(function Icon2({ icon: IconComponent, size = DEFAULT_SIZE, color, className, "aria-label": ariaLabel }, ref) {
|
|
208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
209
|
+
IconWrapper,
|
|
210
|
+
{
|
|
211
|
+
ref,
|
|
212
|
+
className,
|
|
213
|
+
$color: color,
|
|
214
|
+
"aria-label": ariaLabel,
|
|
215
|
+
role: ariaLabel ? "img" : void 0,
|
|
216
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconComponent, { width: size, height: size, strokeWidth: getStrokeWidth(size) })
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// src/components/icon-button/icon-button-styles.ts
|
|
222
|
+
var import_styled_components2 = __toESM(require("styled-components"), 1);
|
|
58
223
|
|
|
59
224
|
// src/tokens/colors.ts
|
|
60
225
|
var colors = {
|
|
@@ -144,54 +309,229 @@ var colors = {
|
|
|
144
309
|
"red-600": "#DC2626"
|
|
145
310
|
};
|
|
146
311
|
|
|
147
|
-
// src/components/
|
|
148
|
-
var
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
var
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
312
|
+
// src/components/icon-button/icon-button-styles.ts
|
|
313
|
+
var PADDING = {
|
|
314
|
+
short: {
|
|
315
|
+
small: "4px",
|
|
316
|
+
medium: "6px"
|
|
317
|
+
},
|
|
318
|
+
long: {
|
|
319
|
+
small: "4px 6px",
|
|
320
|
+
medium: "6px 10px"
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var BORDER_RADIUS = {
|
|
324
|
+
small: 6,
|
|
325
|
+
medium: 10
|
|
326
|
+
};
|
|
327
|
+
var Button = import_styled_components2.default.button`
|
|
328
|
+
display: inline-flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: center;
|
|
331
|
+
padding: ${({ $size, $variant }) => PADDING[$variant][$size]};
|
|
332
|
+
border: none;
|
|
333
|
+
border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;
|
|
334
|
+
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : "transparent"};
|
|
335
|
+
color: ${colors["brown-100"]};
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
transition: background-color 100ms ease;
|
|
338
|
+
|
|
339
|
+
&:hover {
|
|
340
|
+
background: ${({ $isSelected }) => $isSelected ? colors["brown-40"] : colors["brown-20"]};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
&:active {
|
|
344
|
+
background: ${colors["brown-40"]};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
&:disabled {
|
|
348
|
+
color: ${colors["brown-50"]};
|
|
349
|
+
background: transparent;
|
|
350
|
+
cursor: not-allowed;
|
|
351
|
+
}
|
|
166
352
|
`;
|
|
167
353
|
|
|
168
|
-
// src/components/
|
|
169
|
-
var
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
354
|
+
// src/components/icon-button/icon-button.tsx
|
|
355
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
356
|
+
var ICON_SIZE_BY_BUTTON_SIZE = {
|
|
357
|
+
small: 16,
|
|
358
|
+
medium: 20
|
|
359
|
+
};
|
|
360
|
+
var IconButton = (0, import_react3.forwardRef)(function IconButton2({
|
|
361
|
+
icon,
|
|
362
|
+
size = "small",
|
|
363
|
+
variant = "short",
|
|
364
|
+
isSelected = false,
|
|
365
|
+
disabled = false,
|
|
366
|
+
onClick,
|
|
367
|
+
className,
|
|
368
|
+
id,
|
|
369
|
+
as,
|
|
370
|
+
"aria-label": ariaLabel,
|
|
371
|
+
...rest
|
|
372
|
+
}, ref) {
|
|
373
|
+
const isNativeButton = as === void 0 || as === "button";
|
|
374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
375
|
+
Button,
|
|
376
|
+
{
|
|
377
|
+
ref,
|
|
378
|
+
className,
|
|
379
|
+
id,
|
|
380
|
+
as,
|
|
381
|
+
$size: size,
|
|
382
|
+
$variant: variant,
|
|
383
|
+
$isSelected: isSelected,
|
|
384
|
+
disabled,
|
|
385
|
+
onClick: disabled ? void 0 : onClick,
|
|
386
|
+
"aria-label": ariaLabel,
|
|
387
|
+
...isNativeButton ? { type: "button" } : {},
|
|
388
|
+
...rest,
|
|
389
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// src/components/ui/tooltip.tsx
|
|
395
|
+
var React = __toESM(require("react"), 1);
|
|
396
|
+
var TooltipPrimitive = __toESM(require("@radix-ui/react-tooltip"), 1);
|
|
397
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
398
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
399
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
400
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
401
|
+
var TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
402
|
+
TooltipPrimitive.Content,
|
|
403
|
+
{
|
|
404
|
+
ref,
|
|
405
|
+
sideOffset,
|
|
406
|
+
className: cn(
|
|
407
|
+
"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]",
|
|
408
|
+
className
|
|
409
|
+
),
|
|
410
|
+
...props
|
|
411
|
+
}
|
|
412
|
+
));
|
|
413
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
414
|
+
|
|
415
|
+
// src/components/artifacts/artifact-download-button/artifact-download-button.tsx
|
|
416
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
417
|
+
var DEFAULT_LABEL = "Download CSV";
|
|
418
|
+
function ArtifactDownloadButton({
|
|
419
|
+
href,
|
|
420
|
+
label = DEFAULT_LABEL,
|
|
421
|
+
onClick
|
|
422
|
+
}) {
|
|
423
|
+
if (!isSafeDownloadUrl(href)) return null;
|
|
424
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tooltip, { children: [
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
426
|
+
IconButton,
|
|
427
|
+
{
|
|
428
|
+
as: "a",
|
|
429
|
+
href,
|
|
430
|
+
download: true,
|
|
431
|
+
rel: "noopener noreferrer",
|
|
432
|
+
onClick,
|
|
433
|
+
icon: IconDownload,
|
|
434
|
+
size: "small",
|
|
435
|
+
"aria-label": label
|
|
436
|
+
}
|
|
437
|
+
) }),
|
|
438
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TooltipContent, { children: label })
|
|
439
|
+
] }) });
|
|
440
|
+
}
|
|
441
|
+
function isSafeDownloadUrl(url) {
|
|
442
|
+
try {
|
|
443
|
+
const parsed = new URL(url);
|
|
444
|
+
return parsed.protocol === "https:" || parsed.protocol === "http:";
|
|
445
|
+
} catch {
|
|
446
|
+
return false;
|
|
174
447
|
}
|
|
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
|
-
] });
|
|
183
448
|
}
|
|
184
449
|
|
|
185
|
-
// src/components/
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
450
|
+
// src/components/artifacts/status-message.tsx
|
|
451
|
+
var import_react4 = require("react");
|
|
452
|
+
function StatusMessage({ $tone, children }) {
|
|
453
|
+
return (0, import_react4.createElement)(
|
|
454
|
+
"div",
|
|
455
|
+
{
|
|
456
|
+
className: `py-6 px-2 text-center text-sm font-medium leading-5 ${$tone === "error" ? "text-red-600" : "text-brown-70"}`
|
|
457
|
+
},
|
|
458
|
+
children
|
|
459
|
+
);
|
|
191
460
|
}
|
|
192
461
|
|
|
193
|
-
// src/components/
|
|
194
|
-
var
|
|
462
|
+
// src/components/chart/bar-chart/bar-chart.tsx
|
|
463
|
+
var import_react5 = require("react");
|
|
464
|
+
var import_recharts2 = require("recharts");
|
|
465
|
+
|
|
466
|
+
// src/components/chart/chart-container.tsx
|
|
467
|
+
var import_recharts = require("recharts");
|
|
468
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
469
|
+
function ChartContainer({
|
|
470
|
+
children,
|
|
471
|
+
height = 320,
|
|
472
|
+
width = "100%",
|
|
473
|
+
className,
|
|
474
|
+
style
|
|
475
|
+
}) {
|
|
476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("text-xs text-foreground", className), style: { width, height, ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children }) });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/components/chart/chart-legend-content.tsx
|
|
480
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
481
|
+
function ChartLegendContent({ payload, className, iconType = "square" }) {
|
|
482
|
+
if (!payload || payload.length === 0) return null;
|
|
483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
484
|
+
"ul",
|
|
485
|
+
{
|
|
486
|
+
className: cn("flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-brown-80", className),
|
|
487
|
+
children: payload.map((entry, i) => {
|
|
488
|
+
const swatchColor = typeof entry.color === "string" ? entry.color : "transparent";
|
|
489
|
+
const label = entry.value !== void 0 ? String(entry.value) : "";
|
|
490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "flex items-center gap-1.5", children: [
|
|
491
|
+
iconType === "square" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-block h-2.5 w-2.5 shrink-0 rounded-sm", style: { background: swatchColor } }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-block h-[3px] w-4 shrink-0 rounded-full", style: { background: swatchColor } }),
|
|
492
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
|
|
493
|
+
] }, `${label}-${i}`);
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/components/chart/chart-tooltip-content.tsx
|
|
500
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
501
|
+
function ChartTooltipContent({
|
|
502
|
+
active,
|
|
503
|
+
payload,
|
|
504
|
+
label,
|
|
505
|
+
className,
|
|
506
|
+
showLabel = true,
|
|
507
|
+
formatValue
|
|
508
|
+
}) {
|
|
509
|
+
if (!active || !payload || payload.length === 0) return null;
|
|
510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
511
|
+
"div",
|
|
512
|
+
{
|
|
513
|
+
className: cn(
|
|
514
|
+
"rounded-lg border border-brown-40 bg-brown-10 px-3 py-2 text-xs shadow-sm",
|
|
515
|
+
"min-w-32 text-foreground",
|
|
516
|
+
className
|
|
517
|
+
),
|
|
518
|
+
children: [
|
|
519
|
+
showLabel && label !== void 0 && label !== null ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-1.5 font-medium text-brown-90", children: String(label) }) : null,
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1", children: payload.map((entry, i) => {
|
|
521
|
+
const name = entry.name !== void 0 ? String(entry.name) : "";
|
|
522
|
+
const rawValue = entry.value;
|
|
523
|
+
const display = formatValue ? formatValue(rawValue, name) : rawValue === void 0 ? "" : String(rawValue);
|
|
524
|
+
const swatch = typeof entry.color === "string" ? entry.color : "transparent";
|
|
525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
526
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block h-2.5 w-2.5 shrink-0 rounded-sm", style: { background: swatch } }),
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-brown-70", children: name }),
|
|
528
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ml-auto font-semibold tabular-nums text-brown-90", children: display })
|
|
529
|
+
] }, `${name}-${i}`);
|
|
530
|
+
}) })
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
}
|
|
195
535
|
|
|
196
536
|
// src/tokens/typography.ts
|
|
197
537
|
var fontFamilies = {
|
|
@@ -488,8 +828,1010 @@ function typographyMixin(type) {
|
|
|
488
828
|
].join(";\n ");
|
|
489
829
|
}
|
|
490
830
|
|
|
491
|
-
// src/components/
|
|
492
|
-
var
|
|
831
|
+
// src/components/chart/chart-palette.ts
|
|
832
|
+
var CHART_PALETTE = [
|
|
833
|
+
colors["blue-600"],
|
|
834
|
+
// #2563EB — primary
|
|
835
|
+
colors["blue-400"],
|
|
836
|
+
// #60A5FA — mid
|
|
837
|
+
colors["blue-300"],
|
|
838
|
+
// #93C5FD — light
|
|
839
|
+
colors["blue-100"],
|
|
840
|
+
// #DBEAFE — very light
|
|
841
|
+
colors["indigo-400"],
|
|
842
|
+
// #818CF8
|
|
843
|
+
colors["indigo-500"],
|
|
844
|
+
// #6366F1
|
|
845
|
+
colors["indigo-600"],
|
|
846
|
+
// #4F46E5
|
|
847
|
+
colors["indigo-800"],
|
|
848
|
+
// #3730A3
|
|
849
|
+
colors["blue-800"]
|
|
850
|
+
// #1E40AF — deepest
|
|
851
|
+
];
|
|
852
|
+
var OTHERS_SLICE_COLOR = colors["brown-60"];
|
|
853
|
+
var DEFAULT_OTHERS_CATEGORY_LABELS = ["others", "other"];
|
|
854
|
+
function isOthersCategory(value, labels) {
|
|
855
|
+
if (value === null || value === void 0) return false;
|
|
856
|
+
if (labels.length === 0) return false;
|
|
857
|
+
const normalized = String(value).trim().toLowerCase();
|
|
858
|
+
for (const label of labels) {
|
|
859
|
+
if (normalized === label.toLowerCase()) return true;
|
|
860
|
+
}
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
function colorForSeriesIndex(index, palette = CHART_PALETTE) {
|
|
864
|
+
return palette[index % palette.length] ?? CHART_PALETTE[0];
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/components/chart/chart-styles.ts
|
|
868
|
+
var GRID_STROKE = colors["brown-40"];
|
|
869
|
+
var AXIS_STROKE = colors["brown-50"];
|
|
870
|
+
var AXIS_TICK_STYLE = {
|
|
871
|
+
fill: colors["brown-70"],
|
|
872
|
+
fontSize: 12,
|
|
873
|
+
fontWeight: 500
|
|
874
|
+
};
|
|
875
|
+
var CHART_VALUE_LABEL_STYLE = {
|
|
876
|
+
fill: colors["brown-70"],
|
|
877
|
+
fontSize: 12,
|
|
878
|
+
fontWeight: 500
|
|
879
|
+
};
|
|
880
|
+
var BAR_TOOLTIP_CURSOR_FILL = colors["brown-alpha-12"];
|
|
881
|
+
var LINE_ACTIVE_DOT_STROKE = colors["brown-10"];
|
|
882
|
+
var CHART_MARGIN = { top: 8, right: 16, left: 0, bottom: 4 };
|
|
883
|
+
var PIE_CHART_MARGIN = { top: 0, right: 24, left: 24, bottom: 4 };
|
|
884
|
+
var CHART_AXIS_PROPS = {
|
|
885
|
+
stroke: AXIS_STROKE,
|
|
886
|
+
tick: AXIS_TICK_STYLE,
|
|
887
|
+
tickLine: false,
|
|
888
|
+
axisLine: false
|
|
889
|
+
};
|
|
890
|
+
var CHART_GRID_PROPS = {
|
|
891
|
+
stroke: GRID_STROKE,
|
|
892
|
+
strokeDasharray: "3 3",
|
|
893
|
+
vertical: false
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
// src/components/chart/chart-tick-utils.ts
|
|
897
|
+
function maxTickCharsByCategoryCount(n) {
|
|
898
|
+
if (n <= 5) return 24;
|
|
899
|
+
if (n <= 8) return 14;
|
|
900
|
+
if (n <= 12) return 10;
|
|
901
|
+
if (n <= 16) return 7;
|
|
902
|
+
return 5;
|
|
903
|
+
}
|
|
904
|
+
function truncateLabel(value, maxChars) {
|
|
905
|
+
const s = String(value ?? "");
|
|
906
|
+
if (s.length <= maxChars) return s;
|
|
907
|
+
return s.slice(0, Math.max(1, maxChars - 1)).trimEnd() + "\u2026";
|
|
908
|
+
}
|
|
909
|
+
function evenlyDistributedVisibleIndices(n, target) {
|
|
910
|
+
if (n <= target) {
|
|
911
|
+
return new Set(Array.from({ length: n }, (_, i) => i));
|
|
912
|
+
}
|
|
913
|
+
const set = /* @__PURE__ */ new Set();
|
|
914
|
+
const step = (n - 1) / (target - 1);
|
|
915
|
+
for (let i = 0; i < target; i++) {
|
|
916
|
+
set.add(Math.round(i * step));
|
|
917
|
+
}
|
|
918
|
+
return set;
|
|
919
|
+
}
|
|
920
|
+
function defaultBarTickFormatter(categoryCount) {
|
|
921
|
+
const max = maxTickCharsByCategoryCount(categoryCount);
|
|
922
|
+
return (value) => truncateLabel(value, max);
|
|
923
|
+
}
|
|
924
|
+
function defaultLineChartTickFormatter(categoryCount, visibleIndices) {
|
|
925
|
+
const max = maxTickCharsByCategoryCount(visibleIndices.size || categoryCount);
|
|
926
|
+
return (value, index) => {
|
|
927
|
+
if (!visibleIndices.has(index)) return "";
|
|
928
|
+
return truncateLabel(value, max);
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/components/chart/coerce-numeric.ts
|
|
933
|
+
function coerceNumericColumns(rows, numericKeys) {
|
|
934
|
+
return rows.map((row) => {
|
|
935
|
+
const out = { ...row };
|
|
936
|
+
for (const k of numericKeys) {
|
|
937
|
+
const v = out[k];
|
|
938
|
+
if (v == null) continue;
|
|
939
|
+
const n = Number(v);
|
|
940
|
+
out[k] = Number.isFinite(n) ? n : NaN;
|
|
941
|
+
}
|
|
942
|
+
return out;
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// src/components/chart/bar-chart/bar-chart.tsx
|
|
947
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
948
|
+
function BarChart({
|
|
949
|
+
data,
|
|
950
|
+
xKey,
|
|
951
|
+
series,
|
|
952
|
+
stacked = false,
|
|
953
|
+
height = 320,
|
|
954
|
+
width = "100%",
|
|
955
|
+
className,
|
|
956
|
+
palette = CHART_PALETTE,
|
|
957
|
+
showTooltip = true,
|
|
958
|
+
showLegend,
|
|
959
|
+
showGrid = true,
|
|
960
|
+
showXAxis = true,
|
|
961
|
+
showYAxis = true,
|
|
962
|
+
showValueLabels,
|
|
963
|
+
xAxisInterval = 0,
|
|
964
|
+
xAxisAngle,
|
|
965
|
+
xAxisTickFormatter
|
|
966
|
+
}) {
|
|
967
|
+
const seriesKeys = (0, import_react5.useMemo)(() => series.map((s) => s.key), [series]);
|
|
968
|
+
const coercedData = (0, import_react5.useMemo)(
|
|
969
|
+
() => coerceNumericColumns(data, seriesKeys),
|
|
970
|
+
[data, seriesKeys]
|
|
971
|
+
);
|
|
972
|
+
const resolvedShowLegend = showLegend ?? series.length > 1;
|
|
973
|
+
const resolvedShowValueLabels = showValueLabels ?? (!stacked && series.length === 1);
|
|
974
|
+
const resolvedAngle = xAxisAngle ?? (coercedData.length >= 8 ? -30 : 0);
|
|
975
|
+
const resolvedTickFormatter = (0, import_react5.useMemo)(
|
|
976
|
+
() => xAxisTickFormatter ?? defaultBarTickFormatter(coercedData.length),
|
|
977
|
+
[xAxisTickFormatter, coercedData.length]
|
|
978
|
+
);
|
|
979
|
+
const angledLabelPad = resolvedAngle === 0 ? 0 : Math.min(Math.abs(resolvedAngle) * 0.7, 40);
|
|
980
|
+
const chartMargin = { ...CHART_MARGIN, bottom: CHART_MARGIN.bottom + angledLabelPad };
|
|
981
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_recharts2.BarChart, { data: coercedData, margin: chartMargin, barCategoryGap: "22%", children: [
|
|
982
|
+
showGrid ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
|
|
983
|
+
showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
984
|
+
import_recharts2.XAxis,
|
|
985
|
+
{
|
|
986
|
+
dataKey: xKey,
|
|
987
|
+
...CHART_AXIS_PROPS,
|
|
988
|
+
interval: xAxisInterval,
|
|
989
|
+
angle: resolvedAngle,
|
|
990
|
+
textAnchor: resolvedAngle !== 0 ? "end" : "middle",
|
|
991
|
+
tickMargin: resolvedAngle === 0 ? 8 : 12,
|
|
992
|
+
height: resolvedAngle !== 0 ? 60 : void 0,
|
|
993
|
+
tickFormatter: resolvedTickFormatter
|
|
994
|
+
}
|
|
995
|
+
) : null,
|
|
996
|
+
showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
|
|
997
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.Tooltip, { cursor: { fill: BAR_TOOLTIP_CURSOR_FILL }, content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartTooltipContent, {}) }) : null,
|
|
998
|
+
resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
999
|
+
import_recharts2.Legend,
|
|
1000
|
+
{
|
|
1001
|
+
verticalAlign: "top",
|
|
1002
|
+
align: "center",
|
|
1003
|
+
wrapperStyle: { top: 0 },
|
|
1004
|
+
itemSorter: null,
|
|
1005
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChartLegendContent, { iconType: "square" })
|
|
1006
|
+
}
|
|
1007
|
+
) : null,
|
|
1008
|
+
series.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1009
|
+
import_recharts2.Bar,
|
|
1010
|
+
{
|
|
1011
|
+
dataKey: s.key,
|
|
1012
|
+
name: s.label,
|
|
1013
|
+
fill: colorForSeriesIndex(i, palette),
|
|
1014
|
+
stackId: stacked ? "default" : s.key,
|
|
1015
|
+
radius: barRadius(stacked, i, series.length),
|
|
1016
|
+
children: resolvedShowValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_recharts2.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
|
|
1017
|
+
},
|
|
1018
|
+
s.key
|
|
1019
|
+
))
|
|
1020
|
+
] }) });
|
|
1021
|
+
}
|
|
1022
|
+
function barRadius(stacked, seriesIndex, seriesCount) {
|
|
1023
|
+
if (!stacked) return [6, 6, 0, 0];
|
|
1024
|
+
const isTopSegment = seriesIndex === seriesCount - 1;
|
|
1025
|
+
return isTopSegment ? [6, 6, 0, 0] : [0, 0, 0, 0];
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/constants.ts
|
|
1029
|
+
var VARIANT_CHART_HEIGHT = "100%";
|
|
1030
|
+
|
|
1031
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-component.tsx
|
|
1032
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1033
|
+
function BarChartVariantComponent({ config, rows }) {
|
|
1034
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1035
|
+
BarChart,
|
|
1036
|
+
{
|
|
1037
|
+
data: rows,
|
|
1038
|
+
xKey: config.xKey,
|
|
1039
|
+
series: config.series,
|
|
1040
|
+
stacked: config.stacked,
|
|
1041
|
+
height: VARIANT_CHART_HEIGHT
|
|
1042
|
+
}
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/shared-column-keys.ts
|
|
1047
|
+
function seriesBasedColumnKeys(config) {
|
|
1048
|
+
return [config.xKey, ...config.series.map((s) => s.key)];
|
|
1049
|
+
}
|
|
1050
|
+
function pieColumnKeys(config) {
|
|
1051
|
+
return [config.categoryKey, config.valueKey];
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/shared-validators.ts
|
|
1055
|
+
function isValidChartSeries(series) {
|
|
1056
|
+
if (!series || typeof series !== "object") return false;
|
|
1057
|
+
const candidate = series;
|
|
1058
|
+
if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
|
|
1059
|
+
if (typeof candidate.label !== "string") return false;
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/bar-chart-variant-service.tsx
|
|
1064
|
+
function isValidBarChartConfig(config) {
|
|
1065
|
+
if (!config || typeof config !== "object") return false;
|
|
1066
|
+
const candidate = config;
|
|
1067
|
+
if (candidate.variant !== "BAR") return false;
|
|
1068
|
+
if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
|
|
1069
|
+
if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
|
|
1070
|
+
if (!candidate.series.every(isValidChartSeries)) return false;
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
var BarChartVariantService = {
|
|
1074
|
+
variant: "BAR",
|
|
1075
|
+
isValidConfig: isValidBarChartConfig,
|
|
1076
|
+
columnKeys: seriesBasedColumnKeys,
|
|
1077
|
+
Component: BarChartVariantComponent
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
// src/components/chart/line-chart/line-chart.tsx
|
|
1081
|
+
var import_react6 = require("react");
|
|
1082
|
+
var import_recharts3 = require("recharts");
|
|
1083
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1084
|
+
var TARGET_VISIBLE_LABELS = 5;
|
|
1085
|
+
function LineChart({
|
|
1086
|
+
data,
|
|
1087
|
+
xKey,
|
|
1088
|
+
series,
|
|
1089
|
+
smooth = true,
|
|
1090
|
+
showDots = true,
|
|
1091
|
+
strokeWidth = 2,
|
|
1092
|
+
height = 320,
|
|
1093
|
+
width = "100%",
|
|
1094
|
+
className,
|
|
1095
|
+
palette = CHART_PALETTE,
|
|
1096
|
+
showTooltip = true,
|
|
1097
|
+
showLegend,
|
|
1098
|
+
showGrid = true,
|
|
1099
|
+
showXAxis = true,
|
|
1100
|
+
showYAxis = true,
|
|
1101
|
+
showValueLabels = false,
|
|
1102
|
+
xAxisInterval = 0,
|
|
1103
|
+
xAxisAngle = 0,
|
|
1104
|
+
xAxisTickFormatter
|
|
1105
|
+
}) {
|
|
1106
|
+
const seriesKeys = (0, import_react6.useMemo)(() => series.map((s) => s.key), [series]);
|
|
1107
|
+
const coercedData = (0, import_react6.useMemo)(
|
|
1108
|
+
() => coerceNumericColumns(data, seriesKeys),
|
|
1109
|
+
[data, seriesKeys]
|
|
1110
|
+
);
|
|
1111
|
+
const lineType = smooth ? "monotone" : "linear";
|
|
1112
|
+
const resolvedShowLegend = showLegend ?? series.length > 1;
|
|
1113
|
+
const resolvedTickFormatter = (0, import_react6.useMemo)(() => {
|
|
1114
|
+
if (xAxisTickFormatter) return xAxisTickFormatter;
|
|
1115
|
+
const visible = evenlyDistributedVisibleIndices(coercedData.length, TARGET_VISIBLE_LABELS);
|
|
1116
|
+
return defaultLineChartTickFormatter(coercedData.length, visible);
|
|
1117
|
+
}, [xAxisTickFormatter, coercedData.length]);
|
|
1118
|
+
const angledLabelPad = xAxisAngle === 0 ? 0 : Math.min(Math.abs(xAxisAngle) * 0.7, 40);
|
|
1119
|
+
const chartMargin = { ...CHART_MARGIN, right: CHART_MARGIN.right + 16, bottom: CHART_MARGIN.bottom + angledLabelPad };
|
|
1120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_recharts3.LineChart, { data: coercedData, margin: chartMargin, children: [
|
|
1121
|
+
showGrid ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.CartesianGrid, { ...CHART_GRID_PROPS }) : null,
|
|
1122
|
+
showXAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1123
|
+
import_recharts3.XAxis,
|
|
1124
|
+
{
|
|
1125
|
+
dataKey: xKey,
|
|
1126
|
+
...CHART_AXIS_PROPS,
|
|
1127
|
+
interval: xAxisInterval,
|
|
1128
|
+
angle: xAxisAngle,
|
|
1129
|
+
textAnchor: xAxisAngle !== 0 ? "end" : "middle",
|
|
1130
|
+
tickMargin: xAxisAngle === 0 ? 8 : 12,
|
|
1131
|
+
height: xAxisAngle !== 0 ? 60 : void 0,
|
|
1132
|
+
tickFormatter: resolvedTickFormatter
|
|
1133
|
+
}
|
|
1134
|
+
) : null,
|
|
1135
|
+
showYAxis ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.YAxis, { ...CHART_AXIS_PROPS, width: 40 }) : null,
|
|
1136
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartTooltipContent, {}) }) : null,
|
|
1137
|
+
resolvedShowLegend ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1138
|
+
import_recharts3.Legend,
|
|
1139
|
+
{
|
|
1140
|
+
verticalAlign: "top",
|
|
1141
|
+
align: "center",
|
|
1142
|
+
wrapperStyle: { top: 0 },
|
|
1143
|
+
itemSorter: null,
|
|
1144
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChartLegendContent, { iconType: "line" })
|
|
1145
|
+
}
|
|
1146
|
+
) : null,
|
|
1147
|
+
series.map((s, i) => {
|
|
1148
|
+
const color = colorForSeriesIndex(i, palette);
|
|
1149
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1150
|
+
import_recharts3.Line,
|
|
1151
|
+
{
|
|
1152
|
+
type: lineType,
|
|
1153
|
+
dataKey: s.key,
|
|
1154
|
+
name: s.label,
|
|
1155
|
+
stroke: color,
|
|
1156
|
+
strokeWidth,
|
|
1157
|
+
dot: showDots ? { r: 3, fill: color, stroke: color, strokeWidth: 0 } : false,
|
|
1158
|
+
activeDot: { r: 5, fill: color, stroke: LINE_ACTIVE_DOT_STROKE, strokeWidth: 2 },
|
|
1159
|
+
children: showValueLabels ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_recharts3.LabelList, { dataKey: s.key, position: "top", style: CHART_VALUE_LABEL_STYLE }) : null
|
|
1160
|
+
},
|
|
1161
|
+
s.key
|
|
1162
|
+
);
|
|
1163
|
+
})
|
|
1164
|
+
] }) });
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-component.tsx
|
|
1168
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1169
|
+
function LineChartVariantComponent({ config, rows }) {
|
|
1170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LineChart, { data: rows, xKey: config.xKey, series: config.series, height: VARIANT_CHART_HEIGHT });
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/line-chart-variant-service.tsx
|
|
1174
|
+
function isValidLineChartConfig(config) {
|
|
1175
|
+
if (!config || typeof config !== "object") return false;
|
|
1176
|
+
const candidate = config;
|
|
1177
|
+
if (candidate.variant !== "LINE") return false;
|
|
1178
|
+
if (typeof candidate.xKey !== "string" || candidate.xKey.length === 0) return false;
|
|
1179
|
+
if (!Array.isArray(candidate.series) || candidate.series.length === 0) return false;
|
|
1180
|
+
if (!candidate.series.every(isValidChartSeries)) return false;
|
|
1181
|
+
return true;
|
|
1182
|
+
}
|
|
1183
|
+
var LineChartVariantService = {
|
|
1184
|
+
variant: "LINE",
|
|
1185
|
+
isValidConfig: isValidLineChartConfig,
|
|
1186
|
+
columnKeys: seriesBasedColumnKeys,
|
|
1187
|
+
Component: LineChartVariantComponent
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// src/components/chart/pie-chart/pie-chart.tsx
|
|
1191
|
+
var import_react7 = require("react");
|
|
1192
|
+
var import_recharts4 = require("recharts");
|
|
1193
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1194
|
+
function PieChart({
|
|
1195
|
+
data,
|
|
1196
|
+
categoryKey,
|
|
1197
|
+
valueKey,
|
|
1198
|
+
height = 320,
|
|
1199
|
+
width = "100%",
|
|
1200
|
+
className,
|
|
1201
|
+
palette = CHART_PALETTE,
|
|
1202
|
+
showLabels = true,
|
|
1203
|
+
showTooltip = true,
|
|
1204
|
+
showLegend = true,
|
|
1205
|
+
innerRadius = 0,
|
|
1206
|
+
outerRadius = "68%",
|
|
1207
|
+
othersCategoryLabels = DEFAULT_OTHERS_CATEGORY_LABELS
|
|
1208
|
+
}) {
|
|
1209
|
+
const { orderedData, sliceColors } = (0, import_react7.useMemo)(() => {
|
|
1210
|
+
const coerced = coerceNumericColumns(data, [valueKey]);
|
|
1211
|
+
const namedRows = [];
|
|
1212
|
+
const namedColors = [];
|
|
1213
|
+
const othersRows = [];
|
|
1214
|
+
const othersColors = [];
|
|
1215
|
+
for (const row of coerced) {
|
|
1216
|
+
if (isOthersCategory(row[categoryKey], othersCategoryLabels)) {
|
|
1217
|
+
othersRows.push(row);
|
|
1218
|
+
othersColors.push(OTHERS_SLICE_COLOR);
|
|
1219
|
+
} else {
|
|
1220
|
+
namedRows.push(row);
|
|
1221
|
+
namedColors.push(colorForSeriesIndex(namedRows.length - 1, palette));
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
orderedData: [...namedRows, ...othersRows],
|
|
1226
|
+
sliceColors: [...namedColors, ...othersColors]
|
|
1227
|
+
};
|
|
1228
|
+
}, [data, valueKey, categoryKey, othersCategoryLabels, palette]);
|
|
1229
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartContainer, { height, width, className, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_recharts4.PieChart, { margin: PIE_CHART_MARGIN, children: [
|
|
1230
|
+
showTooltip ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartTooltipContent, {}) }) : null,
|
|
1231
|
+
showLegend ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1232
|
+
import_recharts4.Legend,
|
|
1233
|
+
{
|
|
1234
|
+
verticalAlign: "top",
|
|
1235
|
+
align: "center",
|
|
1236
|
+
wrapperStyle: { top: 0 },
|
|
1237
|
+
itemSorter: null,
|
|
1238
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChartLegendContent, { iconType: "square" })
|
|
1239
|
+
}
|
|
1240
|
+
) : null,
|
|
1241
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1242
|
+
import_recharts4.Pie,
|
|
1243
|
+
{
|
|
1244
|
+
data: orderedData,
|
|
1245
|
+
dataKey: valueKey,
|
|
1246
|
+
nameKey: categoryKey,
|
|
1247
|
+
cx: "50%",
|
|
1248
|
+
cy: "52%",
|
|
1249
|
+
startAngle: 90,
|
|
1250
|
+
endAngle: -270,
|
|
1251
|
+
innerRadius,
|
|
1252
|
+
outerRadius,
|
|
1253
|
+
label: showLabels ? (entry) => {
|
|
1254
|
+
const v = entry.value;
|
|
1255
|
+
if (v === void 0 || v === null) return null;
|
|
1256
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1257
|
+
"text",
|
|
1258
|
+
{
|
|
1259
|
+
x: entry.x,
|
|
1260
|
+
y: entry.y,
|
|
1261
|
+
fill: CHART_VALUE_LABEL_STYLE.fill,
|
|
1262
|
+
fontSize: CHART_VALUE_LABEL_STYLE.fontSize,
|
|
1263
|
+
fontWeight: CHART_VALUE_LABEL_STYLE.fontWeight,
|
|
1264
|
+
textAnchor: entry.textAnchor,
|
|
1265
|
+
dominantBaseline: "central",
|
|
1266
|
+
children: String(v)
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
} : false,
|
|
1270
|
+
labelLine: false,
|
|
1271
|
+
stroke: "none",
|
|
1272
|
+
strokeWidth: 0,
|
|
1273
|
+
children: orderedData.map((_row, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_recharts4.Cell, { fill: sliceColors[i] }, i))
|
|
1274
|
+
}
|
|
1275
|
+
)
|
|
1276
|
+
] }) });
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-component.tsx
|
|
1280
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1281
|
+
function PieChartVariantComponent({
|
|
1282
|
+
config,
|
|
1283
|
+
rows,
|
|
1284
|
+
labels
|
|
1285
|
+
}) {
|
|
1286
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1287
|
+
PieChart,
|
|
1288
|
+
{
|
|
1289
|
+
data: rows,
|
|
1290
|
+
categoryKey: config.categoryKey,
|
|
1291
|
+
valueKey: config.valueKey,
|
|
1292
|
+
othersCategoryLabels: labels.othersCategoryLabels,
|
|
1293
|
+
height: VARIANT_CHART_HEIGHT
|
|
1294
|
+
}
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/pie-chart-variant-service.tsx
|
|
1299
|
+
function isValidPieChartConfig(config) {
|
|
1300
|
+
if (!config || typeof config !== "object") return false;
|
|
1301
|
+
const candidate = config;
|
|
1302
|
+
if (candidate.variant !== "PIE") return false;
|
|
1303
|
+
if (typeof candidate.categoryKey !== "string" || candidate.categoryKey.length === 0) return false;
|
|
1304
|
+
if (typeof candidate.valueKey !== "string" || candidate.valueKey.length === 0) return false;
|
|
1305
|
+
if (!Array.isArray(candidate.series)) return false;
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
var PieChartVariantService = {
|
|
1309
|
+
variant: "PIE",
|
|
1310
|
+
isValidConfig: isValidPieChartConfig,
|
|
1311
|
+
columnKeys: pieColumnKeys,
|
|
1312
|
+
Component: PieChartVariantComponent
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
// src/components/artifacts/chart-artifact/chart-variant-services/index.ts
|
|
1316
|
+
var ChartVariantServices = {
|
|
1317
|
+
LINE: LineChartVariantService,
|
|
1318
|
+
BAR: BarChartVariantService,
|
|
1319
|
+
PIE: PieChartVariantService
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
// src/components/artifacts/chart-artifact/chart-artifact-content.tsx
|
|
1323
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1324
|
+
function ChartArtifactContent({ config, rows, labels }) {
|
|
1325
|
+
const service = ChartVariantServices[config.variant];
|
|
1326
|
+
if (!service) {
|
|
1327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1328
|
+
}
|
|
1329
|
+
const Component = service.Component;
|
|
1330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Component, { config, rows, labels });
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// src/components/artifacts/parse-csv.ts
|
|
1334
|
+
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
1335
|
+
function parseCsv(text, columns) {
|
|
1336
|
+
const result = import_papaparse.default.parse(text, {
|
|
1337
|
+
header: true,
|
|
1338
|
+
skipEmptyLines: true,
|
|
1339
|
+
dynamicTyping: false
|
|
1340
|
+
});
|
|
1341
|
+
const csvHeaders = new Set(result.meta?.fields ?? []);
|
|
1342
|
+
const presentColumns = columns.filter((col) => csvHeaders.has(col.key));
|
|
1343
|
+
if (presentColumns.length === 0) return [];
|
|
1344
|
+
return result.data.map((rawRow) => {
|
|
1345
|
+
const row = {};
|
|
1346
|
+
for (const column of presentColumns) {
|
|
1347
|
+
const value = rawRow[column.key];
|
|
1348
|
+
row[column.key] = value === void 0 || value === "" ? null : value;
|
|
1349
|
+
}
|
|
1350
|
+
return row;
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// src/components/artifacts/use-rows-artifact-data.ts
|
|
1355
|
+
var import_react8 = require("react");
|
|
1356
|
+
var LOADING = { status: "loading", rows: null, error: null };
|
|
1357
|
+
var UNAVAILABLE = { status: "unavailable", rows: null, error: null };
|
|
1358
|
+
var INITIAL_INTERNAL = { cacheKey: null, data: LOADING };
|
|
1359
|
+
function columnsFingerprint(columns) {
|
|
1360
|
+
return columns.map((c) => c.key).join("|");
|
|
1361
|
+
}
|
|
1362
|
+
function scrubUrl(url) {
|
|
1363
|
+
const q = url.indexOf("?");
|
|
1364
|
+
return q === -1 ? url : `${url.slice(0, q)}?<redacted>`;
|
|
1365
|
+
}
|
|
1366
|
+
function useRowsArtifactData(presignedUrl, columns, parse) {
|
|
1367
|
+
const colsKey = (0, import_react8.useMemo)(() => columnsFingerprint(columns), [columns]);
|
|
1368
|
+
const safeUrl = presignedUrl && isSafeDownloadUrl(presignedUrl) ? presignedUrl : null;
|
|
1369
|
+
const cacheKey = safeUrl ? `${safeUrl}::${colsKey}` : null;
|
|
1370
|
+
const [state, setState] = (0, import_react8.useState)(INITIAL_INTERNAL);
|
|
1371
|
+
(0, import_react8.useEffect)(() => {
|
|
1372
|
+
if (!safeUrl) return;
|
|
1373
|
+
const controller = new AbortController();
|
|
1374
|
+
fetch(safeUrl, {
|
|
1375
|
+
signal: controller.signal,
|
|
1376
|
+
headers: { Accept: "text/csv" },
|
|
1377
|
+
// Presigned URLs are cross-origin S3 in production and don't need credentials. Explicit
|
|
1378
|
+
// omit so a same-origin URL (compromised payload, local dev) can't accidentally receive
|
|
1379
|
+
// session cookies or auth headers.
|
|
1380
|
+
credentials: "omit"
|
|
1381
|
+
}).then(async (response) => {
|
|
1382
|
+
if (!response.ok) throw new Error(`Failed to load artifact data (${response.status})`);
|
|
1383
|
+
return response.text();
|
|
1384
|
+
}).then((text) => {
|
|
1385
|
+
if (controller.signal.aborted) return;
|
|
1386
|
+
try {
|
|
1387
|
+
const rows = parse(text, columns);
|
|
1388
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "ready", rows, error: null } });
|
|
1389
|
+
} catch (parseError) {
|
|
1390
|
+
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
|
|
1391
|
+
console.error("[ArtifactData] CSV parse failed:", err.message);
|
|
1392
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
1393
|
+
}
|
|
1394
|
+
}).catch((error) => {
|
|
1395
|
+
if (controller.signal.aborted) return;
|
|
1396
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1397
|
+
console.error(`[ArtifactData] Fetch failed [${scrubUrl(safeUrl)}]:`, err.message);
|
|
1398
|
+
setState({ cacheKey: `${safeUrl}::${colsKey}`, data: { status: "error", rows: null, error: err } });
|
|
1399
|
+
});
|
|
1400
|
+
return () => {
|
|
1401
|
+
controller.abort();
|
|
1402
|
+
};
|
|
1403
|
+
}, [safeUrl, colsKey]);
|
|
1404
|
+
if (!safeUrl) return UNAVAILABLE;
|
|
1405
|
+
if (state.cacheKey !== cacheKey) return LOADING;
|
|
1406
|
+
return state.data;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/components/artifacts/chart-artifact/use-chart-artifact-data.ts
|
|
1410
|
+
function useChartArtifactData(presignedUrl, config) {
|
|
1411
|
+
const service = ChartVariantServices[config.variant];
|
|
1412
|
+
const keys = service ? service.columnKeys(config) : [];
|
|
1413
|
+
const columns = keys.map((key) => ({ key }));
|
|
1414
|
+
return useRowsArtifactData(presignedUrl, columns, parseCsv);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/components/artifacts/chart-artifact/validate.ts
|
|
1418
|
+
function isValidChartConfig(config) {
|
|
1419
|
+
if (!config || typeof config !== "object") return false;
|
|
1420
|
+
const variant = config.variant;
|
|
1421
|
+
if (typeof variant !== "string") return false;
|
|
1422
|
+
const service = ChartVariantServices[variant];
|
|
1423
|
+
if (!service) return false;
|
|
1424
|
+
return service.isValidConfig(config);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/components/artifacts/chart-artifact/chart-artifact.tsx
|
|
1428
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1429
|
+
var TITLE_CLASSES = "text-sm font-semibold text-foreground";
|
|
1430
|
+
var CHART_HEIGHT = 320;
|
|
1431
|
+
function ChartArtifact({ artifact, labels, callbacks }) {
|
|
1432
|
+
const validConfig = isValidChartConfig(artifact.config);
|
|
1433
|
+
(0, import_react9.useEffect)(() => {
|
|
1434
|
+
if (validConfig) return;
|
|
1435
|
+
console.warn("[clay] ChartArtifact: malformed `config` \u2014 skipping render.");
|
|
1436
|
+
}, [validConfig]);
|
|
1437
|
+
const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
|
|
1438
|
+
const resolvedLabels = (0, import_react9.useMemo)(
|
|
1439
|
+
() => ({ ...DEFAULT_CHART_ARTIFACT_LABELS, ...labels?.[artifactTypes.CHART] ?? {} }),
|
|
1440
|
+
[labels]
|
|
1441
|
+
);
|
|
1442
|
+
const data = useChartArtifactData(
|
|
1443
|
+
validConfig ? presignedUrl : null,
|
|
1444
|
+
validConfig ? artifact.config : { variant: "LINE", series: [], xKey: "x" }
|
|
1445
|
+
);
|
|
1446
|
+
const onDownload = callbacks?.[artifactTypes.CHART]?.onDownload;
|
|
1447
|
+
const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
|
|
1448
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1450
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactTitle, { className: TITLE_CLASSES, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", {}),
|
|
1451
|
+
presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactDownloadButton, { href: presignedUrl, label: resolvedLabels.download, onClick: handleDownloadClick }) }) : null
|
|
1452
|
+
] }),
|
|
1453
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1454
|
+
ArtifactContent,
|
|
1455
|
+
{
|
|
1456
|
+
className: "p-4 overflow-visible flex flex-none items-center justify-center",
|
|
1457
|
+
style: { height: CHART_HEIGHT },
|
|
1458
|
+
children: renderBody(validConfig, artifact, data, resolvedLabels)
|
|
1459
|
+
}
|
|
1460
|
+
)
|
|
1461
|
+
] });
|
|
1462
|
+
}
|
|
1463
|
+
function renderBody(validConfig, artifact, data, labels) {
|
|
1464
|
+
if (!validConfig) {
|
|
1465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1466
|
+
}
|
|
1467
|
+
if (data.status === "error") {
|
|
1468
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
|
|
1469
|
+
}
|
|
1470
|
+
if (data.status === "unavailable") {
|
|
1471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1472
|
+
}
|
|
1473
|
+
if (data.status === "loading") {
|
|
1474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
|
|
1475
|
+
}
|
|
1476
|
+
if (data.rows.length === 0) {
|
|
1477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusMessage, { $tone: "info", children: labels.empty });
|
|
1478
|
+
}
|
|
1479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChartArtifactContent, { config: artifact.config, rows: data.rows, labels });
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// src/components/artifacts/chart-artifact/to-clipboard-text.ts
|
|
1483
|
+
function chartArtifactToClipboardText(artifact) {
|
|
1484
|
+
const trimmedTitle = artifact.title?.trim();
|
|
1485
|
+
return trimmedTitle || "Chart";
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// src/components/artifacts/artifact-services/chart-artifact-service.ts
|
|
1489
|
+
var ChartArtifactService = {
|
|
1490
|
+
type: artifactTypes.CHART,
|
|
1491
|
+
Component: ChartArtifact,
|
|
1492
|
+
toClipboardText: chartArtifactToClipboardText
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
1496
|
+
var import_react11 = require("react");
|
|
1497
|
+
|
|
1498
|
+
// src/components/ui/table.tsx
|
|
1499
|
+
var React2 = __toESM(require("react"), 1);
|
|
1500
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1501
|
+
var Table = React2.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: cn("relative w-full overflow-auto", containerClassName), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("table", { ref, className: cn("w-full caption-bottom text-sm", className), ...props }) }));
|
|
1502
|
+
Table.displayName = "Table";
|
|
1503
|
+
var TableHeader = React2.forwardRef(
|
|
1504
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { ref, className: cn("[&_tr]:border-b", className), ...props })
|
|
1505
|
+
);
|
|
1506
|
+
TableHeader.displayName = "TableHeader";
|
|
1507
|
+
var TableBody = React2.forwardRef(
|
|
1508
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props })
|
|
1509
|
+
);
|
|
1510
|
+
TableBody.displayName = "TableBody";
|
|
1511
|
+
var TableFooter = React2.forwardRef(
|
|
1512
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tfoot", { ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })
|
|
1513
|
+
);
|
|
1514
|
+
TableFooter.displayName = "TableFooter";
|
|
1515
|
+
var TableRow = React2.forwardRef(
|
|
1516
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1517
|
+
"tr",
|
|
1518
|
+
{
|
|
1519
|
+
ref,
|
|
1520
|
+
className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
|
|
1521
|
+
...props
|
|
1522
|
+
}
|
|
1523
|
+
)
|
|
1524
|
+
);
|
|
1525
|
+
TableRow.displayName = "TableRow";
|
|
1526
|
+
var TableHead = React2.forwardRef(
|
|
1527
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1528
|
+
"th",
|
|
1529
|
+
{
|
|
1530
|
+
ref,
|
|
1531
|
+
className: cn(
|
|
1532
|
+
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
1533
|
+
className
|
|
1534
|
+
),
|
|
1535
|
+
...props
|
|
1536
|
+
}
|
|
1537
|
+
)
|
|
1538
|
+
);
|
|
1539
|
+
TableHead.displayName = "TableHead";
|
|
1540
|
+
var TableCell = React2.forwardRef(
|
|
1541
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className), ...props })
|
|
1542
|
+
);
|
|
1543
|
+
TableCell.displayName = "TableCell";
|
|
1544
|
+
var TableCaption = React2.forwardRef(
|
|
1545
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })
|
|
1546
|
+
);
|
|
1547
|
+
TableCaption.displayName = "TableCaption";
|
|
1548
|
+
|
|
1549
|
+
// src/components/table/use-scroll-shadow.ts
|
|
1550
|
+
var import_react10 = require("react");
|
|
1551
|
+
function useScrollShadow() {
|
|
1552
|
+
const targetRef = (0, import_react10.useRef)(null);
|
|
1553
|
+
const [isScrolled, setIsScrolled] = (0, import_react10.useState)(false);
|
|
1554
|
+
(0, import_react10.useEffect)(() => {
|
|
1555
|
+
const target = targetRef.current;
|
|
1556
|
+
if (!target) return;
|
|
1557
|
+
let scrollContainer = target;
|
|
1558
|
+
while (scrollContainer) {
|
|
1559
|
+
const overflowY = getComputedStyle(scrollContainer).overflowY;
|
|
1560
|
+
if (overflowY === "auto" || overflowY === "scroll") break;
|
|
1561
|
+
scrollContainer = scrollContainer.parentElement;
|
|
1562
|
+
}
|
|
1563
|
+
if (!scrollContainer) return;
|
|
1564
|
+
setIsScrolled(scrollContainer.scrollTop > 0);
|
|
1565
|
+
const handleScroll = () => {
|
|
1566
|
+
if (!scrollContainer) return;
|
|
1567
|
+
setIsScrolled(scrollContainer.scrollTop > 0);
|
|
1568
|
+
};
|
|
1569
|
+
scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
|
|
1570
|
+
return () => scrollContainer.removeEventListener("scroll", handleScroll);
|
|
1571
|
+
}, []);
|
|
1572
|
+
return { targetRef, isScrolled };
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// src/components/artifacts/table-artifact/table-artifact-content.constants.ts
|
|
1576
|
+
var TABLE_STATUS = {
|
|
1577
|
+
LOADING: "loading",
|
|
1578
|
+
READY: "ready",
|
|
1579
|
+
ERROR: "error",
|
|
1580
|
+
UNAVAILABLE: "unavailable"
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
// src/components/artifacts/table-artifact/table-artifact-content.utils.ts
|
|
1584
|
+
function formatCell(row, column) {
|
|
1585
|
+
return row[column.key] ?? "";
|
|
1586
|
+
}
|
|
1587
|
+
var HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
|
|
1588
|
+
var CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
|
|
1589
|
+
var ROW_CLASSES = "border-b-brown-30 bg-background/70 hover:bg-secondary";
|
|
1590
|
+
var DATA_ROW_CLASSES = `${ROW_CLASSES} animate-in fade-in duration-300`;
|
|
1591
|
+
var SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
|
|
1592
|
+
|
|
1593
|
+
// src/components/artifacts/table-artifact/table-artifact-content.tsx
|
|
1594
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1595
|
+
function TableArtifactContent({
|
|
1596
|
+
columns,
|
|
1597
|
+
data,
|
|
1598
|
+
knownRowCount,
|
|
1599
|
+
maxShimmerRows,
|
|
1600
|
+
maxBodyHeight,
|
|
1601
|
+
labels
|
|
1602
|
+
}) {
|
|
1603
|
+
if (data.status === TABLE_STATUS.ERROR) {
|
|
1604
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "error", children: labels.loadError });
|
|
1605
|
+
}
|
|
1606
|
+
if (data.status === TABLE_STATUS.UNAVAILABLE) {
|
|
1607
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.unavailable });
|
|
1608
|
+
}
|
|
1609
|
+
if (data.status === TABLE_STATUS.LOADING) {
|
|
1610
|
+
if (typeof knownRowCount === "number" && knownRowCount > 0) {
|
|
1611
|
+
const shimmerCount = Math.min(knownRowCount, maxShimmerRows);
|
|
1612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: renderShimmerRows(columns, shimmerCount) });
|
|
1613
|
+
}
|
|
1614
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StatusMessage, { $tone: "info", children: labels.loading });
|
|
1615
|
+
}
|
|
1616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactTable, { columns, maxBodyHeight, children: data.rows.length === 0 ? renderEmptyRow(columns.length, labels.empty) : renderDataRows(columns, data.rows) });
|
|
1617
|
+
}
|
|
1618
|
+
function ArtifactTable({ columns, maxBodyHeight, children }) {
|
|
1619
|
+
const { targetRef, isScrolled } = useScrollShadow();
|
|
1620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: targetRef, className: "overscroll-none", style: { maxHeight: maxBodyHeight, overflowY: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Table, { containerClassName: "overflow-visible", children: [
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1622
|
+
TableHeader,
|
|
1623
|
+
{
|
|
1624
|
+
"data-scrolled": isScrolled ? "true" : void 0,
|
|
1625
|
+
className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
|
|
1626
|
+
style: isScrolled ? { boxShadow: SCROLL_SHADOW } : void 0,
|
|
1627
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: "hover:bg-transparent", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableHead, { scope: "col", className: HEAD_CLASS, children: column.label }, column.key)) })
|
|
1628
|
+
}
|
|
1629
|
+
),
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableBody, { children })
|
|
1631
|
+
] }) });
|
|
1632
|
+
}
|
|
1633
|
+
function renderShimmerRows(columns, count) {
|
|
1634
|
+
return Array.from({ length: count }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: ROW_CLASSES, "aria-hidden": "true", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { className: CELL_CLASS, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block h-3 w-3/4 animate-pulse rounded bg-muted-foreground/20" }) }, column.key)) }, `shimmer-${index}`));
|
|
1635
|
+
}
|
|
1636
|
+
function renderDataRows(columns, rows) {
|
|
1637
|
+
return rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: DATA_ROW_CLASSES, children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { className: CELL_CLASS, children: formatCell(row, column) }, column.key)) }, index));
|
|
1638
|
+
}
|
|
1639
|
+
function renderEmptyRow(colSpan, emptyMessage) {
|
|
1640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TableCell, { colSpan, className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) });
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// src/components/artifacts/table-artifact/use-table-artifact-data.ts
|
|
1644
|
+
function useTableArtifactData(presignedUrl, columns) {
|
|
1645
|
+
return useRowsArtifactData(presignedUrl, columns, parseCsv);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
// src/components/artifacts/table-artifact/validate.ts
|
|
1649
|
+
function isValidTableConfig(config) {
|
|
1650
|
+
if (!config || typeof config !== "object") return false;
|
|
1651
|
+
const candidate = config;
|
|
1652
|
+
if (!Array.isArray(candidate.columns)) return false;
|
|
1653
|
+
if (candidate.columns.length === 0) return false;
|
|
1654
|
+
return candidate.columns.every(isValidTableColumn);
|
|
1655
|
+
}
|
|
1656
|
+
function isValidTableColumn(column) {
|
|
1657
|
+
if (!column || typeof column !== "object") return false;
|
|
1658
|
+
const candidate = column;
|
|
1659
|
+
if (typeof candidate.key !== "string" || candidate.key.length === 0) return false;
|
|
1660
|
+
if (typeof candidate.label !== "string") return false;
|
|
1661
|
+
return true;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// src/components/artifacts/table-artifact/table-artifact.tsx
|
|
1665
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1666
|
+
var TITLE_CLASSES2 = "text-sm font-semibold text-foreground";
|
|
1667
|
+
var TABLE_MAX_BODY_HEIGHT = 480;
|
|
1668
|
+
var MAX_SHIMMER_ROWS = 10;
|
|
1669
|
+
function TableArtifact({ artifact, labels, callbacks }) {
|
|
1670
|
+
const validConfig = isValidTableConfig(artifact.config);
|
|
1671
|
+
const validColumns = validConfig ? artifact.config.columns : null;
|
|
1672
|
+
(0, import_react11.useEffect)(() => {
|
|
1673
|
+
if (validConfig) return;
|
|
1674
|
+
console.warn("[clay] TableArtifact: malformed `config` \u2014 skipping render.");
|
|
1675
|
+
}, [validConfig]);
|
|
1676
|
+
const resolvedLabels = (0, import_react11.useMemo)(
|
|
1677
|
+
() => ({ ...DEFAULT_TABLE_ARTIFACT_LABELS, ...labels?.[artifactTypes.TABLE] ?? {} }),
|
|
1678
|
+
[labels]
|
|
1679
|
+
);
|
|
1680
|
+
const data = useTableArtifactData(
|
|
1681
|
+
validConfig ? artifact.protectedAsset?.presignedUrl ?? null : null,
|
|
1682
|
+
validColumns ?? []
|
|
1683
|
+
);
|
|
1684
|
+
const presignedUrl = artifact.protectedAsset?.presignedUrl ?? null;
|
|
1685
|
+
const onDownload = callbacks?.[artifactTypes.TABLE]?.onDownload;
|
|
1686
|
+
const handleDownloadClick = onDownload ? () => onDownload(artifact) : void 0;
|
|
1687
|
+
if (!validConfig || !validColumns) {
|
|
1688
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1689
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1690
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
|
|
1691
|
+
presignedUrl ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1692
|
+
ArtifactDownloadButton,
|
|
1693
|
+
{
|
|
1694
|
+
href: presignedUrl,
|
|
1695
|
+
label: resolvedLabels.download,
|
|
1696
|
+
onClick: handleDownloadClick
|
|
1697
|
+
}
|
|
1698
|
+
) }) : null
|
|
1699
|
+
] }),
|
|
1700
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactContent, { className: "p-0 overflow-auto", style: { maxHeight: TABLE_MAX_BODY_HEIGHT }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusMessage, { $tone: "info", children: resolvedLabels.unavailable }) })
|
|
1701
|
+
] });
|
|
1702
|
+
}
|
|
1703
|
+
return (
|
|
1704
|
+
// The table body owns its own scroll container (`ArtifactTable` inside `TableArtifactContent`).
|
|
1705
|
+
// Disable the outer overflow container so only one scroll context exists — this prevents
|
|
1706
|
+
// double-scroll nesting and lets `useScrollShadow` find the correct scroll ancestor.
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Artifact, { className: "shadow-none w-full rounded-2xl bg-background/70", children: [
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(ArtifactHeader, { className: "bg-background/70", children: [
|
|
1709
|
+
artifact.title ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactTitle, { className: TITLE_CLASSES2, children: artifact.title }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
|
|
1710
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1711
|
+
ArtifactDownloadButton,
|
|
1712
|
+
{
|
|
1713
|
+
href: presignedUrl ?? "",
|
|
1714
|
+
label: resolvedLabels.download,
|
|
1715
|
+
onClick: handleDownloadClick
|
|
1716
|
+
}
|
|
1717
|
+
) })
|
|
1718
|
+
] }),
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ArtifactContent, { className: "p-0 overflow-visible", style: { maxHeight: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1720
|
+
TableArtifactContent,
|
|
1721
|
+
{
|
|
1722
|
+
columns: validColumns,
|
|
1723
|
+
data,
|
|
1724
|
+
knownRowCount: artifact.config.stats?.rowCount,
|
|
1725
|
+
maxShimmerRows: MAX_SHIMMER_ROWS,
|
|
1726
|
+
maxBodyHeight: TABLE_MAX_BODY_HEIGHT,
|
|
1727
|
+
labels: resolvedLabels
|
|
1728
|
+
}
|
|
1729
|
+
) })
|
|
1730
|
+
] })
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/components/artifacts/table-artifact/to-clipboard-text.ts
|
|
1735
|
+
function tableArtifactToClipboardText(artifact) {
|
|
1736
|
+
const trimmedTitle = artifact.title?.trim();
|
|
1737
|
+
return trimmedTitle || "Table";
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/components/artifacts/artifact-services/table-artifact-service.ts
|
|
1741
|
+
var TableArtifactService = {
|
|
1742
|
+
type: artifactTypes.TABLE,
|
|
1743
|
+
Component: TableArtifact,
|
|
1744
|
+
toClipboardText: tableArtifactToClipboardText
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
// src/components/artifacts/artifact-services/index.ts
|
|
1748
|
+
var ArtifactServices = {
|
|
1749
|
+
[artifactTypes.TABLE]: TableArtifactService,
|
|
1750
|
+
[artifactTypes.CHART]: ChartArtifactService
|
|
1751
|
+
};
|
|
1752
|
+
function artifactToClipboardText(artifact) {
|
|
1753
|
+
const entry = ArtifactServices[artifact.type];
|
|
1754
|
+
if (!entry) {
|
|
1755
|
+
return `[Artifact: ${artifact.title ?? artifact.type}]`;
|
|
1756
|
+
}
|
|
1757
|
+
return entry.toClipboardText(artifact);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// src/components/blocks/artifact-ref-block/artifact-ref-block.tsx
|
|
1761
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1762
|
+
function ArtifactRefBlock({ block }) {
|
|
1763
|
+
const { artifact, labels, callbacks } = block.payload;
|
|
1764
|
+
const entry = ArtifactServices[artifact.type];
|
|
1765
|
+
if (!entry) return null;
|
|
1766
|
+
const { Component } = entry;
|
|
1767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Component, { artifact, labels, callbacks });
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// src/components/blocks/block-services/artifact-ref-block-service.ts
|
|
1771
|
+
var ArtifactRefBlockService = {
|
|
1772
|
+
type: blockTypes.ARTIFACT_REF,
|
|
1773
|
+
Component: ArtifactRefBlock,
|
|
1774
|
+
/**
|
|
1775
|
+
* Delegates to the registered artifact component's `toClipboardText`. Falls back to
|
|
1776
|
+
* `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type
|
|
1777
|
+
* has no registered component, so copy never silently produces an empty string.
|
|
1778
|
+
*/
|
|
1779
|
+
toClipboardText: (block) => artifactToClipboardText(block.payload.artifact)
|
|
1780
|
+
};
|
|
1781
|
+
|
|
1782
|
+
// src/utils/markdown-to-plain-text.ts
|
|
1783
|
+
var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
|
|
1784
|
+
var PLACEHOLDER_SUFFIX = "\u200B\u200B";
|
|
1785
|
+
var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
|
|
1786
|
+
function stripInlineMarks(text) {
|
|
1787
|
+
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");
|
|
1788
|
+
}
|
|
1789
|
+
function markdownToPlainText(markdown) {
|
|
1790
|
+
if (!markdown) return "";
|
|
1791
|
+
const inlineCodeSnippets = [];
|
|
1792
|
+
let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
|
|
1793
|
+
inlineCodeSnippets.push(code);
|
|
1794
|
+
return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
|
|
1795
|
+
}).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
|
|
1796
|
+
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();
|
|
1797
|
+
result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
|
|
1798
|
+
return result;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// src/components/blocks/text-block/text-block.tsx
|
|
1802
|
+
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
1803
|
+
var import_remark_breaks = __toESM(require("remark-breaks"), 1);
|
|
1804
|
+
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
1805
|
+
|
|
1806
|
+
// src/components/blocks/scrollable-table/scrollable-table-styles.ts
|
|
1807
|
+
var import_styled_components3 = __toESM(require("styled-components"), 1);
|
|
1808
|
+
var TableContainer = import_styled_components3.default.div`
|
|
1809
|
+
position: relative;
|
|
1810
|
+
margin-block: 8px 16px;
|
|
1811
|
+
`;
|
|
1812
|
+
var TableScroll = import_styled_components3.default.div`
|
|
1813
|
+
width: 100%;
|
|
1814
|
+
overflow-x: auto;
|
|
1815
|
+
overflow-y: hidden;
|
|
1816
|
+
`;
|
|
1817
|
+
|
|
1818
|
+
// src/components/blocks/scrollable-table/scrollable-table.tsx
|
|
1819
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1820
|
+
function ScrollableTable({ node: _node, ...props }) {
|
|
1821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableScroll, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("table", { ...props }) }) });
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/components/blocks/text-block/sanitize.ts
|
|
1825
|
+
function sanitize(text) {
|
|
1826
|
+
return text.split("\n").filter((line) => {
|
|
1827
|
+
const t = line.trim();
|
|
1828
|
+
return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
|
|
1829
|
+
}).join("\n");
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/components/blocks/text-block/text-block-styles.ts
|
|
1833
|
+
var import_styled_components4 = __toESM(require("styled-components"), 1);
|
|
1834
|
+
var TextBlockWrapper = import_styled_components4.default.div`
|
|
493
1835
|
${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
|
|
494
1836
|
color: ${colors["brown-100"]};
|
|
495
1837
|
word-break: break-word;
|
|
@@ -607,248 +1949,71 @@ var TextBlockWrapper = import_styled_components2.default.div`
|
|
|
607
1949
|
`;
|
|
608
1950
|
|
|
609
1951
|
// src/components/blocks/text-block/text-block.tsx
|
|
610
|
-
var
|
|
1952
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1953
|
+
var markdownComponents = {
|
|
1954
|
+
table: ((tableProps) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScrollableTable, { ...tableProps }))
|
|
1955
|
+
};
|
|
611
1956
|
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) }) });
|
|
1957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TextBlockWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], components: markdownComponents, children: sanitize(block.payload.text) }) });
|
|
617
1958
|
}
|
|
618
1959
|
|
|
619
|
-
// src/components/blocks/types.ts
|
|
620
|
-
var blockTypes = {
|
|
621
|
-
TEXT: "TEXT"
|
|
622
|
-
};
|
|
623
|
-
|
|
624
1960
|
// src/components/blocks/block-services/text-block-service.ts
|
|
625
1961
|
var TextBlockService = {
|
|
626
1962
|
type: blockTypes.TEXT,
|
|
627
|
-
Component: TextBlock
|
|
1963
|
+
Component: TextBlock,
|
|
1964
|
+
/**
|
|
1965
|
+
* Converts the block's markdown source to plain text so the final clipboard value is clean
|
|
1966
|
+
* when pasted into tools that don't render markdown. Each block type owns its own clipboard
|
|
1967
|
+
* conversion so `useCopyToClipboard` can write text verbatim without a second pass.
|
|
1968
|
+
*/
|
|
1969
|
+
toClipboardText: (block) => markdownToPlainText(block.payload.text)
|
|
628
1970
|
};
|
|
629
1971
|
|
|
630
1972
|
// src/components/blocks/block-services/index.ts
|
|
631
1973
|
var BlockServices = {
|
|
632
|
-
[
|
|
1974
|
+
[blockTypes.TEXT]: TextBlockService,
|
|
1975
|
+
[blockTypes.ARTIFACT_REF]: ArtifactRefBlockService
|
|
633
1976
|
};
|
|
634
1977
|
|
|
635
1978
|
// src/components/blocks/block/block.tsx
|
|
636
|
-
var
|
|
1979
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
637
1980
|
function Block({ block }) {
|
|
638
1981
|
const service = BlockServices[block.type];
|
|
639
1982
|
if (!service) return null;
|
|
640
1983
|
const { Component } = service;
|
|
641
|
-
return /* @__PURE__ */ (0,
|
|
1984
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Component, { block });
|
|
642
1985
|
}
|
|
643
1986
|
|
|
644
|
-
// src/components/chat/
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1987
|
+
// src/components/chat/hooks/copy-message-text.ts
|
|
1988
|
+
function copyMessageText(message) {
|
|
1989
|
+
const parts = [];
|
|
1990
|
+
for (const block of message.blocks) {
|
|
1991
|
+
const service = BlockServices[block.type];
|
|
1992
|
+
if (!service) continue;
|
|
1993
|
+
if (!service.toClipboardText) continue;
|
|
1994
|
+
const text = service.toClipboardText(block);
|
|
1995
|
+
if (text) parts.push(text);
|
|
651
1996
|
}
|
|
652
|
-
return
|
|
1997
|
+
if (parts.length === 0) return message.fallbackText;
|
|
1998
|
+
return parts.join("\n\n");
|
|
653
1999
|
}
|
|
654
2000
|
|
|
655
2001
|
// 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
|
-
`;
|
|
2002
|
+
var import_react14 = require("react");
|
|
754
2003
|
|
|
755
|
-
// src/components/
|
|
756
|
-
var
|
|
757
|
-
var
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
}
|
|
803
|
-
|
|
804
|
-
&:active {
|
|
805
|
-
background: ${colors["brown-40"]};
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
&:disabled {
|
|
809
|
-
color: ${colors["brown-50"]};
|
|
810
|
-
background: transparent;
|
|
811
|
-
cursor: not-allowed;
|
|
2004
|
+
// src/components/chat/chat-context/chat-context.ts
|
|
2005
|
+
var import_react12 = require("react");
|
|
2006
|
+
var ChatContext = (0, import_react12.createContext)(null);
|
|
2007
|
+
function useChatContext() {
|
|
2008
|
+
const context = (0, import_react12.useContext)(ChatContext);
|
|
2009
|
+
if (!context) {
|
|
2010
|
+
throw new Error("useChatContext must be used within a Chat.Root component");
|
|
812
2011
|
}
|
|
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
|
-
);
|
|
2012
|
+
return context;
|
|
848
2013
|
}
|
|
849
2014
|
|
|
850
2015
|
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
851
|
-
var
|
|
2016
|
+
var import_react13 = require("react");
|
|
852
2017
|
|
|
853
2018
|
// src/utils/clipboard.ts
|
|
854
2019
|
function copyToClipboard(text, options) {
|
|
@@ -858,37 +2023,18 @@ function copyToClipboard(text, options) {
|
|
|
858
2023
|
);
|
|
859
2024
|
}
|
|
860
2025
|
|
|
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
2026
|
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
881
2027
|
var RESET_DELAY_MS = 2e3;
|
|
882
2028
|
function useCopyToClipboard() {
|
|
883
|
-
const [isCopied, setIsCopied] = (0,
|
|
884
|
-
const timeoutRef = (0,
|
|
885
|
-
(0,
|
|
2029
|
+
const [isCopied, setIsCopied] = (0, import_react13.useState)(false);
|
|
2030
|
+
const timeoutRef = (0, import_react13.useRef)(null);
|
|
2031
|
+
(0, import_react13.useEffect)(() => {
|
|
886
2032
|
return () => {
|
|
887
2033
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
888
2034
|
};
|
|
889
2035
|
}, []);
|
|
890
|
-
const copy = (0,
|
|
891
|
-
copyToClipboard(
|
|
2036
|
+
const copy = (0, import_react13.useCallback)((text) => {
|
|
2037
|
+
copyToClipboard(text, {
|
|
892
2038
|
onSuccess: () => {
|
|
893
2039
|
setIsCopied(true);
|
|
894
2040
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -927,24 +2073,24 @@ var AnimatedAction = import_styled_components5.default.div`
|
|
|
927
2073
|
`;
|
|
928
2074
|
|
|
929
2075
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
930
|
-
var
|
|
2076
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
931
2077
|
function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
|
|
932
2078
|
const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
|
|
933
2079
|
const { isCopied, copy } = useCopyToClipboard();
|
|
934
|
-
const handleCopy = (0,
|
|
2080
|
+
const handleCopy = (0, import_react14.useCallback)(() => {
|
|
935
2081
|
copy(content);
|
|
936
2082
|
onCopyMessage?.(messageId);
|
|
937
2083
|
}, [content, messageId, copy, onCopyMessage]);
|
|
938
|
-
const handleThumbUp = (0,
|
|
2084
|
+
const handleThumbUp = (0, import_react14.useCallback)(() => {
|
|
939
2085
|
onThumbUpClick?.(messageId, isHelpful === true ? null : true);
|
|
940
2086
|
}, [messageId, isHelpful, onThumbUpClick]);
|
|
941
|
-
const handleThumbDown = (0,
|
|
2087
|
+
const handleThumbDown = (0, import_react14.useCallback)(() => {
|
|
942
2088
|
onThumbDownClick?.(messageId, isHelpful === false ? null : false);
|
|
943
2089
|
}, [messageId, isHelpful, onThumbDownClick]);
|
|
944
2090
|
const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
|
|
945
2091
|
const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
|
|
946
|
-
return /* @__PURE__ */ (0,
|
|
947
|
-
/* @__PURE__ */ (0,
|
|
2092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(ActionsContainer, { $role: role, className, children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
948
2094
|
IconButton,
|
|
949
2095
|
{
|
|
950
2096
|
icon: isCopied ? IconCheck : IconCopy,
|
|
@@ -952,7 +2098,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
952
2098
|
"aria-label": isCopied ? "Copied" : "Copy message"
|
|
953
2099
|
}
|
|
954
2100
|
),
|
|
955
|
-
hasFeedback && /* @__PURE__ */ (0,
|
|
2101
|
+
hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
956
2102
|
IconButton,
|
|
957
2103
|
{
|
|
958
2104
|
icon: IconThumbUp,
|
|
@@ -961,7 +2107,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
961
2107
|
"aria-label": "Good response"
|
|
962
2108
|
}
|
|
963
2109
|
) }),
|
|
964
|
-
hasFeedback && /* @__PURE__ */ (0,
|
|
2110
|
+
hasFeedback && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
965
2111
|
IconButton,
|
|
966
2112
|
{
|
|
967
2113
|
icon: IconThumbDown,
|
|
@@ -999,26 +2145,28 @@ var MessageBubble = import_styled_components6.default.div`
|
|
|
999
2145
|
padding: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "12px 16px" : "0"};
|
|
1000
2146
|
border-radius: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "16px" : "0"};
|
|
1001
2147
|
max-width: 100%;
|
|
2148
|
+
width: 100%;
|
|
1002
2149
|
min-width: 0;
|
|
2150
|
+
/* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.
|
|
2151
|
+
* Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies
|
|
2152
|
+
* spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer
|
|
2153
|
+
* gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */
|
|
2154
|
+
display: flex;
|
|
2155
|
+
flex-direction: column;
|
|
2156
|
+
gap: 12px;
|
|
1003
2157
|
`;
|
|
1004
2158
|
|
|
1005
2159
|
// src/components/chat/chat-message/chat-message.tsx
|
|
1006
|
-
var
|
|
2160
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1007
2161
|
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)(
|
|
2162
|
+
const clipboardText = (0, import_react15.useMemo)(() => copyMessageText(message), [message]);
|
|
2163
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(MessageContainer, { $role: message.role, children: [
|
|
2164
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Block, { block }, index)) }),
|
|
2165
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1018
2166
|
ChatMessageActions,
|
|
1019
2167
|
{
|
|
1020
2168
|
messageId: message.id,
|
|
1021
|
-
content:
|
|
2169
|
+
content: clipboardText,
|
|
1022
2170
|
role: message.role,
|
|
1023
2171
|
isHelpful: message.isHelpful
|
|
1024
2172
|
}
|
|
@@ -1030,6 +2178,7 @@ function ChatMessage({ message }) {
|
|
|
1030
2178
|
ChatContext,
|
|
1031
2179
|
ChatMessage,
|
|
1032
2180
|
MESSAGE_ROLE,
|
|
2181
|
+
copyMessageText,
|
|
1033
2182
|
useChatContext,
|
|
1034
2183
|
useCopyToClipboard
|
|
1035
2184
|
});
|
|
@@ -1039,6 +2188,7 @@ function ChatMessage({ message }) {
|
|
|
1039
2188
|
@tabler/icons-react/dist/esm/createReactComponent.mjs:
|
|
1040
2189
|
@tabler/icons-react/dist/esm/icons/IconCheck.mjs:
|
|
1041
2190
|
@tabler/icons-react/dist/esm/icons/IconCopy.mjs:
|
|
2191
|
+
@tabler/icons-react/dist/esm/icons/IconDownload.mjs:
|
|
1042
2192
|
@tabler/icons-react/dist/esm/icons/IconThumbDown.mjs:
|
|
1043
2193
|
@tabler/icons-react/dist/esm/icons/IconThumbUp.mjs:
|
|
1044
2194
|
@tabler/icons-react/dist/esm/tabler-icons-react.mjs:
|