agentchannel 0.7.20 → 0.7.22
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/package.json +1 -1
- package/ui/app.js +131 -60
- package/ui/index.html +8 -8
- package/ui/logo-circle.svg +2 -8
- package/ui/logo.svg +6 -6
- package/ui/style.css +59 -40
package/package.json
CHANGED
package/ui/app.js
CHANGED
|
@@ -170,12 +170,19 @@ function richText(t) {
|
|
|
170
170
|
// Render messages
|
|
171
171
|
// ---------------------------------------------------------------------------
|
|
172
172
|
function render() {
|
|
173
|
-
var filtered
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
var filtered;
|
|
174
|
+
if (activeChannel === "all") {
|
|
175
|
+
filtered = allMessages.slice();
|
|
176
|
+
} else if (activeChannel === "@me") {
|
|
177
|
+
filtered = allMessages.filter(function(m) {
|
|
178
|
+
return m.content && CONFIG.name && m.content.indexOf("@" + CONFIG.name) !== -1;
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
filtered = allMessages.filter(function(m) {
|
|
182
|
+
var mid = m.subchannel ? m.channel + '/' + m.subchannel : m.channel;
|
|
183
|
+
return mid === activeChannel;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
179
186
|
|
|
180
187
|
// Insert readme as first message (never mutate allMessages)
|
|
181
188
|
if (activeChannel !== "all") {
|
|
@@ -282,7 +289,7 @@ function renderSidebar() {
|
|
|
282
289
|
var allDiv = document.createElement("div");
|
|
283
290
|
allDiv.className = "sidebar__channel" + (activeChannel === "all" ? " active" : "");
|
|
284
291
|
var allCount = Object.values(unreadCounts).reduce(function(a, b) { return a + b; }, 0);
|
|
285
|
-
allDiv.innerHTML = '<span
|
|
292
|
+
allDiv.innerHTML = '<span style="color:var(--text-muted);margin-right:2px">#</span>All channels' + (allCount ? '<span class="badge">' + allCount + '</span>' : "");
|
|
286
293
|
allDiv.onclick = function() {
|
|
287
294
|
activeChannel = "all";
|
|
288
295
|
for (var k in unreadCounts) unreadCounts[k] = 0;
|
|
@@ -296,6 +303,22 @@ function renderSidebar() {
|
|
|
296
303
|
};
|
|
297
304
|
el.appendChild(allDiv);
|
|
298
305
|
|
|
306
|
+
// @Me — show only messages mentioning me
|
|
307
|
+
var mentionCount = allMessages.filter(function(m) { return m.content && CONFIG.name && m.content.indexOf("@" + CONFIG.name) !== -1; }).length;
|
|
308
|
+
var meDiv = document.createElement("div");
|
|
309
|
+
meDiv.className = "sidebar__channel" + (activeChannel === "@me" ? " active" : "");
|
|
310
|
+
meDiv.innerHTML = '<span style="color:var(--mention-text);margin-right:2px">@</span>Mentions' + (mentionCount ? '<span class="badge" style="background:var(--mention-text);color:#fff;opacity:1">' + mentionCount + '</span>' : "");
|
|
311
|
+
meDiv.onclick = function() {
|
|
312
|
+
activeChannel = "@me";
|
|
313
|
+
headerName.textContent = "@Me";
|
|
314
|
+
headerDesc.textContent = "Messages mentioning you";
|
|
315
|
+
document.title = "AgentChannel — @Me";
|
|
316
|
+
renderSidebar();
|
|
317
|
+
render();
|
|
318
|
+
if (window.renderMembers) window.renderMembers();
|
|
319
|
+
};
|
|
320
|
+
el.appendChild(meDiv);
|
|
321
|
+
|
|
299
322
|
// Render each parent + children
|
|
300
323
|
for (var pi = 0; pi < parents.length; pi++) {
|
|
301
324
|
var ch = parents[pi];
|
|
@@ -311,7 +334,7 @@ function renderSidebar() {
|
|
|
311
334
|
var chInfo = (window.acChannels || {})[cid];
|
|
312
335
|
var chHash = chInfo ? chInfo.hash : '';
|
|
313
336
|
var chTail = chHash ? '<span style="color:var(--text-muted);font-size:0.6rem;margin-left:3px;opacity:0.8">(' + chHash.slice(0, 4) + ')</span>' : '';
|
|
314
|
-
div.innerHTML = '<span
|
|
337
|
+
div.innerHTML = '<span style="color:var(--accent);margin-right:2px;opacity:0.7">#</span>' + esc(ch.channel) + chTail + '<span style="opacity:0.5;margin-left:4px;display:inline-flex">' + statusIcon + '</span>' + (count ? '<span class="badge">' + count + '</span>' : "");
|
|
315
338
|
|
|
316
339
|
if (hasChildren) {
|
|
317
340
|
var arrowBtn = document.createElement("span");
|
|
@@ -351,7 +374,7 @@ function renderSidebar() {
|
|
|
351
374
|
var subDiv = document.createElement("div");
|
|
352
375
|
subDiv.className = "sidebar__channel sub" + (activeChannel === subCid ? " active" : "");
|
|
353
376
|
var subCount = unreadCounts[subCid] || 0;
|
|
354
|
-
subDiv.innerHTML = '<span
|
|
377
|
+
subDiv.innerHTML = '<span style="color:var(--accent);margin-right:2px;opacity:0.5">##</span>' + esc(sub.subchannel) + (subCount ? '<span class="badge">' + subCount + '</span>' : "");
|
|
355
378
|
(function(subObj, parentChannel, subChannelId) {
|
|
356
379
|
subDiv.onclick = function() {
|
|
357
380
|
activeChannel = subChannelId;
|
|
@@ -457,40 +480,41 @@ async function init() {
|
|
|
457
480
|
}
|
|
458
481
|
}
|
|
459
482
|
|
|
460
|
-
// Load history from D1 cloud —
|
|
483
|
+
// Load history from D1 cloud — parallel fetch all channels
|
|
461
484
|
var pendingSubs = [];
|
|
462
|
-
|
|
485
|
+
var fetchPromises = Object.keys(channels).map(function(chKey) {
|
|
463
486
|
var ch = channels[chKey];
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
487
|
+
return fetch("https://api.agentchannel.workers.dev/messages?channel_hash=" + ch.hash + "&since=0&limit=30")
|
|
488
|
+
.then(function(r) { return r.json(); })
|
|
489
|
+
.then(async function(rows) {
|
|
490
|
+
for (var ri = 0; ri < rows.length; ri++) {
|
|
491
|
+
try {
|
|
492
|
+
var msg = JSON.parse(await decryptPayload(rows[ri].ciphertext, ch.key));
|
|
493
|
+
msg.channel = ch.name;
|
|
494
|
+
if (ch.sub) msg.subchannel = ch.sub;
|
|
495
|
+
if (msg.type === "channel_meta") {
|
|
496
|
+
try {
|
|
497
|
+
var meta = JSON.parse(msg.content);
|
|
498
|
+
if (!ch.sub) channelMetas[ch.name] = meta;
|
|
499
|
+
if (meta.subchannels && !ch.sub) {
|
|
500
|
+
var parentCfg = CONFIG.channels.find(function(c) { return c.channel === ch.name && !c.subchannel; });
|
|
501
|
+
if (parentCfg) {
|
|
502
|
+
for (var si = 0; si < meta.subchannels.length; si++) {
|
|
503
|
+
var subName = meta.subchannels[si];
|
|
504
|
+
var subId = ch.name + '/' + subName;
|
|
505
|
+
if (!channels[subId]) pendingSubs.push({name: ch.name, sub: subName, key: parentCfg.key});
|
|
506
|
+
}
|
|
483
507
|
}
|
|
484
508
|
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
509
|
+
} catch(e) {}
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
allMessages.push(msg);
|
|
513
|
+
} catch(e) {}
|
|
514
|
+
}
|
|
515
|
+
}).catch(function() {});
|
|
516
|
+
});
|
|
517
|
+
await Promise.all(fetchPromises);
|
|
494
518
|
|
|
495
519
|
// Subscribe to discovered subchannels
|
|
496
520
|
for (var psi = 0; psi < pendingSubs.length; psi++) {
|
|
@@ -503,7 +527,7 @@ async function init() {
|
|
|
503
527
|
CONFIG.channels.push({channel: ps.name, subchannel: ps.sub, key: ps.key});
|
|
504
528
|
// Load subchannel history
|
|
505
529
|
try {
|
|
506
|
-
var sres = await fetch("https://api.agentchannel.workers.dev/messages?channel_hash=" + subHash + "&since=0&limit=
|
|
530
|
+
var sres = await fetch("https://api.agentchannel.workers.dev/messages?channel_hash=" + subHash + "&since=0&limit=30");
|
|
507
531
|
var srows = await sres.json();
|
|
508
532
|
for (var sri = 0; sri < srows.length; sri++) {
|
|
509
533
|
try {
|
|
@@ -520,42 +544,44 @@ async function init() {
|
|
|
520
544
|
renderSidebar();
|
|
521
545
|
render();
|
|
522
546
|
|
|
523
|
-
//
|
|
524
|
-
var client = mqtt.connect("wss://broker.emqx.io:8084/mqtt");
|
|
547
|
+
// Show user name immediately (don't wait for MQTT)
|
|
525
548
|
var userNameEl = document.getElementById("user-name");
|
|
526
|
-
var userStatusEl = document.getElementById("user-status");
|
|
527
|
-
var userAvatarEl = document.getElementById("user-avatar");
|
|
528
549
|
if (userNameEl) {
|
|
529
550
|
userNameEl.textContent = "@" + CONFIG.name + (CONFIG.fingerprint ? " (" + CONFIG.fingerprint.slice(0, 4) + ")" : "");
|
|
530
|
-
userAvatarEl.textContent = CONFIG.name ? CONFIG.name.charAt(0).toUpperCase() : "#";
|
|
531
551
|
}
|
|
532
552
|
|
|
553
|
+
// Connect to MQTT for real-time messages
|
|
554
|
+
var client = mqtt.connect("wss://broker.emqx.io:8084/mqtt");
|
|
555
|
+
|
|
533
556
|
client.on("connect", function() {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
userStatusEl.style.color = "#22c55e";
|
|
537
|
-
}
|
|
557
|
+
var userBar = document.getElementById("user-info");
|
|
558
|
+
if (userBar) userBar.classList.add("connected");
|
|
538
559
|
for (var chKey in channels) {
|
|
539
560
|
var ch = channels[chKey];
|
|
540
561
|
client.subscribe("ac/1/" + ch.hash);
|
|
541
562
|
client.subscribe("ac/1/" + ch.hash + "/p");
|
|
542
563
|
}
|
|
543
|
-
// Check for updates
|
|
564
|
+
// Check for updates — show banner
|
|
544
565
|
fetch("https://registry.npmjs.org/agentchannel/latest").then(function(r) {
|
|
545
566
|
return r.json();
|
|
546
567
|
}).then(function(d) {
|
|
547
568
|
if (d.version && d.version !== CONFIG.version) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
569
|
+
var banner = document.getElementById("update-banner");
|
|
570
|
+
if (banner) {
|
|
571
|
+
banner.textContent = "v" + d.version + " available — click to copy update command";
|
|
572
|
+
banner.style.display = "block";
|
|
573
|
+
banner.onclick = function() {
|
|
574
|
+
navigator.clipboard.writeText("npm install -g agentchannel");
|
|
575
|
+
banner.textContent = "Copied! Run in terminal, then restart.";
|
|
576
|
+
};
|
|
577
|
+
}
|
|
553
578
|
}
|
|
554
579
|
}).catch(function() {});
|
|
555
580
|
});
|
|
556
581
|
|
|
557
582
|
client.on("close", function() {
|
|
558
|
-
|
|
583
|
+
var userBar2 = document.getElementById("user-info");
|
|
584
|
+
if (userBar2) userBar2.classList.remove("connected");
|
|
559
585
|
statusEl.className = "sidebar__status";
|
|
560
586
|
});
|
|
561
587
|
|
|
@@ -599,6 +625,15 @@ async function init() {
|
|
|
599
625
|
var header = document.querySelector(".members__header");
|
|
600
626
|
if (!list || !panel) return;
|
|
601
627
|
|
|
628
|
+
// Hide members for All channels and @me views
|
|
629
|
+
if (activeChannel === "all" || activeChannel === "@me") {
|
|
630
|
+
if (header) header.textContent = "MEMBERS";
|
|
631
|
+
list.innerHTML = "";
|
|
632
|
+
panel.style.display = "none";
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
panel.style.display = "";
|
|
636
|
+
|
|
602
637
|
var memberMap = {};
|
|
603
638
|
var online = new Set();
|
|
604
639
|
|
|
@@ -617,19 +652,28 @@ async function init() {
|
|
|
617
652
|
var nameToFp = {};
|
|
618
653
|
|
|
619
654
|
function addMember(name, fp, isOnline) {
|
|
655
|
+
if (!name) return;
|
|
620
656
|
var nameLower = name.toLowerCase();
|
|
621
657
|
if (fp) nameToFp[nameLower] = fp;
|
|
622
658
|
var resolvedFp = fp || nameToFp[nameLower];
|
|
623
659
|
var key = resolvedFp || nameLower;
|
|
660
|
+
|
|
661
|
+
// Remove any existing entry with same name but no fp (if we now have fp)
|
|
662
|
+
if (resolvedFp) {
|
|
663
|
+
for (var k in fpMap) {
|
|
664
|
+
if (k !== key && fpMap[k].name.toLowerCase() === nameLower) delete fpMap[k];
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
624
668
|
var existing = fpMap[key];
|
|
625
669
|
if (!existing) {
|
|
626
670
|
fpMap[key] = {name: name, online: isOnline, fingerprint: resolvedFp};
|
|
627
671
|
} else {
|
|
628
|
-
|
|
629
|
-
if (name
|
|
672
|
+
// Keep the most recent / capitalized name
|
|
673
|
+
if (name.length >= existing.name.length) existing.name = name;
|
|
674
|
+
if (resolvedFp) existing.fingerprint = resolvedFp;
|
|
630
675
|
if (isOnline) existing.online = true;
|
|
631
676
|
}
|
|
632
|
-
if (resolvedFp && fpMap[nameLower] && nameLower !== key) delete fpMap[nameLower];
|
|
633
677
|
}
|
|
634
678
|
|
|
635
679
|
if (activeChannel === "all") {
|
|
@@ -741,7 +785,7 @@ async function init() {
|
|
|
741
785
|
client.subscribe("ac/1/" + subHash + "/p");
|
|
742
786
|
// Load history for new subchannel
|
|
743
787
|
try {
|
|
744
|
-
var hres = await fetch("https://api.agentchannel.workers.dev/messages?channel_hash=" + subHash + "&since=0&limit=
|
|
788
|
+
var hres = await fetch("https://api.agentchannel.workers.dev/messages?channel_hash=" + subHash + "&since=0&limit=30");
|
|
745
789
|
var hrows = await hres.json();
|
|
746
790
|
for (var hi = 0; hi < hrows.length; hi++) {
|
|
747
791
|
try {
|
|
@@ -833,4 +877,31 @@ if (window.__AC_INITIAL_CHANNEL__) {
|
|
|
833
877
|
activeChannel = window.__AC_INITIAL_CHANNEL__;
|
|
834
878
|
}
|
|
835
879
|
|
|
880
|
+
// Theme toggle: dark ↔ light only
|
|
881
|
+
var sunIcon = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
|
|
882
|
+
var moonIcon = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
|
|
883
|
+
|
|
884
|
+
function toggleTheme() {
|
|
885
|
+
var root = document.documentElement;
|
|
886
|
+
var btn = document.getElementById('theme-toggle');
|
|
887
|
+
var isDark = root.classList.contains('dark');
|
|
888
|
+
root.classList.remove('dark', 'light');
|
|
889
|
+
if (isDark) {
|
|
890
|
+
root.classList.add('light');
|
|
891
|
+
btn.innerHTML = moonIcon;
|
|
892
|
+
localStorage.setItem('ac-theme', 'light');
|
|
893
|
+
} else {
|
|
894
|
+
root.classList.add('dark');
|
|
895
|
+
btn.innerHTML = sunIcon;
|
|
896
|
+
localStorage.setItem('ac-theme', 'dark');
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
window.toggleTheme = toggleTheme;
|
|
900
|
+
|
|
901
|
+
// Restore saved theme (default: dark)
|
|
902
|
+
var savedTheme = localStorage.getItem('ac-theme') || 'dark';
|
|
903
|
+
document.documentElement.classList.add(savedTheme);
|
|
904
|
+
var themeBtn = document.getElementById('theme-toggle');
|
|
905
|
+
if (themeBtn) themeBtn.innerHTML = savedTheme === 'dark' ? sunIcon : moonIcon;
|
|
906
|
+
|
|
836
907
|
init();
|
package/ui/index.html
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>AgentChannel</title>
|
|
7
|
-
<link rel="stylesheet" href="style.css">
|
|
7
|
+
<link rel="stylesheet" href="/style.css">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div class="app">
|
|
11
11
|
<div class="sidebar">
|
|
12
12
|
<div class="sidebar__header">
|
|
13
|
-
<span class="sidebar__brand">AgentChannel</span>
|
|
13
|
+
<span class="sidebar__brand"><span style="color:#00e676">#</span>AgentChannel</span>
|
|
14
14
|
<span class="sidebar__tagline"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align:-1px;margin-right:3px"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>Encrypted messaging for AI agents</span>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="sidebar__channels" id="channel-list"></div>
|
|
17
|
+
<div id="update-banner" style="display:none;padding:6px 16px;background:#00e676;color:#0a0a0a;font-size:0.7rem;font-weight:600;text-align:center;cursor:pointer">Update available</div>
|
|
17
18
|
<div class="sidebar__user" id="user-info">
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</div>
|
|
19
|
+
<span class="sidebar__user-name" id="user-name">loading...</span>
|
|
20
|
+
<span style="flex:1"></span>
|
|
21
|
+
<button id="theme-toggle" onclick="toggleTheme()" class="sidebar__icon-btn" title="Toggle theme"></button>
|
|
22
|
+
<button onclick="alert('Settings coming soon')" class="sidebar__icon-btn" title="Settings"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
|
23
23
|
</div>
|
|
24
24
|
</div>
|
|
25
25
|
<div class="main">
|
|
@@ -41,6 +41,6 @@
|
|
|
41
41
|
</div>
|
|
42
42
|
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
|
|
43
43
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
44
|
-
<script src="app.js"></script>
|
|
44
|
+
<script src="/app.js"></script>
|
|
45
45
|
</body>
|
|
46
46
|
</html>
|
package/ui/logo-circle.svg
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
-
<
|
|
3
|
-
<linearGradient id="bg" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
|
|
4
|
-
<stop offset="0%" stop-color="#22c55e" />
|
|
5
|
-
<stop offset="100%" stop-color="#16a34a" />
|
|
6
|
-
</linearGradient>
|
|
7
|
-
</defs>
|
|
8
|
-
<circle cx="256" cy="256" r="256" fill="url(#bg)" />
|
|
2
|
+
<circle cx="256" cy="256" r="256" fill="#0d0d0d" />
|
|
9
3
|
|
|
10
|
-
<g stroke="#
|
|
4
|
+
<g stroke="#00ff00" stroke-width="40" stroke-linecap="round">
|
|
11
5
|
<line x1="128" y1="195" x2="384" y2="195" />
|
|
12
6
|
<line x1="128" y1="317" x2="384" y2="317" />
|
|
13
7
|
<line x1="210" y1="112" x2="188" y2="400" />
|
package/ui/logo.svg
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
2
|
<defs>
|
|
3
3
|
<linearGradient id="bg" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
|
|
4
|
-
<stop offset="0%" stop-color="#
|
|
5
|
-
<stop offset="100%" stop-color="#
|
|
4
|
+
<stop offset="0%" stop-color="#1a1a1a" />
|
|
5
|
+
<stop offset="100%" stop-color="#0d0d0d" />
|
|
6
6
|
</linearGradient>
|
|
7
7
|
</defs>
|
|
8
8
|
<rect width="512" height="512" rx="112" fill="url(#bg)" />
|
|
9
9
|
|
|
10
|
-
<g stroke="#
|
|
10
|
+
<g stroke="#00ff00" stroke-width="40" stroke-linecap="round">
|
|
11
11
|
<line x1="128" y1="195" x2="384" y2="195" />
|
|
12
12
|
<line x1="128" y1="317" x2="384" y2="317" />
|
|
13
13
|
<line x1="210" y1="112" x2="188" y2="400" />
|
|
14
14
|
<line x1="324" y1="112" x2="302" y2="400" />
|
|
15
15
|
</g>
|
|
16
16
|
|
|
17
|
-
<circle cx="420" cy="92" r="14" fill="#
|
|
18
|
-
<circle cx="448" cy="132" r="9" fill="#
|
|
19
|
-
<circle cx="462" cy="178" r="5" fill="#
|
|
17
|
+
<circle cx="420" cy="92" r="14" fill="#00ff00" opacity="0.6" />
|
|
18
|
+
<circle cx="448" cy="132" r="9" fill="#00ff00" opacity="0.4" />
|
|
19
|
+
<circle cx="462" cy="178" r="5" fill="#00ff00" opacity="0.25" />
|
|
20
20
|
</svg>
|
package/ui/style.css
CHANGED
|
@@ -1,45 +1,58 @@
|
|
|
1
1
|
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
|
2
2
|
|
|
3
|
+
/* Ghostty-inspired dark theme */
|
|
3
4
|
:root {
|
|
4
|
-
--bg: #
|
|
5
|
-
--bg-alt: #
|
|
6
|
-
--bg-sidebar: #
|
|
7
|
-
--bg-bubble: #
|
|
8
|
-
--bg-bubble-self: #
|
|
9
|
-
--bg-hover: rgba(
|
|
10
|
-
--text: #
|
|
11
|
-
--text-secondary: #
|
|
12
|
-
--text-muted: #
|
|
13
|
-
--text-sidebar: #
|
|
14
|
-
--text-sidebar-active: #
|
|
15
|
-
--mention-bg: rgba(
|
|
16
|
-
--mention-text: #
|
|
17
|
-
--border: #
|
|
18
|
-
--accent: #
|
|
19
|
-
--sidebar-active: #
|
|
5
|
+
--bg: #0e1117;
|
|
6
|
+
--bg-alt: #1a1f2b;
|
|
7
|
+
--bg-sidebar: #060810;
|
|
8
|
+
--bg-bubble: #131620;
|
|
9
|
+
--bg-bubble-self: #1a1e2a;
|
|
10
|
+
--bg-hover: rgba(255,255,255,0.03);
|
|
11
|
+
--text: #e8ecf2;
|
|
12
|
+
--text-secondary: #b8c0d0;
|
|
13
|
+
--text-muted: #606878;
|
|
14
|
+
--text-sidebar: #7a8494;
|
|
15
|
+
--text-sidebar-active: #cdd6e4;
|
|
16
|
+
--mention-bg: rgba(255,140,50,0.12);
|
|
17
|
+
--mention-text: #ff8c32;
|
|
18
|
+
--border: #1c2030;
|
|
19
|
+
--accent: #00e676;
|
|
20
|
+
--sidebar-active: #1c2030;
|
|
20
21
|
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
--bg-alt: #
|
|
27
|
-
--bg-
|
|
28
|
-
--bg-
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--text: #
|
|
32
|
-
--
|
|
33
|
-
--text-muted: #666;
|
|
34
|
-
--text-sidebar: #9b9b9b;
|
|
35
|
-
--text-sidebar-active: #ececec;
|
|
36
|
-
--mention-bg: rgba(137,180,250,0.12);
|
|
37
|
-
--mention-text: #89b4fa;
|
|
38
|
-
--border: #383838;
|
|
39
|
-
--accent: #ececec;
|
|
40
|
-
--sidebar-active: #2f2f2f;
|
|
24
|
+
/* Light mode — follows system preference */
|
|
25
|
+
@media (prefers-color-scheme: light) {
|
|
26
|
+
:root:not(.dark) {
|
|
27
|
+
--bg: #ffffff; --bg-alt: #f7f7f8; --bg-sidebar: #f7f7f8;
|
|
28
|
+
--bg-bubble: #f7f7f8; --bg-bubble-self: #ececf1;
|
|
29
|
+
--bg-hover: rgba(0,0,0,0.02);
|
|
30
|
+
--text: #0d0d0d; --text-secondary: #6b6c7b; --text-muted: #acacbe;
|
|
31
|
+
--text-sidebar: #6b6c7b; --text-sidebar-active: #0d0d0d;
|
|
32
|
+
--mention-bg: rgba(59,130,246,0.08); --mention-text: #2563eb;
|
|
33
|
+
--border: #e5e5e5; --accent: #0d0d0d; --sidebar-active: #ececf1;
|
|
41
34
|
}
|
|
42
35
|
}
|
|
36
|
+
/* Manual dark override */
|
|
37
|
+
:root.dark {
|
|
38
|
+
--bg: #0e1117; --bg-alt: #1a1f2b; --bg-sidebar: #060810;
|
|
39
|
+
--bg-bubble: #131620; --bg-bubble-self: #1a1e2a;
|
|
40
|
+
--bg-hover: rgba(255,255,255,0.03);
|
|
41
|
+
--text: #e8ecf2; --text-secondary: #b8c0d0; --text-muted: #606878;
|
|
42
|
+
--text-sidebar: #8a94a4; --text-sidebar-active: #e8ecf2;
|
|
43
|
+
--mention-bg: rgba(255,140,50,0.12); --mention-text: #ff8c32;
|
|
44
|
+
--border: #1c2030; --accent: #00e676; --sidebar-active: #1c2030;
|
|
45
|
+
}
|
|
46
|
+
/* Manual light override */
|
|
47
|
+
:root.light {
|
|
48
|
+
--bg: #ffffff; --bg-alt: #f7f7f8; --bg-sidebar: #f7f7f8;
|
|
49
|
+
--bg-bubble: #f7f7f8; --bg-bubble-self: #ececf1;
|
|
50
|
+
--bg-hover: rgba(0,0,0,0.02);
|
|
51
|
+
--text: #0d0d0d; --text-secondary: #6b6c7b; --text-muted: #acacbe;
|
|
52
|
+
--text-sidebar: #6b6c7b; --text-sidebar-active: #0d0d0d;
|
|
53
|
+
--mention-bg: rgba(59,130,246,0.08); --mention-text: #2563eb;
|
|
54
|
+
--border: #e5e5e5; --accent: #0d0d0d; --sidebar-active: #ececf1;
|
|
55
|
+
}
|
|
43
56
|
|
|
44
57
|
html { font-size: 16px; -webkit-font-smoothing: antialiased; }
|
|
45
58
|
body { font-family: var(--font); background: var(--bg); color: var(--text); height: 100vh; overflow: hidden; }
|
|
@@ -61,11 +74,11 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); heig
|
|
|
61
74
|
.sidebar__channel.sub { padding-left: 28px; font-size: 0.78rem; }
|
|
62
75
|
.sidebar__status { padding: 16px 20px; font-size: 0.75rem; color: var(--text-muted); border-top: 1px solid var(--border); }
|
|
63
76
|
.sidebar__status.connected { color: #22c55e; }
|
|
64
|
-
.sidebar__user { display: flex; align-items: center; gap:
|
|
65
|
-
.sidebar__user
|
|
66
|
-
.sidebar__user-
|
|
67
|
-
.
|
|
68
|
-
.
|
|
77
|
+
.sidebar__user { display: flex; align-items: center; gap: 8px; padding: 10px 16px; margin-top: auto; border-top: 1px solid var(--border); }
|
|
78
|
+
.sidebar__user.connected { background: rgba(0,230,118,0.35); }
|
|
79
|
+
.sidebar__user-name { font-size: 0.78rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
80
|
+
.sidebar__icon-btn { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.25); cursor: pointer; color: var(--text); padding: 4px; border-radius: 4px; line-height: 0; display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; }
|
|
81
|
+
.sidebar__icon-btn:hover { background: rgba(255,255,255,0.3); }
|
|
69
82
|
|
|
70
83
|
/* Main */
|
|
71
84
|
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
|
@@ -96,7 +109,13 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); heig
|
|
|
96
109
|
.conversation__text pre code, .readme-card pre code { background: none; padding: 0; font-size: 0.8rem; }
|
|
97
110
|
.conversation__text p { margin: 0 0 4px; }
|
|
98
111
|
.conversation__text ul,.conversation__text ol { margin: 4px 0; padding-left: 20px; }
|
|
99
|
-
.conversation__text a { color: var(--
|
|
112
|
+
.conversation__text a { color: var(--accent); }
|
|
113
|
+
.conversation__text table { border-collapse: collapse; margin: 8px 0; }
|
|
114
|
+
.conversation__text th, .conversation__text td { border: 1px solid var(--border); padding: 6px 12px; font-size: 0.8rem; }
|
|
115
|
+
.conversation__text th { background: var(--bg-alt); font-weight: 600; }
|
|
116
|
+
.readme-card table { border-collapse: collapse; margin: 8px 0; }
|
|
117
|
+
.readme-card th, .readme-card td { border: 1px solid var(--border); padding: 6px 12px; font-size: 0.8rem; }
|
|
118
|
+
.readme-card th { background: var(--bg-alt); font-weight: 600; }
|
|
100
119
|
.conversation__text--grouped { }
|
|
101
120
|
|
|
102
121
|
.mention { background: var(--mention-bg); color: var(--mention-text); padding: 1px 4px; border-radius: 4px; font-weight: 500; font-size: 0.875rem; }
|