@cotal-ai/web 0.0.0 → 0.11.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.
@@ -0,0 +1,207 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Cotal · graph</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500;600;700&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <style>
14
+ :root {
15
+ --bg: #131a26; --panel: #1b2330cc; --line: #38414f; --fg: #eef2f7;
16
+ --dim: #8b949e; --faint: #6e7681;
17
+ --chat: #58a6ff; --unicast: #d29922; --anycast: #3fb950;
18
+ --working: #3fb950; --waiting: #e3b341; --idle: #6e7681; --red: #f85149;
19
+ --font: "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
20
+ }
21
+ * { box-sizing: border-box; }
22
+ html, body { height: 100%; margin: 0; }
23
+ body {
24
+ font-family: var(--font); font-size: 13px; background: var(--bg); color: var(--fg);
25
+ overflow: hidden; -webkit-font-smoothing: antialiased;
26
+ }
27
+ #graph { position: fixed; inset: 0; display: block; cursor: grab; }
28
+ #graph.hover { cursor: pointer; }
29
+
30
+ /* floating glass surfaces */
31
+ .glass {
32
+ background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
33
+ backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
34
+ }
35
+
36
+ /* ── header (top, floating) ── */
37
+ header {
38
+ position: fixed; top: 14px; left: 14px; right: 14px; z-index: 10;
39
+ display: flex; align-items: center; gap: 16px; padding: 9px 14px;
40
+ }
41
+ .brand { display: flex; align-items: center; gap: 9px; }
42
+ .brand .mark { width: 9px; height: 9px; border-radius: 50%; background: var(--chat);
43
+ box-shadow: 0 0 10px var(--chat); }
44
+ .brand .title { font-size: 16px; font-weight: 700; }
45
+ .brand .space { font-size: 13px; color: var(--dim); }
46
+ .navlink {
47
+ font-size: 12px; font-weight: 600; color: var(--dim); text-decoration: none;
48
+ padding: 5px 11px; border-radius: 8px; border: 1px solid var(--line);
49
+ }
50
+ .navlink:hover { color: var(--fg); border-color: var(--chat); }
51
+ .pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px;
52
+ border-radius: 20px; background: #1f2d24; }
53
+ .pill .d { width: 7px; height: 7px; border-radius: 50%; background: var(--working); }
54
+ .pill .t { font-size: 11px; font-weight: 600; color: var(--working); }
55
+ .pill.down { background: #2a1717; } .pill.down .d { background: var(--red); }
56
+ .pill.down .t { color: var(--red); }
57
+ /* membership-freshness pill states */
58
+ .pill.stale { background: #2a2417; } .pill.stale .d { background: var(--waiting); }
59
+ .pill.stale .t { color: var(--waiting); }
60
+ .pill.off { background: #1f242c; } .pill.off .d { background: var(--faint); }
61
+ .pill.off .t { color: var(--dim); }
62
+
63
+ .ctrls { margin-left: auto; display: flex; align-items: center; gap: 7px; }
64
+ .grp { display: flex; align-items: center; gap: 4px; padding-right: 7px;
65
+ margin-right: 0; border-right: 1px solid var(--line); }
66
+ .grp:last-child { border-right: 0; padding-right: 0; }
67
+ .chip {
68
+ font-size: 11px; padding: 4px 9px; border-radius: 13px; cursor: pointer;
69
+ color: var(--faint); user-select: none; border: 1px solid transparent; white-space: nowrap;
70
+ }
71
+ .chip:hover { color: var(--fg); }
72
+ .chip.on { background: #ffffff10; border-color: var(--line); color: var(--fg); }
73
+ .chip.mode.chat.on { color: var(--chat); } .chip.mode.unicast.on { color: var(--unicast); }
74
+ .chip.mode.anycast.on { color: var(--anycast); }
75
+ .chip.pause.on { color: var(--waiting); border-color: var(--waiting); }
76
+ .chip.offline.on { color: var(--idle); border-color: var(--line); }
77
+
78
+ /* ── legend (bottom-left, collapsible) ── */
79
+ #legend { position: fixed; left: 14px; bottom: 14px; z-index: 10; padding: 0;
80
+ max-width: 200px; overflow: hidden; }
81
+ #legend .ltoggle {
82
+ display: flex; align-items: center; justify-content: space-between;
83
+ width: 100%; padding: 7px 12px; cursor: pointer; appearance: none;
84
+ font-size: 10px; font-weight: 600; letter-spacing: .6px; color: var(--faint);
85
+ text-transform: uppercase; background: transparent; border: 0;
86
+ text-align: left; font-family: inherit;
87
+ }
88
+ #legend .ltoggle:hover { color: var(--dim); }
89
+ #legend .ltoggle .chev { font-size: 9px; opacity: .5; transition: transform .2s ease; }
90
+ #legend.collapsed .ltoggle .chev { transform: rotate(-90deg); }
91
+ #legend .lbody { display: flex; flex-direction: column; gap: 5px; padding: 2px 12px 10px; }
92
+ #legend.collapsed .lbody { display: none; }
93
+ #legend .ls { font-size: 9px; font-weight: 600; letter-spacing: .5px; color: var(--faint);
94
+ text-transform: uppercase; opacity: .65; margin-top: 3px; }
95
+ #legend .ls:first-child { margin-top: 0; }
96
+ #legend .row { display: flex; align-items: center; gap: 9px; font-size: 11px; color: var(--dim); }
97
+ #legend .sw { width: 14px; height: 2px; border-radius: 1px; flex: none; }
98
+ #legend .sw-dot { width: 8px; height: 8px; border-radius: 50%; }
99
+ #legend .ring { width: 8px; height: 8px; border-radius: 50%; flex: none;
100
+ background: transparent; box-shadow: 0 0 0 1.5px currentColor inset; }
101
+
102
+ /* ── detail panel (right, floating) ── */
103
+ #detail {
104
+ position: fixed; top: 64px; right: 14px; bottom: 14px; width: 308px; z-index: 10;
105
+ padding: 16px 16px 18px; overflow-y: auto; display: none; flex-direction: column; gap: 14px;
106
+ scrollbar-width: thin; scrollbar-color: var(--line) transparent;
107
+ }
108
+ #detail::-webkit-scrollbar { width: 6px; }
109
+ #detail::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
110
+ #detail.open { display: flex; }
111
+ #detail .x { position: absolute; top: 10px; right: 10px; cursor: pointer; color: var(--faint);
112
+ font-size: 13px; width: 22px; height: 22px; display: flex; align-items: center;
113
+ justify-content: center; border-radius: 5px; line-height: 1; }
114
+ #detail .x:hover { color: var(--fg); background: #ffffff12; }
115
+ .d-kind { font-size: 10px; font-weight: 600; letter-spacing: .6px; color: var(--faint);
116
+ text-transform: uppercase; }
117
+ .d-who { font-size: 16px; font-weight: 700; line-height: 1.25; padding-right: 26px; }
118
+ .d-who .role { font-size: 11px; font-weight: 500; color: var(--faint); margin-left: 6px; }
119
+ .d-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px;
120
+ font-weight: 500; padding: 3px 10px; border-radius: 20px; background: #ffffff08;
121
+ color: var(--dim); width: fit-content; }
122
+ .d-status .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor;
123
+ box-shadow: 0 0 6px currentColor; }
124
+ .d-status.working { background: #16231a; color: var(--working); }
125
+ .d-status.waiting { background: #2a2117; color: var(--waiting); }
126
+ .d-status.idle { color: var(--dim); }
127
+ .d-status.offline { color: var(--faint); }
128
+ .d-section { display: flex; flex-direction: column; gap: 5px; }
129
+ .d-label { font-size: 9.5px; font-weight: 600; letter-spacing: .5px; color: var(--faint);
130
+ text-transform: uppercase; }
131
+ .d-block { font-size: 12px; line-height: 1.55; padding: 9px 11px; border-radius: 7px;
132
+ background: #ffffff08; border: 1px solid var(--line); white-space: pre-wrap; word-break: break-word; }
133
+ .d-block.muted { color: var(--dim); font-style: italic; }
134
+ .d-rows { display: flex; flex-direction: column; gap: 5px; }
135
+ .d-row { display: flex; gap: 10px; font-size: 11.5px; line-height: 1.4; }
136
+ .d-row .k { color: var(--faint); min-width: 64px; flex: none; }
137
+ .d-row .v { color: var(--fg); word-break: break-word; }
138
+ .d-msgs { display: flex; flex-direction: column; gap: 5px; }
139
+ .d-msg { font-size: 11px; line-height: 1.45; padding: 7px 10px 8px; border-radius: 6px;
140
+ background: #ffffff06; border-left: 2px solid var(--line); }
141
+ .d-msg .mhead { display: flex; align-items: baseline; gap: 6px; margin-bottom: 3px; }
142
+ .d-msg .m { font-size: 9px; font-weight: 700; letter-spacing: .3px; text-transform: uppercase; }
143
+ .d-msg .who { color: var(--fg); font-weight: 600; }
144
+ .d-msg .tgt { color: var(--dim); }
145
+ .d-msg .body { color: var(--dim); word-break: break-word; }
146
+ .d-msg.empty { text-align: center; color: var(--faint); font-style: italic; border-left-color: transparent; }
147
+ /* member / subscription tag rows in the detail panel */
148
+ .d-tags { display: flex; flex-wrap: wrap; gap: 5px; }
149
+ .mtag { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--fg);
150
+ padding: 3px 8px; border-radius: 13px; background: #ffffff08; border: 1px solid var(--line); }
151
+ .mtag .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; box-shadow: 0 0 5px currentColor; }
152
+ .mtag .act { font-size: 9px; font-weight: 700; letter-spacing: .3px; text-transform: uppercase; color: var(--chat); }
153
+ .mtag .off { font-size: 9px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; color: var(--faint); }
154
+ .ctag { font-size: 11px; color: var(--chat); padding: 3px 8px; border-radius: 13px;
155
+ background: #58a6ff14; border: 1px solid #58a6ff33; }
156
+ .ctag.off { color: var(--dim); background: #ffffff06; border-color: var(--line); }
157
+ .hint { position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%); z-index: 9;
158
+ font-size: 11.5px; color: var(--faint); pointer-events: none; transition: opacity .4s; }
159
+ </style>
160
+ </head>
161
+ <body>
162
+ <canvas id="graph"></canvas>
163
+
164
+ <header class="glass">
165
+ <span class="brand">
166
+ <span class="mark"></span>
167
+ <span class="title">Cotal</span>
168
+ <span class="space" id="space"></span>
169
+ </span>
170
+ <a class="navlink" href="/">← Monitor</a>
171
+ <span class="pill down" id="conn"><span class="d"></span><span class="t">connecting</span></span>
172
+ <span class="pill off" id="feed" hidden><span class="d"></span><span class="t">membership</span></span>
173
+ <div class="ctrls">
174
+ <div class="grp" id="modes">
175
+ <span class="chip mode chat on" data-mode="chat">channel</span>
176
+ <span class="chip mode unicast on" data-mode="unicast">direct</span>
177
+ <span class="chip mode anycast on" data-mode="anycast">anycast</span>
178
+ </div>
179
+ <span class="chip offline on" id="hideOffline">hide offline</span>
180
+ <span class="chip offline on" id="hideEmpty">hide empty</span>
181
+ <span class="chip pause" id="pause">⏸ pause</span>
182
+ </div>
183
+ </header>
184
+
185
+ <div id="legend" class="glass">
186
+ <button class="ltoggle" id="legendToggle" type="button"><span>legend</span><span class="chev">▾</span></button>
187
+ <div class="lbody">
188
+ <span class="ls">traffic</span>
189
+ <span class="row"><span class="sw" style="background:var(--chat)"></span> channel post → hub</span>
190
+ <span class="row"><span class="sw" style="background:var(--unicast)"></span> direct message</span>
191
+ <span class="row"><span class="sw sw-dot" style="background:var(--anycast)"></span> anycast (to a role)</span>
192
+ <span class="ls">membership</span>
193
+ <span class="row"><span class="sw" style="background:#8493a8"></span> subscribed (live)</span>
194
+ <span class="row"><span class="sw" style="background:#5a6472;opacity:.7"></span> member · offline</span>
195
+ <span class="ls">agents</span>
196
+ <span class="row"><span class="ring" style="color:var(--working)"></span> working</span>
197
+ <span class="row"><span class="ring" style="color:var(--waiting)"></span> waiting · needs input</span>
198
+ <span class="row"><span class="ring" style="color:var(--idle)"></span> idle / offline</span>
199
+ </div>
200
+ </div>
201
+
202
+ <aside id="detail" class="glass"></aside>
203
+ <div class="hint" id="hint">click a node for detail · scroll to zoom · drag to pan</div>
204
+
205
+ <script src="/graph.js"></script>
206
+ </body>
207
+ </html>