painapple-code 1.0.0__py3-none-any.whl

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 (436) hide show
  1. painapple_code/__init__.py +30 -0
  2. painapple_code/__main__.py +18 -0
  3. painapple_code/_version.py +24 -0
  4. painapple_code/auth_middleware.py +930 -0
  5. painapple_code/cli/__init__.py +204 -0
  6. painapple_code/cli/deploy/__init__.py +15 -0
  7. painapple_code/cli/deploy/claude_seed.py +41 -0
  8. painapple_code/cli/deploy/config.py +260 -0
  9. painapple_code/cli/deploy/container.py +715 -0
  10. painapple_code/cli/deploy/launch.py +163 -0
  11. painapple_code/cli/deploy/runtime.py +223 -0
  12. painapple_code/cli/lifecycle_cmd.py +443 -0
  13. painapple_code/cli/list_cmd.py +572 -0
  14. painapple_code/cli/manage_cmd.py +670 -0
  15. painapple_code/cli/netinfo.py +125 -0
  16. painapple_code/cli/profiles.py +390 -0
  17. painapple_code/cli/serve_args.py +163 -0
  18. painapple_code/cli/serve_config.py +256 -0
  19. painapple_code/cli/setup_cmd.py +664 -0
  20. painapple_code/cli/ui.py +438 -0
  21. painapple_code/cost_analytics.py +879 -0
  22. painapple_code/data/models.defaults.yaml +18 -0
  23. painapple_code/data/models.yaml +18 -0
  24. painapple_code/data/presets.defaults.json +32 -0
  25. painapple_code/data/strings.yaml +2269 -0
  26. painapple_code/helpers.py +406 -0
  27. painapple_code/paths.py +1514 -0
  28. painapple_code/prompt_explorer.py +984 -0
  29. painapple_code/providers/__init__.py +224 -0
  30. painapple_code/providers/base.py +710 -0
  31. painapple_code/providers/claude/__init__.py +74 -0
  32. painapple_code/providers/claude/capabilities.py +218 -0
  33. painapple_code/providers/claude/errors.py +79 -0
  34. painapple_code/providers/claude/launch.py +50 -0
  35. painapple_code/providers/claude/summary.py +89 -0
  36. painapple_code/providers/claude/translate.py +74 -0
  37. painapple_code/providers/claude_sdk/__init__.py +139 -0
  38. painapple_code/providers/claude_sdk/driver.py +412 -0
  39. painapple_code/providers/codex/__init__.py +87 -0
  40. painapple_code/providers/codex/capabilities.py +321 -0
  41. painapple_code/providers/codex/errors.py +70 -0
  42. painapple_code/providers/codex/launch.py +142 -0
  43. painapple_code/providers/codex/summary.py +196 -0
  44. painapple_code/providers/codex/translate.py +204 -0
  45. painapple_code/providers/codex_app_server/__init__.py +100 -0
  46. painapple_code/providers/codex_app_server/capabilities.py +41 -0
  47. painapple_code/providers/codex_app_server/launch.py +202 -0
  48. painapple_code/providers/codex_app_server/summary.py +291 -0
  49. painapple_code/providers/codex_app_server/translate.py +263 -0
  50. painapple_code/providers/codex_app_server/transport.py +238 -0
  51. painapple_code/routes/__init__.py +0 -0
  52. painapple_code/routes/api_agents.py +441 -0
  53. painapple_code/routes/api_app.py +368 -0
  54. painapple_code/routes/api_app_commit_sections.py +127 -0
  55. painapple_code/routes/api_app_config.py +997 -0
  56. painapple_code/routes/api_app_session_prefs.py +217 -0
  57. painapple_code/routes/api_browser.py +690 -0
  58. painapple_code/routes/api_commands.py +374 -0
  59. painapple_code/routes/api_costs.py +128 -0
  60. painapple_code/routes/api_drafts.py +139 -0
  61. painapple_code/routes/api_exec.py +149 -0
  62. painapple_code/routes/api_files.py +542 -0
  63. painapple_code/routes/api_git.py +657 -0
  64. painapple_code/routes/api_logs.py +649 -0
  65. painapple_code/routes/api_plugins.py +275 -0
  66. painapple_code/routes/api_project_config.py +418 -0
  67. painapple_code/routes/api_prompts.py +231 -0
  68. painapple_code/routes/api_providers.py +53 -0
  69. painapple_code/routes/api_search.py +411 -0
  70. painapple_code/routes/api_session_stash.py +234 -0
  71. painapple_code/routes/api_session_welcome.py +151 -0
  72. painapple_code/routes/api_sessions.py +742 -0
  73. painapple_code/routes/api_shadow.py +323 -0
  74. painapple_code/routes/api_shadow_db.py +512 -0
  75. painapple_code/routes/api_shadow_files.py +332 -0
  76. painapple_code/routes/api_shadow_search.py +476 -0
  77. painapple_code/routes/api_skills.py +610 -0
  78. painapple_code/routes/api_tasks.py +147 -0
  79. painapple_code/routes/api_terminal.py +540 -0
  80. painapple_code/routes/api_upload.py +293 -0
  81. painapple_code/routes/api_viewer.py +229 -0
  82. painapple_code/routes/dependencies.py +109 -0
  83. painapple_code/routes/ws_chat.py +837 -0
  84. painapple_code/server.py +2208 -0
  85. painapple_code/server_logging.py +263 -0
  86. painapple_code/services/__init__.py +0 -0
  87. painapple_code/services/agent_session.py +2738 -0
  88. painapple_code/session_store.py +253 -0
  89. painapple_code/session_store_core.py +879 -0
  90. painapple_code/shadow_db.py +830 -0
  91. painapple_code/shadow_db_plans.py +369 -0
  92. painapple_code/shadow_db_queries.py +595 -0
  93. painapple_code/shadow_db_schema.py +676 -0
  94. painapple_code/shadow_git.py +1296 -0
  95. painapple_code/shadow_git_frontmatter.py +314 -0
  96. painapple_code/shadow_git_sections.py +520 -0
  97. painapple_code/shadow_git_summary.py +462 -0
  98. painapple_code/shadow_parser.py +560 -0
  99. painapple_code/static/css/00-variables.css +276 -0
  100. painapple_code/static/css/01-base.css +245 -0
  101. painapple_code/static/css/02-tooltips.css +36 -0
  102. painapple_code/static/css/09-left-rail.css +448 -0
  103. painapple_code/static/css/10-header.css +777 -0
  104. painapple_code/static/css/11-connection-bar.css +120 -0
  105. painapple_code/static/css/20-messages.css +750 -0
  106. painapple_code/static/css/21-tool-blocks.css +2340 -0
  107. painapple_code/static/css/22-edit-diff.css +619 -0
  108. painapple_code/static/css/23-thinking.css +1584 -0
  109. painapple_code/static/css/24-background-tasks.css +359 -0
  110. painapple_code/static/css/25-context-block.css +252 -0
  111. painapple_code/static/css/26-session-setup.css +144 -0
  112. painapple_code/static/css/30-markdown.css +854 -0
  113. painapple_code/static/css/40-input.css +2418 -0
  114. painapple_code/static/css/41-autocomplete.css +127 -0
  115. painapple_code/static/css/42-activity-strip.css +97 -0
  116. painapple_code/static/css/43-file-autocomplete.css +121 -0
  117. painapple_code/static/css/44-snippets-autocomplete.css +224 -0
  118. painapple_code/static/css/45-selection-bar.css +433 -0
  119. painapple_code/static/css/46-keyboard-bar.css +119 -0
  120. painapple_code/static/css/47-keyboard-longpress.css +116 -0
  121. painapple_code/static/css/47-token-profile.css +106 -0
  122. painapple_code/static/css/50-welcome.css +3095 -0
  123. painapple_code/static/css/51-modals.css +301 -0
  124. painapple_code/static/css/52-utilities.css +77 -0
  125. painapple_code/static/css/53-image-upload.css +456 -0
  126. painapple_code/static/css/54-todo-list.css +102 -0
  127. painapple_code/static/css/55-file-upload.css +113 -0
  128. painapple_code/static/css/56-chat-navigator.css +218 -0
  129. painapple_code/static/css/56-permission-card.css +332 -0
  130. painapple_code/static/css/56-plan-approval.css +204 -0
  131. painapple_code/static/css/56-question-form.css +602 -0
  132. painapple_code/static/css/57-swipe-indicator.css +95 -0
  133. painapple_code/static/css/58-session-families.css +627 -0
  134. painapple_code/static/css/59-about.css +196 -0
  135. painapple_code/static/css/61-log-explorer.css +723 -0
  136. painapple_code/static/css/62-json-tree.css +489 -0
  137. painapple_code/static/css/62-skills-widget.css +650 -0
  138. painapple_code/static/css/63-commands-widget.css +30 -0
  139. painapple_code/static/css/63-plugins-widget.css +174 -0
  140. painapple_code/static/css/63-terminal.css +516 -0
  141. painapple_code/static/css/64-search-files-widget.css +448 -0
  142. painapple_code/static/css/65-snippets-widget.css +426 -0
  143. painapple_code/static/css/66-active-sessions.css +979 -0
  144. painapple_code/static/css/66-agents-widget.css +265 -0
  145. painapple_code/static/css/66-file-explorer-widget.css +855 -0
  146. painapple_code/static/css/66-git-panel.css +791 -0
  147. painapple_code/static/css/66-history-explorer.css +1496 -0
  148. painapple_code/static/css/67-diff-viewer.css +602 -0
  149. painapple_code/static/css/67-editor-view.css +748 -0
  150. painapple_code/static/css/67-file-preview-widget.css +1873 -0
  151. painapple_code/static/css/68-browser-widget.css +337 -0
  152. painapple_code/static/css/68-compare-wizard.css +355 -0
  153. painapple_code/static/css/68-csv-preview.css +106 -0
  154. painapple_code/static/css/68-diff-preview.css +103 -0
  155. painapple_code/static/css/68-helpers-install-widget.css +491 -0
  156. painapple_code/static/css/68-image-annotate.css +346 -0
  157. painapple_code/static/css/68-terminal-view.css +98 -0
  158. painapple_code/static/css/68-uploads-widget.css +229 -0
  159. painapple_code/static/css/69-cost-analytics.css +537 -0
  160. painapple_code/static/css/70-config-panel.css +3736 -0
  161. painapple_code/static/css/70-excalidraw.css +136 -0
  162. painapple_code/static/css/70-prompt-explorer.css +890 -0
  163. painapple_code/static/css/71-chart.css +111 -0
  164. painapple_code/static/css/72-discussion-sidebar.css +678 -0
  165. painapple_code/static/css/74-debug-logs.css +656 -0
  166. painapple_code/static/css/80-open-dialog.css +146 -0
  167. painapple_code/static/css/80-quick-switcher.css +383 -0
  168. painapple_code/static/css/80-widget-system.css +1763 -0
  169. painapple_code/static/css/81-quick-actions.css +1020 -0
  170. painapple_code/static/css/82-context-popover.css +1233 -0
  171. painapple_code/static/css/83-context-menu.css +339 -0
  172. painapple_code/static/css/85-lazy-loading.css +102 -0
  173. painapple_code/static/css/86-zen-mode.css +708 -0
  174. painapple_code/static/css/87-grid-switcher.css +346 -0
  175. painapple_code/static/css/login.css +198 -0
  176. painapple_code/static/dist/app.bundle.js +7177 -0
  177. painapple_code/static/feature-triage.html +446 -0
  178. painapple_code/static/icons/apple-touch-icon.png +0 -0
  179. painapple_code/static/icons/favicon-32.png +0 -0
  180. painapple_code/static/icons/favicon.ico +0 -0
  181. painapple_code/static/icons/icon-128.png +0 -0
  182. painapple_code/static/icons/icon-144.png +0 -0
  183. painapple_code/static/icons/icon-152.png +0 -0
  184. painapple_code/static/icons/icon-180.png +0 -0
  185. painapple_code/static/icons/icon-192.png +0 -0
  186. painapple_code/static/icons/icon-384.png +0 -0
  187. painapple_code/static/icons/icon-512.png +0 -0
  188. painapple_code/static/icons/icon-72.png +0 -0
  189. painapple_code/static/icons/icon-96.png +0 -0
  190. painapple_code/static/js/action-delegate.js +187 -0
  191. painapple_code/static/js/activity-strip.js +193 -0
  192. painapple_code/static/js/app/context-menus.js +619 -0
  193. painapple_code/static/js/app/file-preview.js +163 -0
  194. painapple_code/static/js/app/init-ui.js +574 -0
  195. painapple_code/static/js/app/input-events.js +561 -0
  196. painapple_code/static/js/app/input-resize.js +313 -0
  197. painapple_code/static/js/app/message-actions.js +419 -0
  198. painapple_code/static/js/app/panels.js +210 -0
  199. painapple_code/static/js/app/rendering-delegators.js +246 -0
  200. painapple_code/static/js/app/server-loading.js +365 -0
  201. painapple_code/static/js/app/session-ops.js +287 -0
  202. painapple_code/static/js/app/session-switch.js +443 -0
  203. painapple_code/static/js/app/settings-helpers.js +280 -0
  204. painapple_code/static/js/app/startup-welcome.js +290 -0
  205. painapple_code/static/js/app-context.js +157 -0
  206. painapple_code/static/js/app.js +2134 -0
  207. painapple_code/static/js/auth-fetch.js +46 -0
  208. painapple_code/static/js/background-tasks.js +274 -0
  209. painapple_code/static/js/boot.js +73 -0
  210. painapple_code/static/js/caret-position.js +171 -0
  211. painapple_code/static/js/chat-navigator.js +710 -0
  212. painapple_code/static/js/chat-search.js +323 -0
  213. painapple_code/static/js/command-executor.js +176 -0
  214. painapple_code/static/js/command-store.js +497 -0
  215. painapple_code/static/js/components.js +1414 -0
  216. painapple_code/static/js/config.js +218 -0
  217. painapple_code/static/js/context-menu.js +740 -0
  218. painapple_code/static/js/controllers/chat-controller.js +4252 -0
  219. painapple_code/static/js/controllers/dialog-controller.js +217 -0
  220. painapple_code/static/js/controllers/tab-controller.js +1828 -0
  221. painapple_code/static/js/controllers/thinking-controller.js +915 -0
  222. painapple_code/static/js/diff-utils.js +486 -0
  223. painapple_code/static/js/drafts-pill.js +95 -0
  224. painapple_code/static/js/editor-view.js +815 -0
  225. painapple_code/static/js/effort-settings.js +430 -0
  226. painapple_code/static/js/feature-triage.js +811 -0
  227. painapple_code/static/js/file-autocomplete.js +516 -0
  228. painapple_code/static/js/file-tabs.js +123 -0
  229. painapple_code/static/js/gestures.js +448 -0
  230. painapple_code/static/js/grid-switcher.js +334 -0
  231. painapple_code/static/js/input-handler.js +1472 -0
  232. painapple_code/static/js/keyboard-bar.js +495 -0
  233. painapple_code/static/js/layout-switcher.js +109 -0
  234. painapple_code/static/js/linkify-utils.js +200 -0
  235. painapple_code/static/js/login.js +141 -0
  236. painapple_code/static/js/open-dialog.js +879 -0
  237. painapple_code/static/js/orphan-terminals.js +373 -0
  238. painapple_code/static/js/path-utils.js +179 -0
  239. painapple_code/static/js/perf-marks.js +102 -0
  240. painapple_code/static/js/permission-settings.js +475 -0
  241. painapple_code/static/js/pointer-input.js +59 -0
  242. painapple_code/static/js/preview/json-tree-menu.js +226 -0
  243. painapple_code/static/js/preview/json-tree-toolbar.js +291 -0
  244. painapple_code/static/js/preview/json-tree.js +457 -0
  245. painapple_code/static/js/preview/preview-breadcrumb.js +387 -0
  246. painapple_code/static/js/preview/preview-edit.js +367 -0
  247. painapple_code/static/js/preview/preview-events.js +361 -0
  248. painapple_code/static/js/preview/preview-history.js +567 -0
  249. painapple_code/static/js/preview/preview-inline-edit.js +869 -0
  250. painapple_code/static/js/preview/preview-poll.js +108 -0
  251. painapple_code/static/js/preview/preview-render.js +572 -0
  252. painapple_code/static/js/preview/preview-search.js +421 -0
  253. painapple_code/static/js/preview/preview-state.js +256 -0
  254. painapple_code/static/js/preview/preview-utils.js +251 -0
  255. painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
  256. painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
  257. painapple_code/static/js/preview-plugins/diff-plugin.js +258 -0
  258. painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
  259. painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
  260. painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
  261. painapple_code/static/js/preview-plugins/index.js +34 -0
  262. painapple_code/static/js/preview-plugins/json-plugin.js +99 -0
  263. painapple_code/static/js/preview-plugins/jsonl-plugin.js +152 -0
  264. painapple_code/static/js/preview-plugins/markdown-plugin.js +266 -0
  265. painapple_code/static/js/preview-plugins/panzoom-plugin.js +63 -0
  266. painapple_code/static/js/preview-plugins/plugin-helpers.js +282 -0
  267. painapple_code/static/js/project-color-picker.js +110 -0
  268. painapple_code/static/js/project-colors.js +163 -0
  269. painapple_code/static/js/prompt-favorites.js +187 -0
  270. painapple_code/static/js/quick-actions-menu.js +1736 -0
  271. painapple_code/static/js/quick-actions-registry.js +1239 -0
  272. painapple_code/static/js/quick-switcher/controller.js +294 -0
  273. painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
  274. painapple_code/static/js/quick-switcher/index.js +30 -0
  275. painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
  276. painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
  277. painapple_code/static/js/quick-switcher/providers/file-provider.js +291 -0
  278. painapple_code/static/js/quick-switcher/providers/panel-provider.js +132 -0
  279. painapple_code/static/js/quick-switcher/providers/project-provider.js +361 -0
  280. painapple_code/static/js/quick-switcher/providers/read-files-provider.js +104 -0
  281. painapple_code/static/js/quick-switcher/providers/skills-provider.js +129 -0
  282. painapple_code/static/js/quick-switcher/registry.js +69 -0
  283. painapple_code/static/js/quick-switcher/ui/item.js +54 -0
  284. painapple_code/static/js/quick-switcher/ui/picker.js +464 -0
  285. painapple_code/static/js/recent-opens.js +40 -0
  286. painapple_code/static/js/scroll-manager.js +530 -0
  287. painapple_code/static/js/scroll-state-machine.js +359 -0
  288. painapple_code/static/js/selection/action-bar.js +793 -0
  289. painapple_code/static/js/selection/index.js +15 -0
  290. painapple_code/static/js/selection/selection-handler.js +877 -0
  291. painapple_code/static/js/selection/state.js +181 -0
  292. painapple_code/static/js/session/agent-progress.js +202 -0
  293. painapple_code/static/js/session/handle-agent-message.js +481 -0
  294. painapple_code/static/js/session/handle-message.js +550 -0
  295. painapple_code/static/js/session/interactive.js +587 -0
  296. painapple_code/static/js/session/message-store.js +271 -0
  297. painapple_code/static/js/session/messages.js +253 -0
  298. painapple_code/static/js/session/persistence.js +179 -0
  299. painapple_code/static/js/session/restore.js +136 -0
  300. painapple_code/static/js/session/sync.js +333 -0
  301. painapple_code/static/js/session-container-pool.js +371 -0
  302. painapple_code/static/js/session-setup-panel.js +274 -0
  303. painapple_code/static/js/session.js +1047 -0
  304. painapple_code/static/js/shortcut-hints.js +272 -0
  305. painapple_code/static/js/shortcuts.js +1146 -0
  306. painapple_code/static/js/skills-autocomplete.js +262 -0
  307. painapple_code/static/js/snippets-autocomplete.js +738 -0
  308. painapple_code/static/js/stash-refs-view.js +83 -0
  309. painapple_code/static/js/stash-ui.js +768 -0
  310. painapple_code/static/js/stash.js +785 -0
  311. painapple_code/static/js/status-bar.js +1184 -0
  312. painapple_code/static/js/token-profile.js +274 -0
  313. painapple_code/static/js/tool-renderer-blocks.js +1626 -0
  314. painapple_code/static/js/tool-renderer-thinking.js +813 -0
  315. painapple_code/static/js/tool-renderer.js +1500 -0
  316. painapple_code/static/js/tooltips.js +197 -0
  317. painapple_code/static/js/upload-manager.js +1019 -0
  318. painapple_code/static/js/utils.js +521 -0
  319. painapple_code/static/js/welcome/api.js +363 -0
  320. painapple_code/static/js/welcome/cards.js +626 -0
  321. painapple_code/static/js/welcome/context-menu.js +585 -0
  322. painapple_code/static/js/welcome/families.js +801 -0
  323. painapple_code/static/js/welcome/preview.js +234 -0
  324. painapple_code/static/js/welcome/state.js +129 -0
  325. painapple_code/static/js/welcome.js +1874 -0
  326. painapple_code/static/js/widget-system/base-widget.js +717 -0
  327. painapple_code/static/js/widget-system/device-manager.js +178 -0
  328. painapple_code/static/js/widget-system/event-bus.js +152 -0
  329. painapple_code/static/js/widget-system/icons.js +120 -0
  330. painapple_code/static/js/widget-system/index.js +49 -0
  331. painapple_code/static/js/widget-system/init.js +54 -0
  332. painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
  333. painapple_code/static/js/widget-system/types/floating.js +915 -0
  334. painapple_code/static/js/widget-system/types/index.js +40 -0
  335. painapple_code/static/js/widget-system/types/modal.js +147 -0
  336. painapple_code/static/js/widget-system/types/sidebar.js +158 -0
  337. painapple_code/static/js/widget-system/types/tab.js +146 -0
  338. painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
  339. painapple_code/static/js/widget-system/widget-manager.js +1187 -0
  340. painapple_code/static/js/widgets/about-widget.js +260 -0
  341. painapple_code/static/js/widgets/active-sessions-widget.js +760 -0
  342. painapple_code/static/js/widgets/agents-widget.js +897 -0
  343. painapple_code/static/js/widgets/browser-widget.js +579 -0
  344. painapple_code/static/js/widgets/commands-widget.js +694 -0
  345. painapple_code/static/js/widgets/config/commit-sections.js +449 -0
  346. painapple_code/static/js/widgets/config/dir-autocomplete.js +224 -0
  347. painapple_code/static/js/widgets/config/gestures.js +161 -0
  348. painapple_code/static/js/widgets/config/models-tab.js +988 -0
  349. painapple_code/static/js/widgets/config/quick-actions-tab.js +882 -0
  350. painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
  351. painapple_code/static/js/widgets/config/state.js +517 -0
  352. painapple_code/static/js/widgets/config/system-controls.js +90 -0
  353. painapple_code/static/js/widgets/config-widget.js +1535 -0
  354. painapple_code/static/js/widgets/cost-analytics-widget.js +621 -0
  355. painapple_code/static/js/widgets/debug-widget.js +693 -0
  356. painapple_code/static/js/widgets/diff-viewer-widget.js +1420 -0
  357. painapple_code/static/js/widgets/discussion-widget.js +1107 -0
  358. painapple_code/static/js/widgets/file-explorer-widget.js +2166 -0
  359. painapple_code/static/js/widgets/file-preview-widget.js +861 -0
  360. painapple_code/static/js/widgets/git-widget.js +794 -0
  361. painapple_code/static/js/widgets/helpers-install-widget.js +720 -0
  362. painapple_code/static/js/widgets/history-explorer-widget.js +1804 -0
  363. painapple_code/static/js/widgets/image-annotate-widget.js +951 -0
  364. painapple_code/static/js/widgets/image-preview-widget.js +692 -0
  365. painapple_code/static/js/widgets/index.js +176 -0
  366. painapple_code/static/js/widgets/log-explorer-widget.js +1057 -0
  367. painapple_code/static/js/widgets/plugins-widget.js +305 -0
  368. painapple_code/static/js/widgets/prompt-explorer-widget.js +1119 -0
  369. painapple_code/static/js/widgets/search-files-widget.js +529 -0
  370. painapple_code/static/js/widgets/skills-widget.js +843 -0
  371. painapple_code/static/js/widgets/snippets-widget.js +479 -0
  372. painapple_code/static/js/widgets/sub-agents-widget.js +333 -0
  373. painapple_code/static/js/widgets/tasks-widget.js +379 -0
  374. painapple_code/static/js/widgets/terminal/connection.js +283 -0
  375. painapple_code/static/js/widgets/terminal/gestures.js +357 -0
  376. painapple_code/static/js/widgets/terminal/init.js +642 -0
  377. painapple_code/static/js/widgets/terminal/link-providers.js +532 -0
  378. painapple_code/static/js/widgets/terminal/osc52.js +213 -0
  379. painapple_code/static/js/widgets/terminal/render.js +159 -0
  380. painapple_code/static/js/widgets/terminal/size.js +54 -0
  381. painapple_code/static/js/widgets/terminal/state.js +211 -0
  382. painapple_code/static/js/widgets/terminal/wrap-utils.js +101 -0
  383. painapple_code/static/js/widgets/terminal-widget.js +793 -0
  384. painapple_code/static/js/widgets/uploads-widget.js +461 -0
  385. painapple_code/static/js/widgets/zen-widget.js +1001 -0
  386. painapple_code/static/login.html +51 -0
  387. painapple_code/static/manifest.json +73 -0
  388. painapple_code/static/sw.js +293 -0
  389. painapple_code/static/vendor/LICENSES.txt +769 -0
  390. painapple_code/static/vendor/README.md +103 -0
  391. painapple_code/static/vendor/codemirror.js +35 -0
  392. painapple_code/static/vendor/codemirror.js.LEGAL.txt +304 -0
  393. painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
  394. painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
  395. painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
  396. painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
  397. painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
  398. painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
  399. painapple_code/static/vendor/highlight.min.js +1213 -0
  400. painapple_code/static/vendor/marked.min.js +6 -0
  401. painapple_code/static/vendor/purify.min.js +3 -0
  402. painapple_code/static/vendor/xterm-addon-fit.js +2 -0
  403. painapple_code/static/vendor/xterm.css +209 -0
  404. painapple_code/static/vendor/xterm.js +2 -0
  405. painapple_code/static/web-client.html +635 -0
  406. painapple_code/subprocess_registry.py +535 -0
  407. painapple_code/tls_cert.py +79 -0
  408. painapple_code/tools/agents/shadow-git-helper.md +400 -0
  409. painapple_code/tools/excalidraw-to-svg.js +143 -0
  410. painapple_code/tools/install-helpers.sh +148 -0
  411. painapple_code/tools/shadow-git +320 -0
  412. painapple_code/tools/shadow-query +120 -0
  413. painapple_code/tools/vegalite-to-svg.js +79 -0
  414. painapple_code/turn_query.py +766 -0
  415. painapple_code/turn_tracker.py +221 -0
  416. painapple_code/utils/__init__.py +0 -0
  417. painapple_code/utils/agent_cli.py +306 -0
  418. painapple_code/utils/chart.py +103 -0
  419. painapple_code/utils/excalidraw.py +107 -0
  420. painapple_code/utils/file_lock.py +162 -0
  421. painapple_code/utils/file_paths.py +648 -0
  422. painapple_code/utils/generate_icons.py +251 -0
  423. painapple_code/utils/proc.py +205 -0
  424. painapple_code/utils/pty_backend.py +59 -0
  425. painapple_code/utils/pty_posix.py +160 -0
  426. painapple_code/utils/pty_windows.py +245 -0
  427. painapple_code/utils/token_profiles.py +91 -0
  428. painapple_code/viewer_templates.py +657 -0
  429. painapple_code/welcome_search.py +879 -0
  430. painapple_code-1.0.0.dist-info/METADATA +292 -0
  431. painapple_code-1.0.0.dist-info/RECORD +436 -0
  432. painapple_code-1.0.0.dist-info/WHEEL +5 -0
  433. painapple_code-1.0.0.dist-info/entry_points.txt +2 -0
  434. painapple_code-1.0.0.dist-info/licenses/LICENSE +661 -0
  435. painapple_code-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +261 -0
  436. painapple_code-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2418 @@
1
+ /* ═══════════════════════════════════════════════════════════════════
2
+ INPUT AREA
3
+ ═══════════════════════════════════════════════════════════════════ */
4
+ #input-area {
5
+ /* Stacking context so autocomplete dropdowns (which overflow upward into messages area)
6
+ paint above message content like turn-summary-bar and context indicators.
7
+ Needed because #input-container has isolation:isolate which caps child z-indexes. */
8
+ background: var(--bg-secondary);
9
+ border-top: 1px solid var(--border-primary);
10
+ padding: var(--layout-input-padding-v) var(--layout-input-padding-h);
11
+ flex-shrink: 0;
12
+ position: relative;
13
+ z-index: var(--z-base-ui);
14
+ }
15
+
16
+ #input-wrapper {
17
+ /* Left-aligned layout for comment sidebar */
18
+ max-width: 900px;
19
+ margin: 0; /* No longer centered - left-aligned */
20
+ position: relative;
21
+ }
22
+
23
+ #input-container {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--layout-gap-xs);
27
+ background: var(--bg-tertiary);
28
+ border: 1px solid var(--border-primary);
29
+ border-radius: var(--radius-lg);
30
+ padding: var(--layout-gap-md) var(--layout-gap-md) var(--layout-gap-sm);
31
+ transition: border-color 0.15s ease;
32
+ }
33
+
34
+ #input-container:focus-within {
35
+ border-color: var(--permission-color, var(--accent));
36
+ }
37
+
38
+ /* Resize handle at top of input container */
39
+ #input-resize-handle {
40
+ position: absolute;
41
+ top: -4px;
42
+ left: 50%;
43
+ transform: translateX(-50%);
44
+ width: 48px;
45
+ height: 8px;
46
+ cursor: ns-resize;
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ z-index: 10;
51
+ border-radius: var(--radius-sm);
52
+ transition: background 0.15s ease;
53
+ }
54
+
55
+ #input-resize-handle::before {
56
+ content: '';
57
+ width: 32px;
58
+ height: 3px;
59
+ background: var(--border-primary);
60
+ border-radius: var(--radius-xs);
61
+ transition: background 0.15s ease;
62
+ }
63
+
64
+ #input-resize-handle:hover::before,
65
+ #input-resize-handle:active::before {
66
+ background: var(--text-muted);
67
+ }
68
+
69
+ #input-container.resizing {
70
+ user-select: none;
71
+ }
72
+
73
+ /* Allow position:relative for handle positioning */
74
+ #input-container {
75
+ position: relative;
76
+ }
77
+
78
+ #input-container.disabled {
79
+ opacity: 0.6;
80
+ pointer-events: none;
81
+ }
82
+
83
+ /* Input area with keyword highlighting overlay */
84
+ #input-container {
85
+ /* Ensure stacking context for overlay */
86
+ isolation: isolate;
87
+ }
88
+
89
+ /* Wrapper for textarea with highlight overlay */
90
+ .input-textarea-wrapper {
91
+ /* Force the textarea + its keyword-backdrop onto their own GPU
92
+ compositing layer so paints elsewhere in the page (activity-strip
93
+ text changes on every tool call, status-dot pulses, hover transitions
94
+ on toolbar buttons/icons, animations, etc.) don't invalidate the
95
+ iOS WebKit caret + selection paint inside the textarea.
96
+ This is the user-reported "cursor/selection blinks in the text input
97
+ during mouse move and continuously while Claude is working". */
98
+ position: relative;
99
+ width: 100%;
100
+ display: flex;
101
+ align-items: flex-start;
102
+ /* Reset any inherited spacing */
103
+ margin: 0;
104
+ padding: 0;
105
+ transform: translateZ(0);
106
+ will-change: transform;
107
+ }
108
+
109
+ #input-highlight-backdrop {
110
+ /* Position directly behind textarea */
111
+ position: absolute;
112
+ top: 0;
113
+ left: 0;
114
+ width: 100%;
115
+ /* Match textarea height constraints for scroll sync */
116
+ height: 100%;
117
+ max-height: 400px; /* Same as #message-input max-height */
118
+ pointer-events: none;
119
+ z-index: 0;
120
+
121
+ /* Match textarea styling EXACTLY */
122
+ font-size: var(--font-size-md);
123
+ font-family: var(--font-sans);
124
+ line-height: 1.5;
125
+ white-space: pre-wrap;
126
+ word-wrap: break-word;
127
+ overflow: hidden; /* Content hidden but scrollTop still works programmatically */
128
+ padding: var(--sp-2) 0; /* Match textarea padding */
129
+ box-sizing: border-box;
130
+
131
+ /* Text styling - non-keyword text is transparent, keywords show through */
132
+ color: var(--text-primary);
133
+ }
134
+
135
+ /* Override keyword styling in backdrop - NO PADDING/BOLD to maintain alignment */
136
+ #input-highlight-backdrop .thinking-keyword {
137
+ padding: 0;
138
+ border-radius: 0;
139
+ font-weight: inherit; /* Don't change font weight - affects character width */
140
+ text-shadow: none; /* No shadow effects in input */
141
+ }
142
+
143
+ /* Show backdrop only when has content (hide placeholder state) */
144
+ #input-highlight-backdrop:empty {
145
+ display: none;
146
+ }
147
+
148
+ /* ═════════════════════════════════════════════════════════════
149
+ Shortcut Hints Overlay
150
+ Absolutely positioned inside the textarea wrapper so it behaves
151
+ like a richer placeholder — no layout shift when typing starts.
152
+ The wrapper gets .has-hints when the feature is enabled (bumps
153
+ textarea min-height so hints fit without jumping on first keystroke).
154
+ ═════════════════════════════════════════════════════════════ */
155
+ .shortcut-hints-overlay {
156
+ /* Start below the first textarea row so the native "Message Claude..."
157
+ placeholder and the cursor have room at the top. */
158
+ position: absolute;
159
+ top: 36px;
160
+ left: 0;
161
+ right: 0;
162
+ bottom: 2px;
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: var(--sp-3);
166
+ padding: 0;
167
+ font-size: var(--font-size-sm);
168
+ line-height: 1.35;
169
+ color: var(--text-muted);
170
+ pointer-events: none; /* clicks pass through to textarea */
171
+ opacity: 0;
172
+ visibility: hidden;
173
+ transition: opacity 0.15s ease;
174
+ z-index: 2;
175
+ overflow: hidden;
176
+ }
177
+
178
+ .shortcut-hints-overlay.visible {
179
+ opacity: 0.85;
180
+ visibility: visible;
181
+ }
182
+
183
+ /* Rows are clickable, so the whole sheet comes up to full strength while the
184
+ pointer is over it. The overlay itself is pointer-events:none, but :hover
185
+ still applies to it as an ancestor of the hovered (pointer-events:auto) row. */
186
+ .shortcut-hints-overlay.visible:hover {
187
+ opacity: 1;
188
+ }
189
+
190
+ .shortcut-hints-overlay.empty {
191
+ display: none !important;
192
+ }
193
+
194
+ /* Textarea gets taller when hints are active so placeholder + overlay fit
195
+ without triggering a layout shift when the user starts typing. */
196
+ .input-textarea-wrapper.has-hints #message-input {
197
+ min-height: 102px;
198
+ }
199
+
200
+ .shortcut-hints-title {
201
+ font-size: 10px;
202
+ font-weight: 600;
203
+ letter-spacing: 0.08em;
204
+ text-transform: uppercase;
205
+ color: var(--text-muted);
206
+ opacity: 0.7;
207
+ }
208
+
209
+ .shortcut-hints-grid {
210
+ display: grid;
211
+ grid-template-columns: 1fr;
212
+ gap: var(--sp-2) 0;
213
+ align-items: center;
214
+ }
215
+
216
+ /* Each pair is a flex unit so its key+label stay glued together.
217
+ At wide viewports we switch to 2 auto-sized tracks centred as a group —
218
+ pairs hug their content with a modest fixed gap, so the dead space sits
219
+ symmetrically on the outside instead of ballooning between them. */
220
+ .shortcut-hints-pair {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: var(--sp-8);
224
+ min-width: 0;
225
+ /* Rows start INERT. The overlay is drawn inside the empty textarea, so
226
+ the click that focuses the input must not also fire whatever row sits
227
+ under the cursor — see the :focus-within rule below, which is what
228
+ actually arms them. The gaps around a row always fall through to the
229
+ textarea, so clicking empty overlay space just focuses the input. */
230
+ pointer-events: none;
231
+ cursor: pointer;
232
+ /* Padding gives the hover chrome some breathing room; the matching
233
+ negative margin keeps the grid metrics identical to the flat version,
234
+ so enabling hover doesn't reflow the sheet or clip its last row. */
235
+ padding: 1px var(--sp-6);
236
+ margin: -1px calc(var(--sp-6) * -1);
237
+ border-radius: var(--radius-sm);
238
+ transition: background 0.12s ease;
239
+ }
240
+
241
+ /* Hover picks up the instance accent — deliberately NOT the focus ring's
242
+ var(--permission-color, var(--accent)): the ring tracks the permission mode
243
+ (green in YOLO, orange in Accept Edits, ...), so borrowing it would make a
244
+ red-accent instance hover in whatever hue the current mode happens to be. */
245
+ .shortcut-hints-pair:hover {
246
+ background: var(--bg-hover);
247
+ }
248
+
249
+ .shortcut-hints-pair:hover .shortcut-hints-label {
250
+ color: var(--accent);
251
+ }
252
+
253
+ .shortcut-hints-pair:hover .shortcut-hints-key {
254
+ color: var(--accent);
255
+ border-color: var(--accent);
256
+ }
257
+
258
+ .shortcut-hints-pair:active {
259
+ transform: translateY(1px);
260
+ }
261
+
262
+ /* A row is clickable only once the input ALREADY has focus. This is what
263
+ stops "click the chat to focus it" from doubling as "close the tab": the
264
+ first click lands on the textarea underneath (rows inert), focus moves,
265
+ and only the NEXT click can reach a row. Ordering matters as much as the
266
+ selector — a media query adds no specificity, so the touch gate further
267
+ down has to restate this selector to beat it. */
268
+ .input-textarea-wrapper:focus-within .shortcut-hints-pair {
269
+ pointer-events: auto;
270
+ }
271
+
272
+ @media (prefers-reduced-motion: reduce) {
273
+ .shortcut-hints-pair,
274
+ .shortcut-hints-key,
275
+ .shortcut-hints-label {
276
+ transition: none;
277
+ }
278
+ .shortcut-hints-pair:active {
279
+ transform: none;
280
+ }
281
+ }
282
+
283
+ /* Pure-touch devices get NO clickable rows. The overlay is drawn INSIDE the
284
+ empty textarea, so a tap meant to focus the input would fire whatever row
285
+ sits under the finger (the default set includes Close Tab) — and with no
286
+ hover there is nothing to advertise that the rows are buttons at all.
287
+ Phones (≤768px) hide the overlay outright; this is what covers tablets.
288
+ iPadOS always reports hover:none + pointer:coarse, even with a Magic
289
+ Keyboard trackpad, so an iPad matches here no matter what is driving it —
290
+ see the tooltip section of CLAUDE.md for why (hover: hover) is never the
291
+ right test. That makes this query a SAFE DEFAULT, not the whole answer:
292
+ the runtime body.fine-pointer signal (js/pointer-input.js) lifts the gate
293
+ the moment a real cursor appears, which restores clicking for an iPad on a
294
+ trackpad without ever arming a bare-finger tap. */
295
+ @media (hover: none) and (pointer: coarse) {
296
+ .input-textarea-wrapper:focus-within .shortcut-hints-pair {
297
+ pointer-events: none;
298
+ cursor: default;
299
+ }
300
+
301
+ body.fine-pointer .input-textarea-wrapper:focus-within .shortcut-hints-pair {
302
+ pointer-events: auto;
303
+ cursor: pointer;
304
+ }
305
+ }
306
+
307
+ @media (min-width: 700px) {
308
+ .shortcut-hints-grid {
309
+ grid-template-columns: auto auto;
310
+ justify-content: start;
311
+ column-gap: var(--sp-48);
312
+ }
313
+ }
314
+
315
+ @media (min-width: 820px) {
316
+ .shortcut-hints-grid {
317
+ grid-template-columns: auto auto auto;
318
+ }
319
+ }
320
+
321
+ .shortcut-hints-key {
322
+ font-family: var(--font-mono, monospace);
323
+ font-size: var(--font-size-xs);
324
+ background: var(--bg-secondary);
325
+ border: 1px solid var(--border-primary);
326
+ border-radius: var(--radius-sm);
327
+ padding: 1px var(--sp-6);
328
+ color: var(--text-primary);
329
+ white-space: nowrap;
330
+ min-width: 32px;
331
+ text-align: center;
332
+ box-shadow: 0 1px 0 rgba(0,0,0,0.2);
333
+ transition: color 0.12s ease, border-color 0.12s ease;
334
+ }
335
+
336
+ .shortcut-hints-label {
337
+ color: var(--text-muted);
338
+ white-space: nowrap;
339
+ overflow: hidden;
340
+ text-overflow: ellipsis;
341
+ transition: color 0.12s ease;
342
+ }
343
+
344
+ /* ═════════════════════════════════════════════════════════════
345
+ Drafts Pill
346
+ Clickable "N drafts" entry point (→ Prompt Explorer · Drafts) at the
347
+ start of the toolbar's right cluster (first item in .toolbar-right,
348
+ before the permission chip), shown only on empty input.
349
+ Visibility driven by DraftsPill (drafts-pill.js); hidden while typing.
350
+ ═════════════════════════════════════════════════════════════ */
351
+ .drafts-pill {
352
+ height: var(--layout-btn-xs);
353
+ flex-shrink: 0;
354
+ display: none;
355
+ align-items: center;
356
+ padding: 1px var(--sp-8);
357
+ font-size: var(--font-size-xs);
358
+ line-height: 1.5;
359
+ white-space: nowrap;
360
+ color: var(--text-muted);
361
+ background: var(--bg-secondary);
362
+ border: 1px solid var(--border-primary);
363
+ border-radius: var(--radius-sm);
364
+ cursor: pointer;
365
+ transition: color 0.15s ease, border-color 0.15s ease;
366
+ }
367
+
368
+ .drafts-pill.visible {
369
+ display: inline-flex;
370
+ }
371
+
372
+ .drafts-pill:hover {
373
+ color: var(--text-primary);
374
+ border-color: var(--accent);
375
+ }
376
+
377
+ /* Hide hints + drafts pill in shell/plan modes (redundant with JS suppression, belt-and-braces) */
378
+ #input-container.shell-mode .shortcut-hints-overlay,
379
+ #input-container.plan-input-mode .shortcut-hints-overlay,
380
+ #input-container.shell-mode .drafts-pill,
381
+ #input-container.plan-input-mode .drafts-pill {
382
+ display: none;
383
+ }
384
+
385
+ #message-input {
386
+ flex: 1;
387
+ min-width: 0;
388
+ background: transparent;
389
+ border: none;
390
+ /* Text color - switches to transparent when keywords detected */
391
+ color: var(--text-primary);
392
+ font-size: var(--font-size-md);
393
+ font-family: var(--font-sans);
394
+ resize: none;
395
+ min-height: var(--input-min-height, 24px);
396
+ max-height: 400px;
397
+ padding: var(--sp-2) 0;
398
+ line-height: 1.5;
399
+ /* Stack above backdrop */
400
+ position: relative;
401
+ z-index: 1;
402
+ /* Ensure it fills wrapper */
403
+ display: block;
404
+ box-sizing: border-box;
405
+ /* Reset any default margins */
406
+ margin: 0;
407
+ /* Ensure same word breaking as backdrop */
408
+ word-wrap: break-word;
409
+ overflow-wrap: break-word;
410
+ }
411
+
412
+ /* When input has thinking keywords, make text transparent so backdrop shows through */
413
+ #message-input.has-thinking-keywords {
414
+ color: transparent;
415
+ caret-color: var(--text-primary); /* Keep cursor visible */
416
+ }
417
+
418
+ /* Selection visibility when text is transparent */
419
+ #message-input.has-thinking-keywords::selection {
420
+ background: rgba(var(--accent-rgb), 0.3);
421
+ }
422
+
423
+ #message-input:focus {
424
+ outline: none;
425
+ }
426
+
427
+ #message-input::placeholder {
428
+ color: var(--text-muted);
429
+ }
430
+
431
+ /* Toolbar row - Slack-style layout with buttons below input */
432
+ #input-toolbar {
433
+ display: flex;
434
+ align-items: center;
435
+ justify-content: space-between;
436
+ gap: var(--layout-gap-md);
437
+ }
438
+
439
+ .toolbar-left {
440
+ display: flex;
441
+ align-items: center;
442
+ gap: var(--layout-gap-xs);
443
+ /* Allow scrolling on small screens */
444
+ overflow-x: auto;
445
+ overflow-y: hidden;
446
+ flex-shrink: 1;
447
+ min-width: 0;
448
+ scrollbar-width: none;
449
+ -ms-overflow-style: none;
450
+ }
451
+
452
+ .toolbar-left::-webkit-scrollbar {
453
+ display: none;
454
+ }
455
+
456
+ .toolbar-right {
457
+ display: flex;
458
+ align-items: center;
459
+ gap: var(--layout-gap-xs);
460
+ /* CRITICAL: Never shrink - send button must always be visible */
461
+ flex-shrink: 0;
462
+ }
463
+
464
+ .toolbar-btn {
465
+ position: relative;
466
+ width: var(--layout-btn-sm);
467
+ height: var(--layout-btn-sm);
468
+ border-radius: var(--radius-sm);
469
+ border: none;
470
+ background: transparent;
471
+ color: var(--text-tertiary);
472
+ cursor: pointer;
473
+ display: flex;
474
+ align-items: center;
475
+ justify-content: center;
476
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
477
+ }
478
+
479
+ .toolbar-btn:hover {
480
+ background: var(--bg-hover);
481
+ color: var(--text-secondary);
482
+ }
483
+
484
+ .toolbar-btn:active {
485
+ background: var(--bg-active);
486
+ }
487
+
488
+ .toolbar-btn.tab-cycle-active,
489
+ .toolbar-btn.trigger-armed {
490
+ background: var(--bg-active);
491
+ color: var(--accent);
492
+ box-shadow: inset 0 0 0 1px var(--accent);
493
+ }
494
+
495
+ .toolbar-btn svg {
496
+ width: 16px;
497
+ height: 16px;
498
+ }
499
+
500
+ /* Favorite prompt button - mark current prompt as favorite */
501
+ .favorite-prompt-btn {
502
+ position: relative;
503
+ }
504
+
505
+ .favorite-prompt-btn svg {
506
+ width: 18px;
507
+ height: 18px;
508
+ }
509
+
510
+ .favorite-prompt-btn .heart-outline {
511
+ display: block;
512
+ color: var(--text-muted);
513
+ }
514
+
515
+ .favorite-prompt-btn .heart-filled {
516
+ display: none;
517
+ color: #ff4757;
518
+ }
519
+
520
+ .favorite-prompt-btn:hover {
521
+ background: rgba(255, 71, 87, 0.1) !important;
522
+ }
523
+
524
+ .favorite-prompt-btn:hover .heart-outline {
525
+ color: #ff4757;
526
+ }
527
+
528
+ .favorite-prompt-btn.active .heart-outline {
529
+ display: none;
530
+ }
531
+
532
+ .favorite-prompt-btn.active .heart-filled {
533
+ display: block;
534
+ }
535
+
536
+ #send-btn {
537
+ width: var(--layout-btn-md);
538
+ height: var(--layout-btn-md);
539
+ border-radius: var(--radius-md);
540
+ border: none;
541
+ background: var(--accent);
542
+ color: white;
543
+ cursor: pointer;
544
+ display: flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
548
+ flex-shrink: 0;
549
+ }
550
+
551
+ #send-btn:hover {
552
+ background: var(--accent-hover);
553
+ }
554
+
555
+ #send-btn:disabled {
556
+ background: var(--bg-hover);
557
+ color: var(--text-muted);
558
+ cursor: not-allowed;
559
+ }
560
+
561
+ #send-btn svg {
562
+ width: 24px;
563
+ height: 24px;
564
+ }
565
+
566
+ #send-btn.hidden {
567
+ display: none;
568
+ }
569
+
570
+ /* Send pressed while an attachment was still uploading — the send is parked
571
+ until the upload lands, so keep the button reading as "working", not as the
572
+ dead grey of a plain :disabled. Overrides the :disabled rule above. */
573
+ #send-btn.awaiting-upload:disabled,
574
+ #send-btn.awaiting-upload {
575
+ background: var(--accent);
576
+ color: white;
577
+ cursor: progress;
578
+ opacity: 0.65;
579
+ animation: send-awaiting-pulse 1.2s ease-in-out infinite;
580
+ }
581
+
582
+ @keyframes send-awaiting-pulse {
583
+ 0%, 100% { opacity: 0.45; }
584
+ 50% { opacity: 0.85; }
585
+ }
586
+
587
+ @media (prefers-reduced-motion: reduce) {
588
+ #send-btn.awaiting-upload {
589
+ animation: none;
590
+ opacity: 0.65;
591
+ }
592
+ }
593
+
594
+ /* Follow-up button - shown when Claude is processing (queues message) */
595
+ #followup-btn {
596
+ width: var(--layout-btn-md);
597
+ height: var(--layout-btn-md);
598
+ border-radius: var(--radius-md);
599
+ border: none;
600
+ background: var(--accent);
601
+ color: white;
602
+ cursor: pointer;
603
+ display: none;
604
+ align-items: center;
605
+ justify-content: center;
606
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
607
+ flex-shrink: 0;
608
+ }
609
+
610
+ #followup-btn:hover {
611
+ background: var(--accent-hover);
612
+ }
613
+
614
+ #followup-btn:disabled {
615
+ background: var(--bg-hover);
616
+ color: var(--text-muted);
617
+ cursor: not-allowed;
618
+ }
619
+
620
+ #followup-btn svg {
621
+ width: 24px;
622
+ height: 24px;
623
+ }
624
+
625
+ #followup-btn.visible {
626
+ display: flex;
627
+ }
628
+
629
+ /* Stop button - shown when Claude is processing */
630
+ #stop-btn {
631
+ width: var(--layout-btn-md);
632
+ height: var(--layout-btn-md);
633
+ border-radius: var(--radius-md);
634
+ border: none;
635
+ background: #ef4444;
636
+ color: white;
637
+ cursor: pointer;
638
+ display: none;
639
+ align-items: center;
640
+ justify-content: center;
641
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
642
+ flex-shrink: 0;
643
+ }
644
+
645
+ #stop-btn.visible {
646
+ display: flex;
647
+ }
648
+
649
+ #stop-btn:hover {
650
+ background: #dc2626;
651
+ }
652
+
653
+ #stop-btn svg {
654
+ width: 22px;
655
+ height: 22px;
656
+ }
657
+
658
+ /* Combined status + hint bar */
659
+ #input-hint {
660
+ display: none; /* Hidden - merged into status bar */
661
+ }
662
+
663
+ #input-hint kbd {
664
+ background: var(--bg-tertiary);
665
+ padding: var(--sp-2) var(--sp-6);
666
+ border-radius: var(--radius-sm);
667
+ font-family: var(--font-mono);
668
+ font-size: 10px;
669
+ }
670
+
671
+ #status-bar {
672
+ display: flex;
673
+ align-items: center;
674
+ justify-content: space-between;
675
+ padding: var(--sp-4) var(--sp-4) 0;
676
+ font-size: var(--font-size-xs);
677
+ color: var(--text-muted);
678
+ }
679
+
680
+ #status-bar .status-left {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: var(--sp-8);
684
+ }
685
+
686
+ #status-bar .status-item {
687
+ display: flex;
688
+ align-items: center;
689
+ gap: var(--sp-3);
690
+ }
691
+
692
+ #status-bar #status-connection.connected { color: var(--success); }
693
+ #status-bar #status-connection.connecting { color: var(--warning); }
694
+ #status-bar #status-connection.disconnected { color: var(--error); }
695
+
696
+ /* Clickable when not connected — tap to force-reconnect.
697
+ The :hover rule works with iPadOS trackpads; gating with `(hover: hover)`
698
+ would never match on iPadOS even with a Magic Keyboard. See
699
+ docs-ai/2026-02-12-ipados-tooltip-behavior.md */
700
+ #status-bar #status-connection.clickable {
701
+ cursor: pointer;
702
+ /* A pill, not a circle — the element is a word now, not a dot. */
703
+ border-radius: var(--radius-sm);
704
+ padding: 0 var(--sp-4);
705
+ margin: 0 calc(var(--sp-4) * -1);
706
+ transition: background-color 120ms ease, transform 120ms ease;
707
+ }
708
+ #status-bar #status-connection.clickable:hover {
709
+ background-color: var(--bg-hover, rgba(255, 255, 255, 0.08));
710
+ }
711
+ #status-bar #status-connection.clickable:active {
712
+ transform: scale(0.92);
713
+ }
714
+ /* Subtle pulse on the disconnected dot so it reads as actionable
715
+ rather than passive. */
716
+ @keyframes status-connection-pulse {
717
+ 0%, 100% { opacity: 1; }
718
+ 50% { opacity: 0.55; }
719
+ }
720
+ #status-bar #status-connection.disconnected.clickable {
721
+ animation: status-connection-pulse 1.6s ease-in-out infinite;
722
+ }
723
+
724
+ /* Auto-journal pill — its own dot + word. Colours mirror the install
725
+ widget's status banner (.pair-* in 68-helpers-install-widget.css) so the
726
+ pill and the panel it opens read as one feature. The dot lives INSIDE the
727
+ pill: previously the only dot nearby was the connection indicator sitting
728
+ flush to the left, which made a green "connected" look like an
729
+ "auto-journal on" light. */
730
+ #status-bar #status-helpers {
731
+ font-size: 10px;
732
+ cursor: pointer;
733
+ padding: 0 var(--sp-4);
734
+ border-radius: var(--radius-sm);
735
+ gap: var(--sp-4);
736
+ }
737
+ #status-bar #status-helpers .status-pill-dot {
738
+ width: 6px;
739
+ height: 6px;
740
+ border-radius: 50%;
741
+ background: currentColor;
742
+ flex: 0 0 auto;
743
+ }
744
+ #status-bar #status-helpers.unknown { color: var(--text-muted); }
745
+ #status-bar #status-helpers.unknown .status-pill-dot { opacity: 0.4; }
746
+ #status-bar #status-helpers.recording { color: var(--success); }
747
+ #status-bar #status-helpers.basic { color: var(--text-muted); }
748
+ #status-bar #status-helpers.off { color: var(--text-muted); }
749
+ #status-bar #status-helpers.off .status-pill-dot { opacity: 0.4; }
750
+ #status-bar #status-helpers.outdated { color: var(--warning); }
751
+ #status-bar #status-helpers.missing { color: var(--error); }
752
+ #status-bar #status-helpers:hover {
753
+ background: var(--bg-elevated, rgba(255, 255, 255, 0.04));
754
+ }
755
+
756
+ #status-bar .status-item svg {
757
+ width: 8px;
758
+ height: 8px;
759
+ }
760
+
761
+ #status-bar .status-right {
762
+ display: flex;
763
+ align-items: center;
764
+ gap: var(--sp-8);
765
+ }
766
+
767
+ #status-bar .cost {
768
+ font-family: var(--font-mono);
769
+ }
770
+
771
+ /* Git branch in status bar */
772
+ #status-bar #status-branch {
773
+ display: none;
774
+ font-family: var(--font-mono);
775
+ font-size: 10px;
776
+ color: var(--text-muted);
777
+ }
778
+
779
+ #status-bar #status-branch.visible {
780
+ display: flex;
781
+ }
782
+
783
+ #status-bar #status-branch svg {
784
+ width: 12px;
785
+ height: 12px;
786
+ opacity: 0.7;
787
+ }
788
+
789
+ #status-bar #status-branch span {
790
+ white-space: nowrap;
791
+ }
792
+
793
+ /* Project name in status bar */
794
+ #status-bar #status-project {
795
+ display: none;
796
+ font-family: var(--font-mono);
797
+ font-size: 10px;
798
+ color: var(--text-muted);
799
+ cursor: pointer;
800
+ padding: var(--sp-2) var(--sp-6);
801
+ border-radius: var(--radius-sm);
802
+ transition: background 0.15s, color 0.15s;
803
+ max-width: 180px;
804
+ }
805
+
806
+ #status-bar #status-project.visible {
807
+ display: flex;
808
+ }
809
+
810
+ #status-bar #status-project:hover {
811
+ background: var(--bg-tertiary);
812
+ color: var(--text-secondary);
813
+ }
814
+
815
+ #status-bar #status-project svg {
816
+ width: 12px;
817
+ height: 12px;
818
+ opacity: 0.7;
819
+ flex-shrink: 0;
820
+ }
821
+
822
+ #status-bar #status-project span {
823
+ white-space: nowrap;
824
+ overflow: hidden;
825
+ text-overflow: ellipsis;
826
+ }
827
+
828
+ #status-bar #status-project .project-sep {
829
+ opacity: 0.4;
830
+ margin: 0 var(--sp-2);
831
+ }
832
+
833
+ /* Model selector in status bar */
834
+ #status-bar #status-model.clickable {
835
+ cursor: pointer;
836
+ padding: var(--sp-2) var(--sp-6);
837
+ border-radius: var(--radius-sm);
838
+ transition: background 0.15s, color 0.15s;
839
+ color: var(--accent);
840
+ }
841
+
842
+ #status-bar #status-model.clickable:hover {
843
+ background: var(--bg-tertiary);
844
+ color: var(--text-primary);
845
+ }
846
+
847
+ .model-popup {
848
+ position: fixed;
849
+ z-index: var(--z-always-top);
850
+ background: var(--bg-secondary);
851
+ border: 1px solid var(--border-color);
852
+ border-radius: var(--radius-md);
853
+ padding: var(--sp-4);
854
+ min-width: 180px;
855
+ box-shadow: 0 4px 16px rgba(0,0,0,0.4);
856
+ opacity: 0;
857
+ transform: translateY(4px);
858
+ transition: opacity 0.12s, transform 0.12s;
859
+ pointer-events: none;
860
+ }
861
+
862
+ .model-popup.open {
863
+ opacity: 1;
864
+ transform: translateY(0);
865
+ pointer-events: auto;
866
+ }
867
+
868
+ .model-option {
869
+ display: flex;
870
+ align-items: baseline;
871
+ gap: var(--sp-8);
872
+ padding: var(--sp-6) var(--sp-10);
873
+ line-height: var(--layout-menu-line-height);
874
+ border-radius: var(--radius-md);
875
+ cursor: pointer;
876
+ transition: background 0.1s;
877
+ }
878
+
879
+ .model-option:hover {
880
+ background: var(--bg-tertiary);
881
+ }
882
+
883
+ .model-option.selected {
884
+ background: var(--bg-tertiary);
885
+ color: var(--accent);
886
+ }
887
+
888
+ .model-option-label {
889
+ font-size: var(--font-size-sm);
890
+ font-weight: 500;
891
+ color: inherit;
892
+ }
893
+
894
+ .model-option-desc {
895
+ font-size: 10px;
896
+ color: var(--text-muted);
897
+ }
898
+
899
+ .model-popup-footer {
900
+ border-top: 1px solid var(--border-color);
901
+ margin-top: var(--sp-4);
902
+ padding: var(--sp-6) var(--sp-4) var(--sp-2);
903
+ }
904
+
905
+ .model-set-default {
906
+ width: 100%;
907
+ background: none;
908
+ border: none;
909
+ color: var(--text-muted);
910
+ font-size: var(--font-size-xs);
911
+ padding: var(--sp-4) var(--sp-6);
912
+ border-radius: var(--radius-sm);
913
+ cursor: pointer;
914
+ text-align: left;
915
+ transition: background 0.1s, color 0.1s;
916
+ }
917
+
918
+ .model-set-default:hover {
919
+ background: var(--bg-tertiary);
920
+ color: var(--text-secondary);
921
+ }
922
+
923
+ /* ── Engine (provider) chip + picker — mirrors the model popup ── */
924
+
925
+ #status-bar #status-engine.clickable {
926
+ cursor: pointer;
927
+ display: inline-flex;
928
+ align-items: center;
929
+ gap: var(--sp-4);
930
+ padding: var(--sp-2) var(--sp-6);
931
+ border-radius: var(--radius-sm);
932
+ transition: background 0.15s, color 0.15s;
933
+ color: var(--text-secondary);
934
+ }
935
+
936
+ .engine-popup .model-option-label {
937
+ display: inline-flex;
938
+ align-items: center;
939
+ gap: var(--sp-5);
940
+ }
941
+
942
+ #status-bar #status-engine.clickable:hover {
943
+ background: var(--bg-tertiary);
944
+ color: var(--text-primary);
945
+ }
946
+
947
+ /* Tab not yet bound — choice still open, hint that it's actionable */
948
+ #status-bar #status-engine.pending {
949
+ color: var(--accent);
950
+ font-style: italic;
951
+ }
952
+
953
+ .engine-popup {
954
+ min-width: 240px;
955
+ max-width: 340px;
956
+ }
957
+
958
+ /* Engine rows stack label over the (longer) self-description */
959
+ .engine-popup .engine-option {
960
+ flex-direction: column;
961
+ align-items: flex-start;
962
+ gap: 1px;
963
+ }
964
+
965
+ .engine-popup .engine-option.unavailable {
966
+ opacity: 0.45;
967
+ cursor: default;
968
+ }
969
+
970
+ .engine-popup .engine-option.unavailable:hover {
971
+ background: none;
972
+ }
973
+
974
+ .engine-popup-note {
975
+ font-size: 10px;
976
+ color: var(--text-muted);
977
+ padding: var(--sp-4) var(--sp-10) var(--sp-6);
978
+ border-bottom: 1px solid var(--border-color);
979
+ margin-bottom: var(--sp-4);
980
+ max-width: 260px;
981
+ }
982
+
983
+ .engine-set-default {
984
+ width: 100%;
985
+ background: none;
986
+ border: none;
987
+ color: var(--text-muted);
988
+ font-size: var(--font-size-xs);
989
+ padding: var(--sp-4) var(--sp-6);
990
+ border-radius: var(--radius-sm);
991
+ cursor: pointer;
992
+ text-align: left;
993
+ transition: background 0.1s, color 0.1s;
994
+ }
995
+
996
+ .engine-set-default:hover:not(:disabled) {
997
+ background: var(--bg-tertiary);
998
+ color: var(--text-secondary);
999
+ }
1000
+
1001
+ .engine-set-default:disabled {
1002
+ opacity: 0.5;
1003
+ cursor: default;
1004
+ }
1005
+
1006
+ /* Keyboard hints shown in status bar */
1007
+ #status-bar .kbd-hints {
1008
+ display: flex;
1009
+ align-items: center;
1010
+ gap: var(--sp-4);
1011
+ }
1012
+
1013
+ #status-bar .kbd-hints kbd {
1014
+ background: var(--bg-tertiary);
1015
+ padding: 1px var(--sp-5);
1016
+ border-radius: var(--radius-sm);
1017
+ font-family: var(--font-mono);
1018
+ font-size: 10px;
1019
+ }
1020
+
1021
+ #status-bar .kbd-hints .hint-sep {
1022
+ color: var(--text-muted);
1023
+ opacity: 0.5;
1024
+ }
1025
+
1026
+ /* Token meter */
1027
+ #status-bar .token-meter {
1028
+ display: none;
1029
+ align-items: center;
1030
+ gap: var(--sp-6);
1031
+ cursor: help;
1032
+ }
1033
+
1034
+ #status-bar .token-meter.visible {
1035
+ display: flex;
1036
+ }
1037
+
1038
+ #status-bar .token-bar {
1039
+ width: 60px;
1040
+ height: 6px;
1041
+ background: var(--bg-tertiary);
1042
+ border-radius: var(--radius-sm);
1043
+ overflow: hidden;
1044
+ }
1045
+
1046
+ #status-bar .token-fill {
1047
+ display: block;
1048
+ width: 0;
1049
+ height: 100%;
1050
+ background: var(--success);
1051
+ border-radius: var(--radius-sm);
1052
+ transition: width 0.3s ease, background-color 0.3s ease;
1053
+ }
1054
+
1055
+ #status-bar .token-fill.warning {
1056
+ background: var(--warning, #f59e0b);
1057
+ }
1058
+
1059
+ #status-bar .token-fill.danger {
1060
+ background: var(--error);
1061
+ }
1062
+
1063
+ #status-bar .token-text {
1064
+ font-family: var(--font-mono);
1065
+ font-size: 10px;
1066
+ white-space: nowrap;
1067
+ color: var(--success);
1068
+ }
1069
+
1070
+ #status-bar .token-text.warning {
1071
+ color: var(--warning);
1072
+ }
1073
+
1074
+ #status-bar .token-text.danger {
1075
+ color: var(--error);
1076
+ }
1077
+
1078
+ /* ═══════════════════════════════════════════════════════════════════
1079
+ TOOLBAR SEPARATOR
1080
+ ═══════════════════════════════════════════════════════════════════ */
1081
+ .toolbar-separator {
1082
+ width: 1px;
1083
+ height: 18px;
1084
+ background: var(--border-primary);
1085
+ margin: 0 var(--sp-4);
1086
+ opacity: 0.6;
1087
+ }
1088
+
1089
+ /* ═══════════════════════════════════════════════════════════════════
1090
+ CHAT SEARCH BAR
1091
+ ═══════════════════════════════════════════════════════════════════ */
1092
+ .chat-search-bar {
1093
+ display: none;
1094
+ align-items: center;
1095
+ gap: var(--sp-8);
1096
+ padding: var(--sp-8) var(--sp-12);
1097
+ background: var(--bg-secondary);
1098
+ border-bottom: 1px solid var(--border-primary);
1099
+ flex-shrink: 0;
1100
+ }
1101
+
1102
+ .chat-search-bar.visible {
1103
+ display: flex;
1104
+ }
1105
+
1106
+ .chat-search-input-wrap {
1107
+ flex: 1;
1108
+ display: flex;
1109
+ align-items: center;
1110
+ gap: var(--sp-8);
1111
+ background: var(--bg-primary);
1112
+ border: 1px solid var(--border-primary);
1113
+ border-radius: var(--radius-md);
1114
+ padding: var(--sp-6) var(--sp-10);
1115
+ transition: border-color 0.15s ease;
1116
+ }
1117
+
1118
+ .chat-search-input-wrap:focus-within {
1119
+ border-color: var(--accent);
1120
+ }
1121
+
1122
+ .chat-search-icon {
1123
+ width: 16px;
1124
+ height: 16px;
1125
+ color: var(--text-muted);
1126
+ flex-shrink: 0;
1127
+ }
1128
+
1129
+ #chat-search-input {
1130
+ flex: 1;
1131
+ border: none;
1132
+ background: transparent;
1133
+ color: var(--text-primary);
1134
+ font-size: var(--font-size-md);
1135
+ font-family: var(--font-sans);
1136
+ outline: none;
1137
+ min-width: 0;
1138
+ }
1139
+
1140
+ #chat-search-input::placeholder {
1141
+ color: var(--text-muted);
1142
+ }
1143
+
1144
+ .chat-search-count {
1145
+ font-size: var(--font-size-sm);
1146
+ color: var(--text-muted);
1147
+ font-family: var(--font-mono);
1148
+ white-space: nowrap;
1149
+ }
1150
+
1151
+ .chat-search-count:empty {
1152
+ display: none;
1153
+ }
1154
+
1155
+ .chat-search-count.no-results {
1156
+ color: var(--error);
1157
+ }
1158
+
1159
+ .chat-search-nav {
1160
+ display: flex;
1161
+ align-items: center;
1162
+ gap: var(--sp-2);
1163
+ }
1164
+
1165
+ .chat-search-nav-btn,
1166
+ .chat-search-close-btn {
1167
+ position: relative;
1168
+ width: var(--layout-btn-sm);
1169
+ height: var(--layout-btn-sm);
1170
+ border: none;
1171
+ border-radius: var(--radius-sm);
1172
+ background: transparent;
1173
+ color: var(--text-muted);
1174
+ cursor: pointer;
1175
+ display: flex;
1176
+ align-items: center;
1177
+ justify-content: center;
1178
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
1179
+ }
1180
+
1181
+ .chat-search-nav-btn:hover,
1182
+ .chat-search-close-btn:hover {
1183
+ background: var(--bg-hover);
1184
+ color: var(--text-primary);
1185
+ }
1186
+
1187
+ .chat-search-nav-btn:disabled {
1188
+ opacity: 0.4;
1189
+ cursor: not-allowed;
1190
+ }
1191
+
1192
+ .chat-search-nav-btn:disabled:hover {
1193
+ background: transparent;
1194
+ color: var(--text-muted);
1195
+ }
1196
+
1197
+ .chat-search-nav-btn svg,
1198
+ .chat-search-close-btn svg {
1199
+ width: 16px;
1200
+ height: 16px;
1201
+ }
1202
+
1203
+ /* Search match highlighting in messages */
1204
+ .search-highlight {
1205
+ background-color: rgba(255, 200, 50, 0.35);
1206
+ border-radius: var(--radius-xs);
1207
+ padding: 0 1px;
1208
+ }
1209
+
1210
+ .search-highlight.current {
1211
+ background-color: rgba(255, 200, 50, 0.6);
1212
+ outline: 2px solid rgba(255, 180, 0, 0.8);
1213
+ }
1214
+
1215
+ /* Mobile adjustments */
1216
+ @media (max-width: 768px) {
1217
+ #input-area {
1218
+ padding: var(--sp-6) var(--sp-12);
1219
+ }
1220
+
1221
+ #status-bar .kbd-hints {
1222
+ display: none;
1223
+ }
1224
+
1225
+ .chat-search-bar {
1226
+ padding: var(--sp-6) var(--sp-10);
1227
+ gap: var(--sp-6);
1228
+ }
1229
+
1230
+ .chat-search-input-wrap {
1231
+ padding: var(--sp-5) var(--sp-8);
1232
+ gap: var(--sp-6);
1233
+ }
1234
+
1235
+ #chat-search-input {
1236
+ font-size: var(--font-size-base);
1237
+ }
1238
+
1239
+ .chat-search-count {
1240
+ font-size: var(--font-size-xs);
1241
+ }
1242
+
1243
+ .chat-search-nav-btn,
1244
+ .chat-search-close-btn {
1245
+ width: 26px;
1246
+ height: 26px;
1247
+ }
1248
+
1249
+ /* Hide secondary toolbar buttons on mobile */
1250
+ .toolbar-secondary {
1251
+ display: none !important;
1252
+ }
1253
+
1254
+ /* Simplify toolbar spacing on mobile */
1255
+ .toolbar-separator {
1256
+ display: none;
1257
+ }
1258
+
1259
+ /* Tighter toolbar gap on mobile to fit more icons */
1260
+ .toolbar-left,
1261
+ .toolbar-right {
1262
+ gap: var(--sp-2);
1263
+ }
1264
+
1265
+ /* Larger touch targets on mobile/tablet */
1266
+ .toolbar-btn {
1267
+ width: var(--layout-btn-md);
1268
+ height: var(--layout-btn-md);
1269
+ }
1270
+
1271
+ #input-toolbar .toolbar-btn {
1272
+ margin-right: 0;
1273
+ }
1274
+
1275
+ .toolbar-btn svg,
1276
+ #input-toolbar .toolbar-btn svg {
1277
+ width: 20px;
1278
+ height: 20px;
1279
+ }
1280
+
1281
+ /* Hide shortcut hints — shortcuts need a physical keyboard, and the
1282
+ taller textarea eats scarce vertical space on small screens. */
1283
+ .shortcut-hints-overlay {
1284
+ display: none;
1285
+ }
1286
+ .input-textarea-wrapper.has-hints #message-input {
1287
+ min-height: var(--input-min-height, 24px);
1288
+ }
1289
+ }
1290
+
1291
+ /* ═══════════════════════════════════════════════════════════════════
1292
+ STASH BUTTON & PICKER
1293
+ ═══════════════════════════════════════════════════════════════════ */
1294
+
1295
+ /* Stash button with badge */
1296
+ #stash-btn {
1297
+ position: relative;
1298
+ }
1299
+
1300
+ #stash-btn.has-items {
1301
+ color: var(--accent);
1302
+ }
1303
+
1304
+ .stash-badge {
1305
+ position: absolute;
1306
+ top: -4px;
1307
+ right: -4px;
1308
+ display: none;
1309
+ align-items: center;
1310
+ justify-content: center;
1311
+ min-width: 16px;
1312
+ height: 16px;
1313
+ padding: 0 var(--sp-4);
1314
+ font-size: 10px;
1315
+ font-weight: 700;
1316
+ background: var(--accent);
1317
+ color: white;
1318
+ border-radius: var(--radius-md);
1319
+ line-height: 1;
1320
+ box-sizing: border-box;
1321
+ }
1322
+
1323
+ .stash-badge:not(:empty) {
1324
+ display: flex;
1325
+ }
1326
+
1327
+ /* Stash picker dropdown */
1328
+ .stash-picker {
1329
+ position: absolute;
1330
+ bottom: 100%;
1331
+ left: 0;
1332
+ margin-bottom: 8px;
1333
+ width: 480px;
1334
+ max-width: calc(100vw - 24px);
1335
+ max-height: 400px;
1336
+ background: var(--bg-secondary);
1337
+ border: 1px solid var(--border-primary);
1338
+ border-radius: var(--radius-lg);
1339
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
1340
+ display: none;
1341
+ flex-direction: column;
1342
+ overflow: hidden;
1343
+ z-index: 1000;
1344
+ }
1345
+
1346
+ .stash-picker.visible {
1347
+ display: flex;
1348
+ }
1349
+
1350
+ /* Header mirrors .widget-header sizing so it tracks the density modes */
1351
+ .stash-picker-header {
1352
+ display: flex;
1353
+ align-items: center;
1354
+ justify-content: space-between;
1355
+ height: var(--layout-widget-header-height);
1356
+ flex-shrink: 0;
1357
+ padding: 0 var(--sp-12);
1358
+ border-bottom: 1px solid var(--border-primary);
1359
+ background: var(--bg-tertiary);
1360
+ }
1361
+
1362
+ .stash-picker-title {
1363
+ font-size: var(--font-size-base);
1364
+ font-weight: 500;
1365
+ color: var(--text-primary);
1366
+ }
1367
+
1368
+ .stash-picker-actions {
1369
+ display: flex;
1370
+ gap: var(--sp-8);
1371
+ }
1372
+
1373
+ .stash-picker-clear {
1374
+ font-size: var(--font-size-xs);
1375
+ color: var(--text-muted);
1376
+ background: none;
1377
+ border: none;
1378
+ cursor: pointer;
1379
+ padding: var(--sp-4) var(--sp-8);
1380
+ border-radius: var(--radius-sm);
1381
+ }
1382
+
1383
+ .stash-picker-clear:hover {
1384
+ background: var(--bg-hover);
1385
+ color: var(--error);
1386
+ }
1387
+
1388
+ /* Two-click confirm: first click arms (red), second commits */
1389
+ .stash-picker-clear.armed {
1390
+ background: rgba(239, 68, 68, 0.12);
1391
+ color: var(--error);
1392
+ font-weight: 600;
1393
+ }
1394
+
1395
+ .stash-picker-close {
1396
+ width: 24px;
1397
+ height: 24px;
1398
+ display: flex;
1399
+ align-items: center;
1400
+ justify-content: center;
1401
+ background: none;
1402
+ border: none;
1403
+ color: var(--text-muted);
1404
+ cursor: pointer;
1405
+ border-radius: var(--radius-sm);
1406
+ }
1407
+
1408
+ .stash-picker-close:hover {
1409
+ background: var(--bg-hover);
1410
+ color: var(--text-primary);
1411
+ }
1412
+
1413
+ .stash-picker-close svg {
1414
+ width: 14px;
1415
+ height: 14px;
1416
+ }
1417
+
1418
+ .stash-picker-list {
1419
+ overflow-y: auto;
1420
+ flex: 1;
1421
+ padding: var(--sp-6);
1422
+ }
1423
+
1424
+ .stash-picker-empty {
1425
+ padding: var(--sp-24);
1426
+ text-align: center;
1427
+ color: var(--text-muted);
1428
+ font-size: var(--font-size-base);
1429
+ }
1430
+
1431
+ /* Stash item in picker */
1432
+ .stash-item {
1433
+ display: flex;
1434
+ align-items: flex-start;
1435
+ gap: var(--sp-8);
1436
+ padding: var(--sp-6) var(--sp-8);
1437
+ border-radius: var(--radius-md);
1438
+ cursor: pointer;
1439
+ transition: background 0.15s ease;
1440
+ }
1441
+
1442
+ .stash-item + .stash-item {
1443
+ margin-top: var(--sp-2);
1444
+ }
1445
+
1446
+ .stash-item:hover {
1447
+ background: var(--bg-hover);
1448
+ }
1449
+
1450
+ .stash-item.disabled {
1451
+ opacity: 0.5;
1452
+ }
1453
+
1454
+ .stash-item-check {
1455
+ width: 18px;
1456
+ height: 18px;
1457
+ flex-shrink: 0;
1458
+ margin-top: 1px;
1459
+ border: 2px solid var(--border-primary);
1460
+ border-radius: var(--radius-sm);
1461
+ display: flex;
1462
+ align-items: center;
1463
+ justify-content: center;
1464
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
1465
+ }
1466
+
1467
+ .stash-item:not(.disabled) .stash-item-check {
1468
+ background: var(--accent);
1469
+ border-color: var(--accent);
1470
+ color: white;
1471
+ }
1472
+
1473
+ .stash-item-check:hover {
1474
+ border-color: var(--accent);
1475
+ filter: brightness(1.15);
1476
+ }
1477
+
1478
+ .stash-item-check svg {
1479
+ width: 12px;
1480
+ height: 12px;
1481
+ }
1482
+
1483
+ .stash-item-content {
1484
+ flex: 1;
1485
+ min-width: 0;
1486
+ }
1487
+
1488
+ .stash-item-anchor {
1489
+ font-size: var(--font-size-sm);
1490
+ font-weight: 500;
1491
+ line-height: 18px; /* optically centers against the 18px checkbox */
1492
+ color: var(--accent);
1493
+ font-family: var(--font-mono);
1494
+ white-space: nowrap;
1495
+ overflow: hidden;
1496
+ text-overflow: ellipsis;
1497
+ }
1498
+
1499
+ /* Quoted selection — neutral quote bar, reads as "what was selected" */
1500
+ .stash-item-text {
1501
+ font-size: var(--font-size-sm);
1502
+ color: var(--text-secondary);
1503
+ margin-top: var(--sp-4);
1504
+ padding-left: var(--sp-8);
1505
+ border-left: 2px solid var(--border-primary);
1506
+ display: -webkit-box;
1507
+ -webkit-line-clamp: 2;
1508
+ -webkit-box-orient: vertical;
1509
+ overflow: hidden;
1510
+ line-height: 1.4;
1511
+ }
1512
+
1513
+ /* User's note — sits BELOW the quote it comments on, accent-tinted */
1514
+ .stash-item-note {
1515
+ display: flex;
1516
+ align-items: flex-start;
1517
+ gap: var(--sp-6);
1518
+ font-size: var(--font-size-sm);
1519
+ font-style: italic;
1520
+ color: var(--text-primary);
1521
+ margin-top: var(--sp-6);
1522
+ padding: var(--sp-4) var(--sp-8);
1523
+ background: rgba(139, 92, 246, 0.08);
1524
+ border-radius: var(--radius-sm);
1525
+ cursor: pointer;
1526
+ line-height: 1.4;
1527
+ }
1528
+
1529
+ .stash-item-note:hover {
1530
+ background: rgba(139, 92, 246, 0.15);
1531
+ }
1532
+
1533
+ .stash-item-note svg {
1534
+ width: 12px;
1535
+ height: 12px;
1536
+ flex-shrink: 0;
1537
+ margin-top: 2px;
1538
+ color: #a78bfa; /* stash purple — matches the bubble/indicator family */
1539
+ }
1540
+
1541
+ .stash-item-add-note {
1542
+ font-size: var(--font-size-xs);
1543
+ color: var(--text-muted);
1544
+ background: none;
1545
+ border: 1px dashed var(--border-primary);
1546
+ border-radius: var(--radius-sm);
1547
+ padding: var(--sp-3) var(--sp-8);
1548
+ margin-top: var(--sp-6);
1549
+ cursor: pointer;
1550
+ }
1551
+
1552
+ .stash-item-add-note:hover {
1553
+ color: var(--accent);
1554
+ border-color: var(--accent);
1555
+ background: rgba(139, 92, 246, 0.05);
1556
+ }
1557
+
1558
+ .stash-item-actions {
1559
+ display: flex;
1560
+ flex-direction: column;
1561
+ align-items: center;
1562
+ gap: var(--sp-2);
1563
+ flex-shrink: 0;
1564
+ }
1565
+
1566
+ .stash-item-remove {
1567
+ width: 20px;
1568
+ height: 20px;
1569
+ flex-shrink: 0;
1570
+ display: flex;
1571
+ align-items: center;
1572
+ justify-content: center;
1573
+ background: none;
1574
+ border: none;
1575
+ color: var(--text-muted);
1576
+ cursor: pointer;
1577
+ border-radius: var(--radius-sm);
1578
+ opacity: 0;
1579
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
1580
+ }
1581
+
1582
+ .stash-item:hover .stash-item-remove {
1583
+ opacity: 1;
1584
+ }
1585
+
1586
+ .stash-item-remove:hover {
1587
+ background: rgba(239, 68, 68, 0.1);
1588
+ color: var(--error);
1589
+ }
1590
+
1591
+ .stash-item-remove svg {
1592
+ width: 12px;
1593
+ height: 12px;
1594
+ }
1595
+
1596
+ .stash-item-open {
1597
+ width: 20px;
1598
+ height: 20px;
1599
+ flex-shrink: 0;
1600
+ display: flex;
1601
+ align-items: center;
1602
+ justify-content: center;
1603
+ background: none;
1604
+ border: none;
1605
+ color: var(--text-muted);
1606
+ cursor: pointer;
1607
+ border-radius: var(--radius-sm);
1608
+ opacity: 0;
1609
+ transition: background-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
1610
+ }
1611
+
1612
+ .stash-item:hover .stash-item-open {
1613
+ opacity: 1;
1614
+ }
1615
+
1616
+ .stash-item-open:hover {
1617
+ background: rgba(139, 92, 246, 0.1);
1618
+ color: var(--accent);
1619
+ }
1620
+
1621
+ .stash-item-open svg {
1622
+ width: 12px;
1623
+ height: 12px;
1624
+ }
1625
+
1626
+ /* Touch devices have no hover — keep the item action buttons visible */
1627
+ @media (hover: none) and (pointer: coarse) {
1628
+ .stash-item-open,
1629
+ .stash-item-remove {
1630
+ opacity: 1;
1631
+ }
1632
+ }
1633
+
1634
+ /* ─── Sent history (collapsible section below pending items) ─── */
1635
+
1636
+ .stash-history {
1637
+ margin-top: var(--sp-8);
1638
+ border-top: 1px solid var(--border-secondary);
1639
+ padding-top: var(--sp-4);
1640
+ }
1641
+
1642
+ .stash-history-header {
1643
+ display: flex;
1644
+ align-items: center;
1645
+ gap: var(--sp-6);
1646
+ width: 100%;
1647
+ padding: var(--sp-6) var(--sp-8);
1648
+ background: none;
1649
+ border: none;
1650
+ color: var(--text-muted);
1651
+ font-size: var(--font-size-xs);
1652
+ font-weight: 600;
1653
+ text-transform: uppercase;
1654
+ letter-spacing: 0.5px;
1655
+ cursor: pointer;
1656
+ border-radius: var(--radius-sm);
1657
+ transition: background 0.15s ease, color 0.15s ease;
1658
+ }
1659
+
1660
+ .stash-history-header:hover {
1661
+ background: var(--bg-tertiary);
1662
+ color: var(--text-secondary);
1663
+ }
1664
+
1665
+ .stash-history-chevron {
1666
+ width: 12px;
1667
+ height: 12px;
1668
+ transition: transform 0.15s ease;
1669
+ }
1670
+
1671
+ .stash-history-chevron.expanded {
1672
+ transform: rotate(90deg);
1673
+ }
1674
+
1675
+ .stash-history-count {
1676
+ margin-left: auto;
1677
+ font-weight: 500;
1678
+ opacity: 0.8;
1679
+ }
1680
+
1681
+ .stash-history-item {
1682
+ opacity: 0.75;
1683
+ cursor: pointer;
1684
+ }
1685
+
1686
+ .stash-history-item:hover {
1687
+ opacity: 1;
1688
+ }
1689
+
1690
+ .stash-history-time {
1691
+ color: var(--text-muted);
1692
+ font-weight: 400;
1693
+ }
1694
+
1695
+ .stash-history-clear {
1696
+ display: block;
1697
+ margin: var(--sp-6) auto var(--sp-2);
1698
+ padding: var(--sp-4) var(--sp-12);
1699
+ background: none;
1700
+ border: 1px solid var(--border-secondary);
1701
+ border-radius: var(--radius-sm);
1702
+ color: var(--text-muted);
1703
+ font-size: var(--font-size-xs);
1704
+ cursor: pointer;
1705
+ transition: all 0.15s ease;
1706
+ }
1707
+
1708
+ .stash-history-clear:hover {
1709
+ color: var(--text-secondary);
1710
+ border-color: var(--border-primary);
1711
+ }
1712
+
1713
+ .stash-history-clear.armed {
1714
+ background: rgba(239, 68, 68, 0.12);
1715
+ border-color: #ef4444;
1716
+ color: #ef4444;
1717
+ }
1718
+
1719
+ /* Recall hint — what the history entry under the cursor was sent with.
1720
+ Read-only sibling of .stash-preview: same column, deliberately quieter
1721
+ (no border, muted text) so it reads as annotation, not as pending state. */
1722
+ .history-refs-hint {
1723
+ display: none;
1724
+ margin-bottom: var(--sp-8);
1725
+ }
1726
+
1727
+ .history-refs-hint.visible {
1728
+ display: block;
1729
+ }
1730
+
1731
+ /* Reuses the message bubble's .stash-refs-* block (20-messages.css) so the
1732
+ recalled prompt previews its references exactly as the sent one did. */
1733
+ .history-refs-hint .stash-refs-block {
1734
+ margin: 0;
1735
+ }
1736
+
1737
+ /* A prompt can carry a large stash — keep the preview from pushing the
1738
+ textarea off screen. */
1739
+ .history-refs-hint .stash-refs-content {
1740
+ max-height: 30vh;
1741
+ overflow-y: auto;
1742
+ }
1743
+
1744
+ .history-refs-reattach {
1745
+ margin-top: var(--sp-4);
1746
+ padding: var(--sp-4) var(--sp-10);
1747
+ border: 1px solid rgba(139, 92, 246, 0.35);
1748
+ border-radius: var(--radius-sm);
1749
+ background: rgba(139, 92, 246, 0.10);
1750
+ color: var(--accent);
1751
+ font-size: var(--font-size-xs);
1752
+ font-family: inherit;
1753
+ cursor: pointer;
1754
+ transition: background 0.15s ease, border-color 0.15s ease;
1755
+ }
1756
+
1757
+ .history-refs-reattach:hover {
1758
+ background: rgba(139, 92, 246, 0.18);
1759
+ border-color: rgba(139, 92, 246, 0.55);
1760
+ }
1761
+
1762
+ /* Stash preview in input (shown above input when items exist) */
1763
+ .stash-preview {
1764
+ display: none;
1765
+ align-items: center;
1766
+ gap: var(--sp-8);
1767
+ padding: var(--sp-8) var(--sp-12);
1768
+ margin-bottom: var(--sp-8);
1769
+ background: rgba(139, 92, 246, 0.08);
1770
+ border: 1px solid rgba(139, 92, 246, 0.2);
1771
+ border-radius: var(--radius-md);
1772
+ font-size: var(--font-size-sm);
1773
+ color: var(--text-secondary);
1774
+ transition: opacity 0.15s ease, background 0.15s ease;
1775
+ }
1776
+
1777
+ .stash-preview.visible {
1778
+ display: flex;
1779
+ }
1780
+
1781
+ /* Paused state - dimmed appearance */
1782
+ .stash-preview.paused {
1783
+ background: rgba(139, 92, 246, 0.03);
1784
+ border-color: rgba(139, 92, 246, 0.1);
1785
+ }
1786
+
1787
+ .stash-preview.paused .stash-preview-icon,
1788
+ .stash-preview.paused .stash-preview-count {
1789
+ color: var(--text-muted);
1790
+ }
1791
+
1792
+ .stash-preview.paused .stash-preview-text {
1793
+ color: var(--text-muted);
1794
+ text-decoration: line-through;
1795
+ text-decoration-color: var(--text-muted);
1796
+ }
1797
+
1798
+ .stash-preview-icon {
1799
+ color: var(--accent);
1800
+ display: flex;
1801
+ align-items: center;
1802
+ justify-content: center;
1803
+ }
1804
+
1805
+ .stash-preview-icon svg {
1806
+ width: 14px;
1807
+ height: 14px;
1808
+ }
1809
+
1810
+ .stash-preview-text {
1811
+ cursor: pointer;
1812
+ border-radius: var(--radius-sm);
1813
+ padding: var(--sp-2) var(--sp-6);
1814
+ margin: -2px -6px;
1815
+ transition: background 0.15s ease;
1816
+ }
1817
+
1818
+ .stash-preview-text:hover {
1819
+ background: var(--bg-hover);
1820
+ }
1821
+
1822
+ .stash-preview-count {
1823
+ font-weight: 600;
1824
+ color: var(--accent);
1825
+ }
1826
+
1827
+ /* Toggle switch - iOS style */
1828
+ .stash-toggle {
1829
+ position: relative;
1830
+ display: inline-flex;
1831
+ align-items: center;
1832
+ cursor: pointer;
1833
+ user-select: none;
1834
+ }
1835
+
1836
+ .stash-toggle input {
1837
+ position: absolute;
1838
+ opacity: 0;
1839
+ width: 0;
1840
+ height: 0;
1841
+ }
1842
+
1843
+ .stash-toggle-track {
1844
+ width: 32px;
1845
+ height: 18px;
1846
+ background: var(--text-muted);
1847
+ border-radius: var(--radius-lg);
1848
+ position: relative;
1849
+ transition: background 0.2s ease;
1850
+ }
1851
+
1852
+ .stash-toggle-thumb {
1853
+ position: absolute;
1854
+ top: 2px;
1855
+ left: 2px;
1856
+ width: 14px;
1857
+ height: 14px;
1858
+ background: white;
1859
+ border-radius: 50%;
1860
+ transition: transform 0.2s ease;
1861
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
1862
+ }
1863
+
1864
+ /* Checked state - green and thumb slides right */
1865
+ .stash-toggle input:checked + .stash-toggle-track {
1866
+ background: var(--success, #22c55e);
1867
+ }
1868
+
1869
+ .stash-toggle input:checked + .stash-toggle-track .stash-toggle-thumb {
1870
+ transform: translateX(14px);
1871
+ }
1872
+
1873
+ /* Hover effect */
1874
+ .stash-toggle:hover .stash-toggle-track {
1875
+ opacity: 0.9;
1876
+ }
1877
+
1878
+ /* Focus ring for accessibility */
1879
+ .stash-toggle input:focus-visible + .stash-toggle-track {
1880
+ outline: 2px solid var(--accent);
1881
+ outline-offset: 2px;
1882
+ }
1883
+
1884
+ .stash-preview-edit {
1885
+ font-size: var(--font-size-xs);
1886
+ color: var(--accent);
1887
+ background: none;
1888
+ border: none;
1889
+ cursor: pointer;
1890
+ padding: var(--sp-4) var(--sp-8);
1891
+ border-radius: var(--radius-sm);
1892
+ }
1893
+
1894
+ .stash-preview-edit:hover {
1895
+ background: rgba(139, 92, 246, 0.1);
1896
+ }
1897
+
1898
+ /* Move-to-session footer */
1899
+ /* Compact "Move to…" header button — sessions open in a context menu.
1900
+ Styled like the Clear All text button so the header stays quiet. */
1901
+ .stash-move-btn {
1902
+ display: inline-flex;
1903
+ align-items: center;
1904
+ gap: var(--sp-4);
1905
+ padding: var(--sp-4) var(--sp-8);
1906
+ font-size: var(--font-size-xs);
1907
+ color: var(--text-muted);
1908
+ background: none;
1909
+ border: none;
1910
+ border-radius: var(--radius-sm);
1911
+ cursor: pointer;
1912
+ white-space: nowrap;
1913
+ transition: background-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
1914
+ }
1915
+
1916
+ .stash-move-btn[hidden] {
1917
+ display: none;
1918
+ }
1919
+
1920
+ .stash-move-btn:hover {
1921
+ background: var(--bg-hover);
1922
+ color: var(--accent);
1923
+ }
1924
+
1925
+ .stash-move-btn.moving {
1926
+ opacity: 0.6;
1927
+ pointer-events: none;
1928
+ }
1929
+
1930
+ .stash-move-btn svg {
1931
+ flex-shrink: 0;
1932
+ }
1933
+
1934
+
1935
+ /* ═══════════════════════════════════════════════════════════════════
1936
+ SHELL MODE VISUAL (! bang commands)
1937
+ ═══════════════════════════════════════════════════════════════════ */
1938
+
1939
+ /* $ prompt prefix — hidden by default, shown in shell mode */
1940
+ .shell-prompt-prefix {
1941
+ display: none;
1942
+ font-family: var(--font-mono);
1943
+ font-size: 0.95em;
1944
+ color: #3fb950;
1945
+ padding-left: var(--sp-2);
1946
+ padding-right: var(--sp-6);
1947
+ padding-top: 1px;
1948
+ line-height: 1.5;
1949
+ flex-shrink: 0;
1950
+ user-select: none;
1951
+ pointer-events: none;
1952
+ }
1953
+
1954
+ #input-container.shell-mode {
1955
+ background: var(--bg-primary);
1956
+ border-color: rgba(63, 185, 80, 0.4);
1957
+ }
1958
+
1959
+ #input-container.shell-mode:focus-within {
1960
+ border-color: #3fb950;
1961
+ }
1962
+
1963
+ #input-container.shell-mode .shell-prompt-prefix {
1964
+ display: inline;
1965
+ }
1966
+
1967
+ #input-container.shell-mode #message-input {
1968
+ font-family: var(--font-mono);
1969
+ color: #e6edf3;
1970
+ caret-color: #3fb950;
1971
+ }
1972
+
1973
+ #input-container.shell-mode #message-input::placeholder {
1974
+ color: #3fb95088;
1975
+ }
1976
+
1977
+ /* Plan input mode (/plan command visual transform) */
1978
+ #input-container.plan-input-mode {
1979
+ background: rgba(99, 102, 241, 0.08);
1980
+ border-color: rgba(139, 92, 246, 0.4);
1981
+ }
1982
+
1983
+ #input-container.plan-input-mode:focus-within {
1984
+ border-color: #a78bfa;
1985
+ }
1986
+
1987
+ #input-container.plan-input-mode #message-input {
1988
+ color: #e2d9f3;
1989
+ caret-color: #a78bfa;
1990
+ }
1991
+
1992
+ #input-container.plan-input-mode #message-input::placeholder {
1993
+ color: #a78bfa88;
1994
+ }
1995
+
1996
+ /* ═══════════════════════════════════════════════════════════════════
1997
+ EFFORT LEVEL BUTTON & POPUP
1998
+ ═══════════════════════════════════════════════════════════════════ */
1999
+
2000
+ .effort-btn {
2001
+ position: relative;
2002
+ width: auto !important;
2003
+ padding: 0 var(--sp-8) !important;
2004
+ gap: var(--sp-4);
2005
+ }
2006
+
2007
+ /* Level-based colors (minimal→ultra: the union across engine vocabularies —
2008
+ each engine's popup only offers its own subset) */
2009
+ .effort-btn[data-level="minimal"] {
2010
+ color: var(--text-muted);
2011
+ opacity: 0.45;
2012
+ }
2013
+
2014
+ .effort-btn[data-level="low"] {
2015
+ color: var(--text-muted);
2016
+ opacity: 0.6;
2017
+ }
2018
+
2019
+ .effort-btn[data-level="medium"] {
2020
+ color: var(--accent-blue);
2021
+ }
2022
+
2023
+ .effort-btn[data-level="high"] {
2024
+ color: #8b5cf6; /* purple - default */
2025
+ }
2026
+
2027
+ .effort-btn[data-level="xhigh"] {
2028
+ color: #d946ef; /* fuchsia - between high purple and max pink */
2029
+ }
2030
+
2031
+ .effort-btn[data-level="max"] {
2032
+ color: #f472b6; /* pink */
2033
+ }
2034
+
2035
+ .effort-btn[data-level="ultra"] {
2036
+ color: #fb7185; /* rose — past max pink */
2037
+ }
2038
+
2039
+ /* Level-fill indicator: matches popup's ◔/◑/◕/◉/● glyphs */
2040
+ .effort-fill {
2041
+ width: 13px;
2042
+ height: 13px;
2043
+ border-radius: 50%;
2044
+ flex-shrink: 0;
2045
+ position: relative;
2046
+ box-shadow: inset 0 0 0 1.5px currentColor;
2047
+ }
2048
+
2049
+ .effort-fill::after {
2050
+ content: '';
2051
+ position: absolute;
2052
+ inset: 1.5px;
2053
+ border-radius: 50%;
2054
+ background: conic-gradient(currentColor var(--effort-fill, 0%), transparent var(--effort-fill, 0%));
2055
+ }
2056
+
2057
+ .effort-btn[data-level="minimal"] { --effort-fill: 10%; }
2058
+ .effort-btn[data-level="low"] { --effort-fill: 20%; }
2059
+ .effort-btn[data-level="medium"] { --effort-fill: 40%; }
2060
+ .effort-btn[data-level="high"] { --effort-fill: 60%; }
2061
+ .effort-btn[data-level="xhigh"] { --effort-fill: 80%; }
2062
+ .effort-btn[data-level="max"] { --effort-fill: 100%; }
2063
+ .effort-btn[data-level="ultra"] { --effort-fill: 100%; }
2064
+
2065
+ .effort-btn[data-level="max"] .effort-fill {
2066
+ filter: drop-shadow(0 0 4px rgba(244, 114, 182, 0.5));
2067
+ }
2068
+
2069
+ .effort-btn[data-level="ultra"] .effort-fill {
2070
+ filter: drop-shadow(0 0 5px rgba(251, 113, 133, 0.7));
2071
+ }
2072
+
2073
+ @media (max-width: 768px) {
2074
+ .effort-fill { width: 16px; height: 16px; }
2075
+ }
2076
+
2077
+ /* One-shot override badge — shown only while a Ctrl+Shift+' override is armed.
2078
+ Mirrors the "next send only" visual contract: the button's level color is
2079
+ already promoted to the armed level (via updateButtonState swapping
2080
+ data-level), and this badge marks why. */
2081
+ .effort-oneshot-badge {
2082
+ display: none;
2083
+ position: absolute;
2084
+ top: -3px;
2085
+ right: -3px;
2086
+ min-width: 13px;
2087
+ height: 13px;
2088
+ padding: 0 var(--sp-3);
2089
+ border-radius: var(--radius-md);
2090
+ background: var(--accent);
2091
+ color: #fff;
2092
+ font-size: 9px;
2093
+ font-weight: 700;
2094
+ line-height: 13px;
2095
+ text-align: center;
2096
+ box-shadow: 0 0 0 1.5px var(--bg-secondary, #1a1a1a);
2097
+ pointer-events: none;
2098
+ }
2099
+
2100
+ .effort-btn[data-pending] .effort-oneshot-badge {
2101
+ display: block;
2102
+ }
2103
+
2104
+ /* Subtle pulse so the armed state reads at a glance without being noisy. */
2105
+ .effort-btn[data-pending] .effort-fill {
2106
+ animation: effort-oneshot-pulse 1.4s ease-in-out infinite;
2107
+ }
2108
+
2109
+ @keyframes effort-oneshot-pulse {
2110
+ 0%, 100% { filter: drop-shadow(0 0 0 transparent); }
2111
+ 50% { filter: drop-shadow(0 0 4px currentColor); }
2112
+ }
2113
+
2114
+ /* Value display */
2115
+ .effort-value {
2116
+ font-size: var(--font-size-xs);
2117
+ font-weight: 600;
2118
+ margin-left: var(--sp-2);
2119
+ color: currentColor;
2120
+ }
2121
+
2122
+ /* Popup */
2123
+ .effort-popup {
2124
+ position: fixed;
2125
+ background: var(--bg-secondary);
2126
+ border: 1px solid var(--border-primary);
2127
+ border-radius: var(--radius-lg);
2128
+ padding: var(--sp-8);
2129
+ min-width: 180px;
2130
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
2131
+ z-index: var(--z-tooltip);
2132
+ display: none;
2133
+ }
2134
+
2135
+ .effort-popup.open {
2136
+ display: block;
2137
+ animation: fadeInUp 0.15s ease;
2138
+ }
2139
+
2140
+ .effort-popup-header {
2141
+ font-size: var(--font-size-xs);
2142
+ font-weight: 600;
2143
+ color: var(--text-muted);
2144
+ text-transform: uppercase;
2145
+ letter-spacing: 0.5px;
2146
+ padding: var(--sp-4) var(--sp-8) var(--sp-8);
2147
+ border-bottom: 1px solid var(--border-primary);
2148
+ margin-bottom: var(--sp-8);
2149
+ }
2150
+
2151
+ .effort-popup-footer {
2152
+ border-top: 1px solid var(--border-primary);
2153
+ margin-top: var(--sp-4);
2154
+ padding: var(--sp-6) var(--sp-4) var(--sp-2);
2155
+ }
2156
+
2157
+ .effort-set-default {
2158
+ display: inline-flex;
2159
+ align-items: center;
2160
+ justify-content: center;
2161
+ height: var(--layout-btn-xs);
2162
+ width: 100%;
2163
+ background: none;
2164
+ border: none;
2165
+ color: var(--text-muted);
2166
+ font-size: var(--font-size-xs);
2167
+ padding: 0 var(--sp-6);
2168
+ border-radius: var(--radius-sm);
2169
+ cursor: pointer;
2170
+ text-align: left;
2171
+ }
2172
+
2173
+ .effort-set-default:hover {
2174
+ background: var(--bg-tertiary);
2175
+ color: var(--text-primary);
2176
+ }
2177
+
2178
+ .effort-presets {
2179
+ display: flex;
2180
+ flex-direction: column;
2181
+ gap: var(--sp-4);
2182
+ }
2183
+
2184
+ .effort-preset {
2185
+ display: flex;
2186
+ align-items: center;
2187
+ gap: var(--sp-10);
2188
+ padding: var(--sp-5) var(--sp-8);
2189
+ line-height: var(--layout-menu-line-height);
2190
+ background: transparent;
2191
+ border: 1px solid transparent;
2192
+ border-radius: var(--radius-md);
2193
+ cursor: pointer;
2194
+ text-align: left;
2195
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
2196
+ }
2197
+
2198
+ .effort-preset:hover {
2199
+ background: var(--bg-hover);
2200
+ }
2201
+
2202
+ .effort-preset.selected {
2203
+ background: rgba(139, 92, 246, 0.15);
2204
+ border-color: rgba(139, 92, 246, 0.3);
2205
+ }
2206
+
2207
+ .effort-preset .preset-icon {
2208
+ font-size: var(--font-size-md);
2209
+ width: 18px;
2210
+ text-align: center;
2211
+ color: var(--text-muted);
2212
+ }
2213
+
2214
+ .effort-preset[data-level="minimal"] .preset-icon { color: var(--text-muted); opacity: 0.6; }
2215
+ .effort-preset[data-level="low"] .preset-icon { color: var(--text-muted); }
2216
+ .effort-preset[data-level="medium"] .preset-icon { color: var(--accent-blue); }
2217
+ .effort-preset[data-level="high"] .preset-icon { color: #8b5cf6; }
2218
+ .effort-preset[data-level="xhigh"] .preset-icon { color: #d946ef; }
2219
+ .effort-preset[data-level="max"] .preset-icon { color: #f472b6; }
2220
+ .effort-preset[data-level="ultra"] .preset-icon { color: #fb7185; }
2221
+
2222
+ .effort-preset .preset-label {
2223
+ font-size: var(--font-size-base);
2224
+ font-weight: 500;
2225
+ color: var(--text-primary);
2226
+ flex: 1;
2227
+ }
2228
+
2229
+ .effort-preset .preset-desc {
2230
+ font-size: var(--font-size-xs);
2231
+ color: var(--text-muted);
2232
+ }
2233
+
2234
+ /* Dynamic "default" tag — shown on whichever preset matches the global
2235
+ default (permission + effort popups). Mirrors token-profile's "default"
2236
+ indicator. Toggled via .is-default on the preset element. */
2237
+ .preset-default-tag {
2238
+ display: none;
2239
+ font-size: 9px;
2240
+ font-weight: 500;
2241
+ color: var(--text-muted);
2242
+ background: var(--bg-tertiary);
2243
+ padding: 1px var(--sp-5);
2244
+ border-radius: var(--radius-sm);
2245
+ margin-left: var(--sp-6);
2246
+ text-transform: uppercase;
2247
+ letter-spacing: 0.3px;
2248
+ vertical-align: middle;
2249
+ }
2250
+
2251
+ .permission-preset.is-default .preset-default-tag,
2252
+ .effort-preset.is-default .preset-default-tag {
2253
+ display: inline-block;
2254
+ }
2255
+
2256
+ /* ═══════════════════════════════════════════════════════════════════
2257
+ PERMISSION LEVEL BUTTON & POPUP
2258
+ ═══════════════════════════════════════════════════════════════════ */
2259
+
2260
+ .permission-btn {
2261
+ position: relative;
2262
+ width: auto !important;
2263
+ padding: 0 var(--sp-8) !important;
2264
+ gap: var(--sp-4);
2265
+ }
2266
+
2267
+ .permission-btn[data-level="bypassPermissions"] { color: #4ade80; }
2268
+ .permission-btn[data-level="acceptEdits"] { color: #fb923c; }
2269
+ .permission-btn[data-level="auto"] { color: var(--accent-blue); }
2270
+ .permission-btn[data-level="dontAsk"] { color: #facc15; }
2271
+ .permission-btn[data-level="plan"] { color: #8b5cf6; }
2272
+
2273
+ .permission-btn svg {
2274
+ width: 13px;
2275
+ height: 13px;
2276
+ }
2277
+
2278
+ @media (max-width: 768px) {
2279
+ .permission-btn svg,
2280
+ #input-toolbar .permission-btn svg {
2281
+ width: 16px;
2282
+ height: 16px;
2283
+ }
2284
+ }
2285
+
2286
+ .permission-label {
2287
+ font-size: var(--font-size-xs);
2288
+ font-weight: 600;
2289
+ margin-left: var(--sp-2);
2290
+ color: currentColor;
2291
+ }
2292
+
2293
+ /* Popup */
2294
+ .permission-popup {
2295
+ position: fixed;
2296
+ background: var(--bg-secondary);
2297
+ border: 1px solid var(--border-primary);
2298
+ border-radius: var(--radius-lg);
2299
+ padding: var(--sp-8);
2300
+ min-width: 210px;
2301
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
2302
+ z-index: var(--z-tooltip);
2303
+ display: none;
2304
+ }
2305
+
2306
+ .permission-popup.open {
2307
+ display: block;
2308
+ animation: fadeInUp 0.15s ease;
2309
+ }
2310
+
2311
+ .permission-popup-header {
2312
+ font-size: var(--font-size-xs);
2313
+ font-weight: 600;
2314
+ color: var(--text-muted);
2315
+ text-transform: uppercase;
2316
+ letter-spacing: 0.5px;
2317
+ padding: var(--sp-4) var(--sp-8) var(--sp-8);
2318
+ border-bottom: 1px solid var(--border-primary);
2319
+ margin-bottom: var(--sp-8);
2320
+ }
2321
+
2322
+ .permission-presets {
2323
+ display: flex;
2324
+ flex-direction: column;
2325
+ gap: var(--sp-4);
2326
+ }
2327
+
2328
+ .permission-preset {
2329
+ display: flex;
2330
+ align-items: center;
2331
+ gap: var(--sp-10);
2332
+ padding: var(--sp-5) var(--sp-8);
2333
+ line-height: var(--layout-menu-line-height);
2334
+ background: transparent;
2335
+ border: 1px solid transparent;
2336
+ border-radius: var(--radius-md);
2337
+ cursor: pointer;
2338
+ text-align: left;
2339
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
2340
+ }
2341
+
2342
+ .permission-preset:hover {
2343
+ background: var(--bg-hover);
2344
+ }
2345
+
2346
+ .permission-preset[data-level="bypassPermissions"].selected { background: rgba(74, 222, 128, 0.1); border-color: rgba(74, 222, 128, 0.3); }
2347
+ .permission-preset[data-level="acceptEdits"].selected { background: rgba(251, 146, 60, 0.1); border-color: rgba(251, 146, 60, 0.3); }
2348
+ .permission-preset[data-level="auto"].selected { background: rgba(96, 165, 250, 0.1); border-color: rgba(96, 165, 250, 0.3); }
2349
+ .permission-preset[data-level="dontAsk"].selected { background: rgba(250, 204, 21, 0.1); border-color: rgba(250, 204, 21, 0.3); }
2350
+ .permission-preset[data-level="plan"].selected { background: rgba(139, 92, 246, 0.15); border-color: rgba(139, 92, 246, 0.3); }
2351
+
2352
+ .preset-dot {
2353
+ width: 10px;
2354
+ height: 10px;
2355
+ border-radius: 50%;
2356
+ flex-shrink: 0;
2357
+ }
2358
+
2359
+ .preset-dot.bypassPermissions-dot { background: #4ade80; }
2360
+ .preset-dot.acceptEdits-dot { background: #fb923c; }
2361
+ .preset-dot.auto-dot { background: var(--accent-blue); }
2362
+ .preset-dot.dontAsk-dot { background: #facc15; }
2363
+ .preset-dot.plan-dot { background: #8b5cf6; }
2364
+
2365
+ .permission-preset .preset-info {
2366
+ display: flex;
2367
+ flex-direction: column;
2368
+ flex: 1;
2369
+ }
2370
+
2371
+ .permission-preset .preset-label {
2372
+ font-size: var(--font-size-base);
2373
+ font-weight: 500;
2374
+ color: var(--text-primary);
2375
+ }
2376
+
2377
+ .permission-preset .preset-desc {
2378
+ font-size: var(--font-size-xs);
2379
+ color: var(--text-muted);
2380
+ }
2381
+
2382
+ .permission-preset .preset-check {
2383
+ width: 16px;
2384
+ text-align: center;
2385
+ }
2386
+
2387
+ .permission-preset.selected .preset-check::after {
2388
+ content: '\2713';
2389
+ }
2390
+
2391
+ .permission-preset[data-level="bypassPermissions"].selected .preset-check::after { color: #4ade80; }
2392
+ .permission-preset[data-level="acceptEdits"].selected .preset-check::after { color: #fb923c; }
2393
+ .permission-preset[data-level="auto"].selected .preset-check::after { color: var(--accent-blue); }
2394
+ .permission-preset[data-level="dontAsk"].selected .preset-check::after { color: #facc15; }
2395
+ .permission-preset[data-level="plan"].selected .preset-check::after { color: #8b5cf6; }
2396
+
2397
+ .permission-popup-footer {
2398
+ border-top: 1px solid var(--border-primary);
2399
+ margin-top: var(--sp-4);
2400
+ padding: var(--sp-6) var(--sp-4) var(--sp-2);
2401
+ }
2402
+
2403
+ .permission-set-default {
2404
+ width: 100%;
2405
+ background: none;
2406
+ border: none;
2407
+ color: var(--text-muted);
2408
+ font-size: var(--font-size-xs);
2409
+ padding: var(--sp-4) var(--sp-6);
2410
+ border-radius: var(--radius-sm);
2411
+ cursor: pointer;
2412
+ text-align: left;
2413
+ }
2414
+
2415
+ .permission-set-default:hover {
2416
+ background: var(--bg-tertiary);
2417
+ color: var(--text-primary);
2418
+ }