@oddessentials/agent-guild 0.1.1 → 0.3.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;
@@ -109,6 +111,50 @@
109
111
  .meter.low .meter-fill { background: var(--warn); box-shadow: 0 0 8px rgba(224, 168, 74, 0.5); }
110
112
  .meter.empty .meter-fill { background: var(--danger); box-shadow: 0 0 8px rgba(239, 107, 102, 0.5); }
111
113
 
114
+ .model-stats-head { color: var(--gold); }
115
+ .info { color: #b89a5e; }
116
+ .info:hover, .info:focus-visible { color: var(--gold); }
117
+ .tip {
118
+ background: #12141a; color: #e9e2cf; border: 1px solid #4a3d25;
119
+ box-shadow: inset 0 0 0 1px rgba(232, 196, 124, 0.06), 0 10px 28px rgba(0, 0, 0, 0.6);
120
+ }
121
+ .tip strong { color: var(--gold); font-family: "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif; }
122
+ .stat-bar { height: 7px; background: #0c0e12; box-shadow: inset 0 0 0 1px rgba(232, 196, 124, 0.18); }
123
+ .stat-fill {
124
+ background: linear-gradient(90deg, color-mix(in srgb, var(--tier) 55%, #000), var(--tier));
125
+ box-shadow: 0 0 8px color-mix(in srgb, var(--tier) 50%, transparent);
126
+ }
127
+ .stat-tier {
128
+ border-radius: 3px; font-family: "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
129
+ background: linear-gradient(color-mix(in srgb, var(--tier) 34%, #1a1d26), #0d0f15);
130
+ border-color: color-mix(in srgb, var(--tier) 70%, #000);
131
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 0 6px color-mix(in srgb, var(--tier) 30%, transparent);
132
+ }
133
+ .tier-s .stat-tier {
134
+ color: #2b1b06; border-color: #f6dea6;
135
+ background: linear-gradient(180deg, #fbe9bd 0%, #e2b867 55%, #a97b35 100%);
136
+ box-shadow: 0 0 10px rgba(232, 196, 124, 0.6);
137
+ }
138
+
139
+ .models {
140
+ border-radius: 6px; border-color: #4a3d25; background: #12141a;
141
+ box-shadow: inset 0 0 0 1px rgba(232, 196, 124, 0.06), 0 24px 60px rgba(0, 0, 0, 0.6);
142
+ }
143
+ .models::after {
144
+ content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
145
+ border: 44px solid transparent; border-image: url("/art/ui/frame.png") 168;
146
+ }
147
+ .models::backdrop { background: rgba(5, 6, 9, 0.72); }
148
+ .models-bar { padding: 20px 24px 12px; }
149
+ .models-body { padding: 0 24px 14px; }
150
+ .models-source { padding: 10px 24px 20px; }
151
+ .models-bar, .models-source, .model-columns, .model { border-color: #2a2418; }
152
+ .model-columns { background: #12141a; }
153
+ .model-toggle:hover { background: rgba(232, 196, 124, 0.06); }
154
+ .model-group h4 { color: #c9ab72; }
155
+ .badge { background: rgba(0, 0, 0, 0.45); border-color: #4a3d25; }
156
+ .model-pill:hover { border-color: var(--gold-dim); }
157
+
112
158
  .session-card { padding-left: 152px; min-height: 196px; }
113
159
  .session-card::before {
114
160
  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>
@@ -83,6 +87,26 @@
83
87
  <div id="terminal-host" class="terminal-host"></div>
84
88
  </div>
85
89
 
90
+ <dialog id="models" class="models" aria-labelledby="models-title">
91
+ <div class="models-bar">
92
+ <span id="models-icon" class="provider-icon small"></span>
93
+ <div class="models-heading">
94
+ <h2 id="models-title"></h2>
95
+ <p id="models-sub" class="sub"></p>
96
+ </div>
97
+ <button id="models-close" class="btn" type="button">Close</button>
98
+ </div>
99
+ <div class="models-body">
100
+ <div id="models-columns" class="model-columns" aria-hidden="true"></div>
101
+ <div id="models-list"></div>
102
+ <button id="models-more" class="btn models-more" type="button" hidden></button>
103
+ <p id="models-note" class="models-note"></p>
104
+ </div>
105
+ <p id="models-source" class="models-source"></p>
106
+ </dialog>
107
+
108
+ <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>
109
+
86
110
  <div id="toast" class="toast" role="alert" hidden></div>
87
111
 
88
112
  <template id="provider-template">
@@ -93,7 +117,9 @@
93
117
  <span class="tool"></span>
94
118
  <span class="state"></span>
95
119
  </span>
120
+ <div class="accounts" role="tablist" aria-label="Accounts" hidden></div>
96
121
  <div class="usage"></div>
122
+ <div class="model-stats"></div>
97
123
  <div class="provider-links">
98
124
  <a class="console-link usage-link" target="_blank" rel="noopener noreferrer">Usage ↗</a>
99
125
  <a class="console-link billing-link" target="_blank" rel="noopener noreferrer">Billing ↗</a>
@@ -120,6 +146,28 @@
120
146
  </div>
121
147
  </template>
122
148
 
149
+ <template id="stat-template">
150
+ <div class="stat">
151
+ <span class="stat-label"><span class="stat-name"></span><button class="info" type="button">i</button></span>
152
+ <span class="stat-reading" role="img">
153
+ <span class="stat-bar"><span class="stat-fill"></span></span>
154
+ <span class="stat-value"></span>
155
+ <span class="stat-note"></span>
156
+ </span>
157
+ </div>
158
+ </template>
159
+
160
+ <template id="model-template">
161
+ <section class="model">
162
+ <h3 class="model-head">
163
+ <button class="model-toggle" type="button" aria-expanded="false">
164
+ <span class="model-title"><span class="model-name"></span></span>
165
+ </button>
166
+ </h3>
167
+ <div class="model-detail" hidden></div>
168
+ </section>
169
+ </template>
170
+
123
171
  <template id="session-template">
124
172
  <article class="session-card" role="listitem" tabindex="0">
125
173
  <span class="level-badge" aria-hidden="true"></span>
@@ -129,7 +177,7 @@
129
177
  <div class="name"></div>
130
178
  <div class="meta"></div>
131
179
  </div>
132
- <span class="model-pill"></span>
180
+ <button class="model-pill" type="button"></button>
133
181
  <span class="status-pill"></span>
134
182
  </div>
135
183
  <div class="cwd-line"></div>
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; }
@@ -69,7 +68,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
69
68
 
70
69
  .providers { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
71
70
  .provider {
72
- display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 10px 12px; padding: 14px;
71
+ display: grid; grid-template-columns: auto 1fr; align-items: center; align-content: start; gap: 10px 12px; padding: 14px;
73
72
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow);
74
73
  }
75
74
  .provider.unavailable .provider-icon { filter: grayscale(1); opacity: 0.45; }
@@ -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); }
@@ -96,6 +105,69 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
96
105
  .meter.empty .meter-fill { background: var(--danger); }
97
106
  .meter-value { white-space: nowrap; font-variant-numeric: tabular-nums; }
98
107
  .usage-note { font-size: 0.75rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
108
+ .provider .model-stats {
109
+ grid-column: 1 / -1; display: grid; grid-template-columns: max-content minmax(24px, 1fr) auto; align-items: center; gap: 2px 8px;
110
+ }
111
+ .provider .model-stats:empty { display: none; }
112
+ .provider .model-stats > .usage-note { grid-column: 1 / -1; contain: inline-size; }
113
+ .model-stats-head {
114
+ grid-column: 1 / -1; justify-self: start; max-width: 100%; min-height: 24px; padding: 0; border: 0; background: none; color: var(--accent);
115
+ font: inherit; font-size: 0.78rem; text-align: left; cursor: pointer;
116
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
117
+ }
118
+ .model-stats-head::after { content: " ›"; }
119
+ .model-stats-head:hover { text-decoration: underline; }
120
+ .model-stats-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
121
+
122
+ .stat {
123
+ --tier: var(--border);
124
+ grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: center;
125
+ min-height: 24px; font-size: 0.75rem; color: var(--muted);
126
+ }
127
+ .stat-label { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
128
+ .stat-reading { grid-column: 2 / -1; display: grid; grid-template-columns: subgrid; align-items: center; }
129
+ .info {
130
+ position: relative; flex: none; display: inline-grid; place-items: center; width: 1.3em; height: 1.3em; padding: 0;
131
+ border: 1px solid color-mix(in srgb, currentColor 50%, transparent); border-radius: 50%; background: none; color: var(--muted);
132
+ font: italic 700 0.68rem/1 Georgia, "Times New Roman", serif; cursor: help;
133
+ }
134
+ .info::after { content: ""; position: absolute; inset: -6px; }
135
+ .info:hover, .info:focus-visible { color: var(--accent); border-color: currentColor; }
136
+ .info:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
137
+ .tip {
138
+ position: fixed; z-index: 30; width: max-content; max-width: min(320px, calc(100vw - 16px)); margin: 0; padding: 9px 11px;
139
+ border-radius: 8px; background: var(--text); color: var(--bg); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
140
+ font-size: 0.78rem; line-height: 1.45; pointer-events: none;
141
+ }
142
+ .tip strong { font-weight: 600; }
143
+ .tip span { margin-left: 6px; font-size: 0.72rem; opacity: 0.75; }
144
+ .tip span::before { content: "· "; }
145
+ .tip p { margin: 4px 0 0; }
146
+ .stat-bar { height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
147
+ .stat-fill { display: block; width: 0; height: 100%; border-radius: 3px; background: var(--tier); }
148
+ .stat-value, .model-index { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--text); }
149
+ .stat-value { justify-content: flex-end; }
150
+ .stat-value .level, .model-index .level { min-width: 3ch; text-align: right; }
151
+ .stat.unmeasured .stat-value, .model-index.unmeasured { color: var(--muted); }
152
+ .stat-note { display: none; }
153
+ .stat-tier {
154
+ display: inline-grid; place-items: center; width: 1.7em; height: 1.7em; border-radius: 4px;
155
+ font-size: 0.68rem; font-weight: 700; line-height: 1;
156
+ color: var(--tier-ink); background: color-mix(in srgb, var(--tier) 16%, var(--surface));
157
+ border: 1px solid color-mix(in srgb, var(--tier) 50%, transparent);
158
+ }
159
+ .tier-s { --tier: #c99a2e; --tier-ink: #7a5200; }
160
+ .tier-a { --tier: #8b5cf6; --tier-ink: #5b21b6; }
161
+ .tier-b { --tier: #3b82f6; --tier-ink: #1d4ed8; }
162
+ .tier-c { --tier: #22a35a; --tier-ink: #166534; }
163
+ .tier-d { --tier: #94a3b8; --tier-ink: #475569; }
164
+ :where(:root[data-theme="dark"]) {
165
+ .tier-s { --tier: #e0b45c; --tier-ink: #f6dea6; }
166
+ .tier-a { --tier: #a78bfa; --tier-ink: #ddd3ff; }
167
+ .tier-b { --tier: #60a5fa; --tier-ink: #cfe3ff; }
168
+ .tier-c { --tier: #4ade80; --tier-ink: #c6f6d5; }
169
+ .tier-d { --tier: #94a3b8; --tier-ink: #d4dbe4; }
170
+ }
99
171
  .provider-links { grid-column: 1 / -1; display: flex; gap: 14px; font-size: 0.78rem; }
100
172
  .console-link { color: var(--accent); text-decoration: none; }
101
173
  .console-link:hover { text-decoration: underline; }
@@ -139,7 +211,10 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
139
211
  font-size: 0.72rem; font-weight: 600; padding: 3px 8px; border-radius: 999px; white-space: nowrap;
140
212
  max-width: 11em; overflow: hidden; text-overflow: ellipsis;
141
213
  background: color-mix(in srgb, var(--accent) 14%, var(--surface)); color: var(--accent);
214
+ border: 0; font-family: inherit; cursor: pointer;
142
215
  }
216
+ .model-pill:hover { background: color-mix(in srgb, var(--accent) 24%, var(--surface)); }
217
+ .model-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
143
218
  .model-pill.screen { font-weight: 500; }
144
219
  .status-pill.active { color: var(--ok); }
145
220
  .status-pill.active::before { content: "● "; animation: pulse 1.2s ease-in-out infinite; }
@@ -174,7 +249,9 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
174
249
  .btn.danger { color: var(--danger); }
175
250
  .btn.danger:hover { border-color: var(--danger); }
176
251
  .btn:disabled { opacity: 0.6; cursor: progress; }
177
- .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); }
178
255
 
179
256
  .empty { color: var(--muted); margin: 8px 0 0; }
180
257
 
@@ -203,6 +280,67 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
203
280
  .terminal-host { flex: 1; min-height: 0; padding: 8px 4px 4px 10px; }
204
281
  .terminal-host > div { height: 100%; }
205
282
 
283
+ .models {
284
+ width: min(880px, calc(100vw - 32px)); max-height: calc(100vh - 48px); padding: 0; overflow: hidden;
285
+ border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text);
286
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
287
+ }
288
+ .models[open] { display: flex; flex-direction: column; }
289
+ .models::backdrop { background: rgba(15, 17, 21, 0.5); }
290
+ .models-bar { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
291
+ .models-heading { flex: 1; min-width: 0; }
292
+ .models-heading .sub { margin: 2px 0 0; color: var(--muted); font-size: 0.78rem; }
293
+ .models-body { flex: 1; min-height: 0; overflow: auto; padding: 0 14px 14px; }
294
+ .model-columns, .model-toggle { display: grid; grid-template-columns: minmax(0, 1fr) repeat(3, 6.5rem) 4.5rem; align-items: center; gap: 8px; }
295
+ .model-columns {
296
+ position: sticky; top: 0; z-index: 1; padding: 10px 8px 6px; background: var(--surface);
297
+ border-bottom: 1px solid var(--border); color: var(--muted); font-size: 0.72rem; font-weight: 600;
298
+ }
299
+ .model-columns span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
300
+ .model-columns span:last-child, .model-context { text-align: right; }
301
+ .model { border-bottom: 1px solid var(--border); }
302
+ .model-head { margin: 0; font: inherit; }
303
+ .model-toggle {
304
+ width: 100%; padding: 9px 8px; border: 0; border-radius: 6px; background: none; color: var(--text);
305
+ font: inherit; font-size: 0.85rem; text-align: left; cursor: pointer;
306
+ }
307
+ .model-toggle:hover { background: var(--surface-2); }
308
+ .model-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
309
+ .model-title { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-width: 0; }
310
+ .model-name { font-weight: 600; overflow-wrap: anywhere; }
311
+ .model-name::before { content: "▸"; content: "▸" / ""; display: inline-block; width: 1em; color: var(--muted); transition: transform 0.15s; }
312
+ .model-toggle[aria-expanded="true"] .model-name::before { transform: rotate(90deg); }
313
+ .model-context { color: var(--muted); font-variant-numeric: tabular-nums; }
314
+ .badge {
315
+ font-size: 0.66rem; font-weight: 600; padding: 1px 7px; border-radius: 999px; white-space: nowrap;
316
+ background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
317
+ }
318
+ .badge.in-use { --badge: var(--accent); color: color-mix(in srgb, var(--badge) 80%, #000); }
319
+ .badge.new { --badge: var(--ok); color: color-mix(in srgb, var(--badge) 72%, #000); }
320
+ .badge.expires { --badge: var(--warn); color: color-mix(in srgb, var(--badge) 62%, #000); }
321
+ .badge.in-use, .badge.new, .badge.expires { border-color: color-mix(in srgb, var(--badge) 45%, transparent); }
322
+ :where(:root[data-theme="dark"]) {
323
+ .badge.in-use, .badge.new, .badge.expires { color: var(--badge); }
324
+ }
325
+ .model-detail {
326
+ display: grid; grid-template-columns: max-content minmax(48px, 1fr) auto minmax(0, 19em); gap: 14px 10px;
327
+ padding: 4px 8px 16px calc(8px + 1em);
328
+ }
329
+ .model-group { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: center; row-gap: 2px; }
330
+ .model-group h4, .model-none { grid-column: 1 / -1; }
331
+ .model-group h4 { margin: 0 0 2px; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
332
+ .model-detail .stat-note { display: block; font-size: 0.72rem; }
333
+ .model-detail .stat-fill { transform-origin: left; animation: stat-fill 0.45s ease-out; }
334
+ .model-none { margin: 0; font-size: 0.75rem; color: var(--muted); }
335
+ .model-more { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 16px; font-size: 0.78rem; }
336
+ .model-facts { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 0; }
337
+ .model-facts div { display: flex; gap: 6px; }
338
+ .model-facts dt { color: var(--muted); }
339
+ .model-facts dd { margin: 0; }
340
+ .models-more { margin-top: 12px; }
341
+ .models-note { margin: 14px 0 0; color: var(--muted); }
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; }
343
+
206
344
  .toast {
207
345
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 20;
208
346
  max-width: min(640px, calc(100vw - 32px)); padding: 10px 14px; border-radius: 8px;
@@ -211,9 +349,11 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
211
349
 
212
350
  @keyframes pulse { 50% { opacity: 0.35; } }
213
351
  @keyframes ring { 0% { transform: scale(0.85); opacity: 0.8; } 100% { transform: scale(1.3); opacity: 0; } }
352
+ @keyframes stat-fill { from { transform: scaleX(0); } }
214
353
 
215
354
  @media (prefers-reduced-motion: reduce) {
216
- .status-pill.active::before, .agent.working::after { animation: none; }
355
+ .status-pill.active::before, .agent.working::after, .model-detail .stat-fill { animation: none; }
356
+ .model-name::before { transition: none; }
217
357
  }
218
358
 
219
359
  @media (max-width: 640px) {
@@ -223,4 +363,11 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 16px 48px; display: grid
223
363
  .terminal-panel { inset: var(--topbar-h, 56px) 0 0; border-radius: 0; }
224
364
  .session-grid { grid-template-columns: 1fr; }
225
365
  .cwd { flex-basis: 100%; }
366
+ .models { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; }
367
+ .model-columns, .model-toggle { grid-template-columns: minmax(0, 1fr) repeat(3, 4.6rem); }
368
+ .model-columns span:last-child, .model-context { display: none; }
369
+ .model-detail { padding-left: 8px; grid-template-columns: max-content minmax(32px, 1fr) auto; }
370
+ .model-detail .stat { padding-bottom: 6px; }
371
+ .model-detail .stat-reading { row-gap: 1px; }
372
+ .model-detail .stat-note { grid-column: 1 / -1; }
226
373
  }
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
+ })();