@shapesos/clay 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-elements.cjs +3 -1
- package/dist/ai-elements.cjs.map +1 -1
- package/dist/ai-elements.js +4 -4
- package/dist/artifacts.cjs +1010 -60
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +20 -3
- package/dist/artifacts.d.ts +20 -3
- package/dist/artifacts.js +15 -6
- package/dist/badge.cjs +70 -0
- package/dist/badge.cjs.map +1 -0
- package/dist/badge.d.cts +28 -0
- package/dist/badge.d.ts +28 -0
- package/dist/badge.js +9 -0
- package/dist/blocks.cjs +981 -96
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +1 -1
- package/dist/blocks.d.cts +15 -4
- package/dist/blocks.d.ts +15 -4
- package/dist/blocks.js +20 -8
- package/dist/button.cjs +3 -1
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +2 -2
- package/dist/capture.cjs +3 -1
- package/dist/capture.cjs.map +1 -1
- package/dist/capture.js +2 -2
- package/dist/chart.cjs +3 -1
- package/dist/chart.cjs.map +1 -1
- package/dist/chart.js +2 -2
- package/dist/chat.cjs +986 -151
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +2 -2
- package/dist/chat.d.ts +2 -2
- package/dist/chat.js +13 -8
- package/dist/{chunk-JGMN6W72.js → chunk-2X4KUXYP.js} +1 -1
- package/dist/chunk-BHHJOG5X.js +1 -0
- package/dist/{chunk-B4U37WIH.js → chunk-CFCLVJIO.js} +2 -2
- package/dist/chunk-CSTCTO3B.js +161 -0
- package/dist/chunk-CSTCTO3B.js.map +1 -0
- package/dist/{chunk-6YZ65KPW.js → chunk-DC5KYT2G.js} +669 -171
- package/dist/chunk-DC5KYT2G.js.map +1 -0
- package/dist/{chunk-PYDVANWI.js → chunk-E36VI2HA.js} +2 -2
- package/dist/chunk-EIAWZXE4.js +69 -0
- package/dist/chunk-EIAWZXE4.js.map +1 -0
- package/dist/chunk-EXRNGIQ2.js +41 -0
- package/dist/chunk-EXRNGIQ2.js.map +1 -0
- package/dist/{chunk-B6GAPZ2R.js → chunk-FCSYVF4Z.js} +12 -46
- package/dist/chunk-FCSYVF4Z.js.map +1 -0
- package/dist/{chunk-PUVZ2S3F.js → chunk-G4NXVFXU.js} +2 -2
- package/dist/{chunk-JAGNXEWK.js → chunk-GK4XW4BC.js} +2 -2
- package/dist/{chunk-4MZZH3WX.js → chunk-HU5W7CGE.js} +37 -2
- package/dist/chunk-HU5W7CGE.js.map +1 -0
- package/dist/chunk-JMHE3Q5K.js +406 -0
- package/dist/chunk-JMHE3Q5K.js.map +1 -0
- package/dist/{chunk-4VE6ZXXW.js → chunk-MQP57DOP.js} +3 -3
- package/dist/{chunk-JF3P66JF.js → chunk-SN3ORQSJ.js} +4 -2
- package/dist/{chunk-JF3P66JF.js.map → chunk-SN3ORQSJ.js.map} +1 -1
- package/dist/{chunk-PGR2MDDU.js → chunk-W66OJM55.js} +2 -2
- package/dist/{chunk-7OYIDM42.js → chunk-XKURJ72I.js} +2 -2
- package/dist/copy-button.cjs +439 -0
- package/dist/copy-button.cjs.map +1 -0
- package/dist/copy-button.d.cts +20 -0
- package/dist/copy-button.d.ts +20 -0
- package/dist/copy-button.js +12 -0
- package/dist/copy-button.js.map +1 -0
- package/dist/{dom-to-image-NATF3446.js → dom-to-image-QB7KMTAE.js} +3 -3
- package/dist/dom-to-image-QB7KMTAE.js.map +1 -0
- package/dist/icon.cjs +3 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +2 -2
- package/dist/index.cjs +1070 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +42 -17
- package/dist/keyboard-shortcut.cjs +3 -1
- package/dist/keyboard-shortcut.cjs.map +1 -1
- package/dist/keyboard-shortcut.js +2 -2
- package/dist/table.cjs +40 -0
- package/dist/table.cjs.map +1 -1
- package/dist/table.d.cts +37 -2
- package/dist/table.d.ts +37 -2
- package/dist/table.js +11 -1
- package/dist/text-area.cjs +3 -1
- package/dist/text-area.cjs.map +1 -1
- package/dist/text-area.js +2 -2
- package/dist/tokens.cjs +3 -1
- package/dist/tokens.cjs.map +1 -1
- package/dist/tokens.d.cts +1 -0
- package/dist/tokens.d.ts +1 -0
- package/dist/tokens.js +1 -1
- package/dist/{types-CW45GBXl.d.cts → types-Bhg0_ES8.d.cts} +75 -3
- package/dist/{types-GCkXbyFm.d.ts → types-C8JtnwNY.d.ts} +200 -4
- package/dist/{types-BzXPBy6s.d.cts → types-DTdDFU3O.d.cts} +200 -4
- package/dist/{types-D1b8v7EY.d.ts → types-orOQcI2z.d.ts} +75 -3
- package/package.json +11 -1
- package/dist/chunk-46ZYTBX5.js +0 -250
- package/dist/chunk-46ZYTBX5.js.map +0 -1
- package/dist/chunk-4MZZH3WX.js.map +0 -1
- package/dist/chunk-6YZ65KPW.js.map +0 -1
- package/dist/chunk-B6GAPZ2R.js.map +0 -1
- package/dist/chunk-RSRNSWJN.js +0 -1
- /package/dist/{chunk-RSRNSWJN.js.map → badge.js.map} +0 -0
- /package/dist/{chunk-JGMN6W72.js.map → chunk-2X4KUXYP.js.map} +0 -0
- /package/dist/{dom-to-image-NATF3446.js.map → chunk-BHHJOG5X.js.map} +0 -0
- /package/dist/{chunk-B4U37WIH.js.map → chunk-CFCLVJIO.js.map} +0 -0
- /package/dist/{chunk-PYDVANWI.js.map → chunk-E36VI2HA.js.map} +0 -0
- /package/dist/{chunk-PUVZ2S3F.js.map → chunk-G4NXVFXU.js.map} +0 -0
- /package/dist/{chunk-JAGNXEWK.js.map → chunk-GK4XW4BC.js.map} +0 -0
- /package/dist/{chunk-4VE6ZXXW.js.map → chunk-MQP57DOP.js.map} +0 -0
- /package/dist/{chunk-PGR2MDDU.js.map → chunk-W66OJM55.js.map} +0 -0
- /package/dist/{chunk-7OYIDM42.js.map → chunk-XKURJ72I.js.map} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-P6GUNIAE.js";
|
|
4
|
+
|
|
5
|
+
// src/components/badge/badge.tsx
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
var BASE = "inline-flex items-center whitespace-nowrap rounded-full";
|
|
8
|
+
var SIZES = {
|
|
9
|
+
// `sm` is a uniform scale-down of `md` (text 10/12 of caption): padding and line-height keep
|
|
10
|
+
// md's proportions (caption is 12px text on a 16px line = 1.333 ratio) so the pill shape matches.
|
|
11
|
+
md: "px-2.5 py-0.5 text-geist-label-caption-medium",
|
|
12
|
+
sm: "px-2 py-0.5 text-[11px]/[1.333] font-medium"
|
|
13
|
+
};
|
|
14
|
+
var SUBTLE = {
|
|
15
|
+
neutral: "bg-brown-20 text-brown-70",
|
|
16
|
+
warning: "bg-orange-50 text-orange-600",
|
|
17
|
+
violet: "bg-violet-50 text-violet-600"
|
|
18
|
+
};
|
|
19
|
+
var OUTLINE = {
|
|
20
|
+
neutral: "border border-brown-30 bg-white text-brown-100",
|
|
21
|
+
warning: "border border-orange-200 bg-white text-orange-600",
|
|
22
|
+
violet: "border border-violet-400 bg-white text-violet-600"
|
|
23
|
+
};
|
|
24
|
+
var VARIANTS = {
|
|
25
|
+
subtle: SUBTLE,
|
|
26
|
+
outline: OUTLINE
|
|
27
|
+
};
|
|
28
|
+
function Badge({
|
|
29
|
+
variant = "subtle",
|
|
30
|
+
tone = "neutral",
|
|
31
|
+
size = "md",
|
|
32
|
+
className,
|
|
33
|
+
children
|
|
34
|
+
}) {
|
|
35
|
+
return /* @__PURE__ */ jsx("span", { className: cn(BASE, SIZES[size], VARIANTS[variant][tone], className), children });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
Badge
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=chunk-EXRNGIQ2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/badge/badge.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Fill treatment of the badge. */\nexport type BadgeVariant = \"subtle\" | \"outline\";\n\n/** Semantic color of the badge. */\nexport type BadgeTone = \"neutral\" | \"warning\" | \"violet\";\n\n/** Overall size — `md` (default) or a more compact `sm` for eyebrow-style tags. */\nexport type BadgeSize = \"sm\" | \"md\";\n\nexport interface BadgeProps {\n /** Fill treatment. `subtle` = tinted background, `outline` = bordered on white. Default `subtle`. */\n variant?: BadgeVariant;\n /** Semantic color. `neutral` = brown, `warning` = orange, `violet` = violet. Default `neutral`. */\n tone?: BadgeTone;\n /** Size. `md` (12px text) or `sm` (10px text, tighter padding). Default `md`. */\n size?: BadgeSize;\n /** Extra classes merged after the variant/tone classes (last-wins via tailwind-merge). */\n className?: string;\n /** Badge label content. */\n children: ReactNode;\n}\n\nconst BASE = \"inline-flex items-center whitespace-nowrap rounded-full\";\n\n/** Padding + text size per size. Kept as one class each so there is a single font-size utility (deterministic override). */\nconst SIZES: Record<BadgeSize, string> = {\n // `sm` is a uniform scale-down of `md` (text 10/12 of caption): padding and line-height keep\n // md's proportions (caption is 12px text on a 16px line = 1.333 ratio) so the pill shape matches.\n md: \"px-2.5 py-0.5 text-geist-label-caption-medium\",\n sm: \"px-2 py-0.5 text-[11px]/[1.333] font-medium\",\n};\n\n/** Tinted background — low-emphasis tag. */\nconst SUBTLE: Record<BadgeTone, string> = {\n neutral: \"bg-brown-20 text-brown-70\",\n warning: \"bg-orange-50 text-orange-600\",\n violet: \"bg-violet-50 text-violet-600\",\n};\n\n/** Bordered on white — reads as a discrete chip. */\nconst OUTLINE: Record<BadgeTone, string> = {\n neutral: \"border border-brown-30 bg-white text-brown-100\",\n warning: \"border border-orange-200 bg-white text-orange-600\",\n violet: \"border border-violet-400 bg-white text-violet-600\",\n};\n\nconst VARIANTS: Record<BadgeVariant, Record<BadgeTone, string>> = {\n subtle: SUBTLE,\n outline: OUTLINE,\n};\n\n/**\n * Small pill for labels, counts, and status tags — recipient chips, \"+N\" overflow, \"Email\n * (fallback)\" tags, \"Labs\". Presentational only; on clay tokens. Compose the label at the call\n * site.\n */\nexport function Badge({\n variant = \"subtle\",\n tone = \"neutral\",\n size = \"md\",\n className,\n children,\n}: BadgeProps): ReactNode {\n return <span className={cn(BASE, SIZES[size], VARIANTS[variant][tone], className)}>{children}</span>;\n}\n"],"mappings":";;;;;AAmES;AAzCT,IAAM,OAAO;AAGb,IAAM,QAAmC;AAAA;AAAA;AAAA,EAGvC,IAAI;AAAA,EACJ,IAAI;AACN;AAGA,IAAM,SAAoC;AAAA,EACxC,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AACV;AAGA,IAAM,UAAqC;AAAA,EACzC,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AACV;AAEA,IAAM,WAA4D;AAAA,EAChE,QAAQ;AAAA,EACR,SAAS;AACX;AAOO,SAAS,MAAM;AAAA,EACpB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAA0B;AACxB,SAAO,oBAAC,UAAK,WAAW,GAAG,MAAM,MAAM,IAAI,GAAG,SAAS,OAAO,EAAE,IAAI,GAAG,SAAS,GAAI,UAAS;AAC/F;","names":[]}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Block,
|
|
3
3
|
BlockServices
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JMHE3Q5K.js";
|
|
5
|
+
import {
|
|
6
|
+
useCopyToClipboard
|
|
7
|
+
} from "./chunk-EIAWZXE4.js";
|
|
5
8
|
import {
|
|
6
9
|
IconCheck,
|
|
7
10
|
IconCopy,
|
|
8
11
|
IconThumbDown,
|
|
9
12
|
IconThumbUp
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CSTCTO3B.js";
|
|
11
14
|
import {
|
|
12
15
|
IconButton
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-E36VI2HA.js";
|
|
14
17
|
import {
|
|
15
18
|
typographyMixin,
|
|
16
19
|
typographyTypes
|
|
17
20
|
} from "./chunk-UXT2H3FM.js";
|
|
18
21
|
import {
|
|
19
22
|
colors
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-SN3ORQSJ.js";
|
|
21
24
|
|
|
22
25
|
// src/components/chat/chat-message/chat-message.tsx
|
|
23
26
|
import { useMemo } from "react";
|
|
@@ -37,7 +40,7 @@ function copyMessageText(message) {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
40
|
-
import { useCallback
|
|
43
|
+
import { useCallback } from "react";
|
|
41
44
|
|
|
42
45
|
// src/components/chat/chat-context/chat-context.ts
|
|
43
46
|
import { createContext, useContext } from "react";
|
|
@@ -50,42 +53,6 @@ function useChatContext() {
|
|
|
50
53
|
return context;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
54
|
-
import { useState, useCallback, useRef, useEffect } from "react";
|
|
55
|
-
|
|
56
|
-
// src/utils/clipboard.ts
|
|
57
|
-
function copyToClipboard(text, options) {
|
|
58
|
-
navigator.clipboard.writeText(text).then(
|
|
59
|
-
() => options?.onSuccess?.(),
|
|
60
|
-
(error) => options?.onFailure?.(error)
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// src/components/chat/hooks/use-copy-to-clipboard.ts
|
|
65
|
-
var RESET_DELAY_MS = 2e3;
|
|
66
|
-
function useCopyToClipboard() {
|
|
67
|
-
const [isCopied, setIsCopied] = useState(false);
|
|
68
|
-
const timeoutRef = useRef(null);
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
return () => {
|
|
71
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
72
|
-
};
|
|
73
|
-
}, []);
|
|
74
|
-
const copy = useCallback((text) => {
|
|
75
|
-
copyToClipboard(text, {
|
|
76
|
-
onSuccess: () => {
|
|
77
|
-
setIsCopied(true);
|
|
78
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
79
|
-
timeoutRef.current = setTimeout(() => {
|
|
80
|
-
setIsCopied(false);
|
|
81
|
-
timeoutRef.current = null;
|
|
82
|
-
}, RESET_DELAY_MS);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}, []);
|
|
86
|
-
return { isCopied, copy };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
56
|
// src/components/chat/constants.ts
|
|
90
57
|
var MESSAGE_ROLE = {
|
|
91
58
|
USER: "user",
|
|
@@ -115,14 +82,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
115
82
|
function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
|
|
116
83
|
const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
|
|
117
84
|
const { isCopied, copy } = useCopyToClipboard();
|
|
118
|
-
const handleCopy =
|
|
85
|
+
const handleCopy = useCallback(() => {
|
|
119
86
|
copy(content);
|
|
120
87
|
onCopyMessage?.(messageId);
|
|
121
88
|
}, [content, messageId, copy, onCopyMessage]);
|
|
122
|
-
const handleThumbUp =
|
|
89
|
+
const handleThumbUp = useCallback(() => {
|
|
123
90
|
onThumbUpClick?.(messageId, isHelpful === true ? null : true);
|
|
124
91
|
}, [messageId, isHelpful, onThumbUpClick]);
|
|
125
|
-
const handleThumbDown =
|
|
92
|
+
const handleThumbDown = useCallback(() => {
|
|
126
93
|
onThumbDownClick?.(messageId, isHelpful === false ? null : false);
|
|
127
94
|
}, [messageId, isHelpful, onThumbDownClick]);
|
|
128
95
|
const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
|
|
@@ -216,8 +183,7 @@ export {
|
|
|
216
183
|
copyMessageText,
|
|
217
184
|
ChatContext,
|
|
218
185
|
useChatContext,
|
|
219
|
-
useCopyToClipboard,
|
|
220
186
|
MESSAGE_ROLE,
|
|
221
187
|
ChatMessage
|
|
222
188
|
};
|
|
223
|
-
//# sourceMappingURL=chunk-
|
|
189
|
+
//# sourceMappingURL=chunk-FCSYVF4Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat/chat-message/chat-message.tsx","../src/components/chat/hooks/copy-message-text.ts","../src/components/chat/chat-message-actions/chat-message-actions.tsx","../src/components/chat/chat-context/chat-context.ts","../src/components/chat/constants.ts","../src/components/chat/chat-message-actions/chat-message-actions-styles.ts","../src/components/chat/chat-message/chat-message-styles.ts"],"sourcesContent":["import { useMemo } from \"react\";\n\nimport { Block } from \"@/components/blocks/block\";\nimport { copyMessageText } from \"../hooks/copy-message-text\";\nimport type { ChatMessage as ChatMessageType } from \"../types\";\nimport { ChatMessageActions } from \"../chat-message-actions/chat-message-actions\";\nimport { MessageRow, MessageBubble, MessageContainer } from \"./chat-message-styles\";\n\ninterface ChatMessageProps {\n message: ChatMessageType;\n}\n\nexport function ChatMessage({ message }: ChatMessageProps) {\n // Aggregate per-block clipboard text so the copy button produces richer output than the\n // server-supplied `fallbackText` (which is typically \"[table: Title]\" for artifact blocks).\n // `useMemo` keeps the string stable across re-renders that don't change the blocks array.\n const clipboardText = useMemo(() => copyMessageText(message), [message]);\n\n return (\n <MessageRow $role={message.role} role=\"article\" aria-label={`${message.role} message`}>\n <MessageContainer $role={message.role}>\n <MessageBubble $role={message.role} data-testid=\"message-bubble\">\n {message.blocks.map((block, index) => (\n <Block key={index} block={block} />\n ))}\n </MessageBubble>\n <ChatMessageActions\n messageId={message.id}\n content={clipboardText}\n role={message.role}\n isHelpful={message.isHelpful}\n />\n </MessageContainer>\n </MessageRow>\n );\n}\n","import { BlockServices } from \"@/components/blocks/block-services\";\nimport type { ChatMessage } from \"../types\";\n\n/**\n * Aggregates the clipboard text for all blocks in a message.\n *\n * Iterates `message.blocks` in order, calls `BlockServices[block.type].toClipboardText(block)`\n * for each, and joins the results with `\"\\n\\n\"` so multi-block messages have a blank line between\n * each section (matching the visual gap rendered between blocks in the chat UI).\n *\n * Block-type-specific behaviour:\n * - `TEXT` blocks: markdown stripped to plain text via `markdownToPlainText`.\n * - `ARTIFACT_REF` blocks: delegates to the registered artifact component's `toClipboardText`\n * (e.g. the artifact title as plain text for table artifacts).\n * - Unknown block types: silently skipped (no registered service → no contribution to output).\n *\n * Falls back to `message.fallbackText` only when every block produces an empty string — this\n * guards against edge cases (empty message, all-unknown block types) while still preferring\n * the richer per-block text in the normal case.\n *\n * @param message The chat message whose blocks should be aggregated.\n * @returns A string suitable for writing to the system clipboard.\n */\nexport function copyMessageText(message: ChatMessage): string {\n const parts: string[] = [];\n\n for (const block of message.blocks) {\n const service = BlockServices[block.type];\n if (!service) continue;\n // `toClipboardText` is optional on the BlockService interface — external implementors may\n // omit it. Skip silently when absent (contributes \"\" to the aggregated output).\n if (!service.toClipboardText) continue;\n // The registry is keyed by BlockType and each service's toClipboardText is typed to its\n // concrete TBlock. The cast is safe because BlockServices[block.type] is always the matching\n // service for block.type — same soundness argument as the registry's own type erasure.\n const text = (service as { toClipboardText: (b: typeof block) => string }).toClipboardText(block);\n if (text) parts.push(text);\n }\n\n if (parts.length === 0) return message.fallbackText;\n return parts.join(\"\\n\\n\");\n}\n","import { useCallback } from \"react\";\nimport { IconCopy, IconCheck, IconThumbUp, IconThumbDown } from \"@tabler/icons-react\";\n\nimport { IconButton } from \"../../icon-button/icon-button\";\nimport { useChatContext } from \"../chat-context/chat-context\";\nimport { useCopyToClipboard } from \"../hooks/use-copy-to-clipboard\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\nimport { ActionsContainer, AnimatedAction } from \"./chat-message-actions-styles\";\n\ninterface ChatMessageActionsProps {\n className?: string;\n messageId: string;\n content: string;\n role: MessageRole;\n isHelpful?: boolean | null;\n}\n\nexport function ChatMessageActions({ className, messageId, content, role, isHelpful }: ChatMessageActionsProps) {\n const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();\n const { isCopied, copy } = useCopyToClipboard();\n\n const handleCopy = useCallback(() => {\n copy(content);\n onCopyMessage?.(messageId);\n }, [content, messageId, copy, onCopyMessage]);\n\n const handleThumbUp = useCallback(() => {\n onThumbUpClick?.(messageId, isHelpful === true ? null : true);\n }, [messageId, isHelpful, onThumbUpClick]);\n\n const handleThumbDown = useCallback(() => {\n onThumbDownClick?.(messageId, isHelpful === false ? null : false);\n }, [messageId, isHelpful, onThumbDownClick]);\n\n const isAssistant = role === MESSAGE_ROLE.ASSISTANT;\n const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);\n\n return (\n <ActionsContainer $role={role} className={className}>\n <IconButton\n icon={isCopied ? IconCheck : IconCopy}\n onClick={handleCopy}\n aria-label={isCopied ? \"Copied\" : \"Copy message\"}\n />\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== false}>\n <IconButton\n icon={IconThumbUp}\n onClick={handleThumbUp}\n isSelected={isHelpful === true}\n aria-label=\"Good response\"\n />\n </AnimatedAction>\n )}\n\n {hasFeedback && (\n <AnimatedAction $visible={isHelpful !== true}>\n <IconButton\n icon={IconThumbDown}\n onClick={handleThumbDown}\n isSelected={isHelpful === false}\n aria-label=\"Bad response\"\n />\n </AnimatedAction>\n )}\n </ActionsContainer>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { ChatContextValue } from \"../types\";\n\nexport const ChatContext = createContext<ChatContextValue | null>(null);\n\nexport function useChatContext(): ChatContextValue {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a Chat.Root component\");\n }\n return context;\n}\n","export const MESSAGE_ROLE = {\n USER: \"user\",\n ASSISTANT: \"assistant\",\n} as const;\n","import styled from \"styled-components\";\n\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nconst FADE_DURATION_MS = 150;\nconst ANIMATE_DURATION_MS = 200;\n\nexport const ActionsContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n opacity: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? 1 : 0)};\n pointer-events: ${({ $role }) => ($role === MESSAGE_ROLE.ASSISTANT ? \"auto\" : \"none\")};\n transition: opacity ${FADE_DURATION_MS}ms ease;\n`;\n\nexport const AnimatedAction = styled.div<{ $visible: boolean }>`\n display: flex;\n overflow: hidden;\n max-width: ${({ $visible }) => ($visible ? \"40px\" : \"0\")};\n opacity: ${({ $visible }) => ($visible ? 1 : 0)};\n transition: max-width ${ANIMATE_DURATION_MS}ms ease, opacity ${ANIMATE_DURATION_MS}ms ease;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../../tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"../../../tokens/typography\";\nimport { ActionsContainer } from \"../chat-message-actions/chat-message-actions-styles\";\nimport { MESSAGE_ROLE } from \"../constants\";\nimport type { MessageRole } from \"../types\";\n\nexport const MessageRow = styled.div<{ $role: MessageRole }>`\n display: flex;\n justify-content: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n\n &:hover ${ActionsContainer} {\n opacity: 1;\n pointer-events: auto;\n }\n`;\n\nexport const MessageContainer = styled.div<{ $role: MessageRole }>`\n display: flex;\n flex-direction: column;\n align-items: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"flex-end\" : \"flex-start\")};\n max-width: 90%;\n width: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"auto\" : \"100%\")};\n gap: 12px;\n`;\n\nexport const MessageBubble = styled.div<{ $role: MessageRole }>`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n background: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? colors.white : \"transparent\")};\n padding: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"12px 16px\" : \"0\")};\n border-radius: ${({ $role }) => ($role === MESSAGE_ROLE.USER ? \"16px\" : \"0\")};\n max-width: 100%;\n width: 100%;\n min-width: 0;\n /* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.\n * Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies\n * spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer\n * gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */\n display: flex;\n flex-direction: column;\n gap: 12px;\n`;\n"],"mappings":"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","names":["styled","styled","jsx","jsxs"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
colors
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SN3ORQSJ.js";
|
|
4
4
|
import {
|
|
5
5
|
cn
|
|
6
6
|
} from "./chunk-P6GUNIAE.js";
|
|
@@ -503,4 +503,4 @@ export {
|
|
|
503
503
|
LineChart,
|
|
504
504
|
PieChart
|
|
505
505
|
};
|
|
506
|
-
//# sourceMappingURL=chunk-
|
|
506
|
+
//# sourceMappingURL=chunk-G4NXVFXU.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-LXYCT4YO.js";
|
|
4
4
|
import {
|
|
5
5
|
colors
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SN3ORQSJ.js";
|
|
7
7
|
|
|
8
8
|
// src/capture/dom-to-image.ts
|
|
9
9
|
import { getFontEmbedCSS, toBlob } from "html-to-image";
|
|
@@ -113,4 +113,4 @@ export {
|
|
|
113
113
|
downloadNodeAsPng,
|
|
114
114
|
truncateErrorMessage
|
|
115
115
|
};
|
|
116
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-GK4XW4BC.js.map
|
|
@@ -79,6 +79,36 @@ function useScrollShadow() {
|
|
|
79
79
|
return { targetRef, isScrolled };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// src/components/table/data-table.tsx
|
|
83
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
84
|
+
var DATA_TABLE_HEAD_CLASS = "h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left";
|
|
85
|
+
var DATA_TABLE_CELL_CLASS = "p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left";
|
|
86
|
+
var DATA_TABLE_ROW_CLASS = "border-b-brown-30 bg-background/70 hover:bg-secondary";
|
|
87
|
+
var DATA_TABLE_SCROLL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)";
|
|
88
|
+
function DataTable({ header, children, maxBodyHeight, className }) {
|
|
89
|
+
const { targetRef, isScrolled } = useScrollShadow();
|
|
90
|
+
return /* @__PURE__ */ jsx2(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
ref: targetRef,
|
|
94
|
+
className: cn("overscroll-none", className),
|
|
95
|
+
style: maxBodyHeight !== void 0 ? { maxHeight: maxBodyHeight, overflowY: "auto" } : void 0,
|
|
96
|
+
children: /* @__PURE__ */ jsxs(Table, { containerClassName: "overflow-visible", children: [
|
|
97
|
+
/* @__PURE__ */ jsx2(
|
|
98
|
+
TableHeader,
|
|
99
|
+
{
|
|
100
|
+
"data-scrolled": isScrolled ? "true" : void 0,
|
|
101
|
+
className: "sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow",
|
|
102
|
+
style: isScrolled ? { boxShadow: DATA_TABLE_SCROLL_SHADOW } : void 0,
|
|
103
|
+
children: header
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
/* @__PURE__ */ jsx2(TableBody, { children })
|
|
107
|
+
] })
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
82
112
|
export {
|
|
83
113
|
Table,
|
|
84
114
|
TableHeader,
|
|
@@ -88,6 +118,11 @@ export {
|
|
|
88
118
|
TableHead,
|
|
89
119
|
TableCell,
|
|
90
120
|
TableCaption,
|
|
91
|
-
useScrollShadow
|
|
121
|
+
useScrollShadow,
|
|
122
|
+
DATA_TABLE_HEAD_CLASS,
|
|
123
|
+
DATA_TABLE_CELL_CLASS,
|
|
124
|
+
DATA_TABLE_ROW_CLASS,
|
|
125
|
+
DATA_TABLE_SCROLL_SHADOW,
|
|
126
|
+
DataTable
|
|
92
127
|
};
|
|
93
|
-
//# sourceMappingURL=chunk-
|
|
128
|
+
//# sourceMappingURL=chunk-HU5W7CGE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/table.tsx","../src/components/table/use-scroll-shadow.ts","../src/components/table/data-table.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Extension of shadcn's `Table` registry component: exposes `containerClassName` so callers can\n * override the wrapping `<div>`'s default `overflow-auto`. We need this for sticky-header support\n * (sticky uses the nearest overflow ancestor as its containing block — when the actual scroll\n * container is one level out, the inner overflow-auto needs to be neutralised with\n * `overflow-visible` so sticky aligns with the real scrolling parent). */\nconst Table = React.forwardRef<\n HTMLTableElement,\n React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }\n>(({ className, containerClassName, ...props }, ref) => (\n <div className={cn(\"relative w-full overflow-auto\", containerClassName)}>\n <table ref={ref} className={cn(\"w-full caption-bottom text-sm\", className)} {...props} />\n </div>\n));\nTable.displayName = \"Table\";\n\nconst TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n);\nTableHeader.displayName = \"TableHeader\";\n\nconst TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tbody ref={ref} className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />\n )\n);\nTableBody.displayName = \"TableBody\";\n\nconst TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(\n ({ className, ...props }, ref) => (\n <tfoot ref={ref} className={cn(\"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\", className)} {...props} />\n )\n);\nTableFooter.displayName = \"TableFooter\";\n\nconst TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(\n ({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\", className)}\n {...props}\n />\n )\n);\nTableRow.displayName = \"TableRow\";\n\nconst TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n \"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",\n className\n )}\n {...props}\n />\n )\n);\nTableHead.displayName = \"TableHead\";\n\nconst TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(\n ({ className, ...props }, ref) => (\n <td ref={ref} className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)} {...props} />\n )\n);\nTableCell.displayName = \"TableCell\";\n\nconst TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(\n ({ className, ...props }, ref) => (\n <caption ref={ref} className={cn(\"mt-4 text-sm text-muted-foreground\", className)} {...props} />\n )\n);\nTableCaption.displayName = \"TableCaption\";\n\nexport { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\n\n/**\n * Tracks whether a scroll container has scrolled past its top edge, for sticky-header shadow effects.\n *\n * Returns `targetRef` (attach to the scroll container itself or any element inside it) and\n * `isScrolled` (true once the container's `scrollTop > 0`). On mount the hook walks up the DOM\n * from the target to find the nearest overflow ancestor (or uses the target itself if it already\n * has `overflowY: auto | scroll`), then listens to its `scroll` event.\n *\n * Used to surface a subtle \"scroll lift\" shadow under sticky headers so users know there is\n * content hidden above.\n *\n * **Why a scroll listener and not a sentinel + IntersectionObserver:** an earlier version used a\n * 1px sentinel as the first child of the scroll container. The sentinel's height pushed the thead\n * down by one pixel in normal flow, and as soon as the user scrolled the thead snapped up to its\n * sticky position — a visible 1px jitter. A scroll listener has no layout footprint.\n *\n * `passive: true` so the listener doesn't block native scroll smoothness; we never call\n * `preventDefault`.\n */\nexport function useScrollShadow(): {\n targetRef: RefObject<HTMLDivElement | null>;\n isScrolled: boolean;\n} {\n const targetRef = useRef<HTMLDivElement>(null);\n const [isScrolled, setIsScrolled] = useState(false);\n\n useEffect(() => {\n const target = targetRef.current;\n if (!target) return;\n\n let scrollContainer: HTMLElement | null = target;\n while (scrollContainer) {\n const overflowY = getComputedStyle(scrollContainer).overflowY;\n if (overflowY === \"auto\" || overflowY === \"scroll\") break;\n scrollContainer = scrollContainer.parentElement;\n }\n if (!scrollContainer) return;\n\n setIsScrolled(scrollContainer.scrollTop > 0);\n\n const handleScroll = () => {\n if (!scrollContainer) return;\n setIsScrolled(scrollContainer.scrollTop > 0);\n };\n scrollContainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => scrollContainer.removeEventListener(\"scroll\", handleScroll);\n }, []);\n\n return { targetRef, isScrolled };\n}\n","import type { ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Table, TableBody, TableHeader } from \"@/components/ui/table\";\nimport { useScrollShadow } from \"./use-scroll-shadow\";\n\n/**\n * Header cell class — compact, left-aligned, non-wrapping. Apply to each `<TableHead>` composed at\n * the call site so every clay data table shares one header treatment.\n */\nexport const DATA_TABLE_HEAD_CLASS = \"h-auto py-2 px-4 text-xs font-medium text-foreground whitespace-nowrap text-left\";\n\n/** Body cell class — roomy padding, top-aligned, left-aligned, min width so columns don't collapse. */\nexport const DATA_TABLE_CELL_CLASS = \"p-4 text-sm font-normal text-foreground align-top min-w-[150px] text-left\";\n\n/**\n * Data-row class matching the frosted-glass card scheme: `border-b-brown-30` overrides shadcn's\n * default `--color-border` (brown-40) with the lighter brown-30; hover lifts to opaque `bg-secondary`.\n */\nexport const DATA_TABLE_ROW_CLASS = \"border-b-brown-30 bg-background/70 hover:bg-secondary\";\n\n/** Layered drop-shadow for the scroll-lift effect on the sticky header. */\nexport const DATA_TABLE_SCROLL_SHADOW = \"0 1px 2px rgb(0 0 0 / 0.01), 0 6px 12px -2px rgb(0 0 0 / 0.03)\";\n\nexport interface DataTableProps {\n /**\n * Header row content — typically a single `<TableRow>` of `<TableHead>` cells (use\n * `DATA_TABLE_HEAD_CLASS`). Rendered inside the sticky, frosted `<TableHeader>` shell.\n */\n header: ReactNode;\n /** Body rows composed at the call site (`<TableRow>`/`<TableCell>`). */\n children: ReactNode;\n /** Max body height (px) before vertical scroll engages. Omit for no cap (grows to content). */\n maxBodyHeight?: number;\n /** Extra classes for the scroll container. */\n className?: string;\n}\n\n/**\n * Shared styled-table shell — the scroll container, sticky frosted (`bg-background/70\n * backdrop-blur-lg`) header, and scroll-lift shadow (via {@link useScrollShadow}) that give clay\n * data tables (table artifact, notification-draft recipients) one consistent look. Callers compose\n * the header cells and body rows at the call site; this is only the shell.\n */\nexport function DataTable({ header, children, maxBodyHeight, className }: DataTableProps): ReactNode {\n const { targetRef, isScrolled } = useScrollShadow();\n return (\n <div\n ref={targetRef}\n className={cn(\"overscroll-none\", className)}\n style={maxBodyHeight !== undefined ? { maxHeight: maxBodyHeight, overflowY: \"auto\" } : undefined}\n >\n <Table containerClassName=\"overflow-visible\">\n <TableHeader\n data-scrolled={isScrolled ? \"true\" : undefined}\n className=\"sticky top-0 z-10 bg-background/70 backdrop-blur-lg transition-shadow\"\n style={isScrolled ? { boxShadow: DATA_TABLE_SCROLL_SHADOW } : undefined}\n >\n {header}\n </TableHeader>\n <TableBody>{children}</TableBody>\n </Table>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx"]}
|