turbo_chat 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +178 -190
- data/app/assets/config/turbo_chat_manifest.js +3 -0
- data/app/assets/javascripts/turbo_chat/application.js +3 -0
- data/app/assets/javascripts/turbo_chat/invite_picker.js +19 -392
- data/app/assets/javascripts/turbo_chat/member_sync.js +426 -0
- data/app/assets/javascripts/turbo_chat/mentions.js +366 -0
- data/app/assets/javascripts/turbo_chat/messages.js +18 -370
- data/app/assets/javascripts/turbo_chat/realtime.js +3 -10
- data/app/assets/javascripts/turbo_chat/scroll_proxy.js +379 -0
- data/app/assets/javascripts/turbo_chat/shared.js +7 -383
- data/app/assets/stylesheets/turbo_chat/application.css +9 -1646
- data/app/assets/stylesheets/turbo_chat/base.css +84 -0
- data/app/assets/stylesheets/turbo_chat/components.css +193 -0
- data/app/assets/stylesheets/turbo_chat/composer.css +241 -0
- data/app/assets/stylesheets/turbo_chat/layout.css +307 -0
- data/app/assets/stylesheets/turbo_chat/members.css +264 -0
- data/app/assets/stylesheets/turbo_chat/menus.css +172 -0
- data/app/assets/stylesheets/turbo_chat/messages.css +430 -0
- data/app/controllers/turbo_chat/application_controller.rb +3 -7
- data/app/controllers/turbo_chat/chat_memberships_controller.rb +35 -1
- data/app/controllers/turbo_chat/chat_messages_controller.rb +4 -8
- data/app/controllers/turbo_chat/chats_controller.rb +10 -12
- data/app/helpers/turbo_chat/application_helper/config_support.rb +42 -32
- data/app/helpers/turbo_chat/application_helper/mention_support.rb +3 -3
- data/app/helpers/turbo_chat/application_helper/message_rendering.rb +24 -13
- data/app/models/turbo_chat/chat.rb +43 -20
- data/app/models/turbo_chat/chat_membership.rb +1 -1
- data/app/models/turbo_chat/chat_message/blocked_words_moderation.rb +9 -25
- data/app/models/turbo_chat/chat_message/body_length_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/broadcasting.rb +2 -6
- data/app/models/turbo_chat/chat_message/formatting.rb +3 -7
- data/app/models/turbo_chat/chat_message/mention_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/signals.rb +1 -1
- data/app/models/turbo_chat/chat_message.rb +3 -8
- data/app/views/turbo_chat/chat_messages/_form.html.erb +9 -9
- data/app/views/turbo_chat/chat_messages/_message.html.erb +2 -2
- data/app/views/turbo_chat/chat_messages/_signals.html.erb +11 -13
- data/app/views/turbo_chat/chat_messages/_system.html.erb +1 -1
- data/app/views/turbo_chat/chats/_invite_form.html.erb +1 -1
- data/app/views/turbo_chat/chats/_member_entries.html.erb +15 -1
- data/app/views/turbo_chat/chats/index.html.erb +1 -1
- data/app/views/turbo_chat/chats/new.html.erb +4 -7
- data/app/views/turbo_chat/chats/show.html.erb +29 -27
- data/config/routes.rb +6 -1
- data/db/migrate/20260325000016_add_chat_mode_to_turbo_chat_chats.rb +6 -0
- data/lib/generators/turbo_chat/install/templates/turbo_chat.rb +8 -0
- data/lib/turbo_chat/configuration/defaults.rb +21 -0
- data/lib/turbo_chat/configuration.rb +105 -0
- data/lib/turbo_chat/moderation/chat_actions.rb +2 -2
- data/lib/turbo_chat/moderation/member_actions.rb +2 -1
- data/lib/turbo_chat/moderation/support.rb +5 -9
- data/lib/turbo_chat/permission/support.rb +6 -2
- data/lib/turbo_chat/permission.rb +1 -5
- data/lib/turbo_chat/signals.rb +1 -1
- data/lib/turbo_chat/version.rb +1 -1
- metadata +13 -2
|
@@ -59,361 +59,6 @@
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
function parseMentionOptions(raw) {
|
|
63
|
-
if (!raw) {
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
var parsed;
|
|
68
|
-
try {
|
|
69
|
-
parsed = JSON.parse(raw);
|
|
70
|
-
} catch (_error) {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!Array.isArray(parsed)) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
var seen = {};
|
|
79
|
-
var options = [];
|
|
80
|
-
|
|
81
|
-
parsed.forEach(function (entry) {
|
|
82
|
-
if (!entry || typeof entry !== "object") {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
var token = String(entry.token || "").trim();
|
|
87
|
-
if (!token || token.charAt(0) !== "@" || /\s/.test(token)) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
var dedupeKey = token.toLowerCase();
|
|
92
|
-
if (seen[dedupeKey]) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
seen[dedupeKey] = true;
|
|
97
|
-
options.push({
|
|
98
|
-
token: token,
|
|
99
|
-
label: String(entry.label || token),
|
|
100
|
-
kind: String(entry.kind || "member")
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return options;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function parseMentionTokens(raw) {
|
|
108
|
-
if (!raw) {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
var parsed = raw;
|
|
113
|
-
if (typeof raw === "string") {
|
|
114
|
-
var trimmed = raw.trim();
|
|
115
|
-
if (!trimmed) {
|
|
116
|
-
return [];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (trimmed.charAt(0) === "[") {
|
|
120
|
-
try {
|
|
121
|
-
parsed = JSON.parse(trimmed);
|
|
122
|
-
} catch (_error) {
|
|
123
|
-
parsed = trimmed.split(",");
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
parsed = trimmed.split(",");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
var source = Array.isArray(parsed) ? parsed : [parsed];
|
|
131
|
-
var seen = {};
|
|
132
|
-
var tokens = [];
|
|
133
|
-
|
|
134
|
-
source.forEach(function (entry) {
|
|
135
|
-
var token = String(entry || "").trim();
|
|
136
|
-
if (!token || token.charAt(0) !== "@" || /\s/.test(token)) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
var dedupeKey = token.toLowerCase();
|
|
141
|
-
if (seen[dedupeKey]) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
seen[dedupeKey] = true;
|
|
146
|
-
tokens.push(token);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
return tokens;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function mentionKind(token) {
|
|
153
|
-
var mentionToken = String(token || "").trim();
|
|
154
|
-
if (!mentionToken) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (mentionToken.toLowerCase() === "@all") {
|
|
159
|
-
return "group";
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (constants.ROLE_MENTION_TOKEN_PATTERN.test(mentionToken)) {
|
|
163
|
-
return "role";
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (constants.MEMBER_MENTION_TOKEN_PATTERN.test(mentionToken)) {
|
|
167
|
-
return "member";
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function emptyMentionAutocomplete() {
|
|
174
|
-
return {
|
|
175
|
-
hideMenu: function () {},
|
|
176
|
-
updateMenu: function () {},
|
|
177
|
-
handleKeydown: function () {
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function setupMentionAutocomplete(input, options) {
|
|
184
|
-
if (!input) {
|
|
185
|
-
return emptyMentionAutocomplete();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
var mentionOptions = Array.isArray(options && options.mentionOptions) ? options.mentionOptions : [];
|
|
189
|
-
var mentionOptionsResolver = options && typeof options.mentionOptionsResolver === "function"
|
|
190
|
-
? options.mentionOptionsResolver
|
|
191
|
-
: null;
|
|
192
|
-
var menuHost = options && options.menuHost ? options.menuHost : input.parentNode;
|
|
193
|
-
var menuClassName = options && options.menuClassName ? options.menuClassName : "chat-mentions-menu";
|
|
194
|
-
if ((!mentionOptions.length && !mentionOptionsResolver) || !menuHost) {
|
|
195
|
-
return emptyMentionAutocomplete();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
var mentionMenu = null;
|
|
199
|
-
var mentionMatches = [];
|
|
200
|
-
var mentionActiveIndex = 0;
|
|
201
|
-
|
|
202
|
-
function availableMentionOptions() {
|
|
203
|
-
if (mentionOptionsResolver) {
|
|
204
|
-
var resolvedMentionOptions = mentionOptionsResolver();
|
|
205
|
-
return Array.isArray(resolvedMentionOptions) ? resolvedMentionOptions : [];
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return mentionOptions;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function ensureMentionMenu() {
|
|
212
|
-
if (mentionMenu) {
|
|
213
|
-
return mentionMenu;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
mentionMenu = document.createElement("div");
|
|
217
|
-
mentionMenu.className = menuClassName;
|
|
218
|
-
mentionMenu.hidden = true;
|
|
219
|
-
menuHost.appendChild(mentionMenu);
|
|
220
|
-
return mentionMenu;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function hideMentionMenu() {
|
|
224
|
-
if (!mentionMenu) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
mentionMenu.hidden = true;
|
|
229
|
-
mentionMenu.classList.remove("chat-mentions-menu--open");
|
|
230
|
-
mentionMenu.innerHTML = "";
|
|
231
|
-
mentionMatches = [];
|
|
232
|
-
mentionActiveIndex = 0;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function setMentionActiveIndex(index) {
|
|
236
|
-
if (!mentionMatches.length) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
mentionActiveIndex = ((index % mentionMatches.length) + mentionMatches.length) % mentionMatches.length;
|
|
241
|
-
if (!mentionMenu) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
mentionMenu.querySelectorAll(".chat-mentions-item").forEach(function (item, itemIndex) {
|
|
246
|
-
item.classList.toggle("chat-mentions-item--active", itemIndex === mentionActiveIndex);
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function mentionContext() {
|
|
251
|
-
var caret = input.selectionStart;
|
|
252
|
-
if (typeof caret !== "number") {
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
var beforeCaret = input.value.slice(0, caret);
|
|
257
|
-
var atIndex = beforeCaret.lastIndexOf("@");
|
|
258
|
-
if (atIndex < 0) {
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
var previousCharacter = atIndex > 0 ? beforeCaret.charAt(atIndex - 1) : "";
|
|
263
|
-
if (/[a-zA-Z0-9_]/.test(previousCharacter)) {
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
var query = beforeCaret.slice(atIndex + 1);
|
|
268
|
-
if (!/^[a-zA-Z0-9_]*$/.test(query)) {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
start: atIndex,
|
|
274
|
-
end: caret,
|
|
275
|
-
query: query
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function matchingMentionOptions(query) {
|
|
280
|
-
var normalizedQuery = String(query || "").toLowerCase();
|
|
281
|
-
return availableMentionOptions()
|
|
282
|
-
.filter(function (option) {
|
|
283
|
-
return option.token.slice(1).toLowerCase().indexOf(normalizedQuery) === 0;
|
|
284
|
-
})
|
|
285
|
-
.slice(0, constants.MENTION_MAX_RESULTS);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function insertMentionOption(option) {
|
|
289
|
-
var context = mentionContext();
|
|
290
|
-
if (!context) {
|
|
291
|
-
hideMentionMenu();
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
var before = input.value.slice(0, context.start);
|
|
296
|
-
var after = input.value.slice(context.end);
|
|
297
|
-
var needsTrailingSpace = !after.match(/^[\s,.!?;:)]/);
|
|
298
|
-
var insertion = option.token + (needsTrailingSpace ? " " : "");
|
|
299
|
-
input.value = before + insertion + after;
|
|
300
|
-
|
|
301
|
-
var caretPosition = before.length + insertion.length;
|
|
302
|
-
input.setSelectionRange(caretPosition, caretPosition);
|
|
303
|
-
input.focus();
|
|
304
|
-
|
|
305
|
-
hideMentionMenu();
|
|
306
|
-
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function renderMentionMenu() {
|
|
310
|
-
if (!mentionMatches.length) {
|
|
311
|
-
hideMentionMenu();
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
var menu = ensureMentionMenu();
|
|
316
|
-
menu.innerHTML = "";
|
|
317
|
-
mentionMatches.forEach(function (option, index) {
|
|
318
|
-
var item = document.createElement("button");
|
|
319
|
-
item.type = "button";
|
|
320
|
-
item.className = "chat-mentions-item";
|
|
321
|
-
item.setAttribute("data-chat-mention-index", String(index));
|
|
322
|
-
if (index === mentionActiveIndex) {
|
|
323
|
-
item.classList.add("chat-mentions-item--active");
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
var token = document.createElement("span");
|
|
327
|
-
token.className = "chat-mentions-item__token";
|
|
328
|
-
token.textContent = option.token;
|
|
329
|
-
|
|
330
|
-
var label = document.createElement("span");
|
|
331
|
-
label.className = "chat-mentions-item__label";
|
|
332
|
-
label.textContent = option.label;
|
|
333
|
-
|
|
334
|
-
item.appendChild(token);
|
|
335
|
-
item.appendChild(label);
|
|
336
|
-
item.addEventListener("mousedown", function (event) {
|
|
337
|
-
event.preventDefault();
|
|
338
|
-
});
|
|
339
|
-
item.addEventListener("click", function () {
|
|
340
|
-
insertMentionOption(option);
|
|
341
|
-
});
|
|
342
|
-
menu.appendChild(item);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
menu.hidden = false;
|
|
346
|
-
menu.classList.add("chat-mentions-menu--open");
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function selectActiveMention() {
|
|
350
|
-
if (!mentionMatches.length) {
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
insertMentionOption(mentionMatches[mentionActiveIndex]);
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function updateMentionMenu() {
|
|
359
|
-
var context = mentionContext();
|
|
360
|
-
if (!context) {
|
|
361
|
-
hideMentionMenu();
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
mentionMatches = matchingMentionOptions(context.query);
|
|
366
|
-
if (!mentionMatches.length) {
|
|
367
|
-
hideMentionMenu();
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
mentionActiveIndex = 0;
|
|
372
|
-
renderMentionMenu();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function handleMentionKeydown(event) {
|
|
376
|
-
if (!mentionMenu || mentionMenu.hidden || !mentionMatches.length) {
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (event.key === "ArrowDown") {
|
|
381
|
-
event.preventDefault();
|
|
382
|
-
setMentionActiveIndex(mentionActiveIndex + 1);
|
|
383
|
-
return true;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (event.key === "ArrowUp") {
|
|
387
|
-
event.preventDefault();
|
|
388
|
-
setMentionActiveIndex(mentionActiveIndex - 1);
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (event.key === "Enter" || event.key === "Tab") {
|
|
393
|
-
event.preventDefault();
|
|
394
|
-
return selectActiveMention();
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (event.key === "Escape") {
|
|
398
|
-
event.preventDefault();
|
|
399
|
-
hideMentionMenu();
|
|
400
|
-
return true;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
return {
|
|
407
|
-
hideMenu: hideMentionMenu,
|
|
408
|
-
updateMenu: updateMentionMenu,
|
|
409
|
-
handleKeydown: handleMentionKeydown,
|
|
410
|
-
setOptions: function (nextMentionOptions) {
|
|
411
|
-
mentionOptions = Array.isArray(nextMentionOptions) ? nextMentionOptions : [];
|
|
412
|
-
updateMentionMenu();
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
62
|
function scrollLastMessageIntoView(container) {
|
|
418
63
|
if (!container) {
|
|
419
64
|
return;
|
|
@@ -430,13 +75,17 @@
|
|
|
430
75
|
return;
|
|
431
76
|
}
|
|
432
77
|
|
|
433
|
-
|
|
78
|
+
// Skip the signals container (always last child) to find the last actual message.
|
|
79
|
+
var lastChild = container.lastElementChild;
|
|
80
|
+
var lastMessage = (lastChild && lastChild.classList.contains("chat-signals"))
|
|
81
|
+
? lastChild.previousElementSibling
|
|
82
|
+
: lastChild;
|
|
434
83
|
if (!lastMessage) {
|
|
435
84
|
return;
|
|
436
85
|
}
|
|
437
86
|
|
|
438
87
|
// Keep the last message fully visible with a tiny breathing space.
|
|
439
|
-
var bottomPadding =
|
|
88
|
+
var bottomPadding = containerBottomPadding(container);
|
|
440
89
|
var lastBottom = lastMessage.offsetTop + lastMessage.offsetHeight;
|
|
441
90
|
var targetScrollTop = Math.max(0, lastBottom - container.clientHeight + bottomPadding + 2);
|
|
442
91
|
container.scrollTop = targetScrollTop;
|
|
@@ -456,25 +105,6 @@
|
|
|
456
105
|
return parsedPadding;
|
|
457
106
|
}
|
|
458
107
|
|
|
459
|
-
function signalOverlayOffset(container) {
|
|
460
|
-
if (!container || typeof window === "undefined") {
|
|
461
|
-
return 0;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
var chatWindow = container.closest(".chat-window");
|
|
465
|
-
if (!chatWindow) {
|
|
466
|
-
return 0;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
var cssOffset = window.getComputedStyle(chatWindow).getPropertyValue("--chat-signal-offset");
|
|
470
|
-
var parsedOffset = parseFloat(cssOffset);
|
|
471
|
-
if (isNaN(parsedOffset) || parsedOffset <= 0) {
|
|
472
|
-
return 0;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return parsedOffset;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
108
|
function prefersReducedMotion() {
|
|
479
109
|
return (
|
|
480
110
|
typeof window !== "undefined" &&
|
|
@@ -491,7 +121,7 @@
|
|
|
491
121
|
var containerRect = container.getBoundingClientRect();
|
|
492
122
|
var messageRect = messageNode.getBoundingClientRect();
|
|
493
123
|
var topPadding = 12;
|
|
494
|
-
var bottomPadding = Math.max(14, containerBottomPadding(container)
|
|
124
|
+
var bottomPadding = Math.max(14, containerBottomPadding(container));
|
|
495
125
|
var aboveVisibleArea = messageRect.top < containerRect.top + topPadding;
|
|
496
126
|
var belowVisibleArea = messageRect.bottom > containerRect.bottom - bottomPadding;
|
|
497
127
|
if (!aboveVisibleArea && !belowVisibleArea) {
|
|
@@ -518,14 +148,8 @@
|
|
|
518
148
|
namespace.renderTurboStreamResponse = renderTurboStreamResponse;
|
|
519
149
|
namespace.datasetFlagEnabled = datasetFlagEnabled;
|
|
520
150
|
namespace.parseJsonObject = parseJsonObject;
|
|
521
|
-
namespace.parseMentionOptions = parseMentionOptions;
|
|
522
|
-
namespace.parseMentionTokens = parseMentionTokens;
|
|
523
|
-
namespace.mentionKind = mentionKind;
|
|
524
|
-
namespace.emptyMentionAutocomplete = emptyMentionAutocomplete;
|
|
525
|
-
namespace.setupMentionAutocomplete = setupMentionAutocomplete;
|
|
526
151
|
namespace.scrollLastMessageIntoView = scrollLastMessageIntoView;
|
|
527
152
|
namespace.containerBottomPadding = containerBottomPadding;
|
|
528
|
-
namespace.signalOverlayOffset = signalOverlayOffset;
|
|
529
153
|
namespace.prefersReducedMotion = prefersReducedMotion;
|
|
530
154
|
namespace.scrollMessageIntoView = scrollMessageIntoView;
|
|
531
155
|
})(window.TurboChatUI = window.TurboChatUI || {});
|