agentgui 1.0.872 → 1.0.874

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/CLAUDE.md CHANGED
@@ -362,7 +362,7 @@ The README.md uses shields.io badges with a consistent pattern:
362
362
  - **Terminal output** is base64-encoded (`encoding: 'base64'` field on message). Client decodes with `decodeURIComponent(escape(atob(data)))` pattern for multibyte safety.
363
363
  - **HTML cache** (`_htmlCache`) is only populated when client accepts gzip. In watch mode it's never cached (always fresh).
364
364
  - **`app.js` and `app-shortcuts.js` script loading:** Both are `<script defer>` tags loaded AFTER `agent-auth.js` in index.html. They depend on `window.wsClient`, `window.conversationManager`, and `window._escHtml` being initialized first. Defer order is guaranteed by source order — adding new defer scripts that depend on these modules requires careful ordering.
365
- - **`window.__debug.getSyncState()`:** Debug API in client.js exposes internal state machine snapshots: `convMachineStates`, `toolInstallMachineStates`, `voiceMachineState`, `convListMachineState`, `promptMachineState`, `wsConnectionState`, `rendererEventQueueLength`, `rendererEventHistoryLength`. All XState v5 machines have no parallel ad-hoc state — this API is the only way to inspect full machine state at once.
365
+ - **`window.__debug` registry:** Structured sub-keys via live getters `machines` (conv/toolInstall/voice/convList/prompt/recording/terminal/ws), `ws` (state/latency/url), `auth`, `perf`, `config`, `renderer`, `conv`. Legacy methods preserved: `getState()`, `getSyncState()`, `getMessageState()`. Uninitialized machines return `'uninitialized'` rather than crashing. All XState v5 machines have no parallel ad-hoc state.
366
366
  - **`isJsonlBacked` flag:** Only `claude-code` (protocol: `direct`) writes JSONL files. All other agents use `stream-event-handler.js` for broadcasting. `isJsonlBacked = resolvedAgentId === 'claude-code'` — guards in `stream-event-handler.js` prevent double-broadcast for claude-code, and gates ACP streaming for non-JSONL agents.
367
367
  - **`toolIds` in `server-startup.js` must match `TOOLS` in `tool-manager.js`:** `initializeToolInstallations` runs for each toolId, creating the `tool_installations` row. `tool_install_history` has a FK to `tool_installations(tool_id)`. Any tool omitted from toolIds will cause a FOREIGN KEY constraint failure when the periodic update checker writes history for it.
368
368
  - **`JsonlWatcher._read(fp)` override:** Captures `this._currentFp` before calling `super._read(fp)`, making the file path available to `_line()` callbacks for project-directory decoding in `_conv()`. JSONL project dirs are encoded (e.g., `-config-workspace-agentgui`) — decoded via `'/' + dirName.slice(1).replace(/-/g, '/')`.
package/README.md CHANGED
@@ -181,13 +181,16 @@ When `DEBUG=1` is set, internal state inspection endpoints become available:
181
181
  - `GET /api/debug/ws-stats` - WebSocket connection metrics and lag distribution
182
182
 
183
183
  **Browser Console** (`window.__debug`):
184
- - `window.__debug.convMachineStates` - Per-conversation UI state machines
185
- - `window.__debug.toolInstallMachineStates` - Tool installation state
186
- - `window.__debug.voiceMachineState` - TTS playback state
187
- - `window.__debug.convListMachineState` - Conversation list machine
188
- - `window.__debug.promptMachineState` - Prompt area state
189
- - `window.__debug.wsConnectionState` - WebSocket connection status
190
- - `window.__debug.getSyncState()` - Full snapshot of all machines at once
184
+ - `window.__debug.machines` - All XState machines (conv, toolInstall, voice, convList, prompt, recording, terminal, ws)
185
+ - `window.__debug.ws` - WebSocket state, latency EMA, latency trend, URL
186
+ - `window.__debug.auth` - Agent auth and OAuth state
187
+ - `window.__debug.perf` - Conversation perf metrics
188
+ - `window.__debug.config` - Base URL and server config
189
+ - `window.__debug.renderer` - Streaming renderer event queue / history lengths
190
+ - `window.__debug.conv` - Current conversation and streaming set
191
+ - `window.__debug.getState()` - WS latency snapshot
192
+ - `window.__debug.getSyncState()` - Flat snapshot of all machines (legacy shape)
193
+ - `window.__debug.getMessageState()` - Message/queue DOM counts
191
194
 
192
195
  See [CLAUDE.md](CLAUDE.md) for complete XState v5 machine documentation and internal architecture details.
193
196
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.872",
3
+ "version": "1.0.874",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/static/app.js CHANGED
@@ -1,5 +1,5 @@
1
- const BASE_URL = window.__BASE_URL || '';
2
-
1
+ const BASE_URL = window.__BASE_URL || '';
2
+
3
3
  (function initSidebarSearch() {
4
4
  const input = document.getElementById('sidebarSearchInput');
5
5
  if (!input) return;
@@ -183,4 +183,44 @@ const BASE_URL = window.__BASE_URL || '';
183
183
  });
184
184
 
185
185
  renderSelector();
186
- })();
186
+ })();
187
+
188
+ (function initSidebarOverflowMenu() {
189
+ const btn = document.getElementById('sidebarOverflowBtn');
190
+ const menu = document.getElementById('sidebarOverflowMenu');
191
+ if (!btn || !menu) return;
192
+ const close = () => menu.classList.remove('open');
193
+ btn.addEventListener('click', (e) => {
194
+ e.stopPropagation();
195
+ menu.classList.toggle('open');
196
+ });
197
+ document.addEventListener('click', (e) => {
198
+ if (!menu.contains(e.target) && e.target !== btn) close();
199
+ });
200
+ menu.addEventListener('click', (e) => {
201
+ if (e.target.closest('.sidebar-overflow-menu-item')) close();
202
+ });
203
+ document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
204
+ })();
205
+
206
+ (function initMobileSidebarToggle() {
207
+ const toggle = document.querySelector('[data-sidebar-toggle]');
208
+ const sidebar = document.querySelector('[data-sidebar]');
209
+ const overlay = document.querySelector('[data-sidebar-overlay]');
210
+ if (!toggle || !sidebar) return;
211
+ const close = () => {
212
+ sidebar.classList.remove('open');
213
+ if (overlay) overlay.classList.remove('open');
214
+ };
215
+ const isMobile = () => window.matchMedia('(max-width: 768px)').matches;
216
+ toggle.addEventListener('click', (e) => {
217
+ if (!isMobile()) {
218
+ sidebar.classList.toggle('collapsed');
219
+ return;
220
+ }
221
+ sidebar.classList.toggle('open');
222
+ if (overlay) overlay.classList.toggle('open');
223
+ });
224
+ if (overlay) overlay.addEventListener('click', close);
225
+ document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && isMobile()) close(); });
226
+ })();
@@ -0,0 +1,179 @@
1
+ /* =====================================================================
2
+ ui-fixes-2.css — Main panel, welcome screen, messages, input card
3
+ ===================================================================== */
4
+
5
+ /* ---------- Main header: cleaner separation ---------- */
6
+ .main-header {
7
+ gap: 0.5rem;
8
+ padding: 0 0.75rem;
9
+ border-bottom: 1px solid var(--color-border);
10
+ }
11
+ .header-title {
12
+ font-size: 0.9375rem;
13
+ font-weight: 600;
14
+ color: var(--color-text-primary);
15
+ }
16
+
17
+ /* ---------- View toggle bar: pill tabs, not underline-in-void ---------- */
18
+ .view-toggle-bar {
19
+ gap: 0.25rem;
20
+ padding: 0.375rem 0.75rem;
21
+ background: var(--color-bg-primary);
22
+ border-bottom: 1px solid var(--color-border);
23
+ }
24
+ .view-toggle-btn {
25
+ padding: 0.375rem 0.625rem;
26
+ border-radius: var(--radius-sm);
27
+ border: none;
28
+ background: transparent;
29
+ color: var(--color-text-secondary);
30
+ min-height: 32px;
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 0.375rem;
35
+ }
36
+ .view-toggle-btn:hover {
37
+ background: var(--color-bg-hover);
38
+ color: var(--color-text-primary);
39
+ }
40
+ .view-toggle-btn.active {
41
+ background: rgba(59,130,246,0.1);
42
+ color: var(--color-primary);
43
+ border-bottom-color: transparent;
44
+ }
45
+
46
+ /* ---------- Welcome screen ---------- */
47
+ .main-panel > .welcome-screen {
48
+ display: flex;
49
+ flex-direction: column;
50
+ align-items: center;
51
+ justify-content: center;
52
+ gap: 0.75rem;
53
+ padding: 2rem 1.5rem;
54
+ text-align: center;
55
+ flex: 1;
56
+ min-height: 0;
57
+ overflow-y: auto;
58
+ }
59
+ .welcome-logo {
60
+ width: 72px;
61
+ height: 72px;
62
+ border-radius: var(--radius-xl);
63
+ background: linear-gradient(135deg, #3b82f6, #6366f1);
64
+ color: #fff;
65
+ font-size: 2.25rem;
66
+ font-weight: 700;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ box-shadow: var(--shadow-md);
71
+ margin-bottom: 0.5rem;
72
+ }
73
+ .welcome-title {
74
+ font-size: 1.75rem;
75
+ font-weight: 700;
76
+ margin: 0;
77
+ color: var(--color-text-primary);
78
+ }
79
+ .welcome-subtitle {
80
+ font-size: 0.9375rem;
81
+ color: var(--color-text-secondary);
82
+ margin: 0 0 1.5rem;
83
+ }
84
+ .welcome-agents-grid {
85
+ display: grid;
86
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
87
+ gap: 0.75rem;
88
+ width: 100%;
89
+ max-width: 720px;
90
+ }
91
+
92
+ /* Hide welcome when empty / conversation active */
93
+ .main-panel > .welcome-screen.hidden { display: none; }
94
+
95
+ /* #output-scroll needs to take remaining flex space */
96
+ .main-panel > #output-scroll {
97
+ display: flex;
98
+ flex-direction: column;
99
+ flex: 1;
100
+ min-height: 0;
101
+ }
102
+
103
+ /* ---------- Messages scroll: breathing room ---------- */
104
+ #output-scroll {
105
+ background: var(--color-bg-primary);
106
+ }
107
+ .messages-wrapper {
108
+ max-width: 860px;
109
+ margin: 0 auto;
110
+ padding: 1rem 1.25rem 1.5rem;
111
+ width: 100%;
112
+ }
113
+
114
+ /* ---------- Streaming status bar: hidden by default, floating chip ---------- */
115
+ body .streaming-status-bar {
116
+ display: none !important;
117
+ align-items: center;
118
+ gap: 0.5rem;
119
+ padding: 0.5rem 0.875rem;
120
+ margin: 0 auto;
121
+ max-width: 860px;
122
+ background: var(--color-bg-secondary);
123
+ border: 1px solid var(--color-border);
124
+ border-radius: var(--radius-lg);
125
+ font-size: 0.8125rem;
126
+ color: var(--color-text-primary);
127
+ box-shadow: var(--shadow-sm);
128
+ }
129
+ body .streaming-status-bar.visible { display: flex !important; }
130
+ .streaming-status-bar .typing-dots {
131
+ display: inline-flex;
132
+ gap: 3px;
133
+ align-items: center;
134
+ }
135
+ .streaming-status-bar .typing-dots span {
136
+ width: 5px; height: 5px;
137
+ background: var(--color-primary);
138
+ border-radius: 50%;
139
+ animation: typingBlink 1.2s infinite ease-in-out both;
140
+ }
141
+ .streaming-status-bar .typing-dots span:nth-child(2) { animation-delay: 0.15s; }
142
+ .streaming-status-bar .typing-dots span:nth-child(3) { animation-delay: 0.3s; }
143
+ @keyframes typingBlink {
144
+ 0%,80%,100% { opacity: 0.3; transform: scale(0.8); }
145
+ 40% { opacity: 1; transform: scale(1); }
146
+ }
147
+ .status-agent-name { font-weight: 600; }
148
+ .status-cancel-btn {
149
+ margin-left: auto;
150
+ padding: 0.25rem 0.625rem;
151
+ background: transparent;
152
+ border: 1px solid var(--color-border);
153
+ border-radius: var(--radius-sm);
154
+ color: var(--color-text-secondary);
155
+ font-size: 0.75rem;
156
+ cursor: pointer;
157
+ }
158
+ .status-cancel-btn:hover {
159
+ border-color: var(--color-error);
160
+ color: var(--color-error);
161
+ }
162
+
163
+ /* ---------- Drop zone overlay ---------- */
164
+ .drop-zone-overlay {
165
+ position: absolute;
166
+ inset: 0;
167
+ display: none;
168
+ align-items: center;
169
+ justify-content: center;
170
+ background: rgba(59,130,246,0.1);
171
+ border: 2px dashed var(--color-primary);
172
+ border-radius: var(--radius-lg);
173
+ pointer-events: none;
174
+ z-index: 10;
175
+ }
176
+ .drop-zone-overlay.active { display: flex; }
177
+ .drop-zone-content { text-align: center; color: var(--color-primary); }
178
+ .drop-zone-icon { font-size: 3rem; font-weight: 300; }
179
+ .drop-zone-text { font-size: 0.9375rem; font-weight: 500; }
@@ -0,0 +1,235 @@
1
+ /* =====================================================================
2
+ ui-fixes-3.css — Input card (modern bottom composer), mobile, tools popup
3
+ ===================================================================== */
4
+
5
+ /* ---------- Input section: holds the card, proper bottom padding ---------- */
6
+ .input-section {
7
+ padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
8
+ background: var(--color-bg-primary);
9
+ border-top: 1px solid var(--color-border);
10
+ }
11
+
12
+ /* ---------- Input card: pill-shaped composer with toolbar ---------- */
13
+ .input-card {
14
+ max-width: 860px;
15
+ margin: 0 auto;
16
+ background: var(--color-bg-secondary);
17
+ border: 1px solid var(--color-border);
18
+ border-radius: var(--radius-lg);
19
+ padding: 0.5rem 0.625rem;
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 0.375rem;
23
+ transition: border-color 0.15s, box-shadow 0.15s;
24
+ }
25
+ .input-card:focus-within {
26
+ border-color: var(--color-primary);
27
+ box-shadow: 0 0 0 3px var(--color-focus-ring);
28
+ }
29
+ .input-card-textarea {
30
+ width: 100%;
31
+ min-height: 36px;
32
+ max-height: 180px;
33
+ padding: 0.375rem 0.5rem;
34
+ background: transparent;
35
+ border: none;
36
+ outline: none;
37
+ resize: none;
38
+ color: var(--color-text-primary);
39
+ font-family: inherit;
40
+ font-size: 0.9375rem;
41
+ line-height: 1.5;
42
+ }
43
+ .input-card-textarea::placeholder { color: var(--color-text-secondary); }
44
+
45
+ /* toolbar: selectors on the left, action buttons on the right */
46
+ .input-card-toolbar {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 0.375rem;
50
+ flex-wrap: wrap;
51
+ }
52
+ .input-card-selectors {
53
+ display: flex;
54
+ gap: 0.375rem;
55
+ align-items: center;
56
+ flex-wrap: wrap;
57
+ flex: 1;
58
+ min-width: 0;
59
+ }
60
+ .input-card-actions {
61
+ display: flex;
62
+ gap: 0.25rem;
63
+ align-items: center;
64
+ margin-left: auto;
65
+ flex-shrink: 0;
66
+ }
67
+
68
+ .input-chip-select {
69
+ height: 28px;
70
+ padding: 0 0.5rem;
71
+ font-size: 0.75rem;
72
+ background: var(--color-bg-primary);
73
+ color: var(--color-text-primary);
74
+ border: 1px solid var(--color-border);
75
+ border-radius: 999px;
76
+ cursor: pointer;
77
+ max-width: 180px;
78
+ transition: border-color 0.12s;
79
+ }
80
+ .input-chip-select:hover { border-color: var(--color-primary); }
81
+ .input-chip-select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-focus-ring); }
82
+ .input-chip-select:empty,
83
+ .input-chip-select[data-empty="true"] { display: none; }
84
+
85
+ /* When chip selects are empty and only star remains, compact layout */
86
+ #savePresetBtn { width: 28px !important; height: 28px !important; font-size: 0.875rem; }
87
+
88
+ /* Input card: if selectors row is empty (no visible chips), hide whole row */
89
+ .input-card-selectors:not(:has(select:not([style*="display: none"]):not(:empty):not([data-empty="true"]))):not(:has(#savePresetBtn + *)) {
90
+ /* do nothing: keep preset visible */
91
+ }
92
+
93
+ .input-icon-btn {
94
+ display: inline-flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ width: 32px;
98
+ height: 32px;
99
+ padding: 0;
100
+ background: transparent;
101
+ border: 1px solid transparent;
102
+ border-radius: var(--radius-sm);
103
+ color: var(--color-text-secondary);
104
+ cursor: pointer;
105
+ transition: background 0.12s, color 0.12s;
106
+ }
107
+ .input-icon-btn:hover {
108
+ background: var(--color-bg-hover);
109
+ color: var(--color-text-primary);
110
+ }
111
+ .input-icon-btn svg { width: 16px; height: 16px; }
112
+
113
+ .input-send-btn {
114
+ background: var(--color-primary);
115
+ color: #fff;
116
+ width: 34px;
117
+ height: 34px;
118
+ border-radius: var(--radius-md);
119
+ }
120
+ .input-send-btn:hover { background: var(--color-primary-dark); color: #fff; }
121
+ .input-send-btn svg { width: 16px; height: 16px; }
122
+ .input-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
123
+
124
+ /* When streaming: dim the card slightly */
125
+ .input-card[data-streaming="true"] {
126
+ opacity: 0.75;
127
+ }
128
+ .input-card[data-streaming="true"] .input-card-textarea {
129
+ cursor: not-allowed;
130
+ }
131
+
132
+ /* ---------- Tools popup: centered modal ---------- */
133
+ body .tools-popup {
134
+ display: none !important;
135
+ position: fixed;
136
+ inset: 0;
137
+ z-index: 200;
138
+ background: rgba(15,23,42,0.45);
139
+ align-items: center;
140
+ justify-content: center;
141
+ padding: 1rem;
142
+ }
143
+ body .tools-popup.open { display: flex !important; }
144
+ .tools-popup-content {
145
+ width: 100%;
146
+ max-width: 720px;
147
+ max-height: 85vh;
148
+ background: var(--color-bg-raised);
149
+ border: 1px solid var(--color-border);
150
+ border-radius: var(--radius-lg);
151
+ box-shadow: var(--shadow-lg);
152
+ display: flex;
153
+ flex-direction: column;
154
+ overflow: hidden;
155
+ }
156
+ .tools-popup-header {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ gap: 0.75rem;
161
+ padding: 0.75rem 1rem;
162
+ border-bottom: 1px solid var(--color-border);
163
+ background: var(--color-bg-secondary);
164
+ }
165
+ .tools-popup-scroll {
166
+ flex: 1;
167
+ overflow-y: auto;
168
+ padding: 0.75rem 1rem;
169
+ }
170
+ .tools-popup-footer {
171
+ display: flex;
172
+ gap: 0.5rem;
173
+ justify-content: flex-end;
174
+ padding: 0.75rem 1rem;
175
+ border-top: 1px solid var(--color-border);
176
+ background: var(--color-bg-secondary);
177
+ }
178
+ .tools-popup-refresh-btn,
179
+ .tools-popup-update-btn {
180
+ padding: 0.5rem 1rem;
181
+ font-size: 0.8125rem;
182
+ font-weight: 500;
183
+ border-radius: var(--radius-md);
184
+ border: 1px solid var(--color-border);
185
+ background: var(--color-bg-primary);
186
+ color: var(--color-text-primary);
187
+ cursor: pointer;
188
+ }
189
+ .tools-popup-refresh-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
190
+ .tools-popup-update-btn {
191
+ background: var(--color-primary);
192
+ color: #fff;
193
+ border-color: var(--color-primary);
194
+ }
195
+ .tools-popup-update-btn:hover { background: var(--color-primary-dark); }
196
+
197
+ /* ---------- Folder modal (if opened) ---------- */
198
+ body .folder-modal-overlay {
199
+ display: none !important;
200
+ position: fixed;
201
+ inset: 0;
202
+ z-index: 200;
203
+ background: rgba(15,23,42,0.45);
204
+ align-items: center;
205
+ justify-content: center;
206
+ padding: 1rem;
207
+ }
208
+ body .folder-modal-overlay.open { display: flex !important; }
209
+ .folder-modal {
210
+ width: 100%;
211
+ max-width: 560px;
212
+ max-height: 80vh;
213
+ background: var(--color-bg-raised);
214
+ border: 1px solid var(--color-border);
215
+ border-radius: var(--radius-lg);
216
+ box-shadow: var(--shadow-lg);
217
+ display: flex;
218
+ flex-direction: column;
219
+ overflow: hidden;
220
+ }
221
+ .folder-modal-header {
222
+ display: flex; justify-content: space-between; align-items: center;
223
+ padding: 0.75rem 1rem;
224
+ border-bottom: 1px solid var(--color-border);
225
+ }
226
+ .folder-modal-header h3 { margin: 0; font-size: 1rem; }
227
+ .folder-modal-close {
228
+ background: none; border: none; font-size: 1.5rem;
229
+ color: var(--color-text-secondary); cursor: pointer; line-height: 1;
230
+ }
231
+ .folder-modal-footer {
232
+ display: flex; gap: 0.5rem; justify-content: flex-end;
233
+ padding: 0.75rem 1rem;
234
+ border-top: 1px solid var(--color-border);
235
+ }
@@ -0,0 +1,153 @@
1
+ /* =====================================================================
2
+ ui-fixes-4.css — Responsive (mobile/tablet) + dark-theme polish
3
+ ===================================================================== */
4
+
5
+ /* ---------- Tablet (≤ 1024px): narrower sidebar ---------- */
6
+ @media (max-width: 1024px) {
7
+ :root { --sidebar-width: 240px; }
8
+ .messages-wrapper { padding: 0.875rem 1rem; }
9
+ }
10
+
11
+ /* ---------- Mobile (≤ 768px): sidebar becomes drawer ---------- */
12
+ @media (max-width: 768px) {
13
+ .app-shell { flex-direction: row; }
14
+
15
+ .sidebar {
16
+ position: fixed;
17
+ top: 0; left: 0;
18
+ height: 100dvh;
19
+ width: 280px;
20
+ max-width: 85vw;
21
+ transform: translateX(-100%);
22
+ z-index: 80;
23
+ box-shadow: var(--shadow-lg);
24
+ transition: transform 0.2s ease-out !important;
25
+ }
26
+ .sidebar.open { transform: translateX(0); }
27
+
28
+ .sidebar-overlay {
29
+ display: none;
30
+ position: fixed;
31
+ inset: 0;
32
+ background: rgba(15,23,42,0.5);
33
+ z-index: 70;
34
+ }
35
+ .sidebar-overlay.open { display: block; }
36
+
37
+ .main-panel { width: 100%; }
38
+ .main-header { padding: 0 0.5rem; }
39
+ .header-title { font-size: 0.875rem; }
40
+ .header-controls { gap: 0.25rem; }
41
+
42
+ /* hide tertiary chips in very tight space */
43
+ .status-badge span { display: none; }
44
+ .status-badge { padding: 0.25rem; }
45
+
46
+ /* input card on mobile */
47
+ .input-section { padding: 0.5rem 0.5rem calc(0.5rem + env(safe-area-inset-bottom)); }
48
+ .input-card { padding: 0.375rem 0.5rem; border-radius: var(--radius-md); }
49
+ .input-card-textarea { font-size: 1rem; /* prevent iOS zoom */ }
50
+ .input-chip-select { max-width: 140px; }
51
+
52
+ .welcome-title { font-size: 1.375rem; }
53
+ .welcome-logo { width: 56px; height: 56px; font-size: 1.75rem; }
54
+ .messages-wrapper { padding: 0.75rem 0.75rem 1rem; }
55
+ }
56
+
57
+ /* ---------- Small mobile (≤ 480px): hide noncritical header buttons ---------- */
58
+ @media (max-width: 480px) {
59
+ .model-dl-indicator { display: none; }
60
+ .sidebar-header h2 { display: none; }
61
+ .input-card-actions .input-icon-btn:not(.input-send-btn):not(.voice-mic-btn) { display: none; }
62
+ }
63
+
64
+ /* ---------- Touch targets: ensure 40px minimum on pointer:coarse ---------- */
65
+ @media (pointer: coarse) {
66
+ .sidebar-toggle-btn,
67
+ .theme-toggle-btn,
68
+ .header-icon-btn,
69
+ .input-icon-btn,
70
+ .sidebar-overflow-btn,
71
+ .view-toggle-btn,
72
+ .status-cancel-btn {
73
+ min-width: 40px;
74
+ min-height: 40px;
75
+ }
76
+ .conversation-item { padding: 0.625rem 0.625rem; }
77
+ }
78
+
79
+ /* ---------- Focus-visible ring: accessibility, non-intrusive ---------- */
80
+ button:focus-visible,
81
+ select:focus-visible,
82
+ input:focus-visible,
83
+ textarea:focus-visible {
84
+ outline: 2px solid var(--color-primary);
85
+ outline-offset: 2px;
86
+ }
87
+
88
+ /* ---------- Dark mode: primary blue, not muted gray ---------- */
89
+ html.dark {
90
+ --color-primary: #3b82f6;
91
+ --color-primary-dark: #2563eb;
92
+ --color-bg-primary: #0f172a;
93
+ --color-bg-secondary: #111a2e;
94
+ --color-bg-raised: #1a2440;
95
+ --color-bg-hover: rgba(255,255,255,0.05);
96
+ --color-text-primary: #e2e8f0;
97
+ --color-text-secondary: #94a3b8;
98
+ --color-border: #1e293b;
99
+ }
100
+ html.dark body { background: var(--color-bg-primary); color: var(--color-text-primary); }
101
+ html.dark .sidebar { background: #0b1220; border-right: 1px solid var(--color-border); }
102
+ html.dark .main-header,
103
+ html.dark .view-toggle-bar,
104
+ html.dark .input-section { background: var(--color-bg-primary); }
105
+ html.dark .input-card { background: #0b1220; }
106
+ html.dark .input-chip-select { background: #0b1220; }
107
+ html.dark .status-badge { background: #0b1220; }
108
+ html.dark .tools-popup,
109
+ html.dark .folder-modal-overlay { background: rgba(0,0,0,0.65); }
110
+
111
+ /* Light mode polish: softer sidebar separation */
112
+ html:not(.dark) .sidebar { border-right: 1px solid var(--color-border); }
113
+ html:not(.dark) .main-header { background: var(--color-bg-primary); }
114
+
115
+ /* ---------- Scrollbar styling (subtle) ---------- */
116
+ .sidebar-list,
117
+ #output-scroll,
118
+ .tools-popup-scroll {
119
+ scrollbar-width: thin;
120
+ scrollbar-color: var(--color-border) transparent;
121
+ }
122
+ .sidebar-list::-webkit-scrollbar,
123
+ #output-scroll::-webkit-scrollbar,
124
+ .tools-popup-scroll::-webkit-scrollbar {
125
+ width: 8px;
126
+ }
127
+ .sidebar-list::-webkit-scrollbar-thumb,
128
+ #output-scroll::-webkit-scrollbar-thumb,
129
+ .tools-popup-scroll::-webkit-scrollbar-thumb {
130
+ background: var(--color-border);
131
+ border-radius: 4px;
132
+ border: 2px solid transparent;
133
+ background-clip: padding-box;
134
+ }
135
+ .sidebar-list::-webkit-scrollbar-thumb:hover,
136
+ #output-scroll::-webkit-scrollbar-thumb:hover {
137
+ background: var(--color-text-secondary);
138
+ background-clip: padding-box;
139
+ border: 2px solid transparent;
140
+ }
141
+
142
+ /* ---------- Reduced motion ---------- */
143
+ @media (prefers-reduced-motion: reduce) {
144
+ *, *::before, *::after {
145
+ animation-duration: 0.01ms !important;
146
+ transition-duration: 0.01ms !important;
147
+ }
148
+ }
149
+
150
+ /* ---------- Hide legacy selectors leftovers if present ---------- */
151
+ .agent-selector:empty,
152
+ .model-selector:empty,
153
+ .preset-selector:empty { display: none; }
@@ -0,0 +1,193 @@
1
+ /* =====================================================================
2
+ ui-fixes.css — Design corrections layered on top of main.css
3
+ Targets styles missing or broken for current markup (index.html).
4
+ Loaded AFTER main.css so selectors here win on ties.
5
+ ===================================================================== */
6
+
7
+ /* ---------- Design tokens: richer spacing + elevation ---------- */
8
+ :root {
9
+ --radius-sm: 0.375rem;
10
+ --radius-md: 0.5rem;
11
+ --radius-lg: 0.75rem;
12
+ --radius-xl: 1rem;
13
+ --shadow-sm: 0 1px 2px rgba(15,23,42,0.06);
14
+ --shadow-md: 0 4px 12px rgba(15,23,42,0.08);
15
+ --shadow-lg: 0 12px 32px rgba(15,23,42,0.12);
16
+ --color-bg-raised: #ffffff;
17
+ --color-bg-hover: rgba(15,23,42,0.04);
18
+ --color-focus-ring: rgba(59,130,246,0.35);
19
+ --sidebar-width: 272px;
20
+ }
21
+ html.dark {
22
+ --color-bg-raised: #202020;
23
+ --color-bg-hover: rgba(255,255,255,0.05);
24
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
25
+ --shadow-md: 0 4px 12px rgba(0,0,0,0.45);
26
+ --shadow-lg: 0 12px 32px rgba(0,0,0,0.55);
27
+ --color-focus-ring: rgba(96,165,250,0.45);
28
+ }
29
+
30
+ /* ---------- Sidebar header: two rows, fix 422px overflow ---------- */
31
+ .sidebar-header {
32
+ flex-direction: row;
33
+ align-items: center;
34
+ justify-content: space-between;
35
+ gap: 0.5rem;
36
+ padding: 0.625rem 0.75rem;
37
+ min-height: var(--header-height);
38
+ border-bottom: 1px solid var(--color-border);
39
+ }
40
+ .sidebar-header h2 {
41
+ font-size: 0.75rem;
42
+ font-weight: 700;
43
+ letter-spacing: 0.08em;
44
+ color: var(--color-text-secondary);
45
+ flex: 1;
46
+ min-width: 0;
47
+ overflow: hidden;
48
+ text-overflow: ellipsis;
49
+ }
50
+ .sidebar-header-actions {
51
+ display: flex;
52
+ gap: 0.25rem;
53
+ align-items: center;
54
+ flex-shrink: 0;
55
+ }
56
+
57
+ /* ---------- Sidebar overflow menu: hidden by default, floating panel ---------- */
58
+ .sidebar-overflow-menu-wrapper {
59
+ position: relative;
60
+ display: inline-block;
61
+ }
62
+ .sidebar-overflow-btn {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ width: 30px;
67
+ height: 30px;
68
+ padding: 0;
69
+ background: transparent;
70
+ border: 1px solid transparent;
71
+ border-radius: var(--radius-sm);
72
+ color: var(--color-text-secondary);
73
+ cursor: pointer;
74
+ font-size: 1rem;
75
+ line-height: 1;
76
+ }
77
+ .sidebar-overflow-btn:hover {
78
+ background: var(--color-bg-hover);
79
+ color: var(--color-text-primary);
80
+ }
81
+ .sidebar-overflow-menu {
82
+ display: none;
83
+ position: absolute;
84
+ top: calc(100% + 4px);
85
+ right: 0;
86
+ min-width: 200px;
87
+ z-index: 60;
88
+ padding: 0.25rem;
89
+ background: var(--color-bg-raised);
90
+ border: 1px solid var(--color-border);
91
+ border-radius: var(--radius-md);
92
+ box-shadow: var(--shadow-md);
93
+ }
94
+ .sidebar-overflow-menu.open { display: block; }
95
+ .sidebar-overflow-menu-item {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 0.5rem;
99
+ width: 100%;
100
+ padding: 0.5rem 0.625rem;
101
+ background: transparent;
102
+ border: none;
103
+ border-radius: var(--radius-sm);
104
+ color: var(--color-text-primary);
105
+ font-size: 0.8125rem;
106
+ text-align: left;
107
+ cursor: pointer;
108
+ }
109
+ .sidebar-overflow-menu-item:hover { background: var(--color-bg-hover); }
110
+ .sidebar-overflow-menu-item.danger { color: var(--color-error); }
111
+ .sidebar-overflow-menu-item.danger:hover { background: rgba(239,68,68,0.1); }
112
+ .sidebar-overflow-menu-item svg { flex-shrink: 0; }
113
+
114
+ /* ---------- Sidebar new-conversation button ---------- */
115
+ .sidebar-new-btn {
116
+ padding: 0.375rem 0.75rem;
117
+ font-size: 0.8125rem;
118
+ font-weight: 600;
119
+ line-height: 1.2;
120
+ height: 30px;
121
+ }
122
+
123
+ /* ---------- Sidebar search ---------- */
124
+ .sidebar-search-bar {
125
+ padding: 0.5rem 0.75rem;
126
+ flex-shrink: 0;
127
+ }
128
+ .sidebar-search-input {
129
+ width: 100%;
130
+ padding: 0.5rem 0.75rem;
131
+ font-size: 0.8125rem;
132
+ background: var(--color-bg-primary);
133
+ border: 1px solid var(--color-border);
134
+ border-radius: var(--radius-md);
135
+ color: var(--color-text-primary);
136
+ outline: none;
137
+ transition: border-color 0.15s, box-shadow 0.15s;
138
+ }
139
+ .sidebar-search-input:focus {
140
+ border-color: var(--color-primary);
141
+ box-shadow: 0 0 0 3px var(--color-focus-ring);
142
+ }
143
+ .sidebar-search-input::placeholder { color: var(--color-text-secondary); }
144
+
145
+ /* ---------- Sidebar list items: cards ---------- */
146
+ .sidebar-list {
147
+ list-style: none;
148
+ padding: 0.25rem 0.5rem 1rem;
149
+ margin: 0;
150
+ overflow-y: auto;
151
+ flex: 1;
152
+ min-height: 0;
153
+ }
154
+ .conversation-item {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: 0.375rem;
158
+ padding: 0.5rem 0.625rem;
159
+ margin: 0.125rem 0;
160
+ border-radius: var(--radius-md);
161
+ cursor: pointer;
162
+ color: var(--color-text-primary);
163
+ transition: background-color 0.12s;
164
+ position: relative;
165
+ border-left: none;
166
+ }
167
+ .conversation-item-content {
168
+ flex: 1;
169
+ min-width: 0;
170
+ overflow: hidden;
171
+ }
172
+ .conversation-item:hover { background: var(--color-bg-hover); }
173
+ .conversation-item.active {
174
+ background: var(--color-primary);
175
+ color: #fff;
176
+ }
177
+ .conversation-item.active .conversation-item-meta { color: rgba(255,255,255,0.85); }
178
+ .conversation-item-title {
179
+ font-size: 0.8125rem;
180
+ font-weight: 500;
181
+ line-height: 1.3;
182
+ overflow: hidden;
183
+ text-overflow: ellipsis;
184
+ white-space: nowrap;
185
+ margin-bottom: 0.125rem;
186
+ }
187
+ .conversation-item-meta {
188
+ font-size: 0.6875rem;
189
+ color: var(--color-text-secondary);
190
+ overflow: hidden;
191
+ text-overflow: ellipsis;
192
+ white-space: nowrap;
193
+ }
package/static/index.html CHANGED
@@ -41,6 +41,10 @@
41
41
 
42
42
  <link rel="stylesheet" href="/gm/css/main.css">
43
43
  <link rel="stylesheet" href="/gm/css/tools-popup.css" media="print" onload="this.media='all'">
44
+ <link rel="stylesheet" href="/gm/css/ui-fixes.css">
45
+ <link rel="stylesheet" href="/gm/css/ui-fixes-2.css">
46
+ <link rel="stylesheet" href="/gm/css/ui-fixes-3.css">
47
+ <link rel="stylesheet" href="/gm/css/ui-fixes-4.css">
44
48
  </head>
45
49
  <body>
46
50
  <a href="#app" class="skip-link">Skip to conversation</a>