claude-code-kanban 5.1.1 → 5.3.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 (50) hide show
  1. package/README.md +3 -2
  2. package/lib/auto-compact.js +42 -0
  3. package/lib/claude-dir.js +7 -1
  4. package/lib/claude-settings.js +21 -0
  5. package/lib/dispatch.js +11 -4
  6. package/lib/net-guard.js +41 -14
  7. package/lib/proc-stats.js +82 -0
  8. package/lib/retention.js +140 -0
  9. package/lib/session-events.js +30 -7
  10. package/lib/terminal.js +39 -6
  11. package/lib/worktrees.js +105 -0
  12. package/package.json +1 -1
  13. package/plugin/plugins/claude-code-kanban/.claude-plugin/plugin.json +1 -1
  14. package/plugin/plugins/claude-code-kanban/hooks/hooks.json +15 -8
  15. package/plugin/plugins/claude-code-kanban/monitors.json +3 -3
  16. package/plugin/plugins/claude-code-kanban/scripts/postman.js +2 -2
  17. package/plugin/plugins/claude-code-kanban/skills/{kanban-dispatch → dispatch}/SKILL.md +3 -3
  18. package/plugin/plugins/claude-code-kanban/skills/{kanban-follow → follow}/SKILL.md +17 -9
  19. package/plugin/plugins/claude-code-kanban/skills/kanban/SKILL.md +1 -2
  20. package/public/app.js +1332 -490
  21. package/public/fonts/OFL-IBM-Plex.txt +93 -0
  22. package/public/fonts/OFL-Playfair-Display.txt +93 -0
  23. package/public/fonts/fonts.css +244 -0
  24. package/public/fonts/ibm-plex-mono-cyrillic-400.woff2 +0 -0
  25. package/public/fonts/ibm-plex-mono-cyrillic-500.woff2 +0 -0
  26. package/public/fonts/ibm-plex-mono-cyrillic-600.woff2 +0 -0
  27. package/public/fonts/ibm-plex-mono-cyrillic-ext-400.woff2 +0 -0
  28. package/public/fonts/ibm-plex-mono-cyrillic-ext-500.woff2 +0 -0
  29. package/public/fonts/ibm-plex-mono-cyrillic-ext-600.woff2 +0 -0
  30. package/public/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
  31. package/public/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
  32. package/public/fonts/ibm-plex-mono-latin-600.woff2 +0 -0
  33. package/public/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
  34. package/public/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
  35. package/public/fonts/ibm-plex-mono-latin-ext-600.woff2 +0 -0
  36. package/public/fonts/ibm-plex-mono-vietnamese-400.woff2 +0 -0
  37. package/public/fonts/ibm-plex-mono-vietnamese-500.woff2 +0 -0
  38. package/public/fonts/ibm-plex-mono-vietnamese-600.woff2 +0 -0
  39. package/public/fonts/playfair-display-cyrillic-400.woff2 +0 -0
  40. package/public/fonts/playfair-display-latin-400.woff2 +0 -0
  41. package/public/fonts/playfair-display-latin-ext-400.woff2 +0 -0
  42. package/public/fonts/playfair-display-vietnamese-400.woff2 +0 -0
  43. package/public/index.html +27 -17
  44. package/public/project-match.js +6 -4
  45. package/public/style.css +528 -116
  46. package/public/terminal-frame.js +341 -0
  47. package/public/terminal.html +27 -0
  48. package/public/vendor/claude-hub-sdk.js +13 -3
  49. package/server.js +204 -60
  50. package/skill-guides/dispatch.md +5 -5
package/public/style.css CHANGED
@@ -26,7 +26,6 @@
26
26
  --danger: #ef5350;
27
27
  --danger-dim: rgba(239, 83, 80, 0.18);
28
28
  --team: #60a5fa;
29
- --team-dim: rgba(96, 165, 250, 0.18);
30
29
  --plan: #86a886;
31
30
  --plan-dim: rgba(134, 168, 134, 0.18);
32
31
  --ansi-red: #e06c66;
@@ -43,6 +42,7 @@
43
42
  --ansi-bright-cyan: #7ddede;
44
43
  --mono: 'IBM Plex Mono', monospace;
45
44
  --serif: 'Playfair Display', serif;
45
+ --sans: Arial, Helvetica, sans-serif;
46
46
  --card-gap: 7px;
47
47
  /* the name's supporting lines read as one unit with it, so they sit tighter
48
48
  than the gap separating the card's independent rows */
@@ -72,6 +72,13 @@ body {
72
72
  padding: 0;
73
73
  }
74
74
 
75
+ button,
76
+ input,
77
+ select,
78
+ textarea {
79
+ font-family: inherit;
80
+ }
81
+
75
82
  body {
76
83
  font-family: var(--mono);
77
84
  font-size: 14px;
@@ -106,6 +113,18 @@ body::before {
106
113
  ::-webkit-scrollbar-thumb:hover {
107
114
  background: var(--text-muted);
108
115
  }
116
+ .column-tasks::-webkit-scrollbar-thumb,
117
+ .sessions-list::-webkit-scrollbar-thumb {
118
+ background: transparent;
119
+ }
120
+ .column-tasks:hover::-webkit-scrollbar-thumb,
121
+ .sessions-list:hover::-webkit-scrollbar-thumb {
122
+ background: var(--border);
123
+ }
124
+ .column-tasks::-webkit-scrollbar-thumb:hover,
125
+ .sessions-list::-webkit-scrollbar-thumb:hover {
126
+ background: var(--text-muted);
127
+ }
109
128
 
110
129
  /* #endregion */
111
130
 
@@ -480,9 +499,8 @@ body::before {
480
499
 
481
500
  .section-header-label {
482
501
  flex: 0 0 auto;
502
+ font-size: 12px;
483
503
  font-weight: 500;
484
- text-transform: uppercase;
485
- letter-spacing: 0.12em;
486
504
  }
487
505
 
488
506
  /* Non-default filters, spelled out in the header so folding them into the popover
@@ -810,7 +828,7 @@ body::before {
810
828
  padding: 13px 15px;
811
829
  margin-bottom: 6px;
812
830
  background: var(--sidebar-item-bg, transparent);
813
- border: 1px solid var(--border);
831
+ border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
814
832
  border-radius: 9px;
815
833
  text-align: left;
816
834
  cursor: pointer;
@@ -914,8 +932,13 @@ body.booting .boot-splash {
914
932
  /* `.active` is the open session, `.kb-selected` the keyboard cursor. They own different
915
933
  properties — fill and border — so the cursor landing on the open session adds to it
916
934
  instead of replacing how it looks. */
935
+ .session-item:hover {
936
+ background: color-mix(in srgb, var(--bg-hover) 40%, var(--sidebar-item-bg, transparent));
937
+ }
938
+
917
939
  .session-item.active {
918
940
  background: var(--session-active-bg);
941
+ border-color: var(--border);
919
942
  }
920
943
 
921
944
  .session-item.kb-selected,
@@ -1001,6 +1024,7 @@ body.booting .boot-splash {
1001
1024
  }
1002
1025
 
1003
1026
  .session-name {
1027
+ font-family: var(--sans);
1004
1028
  font-size: 14px;
1005
1029
  font-weight: 500;
1006
1030
  color: var(--text-primary);
@@ -1069,6 +1093,7 @@ body.booting .boot-splash {
1069
1093
  }
1070
1094
 
1071
1095
  .session-secondary {
1096
+ font-family: var(--sans);
1072
1097
  font-size: 12px;
1073
1098
  font-weight: 450;
1074
1099
  color: var(--text-tertiary);
@@ -1108,26 +1133,36 @@ body.booting .boot-splash {
1108
1133
  text-overflow: ellipsis;
1109
1134
  }
1110
1135
 
1111
- .session-plan {
1112
- font-size: 10px;
1113
- color: var(--plan);
1114
- margin-top: var(--card-gap-tight);
1115
- white-space: nowrap;
1116
- overflow: hidden;
1117
- text-overflow: ellipsis;
1118
- }
1119
-
1120
1136
  .session-item.plan-reveal {
1121
1137
  outline: 1.5px solid var(--plan);
1122
1138
  background: var(--plan-dim);
1123
1139
  }
1124
1140
 
1141
+ .session-metrics {
1142
+ display: flex;
1143
+ flex-direction: column;
1144
+ gap: var(--card-gap);
1145
+ margin-top: var(--card-gap);
1146
+ line-height: 1;
1147
+ }
1148
+
1125
1149
  .session-progress {
1126
1150
  display: flex;
1127
1151
  align-items: center;
1128
1152
  gap: 9px;
1153
+ }
1154
+
1155
+ .session-footer {
1156
+ display: flex;
1157
+ align-items: center;
1158
+ justify-content: space-between;
1159
+ gap: 8px;
1129
1160
  margin-top: var(--card-gap);
1130
- line-height: 1;
1161
+ min-height: 18px;
1162
+ }
1163
+
1164
+ .session-footer .session-time {
1165
+ min-width: 0;
1131
1166
  }
1132
1167
 
1133
1168
  .progress-bar {
@@ -1158,12 +1193,15 @@ body.booting .boot-splash {
1158
1193
  font-variant-numeric: tabular-nums;
1159
1194
  }
1160
1195
 
1161
- .context-bar {
1162
- display: none;
1163
- margin-top: var(--card-gap);
1164
- }
1165
- .has-context .context-bar {
1166
- display: block;
1196
+ .context-bar-labels {
1197
+ display: flex;
1198
+ justify-content: space-between;
1199
+ gap: 6px;
1200
+ margin-top: 4px;
1201
+ font-size: 10px;
1202
+ font-family: var(--font-mono);
1203
+ color: var(--text-tertiary);
1204
+ white-space: nowrap;
1167
1205
  }
1168
1206
  .context-bar-track {
1169
1207
  height: 2px;
@@ -1188,15 +1226,6 @@ body.booting .boot-splash {
1188
1226
  z-index: 2;
1189
1227
  border-radius: 1px;
1190
1228
  }
1191
- .context-bar-labels {
1192
- display: flex;
1193
- justify-content: space-between;
1194
- margin-top: 2px;
1195
- font-size: 10px;
1196
- font-family: var(--font-mono);
1197
- color: var(--text-tertiary);
1198
- }
1199
-
1200
1229
  .detail-context {
1201
1230
  margin-bottom: 16px;
1202
1231
  }
@@ -1314,7 +1343,6 @@ body.booting .boot-splash {
1314
1343
  font-size: 11px;
1315
1344
  font-weight: 450;
1316
1345
  color: var(--text-tertiary);
1317
- margin-top: var(--card-gap);
1318
1346
  }
1319
1347
 
1320
1348
  .session-latest-msg {
@@ -1673,11 +1701,13 @@ body.booting .boot-splash {
1673
1701
  }
1674
1702
 
1675
1703
  .icon-btn-danger {
1676
- color: var(--danger);
1677
- border-color: var(--danger);
1704
+ color: var(--text-muted);
1705
+ background: transparent;
1706
+ border-color: transparent;
1678
1707
  }
1679
1708
 
1680
- .icon-btn-danger:hover {
1709
+ .icon-btn-danger:hover,
1710
+ .icon-btn-danger:focus-visible {
1681
1711
  background: color-mix(in srgb, var(--danger) 12%, transparent);
1682
1712
  color: color-mix(in srgb, var(--danger) 80%, black);
1683
1713
  border-color: var(--danger);
@@ -1957,7 +1987,10 @@ body.booting .boot-splash {
1957
1987
  }
1958
1988
 
1959
1989
  .task-card.blocked {
1960
- opacity: 0.7;
1990
+ border-left-style: dashed;
1991
+ }
1992
+ .task-card.blocked .task-title {
1993
+ color: var(--text-secondary);
1961
1994
  }
1962
1995
  .task-card.dragging {
1963
1996
  opacity: 0.4;
@@ -1983,30 +2016,17 @@ body.booting .boot-splash {
1983
2016
  .task-id {
1984
2017
  font-size: 11px;
1985
2018
  color: var(--text-muted);
1986
- margin-bottom: 4px;
2019
+ margin-bottom: 6px;
1987
2020
  display: flex;
1988
2021
  align-items: center;
1989
2022
  gap: 8px;
1990
2023
  }
1991
2024
 
1992
- .task-badge {
1993
- font-size: 10px;
1994
- font-weight: 600;
1995
- padding: 3px 8px;
1996
- border-radius: 4px;
1997
- text-transform: uppercase;
1998
- letter-spacing: 0.04em;
1999
- }
2000
-
2001
- .task-badge.blocked {
2002
- background: color-mix(in srgb, var(--accent) 15%, transparent);
2003
- color: var(--accent);
2004
- }
2005
-
2006
2025
  .task-title {
2007
2026
  font-size: 13px;
2027
+ font-weight: 500;
2008
2028
  color: var(--text-primary);
2009
- line-height: 1.35;
2029
+ line-height: 1.4;
2010
2030
  }
2011
2031
 
2012
2032
  .task-card.completed .task-title {
@@ -2036,17 +2056,17 @@ body.booting .boot-splash {
2036
2056
  }
2037
2057
 
2038
2058
  .task-blocked {
2039
- font-size: 11px;
2040
- font-weight: 500;
2059
+ margin-left: auto;
2041
2060
  color: var(--text-muted);
2042
- margin-top: 8px;
2061
+ white-space: nowrap;
2043
2062
  }
2044
2063
 
2045
2064
  .task-desc {
2046
- font-size: 12px;
2047
- font-weight: 450;
2065
+ font-size: 11.5px;
2066
+ font-weight: 400;
2067
+ line-height: 1.5;
2048
2068
  color: var(--text-tertiary);
2049
- margin-top: 6px;
2069
+ margin-top: 4px;
2050
2070
  display: -webkit-box;
2051
2071
  -webkit-line-clamp: 2;
2052
2072
  -webkit-box-orient: vertical;
@@ -2146,36 +2166,58 @@ body.booting .boot-splash {
2146
2166
  font-weight: 500;
2147
2167
  text-transform: uppercase;
2148
2168
  letter-spacing: 0.08em;
2149
- color: var(--text-muted);
2169
+ color: var(--accent-text, var(--accent));
2150
2170
  margin-bottom: 8px;
2171
+ display: flex;
2172
+ align-items: center;
2173
+ gap: 8px;
2151
2174
  }
2152
2175
 
2153
- .detail-title {
2154
- font-family: var(--serif);
2155
- font-size: 22px;
2176
+ .detail-edit-icon {
2177
+ width: 12px;
2178
+ height: 12px;
2179
+ opacity: 0.8;
2180
+ }
2181
+
2182
+ .detail-title,
2183
+ .detail-title-input {
2184
+ font-family: var(--mono);
2185
+ font-size: 17px;
2186
+ font-weight: 600;
2156
2187
  line-height: 1.4;
2157
- cursor: pointer;
2158
2188
  padding: 2px 4px;
2159
2189
  margin: -2px -4px;
2160
2190
  border-radius: 4px;
2191
+ }
2192
+
2193
+ .detail-title {
2161
2194
  border: 1px solid transparent;
2162
- transition: border-color 0.15s ease;
2195
+ transition:
2196
+ border-color 0.15s ease,
2197
+ background 0.15s ease;
2163
2198
  }
2164
2199
 
2165
- .detail-title:hover {
2166
- border-color: var(--border);
2200
+ #detail-content.blocked .detail-title {
2201
+ box-shadow: inset 3px 0 var(--warning);
2202
+ padding-left: 13px;
2203
+ margin-left: -13px;
2204
+ }
2205
+
2206
+ .detail-title.editable,
2207
+ .detail-desc.editable {
2208
+ cursor: pointer;
2209
+ }
2210
+
2211
+ .detail-title.editable:hover,
2212
+ .detail-desc.editable:hover {
2213
+ border-color: color-mix(in srgb, var(--accent) 45%, transparent);
2214
+ background: color-mix(in srgb, var(--accent) 5%, var(--bg-elevated));
2167
2215
  }
2168
2216
 
2169
2217
  .detail-title-input {
2170
- font-family: var(--serif);
2171
- font-size: 22px;
2172
- line-height: 1.4;
2173
2218
  width: 100%;
2174
- padding: 2px 4px;
2175
- margin: -2px -4px;
2176
2219
  background: var(--bg-elevated);
2177
2220
  border: 1px solid var(--accent);
2178
- border-radius: 4px;
2179
2221
  color: var(--text-primary);
2180
2222
  box-shadow: 0 0 0 2px var(--accent-dim);
2181
2223
  }
@@ -2214,11 +2256,19 @@ body.booting .boot-splash {
2214
2256
  border: 1px solid var(--success);
2215
2257
  }
2216
2258
 
2217
- .detail-status .dot {
2259
+ #detail-content.blocked .detail-status.pending {
2260
+ background: var(--warning-dim);
2261
+ color: var(--warning);
2262
+ border-color: color-mix(in srgb, var(--warning) 40%, transparent);
2263
+ }
2264
+
2265
+ .detail-status .dot,
2266
+ .detail-dep .dot {
2218
2267
  width: 8px;
2219
2268
  height: 8px;
2220
2269
  border-radius: 50%;
2221
2270
  background: currentColor;
2271
+ flex-shrink: 0;
2222
2272
  }
2223
2273
 
2224
2274
  .detail-status.in_progress .dot {
@@ -2241,35 +2291,20 @@ body.booting .boot-splash {
2241
2291
  color: var(--accent);
2242
2292
  }
2243
2293
 
2244
- .detail-box.blocked {
2245
- background: var(--warning-dim);
2246
- border: 1px solid rgba(240, 180, 41, 0.35);
2247
- color: var(--warning);
2248
- }
2249
-
2250
- .detail-box.blocks {
2251
- background: var(--team-dim);
2252
- border: 1px solid rgba(96, 165, 250, 0.35);
2253
- color: var(--team);
2254
- }
2255
-
2256
2294
  .detail-desc {
2257
2295
  font-size: 14px;
2258
2296
  line-height: 1.7;
2259
2297
  color: var(--text-secondary);
2260
- cursor: pointer;
2261
2298
  padding: 4px 6px;
2262
2299
  margin: -4px -6px;
2263
2300
  border-radius: 4px;
2264
2301
  border: 1px solid transparent;
2265
- transition: border-color 0.15s ease;
2302
+ transition:
2303
+ border-color 0.15s ease,
2304
+ background 0.15s ease;
2266
2305
  overflow-x: auto;
2267
2306
  }
2268
2307
 
2269
- .detail-desc:hover {
2270
- border-color: var(--border);
2271
- }
2272
-
2273
2308
  .detail-desc-textarea {
2274
2309
  width: 100%;
2275
2310
  min-height: 120px;
@@ -2328,9 +2363,68 @@ body.booting .boot-splash {
2328
2363
  }
2329
2364
 
2330
2365
  .detail-deps {
2331
- font-size: 14px;
2332
- line-height: 1.7;
2333
- color: var(--text-secondary);
2366
+ display: grid;
2367
+ grid-template-columns: max-content minmax(0, 1fr);
2368
+ gap: 8px 16px;
2369
+ align-items: start;
2370
+ font-size: 12px;
2371
+ }
2372
+
2373
+ .detail-deps .wait {
2374
+ --chip-color: var(--warning);
2375
+ }
2376
+
2377
+ .detail-deps .next {
2378
+ --chip-color: var(--success);
2379
+ }
2380
+
2381
+ .detail-deps-label {
2382
+ padding-top: 4px;
2383
+ color: var(--chip-color);
2384
+ }
2385
+
2386
+ .detail-deps-list {
2387
+ display: flex;
2388
+ flex-wrap: wrap;
2389
+ gap: 6px;
2390
+ min-width: 0;
2391
+ }
2392
+
2393
+ .detail-dep {
2394
+ --chip-color: var(--text-tertiary);
2395
+ display: inline-flex;
2396
+ align-items: center;
2397
+ gap: 6px;
2398
+ max-width: 100%;
2399
+ padding: 3px 10px;
2400
+ font: inherit;
2401
+ font-size: 12px;
2402
+ color: var(--chip-color);
2403
+ background: color-mix(in srgb, var(--chip-color) 12%, transparent);
2404
+ border: 1px solid color-mix(in srgb, var(--chip-color) 40%, transparent);
2405
+ border-radius: 999px;
2406
+ cursor: pointer;
2407
+ transition:
2408
+ background 0.15s ease,
2409
+ border-color 0.15s ease;
2410
+ }
2411
+
2412
+ .detail-dep:hover,
2413
+ .detail-dep:focus-visible {
2414
+ outline: none;
2415
+ background: color-mix(in srgb, var(--chip-color) 20%, transparent);
2416
+ border-color: var(--chip-color);
2417
+ }
2418
+
2419
+ .detail-dep .dot {
2420
+ width: 6px;
2421
+ height: 6px;
2422
+ }
2423
+
2424
+ .detail-dep-text {
2425
+ overflow: hidden;
2426
+ text-overflow: ellipsis;
2427
+ white-space: nowrap;
2334
2428
  }
2335
2429
 
2336
2430
  .detail-desc pre {
@@ -2358,6 +2452,12 @@ body.booting .boot-splash {
2358
2452
  font-size: 0.9em;
2359
2453
  }
2360
2454
 
2455
+ #detail-content .detail-desc :not(pre) > code {
2456
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
2457
+ border-color: color-mix(in srgb, var(--accent) 20%, transparent);
2458
+ color: var(--accent-text, var(--accent));
2459
+ }
2460
+
2361
2461
  .detail-desc hr {
2362
2462
  border: none;
2363
2463
  border-top: 1px solid var(--border);
@@ -2430,6 +2530,7 @@ body.booting .boot-splash {
2430
2530
  .plan-indicator,
2431
2531
  .loop-badge,
2432
2532
  .workflow-badge,
2533
+ .dispatched-badge,
2433
2534
  .terminal-badge {
2434
2535
  width: 18px;
2435
2536
  height: 18px;
@@ -3058,17 +3159,23 @@ body.booting .boot-splash {
3058
3159
  background: var(--bg-deep);
3059
3160
  }
3060
3161
 
3061
- .terminal-pane:has(.terminal-prompt.visible) .xterm {
3162
+ .terminal-pane:has(.terminal-prompt.visible) .terminal-frame {
3062
3163
  visibility: hidden;
3063
3164
  }
3064
3165
 
3065
- .terminal-host .xterm {
3166
+ .terminal-frame {
3167
+ display: block;
3168
+ width: 100%;
3066
3169
  height: 100%;
3170
+ border: 0;
3067
3171
  }
3068
3172
 
3069
- /* xterm.css paints the viewport #000; it shows below the last row when the height is not a whole number of rows. */
3070
- .terminal-host .xterm .xterm-viewport {
3071
- background-color: var(--bg-deep);
3173
+ .terminal-key-proxy {
3174
+ position: absolute;
3175
+ width: 0;
3176
+ height: 0;
3177
+ opacity: 0;
3178
+ pointer-events: none;
3072
3179
  }
3073
3180
 
3074
3181
  .terminal-manager-modal {
@@ -3154,6 +3261,15 @@ body.booting .boot-splash {
3154
3261
  text-overflow: ellipsis;
3155
3262
  }
3156
3263
 
3264
+ .terminal-manager-proc {
3265
+ white-space: nowrap;
3266
+ font-variant-numeric: tabular-nums;
3267
+ }
3268
+
3269
+ .terminal-manager-proc:empty {
3270
+ display: none;
3271
+ }
3272
+
3157
3273
  .terminal-manager-actions {
3158
3274
  display: flex;
3159
3275
  gap: 8px;
@@ -3183,6 +3299,15 @@ body.booting .boot-splash {
3183
3299
  color: var(--text-muted);
3184
3300
  }
3185
3301
 
3302
+ .terminal-manager-stats:empty {
3303
+ display: none;
3304
+ }
3305
+
3306
+ .terminal-manager-stats::before {
3307
+ content: '·';
3308
+ margin: 0 6px;
3309
+ }
3310
+
3186
3311
  .terminal-manager-empty {
3187
3312
  color: var(--text-muted);
3188
3313
  padding: 24px 0;
@@ -3211,7 +3336,7 @@ body.booting .boot-splash {
3211
3336
  }
3212
3337
 
3213
3338
  /* Opacity, not visibility: a hidden textarea drops focus, and the terminal must keep it. */
3214
- .terminal-pane:has(.terminal-boot.visible) .xterm {
3339
+ .terminal-pane:has(.terminal-boot.visible) .terminal-frame {
3215
3340
  opacity: 0;
3216
3341
  }
3217
3342
 
@@ -3759,6 +3884,10 @@ body.booting .boot-splash {
3759
3884
  .toast-action:hover {
3760
3885
  background: var(--accent-dim);
3761
3886
  }
3887
+ .toast-hint {
3888
+ margin-top: 2px;
3889
+ color: var(--text-muted);
3890
+ }
3762
3891
  .toast.toast-success {
3763
3892
  border-color: var(--success);
3764
3893
  color: var(--success);
@@ -3822,9 +3951,6 @@ body.booting .boot-splash {
3822
3951
  background: var(--bg-hover);
3823
3952
  font-weight: 600;
3824
3953
  }
3825
- .rendered-md tr:hover td {
3826
- background: var(--bg-hover);
3827
- }
3828
3954
  .rendered-md hr {
3829
3955
  margin: 0.8em 0;
3830
3956
  border-color: var(--border);
@@ -4650,6 +4776,12 @@ body.booting .boot-splash {
4650
4776
  .terminal-badge:hover {
4651
4777
  color: var(--accent, var(--text-primary));
4652
4778
  }
4779
+ .dispatched-badge:not([onclick]) {
4780
+ cursor: default;
4781
+ }
4782
+ .dispatched-badge[onclick]:hover {
4783
+ color: var(--accent, var(--text-primary));
4784
+ }
4653
4785
  .agent-badge-waiting {
4654
4786
  color: var(--warning);
4655
4787
  animation: agent-badge-pulse 1.6s ease-in-out infinite;
@@ -5225,6 +5357,7 @@ body.booting .boot-splash {
5225
5357
 
5226
5358
  /* #region LIGHT_THEME */
5227
5359
  body.light {
5360
+ --on-accent: white;
5228
5361
  --bg-deep: #e8e6e3;
5229
5362
  --bg-surface: #efede9;
5230
5363
  --bg-elevated: #fbfaf9;
@@ -5403,7 +5536,7 @@ body.light .msg-assistant .msg-text {
5403
5536
  fullscreen rule must win, same as for every other per-modal size. */
5404
5537
  .modal.msg-detail-dialog {
5405
5538
  width: 90vw;
5406
- max-width: 860px;
5539
+ max-width: calc(860px + var(--review-extra));
5407
5540
  max-height: 90vh;
5408
5541
  display: flex;
5409
5542
  flex-direction: column;
@@ -5466,9 +5599,27 @@ body.light .msg-assistant .msg-text {
5466
5599
  max-height: 92vh;
5467
5600
  }
5468
5601
 
5602
+ /* The comment panel opens beside the content, so the dialog grows by `--review-extra`
5603
+ instead of squeezing what is under review. */
5604
+ .modal.review-host {
5605
+ --review-panel-w: 320px;
5606
+ --review-extra: 0px;
5607
+ }
5608
+
5609
+ .review-host .modal-body {
5610
+ display: flex;
5611
+ flex-direction: column;
5612
+ overflow: hidden;
5613
+ }
5614
+
5615
+ #plan-modal .modal:not(.fullscreen):not(.user-sized) {
5616
+ width: min(calc(60vw + var(--review-extra)), 96vw);
5617
+ max-width: min(calc(60vw + var(--review-extra)), 96vw);
5618
+ }
5619
+
5469
5620
  .modal.preview-modal-dialog {
5470
- width: 90vw;
5471
- max-width: 1200px;
5621
+ width: min(calc(90vw + var(--review-extra)), 96vw);
5622
+ max-width: calc(1200px + var(--review-extra));
5472
5623
  max-height: 90vh;
5473
5624
  display: flex;
5474
5625
  flex-direction: column;
@@ -5483,9 +5634,6 @@ body.light .msg-assistant .msg-text {
5483
5634
 
5484
5635
  .preview-modal-dialog .modal-body {
5485
5636
  flex: 0 1 auto;
5486
- display: flex;
5487
- flex-direction: column;
5488
- overflow: hidden;
5489
5637
  }
5490
5638
 
5491
5639
  #msg-detail-body,
@@ -5565,11 +5713,46 @@ body.light .msg-assistant .msg-text {
5565
5713
  floor (same rule as .fullscreen) — short content still collapses. min()
5566
5714
  keeps a size saved on a large screen inside the current viewport. */
5567
5715
  .modal.user-sized:not(.fullscreen) {
5568
- width: min(var(--user-modal-w), 98vw);
5569
- max-width: min(var(--user-modal-w), 98vw);
5716
+ width: min(calc(var(--user-modal-w) + var(--review-extra, 0px)), 98vw);
5717
+ max-width: min(calc(var(--user-modal-w) + var(--review-extra, 0px)), 98vw);
5570
5718
  max-height: min(var(--user-modal-h), 95vh);
5571
5719
  }
5572
5720
 
5721
+ .modal.review-host.has-review {
5722
+ --review-extra: var(--review-panel-w);
5723
+ }
5724
+
5725
+ /* Not on user-sized dialogs: the grip drag sets the width on every move and must not lag. */
5726
+ .modal.review-host:not(.user-sized) {
5727
+ transition:
5728
+ width 150ms ease-out,
5729
+ max-width 150ms ease-out;
5730
+ }
5731
+
5732
+ @media (prefers-reduced-motion: reduce) {
5733
+ .modal.review-host {
5734
+ transition: none;
5735
+ }
5736
+ }
5737
+
5738
+ .preview-split {
5739
+ display: flex;
5740
+ gap: 14px;
5741
+ flex: 0 1 auto;
5742
+ min-height: 0;
5743
+ }
5744
+
5745
+ .preview-html .preview-split {
5746
+ flex: 1 1 auto;
5747
+ }
5748
+
5749
+ .preview-split > #msg-detail-body,
5750
+ .preview-split > #preview-modal-body,
5751
+ .preview-split > #plan-modal-body {
5752
+ flex: 1 1 auto;
5753
+ min-width: 0;
5754
+ }
5755
+
5573
5756
  .preview-html .modal-body,
5574
5757
  .preview-html #preview-modal-body {
5575
5758
  flex: 1 1 auto;
@@ -5765,7 +5948,8 @@ body.light .msg-assistant .msg-text {
5765
5948
  /* keycap chips: elevated fill + thicker bottom border reads as a key */
5766
5949
  .sp-hint kbd,
5767
5950
  .shortcuts kbd,
5768
- .terminal-boot-slow kbd {
5951
+ .terminal-boot-slow kbd,
5952
+ .review-offer-btn kbd {
5769
5953
  display: inline-flex;
5770
5954
  align-items: center;
5771
5955
  justify-content: center;
@@ -6024,6 +6208,216 @@ select.form-input option:checked {
6024
6208
 
6025
6209
  /* #endregion */
6026
6210
 
6211
+ /* #region REVIEW */
6212
+ ::highlight(review) {
6213
+ background-color: var(--warning-dim);
6214
+ }
6215
+
6216
+ ::highlight(review-hot),
6217
+ ::highlight(review-pending) {
6218
+ background-color: color-mix(in srgb, var(--warning) 45%, transparent);
6219
+ }
6220
+
6221
+ #review-pop {
6222
+ display: none;
6223
+ position: fixed;
6224
+ z-index: 10010;
6225
+ font-family: var(--mono);
6226
+ font-size: 12px;
6227
+ }
6228
+
6229
+ #review-pop.visible {
6230
+ display: block;
6231
+ }
6232
+
6233
+ #review-pop.editor {
6234
+ width: 320px;
6235
+ padding: 8px;
6236
+ background: var(--bg-elevated);
6237
+ border: 1px solid var(--border);
6238
+ border-radius: 6px;
6239
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
6240
+ }
6241
+
6242
+ #review-pop textarea {
6243
+ width: 100%;
6244
+ box-sizing: border-box;
6245
+ margin: 0 0 6px;
6246
+ padding: 6px;
6247
+ resize: vertical;
6248
+ font: inherit;
6249
+ color: var(--text-primary);
6250
+ background: var(--bg-surface);
6251
+ border: 1px solid var(--border);
6252
+ border-radius: 4px;
6253
+ }
6254
+
6255
+ #review-pop.offer {
6256
+ animation: review-offer-in 120ms ease-out;
6257
+ }
6258
+
6259
+ #review-pop button.review-offer-btn {
6260
+ display: inline-flex;
6261
+ align-items: center;
6262
+ gap: 6px;
6263
+ padding: 4px 6px 4px 8px;
6264
+ background: var(--bg-elevated);
6265
+ border-radius: 6px;
6266
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
6267
+ }
6268
+
6269
+ #review-pop button.review-offer-btn:hover,
6270
+ #review-pop button.review-offer-btn:focus-visible {
6271
+ border-color: var(--accent);
6272
+ }
6273
+
6274
+ .review-offer-btn svg {
6275
+ width: 13px;
6276
+ height: 13px;
6277
+ margin: 0;
6278
+ color: var(--accent);
6279
+ }
6280
+
6281
+ .review-offer-btn kbd {
6282
+ min-width: 16px;
6283
+ height: 16px;
6284
+ padding: 0 4px;
6285
+ font-size: 10px;
6286
+ color: var(--text-muted);
6287
+ border-radius: 3px;
6288
+ }
6289
+
6290
+ @keyframes review-offer-in {
6291
+ from {
6292
+ opacity: 0;
6293
+ transform: translateY(3px);
6294
+ }
6295
+ }
6296
+
6297
+ @media (prefers-reduced-motion: reduce) {
6298
+ #review-pop.offer {
6299
+ animation: none;
6300
+ }
6301
+ }
6302
+
6303
+ .review-card-quote {
6304
+ overflow: hidden;
6305
+ padding-left: 8px;
6306
+ color: var(--text-muted);
6307
+ border-left: 2px solid var(--warning);
6308
+ }
6309
+
6310
+ .review-pop-actions {
6311
+ display: flex;
6312
+ justify-content: flex-end;
6313
+ gap: 6px;
6314
+ }
6315
+
6316
+ #review-pop button,
6317
+ .review-panel button {
6318
+ padding: 3px 10px;
6319
+ font: inherit;
6320
+ color: var(--text-primary);
6321
+ background: var(--bg-hover);
6322
+ border: 1px solid var(--border);
6323
+ border-radius: 4px;
6324
+ cursor: pointer;
6325
+ }
6326
+
6327
+ #review-pop button.primary,
6328
+ .review-panel button.primary {
6329
+ color: var(--on-accent, var(--bg-deep));
6330
+ background: var(--accent);
6331
+ border-color: var(--accent);
6332
+ }
6333
+
6334
+ .review-panel {
6335
+ display: flex;
6336
+ flex-direction: column;
6337
+ flex: 0 0 calc(var(--review-panel-w) - 14px);
6338
+ min-height: 0;
6339
+ padding-left: 12px;
6340
+ font-family: var(--mono);
6341
+ font-size: 12px;
6342
+ border-left: 1px solid var(--border);
6343
+ }
6344
+
6345
+ .review-panel[hidden] {
6346
+ display: none;
6347
+ }
6348
+
6349
+ .review-panel-head {
6350
+ display: flex;
6351
+ align-items: center;
6352
+ gap: 6px;
6353
+ padding-bottom: 8px;
6354
+ }
6355
+
6356
+ .review-panel-head span {
6357
+ flex: 1;
6358
+ color: var(--text-muted);
6359
+ }
6360
+
6361
+ .review-panel-list {
6362
+ flex: 1 1 auto;
6363
+ min-height: 0;
6364
+ overflow-y: auto;
6365
+ margin: 0;
6366
+ padding: 0;
6367
+ list-style: none;
6368
+ }
6369
+
6370
+ .review-panel button.review-discard,
6371
+ .review-panel button.review-remove {
6372
+ padding: 3px 4px;
6373
+ color: var(--text-muted);
6374
+ background: none;
6375
+ border: none;
6376
+ }
6377
+
6378
+ .review-panel button.review-discard:hover,
6379
+ .review-panel button.review-discard:focus-visible {
6380
+ color: var(--text-primary);
6381
+ text-decoration: underline;
6382
+ }
6383
+
6384
+ .review-card {
6385
+ position: relative;
6386
+ padding: 8px 24px 10px 0;
6387
+ cursor: pointer;
6388
+ }
6389
+
6390
+ .review-card + .review-card {
6391
+ border-top: 1px solid var(--border);
6392
+ }
6393
+
6394
+ .review-card-quote {
6395
+ display: -webkit-box;
6396
+ -webkit-line-clamp: 3;
6397
+ -webkit-box-orient: vertical;
6398
+ margin-bottom: 4px;
6399
+ border-left-color: var(--warning-dim);
6400
+ }
6401
+
6402
+ .review-card:hover .review-card-quote,
6403
+ .review-card.hot .review-card-quote {
6404
+ color: var(--text-primary);
6405
+ border-left-color: var(--warning);
6406
+ }
6407
+
6408
+ .review-card-comment {
6409
+ white-space: pre-wrap;
6410
+ word-break: break-word;
6411
+ }
6412
+
6413
+ .review-panel button.review-remove {
6414
+ position: absolute;
6415
+ top: 4px;
6416
+ right: 4px;
6417
+ padding: 0 6px;
6418
+ }
6419
+ /* #endregion */
6420
+
6027
6421
  /* #region SESSION_PICKER */
6028
6422
  .modal.session-picker {
6029
6423
  max-height: 70vh;
@@ -6921,6 +7315,7 @@ select.form-input option:checked {
6921
7315
  /* #region MEDIA_QUERIES */
6922
7316
  @media (prefers-color-scheme: light) {
6923
7317
  body:not(.dark-forced) {
7318
+ --on-accent: white;
6924
7319
  --bg-deep: #e8e6e3;
6925
7320
  --bg-surface: #efede9;
6926
7321
  --bg-elevated: #fbfaf9;
@@ -7068,8 +7463,6 @@ pre.mermaid svg {
7068
7463
  font-size: 11px;
7069
7464
  font-weight: 600;
7070
7465
  color: var(--text-secondary);
7071
- text-transform: uppercase;
7072
- letter-spacing: 0.5px;
7073
7466
  }
7074
7467
 
7075
7468
  .project-group-header:first-child {
@@ -7190,7 +7583,7 @@ pre.mermaid svg {
7190
7583
  font-size: 10px;
7191
7584
  color: var(--text-muted);
7192
7585
  padding: 0 10px 0 28px;
7193
- font-family: 'IBM Plex Mono', monospace;
7586
+ font-family: var(--mono);
7194
7587
  overflow: hidden;
7195
7588
  max-height: 0;
7196
7589
  opacity: 0;
@@ -7321,10 +7714,8 @@ pre.mermaid svg {
7321
7714
  .sg-section-label {
7322
7715
  padding: 10px 10px 2px;
7323
7716
  margin-top: 10px;
7324
- font-size: 10px;
7717
+ font-size: 11px;
7325
7718
  font-weight: 600;
7326
- letter-spacing: 0.6px;
7327
- text-transform: uppercase;
7328
7719
  color: var(--text-muted);
7329
7720
  user-select: none;
7330
7721
  }
@@ -7440,6 +7831,27 @@ pre.mermaid svg {
7440
7831
  background: var(--accent-dim);
7441
7832
  }
7442
7833
 
7834
+ /* Centered in the 6px gap between cards (.session-item margin-bottom). */
7835
+ .session-item.sg-insert-before::after,
7836
+ .session-item.sg-insert-after::after {
7837
+ content: '';
7838
+ position: absolute;
7839
+ left: 4px;
7840
+ right: 4px;
7841
+ height: 2px;
7842
+ border-radius: 1px;
7843
+ background: var(--accent);
7844
+ pointer-events: none;
7845
+ }
7846
+
7847
+ .session-item.sg-insert-before::after {
7848
+ top: -5px;
7849
+ }
7850
+
7851
+ .session-item.sg-insert-after::after {
7852
+ bottom: -5px;
7853
+ }
7854
+
7443
7855
  /* An empty section shows only while a drag needs its ungroup drop zone. */
7444
7856
  .sessions-list:not(.sg-dragging-active) .sg-section-empty {
7445
7857
  display: none;