@portablecore/chat 0.1.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 (57) hide show
  1. package/dist/chat-interface-core.d.ts +27 -0
  2. package/dist/chat-interface-core.d.ts.map +1 -0
  3. package/dist/chat-interface-core.js +35 -0
  4. package/dist/chat-interface-core.js.map +1 -0
  5. package/dist/components/empty-state.d.ts +10 -0
  6. package/dist/components/empty-state.d.ts.map +1 -0
  7. package/dist/components/empty-state.js +16 -0
  8. package/dist/components/empty-state.js.map +1 -0
  9. package/dist/components/input-area.d.ts +26 -0
  10. package/dist/components/input-area.d.ts.map +1 -0
  11. package/dist/components/input-area.js +66 -0
  12. package/dist/components/input-area.js.map +1 -0
  13. package/dist/components/message-bubble.d.ts +20 -0
  14. package/dist/components/message-bubble.d.ts.map +1 -0
  15. package/dist/components/message-bubble.js +36 -0
  16. package/dist/components/message-bubble.js.map +1 -0
  17. package/dist/components/message-list.d.ts +23 -0
  18. package/dist/components/message-list.d.ts.map +1 -0
  19. package/dist/components/message-list.js +24 -0
  20. package/dist/components/message-list.js.map +1 -0
  21. package/dist/components/streaming-text.d.ts +14 -0
  22. package/dist/components/streaming-text.d.ts.map +1 -0
  23. package/dist/components/streaming-text.js +41 -0
  24. package/dist/components/streaming-text.js.map +1 -0
  25. package/dist/components/thinking-indicator.d.ts +18 -0
  26. package/dist/components/thinking-indicator.d.ts.map +1 -0
  27. package/dist/components/thinking-indicator.js +29 -0
  28. package/dist/components/thinking-indicator.js.map +1 -0
  29. package/dist/hooks/use-chat-scroll.d.ts +23 -0
  30. package/dist/hooks/use-chat-scroll.d.ts.map +1 -0
  31. package/dist/hooks/use-chat-scroll.js +66 -0
  32. package/dist/hooks/use-chat-scroll.js.map +1 -0
  33. package/dist/hooks/use-chat-session.d.ts +15 -0
  34. package/dist/hooks/use-chat-session.d.ts.map +1 -0
  35. package/dist/hooks/use-chat-session.js +224 -0
  36. package/dist/hooks/use-chat-session.js.map +1 -0
  37. package/dist/index.d.ts +27 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +47 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/types.d.ts +33 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +9 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/utils/markdown-config.d.ts +9 -0
  46. package/dist/utils/markdown-config.d.ts.map +1 -0
  47. package/dist/utils/markdown-config.js +21 -0
  48. package/dist/utils/markdown-config.js.map +1 -0
  49. package/dist/utils/message-grouping.d.ts +10 -0
  50. package/dist/utils/message-grouping.d.ts.map +1 -0
  51. package/dist/utils/message-grouping.js +36 -0
  52. package/dist/utils/message-grouping.js.map +1 -0
  53. package/dist/utils/time.d.ts +8 -0
  54. package/dist/utils/time.d.ts.map +1 -0
  55. package/dist/utils/time.js +34 -0
  56. package/dist/utils/time.js.map +1 -0
  57. package/package.json +53 -0
@@ -0,0 +1,224 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useChatSession = useChatSession;
5
+ /**
6
+ * useChatSession — Chat lifecycle hook driven by ChatInterfaceConfig.
7
+ *
8
+ * Composes lower-level primitives from @portablecore/chat-runtime:
9
+ * - parseSseStream for SSE parsing
10
+ * - useStreamingMessage for RAF-batched text accumulation
11
+ * - useSkillIndicators for thinking/skill indicator state
12
+ *
13
+ * Unlike useChatRuntime (which takes flat options), this hook is
14
+ * designed to work with the ChatInterfaceConfig extension point API,
15
+ * giving platforms full control over the request body via buildBody.
16
+ */
17
+ const react_1 = require("react");
18
+ const chat_runtime_1 = require("@portablecore/chat-runtime");
19
+ // ---------------------------------------------------------------------------
20
+ // Hook
21
+ // ---------------------------------------------------------------------------
22
+ function useChatSession(config) {
23
+ const { chatId, expert, initialMessages, stream: streamConfig, handlers, } = config;
24
+ // ---- State ----
25
+ const [messages, setMessages] = (0, react_1.useState)(initialMessages || []);
26
+ const [streaming, setStreaming] = (0, react_1.useState)(false);
27
+ const [sending, setSending] = (0, react_1.useState)(false);
28
+ const [error, setError] = (0, react_1.useState)(null);
29
+ // Sync initialMessages when they change (e.g., page navigation)
30
+ const initialMessagesRef = (0, react_1.useRef)(initialMessages);
31
+ (0, react_1.useEffect)(() => {
32
+ if (initialMessages !== initialMessagesRef.current) {
33
+ initialMessagesRef.current = initialMessages;
34
+ setMessages(initialMessages || []);
35
+ }
36
+ }, [initialMessages]);
37
+ // ---- Streaming primitives ----
38
+ const { streamingMessageId, updateStreamingMessage, flushPendingContent, } = (0, chat_runtime_1.useStreamingMessage)(setMessages);
39
+ const { thinkingStages, activeSkill, handleEvent: handleIndicatorEvent, reset: resetIndicators, } = (0, chat_runtime_1.useSkillIndicators)([]);
40
+ // ---- Refs for stable callbacks ----
41
+ const abortRef = (0, react_1.useRef)(null);
42
+ const handlersRef = (0, react_1.useRef)(handlers);
43
+ (0, react_1.useEffect)(() => {
44
+ handlersRef.current = handlers;
45
+ }, [handlers]);
46
+ // ---- Stream response ----
47
+ const streamResponse = (0, react_1.useCallback)(async (userMessage) => {
48
+ setStreaming(true);
49
+ setError(null);
50
+ resetIndicators();
51
+ const tempId = `stream-${Date.now()}`;
52
+ streamingMessageId.current = tempId;
53
+ setMessages((prev) => [
54
+ ...prev,
55
+ {
56
+ id: tempId,
57
+ role: "assistant",
58
+ content: "",
59
+ streaming: true,
60
+ createdAt: new Date().toISOString(),
61
+ },
62
+ ]);
63
+ const controller = new AbortController();
64
+ abortRef.current = controller;
65
+ try {
66
+ const body = streamConfig.buildBody(userMessage, {
67
+ chatId,
68
+ expertId: expert?.id,
69
+ });
70
+ const headers = {
71
+ "Content-Type": "application/json",
72
+ ...streamConfig.authHeaders?.(),
73
+ };
74
+ const response = await fetch(streamConfig.endpoint, {
75
+ method: "POST",
76
+ headers,
77
+ body: JSON.stringify(body),
78
+ signal: controller.signal,
79
+ });
80
+ if (!response.ok || !response.body) {
81
+ const errorText = "Something went wrong. Try again.";
82
+ setMessages((prev) => prev.map((m) => m.id === tempId
83
+ ? { ...m, content: errorText, streaming: false }
84
+ : m));
85
+ setStreaming(false);
86
+ setError(errorText);
87
+ handlersRef.current?.onError?.(new Error(`HTTP ${response.status}`));
88
+ return;
89
+ }
90
+ for await (const event of (0, chat_runtime_1.parseSseStream)(response)) {
91
+ handlersRef.current?.onStreamEvent?.(event);
92
+ handleIndicatorEvent(event);
93
+ switch (event.type) {
94
+ case "text": {
95
+ updateStreamingMessage(event.text);
96
+ break;
97
+ }
98
+ case "replace_content": {
99
+ flushPendingContent();
100
+ const target = streamingMessageId.current || tempId;
101
+ setMessages((prev) => prev.filter((m) => m.id !== target));
102
+ break;
103
+ }
104
+ case "first_response": {
105
+ flushPendingContent();
106
+ const frTarget = streamingMessageId.current || tempId;
107
+ setMessages((prev) => prev.map((m) => m.id === frTarget
108
+ ? { ...m, content: m.content + "\n\n" }
109
+ : m));
110
+ break;
111
+ }
112
+ case "done": {
113
+ flushPendingContent();
114
+ const finalId = event.messageId;
115
+ const doneTarget = streamingMessageId.current || tempId;
116
+ setMessages((prev) => prev.map((m) => m.id === doneTarget
117
+ ? {
118
+ ...m,
119
+ id: finalId || doneTarget,
120
+ streaming: false,
121
+ }
122
+ : m));
123
+ const finalMessage = messages.find((m) => m.id === doneTarget) || { id: finalId || doneTarget, role: "assistant", content: "" };
124
+ handlersRef.current?.onMessageReceived?.(finalMessage);
125
+ break;
126
+ }
127
+ case "error": {
128
+ flushPendingContent();
129
+ const errTarget = streamingMessageId.current || tempId;
130
+ setMessages((prev) => prev.map((m) => m.id === errTarget
131
+ ? {
132
+ ...m,
133
+ content: m.content || event.error,
134
+ streaming: false,
135
+ }
136
+ : m));
137
+ setError(event.error);
138
+ handlersRef.current?.onError?.(new Error(event.error));
139
+ break;
140
+ }
141
+ }
142
+ }
143
+ // Ensure no messages remain in streaming state
144
+ setMessages((prev) => prev.map((m) => (m.streaming ? { ...m, streaming: false } : m)));
145
+ }
146
+ catch (err) {
147
+ if (err.name !== "AbortError") {
148
+ const errorMsg = "Connection lost. Try again.";
149
+ setMessages((prev) => prev.map((m) => m.id === tempId
150
+ ? { ...m, content: errorMsg, streaming: false }
151
+ : m));
152
+ setError(errorMsg);
153
+ handlersRef.current?.onError?.(err);
154
+ }
155
+ }
156
+ finally {
157
+ streamingMessageId.current = null;
158
+ setStreaming(false);
159
+ resetIndicators();
160
+ abortRef.current = null;
161
+ }
162
+ }, [
163
+ chatId,
164
+ expert?.id,
165
+ streamConfig,
166
+ messages,
167
+ streamingMessageId,
168
+ updateStreamingMessage,
169
+ flushPendingContent,
170
+ handleIndicatorEvent,
171
+ resetIndicators,
172
+ ]);
173
+ // ---- Public API ----
174
+ const send = (0, react_1.useCallback)(async (content) => {
175
+ const trimmed = content.trim();
176
+ if (!trimmed || sending || streaming)
177
+ return;
178
+ setSending(true);
179
+ setError(null);
180
+ let processedContent = trimmed;
181
+ if (handlersRef.current?.onBeforeSend) {
182
+ processedContent = await handlersRef.current.onBeforeSend(trimmed);
183
+ }
184
+ const userTempId = `user-${Date.now()}`;
185
+ setMessages((prev) => [
186
+ ...prev,
187
+ {
188
+ id: userTempId,
189
+ role: "user",
190
+ content: processedContent,
191
+ createdAt: new Date().toISOString(),
192
+ },
193
+ ]);
194
+ setSending(false);
195
+ await streamResponse(processedContent);
196
+ }, [sending, streaming, streamResponse]);
197
+ const abort = (0, react_1.useCallback)(() => {
198
+ if (abortRef.current) {
199
+ abortRef.current.abort();
200
+ abortRef.current = null;
201
+ }
202
+ flushPendingContent();
203
+ setMessages((prev) => prev.map((m) => (m.streaming ? { ...m, streaming: false } : m)));
204
+ setStreaming(false);
205
+ resetIndicators();
206
+ }, [flushPendingContent, resetIndicators]);
207
+ const clear = (0, react_1.useCallback)(() => {
208
+ setMessages([]);
209
+ setError(null);
210
+ resetIndicators();
211
+ }, [resetIndicators]);
212
+ return {
213
+ messages,
214
+ streaming,
215
+ sending,
216
+ error,
217
+ thinkingStages,
218
+ activeSkill,
219
+ send,
220
+ abort,
221
+ clear,
222
+ };
223
+ }
224
+ //# sourceMappingURL=use-chat-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-chat-session.js","sourceRoot":"","sources":["../../src/hooks/use-chat-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @portablecore/chat
3
+ *
4
+ * Unified chat UI for Portable platforms.
5
+ * Composes @portablecore/chat-runtime with React components
6
+ * and the ChatInterfaceConfig extension point API.
7
+ *
8
+ * Both Expert and Team render the same ChatInterfaceCore.
9
+ * Platform-specific behavior is injected via config (features,
10
+ * slots, resolvers, handlers).
11
+ */
12
+ export { ChatInterfaceCore } from "./chat-interface-core.js";
13
+ export { useChatSession } from "./hooks/use-chat-session.js";
14
+ export type { ChatSession } from "./hooks/use-chat-session.js";
15
+ export { useChatScroll } from "./hooks/use-chat-scroll.js";
16
+ export { MessageList } from "./components/message-list.js";
17
+ export { MessageBubble } from "./components/message-bubble.js";
18
+ export { InputArea } from "./components/input-area.js";
19
+ export { StreamingText } from "./components/streaming-text.js";
20
+ export { ThinkingIndicator } from "./components/thinking-indicator.js";
21
+ export type { ThinkingStage } from "./components/thinking-indicator.js";
22
+ export { EmptyState } from "./components/empty-state.js";
23
+ export { formatTime, getDateSeparator, isDifferentDay } from "./utils/time.js";
24
+ export { groupMessages } from "./utils/message-grouping.js";
25
+ export { remarkPlugins, rehypePlugins } from "./utils/markdown-config.js";
26
+ export type { ChatInterfaceConfig, ChatFeatureFlags, StreamBodyOpts, MentionSuggestion, SkillSuggestion, DocumentRef, LiteMessage, SseEvent, ChatSlots, RenderableMessage, } from "./types.js";
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAG9D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,YAAY,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAGxD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAGzE,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,QAAQ,EACR,SAAS,EACT,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * @portablecore/chat
4
+ *
5
+ * Unified chat UI for Portable platforms.
6
+ * Composes @portablecore/chat-runtime with React components
7
+ * and the ChatInterfaceConfig extension point API.
8
+ *
9
+ * Both Expert and Team render the same ChatInterfaceCore.
10
+ * Platform-specific behavior is injected via config (features,
11
+ * slots, resolvers, handlers).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.rehypePlugins = exports.remarkPlugins = exports.groupMessages = exports.isDifferentDay = exports.getDateSeparator = exports.formatTime = exports.EmptyState = exports.ThinkingIndicator = exports.StreamingText = exports.InputArea = exports.MessageBubble = exports.MessageList = exports.useChatScroll = exports.useChatSession = exports.ChatInterfaceCore = void 0;
15
+ // The main product
16
+ var chat_interface_core_js_1 = require("./chat-interface-core.js");
17
+ Object.defineProperty(exports, "ChatInterfaceCore", { enumerable: true, get: function () { return chat_interface_core_js_1.ChatInterfaceCore; } });
18
+ // Session hook (for platforms that need direct access to chat state)
19
+ var use_chat_session_js_1 = require("./hooks/use-chat-session.js");
20
+ Object.defineProperty(exports, "useChatSession", { enumerable: true, get: function () { return use_chat_session_js_1.useChatSession; } });
21
+ // Scroll management
22
+ var use_chat_scroll_js_1 = require("./hooks/use-chat-scroll.js");
23
+ Object.defineProperty(exports, "useChatScroll", { enumerable: true, get: function () { return use_chat_scroll_js_1.useChatScroll; } });
24
+ // UI components (for platforms that want to compose their own layout)
25
+ var message_list_js_1 = require("./components/message-list.js");
26
+ Object.defineProperty(exports, "MessageList", { enumerable: true, get: function () { return message_list_js_1.MessageList; } });
27
+ var message_bubble_js_1 = require("./components/message-bubble.js");
28
+ Object.defineProperty(exports, "MessageBubble", { enumerable: true, get: function () { return message_bubble_js_1.MessageBubble; } });
29
+ var input_area_js_1 = require("./components/input-area.js");
30
+ Object.defineProperty(exports, "InputArea", { enumerable: true, get: function () { return input_area_js_1.InputArea; } });
31
+ var streaming_text_js_1 = require("./components/streaming-text.js");
32
+ Object.defineProperty(exports, "StreamingText", { enumerable: true, get: function () { return streaming_text_js_1.StreamingText; } });
33
+ var thinking_indicator_js_1 = require("./components/thinking-indicator.js");
34
+ Object.defineProperty(exports, "ThinkingIndicator", { enumerable: true, get: function () { return thinking_indicator_js_1.ThinkingIndicator; } });
35
+ var empty_state_js_1 = require("./components/empty-state.js");
36
+ Object.defineProperty(exports, "EmptyState", { enumerable: true, get: function () { return empty_state_js_1.EmptyState; } });
37
+ // Utilities
38
+ var time_js_1 = require("./utils/time.js");
39
+ Object.defineProperty(exports, "formatTime", { enumerable: true, get: function () { return time_js_1.formatTime; } });
40
+ Object.defineProperty(exports, "getDateSeparator", { enumerable: true, get: function () { return time_js_1.getDateSeparator; } });
41
+ Object.defineProperty(exports, "isDifferentDay", { enumerable: true, get: function () { return time_js_1.isDifferentDay; } });
42
+ var message_grouping_js_1 = require("./utils/message-grouping.js");
43
+ Object.defineProperty(exports, "groupMessages", { enumerable: true, get: function () { return message_grouping_js_1.groupMessages; } });
44
+ var markdown_config_js_1 = require("./utils/markdown-config.js");
45
+ Object.defineProperty(exports, "remarkPlugins", { enumerable: true, get: function () { return markdown_config_js_1.remarkPlugins; } });
46
+ Object.defineProperty(exports, "rehypePlugins", { enumerable: true, get: function () { return markdown_config_js_1.rehypePlugins; } });
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,mBAAmB;AACnB,mEAA4D;AAAnD,2HAAA,iBAAiB,OAAA;AAE1B,qEAAqE;AACrE,mEAA4D;AAAnD,qHAAA,cAAc,OAAA;AAGvB,oBAAoB;AACpB,iEAA0D;AAAjD,mHAAA,aAAa,OAAA;AAEtB,sEAAsE;AACtE,gEAA0D;AAAjD,8GAAA,WAAW,OAAA;AACpB,oEAA8D;AAArD,kHAAA,aAAa,OAAA;AACtB,4DAAsD;AAA7C,0GAAA,SAAS,OAAA;AAClB,oEAA8D;AAArD,kHAAA,aAAa,OAAA;AACtB,4EAAsE;AAA7D,0HAAA,iBAAiB,OAAA;AAE1B,8DAAwD;AAA/C,4GAAA,UAAU,OAAA;AAEnB,YAAY;AACZ,2CAA8E;AAArE,qGAAA,UAAU,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,yGAAA,cAAc,OAAA;AACrD,mEAA2D;AAAlD,oHAAA,aAAa,OAAA;AACtB,iEAAyE;AAAhE,mHAAA,aAAa,OAAA;AAAE,mHAAA,aAAa,OAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Internal prop types for @portablecore/chat components.
3
+ *
4
+ * Re-exports the public ChatInterfaceConfig from @portablecore/types
5
+ * and adds component-level prop interfaces.
6
+ */
7
+ import type { ReactNode } from "react";
8
+ import type { LiteMessage } from "@portablecore/types/chat";
9
+ export type { ChatInterfaceConfig, ChatFeatureFlags, StreamBodyOpts, MentionSuggestion, SkillSuggestion, DocumentRef, } from "@portablecore/types/chat";
10
+ export type { LiteMessage, SseEvent } from "@portablecore/types/chat";
11
+ /**
12
+ * Slots narrowed to ReactNode for use inside React components.
13
+ * The types package uses `unknown` to avoid a React dependency.
14
+ */
15
+ export interface ChatSlots {
16
+ beforeMessageList?: ReactNode;
17
+ afterMessageList?: ReactNode;
18
+ inputPrefix?: ReactNode;
19
+ inputSuffix?: ReactNode;
20
+ sidePanel?: ReactNode;
21
+ emptyState?: ReactNode;
22
+ }
23
+ /**
24
+ * A message ready for rendering, with grouping metadata attached.
25
+ */
26
+ export interface RenderableMessage {
27
+ message: LiteMessage;
28
+ isFirstInGroup: boolean;
29
+ isLastInGroup: boolean;
30
+ showDateSeparator: boolean;
31
+ dateSeparatorLabel?: string;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAG3D,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,WAAW,GACZ,MAAM,0BAA0B,CAAA;AAEjC,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAErE;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,UAAU,CAAC,EAAE,SAAS,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,CAAA;IACpB,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B"}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Internal prop types for @portablecore/chat components.
4
+ *
5
+ * Re-exports the public ChatInterfaceConfig from @portablecore/types
6
+ * and adds component-level prop interfaces.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared markdown plugin configuration for chat messages.
3
+ *
4
+ * Both StreamingText (post-stream render) and MessageBubble
5
+ * use the same plugin set for consistent rendering.
6
+ */
7
+ export declare const remarkPlugins: any[];
8
+ export declare const rehypePlugins: any[];
9
+ //# sourceMappingURL=markdown-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-config.d.ts","sourceRoot":"","sources":["../../src/utils/markdown-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,eAAO,MAAM,aAAa,EAAE,GAAG,EAA8B,CAAA;AAG7D,eAAO,MAAM,aAAa,EAAE,GAAG,EAAiC,CAAA"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Shared markdown plugin configuration for chat messages.
4
+ *
5
+ * Both StreamingText (post-stream render) and MessageBubble
6
+ * use the same plugin set for consistent rendering.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.rehypePlugins = exports.remarkPlugins = void 0;
13
+ const remark_gfm_1 = __importDefault(require("remark-gfm"));
14
+ const remark_breaks_1 = __importDefault(require("remark-breaks"));
15
+ const rehype_raw_1 = __importDefault(require("rehype-raw"));
16
+ const rehype_highlight_1 = __importDefault(require("rehype-highlight"));
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ exports.remarkPlugins = [remark_gfm_1.default, remark_breaks_1.default];
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ exports.rehypePlugins = [rehype_raw_1.default, rehype_highlight_1.default];
21
+ //# sourceMappingURL=markdown-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-config.js","sourceRoot":"","sources":["../../src/utils/markdown-config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;AAEH,4DAAkC;AAClC,kEAAwC;AACxC,4DAAkC;AAClC,wEAA8C;AAE9C,8DAA8D;AACjD,QAAA,aAAa,GAAU,CAAC,oBAAS,EAAE,uBAAY,CAAC,CAAA;AAE7D,8DAA8D;AACjD,QAAA,aAAa,GAAU,CAAC,oBAAS,EAAE,0BAAe,CAAC,CAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Group consecutive messages from the same sender for visual clustering.
3
+ *
4
+ * Produces RenderableMessage[] with grouping metadata so MessageList
5
+ * can render date separators and tighten spacing within groups.
6
+ */
7
+ import type { LiteMessage } from "@portablecore/types/chat";
8
+ import type { RenderableMessage } from "../types.js";
9
+ export declare function groupMessages(messages: LiteMessage[]): RenderableMessage[];
10
+ //# sourceMappingURL=message-grouping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-grouping.d.ts","sourceRoot":"","sources":["../../src/utils/message-grouping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAGpD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,iBAAiB,EAAE,CA+B1E"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Group consecutive messages from the same sender for visual clustering.
4
+ *
5
+ * Produces RenderableMessage[] with grouping metadata so MessageList
6
+ * can render date separators and tighten spacing within groups.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.groupMessages = groupMessages;
10
+ const time_js_1 = require("./time.js");
11
+ function groupMessages(messages) {
12
+ return messages.map((message, index) => {
13
+ const prev = messages[index - 1];
14
+ const next = messages[index + 1];
15
+ const prevRole = prev?.role;
16
+ const nextRole = next?.role;
17
+ const currRole = message.role;
18
+ const isFirstInGroup = currRole !== prevRole;
19
+ const isLastInGroup = currRole !== nextRole;
20
+ const showDateSeparator = index === 0 ||
21
+ (prev?.createdAt != null &&
22
+ message.createdAt != null &&
23
+ (0, time_js_1.isDifferentDay)(prev.createdAt, message.createdAt));
24
+ const dateSeparatorLabel = showDateSeparator && message.createdAt
25
+ ? (0, time_js_1.getDateSeparator)(message.createdAt)
26
+ : undefined;
27
+ return {
28
+ message,
29
+ isFirstInGroup,
30
+ isLastInGroup,
31
+ showDateSeparator,
32
+ dateSeparatorLabel,
33
+ };
34
+ });
35
+ }
36
+ //# sourceMappingURL=message-grouping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-grouping.js","sourceRoot":"","sources":["../../src/utils/message-grouping.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAMH,sCA+BC;AAjCD,uCAA4D;AAE5D,SAAgB,aAAa,CAAC,QAAuB;IACnD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAEhC,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,CAAA;QAC3B,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,CAAA;QAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAA;QAE7B,MAAM,cAAc,GAAG,QAAQ,KAAK,QAAQ,CAAA;QAC5C,MAAM,aAAa,GAAG,QAAQ,KAAK,QAAQ,CAAA;QAE3C,MAAM,iBAAiB,GACrB,KAAK,KAAK,CAAC;YACX,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI;gBACtB,OAAO,CAAC,SAAS,IAAI,IAAI;gBACzB,IAAA,wBAAc,EAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;QAEtD,MAAM,kBAAkB,GACtB,iBAAiB,IAAI,OAAO,CAAC,SAAS;YACpC,CAAC,CAAC,IAAA,0BAAgB,EAAC,OAAO,CAAC,SAAS,CAAC;YACrC,CAAC,CAAC,SAAS,CAAA;QAEf,OAAO;YACL,OAAO;YACP,cAAc;YACd,aAAa;YACb,iBAAiB;YACjB,kBAAkB;SACnB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Time formatting utilities for chat messages.
3
+ * Extracted from Expert's ChatMessage component.
4
+ */
5
+ export declare function formatTime(dateString: string): string;
6
+ export declare function getDateSeparator(dateString: string): string;
7
+ export declare function isDifferentDay(date1: string, date2: string): boolean;
8
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiB3D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpE"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Time formatting utilities for chat messages.
4
+ * Extracted from Expert's ChatMessage component.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.formatTime = formatTime;
8
+ exports.getDateSeparator = getDateSeparator;
9
+ exports.isDifferentDay = isDifferentDay;
10
+ function formatTime(dateString) {
11
+ const date = new Date(dateString);
12
+ return date.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
13
+ }
14
+ function getDateSeparator(dateString) {
15
+ const date = new Date(dateString);
16
+ const today = new Date();
17
+ const yesterday = new Date(today);
18
+ yesterday.setDate(yesterday.getDate() - 1);
19
+ const isToday = date.toDateString() === today.toDateString();
20
+ const isYesterday = date.toDateString() === yesterday.toDateString();
21
+ if (isToday)
22
+ return "Today";
23
+ if (isYesterday)
24
+ return "Yesterday";
25
+ return date.toLocaleDateString([], {
26
+ weekday: "long",
27
+ month: "short",
28
+ day: "numeric",
29
+ });
30
+ }
31
+ function isDifferentDay(date1, date2) {
32
+ return new Date(date1).toDateString() !== new Date(date2).toDateString();
33
+ }
34
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAEH,gCAGC;AAED,4CAiBC;AAED,wCAEC;AA1BD,SAAgB,UAAU,CAAC,UAAkB;IAC3C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;IACjC,OAAO,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;AAC5E,CAAC;AAED,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;IACjC,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAA;IACxB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAA;IACjC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;IAE1C,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,KAAK,CAAC,YAAY,EAAE,CAAA;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,SAAS,CAAC,YAAY,EAAE,CAAA;IAEpE,IAAI,OAAO;QAAE,OAAO,OAAO,CAAA;IAC3B,IAAI,WAAW;QAAE,OAAO,WAAW,CAAA;IAEnC,OAAO,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE;QACjC,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;KACf,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,KAAa,EAAE,KAAa;IACzD,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,CAAA;AAC1E,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@portablecore/chat",
3
+ "version": "0.1.0",
4
+ "description": "Unified chat UI for Portable platforms — composable ChatInterface with extension points",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "keywords": [
17
+ "portable",
18
+ "chat",
19
+ "ui",
20
+ "streaming",
21
+ "sse"
22
+ ],
23
+ "author": "Portable",
24
+ "license": "UNLICENSED",
25
+ "dependencies": {
26
+ "remark-gfm": "^4.0.1",
27
+ "remark-breaks": "^4.0.0",
28
+ "rehype-raw": "^7.0.0",
29
+ "rehype-highlight": "^7.0.2",
30
+ "@portablecore/types": "0.10.0",
31
+ "@portablecore/chat-runtime": "0.1.0"
32
+ },
33
+ "peerDependencies": {
34
+ "react": ">=18.0.0",
35
+ "react-markdown": ">=9.0.0",
36
+ "@nvq/flowtoken": ">=2.0.0",
37
+ "lucide-react": ">=0.300.0"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.3.0",
41
+ "@types/react": "^18.0.0",
42
+ "react": "^18.0.0",
43
+ "react-markdown": "^10.1.0",
44
+ "@nvq/flowtoken": "^2.0.6",
45
+ "lucide-react": "^0.562.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "dev": "tsc --watch",
50
+ "clean": "rm -rf dist",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }