@oddessentials/agent-guild 0.2.0 → 0.4.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.
package/web/art/art.css CHANGED
@@ -1,11 +1,13 @@
1
1
  .level-badge { display: none; }
2
2
 
3
- @media (prefers-color-scheme: dark) {
4
- :root {
5
- --gold: #e8c47c;
6
- --gold-dim: #8a6d3b;
7
- --card-bg: rgba(18, 20, 26, 0.9);
8
- }
3
+ /* The guild look is the dark theme. The rules below nest under the theme
4
+ attribute that theme.js sets on <html>, so they apply whether the user
5
+ chose dark or the system did. :where() keeps their specificity as it was
6
+ inside the media query, so styles.css still loses to them only on order. */
7
+ :where(:root[data-theme="dark"]) {
8
+ --gold: #e8c47c;
9
+ --gold-dim: #8a6d3b;
10
+ --card-bg: rgba(18, 20, 26, 0.9);
9
11
 
10
12
  .provider, .session-card {
11
13
  --pc: #a67cf6;
@@ -153,6 +155,26 @@
153
155
  .badge { background: rgba(0, 0, 0, 0.45); border-color: #4a3d25; }
154
156
  .model-pill:hover { border-color: var(--gold-dim); }
155
157
 
158
+ .news-latest {
159
+ border-radius: 6px; border-color: #4a3d25; background: var(--card-bg);
160
+ box-shadow: inset 0 0 0 1px rgba(232, 196, 124, 0.06), 0 8px 24px rgba(0, 0, 0, 0.35);
161
+ }
162
+ .news-row + .news-row, .news-bar, .news-filters, .news-item, .news-status { border-color: #2a2418; }
163
+ .news-link { color: #f3ead8; }
164
+ a.news-link:visited { color: var(--muted); }
165
+ .news-panel {
166
+ background: #12141a; border-left-color: #4a3d25;
167
+ box-shadow: inset 1px 0 0 rgba(232, 196, 124, 0.08), -24px 0 60px rgba(0, 0, 0, 0.6);
168
+ }
169
+ .news-panel::backdrop { background: rgba(5, 6, 9, 0.72); }
170
+ .news-bar { padding: 16px 18px 12px; }
171
+ .news-filters, .news-status { padding-left: 18px; padding-right: 18px; }
172
+ .news-body { padding: 0 18px 14px; }
173
+ .news-day { background: #12141a; color: #c9ab72; }
174
+ .news-filter { border-color: #4a3d25; background: rgba(0, 0, 0, 0.35); }
175
+ .news-filter:hover { border-color: var(--gold-dim); }
176
+ .news-filter[aria-pressed="true"] { border-color: #b8955a; color: #fff; background: linear-gradient(#8e8ef6, #6262d6); }
177
+
156
178
  .session-card { padding-left: 152px; min-height: 196px; }
157
179
  .session-card::before {
158
180
  inset: 0 auto 0 0; width: 168px; background-size: 168px auto;
package/web/index.html CHANGED
@@ -5,6 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Agent Guild</title>
7
7
  <meta name="color-scheme" content="light dark">
8
+ <script src="/theme.js"></script>
8
9
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%235B5BD6'/%3E%3Cpath d='M9 22l7-12 7 12' stroke='white' stroke-width='3' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E">
9
10
  <link rel="stylesheet" href="/vendor/xterm/xterm.css">
10
11
  <link rel="stylesheet" href="/styles.css">
@@ -20,6 +21,9 @@
20
21
  <div id="connection" class="connection" role="status" aria-live="polite">
21
22
  <span class="dot"></span><span class="label">Connecting…</span>
22
23
  </div>
24
+ <span id="upgrade-note" class="upgrade-note" hidden></span>
25
+ <button id="upgrade" class="btn upgrade" type="button" hidden></button>
26
+ <button id="theme-toggle" class="btn theme-toggle" type="button"></button>
23
27
  <button id="stop-manager" class="btn danger stop-manager" type="button" hidden title="Stop the session manager. This ends every session.">Stop manager</button>
24
28
  </div>
25
29
  </header>
@@ -43,6 +47,15 @@
43
47
  <div id="sessions" class="session-grid" role="list"></div>
44
48
  <p id="empty" class="empty">No sessions yet. Pick a provider above to start one. Sessions keep running when you close this page.</p>
45
49
  </section>
50
+
51
+ <section class="news" aria-labelledby="news-heading">
52
+ <div class="section-head">
53
+ <h2 id="news-heading">Latest news <span id="news-count" class="count"></span></h2>
54
+ <button id="news-all" class="btn" type="button" aria-haspopup="dialog">All news</button>
55
+ </div>
56
+ <ol id="news-latest" class="news-latest" hidden></ol>
57
+ <p id="news-note" class="empty">Loading news…</p>
58
+ </section>
46
59
  </main>
47
60
 
48
61
  <section id="auth" class="auth" hidden>
@@ -101,6 +114,23 @@
101
114
  <p id="models-source" class="models-source"></p>
102
115
  </dialog>
103
116
 
117
+ <dialog id="news" class="news-panel" aria-labelledby="news-title">
118
+ <div class="news-bar">
119
+ <div class="news-heading">
120
+ <h2 id="news-title">News</h2>
121
+ <p id="news-sub" class="sub"></p>
122
+ </div>
123
+ <button id="news-close" class="btn" type="button">Close</button>
124
+ </div>
125
+ <div id="news-filters" class="news-filters" role="group" aria-label="Show"></div>
126
+ <div class="news-body">
127
+ <button id="news-fresh" class="btn news-fresh" type="button" hidden></button>
128
+ <div id="news-list"></div>
129
+ <p id="news-empty" class="news-empty" hidden></p>
130
+ </div>
131
+ <p id="news-status" class="news-status" hidden></p>
132
+ </dialog>
133
+
104
134
  <div id="tip" class="tip" aria-hidden="true" hidden><strong id="tip-title"></strong><span id="tip-source"></span><p id="tip-text"></p></div>
105
135
 
106
136
  <div id="toast" class="toast" role="alert" hidden></div>
@@ -113,6 +143,7 @@
113
143
  <span class="tool"></span>
114
144
  <span class="state"></span>
115
145
  </span>
146
+ <div class="accounts" role="tablist" aria-label="Accounts" hidden></div>
116
147
  <div class="usage"></div>
117
148
  <div class="model-stats"></div>
118
149
  <div class="provider-links">
package/web/styles.css CHANGED
@@ -18,21 +18,20 @@
18
18
  color-scheme: light;
19
19
  }
20
20
 
21
- @media (prefers-color-scheme: dark) {
22
- :root {
23
- --bg: #0f1115;
24
- --surface: #171a21;
25
- --surface-2: #1e222b;
26
- --border: #2a303b;
27
- --text: #e6e9ef;
28
- --muted: #9aa4b2;
29
- --accent: #7c7cf0;
30
- --danger: #ef6b66;
31
- --ok: #4cc38a;
32
- --warn: #e0a84a;
33
- --shadow: none;
34
- color-scheme: dark;
35
- }
21
+ /* theme.js sets data-theme from the saved choice or the system setting before the first paint. */
22
+ :root[data-theme="dark"] {
23
+ --bg: #0f1115;
24
+ --surface: #171a21;
25
+ --surface-2: #1e222b;
26
+ --border: #2a303b;
27
+ --text: #e6e9ef;
28
+ --muted: #9aa4b2;
29
+ --accent: #7c7cf0;
30
+ --danger: #ef6b66;
31
+ --ok: #4cc38a;
32
+ --warn: #e0a84a;
33
+ --shadow: none;
34
+ color-scheme: dark;
36
35
  }
37
36
 
38
37
  * { box-sizing: border-box; }
@@ -86,6 +85,16 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
86
85
  .provider .state.update-available { color: var(--warn); }
87
86
  .provider.unavailable .state { color: var(--muted); }
88
87
  .provider .update { color: var(--warn); border-color: var(--warn); }
88
+ .provider .accounts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px; }
89
+ .account-chip {
90
+ font: inherit; font-size: 0.75rem; font-weight: 600; padding: 2px 10px; border-radius: 999px; cursor: pointer;
91
+ border: 1px solid var(--border); background: var(--surface-2); color: var(--muted); max-width: 100%;
92
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
93
+ }
94
+ .account-chip:hover { border-color: var(--muted); }
95
+ .account-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
96
+ .account-chip[aria-selected="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
97
+ .account-chip.unsigned { border-style: dashed; }
89
98
  .provider .usage { grid-column: 1 / -1; display: grid; gap: 4px; }
90
99
  .provider .usage:empty { display: none; }
91
100
  .meter { display: grid; grid-template-columns: 5.5em 1fr auto; align-items: center; gap: 8px; font-size: 0.75rem; color: var(--muted); }
@@ -152,7 +161,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
152
161
  .tier-b { --tier: #3b82f6; --tier-ink: #1d4ed8; }
153
162
  .tier-c { --tier: #22a35a; --tier-ink: #166534; }
154
163
  .tier-d { --tier: #94a3b8; --tier-ink: #475569; }
155
- @media (prefers-color-scheme: dark) {
164
+ :where(:root[data-theme="dark"]) {
156
165
  .tier-s { --tier: #e0b45c; --tier-ink: #f6dea6; }
157
166
  .tier-a { --tier: #a78bfa; --tier-ink: #ddd3ff; }
158
167
  .tier-b { --tier: #60a5fa; --tier-ink: #cfe3ff; }
@@ -240,7 +249,9 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
240
249
  .btn.danger { color: var(--danger); }
241
250
  .btn.danger:hover { border-color: var(--danger); }
242
251
  .btn:disabled { opacity: 0.6; cursor: progress; }
243
- .stop-manager { white-space: nowrap; }
252
+ .stop-manager, .theme-toggle, .upgrade { white-space: nowrap; }
253
+ .btn.upgrade { color: var(--warn); border-color: var(--warn); }
254
+ .upgrade-note { font-size: 0.85rem; color: var(--warn); }
244
255
 
245
256
  .empty { color: var(--muted); margin: 8px 0 0; }
246
257
 
@@ -308,7 +319,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
308
319
  .badge.new { --badge: var(--ok); color: color-mix(in srgb, var(--badge) 72%, #000); }
309
320
  .badge.expires { --badge: var(--warn); color: color-mix(in srgb, var(--badge) 62%, #000); }
310
321
  .badge.in-use, .badge.new, .badge.expires { border-color: color-mix(in srgb, var(--badge) 45%, transparent); }
311
- @media (prefers-color-scheme: dark) {
322
+ :where(:root[data-theme="dark"]) {
312
323
  .badge.in-use, .badge.new, .badge.expires { color: var(--badge); }
313
324
  }
314
325
  .model-detail {
@@ -330,6 +341,59 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
330
341
  .models-note { margin: 14px 0 0; color: var(--muted); }
331
342
  .models-source { margin: 0; padding: 10px 14px; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; line-height: 1.45; }
332
343
 
344
+ .news-latest {
345
+ list-style: none; margin: 0; padding: 0;
346
+ border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow);
347
+ }
348
+ .news-row { display: flex; align-items: baseline; gap: 4px 10px; min-width: 0; padding: 9px 14px; }
349
+ .news-row + .news-row { border-top: 1px solid var(--border); }
350
+ .news-row .news-link { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
351
+ .news-row .badge, .news-row .news-meta { flex: none; }
352
+ .news-link { color: var(--text); font-weight: 600; text-decoration: none; }
353
+ a.news-link:hover, .news-discussion:hover { text-decoration: underline; }
354
+ a.news-link:visited { color: var(--muted); }
355
+ .news-link:focus-visible, .news-discussion:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
356
+ .news-meta { color: var(--muted); font-size: 0.78rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
357
+
358
+ .news-panel {
359
+ width: min(460px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0 0 0 auto; padding: 0;
360
+ border: 0; border-left: 1px solid var(--border); background: var(--surface); color: var(--text);
361
+ box-shadow: -16px 0 48px rgba(0, 0, 0, 0.25);
362
+ }
363
+ .news-panel[open] { display: flex; flex-direction: column; animation: news-in 0.18s ease-out; }
364
+ .news-panel::backdrop { background: rgba(15, 17, 21, 0.4); }
365
+ .news-bar { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
366
+ .news-heading { flex: 1; min-width: 0; }
367
+ .news-heading .sub { margin: 2px 0 0; color: var(--muted); font-size: 0.78rem; }
368
+ .news-filters { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 14px; border-bottom: 1px solid var(--border); }
369
+ .news-filter {
370
+ font: inherit; font-size: 0.75rem; font-weight: 600; padding: 3px 11px; border-radius: 999px; cursor: pointer;
371
+ border: 1px solid var(--border); background: var(--surface-2); color: var(--muted);
372
+ }
373
+ .news-filter:hover { border-color: var(--muted); }
374
+ .news-filter:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
375
+ .news-filter[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
376
+ .news-body { flex: 1; min-height: 0; overflow: auto; padding: 0 14px 14px; }
377
+ .news-fresh { width: 100%; margin-top: 12px; }
378
+ .news-day {
379
+ position: sticky; top: 0; z-index: 1; margin: 0; padding: 12px 0 6px; background: var(--surface);
380
+ color: var(--muted); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
381
+ }
382
+ .news-items { list-style: none; margin: 0; padding: 0; }
383
+ .news-item { padding: 10px 0; border-top: 1px solid var(--border); }
384
+ .news-head { display: flex; align-items: baseline; gap: 6px; }
385
+ .news-head .news-link { overflow-wrap: anywhere; }
386
+ .news-head .badge { flex: none; margin-left: auto; }
387
+ .news-item .news-meta { display: block; margin-top: 3px; }
388
+ .news-summary {
389
+ margin: 4px 0 0; color: var(--muted); font-size: 0.82rem; line-height: 1.45; overflow-wrap: anywhere;
390
+ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
391
+ }
392
+ .news-discussion { color: inherit; text-decoration: none; }
393
+ .news-empty { margin: 14px 0 0; color: var(--muted); }
394
+ .news-status { margin: 0; padding: 10px 14px; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; line-height: 1.45; }
395
+ .news-status span { display: block; }
396
+
333
397
  .toast {
334
398
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 20;
335
399
  max-width: min(640px, calc(100vw - 32px)); padding: 10px 14px; border-radius: 8px;
@@ -339,9 +403,10 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
339
403
  @keyframes pulse { 50% { opacity: 0.35; } }
340
404
  @keyframes ring { 0% { transform: scale(0.85); opacity: 0.8; } 100% { transform: scale(1.3); opacity: 0; } }
341
405
  @keyframes stat-fill { from { transform: scaleX(0); } }
406
+ @keyframes news-in { from { transform: translateX(24px); opacity: 0; } }
342
407
 
343
408
  @media (prefers-reduced-motion: reduce) {
344
- .status-pill.active::before, .agent.working::after, .model-detail .stat-fill { animation: none; }
409
+ .status-pill.active::before, .agent.working::after, .model-detail .stat-fill, .news-panel[open] { animation: none; }
345
410
  .model-name::before { transition: none; }
346
411
  }
347
412
 
@@ -359,4 +424,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
359
424
  .model-detail .stat { padding-bottom: 6px; }
360
425
  .model-detail .stat-reading { row-gap: 1px; }
361
426
  .model-detail .stat-note { grid-column: 1 / -1; }
427
+ .news-row { flex-wrap: wrap; }
428
+ .news-row .news-link { flex-basis: 100%; white-space: normal; }
429
+ .news-panel { border-left: 0; }
362
430
  }
package/web/theme.js ADDED
@@ -0,0 +1,10 @@
1
+ // Applies the chosen theme before the first paint, so the page never flashes
2
+ // the other one. Loaded in <head> as a plain script; app.js owns the toggle.
3
+ (function () {
4
+ var theme = null;
5
+ try { theme = localStorage.getItem('agentGuild.theme'); } catch (e) { /* storage unavailable */ }
6
+ if (theme !== 'light' && theme !== 'dark') {
7
+ theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
8
+ }
9
+ document.documentElement.dataset.theme = theme;
10
+ })();