@composy/chat-core 0.0.1 → 0.0.2

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 (168) hide show
  1. package/README.md +187 -123
  2. package/dist/index.js +2018 -1035
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.min.js +1370 -1644
  5. package/dist/index.min.js.map +1 -0
  6. package/es/.dts-output-manifest.json +1 -0
  7. package/es/.dts.tsbuildinfo +1 -0
  8. package/es/constants/dom.d.ts +3 -19
  9. package/es/constants/dom.js +4 -4
  10. package/es/constants/dom.js.map +1 -1
  11. package/es/core/chat-manager.d.ts +33 -22
  12. package/es/core/chat-manager.js +174 -50
  13. package/es/core/chat-manager.js.map +1 -1
  14. package/es/core/event-emitter.d.ts +0 -1
  15. package/es/core/event-emitter.js +14 -7
  16. package/es/core/event-emitter.js.map +1 -1
  17. package/es/core/mention-engine.d.ts +1 -2
  18. package/es/core/mention-engine.js +9 -9
  19. package/es/core/mention-engine.js.map +1 -1
  20. package/es/core/message-schema.d.ts +19 -0
  21. package/es/core/message-schema.js +197 -0
  22. package/es/core/message-schema.js.map +1 -0
  23. package/es/core/outbox.d.ts +63 -0
  24. package/es/core/outbox.js +160 -0
  25. package/es/core/outbox.js.map +1 -0
  26. package/es/core/transport.d.ts +79 -0
  27. package/es/core/transport.js +102 -0
  28. package/es/core/transport.js.map +1 -0
  29. package/es/index.d.ts +12 -7
  30. package/es/index.js +167 -36
  31. package/es/index.js.map +1 -1
  32. package/es/renderer/dom-renderer.d.ts +16 -4
  33. package/es/renderer/dom-renderer.js +316 -61
  34. package/es/renderer/dom-renderer.js.map +1 -1
  35. package/es/renderer/style-manager.d.ts +14 -38
  36. package/es/renderer/style-manager.js +92 -63
  37. package/es/renderer/style-manager.js.map +1 -1
  38. package/es/theme/theme-manager.d.ts +4 -3
  39. package/es/theme/theme-manager.js +19 -9
  40. package/es/theme/theme-manager.js.map +1 -1
  41. package/es/theme/tokens.d.ts +0 -1
  42. package/es/theme/tokens.js +92 -92
  43. package/es/theme/tokens.js.map +1 -1
  44. package/es/types/index.d.ts +104 -24
  45. package/es/types/index.js +8 -3
  46. package/es/types/index.js.map +1 -0
  47. package/esm/constants/dom.d.ts +3 -19
  48. package/esm/constants/dom.js +4 -4
  49. package/esm/constants/dom.js.map +1 -1
  50. package/esm/core/chat-manager.d.ts +33 -22
  51. package/esm/core/chat-manager.js +174 -50
  52. package/esm/core/chat-manager.js.map +1 -1
  53. package/esm/core/event-emitter.d.ts +0 -1
  54. package/esm/core/event-emitter.js +14 -7
  55. package/esm/core/event-emitter.js.map +1 -1
  56. package/esm/core/mention-engine.d.ts +1 -2
  57. package/esm/core/mention-engine.js +9 -9
  58. package/esm/core/mention-engine.js.map +1 -1
  59. package/esm/core/message-schema.d.ts +19 -0
  60. package/esm/core/message-schema.js +197 -0
  61. package/esm/core/message-schema.js.map +1 -0
  62. package/esm/core/outbox.d.ts +63 -0
  63. package/esm/core/outbox.js +160 -0
  64. package/esm/core/outbox.js.map +1 -0
  65. package/esm/core/transport.d.ts +79 -0
  66. package/esm/core/transport.js +102 -0
  67. package/esm/core/transport.js.map +1 -0
  68. package/esm/index.d.ts +12 -7
  69. package/esm/index.js +169 -38
  70. package/esm/index.js.map +1 -1
  71. package/esm/renderer/dom-renderer.d.ts +16 -4
  72. package/esm/renderer/dom-renderer.js +316 -61
  73. package/esm/renderer/dom-renderer.js.map +1 -1
  74. package/esm/renderer/style-manager.d.ts +14 -38
  75. package/esm/renderer/style-manager.js +92 -63
  76. package/esm/renderer/style-manager.js.map +1 -1
  77. package/esm/theme/theme-manager.d.ts +4 -3
  78. package/esm/theme/theme-manager.js +19 -9
  79. package/esm/theme/theme-manager.js.map +1 -1
  80. package/esm/theme/tokens.d.ts +0 -1
  81. package/esm/theme/tokens.js +92 -92
  82. package/esm/theme/tokens.js.map +1 -1
  83. package/esm/types/index.d.ts +104 -24
  84. package/esm/types/index.js +8 -3
  85. package/esm/types/index.js.map +1 -0
  86. package/lib/constants/dom.cjs +4 -4
  87. package/lib/constants/dom.cjs.map +1 -1
  88. package/lib/constants/dom.d.cts +25 -0
  89. package/lib/constants/dom.d.ts +3 -19
  90. package/lib/core/chat-manager.cjs +174 -50
  91. package/lib/core/chat-manager.cjs.map +1 -1
  92. package/lib/core/chat-manager.d.cts +45 -0
  93. package/lib/core/chat-manager.d.ts +33 -22
  94. package/lib/core/event-emitter.cjs +14 -7
  95. package/lib/core/event-emitter.cjs.map +1 -1
  96. package/lib/core/event-emitter.d.cts +47 -0
  97. package/lib/core/event-emitter.d.ts +0 -1
  98. package/lib/core/mention-engine.cjs +9 -9
  99. package/lib/core/mention-engine.cjs.map +1 -1
  100. package/lib/core/mention-engine.d.cts +40 -0
  101. package/lib/core/mention-engine.d.ts +1 -2
  102. package/lib/core/message-schema.cjs +202 -0
  103. package/lib/core/message-schema.cjs.map +1 -0
  104. package/lib/core/message-schema.d.cts +19 -0
  105. package/lib/core/message-schema.d.ts +19 -0
  106. package/lib/core/outbox.cjs +162 -0
  107. package/lib/core/outbox.cjs.map +1 -0
  108. package/lib/core/outbox.d.cts +63 -0
  109. package/lib/core/outbox.d.ts +63 -0
  110. package/lib/core/transport.cjs +103 -0
  111. package/lib/core/transport.cjs.map +1 -0
  112. package/lib/core/transport.d.cts +79 -0
  113. package/lib/core/transport.d.ts +79 -0
  114. package/lib/index.cjs +175 -35
  115. package/lib/index.cjs.map +1 -1
  116. package/lib/index.d.cts +33 -0
  117. package/lib/index.d.ts +12 -7
  118. package/lib/renderer/dom-renderer.cjs +316 -61
  119. package/lib/renderer/dom-renderer.cjs.map +1 -1
  120. package/lib/renderer/dom-renderer.d.cts +91 -0
  121. package/lib/renderer/dom-renderer.d.ts +16 -4
  122. package/lib/renderer/style-manager.cjs +92 -62
  123. package/lib/renderer/style-manager.cjs.map +1 -1
  124. package/lib/renderer/style-manager.d.cts +34 -0
  125. package/lib/renderer/style-manager.d.ts +14 -38
  126. package/lib/theme/theme-manager.cjs +19 -9
  127. package/lib/theme/theme-manager.cjs.map +1 -1
  128. package/lib/theme/theme-manager.d.cts +34 -0
  129. package/lib/theme/theme-manager.d.ts +4 -3
  130. package/lib/theme/tokens.cjs +92 -92
  131. package/lib/theme/tokens.cjs.map +1 -1
  132. package/lib/theme/tokens.d.cts +56 -0
  133. package/lib/theme/tokens.d.ts +0 -1
  134. package/lib/types/index.cjs +8 -3
  135. package/lib/types/index.cjs.map +1 -0
  136. package/lib/types/index.d.cts +284 -0
  137. package/lib/types/index.d.ts +104 -24
  138. package/package.json +16 -13
  139. package/es/constants/dom.d.ts.map +0 -1
  140. package/es/core/chat-manager.d.ts.map +0 -1
  141. package/es/core/event-emitter.d.ts.map +0 -1
  142. package/es/core/mention-engine.d.ts.map +0 -1
  143. package/es/index.d.ts.map +0 -1
  144. package/es/renderer/dom-renderer.d.ts.map +0 -1
  145. package/es/renderer/style-manager.d.ts.map +0 -1
  146. package/es/theme/theme-manager.d.ts.map +0 -1
  147. package/es/theme/tokens.d.ts.map +0 -1
  148. package/es/types/index.d.ts.map +0 -1
  149. package/esm/constants/dom.d.ts.map +0 -1
  150. package/esm/core/chat-manager.d.ts.map +0 -1
  151. package/esm/core/event-emitter.d.ts.map +0 -1
  152. package/esm/core/mention-engine.d.ts.map +0 -1
  153. package/esm/index.d.ts.map +0 -1
  154. package/esm/renderer/dom-renderer.d.ts.map +0 -1
  155. package/esm/renderer/style-manager.d.ts.map +0 -1
  156. package/esm/theme/theme-manager.d.ts.map +0 -1
  157. package/esm/theme/tokens.d.ts.map +0 -1
  158. package/esm/types/index.d.ts.map +0 -1
  159. package/lib/constants/dom.d.ts.map +0 -1
  160. package/lib/core/chat-manager.d.ts.map +0 -1
  161. package/lib/core/event-emitter.d.ts.map +0 -1
  162. package/lib/core/mention-engine.d.ts.map +0 -1
  163. package/lib/index.d.ts.map +0 -1
  164. package/lib/renderer/dom-renderer.d.ts.map +0 -1
  165. package/lib/renderer/style-manager.d.ts.map +0 -1
  166. package/lib/theme/theme-manager.d.ts.map +0 -1
  167. package/lib/theme/tokens.d.ts.map +0 -1
  168. package/lib/types/index.d.ts.map +0 -1
package/lib/index.cjs CHANGED
@@ -1,26 +1,35 @@
1
1
  /*!
2
2
  * ***********************************
3
- * @composy/chat-core v0.0.1 *
3
+ * @composy/chat-core v0.0.2 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:30:32 *
5
+ * Build time: 2024-10-12 21:52:18 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
9
9
  */
10
10
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
11
11
  const require_dom = require("./constants/dom.cjs");
12
+ const require_index = require("./types/index.cjs");
13
+ const require_message_schema = require("./core/message-schema.cjs");
12
14
  const require_chat_manager = require("./core/chat-manager.cjs");
13
15
  const require_event_emitter = require("./core/event-emitter.cjs");
14
16
  const require_mention_engine = require("./core/mention-engine.cjs");
17
+ const require_outbox = require("./core/outbox.cjs");
18
+ const require_transport = require("./core/transport.cjs");
19
+ const require_style_manager = require("./renderer/style-manager.cjs");
20
+ const require_dom_renderer = require("./renderer/dom-renderer.cjs");
15
21
  const require_tokens = require("./theme/tokens.cjs");
16
22
  const require_theme_manager = require("./theme/theme-manager.cjs");
17
- const require_dom_renderer = require("./renderer/dom-renderer.cjs");
18
- const require_style_manager = require("./renderer/style-manager.cjs");
19
23
  //#region src/index.ts
20
24
  const version = "0.1.0";
21
25
  function createChat(config) {
22
- const container = typeof config.container === "string" ? document.querySelector(config.container) : config.container;
26
+ let container;
27
+ if (typeof config.container === "string") {
28
+ if (typeof document === "undefined") throw new Error("[Chat] A browser Document is required to resolve a container selector.");
29
+ container = document.querySelector(config.container);
30
+ } else container = config.container;
23
31
  if (!container) throw new Error(`[Chat] Container not found: ${config.container}`);
32
+ const ownerDocument = container.ownerDocument;
24
33
  const prefix = require_dom.normalizeClassPrefix(config.classPrefix ?? "ldesign-chat");
25
34
  const normalizedConfig = {
26
35
  ...config,
@@ -29,23 +38,70 @@ function createChat(config) {
29
38
  const emitter = new require_event_emitter.EventEmitter();
30
39
  const chatManager = new require_chat_manager.ChatManager(emitter, normalizedConfig.messages);
31
40
  const mentionEngine = new require_mention_engine.MentionEngine(emitter, normalizedConfig.mentionMembers);
32
- const themeManager = new require_theme_manager.ThemeManager(emitter, normalizedConfig.theme ?? "light");
41
+ const themeManager = new require_theme_manager.ThemeManager(emitter, normalizedConfig.theme ?? "light", ownerDocument.defaultView);
33
42
  const renderer = new require_dom_renderer.DOMRenderer(container, normalizedConfig, emitter, mentionEngine);
34
- require_style_manager.injectStyles(themeManager.getTokens(), prefix);
43
+ let destroyed = false;
44
+ const schedule = (callback) => {
45
+ const run = () => {
46
+ if (!destroyed) callback();
47
+ };
48
+ if (ownerDocument.defaultView?.requestAnimationFrame) ownerDocument.defaultView.requestAnimationFrame(run);
49
+ else setTimeout(run, 0);
50
+ };
35
51
  renderer.init();
36
52
  renderer.renderMessages(chatManager.getMessages());
37
- requestAnimationFrame(() => {
53
+ renderer.setThemeTokens(themeManager.getTokens());
54
+ require_style_manager.injectStyles(themeManager.getTokens(), prefix, renderer.getRootElement());
55
+ const applyTransportAck = async (ack) => {
56
+ const localId = chatManager.getMessageIdByClientMessageId(ack.clientMessageId);
57
+ if (localId) chatManager.updateMessage(localId, {
58
+ ...ack.message ?? {},
59
+ id: localId,
60
+ clientMessageId: ack.clientMessageId,
61
+ status: "sent",
62
+ ...ack.sequence !== void 0 && { sequence: ack.sequence },
63
+ ...ack.cursor !== void 0 && { cursor: ack.cursor }
64
+ });
65
+ if (normalizedConfig.outbox) await normalizedConfig.outbox.remove(ack.clientMessageId);
66
+ emitter.emit("transport-ack", ack);
67
+ };
68
+ const handleTransportEvent = (event) => {
69
+ if (event.type === "message") chatManager.upsertMessage({
70
+ ...event.message,
71
+ ...event.cursor !== void 0 && { cursor: event.cursor }
72
+ }, {
73
+ sequence: event.sequence,
74
+ source: "remote"
75
+ });
76
+ else if (event.type === "ack") applyTransportAck(event.ack).catch((error) => {
77
+ emitter.emit("transport-error", error);
78
+ });
79
+ else if (event.type === "cursor") emitter.emit("transport-cursor", event.cursor);
80
+ else emitter.emit("transport-error", event.error, event.recoverable);
81
+ };
82
+ const transportController = normalizedConfig.transport ? new require_transport.ChatTransportController(normalizedConfig.transport, {
83
+ conversationId: normalizedConfig.conversationId,
84
+ cursor: normalizedConfig.transportCursor,
85
+ onEvent: handleTransportEvent,
86
+ onStatusChange: (status) => {
87
+ emitter.emit("transport-status", status);
88
+ }
89
+ }) : null;
90
+ schedule(() => {
38
91
  renderer.scrollToBottom(false);
39
92
  });
40
93
  emitter.on("theme-change", (_resolved, tokens) => {
41
- require_style_manager.updateStyles(tokens, prefix);
94
+ renderer.setThemeTokens(tokens);
42
95
  });
43
96
  emitter.on("message-added", (message) => {
44
97
  renderer.appendMessage(message);
45
- requestAnimationFrame(() => {
98
+ schedule(() => {
46
99
  renderer.scrollToBottom(true);
47
100
  });
48
101
  });
102
+ emitter.on("mention-select", (member) => {
103
+ normalizedConfig.onMentionSelect?.(member);
104
+ });
49
105
  emitter.on("messages-prepended", (messages) => {
50
106
  renderer.prependMessages(messages);
51
107
  });
@@ -57,101 +113,185 @@ function createChat(config) {
57
113
  });
58
114
  emitter.on("message-content-appended", (id, content) => {
59
115
  renderer.updateMessageContent(id, content);
60
- requestAnimationFrame(() => {
116
+ schedule(() => {
61
117
  renderer.scrollToBottom(false);
62
118
  });
63
119
  });
64
- return {
120
+ const instance = {
65
121
  addMessage(message) {
66
- chatManager.addMessage(message);
122
+ if (!destroyed) chatManager.addMessage(message);
67
123
  },
68
124
  prependMessages(messages) {
69
- chatManager.prependMessages(messages);
125
+ if (!destroyed) chatManager.prependMessages(messages);
70
126
  },
71
127
  updateMessage(id, updates) {
72
- chatManager.updateMessage(id, updates);
128
+ if (!destroyed) chatManager.updateMessage(id, updates);
73
129
  },
74
130
  removeMessage(id) {
75
- chatManager.removeMessage(id);
131
+ if (!destroyed) chatManager.removeMessage(id);
76
132
  },
77
133
  setMessages(messages) {
134
+ if (destroyed) return;
78
135
  chatManager.setMessages(messages);
79
136
  renderer.renderMessages(chatManager.getMessages());
80
- requestAnimationFrame(() => {
137
+ schedule(() => {
81
138
  renderer.scrollToBottom(false);
82
139
  });
83
140
  },
84
141
  clearMessages() {
85
- chatManager.clearMessages();
86
- renderer.renderMessages([]);
142
+ if (!destroyed) {
143
+ chatManager.clearMessages();
144
+ renderer.renderMessages([]);
145
+ }
87
146
  },
88
147
  scrollToBottom(smooth = true) {
89
- renderer.scrollToBottom(smooth);
148
+ if (!destroyed) renderer.scrollToBottom(smooth);
90
149
  },
91
150
  setTheme(theme) {
92
- themeManager.setTheme(theme);
151
+ if (!destroyed) themeManager.setTheme(theme);
93
152
  },
94
153
  setMentionMembers(members) {
95
- mentionEngine.setMembers(members);
154
+ if (!destroyed) mentionEngine.setMembers(members);
96
155
  },
97
156
  setLoadingMore(loading) {
98
- renderer.setLoadingMore(loading);
157
+ if (!destroyed) renderer.setLoadingMore(loading);
99
158
  },
100
159
  focusInput() {
101
- renderer.focusInput();
160
+ if (!destroyed) renderer.focusInput();
102
161
  },
103
162
  setInputContent(content) {
104
- renderer.setInputContent(content);
163
+ if (!destroyed) renderer.setInputContent(content);
105
164
  },
106
165
  getInputContent() {
107
- return renderer.getInputContent();
166
+ return destroyed ? "" : renderer.getInputContent();
108
167
  },
109
168
  clearInput() {
110
- renderer.clearInput();
169
+ if (!destroyed) renderer.clearInput();
111
170
  },
112
171
  isAtBottom(threshold) {
113
- return renderer.isAtBottom(threshold);
172
+ return destroyed || renderer.isAtBottom(threshold);
114
173
  },
115
174
  on(event, handler) {
116
- return emitter.on(event, handler);
175
+ return destroyed ? () => {} : emitter.on(event, handler);
117
176
  },
118
177
  getMessages() {
119
- return chatManager.getMessages();
178
+ return destroyed ? [] : chatManager.getMessages();
120
179
  },
121
180
  getMessage(id) {
122
- return chatManager.getMessage(id);
181
+ return destroyed ? void 0 : chatManager.getMessage(id);
123
182
  },
124
183
  appendMessageContent(id, chunk) {
125
- chatManager.appendMessageContent(id, chunk);
184
+ return destroyed ? false : chatManager.appendMessageContent(id, chunk);
126
185
  },
127
- finishStreaming(id) {
128
- chatManager.finishStreaming(id);
186
+ finishStreaming(id, generation) {
187
+ return destroyed ? false : chatManager.finishStreaming(id, generation);
188
+ },
189
+ beginStreaming(id, generation) {
190
+ return destroyed ? null : chatManager.beginStreaming(id, generation);
191
+ },
192
+ cancelStreaming(id, generation) {
193
+ return destroyed ? false : chatManager.cancelStreaming(id, generation);
194
+ },
195
+ mergeMessage(message, sequence) {
196
+ return destroyed ? "ignored" : chatManager.upsertMessage(message, {
197
+ sequence,
198
+ source: "remote"
199
+ });
200
+ },
201
+ async connectTransport(signal) {
202
+ if (!transportController) throw new Error("No chat transport is configured.");
203
+ await transportController.connect(signal);
204
+ },
205
+ async disconnectTransport() {
206
+ await transportController?.disconnect();
207
+ },
208
+ getTransportStatus() {
209
+ return transportController?.getStatus() ?? "idle";
210
+ },
211
+ async sendMessage(message, options = {}) {
212
+ if (!transportController) throw new Error("No chat transport is configured.");
213
+ if (destroyed) throw new Error("Chat instance is destroyed.");
214
+ const prepared = require_message_schema.normalizeChatMessage({
215
+ ...message,
216
+ clientMessageId: message.clientMessageId ?? createClientMessageId(message.id),
217
+ status: message.status ?? "sending"
218
+ });
219
+ chatManager.addMessage(prepared);
220
+ try {
221
+ if (normalizedConfig.outbox) await normalizedConfig.outbox.enqueue(prepared);
222
+ const ack = await transportController.send(prepared, {
223
+ signal: options.signal,
224
+ clientMessageId: prepared.clientMessageId
225
+ });
226
+ if (ack) await applyTransportAck(ack);
227
+ else chatManager.markMessageStatus(prepared.id, "sent");
228
+ emitter.emit("message-sent", prepared, ack);
229
+ return ack;
230
+ } catch (error) {
231
+ chatManager.markMessageStatus(prepared.id, "failed");
232
+ emitter.emit("message-send-failed", prepared, error);
233
+ throw error;
234
+ }
235
+ },
236
+ async flushOutbox(options = {}) {
237
+ if (!normalizedConfig.outbox) return {
238
+ sent: [],
239
+ failed: [],
240
+ remaining: []
241
+ };
242
+ if (!transportController) throw new Error("No chat transport is configured.");
243
+ return normalizedConfig.outbox.flush(async (message, sendOptions) => {
244
+ const ack = await transportController.send(message, sendOptions);
245
+ if (ack) await applyTransportAck(ack);
246
+ else if (message.id) chatManager.markMessageStatus(message.id, "sent");
247
+ }, options);
129
248
  },
130
249
  destroy() {
250
+ if (destroyed) return;
251
+ destroyed = true;
252
+ transportController?.dispose();
131
253
  themeManager.destroy();
132
254
  renderer.destroy();
133
255
  emitter.removeAll();
134
- require_style_manager.removeStyles(prefix);
256
+ require_style_manager.removeStyles(prefix, ownerDocument);
135
257
  }
136
258
  };
259
+ if (normalizedConfig.autoConnect && transportController) instance.connectTransport().catch((error) => {
260
+ emitter.emit("transport-error", error);
261
+ });
262
+ return instance;
263
+ }
264
+ function createClientMessageId(messageId) {
265
+ const cryptoObject = globalThis.crypto;
266
+ if (cryptoObject?.randomUUID) return cryptoObject.randomUUID();
267
+ return `client-${messageId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
137
268
  }
138
269
  //#endregion
270
+ exports.CHAT_MESSAGE_SCHEMA_VERSION = require_index.CHAT_MESSAGE_SCHEMA_VERSION;
139
271
  exports.COMPONENT_CSS_VAR_PREFIX = require_dom.COMPONENT_CSS_VAR_PREFIX;
140
272
  exports.ChatManager = require_chat_manager.ChatManager;
273
+ exports.ChatOutbox = require_outbox.ChatOutbox;
274
+ exports.ChatTransportController = require_transport.ChatTransportController;
141
275
  exports.DEFAULT_CLASS_PREFIX = require_dom.DEFAULT_CLASS_PREFIX;
142
276
  exports.DOMRenderer = require_dom_renderer.DOMRenderer;
143
277
  exports.EventEmitter = require_event_emitter.EventEmitter;
278
+ exports.MemoryChatOutboxStorage = require_outbox.MemoryChatOutboxStorage;
144
279
  exports.MentionEngine = require_mention_engine.MentionEngine;
145
280
  exports.STYLE_ELEMENT_ID = require_dom.STYLE_ELEMENT_ID;
146
281
  exports.ThemeManager = require_theme_manager.ThemeManager;
282
+ exports.cloneChatMessage = require_message_schema.cloneChatMessage;
147
283
  exports.cls = require_style_manager.cls;
148
284
  exports.clsx = require_style_manager.clsx;
285
+ exports.compareChatMessages = require_message_schema.compareChatMessages;
149
286
  exports.createChat = createChat;
150
287
  exports.cx = require_dom.cx;
151
288
  exports.darkTokens = require_tokens.darkTokens;
152
289
  exports.getStyleElementId = require_dom.getStyleElementId;
153
290
  exports.injectStyles = require_style_manager.injectStyles;
154
291
  exports.lightTokens = require_tokens.lightTokens;
292
+ exports.normalizeChatContentBlocks = require_message_schema.normalizeChatContentBlocks;
293
+ exports.normalizeChatMessage = require_message_schema.normalizeChatMessage;
294
+ exports.normalizeChatMessages = require_message_schema.normalizeChatMessages;
155
295
  exports.normalizeClassPrefix = require_dom.normalizeClassPrefix;
156
296
  exports.removeStyles = require_style_manager.removeStyles;
157
297
  exports.updateStyles = require_style_manager.updateStyles;
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @composy/chat-core - 即时通讯/IM 组件核心库\n *\n * 提供聊天气泡、消息列表、@提及、多消息类型(文本/图片/文件/系统消息/自定义)、\n * 消息状态管理(发送中/已发送/已送达/已读/失败)、亮暗主题切换等能力。\n * 框架无关的核心实现,配合 Vue/React 适配层使用。\n *\n * @packageDocumentation\n * @module @composy/chat-core\n */\n\nimport type { ChatConfig, ChatInstance, ChatMessage, ThemeMode, MentionItem, ToolbarConfig } from './types';\nimport type { ThemeTokens } from './theme/tokens';\nimport { EventEmitter } from './core/event-emitter';\nimport { ChatManager } from './core/chat-manager';\nimport { MentionEngine } from './core/mention-engine';\nimport { ThemeManager } from './theme/theme-manager';\nimport { DOMRenderer } from './renderer/dom-renderer';\nimport { injectStyles, updateStyles, removeStyles } from './renderer/style-manager';\nimport { DEFAULT_CLASS_PREFIX, normalizeClassPrefix } from './constants/dom';\n\nexport const version = '0.1.0';\n\n// Types\nexport * from './types';\n\n// Core\nexport { EventEmitter } from './core/event-emitter';\nexport { ChatManager } from './core/chat-manager';\nexport { MentionEngine } from './core/mention-engine';\nexport type { MentionState } from './core/mention-engine';\n\n// Theme\nexport { ThemeManager } from './theme/theme-manager';\nexport type { ThemeTokens } from './theme/tokens';\nexport { lightTokens, darkTokens } from './theme/tokens';\n\n// Renderer\nexport { DOMRenderer } from './renderer/dom-renderer';\nexport { injectStyles, updateStyles, removeStyles, cls, clsx } from './renderer/style-manager';\nexport {\n DEFAULT_CLASS_PREFIX,\n COMPONENT_CSS_VAR_PREFIX,\n STYLE_ELEMENT_ID,\n getStyleElementId,\n normalizeClassPrefix,\n cx,\n} from './constants/dom';\n\n/**\n * 创建聊天实例\n */\nexport function createChat(config: ChatConfig): ChatInstance {\n // 解析容器\n const container =\n typeof config.container === 'string'\n ? document.querySelector<HTMLElement>(config.container)\n : config.container;\n\n if (!container) {\n throw new Error(`[Chat] Container not found: ${config.container}`);\n }\n\n const prefix = normalizeClassPrefix(config.classPrefix ?? DEFAULT_CLASS_PREFIX);\n const normalizedConfig = {\n ...config,\n classPrefix: prefix,\n };\n const emitter = new EventEmitter();\n const chatManager = new ChatManager(emitter, normalizedConfig.messages);\n const mentionEngine = new MentionEngine(emitter, normalizedConfig.mentionMembers);\n const themeManager = new ThemeManager(emitter, normalizedConfig.theme ?? 'light');\n const renderer = new DOMRenderer(container, normalizedConfig, emitter, mentionEngine);\n\n // 注入样式\n injectStyles(themeManager.getTokens(), prefix);\n\n // 初始化渲染\n renderer.init();\n renderer.renderMessages(chatManager.getMessages());\n\n // 初始滚动到底部\n requestAnimationFrame(() => {\n renderer.scrollToBottom(false);\n });\n\n // 监听主题变化\n emitter.on('theme-change', (_resolved: string, tokens: ThemeTokens) => {\n updateStyles(tokens, prefix);\n });\n\n // 监听消息变化\n emitter.on('message-added', (message: ChatMessage) => {\n renderer.appendMessage(message);\n requestAnimationFrame(() => {\n renderer.scrollToBottom(true);\n });\n });\n\n emitter.on('messages-prepended', (messages: ChatMessage[]) => {\n renderer.prependMessages(messages);\n });\n\n emitter.on('message-updated', (message: ChatMessage) => {\n renderer.updateMessage(message.id, message);\n });\n\n emitter.on('message-removed', (message: ChatMessage) => {\n renderer.removeMessage(message.id);\n });\n\n // 流式消息内容追加\n emitter.on('message-content-appended', (id: string, content: string) => {\n renderer.updateMessageContent(id, content);\n requestAnimationFrame(() => {\n renderer.scrollToBottom(false);\n });\n });\n\n // 创建实例\n const instance: ChatInstance = {\n addMessage(message: ChatMessage) {\n chatManager.addMessage(message);\n },\n\n prependMessages(messages: ChatMessage[]) {\n chatManager.prependMessages(messages);\n },\n\n updateMessage(id: string, updates: Partial<ChatMessage>) {\n chatManager.updateMessage(id, updates);\n },\n\n removeMessage(id: string) {\n chatManager.removeMessage(id);\n },\n\n setMessages(messages: ChatMessage[]) {\n chatManager.setMessages(messages);\n renderer.renderMessages(chatManager.getMessages());\n requestAnimationFrame(() => {\n renderer.scrollToBottom(false);\n });\n },\n\n clearMessages() {\n chatManager.clearMessages();\n renderer.renderMessages([]);\n },\n\n scrollToBottom(smooth = true) {\n renderer.scrollToBottom(smooth);\n },\n\n setTheme(theme: ThemeMode) {\n themeManager.setTheme(theme);\n },\n\n setMentionMembers(members: MentionItem[]) {\n mentionEngine.setMembers(members);\n },\n\n setLoadingMore(loading: boolean) {\n renderer.setLoadingMore(loading);\n },\n\n focusInput() {\n renderer.focusInput();\n },\n\n setInputContent(content: string) {\n renderer.setInputContent(content);\n },\n\n getInputContent() {\n return renderer.getInputContent();\n },\n\n clearInput() {\n renderer.clearInput();\n },\n\n isAtBottom(threshold?: number) {\n return renderer.isAtBottom(threshold);\n },\n\n on(event: string, handler: (...args: unknown[]) => void) {\n return emitter.on(event, handler);\n },\n\n getMessages() {\n return chatManager.getMessages();\n },\n\n getMessage(id: string) {\n return chatManager.getMessage(id);\n },\n\n appendMessageContent(id: string, chunk: string) {\n chatManager.appendMessageContent(id, chunk);\n },\n\n finishStreaming(id: string) {\n chatManager.finishStreaming(id);\n },\n\n destroy() {\n themeManager.destroy();\n renderer.destroy();\n emitter.removeAll();\n removeStyles(prefix);\n },\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * @composy/chat-core - 即时通讯/IM 组件核心库\r\n *\r\n * 提供聊天气泡、消息列表、@提及、多消息类型(文本/图片/文件/系统消息/自定义)、\r\n * 消息状态管理(发送中/已发送/已送达/已读/失败)、亮暗主题切换等能力。\r\n * 框架无关的核心实现,配合 Vue/React 适配层使用。\r\n *\r\n * @packageDocumentation\r\n * @module @composy/chat-core\r\n */\r\n\r\nimport type { ChatOutboxFlushOptions } from './core/outbox'\r\nimport type {\r\n ChatTransportAck,\r\n ChatTransportEvent,\r\n ChatTransportStatus,\r\n} from './core/transport'\r\nimport type { ThemeTokens } from './theme/tokens'\r\nimport type {\r\n ChatConfig,\r\n ChatInstance,\r\n ChatMessage,\r\n MentionItem,\r\n ThemeMode,\r\n} from './types'\r\nimport { DEFAULT_CLASS_PREFIX, normalizeClassPrefix } from './constants/dom'\r\nimport { ChatManager } from './core/chat-manager'\r\nimport { EventEmitter } from './core/event-emitter'\r\nimport { MentionEngine } from './core/mention-engine'\r\nimport { normalizeChatMessage } from './core/message-schema'\r\nimport { ChatTransportController } from './core/transport'\r\nimport { DOMRenderer } from './renderer/dom-renderer'\r\nimport { injectStyles, removeStyles } from './renderer/style-manager'\r\nimport { ThemeManager } from './theme/theme-manager'\r\n\r\nexport const version = '0.1.0'\r\n\r\nexport {\r\n COMPONENT_CSS_VAR_PREFIX,\r\n cx,\r\n DEFAULT_CLASS_PREFIX,\r\n getStyleElementId,\r\n normalizeClassPrefix,\r\n STYLE_ELEMENT_ID,\r\n} from './constants/dom'\r\n\r\nexport { ChatManager } from './core/chat-manager'\r\nexport type {\r\n MessageMergeOptions,\r\n MessageMergeResult,\r\n StreamAppendOptions,\r\n} from './core/chat-manager'\r\n// Core\r\nexport { EventEmitter } from './core/event-emitter'\r\nexport { MentionEngine } from './core/mention-engine'\r\nexport type { MentionState } from './core/mention-engine'\r\nexport {\r\n cloneChatMessage,\r\n compareChatMessages,\r\n normalizeChatContentBlocks,\r\n normalizeChatMessage,\r\n normalizeChatMessages,\r\n} from './core/message-schema'\r\nexport { ChatOutbox, MemoryChatOutboxStorage } from './core/outbox'\r\nexport type {\r\n ChatOutboxEntry,\r\n ChatOutboxFlushOptions,\r\n ChatOutboxFlushResult,\r\n ChatOutboxOptions,\r\n ChatOutboxStorage,\r\n} from './core/outbox'\r\nexport { ChatTransportController } from './core/transport'\r\nexport type {\r\n ChatTransport,\r\n ChatTransportAck,\r\n ChatTransportContext,\r\n ChatTransportControllerOptions,\r\n ChatTransportEvent,\r\n ChatTransportStatus,\r\n} from './core/transport'\r\n\r\n// Renderer\r\nexport { DOMRenderer } from './renderer/dom-renderer'\r\nexport {\r\n cls,\r\n clsx,\r\n injectStyles,\r\n removeStyles,\r\n updateStyles,\r\n} from './renderer/style-manager'\r\n// Theme\r\nexport { ThemeManager } from './theme/theme-manager'\r\n\r\nexport type { ThemeTokens } from './theme/tokens'\r\nexport { darkTokens, lightTokens } from './theme/tokens'\r\n// Types\r\nexport * from './types'\r\n\r\n/**\r\n * 创建聊天实例\r\n */\r\nexport function createChat(config: ChatConfig): ChatInstance {\r\n // 解析容器\r\n let container: HTMLElement | null\r\n if (typeof config.container === 'string') {\r\n if (typeof document === 'undefined') {\r\n throw new Error(\r\n '[Chat] A browser Document is required to resolve a container selector.'\r\n )\r\n }\r\n container = document.querySelector<HTMLElement>(config.container)\r\n } else {\r\n container = config.container\r\n }\r\n\r\n if (!container) {\r\n throw new Error(`[Chat] Container not found: ${config.container}`)\r\n }\r\n\r\n const ownerDocument = container.ownerDocument\r\n const prefix = normalizeClassPrefix(\r\n config.classPrefix ?? DEFAULT_CLASS_PREFIX\r\n )\r\n const normalizedConfig = {\r\n ...config,\r\n classPrefix: prefix,\r\n }\r\n const emitter = new EventEmitter()\r\n const chatManager = new ChatManager(emitter, normalizedConfig.messages)\r\n const mentionEngine = new MentionEngine(\r\n emitter,\r\n normalizedConfig.mentionMembers\r\n )\r\n const themeManager = new ThemeManager(\r\n emitter,\r\n normalizedConfig.theme ?? 'light',\r\n ownerDocument.defaultView\r\n )\r\n const renderer = new DOMRenderer(\r\n container,\r\n normalizedConfig,\r\n emitter,\r\n mentionEngine\r\n )\r\n let destroyed = false\r\n const schedule = (callback: () => void) => {\r\n const run = () => {\r\n if (!destroyed) callback()\r\n }\r\n if (ownerDocument.defaultView?.requestAnimationFrame) {\r\n ownerDocument.defaultView.requestAnimationFrame(run)\r\n } else {\r\n setTimeout(run, 0)\r\n }\r\n }\r\n\r\n // 初始化渲染\r\n renderer.init()\r\n renderer.renderMessages(chatManager.getMessages())\r\n renderer.setThemeTokens(themeManager.getTokens())\r\n injectStyles(themeManager.getTokens(), prefix, renderer.getRootElement())\r\n\r\n const applyTransportAck = async (ack: ChatTransportAck): Promise<void> => {\r\n const localId = chatManager.getMessageIdByClientMessageId(\r\n ack.clientMessageId\r\n )\r\n if (localId) {\r\n chatManager.updateMessage(localId, {\r\n ...(ack.message ?? {}),\r\n id: localId,\r\n clientMessageId: ack.clientMessageId,\r\n status: 'sent',\r\n ...(ack.sequence !== undefined && { sequence: ack.sequence }),\r\n ...(ack.cursor !== undefined && { cursor: ack.cursor }),\r\n })\r\n }\r\n if (normalizedConfig.outbox) {\r\n await normalizedConfig.outbox.remove(ack.clientMessageId)\r\n }\r\n emitter.emit('transport-ack', ack)\r\n }\r\n\r\n const handleTransportEvent = (event: ChatTransportEvent): void => {\r\n if (event.type === 'message') {\r\n chatManager.upsertMessage(\r\n {\r\n ...event.message,\r\n ...(event.cursor !== undefined && { cursor: event.cursor }),\r\n },\r\n { sequence: event.sequence, source: 'remote' }\r\n )\r\n } else if (event.type === 'ack') {\r\n void applyTransportAck(event.ack).catch(error => {\r\n emitter.emit('transport-error', error)\r\n })\r\n } else if (event.type === 'cursor') {\r\n emitter.emit('transport-cursor', event.cursor)\r\n } else {\r\n emitter.emit('transport-error', event.error, event.recoverable)\r\n }\r\n }\r\n\r\n const transportController = normalizedConfig.transport\r\n ? new ChatTransportController(normalizedConfig.transport, {\r\n conversationId: normalizedConfig.conversationId,\r\n cursor: normalizedConfig.transportCursor,\r\n onEvent: handleTransportEvent,\r\n onStatusChange: status => {\r\n emitter.emit('transport-status', status)\r\n },\r\n })\r\n : null\r\n\r\n // 初始滚动到底部\r\n schedule(() => {\r\n renderer.scrollToBottom(false)\r\n })\r\n\r\n // 监听主题变化\r\n emitter.on('theme-change', (_resolved: string, tokens: ThemeTokens) => {\r\n renderer.setThemeTokens(tokens)\r\n })\r\n\r\n // 监听消息变化\r\n emitter.on('message-added', (message: ChatMessage) => {\r\n renderer.appendMessage(message)\r\n schedule(() => {\r\n renderer.scrollToBottom(true)\r\n })\r\n })\r\n\r\n emitter.on('mention-select', (member: MentionItem) => {\r\n normalizedConfig.onMentionSelect?.(member)\r\n })\r\n\r\n emitter.on('messages-prepended', (messages: ChatMessage[]) => {\r\n renderer.prependMessages(messages)\r\n })\r\n\r\n emitter.on('message-updated', (message: ChatMessage) => {\r\n renderer.updateMessage(message.id, message)\r\n })\r\n\r\n emitter.on('message-removed', (message: ChatMessage) => {\r\n renderer.removeMessage(message.id)\r\n })\r\n\r\n // 流式消息内容追加\r\n emitter.on('message-content-appended', (id: string, content: string) => {\r\n renderer.updateMessageContent(id, content)\r\n schedule(() => {\r\n renderer.scrollToBottom(false)\r\n })\r\n })\r\n\r\n // 创建实例\r\n const instance: ChatInstance = {\r\n addMessage(message: ChatMessage) {\r\n if (!destroyed) chatManager.addMessage(message)\r\n },\r\n\r\n prependMessages(messages: ChatMessage[]) {\r\n if (!destroyed) chatManager.prependMessages(messages)\r\n },\r\n\r\n updateMessage(id: string, updates: Partial<ChatMessage>) {\r\n if (!destroyed) chatManager.updateMessage(id, updates)\r\n },\r\n\r\n removeMessage(id: string) {\r\n if (!destroyed) chatManager.removeMessage(id)\r\n },\r\n\r\n setMessages(messages: ChatMessage[]) {\r\n if (destroyed) return\r\n chatManager.setMessages(messages)\r\n renderer.renderMessages(chatManager.getMessages())\r\n schedule(() => {\r\n renderer.scrollToBottom(false)\r\n })\r\n },\r\n\r\n clearMessages() {\r\n if (!destroyed) {\r\n chatManager.clearMessages()\r\n renderer.renderMessages([])\r\n }\r\n },\r\n\r\n scrollToBottom(smooth = true) {\r\n if (!destroyed) renderer.scrollToBottom(smooth)\r\n },\r\n\r\n setTheme(theme: ThemeMode) {\r\n if (!destroyed) themeManager.setTheme(theme)\r\n },\r\n\r\n setMentionMembers(members: MentionItem[]) {\r\n if (!destroyed) mentionEngine.setMembers(members)\r\n },\r\n\r\n setLoadingMore(loading: boolean) {\r\n if (!destroyed) renderer.setLoadingMore(loading)\r\n },\r\n\r\n focusInput() {\r\n if (!destroyed) renderer.focusInput()\r\n },\r\n\r\n setInputContent(content: string) {\r\n if (!destroyed) renderer.setInputContent(content)\r\n },\r\n\r\n getInputContent() {\r\n return destroyed ? '' : renderer.getInputContent()\r\n },\r\n\r\n clearInput() {\r\n if (!destroyed) renderer.clearInput()\r\n },\r\n\r\n isAtBottom(threshold?: number) {\r\n return destroyed || renderer.isAtBottom(threshold)\r\n },\r\n\r\n on(event: string, handler: (...args: unknown[]) => void) {\r\n return destroyed ? () => {} : emitter.on(event, handler)\r\n },\r\n\r\n getMessages() {\r\n return destroyed ? [] : chatManager.getMessages()\r\n },\r\n\r\n getMessage(id: string) {\r\n return destroyed ? undefined : chatManager.getMessage(id)\r\n },\r\n\r\n appendMessageContent(id: string, chunk: string) {\r\n return destroyed ? false : chatManager.appendMessageContent(id, chunk)\r\n },\r\n\r\n finishStreaming(id: string, generation?: string) {\r\n return destroyed ? false : chatManager.finishStreaming(id, generation)\r\n },\r\n\r\n beginStreaming(id: string, generation?: string) {\r\n return destroyed ? null : chatManager.beginStreaming(id, generation)\r\n },\r\n\r\n cancelStreaming(id: string, generation?: string) {\r\n return destroyed ? false : chatManager.cancelStreaming(id, generation)\r\n },\r\n\r\n mergeMessage(message: ChatMessage, sequence?: number) {\r\n return destroyed\r\n ? 'ignored'\r\n : chatManager.upsertMessage(message, {\r\n sequence,\r\n source: 'remote',\r\n })\r\n },\r\n\r\n async connectTransport(signal?: AbortSignal) {\r\n if (!transportController) {\r\n throw new Error('No chat transport is configured.')\r\n }\r\n await transportController.connect(signal)\r\n },\r\n\r\n async disconnectTransport() {\r\n await transportController?.disconnect()\r\n },\r\n\r\n getTransportStatus(): ChatTransportStatus {\r\n return transportController?.getStatus() ?? 'idle'\r\n },\r\n\r\n async sendMessage(\r\n message: ChatMessage,\r\n options: { signal?: AbortSignal } = {}\r\n ): Promise<ChatTransportAck | void> {\r\n if (!transportController) {\r\n throw new Error('No chat transport is configured.')\r\n }\r\n if (destroyed) throw new Error('Chat instance is destroyed.')\r\n const prepared = normalizeChatMessage({\r\n ...message,\r\n clientMessageId:\r\n message.clientMessageId ?? createClientMessageId(message.id),\r\n status: message.status ?? 'sending',\r\n })\r\n chatManager.addMessage(prepared)\r\n try {\r\n if (normalizedConfig.outbox) {\r\n await normalizedConfig.outbox.enqueue(prepared)\r\n }\r\n const ack = await transportController.send(prepared, {\r\n signal: options.signal,\r\n clientMessageId: prepared.clientMessageId,\r\n })\r\n if (ack) await applyTransportAck(ack)\r\n else chatManager.markMessageStatus(prepared.id, 'sent')\r\n emitter.emit('message-sent', prepared, ack)\r\n return ack\r\n } catch (error) {\r\n chatManager.markMessageStatus(prepared.id, 'failed')\r\n emitter.emit('message-send-failed', prepared, error)\r\n throw error\r\n }\r\n },\r\n\r\n async flushOutbox(options: ChatOutboxFlushOptions = {}) {\r\n if (!normalizedConfig.outbox) {\r\n return { sent: [], failed: [], remaining: [] }\r\n }\r\n if (!transportController) {\r\n throw new Error('No chat transport is configured.')\r\n }\r\n return normalizedConfig.outbox.flush(async (message, sendOptions) => {\r\n const ack = await transportController.send(message, sendOptions)\r\n if (ack) await applyTransportAck(ack)\r\n else if (message.id) chatManager.markMessageStatus(message.id, 'sent')\r\n }, options)\r\n },\r\n\r\n destroy() {\r\n if (destroyed) return\r\n destroyed = true\r\n transportController?.dispose()\r\n themeManager.destroy()\r\n renderer.destroy()\r\n emitter.removeAll()\r\n removeStyles(prefix, ownerDocument)\r\n },\r\n }\r\n\r\n if (normalizedConfig.autoConnect && transportController) {\r\n void instance.connectTransport().catch(error => {\r\n emitter.emit('transport-error', error)\r\n })\r\n }\r\n\r\n return instance\r\n}\r\n\r\nfunction createClientMessageId(messageId: string): string {\r\n const cryptoObject = globalThis.crypto\r\n if (cryptoObject?.randomUUID) return cryptoObject.randomUUID()\r\n return `client-${messageId}-${Date.now()}-${Math.random().toString(36).slice(2)}`\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @composy/chat-core - 即时通讯/IM 组件核心库
3
+ *
4
+ * 提供聊天气泡、消息列表、@提及、多消息类型(文本/图片/文件/系统消息/自定义)、
5
+ * 消息状态管理(发送中/已发送/已送达/已读/失败)、亮暗主题切换等能力。
6
+ * 框架无关的核心实现,配合 Vue/React 适配层使用。
7
+ *
8
+ * @packageDocumentation
9
+ * @module @composy/chat-core
10
+ */
11
+ import type { ChatConfig, ChatInstance } from './types';
12
+ export declare const version = "0.1.0";
13
+ export { COMPONENT_CSS_VAR_PREFIX, cx, DEFAULT_CLASS_PREFIX, getStyleElementId, normalizeClassPrefix, STYLE_ELEMENT_ID, } from './constants/dom';
14
+ export { ChatManager } from './core/chat-manager';
15
+ export type { MessageMergeOptions, MessageMergeResult, StreamAppendOptions, } from './core/chat-manager';
16
+ export { EventEmitter } from './core/event-emitter';
17
+ export { MentionEngine } from './core/mention-engine';
18
+ export type { MentionState } from './core/mention-engine';
19
+ export { cloneChatMessage, compareChatMessages, normalizeChatContentBlocks, normalizeChatMessage, normalizeChatMessages, } from './core/message-schema';
20
+ export { ChatOutbox, MemoryChatOutboxStorage } from './core/outbox';
21
+ export type { ChatOutboxEntry, ChatOutboxFlushOptions, ChatOutboxFlushResult, ChatOutboxOptions, ChatOutboxStorage, } from './core/outbox';
22
+ export { ChatTransportController } from './core/transport';
23
+ export type { ChatTransport, ChatTransportAck, ChatTransportContext, ChatTransportControllerOptions, ChatTransportEvent, ChatTransportStatus, } from './core/transport';
24
+ export { DOMRenderer } from './renderer/dom-renderer';
25
+ export { cls, clsx, injectStyles, removeStyles, updateStyles, } from './renderer/style-manager';
26
+ export { ThemeManager } from './theme/theme-manager';
27
+ export type { ThemeTokens } from './theme/tokens';
28
+ export { darkTokens, lightTokens } from './theme/tokens';
29
+ export * from './types';
30
+ /**
31
+ * 创建聊天实例
32
+ */
33
+ export declare function createChat(config: ChatConfig): ChatInstance;
package/lib/index.d.ts CHANGED
@@ -10,19 +10,24 @@
10
10
  */
11
11
  import type { ChatConfig, ChatInstance } from './types';
12
12
  export declare const version = "0.1.0";
13
- export * from './types';
14
- export { EventEmitter } from './core/event-emitter';
13
+ export { COMPONENT_CSS_VAR_PREFIX, cx, DEFAULT_CLASS_PREFIX, getStyleElementId, normalizeClassPrefix, STYLE_ELEMENT_ID, } from './constants/dom';
15
14
  export { ChatManager } from './core/chat-manager';
15
+ export type { MessageMergeOptions, MessageMergeResult, StreamAppendOptions, } from './core/chat-manager';
16
+ export { EventEmitter } from './core/event-emitter';
16
17
  export { MentionEngine } from './core/mention-engine';
17
18
  export type { MentionState } from './core/mention-engine';
19
+ export { cloneChatMessage, compareChatMessages, normalizeChatContentBlocks, normalizeChatMessage, normalizeChatMessages, } from './core/message-schema';
20
+ export { ChatOutbox, MemoryChatOutboxStorage } from './core/outbox';
21
+ export type { ChatOutboxEntry, ChatOutboxFlushOptions, ChatOutboxFlushResult, ChatOutboxOptions, ChatOutboxStorage, } from './core/outbox';
22
+ export { ChatTransportController } from './core/transport';
23
+ export type { ChatTransport, ChatTransportAck, ChatTransportContext, ChatTransportControllerOptions, ChatTransportEvent, ChatTransportStatus, } from './core/transport';
24
+ export { DOMRenderer } from './renderer/dom-renderer';
25
+ export { cls, clsx, injectStyles, removeStyles, updateStyles, } from './renderer/style-manager';
18
26
  export { ThemeManager } from './theme/theme-manager';
19
27
  export type { ThemeTokens } from './theme/tokens';
20
- export { lightTokens, darkTokens } from './theme/tokens';
21
- export { DOMRenderer } from './renderer/dom-renderer';
22
- export { injectStyles, updateStyles, removeStyles, cls, clsx } from './renderer/style-manager';
23
- export { DEFAULT_CLASS_PREFIX, COMPONENT_CSS_VAR_PREFIX, STYLE_ELEMENT_ID, getStyleElementId, normalizeClassPrefix, cx, } from './constants/dom';
28
+ export { darkTokens, lightTokens } from './theme/tokens';
29
+ export * from './types';
24
30
  /**
25
31
  * 创建聊天实例
26
32
  */
27
33
  export declare function createChat(config: ChatConfig): ChatInstance;
28
- //# sourceMappingURL=index.d.ts.map