aicodeman 1.8.3 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) 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/public/admin-ui.js.gz +0 -0
  8. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  9. package/dist/web/public/app.adc992cd.js.gz +0 -0
  10. package/dist/web/public/constants.e7175b95.js.gz +0 -0
  11. package/dist/web/public/cron-ui.js.gz +0 -0
  12. package/dist/web/public/i18n.4dcf1b87.js.gz +0 -0
  13. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  14. package/dist/web/public/index.html +10 -7
  15. package/dist/web/public/index.html.br +0 -0
  16. package/dist/web/public/index.html.gz +0 -0
  17. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  18. package/dist/web/public/{keyboard-accessory.3f91acef.js → keyboard-accessory.9936227d.js} +361 -2
  19. package/dist/web/public/keyboard-accessory.9936227d.js.br +0 -0
  20. package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
  21. package/dist/web/public/mobile-handlers.bc2b8c1d.js.gz +0 -0
  22. package/dist/web/public/mobile.40e2d64e.css +1 -0
  23. package/dist/web/public/mobile.40e2d64e.css.br +0 -0
  24. package/dist/web/public/mobile.40e2d64e.css.gz +0 -0
  25. package/dist/web/public/notification-manager.fca811fd.js.gz +0 -0
  26. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  27. package/dist/web/public/panels-ui.3910e1e2.js.gz +0 -0
  28. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  29. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  30. package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
  31. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  32. package/dist/web/public/{session-ui.fd39c53c.js → session-ui.ab6dd9aa.js} +15 -15
  33. package/dist/web/public/session-ui.ab6dd9aa.js.br +0 -0
  34. package/dist/web/public/session-ui.ab6dd9aa.js.gz +0 -0
  35. package/dist/web/public/settings-ui.b8def032.js.gz +0 -0
  36. package/dist/web/public/{styles.bd210c71.css → styles.42dd71e1.css} +1 -1
  37. package/dist/web/public/styles.42dd71e1.css.br +0 -0
  38. package/dist/web/public/styles.42dd71e1.css.gz +0 -0
  39. package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
  40. package/dist/web/public/sw.js.gz +0 -0
  41. package/dist/web/public/{terminal-ui.feaf75c2.js → terminal-ui.f3a51974.js} +1 -1
  42. package/dist/web/public/terminal-ui.f3a51974.js.br +0 -0
  43. package/dist/web/public/terminal-ui.f3a51974.js.gz +0 -0
  44. package/dist/web/public/ultracode-panel.js.gz +0 -0
  45. package/dist/web/public/ultracode-windows.js.gz +0 -0
  46. package/dist/web/public/upload.html.gz +0 -0
  47. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  48. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  49. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  50. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  51. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  52. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  53. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  54. package/dist/web/public/vendor/xterm.css.gz +0 -0
  55. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  56. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  57. package/dist/web/public/webview-tabs.js +43 -13
  58. package/dist/web/public/webview-tabs.js.br +0 -0
  59. package/dist/web/public/webview-tabs.js.gz +0 -0
  60. package/dist/web/routes/file-routes.d.ts.map +1 -1
  61. package/dist/web/routes/file-routes.js +313 -8
  62. package/dist/web/routes/file-routes.js.map +1 -1
  63. package/dist/web/routes/session-routes.d.ts.map +1 -1
  64. package/dist/web/routes/session-routes.js +173 -99
  65. package/dist/web/routes/session-routes.js.map +1 -1
  66. package/dist/web/schemas.d.ts +10 -0
  67. package/dist/web/schemas.d.ts.map +1 -1
  68. package/dist/web/schemas.js +30 -0
  69. package/dist/web/schemas.js.map +1 -1
  70. package/dist/web/webview-proxy.d.ts +18 -1
  71. package/dist/web/webview-proxy.d.ts.map +1 -1
  72. package/dist/web/webview-proxy.js +129 -1
  73. package/dist/web/webview-proxy.js.map +1 -1
  74. package/package.json +17 -6
  75. package/dist/web/public/keyboard-accessory.3f91acef.js.br +0 -0
  76. package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
  77. package/dist/web/public/mobile.6153d275.css +0 -1
  78. package/dist/web/public/mobile.6153d275.css.br +0 -0
  79. package/dist/web/public/mobile.6153d275.css.gz +0 -0
  80. package/dist/web/public/session-ui.fd39c53c.js.br +0 -0
  81. package/dist/web/public/session-ui.fd39c53c.js.gz +0 -0
  82. package/dist/web/public/styles.bd210c71.css.br +0 -0
  83. package/dist/web/public/styles.bd210c71.css.gz +0 -0
  84. package/dist/web/public/terminal-ui.feaf75c2.js.br +0 -0
  85. package/dist/web/public/terminal-ui.feaf75c2.js.gz +0 -0
@@ -9,6 +9,7 @@
9
9
  * - CleanupRegistration / CleanupResourceType — entries for the centralized CleanupManager
10
10
  * - NiceConfig / DEFAULT_NICE_CONFIG — process priority settings for `nice`/`ionice`
11
11
  * - ProcessStats — memory/CPU/child-count snapshot for resource monitoring
12
+ * - FilesystemBrowseData — bounded path-picker directory listing returned to the web UI
12
13
  */
13
14
  /**
14
15
  * Interface for objects that hold resources requiring explicit cleanup.
@@ -59,6 +60,30 @@ export interface ProcessStats {
59
60
  /** Timestamp of stats collection */
60
61
  updatedAt: number;
61
62
  }
63
+ /** A selectable entry returned by the filesystem path-picker API. */
64
+ export type FilesystemPreviewKind = 'image' | 'text' | 'document';
65
+ export interface FilesystemBrowseEntry {
66
+ name: string;
67
+ path: string;
68
+ type: 'file' | 'directory';
69
+ size?: number;
70
+ symlink?: boolean;
71
+ previewKind?: FilesystemPreviewKind;
72
+ }
73
+ /** A named root the path picker may browse without escaping its allowlist. */
74
+ export interface FilesystemBrowseRoot {
75
+ label: string;
76
+ path: string;
77
+ }
78
+ /** Response payload for `GET /api/filesystem/browse`. */
79
+ export interface FilesystemBrowseData {
80
+ path: string;
81
+ parent: string | null;
82
+ root: string;
83
+ roots: FilesystemBrowseRoot[];
84
+ entries: FilesystemBrowseEntry[];
85
+ truncated: boolean;
86
+ }
62
87
  export type CleanupResourceType = 'timer' | 'interval' | 'watcher' | 'listener' | 'stream';
63
88
  /**
64
89
  * Registration entry for a cleanup resource.
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,OAAO,IAAI,IAAI,CAAC;IAChB,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;GAEG;AACH;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3F;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,OAAO,IAAI,IAAI,CAAC;IAChB,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;GAEG;AACH;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qEAAqE;AACrE,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAED,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3F;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB"}
@@ -9,6 +9,7 @@
9
9
  * - CleanupRegistration / CleanupResourceType — entries for the centralized CleanupManager
10
10
  * - NiceConfig / DEFAULT_NICE_CONFIG — process priority settings for `nice`/`ionice`
11
11
  * - ProcessStats — memory/CPU/child-count snapshot for resource monitoring
12
+ * - FilesystemBrowseData — bounded path-picker directory listing returned to the web UI
12
13
  */
13
14
  export const DEFAULT_NICE_CONFIG = {
14
15
  enabled: false,
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwCH,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,EAAE;CACd,CAAC"}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/types/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAwCH,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,EAAE;CACd,CAAC"}
Binary file
Binary file
Binary file
Binary file
@@ -16,8 +16,8 @@
16
16
  <link rel="manifest" href="manifest.json">
17
17
  <title>Codeman</title>
18
18
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
19
- <link rel="stylesheet" href="styles.bd210c71.css">
20
- <link rel="stylesheet" href="mobile.6153d275.css" media="(max-width: 1023px)">
19
+ <link rel="stylesheet" href="styles.42dd71e1.css">
20
+ <link rel="stylesheet" href="mobile.40e2d64e.css" media="(max-width: 1023px)">
21
21
  <!-- xterm.css loaded async — terminal won't display until xterm.js runs anyway -->
22
22
  <link rel="preload" href="vendor/xterm.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
23
23
  <noscript><link rel="stylesheet" href="vendor/xterm.css"></noscript>
@@ -2043,8 +2043,11 @@
2043
2043
  </div>
2044
2044
  <div class="form-row">
2045
2045
  <label>Folder Path</label>
2046
- <input type="text" id="linkCasePath" placeholder="/home/user/projects/my-project" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false">
2047
- <span class="form-hint">Absolute path to an existing project folder, e.g. /home/you/my-project</span>
2046
+ <div class="path-input-group">
2047
+ <input type="text" id="linkCasePath" placeholder="/mnt/d/AI/my-project" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false">
2048
+ <button type="button" class="btn path-input-browse" onclick="app.openLinkCasePathPicker()">Browse&hellip;</button>
2049
+ </div>
2050
+ <span class="form-hint">Choose an existing folder from this computer or enter its absolute path</span>
2048
2051
  </div>
2049
2052
  </div>
2050
2053
  <!-- Remote Tab -->
@@ -2607,12 +2610,12 @@
2607
2610
  <script defer src="mobile-handlers.bc2b8c1d.js"></script>
2608
2611
  <script defer src="voice-input.085e9e73.js"></script>
2609
2612
  <script defer src="notification-manager.fca811fd.js"></script>
2610
- <script defer src="keyboard-accessory.3f91acef.js"></script>
2613
+ <script defer src="keyboard-accessory.9936227d.js"></script>
2611
2614
  <script defer src="input-cjk.63794d0b.js"></script>
2612
2615
  <!-- Hardened markdown HTML sanitizer (wires DOMPurify). Must precede app.js. -->
2613
2616
  <script defer src="sanitize-html.bc7078d6.js"></script>
2614
2617
  <script defer src="app.adc992cd.js"></script>
2615
- <script defer src="terminal-ui.feaf75c2.js"></script>
2618
+ <script defer src="terminal-ui.f3a51974.js"></script>
2616
2619
  <script defer src="respawn-ui.2d249da9.js"></script>
2617
2620
  <script defer src="ralph-panel.6de2d0f8.js"></script>
2618
2621
  <script defer src="orchestrator-panel.js"></script>
@@ -2621,7 +2624,7 @@
2621
2624
  <script defer src="panels-ui.3910e1e2.js"></script>
2622
2625
  <script defer src="ultracode-panel.js"></script>
2623
2626
  <script defer src="admin-ui.js"></script>
2624
- <script defer src="session-ui.fd39c53c.js"></script>
2627
+ <script defer src="session-ui.ab6dd9aa.js"></script>
2625
2628
  <script defer src="webview-tabs.js"></script>
2626
2629
  <script defer src="ralph-wizard.13a1831e.js"></script>
2627
2630
  <script defer src="api-client.c9b1cddc.js"></script>
Binary file
Binary file
@@ -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}}