atom-agent 1.3.0 → 1.5.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 (71) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +220 -224
  3. package/dist/App.js +922 -341
  4. package/dist/adapters.js +127 -14
  5. package/dist/agent/goal-evaluator.js +3 -0
  6. package/dist/agent/loop.js +211 -430
  7. package/dist/agent/tool-pipeline.js +398 -0
  8. package/dist/agent/turn-events.js +12 -0
  9. package/dist/cli.js +57 -8
  10. package/dist/compact.js +72 -8
  11. package/dist/config.js +19 -0
  12. package/dist/context-manager.js +6 -2
  13. package/dist/extensions.js +6 -0
  14. package/dist/file-diffs.js +108 -0
  15. package/dist/kilo.js +1 -1
  16. package/dist/local-discovery.js +2 -2
  17. package/dist/media.js +276 -0
  18. package/dist/overflow.js +140 -0
  19. package/dist/policy.js +8 -0
  20. package/dist/scheduler.js +38 -9
  21. package/dist/session-revert.js +125 -0
  22. package/dist/sessions.js +101 -0
  23. package/dist/snapshots.js +69 -0
  24. package/dist/system.js +2 -89
  25. package/dist/telemetry.js +26 -1
  26. package/dist/todos.js +241 -0
  27. package/dist/tools/filesystem.js +102 -22
  28. package/dist/tools/registry.js +184 -45
  29. package/dist/tools/ripgrep.js +7 -6
  30. package/dist/tools/search.js +172 -17
  31. package/dist/tools/shared.js +6 -0
  32. package/dist/tools.js +7 -39
  33. package/dist/ui/diff-panel.js +5 -5
  34. package/dist/ui/diff-view.js +16 -7
  35. package/dist/ui/diff.js +73 -51
  36. package/dist/ui/errors.js +20 -6
  37. package/dist/ui/input.js +24 -20
  38. package/dist/ui/live-tail.js +36 -1
  39. package/dist/ui/markdown.js +9 -4
  40. package/dist/ui/modals.js +6 -4
  41. package/dist/ui/paint-scheduler.js +120 -0
  42. package/dist/ui/palette.js +4 -2
  43. package/dist/ui/pickers.js +4 -1
  44. package/dist/ui/side-by-side.js +88 -27
  45. package/dist/ui/status-bar.js +63 -8
  46. package/dist/ui/stream-store.js +7 -0
  47. package/dist/ui/theme.js +23 -1
  48. package/dist/ui/todo-panel.js +5 -2
  49. package/dist/ui/tool-inspector.js +33 -4
  50. package/dist/ui/transcript.js +9 -6
  51. package/dist/web/events.js +93 -0
  52. package/dist/web/runtime.js +790 -0
  53. package/dist/web/server.js +570 -0
  54. package/dist/web/ui/app.js +1925 -0
  55. package/dist/web/ui/index.html +135 -0
  56. package/dist/web/ui/styles.css +515 -0
  57. package/dist/zen.js +115 -4
  58. package/documentation/cli.md +5 -5
  59. package/documentation/configuration.md +11 -6
  60. package/documentation/development.md +4 -3
  61. package/documentation/extensions.md +1 -1
  62. package/documentation/goals.md +1 -1
  63. package/documentation/index.md +4 -4
  64. package/documentation/providers.md +2 -3
  65. package/documentation/skills.md +3 -3
  66. package/documentation/tools.md +8 -3
  67. package/documentation/troubleshooting.md +1 -1
  68. package/examples/extensions/01-audit-gate.js +2 -2
  69. package/examples/extensions/02-notes-tool.js +2 -2
  70. package/examples/extensions/03-custom-command.js +2 -2
  71. package/package.json +3 -2
@@ -0,0 +1,135 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="color-scheme" content="light dark">
7
+ <meta name="description" content="ATOM WebUI — local agentic development environment">
8
+ <script>
9
+ /* Theme before first paint: saved choice wins, else the OS preference. */
10
+ try {
11
+ var saved = localStorage.getItem("atom-theme");
12
+ var theme = saved === "light" || saved === "dark" ? saved :
13
+ (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark");
14
+ document.documentElement.dataset.theme = theme;
15
+ } catch (e) {
16
+ document.documentElement.dataset.theme = "dark";
17
+ }
18
+ </script>
19
+ <link rel="stylesheet" href="/styles.css">
20
+ <title>ATOM WebUI</title>
21
+ </head>
22
+ <body>
23
+ <div id="app">
24
+ <aside id="sidebar" aria-label="Sessions and settings">
25
+ <div class="brand-row">
26
+ <button id="menu-close" class="icon-btn only-narrow" type="button" aria-label="Close sidebar">×</button>
27
+ <div class="brand">ATOM</div>
28
+ <div class="brand-sub">local agent</div>
29
+ </div>
30
+ <button id="new-session" type="button">+ New session</button>
31
+ <div id="session-list" aria-label="Session history"></div>
32
+ <div class="side-section">
33
+ <div class="side-label">Workspace</div>
34
+ <div id="workspace" class="workspace" title="Session working directory">—</div>
35
+ <div class="side-label">Settings</div>
36
+ <label>Effort
37
+ <select id="effort">
38
+ <option value="auto">Auto</option>
39
+ <option value="low">Low</option>
40
+ <option value="medium">Medium</option>
41
+ <option value="high">High</option>
42
+ <option value="max">Max</option>
43
+ </select>
44
+ </label>
45
+ <label>Mode
46
+ <select id="mode">
47
+ <option value="normal">normal</option>
48
+ <option value="yolo">yolo</option>
49
+ <option value="plan">plan</option>
50
+ </select>
51
+ </label>
52
+ <div class="theme-row">
53
+ <button id="theme-toggle" type="button" title="Toggle light / dark">Theme</button>
54
+ </div>
55
+ <div id="conn" class="conn">connecting…</div>
56
+ </div>
57
+ </aside>
58
+ <div id="scrim" hidden></div>
59
+ <main id="center">
60
+ <div class="topbar">
61
+ <button id="menu-open" class="icon-btn only-narrow" type="button" aria-label="Open sidebar">☰</button>
62
+ <div id="status" class="status idle">idle</div>
63
+ <button id="panel-toggle" class="icon-btn text-btn only-narrow-wide" type="button">Activity</button>
64
+ </div>
65
+ <div id="error" class="error" hidden></div>
66
+ <div id="transcript" aria-live="polite"></div>
67
+ <div id="modal" hidden>
68
+ <div id="modal-card">
69
+ <div id="modal-title"></div>
70
+ <div id="modal-body"></div>
71
+ <div id="modal-actions"></div>
72
+ </div>
73
+ </div>
74
+ <form id="composer">
75
+ <div class="composer-box">
76
+ <textarea id="input" rows="1" placeholder="Message ATOM…"></textarea>
77
+ <div class="composer-row">
78
+ <button id="attach" class="icon-btn" type="button" title="Attach a file (its content is pasted into the message)" aria-label="Attach a file">+</button>
79
+ <input id="file-input" type="file" hidden>
80
+ <select id="provider" title="Provider"></select>
81
+ <select id="model" title="Model"></select>
82
+ <span class="spacer"></span>
83
+ <button id="send" type="submit" aria-label="Send">↑</button>
84
+ <button id="stop" type="button" aria-label="Stop" disabled>■</button>
85
+ </div>
86
+ </div>
87
+ </form>
88
+ </main>
89
+ <aside id="right" aria-label="Agent activity">
90
+ <div class="right-head">
91
+ <span>Activity</span>
92
+ <button id="panel-close" class="icon-btn only-narrow-wide" type="button" aria-label="Close activity panel">×</button>
93
+ </div>
94
+ <div class="right-section">
95
+ <div class="side-label">Now</div>
96
+ <div id="op-now" class="op-now">idle</div>
97
+ <div id="tool-counts" class="counts"></div>
98
+ </div>
99
+ <div class="right-section">
100
+ <div class="side-label">Timeline</div>
101
+ <div id="timeline"></div>
102
+ </div>
103
+ <div class="right-section">
104
+ <div class="side-label">Files</div>
105
+ <div id="files"></div>
106
+ </div>
107
+ <div class="right-section">
108
+ <div class="side-label">Commands</div>
109
+ <div id="commands"></div>
110
+ </div>
111
+ <div class="right-section">
112
+ <div class="side-label">Errors</div>
113
+ <div id="errors"></div>
114
+ </div>
115
+ </aside>
116
+ </div>
117
+ <div id="viewer" hidden>
118
+ <div id="viewer-card">
119
+ <div class="viewer-head">
120
+ <div>
121
+ <div id="viewer-path"></div>
122
+ <div id="viewer-meta"></div>
123
+ </div>
124
+ <div class="viewer-tabs">
125
+ <button id="tab-unified" type="button" class="tab active">Unified</button>
126
+ <button id="tab-side" type="button" class="tab">Before / after</button>
127
+ <button id="viewer-close" class="icon-btn" type="button" aria-label="Close diff viewer">×</button>
128
+ </div>
129
+ </div>
130
+ <div id="viewer-body"></div>
131
+ </div>
132
+ </div>
133
+ <script src="/app.js"></script>
134
+ </body>
135
+ </html>
@@ -0,0 +1,515 @@
1
+ /* ATOM WebUI — ChatGPT-style calm workspace, ATOM identity.
2
+ * Dual theme via [data-theme] on <html> (toggle persisted, OS respected).
3
+ * Shape rule: 12px surfaces, 24px composer, circular icon buttons.
4
+ * One accent (neutral send); green/red/amber are semantic-only. */
5
+ :root {
6
+ --radius-surface: 12px;
7
+ --radius-composer: 24px;
8
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
9
+ }
10
+ [data-theme="light"] {
11
+ --bg: #ffffff;
12
+ --surface: #f9f9f9;
13
+ --elevated: #f4f4f4;
14
+ --input: #f4f4f4;
15
+ --border: #e5e5e5;
16
+ --text: #0d0d0d;
17
+ --dim: #5d5d5d;
18
+ --faint: #8a8a8a;
19
+ --accent-btn: #0d0d0d;
20
+ --accent-btn-text: #ffffff;
21
+ --hover: #ececec;
22
+ --del-bg: rgba(201, 58, 58, 0.08);
23
+ --add-bg: rgba(23, 138, 76, 0.1);
24
+ --ok: #178a4c;
25
+ --error: #c93a3a;
26
+ --warn: #b7791f;
27
+ --accent: #0d0d0d;
28
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
29
+ --code-bg: #f4f4f4;
30
+ --tok-k: #cf222e;
31
+ --tok-s: #0a3069;
32
+ --tok-c: #6e7781;
33
+ --tok-n: #0550ae;
34
+ }
35
+ [data-theme="dark"] {
36
+ --bg: #212121;
37
+ --surface: #171717;
38
+ --elevated: #2f2f2f;
39
+ --input: #2f2f2f;
40
+ --border: #424242;
41
+ --text: #ececec;
42
+ --dim: #b4b4b4;
43
+ --faint: #8a8a8a;
44
+ --accent-btn: #ffffff;
45
+ --accent-btn-text: #0d0d0d;
46
+ --hover: #383838;
47
+ --del-bg: rgba(248, 81, 73, 0.12);
48
+ --add-bg: rgba(63, 185, 80, 0.12);
49
+ --ok: #3fb950;
50
+ --error: #f85149;
51
+ --warn: #d29922;
52
+ --accent: #ececec;
53
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
54
+ --code-bg: #2f2f2f;
55
+ --tok-k: #ff7b72;
56
+ --tok-s: #a5d6ff;
57
+ --tok-c: #7d8590;
58
+ --tok-n: #79c0ff;
59
+ }
60
+ * { box-sizing: border-box; }
61
+ html, body { height: 100%; }
62
+ body {
63
+ margin: 0;
64
+ background: var(--bg);
65
+ color: var(--text);
66
+ font: 15px/1.6 system-ui, -apple-system, "Segoe UI", sans-serif;
67
+ font-variant-numeric: tabular-nums;
68
+ }
69
+ :focus-visible { outline: 2px solid var(--dim); outline-offset: 1px; }
70
+ button, select, textarea, summary { font: inherit; }
71
+ #app { display: flex; height: 100vh; height: 100dvh; overflow: hidden; }
72
+
73
+ /* ---------- left sidebar ---------- */
74
+ #sidebar {
75
+ width: 260px;
76
+ flex: none;
77
+ background: var(--surface);
78
+ padding: 12px 10px;
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 8px;
82
+ overflow-y: auto;
83
+ }
84
+ .brand-row { display: flex; align-items: baseline; gap: 8px; padding: 2px 6px; }
85
+ .brand { font-weight: 600; letter-spacing: 0.14em; font-size: 15px; }
86
+ .brand-sub { font-size: 11px; color: var(--dim); }
87
+ #new-session {
88
+ background: transparent;
89
+ color: var(--text);
90
+ border: 1px solid var(--border);
91
+ border-radius: 10px;
92
+ padding: 8px 10px;
93
+ cursor: pointer;
94
+ text-align: left;
95
+ font-size: 14px;
96
+ }
97
+ #new-session:hover { background: var(--hover); }
98
+ #session-list { display: flex; flex-direction: column; gap: 2px; }
99
+ .session {
100
+ text-align: left;
101
+ background: transparent;
102
+ color: var(--text);
103
+ border: none;
104
+ border-radius: 10px;
105
+ padding: 8px 10px;
106
+ cursor: pointer;
107
+ font-size: 14px;
108
+ }
109
+ .session:hover { background: var(--hover); }
110
+ .session.active { background: var(--elevated); }
111
+ .session .sub { color: var(--dim); font-size: 12px; margin-top: 1px; }
112
+ .side-section { display: flex; flex-direction: column; gap: 8px; margin-top: auto; padding: 0 2px; }
113
+ .side-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); }
114
+ .side-section label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--dim); }
115
+ .workspace {
116
+ font-family: var(--mono);
117
+ font-size: 12px;
118
+ color: var(--dim);
119
+ word-break: break-all;
120
+ }
121
+ .conn { font-size: 12px; color: var(--dim); }
122
+ .conn.live { color: var(--ok); }
123
+ .conn.down { color: var(--error); }
124
+ .theme-row { display: flex; }
125
+ #theme-toggle {
126
+ background: transparent;
127
+ color: var(--dim);
128
+ border: 1px solid var(--border);
129
+ border-radius: 10px;
130
+ padding: 6px 10px;
131
+ cursor: pointer;
132
+ font-size: 12px;
133
+ width: 100%;
134
+ text-align: left;
135
+ }
136
+ #theme-toggle:hover { background: var(--hover); color: var(--text); }
137
+
138
+ /* ---------- center ---------- */
139
+ #center { flex: 1; display: flex; flex-direction: column; min-width: 0; background: var(--bg); }
140
+ .topbar { display: flex; align-items: center; gap: 8px; padding: 8px 16px 0; }
141
+ .status {
142
+ flex: 1;
143
+ color: var(--faint);
144
+ font-size: 12px;
145
+ white-space: nowrap;
146
+ overflow: hidden;
147
+ text-overflow: ellipsis;
148
+ }
149
+ .status.busy { color: var(--dim); }
150
+ .status.busy::before {
151
+ content: "";
152
+ display: inline-block;
153
+ width: 7px;
154
+ height: 7px;
155
+ border-radius: 50%;
156
+ background: var(--dim);
157
+ margin-right: 7px;
158
+ animation: pulse 1.6s ease-in-out infinite;
159
+ }
160
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
161
+ .icon-btn {
162
+ background: transparent;
163
+ color: var(--dim);
164
+ border: 1px solid transparent;
165
+ border-radius: 50%;
166
+ width: 32px;
167
+ height: 32px;
168
+ padding: 0;
169
+ cursor: pointer;
170
+ font-size: 16px;
171
+ line-height: 1;
172
+ display: inline-flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ flex: none;
176
+ }
177
+ .icon-btn:hover { background: var(--hover); color: var(--text); }
178
+ .icon-btn.text-btn { width: auto; height: auto; border-radius: 10px; padding: 6px 12px; font-size: 13px; }
179
+ .error {
180
+ max-width: 768px;
181
+ width: 100%;
182
+ margin: 8px auto 0;
183
+ padding: 10px 14px;
184
+ border: 1px solid var(--error);
185
+ border-radius: var(--radius-surface);
186
+ color: var(--error);
187
+ white-space: pre-wrap;
188
+ font-size: 13px;
189
+ }
190
+ #transcript { flex: 1; overflow-y: auto; padding: 16px 16px 8px; }
191
+ .msg { max-width: 768px; margin: 0 auto 20px; width: 100%; }
192
+ .msg .role { font-size: 12px; color: var(--faint); margin-bottom: 4px; font-weight: 600; }
193
+ .msg .body { overflow-wrap: break-word; }
194
+ .msg .body p { margin: 0 0 10px; max-width: 65ch; }
195
+ .msg .body p:last-child { margin-bottom: 0; }
196
+ .msg.user { display: flex; flex-direction: column; align-items: flex-end; }
197
+ .msg.user .body {
198
+ background: var(--elevated);
199
+ border-radius: 18px;
200
+ padding: 10px 16px;
201
+ max-width: 85%;
202
+ }
203
+ .msg.tool-row .body { font-size: 13px; color: var(--dim); }
204
+ .msg.tool-row.err .body { color: var(--error); }
205
+ .msg h1, .msg h2, .msg h3, .msg h4 { margin: 14px 0 8px; line-height: 1.3; font-weight: 600; }
206
+ .msg h1 { font-size: 1.3em; } .msg h2 { font-size: 1.18em; }
207
+ .msg h3 { font-size: 1.08em; } .msg h4 { font-size: 1em; }
208
+ .msg ul, .msg ol { margin: 4px 0 10px; padding-left: 22px; }
209
+ .msg blockquote { margin: 4px 0 10px; padding: 2px 12px; border-left: 2px solid var(--border); color: var(--dim); }
210
+ .msg hr { border: none; border-top: 1px solid var(--border); margin: 12px 0; }
211
+ .msg table { border-collapse: collapse; margin: 6px 0 12px; font-size: 13px; }
212
+ .msg th, .msg td { border: 1px solid var(--border); padding: 5px 10px; text-align: left; }
213
+ .msg code.inline {
214
+ font-family: var(--mono);
215
+ font-size: 0.86em;
216
+ background: var(--elevated);
217
+ border-radius: 6px;
218
+ padding: 1px 5px;
219
+ }
220
+ .msg a { color: var(--text); }
221
+ .codeblock { border: none; border-radius: var(--radius-surface); margin: 8px 0 12px; overflow: hidden; background: var(--code-bg); }
222
+ .codeblock .code-head {
223
+ display: flex;
224
+ justify-content: space-between;
225
+ align-items: center;
226
+ padding: 6px 14px;
227
+ font-family: var(--mono);
228
+ font-size: 11px;
229
+ color: var(--dim);
230
+ }
231
+ .codeblock .copy { background: transparent; border: none; color: var(--dim); cursor: pointer; font-size: 11px; padding: 2px 4px; }
232
+ .codeblock .copy:hover { color: var(--text); }
233
+ .codeblock pre { margin: 0; padding: 10px 14px 12px; overflow-x: auto; font-family: var(--mono); font-size: 12.5px; }
234
+ .tok-k { color: var(--tok-k); }
235
+ .tok-s { color: var(--tok-s); }
236
+ .tok-c { color: var(--tok-c); font-style: italic; }
237
+ .tok-n { color: var(--tok-n); }
238
+ .msg details.tool { border: 1px solid var(--border); border-radius: var(--radius-surface); padding: 6px 12px; background: var(--bg); }
239
+ .msg details.tool summary { cursor: pointer; font-family: var(--mono); font-size: 12px; color: var(--dim); }
240
+ .msg details.tool .tool-detail { margin-top: 8px; }
241
+ .msg details.tool pre {
242
+ background: var(--surface);
243
+ border: 1px solid var(--border);
244
+ border-radius: 8px;
245
+ padding: 8px 10px;
246
+ overflow-x: auto;
247
+ font-family: var(--mono);
248
+ font-size: 12px;
249
+ white-space: pre-wrap;
250
+ }
251
+ .msg details.thinking { border: none; padding: 2px 0; }
252
+ .msg details.thinking summary { cursor: pointer; color: var(--faint); font-size: 12px; }
253
+ .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; background: var(--faint); }
254
+ .dot.run { background: var(--dim); }
255
+ .dot.ok { background: var(--ok); }
256
+ .dot.fail { background: var(--error); }
257
+ .dot.denied { background: var(--warn); }
258
+
259
+ /* ---------- composer (pill) ---------- */
260
+ #composer { padding: 8px 16px 14px; }
261
+ .composer-box {
262
+ max-width: 768px;
263
+ margin: 0 auto;
264
+ background: var(--input);
265
+ border-radius: var(--radius-composer);
266
+ padding: 10px 8px 8px 8px;
267
+ box-shadow: var(--shadow);
268
+ }
269
+ .composer-box:focus-within { outline: 1px solid var(--border); }
270
+ #input {
271
+ resize: none;
272
+ width: 100%;
273
+ background: transparent;
274
+ color: var(--text);
275
+ border: none;
276
+ outline: none;
277
+ padding: 6px 12px;
278
+ min-height: 24px;
279
+ max-height: 220px;
280
+ font-size: 15px;
281
+ }
282
+ #input::placeholder { color: var(--faint); }
283
+ .composer-row { display: flex; gap: 6px; align-items: center; padding: 2px 6px 0; }
284
+ .composer-row select {
285
+ background: transparent;
286
+ color: var(--dim);
287
+ border: none;
288
+ border-radius: 8px;
289
+ padding: 5px 4px;
290
+ font-size: 12px;
291
+ max-width: 150px;
292
+ cursor: pointer;
293
+ }
294
+ .composer-row select:hover { background: var(--hover); color: var(--text); }
295
+ /* Slash-command menu (same prefix-then-fuzzy tiers as the TUI). */
296
+ #slash-menu {
297
+ display: flex;
298
+ flex-direction: column;
299
+ gap: 2px;
300
+ padding: 6px;
301
+ margin-bottom: 6px;
302
+ background: var(--bg);
303
+ border: 1px solid var(--border);
304
+ border-radius: 14px;
305
+ box-shadow: var(--shadow);
306
+ max-height: 260px;
307
+ overflow-y: auto;
308
+ }
309
+ .slash-row {
310
+ display: flex;
311
+ gap: 10px;
312
+ align-items: baseline;
313
+ text-align: left;
314
+ background: transparent;
315
+ border: none;
316
+ border-radius: 8px;
317
+ padding: 7px 10px;
318
+ cursor: pointer;
319
+ color: var(--text);
320
+ font-size: 13px;
321
+ }
322
+ .slash-row:hover, .slash-row.active { background: var(--hover); }
323
+ .slash-name { font-family: var(--mono); font-weight: 600; flex: none; }
324
+ .slash-desc { color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
325
+ /* /thinking hides the reasoning blocks (view only — turns untouched). */
326
+ #transcript.hide-thinking details.thinking { display: none; }
327
+ .spacer { flex: 1; }
328
+ #send, #stop {
329
+ width: 32px;
330
+ height: 32px;
331
+ border-radius: 50%;
332
+ border: none;
333
+ cursor: pointer;
334
+ font-size: 16px;
335
+ line-height: 1;
336
+ display: inline-flex;
337
+ align-items: center;
338
+ justify-content: center;
339
+ flex: none;
340
+ padding: 0;
341
+ }
342
+ #send { background: var(--accent-btn); color: var(--accent-btn-text); }
343
+ #stop { background: transparent; color: var(--text); border: 1px solid var(--border); }
344
+ #send:disabled { opacity: 0.25; cursor: default; }
345
+ #stop:disabled { opacity: 0.3; cursor: default; }
346
+ #send:not(:disabled):hover, #stop:not(:disabled):hover { transform: scale(1.04); }
347
+ #send:not(:disabled):active, #stop:not(:disabled):active { transform: scale(0.96); }
348
+
349
+ /* ---------- right activity panel ---------- */
350
+ #right {
351
+ width: 300px;
352
+ flex: none;
353
+ background: var(--surface);
354
+ padding: 14px 12px;
355
+ overflow-y: auto;
356
+ display: flex;
357
+ flex-direction: column;
358
+ gap: 14px;
359
+ font-size: 13px;
360
+ }
361
+ .right-head { font-weight: 600; font-size: 13px; display: flex; justify-content: space-between; align-items: center; }
362
+ .right-section { display: flex; flex-direction: column; gap: 6px; }
363
+ .op-now { font-family: var(--mono); font-size: 12px; color: var(--dim); }
364
+ .counts { font-size: 12px; color: var(--faint); font-family: var(--mono); }
365
+ #timeline { display: flex; flex-direction: column; gap: 8px; }
366
+ .turn { border: 1px solid var(--border); border-radius: var(--radius-surface); overflow: hidden; background: var(--bg); }
367
+ .turn-head {
368
+ display: flex;
369
+ align-items: center;
370
+ gap: 8px;
371
+ width: 100%;
372
+ background: transparent;
373
+ color: var(--text);
374
+ border: none;
375
+ padding: 8px 10px;
376
+ cursor: pointer;
377
+ font-size: 12px;
378
+ font-family: var(--mono);
379
+ text-align: left;
380
+ }
381
+ .turn-head:hover { background: var(--hover); }
382
+ .turn-title { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
383
+ .turn-time { color: var(--faint); }
384
+ .turn-caret { color: var(--faint); }
385
+ .turn-body { display: flex; flex-direction: column; gap: 2px; padding: 4px 6px 8px 12px; border-left: 2px solid var(--border); margin: 2px 0 8px 16px; }
386
+ .tl-row { display: flex; gap: 8px; padding: 4px 0; font-family: var(--mono); font-size: 12px; align-items: baseline; }
387
+ .tl-row .tl-detail { color: var(--dim); word-break: break-all; }
388
+ .tl-round { color: var(--faint); font-size: 11px; font-family: var(--mono); padding: 2px 0 2px 22px; }
389
+ details.tl-tool { font-family: var(--mono); font-size: 12px; }
390
+ details.tl-tool summary { cursor: pointer; padding: 3px 0; word-break: break-all; color: var(--dim); }
391
+ .tl-empty, .list-empty { color: var(--faint); font-size: 12px; }
392
+ .file-row, .cmd-row { font-family: var(--mono); font-size: 12px; word-break: break-all; }
393
+ .file-row.clickable { cursor: pointer; border-radius: 6px; }
394
+ .file-row.clickable:hover { background: var(--hover); }
395
+ .badge {
396
+ display: inline-block;
397
+ font-size: 10px;
398
+ text-transform: uppercase;
399
+ letter-spacing: 0.06em;
400
+ border: 1px solid var(--border);
401
+ border-radius: 6px;
402
+ padding: 0 5px;
403
+ margin-right: 5px;
404
+ color: var(--dim);
405
+ }
406
+ .badge.created { color: var(--ok); border-color: var(--ok); }
407
+ .badge.modified { color: var(--warn); border-color: var(--warn); }
408
+ .cmd-line { margin-bottom: 2px; }
409
+ .cmd-status { color: var(--dim); font-size: 11px; margin: 2px 0; }
410
+ .cmd-status.ok { color: var(--ok); }
411
+ .cmd-status.bad { color: var(--error); }
412
+ .cmd-row details summary { cursor: pointer; color: var(--faint); font-size: 11px; }
413
+ .cmd-row pre {
414
+ margin: 2px 0 6px;
415
+ padding: 6px 8px;
416
+ background: var(--bg);
417
+ border: 1px solid var(--border);
418
+ border-radius: 8px;
419
+ white-space: pre-wrap;
420
+ font-size: 11px;
421
+ color: var(--dim);
422
+ max-height: 260px;
423
+ overflow-y: auto;
424
+ }
425
+ .err-row { font-family: var(--mono); font-size: 12px; word-break: break-word; }
426
+ select, #input { max-width: 100%; }
427
+
428
+ /* ---------- modal + diff viewer ---------- */
429
+ #modal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; z-index: 40; }
430
+ #modal[hidden] { display: none; }
431
+ #modal-card {
432
+ background: var(--bg);
433
+ border: 1px solid var(--border);
434
+ border-radius: 16px;
435
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
436
+ padding: 18px;
437
+ max-width: 640px;
438
+ width: calc(100% - 48px);
439
+ max-height: 80vh;
440
+ overflow-y: auto;
441
+ animation: rise 140ms ease-out;
442
+ }
443
+ #modal-title { font-weight: 600; margin-bottom: 8px; }
444
+ #modal-body { font-family: var(--mono); font-size: 12px; white-space: pre-wrap; margin-bottom: 14px; color: var(--dim); }
445
+ #modal-actions { display: flex; gap: 8px; flex-wrap: wrap; }
446
+ #modal-actions button {
447
+ background: var(--elevated);
448
+ color: var(--text);
449
+ border: 1px solid transparent;
450
+ border-radius: 10px;
451
+ padding: 8px 14px;
452
+ cursor: pointer;
453
+ font-size: 13px;
454
+ }
455
+ #modal-actions button:hover { border-color: var(--border); }
456
+ #viewer { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; z-index: 40; }
457
+ #viewer[hidden] { display: none; }
458
+ #viewer-card {
459
+ background: var(--bg);
460
+ border: 1px solid var(--border);
461
+ border-radius: 16px;
462
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
463
+ width: min(1000px, calc(100% - 48px));
464
+ max-height: 86vh;
465
+ display: flex;
466
+ flex-direction: column;
467
+ animation: rise 140ms ease-out;
468
+ }
469
+ .viewer-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--border); }
470
+ #viewer-path { font-family: var(--mono); font-weight: 600; font-size: 13px; word-break: break-all; }
471
+ #viewer-meta { font-size: 12px; color: var(--dim); font-family: var(--mono); margin-top: 2px; }
472
+ .viewer-tabs { display: flex; gap: 6px; flex: none; align-items: center; }
473
+ .tab { background: transparent; color: var(--dim); border: 1px solid var(--border); border-radius: 10px; padding: 5px 12px; cursor: pointer; font-size: 12px; }
474
+ .tab.active { color: var(--text); background: var(--elevated); border-color: transparent; }
475
+ #viewer-body { overflow: auto; padding: 14px 18px; font-family: var(--mono); font-size: 12px; }
476
+ .hunk-head { color: var(--dim); padding: 10px 0 4px; white-space: pre; font-size: 11px; }
477
+ .hunk-head:first-child { padding-top: 0; }
478
+ .dline { display: flex; white-space: pre-wrap; word-break: break-word; border-radius: 4px; }
479
+ .dline .dno { flex: none; width: 44px; color: var(--faint); text-align: right; padding-right: 8px; user-select: none; }
480
+ .dline .dcode { flex: 1; padding: 1px 8px; }
481
+ .dline.del { background: var(--del-bg); }
482
+ .dline.add { background: var(--add-bg); }
483
+ .sbs-head, .sbs-row { display: flex; gap: 8px; }
484
+ .sbs-head span { flex: 1; color: var(--faint); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; padding: 4px 8px; }
485
+ .sbs-cell { flex: 1; min-width: 0; display: flex; padding: 1px 4px 1px 0; border-radius: 4px; }
486
+ .sbs-cell .dno { flex: none; width: 36px; color: var(--faint); text-align: right; padding-right: 6px; user-select: none; }
487
+ .sbs-cell .dcode { flex: 1; padding: 0 6px; white-space: pre-wrap; word-break: break-word; }
488
+ .sbs-cell.del { background: var(--del-bg); }
489
+ .sbs-cell.add { background: var(--add-bg); }
490
+ .sbs-cell.empty { background: transparent; }
491
+
492
+ /* ---------- responsive ---------- */
493
+ .only-narrow { display: none; }
494
+ .only-narrow-wide { display: none; }
495
+ #scrim { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); z-index: 20; }
496
+ #scrim[hidden] { display: none; }
497
+ @keyframes rise { from { transform: translateY(4px); opacity: 0.6; } to { transform: none; opacity: 1; } }
498
+ @media (max-width: 1240px) {
499
+ #right { position: fixed; right: 0; top: 0; bottom: 0; z-index: 30; box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12); border-left: 1px solid var(--border); }
500
+ #right.hidden-narrow { display: none; }
501
+ .only-narrow-wide { display: inline-flex; }
502
+ }
503
+ @media (min-width: 1241px) { #right.hidden-narrow { display: flex; } }
504
+ @media (max-width: 820px) {
505
+ #sidebar { position: fixed; left: 0; top: 0; bottom: 0; z-index: 30; box-shadow: 8px 0 24px rgba(0, 0, 0, 0.12); }
506
+ #sidebar.hidden-narrow { display: none; }
507
+ .only-narrow { display: inline-flex; }
508
+ .composer-row select { max-width: 110px; }
509
+ .msg { margin-bottom: 16px; }
510
+ }
511
+ @media (prefers-reduced-motion: reduce) {
512
+ .status.busy::before { animation: none; }
513
+ #modal-card, #viewer-card { animation: none; }
514
+ #send:not(:disabled):hover, #stop:not(:disabled):hover { transform: none; }
515
+ }