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,854 @@
1
+ /* ═══════════════════════════════════════════════════════════════════
2
+ MARKDOWN CONTENT
3
+ ═══════════════════════════════════════════════════════════════════ */
4
+ .markdown-content {
5
+ line-height: var(--layout-line-height-tight);
6
+ }
7
+
8
+ /* Remove margins from first/last children to avoid double-padding with container */
9
+ .markdown-content > *:first-child {
10
+ margin-top: 0;
11
+ }
12
+ .markdown-content > *:last-child {
13
+ margin-bottom: 0;
14
+ }
15
+
16
+ .markdown-content h1,
17
+ .markdown-content h2,
18
+ .markdown-content h3,
19
+ .markdown-content h4 {
20
+ margin: var(--layout-gap-md) 0 var(--layout-gap-xs) 0;
21
+ font-weight: 600;
22
+ }
23
+
24
+ .markdown-content h1 { font-size: 1.4em; }
25
+ .markdown-content h2 { font-size: 1.25em; }
26
+ .markdown-content h3 { font-size: 1.1em; }
27
+
28
+ .markdown-content p {
29
+ margin: var(--layout-paragraph-margin) 0;
30
+ }
31
+
32
+ .markdown-content ul,
33
+ .markdown-content ol {
34
+ margin: var(--layout-paragraph-margin) 0;
35
+ /* Marker gutter is alignment geometry (font-driven, not density-driven):
36
+ floor at 1.5em so compact's 13px --sp-20 can't push outside markers
37
+ flush with the bubble edge; spacious keeps its wider 27px. */
38
+ padding-left: max(var(--sp-20), 1.5em);
39
+ }
40
+
41
+ .markdown-content li {
42
+ margin: var(--layout-gap-xs) 0;
43
+ }
44
+
45
+ .markdown-content a {
46
+ color: var(--accent);
47
+ text-decoration: none;
48
+ }
49
+
50
+ .markdown-content a:hover {
51
+ text-decoration: underline;
52
+ }
53
+
54
+ /* External links (http/https URLs) */
55
+ .external-link {
56
+ color: var(--accent);
57
+ text-decoration: none;
58
+ word-break: break-all;
59
+ }
60
+
61
+ .external-link:hover {
62
+ text-decoration: underline;
63
+ }
64
+
65
+ /* Add external link icon after URL */
66
+ .external-link::after {
67
+ content: '';
68
+ display: inline-block;
69
+ width: 10px;
70
+ height: 10px;
71
+ margin-left: var(--sp-3);
72
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2358a6ff' stroke-width='2'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
73
+ background-size: contain;
74
+ background-repeat: no-repeat;
75
+ vertical-align: middle;
76
+ opacity: 0.7;
77
+ }
78
+
79
+ /* File path links - clickable paths that open file preview */
80
+ .file-path-link {
81
+ position: relative;
82
+ color: var(--accent);
83
+ text-decoration: none;
84
+ cursor: pointer;
85
+ border-radius: var(--radius-sm);
86
+ transition: background 0.15s, color 0.15s, opacity 0.15s;
87
+ /* Disable iOS link preview to allow custom context menu */
88
+ -webkit-touch-callout: none;
89
+ }
90
+
91
+ /* On touch devices, let file-path links participate in text selection normally.
92
+ Previously user-select:none was used to prevent iOS long-press menu,
93
+ but that caused selected text to omit linkified paths entirely.
94
+ -webkit-touch-callout:none (above) is sufficient to suppress iOS link preview. */
95
+
96
+ .file-path-link:hover {
97
+ text-decoration: underline;
98
+ background: var(--bg-tertiary);
99
+ }
100
+
101
+ /* File-path tooltips handled by JS tooltip system (tooltips.js) */
102
+
103
+ /* File path links in tool headers - keep tool's native filename styling */
104
+ .file-path-link.read-filename,
105
+ .file-path-link.write-filename,
106
+ .file-path-link.edit-diff-path {
107
+ color: var(--text-primary);
108
+ background: transparent;
109
+ padding: 0;
110
+ border-radius: 0;
111
+ }
112
+
113
+ .file-path-link.read-filename:hover,
114
+ .file-path-link.write-filename:hover,
115
+ .file-path-link.edit-diff-path:hover {
116
+ color: var(--accent);
117
+ background: transparent;
118
+ }
119
+
120
+ /* File path inside inline code - code tag provides background, link just changes color */
121
+ code .file-path-link,
122
+ .markdown-content code .file-path-link {
123
+ background: transparent;
124
+ padding: 0;
125
+ border-radius: 0;
126
+ }
127
+
128
+ code .file-path-link:hover,
129
+ .markdown-content code .file-path-link:hover {
130
+ background: transparent;
131
+ text-decoration: underline;
132
+ }
133
+
134
+ .markdown-content code {
135
+ font-family: var(--font-mono);
136
+ font-size: 0.9em;
137
+ background: var(--bg-tertiary);
138
+ padding: var(--sp-2) var(--sp-6);
139
+ border-radius: var(--radius-sm);
140
+ }
141
+
142
+ .markdown-content pre {
143
+ /* iPadOS: overflow:hidden creates a scroll port that captures touch events.
144
+ Let both directions pass through to child/parent scroll containers. */
145
+ margin: var(--layout-block-margin) 0;
146
+ border-radius: var(--radius-md);
147
+ overflow: hidden;
148
+ position: relative;
149
+ touch-action: pan-x pan-y;
150
+ }
151
+
152
+ .markdown-content pre code {
153
+ /* clip instead of hidden: avoids creating a vertical scroll port that traps
154
+ wheel events on iPadOS when code has horizontal overflow (long lines). */
155
+ display: block;
156
+ padding: var(--layout-gap-md) var(--layout-msg-padding-h);
157
+ background: var(--bg-tertiary);
158
+ overflow-x: auto;
159
+ overflow-y: clip;
160
+ overscroll-behavior-x: contain;
161
+ font-size: var(--font-size-base);
162
+ line-height: var(--layout-line-height);
163
+ /* pan-y: let vertical scroll pass through to parent container */
164
+ touch-action: pan-x pan-y;
165
+ -webkit-overflow-scrolling: touch;
166
+ }
167
+
168
+ .code-block-wrapper {
169
+ /* iPadOS: overflow:hidden creates a scroll port that captures touch events.
170
+ Let vertical pan pass through to parent scroll container. */
171
+ position: relative;
172
+ margin: var(--layout-block-margin) 0;
173
+ border: 1px solid var(--border-primary);
174
+ border-radius: var(--radius-sm);
175
+ overflow: hidden;
176
+ touch-action: pan-x pan-y;
177
+ }
178
+
179
+ .code-block-header {
180
+ display: flex;
181
+ align-items: center;
182
+ gap: var(--sp-8);
183
+ padding: var(--sp-5) var(--sp-8);
184
+ background: var(--bg-secondary);
185
+ border-bottom: 1px solid var(--border-secondary);
186
+ font-size: var(--font-size-xs);
187
+ }
188
+
189
+ .code-block-lang {
190
+ font-family: var(--font-mono);
191
+ font-size: 10px;
192
+ color: var(--text-muted);
193
+ background: var(--bg-tertiary);
194
+ padding: var(--sp-2) var(--sp-6);
195
+ border-radius: var(--radius-sm);
196
+ }
197
+
198
+ .code-block-copy,
199
+ .code-block-wrap {
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ width: 22px;
204
+ height: 22px;
205
+ padding: 0;
206
+ background: transparent;
207
+ border: none;
208
+ border-radius: var(--radius-sm);
209
+ color: var(--text-muted);
210
+ cursor: pointer;
211
+ transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.15s;
212
+ opacity: 0.6;
213
+ }
214
+
215
+ /* First action button carries the auto margin so both toggles hug the right edge. */
216
+ .code-block-wrap {
217
+ margin-left: auto;
218
+ }
219
+
220
+ .code-block-header:hover .code-block-copy,
221
+ .code-block-header:hover .code-block-wrap {
222
+ opacity: 1;
223
+ }
224
+
225
+ .code-block-copy:hover,
226
+ .code-block-wrap:hover {
227
+ background: var(--bg-tertiary);
228
+ color: var(--accent);
229
+ }
230
+
231
+ .code-block-copy.copied {
232
+ color: var(--success);
233
+ opacity: 1;
234
+ }
235
+
236
+ .code-block-wrap.active {
237
+ color: var(--accent);
238
+ opacity: 1;
239
+ }
240
+
241
+ .code-block-copy svg,
242
+ .code-block-wrap svg {
243
+ width: 14px;
244
+ height: 14px;
245
+ }
246
+
247
+ .code-block-wrapper pre {
248
+ margin: 0;
249
+ border-radius: 0;
250
+ }
251
+
252
+ /* Line-wrap toggle: wrap long lines instead of horizontal scroll. */
253
+ .code-block-wrapper.wrapped pre code {
254
+ white-space: pre-wrap;
255
+ overflow-wrap: anywhere;
256
+ overflow-x: hidden;
257
+ }
258
+
259
+ /* Images fit their container — never force horizontal scroll.
260
+ Covers every .markdown-content surface: chat messages, file-preview rendered
261
+ view, prompt-explorer responses, editor-view preview.
262
+ :not(.panzoom-img) — the pan/zoom viewer sizes its image by CSS transform
263
+ against a clipped canvas, so a max-width would break its scale math. */
264
+ .markdown-content img:not(.panzoom-img) {
265
+ max-width: 100%;
266
+ height: auto;
267
+ }
268
+
269
+ /* Table wrapper for horizontal scroll on overflow */
270
+ .markdown-content .table-wrapper {
271
+ overflow-x: auto;
272
+ margin: var(--layout-block-margin) 0;
273
+ -webkit-overflow-scrolling: touch;
274
+ /* Allow horizontal scroll without blocking parent's vertical scroll */
275
+ touch-action: pan-x pan-y;
276
+ }
277
+
278
+ .markdown-content table {
279
+ border-collapse: collapse;
280
+ width: 100%;
281
+ font-size: var(--font-size-base);
282
+ table-layout: auto;
283
+ }
284
+
285
+ .markdown-content th,
286
+ .markdown-content td {
287
+ padding: var(--sp-6) var(--sp-10);
288
+ border: 1px solid var(--border-primary);
289
+ text-align: left;
290
+ overflow-wrap: break-word;
291
+ white-space: normal;
292
+ }
293
+
294
+ /* Prevent header labels from wrapping */
295
+ .markdown-content th {
296
+ white-space: nowrap;
297
+ }
298
+
299
+ .markdown-content th {
300
+ background: var(--bg-tertiary);
301
+ font-weight: 600;
302
+ }
303
+
304
+ /* ═══════════════════════════════════════════════════════════════════
305
+ TABLE LAYOUT SWITCH — table / stacked / text
306
+ One per-table cycling control that reformats a wide table in place:
307
+
308
+ table the plain grid (default, no data-layout attribute)
309
+ stacked each row becomes a card of label|value pairs, the way
310
+ `\G` renders a row in the mysql client
311
+ text all table chrome dropped for a prose report,
312
+ "**label:** value" per line — for reading rather than
313
+ cross-referencing
314
+
315
+ Together these continue the "reflow, don't hide" line the table CSS
316
+ already takes (wrap cells, never truncate) — a wide table that is
317
+ still unreadable after wrapping gets restructured instead of clipped.
318
+
319
+ Deliberately UNSCOPED (no .markdown-content prefix): every surface
320
+ that renders markdown gets .table-block, and the ones outside chat
321
+ (.task-block-response, .tob-response, .thinking-step-content, the
322
+ discussion sidebar) are exactly the ones with no horizontal-scroll
323
+ rule today, so restructuring is their only way out of an overflow.
324
+
325
+ Specificity note: `.table-block[data-layout="x"] td` is (0,2,1),
326
+ which beats both `.markdown-content td` (0,1,1) and the per-surface
327
+ `.x table td` (0,1,2) rules, so it wins without !important.
328
+ ═══════════════════════════════════════════════════════════════════ */
329
+
330
+ /* Positioning context for the switch. .table-wrapper can't host it — that's
331
+ the overflow-x scroller, so the control would scroll away and clip. No
332
+ margin here on purpose: .table-wrapper's own margin collapses through,
333
+ keeping the switch aligned to the table's top edge. */
334
+ .table-block {
335
+ position: relative;
336
+ }
337
+
338
+ /* ── Segmented layout switch ──────────────────────────────────────────────
339
+ Three connected segments, active one highlighted — not a cycling button.
340
+ A cycler shows a single state, so it can't say which layouts exist or
341
+ which one you're in without being clicked to find out; segments make the
342
+ vocabulary and the selection readable at a glance and allow jumping
343
+ straight to a layout.
344
+
345
+ Sits over the header row's right edge, which is the least data-dense
346
+ corner of a table (short header labels, and the widest column is
347
+ conventionally last). */
348
+ .table-block > .table-layout-switch {
349
+ position: absolute;
350
+ top: var(--sp-4);
351
+ right: var(--sp-4);
352
+ z-index: var(--z-internal);
353
+ display: flex;
354
+ align-items: stretch;
355
+ border: 1px solid var(--border-primary);
356
+ border-radius: var(--radius-xs);
357
+ background: var(--bg-tertiary);
358
+ /* Clip the segments' square corners to the group's rounded ones so the
359
+ strip reads as one control. */
360
+ overflow: hidden;
361
+ /* Faintly present at rest rather than hover-only: a table that has been
362
+ reformatted must keep showing why, and an invisible control is an
363
+ undiscovered one. Rises to full on hover. */
364
+ opacity: 0.45;
365
+ transition: opacity 0.15s;
366
+ }
367
+
368
+ .table-block:hover > .table-layout-switch {
369
+ opacity: 1;
370
+ }
371
+
372
+ /* Any non-default layout keeps the switch fully lit and accents its frame, so a
373
+ reformatted table further up the transcript still reads as deliberately
374
+ changed. */
375
+ .table-block[data-layout] > .table-layout-switch {
376
+ opacity: 1;
377
+ border-color: var(--accent);
378
+ }
379
+
380
+ /* Touch devices: keep it permanently legible. Do NOT zero the opacity in a
381
+ pure-touch query — iOS latches :hover on the last-tapped element, so the
382
+ control would fade the instant you used it. That exact bug ate the stash
383
+ bubble on table rows; the fix there was the same "leave it visible" rule. */
384
+ @media (hover: none), (pointer: coarse) {
385
+ .table-block > .table-layout-switch {
386
+ opacity: 0.75;
387
+ }
388
+ }
389
+
390
+ .table-layout-switch > .table-layout-opt {
391
+ display: flex;
392
+ align-items: center;
393
+ justify-content: center;
394
+ width: 22px;
395
+ height: 20px;
396
+ padding: 0;
397
+ border: none;
398
+ background: transparent;
399
+ color: var(--text-muted);
400
+ cursor: pointer;
401
+ transition: background-color 0.15s, color 0.15s;
402
+ }
403
+
404
+ /* Hairline dividers between segments — the "connected" part. */
405
+ .table-layout-switch > .table-layout-opt + .table-layout-opt {
406
+ border-left: 1px solid var(--border-primary);
407
+ }
408
+
409
+ .table-layout-switch > .table-layout-opt svg {
410
+ width: 13px;
411
+ height: 13px;
412
+ }
413
+
414
+ /* Same hover treatment as the code-block copy/wrap buttons: tint the glyph to
415
+ the themed accent rather than inventing a colour for this control. */
416
+ .table-layout-switch > .table-layout-opt:hover {
417
+ background: var(--bg-secondary);
418
+ color: var(--accent);
419
+ }
420
+
421
+ /* The selected segment. Filled rather than merely tinted so the active state
422
+ survives at the switch's resting opacity. Uses the instance --accent (the
423
+ same variable links, focus rings and checked checkboxes use) so the control
424
+ themes with the tier — deliberately NOT the fixed --accent-purple the stash
425
+ bubble uses, which is pinned only because it has to stay distinct from the
426
+ accent as a "has a comment" signal. */
427
+ .table-layout-switch > .table-layout-opt.active,
428
+ .table-layout-switch > .table-layout-opt.active:hover {
429
+ background: var(--accent);
430
+ color: white;
431
+ }
432
+
433
+ /* ── Shared by both reformatted layouts ───────────────────────────────── */
434
+
435
+ /* Nothing overflows once rows are restructured, so drop the scroller (it would
436
+ otherwise keep a stale horizontal scroll offset from the grid layout). */
437
+ .table-block[data-layout="stacked"] > .table-wrapper,
438
+ .table-block[data-layout="text"] > .table-wrapper {
439
+ overflow-x: hidden;
440
+ }
441
+
442
+ /* Headers move into the cells as data-label, so the header row itself goes. */
443
+ .table-block[data-layout="stacked"] thead,
444
+ .table-block[data-layout="text"] thead {
445
+ display: none;
446
+ }
447
+
448
+ .table-block[data-layout="stacked"] table,
449
+ .table-block[data-layout="stacked"] tbody,
450
+ .table-block[data-layout="stacked"] tr,
451
+ .table-block[data-layout="stacked"] td,
452
+ .table-block[data-layout="text"] table,
453
+ .table-block[data-layout="text"] tbody,
454
+ .table-block[data-layout="text"] tr,
455
+ .table-block[data-layout="text"] td {
456
+ display: block;
457
+ width: auto;
458
+ }
459
+
460
+ /* ── STACKED: one card per row, label|value columns ───────────────────── */
461
+
462
+ .table-block[data-layout="stacked"] tr {
463
+ margin-bottom: var(--sp-8);
464
+ border: 1px solid var(--border-primary);
465
+ border-radius: var(--radius-sm);
466
+ overflow: hidden;
467
+ }
468
+
469
+ .table-block[data-layout="stacked"] tr:last-child {
470
+ margin-bottom: 0;
471
+ }
472
+
473
+ /* Two-column grid per cell: the ::before label, then the .cell-value wrapper.
474
+ Padding is inherited from the base th/td rule so density tokens still apply. */
475
+ .table-block[data-layout="stacked"] td {
476
+ display: grid;
477
+ grid-template-columns: minmax(4em, 11em) 1fr;
478
+ gap: var(--sp-10);
479
+ border: none;
480
+ border-bottom: 1px solid var(--border-primary);
481
+ }
482
+
483
+ .table-block[data-layout="stacked"] tr > td:last-child {
484
+ border-bottom: none;
485
+ }
486
+
487
+ .table-block[data-layout="stacked"] td::before {
488
+ content: attr(data-label);
489
+ color: var(--text-secondary);
490
+ font-weight: 600;
491
+ overflow-wrap: break-word;
492
+ /* Grid items default to min-width:auto, which refuses to shrink below the
493
+ content's min-content size — an unbreakable token would then push the
494
+ column wider than its track. */
495
+ min-width: 0;
496
+ }
497
+
498
+ /* The value half. Wrapping the cell's content in one element is what keeps the
499
+ grid at exactly two items (see _prepareTableLayouts) — without it, a cell
500
+ like "Add `env=...` to the call" becomes three items and the code span lands
501
+ in the label gutter. */
502
+ .table-block[data-layout="stacked"] td > .cell-value {
503
+ min-width: 0;
504
+ overflow-wrap: break-word;
505
+ }
506
+
507
+ /* Ragged rows: a cell with no matching header spans the full width instead of
508
+ leaving an empty label gutter. */
509
+ .table-block[data-layout="stacked"] td:not([data-label]),
510
+ .table-block[data-layout="stacked"] td[data-label=""] {
511
+ grid-template-columns: 1fr;
512
+ }
513
+
514
+ .table-block[data-layout="stacked"] td:not([data-label])::before,
515
+ .table-block[data-layout="stacked"] td[data-label=""]::before {
516
+ display: none;
517
+ }
518
+
519
+ /* ── TEXT: prose report, "**label:** value" per line ─────────────────────
520
+ No borders, no cards, no columns — the label sits inline ahead of its
521
+ value exactly like bold markdown, so a row reads as a short paragraph
522
+ instead of a record to cross-reference. Records are separated by a
523
+ paragraph gap plus one hairline rule, which is what keeps multi-line
524
+ values from bleeding into the next row visually. */
525
+
526
+ .table-block[data-layout="text"] tr {
527
+ margin-bottom: var(--layout-paragraph-margin);
528
+ }
529
+
530
+ .table-block[data-layout="text"] tr + tr {
531
+ padding-top: var(--layout-paragraph-margin);
532
+ border-top: 1px solid var(--border-secondary);
533
+ }
534
+
535
+ .table-block[data-layout="text"] tr:last-child {
536
+ margin-bottom: 0;
537
+ }
538
+
539
+ .table-block[data-layout="text"] td {
540
+ padding: 0;
541
+ border: none;
542
+ /* Own line per field, with the tight leading the rest of a report gets. */
543
+ margin: var(--sp-3) 0;
544
+ line-height: var(--layout-line-height);
545
+ }
546
+
547
+ .table-block[data-layout="text"] td::before {
548
+ content: attr(data-label) ": ";
549
+ font-weight: 600;
550
+ color: var(--text-primary);
551
+ }
552
+
553
+ /* Inline, so the value continues on the label's line and wraps beneath it. */
554
+ .table-block[data-layout="text"] td > .cell-value {
555
+ display: inline;
556
+ }
557
+
558
+ /* A cell with no header prints as a bare line — no stray ": ". */
559
+ .table-block[data-layout="text"] td:not([data-label])::before,
560
+ .table-block[data-layout="text"] td[data-label=""]::before {
561
+ content: none;
562
+ }
563
+
564
+ /* The row's stash bubble is anchored to its last cell, which in text layout has
565
+ no padding of its own to sit in — give that one cell the gutter back so the
566
+ comment icon never lands on top of the value. */
567
+ .table-block[data-layout="text"] tr > [data-stash-cell] {
568
+ padding-right: 28px;
569
+ }
570
+
571
+ .markdown-content blockquote {
572
+ margin: var(--layout-block-margin) 0;
573
+ padding: var(--layout-gap-sm) var(--layout-msg-padding-h);
574
+ border-left: 3px solid var(--border-primary);
575
+ color: var(--text-secondary);
576
+ background: var(--bg-secondary);
577
+ border-radius: 0 var(--radius-md) var(--radius-md) 0;
578
+ }
579
+
580
+ .markdown-content hr {
581
+ border: none;
582
+ border-top: 1px solid var(--border-primary);
583
+ margin: var(--layout-gap-lg) 0;
584
+ }
585
+
586
+ /* ═══════════════════════════════════════════════════════════════════
587
+ TASK LIST CHECKBOXES (clickable in preview)
588
+ ═══════════════════════════════════════════════════════════════════ */
589
+
590
+ /* Task list items: remove bullet when checkbox is present */
591
+ .markdown-content li:has(> .md-task-checkbox) {
592
+ list-style: none;
593
+ margin-left: -1.2em;
594
+ }
595
+
596
+ /* Styled checkbox */
597
+ .markdown-content .md-task-checkbox {
598
+ -webkit-appearance: none;
599
+ appearance: none;
600
+ width: 16px;
601
+ height: 16px;
602
+ border: 2px solid var(--text-muted, #666);
603
+ border-radius: var(--radius-sm);
604
+ background: transparent;
605
+ cursor: pointer;
606
+ vertical-align: middle;
607
+ margin-right: var(--sp-6);
608
+ position: relative;
609
+ top: -1px;
610
+ transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.15s;
611
+ }
612
+
613
+ .markdown-content .md-task-checkbox:checked {
614
+ background: var(--accent);
615
+ border-color: var(--accent);
616
+ }
617
+
618
+ .markdown-content .md-task-checkbox:checked::after {
619
+ content: '';
620
+ position: absolute;
621
+ left: 3px;
622
+ top: 0px;
623
+ width: 5px;
624
+ height: 9px;
625
+ border: solid white;
626
+ border-width: 0 2px 2px 0;
627
+ transform: rotate(45deg);
628
+ }
629
+
630
+ .markdown-content .md-task-checkbox:hover {
631
+ border-color: var(--accent);
632
+ }
633
+
634
+ /* Checked task text gets subtle strikethrough */
635
+ .markdown-content li:has(> .md-task-checkbox:checked) {
636
+ color: var(--text-muted, #666);
637
+ text-decoration: line-through;
638
+ text-decoration-color: var(--text-muted, #666);
639
+ }
640
+
641
+ /* ═══════════════════════════════════════════════════════════════════
642
+ TAP-TO-SELECT ICONS (iOS-friendly selection)
643
+ ═══════════════════════════════════════════════════════════════════ */
644
+
645
+ /* Selectable elements get relative positioning for icon placement */
646
+ .markdown-content [data-selectable] {
647
+ position: relative;
648
+ padding-right: var(--sp-24); /* Space for icon */
649
+ }
650
+
651
+ /* Sentence spans within paragraphs */
652
+ .markdown-content span[data-selectable="sentence"] {
653
+ display: inline;
654
+ padding-right: 0; /* No padding on inline spans */
655
+ }
656
+
657
+ /* List items already have bullet, need less padding */
658
+ .markdown-content li[data-selectable] {
659
+ padding-right: var(--sp-20);
660
+ }
661
+
662
+ /* The bubble icon — a real <span> child (injected by components.js) so it
663
+ can carry data-tooltip and have its own :hover. The glyph is drawn by
664
+ ::before so it stays out of parent.textContent.
665
+ Descendant selector (not child) so table-row bubbles inside <td> match too.
666
+
667
+ The glyph is a MASKED svg, not the 💬 emoji: an emoji renders in its own
668
+ fixed colours and cannot be tinted, which is why the "has a comment" state
669
+ used to need a translucent background disc behind it. With a mask,
670
+ background-color paints the icon itself, so the commented state is just a
671
+ colour swap (see .has-stash below) — no disc, far more legible. */
672
+ .markdown-content [data-selectable] .stash-bubble {
673
+ /* Tappable padding kept inside parent so hovering the bubble
674
+ still fires the parent's :hover. */
675
+ position: absolute;
676
+ right: 0;
677
+ bottom: 2px; /* Position at bottom for wrapped paragraphs */
678
+ display: inline-block;
679
+ font-size: var(--font-size-sm);
680
+ line-height: 1;
681
+ opacity: 0;
682
+ cursor: pointer;
683
+ user-select: none;
684
+ -webkit-user-select: none;
685
+ transition: opacity 0.15s, transform 0.15s;
686
+ z-index: 1;
687
+ padding: var(--sp-4);
688
+ }
689
+
690
+ /* Outline speech bubble = "no comment yet" (tinted by background-color). */
691
+ .markdown-content [data-selectable] .stash-bubble::before {
692
+ content: '';
693
+ display: block;
694
+ width: 0.95em;
695
+ height: 0.95em;
696
+ background-color: var(--text-secondary, #8b949e);
697
+ -webkit-mask-image: var(--stash-bubble-glyph-outline);
698
+ mask-image: var(--stash-bubble-glyph-outline);
699
+ -webkit-mask-repeat: no-repeat;
700
+ mask-repeat: no-repeat;
701
+ -webkit-mask-position: center;
702
+ mask-position: center;
703
+ -webkit-mask-size: contain;
704
+ mask-size: contain;
705
+ transition: background-color 0.15s;
706
+ }
707
+
708
+ /* For sentence spans, position icon inline at end */
709
+ .markdown-content span[data-selectable="sentence"] > .stash-bubble {
710
+ position: relative;
711
+ display: inline-block;
712
+ top: auto;
713
+ right: auto;
714
+ transform: none;
715
+ margin-left: var(--sp-4);
716
+ padding: var(--sp-2);
717
+ vertical-align: middle;
718
+ }
719
+
720
+ /* Show icons on touch devices */
721
+ @media (hover: none), (pointer: coarse) {
722
+ .markdown-content [data-selectable] .stash-bubble {
723
+ opacity: 0.35;
724
+ }
725
+ /* Note: :active highlight removed - was highlighting icon on ANY click in text.
726
+ JavaScript adds .select-icon-active class when icon is actually tapped. */
727
+ }
728
+
729
+ /* Show selection icon on hover (desktop + iPadOS with trackpad).
730
+ Hovering the bubble itself also triggers this because the bubble
731
+ lives inside the parent. */
732
+ .markdown-content [data-selectable]:hover .stash-bubble {
733
+ opacity: 0.8;
734
+ }
735
+
736
+ /* Brighten further when hovering the bubble itself (real element → real :hover) */
737
+ .markdown-content [data-selectable] .stash-bubble:hover {
738
+ opacity: 1;
739
+ }
740
+
741
+ /* Preview highlight on hover - lighter than selected state */
742
+ .markdown-content [data-selectable]:hover:not(.selected-for-comment) {
743
+ background: rgba(139, 92, 246, 0.08);
744
+ border-radius: var(--radius-sm);
745
+ }
746
+
747
+ /* Hide sticky hover background on pure-touch (don't zero the bubble's opacity here — :hover sticks after tap). */
748
+ @media (hover: none) and (pointer: coarse) and (not (any-pointer: fine)) {
749
+ .markdown-content [data-selectable]:hover:not(.selected-for-comment) {
750
+ background: transparent;
751
+ }
752
+ }
753
+
754
+ /* Visual feedback when tapped */
755
+ .markdown-content [data-selectable].select-icon-active .stash-bubble {
756
+ opacity: 1 !important;
757
+ transform: scale(1.2);
758
+ }
759
+
760
+ .markdown-content span[data-selectable="sentence"].select-icon-active > .stash-bubble {
761
+ transform: scale(1.2);
762
+ }
763
+
764
+ /* Highlight selected content */
765
+ .markdown-content [data-selectable].selected-for-comment {
766
+ background: rgba(139, 92, 246, 0.15);
767
+ border-radius: var(--radius-sm);
768
+ outline: 1px solid rgba(139, 92, 246, 0.3);
769
+ }
770
+
771
+ /* ── Table row selectables ──
772
+ <tr> can't use position:relative, so bubble lives on last <td>.
773
+ The .stash-bubble for table-row is appended into the last cell
774
+ (see components.js _appendStashBubbles). */
775
+ .markdown-content tr[data-selectable="table-row"] {
776
+ padding-right: 0; /* padding ignored on table-row, but be explicit */
777
+ }
778
+
779
+ /* Put the bubble on the last cell — override the absolute positioning
780
+ from the generic rule above so it sits at the cell's right-middle. */
781
+ .markdown-content tr[data-selectable="table-row"] > [data-stash-cell] {
782
+ position: relative;
783
+ padding-right: 28px; /* fixed: room for the row-select bubble */
784
+ }
785
+
786
+ .markdown-content tr[data-selectable="table-row"] > [data-stash-cell] > .stash-bubble {
787
+ bottom: auto;
788
+ right: 4px;
789
+ top: 50%;
790
+ transform: translateY(-50%);
791
+ }
792
+
793
+ /* Touch devices: always-visible bubble (inherits opacity from generic rule but
794
+ keep this here so future overrides are easy to find). */
795
+
796
+ /* Desktop/trackpad hover — fires when hovering anywhere in the row */
797
+ .markdown-content tr[data-selectable="table-row"]:hover > [data-stash-cell] > .stash-bubble {
798
+ opacity: 0.8;
799
+ }
800
+
801
+ /* Row hover highlight - no border-radius on table rows */
802
+ .markdown-content tr[data-selectable="table-row"]:hover:not(.selected-for-comment) {
803
+ background: rgba(139, 92, 246, 0.08);
804
+ border-radius: 0;
805
+ }
806
+
807
+ /* Hide the sticky hover background on pure-touch. Deliberately does NOT zero
808
+ the bubble's opacity (the generic rule above makes the same point): iOS keeps
809
+ :hover latched on the last-tapped row, so zeroing it here made the bubble
810
+ vanish the moment you used it — the row you just commented on lost its icon. */
811
+ @media (hover: none) and (pointer: coarse) and (not (any-pointer: fine)) {
812
+ .markdown-content tr[data-selectable="table-row"]:hover:not(.selected-for-comment) {
813
+ background: transparent;
814
+ }
815
+ }
816
+
817
+ /* Active tap feedback (keep translateY in transform so the bubble stays centered) */
818
+ .markdown-content tr[data-selectable="table-row"].select-icon-active > [data-stash-cell] > .stash-bubble {
819
+ opacity: 1 !important;
820
+ transform: translateY(-50%) scale(1.2);
821
+ }
822
+
823
+ /* Selected row */
824
+ .markdown-content tr[data-selectable="table-row"].selected-for-comment {
825
+ background: rgba(139, 92, 246, 0.15);
826
+ }
827
+
828
+ /* ── "Has a stashed comment" indicator ──────────────────────────────────
829
+ A commented block's bubble is always visible and simply switches colour:
830
+ the outline glyph becomes a SOLID violet one. No background disc — the
831
+ old translucent circle used the themed --accent-muted (blue/green/red per
832
+ instance), which read as a faint whitish blob rather than a comment mark.
833
+ Violet is fixed (--accent-purple) so it matches the rest of the stash /
834
+ selection language on every tier. */
835
+ .markdown-content tr[data-selectable="table-row"].has-stash > [data-stash-cell] > .stash-bubble,
836
+ .markdown-content [data-selectable].has-stash > .stash-bubble {
837
+ opacity: 1 !important;
838
+ }
839
+
840
+ .markdown-content tr[data-selectable="table-row"].has-stash > [data-stash-cell] > .stash-bubble::before,
841
+ .markdown-content [data-selectable].has-stash > .stash-bubble::before {
842
+ background-color: var(--accent-purple, #8b5cf6);
843
+ -webkit-mask-image: var(--stash-bubble-glyph-filled);
844
+ mask-image: var(--stash-bubble-glyph-filled);
845
+ }
846
+
847
+ /* Hover lifts it brighter still */
848
+ .markdown-content tr[data-selectable="table-row"].has-stash:hover > [data-stash-cell] > .stash-bubble::before,
849
+ .markdown-content [data-selectable].has-stash:hover > .stash-bubble::before,
850
+ .markdown-content [data-selectable].has-stash > .stash-bubble:hover::before {
851
+ background-color: #a78bfa;
852
+ }
853
+
854
+