dynim-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +290 -0
  2. package/dist/builder/ai-prompt-popover.d.ts +26 -0
  3. package/dist/builder/ai-prompt-popover.d.ts.map +1 -0
  4. package/dist/builder/ai-prompt-popover.js +180 -0
  5. package/dist/builder/builder-client.d.ts +48 -0
  6. package/dist/builder/builder-client.d.ts.map +1 -0
  7. package/dist/builder/builder-client.js +157 -0
  8. package/dist/builder/builder.d.ts +41 -0
  9. package/dist/builder/builder.d.ts.map +1 -0
  10. package/dist/builder/builder.js +537 -0
  11. package/dist/builder/bundle-manager.d.ts +60 -0
  12. package/dist/builder/bundle-manager.d.ts.map +1 -0
  13. package/dist/builder/bundle-manager.js +357 -0
  14. package/dist/builder/classifier/classname-analyzer.d.ts +6 -0
  15. package/dist/builder/classifier/classname-analyzer.d.ts.map +1 -0
  16. package/dist/builder/classifier/classname-analyzer.js +107 -0
  17. package/dist/builder/classifier/index.d.ts +20 -0
  18. package/dist/builder/classifier/index.d.ts.map +1 -0
  19. package/dist/builder/classifier/index.js +181 -0
  20. package/dist/builder/classifier/semantic-analyzer.d.ts +24 -0
  21. package/dist/builder/classifier/semantic-analyzer.d.ts.map +1 -0
  22. package/dist/builder/classifier/semantic-analyzer.js +94 -0
  23. package/dist/builder/classifier/size-analyzer.d.ts +7 -0
  24. package/dist/builder/classifier/size-analyzer.d.ts.map +1 -0
  25. package/dist/builder/classifier/size-analyzer.js +120 -0
  26. package/dist/builder/classifier/visual-analyzer.d.ts +6 -0
  27. package/dist/builder/classifier/visual-analyzer.d.ts.map +1 -0
  28. package/dist/builder/classifier/visual-analyzer.js +158 -0
  29. package/dist/builder/client.d.ts +22 -0
  30. package/dist/builder/client.d.ts.map +1 -0
  31. package/dist/builder/client.js +54 -0
  32. package/dist/builder/code-client.d.ts +101 -0
  33. package/dist/builder/code-client.d.ts.map +1 -0
  34. package/dist/builder/code-client.js +418 -0
  35. package/dist/builder/diff-state.d.ts +24 -0
  36. package/dist/builder/diff-state.d.ts.map +1 -0
  37. package/dist/builder/diff-state.js +134 -0
  38. package/dist/builder/dom-scanner.d.ts +20 -0
  39. package/dist/builder/dom-scanner.d.ts.map +1 -0
  40. package/dist/builder/dom-scanner.js +102 -0
  41. package/dist/builder/drag-engine.d.ts +41 -0
  42. package/dist/builder/drag-engine.d.ts.map +1 -0
  43. package/dist/builder/drag-engine.js +686 -0
  44. package/dist/builder/editor-overlays.d.ts +31 -0
  45. package/dist/builder/editor-overlays.d.ts.map +1 -0
  46. package/dist/builder/editor-overlays.js +202 -0
  47. package/dist/builder/editor-state.d.ts +50 -0
  48. package/dist/builder/editor-state.d.ts.map +1 -0
  49. package/dist/builder/editor-state.js +132 -0
  50. package/dist/builder/element-utils.d.ts +43 -0
  51. package/dist/builder/element-utils.d.ts.map +1 -0
  52. package/dist/builder/element-utils.js +227 -0
  53. package/dist/builder/fiber-capture.d.ts +28 -0
  54. package/dist/builder/fiber-capture.d.ts.map +1 -0
  55. package/dist/builder/fiber-capture.js +264 -0
  56. package/dist/builder/freeze-overlay.d.ts +26 -0
  57. package/dist/builder/freeze-overlay.d.ts.map +1 -0
  58. package/dist/builder/freeze-overlay.js +213 -0
  59. package/dist/builder/history-state.d.ts +41 -0
  60. package/dist/builder/history-state.d.ts.map +1 -0
  61. package/dist/builder/history-state.js +76 -0
  62. package/dist/builder/index.d.ts +62 -0
  63. package/dist/builder/index.d.ts.map +1 -0
  64. package/dist/builder/index.js +92 -0
  65. package/dist/builder/state.d.ts +27 -0
  66. package/dist/builder/state.d.ts.map +1 -0
  67. package/dist/builder/state.js +50 -0
  68. package/dist/builder/style-applier.d.ts +61 -0
  69. package/dist/builder/style-applier.d.ts.map +1 -0
  70. package/dist/builder/style-applier.js +311 -0
  71. package/dist/builder/tree-state.d.ts +71 -0
  72. package/dist/builder/tree-state.d.ts.map +1 -0
  73. package/dist/builder/tree-state.js +168 -0
  74. package/dist/builder/widget.d.ts +29 -0
  75. package/dist/builder/widget.d.ts.map +1 -0
  76. package/dist/builder/widget.js +181 -0
  77. package/dist/index.d.ts +11 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +12 -0
  80. package/package.json +25 -0
  81. package/src/styles/base.css +378 -0
  82. package/src/styles/builder.css +422 -0
  83. package/src/styles/editor.css +131 -0
  84. package/src/styles/themes/dark.css +24 -0
  85. package/src/styles/themes/light.css +21 -0
  86. package/src/styles/variables.css +63 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Default DOM widget for vanilla JS users
3
+ * Renders a complete chat interface - floating or inline
4
+ */
5
+ export function createWidget(container, state, client, config = {}) {
6
+ const { mode = 'floating', position = 'bottom-right', startOpen = false, showTimestamps = false, showAvatars = true, theme = 'light', placeholder = 'Type a message...', title = 'Chat' } = config;
7
+ let isOpen = startOpen;
8
+ const root = document.createElement('div');
9
+ root.className = `chatbot chatbot--${theme} chatbot--${position} chatbot--${mode}`;
10
+ if (mode === 'floating') {
11
+ root.innerHTML = `
12
+ <button class="chatbot__launcher" aria-label="Open chat">
13
+ <svg class="chatbot__launcher-icon chatbot__launcher-icon--chat" viewBox="0 0 24 24" width="28" height="28" fill="currentColor">
14
+ <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
15
+ </svg>
16
+ <svg class="chatbot__launcher-icon chatbot__launcher-icon--close" viewBox="0 0 24 24" width="28" height="28" fill="currentColor">
17
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
18
+ </svg>
19
+ </button>
20
+ <div class="chatbot__popup">
21
+ <div class="chatbot__header">
22
+ <span class="chatbot__title">${title}</span>
23
+ <button class="chatbot__close" aria-label="Close chat">
24
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
25
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
26
+ </svg>
27
+ </button>
28
+ </div>
29
+ <div class="chatbot__messages"></div>
30
+ <form class="chatbot__form">
31
+ <input
32
+ type="text"
33
+ class="chatbot__input"
34
+ placeholder="${placeholder}"
35
+ autocomplete="off"
36
+ />
37
+ <button type="submit" class="chatbot__send">
38
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
39
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
40
+ </svg>
41
+ </button>
42
+ </form>
43
+ <div class="chatbot__typing" hidden>
44
+ <span></span><span></span><span></span>
45
+ </div>
46
+ </div>
47
+ `;
48
+ }
49
+ else {
50
+ root.innerHTML = `
51
+ <div class="chatbot__container">
52
+ <div class="chatbot__messages"></div>
53
+ <form class="chatbot__form">
54
+ <input
55
+ type="text"
56
+ class="chatbot__input"
57
+ placeholder="${placeholder}"
58
+ autocomplete="off"
59
+ />
60
+ <button type="submit" class="chatbot__send">
61
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
62
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
63
+ </svg>
64
+ </button>
65
+ </form>
66
+ <div class="chatbot__typing" hidden>
67
+ <span></span><span></span><span></span>
68
+ </div>
69
+ </div>
70
+ `;
71
+ }
72
+ const messagesEl = root.querySelector('.chatbot__messages');
73
+ const formEl = root.querySelector('.chatbot__form');
74
+ const inputEl = root.querySelector('.chatbot__input');
75
+ const typingEl = root.querySelector('.chatbot__typing');
76
+ const sendBtn = root.querySelector('.chatbot__send');
77
+ // Handle send - extracted to be callable from both button click and form submit
78
+ async function handleSend() {
79
+ const text = inputEl.value.trim();
80
+ if (!text)
81
+ return;
82
+ inputEl.value = '';
83
+ state.addMessage({ role: 'user', text });
84
+ typingEl.hidden = false;
85
+ state.setState({ isLoading: true });
86
+ const botMessage = state.addMessage({ role: 'assistant', text: '' });
87
+ try {
88
+ await client.send(text);
89
+ }
90
+ catch (error) {
91
+ state.updateMessage(botMessage.id, { text: 'Sorry, something went wrong.' });
92
+ state.setState({ error: error.message });
93
+ }
94
+ finally {
95
+ typingEl.hidden = true;
96
+ state.setState({ isLoading: false });
97
+ }
98
+ }
99
+ // Handle send on button click (backup in case form submit doesn't fire)
100
+ if (sendBtn) {
101
+ sendBtn.addEventListener('click', (e) => {
102
+ e.preventDefault();
103
+ handleSend();
104
+ });
105
+ }
106
+ if (mode === 'floating') {
107
+ const launcherEl = root.querySelector('.chatbot__launcher');
108
+ const closeEl = root.querySelector('.chatbot__close');
109
+ function toggle() {
110
+ isOpen = !isOpen;
111
+ root.classList.toggle('chatbot--open', isOpen);
112
+ if (isOpen) {
113
+ inputEl.focus();
114
+ }
115
+ }
116
+ launcherEl.addEventListener('click', toggle);
117
+ closeEl.addEventListener('click', toggle);
118
+ if (startOpen) {
119
+ root.classList.add('chatbot--open');
120
+ }
121
+ }
122
+ function escapeHtml(text) {
123
+ const div = document.createElement('div');
124
+ div.textContent = text;
125
+ return div.innerHTML;
126
+ }
127
+ function formatTime(timestamp) {
128
+ return new Date(timestamp).toLocaleTimeString([], {
129
+ hour: '2-digit',
130
+ minute: '2-digit'
131
+ });
132
+ }
133
+ function renderMessages(messages) {
134
+ messagesEl.innerHTML = messages.map(msg => `
135
+ <div class="chatbot__message chatbot__message--${msg.role}">
136
+ ${showAvatars ? `<div class="chatbot__avatar">${msg.role === 'user' ? '👤' : '🤖'}</div>` : ''}
137
+ <div class="chatbot__bubble">
138
+ <div class="chatbot__text">${escapeHtml(msg.text)}</div>
139
+ ${showTimestamps ? `<div class="chatbot__time">${formatTime(msg.timestamp)}</div>` : ''}
140
+ </div>
141
+ </div>
142
+ `).join('');
143
+ messagesEl.scrollTop = messagesEl.scrollHeight;
144
+ }
145
+ formEl.addEventListener('submit', async (e) => {
146
+ e.preventDefault();
147
+ handleSend();
148
+ });
149
+ state.subscribe((newState) => {
150
+ renderMessages(newState.messages);
151
+ typingEl.hidden = !newState.isTyping;
152
+ });
153
+ const containerEl = mode === 'floating'
154
+ ? document.body
155
+ : (typeof container === 'string' ? document.querySelector(container) : container);
156
+ containerEl?.appendChild(root);
157
+ renderMessages(state.getState().messages);
158
+ return {
159
+ root,
160
+ open: () => {
161
+ if (mode === 'floating') {
162
+ isOpen = true;
163
+ root.classList.add('chatbot--open');
164
+ }
165
+ },
166
+ close: () => {
167
+ if (mode === 'floating') {
168
+ isOpen = false;
169
+ root.classList.remove('chatbot--open');
170
+ }
171
+ },
172
+ toggle: () => {
173
+ if (mode === 'floating') {
174
+ isOpen = !isOpen;
175
+ root.classList.toggle('chatbot--open', isOpen);
176
+ }
177
+ },
178
+ isOpen: () => isOpen,
179
+ destroy: () => root.remove()
180
+ };
181
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * dynim-core - Vanilla TS, framework-agnostic core
3
+ *
4
+ * This package provides:
5
+ * - Chatbot state and streaming client
6
+ * - Visual builder for drag-and-drop editing
7
+ * - DOM utilities and element classification
8
+ */
9
+ export * from './builder';
10
+ export { default } from './builder/builder';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,WAAW,CAAC;AAG1B,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * dynim-core - Vanilla TS, framework-agnostic core
3
+ *
4
+ * This package provides:
5
+ * - Chatbot state and streaming client
6
+ * - Visual builder for drag-and-drop editing
7
+ * - DOM utilities and element classification
8
+ */
9
+ // Re-export everything from builder
10
+ export * from './builder';
11
+ // Default export
12
+ export { default } from './builder/builder';
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "dynim-core",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./styles": "./src/styles/base.css",
10
+ "./styles/*": "./src/styles/*"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "src/styles",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsc --watch"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "^5.0.0"
23
+ },
24
+ "license": "MIT"
25
+ }
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Base styles for the chatbot widget
3
+ */
4
+
5
+ @import './variables.css';
6
+ @import './editor.css';
7
+ @import './builder.css';
8
+
9
+ .chatbot {
10
+ font-family: var(--chat-font);
11
+ font-size: var(--chat-font-size);
12
+ line-height: var(--chat-line-height);
13
+ color: var(--chat-text);
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ .chatbot *,
18
+ .chatbot *::before,
19
+ .chatbot *::after {
20
+ box-sizing: inherit;
21
+ }
22
+
23
+ /* ===========================================
24
+ FLOATING MODE
25
+ =========================================== */
26
+
27
+ .chatbot--floating {
28
+ position: fixed;
29
+ z-index: 9999;
30
+ }
31
+
32
+ .chatbot--floating.chatbot--bottom-right {
33
+ bottom: var(--chat-offset);
34
+ right: var(--chat-offset);
35
+ }
36
+
37
+ .chatbot--floating.chatbot--bottom-left {
38
+ bottom: var(--chat-offset);
39
+ left: var(--chat-offset);
40
+ }
41
+
42
+ /* Launcher button - Glass theme (true see-through) */
43
+ .chatbot__launcher {
44
+ width: var(--launcher-size);
45
+ height: var(--launcher-size);
46
+ border: 1px solid rgba(255, 255, 255, 0.2);
47
+ border-radius: 50%;
48
+ background: rgba(0, 0, 0, 0.08);
49
+ backdrop-filter: blur(20px) saturate(180%);
50
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
51
+ color: #ffffff;
52
+ cursor: pointer;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
57
+ transition:
58
+ background var(--chat-transition),
59
+ transform var(--chat-transition),
60
+ box-shadow var(--chat-transition);
61
+ }
62
+
63
+ .chatbot__launcher:hover {
64
+ background: rgba(0, 0, 0, 0.15);
65
+ transform: scale(1.05);
66
+ }
67
+
68
+ .chatbot__launcher:active {
69
+ transform: scale(0.95);
70
+ }
71
+
72
+ /* Launcher icon toggle */
73
+ .chatbot__launcher-icon {
74
+ transition: transform var(--popup-transition), opacity var(--popup-transition);
75
+ }
76
+
77
+ .chatbot__launcher-icon--chat {
78
+ position: absolute;
79
+ }
80
+
81
+ .chatbot__launcher-icon--close {
82
+ position: absolute;
83
+ opacity: 0;
84
+ transform: rotate(-90deg) scale(0.5);
85
+ }
86
+
87
+ .chatbot--open .chatbot__launcher-icon--chat {
88
+ opacity: 0;
89
+ transform: rotate(90deg) scale(0.5);
90
+ }
91
+
92
+ .chatbot--open .chatbot__launcher-icon--close {
93
+ opacity: 1;
94
+ transform: rotate(0deg) scale(1);
95
+ }
96
+
97
+ /* Popup container - Glass effect (true see-through) */
98
+ .chatbot__popup {
99
+ position: absolute;
100
+ bottom: calc(var(--launcher-size) + 16px);
101
+ width: var(--chat-width);
102
+ height: var(--chat-height);
103
+ background: rgba(0, 0, 0, 0.08);
104
+ backdrop-filter: blur(20px) saturate(180%);
105
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
106
+ border-radius: var(--chat-radius);
107
+ border: 1px solid rgba(255, 255, 255, 0.15);
108
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
109
+ display: flex;
110
+ flex-direction: column;
111
+ overflow: hidden;
112
+
113
+ /* Animation */
114
+ opacity: 0;
115
+ transform: scale(0.9) translateY(10px);
116
+ transform-origin: bottom right;
117
+ pointer-events: none;
118
+ transition:
119
+ opacity var(--popup-transition),
120
+ transform var(--popup-transition);
121
+ }
122
+
123
+ .chatbot--bottom-right .chatbot__popup {
124
+ right: 0;
125
+ transform-origin: bottom right;
126
+ }
127
+
128
+ .chatbot--bottom-left .chatbot__popup {
129
+ left: 0;
130
+ transform-origin: bottom left;
131
+ }
132
+
133
+ .chatbot--open .chatbot__popup {
134
+ opacity: 1;
135
+ transform: scale(1) translateY(0);
136
+ pointer-events: auto;
137
+ }
138
+
139
+ /* Header - Glass effect */
140
+ .chatbot__header {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ padding: 0 16px;
145
+ height: var(--header-height);
146
+ background: transparent;
147
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
148
+ color: #ffffff;
149
+ flex-shrink: 0;
150
+ }
151
+
152
+ .chatbot__title {
153
+ font-weight: 600;
154
+ font-size: 16px;
155
+ }
156
+
157
+ .chatbot__close {
158
+ width: 32px;
159
+ height: 32px;
160
+ border: none;
161
+ border-radius: 50%;
162
+ background: rgba(255, 255, 255, 0.08);
163
+ color: rgba(255, 255, 255, 0.7);
164
+ cursor: pointer;
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ transition: background var(--chat-transition), color var(--chat-transition);
169
+ }
170
+
171
+ .chatbot__close:hover {
172
+ background: rgba(255, 255, 255, 0.15);
173
+ color: #ffffff;
174
+ }
175
+
176
+ /* ===========================================
177
+ INLINE MODE
178
+ =========================================== */
179
+
180
+ .chatbot--inline {
181
+ width: var(--chat-width);
182
+ height: var(--chat-height);
183
+ }
184
+
185
+ /* Main container (inline mode) */
186
+ .chatbot__container {
187
+ display: flex;
188
+ flex-direction: column;
189
+ height: 100%;
190
+ background: var(--chat-bg);
191
+ border: 1px solid var(--chat-border);
192
+ border-radius: var(--chat-radius);
193
+ box-shadow: var(--chat-shadow);
194
+ overflow: hidden;
195
+ }
196
+
197
+ /* ===========================================
198
+ SHARED STYLES
199
+ =========================================== */
200
+
201
+ /* Messages area - Glass theme */
202
+ .chatbot__messages {
203
+ flex: 1;
204
+ overflow-y: auto;
205
+ padding: 16px;
206
+ display: flex;
207
+ flex-direction: column;
208
+ gap: var(--message-gap);
209
+ background: transparent;
210
+ }
211
+
212
+ /* Individual message */
213
+ .chatbot__message {
214
+ display: flex;
215
+ gap: 8px;
216
+ max-width: 85%;
217
+ }
218
+
219
+ .chatbot__message--user {
220
+ align-self: flex-end;
221
+ flex-direction: row-reverse;
222
+ }
223
+
224
+ .chatbot__message--assistant {
225
+ align-self: flex-start;
226
+ }
227
+
228
+ /* Avatar - Glass theme */
229
+ .chatbot__avatar {
230
+ width: 32px;
231
+ height: 32px;
232
+ border-radius: 50%;
233
+ display: flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ font-size: 16px;
237
+ flex-shrink: 0;
238
+ background: rgba(255, 255, 255, 0.1);
239
+ }
240
+
241
+ /* Message bubble */
242
+ .chatbot__bubble {
243
+ padding: var(--message-padding);
244
+ border-radius: var(--message-radius);
245
+ word-wrap: break-word;
246
+ }
247
+
248
+ .chatbot__message--user .chatbot__bubble {
249
+ background: rgba(59, 130, 246, 0.8);
250
+ color: #ffffff;
251
+ border-bottom-right-radius: 4px;
252
+ }
253
+
254
+ .chatbot__message--assistant .chatbot__bubble {
255
+ background: rgba(255, 255, 255, 0.1);
256
+ color: #e2e8f0;
257
+ border-bottom-left-radius: 4px;
258
+ }
259
+
260
+ /* Timestamp */
261
+ .chatbot__time {
262
+ font-size: 11px;
263
+ color: var(--chat-text-muted);
264
+ margin-top: 4px;
265
+ opacity: 0.7;
266
+ }
267
+
268
+ .chatbot__message--user .chatbot__time {
269
+ color: var(--message-user-text);
270
+ }
271
+
272
+ /* Input form - Glass theme */
273
+ .chatbot__form {
274
+ display: flex;
275
+ gap: 8px;
276
+ padding: 16px;
277
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
278
+ background: transparent;
279
+ }
280
+
281
+ .chatbot__input {
282
+ flex: 1;
283
+ height: var(--input-height);
284
+ padding: 0 20px;
285
+ border: 1px solid rgba(255, 255, 255, 0.15);
286
+ border-radius: var(--input-radius);
287
+ background: rgba(255, 255, 255, 0.08);
288
+ font-family: inherit;
289
+ font-size: inherit;
290
+ color: #ffffff;
291
+ outline: none;
292
+ transition: border-color var(--chat-transition), background var(--chat-transition);
293
+ }
294
+
295
+ .chatbot__input:focus {
296
+ border-color: rgba(59, 130, 246, 0.6);
297
+ background: rgba(255, 255, 255, 0.12);
298
+ }
299
+
300
+ .chatbot__input::placeholder {
301
+ color: rgba(255, 255, 255, 0.5);
302
+ }
303
+
304
+ /* Send button - Glass theme */
305
+ .chatbot__send {
306
+ width: var(--input-height);
307
+ height: var(--input-height);
308
+ border: none;
309
+ border-radius: 50%;
310
+ background: rgba(59, 130, 246, 0.8);
311
+ color: #ffffff;
312
+ cursor: pointer;
313
+ display: flex;
314
+ align-items: center;
315
+ justify-content: center;
316
+ transition: background var(--chat-transition), transform var(--chat-transition);
317
+ }
318
+
319
+ .chatbot__send:hover {
320
+ background: rgba(59, 130, 246, 1);
321
+ }
322
+
323
+ .chatbot__send:active {
324
+ transform: scale(0.95);
325
+ }
326
+
327
+ /* Typing indicator - Glass theme */
328
+ .chatbot__typing {
329
+ display: flex;
330
+ gap: 4px;
331
+ padding: 12px 16px;
332
+ align-items: center;
333
+ }
334
+
335
+ .chatbot__typing[hidden] {
336
+ display: none;
337
+ }
338
+
339
+ .chatbot__typing span {
340
+ width: 8px;
341
+ height: 8px;
342
+ border-radius: 50%;
343
+ background: rgba(255, 255, 255, 0.5);
344
+ animation: typing 1.4s infinite ease-in-out both;
345
+ }
346
+
347
+ .chatbot__typing span:nth-child(1) { animation-delay: -0.32s; }
348
+ .chatbot__typing span:nth-child(2) { animation-delay: -0.16s; }
349
+ .chatbot__typing span:nth-child(3) { animation-delay: 0s; }
350
+
351
+ @keyframes typing {
352
+ 0%, 80%, 100% {
353
+ transform: scale(0.6);
354
+ opacity: 0.4;
355
+ }
356
+ 40% {
357
+ transform: scale(1);
358
+ opacity: 1;
359
+ }
360
+ }
361
+
362
+ /* Scrollbar styling - Glass theme */
363
+ .chatbot__messages::-webkit-scrollbar {
364
+ width: 6px;
365
+ }
366
+
367
+ .chatbot__messages::-webkit-scrollbar-track {
368
+ background: transparent;
369
+ }
370
+
371
+ .chatbot__messages::-webkit-scrollbar-thumb {
372
+ background: rgba(255, 255, 255, 0.2);
373
+ border-radius: 3px;
374
+ }
375
+
376
+ .chatbot__messages::-webkit-scrollbar-thumb:hover {
377
+ background: rgba(255, 255, 255, 0.3);
378
+ }