agentgui 1.0.938 → 1.0.939

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.938",
3
+ "version": "1.0.939",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <title>agentgui</title>
7
7
  <meta name="description" content="agentgui — multi-agent client with same-origin server, in-process ccsniff history, and ACP chat.">
8
- <link rel="stylesheet" href="https://unpkg.com/anentrypoint-design@0.0.145/dist/247420.css">
8
+ <link rel="stylesheet" href="vendor/anentrypoint-design/247420.css">
9
9
  <script type="importmap">
10
- { "imports": { "anentrypoint-design": "https://unpkg.com/anentrypoint-design@0.0.145/dist/247420.js" } }
10
+ { "imports": { "anentrypoint-design": "./vendor/anentrypoint-design/247420.js" } }
11
11
  </script>
12
12
  <style>
13
13
  :root {
@@ -23,8 +23,8 @@
23
23
  color: var(--fg, var(--agentgui-fg));
24
24
  font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);
25
25
  }
26
- #app { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
27
- #app > * { flex: 1 1 auto; min-height: 0; }
26
+ #app { height: 100vh; height: 100dvh; }
27
+ #app > * { height: 100%; }
28
28
 
29
29
  /* skip link for keyboard/AT users */
30
30
  .skip-link {
@@ -90,6 +90,65 @@
90
90
 
91
91
  .field-error { color: var(--warn, var(--agentgui-warn)); }
92
92
 
93
+ /* chat control cluster in the crumb bar: keep model picker, +new, status
94
+ on one tidy baseline-aligned row instead of wrapping under the nav. */
95
+ .chat-controls { display: flex; align-items: center; gap: .6em; flex-wrap: nowrap; }
96
+ .chat-controls > * { flex: 0 0 auto; }
97
+ .chat-controls select,
98
+ .chat-controls .select,
99
+ .chat-controls [role="combobox"] { min-width: 150px; max-width: 220px; }
100
+ .chat-controls .status-dot { white-space: nowrap; }
101
+
102
+ /* Tame the design-system hero-sized PageHeader h1 to a sensible page title.
103
+ The DS default (--fs-h1, cqi-scaled to ~64-80px) overpowers app content. */
104
+ .agentgui-main .ds-section > h1 {
105
+ font-size: clamp(22px, 2.2vw, 30px);
106
+ line-height: 1.15;
107
+ margin-bottom: .35em;
108
+ }
109
+ .agentgui-main .ds-section { margin: 0 0 var(--space-4, 16px); }
110
+
111
+ /* TextField: stack label above the input so the label can't overlap it. */
112
+ .agentgui-main .ds-field {
113
+ display: flex; flex-direction: column; gap: .35em; align-items: stretch;
114
+ }
115
+ .agentgui-main .ds-field-label { font-size: .8rem; color: var(--fg-2, #ccc); }
116
+ .agentgui-main .ds-field input,
117
+ .agentgui-main .ds-field textarea { width: 100%; }
118
+
119
+ /* readable backend health summary (replaces raw JSON dump) */
120
+ .health-summary { display: flex; flex-wrap: wrap; gap: .4em; margin: .6em 0; }
121
+ .health-chip {
122
+ font-family: var(--ff-mono, ui-monospace, monospace);
123
+ font-size: .8rem; padding: .15em .55em; border-radius: 6px;
124
+ background: color-mix(in srgb, var(--fg, var(--agentgui-fg)) 8%, transparent);
125
+ color: var(--fg-2, var(--agentgui-fg));
126
+ border: 1px solid color-mix(in srgb, var(--fg, var(--agentgui-fg)) 12%, transparent);
127
+ }
128
+ .health-summary.health-ok .health-chip:first-child {
129
+ color: var(--accent, var(--agentgui-accent));
130
+ border-color: color-mix(in srgb, var(--accent, var(--agentgui-accent)) 40%, transparent);
131
+ }
132
+
133
+ /* search input: match the dark theme instead of a bare white box */
134
+ .app input[type="search"],
135
+ .app .search-input input,
136
+ .app input.search,
137
+ input[type="search"] {
138
+ background: color-mix(in srgb, var(--fg, var(--agentgui-fg)) 6%, transparent);
139
+ color: var(--fg, var(--agentgui-fg));
140
+ border: 1px solid color-mix(in srgb, var(--fg, var(--agentgui-fg)) 16%, transparent);
141
+ border-radius: 8px; padding: .5em .7em;
142
+ }
143
+ .app input[type="search"]::placeholder { color: var(--fg-3, #888); }
144
+ .app input[type="search"]:focus-visible {
145
+ outline: 2px solid var(--accent, var(--agentgui-accent)); outline-offset: 1px;
146
+ }
147
+
148
+ /* empty-state: keep it from wrapping awkwardly in the narrow sidebar */
149
+ .empty-state { white-space: normal; }
150
+ p.empty-state { text-align: left; padding: .6em 0; color: var(--fg-3, #888); }
151
+
93
152
  /* generic interactive focus ring */
94
153
  button:focus-visible, [tabindex]:focus-visible, a:focus-visible {
95
154
  outline: 2px solid var(--accent, var(--agentgui-accent)); outline-offset: 2px;
@@ -172,7 +172,7 @@ function view() {
172
172
  });
173
173
 
174
174
  const crumbRight = state.tab === 'chat'
175
- ? [
175
+ ? [h('div', { key: 'cc', class: 'chat-controls' },
176
176
  Select({
177
177
  key: 'modelsel',
178
178
  value: state.selectedModel,
@@ -185,39 +185,32 @@ function view() {
185
185
  ? Btn({ key: 'stop', onClick: cancelChat, children: '◼ stop', title: 'Stop streaming' })
186
186
  : Btn({ key: 'new', onClick: newChat, children: '+ new', title: 'Start new chat (clears history)' }),
187
187
  dot,
188
- ]
188
+ )]
189
189
  : [dot];
190
190
 
191
+ // Topbar already shows "agentgui / <tab>"; the crumb is reserved for contextual
192
+ // controls (model picker, new/stop, live status) so it doesn't duplicate the path.
191
193
  const crumb = Crumb({
192
- trail: ['agentgui'],
193
- leaf: state.tab,
194
+ trail: [],
195
+ leaf: '',
194
196
  right: crumbRight,
195
197
  });
196
198
 
197
- const navSide = Side({
198
- sections: [
199
- {
200
- group: 'navigate',
201
- items: [
202
- { glyph: '▣', label: 'chat', key: 'chat', active: state.tab === 'chat',
203
- onClick: (e) => { e.preventDefault(); navTo('chat'); } },
204
- { glyph: '§', label: 'history', key: 'history', active: state.tab === 'history',
205
- onClick: (e) => { e.preventDefault(); navTo('history'); } },
206
- { glyph: '⌘', label: 'settings', key: 'settings', active: state.tab === 'settings',
207
- onClick: (e) => { e.preventDefault(); navTo('settings'); } },
208
- ],
209
- },
210
- ],
211
- });
212
- const side = state.tab === 'history' ? historySide() : navSide;
199
+ // Sidebar is contextual: history shows the session list; chat/settings have no
200
+ // sidebar (the topbar already provides primary nav) so main content gets full width.
201
+ const side = state.tab === 'history' ? historySide() : null;
213
202
 
214
203
  const status = Status({
215
- left: [state.backend, ok ? '● live' : '○ offline'],
204
+ left: [state.backend || 'same-origin', ok ? '● live' : '○ offline'],
216
205
  right: [state.selectedModel ? '⌘ ' + state.selectedModel : '○ no model'],
217
206
  });
218
207
 
219
- const main = h('div', { id: 'agentgui-main', role: 'main', 'data-chat-scroll': '', style: 'min-height:0;height:100%;overflow:auto' }, mainContent());
220
- return AppShell({ topbar, crumb, side, main, status });
208
+ const mainStyle = state.tab === 'chat'
209
+ ? 'min-height:0;height:100%;display:flex;flex-direction:column'
210
+ : 'min-height:0;height:100%;overflow:auto';
211
+ const main = h('div', { id: 'agentgui-main', role: 'main', 'data-chat-scroll': '', class: 'agentgui-main agentgui-main-' + state.tab, style: mainStyle }, mainContent());
212
+ // settings reads better centered in a measure; chat + history use full width.
213
+ return AppShell({ topbar, crumb, side, main, status, narrow: state.tab === 'settings' });
221
214
  }
222
215
 
223
216
  function mainContent() {
@@ -262,7 +255,7 @@ function chatMain() {
262
255
  resumeBanner,
263
256
  Chat({
264
257
  title: (state.selectedModel || 'agent') + (state.chat.resumeSid ? ' · resume' : ''),
265
- sub: state.chat.busy ? 'streaming…' : (state.chat.messages.length + ' messages'),
258
+ sub: state.chat.busy ? 'streaming…' : undefined,
266
259
  messages: msgs,
267
260
  composer,
268
261
  }),
@@ -436,18 +429,6 @@ function historySide() {
436
429
  const projects = uniqueProjects();
437
430
 
438
431
  return [
439
- Side({
440
- sections: [
441
- {
442
- group: 'navigate',
443
- items: [
444
- { glyph: '▣', label: 'chat', key: 'chat', onClick: (e) => { e.preventDefault(); navTo('chat'); } },
445
- { glyph: '§', label: 'history', key: 'history', active: true },
446
- { glyph: '⌘', label: 'settings', key: 'settings', onClick: (e) => { e.preventDefault(); navTo('settings'); } },
447
- ],
448
- },
449
- ],
450
- }),
451
432
  Panel({
452
433
  title: searching
453
434
  ? 'matches · ' + (state.searchHits.results?.length || 0)
@@ -505,6 +486,20 @@ function saveBackend() {
505
486
  init();
506
487
  }
507
488
 
489
+ function healthSummary() {
490
+ const hh = state.health || {};
491
+ const ok = hh.status === 'ok';
492
+ const dot = ok ? '●' : (hh.status === 'unknown' ? '◌' : '○');
493
+ const bits = [];
494
+ bits.push(dot + ' ' + (hh.status || 'unknown'));
495
+ if (hh.version) bits.push('v' + hh.version);
496
+ if (typeof hh.agents === 'number') bits.push(hh.agents + ' agents');
497
+ if (typeof hh.activeExecutions === 'number') bits.push(hh.activeExecutions + ' active');
498
+ if (hh.db) bits.push('db ' + (hh.db.ok ? 'ok' : 'down'));
499
+ return h('div', { key: 'hp', class: 'health-summary' + (ok ? ' health-ok' : '') },
500
+ ...bits.map((b, i) => h('span', { key: 'hb' + i, class: 'health-chip' }, b)));
501
+ }
502
+
508
503
  function settingsMain() {
509
504
  const ok = state.health.status === 'ok';
510
505
  const isValid = isValidUrl(state.backendDraft);
@@ -530,7 +525,7 @@ function settingsMain() {
530
525
  onInput: (v) => { state.backendDraft = v; render(); },
531
526
  }),
532
527
  !isValid ? h('p', { key: 'err', id: 'backend-url-error', class: 'lede field-error', role: 'alert' }, '⚠ Invalid URL format') : null,
533
- h('p', { key: 'hp', class: 'lede' }, (ok ? '● ' : '○ ') + JSON.stringify(state.health)),
528
+ healthSummary(),
534
529
  Btn({
535
530
  key: 'savebtn',
536
531
  type: 'submit',