openclacky 1.5.20 → 1.5.21

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +18 -0
  4. data/lib/clacky/agent/chunk_index.rb +8 -1
  5. data/lib/clacky/agent/feedback.rb +30 -0
  6. data/lib/clacky/agent/history_navigation.rb +17 -3
  7. data/lib/clacky/agent/hook_manager.rb +6 -1
  8. data/lib/clacky/agent/llm_caller.rb +1 -0
  9. data/lib/clacky/agent/message_compressor_helper.rb +28 -2
  10. data/lib/clacky/agent/session_serializer.rb +20 -2
  11. data/lib/clacky/agent.rb +56 -43
  12. data/lib/clacky/agent_config.rb +82 -4
  13. data/lib/clacky/billing/platform_billing.rb +5 -1
  14. data/lib/clacky/brand_config.rb +1 -1
  15. data/lib/clacky/cli.rb +3 -2
  16. data/lib/clacky/client.rb +5 -5
  17. data/lib/clacky/deepseek_web_tools.rb +156 -0
  18. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +8 -0
  19. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +21 -1
  20. data/lib/clacky/extension/loader.rb +5 -3
  21. data/lib/clacky/locales/en.rb +5 -5
  22. data/lib/clacky/locales/zh.rb +5 -5
  23. data/lib/clacky/message_format/anthropic.rb +20 -0
  24. data/lib/clacky/message_format/open_ai.rb +31 -14
  25. data/lib/clacky/message_history.rb +21 -3
  26. data/lib/clacky/platform_http_client.rb +6 -2
  27. data/lib/clacky/providers.rb +23 -14
  28. data/lib/clacky/search_config.rb +47 -13
  29. data/lib/clacky/server/browser_manager.rb +35 -5
  30. data/lib/clacky/server/channel/channel_manager.rb +6 -6
  31. data/lib/clacky/server/discover.rb +5 -4
  32. data/lib/clacky/server/http_server.rb +194 -47
  33. data/lib/clacky/server/model_prices.rb +5 -3
  34. data/lib/clacky/server/pid_files.rb +25 -0
  35. data/lib/clacky/server/server_master.rb +9 -3
  36. data/lib/clacky/server/session_registry.rb +19 -7
  37. data/lib/clacky/server/web_ui_controller.rb +61 -38
  38. data/lib/clacky/session_manager.rb +7 -1
  39. data/lib/clacky/skill.rb +1 -1
  40. data/lib/clacky/tools/ask_user.rb +4 -1
  41. data/lib/clacky/tools/base.rb +3 -2
  42. data/lib/clacky/tools/browser.rb +18 -2
  43. data/lib/clacky/tools/file_reader.rb +1 -1
  44. data/lib/clacky/tools/terminal/session_manager.rb +19 -1
  45. data/lib/clacky/tools/terminal.rb +65 -23
  46. data/lib/clacky/tools/trash_manager.rb +5 -3
  47. data/lib/clacky/tools/web_fetch.rb +81 -22
  48. data/lib/clacky/tools/web_search.rb +45 -4
  49. data/lib/clacky/ui2/ui_controller.rb +1 -1
  50. data/lib/clacky/ui_interface.rb +4 -3
  51. data/lib/clacky/utils/file_processor.rb +51 -0
  52. data/lib/clacky/utils/model_pricing.rb +132 -13
  53. data/lib/clacky/version.rb +1 -1
  54. data/lib/clacky/web/app.css +366 -105
  55. data/lib/clacky/web/components/chat-navigator.js +8 -1
  56. data/lib/clacky/web/components/mentions.js +2 -13
  57. data/lib/clacky/web/components/model-picker.js +3 -1
  58. data/lib/clacky/web/components/onboard.js +13 -10
  59. data/lib/clacky/web/features/archive/store.js +30 -16
  60. data/lib/clacky/web/features/archive/view.js +20 -10
  61. data/lib/clacky/web/features/backup/view.js +5 -5
  62. data/lib/clacky/web/features/new-session/store.js +3 -3
  63. data/lib/clacky/web/features/trash/store.js +10 -7
  64. data/lib/clacky/web/features/trash/view.js +9 -4
  65. data/lib/clacky/web/i18n.js +106 -30
  66. data/lib/clacky/web/index.html +90 -60
  67. data/lib/clacky/web/projects.js +56 -1
  68. data/lib/clacky/web/sessions.js +244 -131
  69. data/lib/clacky/web/settings.js +143 -8
  70. data/lib/clacky/web/ws-dispatcher.js +9 -13
  71. data/lib/clacky.rb +1 -0
  72. metadata +4 -1
@@ -1237,11 +1237,11 @@ body {
1237
1237
 
1238
1238
  .cmd-palette {
1239
1239
  width: 100%;
1240
- max-width: 34rem;
1240
+ max-width: 50rem;
1241
1241
  margin: 0 1rem;
1242
1242
  background: var(--color-bg-card, var(--color-bg-secondary));
1243
1243
  border: 1px solid var(--color-border-primary);
1244
- border-radius: 12px;
1244
+ border-radius: 14px;
1245
1245
  box-shadow: var(--shadow-lg, 0 12px 40px rgba(0,0,0,0.25));
1246
1246
  overflow: hidden;
1247
1247
  transform: translateY(-8px) scale(0.99);
@@ -1255,27 +1255,147 @@ body {
1255
1255
  border-radius: 0;
1256
1256
  border-bottom: 1px solid var(--color-border-secondary);
1257
1257
  background: transparent;
1258
+ padding: 0.75rem;
1259
+ }
1260
+
1261
+ /* Input row reads as its own surface: pill outline, focus ring on the border */
1262
+ .cmd-palette .search-input-wrap {
1263
+ display: flex;
1264
+ align-items: center;
1265
+ gap: 0.375rem;
1266
+ }
1267
+ .cmd-palette .search-input-row {
1268
+ flex: 1;
1269
+ min-width: 0;
1270
+ height: 2.25rem;
1271
+ padding: 0 0.5rem 0 0.9375rem;
1272
+ gap: 0.5rem;
1273
+ border: 1px solid var(--color-border-primary);
1274
+ border-radius: var(--radius-pill);
1275
+ background: var(--color-bg-card);
1276
+ transition: border-color 0.12s ease;
1258
1277
  }
1259
- .cmd-palette .search-input-row { height: 3rem; }
1278
+ .cmd-palette .search-input-row:focus-within {
1279
+ border-color: color-mix(in srgb, var(--color-accent-primary) 45%, var(--color-border-primary));
1280
+ }
1281
+ .cmd-palette .search-icon svg { width: 1rem; height: 1rem; }
1260
1282
  .cmd-palette .search-input { font-size: 0.9375rem; }
1261
1283
 
1262
- /* ESC chip in the input row */
1263
- .cmd-palette-esc {
1284
+ /* Clear stays a text affordance; the pill outline owns the emphasis */
1285
+ .cmd-palette .btn-search-q-clear {
1264
1286
  flex-shrink: 0;
1265
- font-family: var(--font-mono);
1266
- font-size: 0.625rem;
1267
- letter-spacing: 0.04em;
1287
+ width: auto;
1288
+ height: 1.375rem;
1289
+ padding: 0 0.375rem;
1290
+ border-radius: 0.375rem;
1291
+ background: transparent;
1268
1292
  color: var(--color-text-muted);
1269
- padding: 0.125rem 0.375rem;
1270
- border: 1px solid var(--color-border-secondary);
1271
- border-radius: var(--radius-sm);
1272
- background: var(--color-bg-secondary);
1273
- cursor: pointer;
1274
- transition: color 0.12s, border-color 0.12s;
1293
+ font-size: 0.8125rem;
1294
+ opacity: 1;
1275
1295
  }
1276
- .cmd-palette-esc:hover {
1296
+ .cmd-palette .btn-search-q-clear:hover {
1277
1297
  color: var(--color-text-secondary);
1278
- border-color: var(--color-border-primary);
1298
+ background: var(--color-bg-hover);
1299
+ }
1300
+
1301
+ /* Filter row: chip-shaped triggers, no hairline divider */
1302
+ .cmd-palette .search-filter-row {
1303
+ height: auto;
1304
+ border-top: none;
1305
+ padding: 0.625rem 0 0;
1306
+ gap: 0.5rem;
1307
+ }
1308
+ .cmd-palette .search-select {
1309
+ flex: 0 0 auto;
1310
+ width: fit-content;
1311
+ min-width: 6.5rem;
1312
+ height: 1.875rem;
1313
+ border: 1px solid var(--color-border-primary);
1314
+ border-radius: var(--radius-pill);
1315
+ background: var(--color-bg-card);
1316
+ transition: border-color 0.12s ease, background 0.12s ease;
1317
+ }
1318
+ .cmd-palette .search-select .custom-select-trigger--bare {
1319
+ width: 100%;
1320
+ padding: 0 0.5rem 0 0.625rem;
1321
+ font-size: 0.8125rem;
1322
+ }
1323
+ .cmd-palette .search-select .placeholder,
1324
+ .cmd-palette .search-select .custom-select-value:not(.placeholder),
1325
+ .cmd-palette .search-select .custom-select-arrow {
1326
+ color: var(--color-text-primary);
1327
+ font-weight: 400;
1328
+ }
1329
+ .cmd-palette .search-date-wrap {
1330
+ flex: 0 0 auto;
1331
+ min-width: 6.5rem;
1332
+ height: 1.875rem;
1333
+ padding: 0;
1334
+ border: 1px solid var(--color-border-primary);
1335
+ border-radius: var(--radius-pill);
1336
+ background: var(--color-bg-card);
1337
+ color: var(--color-text-primary);
1338
+ transition: border-color 0.12s ease, background 0.12s ease;
1339
+ }
1340
+ /* Button spans the whole pill so padding and icon areas stay clickable */
1341
+ .cmd-palette .search-date {
1342
+ flex: 1 1 auto;
1343
+ height: 100%;
1344
+ padding: 0 2.1875rem 0 0.625rem;
1345
+ border-radius: inherit;
1346
+ font-size: 0.8125rem;
1347
+ color: inherit;
1348
+ }
1349
+ .cmd-palette .search-date-icon {
1350
+ right: 0.5rem;
1351
+ width: 0.9375rem;
1352
+ height: 0.9375rem;
1353
+ stroke-width: 1.8;
1354
+ color: inherit;
1355
+ }
1356
+ .cmd-palette .search-date-wrap:has(.search-date[data-active="true"]) {
1357
+ color: var(--color-accent-primary);
1358
+ }
1359
+
1360
+ /* Hover / expanded state for the two filter pills */
1361
+ .cmd-palette .search-select:hover,
1362
+ .cmd-palette .search-date-wrap:hover,
1363
+ .cmd-palette .search-select:has(.custom-select-trigger.open) {
1364
+ background: color-mix(in srgb, var(--color-bg-hover) 45%, var(--color-bg-card));
1365
+ }
1366
+ .cmd-palette .search-select:has(.custom-select-trigger.open) {
1367
+ border-color: var(--color-border-strong);
1368
+ }
1369
+ .cmd-palette .search-select:hover .placeholder,
1370
+ .cmd-palette .search-select:has(.custom-select-trigger.open) .placeholder {
1371
+ color: var(--color-text-primary);
1372
+ }
1373
+ .cmd-palette .search-select:hover .custom-select-arrow,
1374
+ .cmd-palette .search-select:has(.custom-select-trigger.open) .custom-select-arrow {
1375
+ color: var(--color-text-primary);
1376
+ }
1377
+ .cmd-palette .btn-search-clear-all { margin-left: auto; }
1378
+
1379
+ /* Close sits outside the pill, matching its height so the row reads as one unit */
1380
+ .cmd-palette-close {
1381
+ flex-shrink: 0;
1382
+ display: inline-flex;
1383
+ align-items: center;
1384
+ justify-content: center;
1385
+ width: 2.25rem;
1386
+ height: 2.25rem;
1387
+ padding: 0;
1388
+ border: none;
1389
+ border-radius: 50%;
1390
+ background: transparent;
1391
+ color: color-mix(in srgb, var(--color-text-secondary) 65%, var(--color-text-muted));
1392
+ cursor: pointer;
1393
+ transition: color 0.12s ease, background 0.12s ease;
1394
+ }
1395
+ .cmd-palette-close svg { width: 1rem; height: 1rem; }
1396
+ .cmd-palette-close:hover {
1397
+ color: var(--color-text-primary);
1398
+ background: var(--color-bg-hover);
1279
1399
  }
1280
1400
 
1281
1401
  /* Results region */
@@ -1285,6 +1405,45 @@ body {
1285
1405
  padding: 0.375rem 0.5rem 0.5rem;
1286
1406
  }
1287
1407
  .cmd-palette-results .session-item { border-radius: 7px; }
1408
+ .cmd-palette-results .session-body {
1409
+ display: grid;
1410
+ grid-template-columns: minmax(0, 1fr) auto;
1411
+ column-gap: 0.75rem;
1412
+ align-items: center;
1413
+ }
1414
+ .cmd-palette-results .session-meta {
1415
+ grid-column: 2;
1416
+ grid-row: 1;
1417
+ margin-top: 0;
1418
+ font-family: inherit;
1419
+ font-size: 0.6875rem;
1420
+ opacity: 1;
1421
+ }
1422
+ .cmd-palette-results .session-item:has(.session-snippet) {
1423
+ min-height: 2.625rem;
1424
+ box-sizing: border-box;
1425
+ }
1426
+ .cmd-palette-results .session-name {
1427
+ color: var(--color-text-primary);
1428
+ font-weight: 500;
1429
+ }
1430
+ .cmd-palette-results .session-name__text mark,
1431
+ .cmd-palette-results .session-snippet mark {
1432
+ background: color-mix(in srgb, #fde047 55%, transparent);
1433
+ color: var(--color-text-primary);
1434
+ }
1435
+ [data-theme="dark"] .cmd-palette-results .session-name__text mark,
1436
+ [data-theme="dark"] .cmd-palette-results .session-snippet mark {
1437
+ background: color-mix(in srgb, #facc15 78%, transparent);
1438
+ color: #111;
1439
+ }
1440
+ .cmd-palette-results .session-snippet {
1441
+ grid-column: 1 / -1;
1442
+ margin-top: 0.25rem;
1443
+ font-size: 0.72rem;
1444
+ color: var(--color-text-muted);
1445
+ line-height: 1.35;
1446
+ }
1288
1447
  .cmd-palette-hint {
1289
1448
  padding: 1.75rem 1rem;
1290
1449
  text-align: center;
@@ -1578,30 +1737,26 @@ body {
1578
1737
  .session-search-group + .session-search-group {
1579
1738
  margin-top: 0.25rem;
1580
1739
  }
1581
-
1582
1740
  .session-snippet {
1583
1741
  white-space: nowrap;
1584
1742
  overflow: hidden;
1585
1743
  text-overflow: ellipsis;
1586
- font-size: 0.6875rem;
1587
- color: var(--color-text-muted);
1744
+ font-size: 0.75rem;
1745
+ color: var(--color-text-secondary);
1588
1746
  margin-top: 2px;
1589
- line-height: 1.35;
1590
- opacity: 0.85;
1591
- }
1592
- .session-snippet mark {
1593
- background: var(--color-warning-bg, #fef3c7);
1594
- color: var(--color-text-primary);
1595
- padding: 0 2px;
1596
- border-radius: 2px;
1597
- font-weight: 500;
1747
+ line-height: 1.4;
1598
1748
  }
1599
-
1749
+ .session-snippet mark,
1600
1750
  .session-name__text mark {
1601
- background: var(--color-warning-bg, #fef3c7);
1602
- color: var(--color-text-primary);
1751
+ background: #fde047;
1752
+ color: #1f2328;
1603
1753
  padding: 0 1px;
1604
- border-radius: 2px;
1754
+ border-radius: 3px;
1755
+ }
1756
+ [data-theme="dark"] .session-snippet mark,
1757
+ [data-theme="dark"] .session-name__text mark {
1758
+ background: #facc15;
1759
+ color: #111;
1605
1760
  }
1606
1761
 
1607
1762
  /* Legacy .btn-new-inline — kept for compat, can be removed after cleanup */
@@ -1629,12 +1784,8 @@ body {
1629
1784
  margin-bottom: 1px;
1630
1785
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
1631
1786
  }
1632
- .session-item[draggable="true"] {
1633
- cursor: grab;
1634
- }
1635
1787
  .session-item.is-dragging {
1636
1788
  opacity: 0.55;
1637
- cursor: grabbing;
1638
1789
  }
1639
1790
  /* Two-row text block next to dot */
1640
1791
  .session-body {
@@ -4208,10 +4359,18 @@ body {
4208
4359
  }
4209
4360
  /* Time color / alignment: anchor to the bubble's own side, let width be
4210
4361
  driven by content — prevents overflow on narrow bubbles. */
4211
- /* User timestamp: appended to .msg-user-wrap (not bubble), sits below action
4212
- buttons, right-aligned. */
4213
- .msg-user-wrap > .msg-time { color: var(--color-text-secondary); top: auto; bottom: 0.2rem; right: 0; left: auto; padding-right: 0.25rem; }
4214
- .msg-assistant .msg-time { color: var(--color-text-secondary); left: 0; right: auto; padding-left: 0.25rem; }
4362
+ /* Timestamps live inside the user hover action bar (see .msg-user-actions),
4363
+ left of the icons. */
4364
+ .msg-user-actions > .msg-time {
4365
+ order: -1;
4366
+ position: static;
4367
+ margin: 0;
4368
+ padding: 0;
4369
+ font-size: 0.75rem;
4370
+ opacity: 1;
4371
+ transform: none;
4372
+ color: var(--color-text-tertiary);
4373
+ }
4215
4374
 
4216
4375
  .msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); white-space: pre-wrap; }
4217
4376
  [data-theme="dark"] .msg-user { background: var(--color-accent-soft); }
@@ -4235,14 +4394,15 @@ body {
4235
4394
  position: relative;
4236
4395
  display: flex;
4237
4396
  justify-content: flex-end;
4238
- padding-bottom: 3rem;
4397
+ padding-bottom: 1.85rem;
4239
4398
  }
4240
4399
  .msg-user-actions {
4241
4400
  position: absolute;
4242
- bottom: 1.1rem;
4401
+ bottom: 0.15rem;
4243
4402
  right: 0;
4244
4403
  display: flex;
4245
- gap: 0.125rem;
4404
+ align-items: center;
4405
+ gap: 0.25rem;
4246
4406
  opacity: 0;
4247
4407
  transition: opacity 0.15s ease;
4248
4408
  pointer-events: none;
@@ -4257,25 +4417,25 @@ body {
4257
4417
  display: inline-flex;
4258
4418
  align-items: center;
4259
4419
  justify-content: center;
4260
- width: 1.625rem;
4261
- height: 1.625rem;
4420
+ width: 1.5rem;
4421
+ height: 1.5rem;
4262
4422
  padding: 0;
4263
- border: 1px solid var(--color-border-primary);
4264
- border-radius: 5px;
4265
- background: var(--color-bg-primary);
4266
- color: var(--color-text-secondary);
4423
+ border: none;
4424
+ border-radius: 4px;
4425
+ background: transparent;
4426
+ color: var(--color-text-tertiary);
4267
4427
  cursor: pointer;
4268
- transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
4428
+ transition: color 0.15s ease, background 0.15s ease;
4269
4429
  }
4270
4430
  .msg-user-action-btn:hover {
4271
4431
  color: var(--color-text-primary);
4272
- background: var(--color-bg-tertiary);
4432
+ background: var(--color-bg-hover);
4273
4433
  }
4274
- .msg-user-action-btn:active { transform: translateY(1px); }
4275
- .msg-user-action-btn.is-copied {
4276
- color: var(--color-success);
4277
- border-color: var(--color-success);
4434
+ .msg-user-action-btn:active {
4435
+ color: var(--color-text-primary);
4436
+ background: var(--color-bg-hover);
4278
4437
  }
4438
+ .msg-user-action-btn.is-copied { color: var(--color-success); }
4279
4439
  .msg-user-copy-icon-check { display: none; }
4280
4440
  .msg-user-action-btn.is-copied .msg-user-copy-icon { display: none; }
4281
4441
  .msg-user-action-btn.is-copied .msg-user-copy-icon-check { display: inline; }
@@ -4702,6 +4862,7 @@ body {
4702
4862
  }
4703
4863
  .feedback-actions {
4704
4864
  display: flex;
4865
+ flex-wrap: wrap;
4705
4866
  justify-content: flex-end;
4706
4867
  align-items: center;
4707
4868
  gap: 0.5rem;
@@ -4736,10 +4897,11 @@ body {
4736
4897
  .feedback-actions [hidden] {
4737
4898
  display: none;
4738
4899
  }
4739
- .feedback-hint {
4900
+ .feedback-status {
4901
+ flex: 1;
4902
+ min-width: 8rem;
4740
4903
  font-size: 0.6875rem;
4741
4904
  color: var(--color-text-secondary);
4742
- margin-top: 0.375rem;
4743
4905
  }
4744
4906
  .feedback-card--submitted {
4745
4907
  opacity: 0.5;
@@ -4939,11 +5101,13 @@ body {
4939
5101
  height: 1.375rem;
4940
5102
  padding: 0.1875rem 0.25rem;
4941
5103
  overflow: hidden;
4942
- color: var(--color-text-tertiary);
4943
5104
  background: transparent;
4944
5105
  border: 0;
4945
5106
  border-radius: 3px;
4946
5107
  font: inherit;
5108
+ /* <button> does not inherit color: without this the icon, count and caret
5109
+ fall back to the UA black, which is unreadable on dark themes. */
5110
+ color: var(--color-text-primary);
4947
5111
  white-space: nowrap;
4948
5112
  cursor: default;
4949
5113
  transition: width 0.18s ease, color 0.15s ease, background-color 0.15s ease;
@@ -4996,6 +5160,8 @@ body {
4996
5160
  width: 0.625rem;
4997
5161
  height: 0.625rem;
4998
5162
  flex-shrink: 0;
5163
+ /* Dimmed relative to the icon and count, matching #session-info-bar .sib-caret. */
5164
+ opacity: 0.75;
4999
5165
  transition: transform 0.15s ease;
5000
5166
  }
5001
5167
  .todo-panel:not(.is-open) .todo-caret { transform: rotate(180deg); }
@@ -5603,11 +5769,6 @@ body {
5603
5769
  opacity: 0.85;
5604
5770
  }
5605
5771
 
5606
- /* Show all fields by default, not just on hover */
5607
- .sib-detail {
5608
- display: contents;
5609
- }
5610
-
5611
5772
  #sib-id {
5612
5773
  opacity: 1;
5613
5774
  font-size: 0.625rem;
@@ -5755,13 +5916,27 @@ body {
5755
5916
  opacity: 1;
5756
5917
  }
5757
5918
 
5758
- #sib-mode { opacity: 0.45; flex-shrink: 0; } /* tier 3 */
5919
+ #sib-mode {
5920
+ flex-shrink: 0;
5921
+ display: inline-flex;
5922
+ align-items: center;
5923
+ gap: 0.3125rem;
5924
+ font: inherit;
5925
+ cursor: pointer;
5926
+ transition: all 0.15s ease;
5927
+ }
5928
+ #sib-mode:disabled { opacity: 0.5; cursor: default; }
5929
+ #sib-mode-menu { position: fixed; width: 240px; box-sizing: border-box; white-space: normal; padding: 4px; z-index: 1000; }
5930
+ #sib-mode-menu[hidden] { display: none; }
5931
+ #sib-mode-menu button { display: block; width: 100%; text-align: left; padding: 9px; border: 0; border-radius: 6px; background: transparent; color: var(--color-text-primary); cursor: pointer; }
5932
+ #sib-mode-menu button:hover, #sib-mode-menu button[aria-checked="true"] { background: var(--color-bg-hover); }
5933
+ #sib-mode-menu small { display: block; margin-top: 4px; color: var(--color-text-secondary); line-height: 1.5; }
5759
5934
  #sib-model { opacity: 1; flex-shrink: 0; } /* tier 1 — the model is primary */
5760
5935
  #sib-cost { opacity: 0.45; flex-shrink: 0; } /* tier 3 */
5761
5936
 
5762
- /* ── Latency signal (right after model name) ──────────────────────────────
5763
- A compact 4-bar signal + TTFT value. Placed adjacent to #sib-model so the
5764
- user's mental mapping "this model is fast/slow" is immediate. Variant
5937
+ /* ── Latency signal (right of the task list) ──────────────────────────────
5938
+ A compact 4-bar signal + TTFT value. A trailing metric, so it sits after
5939
+ the pickers and the task list, just before the cost. Variant
5765
5940
  classes (-ok/-warn/-bad) are applied by Sessions._renderSignal based on
5766
5941
  TTFT thresholds; colours intentionally use CSS vars so the same palette
5767
5942
  works in both light and dark themes. */
@@ -5871,10 +6046,11 @@ body {
5871
6046
  #sib-model.sib-model-no-subs .sib-caret {
5872
6047
  display: none;
5873
6048
  }
5874
- /* The dir/reasoning carets are hints only: clicking them must reach the row,
5875
- and the row hover already provides the feedback. */
6049
+ /* The dir/reasoning/mode carets are hints only: clicking them must reach the
6050
+ row, and the row hover already provides the feedback. */
5876
6051
  #sib-dir .sib-caret,
5877
- .sib-reasoning-clickable .sib-caret {
6052
+ .sib-reasoning-clickable .sib-caret,
6053
+ .sib-mode-clickable .sib-caret {
5878
6054
  pointer-events: none;
5879
6055
  }
5880
6056
  .sib-model-clickable:hover .sib-caret,
@@ -5882,7 +6058,9 @@ body {
5882
6058
  #sib-dir:hover .sib-caret,
5883
6059
  #sib-dir.is-open .sib-caret,
5884
6060
  .sib-reasoning-clickable:hover .sib-caret,
5885
- .sib-reasoning-clickable.is-open .sib-caret {
6061
+ .sib-reasoning-clickable.is-open .sib-caret,
6062
+ .sib-mode-clickable:hover .sib-caret,
6063
+ .sib-mode-clickable.is-open .sib-caret {
5886
6064
  opacity: 1;
5887
6065
  }
5888
6066
  /* Hovering the model caret needs feedback of its own: the row hover already
@@ -5894,7 +6072,8 @@ body {
5894
6072
  }
5895
6073
  .sib-model-clickable.is-open .sib-caret,
5896
6074
  #sib-dir.is-open .sib-caret,
5897
- .sib-reasoning-clickable.is-open .sib-caret {
6075
+ .sib-reasoning-clickable.is-open .sib-caret,
6076
+ .sib-mode-clickable.is-open .sib-caret {
5898
6077
  transform: rotate(180deg);
5899
6078
  }
5900
6079
  .sib-model-disabled .sib-caret {
@@ -5929,7 +6108,8 @@ body {
5929
6108
  /* Pickers render as outlined pills so they read as controls, not status text. */
5930
6109
  #session-info-bar #sib-dir,
5931
6110
  #session-info-bar .sib-model-clickable,
5932
- #session-info-bar .sib-reasoning-clickable {
6111
+ #session-info-bar .sib-reasoning-clickable,
6112
+ #session-info-bar .sib-mode-clickable {
5933
6113
  height: 1.375rem;
5934
6114
  padding: 0 0.4375rem 0 0.5rem;
5935
6115
  border: 1px solid var(--color-border-primary);
@@ -5941,9 +6121,11 @@ body {
5941
6121
  #session-info-bar #sib-dir:hover,
5942
6122
  #session-info-bar .sib-model-clickable:hover,
5943
6123
  #session-info-bar .sib-reasoning-clickable:hover,
6124
+ #session-info-bar .sib-mode-clickable:hover,
5944
6125
  #session-info-bar #sib-dir.is-open,
5945
6126
  #session-info-bar .sib-model-clickable.is-open,
5946
- #session-info-bar .sib-reasoning-clickable.is-open {
6127
+ #session-info-bar .sib-reasoning-clickable.is-open,
6128
+ #session-info-bar .sib-mode-clickable.is-open {
5947
6129
  background: var(--color-bg-secondary);
5948
6130
  border-color: var(--color-text-tertiary);
5949
6131
  color: var(--color-text-primary);
@@ -5960,7 +6142,8 @@ body {
5960
6142
  display: none;
5961
6143
  }
5962
6144
  #sib-model-wrap,
5963
- #sib-reasoning-wrap {
6145
+ #sib-reasoning-wrap,
6146
+ #sib-mode-wrap {
5964
6147
  margin-left: 0.1875rem;
5965
6148
  }
5966
6149
  .sib-reasoning-dropdown {
@@ -6619,7 +6802,8 @@ body {
6619
6802
  }
6620
6803
 
6621
6804
  /* Attachment strip — top part of the composer card, above the input row */
6622
- #image-preview-strip {
6805
+ #image-preview-strip,
6806
+ #ns-image-preview-strip {
6623
6807
  display: flex;
6624
6808
  flex-wrap: wrap;
6625
6809
  gap: 0.5rem;
@@ -6627,12 +6811,6 @@ body {
6627
6811
  /* Divider to the input row — the strip is display:none when no chips exist. */
6628
6812
  border-bottom: 1px solid var(--color-border-primary);
6629
6813
  }
6630
- #ns-image-preview-strip {
6631
- display: flex;
6632
- flex-wrap: wrap;
6633
- gap: 0.5rem;
6634
- padding: 0.25rem 0 0.5rem;
6635
- }
6636
6814
  .img-preview-item {
6637
6815
  position: relative;
6638
6816
  width: 3.25rem;
@@ -7497,6 +7675,16 @@ body {
7497
7675
  line-height: 1.5;
7498
7676
  margin: 0;
7499
7677
  }
7678
+ .settings-network-note {
7679
+ font-size: 0.75rem;
7680
+ color: var(--color-text-tertiary);
7681
+ line-height: 1.5;
7682
+ margin: 0;
7683
+ }
7684
+ .settings-network-divider {
7685
+ border-top: 1px solid var(--color-border-secondary);
7686
+ padding-top: 0.75rem;
7687
+ }
7500
7688
 
7501
7689
  /* ── Backup section ── */
7502
7690
  .backup-auto-card {
@@ -7630,6 +7818,77 @@ body {
7630
7818
  gap: 0.625rem;
7631
7819
  flex-shrink: 0;
7632
7820
  }
7821
+
7822
+ /* ── Browser screenshot settings ────────────────────────────────────────── */
7823
+ .settings-screenshot {
7824
+ display: flex;
7825
+ flex-direction: column;
7826
+ gap: 0.5rem;
7827
+ padding: 0.875rem 1rem;
7828
+ background: var(--color-bg-secondary);
7829
+ border: 1px solid var(--color-border-primary);
7830
+ border-radius: 10px;
7831
+ }
7832
+ .settings-screenshot-row {
7833
+ display: flex;
7834
+ align-items: center;
7835
+ gap: 0.625rem;
7836
+ }
7837
+ .settings-screenshot-head {
7838
+ display: flex;
7839
+ flex-direction: column;
7840
+ gap: 0.1875rem;
7841
+ padding-bottom: 0.125rem;
7842
+ }
7843
+ .settings-screenshot-title {
7844
+ font-size: 0.8125rem;
7845
+ font-weight: 600;
7846
+ color: var(--color-text-primary);
7847
+ }
7848
+ .settings-screenshot-desc {
7849
+ margin: 0.25rem 0 0.5rem 0;
7850
+ font-size: 0.75rem;
7851
+ color: var(--color-text-tertiary);
7852
+ line-height: 1.5;
7853
+ }
7854
+ .settings-screenshot-label {
7855
+ font-size: 0.8125rem;
7856
+ color: var(--color-text-secondary);
7857
+ flex-shrink: 0;
7858
+ }
7859
+ .settings-segmented {
7860
+ display: inline-flex;
7861
+ gap: 2px;
7862
+ padding: 2px;
7863
+ background: var(--color-bg-input);
7864
+ border: 1px solid var(--color-border-primary);
7865
+ border-radius: 7px;
7866
+ }
7867
+ .settings-segmented-btn {
7868
+ border: 0;
7869
+ background: transparent;
7870
+ color: var(--color-text-secondary);
7871
+ font-size: 0.75rem;
7872
+ line-height: 1.4;
7873
+ padding: 0.1875rem 0.625rem;
7874
+ border-radius: 5px;
7875
+ cursor: pointer;
7876
+ transition: background 0.15s, color 0.15s;
7877
+ }
7878
+ .settings-segmented-btn:hover:not(.active) {
7879
+ background: var(--color-bg-hover);
7880
+ color: var(--color-text-primary);
7881
+ }
7882
+ .settings-segmented-btn.active {
7883
+ background: var(--color-accent-primary);
7884
+ color: var(--color-text-inverse);
7885
+ }
7886
+ .settings-screenshot-hint {
7887
+ margin: 0 0 0.25rem 0;
7888
+ font-size: 0.75rem;
7889
+ color: var(--color-text-tertiary);
7890
+ line-height: 1.5;
7891
+ }
7633
7892
  .toggle-switch {
7634
7893
  position: relative;
7635
7894
  display: inline-flex;
@@ -12230,7 +12489,20 @@ body.setup-mode {
12230
12489
  .badge-active { color: var(--color-success); }
12231
12490
  .badge-expired { color: var(--color-error); }
12232
12491
  .badge-expiring { color: var(--color-warning); }
12233
- .badge-unreachable { color: var(--color-text-secondary); }
12492
+ .badge-unreachable { color: var(--color-warning); }
12493
+ .brand-status-state {
12494
+ display: flex;
12495
+ align-items: baseline;
12496
+ gap: 0.375rem;
12497
+ flex-wrap: wrap;
12498
+ min-width: 0;
12499
+ }
12500
+ .brand-status-hint {
12501
+ font-size: 0.75rem;
12502
+ line-height: 1.5;
12503
+ font-weight: 400;
12504
+ color: var(--color-warning);
12505
+ }
12234
12506
  .badge-inactive { color: var(--color-text-secondary); }
12235
12507
 
12236
12508
  /* Support QR code */
@@ -12356,7 +12628,7 @@ body.setup-mode {
12356
12628
  background: var(--color-bg-primary);
12357
12629
  border: 1px solid var(--color-border-primary);
12358
12630
  border-radius: 16px;
12359
- padding: 1.75rem 2rem 1.5rem;
12631
+ padding: 3.25rem 2rem 1.5rem;
12360
12632
  display: flex;
12361
12633
  flex-direction: column;
12362
12634
  align-items: center;
@@ -16297,19 +16569,12 @@ body.setup-mode {
16297
16569
  white-space: nowrap;
16298
16570
  flex-wrap: nowrap;
16299
16571
  }
16300
- /* Always show detail fields on mobile (no hover) */
16301
- #session-info-bar .sib-detail {
16302
- display: contents;
16303
- }
16304
16572
  /* Hide less-important fields and their separators (element + sib-sep-after-element pattern) */
16305
16573
  #sib-id,
16306
16574
  .sib-sep-after-id,
16307
16575
  #sib-dir,
16308
16576
  .sib-sep-after-dir,
16309
- #sib-mode,
16310
- .sib-sep-after-mode,
16311
16577
  #sib-reasoning-wrap,
16312
- .sib-sep-after-reasoning,
16313
16578
  #sib-signal-wrap,
16314
16579
  .sib-sep-after-signal {
16315
16580
  display: none;
@@ -17064,26 +17329,22 @@ body.setup-mode {
17064
17329
  .project-actions-menu-item:hover .project-actions-menu-icon {
17065
17330
  color: var(--color-text-primary);
17066
17331
  }
17067
- .project-actions-menu-item.danger {
17068
- color: var(--color-error);
17069
- margin-top: 0.25rem;
17070
- position: relative;
17071
- }
17072
- .project-actions-menu-item.danger .project-actions-menu-icon,
17073
- .project-actions-menu-item.danger:hover .project-actions-menu-icon {
17074
- color: var(--color-error);
17332
+ .project-actions-menu-item.danger .project-actions-menu-icon {
17333
+ color: var(--color-text-secondary);
17075
17334
  }
17076
- .project-actions-menu-item.danger::before {
17077
- content: "";
17078
- position: absolute;
17079
- left: 0;
17080
- right: 0;
17081
- top: -0.1875rem;
17335
+ /* The last group (archive + delete) is set off by an explicit divider element,
17336
+ so the danger styling itself draws no line. */
17337
+ .project-actions-menu-divider {
17082
17338
  height: 1px;
17339
+ margin: 0.3125rem 0;
17083
17340
  background: var(--color-border-secondary);
17084
17341
  }
17085
17342
  .project-actions-menu-item.danger:hover {
17086
17343
  background: var(--color-error-bg);
17344
+ color: var(--color-error);
17345
+ }
17346
+ .project-actions-menu-item.danger:hover .project-actions-menu-icon {
17347
+ color: var(--color-error);
17087
17348
  }
17088
17349
 
17089
17350
  /* ── Organize menu (view-mode toggle) ──────────────────────────────────────── */