@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/esm/index.js CHANGED
@@ -1,25 +1,34 @@
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
- import { EventEmitter } from "./core/event-emitter.js";
10
+ import { COMPONENT_CSS_VAR_PREFIX, DEFAULT_CLASS_PREFIX, STYLE_ELEMENT_ID, cx, getStyleElementId, normalizeClassPrefix } from "./constants/dom.js";
11
+ import { CHAT_MESSAGE_SCHEMA_VERSION } from "./types/index.js";
12
+ import { cloneChatMessage, compareChatMessages, normalizeChatContentBlocks, normalizeChatMessage, normalizeChatMessages } from "./core/message-schema.js";
11
13
  import { ChatManager } from "./core/chat-manager.js";
14
+ import { EventEmitter } from "./core/event-emitter.js";
12
15
  import { MentionEngine } from "./core/mention-engine.js";
16
+ import { ChatTransportController } from "./core/transport.js";
17
+ import { cls, clsx, injectStyles, removeStyles, updateStyles } from "./renderer/style-manager.js";
18
+ import { DOMRenderer } from "./renderer/dom-renderer.js";
13
19
  import { darkTokens, lightTokens } from "./theme/tokens.js";
14
20
  import { ThemeManager } from "./theme/theme-manager.js";
15
- import { COMPONENT_CSS_VAR_PREFIX, DEFAULT_CLASS_PREFIX, STYLE_ELEMENT_ID, cx, getStyleElementId, normalizeClassPrefix } from "./constants/dom.js";
16
- import { DOMRenderer } from "./renderer/dom-renderer.js";
17
- import { cls, clsx, injectStyles, removeStyles, updateStyles } from "./renderer/style-manager.js";
21
+ import { ChatOutbox, MemoryChatOutboxStorage } from "./core/outbox.js";
18
22
  //#region src/index.ts
19
23
  const version = "0.1.0";
20
24
  function createChat(config) {
21
- const container = typeof config.container === "string" ? document.querySelector(config.container) : config.container;
25
+ let container;
26
+ if (typeof config.container === "string") {
27
+ if (typeof document === "undefined") throw new Error("[Chat] A browser Document is required to resolve a container selector.");
28
+ container = document.querySelector(config.container);
29
+ } else container = config.container;
22
30
  if (!container) throw new Error(`[Chat] Container not found: ${config.container}`);
31
+ const ownerDocument = container.ownerDocument;
23
32
  const prefix = normalizeClassPrefix(config.classPrefix ?? "ldesign-chat");
24
33
  const normalizedConfig = {
25
34
  ...config,
@@ -28,23 +37,70 @@ function createChat(config) {
28
37
  const emitter = new EventEmitter();
29
38
  const chatManager = new ChatManager(emitter, normalizedConfig.messages);
30
39
  const mentionEngine = new MentionEngine(emitter, normalizedConfig.mentionMembers);
31
- const themeManager = new ThemeManager(emitter, normalizedConfig.theme ?? "light");
40
+ const themeManager = new ThemeManager(emitter, normalizedConfig.theme ?? "light", ownerDocument.defaultView);
32
41
  const renderer = new DOMRenderer(container, normalizedConfig, emitter, mentionEngine);
33
- injectStyles(themeManager.getTokens(), prefix);
42
+ let destroyed = false;
43
+ const schedule = (callback) => {
44
+ const run = () => {
45
+ if (!destroyed) callback();
46
+ };
47
+ if (ownerDocument.defaultView?.requestAnimationFrame) ownerDocument.defaultView.requestAnimationFrame(run);
48
+ else setTimeout(run, 0);
49
+ };
34
50
  renderer.init();
35
51
  renderer.renderMessages(chatManager.getMessages());
36
- requestAnimationFrame(() => {
52
+ renderer.setThemeTokens(themeManager.getTokens());
53
+ injectStyles(themeManager.getTokens(), prefix, renderer.getRootElement());
54
+ const applyTransportAck = async (ack) => {
55
+ const localId = chatManager.getMessageIdByClientMessageId(ack.clientMessageId);
56
+ if (localId) chatManager.updateMessage(localId, {
57
+ ...ack.message ?? {},
58
+ id: localId,
59
+ clientMessageId: ack.clientMessageId,
60
+ status: "sent",
61
+ ...ack.sequence !== void 0 && { sequence: ack.sequence },
62
+ ...ack.cursor !== void 0 && { cursor: ack.cursor }
63
+ });
64
+ if (normalizedConfig.outbox) await normalizedConfig.outbox.remove(ack.clientMessageId);
65
+ emitter.emit("transport-ack", ack);
66
+ };
67
+ const handleTransportEvent = (event) => {
68
+ if (event.type === "message") chatManager.upsertMessage({
69
+ ...event.message,
70
+ ...event.cursor !== void 0 && { cursor: event.cursor }
71
+ }, {
72
+ sequence: event.sequence,
73
+ source: "remote"
74
+ });
75
+ else if (event.type === "ack") applyTransportAck(event.ack).catch((error) => {
76
+ emitter.emit("transport-error", error);
77
+ });
78
+ else if (event.type === "cursor") emitter.emit("transport-cursor", event.cursor);
79
+ else emitter.emit("transport-error", event.error, event.recoverable);
80
+ };
81
+ const transportController = normalizedConfig.transport ? new ChatTransportController(normalizedConfig.transport, {
82
+ conversationId: normalizedConfig.conversationId,
83
+ cursor: normalizedConfig.transportCursor,
84
+ onEvent: handleTransportEvent,
85
+ onStatusChange: (status) => {
86
+ emitter.emit("transport-status", status);
87
+ }
88
+ }) : null;
89
+ schedule(() => {
37
90
  renderer.scrollToBottom(false);
38
91
  });
39
92
  emitter.on("theme-change", (_resolved, tokens) => {
40
- updateStyles(tokens, prefix);
93
+ renderer.setThemeTokens(tokens);
41
94
  });
42
95
  emitter.on("message-added", (message) => {
43
96
  renderer.appendMessage(message);
44
- requestAnimationFrame(() => {
97
+ schedule(() => {
45
98
  renderer.scrollToBottom(true);
46
99
  });
47
100
  });
101
+ emitter.on("mention-select", (member) => {
102
+ normalizedConfig.onMentionSelect?.(member);
103
+ });
48
104
  emitter.on("messages-prepended", (messages) => {
49
105
  renderer.prependMessages(messages);
50
106
  });
@@ -56,85 +112,160 @@ function createChat(config) {
56
112
  });
57
113
  emitter.on("message-content-appended", (id, content) => {
58
114
  renderer.updateMessageContent(id, content);
59
- requestAnimationFrame(() => {
115
+ schedule(() => {
60
116
  renderer.scrollToBottom(false);
61
117
  });
62
118
  });
63
- return {
119
+ const instance = {
64
120
  addMessage(message) {
65
- chatManager.addMessage(message);
121
+ if (!destroyed) chatManager.addMessage(message);
66
122
  },
67
123
  prependMessages(messages) {
68
- chatManager.prependMessages(messages);
124
+ if (!destroyed) chatManager.prependMessages(messages);
69
125
  },
70
126
  updateMessage(id, updates) {
71
- chatManager.updateMessage(id, updates);
127
+ if (!destroyed) chatManager.updateMessage(id, updates);
72
128
  },
73
129
  removeMessage(id) {
74
- chatManager.removeMessage(id);
130
+ if (!destroyed) chatManager.removeMessage(id);
75
131
  },
76
132
  setMessages(messages) {
133
+ if (destroyed) return;
77
134
  chatManager.setMessages(messages);
78
135
  renderer.renderMessages(chatManager.getMessages());
79
- requestAnimationFrame(() => {
136
+ schedule(() => {
80
137
  renderer.scrollToBottom(false);
81
138
  });
82
139
  },
83
140
  clearMessages() {
84
- chatManager.clearMessages();
85
- renderer.renderMessages([]);
141
+ if (!destroyed) {
142
+ chatManager.clearMessages();
143
+ renderer.renderMessages([]);
144
+ }
86
145
  },
87
146
  scrollToBottom(smooth = true) {
88
- renderer.scrollToBottom(smooth);
147
+ if (!destroyed) renderer.scrollToBottom(smooth);
89
148
  },
90
149
  setTheme(theme) {
91
- themeManager.setTheme(theme);
150
+ if (!destroyed) themeManager.setTheme(theme);
92
151
  },
93
152
  setMentionMembers(members) {
94
- mentionEngine.setMembers(members);
153
+ if (!destroyed) mentionEngine.setMembers(members);
95
154
  },
96
155
  setLoadingMore(loading) {
97
- renderer.setLoadingMore(loading);
156
+ if (!destroyed) renderer.setLoadingMore(loading);
98
157
  },
99
158
  focusInput() {
100
- renderer.focusInput();
159
+ if (!destroyed) renderer.focusInput();
101
160
  },
102
161
  setInputContent(content) {
103
- renderer.setInputContent(content);
162
+ if (!destroyed) renderer.setInputContent(content);
104
163
  },
105
164
  getInputContent() {
106
- return renderer.getInputContent();
165
+ return destroyed ? "" : renderer.getInputContent();
107
166
  },
108
167
  clearInput() {
109
- renderer.clearInput();
168
+ if (!destroyed) renderer.clearInput();
110
169
  },
111
170
  isAtBottom(threshold) {
112
- return renderer.isAtBottom(threshold);
171
+ return destroyed || renderer.isAtBottom(threshold);
113
172
  },
114
173
  on(event, handler) {
115
- return emitter.on(event, handler);
174
+ return destroyed ? () => {} : emitter.on(event, handler);
116
175
  },
117
176
  getMessages() {
118
- return chatManager.getMessages();
177
+ return destroyed ? [] : chatManager.getMessages();
119
178
  },
120
179
  getMessage(id) {
121
- return chatManager.getMessage(id);
180
+ return destroyed ? void 0 : chatManager.getMessage(id);
122
181
  },
123
182
  appendMessageContent(id, chunk) {
124
- chatManager.appendMessageContent(id, chunk);
183
+ return destroyed ? false : chatManager.appendMessageContent(id, chunk);
184
+ },
185
+ finishStreaming(id, generation) {
186
+ return destroyed ? false : chatManager.finishStreaming(id, generation);
187
+ },
188
+ beginStreaming(id, generation) {
189
+ return destroyed ? null : chatManager.beginStreaming(id, generation);
190
+ },
191
+ cancelStreaming(id, generation) {
192
+ return destroyed ? false : chatManager.cancelStreaming(id, generation);
193
+ },
194
+ mergeMessage(message, sequence) {
195
+ return destroyed ? "ignored" : chatManager.upsertMessage(message, {
196
+ sequence,
197
+ source: "remote"
198
+ });
125
199
  },
126
- finishStreaming(id) {
127
- chatManager.finishStreaming(id);
200
+ async connectTransport(signal) {
201
+ if (!transportController) throw new Error("No chat transport is configured.");
202
+ await transportController.connect(signal);
203
+ },
204
+ async disconnectTransport() {
205
+ await transportController?.disconnect();
206
+ },
207
+ getTransportStatus() {
208
+ return transportController?.getStatus() ?? "idle";
209
+ },
210
+ async sendMessage(message, options = {}) {
211
+ if (!transportController) throw new Error("No chat transport is configured.");
212
+ if (destroyed) throw new Error("Chat instance is destroyed.");
213
+ const prepared = normalizeChatMessage({
214
+ ...message,
215
+ clientMessageId: message.clientMessageId ?? createClientMessageId(message.id),
216
+ status: message.status ?? "sending"
217
+ });
218
+ chatManager.addMessage(prepared);
219
+ try {
220
+ if (normalizedConfig.outbox) await normalizedConfig.outbox.enqueue(prepared);
221
+ const ack = await transportController.send(prepared, {
222
+ signal: options.signal,
223
+ clientMessageId: prepared.clientMessageId
224
+ });
225
+ if (ack) await applyTransportAck(ack);
226
+ else chatManager.markMessageStatus(prepared.id, "sent");
227
+ emitter.emit("message-sent", prepared, ack);
228
+ return ack;
229
+ } catch (error) {
230
+ chatManager.markMessageStatus(prepared.id, "failed");
231
+ emitter.emit("message-send-failed", prepared, error);
232
+ throw error;
233
+ }
234
+ },
235
+ async flushOutbox(options = {}) {
236
+ if (!normalizedConfig.outbox) return {
237
+ sent: [],
238
+ failed: [],
239
+ remaining: []
240
+ };
241
+ if (!transportController) throw new Error("No chat transport is configured.");
242
+ return normalizedConfig.outbox.flush(async (message, sendOptions) => {
243
+ const ack = await transportController.send(message, sendOptions);
244
+ if (ack) await applyTransportAck(ack);
245
+ else if (message.id) chatManager.markMessageStatus(message.id, "sent");
246
+ }, options);
128
247
  },
129
248
  destroy() {
249
+ if (destroyed) return;
250
+ destroyed = true;
251
+ transportController?.dispose();
130
252
  themeManager.destroy();
131
253
  renderer.destroy();
132
254
  emitter.removeAll();
133
- removeStyles(prefix);
255
+ removeStyles(prefix, ownerDocument);
134
256
  }
135
257
  };
258
+ if (normalizedConfig.autoConnect && transportController) instance.connectTransport().catch((error) => {
259
+ emitter.emit("transport-error", error);
260
+ });
261
+ return instance;
262
+ }
263
+ function createClientMessageId(messageId) {
264
+ const cryptoObject = globalThis.crypto;
265
+ if (cryptoObject?.randomUUID) return cryptoObject.randomUUID();
266
+ return `client-${messageId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
136
267
  }
137
268
  //#endregion
138
- export { COMPONENT_CSS_VAR_PREFIX, ChatManager, DEFAULT_CLASS_PREFIX, DOMRenderer, EventEmitter, MentionEngine, STYLE_ELEMENT_ID, ThemeManager, cls, clsx, createChat, cx, darkTokens, getStyleElementId, injectStyles, lightTokens, normalizeClassPrefix, removeStyles, updateStyles, version };
269
+ export { CHAT_MESSAGE_SCHEMA_VERSION, COMPONENT_CSS_VAR_PREFIX, ChatManager, ChatOutbox, ChatTransportController, DEFAULT_CLASS_PREFIX, DOMRenderer, EventEmitter, MemoryChatOutboxStorage, MentionEngine, STYLE_ELEMENT_ID, ThemeManager, cloneChatMessage, cls, clsx, compareChatMessages, createChat, cx, darkTokens, getStyleElementId, injectStyles, lightTokens, normalizeChatContentBlocks, normalizeChatMessage, normalizeChatMessages, normalizeClassPrefix, removeStyles, updateStyles, version };
139
270
  /*! End of @composy/chat-core | Powered by @composy/builder */
140
271
  //# sourceMappingURL=index.js.map
package/esm/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","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.js","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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"}
@@ -1,8 +1,10 @@
1
+ import type { EventEmitter } from '../core/event-emitter';
2
+ import type { MentionEngine } from '../core/mention-engine';
3
+ import type { ThemeTokens } from '../theme/tokens';
1
4
  import type { ChatConfig, ChatMessage, MentionItem } from '../types';
2
- import { EventEmitter } from '../core/event-emitter';
3
- import { MentionEngine } from '../core/mention-engine';
4
5
  export declare class DOMRenderer {
5
6
  private container;
7
+ private document;
6
8
  private config;
7
9
  private emitter;
8
10
  private mentionEngine;
@@ -21,6 +23,8 @@ export declare class DOMRenderer {
21
23
  private isRecording;
22
24
  private recognition;
23
25
  private lastVoiceTranscript;
26
+ private destroyed;
27
+ private pendingFileInputs;
24
28
  constructor(container: HTMLElement, config: ChatConfig, emitter: EventEmitter, mentionEngine: MentionEngine);
25
29
  init(): void;
26
30
  updateMessageContent(id: string, content: string): void;
@@ -32,6 +36,8 @@ export declare class DOMRenderer {
32
36
  scrollToBottom(smooth?: boolean): void;
33
37
  isAtBottom(threshold?: number): boolean;
34
38
  setLoadingMore(loading: boolean): void;
39
+ setThemeTokens(tokens: ThemeTokens): void;
40
+ getRootElement(): HTMLElement;
35
41
  focusInput(): void;
36
42
  setInputContent(content: string): void;
37
43
  getInputContent(): string;
@@ -55,6 +61,7 @@ export declare class DOMRenderer {
55
61
  private autoResize;
56
62
  private updateSendButtonState;
57
63
  private renderLoadMoreButton;
64
+ private loadMore;
58
65
  private renderTimeDivider;
59
66
  private createTimeDivider;
60
67
  private renderMessageRow;
@@ -62,18 +69,23 @@ export declare class DOMRenderer {
62
69
  private createAvatar;
63
70
  private createMessageFooter;
64
71
  private createBubble;
72
+ private renderContentBlocks;
65
73
  private renderTextContent;
74
+ private setSanitizedCustomHtml;
75
+ private sanitizeCustomNode;
76
+ private safeResourceUrl;
77
+ private safeUrl;
66
78
  private renderEmpty;
67
79
  private removeEmpty;
68
80
  private el;
69
81
  private getMessageElements;
82
+ private getMessageRow;
83
+ private refreshTimeDividers;
70
84
  private formatTime;
71
85
  private formatShortTime;
72
86
  private formatFileSize;
73
87
  private limitInput;
74
- private getCssSizeValue;
75
88
  private escapeHtml;
76
89
  private escapeAttr;
77
90
  private escapeRegex;
78
91
  }
79
- //# sourceMappingURL=dom-renderer.d.ts.map