@webship.co/workspace 0.0.0-stage → 1.0.0-beta1

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 (307) hide show
  1. package/PLUGINS.md +249 -0
  2. package/README.md +379 -2
  3. package/WORKSPACES.md +69 -0
  4. package/actions/ai-items.js +293 -0
  5. package/actions/audio.js +48 -0
  6. package/actions/build-order.js +79 -0
  7. package/actions/checks.js +95 -0
  8. package/actions/claude-session.js +104 -0
  9. package/actions/commands-center.js +258 -0
  10. package/actions/composer.js +267 -0
  11. package/actions/ddev-addons.js +90 -0
  12. package/actions/ddev-mail.js +80 -0
  13. package/actions/ddev.js +237 -0
  14. package/actions/folders.js +165 -0
  15. package/actions/images-svg.js +33 -0
  16. package/actions/images.js +145 -0
  17. package/actions/navigation.js +190 -0
  18. package/actions/node.js +222 -0
  19. package/actions/notes.js +55 -0
  20. package/actions/plugins.js +36 -0
  21. package/actions/project-make.js +28 -0
  22. package/actions/rag.js +202 -0
  23. package/actions/row-actions.js +262 -0
  24. package/actions/screens.js +270 -0
  25. package/actions/testing.js +121 -0
  26. package/actions/video-edit.js +54 -0
  27. package/ai-inventory.js +182 -0
  28. package/ai-items.js +202 -0
  29. package/assistant-cast.js +176 -0
  30. package/assistant-suggestions.js +91 -0
  31. package/assistant.js +484 -0
  32. package/audio-voice.js +528 -0
  33. package/auth-google.js +260 -0
  34. package/build-ai.js +52 -0
  35. package/build-order-page.js +191 -0
  36. package/build-order.js +313 -0
  37. package/builder-args.js +117 -0
  38. package/changelog.js +302 -0
  39. package/chat-threads.js +134 -0
  40. package/checks.js +105 -0
  41. package/claude-args.js +282 -0
  42. package/claude-sessions.js +129 -0
  43. package/command-author.js +117 -0
  44. package/command-block.js +20 -0
  45. package/commands/defaults.yml +159 -0
  46. package/commands-page.js +460 -0
  47. package/commands.js +568 -0
  48. package/cucumber.js +43 -0
  49. package/ddev-addons-page.js +124 -0
  50. package/ddev-addons.js +154 -0
  51. package/ddev-custom-commands.js +96 -0
  52. package/ddev-domains.js +84 -0
  53. package/ddev-import.js +93 -0
  54. package/ddev-mail-page.js +167 -0
  55. package/ddev-mail.js +110 -0
  56. package/ddev-options.js +145 -0
  57. package/ddev-snapshots.js +126 -0
  58. package/ddev.js +145 -0
  59. package/docs/DEVELOPER-GUIDE.md +224 -0
  60. package/docs/README.md +252 -0
  61. package/docs/REFERENCE.md +684 -0
  62. package/docs/USER-GUIDE.md +697 -0
  63. package/docs/screenshots/assistant/avatar-board.png +0 -0
  64. package/docs/screenshots/assistant/avatar-coffee.png +0 -0
  65. package/docs/screenshots/assistant/avatar-colours.png +0 -0
  66. package/docs/screenshots/assistant/avatar-icons.png +0 -0
  67. package/docs/screenshots/assistant/avatar-moves.png +0 -0
  68. package/docs/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  69. package/docs/screenshots/assistant/avatar-settings-voices.png +0 -0
  70. package/docs/screenshots/assistant/avatar-status.png +0 -0
  71. package/docs/screenshots/create-card/create-card.png +0 -0
  72. package/docs/screenshots/create-card/start-ai.png +0 -0
  73. package/docs/screenshots/guide-clone-address.png +0 -0
  74. package/docs/screenshots/guide-clone-worktree-off.png +0 -0
  75. package/docs/screenshots/guide-clone-worktree-on.png +0 -0
  76. package/docs/screenshots/guide-ddev-menu.png +0 -0
  77. package/docs/screenshots/guide-domains.png +0 -0
  78. package/docs/screenshots/guide-home.png +0 -0
  79. package/docs/screenshots/guide-ide-menu.png +0 -0
  80. package/docs/screenshots/guide-remove-refusal.png +0 -0
  81. package/docs/screenshots/guide-rows-worktree.png +0 -0
  82. package/docs/screenshots/guide-screens.png +0 -0
  83. package/docs/screenshots/guide-settings.png +0 -0
  84. package/docs/screenshots/guide-workspace-page.png +0 -0
  85. package/docs/screenshots/home/card-settings.png +0 -0
  86. package/docs/screenshots/home/drag-reorder.png +0 -0
  87. package/docs/screenshots/home/edit-card.png +0 -0
  88. package/docs/screenshots/images/claude-code-image.png +0 -0
  89. package/docs/screenshots/images/edit-with-ai.png +0 -0
  90. package/docs/screenshots/images/gallery.png +0 -0
  91. package/docs/screenshots/images/generate-description.png +0 -0
  92. package/docs/screenshots/images/generate-plan.png +0 -0
  93. package/docs/screenshots/images/providers.png +0 -0
  94. package/docs/screenshots/images/svg-form.png +0 -0
  95. package/docs/screenshots/images/svg-result.png +0 -0
  96. package/docs/screenshots/images/upload.png +0 -0
  97. package/docs/screenshots/new-folder/2-form.png +0 -0
  98. package/docs/screenshots/new-folder/8-ddev-options.png +0 -0
  99. package/docs/screenshots/new-folder/files-folder.png +0 -0
  100. package/docs/screenshots/projects/ddev-custom-commands.png +0 -0
  101. package/docs/screenshots/projects/state-chip-stop.png +0 -0
  102. package/docs/screenshots/readme/dashboard-dark.png +0 -0
  103. package/docs/screenshots/readme/dashboard-light.png +0 -0
  104. package/docs/screenshots/readme/dev-dark.png +0 -0
  105. package/docs/screenshots/readme/dev-light.png +0 -0
  106. package/docs/screenshots/readme/docs-dark.png +0 -0
  107. package/docs/screenshots/readme/docs-light.png +0 -0
  108. package/docs/screenshots/readme/icons/ai.svg +4 -0
  109. package/docs/screenshots/readme/icons/code.svg +4 -0
  110. package/docs/screenshots/readme/icons/docs.svg +4 -0
  111. package/docs/screenshots/readme/icons/services.svg +4 -0
  112. package/docs/screenshots/readme/icons/sites.svg +4 -0
  113. package/docs/screenshots/readme/test-report-dark.png +0 -0
  114. package/docs/screenshots/readme/test-report-light.png +0 -0
  115. package/docs/screenshots/readme/test-scenarios.png +0 -0
  116. package/docs/screenshots/readme/test-steps.png +0 -0
  117. package/docs/screenshots/readme/videos-dark.png +0 -0
  118. package/docs/screenshots/readme/videos-light.png +0 -0
  119. package/docs/screenshots/screens-menu.png +0 -0
  120. package/docs/screenshots/screens-new.png +0 -0
  121. package/docs/screenshots/settings/auth-google-lists.png +0 -0
  122. package/docs/screenshots/settings/delete-1-row.png +0 -0
  123. package/docs/screenshots/settings/delete-2-confirm.png +0 -0
  124. package/docs/screenshots/settings/delete-3-type-name.png +0 -0
  125. package/docs/screenshots/settings/delete-4-type-yes.png +0 -0
  126. package/event-log.js +59 -0
  127. package/files-browser.js +247 -0
  128. package/folders.js +121 -0
  129. package/format.js +73 -0
  130. package/git-info.js +81 -0
  131. package/github-auth.js +62 -0
  132. package/html.js +10 -0
  133. package/icons.js +83 -0
  134. package/images-ai.js +447 -0
  135. package/images-create.js +226 -0
  136. package/images-page.js +417 -0
  137. package/images-records.js +39 -0
  138. package/images-render.js +36 -0
  139. package/images-svg-core.js +214 -0
  140. package/images-svg-sanitize.js +188 -0
  141. package/images-svg.js +241 -0
  142. package/images.js +270 -0
  143. package/jobs.js +430 -0
  144. package/linger.js +21 -0
  145. package/lists.js +292 -0
  146. package/media-info.js +136 -0
  147. package/navigation-page.js +264 -0
  148. package/navigation.js +394 -0
  149. package/notes.js +0 -0
  150. package/package.json +47 -4
  151. package/palette-index.js +46 -0
  152. package/playwright.config.js +23 -0
  153. package/plugin-install.js +239 -0
  154. package/plugin-loader.js +466 -0
  155. package/plugin-manifest.js +83 -0
  156. package/plugin-settings.js +44 -0
  157. package/plugins/workspace-notes/index.js +59 -0
  158. package/plugins-page.js +103 -0
  159. package/project-make.js +241 -0
  160. package/project-name.js +210 -0
  161. package/public/assistant-avatar-moves.js +646 -0
  162. package/public/assistant-avatar-ui.js +1223 -0
  163. package/public/assistant-avatar.js +530 -0
  164. package/public/avatar/druplicon.vrm +0 -0
  165. package/public/css/base.css +260 -0
  166. package/public/css/components/ai-assistant.css +263 -0
  167. package/public/css/components/assistant-avatar.css +279 -0
  168. package/public/css/components/assistant-views.css +198 -0
  169. package/public/css/components/build-order.css +74 -0
  170. package/public/css/components/clone-as-a-git-worktree.css +30 -0
  171. package/public/css/components/command-palette.css +61 -0
  172. package/public/css/components/commands-center.css +139 -0
  173. package/public/css/components/confirm-dialog.css +40 -0
  174. package/public/css/components/create-card.css +8 -0
  175. package/public/css/components/ddev-menu.css +23 -0
  176. package/public/css/components/deep-chat.css +17 -0
  177. package/public/css/components/density.css +11 -0
  178. package/public/css/components/floating-assistant-workspace-pages.css +55 -0
  179. package/public/css/components/folders.css +5 -0
  180. package/public/css/components/full-height-left-assistant-sidebar-all-pages.css +39 -0
  181. package/public/css/components/full-height-right-actions-rail-pages-that-have-actions.css +267 -0
  182. package/public/css/components/grid-and-list.css +203 -0
  183. package/public/css/components/help-page.css +36 -0
  184. package/public/css/components/hero.css +16 -0
  185. package/public/css/components/images.css +70 -0
  186. package/public/css/components/layout-rhythm-and-spacing-polish.css +139 -0
  187. package/public/css/components/mic-on-any-field-that-wants-typing.css +416 -0
  188. package/public/css/components/modal-fit.css +113 -0
  189. package/public/css/components/page-width.css +23 -0
  190. package/public/css/components/plugins-page.css +9 -0
  191. package/public/css/components/project-cards.css +256 -0
  192. package/public/css/components/project-rows.css +390 -0
  193. package/public/css/components/reordering-the-workspace-cards.css +85 -0
  194. package/public/css/components/responsive-home-and-the-workspace-pages.css +457 -0
  195. package/public/css/components/running-commands-as-toasts.css +327 -0
  196. package/public/css/components/settings-editor-core-config-yml.css +828 -0
  197. package/public/css/components/short-results-as-a-toast-at-the-top.css +203 -0
  198. package/public/css/components/sound-player.css +84 -0
  199. package/public/css/components/suggest-agents-skills-commands.css +112 -0
  200. package/public/css/components/tab-modal.css +142 -0
  201. package/public/css/components/terminals.css +244 -0
  202. package/public/css/components/test-report-media-one-file-per-row-paged.css +144 -0
  203. package/public/css/components/top-toolbar-logo-breadcrumb.css +107 -0
  204. package/public/css/components/video-edit.css +6 -0
  205. package/public/css/components/web-ide-open-with.css +23 -0
  206. package/public/css/components/working-indicators.css +158 -0
  207. package/public/css/components/workspace-cards.css +79 -0
  208. package/public/favicon.ico +0 -0
  209. package/public/favicon.png +0 -0
  210. package/public/htmx.min.js +1 -0
  211. package/public/logo.png +0 -0
  212. package/public/media-player.js +215 -0
  213. package/public/mic.js +255 -0
  214. package/public/narration.js +110 -0
  215. package/public/palette.js +278 -0
  216. package/public/sortable.js +251 -0
  217. package/public/suggest.js +337 -0
  218. package/public/terminal.js +903 -0
  219. package/public/themes/default/theme.css +267 -0
  220. package/public/themes/ember/theme.css +180 -0
  221. package/public/themes/press/theme.css +290 -0
  222. package/public/themes/slate/theme.css +182 -0
  223. package/public/touch-icon.png +0 -0
  224. package/public/ui.js +2721 -0
  225. package/public/vendor/ace/LICENSE.txt +24 -0
  226. package/public/vendor/ace/ace.js +23 -0
  227. package/public/vendor/ace/ext-language_tools.js +8 -0
  228. package/public/vendor/ace/ext-searchbox.js +8 -0
  229. package/public/vendor/ace/mode-css.js +8 -0
  230. package/public/vendor/ace/mode-html.js +8 -0
  231. package/public/vendor/ace/mode-javascript.js +8 -0
  232. package/public/vendor/ace/mode-markdown.js +8 -0
  233. package/public/vendor/ace/mode-sh.js +8 -0
  234. package/public/vendor/ace/mode-yaml.js +8 -0
  235. package/public/vendor/ace/theme-textmate.js +8 -0
  236. package/public/vendor/ace/theme-tomorrow_night.js +8 -0
  237. package/public/vendor/ace/worker-yaml.js +1 -0
  238. package/public/vendor/deep-chat.LICENSE +21 -0
  239. package/public/vendor/deep-chat.bundle.js +1 -0
  240. package/public/vendor/fonts/dm-serif-text-LICENSE.txt +93 -0
  241. package/public/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  242. package/public/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  243. package/public/vendor/fonts/figtree-LICENSE.txt +93 -0
  244. package/public/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  245. package/public/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  246. package/public/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  247. package/public/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  248. package/public/vendor/fonts/jetbrains-mono-LICENSE.txt +93 -0
  249. package/public/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  250. package/public/vendor/fonts/ubuntu-mono-LICENSE.txt +98 -0
  251. package/public/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  252. package/public/vendor/tabler-LICENSE.txt +21 -0
  253. package/public/vendor/tabler-sprite.svg +1 -0
  254. package/public/vendor/three/LICENSE +21 -0
  255. package/public/vendor/three/addons/loaders/GLTFLoader.js +4860 -0
  256. package/public/vendor/three/addons/utils/BufferGeometryUtils.js +1501 -0
  257. package/public/vendor/three/addons/utils/SkeletonUtils.js +496 -0
  258. package/public/vendor/three/three.core.min.js +6 -0
  259. package/public/vendor/three/three.module.min.js +6 -0
  260. package/public/vendor/three-vrm/LICENSE +22 -0
  261. package/public/vendor/three-vrm/three-vrm.module.min.js +983 -0
  262. package/public/vendor/uikit-icons.min.js +1 -0
  263. package/public/vendor/uikit.min.css +1 -0
  264. package/public/vendor/uikit.min.js +1 -0
  265. package/public/vendor/webship-icons/ui/bookmark-outlined.svg +1 -0
  266. package/public/vendor/webship-icons/ui/headset.svg +1 -0
  267. package/public/vendor/webship-icons/ui/menu.svg +1 -0
  268. package/public/vendor/webship-icons/ui/pause.svg +1 -0
  269. package/public/vendor/webship-icons/ui/play.svg +1 -0
  270. package/public/vendor/webship-icons/ui/share.svg +1 -0
  271. package/public/vendor/webship-icons/ui/sound-off.svg +1 -0
  272. package/public/vendor/webship-icons/ui/sound-on.svg +1 -0
  273. package/public/vendor/webship-icons/ui/stop.svg +1 -0
  274. package/public/vendor/xterm/addon-fit.js +2 -0
  275. package/public/vendor/xterm/addon-unicode11.js +2 -0
  276. package/public/vendor/xterm/addon-web-links.js +2 -0
  277. package/public/vendor/xterm/xterm.css +218 -0
  278. package/public/vendor/xterm/xterm.js +2 -0
  279. package/rag.js +169 -0
  280. package/reader.js +132 -0
  281. package/repo-blobs.js +66 -0
  282. package/report-pdf.js +337 -0
  283. package/row-layout.js +1439 -0
  284. package/screenrc +37 -0
  285. package/screens.js +1063 -0
  286. package/scripts/code-server-start.sh +190 -0
  287. package/scripts/images-claude-finish.js +107 -0
  288. package/scripts/screen-host.sh +114 -0
  289. package/scripts/screen-runner.js +362 -0
  290. package/scripts/screen-then.js +47 -0
  291. package/scripts/update.sh +350 -0
  292. package/server.js +7923 -0
  293. package/settings-file.js +423 -0
  294. package/speech.js +297 -0
  295. package/tab-modal.js +75 -0
  296. package/test-reports.js +260 -0
  297. package/themes.js +381 -0
  298. package/transcripts.js +168 -0
  299. package/urls.js +13 -0
  300. package/validate.js +18 -0
  301. package/version-check.js +94 -0
  302. package/video-edit.js +588 -0
  303. package/waiting.js +85 -0
  304. package/workspace-card-tools.js +41 -0
  305. package/workspace-delete.js +139 -0
  306. package/workspace-types.js +85 -0
  307. package/workspaces.js +553 -0
@@ -0,0 +1,279 @@
1
+ /* The assistant's avatar: Druplicon, free on the page, and nothing else there. Shown from the robot button in
2
+ the assistant's header (html.assistant-avatar-on, decided before first paint), it lives in a frameless
3
+ layer of its own, flying from place to place. Every control is on the assistant's side: the bar under
4
+ the robot button's menu holds show or hide, the board, the windows and the settings. */
5
+ .assistant-avatar-stage { display: none; }
6
+ /* The drawing covers the whole window, transparent and click-through, so a move or a flight can reach
7
+ anywhere on the page and is never cut at a square edge. The card (.avatar-float) is invisible: it is
8
+ where the figure stands, and what you drag; the page around it stays usable. */
9
+ .avatar-layer { position: fixed; inset: 0; z-index: 990; pointer-events: none; }
10
+ .avatar-layer .assistant-avatar-stage { display: block; width: 100%; height: 100%; }
11
+ .assistant-avatar-canvas { display: block; width: 100%; height: 100%; opacity: 0; transition: opacity .3s ease; touch-action: none; }
12
+ .avatar-float { cursor: grab; touch-action: none; }
13
+ .avatar-float.is-dragging { cursor: grabbing; }
14
+ .assistant-avatar-stage.is-ready .assistant-avatar-canvas { opacity: 1; }
15
+ .avatar-layer.is-listening .assistant-avatar-canvas { filter: drop-shadow(0 0 0.6rem var(--brand-primary)); }
16
+ .assistant-avatar-menu-btn.is-current { background: rgba(255, 255, 255, 0.22); }
17
+
18
+ .avatar-float {
19
+ position: fixed; z-index: 991; width: 13rem; height: 17rem; left: calc(100vw - 14rem); top: 40vh;
20
+ background: transparent; border: 0;
21
+ transition: left 1.2s cubic-bezier(.35, .1, .25, 1), top 1.2s cubic-bezier(.35, .1, .25, 1), width .3s ease, height .3s ease;
22
+ }
23
+ .avatar-float[data-size="small"] { width: 9rem; height: 12rem; }
24
+ .avatar-float[data-size="large"] { width: 18rem; height: 23rem; }
25
+ /* Very big and huge: to show it off, or to see its hands and face from across the room. */
26
+ .avatar-float[data-size="xl"] { width: 26rem; height: 34rem; }
27
+ .avatar-float[data-size="huge"] { width: min(40rem, 60vw); height: calc(100vh - 5rem); }
28
+ .avatar-float.is-dragging { transition: none; }
29
+
30
+ /* The robot button's menu: show or hide, the tools, and every setting. */
31
+ .assistant-avatar-bar { width: min(22rem, calc(100vw - 2rem)); padding: 0.75rem; }
32
+ .assistant-avatar-bar.uk-open { display: flex; flex-direction: column; gap: 0.5rem; }
33
+ .assistant-avatar-toggle { align-self: flex-start; }
34
+ .assistant-avatar-toggle[aria-pressed="true"] { background: var(--subtle-brand); color: var(--brand-primary); }
35
+ .assistant-avatar-tools { display: flex; flex-wrap: wrap; gap: 0.3rem; }
36
+ .assistant-avatar-btn {
37
+ display: inline-flex; align-items: center; justify-content: center; width: 2.125rem; height: 2.125rem;
38
+ border: 1px solid var(--line); border-radius: 50%; cursor: pointer;
39
+ background: var(--surface); color: var(--brand-secondary);
40
+ }
41
+ .assistant-avatar-btn:hover, .assistant-avatar-btn:focus-visible { color: var(--brand-primary); border-color: var(--brand-primary); }
42
+ .assistant-avatar-btn:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; }
43
+ .assistant-avatar-mute[aria-pressed="true"], .assistant-avatar-btn[aria-expanded="true"] { background: var(--subtle-brand); color: var(--brand-primary); }
44
+ .assistant-avatar-mic { width: auto; padding: 0 0.7rem; border-radius: 999px; gap: 0.3rem; }
45
+ .assistant-avatar-mic[aria-pressed="true"] { background: var(--brand-primary); color: white; border-color: var(--brand-primary); }
46
+ .assistant-avatar-pops:empty { display: none; }
47
+ .assistant-avatar-pops { padding: 0.5rem; border: 1px solid var(--line); border-radius: 0.6rem; background: var(--surface); }
48
+ .avatar-browser-address:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 1px; }
49
+ .avatar-kit-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
50
+ .avatar-kit-list li { display: flex; align-items: center; gap: 0.3rem; }
51
+ .avatar-kit-list li span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.875rem; }
52
+ .avatar-kit-empty { margin: 0; font-size: 0.8125rem; color: var(--text-subtle, inherit); }
53
+ .avatar-kit-credit { margin: 0; font-size: 0.75rem; line-height: 1.35; color: var(--text-subtle, inherit); }
54
+ .avatar-kit-settings { display: flex; flex-direction: column; gap: 0.45rem; font-size: 0.875rem; }
55
+ .avatar-kit-settings > div { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; }
56
+ .avatar-kit-settings > div > span, .avatar-kit-range > span { min-width: 3.5rem; font-weight: 600; }
57
+ .avatar-kit-range { display: flex; align-items: center; gap: 0.5rem; }
58
+ .avatar-kit-range input { flex: 1; accent-color: var(--brand-primary); }
59
+ .avatar-kit-check { display: flex; align-items: center; gap: 0.4rem; }
60
+ .avatar-kit-check input { accent-color: var(--brand-primary); }
61
+
62
+ /* Quick replies, in the panel under its header: for a while after an answer, and while a video plays. */
63
+ .assistant-avatar-chips { display: flex; flex-wrap: wrap; gap: 0.25rem; padding: 0.4rem 0.75rem; border-bottom: 1px solid var(--line); }
64
+ .assistant-avatar-chips[hidden] { display: none; }
65
+ .avatar-chip {
66
+ padding: 0.15rem 0.6rem; font: inherit; font-size: 0.8125rem; line-height: 1.5;
67
+ border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
68
+ background: var(--surface); color: var(--brand-secondary);
69
+ }
70
+ .avatar-chip:hover, .avatar-chip:focus-visible { border-color: var(--brand-primary); color: var(--brand-primary); }
71
+ .avatar-chip:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 1px; }
72
+ .avatar-chip-media { background: var(--subtle-brand); }
73
+ .avatar-chip[aria-pressed="true"] { background: var(--brand-primary); color: white; border-color: var(--brand-primary); }
74
+
75
+ /* The windows beside it. */
76
+ .avatar-window {
77
+ position: fixed; z-index: 993; /* above the avatar (990, 991): a board, a video or a page is never covered by it */ display: flex; flex-direction: column; max-height: calc(100vh - 6rem);
78
+ border: 1px solid var(--line); border-radius: 0.875rem; overflow: hidden;
79
+ background: var(--surface); box-shadow: 0 0.75rem 2.25rem rgba(0, 0, 0, 0.16);
80
+ animation: avatar-window-in .25s ease-out;
81
+ }
82
+ .avatar-window-head {
83
+ display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0.4rem 0.3rem 0.75rem;
84
+ cursor: grab; user-select: none; touch-action: none;
85
+ background: var(--brand-secondary); color: white;
86
+ }
87
+ .avatar-window-head strong { flex: 1; font-weight: 600; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
88
+ .avatar-window-tools { display: inline-flex; gap: 0.25rem; }
89
+ .avatar-window-btn {
90
+ display: inline-flex; align-items: center; justify-content: center; width: 1.875rem; height: 1.875rem;
91
+ border: 0; border-radius: 50%; background: transparent; color: white; cursor: pointer;
92
+ }
93
+ .avatar-window-btn:hover, .avatar-window-btn:focus-visible { background: rgba(255, 255, 255, 0.18); color: white; }
94
+ .avatar-window-btn:focus-visible { outline: 2px solid white; outline-offset: 1px; }
95
+ .avatar-window-body { flex: 1; min-height: 0; overflow: auto; }
96
+
97
+ /* The board: a whiteboard, its lines of text above the drawing. */
98
+ .avatar-window-board .avatar-window-body { background: white; color: var(--brand-secondary); padding: 0.5rem 0.75rem 0.75rem; }
99
+ .avatar-board-lines { margin: 0 0 0.25rem; padding-left: 1.25rem; font-size: 0.9375rem; }
100
+ .avatar-board-lines:empty { display: none; }
101
+ .avatar-board-svg { display: block; width: 100%; height: auto; color: var(--brand-secondary); }
102
+ .avatar-stroke { fill: none; stroke: currentColor; stroke-width: 0.45; stroke-linecap: round; stroke-linejoin: round; }
103
+ .avatar-board-label { font: 2.6px/1 var(--font-primary, system-ui), sans-serif; fill: var(--brand-secondary); text-anchor: middle; dominant-baseline: middle; }
104
+ .avatar-board-text { text-anchor: start; font-size: 3px; }
105
+ @keyframes avatar-draw { to { stroke-dashoffset: 0; } }
106
+ @keyframes avatar-fade { from { opacity: 0; } }
107
+
108
+ .avatar-video { position: relative; aspect-ratio: 16 / 9; background: black; }
109
+ .avatar-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
110
+ .avatar-window-browser { height: min(36rem, calc(100vh - 8rem)); }
111
+ .avatar-reader { display: block; width: 100%; height: 100%; border: 0; background: white; }
112
+
113
+ @keyframes avatar-window-in { from { opacity: 0; transform: translateY(0.5rem); } }
114
+ @media (max-width: 40em) {
115
+ .avatar-float { width: 9rem; height: 12rem; }
116
+ .avatar-window { left: 0.5rem !important; right: 0.5rem; width: auto !important; }
117
+ }
118
+ @media (prefers-reduced-motion: reduce) {
119
+ .assistant-avatar-canvas, .avatar-float { transition: none; }
120
+ .avatar-window { animation: none; }
121
+ }
122
+
123
+ /* The avatar's tools: three round buttons down the stage's left edge, each opening a small popover. */
124
+ .assistant-avatar-kit { position: absolute; top: 0.5rem; left: 0.5rem; display: flex; flex-direction: column; gap: 0.25rem; align-items: flex-start; }
125
+ .avatar-kit-pop {
126
+ position: absolute; left: 2.5rem; top: 0; z-index: 2; display: flex; flex-wrap: wrap; gap: 0.25rem;
127
+ width: min(17rem, calc(100vw - 6rem)); padding: 0.5rem; border: 1px solid var(--line); border-radius: 0.75rem;
128
+ background: var(--surface); box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.14);
129
+ }
130
+ .avatar-kit-pop[hidden] { display: none; }
131
+
132
+ /* Windows are not limited to their first size: a corner to drag, and one button to fill the page. */
133
+ .avatar-window { resize: both; min-width: 18rem; min-height: 10rem; }
134
+ .avatar-window.is-max { left: 1rem !important; top: 4.5rem !important; width: calc(100vw - 2rem) !important; height: calc(100vh - 5.5rem) !important; max-height: none; resize: none; }
135
+ .avatar-window-board .avatar-window-body { display: flex; flex-direction: column; }
136
+
137
+ /* The board's tools, its drawing area, the pen and the sticky notes. */
138
+ .avatar-board-bar { display: flex; flex-wrap: wrap; gap: 0.25rem; padding-bottom: 0.4rem; margin-bottom: 0.4rem; border-bottom: 1px solid var(--line); }
139
+ .avatar-board-area { position: relative; flex: 1; min-height: 0; }
140
+ .avatar-board-area .avatar-board-svg { width: 100%; height: 100%; max-height: 100%; }
141
+ .avatar-board-area.is-pen .avatar-board-svg { cursor: crosshair; touch-action: none; }
142
+ .avatar-ink { stroke-width: 0.5; }
143
+ .avatar-note {
144
+ position: absolute; width: 22%; min-width: 9rem; display: flex; flex-direction: column;
145
+ background: var(--subtle-accent); border: 1px solid var(--line); border-radius: 0.35rem;
146
+ box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12);
147
+ }
148
+ .avatar-note-grip { display: flex; justify-content: flex-end; height: 1.25rem; cursor: grab; touch-action: none; background: var(--brand-accent); border-radius: 0.35rem 0.35rem 0 0; }
149
+ .avatar-note-x { border: 0; background: transparent; cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 0.4rem; color: var(--brand-secondary); }
150
+ .avatar-note-text { min-height: 3.5rem; padding: 0.35rem 0.5rem; font-size: 0.875rem; outline: none; color: var(--brand-secondary); }
151
+ .avatar-note-text:focus-visible { box-shadow: inset 0 0 0 2px var(--brand-primary); }
152
+
153
+ /* The browser's own bar, and each video's controls. */
154
+ .avatar-window-browser .avatar-window-body { display: flex; flex-direction: column; }
155
+ .avatar-browser-bar, .avatar-video-bar { display: flex; align-items: center; gap: 0.2rem; padding: 0.3rem 0.4rem; border-bottom: 1px solid var(--line); background: var(--subtle-light); }
156
+ .avatar-video-bar { border-bottom: 0; border-top: 1px solid var(--line); flex-wrap: wrap; }
157
+ .avatar-browser-btn {
158
+ display: inline-flex; align-items: center; justify-content: center; width: 1.875rem; height: 1.875rem; flex: 0 0 auto;
159
+ border: 0; border-radius: 50%; background: transparent; color: var(--brand-secondary); cursor: pointer;
160
+ }
161
+ .avatar-browser-btn:hover, .avatar-browser-btn:focus-visible { background: var(--subtle-brand); color: var(--brand-primary); }
162
+ .avatar-browser-btn:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 1px; }
163
+ .avatar-browser-btn[aria-pressed="true"] { color: var(--brand-primary); }
164
+ .avatar-browser-address {
165
+ flex: 1; min-width: 0; padding: 0.25rem 0.6rem; font: inherit; font-size: 0.8125rem;
166
+ border: 1px solid var(--line); border-radius: 999px; background: var(--surface); color: inherit;
167
+ }
168
+ .avatar-window-browser .avatar-reader { flex: 1; height: auto; min-height: 0; }
169
+ .avatar-video-gap { flex: 0 0 0.4rem; }
170
+
171
+ /* The board: soft dotted paper, a palette and pen widths. */
172
+ .avatar-window-board .avatar-board-area {
173
+ background-color: white;
174
+ background-image: radial-gradient(color-mix(in srgb, var(--brand-secondary) 18%, transparent) 1px, transparent 1.2px);
175
+ background-size: 1rem 1rem; border-radius: 0.4rem;
176
+ }
177
+ .avatar-board-palette, .avatar-board-widths { display: inline-flex; align-items: center; gap: 0.2rem; padding: 0 0.2rem; }
178
+ .avatar-swatch {
179
+ width: 1.25rem; height: 1.25rem; border-radius: 50%; cursor: pointer;
180
+ border: 2px solid white; box-shadow: 0 0 0 1px var(--line);
181
+ }
182
+ .avatar-swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--brand-primary); }
183
+ .avatar-swatch:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; }
184
+
185
+ /* The voice dock under the conversation, as in the avatar app: voice on or off, the big hold-to-talk
186
+ button (or the Space bar), and keep listening. */
187
+ /* deep-chat keeps filling the panel, and the dock takes its own height under it. */
188
+ /* deep-chat's container copies its host's height (height: inherit), so deep-chat keeps its own 100% and the
189
+ panel's body leaves room at the bottom instead, where the dock is pinned. */
190
+ .assistant-body:has(> .assistant-dock) { position: relative; padding-bottom: 4.6rem !important; }
191
+ .assistant-dock {
192
+ position: absolute; left: 1.25rem; right: 1.25rem; bottom: 0.9rem;
193
+ display: flex; align-items: center; justify-content: center; gap: 0.5rem;
194
+ padding-top: 0.6rem; border-top: 1px solid var(--line);
195
+ }
196
+ @media (max-width: 40em) { .assistant-dock { left: 0.875rem; right: 0.875rem; bottom: 0.6rem; } }
197
+ .dock-btn {
198
+ display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
199
+ min-width: 2.6rem; height: 2.6rem; padding: 0 0.7rem; border-radius: 999px; cursor: pointer;
200
+ border: 1px solid var(--line); background: var(--surface); color: var(--brand-secondary);
201
+ font: inherit; font-size: 0.875rem; user-select: none; touch-action: none;
202
+ }
203
+ .dock-btn:hover { border-color: var(--brand-primary); color: var(--brand-primary); }
204
+ .dock-btn:focus-visible { outline: 3px solid var(--brand-primary); outline-offset: 2px; }
205
+ .dock-btn[hidden] { display: none; }
206
+ .dock-voice[aria-pressed="false"] { color: var(--danger); border-color: var(--danger); }
207
+ .dock-talk { flex: 1; max-width: 16rem; background: var(--brand-grad); color: white; border-color: transparent; font-weight: 600; }
208
+ .dock-talk:hover { color: white; filter: brightness(1.06); }
209
+ .dock-talk-icon { display: inline-flex; }
210
+ .dock-talk kbd {
211
+ font: inherit; font-size: 0.7rem; padding: 0.05rem 0.4rem; border-radius: 0.3rem;
212
+ color: white; background: rgba(255, 255, 255, 0.22); border: 1px solid rgba(255, 255, 255, 0.45);
213
+ }
214
+ .dock-talk.is-held { background: var(--danger); animation: dock-pulse 1.2s infinite; }
215
+ .dock-mic[aria-pressed="true"] { background: var(--brand-primary); color: white; border-color: transparent; }
216
+ .dock-mic.is-listening { animation: dock-breathe 2.4s ease-in-out infinite; }
217
+ .dock-mic.is-blocked { color: var(--danger); border-color: var(--danger); }
218
+ @keyframes dock-pulse { 50% { filter: brightness(1.15); } }
219
+ @keyframes dock-breathe { 50% { box-shadow: 0 0 0 0.35rem color-mix(in srgb, var(--brand-primary) 30%, transparent); } }
220
+ @media (max-width: 59.99em) { .dock-talk span, .dock-talk kbd { display: none; } }
221
+ @media (prefers-reduced-motion: reduce) { .dock-talk.is-held, .dock-mic.is-listening { animation: none; } }
222
+ .avatar-kit-range select { flex: 1; min-width: 0; padding: 0.2rem 0.4rem; font: inherit; font-size: 0.8125rem; border: 1px solid var(--line); border-radius: 0.4rem; background: var(--surface); color: inherit; }
223
+
224
+ /* Hair colour swatches in the avatar's settings. */
225
+ .avatar-swatch {
226
+ width: 1.4rem; height: 1.4rem; padding: 0; border-radius: 50%; cursor: pointer;
227
+ border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--line); color: var(--text-subtle, inherit); font-size: 0.75rem; line-height: 1;
228
+ }
229
+ .avatar-swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--brand-primary); }
230
+ .avatar-kit-settings input[type="color"] { width: 1.6rem; height: 1.6rem; padding: 0; border: 0; background: none; cursor: pointer; }
231
+ /* On a big move the card follows its path frame by frame: no transition to lag behind it. */
232
+ .avatar-float.is-travelling { transition: width .3s ease, height .3s ease; }
233
+
234
+ /* The interface glyphs, public/vendor/webship-icons. An interface icon is its SVG used as a
235
+ mask, filled with the text colour, at the size the design gives it: so it is black (ink) at rest, brand blue on
236
+ hover, and light in dark mode, from CSS alone. A brand icon (value, service) is an image in its own colours. */
237
+ .vi {
238
+ display: inline-block; flex: none; width: 1.25rem; height: 1.25rem; vertical-align: middle;
239
+ background-color: currentColor;
240
+ -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
241
+ -webkit-mask-position: center; mask-position: center;
242
+ -webkit-mask-size: auto; mask-size: auto;
243
+ }
244
+ .vi-menu, .vi-share { width: 1.5rem; height: 1.5rem; }
245
+ .dock-btn:hover .vi, .avatar-browser-btn:hover .vi, .avatar-chip:hover .vi, .assistant-ctl:hover .vi { color: var(--brand-primary); }
246
+ .assistant-head .assistant-ctl:hover .vi { color: inherit; }
247
+ .avatar-chip-icon { display: inline-flex; align-items: center; gap: 0.25rem; }
248
+ .avatar-note-stamp { width: 0.875rem; height: 0.875rem; margin: auto auto auto 0.35rem; }
249
+ .avatar-float.is-sizing { transition: left 1.2s cubic-bezier(.35, .1, .25, 1), top 1.2s cubic-bezier(.35, .1, .25, 1); }
250
+
251
+ /* Colours: a picker for each part of the avatar, in a wrapping grid, and a way back to its own. */
252
+ .avatar-kit-colours { display: flex; flex-direction: column; align-items: flex-start; gap: 0.35rem; }
253
+ .avatar-kit-colours > div { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.2rem, 1fr)); gap: 0.25rem 0.5rem; width: 100%; max-height: 9.5rem; overflow-y: auto; }
254
+ .avatar-kit-colour { display: flex; align-items: center; gap: 0.35rem; font-size: 0.8125rem; min-width: 0; }
255
+ .avatar-kit-colour span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
256
+ .avatar-kit-colour input[type="color"] { flex: none; width: 1.5rem; height: 1.5rem; padding: 0; border: 1px solid var(--line); border-radius: 50%; background: none; cursor: pointer; }
257
+ .avatar-kit-colour input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
258
+ .avatar-kit-colour input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 50%; }
259
+ .avatar-kit-settings > .avatar-kit-colours { display: flex; flex-direction: column; align-items: stretch; }
260
+ .avatar-kit-colours > span { font-weight: 600; text-align: left; }
261
+ .avatar-kit-colours > .avatar-chip { align-self: flex-start; }
262
+
263
+ /* The agent's status at the avatar's feet, as the Claude Code CLI shows it: a spinner, a verb, the time so far. */
264
+ .avatar-status {
265
+ position: absolute; left: 50%; bottom: 1%; transform: translateX(-50%); z-index: 1; /* at its feet: at home the feet are near the page's bottom, so below them it would be cut off */
266
+ display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.65rem; white-space: nowrap;
267
+ border: 1px solid var(--line); border-radius: 999px; background: var(--surface); pointer-events: none;
268
+ font-size: 0.8125rem; color: var(--brand-secondary); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
269
+ }
270
+ .avatar-status[hidden] { display: none; }
271
+ .avatar-status-spin { display: inline-block; width: 1em; text-align: center; color: var(--brand-primary); font-weight: 700; }
272
+ .avatar-status-verb { font-weight: 600; color: var(--brand-primary); }
273
+ .avatar-status-time { color: var(--text-subtle, inherit); font-variant-numeric: tabular-nums; }
274
+ /* The alert colours of the brand tokens: info while it works, success when done, danger when stopped. */
275
+ .avatar-status-spin { color: var(--info); }
276
+ .avatar-status.is-done { background: var(--success-soft); border-color: var(--success); }
277
+ .avatar-status.is-done .avatar-status-spin, .avatar-status.is-done .avatar-status-verb { color: var(--success-ink); }
278
+ .avatar-status.is-stopped { background: var(--danger-soft); border-color: var(--danger); }
279
+ .avatar-status.is-stopped .avatar-status-spin, .avatar-status.is-stopped .avatar-status-verb { color: var(--danger-ink); }
@@ -0,0 +1,198 @@
1
+ /* ---------- How the assistant is shown: sidebar, wide, floating, full page ----------
2
+ *
3
+ * One panel, four amounts of room. The state is on <html> — data-assistant-view and
4
+ * .assistant-closed, written by the pre-paint script in the head — so the page is laid out right on
5
+ * the first frame rather than reflowing once a deferred script has had its say.
6
+ *
7
+ * After the pattern Drupal's own AI assistant uses: a switch in the top toolbar, and the rest in a
8
+ * menu in the panel's header.
9
+ */
10
+
11
+ /* Closed: the page gets the width back, and the switch in the toolbar is the way in. */
12
+ html.assistant-closed .assistant-side { display: none; }
13
+ html.assistant-closed[data-assistant-view] .app-content,
14
+ html.assistant-closed .app-content { margin-left: 0; }
15
+
16
+ /* Wide — the same column, doubled, for an answer a 25rem column cannot hold: a report, a diff, a
17
+ * list of thirty projects. Capped against the viewport so it never leaves the page a sliver. */
18
+ html[data-assistant-view="wide"] .assistant-side { width: min(45rem, 60vw); }
19
+ html[data-assistant-view="wide"] .app-content { margin-left: min(45rem, 60vw); }
20
+ /* The chevron is the state: it points right to widen, left to come back. */
21
+ html[data-assistant-view="wide"] .assistant-wider .ctl-mark { transform: rotate(180deg); }
22
+
23
+ /* Floating — the panel over the page rather than beside it, and the page keeps its full width.
24
+ * The launcher is the way back when it is closed. */
25
+ html[data-assistant-view="float"] .app-content { margin-left: 0; }
26
+ html[data-assistant-view="float"] .assistant-side {
27
+ top: auto;
28
+ left: 1.375rem;
29
+ bottom: 1.375rem;
30
+ width: min(28rem, calc(100vw - 2.75rem));
31
+ height: min(38rem, calc(100vh - 8rem));
32
+ padding: 0;
33
+ }
34
+ html[data-assistant-view="float"] .assistant-side .assistant-head { display: flex; align-items: center; }
35
+ html[data-assistant-view="float"] .assistant-panel {
36
+ border-radius: 0.9rem;
37
+ box-shadow: var(--shadow-float);
38
+ overflow: hidden;
39
+ }
40
+
41
+ /* Floating means it can be moved: the header is the handle, the way a window's title bar is, with a
42
+ grip drawn before the avatar so it says so. Dragging is offered only in this view — every other one
43
+ is laid out by the stylesheet, and a dragged sidebar would just be a floating panel. */
44
+ html[data-assistant-view="float"] .assistant-side .assistant-head {
45
+ cursor: grab;
46
+ touch-action: none;
47
+ }
48
+ html[data-assistant-view="float"] .assistant-side .assistant-head::before {
49
+ content: "";
50
+ width: 0.625rem;
51
+ height: 1.125rem;
52
+ margin-right: 0.125rem;
53
+ flex: none;
54
+ opacity: .55;
55
+ /* Two columns of dots: the grip every draggable window has, drawn rather than vendored. */
56
+ background-image: radial-gradient(currentColor 1px, transparent 1.2px);
57
+ background-size: 4px 4px;
58
+ align-self: center;
59
+ }
60
+ html[data-assistant-view="float"] .assistant-side .assistant-head-row { align-items: center; }
61
+ html[data-assistant-view="float"] .assistant-side.is-dragging { user-select: none; }
62
+ html[data-assistant-view="float"] .assistant-side.is-dragging .assistant-head { cursor: grabbing; }
63
+
64
+ /* The launcher is the way back in, in EVERY view, whenever the panel is not showing — since the
65
+ * control moved into the rail there is nothing else on the page that opens it, and a collapsed rail is
66
+ * an icon strip a newcomer has no reason to search. Hidden while the panel is open, because a button
67
+ * that opens what is already open is a button that does nothing. */
68
+ .assistant-launcher-wrap { display: none; }
69
+ html.assistant-closed .assistant-launcher-wrap {
70
+ display: block;
71
+ position: fixed;
72
+ left: 1.375rem;
73
+ bottom: 1.375rem;
74
+ z-index: 40;
75
+ }
76
+ .assistant-launcher { display: none; }
77
+ html.assistant-closed .assistant-launcher {
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ width: 3.5rem;
82
+ height: 3.5rem;
83
+ padding: 0;
84
+ border: 0;
85
+ border-radius: 50%;
86
+ background: var(--brand-grad);
87
+ color: var(--text-inverse);
88
+ box-shadow: var(--shadow-float);
89
+ cursor: pointer;
90
+ }
91
+
92
+ /* The menu the launcher offers on hover. It opens to the right of the button, which is where there is
93
+ room — the launcher sits in the bottom-left corner. */
94
+ .launcher-dropdown { min-width: 14rem; }
95
+ html.assistant-closed .assistant-launcher:hover { filter: brightness(1.08); }
96
+ .assistant-launcher .ai-mark { width: 1.5rem; height: 1.5rem; }
97
+
98
+ /* Full page — /assistant. There is no side panel anywhere in this view; the switch in the toolbar
99
+ * is the way to the page, and the page itself is the panel. */
100
+ html[data-assistant-view="full"] .assistant-side { display: none; }
101
+ html[data-assistant-view="full"] .app-content { margin-left: 0; }
102
+
103
+ /* The page the full view goes to. The panel fills the height it is given rather than collapsing to
104
+ * the height of one message, which on an empty conversation is a card floating in a white page. */
105
+ .assistant-page .assistant-panel {
106
+ display: flex;
107
+ flex-direction: column;
108
+ min-height: min(46rem, calc(100vh - 14rem));
109
+ }
110
+ .assistant-page .assistant-body { flex: 1; display: flex; flex-direction: column; min-height: 0; }
111
+
112
+ /* ---------- the header controls ---------- */
113
+
114
+ .assistant-controls { display: flex; align-items: center; gap: 0.125rem; flex: none; }
115
+
116
+ /* On the brand gradient, so light ink and a translucent hover — a default button here would be a
117
+ * light outline with near-black text, which is invisible against both ends of the gradient. */
118
+ .assistant-head .assistant-ctl {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 1.875rem;
123
+ height: 1.875rem;
124
+ padding: 0;
125
+ border: 0;
126
+ border-radius: 0.5rem;
127
+ background: transparent;
128
+ color: var(--text-inverse);
129
+ cursor: pointer;
130
+ }
131
+ .assistant-head .assistant-ctl:hover { background: rgba(0, 0, 0, .18); }
132
+ .assistant-head .assistant-ctl:focus-visible { outline: 2px solid rgba(255, 255, 255, .9); outline-offset: 2px; }
133
+
134
+ /* The menu is an ordinary dropdown on the page's surface, not on the gradient: it hangs below the
135
+ * header, over the conversation. */
136
+ .assistant-dropdown { min-width: 14rem; }
137
+ .assistant-dropdown .uk-nav-header { padding-top: 0.35rem; }
138
+
139
+ /* The assistant's control lives in the rail, with the other things that apply to whatever page you are
140
+ on. It was a tile in the toolbar, which is the corner the brand and the page identity own — and in
141
+ the full-page view no ordinary page draws a panel, so its own header menu is not on screen either.
142
+ One row here carries everything: show, hide, a new conversation, and which of the four ways it is
143
+ shown. */
144
+ .actions-nav .actions-assistant-wrap { display: block; }
145
+ .actions-nav .actions-assistant { cursor: pointer; width: 100%; text-align: left; }
146
+
147
+ /* Show and hide are two items; the stylesheet knows which one applies, so neither needs a script to
148
+ rename it. */
149
+ html.assistant-closed .assistant-when-open,
150
+ html:not(.assistant-closed) .assistant-when-closed { display: none; }
151
+ /* On the full page the assistant is in front of you, and neither applies. */
152
+ html[data-page-assistant] .assistant-when-open { display: none; }
153
+
154
+ /* Under 960px the sidebar views already give way to the page: a 25rem column on a phone is the
155
+ * whole screen. The panel becomes the floating one, which is what there is room for. */
156
+ @media (max-width: 59.99em) {
157
+ html[data-assistant-view="wide"] .assistant-side,
158
+ html[data-assistant-view="sidebar"] .assistant-side {
159
+ top: auto;
160
+ left: 0.75rem;
161
+ right: 0.75rem;
162
+ bottom: 0.75rem;
163
+ width: auto;
164
+ height: min(32rem, calc(100vh - 6rem));
165
+ padding: 0;
166
+ }
167
+ html[data-assistant-view="wide"] .app-content,
168
+ html[data-assistant-view="sidebar"] .app-content { margin-left: 0; }
169
+ }
170
+
171
+ /* On /assistant the panel IS the page: widening it and closing it are not things that page can do,
172
+ so those two controls are not offered there. The menu stays — it is the way back to a sidebar. */
173
+ .assistant-page .assistant-wider,
174
+ .assistant-page .assistant-hide { display: none; }
175
+
176
+ /* The assistant's control is the mark alone, wherever the navigation puts it — the icon the launcher and
177
+ every reply carry, in the brand blue that says which of these controls is the assistant. Sized here
178
+ rather than on the SVG so the rail and the top bar can differ if they ever need to. */
179
+ .actions-assistant .ai-mark { width: 1.05rem; height: 1.05rem; display: block; color: var(--brand-primary); }
180
+ .actions-assistant { display: inline-flex; align-items: center; justify-content: center; }
181
+ html.dark .actions-assistant .ai-mark { color: var(--link); }
182
+ /* In the rail it is a full-width tile like its neighbours, so the mark sits where their icons do. */
183
+ .actions-nav .actions-assistant { justify-content: flex-start; }
184
+
185
+ /* An entry shown as its icon alone, or as its label alone. Both halves are left in the markup — a screen
186
+ reader still reads the name, and the title still answers a hover — so this is presentation and nothing
187
+ else. */
188
+ .nav-show-icon .action-label { display: none; }
189
+ .nav-show-icon > .action-btn { padding-left: 0.45rem; padding-right: 0.45rem; }
190
+ .actions-rail-inner .nav-show-icon > .action-btn { justify-content: center; }
191
+ /* Text alone hides the glyph, whichever kind it is — UIKit's, Tabler's sprite, or the assistant's mark.
192
+ A count badge is not an icon and stays. */
193
+ .nav-show-text [uk-icon],
194
+ .nav-show-text .uk-icon,
195
+ .nav-show-text .tabler-icon,
196
+ .nav-show-text .ai-mark { display: none; }
197
+
198
+ .assistant-dropdown .assistant-switch-row label { display: flex; align-items: center; gap: .5rem; padding: 5px 0; cursor: pointer; }
@@ -0,0 +1,74 @@
1
+ /* Settings → Build order.
2
+ *
3
+ * The navigation editor's `.nav-entries` is a WRAPPING row of pills, which is right for a rail group of
4
+ * six short labels and wrong for this: forty builders whose names run to sixty characters, where the
5
+ * question is only ever "what is above what". So the list keeps the navigation's entry chrome — the
6
+ * handle, the drag behaviour, the classes ui.js already knows — and overrides the layout to a single
7
+ * column of full-width rows, numbered, so a position can be read rather than counted.
8
+ *
9
+ * Every rule here is written `.nav-entries.build-order-list`, TWO classes, rather than the one it
10
+ * needs: components with no entry in COMPONENT_ORDER are concatenated alphabetically, and
11
+ * `build-order` sorts before `commands-center` — which owns `.nav-entries { display: flex }`. At equal
12
+ * specificity the later file wins, so a single class here would lose the tie and this list would
13
+ * silently render as a wrapping row of forty pills. Out-specify rather than depend on file order.
14
+ */
15
+
16
+ .nav-entries.build-order-list {
17
+ display: block; /* not the flex row .nav-entries is */
18
+ counter-reset: build-order;
19
+ margin: 0.75rem 0 1rem;
20
+ max-width: 46rem;
21
+ }
22
+
23
+ .nav-entries.build-order-list .build-order-row {
24
+ counter-increment: build-order;
25
+ width: 100%;
26
+ border-radius: 0.6rem; /* a row, not a pill */
27
+ padding: 0.4rem 0.6rem;
28
+ margin-bottom: 0.3rem;
29
+ gap: 0.6rem;
30
+ }
31
+
32
+ /* The position, as the list's own numbering rather than an <ol> marker: the marker sits outside the
33
+ row and does not move with a drag, so it would go on counting past an item that had been moved. */
34
+ .nav-entries.build-order-list .build-order-row::before {
35
+ content: counter(build-order);
36
+ flex: 0 0 1.6rem;
37
+ text-align: right;
38
+ color: var(--text-subtle);
39
+ font-variant-numeric: tabular-nums;
40
+ font-size: 0.75rem;
41
+ }
42
+
43
+ /* The label takes the room, so the file name and the controls stay at the right edge in one column
44
+ down the list rather than wherever each name happens to end. */
45
+ .nav-entries.build-order-list .nav-entry-label {
46
+ flex: 1 1 auto;
47
+ min-width: 0;
48
+ overflow: hidden;
49
+ text-overflow: ellipsis;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ .build-order-file {
54
+ font-size: 0.7rem;
55
+ color: var(--text-subtle);
56
+ white-space: nowrap;
57
+ }
58
+
59
+ .build-order-where {
60
+ font-size: 0.7rem;
61
+ white-space: nowrap;
62
+ }
63
+
64
+ /* Below the phone breakpoint the file name is the first thing to go: it is the identity of the row for
65
+ somebody editing YAML, and the label is the identity for everyone else. */
66
+ @media (max-width: 47.9375em) {
67
+ .build-order-file,
68
+ .build-order-where { display: none; }
69
+ }
70
+
71
+ .nav-entries.build-order-list .uk-sortable-drag .build-order-row,
72
+ .nav-entries.build-order-list .build-order-row.uk-sortable-drag {
73
+ box-shadow: 0 6px 18px rgb(0 0 0 / 18%);
74
+ }
@@ -0,0 +1,30 @@
1
+ /* Cloning a project as a git worktree.
2
+ *
3
+ * The option is one checkbox with two fields behind it, and a form that shows
4
+ * every field of every branch it might take reads as three decisions instead of
5
+ * one — so the branch field and its note appear only once the option is chosen.
6
+ * Done in CSS keyed on a class the checkbox toggles, so the disclosure survives
7
+ * the fragment being re-swapped by HTMX.
8
+ */
9
+ .clone-worktree {
10
+ border-left: 2px solid var(--subtle-brand);
11
+ padding-left: .75rem;
12
+ }
13
+ .clone-worktree.is-on { border-left-color: var(--brand-primary); }
14
+
15
+ .clone-worktree .worktree-branch,
16
+ .clone-worktree .worktree-branch-note { display: none; }
17
+ .clone-worktree.is-on .worktree-branch,
18
+ .clone-worktree.is-on .worktree-branch-note { display: block; }
19
+
20
+ /* The row's git relationship. A worktree and its main checkout are two folders
21
+ of one repository, and a listing gives no hint of that — which matters because
22
+ removing the main one destroys the other's history. Quiet by default; the main
23
+ checkout carries the brand accent because it is the one with dependents. */
24
+ .proj-worktree,
25
+ .proj-worktree-main { border: 1px solid var(--subtle-brand); border-radius: 999px; padding: 0 .5rem; }
26
+ .proj-worktree-main { border-color: var(--brand-primary); color: var(--brand-primary); }
27
+
28
+ /* The clone form's extra-address option, framed like the worktree one so the two
29
+ optional blocks read as siblings rather than as stray checkboxes in a form. */
30
+ .clone-address { border-left: 2px solid var(--subtle-brand); padding-left: .75rem; }
@@ -0,0 +1,61 @@
1
+ /* The command palette and the shortcut sheet (public/palette.js). A quiet layer: one panel, a hairline,
2
+ the brand blue only on the selected row. */
3
+ #palette {
4
+ position: fixed; inset: 0; z-index: 1100;
5
+ display: flex; align-items: flex-start; justify-content: center;
6
+ padding-top: 12vh;
7
+ background: rgba(10, 18, 36, 0.45);
8
+ overscroll-behavior: contain;
9
+ }
10
+ #palette[hidden] { display: none; }
11
+ #palette .palette-dialog {
12
+ width: min(40rem, 92vw);
13
+ max-height: 70vh;
14
+ display: flex; flex-direction: column;
15
+ background: var(--surface-card);
16
+ color: var(--text-default);
17
+ border: 1px solid var(--line);
18
+ border-radius: 0.875rem;
19
+ box-shadow: var(--shadow-float);
20
+ overflow: hidden;
21
+ }
22
+ #palette .palette-input {
23
+ border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
24
+ padding: 1rem 1.125rem; font-size: 1.0625rem;
25
+ background: transparent; color: inherit; outline: none;
26
+ }
27
+ #palette .palette-input:focus-visible { box-shadow: inset 0 -2px 0 var(--brand-primary); }
28
+ #palette .palette-list { list-style: none; margin: 0; padding: 0.375rem; overflow-y: auto; overscroll-behavior: contain; }
29
+ #palette .palette-group {
30
+ padding: 0.625rem 0.75rem 0.25rem; font-size: 0.8125rem; font-weight: 600; color: var(--text-subtle);
31
+ }
32
+ #palette .palette-row {
33
+ display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
34
+ padding: 0.5rem 0.75rem; border-radius: 0.5rem; cursor: pointer;
35
+ }
36
+ #palette .palette-row[aria-selected="true"] { background: var(--subtle-brand); box-shadow: inset 0.1875rem 0 0 var(--brand-primary); }
37
+ html.dark #palette .palette-row[aria-selected="true"] { background: rgba(0, 116, 224, 0.22); }
38
+ #palette .palette-label { font-weight: 500; }
39
+ #palette .palette-hint { color: var(--text-subtle); font-size: 0.875rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
40
+ #palette .palette-shortcut { cursor: default; display: grid; grid-template-columns: 13rem 1fr; align-items: baseline; gap: 0.75rem; }
41
+ #palette .palette-shortcut kbd { justify-self: start; }
42
+ #palette .palette-empty { padding: 1rem 0.75rem; color: var(--text-subtle); }
43
+ #palette .palette-foot {
44
+ display: flex; gap: 1.25rem; padding: 0.5rem 1rem; border-top: 1px solid var(--line);
45
+ font-size: 0.8125rem; color: var(--text-subtle);
46
+ }
47
+ #palette kbd {
48
+ display: inline-block; margin-right: 0.25rem; padding: 0 0.375rem;
49
+ border: 1px solid var(--line); border-radius: 0.3125rem; border-bottom-width: 2px;
50
+ font: 0.75rem/1.4 var(--font-code, monospace); color: var(--text-default); background: var(--surface-card);
51
+ }
52
+ @media (prefers-reduced-motion: reduce) { #palette * { transition: none !important; animation: none !important; } }
53
+
54
+ /* The way in, in the toolbar: a search icon and the key, quiet until hovered. */
55
+ .palette-trigger { display: inline-flex; align-items: center; gap: 0.5rem; background: none; border: 0; cursor: pointer; color: var(--text-subtle); }
56
+ .palette-trigger:hover { color: var(--text-default); }
57
+ .palette-trigger:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; border-radius: 0.375rem; }
58
+ .palette-trigger .palette-trigger-key {
59
+ padding: 0 0.375rem; border: 1px solid var(--line); border-radius: 0.3125rem;
60
+ font: 0.75rem/1.4 var(--font-code, monospace); color: inherit;
61
+ }