| | @@ -0,0 +1,143 @@ |
| 1 | +<!DOCTYPE html>
|
| 2 | +<html lang=-pane { display:none; flex:1; min-height:0; overflow-y:auto; }
|
| 3 | +.tab-pane.active { display:flex; flex-direction:col umn; }
|
| 4 | +.pane-scroll { flex margin:0 auto; padding:24px; dis 'Cascadia Code', direction:column; gap:20px; }
|
| 5 | +
|
| 6 | +/* cards #0d1117; color: #e6edf3; min-height: 100vh; }
|
| 7 | + header { background: ont-size:13px; cbottom: 0; box-shadow:0 0 6padding: 12px 24px; display: { background:#db61 center; gap: 16px; }
|
| 8 | + header h1 16px; color: O@BF,2R: 0.05em; }
|
| 9 | + header .tagline { font-size: 12px; color: #8b949e; }
|
| 10 | + header .spacer { flex: 1; }
|
| 11 | + .token-badge { font-size: 12px; color: #8b949e; display: I@3Iz,18: center; gap: 8px; }
|
| 12 | + .token-badge code { background: #21262d; border: X@36F,14: 4px; padding: 2px 8px; color: #a5d6ff; max-width: 200px; overflow: M@2e0,16: ellipsis; white-space: nowrap; }
|
| 13 | + button { cursor: pointer; border: X@36F,F: 6px; padding: K@1Ci,1e: 13px; font-family: inherit; background: #21262d; color: #e6edf3; transition: background 0.1s; }
|
| 14 | + buttonK@Vk,E: #30363d; }
|
| 15 | + S@1G0,1: M@1GS,e: #1f6feb; color: #fff; }
|
| 16 | + button.primaryK@Vk,1U: #388bfd; }
|
| 17 | + button.danger { background: #21262d; border-color: #f85149; color: #f85149; }
|
| 18 | + J@1Iy,2J: { background: #3d1f1e; }
|
| 19 | + button.small { padding: 3px 8px; font-size: 12px; }
|
| 20 | + main { max-width: 960px; margin: 0 auto; padding: 24px; display: L@2Wk,y: column; gap: 24px; }
|
| 21 | + .card { background: #161b22; border: X@36F,p: 8px; overflow: hidden; }
|
| 22 | + .card-header { padding: P@Td,1: J@36F,9:display: I@3Iz,N: center; gap: 8px; }
|
| 23 | + S@WE,E: 14px; color: L@3KV,~: 600; }
|
| 24 | + .card-header .badge { background: #1f6feb22; border: H@NsG,I:44; c; }
|
| 25 | + L@aD,Z: 16px; }
|
| 26 | + .status-grid { display: S@dE,1R: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
|
| 27 | + .stat { background: #0d1117; border: J@y~,1J:border-radius: 6px; padding: 12px 16px; }
|
| 28 | + .stat .label { font-size: 11px; color: O@3He,1: Q@20l,2: 0L@3IR,1q: 4px; }
|
| 29 | + .stat .value { font-size: 20px; color: #58a6ff; font-weight: 600; }
|
| 30 | + .stat .sub { font-size: 11px; color: K@1Tz,~: 2px; }
|
| 31 | + .dot { display: inline-block; width: 8px; height: 8pxG@36W,T: 50%; margin-right: 6px; }
|
| 32 | + O@sy,i: #3fformval { color: K@1fC: 600; }
|
| 33 | + .card-header .badgpx; padding:H@1Cl,f: 12px; }
|
| 34 | + .cred-box .cred-row { display: I@3Iz,J: baseline; gap: 8pxG@3IW,K: 6px; }
|
| 35 | + .cred-box a@1dZ,a: 0; }
|
| 36 | + .cred-box .cred-key { color: J@1eV,d: 90px; }
|
| 37 | + .cred-box .cred-val { color: K@1fC,1: G@1fW,2V: 1; }
|
| 38 | + .cred-box .copy-btn { flex-shrink: 0; }
|
| 39 | + .modal-overlay { display: none; position: fixed; inset: 0; background: #0d111788; z-index: 100; align-items: O@2lU,D: center; }
|
| 40 | + U@39V,m: flex; }
|
| 41 | + .modal { background: #161b22; border: X@36F,u: 10px; padding: 24px; width: 480px; max-width: 95vw; }
|
| 42 | + M@3By,5: 15pxG@3IW,B: 16px; }
|
| 43 | + Q@3Ck,7: flex; G@Bw0,1B: flex-end; gap: 8px; margin-top: 16px; }
|
| 44 | +</style>
|
| 45 | +</head>
|
| 46 | +<body>
|
| 47 | +
|
| 48 | +<header>
|
| 49 | +S@3sg,L:<span class="tagline"d@3tE,S@Bj0,1U:<div class="token-badge">
|
| 50 | + <span>token:</span>
|
| 51 | + <code id="token-display">not set</code>
|
| 52 | +L@6Fl,Z:all" onclick="openTokenModal()">setJ@5JF,I:</header>
|
| 53 | +
|
| 54 | +<main>
|
| 55 | +19@487,J@Jx0,B:ℹ</span>
|
| 56 | +f@49n,8:. It wasJ@4AW,S:when scuttlebot started:<br>y@4A~,3:...H@4B~,Q:</div>
|
| 57 | +
|
| 58 | + <!-- Status -->
|
| 59 | +J@AdG,M@7cG,8:-header"L@5uz,Y@4F1,N:
|
| 60 | + <h2>status</h2>
|
| 61 | +G@5A0,G@8yG,4:bodyL@6AG,M:status-grid" id="statuH@4fk,V@4Q0,3:abeT@4LW,2:ueg@4Ly,V@4Q0,3:abeU@4NA,2:ueg@4Nd,V@4Q0,3:abeU@4Oq,2:ueg@4PJ,V@4Q0,3:abeV@4QW,2:ueK@GkW,1Q@4RJ,I@Rhk,12@4T3,Z:</div>
|
| 62 | + </div>
|
| 63 | +
|
| 64 | + <!-- Agents -->
|
| 65 | +J@AdG,M@7cG,U:-header">
|
| 66 | + <h2>agents</h2L@5uz,N@5PG,8:>0</spanK@Fol,J@5Ol,J@FAG,3:allo@5G0,Q@3TG,W@8tZ,9:<div id="G@FCG,2:">I@AXl,U: </div>
|
| 67 | +
|
| 68 | + <!-- Register -->
|
| 69 | +J@AdG,M@7cG,W:-header"><h2>register agent</h2>O@5m0,H@4uW,11@B1R,X@B2Q,3: <H@A8W,U@BUS,H@7l0,r@B3t,J@ACG,S@AMl,U@B5f,a@B66,H@9N0,n:operator">operator — human, +o + full permissionP@9I~,M@B6g,U:>worker — gets +v in channelP@9I~,G@9Fl,U@B7z,I:gets +o in channelP@9I~,G@9Fl,R:bserver">observer — read-O@B9V,N@MGS,X@ARl,J@B2~,P@Brv,P@MGl,3:regP@Bsk,P:fleet, #ops, #project.fooP@6CW,L@Bt~,W@BDS,H@A3W,1:<G@ARW,Y@BEO,P@MGl,s@BFH,O@54W,L@Bt~,Q@BH6,H:is allowed to senG@7iG,G@4f0,w@BI3,M@91G,T@ATj,L@5aG,r@BLG,H@SMl,g:form>
|
| 70 | + </div>
|
| 71 | + </div>
|
| 72 | +
|
| 73 | + <!-- Chat -->
|
| 74 | +P@AdG,3:hatX@4rg,H@6ml,R:header">
|
| 75 | + <h2>chat</h2L@5uz,T:badge" id="chat-channel-badgeN@4rl,6:</spanK@Fol,J@5Ol,7: <spanW@5vW,M@QtW,L@45B,M@GlS,Q:display:flex;height:440px"H@Ba0,b:chat-channel-list" style="width:140px;V@1wh,G@2XW,S:flex-shrink:0;padding:8px 0;I@R8W,G:direction:columnG@BCl,J:style="padding:6px K@8fi,P@oW,L:letter-spacing:0.06emG@8HW,N@7Kg,S:<div style="padding:6px 8px;P@21W,7:21262d;H@M9F,3:4px_@5Y~,I@HPj,W@5Zo,6:flex:1G@Gm0,D:padding:3px 6M@3i0,T@AVj,3:allP@5lN,8: style="R@5sw,3:1pxH@5lk,Z@5bW,K@ATV,M@4YG,I:flex:1;min-width:0G@BCl,I:id="chat-messages"U@5Sh,1:;I@TW,I@R8W,H@1xV,7:gap:2pxP@6CW,z:empty" id="chat-placeholder">select a channel to view messagesZ@4f0,G@8uW,6:0px 14P@3cf,7:30363d;H@M9F,X@Ntl,P@MGl,9:chat-nickL@3h0,g:your nick" style="width:110px;flex-shrink:0K@9m0,Z@5Z0,1Q@5zf,T@600,3:allY@60T,G:ChatMessage()">sp@AXE,Y: </div>
|
| 76 | +</main>
|
| 77 | +
|
| 78 | +<!-- Token modalH@AxV,U:modal-overlay" id="token-modalH@68l,X:modal">
|
| 79 | + <h3>set API token</h3R@Bkz,1:3G@O0G,X:;margin-bottom:14px">The token isJ@4AW,R:when scuttlebot starts:<br>y@4A~,l:<value></code></p>
|
| 80 | + <label>token</labelT@5MG,4:tokeS@3g~,A:token hereM@9m0,G:pellcheck="falseJ@5D~,l:btn-row">
|
| 81 | + <button onclick="closeTokenModalQ@BKh,F:<button class="M@6zW,5:TokenM@6zz,P@67V,V:script>
|
| 82 | +// --- token managementP@C9h,1:{V@Ig0,9:cuttlebotR@CAe,D:setToken(t) {Q@Cfk,1F:cuttlebot_token', t);
|
| 83 | + updateTokenDisplay();
|
| 84 | +}
|
| 85 | +function updateTokenDisplay() {X@CHG,U@Hul,V:token-display');
|
| 86 | + const bannerS@CeW,1Y:no-token-banner');
|
| 87 | + if (t) {
|
| 88 | + el.textContent = t.slice(0, 8) + '…' + t.slice(-4);
|
| 89 | + bannerR@FjW,F:} else {
|
| 90 | + elG@Rcl,K:not set';
|
| 91 | + bannerI@RQ0,_:flex';
|
| 92 | + }
|
| 93 | +}
|
| 94 | +function openTokenModalW@GGk,V:token-input').value = getToken(U@Ojl,B:token-modalS@FTW,f@Faj,O:token-input').focus(), 5K@Fbl,E:TokenModal() {Q@Sx0,B:token-modalS@Fe0,K: }
|
| 95 | +function saveTokem@CeH,S@H3~,i:v) { setToken(v); closeTokenModal(); loadAll(W@H9x,B:token-modalL@HAh,16:click', function(e) {
|
| 96 | + if (e.target === this) closeTokenModal();
|
| 97 | +});
|
| 98 | +R@Jsk,O:keydown', function(e) {
|
| 99 | +H@J5k,S:Escape') closeTokenModal();
|
| 100 | +15@Cnz,5:tokenN@CHQ,h@Cp3,_: 'Bearer ' + token, 'Content-Type': 2G@CqK,3I@Cvm,1I@Cz2,H:
|
| 101 | + const orig =G@Q5l,1:;Q@Q6l,1:�h@Q7I,J:orig; }, 1200);
|
| 102 | + }18@DZ4,1:sP@HQT,7:status'd@DfG,o@Dbh,3: + 2Q@Dc6: <divJ@6oG,1:8P@8fk,W@FwB,m:now — it will not be shown again.</div>`
|
| 103 | + );
|
| 104 | +}u@Jv_,w:const icons = { info: 'ℹ', error: '✕', success: '✓' }O@MTF,_@Jwm,5:icons1D@Jxw,4: || 3W@JzA,9: + ' ' + L@K1k,4:);
|
| 105 | +}d@HBq,5:;
|
| 106 | +let2M@HCU,7:nelListP@HEw,R@Sal,9:chat-cardK@ONW,1:'L@Q6W,q:// bridge disabled or error — keep chat card hiddenN@N9x,B:ChannelListt@HFz,1a:t-channel-list');
|
| 107 | + // Remove old channel items (keep header div and join input div)
|
| 108 | + Array.from(lisL@C3j,c:chan-item')).forEach(el => el.remove())1r@HHL,V: === chatChannel ? ' active' : 1f@HJ_,33@HLD,1: 23@HOG,J:await loadChannels(d@HRe,4:
|
| 109 | + M@H9S,M: + e.message);
|
| 110 | + }
|
| 111 | +}
|
| 112 | +
|
| 113 | +P@TE0,L@HPj,Y@HAk,1:
|
| 114 | +H@J5k,N:Enter') joinChannel();
|
| 115 | +f@HUg,p@HWW,B:annel-badgeH@Dxl,b@HWj,B:annel-badgeK@ONW,U@OGG,1Z@HY9,4:{
|
| 116 | + N@MWB,_:active', el.textContent === ch);
|
| 117 | + }n@H_U,v:essages');
|
| 118 | + // clear previous messages (keep placeholder)k@HaM,1: 14@Hb5,4: '')_@HQD,p@Hcl,T: || []).forEach(appendMessage2F@He9,J: '');
|
| 119 | + const url =N@Hsk,k@HhG,V@HgR,3:urlR@Hh~,6:statusS@CeW,Y@Hiw,G:= () => { statusG@Rcl,H:● live'; statusQ@Rdx,1e:};
|
| 120 | + es.onmessage = (e) => {
|
| 121 | + try {
|
| 122 | + const msg = JSON.parse(e.data);
|
| 123 | + appendMessage(msg);
|
| 124 | + f@HeC,n: } catch(_) {}
|
| 125 | + };
|
| 126 | + es.onerror = () => { statusG@Rcl,v:○ reconnecting…'; status.style.color = '#8b949e'; };
|
| 127 | +}I@I7~,A:essage(msgn@I8Y,x:essages');
|
| 128 | + const t = new Date(msg.at);
|
| 129 | + const timeStr = tN@K1l,j@IHk,X:isBridge = msg.nick === 'bridge';19@IIy,J:;
|
| 130 | + row.innerHTML =O@IK~,O@IM9,6:
|
| 131 | + +J@IK~,15:nick${isBridge ? ' bridge-nick' : ''}">${esc(msg.nick)}</span>`
|
| 132 | + +K@IK~,G:ext">${esc(msg.td@IOV,1:}J@HUj,D:ndChatMessage1f@Irx,W@HiW,4:nickL@H3z,O@IuU,7:= inputM@CfF,2L@IvK,5: '');N@NPl,V@Hcl,s@IyZ,4:
|
| 133 | + M@IzQ,X@N3w,5:inputL@OIl,R@Sal,O@I~v,_: = false;
|
| 134 | + input.focus();
|
| 135 | + }
|
| 136 | +}
|
| 137 | +
|
| 138 | +U@JUl,4:textg@HTT,1:
|
| 139 | +H@J5k,U:Enter') sendChatMessage();
|
| 140 | +});17@TNA,1C:Channels(); }
|
| 141 | +updateTokenDisplay();
|
| 142 | +loadAll();
|
| 143 | +setInterval(loadStatus, 15000T@TOx,2CRpaK; |