@hncode/hncode 0.3.6 → 0.4.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 (146) hide show
  1. package/package.json +4 -2
  2. package/src/agent-task.js +154 -84
  3. package/src/agent.js +533 -145
  4. package/src/clipboard.js +5 -2
  5. package/src/colors.js +242 -238
  6. package/src/config.js +217 -17
  7. package/src/file-history.js +177 -0
  8. package/src/hooks.js +11 -1
  9. package/src/i18n.js +389 -0
  10. package/src/index.js +46 -3
  11. package/src/llm.js +139 -16
  12. package/src/permissions.js +120 -0
  13. package/src/session-hub.js +407 -0
  14. package/src/session.js +31 -0
  15. package/src/term.js +19 -3
  16. package/src/tool-result.js +236 -0
  17. package/src/tools/agent.js +11 -0
  18. package/src/tools/bash.js +84 -17
  19. package/src/tools/edit.js +192 -32
  20. package/src/tools/file-lines.js +5 -0
  21. package/src/tools/index.js +4 -0
  22. package/src/tools/memory.js +88 -0
  23. package/src/tools/read-media-file.js +85 -68
  24. package/src/tools/read.js +15 -1
  25. package/src/tools/utils.js +14 -0
  26. package/src/tools/write.js +35 -5
  27. package/src/tui.js +11665 -9179
  28. package/src/web-auth.js +98 -0
  29. package/src/web-daemon-cli.js +63 -0
  30. package/src/web-daemon-client.js +203 -0
  31. package/src/web-daemon.js +871 -0
  32. package/src/web-public/app.css +1509 -0
  33. package/src/web-public/app.js +2913 -0
  34. package/src/web-public/complete.js +154 -0
  35. package/src/web-public/fonts/JetBrainsMono-Bold.woff2 +0 -0
  36. package/src/web-public/fonts/JetBrainsMono-Regular.woff2 +0 -0
  37. package/src/web-public/fonts/noto-sans-sc-100-400-normal.woff2 +0 -0
  38. package/src/web-public/fonts/noto-sans-sc-101-400-normal.woff2 +0 -0
  39. package/src/web-public/fonts/noto-sans-sc-102-400-normal.woff2 +0 -0
  40. package/src/web-public/fonts/noto-sans-sc-103-400-normal.woff2 +0 -0
  41. package/src/web-public/fonts/noto-sans-sc-104-400-normal.woff2 +0 -0
  42. package/src/web-public/fonts/noto-sans-sc-105-400-normal.woff2 +0 -0
  43. package/src/web-public/fonts/noto-sans-sc-106-400-normal.woff2 +0 -0
  44. package/src/web-public/fonts/noto-sans-sc-107-400-normal.woff2 +0 -0
  45. package/src/web-public/fonts/noto-sans-sc-108-400-normal.woff2 +0 -0
  46. package/src/web-public/fonts/noto-sans-sc-109-400-normal.woff2 +0 -0
  47. package/src/web-public/fonts/noto-sans-sc-110-400-normal.woff2 +0 -0
  48. package/src/web-public/fonts/noto-sans-sc-111-400-normal.woff2 +0 -0
  49. package/src/web-public/fonts/noto-sans-sc-112-400-normal.woff2 +0 -0
  50. package/src/web-public/fonts/noto-sans-sc-113-400-normal.woff2 +0 -0
  51. package/src/web-public/fonts/noto-sans-sc-114-400-normal.woff2 +0 -0
  52. package/src/web-public/fonts/noto-sans-sc-115-400-normal.woff2 +0 -0
  53. package/src/web-public/fonts/noto-sans-sc-116-400-normal.woff2 +0 -0
  54. package/src/web-public/fonts/noto-sans-sc-117-400-normal.woff2 +0 -0
  55. package/src/web-public/fonts/noto-sans-sc-118-400-normal.woff2 +0 -0
  56. package/src/web-public/fonts/noto-sans-sc-119-400-normal.woff2 +0 -0
  57. package/src/web-public/fonts/noto-sans-sc-21-400-normal.woff2 +0 -0
  58. package/src/web-public/fonts/noto-sans-sc-22-400-normal.woff2 +0 -0
  59. package/src/web-public/fonts/noto-sans-sc-23-400-normal.woff2 +0 -0
  60. package/src/web-public/fonts/noto-sans-sc-24-400-normal.woff2 +0 -0
  61. package/src/web-public/fonts/noto-sans-sc-25-400-normal.woff2 +0 -0
  62. package/src/web-public/fonts/noto-sans-sc-26-400-normal.woff2 +0 -0
  63. package/src/web-public/fonts/noto-sans-sc-27-400-normal.woff2 +0 -0
  64. package/src/web-public/fonts/noto-sans-sc-28-400-normal.woff2 +0 -0
  65. package/src/web-public/fonts/noto-sans-sc-29-400-normal.woff2 +0 -0
  66. package/src/web-public/fonts/noto-sans-sc-30-400-normal.woff2 +0 -0
  67. package/src/web-public/fonts/noto-sans-sc-31-400-normal.woff2 +0 -0
  68. package/src/web-public/fonts/noto-sans-sc-32-400-normal.woff2 +0 -0
  69. package/src/web-public/fonts/noto-sans-sc-33-400-normal.woff2 +0 -0
  70. package/src/web-public/fonts/noto-sans-sc-34-400-normal.woff2 +0 -0
  71. package/src/web-public/fonts/noto-sans-sc-35-400-normal.woff2 +0 -0
  72. package/src/web-public/fonts/noto-sans-sc-36-400-normal.woff2 +0 -0
  73. package/src/web-public/fonts/noto-sans-sc-37-400-normal.woff2 +0 -0
  74. package/src/web-public/fonts/noto-sans-sc-38-400-normal.woff2 +0 -0
  75. package/src/web-public/fonts/noto-sans-sc-39-400-normal.woff2 +0 -0
  76. package/src/web-public/fonts/noto-sans-sc-4-400-normal.woff2 +0 -0
  77. package/src/web-public/fonts/noto-sans-sc-40-400-normal.woff2 +0 -0
  78. package/src/web-public/fonts/noto-sans-sc-41-400-normal.woff2 +0 -0
  79. package/src/web-public/fonts/noto-sans-sc-42-400-normal.woff2 +0 -0
  80. package/src/web-public/fonts/noto-sans-sc-43-400-normal.woff2 +0 -0
  81. package/src/web-public/fonts/noto-sans-sc-44-400-normal.woff2 +0 -0
  82. package/src/web-public/fonts/noto-sans-sc-45-400-normal.woff2 +0 -0
  83. package/src/web-public/fonts/noto-sans-sc-46-400-normal.woff2 +0 -0
  84. package/src/web-public/fonts/noto-sans-sc-47-400-normal.woff2 +0 -0
  85. package/src/web-public/fonts/noto-sans-sc-48-400-normal.woff2 +0 -0
  86. package/src/web-public/fonts/noto-sans-sc-49-400-normal.woff2 +0 -0
  87. package/src/web-public/fonts/noto-sans-sc-5-400-normal.woff2 +0 -0
  88. package/src/web-public/fonts/noto-sans-sc-50-400-normal.woff2 +0 -0
  89. package/src/web-public/fonts/noto-sans-sc-51-400-normal.woff2 +0 -0
  90. package/src/web-public/fonts/noto-sans-sc-52-400-normal.woff2 +0 -0
  91. package/src/web-public/fonts/noto-sans-sc-53-400-normal.woff2 +0 -0
  92. package/src/web-public/fonts/noto-sans-sc-54-400-normal.woff2 +0 -0
  93. package/src/web-public/fonts/noto-sans-sc-55-400-normal.woff2 +0 -0
  94. package/src/web-public/fonts/noto-sans-sc-56-400-normal.woff2 +0 -0
  95. package/src/web-public/fonts/noto-sans-sc-57-400-normal.woff2 +0 -0
  96. package/src/web-public/fonts/noto-sans-sc-58-400-normal.woff2 +0 -0
  97. package/src/web-public/fonts/noto-sans-sc-59-400-normal.woff2 +0 -0
  98. package/src/web-public/fonts/noto-sans-sc-6-400-normal.woff2 +0 -0
  99. package/src/web-public/fonts/noto-sans-sc-60-400-normal.woff2 +0 -0
  100. package/src/web-public/fonts/noto-sans-sc-61-400-normal.woff2 +0 -0
  101. package/src/web-public/fonts/noto-sans-sc-62-400-normal.woff2 +0 -0
  102. package/src/web-public/fonts/noto-sans-sc-63-400-normal.woff2 +0 -0
  103. package/src/web-public/fonts/noto-sans-sc-64-400-normal.woff2 +0 -0
  104. package/src/web-public/fonts/noto-sans-sc-65-400-normal.woff2 +0 -0
  105. package/src/web-public/fonts/noto-sans-sc-66-400-normal.woff2 +0 -0
  106. package/src/web-public/fonts/noto-sans-sc-67-400-normal.woff2 +0 -0
  107. package/src/web-public/fonts/noto-sans-sc-68-400-normal.woff2 +0 -0
  108. package/src/web-public/fonts/noto-sans-sc-69-400-normal.woff2 +0 -0
  109. package/src/web-public/fonts/noto-sans-sc-70-400-normal.woff2 +0 -0
  110. package/src/web-public/fonts/noto-sans-sc-71-400-normal.woff2 +0 -0
  111. package/src/web-public/fonts/noto-sans-sc-72-400-normal.woff2 +0 -0
  112. package/src/web-public/fonts/noto-sans-sc-73-400-normal.woff2 +0 -0
  113. package/src/web-public/fonts/noto-sans-sc-74-400-normal.woff2 +0 -0
  114. package/src/web-public/fonts/noto-sans-sc-75-400-normal.woff2 +0 -0
  115. package/src/web-public/fonts/noto-sans-sc-76-400-normal.woff2 +0 -0
  116. package/src/web-public/fonts/noto-sans-sc-77-400-normal.woff2 +0 -0
  117. package/src/web-public/fonts/noto-sans-sc-78-400-normal.woff2 +0 -0
  118. package/src/web-public/fonts/noto-sans-sc-79-400-normal.woff2 +0 -0
  119. package/src/web-public/fonts/noto-sans-sc-80-400-normal.woff2 +0 -0
  120. package/src/web-public/fonts/noto-sans-sc-81-400-normal.woff2 +0 -0
  121. package/src/web-public/fonts/noto-sans-sc-82-400-normal.woff2 +0 -0
  122. package/src/web-public/fonts/noto-sans-sc-83-400-normal.woff2 +0 -0
  123. package/src/web-public/fonts/noto-sans-sc-84-400-normal.woff2 +0 -0
  124. package/src/web-public/fonts/noto-sans-sc-85-400-normal.woff2 +0 -0
  125. package/src/web-public/fonts/noto-sans-sc-86-400-normal.woff2 +0 -0
  126. package/src/web-public/fonts/noto-sans-sc-87-400-normal.woff2 +0 -0
  127. package/src/web-public/fonts/noto-sans-sc-88-400-normal.woff2 +0 -0
  128. package/src/web-public/fonts/noto-sans-sc-89-400-normal.woff2 +0 -0
  129. package/src/web-public/fonts/noto-sans-sc-90-400-normal.woff2 +0 -0
  130. package/src/web-public/fonts/noto-sans-sc-91-400-normal.woff2 +0 -0
  131. package/src/web-public/fonts/noto-sans-sc-97-400-normal.woff2 +0 -0
  132. package/src/web-public/fonts/noto-sans-sc-98-400-normal.woff2 +0 -0
  133. package/src/web-public/fonts/noto-sans-sc-99-400-normal.woff2 +0 -0
  134. package/src/web-public/fonts/noto-sans-sc.css +775 -0
  135. package/src/web-public/fonts.css +31 -0
  136. package/src/web-public/home.css +222 -0
  137. package/src/web-public/home.html +33 -0
  138. package/src/web-public/home.js +160 -0
  139. package/src/web-public/i18n.js +983 -0
  140. package/src/web-public/index.html +196 -0
  141. package/src/web-public/login.css +123 -0
  142. package/src/web-public/login.html +25 -0
  143. package/src/web-public/login.js +46 -0
  144. package/src/web-public/settings.js +290 -0
  145. package/src/web-ws.js +226 -0
  146. package/src/web.js +380 -0
@@ -0,0 +1,1509 @@
1
+ /* hncode web UI — the browser's copy of the terminal transcript.
2
+ Palette values come from src/colors.js so the two front-ends cannot drift. */
3
+
4
+ :root {
5
+ color-scheme: dark;
6
+
7
+ --s0: #08090b;
8
+ --s1: #0d0f13;
9
+ --s2: #12151b;
10
+ --s3: #1a1e26;
11
+
12
+ --fg: #e6eaf2;
13
+ --fg1: #b6bfcf;
14
+ --fg2: #8f97a6;
15
+ --fg3: #6b7280;
16
+
17
+ --line: #1b202a;
18
+ --line2: #272d3a;
19
+
20
+ --teal: #00b8db;
21
+ --cyan: #00d7ff;
22
+ --blue: #4287f5;
23
+ --spring: #00d7af;
24
+ --orange: #ff8c00;
25
+ --branch: #ffdb6e;
26
+ --shell: #bd93f9;
27
+ --red: #ff6b6b;
28
+ --green: #5cff8f;
29
+ --yellow: #ffd93d;
30
+
31
+ --on-accent: #08090b;
32
+
33
+ --mono: "JetBrains Mono", "Noto Sans SC", ui-monospace, SFMono-Regular,
34
+ "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace;
35
+ /* This product is Chinese-first and JetBrains Mono has no CJK glyphs, so UI
36
+ text and code share one stack: the mono face first (paths, numbers), then
37
+ Noto for every Chinese character. A separate Latin sans would only cover the
38
+ few latin glyphs, at the cost of a second font. */
39
+ --sans: var(--mono);
40
+
41
+ --rail-w: 264px;
42
+ --radius: 3px;
43
+ }
44
+
45
+ * { box-sizing: border-box; }
46
+ [hidden] { display: none !important; }
47
+ html, body { margin: 0; height: 100%; }
48
+
49
+ body {
50
+ background: var(--s0);
51
+ color: var(--fg);
52
+ font-family: var(--sans);
53
+ font-size: 13px;
54
+ line-height: 1.5;
55
+ -webkit-font-smoothing: antialiased;
56
+ overflow: hidden;
57
+ }
58
+
59
+ .mono {
60
+ font-family: var(--mono);
61
+ font-size: 12px;
62
+ font-variant-ligatures: none;
63
+ font-variant-numeric: tabular-nums;
64
+ }
65
+ .muted { color: var(--fg2); }
66
+ .ellip { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
67
+ button { font: inherit; color: inherit; }
68
+
69
+ :focus-visible { outline: 1px solid var(--teal); outline-offset: 2px; }
70
+
71
+ * { scrollbar-width: thin; scrollbar-color: var(--line2) transparent; }
72
+ *::-webkit-scrollbar { width: 10px; height: 10px; }
73
+ *::-webkit-scrollbar-track { background: transparent; }
74
+ *::-webkit-scrollbar-thumb {
75
+ background: var(--line2);
76
+ border: 3px solid transparent;
77
+ background-clip: content-box;
78
+ border-radius: 5px;
79
+ }
80
+ *::-webkit-scrollbar-thumb:hover { background: var(--fg3); background-clip: content-box; }
81
+ ::selection { background: rgba(0, 184, 219, .28); }
82
+
83
+ /* ---------------------------------------------------------------- controls */
84
+
85
+ .btn {
86
+ appearance: none;
87
+ border: 1px solid var(--line2);
88
+ border-radius: var(--radius);
89
+ background: transparent;
90
+ color: var(--fg1);
91
+ font-family: var(--mono);
92
+ font-size: 11px;
93
+ padding: 3px 10px;
94
+ cursor: pointer;
95
+ white-space: nowrap;
96
+ transition: border-color .12s ease, color .12s ease, background-color .12s ease;
97
+ }
98
+ .btn:hover { border-color: var(--teal); color: var(--fg); }
99
+ .btn:disabled { color: var(--fg3); border-color: var(--line); cursor: not-allowed; }
100
+
101
+ .btn-quiet {
102
+ appearance: none;
103
+ border: 1px solid transparent;
104
+ border-radius: var(--radius);
105
+ background: transparent;
106
+ color: var(--fg2);
107
+ font-family: var(--mono);
108
+ font-size: 11px;
109
+ padding: 3px 8px;
110
+ cursor: pointer;
111
+ white-space: nowrap;
112
+ transition: color .12s ease, background-color .12s ease;
113
+ }
114
+ .btn-quiet:hover { color: var(--fg); background: var(--s3); }
115
+ .btn-quiet:disabled { color: var(--fg3); cursor: not-allowed; }
116
+
117
+ .pill {
118
+ display: inline-flex;
119
+ align-items: center;
120
+ font-family: var(--mono);
121
+ font-size: 10.5px;
122
+ line-height: 1.5;
123
+ padding: 1px 7px;
124
+ border-radius: var(--radius);
125
+ border: 1px solid var(--line2);
126
+ color: var(--fg1);
127
+ white-space: nowrap;
128
+ }
129
+ .pill-solid { background: var(--teal); color: var(--on-accent); border-color: var(--teal); }
130
+ .pill-ok { color: var(--green); border-color: rgba(92, 255, 143, .4); }
131
+ .pill-warn { color: var(--yellow); border-color: rgba(255, 217, 61, .4); }
132
+ .pill-danger { color: var(--red); border-color: rgba(255, 107, 107, .4); }
133
+ .pill-info { color: var(--teal); border-color: rgba(0, 184, 219, .4); }
134
+ .pill-soft { background: var(--s3); border-color: transparent; color: var(--fg1); }
135
+
136
+ .pill-sub { color: var(--fg2); }
137
+
138
+ /* Stop is the one destructive control in the composer, so it reads as a warning
139
+ rather than as another Send. */
140
+ .btn.is-stop { border-color: rgba(255, 107, 107, .5); color: var(--red); }
141
+ .btn.is-stop:hover { border-color: var(--red); color: var(--red); background: rgba(255, 107, 107, .1); }
142
+
143
+
144
+ .dot {
145
+ width: 6px; height: 6px;
146
+ border-radius: 50%;
147
+ display: inline-block;
148
+ flex: none;
149
+ background: var(--fg3);
150
+ }
151
+ .dot-on { background: var(--green); }
152
+ .dot-off { background: var(--red); }
153
+ .dot-wait { background: var(--yellow); }
154
+
155
+ /* ------------------------------------------------------------------- shell */
156
+
157
+ .app {
158
+ display: grid;
159
+ grid-template-columns: var(--rail-w) minmax(0, 1fr);
160
+ height: 100%;
161
+ }
162
+
163
+ .main {
164
+ display: flex;
165
+ flex-direction: column;
166
+ min-width: 0;
167
+ min-height: 0;
168
+ }
169
+
170
+ /* ------------------------------------------------------------------- rail */
171
+
172
+ .rail {
173
+ display: flex;
174
+ flex-direction: column;
175
+ min-height: 0;
176
+ background: var(--s1);
177
+ border-right: 1px solid var(--line);
178
+ }
179
+
180
+ /* The rail is a column here, so there is nothing to open. The narrow-screen
181
+ rule turns this back on together with the drawer. */
182
+ .rail-toggle { display: none; }
183
+ .rail-scrim { display: none; }
184
+
185
+ /* A square control that carries only a glyph. */
186
+ .icon-btn {
187
+ appearance: none;
188
+ flex: none;
189
+ align-items: center;
190
+ justify-content: center;
191
+ width: 28px;
192
+ height: 28px;
193
+ border: 1px solid transparent;
194
+ border-radius: var(--radius);
195
+ background: transparent;
196
+ color: var(--fg2);
197
+ font-size: 15px;
198
+ line-height: 1;
199
+ cursor: pointer;
200
+ transition: color .12s ease, background-color .12s ease;
201
+ }
202
+ .icon-btn:hover { color: var(--fg); background: var(--s3); }
203
+
204
+
205
+ .rail-search { padding: 10px 12px 8px; flex: none; }
206
+ .rail-search input {
207
+ width: 100%;
208
+ border: 1px solid var(--line2);
209
+ border-radius: var(--radius);
210
+ background: var(--s0);
211
+ padding: 5px 9px;
212
+ font-family: var(--mono);
213
+ font-size: 11.5px;
214
+ color: var(--fg);
215
+ transition: border-color .12s ease;
216
+ }
217
+ .rail-search input::placeholder { color: var(--fg3); }
218
+ .rail-search input:focus { border-color: var(--teal); outline: none; }
219
+
220
+ .rail-meta {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 7px;
224
+ flex: none;
225
+ padding: 0 12px 8px;
226
+ font-family: var(--mono);
227
+ font-size: 10.5px;
228
+ color: var(--fg3);
229
+ }
230
+ .rail-count { color: var(--fg2); }
231
+ .rail-sep { flex: 1; }
232
+ .rail-conn { color: var(--fg2); }
233
+
234
+ .rail-list { flex: 1; min-height: 0; overflow-y: auto; }
235
+
236
+ .rail-group {
237
+ position: sticky;
238
+ top: 0;
239
+ z-index: 1;
240
+ display: flex;
241
+ align-items: baseline;
242
+ gap: 8px;
243
+ padding: 8px 12px 5px;
244
+ background: var(--s1);
245
+ font-family: var(--mono);
246
+ font-size: 10.5px;
247
+ color: var(--fg3);
248
+ }
249
+ .rail-group::after {
250
+ content: "";
251
+ flex: 1;
252
+ height: 1px;
253
+ background: var(--line);
254
+ transform: translateY(-3px);
255
+ }
256
+ .rail-group .ws {
257
+ overflow: hidden;
258
+ text-overflow: ellipsis;
259
+ white-space: nowrap;
260
+ color: var(--fg2);
261
+ }
262
+ .rail-group .mono { color: var(--fg3); }
263
+
264
+ .rail-row {
265
+ display: grid;
266
+ grid-template-columns: 7px minmax(0, 1fr) auto;
267
+ column-gap: 8px;
268
+ align-items: center;
269
+ padding: 7px 12px;
270
+ border-left: 2px solid transparent;
271
+ color: inherit;
272
+ text-decoration: none;
273
+ transition: background-color .1s ease;
274
+ }
275
+ .rail-row:hover { background: var(--s2); }
276
+ .rail-row.is-current { border-left-color: var(--teal); background: var(--s2); }
277
+ .rail-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--fg3); }
278
+ .rail-dot.is-current { background: var(--teal); }
279
+ .rail-row-top { display: contents; }
280
+ .rail-id {
281
+ font-family: var(--mono);
282
+ font-size: 11px;
283
+ color: var(--fg2);
284
+ overflow: hidden;
285
+ text-overflow: ellipsis;
286
+ white-space: nowrap;
287
+ }
288
+ .rail-when {
289
+ font-family: var(--mono);
290
+ font-size: 10.5px;
291
+ color: var(--fg3);
292
+ white-space: nowrap;
293
+ }
294
+ .rail-title {
295
+ grid-column: 2 / -1;
296
+ margin-top: 1px;
297
+ font-size: 12.5px;
298
+ color: var(--fg1);
299
+ overflow: hidden;
300
+ text-overflow: ellipsis;
301
+ white-space: nowrap;
302
+ }
303
+ .rail-row.is-current .rail-title { color: var(--fg); }
304
+ .rail-empty { padding: 14px 12px; font-size: 12px; color: var(--fg3); }
305
+
306
+ .rail-foot {
307
+ display: flex;
308
+ align-items: center;
309
+ gap: 10px;
310
+ flex: none;
311
+ padding: 8px 12px;
312
+ border-top: 1px solid var(--line);
313
+ }
314
+ .rail-link {
315
+ appearance: none;
316
+ border: 0;
317
+ background: none;
318
+ padding: 0;
319
+ font-size: 11.5px;
320
+ color: var(--fg2);
321
+ text-decoration: none;
322
+ cursor: pointer;
323
+ transition: color .12s ease;
324
+ }
325
+ .rail-link:hover { color: var(--teal); }
326
+ .rail-spacer { flex: 1; }
327
+
328
+ .nav-lang { display: flex; gap: 2px; flex: none; }
329
+ .nav-lang button {
330
+ appearance: none;
331
+ border: 0;
332
+ background: none;
333
+ color: var(--fg3);
334
+ font-family: var(--mono);
335
+ font-size: 11px;
336
+ padding: 1px 4px;
337
+ cursor: pointer;
338
+ border-radius: 2px;
339
+ transition: color .12s ease, background-color .12s ease;
340
+ }
341
+ .nav-lang button:hover { color: var(--fg1); }
342
+ .nav-lang button.is-on { color: var(--teal); }
343
+
344
+ /* -------------------------------------------------------------- session head */
345
+
346
+ .sess-head {
347
+ display: flex;
348
+ align-items: center;
349
+ gap: 10px;
350
+ flex: none;
351
+ height: 44px;
352
+ padding: 0 16px;
353
+ border-bottom: 1px solid var(--line);
354
+ overflow: hidden;
355
+ }
356
+ .sess-title {
357
+ font-size: 13.5px;
358
+ font-weight: 600;
359
+ letter-spacing: -.005em;
360
+ overflow: hidden;
361
+ text-overflow: ellipsis;
362
+ white-space: nowrap;
363
+ }
364
+ .sess-id {
365
+ font-family: var(--mono);
366
+ font-size: 11px;
367
+ color: var(--fg3);
368
+ flex: none;
369
+ }
370
+ .sess-spacer { flex: 1; }
371
+ .readonly-badge {
372
+ flex: none;
373
+ margin-left: 8px;
374
+ padding: 0 6px;
375
+ border: 1px solid var(--orange);
376
+ border-radius: var(--radius);
377
+ color: var(--orange);
378
+ font-size: 10px;
379
+ line-height: 16px;
380
+ }
381
+
382
+ .sess-head .btn,
383
+ .sess-head .pill { flex: none; }
384
+
385
+ /* -------------------------------------------------------------------- tabs */
386
+
387
+ .tabs {
388
+ display: flex;
389
+ align-items: center;
390
+ gap: 2px;
391
+ flex: none;
392
+ height: 38px;
393
+ padding: 0 12px;
394
+ border-bottom: 1px solid var(--line);
395
+ overflow: hidden;
396
+ }
397
+ .tab {
398
+ appearance: none;
399
+ border: 0;
400
+ border-radius: var(--radius);
401
+ background: transparent;
402
+ padding: 0 11px;
403
+ height: 26px;
404
+ display: inline-flex;
405
+ align-items: center;
406
+ gap: 6px;
407
+ font-size: 12.5px;
408
+ color: var(--fg2);
409
+ cursor: pointer;
410
+ white-space: nowrap;
411
+ transition: color .12s ease, background-color .12s ease;
412
+ }
413
+ .tab:hover { color: var(--fg); background: var(--s2); }
414
+ .tab.is-on { color: var(--fg); background: var(--s3); }
415
+ .tab-count { font-family: var(--mono); font-size: 10px; color: var(--fg3); }
416
+ .tab.is-on .tab-count { color: var(--teal); }
417
+ .tab-spacer { flex: 1; }
418
+ .tab-sub {
419
+ font-size: 11.5px;
420
+ color: var(--fg3);
421
+ overflow: hidden;
422
+ text-overflow: ellipsis;
423
+ white-space: nowrap;
424
+ padding-right: 4px;
425
+ }
426
+
427
+ /* -------------------------------------------------------------------- body */
428
+
429
+ .body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
430
+ .view { min-height: 0; flex: 1; display: flex; flex-direction: column; overflow: hidden; }
431
+
432
+ /* -------------------------------------------------------------- transcript */
433
+
434
+
435
+ /* The transcript scroll area. Top/bottom padding MUST match the PAD_TOP /
436
+ PAD_BOTTOM constants in app.js — the virtual list offsets rows from them. */
437
+ .stream {
438
+ padding: 14px 16px 4px;
439
+ flex: 1;
440
+ min-height: 0;
441
+ overflow-y: auto;
442
+ }
443
+
444
+ /* Flow virtualisation: rows are normal blocks between two padding divs that
445
+ reserve the unmounted ranges. No absolute positioning, no JS-computed tops —
446
+ the browser lays rows out, so a wrong height estimate can shift things but
447
+ can never overlap them. */
448
+ .win-spacer { width: 100%; }
449
+ .win-pad { width: 100%; }
450
+ .win-row { width: 100%; padding: 2px 0; }
451
+
452
+ /* Rows are NOT flex containers with a marker column. The terminal builds each row
453
+ as `prefix + body` and lets the body's own wrap hang under the prefix, so the
454
+ marker is a 2-column TEXT PREFIX here too, drawn with ::before; the body's
455
+ `padding-left: 2ch` is the hanging indent the terminal gets for free. */
456
+ .msg-body {
457
+ min-width: 0;
458
+ font-family: var(--mono);
459
+ font-size: 12.5px;
460
+ line-height: 1.62;
461
+ white-space: pre-wrap;
462
+ overflow-wrap: anywhere;
463
+ color: var(--fg);
464
+ padding-left: 2ch;
465
+ }
466
+ .msg-body:not([data-mark]) { padding-left: 0; }
467
+
468
+
469
+ .win-row[data-mark] > .msg-body::before {
470
+ content: attr(data-mark) " ";
471
+ float: left;
472
+ margin-left: -2ch;
473
+ width: 2ch;
474
+ }
475
+
476
+ /* user / queued / bash — the terminal frames these in a box, whose border
477
+ replaces the marker column. */
478
+ .msg-box {
479
+ min-width: 0;
480
+ font-family: var(--mono);
481
+ font-size: 12.5px;
482
+ line-height: 1.62;
483
+ white-space: pre-wrap;
484
+ overflow-wrap: anywhere;
485
+ border: 1px solid var(--teal);
486
+ border-radius: var(--radius);
487
+ padding: 6px 11px;
488
+ color: var(--fg);
489
+ background: rgba(0, 184, 219, .045);
490
+ }
491
+ .msg-queued .msg-box { border-color: var(--line2); color: var(--fg2); background: transparent; }
492
+ .msg-bash .msg-box { border-color: var(--shell); background: rgba(189, 147, 249, .05); }
493
+
494
+
495
+ /* Marker colours, one per role, matching the terminal's `msgColorFor` */
496
+ .msg-user > .msg-body::before { color: var(--teal); }
497
+ .msg-queued > .msg-body::before { color: var(--fg3); }
498
+ .msg-steer > .msg-body::before { color: var(--yellow); }
499
+ .msg-bash > .msg-body::before { color: var(--shell); font-weight: 700; }
500
+ .msg-warn > .msg-body::before { color: var(--orange); }
501
+ .msg-thinking > .msg-body::before { color: var(--fg2); }
502
+
503
+ .msg-assistant > .msg-body { color: var(--fg); }
504
+ .msg-system > .msg-body { color: var(--teal); }
505
+ .msg-warn > .msg-body { color: var(--orange); }
506
+ .msg-error > .msg-body { color: var(--red); }
507
+ .msg-aborted > .msg-body { color: var(--fg3); }
508
+
509
+ /* Thinking: dim and italic, marker upright — the terminal closes italics with
510
+ SGR 23 (not a reset) precisely so the marker keeps its own style. */
511
+ .msg-thinking > .msg-body { color: var(--fg2); font-style: italic; }
512
+ .msg-thinking > .msg-body::before { font-style: normal; }
513
+ .think-live { font-style: normal; color: var(--fg2); }
514
+
515
+
516
+ /* Tool call line. The `●` marker carries the STATE (orange running, green done,
517
+ red failed) and the verb stays plain, exactly as the terminal colours it. */
518
+ .msg-tool > .msg-body::before { color: var(--green); }
519
+ .msg-tool.is-running > .msg-body::before { color: var(--orange); }
520
+ .msg-tool.is-failed > .msg-body::before { color: var(--red); }
521
+ .msg-tool .msg-body { color: var(--fg1); }
522
+ .msg-tool .tool-verb { color: var(--fg1); }
523
+ .tool-name { color: var(--teal); font-weight: 600; }
524
+ .tool-arg { color: var(--fg2); }
525
+ .tool-size { color: var(--fg2); }
526
+ .tool-add { color: var(--green); }
527
+ .tool-del { color: var(--red); }
528
+ .tool-live { color: var(--fg2); }
529
+
530
+ /* Tool output: `↳` on the first body row, everything dim, red when it failed. */
531
+ .msg-result > .msg-body::before { color: var(--fg2); }
532
+ .msg-result .msg-body { color: var(--fg2); }
533
+ .msg-result.is-failed > .msg-body::before { color: var(--red); }
534
+ .msg-result.is-failed .msg-body { color: var(--red); }
535
+ .msg-result .diff-add { color: var(--green); }
536
+ .msg-result .diff-del { color: var(--red); }
537
+ .msg-result .res-more { color: var(--fg3); }
538
+
539
+ /* Compaction: the marker takes the phase colour, the text stays dim. */
540
+ .msg-compaction > .msg-body::before { color: var(--green); }
541
+ .msg-compaction.is-running > .msg-body::before { color: var(--cyan); }
542
+ .msg-compaction.is-cancelled > .msg-body::before { color: var(--yellow); }
543
+ .msg-compaction .msg-body { color: var(--fg2); }
544
+ .msg-compaction.is-done .msg-body { color: var(--green); }
545
+ .msg-compaction.is-cancelled .msg-body { color: var(--yellow); }
546
+ .msg-compaction.is-running .msg-body { color: var(--cyan); }
547
+
548
+ /* Background-task card: marker and text share the phase colour. */
549
+ .msg-bgtask > .msg-body::before { color: var(--green); }
550
+ .msg-bgtask.is-started > .msg-body::before { color: var(--cyan); }
551
+ .msg-bgtask.is-failed > .msg-body::before { color: var(--red); }
552
+ .msg-bgtask .msg-body { color: var(--fg1); }
553
+ .msg-bgtask.is-started .msg-body { color: var(--cyan); }
554
+ .msg-bgtask.is-completed .msg-body { color: var(--green); }
555
+ .msg-bgtask.is-failed .msg-body { color: var(--red); }
556
+
557
+ .msg-more {
558
+ display: block;
559
+ border: 0;
560
+ background: none;
561
+ padding: 0;
562
+ margin: 1px 0 3px 2ch;
563
+ font-family: var(--mono);
564
+ font-size: 11px;
565
+ color: var(--fg3);
566
+ cursor: pointer;
567
+ text-align: left;
568
+ transition: color .12s ease;
569
+ }
570
+ .msg-more:hover { color: var(--teal); }
571
+
572
+
573
+ /* Fenced code inside assistant prose. The TUI draws a labelled rule above the
574
+ block; here the label rides the top border instead, which keeps the block one
575
+ element instead of three. */
576
+ .code-block {
577
+ margin: 7px 0;
578
+ border: 1px solid var(--line);
579
+ border-radius: var(--radius);
580
+ background: var(--s0);
581
+ overflow: hidden;
582
+ }
583
+ .code-bar {
584
+ padding: 2px 10px;
585
+ border-bottom: 1px solid var(--line);
586
+ font-family: var(--mono);
587
+ font-size: 10.5px;
588
+ color: var(--fg3);
589
+ background: var(--s1);
590
+ }
591
+ .code-body {
592
+ margin: 0;
593
+ padding: 8px 10px;
594
+ font-family: var(--mono);
595
+ font-size: 12px;
596
+ line-height: 1.55;
597
+ color: var(--cyan);
598
+ white-space: pre;
599
+ overflow-x: auto;
600
+ }
601
+
602
+ .md-h {
603
+ color: var(--cyan);
604
+ font-weight: 600;
605
+ }
606
+ .md-code {
607
+ color: var(--cyan);
608
+ background: rgba(0, 215, 255, .08);
609
+ border-radius: 2px;
610
+ padding: 0 3px;
611
+ }
612
+ .md-b { color: var(--fg); font-weight: 600; }
613
+ .md-link { color: var(--teal); text-decoration: underline; text-underline-offset: 2px; }
614
+ .md-i { font-style: italic; }
615
+ .md-s { text-decoration: line-through; color: var(--fg3); }
616
+ .md-p { margin: 2px 0; }
617
+
618
+ /* Markdown lists — bullet/ordinal in cyan, content in the default fg. */
619
+ .md-list-ul, .md-list-ol { display: block; margin: 2px 0; }
620
+ .md-li { display: flex; align-items: baseline; }
621
+ .md-bullet { flex: none; width: 1.6em; color: var(--cyan); }
622
+ .md-task { display: flex; align-items: baseline; gap: 6px; }
623
+ .md-check { flex: none; color: var(--green); }
624
+
625
+ /* Blockquote: a left bar, dimmed text — mirrors the terminal's `▍` gutter. */
626
+ .md-quote {
627
+ margin: 2px 0;
628
+ padding-left: 10px;
629
+ border-left: 2px solid var(--line2);
630
+ color: var(--fg2);
631
+ }
632
+
633
+ /* Thematic break — a full-width hairline, like the terminal's ─ row. */
634
+ .md-hr {
635
+ height: 1px;
636
+ margin: 6px 0;
637
+ background: var(--line2);
638
+ }
639
+
640
+ /* Markdown table: a CSS grid so cells align into columns. */
641
+ .md-table {
642
+ display: block;
643
+ margin: 4px 0;
644
+ border: 1px solid var(--line2);
645
+ border-radius: var(--radius);
646
+ overflow-x: auto;
647
+ }
648
+ .md-tr { display: flex; }
649
+ .md-tr + .md-tr, .md-table > .md-tr:not(:first-child) { border-top: 1px solid var(--line2); }
650
+ .md-th, .md-td {
651
+ flex: 1 1 0;
652
+ min-width: 0;
653
+ padding: 4px 9px;
654
+ white-space: pre-wrap;
655
+ word-break: break-word;
656
+ }
657
+ .md-th { font-weight: 600; color: var(--fg); }
658
+ .md-td { color: var(--fg1); }
659
+ .md-tr > .md-th + .md-th, .md-tr > .md-th + .md-td, .md-tr > .md-td + .md-th, .md-tr > .md-td + .md-td {
660
+ border-left: 1px solid var(--line2);
661
+ }
662
+ /* --------------------------------------------------------- side tab panels */
663
+
664
+ .cards {
665
+ flex: 1;
666
+ min-height: 0;
667
+ overflow-y: auto;
668
+ padding: 12px 16px 18px;
669
+ display: flex;
670
+ flex-direction: column;
671
+ gap: 3px;
672
+ }
673
+
674
+ .card {
675
+ display: grid;
676
+ grid-template-columns: minmax(0, 1fr) auto;
677
+ column-gap: 14px;
678
+ row-gap: 2px;
679
+ align-items: baseline;
680
+ padding: 8px 10px;
681
+ border: 1px solid var(--line);
682
+ border-radius: var(--radius);
683
+ background: var(--s1);
684
+ }
685
+ .card-head {
686
+ display: flex;
687
+ align-items: baseline;
688
+ flex-wrap: wrap;
689
+ gap: 8px;
690
+ min-width: 0;
691
+ }
692
+ .card-head .right {
693
+ font-family: var(--mono);
694
+ font-size: 11px;
695
+ color: var(--fg3);
696
+ white-space: nowrap;
697
+ }
698
+
699
+ .card-fields {
700
+ grid-column: 1 / -1;
701
+ display: flex;
702
+ flex-direction: column;
703
+ gap: 1px;
704
+ min-width: 0;
705
+ }
706
+ .field {
707
+ display: flex;
708
+ align-items: baseline;
709
+ gap: 10px;
710
+ min-width: 0;
711
+ }
712
+ .field-label {
713
+ flex: none;
714
+ width: 68px;
715
+ font-family: var(--mono);
716
+ font-size: 10.5px;
717
+ color: var(--fg3);
718
+ }
719
+ .field-value {
720
+ flex: 1;
721
+ min-width: 0;
722
+ font-family: var(--mono);
723
+ font-size: 12px;
724
+ color: var(--fg1);
725
+ overflow-wrap: anywhere;
726
+ }
727
+ .field-value.dim { color: var(--fg3); }
728
+
729
+ .cards-empty {
730
+ padding: 32px 4px;
731
+ font-family: var(--mono);
732
+ font-size: 12px;
733
+ color: var(--fg3);
734
+ text-align: center;
735
+ }
736
+
737
+ /* ------------------------------------------------------------------- stack */
738
+
739
+ .stack {
740
+ flex: none;
741
+ display: flex;
742
+ flex-direction: column;
743
+ }
744
+ .stack-panel {
745
+ display: flex;
746
+ flex-direction: column;
747
+ gap: 2px;
748
+ padding: 0 16px 8px;
749
+ background: var(--s1);
750
+ min-width: 0;
751
+ }
752
+
753
+ /* The panel's top edge is a RULE, and that rule is also the drag handle for
754
+ resizing — the terminal draws it full width above the heading and makes it
755
+ the grab target. It is a real element rather than a border so it can own the
756
+ hitbox, the hover colour and the drag cursor. */
757
+ .stack-rule {
758
+ flex: none;
759
+ height: 6px;
760
+ margin: 0 -16px 1px;
761
+ cursor: ns-resize;
762
+ position: relative;
763
+ }
764
+ .stack-rule::after {
765
+ content: "";
766
+ position: absolute;
767
+ left: 0;
768
+ right: 0;
769
+ top: 2px;
770
+ height: 1px;
771
+ background: var(--line);
772
+ transition: background-color .12s ease, height .12s ease;
773
+ }
774
+ .stack-rule:hover::after,
775
+ .stack-rule.is-dragging::after { background: var(--teal); height: 2px; top: 1.5px; }
776
+
777
+ /* The heading is CYAN and BOLD, matching the terminal's `col(' Todo', C.cyan +
778
+ C.bold)`. A dim grey label reads as metadata; this is a title. */
779
+ .stack-label {
780
+ flex: none;
781
+ font-family: var(--mono);
782
+ font-size: 12px;
783
+ font-weight: 600;
784
+ color: var(--teal);
785
+ }
786
+
787
+
788
+ .todos {
789
+ list-style: none;
790
+ margin: 0;
791
+ padding: 0;
792
+ display: flex;
793
+ flex-direction: column;
794
+ gap: 1px;
795
+ min-width: 0;
796
+ }
797
+ .todos li {
798
+ display: flex;
799
+ align-items: baseline;
800
+ gap: 8px;
801
+ min-width: 0;
802
+ font-family: var(--mono);
803
+ font-size: 12px;
804
+ color: var(--fg1);
805
+ }
806
+ .todos li .t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
807
+ .todos li .mark { flex: none; width: 10px; text-align: center; color: var(--fg3); }
808
+ .todos li.done { color: var(--fg3); }
809
+ .todos li.done .t { text-decoration: line-through; }
810
+ .todos li.done .mark { color: var(--green); }
811
+ .todos li.now { color: var(--fg); }
812
+ .todos li.now .t { font-weight: 600; }
813
+ .todos li.now .mark { color: var(--teal); }
814
+ .todos li.more { color: var(--fg3); font-size: 11px; }
815
+
816
+ /* The Working row sits at the TOP of the work stack — above the todo/queue
817
+ panels and the composer — because it shows what the agent is doing right now,
818
+ the most current thing on the page. It carries no rule of its own (a border
819
+ here would make it read as a separate panel). `min-height` keeps the row from
820
+ resizing as the phrase changes. */
821
+ .working {
822
+ display: flex;
823
+ align-items: baseline;
824
+ padding: 0 18px 5px;
825
+ font-family: var(--mono);
826
+ font-size: 12.5px;
827
+ line-height: 1.62;
828
+ white-space: pre;
829
+ overflow: hidden;
830
+ min-height: 1.62em;
831
+ }
832
+ #working-text { display: inline-flex; align-items: baseline; white-space: pre; }
833
+ .spin { flex: none; color: var(--orange); margin-right: 1ch; }
834
+
835
+
836
+ /* ---------------------------------------------------------------- composer */
837
+
838
+ .composer {
839
+ display: flex;
840
+ align-items: flex-end;
841
+ gap: 0;
842
+ flex: none;
843
+ margin: 0 16px 6px;
844
+ border: 1px solid var(--line2);
845
+ border-radius: var(--radius);
846
+ background: var(--s1);
847
+ padding: 2px 4px 2px 0;
848
+ transition: border-color .12s ease;
849
+ }
850
+ .composer:focus-within { border-color: var(--teal); }
851
+ .composer.is-shell { border-color: var(--shell); }
852
+
853
+ .pmode {
854
+ flex: none;
855
+ align-self: stretch;
856
+ display: flex;
857
+ align-items: flex-start;
858
+ justify-content: center;
859
+ width: 30px;
860
+ padding-top: 9px;
861
+ font-family: var(--mono);
862
+ font-size: 13px;
863
+ color: var(--teal);
864
+ user-select: none;
865
+ }
866
+ .composer.is-shell .pmode { color: var(--shell); font-weight: 700; }
867
+
868
+ #input {
869
+ flex: 1;
870
+ min-width: 0;
871
+ resize: none;
872
+ border: 0;
873
+ background: transparent;
874
+ padding: 8px 0;
875
+ font-family: var(--mono);
876
+ font-size: 12.5px;
877
+ line-height: 1.6;
878
+ color: var(--fg);
879
+ max-height: 200px;
880
+ overflow-y: auto;
881
+ }
882
+ #input:focus { outline: none; }
883
+ #input::placeholder { color: var(--fg3); }
884
+
885
+ .composer .btn { align-self: flex-end; margin-bottom: 5px; }
886
+
887
+ .complete {
888
+ flex: none;
889
+ margin: 0 16px;
890
+ border: 1px solid var(--line2);
891
+ border-bottom: 0;
892
+ border-radius: var(--radius) var(--radius) 0 0;
893
+ background: var(--s2);
894
+ max-height: 200px;
895
+ overflow-y: auto;
896
+ }
897
+ .comp-item {
898
+ display: flex;
899
+ align-items: baseline;
900
+ gap: 10px;
901
+ padding: 4px 10px;
902
+ cursor: pointer;
903
+ font-family: var(--mono);
904
+ font-size: 12px;
905
+ min-width: 0;
906
+ }
907
+ .comp-item.is-sel { background: rgba(0, 184, 219, .14); }
908
+ .comp-name { flex: none; color: var(--fg); }
909
+ .comp-item.is-sel .comp-name { color: var(--cyan); }
910
+ .comp-sub { color: var(--fg3); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
911
+
912
+ /* ------------------------------------------------------------ status line */
913
+
914
+ .statusline {
915
+ display: flex;
916
+ align-items: center;
917
+ gap: 12px;
918
+ flex: none;
919
+ padding: 8px 16px 10px;
920
+ font-family: var(--mono);
921
+ font-size: 11.5px;
922
+ white-space: nowrap;
923
+ overflow: hidden;
924
+ }
925
+ .sl-mode {
926
+ flex: none;
927
+ color: var(--orange);
928
+ font-weight: 600;
929
+ cursor: pointer;
930
+ background: none;
931
+ border: 0;
932
+ padding: 0;
933
+ }
934
+ .sl-badge { color: var(--teal); font-weight: 600; flex: none; }
935
+ .sl-model {
936
+ flex: none;
937
+ color: var(--fg);
938
+ cursor: pointer;
939
+ background: none;
940
+ border: 0;
941
+ padding: 0;
942
+ }
943
+ .sl-think { color: var(--fg2); }
944
+ .sl-tasks-bash { color: var(--teal); flex: none; }
945
+ .sl-tasks-agent { color: var(--spring); flex: none; }
946
+ .sl-cwd { color: var(--fg2); flex: none; max-width: 40ch; overflow: hidden; text-overflow: ellipsis; }
947
+ .sl-branch { color: var(--branch); font-weight: 600; flex: none; }
948
+ .sl-add { color: var(--green); flex: none; }
949
+ .sl-del { color: var(--red); flex: none; }
950
+ .sl-spacer { flex: 1; }
951
+ .sl-usage { color: var(--fg2); flex: none; }
952
+ .sl-ctx { color: var(--fg1); flex: none; }
953
+
954
+ .sl-mode:hover, .sl-model:hover { text-decoration: underline; text-underline-offset: 2px; }
955
+
956
+ /* ---------------------------------------------------------------- popover */
957
+
958
+ .popover {
959
+ position: fixed;
960
+ z-index: 70;
961
+ min-width: 220px;
962
+ max-width: min(480px, 84vw);
963
+ max-height: 60vh;
964
+ overflow-y: auto;
965
+ border: 1px solid var(--line2);
966
+ border-radius: var(--radius);
967
+ background: var(--s2);
968
+ padding: 4px;
969
+ box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
970
+ }
971
+ .popover-title {
972
+ font-family: var(--mono);
973
+ font-size: 10.5px;
974
+ color: var(--fg3);
975
+ padding: 4px 9px 6px;
976
+ }
977
+ .popover-list { display: flex; flex-direction: column; gap: 1px; }
978
+ .popover-item {
979
+ appearance: none;
980
+ border: 0;
981
+ background: none;
982
+ text-align: left;
983
+ display: flex;
984
+ align-items: baseline;
985
+ gap: 9px;
986
+ width: 100%;
987
+ padding: 5px 9px;
988
+ border-radius: 2px;
989
+ color: var(--fg1);
990
+ font-family: var(--mono);
991
+ font-size: 12px;
992
+ cursor: pointer;
993
+ min-width: 0;
994
+ }
995
+ .popover-item:hover,
996
+ .popover-item.is-hover { background: var(--s3); color: var(--fg); }
997
+ .popover-item.is-on { color: var(--cyan); }
998
+ .popover-item.is-on::before { content: "\2713"; flex: none; }
999
+ .popover-item:not(.is-on)::before { content: " "; flex: none; }
1000
+ .popover-item .sub {
1001
+ margin-left: auto;
1002
+ color: var(--fg3);
1003
+ font-size: 10.5px;
1004
+ flex: none;
1005
+ }
1006
+
1007
+ /* The model + effort popover is three stacked blocks: model list, a hairline
1008
+ divider, then a "Thinking Effort" row. The level list is NOT inline — it
1009
+ floats out to the right of the effort row as its own panel (.popover-flyout). */
1010
+ .popover-section {
1011
+ display: flex;
1012
+ flex-direction: column;
1013
+ gap: 1px;
1014
+ min-width: 0;
1015
+ max-height: 46vh;
1016
+ overflow-y: auto;
1017
+ }
1018
+ .popover-rule {
1019
+ flex: none;
1020
+ height: 1px;
1021
+ margin: 3px 2px;
1022
+ background: var(--line);
1023
+ }
1024
+
1025
+ /* The "Thinking Effort" row. Hovering it (or a model above) floats the levels out
1026
+ to the right. */
1027
+ .popover-effort {
1028
+ display: flex;
1029
+ align-items: center;
1030
+ gap: 10px;
1031
+ padding: 5px 9px;
1032
+ font-family: var(--mono);
1033
+ font-size: 12px;
1034
+ color: var(--fg2);
1035
+ cursor: default;
1036
+ border-radius: 2px;
1037
+ white-space: nowrap;
1038
+ }
1039
+ .popover-effort-val {
1040
+ margin-left: auto;
1041
+ color: var(--fg3);
1042
+ font-size: 11px;
1043
+ }
1044
+ .popover-effort-val.has-levels {
1045
+ color: var(--cyan);
1046
+ cursor: default;
1047
+ text-decoration: underline;
1048
+ text-underline-offset: 2px;
1049
+ }
1050
+
1051
+ /* The floating level list: a fixed-position panel placed beside the effort row,
1052
+ styled like the main popover so the two read as the same mechanism. */
1053
+ .popover-flyout {
1054
+ position: fixed;
1055
+ z-index: 71;
1056
+ min-width: 120px;
1057
+ border: 1px solid var(--line2);
1058
+ border-radius: var(--radius);
1059
+ background: var(--s2);
1060
+ padding: 4px;
1061
+ box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
1062
+ display: flex;
1063
+ flex-direction: column;
1064
+ gap: 1px;
1065
+ }
1066
+
1067
+ .steer-btn {
1068
+ appearance: none;
1069
+ margin-left: auto;
1070
+ flex: none;
1071
+ border: 1px solid var(--line2);
1072
+ border-radius: 2px;
1073
+ background: transparent;
1074
+ color: var(--fg2);
1075
+ font-family: var(--mono);
1076
+ font-size: 10px;
1077
+ padding: 0 6px;
1078
+ line-height: 16px;
1079
+ cursor: pointer;
1080
+ transition: border-color .12s ease, color .12s ease;
1081
+ }
1082
+ /* `margin-left: auto` pushes the FIRST action to the right edge of the row. Every
1083
+ button after it must NOT repeat that, or each one is pushed right on its own and
1084
+ the `gap` between them stretches to fill the row. */
1085
+ .steer-btn ~ .steer-btn { margin-left: 0; }
1086
+ .steer-btn:hover { border-color: var(--teal); color: var(--teal); }
1087
+
1088
+ /* -------------------------------------------------------------------- logs */
1089
+
1090
+ .logs-bar {
1091
+ display: flex;
1092
+ justify-content: flex-end;
1093
+ flex: none;
1094
+ padding: 8px 16px;
1095
+ border-bottom: 1px solid var(--line);
1096
+ }
1097
+ .logs {
1098
+ flex: 1;
1099
+ min-height: 0;
1100
+ overflow-y: auto;
1101
+ padding: 6px 0 14px;
1102
+ font-family: var(--mono);
1103
+ font-size: 11.5px;
1104
+ }
1105
+ .log-line {
1106
+ display: grid;
1107
+ grid-template-columns: 66px 110px minmax(0, 1fr);
1108
+ gap: 10px;
1109
+ padding: 1px 16px;
1110
+ line-height: 1.55;
1111
+ }
1112
+ .log-line:hover { background: var(--s1); }
1113
+ .log-t { color: var(--fg3); }
1114
+ .log-k { color: var(--fg2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1115
+ .log-d { color: var(--fg1); overflow-wrap: anywhere; }
1116
+
1117
+ /* ------------------------------------------------------- modal and overlay */
1118
+
1119
+ .modal,
1120
+ .overlay {
1121
+ position: fixed;
1122
+ inset: 0;
1123
+ display: flex;
1124
+ align-items: center;
1125
+ justify-content: center;
1126
+ background: rgba(4, 5, 7, .62);
1127
+ backdrop-filter: blur(2px);
1128
+ z-index: 50;
1129
+ }
1130
+ .modal { z-index: 40; }
1131
+
1132
+ .modal-box {
1133
+ width: min(660px, 92vw);
1134
+ max-height: 82vh;
1135
+ display: flex;
1136
+ flex-direction: column;
1137
+ border: 1px solid var(--line2);
1138
+ border-radius: var(--radius);
1139
+ background: var(--s1);
1140
+ padding: 16px 18px;
1141
+ box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
1142
+ }
1143
+ .modal-box.is-approval { border-color: var(--orange); }
1144
+ .modal-box.is-question { border-color: var(--teal); }
1145
+
1146
+ .modal-title {
1147
+ font-family: var(--mono);
1148
+ font-size: 11px;
1149
+ color: var(--fg3);
1150
+ margin-bottom: 10px;
1151
+ }
1152
+ .modal-body {
1153
+ margin: 0;
1154
+ flex: 1;
1155
+ min-height: 0;
1156
+ overflow: auto;
1157
+ font-family: var(--mono);
1158
+ font-size: 12.5px;
1159
+ line-height: 1.6;
1160
+ white-space: pre-wrap;
1161
+ overflow-wrap: anywhere;
1162
+ color: var(--fg);
1163
+ }
1164
+ .modal-input {
1165
+ width: 100%;
1166
+ margin-top: 10px;
1167
+ border: 1px solid var(--line2);
1168
+ border-radius: var(--radius);
1169
+ background: var(--s0);
1170
+ color: var(--fg);
1171
+ padding: 7px 9px;
1172
+ font-family: var(--mono);
1173
+ font-size: 12.5px;
1174
+ resize: vertical;
1175
+ }
1176
+ .modal-input:focus { border-color: var(--teal); outline: none; }
1177
+ /* AskUserQuestion: progress + option rows + per-question Other + whole-request note. */
1178
+ .modal-progress {
1179
+ font-family: var(--mono);
1180
+ font-size: 11px;
1181
+ color: var(--fg3);
1182
+ margin: 2px 0 6px;
1183
+ }
1184
+ .modal-options { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
1185
+ .modal-opt {
1186
+ appearance: none;
1187
+ display: block;
1188
+ width: 100%;
1189
+ text-align: left;
1190
+ border: 1px solid var(--line2);
1191
+ border-radius: var(--radius);
1192
+ background: var(--s2);
1193
+ color: var(--fg1);
1194
+ font-family: var(--mono);
1195
+ font-size: 12.5px;
1196
+ padding: 7px 10px;
1197
+ cursor: pointer;
1198
+ transition: border-color .12s ease, background-color .12s ease, color .12s ease;
1199
+ }
1200
+ .modal-opt:hover { border-color: var(--teal); color: var(--fg); }
1201
+ .modal-opt.is-on { border-color: var(--teal); color: var(--cyan); }
1202
+ .modal-opt-label { color: inherit; }
1203
+ .modal-opt-desc { display: block; margin-top: 2px; color: var(--fg3); font-size: 11px; }
1204
+ .modal-check { display: inline-block; width: 1.4em; color: var(--fg3); }
1205
+ .modal-other { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
1206
+ .modal-other-toggle { color: var(--fg2); }
1207
+ .modal-input.inline { margin-top: 0; }
1208
+ .modal-note { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
1209
+ .modal-note-label { font-family: var(--mono); font-size: 11px; color: var(--fg3); margin-bottom: 4px; }
1210
+
1211
+ .modal-actions {
1212
+ display: flex;
1213
+ flex-wrap: wrap;
1214
+ gap: 8px;
1215
+ justify-content: flex-end;
1216
+ margin-top: 14px;
1217
+ }
1218
+
1219
+ .overlay-box {
1220
+ width: min(860px, 94vw);
1221
+ max-height: 88vh;
1222
+ display: flex;
1223
+ flex-direction: column;
1224
+ border: 1px solid var(--line2);
1225
+ border-radius: var(--radius);
1226
+ background: var(--s1);
1227
+ box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
1228
+ }
1229
+ .overlay-head {
1230
+ display: flex;
1231
+ align-items: center;
1232
+ justify-content: space-between;
1233
+ flex: none;
1234
+ padding: 12px 18px;
1235
+ border-bottom: 1px solid var(--line);
1236
+ }
1237
+ .overlay-head h2 { margin: 0; font-size: 13.5px; font-weight: 600; }
1238
+ .overlay-body { flex: 1; min-height: 0; overflow-y: auto; }
1239
+
1240
+ .set-group { padding: 14px 18px; border-bottom: 1px solid var(--line); }
1241
+ .set-group:last-child { border-bottom: 0; }
1242
+ .set-group h3 {
1243
+ margin: 0 0 10px;
1244
+ font-family: var(--mono);
1245
+ font-size: 10.5px;
1246
+ font-weight: 400;
1247
+ color: var(--fg3);
1248
+ }
1249
+ .set-row { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin-bottom: 7px; }
1250
+ .set-row label { font-size: 12px; color: var(--fg2); flex: none; }
1251
+ .set-row input[type="text"],
1252
+ .set-row input[type="password"],
1253
+ .set-row input:not([type]) {
1254
+ border: 1px solid var(--line2);
1255
+ border-radius: var(--radius);
1256
+ background: var(--s0);
1257
+ color: var(--fg);
1258
+ padding: 5px 9px;
1259
+ font-family: var(--mono);
1260
+ font-size: 12px;
1261
+ min-width: 150px;
1262
+ transition: border-color .12s ease;
1263
+ }
1264
+ .set-row input:focus { border-color: var(--teal); outline: none; }
1265
+ .set-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
1266
+
1267
+ .set-item {
1268
+ display: flex;
1269
+ align-items: baseline;
1270
+ gap: 10px;
1271
+ padding: 6px 9px;
1272
+ border-radius: var(--radius);
1273
+ background: var(--s2);
1274
+ font-family: var(--mono);
1275
+ font-size: 12px;
1276
+ }
1277
+ .set-name { flex: none; color: var(--fg); font-weight: 600; }
1278
+ .set-tag { flex: none; font-size: 10.5px; color: var(--green); }
1279
+ .set-tag.off { color: var(--fg3); }
1280
+ .set-sub {
1281
+ flex: 1;
1282
+ min-width: 0;
1283
+ color: var(--fg3);
1284
+ font-size: 11px;
1285
+ overflow: hidden;
1286
+ text-overflow: ellipsis;
1287
+ white-space: nowrap;
1288
+ }
1289
+ .set-act {
1290
+ appearance: none;
1291
+ flex: none;
1292
+ border: 1px solid var(--line2);
1293
+ border-radius: 2px;
1294
+ background: transparent;
1295
+ color: var(--fg2);
1296
+ font-family: var(--mono);
1297
+ font-size: 10.5px;
1298
+ padding: 1px 7px;
1299
+ cursor: pointer;
1300
+ transition: border-color .12s ease, color .12s ease;
1301
+ }
1302
+ .set-act:hover { border-color: var(--teal); color: var(--teal); }
1303
+ .set-act.danger:hover { border-color: var(--red); color: var(--red); }
1304
+ .set-act:disabled { color: var(--fg3); border-color: var(--line); cursor: not-allowed; }
1305
+
1306
+ .kv {
1307
+ display: grid;
1308
+ grid-template-columns: 96px minmax(0, 1fr);
1309
+ gap: 2px 12px;
1310
+ margin: 0;
1311
+ font-size: 12px;
1312
+ }
1313
+ .kv dt { color: var(--fg3); font-family: var(--mono); font-size: 11px; }
1314
+ .kv dd { margin: 0; color: var(--fg1); font-family: var(--mono); min-width: 0; }
1315
+
1316
+ select,
1317
+ .set-select {
1318
+ appearance: none;
1319
+ -webkit-appearance: none;
1320
+ border: 1px solid var(--line2);
1321
+ border-radius: var(--radius);
1322
+ background-color: var(--s0);
1323
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237a8290' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
1324
+ background-repeat: no-repeat;
1325
+ background-position: right 8px center;
1326
+ color: var(--fg1);
1327
+ padding: 4px 24px 4px 9px;
1328
+ font-family: var(--mono);
1329
+ font-size: 11px;
1330
+ cursor: pointer;
1331
+ transition: border-color .12s ease;
1332
+ }
1333
+ select:hover { border-color: var(--fg3); }
1334
+ select:focus { outline: none; border-color: var(--teal); }
1335
+ select option { background: var(--s1); color: var(--fg1); }
1336
+
1337
+ /* ----------------------------------------------------------------- notice */
1338
+
1339
+ .notice {
1340
+ position: fixed;
1341
+ left: 50%;
1342
+ bottom: 96px;
1343
+ transform: translateX(-50%);
1344
+ z-index: 90;
1345
+ border: 1px solid var(--line2);
1346
+ border-radius: var(--radius);
1347
+ background: var(--s2);
1348
+ color: var(--fg);
1349
+ padding: 6px 14px;
1350
+ font-family: var(--mono);
1351
+ font-size: 12px;
1352
+ max-width: 70vw;
1353
+ overflow: hidden;
1354
+ text-overflow: ellipsis;
1355
+ white-space: nowrap;
1356
+ box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
1357
+ }
1358
+ .notice.is-error { border-color: var(--red); color: var(--red); }
1359
+ .notice.is-ok { border-color: var(--green); color: var(--green); }
1360
+
1361
+ /* ----------------------------------------------------------------- drawer */
1362
+
1363
+ .drawer { position: fixed; inset: 0; z-index: 80; display: flex; justify-content: flex-end; }
1364
+ .drawer-scrim { position: absolute; inset: 0; background: rgba(4, 5, 7, .6); }
1365
+ .drawer-panel {
1366
+ position: relative;
1367
+ width: min(600px, 92vw);
1368
+ height: 100%;
1369
+ display: flex;
1370
+ flex-direction: column;
1371
+ border-left: 1px solid var(--line);
1372
+ background: var(--s1);
1373
+ }
1374
+ .drawer-head {
1375
+ flex: none;
1376
+ height: 48px;
1377
+ display: flex;
1378
+ align-items: center;
1379
+ justify-content: space-between;
1380
+ gap: 10px;
1381
+ padding: 0 18px;
1382
+ border-bottom: 1px solid var(--line);
1383
+ }
1384
+ .drawer-title { font-family: var(--mono); font-size: 11.5px; color: var(--fg2); }
1385
+ .drawer-close {
1386
+ appearance: none;
1387
+ border: 0;
1388
+ background: none;
1389
+ font-family: var(--mono);
1390
+ font-size: 11.5px;
1391
+ color: var(--fg3);
1392
+ cursor: pointer;
1393
+ }
1394
+ .drawer-close:hover { color: var(--fg); }
1395
+ .drawer-body { flex: 1; min-height: 0; overflow: auto; padding: 14px 18px; }
1396
+
1397
+ /* ------------------------------------------------------------ details rows */
1398
+
1399
+ .tool-args-btn {
1400
+ appearance: none;
1401
+ border: 1px solid var(--line);
1402
+ border-radius: 2px;
1403
+ background: transparent;
1404
+ font-family: var(--mono);
1405
+ font-size: 10px;
1406
+ color: var(--fg3);
1407
+ padding: 0 6px;
1408
+ margin: 3px 0 0;
1409
+ cursor: pointer;
1410
+ transition: border-color .12s ease, color .12s ease;
1411
+ }
1412
+ .tool-args-btn:hover, .tool-args-btn.is-on { border-color: var(--line2); color: var(--fg1); }
1413
+ .tool-args {
1414
+ margin: 4px 0 0;
1415
+ padding: 8px 10px;
1416
+ border: 1px solid var(--line);
1417
+ border-radius: var(--radius);
1418
+ background: var(--s0);
1419
+ font-family: var(--mono);
1420
+ font-size: 11.5px;
1421
+ line-height: 1.55;
1422
+ white-space: pre-wrap;
1423
+ overflow-wrap: anywhere;
1424
+ max-height: 340px;
1425
+ overflow: auto;
1426
+ color: var(--fg1);
1427
+ }
1428
+
1429
+ .copy-btn {
1430
+ appearance: none;
1431
+ border: 0;
1432
+ background: none;
1433
+ font-family: var(--mono);
1434
+ font-size: 10.5px;
1435
+ color: var(--fg3);
1436
+ cursor: pointer;
1437
+ padding: 0 3px;
1438
+ margin-left: 8px;
1439
+ transition: color .12s ease;
1440
+ }
1441
+ .copy-btn:hover { color: var(--fg1); }
1442
+ .copy-btn.is-ok { color: var(--green); }
1443
+ .copy-btn.is-err { color: var(--red); }
1444
+
1445
+ /* ------------------------------------------------------ reduced motion / sm */
1446
+
1447
+ @media (prefers-reduced-motion: reduce) {
1448
+ * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
1449
+ }
1450
+
1451
+ @media (max-width: 980px) {
1452
+ :root { --rail-w: 216px; }
1453
+ .sl-cwd, .sl-usage { display: none; }
1454
+ .stream { padding: 12px 12px 16px; }
1455
+ .composer { margin: 0 12px 6px; }
1456
+ .complete { margin: 0 12px; }
1457
+ .working { padding: 0 14px 5px; }
1458
+ .statusline { padding: 7px 12px 9px; gap: 9px; }
1459
+ }
1460
+
1461
+ /* Below this width the rail stops being a column and becomes a drawer. It is NOT
1462
+ hidden: on a phone the drawer is the only route to sessions, settings and the
1463
+ language switch, so `display: none` removes features rather than reflowing
1464
+ them. */
1465
+ @media (max-width: 720px) {
1466
+ :root { --rail-w: 264px; }
1467
+ .app { grid-template-columns: minmax(0, 1fr); }
1468
+ .rail-toggle { display: inline-flex; }
1469
+
1470
+ .rail {
1471
+ position: fixed;
1472
+ top: 0;
1473
+ bottom: 0;
1474
+ left: 0;
1475
+ width: var(--rail-w);
1476
+ z-index: 60;
1477
+ transform: translateX(-100%);
1478
+ transition: transform .18s ease;
1479
+ }
1480
+ body.rail-open .rail { transform: translateX(0); }
1481
+
1482
+ .rail-scrim {
1483
+ position: fixed;
1484
+ inset: 0;
1485
+ z-index: 55;
1486
+ background: rgba(4, 5, 7, .55);
1487
+ opacity: 0;
1488
+ pointer-events: none;
1489
+ transition: opacity .18s ease;
1490
+ }
1491
+ body.rail-open .rail-scrim { opacity: 1; pointer-events: auto; }
1492
+ /* The toggle button must stay tappable ABOVE BOTH the scrim and the drawer, or a
1493
+ drawer that is open can never be closed from the button that opened it. The
1494
+ rail itself sits at z-index 60, so the previous value of 56 left the button
1495
+ UNDERNEATH the open panel — tappable only while the drawer was shut, and gone
1496
+ exactly when it was needed. */
1497
+ .rail-toggle { position: relative; z-index: 62; }
1498
+
1499
+ .tabs { padding: 0 6px; }
1500
+ .tab { padding: 0 8px; }
1501
+ }
1502
+
1503
+ /* Comfortable tap targets on touch, where a 26px control is a miss. */
1504
+ @media (pointer: coarse) {
1505
+ .tab { height: 32px; }
1506
+ .btn, .btn-quiet { padding: 6px 12px; }
1507
+ .steer-btn { padding: 0 10px; line-height: 22px; }
1508
+ }
1509
+