@@ -45,10 +45,18 @@
45 45 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.tag.type-worker { background: #1f6feb22; border-color: #1f6feb44; color: #79c0ff; }
46 46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.tag.type-observer { background: #21262d; border-color: #30363d; color: #8b949e; }
47 47 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.tag.revoked { background: #f8514922; border-color: #f8514944; color: #ff7b72; }
48 48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.actions { display: flex; gap: 6px; flex-wrap: wrap; }
49 49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.empty { color: #8b949e; font-size: 13px; text-align: center; padding: 24px; }
50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .chan-item { padding: 8px 12px; font-size: 13px; cursor: pointer; color: #8b949e; border-bottom: 1px solid #21262d; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .chan-item:hover { background: #1c2128; color: #e6edf3; }
52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .chan-item.active { background: #1f6feb22; color: #58a6ff; border-left: 2px solid #58a6ff; }
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .msg-row { display: flex; gap: 8px; font-size: 13px; line-height: 1.6; padding: 1px 0; }
54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .msg-time { color: #8b949e; font-size: 11px; flex-shrink: 0; padding-top: 3px; min-width: 44px; }
55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .msg-nick { color: #58a6ff; font-weight: 600; flex-shrink: 0; min-width: 80px; text-align: right; }
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .msg-nick.bridge-nick { color: #3fb950; }
57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ .msg-text { color: #e6edf3; word-break: break-word; }
50 58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
form { display: flex; flex-direction: column; gap: 14px; }
51 59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
52 60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
label { display: block; font-size: 12px; color: #8b949e; margin-bottom: 4px; }
53 61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
input, select, textarea { width: 100%; background: #0d1117; border: 1px solid #30363d; border-radius: 6px; padding: 8px 10px; font-size: 13px; font-family: inherit; color: #e6edf3; outline: none; transition: border-color 0.1s; }
54 62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
input:focus, select:focus, textarea:focus { border-color: #58a6ff; }
@@ -157,10 +165,39 @@
157 165 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
<button type="submit" class="primary">register</button>
158 166 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</div>
159 167 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</form>
160 168 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</div>
161 169 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</div>
170 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
171 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <!-- Chat -->
172 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div class="card" id="chat-card" style="display:none">
173 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div class="card-header">
174 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <h2>chat</h2>
175 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <span class="badge" id="chat-channel-badge" style="display:none"></span>
176 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div class="spacer"></div>
177 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <span id="chat-stream-status" style="font-size:11px;color:#8b949e"></span>
178 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
179 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div style="display:flex;height:440px">
180 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div id="chat-channel-list" style="width:140px;border-right:1px solid #30363d;overflow-y:auto;flex-shrink:0;padding:8px 0;display:flex;flex-direction:column">
181 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div style="padding:6px 12px;font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:#8b949e">channels</div>
182 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div style="padding:6px 8px;border-bottom:1px solid #21262d;display:flex;gap:4px">
183 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <input type="text" id="join-channel-input" placeholder="#channel" style="flex:1;font-size:11px;padding:3px 6px" autocomplete="off">
184 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <button class="small" onclick="joinChannel()" style="padding:3px 6px;font-size:11px">+</button>
185 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
186 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
187 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div style="display:flex;flex-direction:column;flex:1;min-width:0">
188 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div id="chat-messages" style="flex:1;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:2px">
189 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div class="empty" id="chat-placeholder">select a channel to view messages</div>
190 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
191 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <div style="padding:10px 14px;border-top:1px solid #30363d;display:flex;gap:8px;align-items:center">
192 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <input type="text" id="chat-nick-input" placeholder="your nick" style="width:110px;flex-shrink:0" autocomplete="off">
193 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <input type="text" id="chat-text-input" placeholder="type a message…" style="flex:1" autocomplete="off">
194 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <button class="primary small" id="chat-send-btn" onclick="sendChatMessage()">send</button>
195 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ </div>
162 199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</main>
163 200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
164 201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
<!-- Token modal -->
165 202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
<div class="modal-overlay" id="token-modal">
166 203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
<div class="modal">
@@ -375,14 +412,136 @@
375 412 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
function fmtTime(iso) {
376 413 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if (!iso) return '—';
377 414 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
const d = new Date(iso);
378 415 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
379 416 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
417 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
418 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // --- chat ---
419 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ let chatChannel = null;
420 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ let chatSSE = null;
421 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
422 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ async function loadChannels() {
423 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (!getToken()) return;
424 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try {
425 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const data = await api('GET', '/v1/channels');
426 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ renderChannelList(data.channels || []);
427 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-card').style.display = '';
428 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } catch(e) {
429 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // bridge disabled or error — keep chat card hidden
430 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
431 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
432 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
433 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ function renderChannelList(channels) {
434 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const list = document.getElementById('chat-channel-list');
435 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Remove old channel items (keep header div and join input div)
436 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Array.from(list.querySelectorAll('.chan-item')).forEach(el => el.remove());
437 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ channels.sort().forEach(ch => {
438 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const el = document.createElement('div');
439 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ el.className = 'chan-item' + (ch === chatChannel ? ' active' : '');
440 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ el.textContent = ch;
441 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ el.onclick = () => selectChannel(ch);
442 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ list.appendChild(el);
443 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ });
444 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
445 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
446 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ async function joinChannel() {
447 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ let ch = document.getElementById('join-channel-input').value.trim();
448 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (!ch) return;
449 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (!ch.startsWith('#')) ch = '#' + ch;
450 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const slug = ch.replace(/^#/, '');
451 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try {
452 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ await api('POST', `/v1/channels/${slug}/join`);
453 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('join-channel-input').value = '';
454 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ await loadChannels();
455 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ selectChannel(ch);
456 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } catch(e) {
457 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ alert('Join failed: ' + e.message);
458 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
459 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
460 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
461 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('join-channel-input').addEventListener('keydown', e => {
462 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (e.key === 'Enter') joinChannel();
463 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ });
464 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
465 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ async function selectChannel(ch) {
466 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ chatChannel = ch;
467 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-channel-badge').textContent = ch;
468 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-channel-badge').style.display = '';
469 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-placeholder').style.display = 'none';
470 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.querySelectorAll('.chan-item').forEach(el => {
471 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ el.classList.toggle('active', el.textContent === ch);
472 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ });
473 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
474 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const area = document.getElementById('chat-messages');
475 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // clear previous messages (keep placeholder)
476 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Array.from(area.children).forEach(el => { if (!el.id) el.remove(); });
477 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
478 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try {
479 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const slug = ch.replace(/^#/, '');
480 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const data = await api('GET', `/v1/channels/${slug}/messages`);
481 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ (data.messages || []).forEach(appendMessage);
482 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ area.scrollTop = area.scrollHeight;
483 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } catch(e) {}
484 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
485 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (chatSSE) { chatSSE.close(); chatSSE = null; }
486 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const slug = ch.replace(/^#/, '');
487 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const url = `/v1/channels/${slug}/stream?token=${encodeURIComponent(getToken())}`;
488 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const es = new EventSource(url);
489 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ chatSSE = es;
490 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const status = document.getElementById('chat-stream-status');
491 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ es.onopen = () => { status.textContent = '● live'; status.style.color = '#3fb950'; };
492 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ es.onmessage = (e) => {
493 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try {
494 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const msg = JSON.parse(e.data);
495 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ appendMessage(msg);
496 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ area.scrollTop = area.scrollHeight;
497 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } catch(_) {}
498 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ };
499 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ es.onerror = () => { status.textContent = '○ reconnecting…'; status.style.color = '#8b949e'; };
500 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
501 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
502 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ function appendMessage(msg) {
503 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const area = document.getElementById('chat-messages');
504 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const t = new Date(msg.at);
505 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const timeStr = t.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'});
506 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const isBridge = msg.nick === 'bridge';
507 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const row = document.createElement('div');
508 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ row.className = 'msg-row';
509 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ row.innerHTML = `<span class="msg-time">${esc(timeStr)}</span>`
510 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ + `<span class="msg-nick${isBridge ? ' bridge-nick' : ''}">${esc(msg.nick)}</span>`
511 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ + `<span class="msg-text">${esc(msg.text)}</span>`;
512 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ area.appendChild(row);
513 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
514 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
515 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ async function sendChatMessage() {
516 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (!chatChannel) return;
517 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const input = document.getElementById('chat-text-input');
518 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const nick = document.getElementById('chat-nick-input').value.trim() || 'web';
519 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const text = input.value.trim();
520 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (!text) return;
521 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ input.disabled = true;
522 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-send-btn').disabled = true;
523 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try {
524 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ const slug = chatChannel.replace(/^#/, '');
525 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ await api('POST', `/v1/channels/${slug}/messages`, { text, nick });
526 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ input.value = '';
527 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } catch(e) {
528 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ alert('Send failed: ' + e.message);
529 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ } finally {
530 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ input.disabled = false;
531 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-send-btn').disabled = false;
532 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ input.focus();
533 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
534 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
535 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
536 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ document.getElementById('chat-text-input').addEventListener('keydown', e => {
537 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if (e.key === 'Enter') sendChatMessage();
538 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ });
380 539 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
381 540 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// --- init ---
382 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- function loadAll() { loadStatus(); loadAgents(); }
541 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ function loadAll() { loadStatus(); loadAgents(); loadChannels(); }
383 542 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
updateTokenDisplay();
384 543 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
loadAll();
385 544 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
setInterval(loadStatus, 15000);
386 545 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</script>
387 546 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</body>
388 547 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
</html>
389 548 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
390 549 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
A DDED internal/bots/bridge/bridge.go