@shapesos/clay 0.9.0 → 0.10.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 +46 -1
- package/dist/blocks.cjs +650 -0
- package/dist/blocks.cjs.map +1 -0
- package/dist/blocks.d.cts +29 -0
- package/dist/blocks.d.ts +29 -0
- package/dist/blocks.js +21 -0
- package/dist/blocks.js.map +1 -0
- package/dist/chat.cjs +89 -47
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +7 -3
- package/dist/chat.d.ts +7 -3
- package/dist/chat.js +3 -2
- package/dist/chunk-OKPNST44.js +1 -0
- package/dist/chunk-OKPNST44.js.map +1 -0
- package/dist/chunk-R3BGPOAM.js +230 -0
- package/dist/chunk-R3BGPOAM.js.map +1 -0
- package/dist/{chunk-TDMJUF4A.js → chunk-WPQQVKWY.js} +4 -4
- package/dist/{chunk-A77BGJH4.js → chunk-WS4IPADR.js} +34 -201
- package/dist/chunk-WS4IPADR.js.map +1 -0
- package/dist/index.cjs +129 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +27 -12
- package/dist/text-area.js +2 -2
- package/dist/types-Q9aqd9nq.d.cts +34 -0
- package/dist/types-Q9aqd9nq.d.ts +34 -0
- package/package.json +6 -1
- package/dist/chunk-A77BGJH4.js.map +0 -1
- /package/dist/{chunk-TDMJUF4A.js.map → chunk-WPQQVKWY.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/blocks/block-context/block-context.tsx","../src/components/blocks/types.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["import { createContext, useContext, type ComponentType } from \"react\";\n\n/** Renderer-level concerns provided to descendants of any `Block`-rendering tree. */\nexport interface BlockContextValue {\n /** Optional component rendered next to each table in a TEXT block. Receives the zero-based table index. */\n TableActions?: ComponentType<{ tableIndex: number }>;\n}\n\nexport const BlockContext = createContext<BlockContextValue | null>(null);\n\n/** Returns the ambient block-rendering context. Returns an empty object outside a `<BlockContext.Provider>`. */\nexport function useBlockContext(): BlockContextValue {\n return useContext(BlockContext) ?? {};\n}\n","import type { ComponentType } from \"react\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete renderer. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n}\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable, type TableCounter } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n const counter: TableCounter = { value: 0 };\n\n const markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} counter={counter} />\n )) as React.ComponentType<unknown>,\n };\n\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n\nexport const TableActionsWrapper = styled.div`\n display: flex;\n justify-content: flex-start;\n margin-block: 8px 0;\n`;\n\nexport const TableActionsDivider = styled.div`\n height: 1px;\n background: ${colors[\"brown-40\"]};\n margin-block: 8px 0;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { useBlockContext } from \"../block-context/block-context\";\nimport { TableActionsWrapper, TableActionsDivider, TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Mutable counter used to assign sequential table indices during a single render pass. */\nexport interface TableCounter {\n /** The current value; incremented each time a table component is rendered. */\n value: number;\n}\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n /** Mutable counter for tracking table indices. */\n counter?: TableCounter;\n}\n\n/** Renders a table inside a horizontally scrollable wrapper with optional actions below. */\nexport function ScrollableTable({ node: _node, counter, ...props }: ScrollableTableProps): ReactNode {\n const currentIndex = counter ? counter.value : 0;\n if (counter) {\n // eslint-disable-next-line react-hooks/immutability -- Counter is an intentionally mutable shared object for sequential table indexing\n counter.value += 1;\n }\n\n const { TableActions } = useBlockContext();\n\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n {TableActions && (\n <>\n <TableActionsWrapper>\n <TableActions tableIndex={currentIndex} />\n </TableActionsWrapper>\n <TableActionsDivider />\n </>\n )}\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n};\n","import type { BlockService, BlockType } from \"../types\";\nimport { TextBlockService } from \"./text-block-service\";\n\nexport const BlockServices: Record<BlockType, BlockService> = {\n [TextBlockService.type]: TextBlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/** Dispatches a single block to its registered concrete component. Unknown types render as a no-op. */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["styled","styled","jsx","jsx"]}
|
|
@@ -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-
|
|
76
|
+
//# sourceMappingURL=chunk-WPQQVKWY.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
markdownToPlainText
|
|
3
|
+
} from "./chunk-NMKKU2UG.js";
|
|
4
|
+
import {
|
|
5
|
+
Block,
|
|
6
|
+
BlockContext
|
|
7
|
+
} from "./chunk-R3BGPOAM.js";
|
|
1
8
|
import {
|
|
2
9
|
IconButton
|
|
3
10
|
} from "./chunk-MLCRDVQ2.js";
|
|
@@ -8,17 +15,9 @@ import {
|
|
|
8
15
|
import {
|
|
9
16
|
colors
|
|
10
17
|
} from "./chunk-JF3P66JF.js";
|
|
11
|
-
import {
|
|
12
|
-
markdownToPlainText
|
|
13
|
-
} from "./chunk-NMKKU2UG.js";
|
|
14
18
|
|
|
15
|
-
// src/components/chat/chat-message
|
|
16
|
-
import
|
|
17
|
-
import remarkBreaks from "remark-breaks";
|
|
18
|
-
import remarkGfm from "remark-gfm";
|
|
19
|
-
|
|
20
|
-
// src/components/chat/scrollable-table/scrollable-table.tsx
|
|
21
|
-
import { useContext as useContext2 } from "react";
|
|
19
|
+
// src/components/chat/chat-message/chat-message.tsx
|
|
20
|
+
import { useMemo } from "react";
|
|
22
21
|
|
|
23
22
|
// src/components/chat/chat-context/chat-context.ts
|
|
24
23
|
import { createContext, useContext } from "react";
|
|
@@ -31,179 +30,6 @@ function useChatContext() {
|
|
|
31
30
|
return context;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
// src/components/chat/scrollable-table/scrollable-table-styles.ts
|
|
35
|
-
import styled from "styled-components";
|
|
36
|
-
var TableContainer = styled.div`
|
|
37
|
-
position: relative;
|
|
38
|
-
margin-block: 8px 16px;
|
|
39
|
-
`;
|
|
40
|
-
var TableScroll = styled.div`
|
|
41
|
-
width: 100%;
|
|
42
|
-
overflow-x: auto;
|
|
43
|
-
overflow-y: hidden;
|
|
44
|
-
`;
|
|
45
|
-
var TableActionsWrapper = styled.div`
|
|
46
|
-
display: flex;
|
|
47
|
-
justify-content: flex-start;
|
|
48
|
-
margin-block: 8px 0;
|
|
49
|
-
`;
|
|
50
|
-
var TableActionsDivider = styled.div`
|
|
51
|
-
height: 1px;
|
|
52
|
-
background: ${colors["brown-40"]};
|
|
53
|
-
margin-block: 8px 0;
|
|
54
|
-
`;
|
|
55
|
-
|
|
56
|
-
// src/components/chat/scrollable-table/scrollable-table.tsx
|
|
57
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
58
|
-
function ScrollableTable({ node: _node, messageId, counter, ...props }) {
|
|
59
|
-
const currentIndex = counter ? counter.value : 0;
|
|
60
|
-
if (counter) {
|
|
61
|
-
counter.value += 1;
|
|
62
|
-
}
|
|
63
|
-
const context = useContext2(ChatContext);
|
|
64
|
-
const TableActions = context?.TableActions;
|
|
65
|
-
return /* @__PURE__ */ jsxs(TableContainer, { children: [
|
|
66
|
-
/* @__PURE__ */ jsx(TableScroll, { children: /* @__PURE__ */ jsx("table", { ...props }) }),
|
|
67
|
-
TableActions && messageId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
-
/* @__PURE__ */ jsx(TableActionsWrapper, { children: /* @__PURE__ */ jsx(TableActions, { messageId, tableIndex: currentIndex }) }),
|
|
69
|
-
/* @__PURE__ */ jsx(TableActionsDivider, {})
|
|
70
|
-
] })
|
|
71
|
-
] });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// src/components/chat/chat-message-content/chat-message-content-styles.ts
|
|
75
|
-
import styled2 from "styled-components";
|
|
76
|
-
var ContentWrapper = styled2.div`
|
|
77
|
-
${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
|
|
78
|
-
color: ${colors["brown-100"]};
|
|
79
|
-
word-break: break-word;
|
|
80
|
-
|
|
81
|
-
& h1 {
|
|
82
|
-
${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};
|
|
83
|
-
margin-block: 20px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
& h2 {
|
|
87
|
-
${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};
|
|
88
|
-
margin-block: 12px;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
& h3,
|
|
92
|
-
& h4,
|
|
93
|
-
& h5,
|
|
94
|
-
& h6 {
|
|
95
|
-
${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};
|
|
96
|
-
margin-block: 8px;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
& strong {
|
|
100
|
-
${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
& p {
|
|
104
|
-
margin-block: 0px;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
& p + p {
|
|
108
|
-
margin-block: 8px 0px;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
& ul,
|
|
112
|
-
& ol {
|
|
113
|
-
margin-block: 16px;
|
|
114
|
-
padding-left: 20px;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
& li + li {
|
|
118
|
-
margin-block: 4px;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
& ol {
|
|
122
|
-
list-style-type: decimal;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
& ol ol {
|
|
126
|
-
list-style-type: lower-alpha;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
& ol ol ol {
|
|
130
|
-
list-style-type: lower-roman;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
& a {
|
|
134
|
-
color: inherit;
|
|
135
|
-
text-decoration-line: underline;
|
|
136
|
-
text-decoration-style: dotted;
|
|
137
|
-
text-decoration-thickness: auto;
|
|
138
|
-
text-underline-offset: auto;
|
|
139
|
-
text-underline-position: from-font;
|
|
140
|
-
transition: color 75ms ease-in-out;
|
|
141
|
-
|
|
142
|
-
&:hover {
|
|
143
|
-
color: ${colors["blue-600"]};
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
& hr {
|
|
148
|
-
border: none;
|
|
149
|
-
border-top: 1px solid ${colors["brown-40"]};
|
|
150
|
-
margin-block: 20px;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
& table {
|
|
154
|
-
margin: 0;
|
|
155
|
-
width: 100%;
|
|
156
|
-
border-collapse: collapse;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
& th,
|
|
160
|
-
& td {
|
|
161
|
-
border: none;
|
|
162
|
-
border-bottom: 1px solid ${colors["brown-40"]};
|
|
163
|
-
text-align: left;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
& th {
|
|
167
|
-
${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};
|
|
168
|
-
padding: 8px;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
& td {
|
|
172
|
-
${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};
|
|
173
|
-
padding: 16px 8px;
|
|
174
|
-
min-width: 150px;
|
|
175
|
-
width: 1%;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
& td strong {
|
|
179
|
-
font-weight: inherit;
|
|
180
|
-
font-size: inherit;
|
|
181
|
-
line-height: inherit;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
& blockquote {
|
|
185
|
-
margin-block: 4px;
|
|
186
|
-
margin-inline: 0px;
|
|
187
|
-
padding-left: 16px;
|
|
188
|
-
border-left: 4px solid ${colors["brown-40"]};
|
|
189
|
-
color: ${colors["brown-80"]};
|
|
190
|
-
}
|
|
191
|
-
`;
|
|
192
|
-
|
|
193
|
-
// src/components/chat/chat-message-content/chat-message-content.tsx
|
|
194
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
195
|
-
function ChatMessageContent({ content, messageId }) {
|
|
196
|
-
const counter = { value: 0 };
|
|
197
|
-
const sanitizedContent = content.split("\n").filter((line) => {
|
|
198
|
-
const t = line.trim();
|
|
199
|
-
return !(t.startsWith("<!-- table-title:") && t.endsWith("-->"));
|
|
200
|
-
}).join("\n");
|
|
201
|
-
const markdownComponents = {
|
|
202
|
-
table: ((tableProps) => /* @__PURE__ */ jsx2(ScrollableTable, { ...tableProps, messageId, counter }))
|
|
203
|
-
};
|
|
204
|
-
return /* @__PURE__ */ jsx2(ContentWrapper, { children: /* @__PURE__ */ jsx2(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkBreaks], components: markdownComponents, children: sanitizedContent }) });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
33
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
208
34
|
import { useCallback as useCallback2 } from "react";
|
|
209
35
|
|
|
@@ -322,16 +148,16 @@ var MESSAGE_ROLE = {
|
|
|
322
148
|
};
|
|
323
149
|
|
|
324
150
|
// src/components/chat/chat-message-actions/chat-message-actions-styles.ts
|
|
325
|
-
import
|
|
151
|
+
import styled from "styled-components";
|
|
326
152
|
var FADE_DURATION_MS = 150;
|
|
327
153
|
var ANIMATE_DURATION_MS = 200;
|
|
328
|
-
var ActionsContainer =
|
|
154
|
+
var ActionsContainer = styled.div`
|
|
329
155
|
display: flex;
|
|
330
156
|
opacity: ${({ $role }) => $role === MESSAGE_ROLE.ASSISTANT ? 1 : 0};
|
|
331
157
|
pointer-events: ${({ $role }) => $role === MESSAGE_ROLE.ASSISTANT ? "auto" : "none"};
|
|
332
158
|
transition: opacity ${FADE_DURATION_MS}ms ease;
|
|
333
159
|
`;
|
|
334
|
-
var AnimatedAction =
|
|
160
|
+
var AnimatedAction = styled.div`
|
|
335
161
|
display: flex;
|
|
336
162
|
overflow: hidden;
|
|
337
163
|
max-width: ${({ $visible }) => $visible ? "40px" : "0"};
|
|
@@ -340,7 +166,7 @@ var AnimatedAction = styled3.div`
|
|
|
340
166
|
`;
|
|
341
167
|
|
|
342
168
|
// src/components/chat/chat-message-actions/chat-message-actions.tsx
|
|
343
|
-
import { jsx
|
|
169
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
344
170
|
function ChatMessageActions({ className, messageId, content, role, isHelpful }) {
|
|
345
171
|
const { onCopyMessage, onThumbUpClick, onThumbDownClick } = useChatContext();
|
|
346
172
|
const { isCopied, copy } = useCopyToClipboard();
|
|
@@ -356,8 +182,8 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
356
182
|
}, [messageId, isHelpful, onThumbDownClick]);
|
|
357
183
|
const isAssistant = role === MESSAGE_ROLE.ASSISTANT;
|
|
358
184
|
const hasFeedback = isAssistant && (onThumbUpClick || onThumbDownClick);
|
|
359
|
-
return /* @__PURE__ */
|
|
360
|
-
/* @__PURE__ */
|
|
185
|
+
return /* @__PURE__ */ jsxs(ActionsContainer, { $role: role, className, children: [
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
361
187
|
IconButton,
|
|
362
188
|
{
|
|
363
189
|
icon: isCopied ? IconCheck : IconCopy,
|
|
@@ -365,7 +191,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
365
191
|
"aria-label": isCopied ? "Copied" : "Copy message"
|
|
366
192
|
}
|
|
367
193
|
),
|
|
368
|
-
hasFeedback && /* @__PURE__ */
|
|
194
|
+
hasFeedback && /* @__PURE__ */ jsx(AnimatedAction, { $visible: isHelpful !== false, children: /* @__PURE__ */ jsx(
|
|
369
195
|
IconButton,
|
|
370
196
|
{
|
|
371
197
|
icon: IconThumbUp,
|
|
@@ -374,7 +200,7 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
374
200
|
"aria-label": "Good response"
|
|
375
201
|
}
|
|
376
202
|
) }),
|
|
377
|
-
hasFeedback && /* @__PURE__ */
|
|
203
|
+
hasFeedback && /* @__PURE__ */ jsx(AnimatedAction, { $visible: isHelpful !== true, children: /* @__PURE__ */ jsx(
|
|
378
204
|
IconButton,
|
|
379
205
|
{
|
|
380
206
|
icon: IconThumbDown,
|
|
@@ -387,8 +213,8 @@ function ChatMessageActions({ className, messageId, content, role, isHelpful })
|
|
|
387
213
|
}
|
|
388
214
|
|
|
389
215
|
// src/components/chat/chat-message/chat-message-styles.ts
|
|
390
|
-
import
|
|
391
|
-
var MessageRow =
|
|
216
|
+
import styled2 from "styled-components";
|
|
217
|
+
var MessageRow = styled2.div`
|
|
392
218
|
display: flex;
|
|
393
219
|
justify-content: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "flex-end" : "flex-start"};
|
|
394
220
|
|
|
@@ -397,7 +223,7 @@ var MessageRow = styled4.div`
|
|
|
397
223
|
pointer-events: auto;
|
|
398
224
|
}
|
|
399
225
|
`;
|
|
400
|
-
var MessageContainer =
|
|
226
|
+
var MessageContainer = styled2.div`
|
|
401
227
|
display: flex;
|
|
402
228
|
flex-direction: column;
|
|
403
229
|
align-items: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "flex-end" : "flex-start"};
|
|
@@ -405,7 +231,7 @@ var MessageContainer = styled4.div`
|
|
|
405
231
|
width: ${({ $role }) => $role === MESSAGE_ROLE.USER ? "auto" : "100%"};
|
|
406
232
|
gap: 12px;
|
|
407
233
|
`;
|
|
408
|
-
var MessageBubble =
|
|
234
|
+
var MessageBubble = styled2.div`
|
|
409
235
|
${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};
|
|
410
236
|
color: ${colors["brown-100"]};
|
|
411
237
|
background: ${({ $role }) => $role === MESSAGE_ROLE.USER ? colors.white : "transparent"};
|
|
@@ -416,15 +242,22 @@ var MessageBubble = styled4.div`
|
|
|
416
242
|
`;
|
|
417
243
|
|
|
418
244
|
// src/components/chat/chat-message/chat-message.tsx
|
|
419
|
-
import { jsx as
|
|
245
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
420
246
|
function ChatMessage({ message }) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
247
|
+
const { TableActions: ChatTableActions } = useChatContext();
|
|
248
|
+
const blockContextValue = useMemo(
|
|
249
|
+
() => ({
|
|
250
|
+
TableActions: ChatTableActions ? ({ tableIndex }) => /* @__PURE__ */ jsx2(ChatTableActions, { messageId: message.id, tableIndex }) : void 0
|
|
251
|
+
}),
|
|
252
|
+
[ChatTableActions, message.id]
|
|
253
|
+
);
|
|
254
|
+
return /* @__PURE__ */ jsx2(MessageRow, { $role: message.role, role: "article", "aria-label": `${message.role} message`, children: /* @__PURE__ */ jsxs2(MessageContainer, { $role: message.role, children: [
|
|
255
|
+
/* @__PURE__ */ jsx2(MessageBubble, { $role: message.role, children: /* @__PURE__ */ jsx2(BlockContext.Provider, { value: blockContextValue, children: message.blocks.map((block, index) => /* @__PURE__ */ jsx2(Block, { block }, index)) }) }),
|
|
256
|
+
/* @__PURE__ */ jsx2(
|
|
424
257
|
ChatMessageActions,
|
|
425
258
|
{
|
|
426
259
|
messageId: message.id,
|
|
427
|
-
content: message.
|
|
260
|
+
content: message.fallbackText,
|
|
428
261
|
role: message.role,
|
|
429
262
|
isHelpful: message.isHelpful
|
|
430
263
|
}
|
|
@@ -455,4 +288,4 @@ export {
|
|
|
455
288
|
* See the LICENSE file in the root directory of this source tree.
|
|
456
289
|
*)
|
|
457
290
|
*/
|
|
458
|
-
//# sourceMappingURL=chunk-
|
|
291
|
+
//# sourceMappingURL=chunk-WS4IPADR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat/chat-message/chat-message.tsx","../src/components/chat/chat-context/chat-context.ts","../src/components/chat/chat-message-actions/chat-message-actions.tsx","../node_modules/@tabler/icons-react/src/defaultAttributes.ts","../node_modules/@tabler/icons-react/src/createReactComponent.ts","../node_modules/@tabler/icons-react/src/icons/IconCheck.ts","../node_modules/@tabler/icons-react/src/icons/IconCopy.ts","../node_modules/@tabler/icons-react/src/icons/IconThumbDown.ts","../node_modules/@tabler/icons-react/src/icons/IconThumbUp.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 { BlockContext, type BlockContextValue } from \"@/components/blocks/block-context\";\nimport { useChatContext } from \"../chat-context/chat-context\";\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 const { TableActions: ChatTableActions } = useChatContext();\n\n // Adapt the chat-shaped TableActions ({ messageId, tableIndex }) into the generic block-shaped\n // signature ({ tableIndex }) by binding this message's id. The blocks namespace stays\n // chat-agnostic; chat owns the messageId-to-action correlation.\n const blockContextValue = useMemo<BlockContextValue>(\n () => ({\n TableActions: ChatTableActions\n ? ({ tableIndex }) => <ChatTableActions messageId={message.id} tableIndex={tableIndex} />\n : undefined,\n }),\n [ChatTableActions, message.id]\n );\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}>\n <BlockContext.Provider value={blockContextValue}>\n {message.blocks.map((block, index) => (\n <Block key={index} block={block} />\n ))}\n </BlockContext.Provider>\n </MessageBubble>\n <ChatMessageActions\n messageId={message.id}\n content={message.fallbackText}\n role={message.role}\n isHelpful={message.isHelpful}\n />\n </MessageContainer>\n </MessageRow>\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 { 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","export default {\n outline: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n },\n filled: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'currentColor',\n stroke: 'none',\n },\n};\n","import { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport type { IconNode, IconProps, Icon } from './types';\n\nconst createReactComponent = (\n type: 'outline' | 'filled',\n iconName: string,\n iconNamePascal: string,\n iconNode: IconNode,\n) => {\n const Component = forwardRef<SVGSVGElement, IconProps>(\n (\n { color = 'currentColor', size = 24, stroke = 2, title, className, children, ...rest }: IconProps,\n ref,\n ) =>\n createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes[type],\n width: size,\n height: size,\n className: [`tabler-icon`, `tabler-icon-${iconName}`, className].join(' '),\n ...(type === 'filled'\n ? {\n fill: color,\n }\n : {\n strokeWidth: stroke,\n stroke: color,\n }),\n ...rest,\n },\n [\n title && createElement('title', { key: 'svg-title' }, title),\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n ),\n );\n\n Component.displayName = `${iconNamePascal}`;\n\n return Component;\n};\n\nexport default createReactComponent;\n","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"path\",{\"d\":\"M5 12l5 5l10 -10\",\"key\":\"svg-0\"}]]\n\nconst IconCheck = createReactComponent('outline', 'check', 'Check', __iconNode);\n\nexport default IconCheck;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconCopy = createReactComponent('outline', 'copy', 'Copy', __iconNode);\n\nexport default IconCopy;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconThumbDown = createReactComponent('outline', 'thumb-down', 'ThumbDown', __iconNode);\n\nexport default IconThumbDown;","import createReactComponent from '../createReactComponent';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [[\"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\"}]]\n\nconst IconThumbUp = createReactComponent('outline', 'thumb-up', 'ThumbUp', __iconNode);\n\nexport default IconThumbUp;","import { useState, useCallback, useRef, useEffect } from \"react\";\n\nimport { copyToClipboard } from \"@/utils/clipboard\";\nimport { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\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 const copy = useCallback((text: string) => {\n copyToClipboard(markdownToPlainText(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 min-width: 0;\n`;\n"],"mappings":"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","names":["useCallback","__iconNode","__iconNode","__iconNode","useCallback","styled","styled","jsx","jsxs"]}
|