openclacky 1.5.13 → 1.5.14

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -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 +38 -97
  6. data/lib/clacky/agent.rb +281 -276
  7. data/lib/clacky/agent_config.rb +1 -1
  8. data/lib/clacky/billing/billing_store.rb +2 -2
  9. data/lib/clacky/billing/platform_billing.rb +6 -0
  10. data/lib/clacky/brand_config.rb +13 -2
  11. data/lib/clacky/cli.rb +27 -0
  12. data/lib/clacky/client.rb +2 -2
  13. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +70 -134
  14. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +91 -5
  15. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +179 -577
  16. data/lib/clacky/default_extensions/git/panels/git/view.js +34 -14
  17. data/lib/clacky/default_extensions/preview/ext.yml +20 -0
  18. data/lib/clacky/default_extensions/preview/panels/preview/view.js +475 -0
  19. data/lib/clacky/default_extensions/time_machine/panels/time_machine/view.js +1 -10
  20. data/lib/clacky/extension/api_extension.rb +1 -1
  21. data/lib/clacky/extension/verifier.rb +1 -1
  22. data/lib/clacky/media/openai_compat.rb +70 -30
  23. data/lib/clacky/message_format/bedrock.rb +6 -1
  24. data/lib/clacky/prompts/base.md +1 -1
  25. data/lib/clacky/providers.rb +38 -3
  26. data/lib/clacky/rich_ui/rich_ui_controller.rb +1 -1
  27. data/lib/clacky/search_config.rb +3 -3
  28. data/lib/clacky/server/channel/channel_manager.rb +8 -5
  29. data/lib/clacky/server/git_panel.rb +10 -2
  30. data/lib/clacky/server/http_server.rb +161 -14
  31. data/lib/clacky/server/preview.rb +351 -0
  32. data/lib/clacky/server/web_ui_controller.rb +1 -1
  33. data/lib/clacky/skill.rb +48 -0
  34. data/lib/clacky/tools/browser.rb +176 -19
  35. data/lib/clacky/tools/web_search.rb +79 -8
  36. data/lib/clacky/ui2/components/command_suggestions.rb +2 -1
  37. data/lib/clacky/ui2/components/input_area.rb +22 -2
  38. data/lib/clacky/ui2/components/modal_component.rb +35 -6
  39. data/lib/clacky/ui2/ui_controller.rb +44 -7
  40. data/lib/clacky/utils/file_processor.rb +19 -7
  41. data/lib/clacky/utils/mac_app_detector.rb +186 -0
  42. data/lib/clacky/utils/model_pricing.rb +114 -66
  43. data/lib/clacky/utils/windows_app_detector.rb +334 -0
  44. data/lib/clacky/version.rb +1 -1
  45. data/lib/clacky/web/app.css +596 -130
  46. data/lib/clacky/web/components/chat-navigator.js +489 -202
  47. data/lib/clacky/web/components/code-editor.js +191 -5
  48. data/lib/clacky/web/components/model-picker.js +2 -1
  49. data/lib/clacky/web/components/quote-select.js +235 -0
  50. data/lib/clacky/web/core/aside.js +157 -8
  51. data/lib/clacky/web/core/ext.js +16 -0
  52. data/lib/clacky/web/features/billing/view.js +75 -21
  53. data/lib/clacky/web/features/skills/store.js +18 -1
  54. data/lib/clacky/web/features/skills/view.js +128 -7
  55. data/lib/clacky/web/features/workspace/store.js +80 -7
  56. data/lib/clacky/web/features/workspace/view.js +392 -40
  57. data/lib/clacky/web/i18n.js +90 -5
  58. data/lib/clacky/web/index.html +15 -6
  59. data/lib/clacky/web/sessions.js +193 -38
  60. data/lib/clacky/web/utils.js +34 -0
  61. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +24 -19
  62. data/lib/clacky/web/vendor/codemirror/entry.js +130 -0
  63. data/lib/clacky/web/vendor/codemirror/package.json +29 -0
  64. data/lib/clacky/web/ws-dispatcher.js +20 -0
  65. data/lib/clacky.rb +2 -0
  66. metadata +11 -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
  }
@@ -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.
@@ -4729,6 +5066,10 @@ body {
4729
5066
  border-radius: 3px;
4730
5067
  opacity: 0.75; /* tier 2 */
4731
5068
  transition: all 0.15s ease;
5069
+ max-width: 17rem;
5070
+ overflow: hidden;
5071
+ text-overflow: ellipsis;
5072
+ white-space: nowrap;
4732
5073
  }
4733
5074
  #sib-dir:hover {
4734
5075
  background: var(--color-bg-hover);
@@ -5995,6 +6336,10 @@ body {
5995
6336
  content: attr(data-placeholder);
5996
6337
  color: var(--color-text-tertiary);
5997
6338
  pointer-events: none;
6339
+ display: block;
6340
+ white-space: nowrap;
6341
+ overflow: hidden;
6342
+ text-overflow: ellipsis;
5998
6343
  }
5999
6344
 
6000
6345
  #btn-send, #btn-interrupt {
@@ -7414,7 +7759,7 @@ body {
7414
7759
  font-size: 0.8125rem;
7415
7760
  }
7416
7761
  .dp-address-input.invalid {
7417
- box-shadow: inset 0 0 0 1px var(--color-text-danger, #ef4444);
7762
+ box-shadow: inset 0 0 0 1px var(--color-error);
7418
7763
  }
7419
7764
  .dp-bc-menu {
7420
7765
  position: fixed;
@@ -7462,7 +7807,7 @@ body {
7462
7807
  font-size: 0.8125rem;
7463
7808
  }
7464
7809
  .dp-bc-menu-error {
7465
- color: var(--color-text-danger, #ef4444);
7810
+ color: var(--color-error);
7466
7811
  }
7467
7812
 
7468
7813
  /* Column header */
@@ -7564,7 +7909,7 @@ body {
7564
7909
  color: var(--color-text-tertiary);
7565
7910
  font-size: 0.8125rem;
7566
7911
  }
7567
- .dp-error { color: var(--color-text-danger, #ef4444); }
7912
+ .dp-error { color: var(--color-error); }
7568
7913
 
7569
7914
  /* Footer */
7570
7915
  .dp-footer {
@@ -7837,8 +8182,10 @@ body {
7837
8182
  .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
8183
  .btn-secondary:hover { background: var(--color-bg-hover); border-color: var(--color-border-strong); color: var(--color-text-primary); }
7839
8184
  [data-theme="dark"] .btn-secondary:hover { background: var(--color-bg-hover); }
8185
+ .btn-secondary:disabled { opacity: 0.6; cursor: not-allowed; }
7840
8186
  .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); }
8187
+ .btn-danger:hover { background: color-mix(in srgb, var(--color-error) 85%, #fff); }
8188
+ .btn-danger:disabled { opacity: 0.6; cursor: not-allowed; }
7842
8189
 
7843
8190
  /* ── Toast stack ─────────────────────────────────────────────────────────
7844
8191
  Non-blocking notifications anchored bottom-right. Stacks vertically.
@@ -9528,7 +9875,7 @@ body {
9528
9875
  /* Inline install-error tip (replaces alert dialog) */
9529
9876
  .brand-install-error {
9530
9877
  font-size: 0.75rem;
9531
- color: var(--color-danger, #f87171);
9878
+ color: var(--color-error);
9532
9879
  margin-top: 0.375rem;
9533
9880
  line-height: 1.4;
9534
9881
  }
@@ -9821,6 +10168,35 @@ body {
9821
10168
  border-radius: 4px;
9822
10169
  }
9823
10170
 
10171
+ /* ── Skill editor modal (reuses shared .modal-box form styles) ──────────── */
10172
+ .skill-editor-box {
10173
+ max-width: 42.5rem;
10174
+ max-height: 86vh;
10175
+ display: flex;
10176
+ flex-direction: column;
10177
+ }
10178
+ .skill-editor-box .modal-body {
10179
+ overflow-y: auto;
10180
+ flex: 1;
10181
+ min-height: 0;
10182
+ }
10183
+ .skill-editor-box textarea.modal-input {
10184
+ resize: none;
10185
+ overflow: hidden;
10186
+ }
10187
+ .skill-editor-body-input {
10188
+ font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
10189
+ min-height: 12rem;
10190
+ }
10191
+ .skill-editor-status {
10192
+ font-size: 0.8125rem;
10193
+ color: var(--color-text-secondary);
10194
+ margin-right: auto;
10195
+ }
10196
+ .skill-editor-status-error {
10197
+ color: var(--color-error);
10198
+ }
10199
+
9824
10200
  /* ── Markdown split-pane layout ─────────────────────────────────────────── */
9825
10201
  .code-editor-modal--md {
9826
10202
  max-width: 1280px;
@@ -13117,6 +13493,23 @@ body.setup-mode[data-theme="dark"] {
13117
13493
  .billing-trend-svg:hover .billing-trend-dot {
13118
13494
  opacity: 1;
13119
13495
  }
13496
+ .billing-trend-svg .billing-trend-dot.is-active {
13497
+ opacity: 1;
13498
+ transform-box: fill-box;
13499
+ transform-origin: center;
13500
+ transform: scale(1.5);
13501
+ transition: none;
13502
+ }
13503
+ .billing-trend-crosshair {
13504
+ stroke: var(--color-accent-primary);
13505
+ stroke-width: 1;
13506
+ stroke-dasharray: 3 3;
13507
+ opacity: 0;
13508
+ pointer-events: none;
13509
+ }
13510
+ .billing-trend-crosshair.is-visible {
13511
+ opacity: 0.5;
13512
+ }
13120
13513
 
13121
13514
  /* ── Chart Card base ─────────────────────────────────────────────────── */
13122
13515
  .billing-chart-row { width: 100%; }
@@ -13615,7 +14008,7 @@ body.setup-mode[data-theme="dark"] {
13615
14008
  padding: 0.5rem 0;
13616
14009
  }
13617
14010
  .mcp-tools-error {
13618
- color: var(--color-danger, #c33);
14011
+ color: var(--color-error);
13619
14012
  }
13620
14013
  .mcp-tools-header {
13621
14014
  font-size: 0.8125rem;
@@ -14749,6 +15142,13 @@ body.setup-mode[data-theme="dark"] {
14749
15142
  overflow: visible;
14750
15143
  }
14751
15144
 
15145
+ .chat-navigator {
15146
+ display: none !important;
15147
+ }
15148
+ .has-chat-navigator > .chat-messages-scroll {
15149
+ padding-right: 1.25rem;
15150
+ }
15151
+
14752
15152
  /* Tighten header padding */
14753
15153
  #top-header {
14754
15154
  padding: 0 0.75rem;
@@ -15182,20 +15582,43 @@ body.setup-mode[data-theme="dark"] {
15182
15582
  #session-aside {
15183
15583
  position: fixed;
15184
15584
  top: 0;
15585
+ left: 0;
15185
15586
  right: 0;
15186
15587
  height: 100%;
15187
15588
  z-index: 200;
15188
15589
  box-shadow: -4px 0 24px rgba(0,0,0,0.18);
15189
15590
  transition: transform 0.3s ease, background-color 0.3s ease;
15190
15591
  transform: translateX(0);
15191
- width: var(--session-aside-width);
15592
+ width: auto;
15192
15593
  }
15193
15594
  #session-aside.collapsed {
15194
15595
  transform: translateX(100%);
15195
- width: var(--session-aside-width);
15596
+ width: auto;
15196
15597
  border-left: 1px solid var(--color-border-primary);
15197
15598
  }
15198
15599
  #session-aside-resize { display: none; }
15600
+ .aside-tabbar-actions #btn-aside-fullscreen { display: none; }
15601
+
15602
+ /* The mobile workspace is a list/detail flow, not a compressed desktop split. */
15603
+ .wv-tree-resize,
15604
+ .wv-root.has-open-files .wv-tree-resize,
15605
+ .wv-toggle-tree {
15606
+ display: none;
15607
+ }
15608
+ .wv-mobile-back { display: inline-flex; }
15609
+ .wv-root.has-open-files:not(.mobile-tree-visible) .wv-tree-pane {
15610
+ display: none;
15611
+ }
15612
+ .wv-root.has-open-files.mobile-tree-visible .wv-tree-pane {
15613
+ display: flex;
15614
+ flex: 1 1 auto;
15615
+ width: 100%;
15616
+ min-width: 0;
15617
+ max-width: none;
15618
+ }
15619
+ .wv-root.has-open-files.mobile-tree-visible .wv-viewer {
15620
+ display: none;
15621
+ }
15199
15622
 
15200
15623
  #workspace-overlay {
15201
15624
  display: none;
@@ -15248,6 +15671,12 @@ body.setup-mode[data-theme="dark"] {
15248
15671
  left: 16px;
15249
15672
  right: 16px;
15250
15673
  }
15674
+
15675
+ /* 200px is wider than half the chart on a phone; let it shrink to fit. */
15676
+ .billing-chart-tooltip {
15677
+ min-width: 0;
15678
+ max-width: calc(100vw - 16px);
15679
+ }
15251
15680
  }
15252
15681
 
15253
15682
  /* ══════════════════════════════════════════════════════════════════════════════
@@ -15781,3 +16210,40 @@ body.setup-mode[data-theme="dark"] {
15781
16210
  .project-picker-done-btn:hover {
15782
16211
  background: var(--color-bg-hover);
15783
16212
  }
16213
+ /* ═══ Quote-on-selection floating button (quote-select.js) ═══
16214
+ Appears above a text selection inside a user/assistant message.
16215
+ Neutral dark pill — deliberately brand-color independent so it stays
16216
+ legible in both light and dark themes. */
16217
+ .quote-select-btn {
16218
+ position: fixed;
16219
+ z-index: 3000;
16220
+ transform: translateX(-50%);
16221
+ display: none;
16222
+ align-items: center;
16223
+ gap: 5px;
16224
+ padding: 5px 11px;
16225
+ font-size: 12px;
16226
+ font-weight: 500;
16227
+ line-height: 1.5;
16228
+ color: #fff;
16229
+ background: rgba(24, 26, 32, 0.92);
16230
+ border: 1px solid rgba(255, 255, 255, 0.14);
16231
+ border-radius: 6px;
16232
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
16233
+ cursor: pointer;
16234
+ user-select: none;
16235
+ transition: opacity var(--transition-fast, 120ms);
16236
+ }
16237
+ .quote-select-btn.visible {
16238
+ display: inline-flex;
16239
+ }
16240
+ .quote-select-btn svg {
16241
+ flex: none;
16242
+ pointer-events: none;
16243
+ }
16244
+ .quote-select-btn:hover {
16245
+ background: rgba(24, 26, 32, 1);
16246
+ }
16247
+ .quote-select-btn:active {
16248
+ background: #000;
16249
+ }