@shapesos/clay 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +46 -1
  2. package/dist/artifacts.cjs +741 -0
  3. package/dist/artifacts.cjs.map +1 -0
  4. package/dist/artifacts.d.cts +56 -0
  5. package/dist/artifacts.d.ts +56 -0
  6. package/dist/artifacts.js +23 -0
  7. package/dist/artifacts.js.map +1 -0
  8. package/dist/blocks.cjs +1282 -0
  9. package/dist/blocks.cjs.map +1 -0
  10. package/dist/blocks.css +2 -0
  11. package/dist/blocks.d.cts +41 -0
  12. package/dist/blocks.d.ts +41 -0
  13. package/dist/blocks.js +31 -0
  14. package/dist/blocks.js.map +1 -0
  15. package/dist/button.d.cts +2 -2
  16. package/dist/button.d.ts +2 -2
  17. package/dist/chat.cjs +755 -281
  18. package/dist/chat.cjs.map +1 -1
  19. package/dist/chat.d.cts +31 -10
  20. package/dist/chat.d.ts +31 -10
  21. package/dist/chat.js +8 -3
  22. package/dist/chunk-BX5TCEPR.js +436 -0
  23. package/dist/chunk-BX5TCEPR.js.map +1 -0
  24. package/dist/chunk-JGMN6W72.js +12 -0
  25. package/dist/chunk-JGMN6W72.js.map +1 -0
  26. package/dist/{chunk-27GJUWVN.js → chunk-JJUIBBBU.js} +14 -8
  27. package/dist/chunk-JJUIBBBU.js.map +1 -0
  28. package/dist/chunk-L35M3OD5.js +99 -0
  29. package/dist/chunk-L35M3OD5.js.map +1 -0
  30. package/dist/chunk-MEJESPTZ.js +1 -0
  31. package/dist/chunk-MEJESPTZ.js.map +1 -0
  32. package/dist/chunk-MXOPG747.js +238 -0
  33. package/dist/chunk-MXOPG747.js.map +1 -0
  34. package/dist/{chunk-MLCRDVQ2.js → chunk-OBOXCBDL.js} +13 -5
  35. package/dist/chunk-OBOXCBDL.js.map +1 -0
  36. package/dist/chunk-OUW6PUEB.js +223 -0
  37. package/dist/chunk-OUW6PUEB.js.map +1 -0
  38. package/dist/{chunk-TDMJUF4A.js → chunk-WPQQVKWY.js} +4 -4
  39. package/dist/icon.cjs +12 -4
  40. package/dist/icon.cjs.map +1 -1
  41. package/dist/icon.d.cts +19 -6
  42. package/dist/icon.d.ts +19 -6
  43. package/dist/icon.js +1 -1
  44. package/dist/index.cjs +789 -273
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +7 -3
  47. package/dist/index.d.ts +7 -3
  48. package/dist/index.js +61 -21
  49. package/dist/lottie.d.cts +2 -2
  50. package/dist/lottie.d.ts +2 -2
  51. package/dist/table.cjs +142 -0
  52. package/dist/table.cjs.map +1 -0
  53. package/dist/table.d.cts +44 -0
  54. package/dist/table.d.ts +44 -0
  55. package/dist/table.js +24 -0
  56. package/dist/table.js.map +1 -0
  57. package/dist/text-area.d.cts +2 -2
  58. package/dist/text-area.d.ts +2 -2
  59. package/dist/text-area.js +2 -2
  60. package/dist/types-3Gzk7cRt.d.cts +121 -0
  61. package/dist/types-3Gzk7cRt.d.ts +121 -0
  62. package/dist/types-C9XX-Uhk.d.ts +68 -0
  63. package/dist/types-DuuRI4ll.d.cts +68 -0
  64. package/dist/utils.cjs +14 -7
  65. package/dist/utils.cjs.map +1 -1
  66. package/dist/utils.d.cts +18 -1
  67. package/dist/utils.d.ts +18 -1
  68. package/dist/utils.js +6 -4
  69. package/package.json +34 -4
  70. package/dist/chunk-27GJUWVN.js.map +0 -1
  71. package/dist/chunk-A77BGJH4.js +0 -458
  72. package/dist/chunk-A77BGJH4.js.map +0 -1
  73. package/dist/chunk-MLCRDVQ2.js.map +0 -1
  74. /package/dist/{chunk-TDMJUF4A.js.map → chunk-WPQQVKWY.js.map} +0 -0
@@ -0,0 +1,223 @@
1
+ import {
2
+ Block,
3
+ BlockServices
4
+ } from "./chunk-MXOPG747.js";
5
+ import {
6
+ typographyMixin,
7
+ typographyTypes
8
+ } from "./chunk-SV24ONND.js";
9
+ import {
10
+ IconCheck,
11
+ IconCopy,
12
+ IconThumbDown,
13
+ IconThumbUp
14
+ } from "./chunk-BX5TCEPR.js";
15
+ import {
16
+ IconButton
17
+ } from "./chunk-OBOXCBDL.js";
18
+ import {
19
+ colors
20
+ } from "./chunk-JF3P66JF.js";
21
+
22
+ // src/components/chat/chat-message/chat-message.tsx
23
+ import { useMemo } from "react";
24
+
25
+ // src/components/chat/hooks/copy-message-text.ts
26
+ function copyMessageText(message) {
27
+ const parts = [];
28
+ for (const block of message.blocks) {
29
+ const service = BlockServices[block.type];
30
+ if (!service) continue;
31
+ if (!service.toClipboardText) continue;
32
+ const text = service.toClipboardText(block);
33
+ if (text) parts.push(text);
34
+ }
35
+ if (parts.length === 0) return message.fallbackText;
36
+ return parts.join("\n\n");
37
+ }
38
+
39
+ // src/components/chat/chat-message-actions/chat-message-actions.tsx
40
+ import { useCallback as useCallback2 } from "react";
41
+
42
+ // src/components/chat/chat-context/chat-context.ts
43
+ import { createContext, useContext } from "react";
44
+ var ChatContext = createContext(null);
45
+ function useChatContext() {
46
+ const context = useContext(ChatContext);
47
+ if (!context) {
48
+ throw new Error("useChatContext must be used within a Chat.Root component");
49
+ }
50
+ return context;
51
+ }
52
+
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
+ // src/components/chat/constants.ts
90
+ var MESSAGE_ROLE = {
91
+ USER: "user",
92
+ ASSISTANT: "assistant"
93
+ };
94
+
95
+ // src/components/chat/chat-message-actions/chat-message-actions-styles.ts
96
+ import styled from "styled-components";
97
+ var FADE_DURATION_MS = 150;
98
+ var ANIMATE_DURATION_MS = 200;
99
+ var ActionsContainer = styled.div`
100
+ display: flex;
101
+ opacity: ${({ $role }) => $role === MESSAGE_ROLE.ASSISTANT ? 1 : 0};
102
+ pointer-events: ${({ $role }) => $role === MESSAGE_ROLE.ASSISTANT ? "auto" : "none"};
103
+ transition: opacity ${FADE_DURATION_MS}ms ease;
104
+ `;
105
+ var AnimatedAction = styled.div`
106
+ display: flex;
107
+ overflow: hidden;
108
+ max-width: ${({ $visible }) => $visible ? "40px" : "0"};
109
+ opacity: ${({ $visible }) => $visible ? 1 : 0};
110
+ transition: max-width ${ANIMATE_DURATION_MS}ms ease, opacity ${ANIMATE_DURATION_MS}ms ease;
111
+ `;
112
+
113
+ // src/components/chat/chat-message-actions/chat-message-actions.tsx
114
+ import { jsx, jsxs } from "react/jsx-runtime";
115
+ function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
116
+ const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
117
+ const { isCopied, copy } = useCopyToClipboard();
118
+ const handleCopy = useCallback2(() => {
119
+ copy(content);
120
+ onCopyMessage?.(messageId);
121
+ }, [content, messageId, copy, onCopyMessage]);
122
+ const handleThumbUp = useCallback2(() => {
123
+ onThumbUpClick?.(messageId, isHelpful === true ? null : true);
124
+ }, [messageId, isHelpful, onThumbUpClick]);
125
+ const handleThumbDown = useCallback2(() => {
126
+ onThumbDownClick?.(messageId, isHelpful === false ? null : false);
127
+ }, [messageId, isHelpful, onThumbDownClick]);
128
+ const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
129
+ const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
130
+ return /* @__PURE__ */ jsxs(ActionsContainer, { $role: role, className, children: [
131
+ /* @__PURE__ */ jsx(
132
+ IconButton,
133
+ {
134
+ icon: isCopied ? IconCheck : IconCopy,
135
+ onClick: handleCopy,
136
+ "aria-label": isCopied ? "Copied" : "Copy message"
137
+ }
138
+ ),
139
+ hasFeedback && /* @__PURE__ */ jsx(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ jsx(
140
+ IconButton,
141
+ {
142
+ icon: IconThumbUp,
143
+ onClick: handleThumbUp,
144
+ isSelected: isHelpful === true,
145
+ "aria-label": "Good response"
146
+ }
147
+ ) }),
148
+ hasFeedback && /* @__PURE__ */ jsx(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ jsx(
149
+ IconButton,
150
+ {
151
+ icon: IconThumbDown,
152
+ onClick: handleThumbDown,
153
+ isSelected: isHelpful === false,
154
+ "aria-label": "Bad response"
155
+ }
156
+ ) })
157
+ ] });
158
+ }
159
+
160
+ // src/components/chat/chat-message/chat-message-styles.ts
161
+ import styled2 from "styled-components";
162
+ var MessageRow = styled2.div`
163
+ display: flex;
164
+ justify-content: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "flex-end" : "flex-start"};
165
+
166
+ &:hover ${ActionsContainer} {
167
+ opacity: 1;
168
+ pointer-events: auto;
169
+ }
170
+ `;
171
+ var MessageContainer = styled2.div`
172
+ display: flex;
173
+ flex-direction: column;
174
+ align-items: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "flex-end" : "flex-start"};
175
+ max-width: 90%;
176
+ width: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "auto" : "100%"};
177
+ gap: 12px;
178
+ `;
179
+ var MessageBubble = styled2.div`
180
+ ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
181
+ color: ${colors["brown-100"]};
182
+ background: ${({ $role }) => $role === MESSAGE_ROLE.USER ? colors.white : "transparent"};
183
+ padding: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "12px 16px" : "0"};
184
+ border-radius: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "16px" : "0"};
185
+ max-width: 100%;
186
+ width: 100%;
187
+ min-width: 0;
188
+ /* Vertical rhythm between consecutive blocks — renderer-owned, not agent-emitted.
189
+ * Industry standard (Claude, ChatGPT, Vercel AI Elements): the chat renderer applies
190
+ * spacing via CSS; agents emit semantic blocks only. Value matches the MessageContainer
191
+ * gap. TODO: replace 12px with a clay spacing token when a spacing scale is added. */
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: 12px;
195
+ `;
196
+
197
+ // src/components/chat/chat-message/chat-message.tsx
198
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
199
+ function ChatMessage({ message }) {
200
+ const clipboardText = useMemo(() => copyMessageText(message), [message]);
201
+ return /* @__PURE__ */ jsx2(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ jsxs2(MessageContainer, { $role: message.role, children: [
202
+ /* @__PURE__ */ jsx2(MessageBubble, { $role: message.role, "data-testid": "message-bubble", children: message.blocks.map((block, index) => /* @__PURE__ */ jsx2(Block, { block }, index)) }),
203
+ /* @__PURE__ */ jsx2(
204
+ ChatMessageActions,
205
+ {
206
+ messageId: message.id,
207
+ content: clipboardText,
208
+ role: message.role,
209
+ isHelpful: message.isHelpful
210
+ }
211
+ )
212
+ ] }) });
213
+ }
214
+
215
+ export {
216
+ copyMessageText,
217
+ ChatContext,
218
+ useChatContext,
219
+ useCopyToClipboard,
220
+ MESSAGE_ROLE,
221
+ ChatMessage
222
+ };
223
+ //# sourceMappingURL=chunk-OUW6PUEB.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/hooks/use-copy-to-clipboard.ts","../src/utils/clipboard.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","import { useState, useCallback, useRef, useEffect } from \"react\";\n\nimport { copyToClipboard } from \"@/utils/clipboard\";\n\nconst RESET_DELAY_MS = 2000;\n\ninterface UseCopyToClipboardResult {\n isCopied: boolean;\n copy: (text: string) => void;\n}\n\nexport function useCopyToClipboard(): UseCopyToClipboardResult {\n const [isCopied, setIsCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n // Write text verbatim — callers (copyMessageText + each BlockService.toClipboardText) are\n // responsible for converting their own format to clipboard-ready plain text.\n const copy = useCallback((text: string) => {\n copyToClipboard(text, {\n onSuccess: () => {\n setIsCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsCopied(false);\n timeoutRef.current = null;\n }, RESET_DELAY_MS);\n },\n });\n }, []);\n\n return { isCopied, copy };\n}\n","/** Options for the {@link copyToClipboard} function. */\ninterface CopyToClipboardOptions {\n /** Called after the text has been successfully written to the clipboard. */\n onSuccess?: () => void;\n /** Called if the clipboard write fails (e.g., permission denied). */\n onFailure?: (error: unknown) => void;\n}\n\n/**\n * Copies text to the system clipboard using the Clipboard API.\n * @param text - The string to copy.\n * @param options - Optional success/failure callbacks.\n */\nexport function copyToClipboard(text: string, options?: CopyToClipboardOptions): void {\n navigator.clipboard.writeText(text).then(\n () => options?.onSuccess?.(),\n (error) => options?.onFailure?.(error)\n );\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":["useCallback","useCallback","styled","styled","jsx","jsxs"]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ mergeRefs
3
+ } from "./chunk-KNYB3RL7.js";
1
4
  import {
2
5
  typographyMixin,
3
6
  typographyTypes
@@ -5,9 +8,6 @@ import {
5
8
  import {
6
9
  colors
7
10
  } from "./chunk-JF3P66JF.js";
8
- import {
9
- mergeRefs
10
- } from "./chunk-KNYB3RL7.js";
11
11
 
12
12
  // src/components/text-area/text-area.tsx
13
13
  import { forwardRef, useCallback, useLayoutEffect, useRef } from "react";
@@ -73,4 +73,4 @@ var TextArea = forwardRef(function TextArea2({ value, onChange, placeholder, max
73
73
  export {
74
74
  TextArea
75
75
  };
76
- //# sourceMappingURL=chunk-TDMJUF4A.js.map
76
+ //# sourceMappingURL=chunk-WPQQVKWY.js.map
package/dist/icon.cjs CHANGED
@@ -77,6 +77,9 @@ var Icon = (0, import_react.forwardRef)(function Icon2({ icon: IconComponent, si
77
77
  );
78
78
  });
79
79
 
80
+ // src/components/icon-button/icon-button.tsx
81
+ var import_react2 = require("react");
82
+
80
83
  // src/components/icon-button/icon-button-styles.ts
81
84
  var import_styled_components2 = __toESM(require("styled-components"), 1);
82
85
 
@@ -216,7 +219,7 @@ var ICON_SIZE_BY_BUTTON_SIZE = {
216
219
  small: 16,
217
220
  medium: 20
218
221
  };
219
- function IconButton({
222
+ var IconButton = (0, import_react2.forwardRef)(function IconButton2({
220
223
  icon,
221
224
  size = "small",
222
225
  variant = "short",
@@ -226,11 +229,14 @@ function IconButton({
226
229
  className,
227
230
  id,
228
231
  as,
229
- "aria-label": ariaLabel
230
- }) {
232
+ "aria-label": ariaLabel,
233
+ ...rest
234
+ }, ref) {
235
+ const isNativeButton = as === void 0 || as === "button";
231
236
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
232
237
  Button,
233
238
  {
239
+ ref,
234
240
  className,
235
241
  id,
236
242
  as,
@@ -240,10 +246,12 @@ function IconButton({
240
246
  disabled,
241
247
  onClick: disabled ? void 0 : onClick,
242
248
  "aria-label": ariaLabel,
249
+ ...isNativeButton ? { type: "button" } : {},
250
+ ...rest,
243
251
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { icon, size: ICON_SIZE_BY_BUTTON_SIZE[size] })
244
252
  }
245
253
  );
246
- }
254
+ });
247
255
  // Annotate the CommonJS export names for ESM import in node:
248
256
  0 && (module.exports = {
249
257
  Icon,
package/dist/icon.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/icon/index.ts","../src/components/icon/icon.tsx","../src/components/icon/icon-styles.ts","../src/components/icon-button/icon-button-styles.ts","../src/tokens/colors.ts","../src/components/icon-button/icon-button.tsx"],"sourcesContent":["export { Icon } from \"./icon\";\nexport type { IconProps } from \"./types\";\nexport { IconButton } from \"../icon-button/icon-button\";\nexport type { IconButtonProps, IconButtonSize, IconButtonVariant } from \"../icon-button/types\";\n","import { forwardRef } from \"react\";\n\nimport { IconWrapper, getStrokeWidth } from \"./icon-styles\";\nimport type { IconProps } from \"./types\";\n\nconst DEFAULT_SIZE = 16;\n\nexport const Icon = forwardRef<HTMLSpanElement, IconProps>(function Icon(\n { icon: IconComponent, size = DEFAULT_SIZE, color, className, \"aria-label\": ariaLabel },\n ref\n) {\n return (\n <IconWrapper\n ref={ref}\n className={className}\n $color={color}\n aria-label={ariaLabel}\n role={ariaLabel ? \"img\" : undefined}\n >\n <IconComponent width={size} height={size} strokeWidth={getStrokeWidth(size)} />\n </IconWrapper>\n );\n});\n","import styled from \"styled-components\";\n\nexport const STROKE_WIDTH_BY_SIZE: Record<number, number> = {\n 12: 1.4,\n 14: 1.4,\n 16: 1.8,\n 18: 1.8,\n 20: 1.8,\n 24: 1.8,\n};\n\nexport const DEFAULT_STROKE_WIDTH = 1.8;\n\nexport function getStrokeWidth(size: number): number {\n return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;\n}\n\nexport const IconWrapper = styled.span<{ $color?: string }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: ${({ $color }) => $color ?? \"currentColor\"};\n flex-shrink: 0;\n`;\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport type { IconButtonSize, IconButtonVariant } from \"./types\";\n\nconst PADDING: Record<IconButtonVariant, Record<IconButtonSize, string>> = {\n short: {\n small: \"4px\",\n medium: \"6px\",\n },\n long: {\n small: \"4px 6px\",\n medium: \"6px 10px\",\n },\n};\n\nconst BORDER_RADIUS: Record<IconButtonSize, number> = {\n small: 6,\n medium: 10,\n};\n\nexport const Button = styled.button<{\n $size: IconButtonSize;\n $variant: IconButtonVariant;\n $isSelected: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: ${({ $size, $variant }) => PADDING[$variant][$size]};\n border: none;\n border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : \"transparent\")};\n color: ${colors[\"brown-100\"]};\n cursor: pointer;\n transition: background-color 100ms ease;\n\n &:hover {\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : colors[\"brown-20\"])};\n }\n\n &:active {\n background: ${colors[\"brown-40\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n background: transparent;\n cursor: not-allowed;\n }\n`;\n","export const colors = {\n // White\n white: \"#FFFFFF\",\n \"white-alpha-85\": \"rgba(255, 255, 255, 0.85)\",\n \"white-alpha-40\": \"rgba(255, 255, 255, 0.40)\",\n\n // Brown\n \"brown-10\": \"#FAF9F8\",\n \"brown-20\": \"#F5F3F0\",\n \"brown-30\": \"#EEEBE5\",\n \"brown-40\": \"#E6E2DA\",\n \"brown-50\": \"#CFCBC4\",\n \"brown-60\": \"#A19E99\",\n \"brown-70\": \"#73716D\",\n \"brown-80\": \"#5C5A57\",\n \"brown-90\": \"#2E2D2C\",\n \"brown-100\": \"#171716\",\n \"brown-alpha-12\": \"rgba(23, 23, 22, 0.12)\",\n \"brown-alpha-20\": \"rgba(23, 23, 22, 0.20)\",\n \"brown-alpha-55\": \"rgba(23, 23, 22, 0.55)\",\n \"brown-alpha-70\": \"rgba(23, 23, 22, 0.70)\",\n \"brown-alpha-80\": \"rgba(23, 23, 22, 0.80)\",\n \"brown-alpha-90\": \"rgba(23, 23, 22, 0.90)\",\n\n // Fuchsia\n \"fuchsia-50\": \"#FAF5FF\",\n \"fuchsia-300\": \"#F0ABFC\",\n \"fuchsia-500\": \"#D946EF\",\n \"fuchsia-600\": \"#C026D3\",\n \"fuchsia-800\": \"#86198F\",\n\n // Pink\n \"pink-50\": \"#FDF2F8\",\n \"pink-400\": \"#F472B6\",\n \"pink-600\": \"#DB2777\",\n \"pink-800\": \"#9D174D\",\n\n // Violet\n \"violet-50\": \"#F5F3FF\",\n \"violet-400\": \"#A78BFA\",\n \"violet-600\": \"#7C3AED\",\n \"violet-800\": \"#5B21B6\",\n\n // Indigo\n \"indigo-50\": \"#EEF2FF\",\n \"indigo-400\": \"#818CF8\",\n \"indigo-500\": \"#6366F1\",\n \"indigo-600\": \"#4F46E5\",\n \"indigo-800\": \"#3730A3\",\n\n // Cyan\n \"cyan-50\": \"#E7FEFF\",\n \"cyan-300\": \"#67E8F9\",\n \"cyan-600\": \"#0891B2\",\n \"cyan-900\": \"#164E63\",\n\n // Teal\n \"teal-50\": \"#EBFDF9\",\n \"teal-300\": \"#5EEAD4\",\n \"teal-600\": \"#0D9488\",\n\n // Rose\n \"rose-400\": \"#FB7185\",\n\n // Purple\n \"purple-400\": \"#C084FC\",\n\n // Blue\n \"blue-50\": \"#EFF6FF\",\n \"blue-100\": \"#DBEAFE\",\n \"blue-300\": \"#93C5FD\",\n \"blue-400\": \"#60A5FA\",\n \"blue-600\": \"#2563EB\",\n \"blue-800\": \"#1E40AF\",\n\n // Sky\n \"sky-300\": \"#7DD3FC\",\n\n // Green\n \"green-50\": \"#ECF9F0\",\n \"green-100\": \"#D4F1D9\",\n \"green-400\": \"#3DC269\",\n \"green-700\": \"#277C43\",\n \"green-800\": \"#1C5930\",\n\n // Orange\n \"orange-50\": \"#FFF7ED\",\n \"orange-100\": \"#FFEDD5\",\n \"orange-200\": \"#FED7AA\",\n \"orange-500\": \"#F97316\",\n \"orange-600\": \"#EA580C\",\n\n // Red\n \"red-50\": \"#FEF2F2\",\n \"red-100\": \"#FEE2E2\",\n \"red-200\": \"#FECACA\",\n \"red-400\": \"#F7776C\",\n \"red-500\": \"#EF4444\",\n \"red-600\": \"#DC2626\",\n} as const;\n\nexport type ColorToken = keyof typeof colors;\n","import { Icon } from \"../icon/icon\";\nimport { Button } from \"./icon-button-styles\";\nimport type { IconButtonProps, IconButtonSize } from \"./types\";\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, number> = {\n small: 16,\n medium: 20,\n};\n\nexport function IconButton({\n icon,\n size = \"small\",\n variant = \"short\",\n isSelected = false,\n disabled = false,\n onClick,\n className,\n id,\n as,\n \"aria-label\": ariaLabel,\n}: IconButtonProps): React.ReactNode {\n return (\n <Button\n className={className}\n id={id}\n as={as}\n $size={size}\n $variant={variant}\n $isSelected={isSelected}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n aria-label={ariaLabel}\n >\n <Icon icon={icon} size={ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n}\n"],"mappings":"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","names":["styled","Icon","import_styled_components","styled","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/components/icon/index.ts","../src/components/icon/icon.tsx","../src/components/icon/icon-styles.ts","../src/components/icon-button/icon-button.tsx","../src/components/icon-button/icon-button-styles.ts","../src/tokens/colors.ts"],"sourcesContent":["export { Icon } from \"./icon\";\nexport type { IconProps } from \"./types\";\nexport { IconButton } from \"../icon-button/icon-button\";\nexport type { IconButtonProps, IconButtonSize, IconButtonVariant } from \"../icon-button/types\";\n","import { forwardRef } from \"react\";\n\nimport { IconWrapper, getStrokeWidth } from \"./icon-styles\";\nimport type { IconProps } from \"./types\";\n\nconst DEFAULT_SIZE = 16;\n\nexport const Icon = forwardRef<HTMLSpanElement, IconProps>(function Icon(\n { icon: IconComponent, size = DEFAULT_SIZE, color, className, \"aria-label\": ariaLabel },\n ref\n) {\n return (\n <IconWrapper\n ref={ref}\n className={className}\n $color={color}\n aria-label={ariaLabel}\n role={ariaLabel ? \"img\" : undefined}\n >\n <IconComponent width={size} height={size} strokeWidth={getStrokeWidth(size)} />\n </IconWrapper>\n );\n});\n","import styled from \"styled-components\";\n\nexport const STROKE_WIDTH_BY_SIZE: Record<number, number> = {\n 12: 1.4,\n 14: 1.4,\n 16: 1.8,\n 18: 1.8,\n 20: 1.8,\n 24: 1.8,\n};\n\nexport const DEFAULT_STROKE_WIDTH = 1.8;\n\nexport function getStrokeWidth(size: number): number {\n return STROKE_WIDTH_BY_SIZE[size] ?? DEFAULT_STROKE_WIDTH;\n}\n\nexport const IconWrapper = styled.span<{ $color?: string }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: ${({ $color }) => $color ?? \"currentColor\"};\n flex-shrink: 0;\n`;\n","import { forwardRef } from \"react\";\n\nimport { Icon } from \"../icon/icon\";\nimport { Button } from \"./icon-button-styles\";\nimport type { IconButtonProps, IconButtonSize } from \"./types\";\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, number> = {\n small: 16,\n medium: 20,\n};\n\n/** Ghost-style icon button. `forwardRef` + the rest-props spread so Radix `Slot`-based wrappers\n * (Tooltip, Popover, DropdownMenu) can attach refs and event handlers via `asChild`. */\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n {\n icon,\n size = \"small\",\n variant = \"short\",\n isSelected = false,\n disabled = false,\n onClick,\n className,\n id,\n as,\n \"aria-label\": ariaLabel,\n ...rest\n },\n ref\n) {\n // Only set `type=\"button\"` when rendering as a native button. Anchors (`as=\"a\"`) treat the\n // `type` attribute as a content-type hint for the linked resource — `type=\"button\"` on an\n // anchor is semantically wrong and shows up as an a11y / HTML-validation noise warning.\n const isNativeButton = as === undefined || as === \"button\";\n return (\n <Button\n ref={ref}\n className={className}\n id={id}\n as={as}\n $size={size}\n $variant={variant}\n $isSelected={isSelected}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n aria-label={ariaLabel}\n {...(isNativeButton ? { type: \"button\" as const } : {})}\n {...rest}\n >\n <Icon icon={icon} size={ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n});\n","import styled from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport type { IconButtonSize, IconButtonVariant } from \"./types\";\n\nconst PADDING: Record<IconButtonVariant, Record<IconButtonSize, string>> = {\n short: {\n small: \"4px\",\n medium: \"6px\",\n },\n long: {\n small: \"4px 6px\",\n medium: \"6px 10px\",\n },\n};\n\nconst BORDER_RADIUS: Record<IconButtonSize, number> = {\n small: 6,\n medium: 10,\n};\n\nexport const Button = styled.button<{\n $size: IconButtonSize;\n $variant: IconButtonVariant;\n $isSelected: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: ${({ $size, $variant }) => PADDING[$variant][$size]};\n border: none;\n border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : \"transparent\")};\n color: ${colors[\"brown-100\"]};\n cursor: pointer;\n transition: background-color 100ms ease;\n\n &:hover {\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : colors[\"brown-20\"])};\n }\n\n &:active {\n background: ${colors[\"brown-40\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n background: transparent;\n cursor: not-allowed;\n }\n`;\n","export const colors = {\n // White\n white: \"#FFFFFF\",\n \"white-alpha-85\": \"rgba(255, 255, 255, 0.85)\",\n \"white-alpha-40\": \"rgba(255, 255, 255, 0.40)\",\n\n // Brown\n \"brown-10\": \"#FAF9F8\",\n \"brown-20\": \"#F5F3F0\",\n \"brown-30\": \"#EEEBE5\",\n \"brown-40\": \"#E6E2DA\",\n \"brown-50\": \"#CFCBC4\",\n \"brown-60\": \"#A19E99\",\n \"brown-70\": \"#73716D\",\n \"brown-80\": \"#5C5A57\",\n \"brown-90\": \"#2E2D2C\",\n \"brown-100\": \"#171716\",\n \"brown-alpha-12\": \"rgba(23, 23, 22, 0.12)\",\n \"brown-alpha-20\": \"rgba(23, 23, 22, 0.20)\",\n \"brown-alpha-55\": \"rgba(23, 23, 22, 0.55)\",\n \"brown-alpha-70\": \"rgba(23, 23, 22, 0.70)\",\n \"brown-alpha-80\": \"rgba(23, 23, 22, 0.80)\",\n \"brown-alpha-90\": \"rgba(23, 23, 22, 0.90)\",\n\n // Fuchsia\n \"fuchsia-50\": \"#FAF5FF\",\n \"fuchsia-300\": \"#F0ABFC\",\n \"fuchsia-500\": \"#D946EF\",\n \"fuchsia-600\": \"#C026D3\",\n \"fuchsia-800\": \"#86198F\",\n\n // Pink\n \"pink-50\": \"#FDF2F8\",\n \"pink-400\": \"#F472B6\",\n \"pink-600\": \"#DB2777\",\n \"pink-800\": \"#9D174D\",\n\n // Violet\n \"violet-50\": \"#F5F3FF\",\n \"violet-400\": \"#A78BFA\",\n \"violet-600\": \"#7C3AED\",\n \"violet-800\": \"#5B21B6\",\n\n // Indigo\n \"indigo-50\": \"#EEF2FF\",\n \"indigo-400\": \"#818CF8\",\n \"indigo-500\": \"#6366F1\",\n \"indigo-600\": \"#4F46E5\",\n \"indigo-800\": \"#3730A3\",\n\n // Cyan\n \"cyan-50\": \"#E7FEFF\",\n \"cyan-300\": \"#67E8F9\",\n \"cyan-600\": \"#0891B2\",\n \"cyan-900\": \"#164E63\",\n\n // Teal\n \"teal-50\": \"#EBFDF9\",\n \"teal-300\": \"#5EEAD4\",\n \"teal-600\": \"#0D9488\",\n\n // Rose\n \"rose-400\": \"#FB7185\",\n\n // Purple\n \"purple-400\": \"#C084FC\",\n\n // Blue\n \"blue-50\": \"#EFF6FF\",\n \"blue-100\": \"#DBEAFE\",\n \"blue-300\": \"#93C5FD\",\n \"blue-400\": \"#60A5FA\",\n \"blue-600\": \"#2563EB\",\n \"blue-800\": \"#1E40AF\",\n\n // Sky\n \"sky-300\": \"#7DD3FC\",\n\n // Green\n \"green-50\": \"#ECF9F0\",\n \"green-100\": \"#D4F1D9\",\n \"green-400\": \"#3DC269\",\n \"green-700\": \"#277C43\",\n \"green-800\": \"#1C5930\",\n\n // Orange\n \"orange-50\": \"#FFF7ED\",\n \"orange-100\": \"#FFEDD5\",\n \"orange-200\": \"#FED7AA\",\n \"orange-500\": \"#F97316\",\n \"orange-600\": \"#EA580C\",\n\n // Red\n \"red-50\": \"#FEF2F2\",\n \"red-100\": \"#FEE2E2\",\n \"red-200\": \"#FECACA\",\n \"red-400\": \"#F7776C\",\n \"red-500\": \"#EF4444\",\n \"red-600\": \"#DC2626\",\n} as const;\n\nexport type ColorToken = keyof typeof colors;\n"],"mappings":"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","names":["styled","Icon","import_react","import_styled_components","styled","import_jsx_runtime","IconButton"]}
package/dist/icon.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import * as react from 'react';
2
- import { ComponentType, SVGProps, ElementType } from 'react';
1
+ import * as React from 'react';
2
+ import { ComponentType, SVGProps, ButtonHTMLAttributes, ElementType, AnchorHTMLAttributes } from 'react';
3
3
 
4
4
  /** Props for the Icon wrapper component. */
5
5
  interface IconProps {
@@ -15,14 +15,17 @@ interface IconProps {
15
15
  "aria-label"?: string;
16
16
  }
17
17
 
18
- declare const Icon: react.ForwardRefExoticComponent<IconProps & react.RefAttributes<HTMLSpanElement>>;
18
+ declare const Icon: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<HTMLSpanElement>>;
19
19
 
20
20
  /** Available button sizes. "small" = 28px tap target, "medium" = 32px. */
21
21
  type IconButtonSize = "small" | "medium";
22
22
  /** Button shape variant. "short" = square (default), "long" = pill / wider hit area. */
23
23
  type IconButtonVariant = "short" | "long";
24
- /** Props for the IconButton ghost-style button component. */
25
- interface IconButtonProps {
24
+ /** Props for the IconButton ghost-style button component. Extends the native button attribute set
25
+ * (minus the props we own — `disabled`, `onClick`, `className`, `id`, `aria-label`) so consumers
26
+ * can wire DOM-level events through, e.g. when wrapping IconButton in Radix's `Tooltip` /
27
+ * `Popover` / `DropdownMenu` (which pass `onPointerEnter`, `onFocus`, `onBlur`, etc. via Slot). */
28
+ interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "disabled" | "onClick" | "className" | "id" | "aria-label" | "type"> {
26
29
  /** The SVG icon component to render inside the button. */
27
30
  icon: ComponentType<SVGProps<SVGSVGElement>>;
28
31
  /** Button tap-target size. @default "small" */
@@ -43,8 +46,18 @@ interface IconButtonProps {
43
46
  as?: ElementType;
44
47
  /** Accessible label for the button action. */
45
48
  "aria-label"?: string;
49
+ /**
50
+ * Anchor-only attributes forwarded when `as="a"`. Optional on every shape so the type works
51
+ * polymorphically; the underlying element ignores them when rendering as a native `<button>`.
52
+ */
53
+ href?: AnchorHTMLAttributes<HTMLAnchorElement>["href"];
54
+ target?: AnchorHTMLAttributes<HTMLAnchorElement>["target"];
55
+ rel?: AnchorHTMLAttributes<HTMLAnchorElement>["rel"];
56
+ download?: AnchorHTMLAttributes<HTMLAnchorElement>["download"];
46
57
  }
47
58
 
48
- declare function IconButton({ icon, size, variant, isSelected, disabled, onClick, className, id, as, "aria-label": ariaLabel, }: IconButtonProps): React.ReactNode;
59
+ /** Ghost-style icon button. `forwardRef` + the rest-props spread so Radix `Slot`-based wrappers
60
+ * (Tooltip, Popover, DropdownMenu) can attach refs and event handlers via `asChild`. */
61
+ declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>>;
49
62
 
50
63
  export { Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconProps };
package/dist/icon.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as react from 'react';
2
- import { ComponentType, SVGProps, ElementType } from 'react';
1
+ import * as React from 'react';
2
+ import { ComponentType, SVGProps, ButtonHTMLAttributes, ElementType, AnchorHTMLAttributes } from 'react';
3
3
 
4
4
  /** Props for the Icon wrapper component. */
5
5
  interface IconProps {
@@ -15,14 +15,17 @@ interface IconProps {
15
15
  "aria-label"?: string;
16
16
  }
17
17
 
18
- declare const Icon: react.ForwardRefExoticComponent<IconProps & react.RefAttributes<HTMLSpanElement>>;
18
+ declare const Icon: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<HTMLSpanElement>>;
19
19
 
20
20
  /** Available button sizes. "small" = 28px tap target, "medium" = 32px. */
21
21
  type IconButtonSize = "small" | "medium";
22
22
  /** Button shape variant. "short" = square (default), "long" = pill / wider hit area. */
23
23
  type IconButtonVariant = "short" | "long";
24
- /** Props for the IconButton ghost-style button component. */
25
- interface IconButtonProps {
24
+ /** Props for the IconButton ghost-style button component. Extends the native button attribute set
25
+ * (minus the props we own — `disabled`, `onClick`, `className`, `id`, `aria-label`) so consumers
26
+ * can wire DOM-level events through, e.g. when wrapping IconButton in Radix's `Tooltip` /
27
+ * `Popover` / `DropdownMenu` (which pass `onPointerEnter`, `onFocus`, `onBlur`, etc. via Slot). */
28
+ interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "disabled" | "onClick" | "className" | "id" | "aria-label" | "type"> {
26
29
  /** The SVG icon component to render inside the button. */
27
30
  icon: ComponentType<SVGProps<SVGSVGElement>>;
28
31
  /** Button tap-target size. @default "small" */
@@ -43,8 +46,18 @@ interface IconButtonProps {
43
46
  as?: ElementType;
44
47
  /** Accessible label for the button action. */
45
48
  "aria-label"?: string;
49
+ /**
50
+ * Anchor-only attributes forwarded when `as="a"`. Optional on every shape so the type works
51
+ * polymorphically; the underlying element ignores them when rendering as a native `<button>`.
52
+ */
53
+ href?: AnchorHTMLAttributes<HTMLAnchorElement>["href"];
54
+ target?: AnchorHTMLAttributes<HTMLAnchorElement>["target"];
55
+ rel?: AnchorHTMLAttributes<HTMLAnchorElement>["rel"];
56
+ download?: AnchorHTMLAttributes<HTMLAnchorElement>["download"];
46
57
  }
47
58
 
48
- declare function IconButton({ icon, size, variant, isSelected, disabled, onClick, className, id, as, "aria-label": ariaLabel, }: IconButtonProps): React.ReactNode;
59
+ /** Ghost-style icon button. `forwardRef` + the rest-props spread so Radix `Slot`-based wrappers
60
+ * (Tooltip, Popover, DropdownMenu) can attach refs and event handlers via `asChild`. */
61
+ declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>>;
49
62
 
50
63
  export { Icon, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconProps };
package/dist/icon.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./chunk-72TJUKMV.js";
2
2
  import {
3
3
  IconButton
4
- } from "./chunk-MLCRDVQ2.js";
4
+ } from "./chunk-OBOXCBDL.js";
5
5
  import {
6
6
  Icon
7
7
  } from "./chunk-OLJIJYB5.js";