@composy/chat-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +123 -0
  2. package/dist/index.js +1868 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.min.js +1868 -0
  5. package/es/constants/dom.d.ts +41 -0
  6. package/es/constants/dom.d.ts.map +1 -0
  7. package/es/constants/dom.js +30 -0
  8. package/es/constants/dom.js.map +1 -0
  9. package/es/core/chat-manager.d.ts +34 -0
  10. package/es/core/chat-manager.d.ts.map +1 -0
  11. package/es/core/chat-manager.js +93 -0
  12. package/es/core/chat-manager.js.map +1 -0
  13. package/es/core/event-emitter.d.ts +48 -0
  14. package/es/core/event-emitter.d.ts.map +1 -0
  15. package/es/core/event-emitter.js +75 -0
  16. package/es/core/event-emitter.js.map +1 -0
  17. package/es/core/mention-engine.d.ts +41 -0
  18. package/es/core/mention-engine.d.ts.map +1 -0
  19. package/es/core/mention-engine.js +117 -0
  20. package/es/core/mention-engine.js.map +1 -0
  21. package/es/index.d.ts +28 -0
  22. package/es/index.d.ts.map +1 -0
  23. package/es/index.js +140 -0
  24. package/es/index.js.map +1 -0
  25. package/es/renderer/dom-renderer.d.ts +79 -0
  26. package/es/renderer/dom-renderer.d.ts.map +1 -0
  27. package/es/renderer/dom-renderer.js +677 -0
  28. package/es/renderer/dom-renderer.js.map +1 -0
  29. package/es/renderer/style-manager.d.ts +58 -0
  30. package/es/renderer/style-manager.d.ts.map +1 -0
  31. package/es/renderer/style-manager.js +641 -0
  32. package/es/renderer/style-manager.js.map +1 -0
  33. package/es/theme/theme-manager.d.ts +33 -0
  34. package/es/theme/theme-manager.d.ts.map +1 -0
  35. package/es/theme/theme-manager.js +70 -0
  36. package/es/theme/theme-manager.js.map +1 -0
  37. package/es/theme/tokens.d.ts +57 -0
  38. package/es/theme/tokens.d.ts.map +1 -0
  39. package/es/theme/tokens.js +108 -0
  40. package/es/theme/tokens.js.map +1 -0
  41. package/es/types/index.d.ts +204 -0
  42. package/es/types/index.d.ts.map +1 -0
  43. package/es/types/index.js +10 -0
  44. package/esm/constants/dom.d.ts +41 -0
  45. package/esm/constants/dom.d.ts.map +1 -0
  46. package/esm/constants/dom.js +30 -0
  47. package/esm/constants/dom.js.map +1 -0
  48. package/esm/core/chat-manager.d.ts +34 -0
  49. package/esm/core/chat-manager.d.ts.map +1 -0
  50. package/esm/core/chat-manager.js +93 -0
  51. package/esm/core/chat-manager.js.map +1 -0
  52. package/esm/core/event-emitter.d.ts +48 -0
  53. package/esm/core/event-emitter.d.ts.map +1 -0
  54. package/esm/core/event-emitter.js +75 -0
  55. package/esm/core/event-emitter.js.map +1 -0
  56. package/esm/core/mention-engine.d.ts +41 -0
  57. package/esm/core/mention-engine.d.ts.map +1 -0
  58. package/esm/core/mention-engine.js +117 -0
  59. package/esm/core/mention-engine.js.map +1 -0
  60. package/esm/index.d.ts +28 -0
  61. package/esm/index.d.ts.map +1 -0
  62. package/esm/index.js +140 -0
  63. package/esm/index.js.map +1 -0
  64. package/esm/renderer/dom-renderer.d.ts +79 -0
  65. package/esm/renderer/dom-renderer.d.ts.map +1 -0
  66. package/esm/renderer/dom-renderer.js +677 -0
  67. package/esm/renderer/dom-renderer.js.map +1 -0
  68. package/esm/renderer/style-manager.d.ts +58 -0
  69. package/esm/renderer/style-manager.d.ts.map +1 -0
  70. package/esm/renderer/style-manager.js +641 -0
  71. package/esm/renderer/style-manager.js.map +1 -0
  72. package/esm/theme/theme-manager.d.ts +33 -0
  73. package/esm/theme/theme-manager.d.ts.map +1 -0
  74. package/esm/theme/theme-manager.js +70 -0
  75. package/esm/theme/theme-manager.js.map +1 -0
  76. package/esm/theme/tokens.d.ts +57 -0
  77. package/esm/theme/tokens.d.ts.map +1 -0
  78. package/esm/theme/tokens.js +108 -0
  79. package/esm/theme/tokens.js.map +1 -0
  80. package/esm/types/index.d.ts +204 -0
  81. package/esm/types/index.d.ts.map +1 -0
  82. package/esm/types/index.js +10 -0
  83. package/lib/constants/dom.cjs +36 -0
  84. package/lib/constants/dom.cjs.map +1 -0
  85. package/lib/constants/dom.d.ts +41 -0
  86. package/lib/constants/dom.d.ts.map +1 -0
  87. package/lib/core/chat-manager.cjs +94 -0
  88. package/lib/core/chat-manager.cjs.map +1 -0
  89. package/lib/core/chat-manager.d.ts +34 -0
  90. package/lib/core/chat-manager.d.ts.map +1 -0
  91. package/lib/core/event-emitter.cjs +76 -0
  92. package/lib/core/event-emitter.cjs.map +1 -0
  93. package/lib/core/event-emitter.d.ts +48 -0
  94. package/lib/core/event-emitter.d.ts.map +1 -0
  95. package/lib/core/mention-engine.cjs +118 -0
  96. package/lib/core/mention-engine.cjs.map +1 -0
  97. package/lib/core/mention-engine.d.ts +41 -0
  98. package/lib/core/mention-engine.d.ts.map +1 -0
  99. package/lib/index.cjs +160 -0
  100. package/lib/index.cjs.map +1 -0
  101. package/lib/index.d.ts +28 -0
  102. package/lib/index.d.ts.map +1 -0
  103. package/lib/package.json +3 -0
  104. package/lib/renderer/dom-renderer.cjs +678 -0
  105. package/lib/renderer/dom-renderer.cjs.map +1 -0
  106. package/lib/renderer/dom-renderer.d.ts +79 -0
  107. package/lib/renderer/dom-renderer.d.ts.map +1 -0
  108. package/lib/renderer/style-manager.cjs +646 -0
  109. package/lib/renderer/style-manager.cjs.map +1 -0
  110. package/lib/renderer/style-manager.d.ts +58 -0
  111. package/lib/renderer/style-manager.d.ts.map +1 -0
  112. package/lib/theme/theme-manager.cjs +71 -0
  113. package/lib/theme/theme-manager.cjs.map +1 -0
  114. package/lib/theme/theme-manager.d.ts +33 -0
  115. package/lib/theme/theme-manager.d.ts.map +1 -0
  116. package/lib/theme/tokens.cjs +110 -0
  117. package/lib/theme/tokens.cjs.map +1 -0
  118. package/lib/theme/tokens.d.ts +57 -0
  119. package/lib/theme/tokens.d.ts.map +1 -0
  120. package/lib/types/index.cjs +11 -0
  121. package/lib/types/index.d.ts +204 -0
  122. package/lib/types/index.d.ts.map +1 -0
  123. package/package.json +107 -0
@@ -0,0 +1,93 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/chat-core v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:30:32 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ //#region src/core/chat-manager.ts
11
+ var ChatManager = class {
12
+ constructor(emitter, initialMessages) {
13
+ this.messages = [];
14
+ this.emitter = emitter;
15
+ if (initialMessages?.length) this.messages = [...initialMessages];
16
+ }
17
+ /** 获取所有消息 */
18
+ getMessages() {
19
+ return [...this.messages];
20
+ }
21
+ /** 根据 ID 获取消息 */
22
+ getMessage(id) {
23
+ return this.messages.find((m) => m.id === id);
24
+ }
25
+ /** 添加消息(末尾追加) */
26
+ addMessage(message) {
27
+ this.messages.push(message);
28
+ this.emitter.emit("message-added", message);
29
+ this.emitter.emit("messages-changed", this.getMessages());
30
+ }
31
+ /** 在顶部批量插入消息(用于加载更多) */
32
+ prependMessages(messages) {
33
+ this.messages.unshift(...messages);
34
+ this.emitter.emit("messages-prepended", messages);
35
+ this.emitter.emit("messages-changed", this.getMessages());
36
+ }
37
+ /** 更新消息 */
38
+ updateMessage(id, updates) {
39
+ const index = this.messages.findIndex((m) => m.id === id);
40
+ if (index === -1) return;
41
+ this.messages[index] = {
42
+ ...this.messages[index],
43
+ ...updates
44
+ };
45
+ this.emitter.emit("message-updated", this.messages[index]);
46
+ this.emitter.emit("messages-changed", this.getMessages());
47
+ }
48
+ /** 删除消息 */
49
+ removeMessage(id) {
50
+ const index = this.messages.findIndex((m) => m.id === id);
51
+ if (index === -1) return;
52
+ const removed = this.messages.splice(index, 1)[0];
53
+ this.emitter.emit("message-removed", removed);
54
+ this.emitter.emit("messages-changed", this.getMessages());
55
+ }
56
+ /** 替换全部消息 */
57
+ setMessages(messages) {
58
+ this.messages = [...messages];
59
+ this.emitter.emit("messages-changed", this.getMessages());
60
+ }
61
+ /** 清空所有消息 */
62
+ clearMessages() {
63
+ this.messages = [];
64
+ this.emitter.emit("messages-changed", []);
65
+ }
66
+ /** 追加流式消息内容 */
67
+ appendMessageContent(id, chunk) {
68
+ const index = this.messages.findIndex((m) => m.id === id);
69
+ if (index === -1) return;
70
+ const msg = this.messages[index];
71
+ this.messages[index] = {
72
+ ...msg,
73
+ content: (msg.content ?? "") + chunk
74
+ };
75
+ this.emitter.emit("message-content-appended", id, this.messages[index].content);
76
+ this.emitter.emit("messages-changed", this.getMessages());
77
+ }
78
+ /** 完成流式消息 */
79
+ finishStreaming(id) {
80
+ const index = this.messages.findIndex((m) => m.id === id);
81
+ if (index === -1) return;
82
+ this.messages[index] = {
83
+ ...this.messages[index],
84
+ streaming: false
85
+ };
86
+ this.emitter.emit("message-updated", this.messages[index]);
87
+ this.emitter.emit("messages-changed", this.getMessages());
88
+ }
89
+ };
90
+ //#endregion
91
+ export { ChatManager };
92
+ /*! End of @composy/chat-core | Powered by @composy/builder */
93
+ //# sourceMappingURL=chat-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-manager.js","names":[],"sources":["../../src/core/chat-manager.ts"],"sourcesContent":["/**\n * 聊天管理器 - 消息增删改查、状态管理\n */\n\nimport type { ChatMessage } from '../types';\nimport { EventEmitter } from './event-emitter';\n\n/**\n * ChatManager - Chat Manager class\n */\nexport class ChatManager {\n private messages: ChatMessage[] = [];\n private emitter: EventEmitter;\n\n constructor(emitter: EventEmitter, initialMessages?: ChatMessage[]) {\n this.emitter = emitter;\n if (initialMessages?.length) {\n this.messages = [...initialMessages];\n }\n }\n\n /** 获取所有消息 */\n getMessages(): ChatMessage[] {\n return [...this.messages];\n }\n\n /** 根据 ID 获取消息 */\n getMessage(id: string): ChatMessage | undefined {\n return this.messages.find((m) => m.id === id);\n }\n\n /** 添加消息(末尾追加) */\n addMessage(message: ChatMessage): void {\n this.messages.push(message);\n this.emitter.emit('message-added', message);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 在顶部批量插入消息(用于加载更多) */\n prependMessages(messages: ChatMessage[]): void {\n this.messages.unshift(...messages);\n this.emitter.emit('messages-prepended', messages);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 更新消息 */\n updateMessage(id: string, updates: Partial<ChatMessage>): void {\n const index = this.messages.findIndex((m) => m.id === id);\n if (index === -1) return;\n\n this.messages[index] = { ...this.messages[index], ...updates };\n this.emitter.emit('message-updated', this.messages[index]);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 删除消息 */\n removeMessage(id: string): void {\n const index = this.messages.findIndex((m) => m.id === id);\n if (index === -1) return;\n\n const removed = this.messages.splice(index, 1)[0];\n this.emitter.emit('message-removed', removed);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 替换全部消息 */\n setMessages(messages: ChatMessage[]): void {\n this.messages = [...messages];\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 清空所有消息 */\n clearMessages(): void {\n this.messages = [];\n this.emitter.emit('messages-changed', []);\n }\n\n /** 追加流式消息内容 */\n appendMessageContent(id: string, chunk: string): void {\n const index = this.messages.findIndex((m) => m.id === id);\n if (index === -1) return;\n\n const msg = this.messages[index];\n this.messages[index] = {\n ...msg,\n content: (msg.content ?? '') + chunk,\n };\n this.emitter.emit('message-content-appended', id, this.messages[index].content);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n\n /** 完成流式消息 */\n finishStreaming(id: string): void {\n const index = this.messages.findIndex((m) => m.id === id);\n if (index === -1) return;\n\n this.messages[index] = { ...this.messages[index], streaming: false };\n this.emitter.emit('message-updated', this.messages[index]);\n this.emitter.emit('messages-changed', this.getMessages());\n }\n}\n"],"mappings":";;;;;;;;;;AAUO,IAAM,cAAN,MAAkB;CAIvB,YAAY,SAAuB,iBAAiC;EAHpE,KAAQ,WAA0B,CAAC;EAIjC,KAAK,UAAU;EACf,IAAI,iBAAiB,QACnB,KAAK,WAAW,CAAC,GAAG,eAAe;CAEvC;;CAGA,cAA6B;EAC3B,OAAO,CAAC,GAAG,KAAK,QAAQ;CAC1B;;CAGA,WAAW,IAAqC;EAC9C,OAAO,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,EAAE;CAC9C;;CAGA,WAAW,SAA4B;EACrC,KAAK,SAAS,KAAK,OAAO;EAC1B,KAAK,QAAQ,KAAK,iBAAiB,OAAO;EAC1C,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,gBAAgB,UAA+B;EAC7C,KAAK,SAAS,QAAQ,GAAG,QAAQ;EACjC,KAAK,QAAQ,KAAK,sBAAsB,QAAQ;EAChD,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,cAAc,IAAY,SAAqC;EAC7D,MAAM,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,OAAO,EAAE;EACxD,IAAI,UAAU,IAAI;EAElB,KAAK,SAAS,SAAS;GAAE,GAAG,KAAK,SAAS;GAAQ,GAAG;EAAQ;EAC7D,KAAK,QAAQ,KAAK,mBAAmB,KAAK,SAAS,MAAM;EACzD,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,cAAc,IAAkB;EAC9B,MAAM,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,OAAO,EAAE;EACxD,IAAI,UAAU,IAAI;EAElB,MAAM,UAAU,KAAK,SAAS,OAAO,OAAO,CAAC,CAAA,CAAE;EAC/C,KAAK,QAAQ,KAAK,mBAAmB,OAAO;EAC5C,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,YAAY,UAA+B;EACzC,KAAK,WAAW,CAAC,GAAG,QAAQ;EAC5B,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,gBAAsB;EACpB,KAAK,WAAW,CAAC;EACjB,KAAK,QAAQ,KAAK,oBAAoB,CAAC,CAAC;CAC1C;;CAGA,qBAAqB,IAAY,OAAqB;EACpD,MAAM,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,OAAO,EAAE;EACxD,IAAI,UAAU,IAAI;EAElB,MAAM,MAAM,KAAK,SAAS;EAC1B,KAAK,SAAS,SAAS;GACrB,GAAG;GACH,UAAU,IAAI,WAAW,MAAM;EACjC;EACA,KAAK,QAAQ,KAAK,4BAA4B,IAAI,KAAK,SAAS,MAAK,CAAE,OAAO;EAC9E,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;;CAGA,gBAAgB,IAAkB;EAChC,MAAM,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,OAAO,EAAE;EACxD,IAAI,UAAU,IAAI;EAElB,KAAK,SAAS,SAAS;GAAE,GAAG,KAAK,SAAS;GAAQ,WAAW;EAAM;EACnE,KAAK,QAAQ,KAAK,mBAAmB,KAAK,SAAS,MAAM;EACzD,KAAK,QAAQ,KAAK,oBAAoB,KAAK,YAAY,CAAC;CAC1D;AACF"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 轻量事件总线
3
+ * 提供发布/订阅模式的事件管理功能,支持事件的注册、注销、触发等操作
4
+ */
5
+ type Handler = (...args: unknown[]) => void;
6
+ /**
7
+ * EventEmitter - 事件发射器类
8
+ * 实现了经典的发布/订阅(Pub/Sub)模式,用于模块间的解耦通信
9
+ * 支持:一次性监听、批量移除、自动清理等功能
10
+ */
11
+ export declare class EventEmitter {
12
+ private listeners;
13
+ /**
14
+ * 注册事件监听器
15
+ * @param event - 要监听的事件名称
16
+ * @param handler - 事件触发时执行的回调函数
17
+ * @returns 返回一个取消订阅的函数,调用即可移除该监听器
18
+ */
19
+ on(event: string, handler: Handler): () => void;
20
+ /**
21
+ * 移除指定事件的某个监听器
22
+ * @param event - 事件名称
23
+ * @param handler - 要移除的处理函数引用(必须与注册时为同一函数引用)
24
+ */
25
+ off(event: string, handler: Handler): void;
26
+ /**
27
+ * 触发(发射)指定事件,依次调用所有已注册的监听器
28
+ * @param event - 要触发的事件名称
29
+ * @param args - 传递给监听器函数的参数列表(剩余参数语法)
30
+ */
31
+ emit(event: string, ...args: unknown[]): void;
32
+ /**
33
+ * 注册一次性事件监听器(触发一次后自动移除)
34
+ * @param event - 要监听的事件名称
35
+ * @param handler - 事件触发时执行的回调函数
36
+ * @returns 返回取消订阅的函数,可在触发前手动取消
37
+ */
38
+ once(event: string, handler: Handler): () => void;
39
+ /**
40
+ * 批量移除事件监听器
41
+ * @param event - 可选的事件名称
42
+ * - 若传入事件名,则只移除该事件的所有监听器
43
+ * - 若不传参数,则清空所有事件的所有监听器
44
+ */
45
+ removeAll(event?: string): void;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=event-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../src/core/event-emitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,KAAK,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAE5C;;;;GAIG;AACH,qBAAa,YAAY;IAIvB,OAAO,CAAC,SAAS,CAAmC;IAEpD;;;;;OAKG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,IAAI;IAY/C;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAM1C;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAc7C;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,IAAI;IAajD;;;;;OAKG;IACH,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAShC"}
@@ -0,0 +1,75 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/chat-core v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:30:32 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ //#region src/core/event-emitter.ts
11
+ var EventEmitter = class {
12
+ constructor() {
13
+ this.listeners = /* @__PURE__ */ new Map();
14
+ }
15
+ /**
16
+ * 注册事件监听器
17
+ * @param event - 要监听的事件名称
18
+ * @param handler - 事件触发时执行的回调函数
19
+ * @returns 返回一个取消订阅的函数,调用即可移除该监听器
20
+ */
21
+ on(event, handler) {
22
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
23
+ this.listeners.get(event).add(handler);
24
+ return () => this.off(event, handler);
25
+ }
26
+ /**
27
+ * 移除指定事件的某个监听器
28
+ * @param event - 事件名称
29
+ * @param handler - 要移除的处理函数引用(必须与注册时为同一函数引用)
30
+ */
31
+ off(event, handler) {
32
+ this.listeners.get(event)?.delete(handler);
33
+ }
34
+ /**
35
+ * 触发(发射)指定事件,依次调用所有已注册的监听器
36
+ * @param event - 要触发的事件名称
37
+ * @param args - 传递给监听器函数的参数列表(剩余参数语法)
38
+ */
39
+ emit(event, ...args) {
40
+ this.listeners.get(event)?.forEach((handler) => {
41
+ try {
42
+ handler(...args);
43
+ } catch (err) {
44
+ console.error(`[Chat] Event handler error for "${event}":`, err);
45
+ }
46
+ });
47
+ }
48
+ /**
49
+ * 注册一次性事件监听器(触发一次后自动移除)
50
+ * @param event - 要监听的事件名称
51
+ * @param handler - 事件触发时执行的回调函数
52
+ * @returns 返回取消订阅的函数,可在触发前手动取消
53
+ */
54
+ once(event, handler) {
55
+ const wrapper = (...args) => {
56
+ this.off(event, wrapper);
57
+ handler(...args);
58
+ };
59
+ return this.on(event, wrapper);
60
+ }
61
+ /**
62
+ * 批量移除事件监听器
63
+ * @param event - 可选的事件名称
64
+ * - 若传入事件名,则只移除该事件的所有监听器
65
+ * - 若不传参数,则清空所有事件的所有监听器
66
+ */
67
+ removeAll(event) {
68
+ if (event) this.listeners.delete(event);
69
+ else this.listeners.clear();
70
+ }
71
+ };
72
+ //#endregion
73
+ export { EventEmitter };
74
+ /*! End of @composy/chat-core | Powered by @composy/builder */
75
+ //# sourceMappingURL=event-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.js","names":[],"sources":["../../src/core/event-emitter.ts"],"sourcesContent":["/**\n * 轻量事件总线\n * 提供发布/订阅模式的事件管理功能,支持事件的注册、注销、触发等操作\n */\n\n// 定义事件处理函数的类型签名:接收任意数量的未知类型参数,无返回值\ntype Handler = (...args: unknown[]) => void;\n\n/**\n * EventEmitter - 事件发射器类\n * 实现了经典的发布/订阅(Pub/Sub)模式,用于模块间的解耦通信\n * 支持:一次性监听、批量移除、自动清理等功能\n */\nexport class EventEmitter {\n // 使用 Map 存储事件名到处理函数集合的映射关系\n // 键为事件名称(字符串),值为该事件对应的处理函数 Set 集合\n // 使用 Set 而非数组,可自动去重同一处理函数的重复注册\n private listeners = new Map<string, Set<Handler>>();\n\n /**\n * 注册事件监听器\n * @param event - 要监听的事件名称\n * @param handler - 事件触发时执行的回调函数\n * @returns 返回一个取消订阅的函数,调用即可移除该监听器\n */\n on(event: string, handler: Handler): () => void {\n // 如果该事件还没有任何监听器,则创建一个新的 Set 集合\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n // 将处理函数添加到该事件的监听器集合中\n // 使用非空断言(!)因为上方已确认该事件集合存在\n this.listeners.get(event)!.add(handler);\n // 返回一个取消订阅函数,方便调用者链式调用后自行移除\n return () => this.off(event, handler);\n }\n\n /**\n * 移除指定事件的某个监听器\n * @param event - 事件名称\n * @param handler - 要移除的处理函数引用(必须与注册时为同一函数引用)\n */\n off(event: string, handler: Handler): void {\n // 可选链调用:如果该事件存在监听器集合,则从中删除指定处理函数\n // 若事件不存在则安全跳过,不会抛出异常\n this.listeners.get(event)?.delete(handler);\n }\n\n /**\n * 触发(发射)指定事件,依次调用所有已注册的监听器\n * @param event - 要触发的事件名称\n * @param args - 传递给监听器函数的参数列表(剩余参数语法)\n */\n emit(event: string, ...args: unknown[]): void {\n // 获取该事件的监听器集合,若存在则遍历调用每个处理函数\n this.listeners.get(event)?.forEach((handler) => {\n try {\n // 使用展开运算符将参数数组传递给处理函数\n handler(...args);\n } catch (err) {\n // 捕获单个处理函数的异常,避免影响其他监听器的执行\n // 同时输出错误日志便于调试,标注所属模块为 [Chat]\n console.error(`[Chat] Event handler error for \"${event}\":`, err);\n }\n });\n }\n\n /**\n * 注册一次性事件监听器(触发一次后自动移除)\n * @param event - 要监听的事件名称\n * @param handler - 事件触发时执行的回调函数\n * @returns 返回取消订阅的函数,可在触发前手动取消\n */\n once(event: string, handler: Handler): () => void {\n // 创建包装函数:先移除自身,再调用原始处理函数\n // 这样保证该监听器只执行一次\n const wrapper = (...args: unknown[]) => {\n // 先从事件中移除包装函数自身,防止重复触发\n this.off(event, wrapper);\n // 再执行用户注册的原始处理函数\n handler(...args);\n };\n // 将包装函数注册为普通监听器,复用 on() 的逻辑\n return this.on(event, wrapper);\n }\n\n /**\n * 批量移除事件监听器\n * @param event - 可选的事件名称\n * - 若传入事件名,则只移除该事件的所有监听器\n * - 若不传参数,则清空所有事件的所有监听器\n */\n removeAll(event?: string): void {\n if (event) {\n // 移除指定事件的全部监听器(直接删除整个事件条目)\n this.listeners.delete(event);\n } else {\n // 不传参数时,清空整个监听器映射表,移除所有事件\n this.listeners.clear();\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAaO,IAAM,eAAN,MAAmB;CAAnB,cAAA;EAIL,KAAQ,4BAAY,IAAI,IAA0B;CAAA;;;;;;;CAQlD,GAAG,OAAe,SAA8B;EAE9C,IAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAC3B,KAAK,UAAU,IAAI,uBAAO,IAAI,IAAI,CAAC;EAIrC,KAAK,UAAU,IAAI,KAAK,CAAA,CAAG,IAAI,OAAO;EAEtC,aAAa,KAAK,IAAI,OAAO,OAAO;CACtC;;;;;;CAOA,IAAI,OAAe,SAAwB;EAGzC,KAAK,UAAU,IAAI,KAAK,CAAA,EAAG,OAAO,OAAO;CAC3C;;;;;;CAOA,KAAK,OAAA,GAAkB,MAAuB;EAE5C,KAAK,UAAU,IAAI,KAAK,CAAA,EAAG,SAAS,YAAY;GAC9C,IAAI;IAEF,QAAQ,GAAG,IAAI;GACjB,SAAS,KAAK;IAGZ,QAAQ,MAAM,mCAAmC,MAAK,KAAM,GAAG;GACjE;EACF,CAAC;CACH;;;;;;;CAQA,KAAK,OAAe,SAA8B;EAGhD,MAAM,WAAU,GAAI,SAAoB;GAEtC,KAAK,IAAI,OAAO,OAAO;GAEvB,QAAQ,GAAG,IAAI;EACjB;EAEA,OAAO,KAAK,GAAG,OAAO,OAAO;CAC/B;;;;;;;CAQA,UAAU,OAAsB;EAC9B,IAAI,OAEF,KAAK,UAAU,OAAO,KAAK;OAG3B,KAAK,UAAU,MAAM;CAEzB;AACF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file mention-engine.ts
3
+ * @description mention-engine 模块
4
+ */
5
+ import type { MentionItem } from '../types';
6
+ import { EventEmitter } from './event-emitter';
7
+ /**
8
+ * MentionState interface
9
+ */
10
+ export interface MentionState {
11
+ active: boolean;
12
+ query: string;
13
+ filtered: MentionItem[];
14
+ activeIndex: number;
15
+ triggerPosition: number;
16
+ }
17
+ /**
18
+ * MentionEngine 类
19
+ */
20
+ export declare class MentionEngine {
21
+ private members;
22
+ private emitter;
23
+ private state;
24
+ private selectedMentions;
25
+ constructor(emitter: EventEmitter, members?: MentionItem[]);
26
+ getState(): MentionState;
27
+ getSelectedMentions(): MentionItem[];
28
+ setMembers(members: MentionItem[]): void;
29
+ handleInput(text: string, cursorPos: number): void;
30
+ moveUp(): void;
31
+ moveDown(): void;
32
+ selectCurrent(): MentionItem | null;
33
+ select(index: number): MentionItem | null;
34
+ deactivate(): void;
35
+ parseMentions(text: string): MentionItem[];
36
+ resetSelected(): void;
37
+ getInsertText(member: MentionItem): string;
38
+ private filter;
39
+ private escapeRegex;
40
+ }
41
+ //# sourceMappingURL=mention-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-engine.d.ts","sourceRoot":"","sources":["../../src/core/mention-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,KAAK,CAMX;IACF,OAAO,CAAC,gBAAgB,CAAqB;gBAEjC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE;IAK1D,QAAQ,IAAI,YAAY;IAOxB,mBAAmB,IAAI,WAAW,EAAE;IAIpC,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAOxC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAyBlD,MAAM,IAAI,IAAI;IAQd,QAAQ,IAAI,IAAI;IAOhB,aAAa,IAAI,WAAW,GAAG,IAAI;IAKnC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAWzC,UAAU,IAAI,IAAI;IAYlB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE;IAc1C,aAAa,IAAI,IAAI;IAIrB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAI1C,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,WAAW;CAGpB"}
@@ -0,0 +1,117 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/chat-core v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:30:32 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ //#region src/core/mention-engine.ts
11
+ var MentionEngine = class {
12
+ constructor(emitter, members) {
13
+ this.members = [];
14
+ this.state = {
15
+ active: false,
16
+ query: "",
17
+ filtered: [],
18
+ activeIndex: 0,
19
+ triggerPosition: -1
20
+ };
21
+ this.selectedMentions = [];
22
+ this.emitter = emitter;
23
+ if (members) this.members = [...members];
24
+ }
25
+ getState() {
26
+ return {
27
+ ...this.state,
28
+ filtered: [...this.state.filtered]
29
+ };
30
+ }
31
+ getSelectedMentions() {
32
+ return [...this.selectedMentions];
33
+ }
34
+ setMembers(members) {
35
+ this.members = [...members];
36
+ if (this.state.active) this.filter(this.state.query);
37
+ }
38
+ handleInput(text, cursorPos) {
39
+ const beforeCursor = text.slice(0, cursorPos);
40
+ const atIndex = beforeCursor.lastIndexOf("@");
41
+ if (atIndex === -1) {
42
+ this.deactivate();
43
+ return;
44
+ }
45
+ if (atIndex > 0 && !/\s/.test(beforeCursor[atIndex - 1])) {
46
+ this.deactivate();
47
+ return;
48
+ }
49
+ const query = beforeCursor.slice(atIndex + 1);
50
+ if (/[^\S\r\n]/.test(query)) {
51
+ this.deactivate();
52
+ return;
53
+ }
54
+ this.state.active = true;
55
+ this.state.triggerPosition = atIndex;
56
+ this.filter(query);
57
+ }
58
+ moveUp() {
59
+ if (!this.state.active || this.state.filtered.length === 0) return;
60
+ this.state.activeIndex = (this.state.activeIndex - 1 + this.state.filtered.length) % this.state.filtered.length;
61
+ this.emitter.emit("mention-active-change", this.state.activeIndex);
62
+ }
63
+ moveDown() {
64
+ if (!this.state.active || this.state.filtered.length === 0) return;
65
+ this.state.activeIndex = (this.state.activeIndex + 1) % this.state.filtered.length;
66
+ this.emitter.emit("mention-active-change", this.state.activeIndex);
67
+ }
68
+ selectCurrent() {
69
+ if (!this.state.active || this.state.filtered.length === 0) return null;
70
+ return this.select(this.state.activeIndex);
71
+ }
72
+ select(index) {
73
+ if (index < 0 || index >= this.state.filtered.length) return null;
74
+ const member = this.state.filtered[index];
75
+ if (!this.selectedMentions.find((m) => m.id === member.id)) this.selectedMentions.push(member);
76
+ this.emitter.emit("mention-select", member);
77
+ this.deactivate();
78
+ return member;
79
+ }
80
+ deactivate() {
81
+ if (!this.state.active) return;
82
+ this.state = {
83
+ active: false,
84
+ query: "",
85
+ filtered: [],
86
+ activeIndex: 0,
87
+ triggerPosition: -1
88
+ };
89
+ this.emitter.emit("mention-deactivate");
90
+ }
91
+ parseMentions(text) {
92
+ const mentions = [];
93
+ const sortedMembers = [...this.members].sort((a, b) => b.name.length - a.name.length);
94
+ for (const member of sortedMembers) if (new RegExp(`(^|\\s)@${this.escapeRegex(member.name)}(?=\\s|$|[\uFF0C\u3002\uFF01\uFF1F,.!?])`, "u").test(text) && !mentions.find((m) => m.id === member.id)) mentions.push(member);
95
+ return mentions;
96
+ }
97
+ resetSelected() {
98
+ this.selectedMentions = [];
99
+ }
100
+ getInsertText(member) {
101
+ return `@${member.name} `;
102
+ }
103
+ filter(query) {
104
+ const normalizedQuery = query.trim().toLowerCase();
105
+ this.state.query = query;
106
+ this.state.filtered = this.members.filter((member) => member.name.toLowerCase().includes(normalizedQuery) || member.id.toLowerCase().includes(normalizedQuery));
107
+ this.state.activeIndex = 0;
108
+ this.emitter.emit("mention-filter", this.getState());
109
+ }
110
+ escapeRegex(str) {
111
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
112
+ }
113
+ };
114
+ //#endregion
115
+ export { MentionEngine };
116
+ /*! End of @composy/chat-core | Powered by @composy/builder */
117
+ //# sourceMappingURL=mention-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-engine.js","names":[],"sources":["../../src/core/mention-engine.ts"],"sourcesContent":["/**\n * @file mention-engine.ts\n * @description mention-engine 模块\n */\n\nimport type { MentionItem } from '../types';\nimport { EventEmitter } from './event-emitter';\n\n/**\n * MentionState interface\n */\nexport interface MentionState {\n active: boolean;\n query: string;\n filtered: MentionItem[];\n activeIndex: number;\n triggerPosition: number;\n}\n\n/**\n * MentionEngine 类\n */\nexport class MentionEngine {\n private members: MentionItem[] = [];\n private emitter: EventEmitter;\n private state: MentionState = {\n active: false,\n query: '',\n filtered: [],\n activeIndex: 0,\n triggerPosition: -1,\n };\n private selectedMentions: MentionItem[] = [];\n\n constructor(emitter: EventEmitter, members?: MentionItem[]) {\n this.emitter = emitter;\n if (members) this.members = [...members];\n }\n\n getState(): MentionState {\n return {\n ...this.state,\n filtered: [...this.state.filtered],\n };\n }\n\n getSelectedMentions(): MentionItem[] {\n return [...this.selectedMentions];\n }\n\n setMembers(members: MentionItem[]): void {\n this.members = [...members];\n if (this.state.active) {\n this.filter(this.state.query);\n }\n }\n\n handleInput(text: string, cursorPos: number): void {\n const beforeCursor = text.slice(0, cursorPos);\n const atIndex = beforeCursor.lastIndexOf('@');\n\n if (atIndex === -1) {\n this.deactivate();\n return;\n }\n\n if (atIndex > 0 && !/\\s/.test(beforeCursor[atIndex - 1])) {\n this.deactivate();\n return;\n }\n\n const query = beforeCursor.slice(atIndex + 1);\n if (/[^\\S\\r\\n]/.test(query)) {\n this.deactivate();\n return;\n }\n\n this.state.active = true;\n this.state.triggerPosition = atIndex;\n this.filter(query);\n }\n\n moveUp(): void {\n if (!this.state.active || this.state.filtered.length === 0) return;\n this.state.activeIndex =\n (this.state.activeIndex - 1 + this.state.filtered.length) %\n this.state.filtered.length;\n this.emitter.emit('mention-active-change', this.state.activeIndex);\n }\n\n moveDown(): void {\n if (!this.state.active || this.state.filtered.length === 0) return;\n this.state.activeIndex =\n (this.state.activeIndex + 1) % this.state.filtered.length;\n this.emitter.emit('mention-active-change', this.state.activeIndex);\n }\n\n selectCurrent(): MentionItem | null {\n if (!this.state.active || this.state.filtered.length === 0) return null;\n return this.select(this.state.activeIndex);\n }\n\n select(index: number): MentionItem | null {\n if (index < 0 || index >= this.state.filtered.length) return null;\n const member = this.state.filtered[index];\n if (!this.selectedMentions.find((m) => m.id === member.id)) {\n this.selectedMentions.push(member);\n }\n this.emitter.emit('mention-select', member);\n this.deactivate();\n return member;\n }\n\n deactivate(): void {\n if (!this.state.active) return;\n this.state = {\n active: false,\n query: '',\n filtered: [],\n activeIndex: 0,\n triggerPosition: -1,\n };\n this.emitter.emit('mention-deactivate');\n }\n\n parseMentions(text: string): MentionItem[] {\n const mentions: MentionItem[] = [];\n const sortedMembers = [...this.members].sort((a, b) => b.name.length - a.name.length);\n\n for (const member of sortedMembers) {\n const pattern = new RegExp(`(^|\\\\s)@${this.escapeRegex(member.name)}(?=\\\\s|$|[,。!?,.!?])`, 'u');\n if (pattern.test(text) && !mentions.find((m) => m.id === member.id)) {\n mentions.push(member);\n }\n }\n\n return mentions;\n }\n\n resetSelected(): void {\n this.selectedMentions = [];\n }\n\n getInsertText(member: MentionItem): string {\n return `@${member.name} `;\n }\n\n private filter(query: string): void {\n const normalizedQuery = query.trim().toLowerCase();\n this.state.query = query;\n this.state.filtered = this.members.filter((member) =>\n member.name.toLowerCase().includes(normalizedQuery) ||\n member.id.toLowerCase().includes(normalizedQuery),\n );\n this.state.activeIndex = 0;\n this.emitter.emit('mention-filter', this.getState());\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n}\n"],"mappings":"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"}
package/esm/index.d.ts ADDED
@@ -0,0 +1,28 @@
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 * from './types';
14
+ export { EventEmitter } from './core/event-emitter';
15
+ export { ChatManager } from './core/chat-manager';
16
+ export { MentionEngine } from './core/mention-engine';
17
+ export type { MentionState } from './core/mention-engine';
18
+ export { ThemeManager } from './theme/theme-manager';
19
+ 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';
24
+ /**
25
+ * 创建聊天实例
26
+ */
27
+ export declare function createChat(config: ChatConfig): ChatInstance;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAsD,MAAM,SAAS,CAAC;AAU5G,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,EAAE,GACH,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAmK3D"}
package/esm/index.js ADDED
@@ -0,0 +1,140 @@
1
+ /*!
2
+ * ***********************************
3
+ * @composy/chat-core v0.0.1 *
4
+ * Built with rollup *
5
+ * Build time: 2024-08-17 11:30:32 *
6
+ * Build mode: production *
7
+ * Minified: No *
8
+ * ***********************************
9
+ */
10
+ import { EventEmitter } from "./core/event-emitter.js";
11
+ import { ChatManager } from "./core/chat-manager.js";
12
+ import { MentionEngine } from "./core/mention-engine.js";
13
+ import { darkTokens, lightTokens } from "./theme/tokens.js";
14
+ 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";
18
+ //#region src/index.ts
19
+ const version = "0.1.0";
20
+ function createChat(config) {
21
+ const container = typeof config.container === "string" ? document.querySelector(config.container) : config.container;
22
+ if (!container) throw new Error(`[Chat] Container not found: ${config.container}`);
23
+ const prefix = normalizeClassPrefix(config.classPrefix ?? "ldesign-chat");
24
+ const normalizedConfig = {
25
+ ...config,
26
+ classPrefix: prefix
27
+ };
28
+ const emitter = new EventEmitter();
29
+ const chatManager = new ChatManager(emitter, normalizedConfig.messages);
30
+ const mentionEngine = new MentionEngine(emitter, normalizedConfig.mentionMembers);
31
+ const themeManager = new ThemeManager(emitter, normalizedConfig.theme ?? "light");
32
+ const renderer = new DOMRenderer(container, normalizedConfig, emitter, mentionEngine);
33
+ injectStyles(themeManager.getTokens(), prefix);
34
+ renderer.init();
35
+ renderer.renderMessages(chatManager.getMessages());
36
+ requestAnimationFrame(() => {
37
+ renderer.scrollToBottom(false);
38
+ });
39
+ emitter.on("theme-change", (_resolved, tokens) => {
40
+ updateStyles(tokens, prefix);
41
+ });
42
+ emitter.on("message-added", (message) => {
43
+ renderer.appendMessage(message);
44
+ requestAnimationFrame(() => {
45
+ renderer.scrollToBottom(true);
46
+ });
47
+ });
48
+ emitter.on("messages-prepended", (messages) => {
49
+ renderer.prependMessages(messages);
50
+ });
51
+ emitter.on("message-updated", (message) => {
52
+ renderer.updateMessage(message.id, message);
53
+ });
54
+ emitter.on("message-removed", (message) => {
55
+ renderer.removeMessage(message.id);
56
+ });
57
+ emitter.on("message-content-appended", (id, content) => {
58
+ renderer.updateMessageContent(id, content);
59
+ requestAnimationFrame(() => {
60
+ renderer.scrollToBottom(false);
61
+ });
62
+ });
63
+ return {
64
+ addMessage(message) {
65
+ chatManager.addMessage(message);
66
+ },
67
+ prependMessages(messages) {
68
+ chatManager.prependMessages(messages);
69
+ },
70
+ updateMessage(id, updates) {
71
+ chatManager.updateMessage(id, updates);
72
+ },
73
+ removeMessage(id) {
74
+ chatManager.removeMessage(id);
75
+ },
76
+ setMessages(messages) {
77
+ chatManager.setMessages(messages);
78
+ renderer.renderMessages(chatManager.getMessages());
79
+ requestAnimationFrame(() => {
80
+ renderer.scrollToBottom(false);
81
+ });
82
+ },
83
+ clearMessages() {
84
+ chatManager.clearMessages();
85
+ renderer.renderMessages([]);
86
+ },
87
+ scrollToBottom(smooth = true) {
88
+ renderer.scrollToBottom(smooth);
89
+ },
90
+ setTheme(theme) {
91
+ themeManager.setTheme(theme);
92
+ },
93
+ setMentionMembers(members) {
94
+ mentionEngine.setMembers(members);
95
+ },
96
+ setLoadingMore(loading) {
97
+ renderer.setLoadingMore(loading);
98
+ },
99
+ focusInput() {
100
+ renderer.focusInput();
101
+ },
102
+ setInputContent(content) {
103
+ renderer.setInputContent(content);
104
+ },
105
+ getInputContent() {
106
+ return renderer.getInputContent();
107
+ },
108
+ clearInput() {
109
+ renderer.clearInput();
110
+ },
111
+ isAtBottom(threshold) {
112
+ return renderer.isAtBottom(threshold);
113
+ },
114
+ on(event, handler) {
115
+ return emitter.on(event, handler);
116
+ },
117
+ getMessages() {
118
+ return chatManager.getMessages();
119
+ },
120
+ getMessage(id) {
121
+ return chatManager.getMessage(id);
122
+ },
123
+ appendMessageContent(id, chunk) {
124
+ chatManager.appendMessageContent(id, chunk);
125
+ },
126
+ finishStreaming(id) {
127
+ chatManager.finishStreaming(id);
128
+ },
129
+ destroy() {
130
+ themeManager.destroy();
131
+ renderer.destroy();
132
+ emitter.removeAll();
133
+ removeStyles(prefix);
134
+ }
135
+ };
136
+ }
137
+ //#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 };
139
+ /*! End of @composy/chat-core | Powered by @composy/builder */
140
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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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"}