@extrachill/chat 0.3.0 → 0.3.1

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.
@@ -7,7 +7,6 @@ export interface ChatMessageProps {
7
7
  contentFormat?: ContentFormat;
8
8
  /**
9
9
  * Custom content renderer. When provided, overrides contentFormat.
10
- * Use this to plug in your own markdown renderer (react-markdown, etc.).
11
10
  */
12
11
  renderContent?: (content: string, role: ChatMessageType['role']) => ReactNode;
13
12
  /** Additional CSS class name on the outer wrapper. */
@@ -17,7 +16,7 @@ export interface ChatMessageProps {
17
16
  * Renders a single chat message bubble.
18
17
  *
19
18
  * User messages align right, assistant messages align left.
20
- * Content rendering is pluggable via `renderContent` or `contentFormat`.
19
+ * Markdown content is rendered via react-markdown (lazy-loaded).
21
20
  */
22
21
  export declare function ChatMessage({ message, contentFormat, renderContent, className, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
23
22
  //# sourceMappingURL=ChatMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatMessage.d.ts","sourceRoot":"","sources":["../../src/components/ChatMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvF,MAAM,WAAW,gBAAgB;IAChC,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;IAC9E,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAC3B,OAAO,EACP,aAA0B,EAC1B,aAAa,EACb,SAAS,GACT,EAAE,gBAAgB,2CAyBlB"}
1
+ {"version":3,"file":"ChatMessage.d.ts","sourceRoot":"","sources":["../../src/components/ChatMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvF,MAAM,WAAW,gBAAgB;IAChC,6BAA6B;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;IAC9E,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAC3B,OAAO,EACP,aAA0B,EAC1B,aAAa,EACb,SAAS,GACT,EAAE,gBAAgB,2CAyBlB"}
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
2
+ import { lazy, Suspense } from 'react';
3
3
  import { markdownToHtml } from "../markdown.js";
4
+ const ReactMarkdown = lazy(() => import('react-markdown'));
4
5
  /**
5
6
  * Renders a single chat message bubble.
6
7
  *
7
8
  * User messages align right, assistant messages align left.
8
- * Content rendering is pluggable via `renderContent` or `contentFormat`.
9
+ * Markdown content is rendered via react-markdown (lazy-loaded).
9
10
  */
10
11
  export function ChatMessage({ message, contentFormat = 'markdown', renderContent, className, }) {
11
12
  const isUser = message.role === 'user';
@@ -16,16 +17,19 @@ export function ChatMessage({ message, contentFormat = 'markdown', renderContent
16
17
  ? renderContent(message.content, message.role)
17
18
  : _jsx(DefaultContent, { content: message.content, format: contentFormat }) }), message.timestamp && (_jsx("time", { className: `${baseClass}__timestamp`, dateTime: message.timestamp, title: new Date(message.timestamp).toLocaleString(), children: formatTime(message.timestamp) }))] }));
18
19
  }
20
+ /**
21
+ * Markdown rendered via lazy-loaded react-markdown.
22
+ * Falls back to the built-in lightweight parser while loading.
23
+ */
24
+ function MarkdownContent({ content }) {
25
+ return (_jsx(Suspense, { fallback: _jsx("div", { dangerouslySetInnerHTML: { __html: markdownToHtml(content) } }), children: _jsx(ReactMarkdown, { children: content }) }));
26
+ }
19
27
  function DefaultContent({ content, format }) {
20
- const html = useMemo(() => {
21
- if (format === 'html')
22
- return content;
23
- if (format === 'markdown')
24
- return markdownToHtml(content);
25
- return null;
26
- }, [content, format]);
27
- if (html !== null) {
28
- return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
28
+ if (format === 'html') {
29
+ return _jsx("div", { dangerouslySetInnerHTML: { __html: content } });
30
+ }
31
+ if (format === 'markdown') {
32
+ return _jsx(MarkdownContent, { content: content });
29
33
  }
30
34
  // Plain text — split on double newlines for paragraphs.
31
35
  return (_jsx(_Fragment, { children: content.split('\n\n').map((paragraph, i) => (_jsx("p", { children: paragraph }, i))) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extrachill/chat",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Chat UI components with built-in REST API client. Speaks the standard chat message format natively.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,10 +52,13 @@
52
52
  "react-dom": ">=18.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "typescript": "^5.7.0",
56
55
  "@types/react": "^18.0.0",
57
56
  "@types/react-dom": "^18.0.0",
58
57
  "react": "^18.0.0",
59
- "react-dom": "^18.0.0"
58
+ "react-dom": "^18.0.0",
59
+ "typescript": "^5.7.0"
60
+ },
61
+ "dependencies": {
62
+ "react-markdown": "^10.1.0"
60
63
  }
61
64
  }
@@ -1,7 +1,9 @@
1
- import { type ReactNode, useMemo } from 'react';
1
+ import { type ReactNode, lazy, Suspense } from 'react';
2
2
  import type { ChatMessage as ChatMessageType, ContentFormat } from '../types/index.ts';
3
3
  import { markdownToHtml } from '../markdown.ts';
4
4
 
5
+ const ReactMarkdown = lazy(() => import('react-markdown'));
6
+
5
7
  export interface ChatMessageProps {
6
8
  /** The message to render. */
7
9
  message: ChatMessageType;
@@ -9,7 +11,6 @@ export interface ChatMessageProps {
9
11
  contentFormat?: ContentFormat;
10
12
  /**
11
13
  * Custom content renderer. When provided, overrides contentFormat.
12
- * Use this to plug in your own markdown renderer (react-markdown, etc.).
13
14
  */
14
15
  renderContent?: (content: string, role: ChatMessageType['role']) => ReactNode;
15
16
  /** Additional CSS class name on the outer wrapper. */
@@ -20,7 +21,7 @@ export interface ChatMessageProps {
20
21
  * Renders a single chat message bubble.
21
22
  *
22
23
  * User messages align right, assistant messages align left.
23
- * Content rendering is pluggable via `renderContent` or `contentFormat`.
24
+ * Markdown content is rendered via react-markdown (lazy-loaded).
24
25
  */
25
26
  export function ChatMessage({
26
27
  message,
@@ -59,15 +60,29 @@ interface DefaultContentProps {
59
60
  format: ContentFormat;
60
61
  }
61
62
 
63
+ /**
64
+ * Markdown rendered via lazy-loaded react-markdown.
65
+ * Falls back to the built-in lightweight parser while loading.
66
+ */
67
+ function MarkdownContent({ content }: { content: string }) {
68
+ return (
69
+ <Suspense
70
+ fallback={
71
+ <div dangerouslySetInnerHTML={{ __html: markdownToHtml(content) }} />
72
+ }
73
+ >
74
+ <ReactMarkdown>{content}</ReactMarkdown>
75
+ </Suspense>
76
+ );
77
+ }
78
+
62
79
  function DefaultContent({ content, format }: DefaultContentProps) {
63
- const html = useMemo(() => {
64
- if (format === 'html') return content;
65
- if (format === 'markdown') return markdownToHtml(content);
66
- return null;
67
- }, [content, format]);
80
+ if (format === 'html') {
81
+ return <div dangerouslySetInnerHTML={{ __html: content }} />;
82
+ }
68
83
 
69
- if (html !== null) {
70
- return <div dangerouslySetInnerHTML={{ __html: html }} />;
84
+ if (format === 'markdown') {
85
+ return <MarkdownContent content={content} />;
71
86
  }
72
87
 
73
88
  // Plain text — split on double newlines for paragraphs.