openclacky 1.5.13 → 1.5.15

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -0
  3. data/lib/clacky/agent/chunk_index.rb +83 -0
  4. data/lib/clacky/agent/history_navigation.rb +239 -0
  5. data/lib/clacky/agent/session_serializer.rb +53 -98
  6. data/lib/clacky/agent.rb +477 -297
  7. data/lib/clacky/agent_config.rb +5 -3
  8. data/lib/clacky/billing/billing_store.rb +2 -2
  9. data/lib/clacky/billing/platform_billing.rb +7 -0
  10. data/lib/clacky/brand_config.rb +27 -19
  11. data/lib/clacky/cli.rb +92 -12
  12. data/lib/clacky/cli_guidance.rb +48 -0
  13. data/lib/clacky/client.rb +2 -2
  14. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +70 -134
  15. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +4 -1
  16. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +307 -98
  17. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +179 -577
  18. data/lib/clacky/default_extensions/git/panels/git/view.js +34 -14
  19. data/lib/clacky/default_extensions/preview/ext.yml +20 -0
  20. data/lib/clacky/default_extensions/preview/panels/preview/view.js +475 -0
  21. data/lib/clacky/default_extensions/time_machine/panels/time_machine/view.js +1 -10
  22. data/lib/clacky/extension/api_extension.rb +18 -6
  23. data/lib/clacky/extension/verifier.rb +1 -1
  24. data/lib/clacky/json_ui_controller.rb +4 -2
  25. data/lib/clacky/media/openai_compat.rb +70 -30
  26. data/lib/clacky/message_format/bedrock.rb +6 -1
  27. data/lib/clacky/message_format/open_ai.rb +22 -5
  28. data/lib/clacky/message_format/open_ai_responses.rb +7 -3
  29. data/lib/clacky/plain_ui_controller.rb +1 -1
  30. data/lib/clacky/prompts/base.md +1 -1
  31. data/lib/clacky/providers.rb +44 -13
  32. data/lib/clacky/rich_ui/components/composer_guidance.rb +49 -0
  33. data/lib/clacky/rich_ui/rich_ui_controller.rb +24 -4
  34. data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +13 -0
  35. data/lib/clacky/search_config.rb +3 -3
  36. data/lib/clacky/server/channel/channel_manager.rb +25 -7
  37. data/lib/clacky/server/channel/channel_ui_controller.rb +1 -1
  38. data/lib/clacky/server/dir_picker.rb +67 -4
  39. data/lib/clacky/server/git_panel.rb +10 -2
  40. data/lib/clacky/server/http_server.rb +391 -75
  41. data/lib/clacky/server/preview.rb +351 -0
  42. data/lib/clacky/server/session_registry.rb +4 -0
  43. data/lib/clacky/server/web_ui_controller.rb +10 -5
  44. data/lib/clacky/session_manager.rb +52 -1
  45. data/lib/clacky/skill.rb +48 -0
  46. data/lib/clacky/tools/browser.rb +176 -19
  47. data/lib/clacky/tools/web_search.rb +91 -8
  48. data/lib/clacky/ui2/components/command_suggestions.rb +3 -1
  49. data/lib/clacky/ui2/components/input_area.rb +37 -4
  50. data/lib/clacky/ui2/components/modal_component.rb +35 -6
  51. data/lib/clacky/ui2/screen_buffer.rb +1 -0
  52. data/lib/clacky/ui2/ui_controller.rb +72 -12
  53. data/lib/clacky/ui_interface.rb +1 -1
  54. data/lib/clacky/utils/file_processor.rb +19 -7
  55. data/lib/clacky/utils/mac_app_detector.rb +186 -0
  56. data/lib/clacky/utils/model_pricing.rb +118 -68
  57. data/lib/clacky/utils/windows_app_detector.rb +334 -0
  58. data/lib/clacky/version.rb +1 -1
  59. data/lib/clacky/web/app.css +998 -168
  60. data/lib/clacky/web/app.js +19 -0
  61. data/lib/clacky/web/components/chat-navigator.js +489 -202
  62. data/lib/clacky/web/components/code-editor.js +191 -5
  63. data/lib/clacky/web/components/composer.js +3 -1
  64. data/lib/clacky/web/components/datepicker.js +19 -19
  65. data/lib/clacky/web/components/mentions.js +11 -14
  66. data/lib/clacky/web/components/model-picker.js +66 -23
  67. data/lib/clacky/web/components/quote-select.js +341 -0
  68. data/lib/clacky/web/core/aside.js +157 -8
  69. data/lib/clacky/web/core/ext.js +16 -0
  70. data/lib/clacky/web/features/billing/view.js +75 -21
  71. data/lib/clacky/web/features/extensions/store.js +4 -1
  72. data/lib/clacky/web/features/model-tester/store.js +37 -4
  73. data/lib/clacky/web/features/skills/store.js +18 -1
  74. data/lib/clacky/web/features/skills/view.js +128 -7
  75. data/lib/clacky/web/features/workspace/store.js +80 -7
  76. data/lib/clacky/web/features/workspace/view.js +474 -43
  77. data/lib/clacky/web/i18n.js +158 -23
  78. data/lib/clacky/web/index.html +57 -29
  79. data/lib/clacky/web/sessions.js +517 -127
  80. data/lib/clacky/web/settings.js +155 -6
  81. data/lib/clacky/web/utils.js +34 -0
  82. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +24 -19
  83. data/lib/clacky/web/vendor/codemirror/entry.js +130 -0
  84. data/lib/clacky/web/vendor/codemirror/package.json +29 -0
  85. data/lib/clacky/web/ws-dispatcher.js +161 -4
  86. data/lib/clacky.rb +2 -0
  87. metadata +13 -1
@@ -748,6 +748,20 @@ body {
748
748
  cursor: col-resize;
749
749
  z-index: 10;
750
750
  }
751
+ #sidebar-resize-handle::after {
752
+ content: "";
753
+ position: absolute;
754
+ top: 0;
755
+ bottom: 0;
756
+ left: 2px;
757
+ width: 1px;
758
+ background: transparent;
759
+ transition: background-color var(--transition-base);
760
+ }
761
+ #sidebar-resize-handle:hover::after,
762
+ #sidebar-resize-handle.active::after {
763
+ background: var(--color-accent-primary);
764
+ }
751
765
  #sidebar:has(#sidebar-resize-handle.active) {
752
766
  transition: background-color var(--transition-base);
753
767
  }
@@ -824,7 +838,7 @@ body {
824
838
  }
825
839
  #ext-slot-sidebar-nav {
826
840
  margin-top: 10px;
827
- padding: 10px 0 0px;
841
+ padding: 10px 0 0px;
828
842
  border-top: 1px solid var(--color-border-secondary);
829
843
  }
830
844
  #ext-slot-sidebar-nav-bottom {
@@ -1317,7 +1331,7 @@ body {
1317
1331
  }
1318
1332
 
1319
1333
  /* ── DatePicker popup ─────────────────────────────────────────────────────── */
1320
- .dp-popup {
1334
+ .dpk-popup {
1321
1335
  position: fixed;
1322
1336
  z-index: 9999;
1323
1337
  background: var(--color-bg-primary, #fff);
@@ -1330,17 +1344,17 @@ body {
1330
1344
  color: var(--color-text-primary);
1331
1345
  user-select: none;
1332
1346
  }
1333
- .dp-header {
1347
+ .dpk-header {
1334
1348
  display: flex;
1335
1349
  align-items: center;
1336
1350
  justify-content: space-between;
1337
1351
  margin-bottom: 0.5rem;
1338
1352
  }
1339
- .dp-title {
1353
+ .dpk-title {
1340
1354
  font-weight: 500;
1341
1355
  font-size: 0.8125rem;
1342
1356
  }
1343
- .dp-nav {
1357
+ .dpk-nav {
1344
1358
  background: none;
1345
1359
  border: none;
1346
1360
  cursor: pointer;
@@ -1350,20 +1364,20 @@ body {
1350
1364
  border-radius: 4px;
1351
1365
  line-height: 1;
1352
1366
  }
1353
- .dp-nav:hover { background: var(--color-bg-hover); }
1354
- .dp-grid {
1367
+ .dpk-nav:hover { background: var(--color-bg-hover); }
1368
+ .dpk-grid {
1355
1369
  display: grid;
1356
1370
  grid-template-columns: repeat(7, 1fr);
1357
1371
  gap: 2px;
1358
1372
  }
1359
- .dp-dow {
1373
+ .dpk-dow {
1360
1374
  text-align: center;
1361
1375
  font-size: 0.625rem;
1362
1376
  color: var(--color-text-muted);
1363
1377
  padding: 0.1875rem 0;
1364
1378
  font-weight: 500;
1365
1379
  }
1366
- .dp-day {
1380
+ .dpk-day {
1367
1381
  background: none;
1368
1382
  border: none;
1369
1383
  cursor: pointer;
@@ -1374,26 +1388,26 @@ body {
1374
1388
  font-size: 0.75rem;
1375
1389
  line-height: 1.4;
1376
1390
  }
1377
- .dp-day:hover { background: var(--color-bg-hover); }
1378
- .dp-day--today {
1391
+ .dpk-day:hover { background: var(--color-bg-hover); }
1392
+ .dpk-day--today {
1379
1393
  color: var(--color-accent-primary);
1380
1394
  font-weight: 700;
1381
1395
  }
1382
- .dp-day--selected {
1396
+ .dpk-day--selected {
1383
1397
  background: var(--color-accent-primary);
1384
1398
  color: var(--color-text-inverse) !important;
1385
1399
  font-weight: 500;
1386
1400
  }
1387
- .dp-day--selected:hover { background: var(--color-accent-primary); opacity: 0.88; }
1388
- .dp-footer {
1401
+ .dpk-day--selected:hover { background: var(--color-accent-primary); opacity: 0.88; }
1402
+ .dpk-footer {
1389
1403
  display: flex;
1390
1404
  justify-content: space-between;
1391
1405
  margin-top: 0.5rem;
1392
1406
  padding-top: 0.5rem;
1393
1407
  border-top: 1px solid var(--color-border-primary);
1394
1408
  }
1395
- .dp-btn-clear,
1396
- .dp-btn-today {
1409
+ .dpk-btn-clear,
1410
+ .dpk-btn-today {
1397
1411
  background: none;
1398
1412
  border: none;
1399
1413
  cursor: pointer;
@@ -1402,8 +1416,8 @@ body {
1402
1416
  padding: 0.25rem 0.5rem;
1403
1417
  border-radius: 4px;
1404
1418
  }
1405
- .dp-btn-clear:hover,
1406
- .dp-btn-today:hover { background: var(--color-bg-hover); }
1419
+ .dpk-btn-clear:hover,
1420
+ .dpk-btn-today:hover { background: var(--color-bg-hover); }
1407
1421
 
1408
1422
  /* 清除筛选 — small ✕ icon button */
1409
1423
  .btn-search-clear-all {
@@ -1664,8 +1678,12 @@ body {
1664
1678
  flex-shrink: 0;
1665
1679
  }
1666
1680
  .session-actions-menu-label {
1667
- flex: 1;
1668
- min-width: 0;
1681
+ flex: none;
1682
+ white-space: nowrap;
1683
+ }
1684
+ .wv-open-menu {
1685
+ width: max-content;
1686
+ max-width: calc(100vw - 16px);
1669
1687
  }
1670
1688
  .session-actions-menu-item:hover {
1671
1689
  background: var(--color-bg-hover);
@@ -2801,7 +2819,18 @@ body {
2801
2819
 
2802
2820
  /* ── Chat panel ──────────────────────────────────────────────────────────── */
2803
2821
  #chat-panel { flex: 1; display: flex; flex-direction: row; overflow: hidden; position: relative; }
2804
- #chat-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; min-width: 0; }
2822
+ #chat-main {
2823
+ --chat-nav-width: 2.5rem;
2824
+ --chat-nav-expanded-width: 22rem;
2825
+ --chat-nav-row-height: 2rem;
2826
+ --chat-nav-scrollbar-space: 12px;
2827
+ flex: 1;
2828
+ display: flex;
2829
+ flex-direction: column;
2830
+ overflow: hidden;
2831
+ position: relative;
2832
+ min-width: 0;
2833
+ }
2805
2834
 
2806
2835
  /* ── Session extension slots (agent-scoped panels mount here) ───────────────
2807
2836
  Each slot is laid out by the host; an empty slot collapses to zero so it
@@ -2827,7 +2856,7 @@ body {
2827
2856
  /* Empty slot (no panels for this agent) → collapse the whole column. */
2828
2857
  #session-aside:has(.session-aside-slot:empty) { width: 0; border-left: none; }
2829
2858
  #session-aside:has(.session-aside-slot:empty) .session-aside-resize,
2830
- #session-aside:has(.session-aside-slot:empty) .session-aside-collapse { display: none; }
2859
+ #session-aside:has(.session-aside-slot:empty) .aside-tabbar-actions { display: none; }
2831
2860
 
2832
2861
  .session-aside-resize {
2833
2862
  position: absolute;
@@ -2850,38 +2879,61 @@ body {
2850
2879
  transition: background-color var(--transition-base);
2851
2880
  }
2852
2881
  .session-aside-resize:hover::after,
2853
- .session-aside-resize.active::after { background: transparent; }
2882
+ .session-aside-resize.active::after { background: var(--color-accent-primary); }
2854
2883
 
2855
- .session-aside-collapse {
2884
+ /* Actions docked at the right end of the tab bar: same height, solid
2885
+ background so scrolling tabs never bleed underneath. */
2886
+ .aside-tabbar-actions {
2856
2887
  position: absolute;
2857
- top: 4px;
2858
- right: 0.5rem;
2888
+ top: 0;
2889
+ right: 0;
2859
2890
  z-index: 11;
2891
+ height: 40px;
2892
+ display: flex;
2893
+ align-items: center;
2894
+ gap: 2px;
2895
+ padding-right: 4px;
2896
+ background: var(--color-bg-secondary);
2897
+ border-bottom: 1px solid var(--color-border-primary);
2898
+ }
2899
+ .session-aside-collapse,
2900
+ .session-aside-fullscreen {
2860
2901
  display: inline-flex;
2861
2902
  align-items: center;
2862
2903
  justify-content: center;
2863
2904
  width: 2rem;
2864
2905
  height: 2rem;
2865
- border: 1px solid var(--color-border-primary);
2866
- background: var(--color-bg-secondary);
2867
- color: var(--color-text-secondary);
2906
+ border: none;
2907
+ background: transparent;
2908
+ color: var(--color-text-tertiary);
2868
2909
  border-radius: var(--radius-sm);
2869
2910
  cursor: pointer;
2870
- box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,0.06));
2871
- transition: background-color var(--transition-base), color var(--transition-base);
2911
+ transition: background-color var(--transition-fast), color var(--transition-fast);
2912
+ }
2913
+ .session-aside-collapse:hover,
2914
+ .session-aside-fullscreen:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
2915
+ .session-aside-fullscreen .icon-fs-exit { display: none; }
2916
+ #chat-panel.aside-fullscreen .session-aside-fullscreen .icon-fs-enter { display: none; }
2917
+ #chat-panel.aside-fullscreen .session-aside-fullscreen .icon-fs-exit { display: inline-flex; }
2918
+
2919
+ /* Fullscreen mode: the chat column is hidden and the aside takes over. */
2920
+ #chat-panel.aside-fullscreen #chat-main { display: none; }
2921
+ #chat-panel.aside-fullscreen #session-aside {
2922
+ width: 100%;
2923
+ border-left: none;
2872
2924
  }
2873
- .session-aside-collapse:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
2925
+ #chat-panel.aside-fullscreen .session-aside-resize { display: none; }
2874
2926
 
2875
2927
  .session-aside-slot { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
2876
2928
 
2877
- /* Tab bar (rendered by Clacky.ext). Leaves room on the right for the collapse
2878
- button which is absolutely positioned over it. */
2929
+ /* Tab bar (rendered by Clacky.ext). Leaves room on the right for the
2930
+ fullscreen + collapse buttons docked by the host over that end. */
2879
2931
  .aside-tabs {
2880
2932
  flex: none;
2881
2933
  display: flex;
2882
2934
  align-items: stretch;
2883
2935
  height: 40px;
2884
- padding: 0 38px 0 4px;
2936
+ padding: 0 78px 0 4px;
2885
2937
  gap: 2px;
2886
2938
  border-bottom: 1px solid var(--color-border-primary);
2887
2939
  overflow-x: auto;
@@ -2961,8 +3013,8 @@ body {
2961
3013
  flex: none;
2962
3014
  display: flex;
2963
3015
  align-items: center;
2964
- gap: 6px;
2965
- padding: 6px 8px;
3016
+ gap: 0.375rem;
3017
+ padding: 0.375rem 0.5rem;
2966
3018
  border-bottom: 1px solid var(--color-border-secondary);
2967
3019
  }
2968
3020
  .wt-search {
@@ -2970,9 +3022,9 @@ body {
2970
3022
  min-width: 0;
2971
3023
  display: flex;
2972
3024
  align-items: center;
2973
- gap: 6px;
2974
- height: 26px;
2975
- padding: 0 4px 0 8px;
3025
+ gap: 0.375rem;
3026
+ height: 1.625rem;
3027
+ padding: 0 0.25rem 0 0.5rem;
2976
3028
  background: var(--color-bg-primary);
2977
3029
  border: 1px solid var(--color-border-primary);
2978
3030
  border-radius: var(--radius-sm);
@@ -2986,7 +3038,7 @@ body {
2986
3038
  border: none;
2987
3039
  outline: none;
2988
3040
  background: transparent;
2989
- font-size: 12px;
3041
+ font-size: 0.75rem;
2990
3042
  color: var(--color-text-primary);
2991
3043
  padding: 0;
2992
3044
  }
@@ -2996,8 +3048,8 @@ body {
2996
3048
  display: none;
2997
3049
  align-items: center;
2998
3050
  justify-content: center;
2999
- width: 18px;
3000
- height: 18px;
3051
+ width: 1.125rem;
3052
+ height: 1.125rem;
3001
3053
  padding: 0;
3002
3054
  border: none;
3003
3055
  border-radius: var(--radius-xs);
@@ -3009,10 +3061,10 @@ body {
3009
3061
  .wt-search-list {
3010
3062
  flex: 1;
3011
3063
  overflow-y: auto;
3012
- padding: 4px 6px 8px;
3064
+ padding: 0.25rem 0.375rem 0.5rem;
3013
3065
  font-size: 0.8125rem;
3014
3066
  }
3015
- .wt-search-row { gap: 6px; }
3067
+ .wt-search-row { gap: 0.375rem; }
3016
3068
  .wt-search-dir {
3017
3069
  margin-left: auto;
3018
3070
  padding-left: 0.5rem;
@@ -3034,8 +3086,8 @@ body {
3034
3086
  display: inline-flex;
3035
3087
  align-items: center;
3036
3088
  justify-content: center;
3037
- width: 24px;
3038
- height: 24px;
3089
+ width: 1.5rem;
3090
+ height: 1.5rem;
3039
3091
  padding: 0;
3040
3092
  border: none;
3041
3093
  border-radius: var(--radius-xs);
@@ -3047,16 +3099,16 @@ body {
3047
3099
  .wt-tree {
3048
3100
  flex: 1;
3049
3101
  overflow-y: auto;
3050
- padding: 4px 6px 8px;
3102
+ padding: 0.25rem 0.375rem 0.5rem;
3051
3103
  font-size: 0.8125rem;
3052
3104
  }
3053
3105
  .wt-node { user-select: none; }
3054
3106
  .wt-row {
3055
3107
  display: flex;
3056
3108
  align-items: center;
3057
- gap: 5px;
3058
- height: 24px;
3059
- padding: 0 8px 0 5px;
3109
+ gap: 0.3125rem;
3110
+ height: 1.5rem;
3111
+ padding: 0 0.5rem 0 0.3125rem;
3060
3112
  border-radius: var(--radius-xs);
3061
3113
  cursor: pointer;
3062
3114
  color: var(--color-text-primary);
@@ -3080,7 +3132,7 @@ body {
3080
3132
  .wt-size { margin-left: auto; padding-left: 0.5rem; font-size: 0.6875rem; color: var(--color-text-muted); opacity: 0; transition: opacity var(--transition-base); flex-shrink: 0; }
3081
3133
  .wt-row:hover .wt-size, .wt-row.active .wt-size { opacity: 1; }
3082
3134
  .wt-children {
3083
- margin-left: 11px;
3135
+ margin-left: 0.6875rem;
3084
3136
  border-left: 1px solid transparent;
3085
3137
  }
3086
3138
  .wt-children.guide-on { border-left-color: color-mix(in srgb, var(--color-text-primary) 20%, transparent); }
@@ -3089,22 +3141,22 @@ body {
3089
3141
  font-size: 0.75rem;
3090
3142
  color: var(--color-text-secondary);
3091
3143
  }
3092
- .wt-error { color: var(--color-danger, #d23); }
3144
+ .wt-error { color: var(--color-error); }
3093
3145
 
3094
3146
  /* ── Workspace viewer (file tree + tabbed preview, inside the Files tab) ── */
3095
3147
  .wv-root { flex: 1; min-height: 0; display: flex; }
3096
3148
  .wv-tree-pane {
3097
3149
  flex: 0 0 auto;
3098
3150
  width: var(--wv-tree-width, 38%);
3099
- min-width: 168px;
3100
- max-width: 460px;
3151
+ min-width: 10.5rem;
3152
+ max-width: 28.75rem;
3101
3153
  display: flex;
3102
3154
  flex-direction: column;
3103
3155
  min-height: 0;
3104
3156
  }
3105
3157
  .wv-tree-resize {
3106
3158
  flex: none;
3107
- width: 6px;
3159
+ width: 3px;
3108
3160
  cursor: col-resize;
3109
3161
  position: relative;
3110
3162
  z-index: 2;
@@ -3112,6 +3164,8 @@ body {
3112
3164
  display: none;
3113
3165
  }
3114
3166
  .wv-root.has-open-files .wv-tree-resize { display: block; }
3167
+ .wv-root.tree-hidden .wv-tree-pane { display: none; }
3168
+ .wv-root.tree-hidden .wv-tree-resize { display: none; }
3115
3169
  .wv-tree-resize::after {
3116
3170
  content: "";
3117
3171
  position: absolute;
@@ -3120,6 +3174,11 @@ body {
3120
3174
  left: 2px;
3121
3175
  width: 1px;
3122
3176
  background: var(--color-border-primary);
3177
+ transition: background-color var(--transition-base);
3178
+ }
3179
+ .wv-tree-resize:hover::after,
3180
+ .wv-tree-resize.active::after {
3181
+ background: var(--color-accent-primary);
3123
3182
  }
3124
3183
  .wv-viewer { flex: 1; min-width: 0; display: none; flex-direction: column; min-height: 0; }
3125
3184
  .wv-root.has-open-files .wv-viewer { display: flex; }
@@ -3129,8 +3188,8 @@ body {
3129
3188
  flex: none;
3130
3189
  display: flex;
3131
3190
  align-items: center;
3132
- gap: 6px;
3133
- padding: 4px 6px 4px 8px;
3191
+ gap: 0.375rem;
3192
+ padding: 0.32rem 0.375rem 0.32rem 0.5rem;
3134
3193
  border-bottom: 1px solid var(--color-border-secondary);
3135
3194
  }
3136
3195
  .wv-tabs {
@@ -3138,7 +3197,7 @@ body {
3138
3197
  min-width: 0;
3139
3198
  display: flex;
3140
3199
  align-items: center;
3141
- gap: 2px;
3200
+ gap: 0.125rem;
3142
3201
  overflow-x: auto;
3143
3202
  scrollbar-width: none;
3144
3203
  }
@@ -3147,13 +3206,14 @@ body {
3147
3206
  flex: none;
3148
3207
  display: inline-flex;
3149
3208
  align-items: center;
3150
- gap: 4px;
3151
- max-width: 150px;
3152
- padding: 3px 4px 3px 9px;
3209
+ gap: 0.25rem;
3210
+ max-width: min(9.375rem, 100%);
3211
+ height: 1.625rem;
3212
+ padding: 0.1875rem 0.25rem 0.1875rem 0.5625rem;
3153
3213
  border: 1px solid transparent;
3154
3214
  border-radius: var(--radius-sm);
3155
3215
  background: none;
3156
- font-size: 12px;
3216
+ font-size: 0.75rem;
3157
3217
  color: var(--color-text-tertiary);
3158
3218
  cursor: pointer;
3159
3219
  white-space: nowrap;
@@ -3165,33 +3225,34 @@ body {
3165
3225
  border-color: var(--color-border-secondary);
3166
3226
  font-weight: 500;
3167
3227
  }
3168
- .wv-tab-label { overflow: hidden; text-overflow: ellipsis; }
3228
+ .wv-tab-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
3169
3229
  .wv-tab-dot {
3170
3230
  flex: none;
3171
- width: 6px;
3172
- height: 6px;
3231
+ width: 0.375rem;
3232
+ height: 0.375rem;
3173
3233
  border-radius: 50%;
3174
3234
  background: var(--color-accent-primary);
3175
3235
  }
3176
3236
  .wv-tab-close {
3237
+ flex: none;
3177
3238
  display: inline-flex;
3178
3239
  align-items: center;
3179
3240
  justify-content: center;
3180
- width: 16px;
3181
- height: 16px;
3182
- border-radius: 4px;
3241
+ width: 1rem;
3242
+ height: 1rem;
3243
+ border-radius: var(--radius-xs);
3183
3244
  color: var(--color-text-tertiary);
3184
3245
  cursor: pointer;
3185
3246
  }
3186
3247
  .wv-tab-close:hover { background: var(--color-border-primary); color: var(--color-text-primary); }
3187
3248
 
3188
- .wv-actions { flex: none; display: flex; align-items: center; gap: 2px; }
3249
+ .wv-actions { flex: none; display: flex; align-items: center; gap: 0.25rem; }
3189
3250
  .wv-action {
3190
3251
  display: inline-flex;
3191
3252
  align-items: center;
3192
3253
  justify-content: center;
3193
- width: 26px;
3194
- height: 26px;
3254
+ width: 1.625rem;
3255
+ height: 1.625rem;
3195
3256
  border: none;
3196
3257
  border-radius: var(--radius-sm);
3197
3258
  background: none;
@@ -3201,6 +3262,7 @@ body {
3201
3262
  .wv-action:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
3202
3263
  .wv-action.active { background: var(--color-bg-hover); color: var(--color-accent-primary); }
3203
3264
  .wv-action.dirty { color: var(--color-accent-primary); }
3265
+ .wv-mobile-back { display: none; }
3204
3266
 
3205
3267
  .wv-content { flex: 1; min-height: 0; position: relative; }
3206
3268
  .wv-pane { position: absolute; inset: 0; display: none; }
@@ -3208,6 +3270,9 @@ body {
3208
3270
  .wv-pane--text { flex-direction: column; }
3209
3271
  .wv-pane[data-mode="edit"] .wv-md-preview { display: none; }
3210
3272
  .wv-pane[data-mode="preview"] .wv-code { display: none; }
3273
+ .wv-pane--text[data-mode="split"] { flex-direction: row; }
3274
+ .wv-pane--text[data-mode="split"] .wv-code { flex: 1; min-width: 0; }
3275
+ .wv-pane--text[data-mode="split"] .wv-md-preview { min-width: 0; border-left: 1px solid var(--color-border-primary); }
3211
3276
  .wv-code { flex: 1; min-height: 0; overflow: hidden; }
3212
3277
  .wv-code .cm-editor {
3213
3278
  height: 100%;
@@ -3231,6 +3296,145 @@ body {
3231
3296
  }
3232
3297
  .wv-pane--image img { max-width: 100%; max-height: 100%; object-fit: contain; }
3233
3298
 
3299
+ /* PDF: browser-native rendering inside an iframe pointing at /api/local-image */
3300
+ .wv-pane--pdf { background: var(--color-bg-primary); }
3301
+ .wv-pane--pdf iframe {
3302
+ flex: 1;
3303
+ width: 100%;
3304
+ border: none;
3305
+ background: var(--color-bg-primary);
3306
+ }
3307
+
3308
+ /* CSV/TSV: read-only table view; edit the original via "Open ▾" */
3309
+ .wv-pane--table {
3310
+ flex-direction: column;
3311
+ background: var(--color-bg-primary);
3312
+ overflow: hidden;
3313
+ }
3314
+ .wv-table-scroll {
3315
+ flex: 1;
3316
+ overflow: auto;
3317
+ }
3318
+ .wv-table-scroll table {
3319
+ /* Padding must live on the table, not the scroller: a sticky header stops at the
3320
+ scroller's padding box, so scroller padding leaves a transparent gap that rows
3321
+ scroll through. `separate` keeps cell borders from detaching while stuck. */
3322
+ margin: 0.5rem;
3323
+ border-collapse: separate;
3324
+ border-spacing: 0;
3325
+ font-size: 0.8125rem;
3326
+ font-family: var(--font-mono, ui-monospace, monospace);
3327
+ }
3328
+ .wv-table-scroll th,
3329
+ .wv-table-scroll td {
3330
+ border-right: 1px solid var(--color-border-primary);
3331
+ border-bottom: 1px solid var(--color-border-primary);
3332
+ padding: 0.25rem 0.5rem;
3333
+ text-align: left;
3334
+ white-space: pre;
3335
+ vertical-align: top;
3336
+ }
3337
+ .wv-table-scroll tr > :first-child { border-left: 1px solid var(--color-border-primary); }
3338
+ .wv-table-scroll thead th {
3339
+ position: sticky;
3340
+ top: 0;
3341
+ z-index: 1;
3342
+ border-top: 1px solid var(--color-border-primary);
3343
+ background: var(--color-bg-secondary);
3344
+ font-weight: 600;
3345
+ color: var(--color-text-primary);
3346
+ }
3347
+ .wv-table-scroll tbody td { color: var(--color-text-secondary); }
3348
+ .wv-table-scroll tbody tr:hover td { background: var(--color-bg-hover); }
3349
+ .wv-table-note {
3350
+ flex-shrink: 0;
3351
+ padding: 0.375rem 0.75rem;
3352
+ border-top: 1px solid var(--color-border-primary);
3353
+ font-size: 0.75rem;
3354
+ color: var(--color-text-tertiary);
3355
+ background: var(--color-bg-secondary);
3356
+ }
3357
+
3358
+ /* Binary fallback: file icon + name + hints; "Open ▾" lives in the tabbar */
3359
+ .wv-pane--fallback {
3360
+ flex-direction: column;
3361
+ align-items: center;
3362
+ justify-content: center;
3363
+ gap: 0.375rem;
3364
+ padding: 1.5rem 1.25rem;
3365
+ text-align: center;
3366
+ background: var(--color-bg-primary);
3367
+ }
3368
+ .wv-fallback-icon { color: var(--color-text-tertiary); }
3369
+ .wv-fallback-icon svg { width: 2.5rem; height: 2.5rem; }
3370
+ .wv-fallback-name {
3371
+ font-weight: 600;
3372
+ color: var(--color-text-primary);
3373
+ word-break: break-all;
3374
+ }
3375
+ .wv-fallback-hint {
3376
+ font-size: 0.8125rem;
3377
+ color: var(--color-text-secondary);
3378
+ }
3379
+ .wv-fallback-subhint {
3380
+ font-size: 0.75rem;
3381
+ color: var(--color-text-tertiary);
3382
+ }
3383
+
3384
+ /* Toolbar "Open ▾" split group for binary tabs */
3385
+ .wv-action-group {
3386
+ display: inline-flex;
3387
+ align-items: center;
3388
+ border-radius: var(--radius-sm);
3389
+ overflow: hidden;
3390
+ border: 1px solid var(--color-border-primary);
3391
+ }
3392
+ .wv-action-group .wv-action { border-radius: 0; }
3393
+ .wv-action-group .wv-action + .wv-action {
3394
+ border-left: 1px solid var(--color-border-primary);
3395
+ }
3396
+ .wv-action--open {
3397
+ width: auto;
3398
+ padding: 0 0.5rem;
3399
+ font-size: 0.75rem;
3400
+ font-weight: 500;
3401
+ color: var(--color-text-secondary);
3402
+ gap: 0.375rem;
3403
+ }
3404
+ .wv-action--open:hover { color: var(--color-text-primary); }
3405
+
3406
+ /* Real app icons inside the "open with" dropdown (served by /api/app-icon) */
3407
+ .wv-open-app-icon { width: 1rem; height: 1rem; }
3408
+ .wv-open-app-icon img {
3409
+ width: 100%;
3410
+ height: 100%;
3411
+ display: block;
3412
+ border-radius: 0.1875rem;
3413
+ }
3414
+
3415
+ /* Default app icon on the main "Open" button */
3416
+ .wv-main-app-icon { display: inline-flex; align-items: center; justify-content: center; width: 1rem; height: 1rem; }
3417
+ .wv-main-app-icon svg { width: 1rem; height: 1rem; display: block; }
3418
+ .wv-main-app-icon img {
3419
+ width: 1rem;
3420
+ height: 1rem;
3421
+ display: block;
3422
+ border-radius: 0.1875rem;
3423
+ }
3424
+
3425
+ /* Highlight the OS default app in the "open with" dropdown */
3426
+ .wv-open-menu .session-actions-menu-item.wv-open-menu-item--default {
3427
+ background: var(--color-bg-hover);
3428
+ font-weight: 600;
3429
+ }
3430
+
3431
+ /* Separator inside the "open with" dropdown */
3432
+ .wv-open-menu-separator {
3433
+ height: 1px;
3434
+ margin: 0.25rem 0.375rem;
3435
+ background: var(--color-border-secondary);
3436
+ }
3437
+
3234
3438
 
3235
3439
  /* Mobile-only floating back button — replaces the old in-header back button.
3236
3440
  Hidden on desktop; mobile media query enables it. Positioned absolutely so
@@ -3249,115 +3453,248 @@ body {
3249
3453
  flex-direction: column;
3250
3454
  gap: 0.75rem;
3251
3455
  }
3456
+ .has-chat-navigator > .chat-messages-scroll {
3457
+ padding-right: calc(var(--chat-nav-width) + var(--chat-nav-overlay-space, var(--chat-nav-scrollbar-space)) + 0.5rem);
3458
+ }
3252
3459
 
3253
3460
  /* ── Chat navigator (right-edge minimap) ───────────────────────────────────
3254
3461
  Absolutely positioned inside #chat-main, vertically synced to #messages
3255
- via JS (ResizeObserver). Bars are evenly distributed; the active one
3256
- (matching current scroll position) is highlighted. Hover opens a popup
3257
- listing every message preview; click scrolls to that message. */
3462
+ via JS (ResizeObserver). The track scrolls independently of the chat. */
3258
3463
  .chat-navigator {
3464
+ --chat-nav-frame-top: 0px;
3465
+ --chat-nav-frame-height: 100%;
3259
3466
  position: absolute;
3260
- right: 0;
3261
- width: 1.5rem;
3467
+ right: var(--chat-nav-scrollbar-space);
3468
+ width: var(--chat-nav-width);
3262
3469
  z-index: 10;
3263
3470
  pointer-events: none;
3264
3471
  display: flex;
3265
3472
  flex-direction: column;
3473
+ transition: width 0.14s ease;
3474
+ }
3475
+ .chat-nav-frame {
3476
+ position: absolute;
3477
+ z-index: 0;
3478
+ left: 0;
3479
+ right: 0;
3480
+ top: var(--chat-nav-frame-top);
3481
+ height: var(--chat-nav-frame-height);
3482
+ border-radius: 0.75rem;
3483
+ background: var(--color-bg-primary);
3484
+ box-shadow: 0 0 0 1px var(--color-border-primary), 0 0.5rem 1.75rem rgba(0, 0, 0, 0.1);
3485
+ opacity: 0;
3486
+ transition: opacity 0.14s ease;
3487
+ pointer-events: none;
3488
+ }
3489
+ .chat-nav-viewport {
3490
+ position: absolute;
3491
+ z-index: 1;
3492
+ inset: 0;
3493
+ overflow: visible;
3494
+ pointer-events: none;
3495
+ }
3496
+ .chat-navigator.expanded {
3497
+ width: var(--chat-nav-expanded-width);
3498
+ }
3499
+ .chat-navigator.expanded .chat-nav-frame {
3500
+ opacity: 1;
3501
+ pointer-events: auto;
3502
+ }
3503
+ .chat-navigator.expanded .chat-nav-viewport {
3504
+ /* Keep the track geometry stable; only its paint/hit-test viewport changes. */
3505
+ clip-path: inset(
3506
+ calc(var(--chat-nav-frame-top) + 0.75rem)
3507
+ 0
3508
+ calc(100% - var(--chat-nav-frame-top) - var(--chat-nav-frame-height) + 0.75rem)
3509
+ 0
3510
+ round 0.6875rem
3511
+ );
3266
3512
  }
3267
3513
  .chat-nav-track {
3268
- flex: 1;
3269
- display: flex;
3270
- flex-direction: column;
3271
- justify-content: center;
3272
- gap: 0.25rem;
3273
- padding: 0.625rem 0.375rem;
3514
+ position: relative;
3515
+ width: 100%;
3516
+ height: 100%;
3517
+ min-height: 0;
3518
+ overflow-y: auto;
3519
+ overflow-x: hidden;
3520
+ scrollbar-width: none;
3521
+ overscroll-behavior: contain;
3274
3522
  pointer-events: none;
3275
- overflow: hidden;
3523
+ }
3524
+ .chat-navigator.expanded .chat-nav-track {
3525
+ pointer-events: auto;
3526
+ cursor: pointer;
3527
+ }
3528
+ .chat-nav-track::-webkit-scrollbar { display: none; }
3529
+ .chat-nav-track:focus-visible {
3530
+ outline: 1px solid var(--color-accent-primary);
3531
+ outline-offset: -1px;
3532
+ }
3533
+ .chat-nav-canvas {
3534
+ position: relative;
3535
+ }
3536
+ .chat-nav-row {
3537
+ position: absolute;
3538
+ left: 0;
3539
+ right: 0;
3540
+ height: var(--chat-nav-row-height);
3541
+ transform: translateY(-50%);
3542
+ border-radius: 0.375rem;
3543
+ transition: background 0.12s ease;
3544
+ }
3545
+ .chat-navigator.expanded .chat-nav-row:hover,
3546
+ .chat-navigator.expanded .chat-nav-row.hovered {
3547
+ background: var(--color-bg-hover);
3276
3548
  }
3277
3549
  .chat-nav-bar {
3278
- height: 0.1875rem;
3279
- flex-shrink: 0;
3550
+ position: absolute;
3551
+ right: 0.5rem;
3552
+ top: 50%;
3553
+ width: 0.525rem;
3554
+ height: 0.1475rem;
3555
+ transform: translateY(-50%);
3280
3556
  border-radius: 0.125rem;
3281
- background: var(--color-border-secondary);
3282
- cursor: pointer;
3557
+ background: var(--color-text-muted);
3283
3558
  pointer-events: auto;
3284
- opacity: 0.45;
3285
- transition: opacity 0.15s ease, background 0.15s ease, transform 0.12s ease;
3559
+ cursor: pointer;
3560
+ opacity: 0.42;
3561
+ transition: opacity 0.12s ease, background 0.12s ease;
3286
3562
  }
3287
- .chat-nav-bar:hover {
3288
- opacity: 0.85;
3289
- background: var(--color-text-muted);
3290
- transform: scaleX(1.6);
3563
+ .chat-nav-bar::before {
3564
+ content: "";
3565
+ position: absolute;
3566
+ inset: -0.625rem -0.5rem;
3291
3567
  }
3292
- .chat-nav-bar.hovered {
3293
- opacity: 0.85;
3294
- background: var(--color-text-muted);
3295
- transform: scaleX(1.6);
3568
+ .chat-navigator.expanded .chat-nav-bar {
3569
+ pointer-events: none;
3296
3570
  }
3297
- .chat-nav-bar.active {
3571
+ .chat-nav-row.active .chat-nav-bar,
3572
+ .chat-nav-row.hovered .chat-nav-bar {
3298
3573
  opacity: 1;
3299
3574
  background: var(--color-accent-primary);
3300
3575
  }
3576
+ .chat-nav-user {
3577
+ position: absolute;
3578
+ left: 1rem;
3579
+ right: var(--chat-nav-width);
3580
+ top: 50%;
3581
+ transform: translateY(-50%);
3582
+ visibility: hidden;
3583
+ opacity: 0;
3584
+ overflow: hidden;
3585
+ text-overflow: ellipsis;
3586
+ white-space: nowrap;
3587
+ color: var(--color-text-muted);
3588
+ font-size: 0.8125rem;
3589
+ font-weight: 400;
3590
+ line-height: 1.4;
3591
+ transition: color 0.1s ease, opacity 0.1s ease;
3592
+ }
3593
+ .chat-navigator.expanded .chat-nav-user {
3594
+ visibility: visible;
3595
+ opacity: 1;
3596
+ }
3597
+ .chat-nav-row.active .chat-nav-user,
3598
+ .chat-nav-row.hovered .chat-nav-user {
3599
+ color: var(--color-text-primary);
3600
+ }
3601
+ .chat-nav-user code {
3602
+ padding: 0.0625rem 0.1875rem;
3603
+ border-radius: 0.1875rem;
3604
+ background: var(--color-bg-tertiary);
3605
+ font-size: 0.9em;
3606
+ }
3607
+ .chat-nav-row.loading .chat-nav-user::after {
3608
+ content: "";
3609
+ display: block;
3610
+ width: 72%;
3611
+ height: 0.75rem;
3612
+ margin-top: 0.25rem;
3613
+ border-radius: 0.25rem;
3614
+ background: var(--color-border-secondary);
3615
+ animation: chat-nav-pulse 1.2s ease-in-out infinite alternate;
3616
+ }
3301
3617
 
3302
- /* ── Hover popup ─────────────────────────────────────────────────────────── */
3618
+ /* ── Final-answer tooltip ────────────────────────────────────────────────── */
3303
3619
  .chat-nav-popup {
3304
3620
  position: absolute;
3305
- right: 100%;
3621
+ z-index: 3;
3622
+ right: calc(100% + 0.5rem);
3306
3623
  top: 0;
3307
- width: 17rem;
3308
- max-width: 60vw;
3624
+ width: 20rem;
3625
+ max-width: 55vw;
3309
3626
  max-height: 100%;
3310
- margin-right: 0.25rem;
3627
+ margin: 0;
3628
+ padding: 0.75rem;
3629
+ text-align: left;
3630
+ font: inherit;
3311
3631
  background: var(--color-bg-primary);
3312
3632
  border: 1px solid var(--color-border-primary);
3313
3633
  border-radius: 0.5rem;
3314
3634
  box-shadow: 0 0.25rem 1.5rem rgba(0, 0, 0, 0.12);
3315
3635
  display: flex;
3316
3636
  flex-direction: column;
3637
+ gap: 0.375rem;
3317
3638
  overflow: hidden;
3318
- pointer-events: auto;
3319
- }
3320
- .chat-nav-popup-header {
3321
- flex-shrink: 0;
3322
- padding: 0.5rem 0.75rem;
3323
- border-bottom: 1px solid var(--color-border-secondary);
3324
- font-size: 0.75rem;
3325
- font-weight: 600;
3326
- color: var(--color-text-secondary);
3327
- text-transform: uppercase;
3328
- letter-spacing: 0.04em;
3639
+ pointer-events: none;
3329
3640
  }
3330
- .chat-nav-popup-list {
3331
- flex: 1;
3332
- overflow-y: auto;
3333
- overflow-x: hidden;
3334
- padding: 0.25rem;
3641
+ .chat-nav-popup[hidden] { display: none; }
3642
+ .chat-nav-popup-label {
3335
3643
  display: flex;
3336
- flex-direction: column;
3337
- gap: 0.0625rem;
3644
+ align-items: center;
3645
+ gap: 0.375rem;
3646
+ color: var(--color-accent-primary);
3647
+ font-size: 0.6875rem;
3648
+ font-weight: 500;
3649
+ line-height: 1.2;
3338
3650
  }
3339
- .chat-nav-popup-item {
3340
- padding: 0.4375rem 0.625rem;
3341
- border-radius: 0.375rem;
3342
- cursor: pointer;
3651
+ .chat-nav-popup-dot {
3652
+ flex: 0 0 auto;
3653
+ width: 0.375rem;
3654
+ height: 0.375rem;
3655
+ border-radius: 50%;
3656
+ background: currentColor;
3657
+ opacity: 0.8;
3658
+ }
3659
+ .chat-nav-answer {
3660
+ display: -webkit-box;
3661
+ -webkit-box-orient: vertical;
3662
+ -webkit-line-clamp: 3;
3663
+ overflow: hidden;
3664
+ overflow-wrap: anywhere;
3343
3665
  font-size: 0.8125rem;
3344
- line-height: 1.4;
3666
+ line-height: 1.5;
3345
3667
  color: var(--color-text-secondary);
3346
- display: flex;
3347
- align-items: flex-start;
3348
- gap: 0.375rem;
3349
- transition: background 0.1s ease, color 0.1s ease;
3350
3668
  }
3351
- .chat-nav-popup-item:hover,
3352
- .chat-nav-popup-item.highlighted {
3353
- background: var(--color-bg-hover);
3354
- color: var(--color-text-primary);
3669
+ .chat-nav-popup code {
3670
+ padding: 0.0625rem 0.1875rem;
3671
+ border-radius: 0.1875rem;
3672
+ background: var(--color-bg-tertiary);
3673
+ font-size: 0.9em;
3674
+ white-space: normal;
3355
3675
  }
3356
- .chat-nav-popup-item-text {
3357
- overflow: hidden;
3358
- text-overflow: ellipsis;
3359
- white-space: nowrap;
3360
- min-width: 0;
3676
+ .chat-nav-popup.loading span {
3677
+ width: 100%;
3678
+ height: 1rem;
3679
+ margin: 0.25rem 0;
3680
+ border-radius: 0.25rem;
3681
+ background: var(--color-border-secondary);
3682
+ animation: chat-nav-pulse 1.2s ease-in-out infinite alternate;
3683
+ }
3684
+ .chat-nav-popup.loading .chat-nav-answer { width: 80%; height: 2.5rem; }
3685
+ @keyframes chat-nav-pulse {
3686
+ from { opacity: 0.35; }
3687
+ to { opacity: 0.8; }
3688
+ }
3689
+ @media (prefers-reduced-motion: reduce) {
3690
+ .chat-navigator,
3691
+ .chat-nav-frame,
3692
+ .chat-nav-track,
3693
+ .chat-nav-row,
3694
+ .chat-nav-bar,
3695
+ .chat-nav-user { transition: none; }
3696
+ .chat-nav-row.loading .chat-nav-user::after,
3697
+ .chat-nav-popup.loading span { animation: none; }
3361
3698
  }
3362
3699
 
3363
3700
  /* Empty-state hint: shown inside #messages when a session has no messages yet.
@@ -3841,6 +4178,7 @@ body {
3841
4178
  .msg-assistant strong { font-weight: 500; color: var(--color-text-primary); }
3842
4179
  .msg-assistant em { font-style: italic; color: var(--color-text-secondary); }
3843
4180
  .msg-tool { background: var(--color-bg-primary); border: 1px solid var(--color-border-primary); font-family: monospace; font-size: 0.75rem; color: var(--color-text-secondary); align-self: flex-start; }
4181
+ .msg-stopped { color: var(--color-text-secondary); font-size: 0.75rem; align-self: center; font-style: normal; }
3844
4182
  .msg-info { color: var(--color-text-secondary); font-size: 0.75rem; align-self: center; font-style: italic; }
3845
4183
  .msg-info.msg-info-main { font-style: normal; }
3846
4184
  .msg-info-sub { color: var(--color-text-secondary); font-size: 0.6875rem; align-self: center; opacity: 0.7; margin-top: -1.6875rem; }
@@ -4380,6 +4718,14 @@ body {
4380
4718
  .tool-item-stdout .ansi-black { color: #5c6370; }
4381
4719
  .tool-item-stdout .ansi-bold { font-weight: 700; }
4382
4720
 
4721
+ .search-results { display: grid; gap: 10px; }
4722
+ .search-results-meta, .search-result-url { color: var(--color-text-secondary); font-size: 0.75rem; }
4723
+ .search-result { display: grid; grid-template-columns: 22px 1fr; gap: 8px; }
4724
+ .search-result-index { color: var(--color-text-tertiary); font-size: 0.75rem; padding-top: 2px; }
4725
+ .search-result a { color: var(--color-accent, #6ea8fe); font-weight: 600; text-decoration: none; }
4726
+ .search-result a:hover { text-decoration: underline; }
4727
+ .search-result-snippet { margin-top: 3px; color: var(--color-text-primary); white-space: normal; }
4728
+
4383
4729
  /* ── Diff block (rendered inline within edit/write tool-item) ─────────────── */
4384
4730
  .tool-item-diff {
4385
4731
  margin: 0.375rem 0 0.375rem 1.25rem;
@@ -4558,7 +4904,7 @@ body {
4558
4904
  #session-info-bar {
4559
4905
  display: flex;
4560
4906
  align-items: center;
4561
- gap: 0.375rem;
4907
+ gap: 0.275rem;
4562
4908
  flex-wrap: nowrap;
4563
4909
  padding: 0.375rem 0.75rem;
4564
4910
  margin-left: 1.5rem;
@@ -4624,6 +4970,7 @@ body {
4624
4970
  .sib-id-clickable {
4625
4971
  cursor: pointer;
4626
4972
  border-radius: 3px;
4973
+ padding: 0.2rem;
4627
4974
  transition: all 0.15s ease;
4628
4975
  }
4629
4976
  .sib-id-clickable:hover {
@@ -4724,11 +5071,32 @@ body {
4724
5071
  }
4725
5072
 
4726
5073
  #sib-dir {
5074
+ display: inline-flex;
5075
+ align-items: center;
5076
+ gap: 0.3125rem;
4727
5077
  flex-shrink: 0;
4728
5078
  cursor: pointer;
4729
5079
  border-radius: 3px;
5080
+ padding: 0.2rem;
4730
5081
  opacity: 0.75; /* tier 2 */
4731
5082
  transition: all 0.15s ease;
5083
+ max-width: 17rem;
5084
+ }
5085
+ /* The path is the only shrinkable part — the caret must stay visible.
5086
+ line-height is pinned so the wrapper's height stays driven by the text and
5087
+ the bar keeps its height (an unconstrained flex item would grow to its line
5088
+ box, ~3px taller). */
5089
+ #sib-dir-text,
5090
+ #sib-reasoning-text {
5091
+ min-width: 0;
5092
+ line-height: 1.2;
5093
+ overflow: hidden;
5094
+ text-overflow: ellipsis;
5095
+ white-space: nowrap;
5096
+ }
5097
+ /* display:inline-flex above would beat the UA [hidden] rule. */
5098
+ #sib-dir[hidden] {
5099
+ display: none;
4732
5100
  }
4733
5101
  #sib-dir:hover {
4734
5102
  background: var(--color-bg-hover);
@@ -4807,15 +5175,77 @@ body {
4807
5175
  position: relative;
4808
5176
  }
4809
5177
  .sib-model-clickable {
5178
+ display: inline-flex;
5179
+ align-items: center;
5180
+ padding: 0.2rem;
5181
+ gap: 0.3125rem;
4810
5182
  cursor: pointer;
4811
5183
  border-radius: 3px;
4812
5184
  transition: all 0.15s ease;
4813
5185
  }
5186
+ /* The bar text is 11px, so the dropdown's 18px tile would loom over it. */
5187
+ .sib-model-clickable .sib-vendor-badge {
5188
+ width: 0.875rem;
5189
+ height: 0.875rem;
5190
+ border-radius: 4px;
5191
+ font-size: 0.4375rem;
5192
+ }
5193
+ .sib-model-clickable .sib-vendor-badge svg {
5194
+ width: 0.5625rem;
5195
+ height: 0.5625rem;
5196
+ }
4814
5197
  .sib-model-clickable:hover {
4815
5198
  background: var(--color-bg-hover);
4816
5199
  opacity: 1 !important;
4817
5200
  color: var(--color-accent-primary);
4818
5201
  }
5202
+ /* Disclosure caret for the clickable status-bar fields. Those fields render as
5203
+ plain text, so the caret is the only persistent hint that they open a picker.
5204
+ Kept dim until hovered or expanded. */
5205
+ .sib-caret {
5206
+ display: inline-flex;
5207
+ align-items: center;
5208
+ border-radius: 3px;
5209
+ opacity: 0.6;
5210
+ transition: opacity 0.15s ease, transform 0.15s ease, background 0.15s ease;
5211
+ }
5212
+ /* The model caret is a click target of its own — it opens the quick-switch
5213
+ panel directly — so it gets a padded hit area pulled back with a negative
5214
+ margin to keep the bar height, and a small horizontal margin so the hover
5215
+ fill does not touch the row edge. */
5216
+ #sib-model .sib-caret {
5217
+ margin: -3px 2px -3px 0;
5218
+ padding: 3px 2px;
5219
+ }
5220
+ /* The dir/reasoning carets are hints only: clicking them must reach the row,
5221
+ and the row hover already provides the feedback. */
5222
+ #sib-dir .sib-caret,
5223
+ .sib-reasoning-clickable .sib-caret {
5224
+ pointer-events: none;
5225
+ }
5226
+ .sib-model-clickable:hover .sib-caret,
5227
+ .sib-model-clickable.is-open .sib-caret,
5228
+ #sib-dir:hover .sib-caret,
5229
+ #sib-dir.is-open .sib-caret,
5230
+ .sib-reasoning-clickable:hover .sib-caret,
5231
+ .sib-reasoning-clickable.is-open .sib-caret {
5232
+ opacity: 1;
5233
+ }
5234
+ /* Hovering the model caret needs feedback of its own: the row hover already
5235
+ paints --color-bg-hover underneath, which made an identical fill invisible.
5236
+ Accent tint mirrors .sib-submodel-toggle. */
5237
+ .sib-model-clickable .sib-caret:hover {
5238
+ background: color-mix(in srgb, var(--color-accent-primary) 16%, transparent);
5239
+ opacity: 1;
5240
+ }
5241
+ .sib-model-clickable.is-open .sib-caret,
5242
+ #sib-dir.is-open .sib-caret,
5243
+ .sib-reasoning-clickable.is-open .sib-caret {
5244
+ transform: rotate(180deg);
5245
+ }
5246
+ .sib-model-disabled .sib-caret {
5247
+ opacity: 0.3;
5248
+ }
4819
5249
  .sib-model-disabled {
4820
5250
  cursor: not-allowed !important;
4821
5251
  opacity: 0.45 !important;
@@ -4827,8 +5257,12 @@ body {
4827
5257
 
4828
5258
  /* Reasoning effort switcher — same dropdown pattern as model switcher */
4829
5259
  .sib-reasoning-clickable {
5260
+ display: inline-flex;
5261
+ align-items: center;
5262
+ gap: 0.3125rem;
4830
5263
  cursor: pointer;
4831
5264
  border-radius: 3px;
5265
+ padding: 0.2rem;
4832
5266
  transition: all 0.15s ease;
4833
5267
  font-variant-numeric: tabular-nums;
4834
5268
  }
@@ -4921,8 +5355,8 @@ body {
4921
5355
  opacity: 0.8;
4922
5356
  }
4923
5357
  .sib-model-option .model-badge.default {
4924
- background: rgba(63, 185, 80, 0.12);
4925
- color: #2da44e;
5358
+ background: var(--color-success-bg);
5359
+ color: var(--color-success);
4926
5360
  }
4927
5361
  .sib-model-option .model-badge.lite {
4928
5362
  background: color-mix(in srgb, var(--color-accent-primary) 10%, transparent);
@@ -5207,7 +5641,7 @@ body {
5207
5641
  }
5208
5642
  .sib-model-option .sib-model-name.has-sub {
5209
5643
  flex-direction: column;
5210
- align-items: flex-start;
5644
+ align-items: stretch;
5211
5645
  gap: 0.325rem;
5212
5646
  }
5213
5647
  .sib-model-option .sib-model-name-main {
@@ -5333,7 +5767,7 @@ body {
5333
5767
  flex-shrink: 0;
5334
5768
  display: flex;
5335
5769
  flex-direction: column;
5336
- padding: 0px 24px 20px;
5770
+ padding: 0px 24px 12px;
5337
5771
  }
5338
5772
 
5339
5773
  /* Hide top border when skill autocomplete is visible */
@@ -5970,6 +6404,18 @@ body {
5970
6404
  flex-shrink: 0;
5971
6405
  }
5972
6406
 
6407
+ /* A quote badge carries an excerpt, not a filename — excerpts run to paragraphs,
6408
+ so they stay on one line, same as the composer chip they were staged in. */
6409
+ .msg-quote-badge {
6410
+ max-width: 100%;
6411
+ }
6412
+ .msg-quote-badge .msg-mention-badge-name {
6413
+ max-width: 46ch;
6414
+ overflow: hidden;
6415
+ text-overflow: ellipsis;
6416
+ white-space: nowrap;
6417
+ }
6418
+
5973
6419
  #user-input {
5974
6420
  flex: 1;
5975
6421
  background: transparent !important;
@@ -5995,6 +6441,10 @@ body {
5995
6441
  content: attr(data-placeholder);
5996
6442
  color: var(--color-text-tertiary);
5997
6443
  pointer-events: none;
6444
+ display: block;
6445
+ white-space: nowrap;
6446
+ overflow: hidden;
6447
+ text-overflow: ellipsis;
5998
6448
  }
5999
6449
 
6000
6450
  #btn-send, #btn-interrupt {
@@ -6424,8 +6874,7 @@ body {
6424
6874
  border-radius: 20px;
6425
6875
  letter-spacing: 0.3px;
6426
6876
  }
6427
- .badge-default { background: rgba(63, 185, 80, 0.12); color: #2da44e; border: 1px solid rgba(63, 185, 80, 0.35); }
6428
- [data-theme="dark"] .badge-default { background: #0d3818; color: #3fb950; border: 1px solid #238636; }
6877
+ .badge-default { background: var(--color-success-bg); color: var(--color-success); border: 1px solid var(--color-success-border); }
6429
6878
  .badge-lite { background: color-mix(in srgb, var(--color-accent-primary) 10%, transparent); color: var(--color-accent-primary); border: 1px solid color-mix(in srgb, var(--color-accent-primary) 35%, transparent); }
6430
6879
  [data-theme="dark"] .badge-lite { background: #1a2f4e; color: var(--color-accent-primary); border: 1px solid var(--color-accent-primary); }
6431
6880
  .badge-secondary { background: var(--color-border-secondary); color: var(--color-text-secondary); border: 1px solid var(--color-border-primary); }
@@ -7170,6 +7619,9 @@ body {
7170
7619
  overflow: hidden;
7171
7620
  border-radius: 10px;
7172
7621
  }
7622
+ .dp-mobile-header {
7623
+ display: none;
7624
+ }
7173
7625
 
7174
7626
  /* Sidebar — quick-access places (favorites + locations) */
7175
7627
  .dp-sidebar {
@@ -7209,6 +7661,15 @@ body {
7209
7661
  .dp-sidebar-item:hover {
7210
7662
  background: var(--color-bg-hover);
7211
7663
  }
7664
+ .dp-sidebar-item.active,
7665
+ .dp-sidebar-item.active:hover {
7666
+ background: var(--color-bg-hover);
7667
+ color: var(--color-text-primary);
7668
+ font-weight: 600;
7669
+ }
7670
+ .dp-sidebar-item.active .dp-sidebar-icon {
7671
+ color: var(--color-text-primary);
7672
+ }
7212
7673
  .dp-sidebar-icon {
7213
7674
  display: inline-flex;
7214
7675
  align-items: center;
@@ -7232,6 +7693,13 @@ body {
7232
7693
  .dp-sidebar-favorite:focus-within {
7233
7694
  background: var(--color-bg-hover);
7234
7695
  }
7696
+ /* The selected favorite paints the whole row so the highlight stays one block */
7697
+ .dp-sidebar-favorite:has(.dp-sidebar-item.active) {
7698
+ background: var(--color-bg-hover);
7699
+ }
7700
+ .dp-sidebar-favorite:has(.dp-sidebar-item.active) .dp-sidebar-item.active {
7701
+ background: transparent;
7702
+ }
7235
7703
  .dp-sidebar-favorite-open {
7236
7704
  min-width: 0;
7237
7705
  padding-right: 0.25rem;
@@ -7249,7 +7717,7 @@ body {
7249
7717
  padding: 0;
7250
7718
  flex-shrink: 0;
7251
7719
  border: 0;
7252
- border-radius: 4px;
7720
+ border-radius: 6px;
7253
7721
  background: transparent;
7254
7722
  color: var(--color-text-tertiary);
7255
7723
  cursor: pointer;
@@ -7263,9 +7731,18 @@ body {
7263
7731
  pointer-events: auto;
7264
7732
  }
7265
7733
  .dp-sidebar-favorite-remove:hover {
7266
- background: var(--color-bg-active);
7734
+ background: color-mix(in srgb, var(--color-text-primary) 8%, transparent);
7267
7735
  color: var(--color-text-primary);
7268
7736
  }
7737
+ .dp-sidebar-favorite-remove:active {
7738
+ background: color-mix(in srgb, var(--color-text-primary) 14%, transparent);
7739
+ }
7740
+ .dp-sidebar-favorite-remove:focus-visible {
7741
+ opacity: 1;
7742
+ pointer-events: auto;
7743
+ outline: 2px solid var(--color-accent-primary);
7744
+ outline-offset: 1px;
7745
+ }
7269
7746
 
7270
7747
  /* Main column — toolbar + list + footer */
7271
7748
  .dp-main {
@@ -7331,7 +7808,9 @@ body {
7331
7808
  overflow-y: hidden;
7332
7809
  border: 1px solid transparent;
7333
7810
  border-radius: 5px;
7334
- background: var(--color-bg-primary);
7811
+ /* Recessed field: a touch darker than the toolbar in every theme, so it
7812
+ reads as sunken rather than as a second hover fill. */
7813
+ background: color-mix(in srgb, var(--color-bg-secondary) 96%, #000);
7335
7814
  cursor: text;
7336
7815
  transition: border-color 0.12s, background 0.12s;
7337
7816
  scrollbar-width: none; /* Firefox */
@@ -7414,7 +7893,7 @@ body {
7414
7893
  font-size: 0.8125rem;
7415
7894
  }
7416
7895
  .dp-address-input.invalid {
7417
- box-shadow: inset 0 0 0 1px var(--color-text-danger, #ef4444);
7896
+ box-shadow: inset 0 0 0 1px var(--color-error);
7418
7897
  }
7419
7898
  .dp-bc-menu {
7420
7899
  position: fixed;
@@ -7462,7 +7941,7 @@ body {
7462
7941
  font-size: 0.8125rem;
7463
7942
  }
7464
7943
  .dp-bc-menu-error {
7465
- color: var(--color-text-danger, #ef4444);
7944
+ color: var(--color-error);
7466
7945
  }
7467
7946
 
7468
7947
  /* Column header */
@@ -7564,7 +8043,7 @@ body {
7564
8043
  color: var(--color-text-tertiary);
7565
8044
  font-size: 0.8125rem;
7566
8045
  }
7567
- .dp-error { color: var(--color-text-danger, #ef4444); }
8046
+ .dp-error { color: var(--color-error); }
7568
8047
 
7569
8048
  /* Footer */
7570
8049
  .dp-footer {
@@ -7641,6 +8120,9 @@ body {
7641
8120
  z-index: 1000;
7642
8121
  }
7643
8122
  .model-dropdown-option {
8123
+ display: flex;
8124
+ align-items: center;
8125
+ gap: 0.5rem;
7644
8126
  padding: 0.5rem 0.75rem;
7645
8127
  font-size: 0.8125rem;
7646
8128
  color: var(--color-text-primary);
@@ -7648,6 +8130,11 @@ body {
7648
8130
  transition: background-color 0.15s ease;
7649
8131
  font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Courier New', monospace;
7650
8132
  }
8133
+ .model-dropdown-option-label {
8134
+ overflow: hidden;
8135
+ text-overflow: ellipsis;
8136
+ white-space: nowrap;
8137
+ }
7651
8138
  .model-dropdown-option:hover {
7652
8139
  background: var(--color-bg-hover);
7653
8140
  color: var(--color-accent-primary);
@@ -7659,6 +8146,19 @@ body {
7659
8146
  text-align: center;
7660
8147
  font-style: italic;
7661
8148
  }
8149
+ /* Provenance row pinned to the top of the model dropdown when the list came
8150
+ from a live endpoint query ("Fetched from llm.example.com/v1 · 5 models"),
8151
+ so a fetched list is never mistaken for the built-in presets. */
8152
+ .model-dropdown-status {
8153
+ position: sticky;
8154
+ top: 0;
8155
+ z-index: 1;
8156
+ padding: 0.375rem 0.75rem;
8157
+ font-size: 0.6875rem;
8158
+ color: var(--color-text-secondary);
8159
+ background: var(--color-bg-secondary);
8160
+ border-bottom: 1px solid var(--color-border-primary);
8161
+ }
7662
8162
 
7663
8163
  /* Base URL combobox — mirrors model-name-combobox shape; differs only in
7664
8164
  showing a two-line option (label + URL) so users can distinguish
@@ -7836,9 +8336,11 @@ body {
7836
8336
  .btn-primary:disabled { opacity: 0.6; cursor: default; transform: none; box-shadow: var(--shadow-xs); }
7837
8337
  .btn-secondary { background: transparent; color: var(--color-text-secondary); border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); padding: 0.5rem 1.25rem; font-weight: 500; cursor: pointer; transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast); }
7838
8338
  .btn-secondary:hover { background: var(--color-bg-hover); border-color: var(--color-border-strong); color: var(--color-text-primary); }
7839
- [data-theme="dark"] .btn-secondary:hover { background: var(--color-bg-hover); }
8339
+ .btn-secondary:disabled { opacity: 0.6; cursor: not-allowed; }
7840
8340
  .btn-danger { background: var(--color-error); color: #fff; border: none; border-radius: 0.375rem; padding: 0.5rem 1.25rem; cursor: pointer; }
7841
- .btn-danger:hover { background: var(--color-error); }
8341
+ .btn-danger:hover { background: color-mix(in srgb, var(--color-error) 85%, #fff); }
8342
+ .btn-danger:disabled { opacity: 0.6; cursor: not-allowed; }
8343
+ button:focus-visible { outline: 2px solid var(--color-accent-primary); outline-offset: 2px; }
7842
8344
 
7843
8345
  /* ── Toast stack ─────────────────────────────────────────────────────────
7844
8346
  Non-blocking notifications anchored bottom-right. Stacks vertically.
@@ -9528,7 +10030,7 @@ body {
9528
10030
  /* Inline install-error tip (replaces alert dialog) */
9529
10031
  .brand-install-error {
9530
10032
  font-size: 0.75rem;
9531
- color: var(--color-danger, #f87171);
10033
+ color: var(--color-error);
9532
10034
  margin-top: 0.375rem;
9533
10035
  line-height: 1.4;
9534
10036
  }
@@ -9821,6 +10323,35 @@ body {
9821
10323
  border-radius: 4px;
9822
10324
  }
9823
10325
 
10326
+ /* ── Skill editor modal (reuses shared .modal-box form styles) ──────────── */
10327
+ .skill-editor-box {
10328
+ max-width: 42.5rem;
10329
+ max-height: 86vh;
10330
+ display: flex;
10331
+ flex-direction: column;
10332
+ }
10333
+ .skill-editor-box .modal-body {
10334
+ overflow-y: auto;
10335
+ flex: 1;
10336
+ min-height: 0;
10337
+ }
10338
+ .skill-editor-box textarea.modal-input {
10339
+ resize: none;
10340
+ overflow: hidden;
10341
+ }
10342
+ .skill-editor-body-input {
10343
+ font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
10344
+ min-height: 12rem;
10345
+ }
10346
+ .skill-editor-status {
10347
+ font-size: 0.8125rem;
10348
+ color: var(--color-text-secondary);
10349
+ margin-right: auto;
10350
+ }
10351
+ .skill-editor-status-error {
10352
+ color: var(--color-error);
10353
+ }
10354
+
9824
10355
  /* ── Markdown split-pane layout ─────────────────────────────────────────── */
9825
10356
  .code-editor-modal--md {
9826
10357
  max-width: 1280px;
@@ -10875,9 +11406,6 @@ body.setup-mode[data-theme="dark"] {
10875
11406
  .brand-support-qr-btn:hover .brand-support-qr-zoom-hint {
10876
11407
  opacity: 1;
10877
11408
  }
10878
- .brand-support-qr-btn:focus-visible {
10879
- box-shadow: 0 0 0 2px var(--color-accent-primary);
10880
- }
10881
11409
 
10882
11410
  .brand-support-qr-img {
10883
11411
  width: 6.875rem;
@@ -11453,7 +11981,7 @@ body.setup-mode[data-theme="dark"] {
11453
11981
  white-space: nowrap;
11454
11982
  }
11455
11983
  .badge-running { background: var(--color-success-bg); color: var(--color-success); border: 1px solid var(--color-success-border); }
11456
- .badge-enabled { background: #1a2f4e; color: var(--color-accent-primary); border: 1px solid var(--color-accent-primary); }
11984
+ .badge-enabled { background: var(--color-accent-soft); color: var(--color-accent-primary); border: 1px solid color-mix(in srgb, var(--color-accent-primary) 35%, transparent); }
11457
11985
  .badge-disabled { background: var(--color-bg-tertiary); color: var(--color-text-muted); border: 1px solid var(--color-border-primary); }
11458
11986
 
11459
11987
  /* ── Channel card body ───────────────────────────────────────────────────── */
@@ -12805,10 +13333,14 @@ body.setup-mode[data-theme="dark"] {
12805
13333
  font-weight: 500;
12806
13334
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
12807
13335
  }
12808
- [data-theme="dark"] .segmented-control {
13336
+ [data-theme="dark"] .segmented-control,
13337
+ [data-theme="dim"] .segmented-control,
13338
+ [data-theme="bluedark"] .segmented-control {
12809
13339
  background: var(--color-bg-primary);
12810
13340
  }
12811
- [data-theme="dark"] .segmented-control__btn.active {
13341
+ [data-theme="dark"] .segmented-control__btn.active,
13342
+ [data-theme="dim"] .segmented-control__btn.active,
13343
+ [data-theme="bluedark"] .segmented-control__btn.active {
12812
13344
  background: var(--color-bg-hover);
12813
13345
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
12814
13346
  }
@@ -12866,7 +13398,7 @@ body.setup-mode[data-theme="dark"] {
12866
13398
  }
12867
13399
  .billing-title-row {
12868
13400
  display: flex;
12869
- align-items: baseline;
13401
+ align-items: center;
12870
13402
  flex-wrap: wrap;
12871
13403
  gap: 0.75rem;
12872
13404
  min-width: 0;
@@ -12914,8 +13446,7 @@ body.setup-mode[data-theme="dark"] {
12914
13446
 
12915
13447
  /* ── Model Filter ────────────────────────────────────────────────────── */
12916
13448
  .billing-model-filter {
12917
- width: 16rem;
12918
- flex: 0 0 16rem;
13449
+ width: 10.7rem;
12919
13450
  min-width: 0;
12920
13451
  }
12921
13452
  .billing-model-filter .custom-select-trigger {
@@ -13117,6 +13648,23 @@ body.setup-mode[data-theme="dark"] {
13117
13648
  .billing-trend-svg:hover .billing-trend-dot {
13118
13649
  opacity: 1;
13119
13650
  }
13651
+ .billing-trend-svg .billing-trend-dot.is-active {
13652
+ opacity: 1;
13653
+ transform-box: fill-box;
13654
+ transform-origin: center;
13655
+ transform: scale(1.5);
13656
+ transition: none;
13657
+ }
13658
+ .billing-trend-crosshair {
13659
+ stroke: var(--color-accent-primary);
13660
+ stroke-width: 1;
13661
+ stroke-dasharray: 3 3;
13662
+ opacity: 0;
13663
+ pointer-events: none;
13664
+ }
13665
+ .billing-trend-crosshair.is-visible {
13666
+ opacity: 0.5;
13667
+ }
13120
13668
 
13121
13669
  /* ── Chart Card base ─────────────────────────────────────────────────── */
13122
13670
  .billing-chart-row { width: 100%; }
@@ -13615,7 +14163,7 @@ body.setup-mode[data-theme="dark"] {
13615
14163
  padding: 0.5rem 0;
13616
14164
  }
13617
14165
  .mcp-tools-error {
13618
- color: var(--color-danger, #c33);
14166
+ color: var(--color-error);
13619
14167
  }
13620
14168
  .mcp-tools-header {
13621
14169
  font-size: 0.8125rem;
@@ -14710,6 +15258,13 @@ body.setup-mode[data-theme="dark"] {
14710
15258
  #share-toggle-header svg { display: block; }
14711
15259
 
14712
15260
  @media (max-width: 768px) {
15261
+ /* Soft keyboards shrink the visual viewport but leave the layout viewport
15262
+ (and 100dvh) at full height, which parks the composer behind the keyboard.
15263
+ --app-viewport-height tracks the visual viewport instead (see app.js). */
15264
+ body {
15265
+ height: var(--app-viewport-height, 100dvh);
15266
+ }
15267
+
14713
15268
  /* Sidebar becomes a fixed drawer on top of content */
14714
15269
  #sidebar {
14715
15270
  position: fixed;
@@ -14749,6 +15304,118 @@ body.setup-mode[data-theme="dark"] {
14749
15304
  overflow: visible;
14750
15305
  }
14751
15306
 
15307
+ .chat-navigator {
15308
+ display: none !important;
15309
+ }
15310
+ .has-chat-navigator > .chat-messages-scroll {
15311
+ padding-right: 1.25rem;
15312
+ }
15313
+
15314
+ /* Directory picker: full-screen layout with horizontally scrollable places. */
15315
+ .dp-overlay {
15316
+ align-items: stretch;
15317
+ padding: 0;
15318
+ }
15319
+ .dp-modal {
15320
+ width: 100%;
15321
+ max-width: none;
15322
+ height: 100vh;
15323
+ height: 100dvh;
15324
+ max-height: none;
15325
+ flex-direction: column;
15326
+ border-radius: 0;
15327
+ }
15328
+ .dp-mobile-header {
15329
+ display: flex;
15330
+ align-items: center;
15331
+ justify-content: space-between;
15332
+ gap: 0.75rem;
15333
+ min-height: 3rem;
15334
+ padding: 0.5rem 0.75rem;
15335
+ border-bottom: 1px solid var(--color-border-primary);
15336
+ background: var(--color-bg-secondary);
15337
+ flex-shrink: 0;
15338
+ }
15339
+ .dp-mobile-title {
15340
+ min-width: 0;
15341
+ overflow: hidden;
15342
+ text-overflow: ellipsis;
15343
+ white-space: nowrap;
15344
+ color: var(--color-text-primary);
15345
+ font-size: 0.9375rem;
15346
+ font-weight: 500;
15347
+ }
15348
+ .dp-mobile-close {
15349
+ flex-shrink: 0;
15350
+ }
15351
+ .dp-sidebar {
15352
+ display: flex;
15353
+ align-items: center;
15354
+ gap: 0.375rem;
15355
+ width: 100%;
15356
+ overflow-x: auto;
15357
+ overflow-y: hidden;
15358
+ padding: 0.5rem 0.75rem;
15359
+ border-right: 0;
15360
+ border-bottom: 1px solid var(--color-border-primary);
15361
+ scrollbar-width: none;
15362
+ -webkit-overflow-scrolling: touch;
15363
+ }
15364
+ .dp-sidebar::-webkit-scrollbar {
15365
+ display: none;
15366
+ }
15367
+ .dp-sidebar-label {
15368
+ display: none;
15369
+ }
15370
+ .dp-sidebar > .dp-sidebar-item,
15371
+ .dp-sidebar-favorite {
15372
+ width: auto;
15373
+ flex: 0 0 auto;
15374
+ border: 1px solid var(--color-border-primary);
15375
+ background: var(--color-bg-primary);
15376
+ }
15377
+ .dp-sidebar > .dp-sidebar-item {
15378
+ padding: 0.4rem 0.625rem;
15379
+ }
15380
+ .dp-sidebar-favorite-open {
15381
+ width: auto;
15382
+ padding: 0.4rem 0.25rem 0.4rem 0.625rem;
15383
+ border: 0;
15384
+ }
15385
+ .dp-sidebar > .dp-sidebar-item.active,
15386
+ .dp-sidebar-favorite:has(.dp-sidebar-item.active) {
15387
+ border-color: color-mix(in srgb, var(--color-text-primary) 22%, var(--color-border-primary));
15388
+ background: var(--color-bg-hover);
15389
+ }
15390
+ .dp-sidebar-favorite-remove {
15391
+ width: 28px;
15392
+ height: 28px;
15393
+ opacity: 1;
15394
+ pointer-events: auto;
15395
+ }
15396
+ .dp-main {
15397
+ width: 100%;
15398
+ }
15399
+ .dp-toolbar {
15400
+ gap: 0.25rem;
15401
+ padding: 0.5rem 0.75rem;
15402
+ }
15403
+ .dp-nav-btns {
15404
+ flex-shrink: 0;
15405
+ }
15406
+ .dp-col-header {
15407
+ padding-inline: 0.75rem;
15408
+ }
15409
+ .dp-footer {
15410
+ gap: 0.5rem;
15411
+ padding: 0.75rem;
15412
+ }
15413
+ .dp-footer-actions,
15414
+ .dp-newfolder-btn {
15415
+ flex-shrink: 0;
15416
+ white-space: nowrap;
15417
+ }
15418
+
14752
15419
  /* Tighten header padding */
14753
15420
  #top-header {
14754
15421
  padding: 0 0.75rem;
@@ -14971,6 +15638,26 @@ body.setup-mode[data-theme="dark"] {
14971
15638
  padding: 0.75rem 1rem;
14972
15639
  font-size: 0.9375rem;
14973
15640
  }
15641
+ #settings-tabs {
15642
+ flex-wrap: nowrap;
15643
+ overflow-x: auto;
15644
+ overflow-y: hidden;
15645
+ padding: 0 0.5rem;
15646
+ -webkit-overflow-scrolling: touch;
15647
+ scrollbar-width: none;
15648
+ }
15649
+ #settings-tabs::-webkit-scrollbar {
15650
+ display: none;
15651
+ }
15652
+ #settings-tabs .settings-tab {
15653
+ flex: 0 0 auto;
15654
+ padding: 0.625rem 0.875rem;
15655
+ white-space: nowrap;
15656
+ }
15657
+ #ext-slot-settings-tabs,
15658
+ #ext-slot-settings-tabs > div {
15659
+ display: contents;
15660
+ }
14974
15661
  #settings-body {
14975
15662
  padding: 1rem 1rem 5rem;
14976
15663
  gap: 1.5rem;
@@ -14989,8 +15676,35 @@ body.setup-mode[data-theme="dark"] {
14989
15676
  flex-wrap: wrap;
14990
15677
  gap: 0.5rem;
14991
15678
  }
15679
+ .settings-tab-content[data-tab-content="models"] .settings-section-title {
15680
+ align-items: stretch;
15681
+ }
15682
+ .settings-tab-content[data-tab-content="models"] .settings-section-title > span {
15683
+ width: 100%;
15684
+ }
15685
+ .models-header-actions {
15686
+ display: grid;
15687
+ grid-template-columns: minmax(0, 1fr) minmax(7rem, 7.5rem);
15688
+ align-items: center;
15689
+ flex: 1 0 100%;
15690
+ width: 100%;
15691
+ }
15692
+ .model-search-box {
15693
+ width: 100%;
15694
+ max-width: none;
15695
+ }
15696
+ .model-provider-filter {
15697
+ width: 100%;
15698
+ max-width: none;
15699
+ }
15700
+ .model-provider-filter .custom-select-dropdown {
15701
+ right: 0;
15702
+ left: auto;
15703
+ max-width: calc(100vw - 2rem);
15704
+ }
14992
15705
  /* Add Model button: full width on mobile */
14993
15706
  .btn-settings-add {
15707
+ grid-column: 1 / -1;
14994
15708
  width: 100%;
14995
15709
  justify-content: center;
14996
15710
  }
@@ -15182,20 +15896,43 @@ body.setup-mode[data-theme="dark"] {
15182
15896
  #session-aside {
15183
15897
  position: fixed;
15184
15898
  top: 0;
15899
+ left: 0;
15185
15900
  right: 0;
15186
15901
  height: 100%;
15187
15902
  z-index: 200;
15188
15903
  box-shadow: -4px 0 24px rgba(0,0,0,0.18);
15189
15904
  transition: transform 0.3s ease, background-color 0.3s ease;
15190
15905
  transform: translateX(0);
15191
- width: var(--session-aside-width);
15906
+ width: auto;
15192
15907
  }
15193
15908
  #session-aside.collapsed {
15194
15909
  transform: translateX(100%);
15195
- width: var(--session-aside-width);
15910
+ width: auto;
15196
15911
  border-left: 1px solid var(--color-border-primary);
15197
15912
  }
15198
15913
  #session-aside-resize { display: none; }
15914
+ .aside-tabbar-actions #btn-aside-fullscreen { display: none; }
15915
+
15916
+ /* The mobile workspace is a list/detail flow, not a compressed desktop split. */
15917
+ .wv-tree-resize,
15918
+ .wv-root.has-open-files .wv-tree-resize,
15919
+ .wv-toggle-tree {
15920
+ display: none;
15921
+ }
15922
+ .wv-mobile-back { display: inline-flex; }
15923
+ .wv-root.has-open-files:not(.mobile-tree-visible) .wv-tree-pane {
15924
+ display: none;
15925
+ }
15926
+ .wv-root.has-open-files.mobile-tree-visible .wv-tree-pane {
15927
+ display: flex;
15928
+ flex: 1 1 auto;
15929
+ width: 100%;
15930
+ min-width: 0;
15931
+ max-width: none;
15932
+ }
15933
+ .wv-root.has-open-files.mobile-tree-visible .wv-viewer {
15934
+ display: none;
15935
+ }
15199
15936
 
15200
15937
  #workspace-overlay {
15201
15938
  display: none;
@@ -15248,6 +15985,12 @@ body.setup-mode[data-theme="dark"] {
15248
15985
  left: 16px;
15249
15986
  right: 16px;
15250
15987
  }
15988
+
15989
+ /* 200px is wider than half the chart on a phone; let it shrink to fit. */
15990
+ .billing-chart-tooltip {
15991
+ min-width: 0;
15992
+ max-width: calc(100vw - 16px);
15993
+ }
15251
15994
  }
15252
15995
 
15253
15996
  /* ══════════════════════════════════════════════════════════════════════════════
@@ -15781,3 +16524,90 @@ body.setup-mode[data-theme="dark"] {
15781
16524
  .project-picker-done-btn:hover {
15782
16525
  background: var(--color-bg-hover);
15783
16526
  }
16527
+ /* ═══ Quote-on-selection floating button (quote-select.js) ═══
16528
+ Appears above a text selection inside a user/assistant message.
16529
+ Neutral dark pill — deliberately brand-color independent so it stays
16530
+ legible in both light and dark themes. */
16531
+ .quote-select-btn {
16532
+ position: fixed;
16533
+ z-index: 3000;
16534
+ transform: translateX(-50%);
16535
+ display: none;
16536
+ align-items: center;
16537
+ gap: 5px;
16538
+ padding: 5px 11px;
16539
+ font-size: 12px;
16540
+ font-weight: 500;
16541
+ line-height: 1.5;
16542
+ color: #fff;
16543
+ background: rgba(24, 26, 32, 0.92);
16544
+ border: 1px solid rgba(255, 255, 255, 0.14);
16545
+ border-radius: 6px;
16546
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
16547
+ cursor: pointer;
16548
+ user-select: none;
16549
+ transition: opacity var(--transition-fast, 120ms);
16550
+ }
16551
+ .quote-select-btn.visible {
16552
+ display: inline-flex;
16553
+ }
16554
+ .quote-select-btn svg {
16555
+ flex: none;
16556
+ pointer-events: none;
16557
+ }
16558
+ .quote-select-btn:hover {
16559
+ background: rgba(24, 26, 32, 1);
16560
+ }
16561
+ .quote-select-btn:active {
16562
+ background: #000;
16563
+ }
16564
+
16565
+ /* ═══ Staged quote chip (quote-select.js) ═══
16566
+ The chip borrows the mention-chip look and mechanics (see the "Mention chips
16567
+ inside the contenteditable composer" block): atomic in the composer, skipped
16568
+ by Composer.text(). It only adds the single-line truncation — a quote can be
16569
+ paragraphs long, the chip must stay one line. */
16570
+ .quote-chip {
16571
+ max-width: 100%;
16572
+ }
16573
+ .quote-chip .mention-chip-label {
16574
+ max-width: 46ch;
16575
+ overflow: hidden;
16576
+ text-overflow: ellipsis;
16577
+ white-space: nowrap;
16578
+ }
16579
+
16580
+ /* Running-task input behavior menu. */
16581
+ #input-behavior-control { position: relative; flex-shrink: 0; align-self: center; }
16582
+ #input-behavior-control[hidden], #input-behavior-menu[hidden] { display: none; }
16583
+ #input-behavior-toggle { display: grid; place-items: center; width: 34px; height: 34px; padding: 0; border: none; border-radius: 9px; background: transparent; color: var(--color-text-secondary); cursor: pointer; }
16584
+ #input-behavior-toggle:hover, #input-behavior-toggle[aria-expanded="true"] { background: var(--color-bg-hover); color: var(--color-text-primary); }
16585
+ #input-behavior-menu { position: absolute; bottom: calc(100% + 12px); right: 0; width: min(280px, calc(100vw - 48px)); padding: 6px; border: 1px solid var(--color-border-primary); border-radius: 12px; background: var(--color-bg-primary); box-shadow: 0 8px 30px rgba(0,0,0,.14); z-index: 1000; }
16586
+ .input-behavior-heading { padding: 6px 10px 8px; font-size: 11px; color: var(--color-text-tertiary); }
16587
+ #input-behavior-menu button { display: flex; gap: 9px; align-items: flex-start; width: 100%; padding: 10px 8px; border: 0; border-radius: 7px; text-align: left; font-family: inherit; background: transparent; color: var(--color-text-primary); cursor: pointer; }
16588
+ #input-behavior-menu button:hover { background: var(--color-bg-hover); }
16589
+ #input-behavior-menu button:focus-visible, #input-behavior-toggle:focus-visible { outline: 2px solid var(--color-accent-primary); outline-offset: 2px; }
16590
+ #input-behavior-menu strong { display: block; font-size: 13px; font-weight: 500; line-height: 1.5; }
16591
+ #input-behavior-menu small { display: block; margin-top: 2px; font-size: 11px; line-height: 1.5; color: var(--color-text-secondary); }
16592
+ .input-behavior-check { width: 15px; flex-shrink: 0; line-height: 20px; color: var(--color-accent-primary); visibility: hidden; }
16593
+ #input-behavior-menu [aria-checked="true"] .input-behavior-check { visibility: visible; }
16594
+ @media (max-width: 768px) { #input-behavior-menu button { min-height: 56px; } }
16595
+
16596
+ /* Pending guidance: single-line previews and always-visible actions. */
16597
+ #input-queue[hidden] { display: none; }
16598
+ #input-queue { max-height: 220px; overflow-y: auto; padding: 0 0 8px; }
16599
+ .input-queue-row { display: flex; align-items: center; gap: 8px; margin-top: 4px; padding: 6px 10px; border: 1px solid var(--color-border-primary); border-radius: 10px; background: var(--color-bg-primary); }
16600
+ .input-queue-body { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
16601
+ .input-queue-status { flex-shrink: 0; white-space: nowrap; font-size: 11px; line-height: 1.5; color: var(--color-text-tertiary); }
16602
+ .input-queue-content { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 13px; line-height: 1.6; color: var(--color-text-secondary); white-space: nowrap; }
16603
+ .input-queue-files { flex-shrink: 1; max-width: 35%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--color-text-tertiary); overflow-wrap: anywhere; }
16604
+ .input-queue-actions { display: flex; gap: 4px; flex-shrink: 0; }
16605
+ .input-queue-actions[hidden] { display: none; }
16606
+ .input-queue-actions .btn-icon-sm { width: 28px; height: 28px; }
16607
+ .input-queue-actions .input-queue-remove:hover { color: var(--color-error); background: color-mix(in srgb, var(--color-error) 8%, transparent); }
16608
+ .input-queue-row.editing { border-color: var(--color-accent-primary); }
16609
+ .input-queue-row .msg-user-edit-textarea { flex: 1; min-width: 0; height: 24px; min-height: 24px; line-height: 24px; overflow: auto; white-space: pre; }
16610
+ .input-queue-row .msg-user-edit-actions { flex-shrink: 0; margin: 0; }
16611
+ .input-queue-row.editing .input-queue-status, .input-queue-row.editing .input-queue-files { display: none; }
16612
+ .input-queue-row button:focus-visible { outline: 2px solid var(--color-accent-primary); outline-offset: 2px; }
16613
+ @media (hover: none), (pointer: coarse) { .input-queue-actions .btn-icon-sm { width: 36px; height: 36px; } }