openclacky 1.3.2 → 1.3.4

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/Dockerfile +3 -0
  4. data/README.md +1 -1
  5. data/README_JA.md +237 -0
  6. data/docs/rich_ui_guide.md +277 -0
  7. data/docs/rich_ui_refactor_plan.md +396 -0
  8. data/lib/clacky/agent/llm_caller.rb +10 -4
  9. data/lib/clacky/agent/session_serializer.rb +52 -7
  10. data/lib/clacky/agent/time_machine.rb +247 -26
  11. data/lib/clacky/agent.rb +15 -3
  12. data/lib/clacky/api_extension.rb +262 -0
  13. data/lib/clacky/api_extension_loader.rb +156 -0
  14. data/lib/clacky/cli.rb +93 -3
  15. data/lib/clacky/client.rb +38 -13
  16. data/lib/clacky/default_agents/_panels/git/panel.js +201 -0
  17. data/lib/clacky/default_agents/_panels/time_machine/panel.js +640 -0
  18. data/lib/clacky/default_agents/coding/profile.yml +3 -0
  19. data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
  20. data/lib/clacky/default_skills/cron-task-creator/SKILL.md +1 -1
  21. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +6 -4
  22. data/lib/clacky/default_skills/media-gen/SKILL.md +37 -10
  23. data/lib/clacky/idle_compression_timer.rb +3 -1
  24. data/lib/clacky/locales/en.rb +26 -0
  25. data/lib/clacky/locales/i18n.rb +26 -0
  26. data/lib/clacky/locales/zh.rb +26 -0
  27. data/lib/clacky/media/openai_compat.rb +64 -1
  28. data/lib/clacky/message_history.rb +9 -0
  29. data/lib/clacky/rich_ui/components/base_component.rb +50 -0
  30. data/lib/clacky/rich_ui/components/dialogs/approval_dialog.rb +142 -0
  31. data/lib/clacky/rich_ui/components/dialogs/config_menu_dialog.rb +106 -0
  32. data/lib/clacky/rich_ui/components/dialogs/form_dialog.rb +128 -0
  33. data/lib/clacky/rich_ui/components/sidebar.rb +119 -0
  34. data/lib/clacky/rich_ui/components/sidebar_panels.rb +134 -0
  35. data/lib/clacky/rich_ui/components/status_view.rb +58 -0
  36. data/lib/clacky/rich_ui/components/thinking_live_view.rb +79 -0
  37. data/lib/clacky/rich_ui/entry_tracker.rb +56 -0
  38. data/lib/clacky/rich_ui/layout_adapter.rb +16 -0
  39. data/lib/clacky/rich_ui/progress_handle_adapter.rb +24 -0
  40. data/lib/clacky/rich_ui/rich_ui_controller.rb +868 -0
  41. data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +184 -0
  42. data/lib/clacky/rich_ui/view_renderer.rb +291 -0
  43. data/lib/clacky/rich_ui.rb +57 -0
  44. data/lib/clacky/rich_ui_controller.rb +3 -1549
  45. data/lib/clacky/server/api_extension_dispatcher.rb +120 -0
  46. data/lib/clacky/server/channel/channel_manager.rb +26 -0
  47. data/lib/clacky/server/git_panel.rb +115 -0
  48. data/lib/clacky/server/http_server.rb +547 -15
  49. data/lib/clacky/server/server_master.rb +6 -4
  50. data/lib/clacky/server/session_registry.rb +1 -1
  51. data/lib/clacky/shell_hook_loader.rb +1 -1
  52. data/lib/clacky/tools/edit.rb +14 -2
  53. data/lib/clacky/ui2/ui_controller.rb +7 -0
  54. data/lib/clacky/version.rb +1 -1
  55. data/lib/clacky/web/app.css +511 -101
  56. data/lib/clacky/web/app.js +95 -14
  57. data/lib/clacky/web/components/code-editor.js +197 -0
  58. data/lib/clacky/web/{notify.js → components/notify.js} +1 -1
  59. data/lib/clacky/web/{onboard.js → components/onboard.js} +18 -2
  60. data/lib/clacky/web/core/aside.js +117 -0
  61. data/lib/clacky/web/core/ext.js +387 -0
  62. data/lib/clacky/web/features/backup/store.js +92 -0
  63. data/lib/clacky/web/features/backup/view.js +94 -0
  64. data/lib/clacky/web/features/billing/store.js +163 -0
  65. data/lib/clacky/web/{billing.js → features/billing/view.js} +132 -240
  66. data/lib/clacky/web/features/brand/store.js +110 -0
  67. data/lib/clacky/web/{brand.js → features/brand/view.js} +49 -199
  68. data/lib/clacky/web/features/channels/store.js +103 -0
  69. data/lib/clacky/web/{channels.js → features/channels/view.js} +50 -127
  70. data/lib/clacky/web/features/creator/store.js +81 -0
  71. data/lib/clacky/web/{creator.js → features/creator/view.js} +53 -102
  72. data/lib/clacky/web/features/mcp/store.js +158 -0
  73. data/lib/clacky/web/{mcp.js → features/mcp/view.js} +57 -134
  74. data/lib/clacky/web/features/model-tester/store.js +77 -0
  75. data/lib/clacky/web/features/model-tester/view.js +7 -0
  76. data/lib/clacky/web/features/profile/store.js +170 -0
  77. data/lib/clacky/web/{profile.js → features/profile/view.js} +94 -144
  78. data/lib/clacky/web/features/share/store.js +145 -0
  79. data/lib/clacky/web/{share.js → features/share/view.js} +66 -202
  80. data/lib/clacky/web/features/skills/store.js +331 -0
  81. data/lib/clacky/web/features/skills/view.js +581 -0
  82. data/lib/clacky/web/features/tasks/store.js +135 -0
  83. data/lib/clacky/web/features/tasks/view.js +241 -0
  84. data/lib/clacky/web/features/trash/store.js +242 -0
  85. data/lib/clacky/web/{trash.js → features/trash/view.js} +102 -293
  86. data/lib/clacky/web/features/version/store.js +165 -0
  87. data/lib/clacky/web/features/version/view.js +323 -0
  88. data/lib/clacky/web/features/workspace/store.js +99 -0
  89. data/lib/clacky/web/features/workspace/view.js +305 -0
  90. data/lib/clacky/web/i18n.js +68 -6
  91. data/lib/clacky/web/index.html +113 -62
  92. data/lib/clacky/web/sessions.js +493 -39
  93. data/lib/clacky/web/settings.js +143 -49
  94. data/lib/clacky/web/skills.js +3 -863
  95. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +29 -0
  96. data/lib/clacky/web/ws-dispatcher.js +7 -3
  97. data/lib/clacky.rb +17 -1
  98. metadata +81 -20
  99. data/lib/clacky/web/backup.js +0 -119
  100. data/lib/clacky/web/model-tester.js +0 -66
  101. data/lib/clacky/web/tasks.js +0 -373
  102. data/lib/clacky/web/version.js +0 -449
  103. data/lib/clacky/web/workspace.js +0 -316
  104. /data/lib/clacky/web/{notify.mp3 → assets/notify.mp3} +0 -0
  105. /data/lib/clacky/web/{datepicker.js → components/datepicker.js} +0 -0
  106. /data/lib/clacky/web/{sidebar.js → components/sidebar.js} +0 -0
  107. /data/lib/clacky/web/{marked.min.js → vendor/marked/marked.min.js} +0 -0
@@ -526,7 +526,7 @@ body {
526
526
  /* The sidebar sits on the outer frame (bg-primary) so it's visibly a layer
527
527
  BEHIND the chat surface (bg-secondary). Subtle right border separates them. */
528
528
  #sidebar {
529
- --sidebar-width: 15rem;
529
+ --sidebar-width: 16rem;
530
530
  width: var(--sidebar-width);
531
531
  min-width: var(--sidebar-width);
532
532
  background: var(--color-bg-primary);
@@ -1929,63 +1929,175 @@ body {
1929
1929
  #chat-panel { flex: 1; display: flex; flex-direction: row; overflow: hidden; position: relative; }
1930
1930
  #chat-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; min-width: 0; }
1931
1931
 
1932
- /* ── Workspace panel (right file browser) ───────────────────────────────── */
1933
- #workspace-panel {
1934
- --workspace-width: 280px;
1935
- width: var(--workspace-width);
1932
+ /* ── Session extension slots (agent-scoped panels mount here) ───────────────
1933
+ Each slot is laid out by the host; an empty slot collapses to zero so it
1934
+ never reserves space when no panel is mounted for the current agent. */
1935
+ /* ── Session aside — one resizable / collapsible right column with tabs ─────
1936
+ The host owns the chrome (resize handle, collapse button, floating opener);
1937
+ Clacky.ext renders the tab bar + bodies into #ext-slot-session-aside. When
1938
+ the slot has no panels for the current agent it stays empty and the whole
1939
+ column collapses to zero width. */
1940
+ #session-aside {
1941
+ --session-aside-width: 16rem;
1942
+ width: var(--session-aside-width);
1936
1943
  flex-shrink: 0;
1944
+ position: relative;
1937
1945
  display: flex;
1938
1946
  flex-direction: column;
1939
- border-left: 1px solid var(--color-border-primary);
1940
- background: var(--color-bg-primary);
1941
1947
  overflow: hidden;
1948
+ border-left: 1px solid var(--color-border-primary);
1949
+ background: var(--color-bg-secondary);
1942
1950
  transition: width var(--transition-base);
1943
- position: relative;
1944
1951
  }
1945
- #workspace-panel.collapsed { width: 0; border-left: none; }
1946
- #workspace-resize-handle {
1952
+ #session-aside.collapsed { width: 0; border-left: none; }
1953
+ /* Empty slot (no panels for this agent) → collapse the whole column. */
1954
+ #session-aside:has(.session-aside-slot:empty) { width: 0; border-left: none; }
1955
+ #session-aside:has(.session-aside-slot:empty) .session-aside-resize,
1956
+ #session-aside:has(.session-aside-slot:empty) .session-aside-collapse { display: none; }
1957
+
1958
+ .session-aside-resize {
1947
1959
  position: absolute;
1948
1960
  top: 0;
1949
- left: -3px;
1961
+ left: -6px;
1950
1962
  bottom: 0;
1951
- width: 6px;
1963
+ width: 12px;
1952
1964
  cursor: col-resize;
1953
1965
  z-index: 10;
1966
+ background: transparent;
1954
1967
  }
1955
- #workspace-panel:has(#workspace-resize-handle.active) {
1968
+ .session-aside-resize::after {
1969
+ content: "";
1970
+ position: absolute;
1971
+ top: 0;
1972
+ bottom: 0;
1973
+ left: 6px;
1974
+ width: 1px;
1975
+ background: transparent;
1956
1976
  transition: background-color var(--transition-base);
1957
1977
  }
1958
- #workspace-header {
1959
- display: flex;
1978
+ .session-aside-resize:hover::after,
1979
+ .session-aside-resize.active::after { background: transparent; }
1980
+
1981
+ .session-aside-collapse {
1982
+ position: absolute;
1983
+ top: 7px;
1984
+ right: 8px;
1985
+ z-index: 11;
1986
+ display: inline-flex;
1960
1987
  align-items: center;
1961
- justify-content: space-between;
1962
- height: 2.5rem;
1963
- min-height: 2.5rem;
1964
- padding: 0 0.5rem 0 0.75rem;
1988
+ justify-content: center;
1989
+ width: 1.625rem;
1990
+ height: 1.625rem;
1991
+ border: none;
1992
+ background: transparent;
1993
+ color: var(--color-text-tertiary);
1994
+ border-radius: var(--radius-sm);
1995
+ cursor: pointer;
1996
+ transition: background-color var(--transition-base), color var(--transition-base);
1997
+ }
1998
+ .session-aside-collapse:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
1999
+
2000
+ .session-aside-slot { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
2001
+
2002
+ /* Tab bar (rendered by Clacky.ext). Leaves room on the right for the collapse
2003
+ button which is absolutely positioned over it. */
2004
+ .aside-tabs {
2005
+ flex: none;
2006
+ display: flex;
2007
+ align-items: stretch;
2008
+ height: 40px;
2009
+ padding: 0 38px 0 4px;
2010
+ gap: 2px;
1965
2011
  border-bottom: 1px solid var(--color-border-primary);
1966
- font-size: 0.8125rem;
1967
- font-weight: 600;
1968
- color: var(--color-text-primary);
2012
+ overflow-x: auto;
2013
+ scrollbar-width: none;
1969
2014
  }
1970
- .workspace-header-actions { display: flex; gap: 0.125rem; }
1971
- .workspace-icon-btn {
2015
+ .aside-tabs::-webkit-scrollbar { display: none; }
2016
+ .aside-tab {
1972
2017
  display: inline-flex;
1973
2018
  align-items: center;
1974
- justify-content: center;
1975
- width: 1.75rem;
1976
- height: 1.75rem;
2019
+ gap: 6px;
2020
+ padding: 0 12px;
2021
+ white-space: nowrap;
2022
+ cursor: pointer;
1977
2023
  border: none;
1978
- background: transparent;
2024
+ background: none;
2025
+ font-size: 12.5px;
2026
+ color: var(--color-text-tertiary);
2027
+ border-bottom: 2px solid transparent;
2028
+ }
2029
+ .aside-tab:hover { color: var(--color-text-secondary); }
2030
+ .aside-tab.active { color: var(--color-text-primary); border-bottom-color: var(--color-accent-primary); font-weight: 600; }
2031
+ .aside-tab-badge {
2032
+ font-size: 10px;
2033
+ min-width: 16px;
2034
+ height: 16px;
2035
+ padding: 0 4px;
2036
+ border-radius: var(--radius-pill);
2037
+ background: var(--color-accent-primary);
2038
+ color: var(--color-text-inverse);
2039
+ display: inline-flex;
2040
+ align-items: center;
2041
+ justify-content: center;
2042
+ }
2043
+
2044
+ .aside-bodies { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
2045
+ .aside-panel { flex: 1; min-height: 0; overflow: auto; display: none; flex-direction: column; }
2046
+ .aside-panel.active { display: flex; }
2047
+
2048
+ /* Floating opener shown when the column is collapsed. */
2049
+ .session-aside-opener {
2050
+ position: absolute;
2051
+ top: 0.5rem;
2052
+ right: 0.5rem;
2053
+ z-index: 8;
2054
+ display: inline-flex;
2055
+ align-items: center;
2056
+ justify-content: center;
2057
+ width: 2rem;
2058
+ height: 2rem;
2059
+ border: 1px solid var(--color-border-primary);
2060
+ background: var(--color-bg-secondary);
1979
2061
  color: var(--color-text-secondary);
1980
2062
  border-radius: var(--radius-sm);
1981
2063
  cursor: pointer;
2064
+ box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,0.06));
1982
2065
  transition: background-color var(--transition-base), color var(--transition-base);
1983
2066
  }
1984
- .workspace-icon-btn:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
1985
- #workspace-tree {
2067
+ .session-aside-opener:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
2068
+
2069
+ .session-banner:empty,
2070
+ .session-composer:empty { display: none; }
2071
+ .session-banner { flex-shrink: 0; }
2072
+ .session-composer { flex-shrink: 0; }
2073
+
2074
+ /* ── File tree (used inside the Files tab) ──────────────────────────────── */
2075
+ .wt-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }
2076
+ .wt-bar {
2077
+ flex: none;
2078
+ display: flex;
2079
+ align-items: center;
2080
+ justify-content: space-between;
2081
+ gap: 8px;
2082
+ padding: 8px 12px;
2083
+ border-bottom: 1px solid var(--color-border-secondary);
2084
+ }
2085
+ .wt-bar-hint { font-size: 11px; color: var(--color-text-tertiary); }
2086
+ .wt-bar-btn {
2087
+ flex: none;
2088
+ padding: 3px 10px;
2089
+ border: 1px solid var(--color-border-primary);
2090
+ border-radius: var(--radius-sm);
2091
+ background: var(--color-bg-secondary);
2092
+ color: var(--color-text-secondary);
2093
+ font-size: 11px;
2094
+ cursor: pointer;
2095
+ }
2096
+ .wt-bar-btn:hover { background: var(--color-bg-hover); }
2097
+ .wt-tree {
1986
2098
  flex: 1;
1987
2099
  overflow-y: auto;
1988
- padding: 0.375rem 0.25rem;
2100
+ padding: 6px 8px;
1989
2101
  font-size: 0.8125rem;
1990
2102
  font-family: var(--font-mono);
1991
2103
  }
@@ -2022,26 +2134,6 @@ body {
2022
2134
  }
2023
2135
  .wt-error { color: var(--color-danger, #d23); }
2024
2136
 
2025
- /* Collapsed-state opener tab — floats at the right edge of the chat area */
2026
- #btn-workspace-open {
2027
- position: absolute;
2028
- top: 0.5rem;
2029
- right: 0.5rem;
2030
- z-index: 8;
2031
- display: inline-flex;
2032
- align-items: center;
2033
- justify-content: center;
2034
- width: 2rem;
2035
- height: 2rem;
2036
- border: 1px solid var(--color-border-primary);
2037
- background: var(--color-bg-secondary);
2038
- color: var(--color-text-secondary);
2039
- border-radius: var(--radius-sm);
2040
- cursor: pointer;
2041
- transition: background-color var(--transition-base), color var(--transition-base);
2042
- }
2043
- #btn-workspace-open:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
2044
-
2045
2137
  /* Mobile-only floating back button — replaces the old in-header back button.
2046
2138
  Hidden on desktop; mobile media query enables it. Positioned absolutely so
2047
2139
  it doesn't take layout space or add visual chrome on desktop. */
@@ -2179,19 +2271,143 @@ body {
2179
2271
  /* Sit above the next message in case text would otherwise clip beneath it. */
2180
2272
  z-index: 1;
2181
2273
  }
2182
- .msg:hover .msg-time {
2274
+ .msg:hover .msg-time,
2275
+ .msg-user-wrap:hover .msg-time {
2183
2276
  opacity: 1;
2184
2277
  transform: translateY(0);
2185
2278
  }
2186
2279
  /* Time color / alignment: anchor to the bubble's own side, let width be
2187
2280
  driven by content — prevents overflow on narrow bubbles. */
2188
- .msg-user .msg-time { color: var(--color-text-secondary); right: 0; left: auto; padding-right: 0.25rem; }
2281
+ /* User timestamp: appended to .msg-user-wrap (not bubble), sits below action
2282
+ buttons, right-aligned. */
2283
+ .msg-user-wrap > .msg-time { color: var(--color-text-secondary); top: auto; bottom: 0.2rem; right: 0; left: auto; padding-right: 0.25rem; }
2189
2284
  .msg-assistant .msg-time { color: var(--color-text-secondary); left: 0; right: auto; padding-left: 0.25rem; }
2190
2285
 
2191
- .msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); align-self: flex-end; white-space: pre-wrap; }
2286
+ .msg-user { background: var(--color-accent-soft); color: var(--color-text-primary); white-space: pre-wrap; }
2192
2287
  [data-theme="dark"] .msg-user { background: var(--color-accent-soft); }
2193
2288
  .msg-assistant { background: var(--color-bg-tertiary); border: 1px solid var(--color-border-primary); align-self: flex-start; }
2194
2289
 
2290
+ /* ── User message action bar (copy + edit) ─────────────────────────────── */
2291
+ .msg-user-wrap {
2292
+ position: relative;
2293
+ display: flex;
2294
+ justify-content: flex-end;
2295
+ padding-bottom: 3rem;
2296
+ }
2297
+ .msg-user-actions {
2298
+ position: absolute;
2299
+ bottom: 1.1rem;
2300
+ right: 0;
2301
+ display: flex;
2302
+ gap: 0.125rem;
2303
+ opacity: 0;
2304
+ transition: opacity 0.15s ease;
2305
+ pointer-events: none;
2306
+ z-index: 3;
2307
+ }
2308
+ .msg-user-wrap:hover .msg-user-actions { opacity: 1; pointer-events: auto; }
2309
+ .msg-user-wrap:has(.msg-user.editing) { padding-bottom: 0; }
2310
+ .msg-user-wrap:has(.msg-user.editing) .msg-user-actions { display: none; }
2311
+ .msg-user-wrap:has(.msg-user.editing) .msg-time { display: none; }
2312
+
2313
+ .msg-user-action-btn {
2314
+ display: inline-flex;
2315
+ align-items: center;
2316
+ justify-content: center;
2317
+ width: 1.625rem;
2318
+ height: 1.625rem;
2319
+ padding: 0;
2320
+ border: 1px solid var(--color-border-primary);
2321
+ border-radius: 5px;
2322
+ background: var(--color-bg-primary);
2323
+ color: var(--color-text-secondary);
2324
+ cursor: pointer;
2325
+ transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
2326
+ }
2327
+ .msg-user-action-btn:hover {
2328
+ color: var(--color-text-primary);
2329
+ background: var(--color-bg-tertiary);
2330
+ }
2331
+ .msg-user-action-btn:active { transform: translateY(1px); }
2332
+ .msg-user-action-btn.is-copied {
2333
+ color: var(--color-success);
2334
+ border-color: var(--color-success);
2335
+ }
2336
+ .msg-user-copy-icon-check { display: none; }
2337
+ .msg-user-action-btn.is-copied .msg-user-copy-icon { display: none; }
2338
+ .msg-user-action-btn.is-copied .msg-user-copy-icon-check { display: inline; }
2339
+
2340
+ /* Keep the buttons reachable without hover on touch / narrow viewports. */
2341
+ @media (hover: none) {
2342
+ .msg-user-actions { opacity: 0.65; pointer-events: auto; }
2343
+ }
2344
+
2345
+ /* ── Edit mode ─────────────────────────────────────────────────────────── */
2346
+ .msg-user.editing {
2347
+ background: var(--color-bg-primary);
2348
+ padding: 0.625rem 0.75rem 0.5rem;
2349
+ overflow: visible;
2350
+ border-radius: 8px;
2351
+ border: 1px solid var(--color-border-primary);
2352
+ max-width: min(42rem, 90%);
2353
+ width: min(42rem, 90%);
2354
+ }
2355
+ .msg-user-edit-wrap {
2356
+ display: flex;
2357
+ flex-direction: column;
2358
+ gap: 0.5rem;
2359
+ width: 100%;
2360
+ }
2361
+ .msg-user-edit-textarea {
2362
+ width: 100%;
2363
+ min-height: 1.5rem;
2364
+ padding: 0;
2365
+ border: none;
2366
+ border-radius: 0;
2367
+ background: transparent;
2368
+ color: var(--color-text-primary);
2369
+ font-family: inherit;
2370
+ font-size: 0.875rem;
2371
+ line-height: 1.6;
2372
+ resize: none;
2373
+ overflow: hidden;
2374
+ outline: none;
2375
+ white-space: pre-wrap;
2376
+ }
2377
+ .msg-user-edit-actions {
2378
+ display: flex;
2379
+ justify-content: flex-end;
2380
+ gap: 0.375rem;
2381
+ }
2382
+ .msg-user-edit-cancel {
2383
+ padding: 0.25rem 0.625rem;
2384
+ border: 1px solid var(--color-border-primary);
2385
+ border-radius: 5px;
2386
+ background: transparent;
2387
+ color: var(--color-text-secondary);
2388
+ font-size: 0.75rem;
2389
+ font-weight: 500;
2390
+ cursor: pointer;
2391
+ transition: background 0.15s ease, color 0.15s ease;
2392
+ }
2393
+ .msg-user-edit-cancel:hover {
2394
+ background: var(--color-bg-hover);
2395
+ color: var(--color-text-primary);
2396
+ }
2397
+ .msg-user-edit-send {
2398
+ padding: 0.25rem 0.75rem;
2399
+ border: none;
2400
+ border-radius: 5px;
2401
+ background: var(--color-button-primary);
2402
+ color: var(--color-button-primary-text);
2403
+ font-size: 0.75rem;
2404
+ font-weight: 500;
2405
+ cursor: pointer;
2406
+ transition: opacity 0.15s ease;
2407
+ }
2408
+ .msg-user-edit-send:hover { opacity: 0.88; }
2409
+ .msg-user-edit-send:active { opacity: 0.76; }
2410
+
2195
2411
  /* ── Copy button on assistant messages ────────────────────────────────────
2196
2412
  Hidden by default, revealed on bubble hover (same pattern as .msg-time).
2197
2413
  On touch devices (hover: none) we keep the button visible at low opacity
@@ -2225,8 +2441,8 @@ body {
2225
2441
  }
2226
2442
  .msg-copy-btn.is-copied {
2227
2443
  opacity: 1;
2228
- color: #10b981; /* success green — same across themes */
2229
- border-color: #10b981;
2444
+ color: var(--color-success);
2445
+ border-color: var(--color-success);
2230
2446
  background: var(--color-bg-primary);
2231
2447
  }
2232
2448
  .msg-copy-btn .msg-copy-icon,
@@ -2838,7 +3054,7 @@ body {
2838
3054
  display: flex;
2839
3055
  align-items: center;
2840
3056
  gap: 0.375rem;
2841
- flex-wrap: wrap;
3057
+ flex-wrap: nowrap;
2842
3058
  padding: 0.375rem 0.75rem;
2843
3059
  margin-left: 1.5rem;
2844
3060
  margin-right: 1.5rem;
@@ -2848,13 +3064,17 @@ body {
2848
3064
  color: var(--color-text-tertiary);
2849
3065
  font-family: var(--font-mono, monospace);
2850
3066
  white-space: nowrap;
2851
- overflow: hidden;
3067
+ overflow-x: auto;
3068
+ overflow-y: hidden;
3069
+ scrollbar-width: none;
2852
3070
  flex-shrink: 0;
2853
3071
  opacity: 0.95;
2854
3072
  transition: opacity 0.15s ease;
2855
3073
  cursor: default;
2856
3074
  }
2857
3075
 
3076
+ #session-info-bar::-webkit-scrollbar { display: none; }
3077
+
2858
3078
  #session-info-bar .sib-sep {
2859
3079
  opacity: 0.3;
2860
3080
  }
@@ -4097,7 +4317,6 @@ body {
4097
4317
  padding: 2rem 2rem 1.5rem;
4098
4318
  display: flex;
4099
4319
  flex-direction: column;
4100
- gap: 2rem;
4101
4320
  }
4102
4321
  .settings-section {
4103
4322
  display: flex;
@@ -4182,6 +4401,15 @@ body {
4182
4401
  }
4183
4402
 
4184
4403
  /* ── Backup section ── */
4404
+ .backup-auto-card {
4405
+ border: 1px solid var(--color-border-primary);
4406
+ border-radius: 10px;
4407
+ padding: 0.625rem 0.875rem;
4408
+ background: var(--color-bg-secondary);
4409
+ }
4410
+ .backup-auto-card .backup-auto-row {
4411
+ margin: 0.25rem 0;
4412
+ }
4185
4413
  .backup-option,
4186
4414
  .backup-auto-label {
4187
4415
  font-size: 0.8125rem;
@@ -4193,6 +4421,11 @@ body {
4193
4421
  .backup-option {
4194
4422
  margin: 0.75rem 0;
4195
4423
  }
4424
+ .backup-option-nested {
4425
+ margin: 0.5rem 0 0.25rem 2.75rem;
4426
+ font-size: 0.78125rem;
4427
+ color: var(--color-text-secondary);
4428
+ }
4196
4429
  .backup-auto-row {
4197
4430
  display: flex;
4198
4431
  align-items: center;
@@ -4208,7 +4441,6 @@ body {
4208
4441
  display: flex;
4209
4442
  align-items: center;
4210
4443
  gap: 0.75rem;
4211
- margin: 0.75rem 0;
4212
4444
  flex-wrap: wrap;
4213
4445
  }
4214
4446
 
@@ -4355,7 +4587,6 @@ body {
4355
4587
  font-size: 0.8125rem;
4356
4588
  color: var(--color-text-primary);
4357
4589
  cursor: pointer;
4358
- margin-top: 0.5rem;
4359
4590
  }
4360
4591
  .model-field-checkbox input[type="checkbox"] {
4361
4592
  width: 15px;
@@ -4552,7 +4783,7 @@ body {
4552
4783
  /* ── Model Cards Grid Container ─────────────────────────────────────────────── */
4553
4784
  #model-cards {
4554
4785
  display: grid;
4555
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
4786
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
4556
4787
  gap: 1rem;
4557
4788
  }
4558
4789
 
@@ -4609,7 +4840,6 @@ body {
4609
4840
  color: var(--color-text-secondary);
4610
4841
  font-family: var(--font-mono, monospace);
4611
4842
  background: var(--color-bg-tertiary);
4612
- padding: 0.125rem 0.375rem;
4613
4843
  border-radius: 4px;
4614
4844
  display: inline-block;
4615
4845
  max-width: 100%;
@@ -6471,24 +6701,14 @@ body {
6471
6701
  white-space: nowrap;
6472
6702
  }
6473
6703
  .brand-skill-version.installed {
6474
- background: #1f3a5f;
6475
- color: #7eb8f7;
6476
- border: 1px solid #1d4070;
6704
+ background: color-mix(in srgb, var(--color-accent-primary) 15%, transparent);
6705
+ color: var(--color-accent-primary);
6706
+ border: 1px solid color-mix(in srgb, var(--color-accent-primary) 35%, transparent);
6477
6707
  }
6478
6708
  .brand-skill-version.latest {
6479
- background: #1f4e9e;
6480
- color: #bfdbfe;
6481
- border: 1px solid #1d4070;
6482
- }
6483
- [data-theme="light"] .brand-skill-version.installed {
6484
- background: #eff6ff;
6485
- color: #1d4ed8;
6486
- border: 1px solid #bfdbfe;
6487
- }
6488
- [data-theme="light"] .brand-skill-version.latest {
6489
- background: #1d4ed8;
6709
+ background: var(--color-accent-primary);
6490
6710
  color: #ffffff;
6491
- border: 1px solid #1e40af;
6711
+ border: 1px solid var(--color-accent-primary);
6492
6712
  }
6493
6713
  .brand-skill-update-arrow {
6494
6714
  color: var(--color-text-secondary);
@@ -6568,17 +6788,11 @@ body {
6568
6788
  border-color: var(--color-accent-primary);
6569
6789
  }
6570
6790
  .btn-brand-update {
6571
- background: #1f4e9e;
6572
- color: #bfdbfe;
6573
- border: 1px solid #1d4070;
6574
- }
6575
- .btn-brand-update:hover { background: #1a3f85; }
6576
- [data-theme="light"] .btn-brand-update {
6577
- background: #1d4ed8;
6791
+ background: var(--color-accent-primary);
6578
6792
  color: #ffffff;
6579
- border: 1px solid #1e40af;
6793
+ border: 1px solid var(--color-accent-primary);
6580
6794
  }
6581
- [data-theme="light"] .btn-brand-update:hover { background: #1e40af; }
6795
+ .btn-brand-update:hover { background: var(--color-accent-hover); }
6582
6796
  .btn-brand-install:disabled,
6583
6797
  .btn-brand-update:disabled {
6584
6798
  opacity: 0.5;
@@ -6619,6 +6833,139 @@ body {
6619
6833
  border-color: var(--color-error-border, #f5c6c6);
6620
6834
  background: var(--color-error-bg, #fff0f0);
6621
6835
  }
6836
+ .btn-skill-edit {
6837
+ background: transparent;
6838
+ border: 1px solid transparent;
6839
+ border-radius: 6px;
6840
+ color: var(--color-text-tertiary);
6841
+ cursor: pointer;
6842
+ padding: 0.25rem;
6843
+ display: inline-flex;
6844
+ align-items: center;
6845
+ transition: color .15s, border-color .15s, background .15s;
6846
+ }
6847
+ .btn-skill-edit:hover {
6848
+ color: var(--color-text-primary);
6849
+ border-color: var(--color-border-primary);
6850
+ background: var(--color-bg-hover);
6851
+ }
6852
+
6853
+
6854
+ /* ── CodeEditor (CodeMirror 6) Modal ────────────────────────────────────── */
6855
+ .code-editor-modal {
6856
+ background: var(--color-bg-secondary);
6857
+ border-radius: 12px;
6858
+ border: 1px solid var(--color-border-primary);
6859
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
6860
+ width: 92%;
6861
+ max-width: 820px;
6862
+ height: 82vh;
6863
+ max-height: 860px;
6864
+ display: flex;
6865
+ flex-direction: column;
6866
+ overflow: hidden;
6867
+ }
6868
+ .code-editor-header {
6869
+ display: flex;
6870
+ align-items: center;
6871
+ justify-content: space-between;
6872
+ padding: 0.75rem 1.25rem;
6873
+ border-bottom: 1px solid var(--color-border);
6874
+ }
6875
+ .code-editor-header h3 {
6876
+ margin: 0;
6877
+ font-size: 0.875rem;
6878
+ font-weight: 600;
6879
+ font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
6880
+ color: var(--color-text-primary);
6881
+ }
6882
+ .code-editor-close {
6883
+ background: none;
6884
+ border: none;
6885
+ font-size: 1.25rem;
6886
+ line-height: 1;
6887
+ color: var(--color-text-tertiary);
6888
+ cursor: pointer;
6889
+ width: 2rem;
6890
+ height: 2rem;
6891
+ display: flex;
6892
+ align-items: center;
6893
+ justify-content: center;
6894
+ border-radius: 6px;
6895
+ padding: 0;
6896
+ }
6897
+ .code-editor-close:hover {
6898
+ color: var(--color-text-primary);
6899
+ background: var(--color-bg-hover);
6900
+ }
6901
+ .code-editor-body {
6902
+ flex: 1;
6903
+ overflow: hidden;
6904
+ position: relative;
6905
+ }
6906
+ .code-editor-body .cm-editor {
6907
+ height: 100%;
6908
+ font-size: 0.8125rem;
6909
+ line-height: 1.6;
6910
+ background: transparent;
6911
+ }
6912
+ .code-editor-body .cm-editor .cm-content {
6913
+ background: transparent;
6914
+ }
6915
+ .code-editor-body .cm-editor.cm-focused {
6916
+ outline: none;
6917
+ }
6918
+ .code-editor-body .cm-scroller {
6919
+ overflow: auto;
6920
+ font-family: var(--font-mono, 'SF Mono', 'Fira Code', monospace);
6921
+ }
6922
+ .code-editor-body .cm-gutters {
6923
+ background: transparent;
6924
+ border-right: 1px solid var(--color-border-primary);
6925
+ color: var(--color-text-tertiary);
6926
+ }
6927
+
6928
+ .code-editor-body .cm-activeLineGutter {
6929
+ background: var(--color-bg-hover);
6930
+ }
6931
+ .code-editor-body .cm-activeLine {
6932
+ background: var(--color-bg-hover);
6933
+ }
6934
+ .code-editor-body .cm-selectionBackground {
6935
+ background: color-mix(in srgb, var(--color-accent-primary) 20%, transparent) !important;
6936
+ }
6937
+ .code-editor-footer {
6938
+ display: flex;
6939
+ align-items: center;
6940
+ justify-content: space-between;
6941
+ padding: 0.625rem 1.25rem;
6942
+ border-top: 1px solid var(--color-border);
6943
+ gap: 0.75rem;
6944
+ }
6945
+ .code-editor-status {
6946
+ font-size: 0.8125rem;
6947
+ color: var(--color-text-secondary);
6948
+ }
6949
+ .code-editor-status-error {
6950
+ color: var(--color-error, #c0392b);
6951
+ }
6952
+ .code-editor-actions {
6953
+ display: flex;
6954
+ gap: 0.5rem;
6955
+ }
6956
+ .code-editor-modal--image .code-editor-body {
6957
+ display: flex;
6958
+ align-items: center;
6959
+ justify-content: center;
6960
+ background: var(--color-bg-primary);
6961
+ overflow: auto;
6962
+ }
6963
+ .code-editor-img-preview {
6964
+ max-width: 100%;
6965
+ max-height: 100%;
6966
+ object-fit: contain;
6967
+ border-radius: 4px;
6968
+ }
6622
6969
 
6623
6970
  /* ── Skills sidebar section ──────────────────────────────────────────────── */
6624
6971
  #skill-list-items { padding: 0 0.5rem 0.5rem; display: flex; flex-direction: column; gap: 2px; }
@@ -7052,28 +7399,35 @@ body.setup-mode[data-theme="dark"] {
7052
7399
  transform: translateY(1px);
7053
7400
  }
7054
7401
  #setup-device-error {
7055
- margin-top: 0.5rem;
7402
+ margin-top: 0;
7403
+ min-height: 0;
7056
7404
  }
7057
7405
  #setup-device-error.result-fail { color: var(--color-error); }
7058
7406
 
7059
7407
  /* Manual-config collapsible (secondary path) */
7060
- #setup-manual-details {
7061
- margin-top: 0.25rem;
7062
- }
7063
- #setup-manual-summary {
7408
+ #setup-manual-toggle {
7409
+ display: block;
7410
+ width: 100%;
7411
+ margin-top: 0.5rem;
7412
+ padding: 0.5rem 0;
7413
+ background: none;
7414
+ border: none;
7064
7415
  cursor: pointer;
7065
- font-size: 0.75rem;
7416
+ font-size: 0.8rem;
7066
7417
  color: var(--color-text-secondary);
7067
7418
  text-align: center;
7068
- list-style: none;
7069
- padding: 0.5rem 0;
7419
+ text-decoration: underline;
7420
+ text-underline-offset: 3px;
7070
7421
  user-select: none;
7422
+ transition: color 0.15s;
7071
7423
  }
7072
- #setup-manual-summary::-webkit-details-marker { display: none; }
7073
- #setup-manual-summary:hover { color: var(--color-text-primary); }
7074
- #setup-manual-details[open] #setup-manual-summary {
7075
- margin-bottom: 0.5rem;
7076
- border-bottom: 1px solid var(--color-border-primary);
7424
+ #setup-manual-toggle:hover {
7425
+ color: var(--color-text-primary);
7426
+ }
7427
+ #setup-manual-section {
7428
+ margin-top: 0.75rem;
7429
+ padding-top: 0.75rem;
7430
+ border-top: 1px solid var(--color-border-primary);
7077
7431
  }
7078
7432
 
7079
7433
  /* Bottom action row: [← Back] [Test & Continue →] */
@@ -8690,6 +9044,29 @@ body.setup-mode[data-theme="dark"] {
8690
9044
  cursor: wait;
8691
9045
  }
8692
9046
 
9047
+ .profile-pane-footer-actions {
9048
+ display: flex;
9049
+ gap: 0.5rem;
9050
+ align-items: center;
9051
+ }
9052
+
9053
+ .btn-profile-edit {
9054
+ padding: 0.5rem 1.125rem;
9055
+ border-radius: 8px;
9056
+ border: 1px solid var(--color-border-primary);
9057
+ background: transparent;
9058
+ color: var(--color-text-primary);
9059
+ font-size: 0.8125rem;
9060
+ font-weight: 500;
9061
+ cursor: pointer;
9062
+ transition: border-color 0.15s, color 0.15s;
9063
+ }
9064
+
9065
+ .btn-profile-edit:hover {
9066
+ border-color: var(--color-button-primary);
9067
+ color: var(--color-button-primary);
9068
+ }
9069
+
8693
9070
  /* ── Minimal-Markdown rendered body ───────────────────────────────────── */
8694
9071
  .profile-markdown {
8695
9072
  font-size: 0.8438rem;
@@ -8830,6 +9207,7 @@ body.setup-mode[data-theme="dark"] {
8830
9207
 
8831
9208
  /* Shared base for per-card buttons. */
8832
9209
  .btn-memory-curate,
9210
+ .btn-memory-edit,
8833
9211
  .btn-memory-delete,
8834
9212
  .btn-memory-expand {
8835
9213
  padding: 0.3125rem 0.625rem;
@@ -8845,7 +9223,8 @@ body.setup-mode[data-theme="dark"] {
8845
9223
  transition: background 0.15s, border-color 0.15s, color 0.15s;
8846
9224
  }
8847
9225
 
8848
- .btn-memory-curate:hover {
9226
+ .btn-memory-curate:hover,
9227
+ .btn-memory-edit:hover {
8849
9228
  border-color: var(--color-text-primary);
8850
9229
  background: var(--color-bg-hover);
8851
9230
  }
@@ -11712,4 +12091,35 @@ body.setup-mode[data-theme="dark"] {
11712
12091
  font-size: 0.7rem;
11713
12092
  color: var(--color-text-tertiary);
11714
12093
  }
12094
+
12095
+ /* Session aside: fixed overlay on mobile */
12096
+ #session-aside {
12097
+ position: fixed;
12098
+ top: 0;
12099
+ right: 0;
12100
+ height: 100%;
12101
+ z-index: 200;
12102
+ box-shadow: -4px 0 24px rgba(0,0,0,0.18);
12103
+ transition: transform 0.3s ease, background-color 0.3s ease;
12104
+ transform: translateX(0);
12105
+ width: var(--session-aside-width);
12106
+ }
12107
+ #session-aside.collapsed {
12108
+ transform: translateX(100%);
12109
+ width: var(--session-aside-width);
12110
+ border-left: 1px solid var(--color-border-primary);
12111
+ }
12112
+ #session-aside-resize { display: none; }
12113
+
12114
+ #workspace-overlay {
12115
+ display: none;
12116
+ position: fixed;
12117
+ inset: 0;
12118
+ z-index: 199;
12119
+ background: rgba(0,0,0,0.4);
12120
+ }
12121
+ #workspace-overlay.active {
12122
+ display: block;
12123
+ }
11715
12124
  }
12125
+