aicodeman 1.8.3 → 1.9.1

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.
Files changed (92) hide show
  1. package/README.md +80 -103
  2. package/README.zh-CN.md +80 -103
  3. package/dist/types/common.d.ts +25 -0
  4. package/dist/types/common.d.ts.map +1 -1
  5. package/dist/types/common.js +1 -0
  6. package/dist/types/common.js.map +1 -1
  7. package/dist/web/middleware/auth.d.ts.map +1 -1
  8. package/dist/web/middleware/auth.js +39 -1
  9. package/dist/web/middleware/auth.js.map +1 -1
  10. package/dist/web/public/admin-ui.js.gz +0 -0
  11. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  12. package/dist/web/public/app.adc992cd.js.gz +0 -0
  13. package/dist/web/public/constants.e7175b95.js.gz +0 -0
  14. package/dist/web/public/cron-ui.js.gz +0 -0
  15. package/dist/web/public/i18n.4dcf1b87.js.gz +0 -0
  16. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  17. package/dist/web/public/index.html +10 -7
  18. package/dist/web/public/index.html.br +0 -0
  19. package/dist/web/public/index.html.gz +0 -0
  20. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  21. package/dist/web/public/{keyboard-accessory.3f91acef.js → keyboard-accessory.9936227d.js} +361 -2
  22. package/dist/web/public/keyboard-accessory.9936227d.js.br +0 -0
  23. package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
  24. package/dist/web/public/mobile-handlers.bc2b8c1d.js.gz +0 -0
  25. package/dist/web/public/mobile.40e2d64e.css +1 -0
  26. package/dist/web/public/mobile.40e2d64e.css.br +0 -0
  27. package/dist/web/public/mobile.40e2d64e.css.gz +0 -0
  28. package/dist/web/public/notification-manager.fca811fd.js.gz +0 -0
  29. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  30. package/dist/web/public/panels-ui.3910e1e2.js.gz +0 -0
  31. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  32. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  33. package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
  34. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  35. package/dist/web/public/{session-ui.fd39c53c.js → session-ui.ab6dd9aa.js} +15 -15
  36. package/dist/web/public/session-ui.ab6dd9aa.js.br +0 -0
  37. package/dist/web/public/session-ui.ab6dd9aa.js.gz +0 -0
  38. package/dist/web/public/settings-ui.b8def032.js.gz +0 -0
  39. package/dist/web/public/styles.30e64547.css +1 -0
  40. package/dist/web/public/styles.30e64547.css.br +0 -0
  41. package/dist/web/public/styles.30e64547.css.gz +0 -0
  42. package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
  43. package/dist/web/public/sw.js.gz +0 -0
  44. package/dist/web/public/{terminal-ui.feaf75c2.js → terminal-ui.f3a51974.js} +1 -1
  45. package/dist/web/public/terminal-ui.f3a51974.js.br +0 -0
  46. package/dist/web/public/terminal-ui.f3a51974.js.gz +0 -0
  47. package/dist/web/public/ultracode-panel.js.gz +0 -0
  48. package/dist/web/public/ultracode-windows.js.gz +0 -0
  49. package/dist/web/public/upload.html.gz +0 -0
  50. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  51. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  52. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  53. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  54. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  55. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  56. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  57. package/dist/web/public/vendor/xterm.css.gz +0 -0
  58. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  59. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  60. package/dist/web/public/webview-tabs.js +43 -13
  61. package/dist/web/public/webview-tabs.js.br +0 -0
  62. package/dist/web/public/webview-tabs.js.gz +0 -0
  63. package/dist/web/routes/file-routes.d.ts.map +1 -1
  64. package/dist/web/routes/file-routes.js +313 -8
  65. package/dist/web/routes/file-routes.js.map +1 -1
  66. package/dist/web/routes/session-routes.d.ts.map +1 -1
  67. package/dist/web/routes/session-routes.js +173 -99
  68. package/dist/web/routes/session-routes.js.map +1 -1
  69. package/dist/web/schemas.d.ts +10 -0
  70. package/dist/web/schemas.d.ts.map +1 -1
  71. package/dist/web/schemas.js +30 -0
  72. package/dist/web/schemas.js.map +1 -1
  73. package/dist/web/server.d.ts.map +1 -1
  74. package/dist/web/server.js +13 -6
  75. package/dist/web/server.js.map +1 -1
  76. package/dist/web/webview-proxy.d.ts +23 -6
  77. package/dist/web/webview-proxy.d.ts.map +1 -1
  78. package/dist/web/webview-proxy.js +154 -6
  79. package/dist/web/webview-proxy.js.map +1 -1
  80. package/package.json +17 -6
  81. package/dist/web/public/keyboard-accessory.3f91acef.js.br +0 -0
  82. package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
  83. package/dist/web/public/mobile.6153d275.css +0 -1
  84. package/dist/web/public/mobile.6153d275.css.br +0 -0
  85. package/dist/web/public/mobile.6153d275.css.gz +0 -0
  86. package/dist/web/public/session-ui.fd39c53c.js.br +0 -0
  87. package/dist/web/public/session-ui.fd39c53c.js.gz +0 -0
  88. package/dist/web/public/styles.bd210c71.css +0 -1
  89. package/dist/web/public/styles.bd210c71.css.br +0 -0
  90. package/dist/web/public/styles.bd210c71.css.gz +0 -0
  91. package/dist/web/public/terminal-ui.feaf75c2.js.br +0 -0
  92. package/dist/web/public/terminal-ui.feaf75c2.js.gz +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Mobile keyboard accessory bar and modal focus trap.
3
3
  *
4
- * Defines two exports:
4
+ * Defines three exports:
5
5
  *
6
6
  * - KeyboardAccessoryBar (singleton object) — Quick action buttons shown above the virtual
7
7
  * keyboard on mobile: arrow up/down, /init, /clear, /compact, paste, Esc, and dismiss.
@@ -10,12 +10,15 @@
10
10
  * Destructive actions (/clear, /compact) require double-tap confirmation (2s amber state).
11
11
  * Commands are sent as text + Enter separately for Ink compatibility.
12
12
  * Only initializes on touch devices (MobileDetection.isTouchDevice guard).
13
+ * - PathPicker (singleton object) — Lazy server-side file/folder browser shared
14
+ * by Link Existing and the extended mobile keyboard bar.
13
15
  *
14
16
  * - FocusTrap (class) — Traps Tab/Shift+Tab keyboard focus within a modal element.
15
17
  * Saves and restores previously focused element on deactivate. Used by Ralph wizard
16
18
  * and other modal dialogs.
17
19
  *
18
20
  * @globals {object} KeyboardAccessoryBar
21
+ * @globals {object} PathPicker
19
22
  * @globals {class} FocusTrap
20
23
  *
21
24
  * @dependency mobile-handlers.js (MobileDetection.isTouchDevice)
@@ -26,6 +29,338 @@
26
29
  // Codeman — Keyboard accessory bar and focus trap for modals
27
30
  // Loaded after mobile-handlers.js, before app.js
28
31
 
32
+ // ═══════════════════════════════════════════════════════════════
33
+ // Shared Filesystem Path Picker
34
+ // ═══════════════════════════════════════════════════════════════
35
+
36
+ const PathPicker = {
37
+ overlay: null,
38
+ _options: null,
39
+ _selectedPath: '',
40
+ _previousFocus: null,
41
+ _keydownHandler: null,
42
+ _loadSequence: 0,
43
+ _previewOverlay: null,
44
+ _previewRequestSequence: 0,
45
+ _previewPreviousFocus: null,
46
+
47
+ /**
48
+ * Open the lazy filesystem browser.
49
+ * @param {{sessionId?: string, initialPath?: string, directoriesOnly?: boolean,
50
+ * title?: string, onSelect: (path: string) => void}} options
51
+ */
52
+ open(options) {
53
+ this.close(false);
54
+ this._options = options;
55
+ this._selectedPath = '';
56
+ this._previousFocus = document.activeElement;
57
+ this._previousFocus?.blur?.();
58
+
59
+ const overlay = document.createElement('div');
60
+ overlay.className = 'path-picker-overlay';
61
+ overlay.setAttribute('role', 'dialog');
62
+ overlay.setAttribute('aria-modal', 'true');
63
+ overlay.setAttribute('aria-label', options.title || 'Select a path');
64
+ overlay.innerHTML = `
65
+ <div class="path-picker-dialog">
66
+ <div class="path-picker-header">
67
+ <strong class="path-picker-title"></strong>
68
+ <button type="button" class="path-picker-close" aria-label="Close">&times;</button>
69
+ </div>
70
+ <div class="path-picker-roots-row">
71
+ <label for="pathPickerRoot">Location</label>
72
+ <select id="pathPickerRoot" class="path-picker-roots"></select>
73
+ </div>
74
+ <div class="path-picker-nav">
75
+ <button type="button" class="path-picker-up" title="Parent folder" aria-label="Parent folder">&#x2191;</button>
76
+ <div class="path-picker-current" title="Current folder"></div>
77
+ <button type="button" class="path-picker-refresh" title="Refresh" aria-label="Refresh">&#x21BB;</button>
78
+ </div>
79
+ <div class="path-picker-status" aria-live="polite">Loading...</div>
80
+ <div class="path-picker-list" role="listbox"></div>
81
+ <div class="path-picker-selection">
82
+ <span class="path-picker-selection-label">Selected</span>
83
+ <span class="path-picker-selection-value">None</span>
84
+ </div>
85
+ <div class="path-picker-actions">
86
+ <button type="button" class="path-picker-current-select">Select Current Folder</button>
87
+ <span class="path-picker-action-spacer"></span>
88
+ <button type="button" class="path-picker-cancel">Cancel</button>
89
+ <button type="button" class="path-picker-confirm" disabled>Select</button>
90
+ </div>
91
+ </div>`;
92
+
93
+ this.overlay = overlay;
94
+ overlay.querySelector('.path-picker-title').textContent = options.title || 'Select a Path';
95
+ overlay.querySelector('.path-picker-close').addEventListener('click', () => this.close(true));
96
+ overlay.querySelector('.path-picker-cancel').addEventListener('click', () => this.close(true));
97
+ overlay.querySelector('.path-picker-confirm').addEventListener('click', () => this.confirm());
98
+ overlay.querySelector('.path-picker-current-select').addEventListener('click', () => {
99
+ const current = overlay.querySelector('.path-picker-current').textContent;
100
+ if (current) this.select(current);
101
+ });
102
+ overlay.querySelector('.path-picker-refresh').addEventListener('click', () => this.load());
103
+ overlay.querySelector('.path-picker-up').addEventListener('click', () => {
104
+ const parent = overlay.querySelector('.path-picker-up').dataset.parent;
105
+ if (parent) this.load(parent);
106
+ });
107
+ overlay.querySelector('.path-picker-roots').addEventListener('change', (event) => this.load(event.target.value));
108
+ overlay.addEventListener('click', (event) => {
109
+ if (event.target === overlay) this.close(true);
110
+ });
111
+ this._keydownHandler = (event) => {
112
+ if (event.key === 'Escape') {
113
+ event.preventDefault();
114
+ if (this._previewOverlay) this.closePreview(true);
115
+ else this.close(true);
116
+ }
117
+ };
118
+ document.addEventListener('keydown', this._keydownHandler);
119
+ document.body.appendChild(overlay);
120
+ this.load(options.initialPath || '');
121
+ },
122
+
123
+ async load(path) {
124
+ if (!this.overlay || !this._options) return;
125
+ const loadSequence = ++this._loadSequence;
126
+ const list = this.overlay.querySelector('.path-picker-list');
127
+ const status = this.overlay.querySelector('.path-picker-status');
128
+ list.replaceChildren();
129
+ status.textContent = 'Loading...';
130
+
131
+ const params = new URLSearchParams();
132
+ if (path) params.set('path', path);
133
+ if (this._options.sessionId) params.set('sessionId', this._options.sessionId);
134
+ try {
135
+ const response = await fetch(`/api/filesystem/browse?${params.toString()}`);
136
+ const result = await response.json();
137
+ if (!response.ok || !result.success) throw new Error(result.error || 'Failed to browse this folder');
138
+ if (!this.overlay || loadSequence !== this._loadSequence) return;
139
+ this.render(result.data);
140
+ } catch (error) {
141
+ if (!this.overlay || loadSequence !== this._loadSequence) return;
142
+ if (path) {
143
+ this.load('');
144
+ return;
145
+ }
146
+ status.textContent = error.message || 'Failed to browse this folder';
147
+ status.classList.add('error');
148
+ }
149
+ },
150
+
151
+ render(data) {
152
+ const rootSelect = this.overlay.querySelector('.path-picker-roots');
153
+ rootSelect.replaceChildren();
154
+ for (const root of data.roots) {
155
+ const option = document.createElement('option');
156
+ option.value = root.path;
157
+ option.textContent = `${root.label} — ${root.path}`;
158
+ option.selected = data.path === root.path || data.root === root.path;
159
+ rootSelect.appendChild(option);
160
+ }
161
+
162
+ this.overlay.querySelector('.path-picker-current').textContent = data.path;
163
+ const up = this.overlay.querySelector('.path-picker-up');
164
+ up.dataset.parent = data.parent || '';
165
+ up.disabled = !data.parent;
166
+ const status = this.overlay.querySelector('.path-picker-status');
167
+ status.classList.remove('error');
168
+ status.textContent = data.entries.length === 0
169
+ ? 'This folder is empty'
170
+ : `${data.entries.length} item${data.entries.length === 1 ? '' : 's'}${data.truncated ? ' (first 500)' : ''}`;
171
+
172
+ const list = this.overlay.querySelector('.path-picker-list');
173
+ list.replaceChildren();
174
+ for (const entry of data.entries) {
175
+ const row = document.createElement('div');
176
+ row.className = 'path-picker-item';
177
+ if (entry.type === 'file' && this._options.directoriesOnly && !entry.previewKind) {
178
+ row.classList.add('not-selectable');
179
+ }
180
+ row.dataset.path = entry.path;
181
+ row.dataset.type = entry.type;
182
+ row.setAttribute('role', 'option');
183
+
184
+ const open = document.createElement('button');
185
+ open.type = 'button';
186
+ open.className = 'path-picker-item-main';
187
+ const icon = document.createElement('span');
188
+ icon.className = 'path-picker-item-icon';
189
+ icon.textContent = entry.type === 'directory' ? '\uD83D\uDCC1' : '\uD83D\uDCC4';
190
+ const name = document.createElement('span');
191
+ name.className = 'path-picker-item-name';
192
+ name.textContent = entry.name;
193
+ open.append(icon, name);
194
+ if (entry.symlink) {
195
+ const link = document.createElement('span');
196
+ link.className = 'path-picker-item-link';
197
+ link.textContent = '\u2197';
198
+ open.appendChild(link);
199
+ }
200
+ if (entry.type === 'directory') {
201
+ const chevron = document.createElement('span');
202
+ chevron.className = 'path-picker-item-chevron';
203
+ chevron.textContent = '\u203A';
204
+ open.appendChild(chevron);
205
+ open.addEventListener('click', () => this.load(entry.path));
206
+ } else if (entry.previewKind) {
207
+ const preview = document.createElement('span');
208
+ preview.className = 'path-picker-item-preview';
209
+ preview.textContent = '\uD83D\uDC41';
210
+ open.appendChild(preview);
211
+ open.title = `Preview ${entry.name}`;
212
+ open.setAttribute('aria-label', `Preview ${entry.name}`);
213
+ open.addEventListener('click', () => this.openPreview(entry));
214
+ } else if (!this._options.directoriesOnly) {
215
+ open.addEventListener('click', () => this.select(entry.path));
216
+ } else {
217
+ open.disabled = true;
218
+ }
219
+ row.appendChild(open);
220
+
221
+ if (entry.type === 'directory' || !this._options.directoriesOnly) {
222
+ const choose = document.createElement('button');
223
+ choose.type = 'button';
224
+ choose.className = 'path-picker-item-select';
225
+ choose.textContent = 'Choose';
226
+ choose.addEventListener('click', () => this.select(entry.path));
227
+ row.appendChild(choose);
228
+ }
229
+ list.appendChild(row);
230
+ }
231
+ },
232
+
233
+ select(path) {
234
+ if (!this.overlay) return;
235
+ this._selectedPath = path;
236
+ this.overlay.querySelector('.path-picker-selection-value').textContent = path;
237
+ this.overlay.querySelector('.path-picker-confirm').disabled = false;
238
+ this.overlay.querySelectorAll('.path-picker-item').forEach((row) => {
239
+ const selected = row.dataset.path === path;
240
+ row.classList.toggle('selected', selected);
241
+ row.setAttribute('aria-selected', selected ? 'true' : 'false');
242
+ });
243
+ },
244
+
245
+ openPreview(entry) {
246
+ this.closePreview(false);
247
+ this._previewPreviousFocus = document.activeElement;
248
+ const requestSequence = ++this._previewRequestSequence;
249
+ const params = new URLSearchParams({ path: entry.path });
250
+ if (this._options?.sessionId) params.set('sessionId', this._options.sessionId);
251
+ const previewUrl = `/api/filesystem/preview?${params.toString()}`;
252
+
253
+ const overlay = document.createElement('div');
254
+ overlay.className = 'path-preview-overlay';
255
+ overlay.setAttribute('role', 'dialog');
256
+ overlay.setAttribute('aria-modal', 'true');
257
+ overlay.setAttribute('aria-label', `Preview ${entry.name}`);
258
+ overlay.innerHTML = `
259
+ <div class="path-preview-dialog">
260
+ <div class="path-preview-header">
261
+ <div class="path-preview-heading">
262
+ <strong class="path-preview-title"></strong>
263
+ <span class="path-preview-path"></span>
264
+ </div>
265
+ <a class="path-preview-open" target="_blank" rel="noopener noreferrer">Open</a>
266
+ <button type="button" class="path-preview-close" aria-label="Close preview">&times;</button>
267
+ </div>
268
+ <div class="path-preview-body"><div class="path-preview-loading">Loading preview...</div></div>
269
+ </div>`;
270
+ overlay.querySelector('.path-preview-title').textContent = entry.name;
271
+ overlay.querySelector('.path-preview-path').textContent = entry.path;
272
+ overlay.querySelector('.path-preview-open').href = previewUrl;
273
+ overlay.querySelector('.path-preview-close').addEventListener('click', () => this.closePreview(true));
274
+ overlay.addEventListener('click', (event) => {
275
+ if (event.target === overlay) this.closePreview(true);
276
+ });
277
+ document.body.appendChild(overlay);
278
+ this._previewOverlay = overlay;
279
+
280
+ const body = overlay.querySelector('.path-preview-body');
281
+ if (entry.previewKind === 'image') {
282
+ const image = document.createElement('img');
283
+ image.className = 'path-preview-image';
284
+ image.alt = entry.name;
285
+ image.addEventListener('load', () => body.querySelector('.path-preview-loading')?.remove());
286
+ image.addEventListener('error', () => this.showPreviewError('Image preview failed to load'));
287
+ image.src = previewUrl;
288
+ body.appendChild(image);
289
+ } else if (entry.previewKind === 'text') {
290
+ fetch(previewUrl)
291
+ .then(async (response) => {
292
+ const content = await response.text();
293
+ if (!response.ok) {
294
+ let message = 'Text preview failed to load';
295
+ try {
296
+ message = JSON.parse(content).error || message;
297
+ } catch {}
298
+ throw new Error(message);
299
+ }
300
+ return content;
301
+ })
302
+ .then((content) => {
303
+ if (!this._previewOverlay || requestSequence !== this._previewRequestSequence) return;
304
+ const pre = document.createElement('pre');
305
+ pre.className = 'path-preview-text';
306
+ pre.textContent = content;
307
+ body.replaceChildren(pre);
308
+ })
309
+ .catch((error) => {
310
+ if (requestSequence === this._previewRequestSequence) this.showPreviewError(error.message);
311
+ });
312
+ } else {
313
+ const frame = document.createElement('iframe');
314
+ frame.className = 'path-preview-frame';
315
+ frame.title = entry.name;
316
+ frame.addEventListener('load', () => body.querySelector('.path-preview-loading')?.remove());
317
+ frame.src = previewUrl;
318
+ body.appendChild(frame);
319
+ }
320
+ overlay.querySelector('.path-preview-close').focus();
321
+ },
322
+
323
+ showPreviewError(message) {
324
+ const body = this._previewOverlay?.querySelector('.path-preview-body');
325
+ if (!body) return;
326
+ const error = document.createElement('div');
327
+ error.className = 'path-preview-error';
328
+ error.textContent = message || 'Preview failed to load';
329
+ body.replaceChildren(error);
330
+ },
331
+
332
+ closePreview(restoreFocus = true) {
333
+ this._previewRequestSequence += 1;
334
+ this._previewOverlay?.remove();
335
+ this._previewOverlay = null;
336
+ const previousFocus = this._previewPreviousFocus;
337
+ this._previewPreviousFocus = null;
338
+ if (restoreFocus) previousFocus?.focus?.();
339
+ },
340
+
341
+ confirm() {
342
+ if (!this._selectedPath || !this._options) return;
343
+ const selectedPath = this._selectedPath;
344
+ const onSelect = this._options.onSelect;
345
+ this.close(false);
346
+ onSelect(selectedPath);
347
+ },
348
+
349
+ close(restoreFocus = true) {
350
+ if (this._keydownHandler) document.removeEventListener('keydown', this._keydownHandler);
351
+ this._keydownHandler = null;
352
+ this._loadSequence += 1;
353
+ this.closePreview(false);
354
+ this.overlay?.remove();
355
+ this.overlay = null;
356
+ const previousFocus = this._previousFocus;
357
+ this._previousFocus = null;
358
+ this._options = null;
359
+ this._selectedPath = '';
360
+ if (restoreFocus) previousFocus?.focus?.();
361
+ },
362
+ };
363
+
29
364
  // ═══════════════════════════════════════════════════════════════
30
365
  // Mobile Keyboard Accessory Bar
31
366
  // ═══════════════════════════════════════════════════════════════
@@ -92,6 +427,8 @@ const KeyboardAccessoryBar = {
92
427
  <rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
93
428
  </svg>
94
429
  </button>
430
+ <button class="accessory-btn" data-action="pick-path" title="Insert a file or folder path">&#x1F4C1; Path</button>
431
+ <button class="accessory-btn" data-action="clear-input" title="Clear the current unsent input">&#x232B; All</button>
95
432
  <button class="accessory-btn" data-action="tab" title="Tab">Tab</button>
96
433
  <button class="accessory-btn" data-action="shift-tab" title="Shift+Tab">⇧Tab</button>
97
434
  <button class="accessory-btn" data-action="effort-max" title="/effort max">Max</button>
@@ -128,7 +465,7 @@ const KeyboardAccessoryBar = {
128
465
  this.handleAction(action, btn);
129
466
 
130
467
  // Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
131
- const refocusActions = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'ctrl-o', 'opt-enter', 'esc', 'effort-max']);
468
+ const refocusActions = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'ctrl-o', 'opt-enter', 'esc', 'effort-max', 'clear-input']);
132
469
  if (refocusActions.has(action) ||
133
470
  ((action === 'clear' || action === 'compact') && this._confirmAction)) {
134
471
  if (typeof app !== 'undefined' && app.terminal) {
@@ -207,6 +544,12 @@ const KeyboardAccessoryBar = {
207
544
  case 'paste':
208
545
  this.pasteFromClipboard();
209
546
  break;
547
+ case 'pick-path':
548
+ this.pickPath();
549
+ break;
550
+ case 'clear-input':
551
+ app.clearTerminalInput?.();
552
+ break;
210
553
  case 'dismiss':
211
554
  // Blur active element to dismiss keyboard
212
555
  document.activeElement?.blur();
@@ -265,6 +608,22 @@ const KeyboardAccessoryBar = {
265
608
  }).catch(() => {});
266
609
  },
267
610
 
611
+ /** Browse the active session's workspace and insert a selected path without Enter. */
612
+ pickPath() {
613
+ if (!app.activeSessionId) return;
614
+ const session = app.sessions?.get(app.activeSessionId);
615
+ PathPicker.open({
616
+ title: 'Insert File or Folder Path',
617
+ sessionId: app.activeSessionId,
618
+ initialPath: session?.workingDir || '',
619
+ directoriesOnly: false,
620
+ onSelect: (path) => {
621
+ app.insertTerminalText?.(path);
622
+ setTimeout(() => app.terminal?.focus(), 100);
623
+ },
624
+ });
625
+ },
626
+
268
627
  /** Show a paste overlay for iOS compatibility.
269
628
  * Handles three input paths from one dialog:
270
629
  * - Text: long-press the textarea → Paste → Send (unchanged).
@@ -0,0 +1 @@
1
+ html.mobile-init .header-font-controls,html.mobile-init .header-system-stats,html.mobile-init .header-tokens,html.mobile-init .monitor-panel,html.mobile-init .subagents-panel,html.mobile-init .project-insights-panel,html.mobile-init .file-browser-panel{display:none!important}@media(max-width:768px){input,textarea,select{font-size:16px!important}html{touch-action:manipulation}.session-tab .tab-detach{display:none!important}}@media(max-width:768px)and (min-width:430px){.header{position:fixed;top:0;left:0;right:0;min-height:48px;max-height:48px;padding:.35rem .5rem;padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right));background:#0a0a0a;border-bottom:1px solid rgba(255,255,255,.08);contain:none;overflow:visible;z-index:1200}.ios-device .header{padding-top:calc(.35rem + var(--safe-area-top));min-height:calc(48px + var(--safe-area-top));max-height:calc(48px + var(--safe-area-top))}.app{padding-top:54px}.ios-device .app{padding-top:calc(54px + var(--safe-area-top))}.header-font-controls{gap:.15rem;padding:.15rem .25rem}.header-font-controls .btn-icon-sm{width:18px;height:18px}.header-system-stats{font-size:.6rem;gap:.25rem}.header-right{gap:.35rem}.btn-icon-header:not(.btn-sm){width:44px;height:44px}.session-tabs,.session-tabs.tabs-two-rows{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none;max-height:52px;gap:3px}.session-tabs::-webkit-scrollbar{display:none}.session-tab{padding:.4rem .6rem;font-size:.75rem;min-height:40px}.session-tab .tab-name{max-width:80px}.toolbar{padding:.4rem .5rem;padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right));gap:.4rem}.tab-count-group{display:none}.btn-toolbar{padding:.4rem .8rem;font-size:.8rem;min-height:40px}.case-select-group{max-width:none}.case-combobox{width:160px}.toolbar-select{font-size:.75rem}.monitor-panel,.subagents-panel{width:100%;max-width:100%;left:0;right:0;border-radius:8px 8px 0 0;max-height:40vh}.project-insights-panel{max-width:280px;font-size:.7rem}.modal{z-index:1300}.command-palette-modal{padding:10vh .75rem 0}.command-palette-shell{width:100%;max-height:74vh}.command-palette-input-row{grid-template-columns:20px minmax(0,1fr)}.command-palette-input-row kbd{display:none}.command-palette-item{min-height:56px}.modal-tabs{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;flex-wrap:nowrap}.modal-tabs::-webkit-scrollbar{display:none}.modal-tab-btn{padding:.4rem .75rem;font-size:.7rem;white-space:nowrap;flex-shrink:0}.settings-grid{gap:.4rem .75rem}.settings-item{font-size:.7rem}.event-type-grid{grid-template-columns:1fr 40px 40px 40px;gap:5px 6px}.event-label{font-size:.7rem}.subagent-window{width:320px;height:280px;min-width:240px;min-height:160px}.subagent-window-header{padding:.35rem .5rem}.subagent-window-title .id{font-size:.7rem;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.subagent-window-title .status{font-size:.55rem}.subagent-window-body{font-size:.7rem}.respawn-banner{padding:.25rem .5rem;padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right));font-size:.65rem}.respawn-compact-layout{gap:.75rem}.respawn-status-row1{gap:.4rem}.respawn-action-log{max-height:2.6em;font-size:.6rem}.respawn-countdown-timer{font-size:.55rem}.respawn-timer-bar{width:24px}.toolbar-center .btn-toolbar.btn-voice{display:flex!important}.toolbar{padding:0 .5rem;gap:.5rem}.toolbar-left,.toolbar-right,.toolbar-group{gap:.5rem}.btn-toolbar{padding:.4rem .75rem;font-size:.75rem;min-height:unset}}@media(max-width:430px){.header-brand{padding-right:.25rem;margin-right:.2rem;border-right:none}.header-brand .logo{font-size:.85rem}.header-brand .logo .logo-text{display:none}.header-brand .logo .logo-compact{display:inline}.header-font-controls{gap:.1rem;padding:.1rem .2rem}.header-font-controls .btn-icon-sm{width:16px;height:16px}.header-font-controls .font-size-display{font-size:.6rem;min-width:14px}.header-system-stats{font-size:.55rem;gap:.15rem}.header-tokens{font-size:.6rem;padding:.1rem .3rem}.header{position:fixed;top:0;left:0;right:0;min-height:36px;max-height:36px;padding:.15rem .3rem;padding-left:calc(.3rem + var(--safe-area-left));padding-right:calc(.3rem + var(--safe-area-right));gap:.15rem;overflow:hidden;background:#0a0a0a;border-bottom:1px solid rgba(255,255,255,.08);contain:none;z-index:1200}.ios-device .header{padding-top:calc(.15rem + var(--safe-area-top));min-height:calc(36px + var(--safe-area-top));max-height:calc(36px + var(--safe-area-top))}.app{padding-top:42px}.ios-device .app{padding-top:calc(42px + var(--safe-area-top))}.main{padding-bottom:calc(40px + var(--safe-area-bottom))}.ios-device.safari-browser .main{padding-bottom:calc(40px + var(--safe-area-bottom) + (100vh - var(--app-height, 100vh)))}.header-right{padding-left:.2rem;gap:.1rem;flex-shrink:0;border-left:none}.btn-icon-header{width:26px;height:26px;padding:0}.btn-icon-header svg{width:12px;height:12px}.btn-icon-header.btn-settings,.btn-icon-header.btn-lifecycle-log,.btn-icon-header.btn-away-digest,.btn-icon-header.btn-session-manager,.btn-icon-header.btn-file-viewer,.btn-admin-panel{display:none!important}.btn-voice-mobile{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;background:#1a2a3f;border:1px solid rgba(59,130,246,.3);border-radius:4px;color:#93c5fd;cursor:pointer;flex-shrink:0;order:5}.btn-voice-mobile svg{width:13px;height:13px}.btn-voice-mobile:active{background:#2a3a5f}.btn-voice-mobile.recording{background:#ef444440;border-color:#ef444499;color:#ef4444;animation:mobile-voice-pulse 1.2s ease-in-out infinite}@keyframes mobile-voice-pulse{0%,to{box-shadow:inset 0 0 0 1px #ef444459;background:#ef444433}50%{box-shadow:inset 0 0 0 2px #ef4444bf;background:#ef444459}}.btn-settings-mobile{display:inline-flex!important;align-items:center;justify-content:center;width:26px;height:26px;background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:4px;color:#9ca3af;font-size:.85rem;cursor:pointer;flex-shrink:0;order:6;position:relative}.btn-settings-mobile svg{width:13px;height:13px}.btn-settings-mobile:active{background:#ffffff1a;color:#fff}.app{overflow:hidden;position:relative}.keyboard-visible .app{position:fixed;inset:0 0 auto;height:var(--app-height, 100dvh)}.session-tabs,.session-tabs.tabs-two-rows{flex:1;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none;max-height:36px;gap:2px;padding:0}.session-tabs::-webkit-scrollbar{display:none}.session-tab{flex-shrink:0;min-height:32px;max-height:32px;padding:.35rem .5rem;font-size:.7rem;gap:.25rem;border-radius:4px}.session-tab .tab-status{width:4px;height:4px}.session-tab .tab-name{max-width:50px;overflow:hidden;text-overflow:ellipsis}.session-tab .tab-close,.session-tab .tab-gear{display:none}.session-tab.active .tab-gear{display:inline-flex;align-items:center;justify-content:center;font-size:.65rem;line-height:1;width:32px;height:32px;margin-left:auto;opacity:.6}.session-tab.active .tab-close{display:inline-flex;align-items:center;justify-content:center;font-size:1.1rem;line-height:1;width:20px;height:18px;margin-left:-2px}.toolbar{position:fixed;bottom:var(--safe-area-bottom);left:0;right:0;height:40px;min-height:40px;max-height:40px;padding:.3rem .4rem;padding-left:calc(.4rem + var(--safe-area-left));padding-right:calc(.4rem + var(--safe-area-right));flex-wrap:nowrap;gap:.3rem;align-items:center;justify-content:space-between;background:#111;border-top:1px solid rgba(255,255,255,.1);z-index:50;transition:transform .15s ease-out;will-change:transform}.ios-device.safari-browser .toolbar{bottom:calc(var(--safe-area-bottom) + (100vh - var(--app-height, 100vh)))}.keyboard-visible.ios-device.safari-browser .toolbar{bottom:var(--safe-area-bottom)}.keyboard-visible.ios-device.safari-browser .keyboard-accessory-bar{bottom:calc(var(--safe-area-bottom) + 40px)}.toolbar-center{display:flex!important;align-items:center}.toolbar-center .btn-toolbar.btn-voice,.toolbar-right,.version-display{display:none!important}.toolbar-left{flex:1 1 0;min-width:0;align-items:center;gap:.3rem}.toolbar-left .toolbar-group{display:flex;flex:1 1 0;min-width:0;align-items:center;gap:.3rem}.toolbar-left .toolbar-group:first-child{flex:1 1 0;min-width:0;justify-content:space-between;gap:.3rem;flex-wrap:nowrap;align-items:center}.tab-count-group{display:none!important}.btn-toolbar{min-height:26px!important;max-height:26px!important;height:26px!important;padding:0 .5rem!important;font-size:.65rem!important;border-radius:4px;line-height:26px;display:inline-flex;align-items:center;justify-content:center;font-weight:500;letter-spacing:.01em;color:#fff}.run-btn-group{display:flex;flex:0 0 auto}.btn-toolbar.btn-run{flex:0 0 auto;padding:0 .6rem!important;font-weight:500;border-radius:4px 0 0 4px!important;border-right:none!important}.btn-toolbar.btn-run svg{width:10px;height:10px;margin-right:3px}.btn-toolbar.btn-run-gear{flex:0 0 auto;padding:0 .35rem!important;min-width:unset!important;border-radius:0 4px 4px 0!important;border-left:1px solid rgba(255,255,255,.15)!important}.btn-toolbar.btn-run-gear svg{width:10px;height:10px}.btn-toolbar.btn-run.mode-claude,.btn-toolbar.btn-run-gear.mode-claude{background:#1e3a5f;border-color:#3b82f64d;color:#93c5fd}.btn-toolbar.btn-run.mode-claude:active,.btn-toolbar.btn-run-gear.mode-claude:active{background:#2563eb;border-color:#3b82f680}.btn-toolbar.btn-run.mode-opencode,.btn-toolbar.btn-run-gear.mode-opencode{background:#0a2e2a;border-color:#10b9814d;color:#6ee7b7}.btn-toolbar.btn-run.mode-opencode:active,.btn-toolbar.btn-run-gear.mode-opencode:active{background:#0d4a40;border-color:#10b98180}.btn-toolbar.btn-run.mode-gemini,.btn-toolbar.btn-run-gear.mode-gemini{background:#10243f;border-color:#60a5fa4d;color:#dbeafe}.btn-toolbar.btn-run.mode-gemini:active,.btn-toolbar.btn-run-gear.mode-gemini:active{background:#174ea6;border-color:#60a5fa80}.run-mode-menu{bottom:100%;left:0;margin-bottom:6px;min-width:160px;max-width:80vw}.run-mode-option{padding:10px 12px;font-size:.8rem;cursor:pointer;-webkit-tap-highlight-color:rgba(255,255,255,.1)}.run-mode-row-btn{width:34px;height:34px;font-size:1rem;-webkit-tap-highlight-color:rgba(255,255,255,.1)}.run-mode-webview-delete{font-size:1.2rem}.run-mode-history{-webkit-overflow-scrolling:touch;touch-action:manipulation}.btn-toolbar.btn-stop{display:flex!important;flex:0 0 auto;padding:0 8px!important;min-width:unset;order:3}.btn-toolbar.btn-stop svg{width:10px;height:10px;margin-right:0}.btn-toolbar.btn-shell{display:none!important}.btn-toolbar.btn-enter{display:flex!important;flex:0 0 auto;align-items:center;justify-content:center;min-width:54px;width:54px;white-space:nowrap;padding:0 8px!important;overflow:hidden;font-size:.65rem;font-weight:600;letter-spacing:.01em;background:#1e3a5fd9!important;border:1px solid rgba(59,130,246,.45)!important;color:#dbeafe!important;order:4}.btn-toolbar.btn-enter:hover,.btn-toolbar.btn-enter:active{background:#254a7af2!important;border-color:#3b82f6b3!important;color:#fff!important}.case-select-group{display:none!important}.toolbar-left .toolbar-group:first-child{width:100%;gap:8px}.btn-toolbar.btn-case-mobile{display:flex!important;flex:1 1 0!important;min-width:0;padding:0 8px!important;order:2;overflow:hidden;justify-content:center;gap:4px}.btn-toolbar.btn-case-mobile #mobileCaseName{max-width:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn-case-settings-mobile{display:inline-flex!important;align-items:center;justify-content:center;width:26px;height:26px;background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:4px;color:#9ca3af;cursor:pointer;flex-shrink:0;order:1}.btn-case-settings-mobile:active{background:#ffffff1a;color:#fff}@media(max-width:374px){.toolbar{padding:0 2px}.toolbar-left,.toolbar-left .toolbar-group,.toolbar-left .toolbar-group:first-child{gap:2px}}.btn-case-settings-mobile{display:none!important}.case-settings-popover-mobile{position:fixed;bottom:calc(var(--safe-area-bottom) + 46px);left:8px;right:8px;background:#1e1e1e;border:1px solid rgba(255,255,255,.15);border-radius:8px;padding:.6rem .75rem;z-index:1000;box-shadow:0 4px 16px #00000080;display:block}.case-settings-popover-mobile.hidden{display:none!important}.case-settings-popover-mobile .checkbox-inline{display:flex;align-items:center;gap:6px;font-size:.75rem;color:#e5e7eb}.case-settings-popover-mobile .form-hint{display:block;margin-top:.2rem;font-size:.6rem;color:#6b7280}.keyboard-accessory-bar{display:none;position:fixed;bottom:calc(var(--safe-area-bottom) + 40px);left:0;right:0;height:44px;background:#1a1a1a;border-top:1px solid rgba(255,255,255,.1);padding:6px 8px;padding-left:calc(8px + var(--safe-area-left));padding-right:calc(8px + var(--safe-area-right));gap:8px;align-items:center;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;z-index:51;transition:transform .15s ease-out;will-change:transform}.keyboard-accessory-bar.visible{display:flex}.keyboard-accessory-bar::-webkit-scrollbar{display:none}.accessory-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;gap:4px;padding:6px 12px;background:#2a2a2a;border:1px solid rgba(255,255,255,.15);border-radius:6px;color:#e5e5e5;font-size:.65rem;font-weight:500;cursor:pointer;transition:background .15s,border-color .15s}.accessory-btn.confirming{background:#6b4f00;border-color:#b8860b;color:#ffd54f}.accessory-btn:active{background:#3a3a3a}.accessory-btn svg{width:14px;height:14px}.accessory-btn-arrow{padding:6px 10px;background:#2563eb;border-color:#3b82f680;color:#fff}.accessory-btn-arrow:active{background:#1d4ed8}.accessory-btn-dismiss{margin-left:auto;flex:1 1 0;max-width:100px;padding:10px 8px;background:#2563eb;border-color:#3b82f680;color:#fff;font-weight:600}.accessory-btn-dismiss svg{width:22px;height:22px}.accessory-btn-dismiss:active{background:#1d4ed8}.voice-preview{bottom:calc(var(--safe-area-bottom) + 94px);font-size:.8rem;max-width:90%;padding:6px 14px}.btn-case-add,.btn-case-settings,.toolbar-select{display:none!important}.toolbar .btn-case-add{min-width:26px!important;max-width:26px!important;width:26px!important;min-height:26px!important;max-height:26px!important;height:26px!important;padding:0!important;font-size:.9rem;font-weight:700;display:inline-flex!important;align-items:center;justify-content:center;line-height:1;border-radius:4px;background:transparent;border:1px solid rgba(255,255,255,.15);color:#9ca3af}.btn-case-add:hover,.btn-case-add:active{background:#ffffff1a;color:#fff}.monitor-panel,.subagents-panel{width:100%;max-width:100%;left:0;right:0;border-radius:8px 8px 0 0;bottom:calc(44px + 2rem + var(--safe-area-bottom));max-height:35vh}.modal{z-index:1300}.modal-content{width:100%;max-width:100%;height:100%;max-height:100%;border-radius:0;margin:0;display:flex;flex-direction:column}.modal-content.modal-sm{height:auto;max-height:85vh;border-radius:12px;margin:1rem;width:calc(100% - 2rem)}.ios-device .modal-content{padding-top:var(--safe-area-top);padding-bottom:var(--safe-area-bottom);padding-left:var(--safe-area-left);padding-right:var(--safe-area-right)}.modal-header{padding:.75rem 1rem}.modal-header h3{font-size:1rem}.modal-body{padding:.75rem 1rem;flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}.away-digest-modal{width:100%;max-width:100%;height:100%;max-height:100%}.away-digest-ranges{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.4rem}.away-digest-ranges .filter-btn{min-height:38px;padding:.4rem .5rem}.away-digest-custom-range,.away-digest-custom-range.active{grid-template-columns:1fr}.away-digest-summary{grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem}.away-digest-item{grid-template-columns:1fr;gap:.5rem}.away-digest-action{width:100%;min-height:38px}.modal-footer,.form-actions{padding:.75rem 1rem;padding-bottom:calc(.75rem + var(--safe-area-bottom))}.ios-device .terminal-container{padding-bottom:var(--safe-area-bottom)}.main{flex:1;min-height:0}.cli-info-bar{display:block}.terminal-container{height:100%;min-height:0;position:relative;overflow:visible;touch-action:none}.terminal-container .xterm,.terminal-container .xterm-viewport,.terminal-container .xterm-screen{touch-action:none}.response-viewer{padding-bottom:var(--safe-area-bottom, 0px);max-height:88vh;max-height:92dvh}.response-viewer-body{font-size:14.5px;line-height:1.65;padding:16px 16px 24px;--rv-content-max: 100%}.response-viewer-body .rv-text pre,.response-viewer-body pre{padding:12px 14px;margin-left:-4px;margin-right:-4px;border-radius:6px}.response-viewer-body .rv-text pre code,.response-viewer-body pre code{font-size:11.5px;line-height:1.5}.response-viewer-body .rv-text h1,.response-viewer-body>h1{font-size:1.35em}.response-viewer-body .rv-text h2,.response-viewer-body>h2{font-size:1.2em}.response-viewer-body .rv-text h3,.response-viewer-body>h3{font-size:1.08em}.welcome-content{max-width:calc(100vw - 1.5rem);padding:1rem .75rem}.welcome-title{font-size:1.2rem;margin-bottom:.5rem}.welcome-desc{font-size:.8rem;margin-bottom:.4rem}.welcome-actions{flex-direction:column;gap:.5rem;margin-top:1rem}.welcome-btn{width:100%;justify-content:center;min-height:44px;padding:.75rem 1rem;font-size:.85rem}.history-show-more{display:block;margin-bottom:.75rem}.welcome-ralph-link{display:block;margin:.75rem auto 0}.welcome-hint{font-size:.7rem;margin-top:.75rem}.modal-wizard{display:flex;flex-direction:column}.wizard-progress{padding:.5rem}.wizard-step{padding:.3rem}.wizard-step-number{width:24px;height:24px;font-size:.7rem}.wizard-step-label{display:none}.timer-banner{padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right))}.respawn-banner{padding:.3rem .5rem;padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right));font-size:.65rem}.respawn-compact-layout{flex-direction:column;gap:.15rem}.respawn-status-col{flex-shrink:1;min-width:0;gap:.1rem}.respawn-status-row1{flex-wrap:wrap;gap:.25rem;row-gap:.1rem}.respawn-state{font-size:.6rem;padding:.05rem .3rem}.respawn-banner .detection-confidence{font-size:.5rem;padding:.02rem .2rem}.respawn-cycles{font-size:.55rem}.respawn-timer{font-size:.55rem;padding:0 .25rem}.respawn-tokens{font-size:.55rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:120px}.respawn-indicator{font-size:.65rem}.respawn-banner .btn-icon-only{min-width:36px;min-height:36px;font-size:1rem;display:flex;align-items:center;justify-content:center;margin-left:auto;border-radius:6px;flex-shrink:0}.respawn-status-row2{font-size:.55rem;gap:.3rem;min-height:0;flex-wrap:wrap}.respawn-banner .detection-hook,.respawn-banner .detection-ai-check,.respawn-banner .detection-status{font-size:.55rem;padding:.02rem .2rem}.respawn-countdown-timers{gap:.2rem}.respawn-countdown-timer{font-size:.5rem;padding:.02rem .2rem;gap:.15rem}.respawn-countdown-timer .respawn-timer-bar{display:none}.respawn-action-log{border-left:none;border-top:1px solid rgba(34,197,94,.15);padding-left:0;padding-top:.15rem;max-height:1.4em;font-size:.55rem;overflow:hidden}.respawn-header{flex-direction:row;gap:.4rem;align-items:center}.respawn-header .respawn-actions{display:flex;gap:.3rem;flex-shrink:0}.respawn-header .respawn-actions .btn-toolbar{min-height:28px!important;font-size:.7rem!important;padding:.2rem .5rem!important;border-radius:5px}.duration-presets{display:flex;flex-wrap:wrap;gap:.2rem}.duration-preset-btn{min-height:24px;padding:.1rem .4rem;font-size:.6rem;border-radius:4px;text-align:center}.duration-custom{display:flex;gap:.2rem;align-items:center}.duration-custom-input.visible{flex:0 1 auto}.duration-custom-input input{width:64px;min-height:24px;font-size:16px;padding:.1rem .3rem}.preset-selector{display:grid;grid-template-columns:1fr 1fr;gap:.25rem}.preset-selector select{grid-column:1 / -1;min-height:32px;font-size:16px;border-radius:5px;padding:.2rem .4rem}.preset-selector .btn{min-height:32px;font-size:.65rem;border-radius:5px;padding:.2rem .4rem}#sessionOptionsModal textarea{font-size:16px;min-height:32px!important;max-height:56px;height:auto!important;border-radius:6px;padding:.3rem .5rem}#modalRespawnPrompt{min-height:32px!important;max-height:56px}#modalRespawnKickstart{min-height:32px!important;max-height:48px}#sessionOptionsModal .checkbox-inline{min-height:30px;font-size:.75rem;padding:.15rem 0;gap:.4rem}#sessionOptionsModal .checkbox-inline input[type=checkbox]{width:18px;height:18px}#sessionOptionsModal .respawn-loop-title{font-size:.75rem}#sessionOptionsModal .respawn-loop-box .checkbox-inline{font-size:.65rem}#sessionOptionsModal .respawn-options-row{gap:.5rem}#sessionOptionsModal .form-section-header{margin-top:.75rem;padding-top:.5rem;font-size:.65rem}#sessionOptionsModal .form-row{margin-bottom:.5rem}#sessionOptionsModal .form-row label{font-size:.65rem;margin-bottom:.2rem}#sessionOptionsModal .form-hint{font-size:.6rem;margin-top:.2rem}.context-setting{padding:.625rem}.context-setting-header{flex-wrap:wrap;gap:.4rem}.context-setting-header .input-suffix-sm input{width:60px;font-size:16px;min-height:36px}.context-setting input[type=text]{font-size:16px;min-height:36px;padding:.4rem .6rem;border-radius:6px}.color-picker{gap:8px}.color-swatch{width:36px;height:36px;border-radius:6px}.form-row-switch{min-height:40px;gap:.4rem}.context-settings-grid input[type=number]{font-size:16px;min-height:36px}.ralph-limits-grid{grid-template-columns:1fr 1fr;gap:.5rem}.ralph-limits-grid .form-col input[type=number]{font-size:16px;min-height:36px;padding:.4rem .5rem;border-radius:6px}#modalRalphPhrase{font-size:16px;min-height:40px;border-radius:6px}.ralph-config-actions{margin-top:1rem}.ralph-config-actions .btn-toolbar{width:100%;min-height:44px;font-size:.85rem;border-radius:8px}.run-summary-filters{gap:.3rem;margin-bottom:.5rem}.filter-btn{padding:.35rem .75rem;font-size:.7rem;min-height:32px}.timeline-event{padding:.4rem .5rem;margin-bottom:.25rem;font-size:.7rem}.run-summary-footer{flex-direction:column;gap:.4rem;padding:.5rem 0}.run-summary-actions{flex-wrap:wrap;gap:.3rem}.run-summary-actions .btn-toolbar{flex:1;min-height:36px;font-size:.7rem;white-space:nowrap}.auto-refresh-label{font-size:.7rem}.ralph-panel{font-size:.75rem}.ralph-summary{padding:.4rem .5rem;padding-left:calc(.5rem + var(--safe-area-left));padding-right:calc(.5rem + var(--safe-area-right))}.project-insights-panel{max-width:100%;font-size:.65rem;bottom:calc(44px + 2rem + var(--safe-area-bottom))}.file-browser-panel{max-width:100%;max-height:50vh;bottom:calc(44px + 2rem + var(--safe-area-bottom))}.notification-drawer{width:100%;max-width:100%;right:0;border-radius:0;padding-left:var(--safe-area-left);padding-right:var(--safe-area-right);padding-bottom:var(--safe-area-bottom)}input,textarea,[contenteditable]{scroll-margin-bottom:200px;scroll-margin-top:80px}.keyboard-visible .modal-body{max-height:40vh;overflow-y:auto}.keyboard-visible #createCaseModal .modal-body{max-height:60vh}.mobile-case-picker .modal-backdrop{background:#00000080}.mobile-case-picker-sheet{max-height:60vh;padding-bottom:var(--safe-area-bottom);animation:slideUp .2s ease-out}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.mobile-case-picker-header .modal-close{width:32px;height:32px;font-size:1.5rem}.mobile-case-picker-footer{padding-bottom:calc(12px + var(--safe-area-bottom))}.modal-tabs{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;gap:.25rem;padding:0 .75rem .5rem;flex-wrap:nowrap}.modal-tabs::-webkit-scrollbar{display:none}.modal-tab-btn{padding:.35rem .6rem;font-size:.65rem;white-space:nowrap;flex-shrink:0}#createCaseModal .modal-tabs{gap:.5rem;padding:.5rem 1rem .75rem}#createCaseModal .modal-tab-btn{flex:1;min-height:44px;padding:.6rem 1rem;font-size:.8rem;font-weight:500;border-radius:8px;justify-content:center;text-align:center}#createCaseModal .form-row{margin-bottom:1rem}#createCaseModal .form-row label{font-size:.8rem;margin-bottom:.4rem;font-weight:500;color:#e5e7eb}#createCaseModal .form-row input[type=text]{min-height:44px;font-size:16px;padding:.5rem .75rem;border-radius:8px}#createCaseModal .form-row .form-hint{font-size:.65rem;margin-top:.35rem;line-height:1.4}#createCaseModal .form-actions{padding:.75rem 1rem;padding-bottom:calc(.75rem + var(--safe-area-bottom));gap:.75rem}#createCaseModal .form-actions .btn-toolbar{min-height:44px!important;max-height:44px!important;height:44px!important;font-size:.85rem!important;font-weight:500;border-radius:8px}#caseModalSubmit.loading{opacity:.6;pointer-events:none}#createCaseModal.from-mobile .modal-content{animation:caseModalSlideUp .25s ease-out}@keyframes caseModalSlideUp{0%{transform:translateY(30px);opacity:0}to{transform:translateY(0);opacity:1}}.btn-case-create-mobile{min-height:48px!important;max-height:48px!important;height:48px!important;font-size:.85rem!important;font-weight:500;border-radius:10px}.settings-grid{grid-template-columns:1fr;gap:.35rem}.settings-grid-3col{grid-template-columns:1fr 1fr 1fr}.settings-item{padding:.35rem .5rem;font-size:.7rem}.settings-item-label{font-size:.7rem}.settings-section-header{font-size:.6rem;padding:.35rem 0 .2rem;margin-top:.5rem}.form-row{margin-bottom:.5rem}.form-row label{font-size:.7rem;margin-bottom:.2rem}.form-row .form-hint{font-size:.6rem}.form-row input[type=text],.form-row input[type=number],.form-row textarea,.form-row .form-select,.form-row select{font-size:.75rem;padding:.35rem .5rem;min-height:32px}.form-row-switch{gap:.3rem}.form-row-switch>label:first-child{font-size:.7rem}.form-section-header{font-size:.6rem;margin-top:.75rem}.event-type-grid{grid-template-columns:1fr 36px 36px 36px;gap:4px 4px;padding:6px;margin-top:6px}.event-header{font-size:.55rem}.event-label{font-size:.65rem}.event-type-grid input[type=checkbox]{width:12px;height:12px}.form-actions{gap:.5rem}.form-actions .btn-toolbar{flex:1;min-height:36px!important;max-height:36px!important;height:36px!important;font-size:.75rem!important}.subagent-window{position:fixed;width:calc(100% - 8px);max-width:calc(100% - 8px);height:110px;min-height:80px;max-height:110px;min-width:200px;border-radius:6px;resize:none;box-shadow:0 2px 8px #0006}.subagent-window:after{display:none}.subagent-window-header{padding:.2rem .4rem;min-height:24px}.subagent-window-title{gap:.2rem;overflow:hidden}.subagent-window-title .icon{font-size:.7rem}.subagent-window-title .id{font-size:.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:45vw}.subagent-window-title .status{font-size:.45rem;padding:.05rem .15rem}.subagent-model-badge{font-size:.45rem!important;padding:.05rem .15rem!important}.subagent-window-actions button{font-size:.7rem;padding:.15rem .35rem;min-width:26px;min-height:26px;display:flex;align-items:center;justify-content:center}.subagent-window-body{font-size:.6rem;padding:.2rem .35rem}.subagent-window-body .activity-line{gap:.15rem;padding:.05rem 0}.subagent-window-body .activity-line .time{font-size:.5rem}.subagent-window-body .activity-line .tool-name,.subagent-window-body .activity-line .tool-detail{font-size:.55rem}.subagent-window-parent{display:none}.session-tab .tab-subagent-badge{height:14px;padding:0 3px;border-radius:7px;margin-left:2px}.session-tab .tab-subagent-badge .subagent-label{font-size:.45rem}.subagent-dropdown{position:fixed!important;left:8px!important;right:8px!important;bottom:auto!important;min-width:auto!important;max-width:none!important;border-radius:8px;transform:none!important}}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.header,.toolbar,.keyboard-accessory-bar){background:var(--glass-bg);border-color:var(--glass-border);color:var(--text)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.btn-voice-mobile,.btn-settings-mobile,.btn-toolbar.btn-shell,.toolbar .btn-case-add,.accessory-btn){background:var(--control-bg);border-color:var(--control-border);color:var(--text-dim)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.btn-voice-mobile:active,.btn-settings-mobile:active,.btn-toolbar.btn-shell:hover,.btn-toolbar.btn-shell:active,.btn-case-add:hover,.btn-case-add:active,.accessory-btn:active){background:var(--control-bg-hover);border-color:var(--control-border-hover);color:var(--text)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.btn-toolbar.btn-run.mode-claude,.btn-toolbar.btn-run-gear.mode-claude){background:linear-gradient(135deg,var(--accent-grad-a),var(--accent-grad-b));border-color:var(--accent);color:var(--accent-ink)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.btn-toolbar.btn-run.mode-opencode,.btn-toolbar.btn-run-gear.mode-opencode){background:linear-gradient(135deg,var(--accent-d),var(--accent-grad-b));border-color:var(--accent);color:var(--accent-ink)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.btn-toolbar.btn-run.mode-gemini,.btn-toolbar.btn-run-gear.mode-gemini){background:linear-gradient(135deg,#174ea6,#4f46e5);border-color:#315fc3;color:#fff}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) .btn-toolbar.btn-run-gear{border-left-color:var(--control-border-hover)!important}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.case-settings-popover-mobile,.mobile-case-picker-sheet){background:var(--floating-bg);border-color:var(--control-border);color:var(--text);box-shadow:var(--elevated-shadow)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) :is(.case-settings-popover-mobile .checkbox-inline,#createCaseModal .form-row label){color:var(--text)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) .case-settings-popover-mobile .form-hint{color:var(--text-muted)}html:is([data-skin=paper-gray],[data-skin=solarized-light],[data-skin=catppuccin-latte],[data-skin=rose-pine-dawn]) .mobile-case-picker .modal-backdrop{background:var(--modal-backdrop)}.ios-device.safari-browser{overscroll-behavior:none}@media(hover:none)and (pointer:coarse){.session-tab .tab-close,.session-tab .tab-gear{opacity:1;width:auto;padding:.15rem .25rem;align-items:center;justify-content:center}.btn-toolbar:hover,.btn-icon-header:hover{transform:none}.subagent-dropdown-trigger:active+.subagent-dropdown-menu,.subagent-dropdown-trigger:focus+.subagent-dropdown-menu{display:block}}@media(max-width:430px){.attachment-history-drawer{top:0;bottom:0;width:100%;max-width:100%;height:100vh;height:100dvh;border-left:none;border-radius:0;padding-top:var(--safe-area-top);padding-left:var(--safe-area-left);padding-right:var(--safe-area-right);padding-bottom:var(--safe-area-bottom)}.attachment-history-header{padding:12px}.attachment-history-list{padding:6px}.attachment-history-item{grid-template-columns:104px minmax(0,1fr);gap:8px;padding:8px 6px}.attachment-history-thumb{width:104px}.attachment-history-actions{gap:4px}.attachment-history-actions button{padding:4px 7px}}