@copilotz/chat-ui 0.1.38 → 0.2.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 +89 -0
- package/dist/index.cjs +40 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +40 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -202,6 +202,78 @@ import { ChatUI, chatConfigPresets } from '@copilotz/chat-ui';
|
|
|
202
202
|
/>
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
### Markdown Extensions
|
|
206
|
+
|
|
207
|
+
By default, `@copilotz/chat-ui` renders messages with:
|
|
208
|
+
|
|
209
|
+
- `remark-gfm`
|
|
210
|
+
- syntax highlighting for non-streaming code blocks
|
|
211
|
+
- the built-in markdown component overrides used by the chat UI
|
|
212
|
+
|
|
213
|
+
If you need more control, you can extend the markdown pipeline through `config.markdown`.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import type { Components } from 'react-markdown';
|
|
217
|
+
|
|
218
|
+
const markdownComponents: Components = {
|
|
219
|
+
code: MyCustomCodeBlock,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
<ChatUI
|
|
223
|
+
config={{
|
|
224
|
+
markdown: {
|
|
225
|
+
remarkPlugins: [myRemarkPlugin],
|
|
226
|
+
rehypePlugins: [myRehypePlugin],
|
|
227
|
+
components: markdownComponents,
|
|
228
|
+
},
|
|
229
|
+
}}
|
|
230
|
+
/>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Notes:
|
|
234
|
+
|
|
235
|
+
- `remarkPlugins` and `rehypePlugins` are appended to the built-in defaults.
|
|
236
|
+
- `components` are merged with the built-in markdown component map.
|
|
237
|
+
- This is the recommended way to add Mermaid, custom code blocks, callouts, or project-specific markdown behavior without increasing the core `chat-ui` bundle.
|
|
238
|
+
|
|
239
|
+
Example: Mermaid via a custom `code` renderer
|
|
240
|
+
|
|
241
|
+
````tsx
|
|
242
|
+
function MermaidCodeBlock({ className, children, inline, ...props }) {
|
|
243
|
+
const isMermaid = !inline && /\blanguage-mermaid\b/.test(className || '');
|
|
244
|
+
|
|
245
|
+
if (isMermaid) {
|
|
246
|
+
return <MyMermaidRenderer definition={String(children)} />;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return !inline ? (
|
|
250
|
+
<pre>
|
|
251
|
+
<code className={className} {...props}>
|
|
252
|
+
{children}
|
|
253
|
+
</code>
|
|
254
|
+
</pre>
|
|
255
|
+
) : (
|
|
256
|
+
<code {...props}>{children}</code>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
<ChatUI
|
|
261
|
+
config={{
|
|
262
|
+
markdown: {
|
|
263
|
+
components: {
|
|
264
|
+
code: MermaidCodeBlock,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
````
|
|
270
|
+
|
|
271
|
+
Recommended approach for Mermaid:
|
|
272
|
+
|
|
273
|
+
- keep Mermaid project-specific instead of bundling it into every `chat-ui` consumer
|
|
274
|
+
- lazy-load the Mermaid runtime inside your custom renderer
|
|
275
|
+
- render Mermaid only for completed code blocks, not for token-by-token streaming content
|
|
276
|
+
|
|
205
277
|
### Custom Right Sidebar
|
|
206
278
|
|
|
207
279
|
Add a custom component to the right sidebar (e.g., profile info, settings, context):
|
|
@@ -280,6 +352,23 @@ All user interactions are handled through callbacks. This keeps the component pu
|
|
|
280
352
|
| `onInitialInputConsumed` | `() => void` | Called when initial input is modified/sent |
|
|
281
353
|
| `className` | `string` | Additional CSS classes |
|
|
282
354
|
|
|
355
|
+
### ChatConfig Markdown
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
interface ChatMarkdownConfig {
|
|
359
|
+
remarkPlugins?: ReactMarkdownOptions['remarkPlugins'];
|
|
360
|
+
rehypePlugins?: ReactMarkdownOptions['rehypePlugins'];
|
|
361
|
+
components?: Components;
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
interface ChatConfig {
|
|
367
|
+
// ...
|
|
368
|
+
markdown?: ChatMarkdownConfig;
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
283
372
|
### ChatMessage
|
|
284
373
|
|
|
285
374
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -170,6 +170,11 @@ var defaultChatConfig = {
|
|
|
170
170
|
longMessageChunkChars: 12e3,
|
|
171
171
|
renderUserMarkdown: true
|
|
172
172
|
},
|
|
173
|
+
markdown: {
|
|
174
|
+
remarkPlugins: [],
|
|
175
|
+
rehypePlugins: [],
|
|
176
|
+
components: {}
|
|
177
|
+
},
|
|
173
178
|
voiceCompose: {
|
|
174
179
|
enabled: false,
|
|
175
180
|
defaultMode: "text",
|
|
@@ -203,6 +208,10 @@ function mergeConfig(_baseConfig, userConfig) {
|
|
|
203
208
|
...defaultChatConfig.ui,
|
|
204
209
|
...userConfig.ui
|
|
205
210
|
},
|
|
211
|
+
markdown: {
|
|
212
|
+
...defaultChatConfig.markdown,
|
|
213
|
+
...userConfig.markdown
|
|
214
|
+
},
|
|
206
215
|
voiceCompose: {
|
|
207
216
|
...defaultChatConfig.voiceCompose,
|
|
208
217
|
...userConfig.voiceCompose
|
|
@@ -698,7 +707,7 @@ var ThinkingBlock = (0, import_react.memo)(function ThinkingBlock2({ reasoning,
|
|
|
698
707
|
] }) })
|
|
699
708
|
] });
|
|
700
709
|
});
|
|
701
|
-
var
|
|
710
|
+
var defaultMarkdownComponents = {
|
|
702
711
|
code: ({ node, className, children, ...props }) => {
|
|
703
712
|
const inline = props.inline;
|
|
704
713
|
const match = /language-(\w+)/.exec(className || "");
|
|
@@ -763,12 +772,34 @@ var StreamingText = (0, import_react.memo)(function StreamingText2({
|
|
|
763
772
|
thinkingLabel = "Thinking...",
|
|
764
773
|
className = "",
|
|
765
774
|
renderMarkdown = true,
|
|
775
|
+
markdown,
|
|
766
776
|
plainTextChunkChars = 12e3,
|
|
767
777
|
contentStyle,
|
|
768
778
|
hideThinkingIndicator = false
|
|
769
779
|
}) {
|
|
770
780
|
const hasContent = content.trim().length > 0;
|
|
771
781
|
const enableSyntaxHighlight = renderMarkdown && !isStreaming && hasCodeBlocks(content);
|
|
782
|
+
const mergedComponents = (0, import_react.useMemo)(
|
|
783
|
+
() => ({
|
|
784
|
+
...defaultMarkdownComponents,
|
|
785
|
+
...markdown?.components
|
|
786
|
+
}),
|
|
787
|
+
[markdown?.components]
|
|
788
|
+
);
|
|
789
|
+
const mergedRemarkPlugins = (0, import_react.useMemo)(
|
|
790
|
+
() => [
|
|
791
|
+
...remarkPluginsDefault,
|
|
792
|
+
...markdown?.remarkPlugins ?? []
|
|
793
|
+
],
|
|
794
|
+
[markdown?.remarkPlugins]
|
|
795
|
+
);
|
|
796
|
+
const mergedRehypePlugins = (0, import_react.useMemo)(
|
|
797
|
+
() => [
|
|
798
|
+
...enableSyntaxHighlight ? rehypePluginsDefault : rehypePluginsEmpty,
|
|
799
|
+
...markdown?.rehypePlugins ?? []
|
|
800
|
+
],
|
|
801
|
+
[enableSyntaxHighlight, markdown?.rehypePlugins]
|
|
802
|
+
);
|
|
772
803
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
773
804
|
hasContent ? renderMarkdown ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
774
805
|
LongContentShell,
|
|
@@ -778,9 +809,9 @@ var StreamingText = (0, import_react.memo)(function StreamingText2({
|
|
|
778
809
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
779
810
|
import_react_markdown.default,
|
|
780
811
|
{
|
|
781
|
-
remarkPlugins:
|
|
782
|
-
rehypePlugins:
|
|
783
|
-
components:
|
|
812
|
+
remarkPlugins: mergedRemarkPlugins,
|
|
813
|
+
rehypePlugins: mergedRehypePlugins,
|
|
814
|
+
components: mergedComponents,
|
|
784
815
|
children: content
|
|
785
816
|
}
|
|
786
817
|
)
|
|
@@ -954,6 +985,7 @@ var arePropsEqual = (prevProps, nextProps) => {
|
|
|
954
985
|
if (prevProps.longMessagePreviewChars !== nextProps.longMessagePreviewChars) return false;
|
|
955
986
|
if (prevProps.longMessageChunkChars !== nextProps.longMessageChunkChars) return false;
|
|
956
987
|
if (prevProps.renderUserMarkdown !== nextProps.renderUserMarkdown) return false;
|
|
988
|
+
if (prevProps.markdown !== nextProps.markdown) return false;
|
|
957
989
|
if (prevProps.isExpanded !== nextProps.isExpanded) return false;
|
|
958
990
|
if (prevProps.onToggleExpanded !== nextProps.onToggleExpanded) return false;
|
|
959
991
|
if (prevProps.isGrouped !== nextProps.isGrouped) return false;
|
|
@@ -985,6 +1017,7 @@ var Message = (0, import_react.memo)(({
|
|
|
985
1017
|
longMessagePreviewChars = 4e3,
|
|
986
1018
|
longMessageChunkChars = 12e3,
|
|
987
1019
|
renderUserMarkdown = true,
|
|
1020
|
+
markdown,
|
|
988
1021
|
isExpanded = false,
|
|
989
1022
|
onToggleExpanded,
|
|
990
1023
|
isGrouped = false
|
|
@@ -1102,6 +1135,7 @@ var Message = (0, import_react.memo)(({
|
|
|
1102
1135
|
isStreaming: message.isStreaming,
|
|
1103
1136
|
thinkingLabel,
|
|
1104
1137
|
renderMarkdown: shouldRenderMarkdown,
|
|
1138
|
+
markdown,
|
|
1105
1139
|
plainTextChunkChars: normalizedChunkChars,
|
|
1106
1140
|
contentStyle,
|
|
1107
1141
|
hideThinkingIndicator: !!message.reasoning
|
|
@@ -5165,6 +5199,7 @@ var ChatUI = ({
|
|
|
5165
5199
|
longMessagePreviewChars: config.ui.longMessagePreviewChars,
|
|
5166
5200
|
longMessageChunkChars: config.ui.longMessageChunkChars,
|
|
5167
5201
|
renderUserMarkdown: config.ui.renderUserMarkdown,
|
|
5202
|
+
markdown: config.markdown,
|
|
5168
5203
|
onToggleExpanded: handleToggleMessageExpansion
|
|
5169
5204
|
}), [
|
|
5170
5205
|
user?.avatar,
|
|
@@ -5187,6 +5222,7 @@ var ChatUI = ({
|
|
|
5187
5222
|
config.ui.longMessagePreviewChars,
|
|
5188
5223
|
config.ui.longMessageChunkChars,
|
|
5189
5224
|
config.ui.renderUserMarkdown,
|
|
5225
|
+
config.markdown,
|
|
5190
5226
|
handleMessageAction,
|
|
5191
5227
|
handleToggleMessageExpansion
|
|
5192
5228
|
]);
|