tacel-chat 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/themes.js ADDED
@@ -0,0 +1,495 @@
1
+ /**
2
+ * Built-in preset themes for the Tacel Chat Module.
3
+ * 30 themes — light, dark, colorful, professional, and app-specific palettes.
4
+ *
5
+ * Usage:
6
+ * // Use a preset by name
7
+ * chat.initialize(container, { theme: 'dark' });
8
+ *
9
+ * // Extend a preset with overrides
10
+ * const { themes } = require('tacel-chat');
11
+ * chat.initialize(container, {
12
+ * theme: { ...themes.dark, '--chat-accent': '#ff6600' }
13
+ * });
14
+ *
15
+ * // Fully custom (object of CSS variables)
16
+ * chat.initialize(container, { theme: { '--chat-bg': '#111', ... } });
17
+ */
18
+
19
+ function _t(o) {
20
+ const a = o.accent;
21
+ const ar = _hex2rgb(a);
22
+ return {
23
+ '--chat-bg': o.bg,
24
+ '--chat-sidebar-bg': o.sidebarBg || o.bg,
25
+ '--chat-border': o.border,
26
+ '--chat-shadow': o.shadow || '0 2px 8px rgba(0,0,0,0.08)',
27
+ '--chat-text': o.text,
28
+ '--chat-text-secondary': o.textSec,
29
+ '--chat-text-muted': o.textMuted,
30
+ '--chat-accent': a,
31
+ '--chat-accent-transparent': o.accentT || `rgba(${ar}, 0.1)`,
32
+ '--chat-accent-gradient': o.accentGrad || `linear-gradient(135deg, ${a}, ${o.accentDark || a})`,
33
+ '--chat-own-bubble-bg': o.ownBg || `rgba(${ar}, 0.1)`,
34
+ '--chat-own-bubble-border': o.ownBorder || `rgba(${ar}, 0.2)`,
35
+ '--chat-own-bubble-text': o.ownText || o.text,
36
+ '--chat-other-bubble-bg': o.otherBg || 'rgba(0,0,0,0.03)',
37
+ '--chat-other-bubble-border': o.otherBorder || 'rgba(0,0,0,0.08)',
38
+ '--chat-other-bubble-text': o.otherText || o.text,
39
+ '--chat-date-divider-bg': o.dividerBg || 'rgba(0,0,0,0.03)',
40
+ '--chat-date-divider-text': o.dividerText || a,
41
+ '--chat-date-divider-border': o.dividerBorder || o.border,
42
+ '--chat-avatar-bg': o.avatarBg || `rgba(${ar}, 0.1)`,
43
+ '--chat-avatar-text': o.avatarText || a,
44
+ '--chat-avatar-team-bg': o.avatarTeamBg || `rgba(${ar}, 0.15)`,
45
+ '--chat-online-color': o.online || '#4caf50',
46
+ '--chat-online-glow': o.onlineGlow || 'rgba(76,175,80,0.3)',
47
+ '--chat-offline-color': o.offline || 'rgba(0,0,0,0.3)',
48
+ '--chat-unread-bg': o.unreadBg || a,
49
+ '--chat-unread-text': o.unreadText || '#ffffff',
50
+ '--chat-input-bg': o.inputBg || 'rgba(0,0,0,0.02)',
51
+ '--chat-input-border': o.inputBorder || o.border,
52
+ '--chat-input-focus-border': o.inputFocus || a,
53
+ '--chat-send-bg': o.sendBg || o.accentGrad || `linear-gradient(135deg, ${a}, ${o.accentDark || a})`,
54
+ '--chat-send-text': o.sendText || '#ffffff',
55
+ '--chat-seen-color': o.seenColor || a,
56
+ '--chat-mention-bg': o.mentionBg || `rgba(${ar}, 0.15)`,
57
+ '--chat-mention-text': o.mentionText || a,
58
+ '--chat-mention-self-bg': o.mentionSelfBg || 'rgba(255,193,7,0.25)',
59
+ '--chat-mention-self-text': o.mentionSelfText || '#f57f17',
60
+ '--chat-attachment-bg': o.attachBg || 'rgba(0,0,0,0.04)',
61
+ '--chat-attachment-btn-bg': o.attachBtnBg || '#f0f0f0',
62
+ '--chat-attachment-btn-hover': o.attachBtnHover || '#e0e0e0',
63
+ '--chat-attachment-active-bg': o.attachActiveBg || '#4caf50',
64
+ '--chat-attachment-active-text': o.attachActiveText || '#ffffff',
65
+ '--chat-link-color': o.linkColor || a,
66
+ '--chat-tab-bg': o.tabBg || '#f5f5f5',
67
+ '--chat-tab-active-bg': o.tabActiveBg || o.bg,
68
+ '--chat-tab-active-color': o.tabActiveColor || a,
69
+ '--chat-tab-active-border': o.tabActiveBorder || a,
70
+ '--chat-tab-badge-bg': o.tabBadgeBg || '#ff5252',
71
+ '--chat-tab-badge-text': o.tabBadgeText || '#ffffff',
72
+ '--chat-modal-backdrop': o.modalBackdrop || 'rgba(0,0,0,0.5)',
73
+ '--chat-modal-bg': o.modalBg || o.bg,
74
+ '--chat-modal-shadow': o.modalShadow || '0 8px 32px rgba(0,0,0,0.2)',
75
+ '--chat-spinner-color': o.spinnerColor || a,
76
+ '--chat-spinner-track': o.spinnerTrack || 'rgba(0,0,0,0.08)',
77
+ '--chat-error-color': o.errorColor || '#e74c3c',
78
+ '--chat-current-user-bg': o.currentUserBg || `rgba(${ar}, 0.08)`,
79
+ '--chat-current-user-border': o.currentUserBorder || `rgba(${ar}, 0.15)`,
80
+ '--chat-current-user-avatar': o.currentUserAvatar || '#27ae60',
81
+ };
82
+ }
83
+
84
+ function _hex2rgb(hex) {
85
+ hex = hex.replace('#', '');
86
+ if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
87
+ const n = parseInt(hex, 16);
88
+ return `${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}`;
89
+ }
90
+
91
+ const THEMES = {
92
+
93
+ // ════════════════════════════════════════════
94
+ // 1. DEFAULT — CSS defaults, no overrides
95
+ // ════════════════════════════════════════════
96
+ default: {},
97
+
98
+ // ════════════════════════════════════════════
99
+ // 2. LIGHT — Clean white
100
+ // ════════════════════════════════════════════
101
+ light: _t({
102
+ bg: '#ffffff', border: 'rgba(0,0,0,0.1)', text: '#333333', textSec: '#666666', textMuted: '#999999',
103
+ accent: '#1976d2', accentDark: '#1565c0',
104
+ }),
105
+
106
+ // ════════════════════════════════════════════
107
+ // 3. DARK — Deep charcoal
108
+ // ════════════════════════════════════════════
109
+ dark: _t({
110
+ bg: '#1e1e2e', sidebarBg: '#181825', border: 'rgba(255,255,255,0.08)',
111
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
112
+ text: '#e0e0e0', textSec: '#a0a0a0', textMuted: '#666666',
113
+ accent: '#89b4fa', accentDark: '#74a8f7',
114
+ ownBg: 'rgba(137,180,250,0.12)', ownBorder: 'rgba(137,180,250,0.2)', ownText: '#e0e0e0',
115
+ otherBg: 'rgba(255,255,255,0.04)', otherBorder: 'rgba(255,255,255,0.08)', otherText: '#e0e0e0',
116
+ dividerBg: 'rgba(255,255,255,0.04)', dividerBorder: 'rgba(255,255,255,0.08)',
117
+ avatarBg: 'rgba(137,180,250,0.15)',
118
+ offline: 'rgba(255,255,255,0.2)',
119
+ inputBg: 'rgba(255,255,255,0.05)', inputBorder: 'rgba(255,255,255,0.1)',
120
+ attachBg: 'rgba(255,255,255,0.06)', attachBtnBg: 'rgba(255,255,255,0.08)', attachBtnHover: 'rgba(255,255,255,0.12)',
121
+ tabBg: '#181825', tabActiveBg: '#1e1e2e',
122
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#1e1e2e', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
123
+ spinnerTrack: 'rgba(255,255,255,0.08)',
124
+ currentUserBg: 'rgba(137,180,250,0.08)', currentUserBorder: 'rgba(137,180,250,0.15)', currentUserAvatar: '#a6e3a1',
125
+ }),
126
+
127
+ // ════════════════════════════════════════════
128
+ // 4. MIDNIGHT — Deep blue-black
129
+ // ════════════════════════════════════════════
130
+ midnight: _t({
131
+ bg: '#0f0f1a', sidebarBg: '#0a0a14', border: 'rgba(100,120,200,0.15)',
132
+ shadow: '0 2px 8px rgba(0,0,0,0.4)',
133
+ text: '#c8cdf0', textSec: '#8890b5', textMuted: '#555a7a',
134
+ accent: '#6c7bd4', accentDark: '#5a69c0',
135
+ ownBg: 'rgba(108,123,212,0.15)', ownBorder: 'rgba(108,123,212,0.25)', ownText: '#c8cdf0',
136
+ otherBg: 'rgba(255,255,255,0.03)', otherBorder: 'rgba(100,120,200,0.1)', otherText: '#c8cdf0',
137
+ dividerBg: 'rgba(100,120,200,0.06)', dividerBorder: 'rgba(100,120,200,0.1)',
138
+ offline: 'rgba(200,205,240,0.2)',
139
+ inputBg: 'rgba(100,120,200,0.06)', inputBorder: 'rgba(100,120,200,0.15)',
140
+ attachBtnBg: 'rgba(100,120,200,0.1)', attachBtnHover: 'rgba(100,120,200,0.18)',
141
+ tabBg: '#0a0a14', tabActiveBg: '#0f0f1a',
142
+ modalBackdrop: 'rgba(0,0,0,0.75)', modalBg: '#0f0f1a', modalShadow: '0 8px 32px rgba(0,0,0,0.6)',
143
+ spinnerTrack: 'rgba(100,120,200,0.1)',
144
+ }),
145
+
146
+ // ════════════════════════════════════════════
147
+ // 5. OCEAN — Teal/cyan
148
+ // ════════════════════════════════════════════
149
+ ocean: _t({
150
+ bg: '#f0fafa', sidebarBg: '#e8f6f6', border: 'rgba(0,150,136,0.15)',
151
+ text: '#1a3a3a', textSec: '#3a6a6a', textMuted: '#7a9a9a',
152
+ accent: '#00897b', accentDark: '#00796b',
153
+ }),
154
+
155
+ // ════════════════════════════════════════════
156
+ // 6. FOREST — Deep green
157
+ // ════════════════════════════════════════════
158
+ forest: _t({
159
+ bg: '#f2f7f2', sidebarBg: '#e8f0e8', border: 'rgba(46,125,50,0.15)',
160
+ text: '#1b3a1b', textSec: '#3a6a3a', textMuted: '#7a9a7a',
161
+ accent: '#2e7d32', accentDark: '#1b5e20',
162
+ }),
163
+
164
+ // ════════════════════════════════════════════
165
+ // 7. SUNSET — Warm orange/red
166
+ // ════════════════════════════════════════════
167
+ sunset: _t({
168
+ bg: '#fff8f0', sidebarBg: '#fff3e8', border: 'rgba(230,81,0,0.12)',
169
+ text: '#3a2010', textSec: '#6a4a30', textMuted: '#9a7a60',
170
+ accent: '#e65100', accentDark: '#bf360c',
171
+ online: '#66bb6a',
172
+ }),
173
+
174
+ // ════════════════════════════════════════════
175
+ // 8. ROSE — Pink/magenta
176
+ // ════════════════════════════════════════════
177
+ rose: _t({
178
+ bg: '#fff5f8', sidebarBg: '#ffeef3', border: 'rgba(194,24,91,0.12)',
179
+ text: '#3a1020', textSec: '#6a3050', textMuted: '#9a6080',
180
+ accent: '#c2185b', accentDark: '#ad1457',
181
+ }),
182
+
183
+ // ════════════════════════════════════════════
184
+ // 9. LAVENDER — Soft purple
185
+ // ════════════════════════════════════════════
186
+ lavender: _t({
187
+ bg: '#f8f5ff', sidebarBg: '#f0eaff', border: 'rgba(103,58,183,0.12)',
188
+ text: '#2a1a40', textSec: '#5a3a80', textMuted: '#8a6ab0',
189
+ accent: '#673ab7', accentDark: '#5e35b1',
190
+ }),
191
+
192
+ // ════════════════════════════════════════════
193
+ // 10. SLATE — Neutral gray
194
+ // ════════════════════════════════════════════
195
+ slate: _t({
196
+ bg: '#f8f9fa', sidebarBg: '#f1f3f5', border: 'rgba(0,0,0,0.1)',
197
+ text: '#212529', textSec: '#495057', textMuted: '#868e96',
198
+ accent: '#495057', accentDark: '#343a40',
199
+ }),
200
+
201
+ // ════════════════════════════════════════════
202
+ // 11. NORD — Nord color palette
203
+ // ════════════════════════════════════════════
204
+ nord: _t({
205
+ bg: '#2e3440', sidebarBg: '#282e3a', border: 'rgba(216,222,233,0.1)',
206
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
207
+ text: '#d8dee9', textSec: '#a0aec0', textMuted: '#616e88',
208
+ accent: '#88c0d0', accentDark: '#81a1c1',
209
+ ownBg: 'rgba(136,192,208,0.12)', ownBorder: 'rgba(136,192,208,0.2)', ownText: '#d8dee9',
210
+ otherBg: 'rgba(216,222,233,0.04)', otherBorder: 'rgba(216,222,233,0.08)', otherText: '#d8dee9',
211
+ dividerBg: 'rgba(216,222,233,0.04)', dividerBorder: 'rgba(216,222,233,0.08)',
212
+ offline: 'rgba(216,222,233,0.2)',
213
+ inputBg: 'rgba(216,222,233,0.05)', inputBorder: 'rgba(216,222,233,0.1)',
214
+ attachBtnBg: 'rgba(216,222,233,0.08)', attachBtnHover: 'rgba(216,222,233,0.12)',
215
+ tabBg: '#282e3a', tabActiveBg: '#2e3440',
216
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#2e3440', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
217
+ spinnerTrack: 'rgba(216,222,233,0.08)',
218
+ currentUserAvatar: '#a3be8c',
219
+ }),
220
+
221
+ // ════════════════════════════════════════════
222
+ // 12. DRACULA — Dracula color palette
223
+ // ════════════════════════════════════════════
224
+ dracula: _t({
225
+ bg: '#282a36', sidebarBg: '#21222c', border: 'rgba(248,248,242,0.08)',
226
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
227
+ text: '#f8f8f2', textSec: '#bfbfb8', textMuted: '#6272a4',
228
+ accent: '#bd93f9', accentDark: '#a87de8',
229
+ ownBg: 'rgba(189,147,249,0.12)', ownBorder: 'rgba(189,147,249,0.2)', ownText: '#f8f8f2',
230
+ otherBg: 'rgba(248,248,242,0.04)', otherBorder: 'rgba(248,248,242,0.08)', otherText: '#f8f8f2',
231
+ dividerBg: 'rgba(248,248,242,0.04)', dividerBorder: 'rgba(248,248,242,0.08)',
232
+ offline: 'rgba(248,248,242,0.2)',
233
+ inputBg: 'rgba(248,248,242,0.05)', inputBorder: 'rgba(248,248,242,0.1)',
234
+ attachBtnBg: 'rgba(248,248,242,0.08)', attachBtnHover: 'rgba(248,248,242,0.12)',
235
+ tabBg: '#21222c', tabActiveBg: '#282a36',
236
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#282a36', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
237
+ spinnerTrack: 'rgba(248,248,242,0.08)',
238
+ online: '#50fa7b', currentUserAvatar: '#50fa7b',
239
+ mentionSelfBg: 'rgba(255,184,108,0.25)', mentionSelfText: '#ffb86c',
240
+ }),
241
+
242
+ // ════════════════════════════════════════════
243
+ // 13. MONOKAI — Monokai-inspired
244
+ // ════════════════════════════════════════════
245
+ monokai: _t({
246
+ bg: '#272822', sidebarBg: '#1e1f1a', border: 'rgba(248,248,240,0.08)',
247
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
248
+ text: '#f8f8f0', textSec: '#b8b8a8', textMuted: '#75715e',
249
+ accent: '#a6e22e', accentDark: '#8cc220',
250
+ ownBg: 'rgba(166,226,46,0.1)', ownBorder: 'rgba(166,226,46,0.2)', ownText: '#f8f8f0',
251
+ otherBg: 'rgba(248,248,240,0.04)', otherBorder: 'rgba(248,248,240,0.08)', otherText: '#f8f8f0',
252
+ dividerBg: 'rgba(248,248,240,0.04)', dividerBorder: 'rgba(248,248,240,0.08)',
253
+ offline: 'rgba(248,248,240,0.2)',
254
+ inputBg: 'rgba(248,248,240,0.05)', inputBorder: 'rgba(248,248,240,0.1)',
255
+ attachBtnBg: 'rgba(248,248,240,0.08)', attachBtnHover: 'rgba(248,248,240,0.12)',
256
+ tabBg: '#1e1f1a', tabActiveBg: '#272822',
257
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#272822', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
258
+ spinnerTrack: 'rgba(248,248,240,0.08)',
259
+ online: '#a6e22e', currentUserAvatar: '#a6e22e',
260
+ linkColor: '#66d9ef',
261
+ }),
262
+
263
+ // ════════════════════════════════════════════
264
+ // 14. SOLARIZED LIGHT
265
+ // ════════════════════════════════════════════
266
+ 'solarized-light': _t({
267
+ bg: '#fdf6e3', sidebarBg: '#eee8d5', border: 'rgba(88,110,117,0.15)',
268
+ text: '#586e75', textSec: '#657b83', textMuted: '#93a1a1',
269
+ accent: '#268bd2', accentDark: '#2176b8',
270
+ }),
271
+
272
+ // ════════════════════════════════════════════
273
+ // 15. SOLARIZED DARK
274
+ // ════════════════════════════════════════════
275
+ 'solarized-dark': _t({
276
+ bg: '#002b36', sidebarBg: '#073642', border: 'rgba(147,161,161,0.12)',
277
+ shadow: '0 2px 8px rgba(0,0,0,0.4)',
278
+ text: '#839496', textSec: '#93a1a1', textMuted: '#586e75',
279
+ accent: '#268bd2', accentDark: '#2176b8',
280
+ ownBg: 'rgba(38,139,210,0.12)', ownBorder: 'rgba(38,139,210,0.2)', ownText: '#93a1a1',
281
+ otherBg: 'rgba(147,161,161,0.04)', otherBorder: 'rgba(147,161,161,0.08)', otherText: '#93a1a1',
282
+ dividerBg: 'rgba(147,161,161,0.04)', dividerBorder: 'rgba(147,161,161,0.08)',
283
+ offline: 'rgba(147,161,161,0.2)',
284
+ inputBg: 'rgba(147,161,161,0.05)', inputBorder: 'rgba(147,161,161,0.1)',
285
+ attachBtnBg: 'rgba(147,161,161,0.08)', attachBtnHover: 'rgba(147,161,161,0.12)',
286
+ tabBg: '#073642', tabActiveBg: '#002b36',
287
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#002b36', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
288
+ spinnerTrack: 'rgba(147,161,161,0.08)',
289
+ online: '#859900', currentUserAvatar: '#859900',
290
+ }),
291
+
292
+ // ════════════════════════════════════════════
293
+ // 16. CATPPUCCIN MOCHA
294
+ // ════════════════════════════════════════════
295
+ 'catppuccin-mocha': _t({
296
+ bg: '#1e1e2e', sidebarBg: '#181825', border: 'rgba(205,214,244,0.08)',
297
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
298
+ text: '#cdd6f4', textSec: '#a6adc8', textMuted: '#585b70',
299
+ accent: '#cba6f7', accentDark: '#b490e0',
300
+ ownBg: 'rgba(203,166,247,0.1)', ownBorder: 'rgba(203,166,247,0.2)', ownText: '#cdd6f4',
301
+ otherBg: 'rgba(205,214,244,0.04)', otherBorder: 'rgba(205,214,244,0.08)', otherText: '#cdd6f4',
302
+ dividerBg: 'rgba(205,214,244,0.04)', dividerBorder: 'rgba(205,214,244,0.08)',
303
+ offline: 'rgba(205,214,244,0.2)',
304
+ inputBg: 'rgba(205,214,244,0.05)', inputBorder: 'rgba(205,214,244,0.1)',
305
+ attachBtnBg: 'rgba(205,214,244,0.08)', attachBtnHover: 'rgba(205,214,244,0.12)',
306
+ tabBg: '#181825', tabActiveBg: '#1e1e2e',
307
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#1e1e2e', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
308
+ spinnerTrack: 'rgba(205,214,244,0.08)',
309
+ online: '#a6e3a1', currentUserAvatar: '#a6e3a1',
310
+ }),
311
+
312
+ // ════════════════════════════════════════════
313
+ // 17. CATPPUCCIN LATTE
314
+ // ════════════════════════════════════════════
315
+ 'catppuccin-latte': _t({
316
+ bg: '#eff1f5', sidebarBg: '#e6e9ef', border: 'rgba(76,79,105,0.12)',
317
+ text: '#4c4f69', textSec: '#6c6f85', textMuted: '#9ca0b0',
318
+ accent: '#8839ef', accentDark: '#7629d9',
319
+ }),
320
+
321
+ // ════════════════════════════════════════════
322
+ // 18. GITHUB LIGHT
323
+ // ════════════════════════════════════════════
324
+ 'github-light': _t({
325
+ bg: '#ffffff', sidebarBg: '#f6f8fa', border: 'rgba(31,35,40,0.1)',
326
+ text: '#1f2328', textSec: '#59636e', textMuted: '#8b949e',
327
+ accent: '#0969da', accentDark: '#0550ae',
328
+ }),
329
+
330
+ // ════════════════════════════════════════════
331
+ // 19. GITHUB DARK
332
+ // ════════════════════════════════════════════
333
+ 'github-dark': _t({
334
+ bg: '#0d1117', sidebarBg: '#010409', border: 'rgba(139,148,158,0.12)',
335
+ shadow: '0 2px 8px rgba(0,0,0,0.4)',
336
+ text: '#e6edf3', textSec: '#8b949e', textMuted: '#484f58',
337
+ accent: '#58a6ff', accentDark: '#4090e0',
338
+ ownBg: 'rgba(88,166,255,0.1)', ownBorder: 'rgba(88,166,255,0.2)', ownText: '#e6edf3',
339
+ otherBg: 'rgba(139,148,158,0.04)', otherBorder: 'rgba(139,148,158,0.08)', otherText: '#e6edf3',
340
+ dividerBg: 'rgba(139,148,158,0.04)', dividerBorder: 'rgba(139,148,158,0.08)',
341
+ offline: 'rgba(139,148,158,0.2)',
342
+ inputBg: 'rgba(139,148,158,0.05)', inputBorder: 'rgba(139,148,158,0.1)',
343
+ attachBtnBg: 'rgba(139,148,158,0.08)', attachBtnHover: 'rgba(139,148,158,0.12)',
344
+ tabBg: '#010409', tabActiveBg: '#0d1117',
345
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#0d1117', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
346
+ spinnerTrack: 'rgba(139,148,158,0.08)',
347
+ online: '#3fb950', currentUserAvatar: '#3fb950',
348
+ }),
349
+
350
+ // ════════════════════════════════════════════
351
+ // 20. OFFICE-HQ GOLD — Tacel Office-HQ theme
352
+ // ════════════════════════════════════════════
353
+ 'office-hq': _t({
354
+ bg: '#ffffff', sidebarBg: '#faf8f0', border: 'rgba(201,162,39,0.15)',
355
+ text: '#3d3520', textSec: '#6a5a30', textMuted: '#8a7d5a',
356
+ accent: '#c9a227', accentDark: '#b8922a',
357
+ }),
358
+
359
+ // ════════════════════════════════════════════
360
+ // 21. SHIPWORKS BLUE — Tacel ShipWorks theme
361
+ // ════════════════════════════════════════════
362
+ shipworks: _t({
363
+ bg: '#ffffff', sidebarBg: '#f0f5ff', border: 'rgba(25,118,210,0.12)',
364
+ text: '#1a2a40', textSec: '#3a5a80', textMuted: '#6a8ab0',
365
+ accent: '#1976d2', accentDark: '#1565c0',
366
+ }),
367
+
368
+ // ════════════════════════════════════════════
369
+ // 22. TECH-PORTAL — Tacel Tech-Portal dark theme
370
+ // ════════════════════════════════════════════
371
+ 'tech-portal': _t({
372
+ bg: '#1a1a2e', sidebarBg: '#16213e', border: 'rgba(83,193,222,0.12)',
373
+ shadow: '0 2px 8px rgba(0,0,0,0.3)',
374
+ text: '#e0e0e0', textSec: '#a0a0a0', textMuted: '#606060',
375
+ accent: '#53c1de', accentDark: '#40a8c4',
376
+ ownBg: 'rgba(83,193,222,0.1)', ownBorder: 'rgba(83,193,222,0.2)', ownText: '#e0e0e0',
377
+ otherBg: 'rgba(255,255,255,0.04)', otherBorder: 'rgba(255,255,255,0.08)', otherText: '#e0e0e0',
378
+ dividerBg: 'rgba(255,255,255,0.04)', dividerBorder: 'rgba(255,255,255,0.08)',
379
+ offline: 'rgba(255,255,255,0.2)',
380
+ inputBg: 'rgba(255,255,255,0.05)', inputBorder: 'rgba(83,193,222,0.15)',
381
+ attachBtnBg: 'rgba(255,255,255,0.08)', attachBtnHover: 'rgba(255,255,255,0.12)',
382
+ tabBg: '#16213e', tabActiveBg: '#1a1a2e',
383
+ modalBackdrop: 'rgba(0,0,0,0.7)', modalBg: '#1a1a2e', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
384
+ spinnerTrack: 'rgba(255,255,255,0.08)',
385
+ online: '#4caf50', currentUserAvatar: '#4caf50',
386
+ }),
387
+
388
+ // ════════════════════════════════════════════
389
+ // 23. CORAL — Warm coral/salmon
390
+ // ════════════════════════════════════════════
391
+ coral: _t({
392
+ bg: '#fff5f3', sidebarBg: '#ffefeb', border: 'rgba(255,87,51,0.12)',
393
+ text: '#3a1a10', textSec: '#6a3a28', textMuted: '#9a6a58',
394
+ accent: '#ff5733', accentDark: '#e04828',
395
+ }),
396
+
397
+ // ════════════════════════════════════════════
398
+ // 24. MINT — Fresh mint green
399
+ // ════════════════════════════════════════════
400
+ mint: _t({
401
+ bg: '#f0fff4', sidebarBg: '#e0f8e8', border: 'rgba(0,200,83,0.12)',
402
+ text: '#1a3a20', textSec: '#3a6a40', textMuted: '#6a9a70',
403
+ accent: '#00c853', accentDark: '#00a844',
404
+ }),
405
+
406
+ // ════════════════════════════════════════════
407
+ // 25. AMBER — Warm amber/honey
408
+ // ════════════════════════════════════════════
409
+ amber: _t({
410
+ bg: '#fffbf0', sidebarBg: '#fff8e1', border: 'rgba(255,160,0,0.12)',
411
+ text: '#3a2800', textSec: '#6a4a10', textMuted: '#9a7a40',
412
+ accent: '#ffa000', accentDark: '#ff8f00',
413
+ unreadText: '#3a2800',
414
+ }),
415
+
416
+ // ════════════════════════════════════════════
417
+ // 26. INDIGO — Deep indigo
418
+ // ════════════════════════════════════════════
419
+ indigo: _t({
420
+ bg: '#f5f5ff', sidebarBg: '#ececff', border: 'rgba(48,63,159,0.12)',
421
+ text: '#1a1a40', textSec: '#3a3a70', textMuted: '#6a6aa0',
422
+ accent: '#303f9f', accentDark: '#283593',
423
+ }),
424
+
425
+ // ════════════════════════════════════════════
426
+ // 27. ABYSS — Very dark blue
427
+ // ════════════════════════════════════════════
428
+ abyss: _t({
429
+ bg: '#060818', sidebarBg: '#040610', border: 'rgba(80,100,180,0.12)',
430
+ shadow: '0 2px 8px rgba(0,0,0,0.5)',
431
+ text: '#b0b8d8', textSec: '#7880a0', textMuted: '#484e6a',
432
+ accent: '#4fc3f7', accentDark: '#39aee0',
433
+ ownBg: 'rgba(79,195,247,0.1)', ownBorder: 'rgba(79,195,247,0.2)', ownText: '#b0b8d8',
434
+ otherBg: 'rgba(80,100,180,0.05)', otherBorder: 'rgba(80,100,180,0.1)', otherText: '#b0b8d8',
435
+ dividerBg: 'rgba(80,100,180,0.05)', dividerBorder: 'rgba(80,100,180,0.1)',
436
+ offline: 'rgba(176,184,216,0.2)',
437
+ inputBg: 'rgba(80,100,180,0.06)', inputBorder: 'rgba(80,100,180,0.12)',
438
+ attachBtnBg: 'rgba(80,100,180,0.08)', attachBtnHover: 'rgba(80,100,180,0.14)',
439
+ tabBg: '#040610', tabActiveBg: '#060818',
440
+ modalBackdrop: 'rgba(0,0,0,0.8)', modalBg: '#060818', modalShadow: '0 8px 32px rgba(0,0,0,0.6)',
441
+ spinnerTrack: 'rgba(80,100,180,0.1)',
442
+ }),
443
+
444
+ // ════════════════════════════════════════════
445
+ // 28. CREAM — Warm off-white
446
+ // ════════════════════════════════════════════
447
+ cream: _t({
448
+ bg: '#fefcf3', sidebarBg: '#f8f4e8', border: 'rgba(139,119,80,0.15)',
449
+ text: '#3a3020', textSec: '#6a5a40', textMuted: '#9a8a6a',
450
+ accent: '#8b7750', accentDark: '#7a6840',
451
+ }),
452
+
453
+ // ════════════════════════════════════════════
454
+ // 29. NEON — Bright neon on dark
455
+ // ════════════════════════════════════════════
456
+ neon: _t({
457
+ bg: '#0a0a0a', sidebarBg: '#050505', border: 'rgba(0,255,136,0.12)',
458
+ shadow: '0 2px 8px rgba(0,255,136,0.08)',
459
+ text: '#e0ffe0', textSec: '#a0d0a0', textMuted: '#507050',
460
+ accent: '#00ff88', accentDark: '#00dd77',
461
+ ownBg: 'rgba(0,255,136,0.08)', ownBorder: 'rgba(0,255,136,0.2)', ownText: '#e0ffe0',
462
+ otherBg: 'rgba(0,255,136,0.03)', otherBorder: 'rgba(0,255,136,0.08)', otherText: '#e0ffe0',
463
+ dividerBg: 'rgba(0,255,136,0.04)', dividerBorder: 'rgba(0,255,136,0.08)',
464
+ offline: 'rgba(0,255,136,0.15)',
465
+ inputBg: 'rgba(0,255,136,0.04)', inputBorder: 'rgba(0,255,136,0.12)',
466
+ attachBtnBg: 'rgba(0,255,136,0.06)', attachBtnHover: 'rgba(0,255,136,0.12)',
467
+ tabBg: '#050505', tabActiveBg: '#0a0a0a',
468
+ modalBackdrop: 'rgba(0,0,0,0.8)', modalBg: '#0a0a0a', modalShadow: '0 4px 24px rgba(0,255,136,0.15)',
469
+ spinnerTrack: 'rgba(0,255,136,0.08)',
470
+ online: '#00ff88', currentUserAvatar: '#00ff88',
471
+ sendText: '#0a0a0a',
472
+ }),
473
+
474
+ // ════════════════════════════════════════════
475
+ // 30. CHERRY — Dark red/crimson
476
+ // ════════════════════════════════════════════
477
+ cherry: _t({
478
+ bg: '#1a0a0e', sidebarBg: '#140810', border: 'rgba(220,60,80,0.12)',
479
+ shadow: '0 2px 8px rgba(0,0,0,0.4)',
480
+ text: '#f0d0d5', textSec: '#b08088', textMuted: '#705058',
481
+ accent: '#dc3c50', accentDark: '#c03040',
482
+ ownBg: 'rgba(220,60,80,0.12)', ownBorder: 'rgba(220,60,80,0.2)', ownText: '#f0d0d5',
483
+ otherBg: 'rgba(220,60,80,0.04)', otherBorder: 'rgba(220,60,80,0.08)', otherText: '#f0d0d5',
484
+ dividerBg: 'rgba(220,60,80,0.04)', dividerBorder: 'rgba(220,60,80,0.08)',
485
+ offline: 'rgba(240,208,213,0.2)',
486
+ inputBg: 'rgba(220,60,80,0.05)', inputBorder: 'rgba(220,60,80,0.12)',
487
+ attachBtnBg: 'rgba(220,60,80,0.08)', attachBtnHover: 'rgba(220,60,80,0.14)',
488
+ tabBg: '#140810', tabActiveBg: '#1a0a0e',
489
+ modalBackdrop: 'rgba(0,0,0,0.75)', modalBg: '#1a0a0e', modalShadow: '0 8px 32px rgba(0,0,0,0.5)',
490
+ spinnerTrack: 'rgba(220,60,80,0.1)',
491
+ online: '#ff6b7a', currentUserAvatar: '#ff6b7a',
492
+ }),
493
+ };
494
+
495
+ module.exports = { THEMES };
package/utils/dom.js ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * DOM utility helpers for tacel-chat
3
+ */
4
+
5
+ /**
6
+ * Escape HTML special characters to prevent XSS
7
+ */
8
+ function escapeHtml(text) {
9
+ if (!text) return '';
10
+ const div = document.createElement('div');
11
+ div.textContent = text;
12
+ return div.innerHTML;
13
+ }
14
+
15
+ /**
16
+ * Create a DOM element with optional classes, attributes, and children
17
+ */
18
+ function createElement(tag, options = {}) {
19
+ const el = document.createElement(tag);
20
+ if (options.className) el.className = options.className;
21
+ if (options.id) el.id = options.id;
22
+ if (options.innerHTML) el.innerHTML = options.innerHTML;
23
+ if (options.textContent) el.textContent = options.textContent;
24
+ if (options.style) Object.assign(el.style, options.style);
25
+ if (options.attrs) {
26
+ for (const [key, val] of Object.entries(options.attrs)) {
27
+ el.setAttribute(key, val);
28
+ }
29
+ }
30
+ if (options.events) {
31
+ for (const [evt, handler] of Object.entries(options.events)) {
32
+ el.addEventListener(evt, handler);
33
+ }
34
+ }
35
+ if (options.children) {
36
+ for (const child of options.children) {
37
+ if (typeof child === 'string') {
38
+ el.appendChild(document.createTextNode(child));
39
+ } else if (child) {
40
+ el.appendChild(child);
41
+ }
42
+ }
43
+ }
44
+ return el;
45
+ }
46
+
47
+ /**
48
+ * Check if an element is scrolled near the bottom
49
+ */
50
+ function isNearBottom(el, threshold = 50) {
51
+ if (!el) return true;
52
+ return el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
53
+ }
54
+
55
+ /**
56
+ * Scroll an element to the bottom
57
+ */
58
+ function scrollToBottom(el) {
59
+ if (!el) return;
60
+ el.scrollTop = el.scrollHeight;
61
+ }
62
+
63
+ /**
64
+ * Generate initials from a username (first 1-2 chars)
65
+ */
66
+ function getInitials(name) {
67
+ if (!name) return '?';
68
+ const parts = name.trim().split(/\s+/);
69
+ if (parts.length >= 2) {
70
+ return (parts[0][0] + parts[1][0]).toUpperCase();
71
+ }
72
+ return name.substring(0, 2).toUpperCase();
73
+ }
74
+
75
+ module.exports = { escapeHtml, createElement, isNearBottom, scrollToBottom, getInitials };