@1agh/maude 0.42.0 → 0.44.0

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 (332) hide show
  1. package/apps/studio/.ai/cache/_stats.json +7 -0
  2. package/apps/studio/acp/bootstrap-brief.ts +12 -2
  3. package/apps/studio/acp/bridge.ts +44 -10
  4. package/apps/studio/acp/env.ts +23 -0
  5. package/apps/studio/acp/login-state.ts +263 -0
  6. package/apps/studio/acp/plugin-bootstrap.ts +31 -25
  7. package/apps/studio/acp/probe.ts +152 -1
  8. package/apps/studio/annotations-layer.tsx +1451 -324
  9. package/apps/studio/annotations-model.ts +58 -0
  10. package/apps/studio/api.ts +568 -24
  11. package/apps/studio/bin/_audio-search.mjs +140 -0
  12. package/apps/studio/bin/_import-asset-pdf-worker.mjs +34 -0
  13. package/apps/studio/bin/_import-asset.mjs +815 -0
  14. package/apps/studio/bin/_import-brand.mjs +635 -0
  15. package/apps/studio/bin/_import-tokens-alias-resolver.mjs +95 -0
  16. package/apps/studio/bin/_import-tokens.mjs +1201 -0
  17. package/apps/studio/bin/_ingest-footage.mjs +386 -0
  18. package/apps/studio/bin/_probe-footage-playwright.mjs +269 -0
  19. package/apps/studio/bin/_transcribe.mjs +419 -0
  20. package/apps/studio/bin/_transcribe.test.mjs +80 -0
  21. package/apps/studio/bin/_video-playwright.mjs +141 -71
  22. package/apps/studio/bin/audio-search.sh +28 -0
  23. package/apps/studio/bin/generate.sh +154 -0
  24. package/apps/studio/bin/import-asset.sh +34 -0
  25. package/apps/studio/bin/import-brand.sh +29 -0
  26. package/apps/studio/bin/import-tokens.sh +33 -0
  27. package/apps/studio/bin/ingest-footage.sh +27 -0
  28. package/apps/studio/bin/photo-adjust.sh +163 -0
  29. package/apps/studio/bin/photo-bg-remove.sh +258 -0
  30. package/apps/studio/bin/probe-footage.sh +28 -0
  31. package/apps/studio/bin/read-annotations.mjs +125 -2
  32. package/apps/studio/bin/transcribe.sh +39 -0
  33. package/apps/studio/canvas-edit.ts +656 -12
  34. package/apps/studio/canvas-lib.tsx +637 -10
  35. package/apps/studio/canvas-pipeline.ts +65 -7
  36. package/apps/studio/canvas-shell.tsx +156 -16
  37. package/apps/studio/client/app.jsx +2385 -699
  38. package/apps/studio/client/export-center.jsx +35 -1
  39. package/apps/studio/client/generate-dialog.jsx +352 -0
  40. package/apps/studio/client/github.js +18 -0
  41. package/apps/studio/client/inspector-controls.jsx +781 -0
  42. package/apps/studio/client/panels/BrandUploadPanel.jsx +233 -0
  43. package/apps/studio/client/panels/ChatPanel.jsx +15 -33
  44. package/apps/studio/client/panels/GitPanel.jsx +8 -0
  45. package/apps/studio/client/panels/IntroVideoDialog.jsx +44 -0
  46. package/apps/studio/client/panels/OnboardingWizard.jsx +12 -0
  47. package/apps/studio/client/panels/ReadinessList.jsx +227 -7
  48. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +133 -21
  49. package/apps/studio/client/panels/SettingsPanel.jsx +828 -0
  50. package/apps/studio/client/panels/SetupChecklist.jsx +223 -0
  51. package/apps/studio/client/panels/StickerPicker.jsx +160 -0
  52. package/apps/studio/client/panels/TimelinePanel.jsx +15 -2
  53. package/apps/studio/client/photo-knobs.jsx +432 -0
  54. package/apps/studio/client/styles/3-shell-maude.css +376 -28
  55. package/apps/studio/client/styles/4-components.css +223 -0
  56. package/apps/studio/client/styles/6-acp-chat.css +86 -0
  57. package/apps/studio/client/tour/quick-setup-tour.js +39 -0
  58. package/apps/studio/commands/annotation-strokes-command.ts +13 -3
  59. package/apps/studio/commands/edit-source-command.ts +31 -4
  60. package/apps/studio/config.schema.json +70 -0
  61. package/apps/studio/context-menu.tsx +42 -8
  62. package/apps/studio/context.ts +16 -0
  63. package/apps/studio/design-setup-readiness.ts +115 -0
  64. package/apps/studio/dist/client.bundle.js +5572 -21
  65. package/apps/studio/dist/comment-mount.js +2 -2
  66. package/apps/studio/dist/runtime/.min-sizes.json +1 -0
  67. package/apps/studio/dist/runtime/@imgly_background-removal.js +5543 -0
  68. package/apps/studio/dist/runtime/pixi-js.js +519 -519
  69. package/apps/studio/dist/styles.css +1 -1
  70. package/apps/studio/dom-selection.ts +25 -0
  71. package/apps/studio/draw/palette.ts +34 -0
  72. package/apps/studio/exporters/jobs.ts +47 -5
  73. package/apps/studio/exporters/video.ts +24 -4
  74. package/apps/studio/footage/schema.test.ts +287 -0
  75. package/apps/studio/footage/schema.ts +662 -0
  76. package/apps/studio/footage-store.ts +235 -0
  77. package/apps/studio/fs-watch.ts +10 -0
  78. package/apps/studio/generation/adapters/elevenlabs.ts +370 -0
  79. package/apps/studio/generation/adapters/gemini.ts +542 -0
  80. package/apps/studio/generation/adapters/groq.test.ts +80 -0
  81. package/apps/studio/generation/adapters/groq.ts +247 -0
  82. package/apps/studio/generation/audio-library.test.ts +95 -0
  83. package/apps/studio/generation/audio-library.ts +156 -0
  84. package/apps/studio/generation/captions.test.ts +78 -0
  85. package/apps/studio/generation/captions.ts +132 -0
  86. package/apps/studio/generation/download.ts +76 -0
  87. package/apps/studio/generation/elevenlabs.test.ts +223 -0
  88. package/apps/studio/generation/gemini.test.ts +350 -0
  89. package/apps/studio/generation/jobs.test.ts +128 -0
  90. package/apps/studio/generation/jobs.ts +243 -0
  91. package/apps/studio/generation/keys.test.ts +75 -0
  92. package/apps/studio/generation/keys.ts +162 -0
  93. package/apps/studio/generation/prefs.test.ts +95 -0
  94. package/apps/studio/generation/prefs.ts +82 -0
  95. package/apps/studio/generation/registry.ts +88 -0
  96. package/apps/studio/generation/types.test.ts +123 -0
  97. package/apps/studio/generation/types.ts +266 -0
  98. package/apps/studio/generation/whisper-models.test.ts +152 -0
  99. package/apps/studio/generation/whisper-models.ts +257 -0
  100. package/apps/studio/generation/whisper-spike-results.md +61 -0
  101. package/apps/studio/git/endpoints.ts +68 -7
  102. package/apps/studio/git/service.ts +199 -21
  103. package/apps/studio/github/service.ts +67 -0
  104. package/apps/studio/handoff.ts +3 -2
  105. package/apps/studio/http.ts +1039 -22
  106. package/apps/studio/input-router.tsx +10 -0
  107. package/apps/studio/inspect.ts +53 -1
  108. package/apps/studio/media/intro.mp4 +0 -0
  109. package/apps/studio/media-commit-chain.ts +119 -0
  110. package/apps/studio/paths.ts +20 -0
  111. package/apps/studio/photo/filters.ts +285 -0
  112. package/apps/studio/photo/pipeline.ts +607 -0
  113. package/apps/studio/photo/schema.ts +367 -0
  114. package/apps/studio/photo-store.ts +154 -0
  115. package/apps/studio/readiness.ts +108 -20
  116. package/apps/studio/runtime-bundle.ts +29 -6
  117. package/apps/studio/scaffold-design.ts +1145 -0
  118. package/apps/studio/server.ts +18 -2
  119. package/apps/studio/stickers/figjam-doodle/manifest.json +104 -0
  120. package/apps/studio/stickers/figjam-doodle/slice1.png +0 -0
  121. package/apps/studio/stickers/figjam-doodle/slice10.png +0 -0
  122. package/apps/studio/stickers/figjam-doodle/slice11.png +0 -0
  123. package/apps/studio/stickers/figjam-doodle/slice12.png +0 -0
  124. package/apps/studio/stickers/figjam-doodle/slice13.png +0 -0
  125. package/apps/studio/stickers/figjam-doodle/slice14.png +0 -0
  126. package/apps/studio/stickers/figjam-doodle/slice15.png +0 -0
  127. package/apps/studio/stickers/figjam-doodle/slice16.png +0 -0
  128. package/apps/studio/stickers/figjam-doodle/slice17.png +0 -0
  129. package/apps/studio/stickers/figjam-doodle/slice18.png +0 -0
  130. package/apps/studio/stickers/figjam-doodle/slice19.png +0 -0
  131. package/apps/studio/stickers/figjam-doodle/slice2.png +0 -0
  132. package/apps/studio/stickers/figjam-doodle/slice20.png +0 -0
  133. package/apps/studio/stickers/figjam-doodle/slice21.png +0 -0
  134. package/apps/studio/stickers/figjam-doodle/slice22.png +0 -0
  135. package/apps/studio/stickers/figjam-doodle/slice23.png +0 -0
  136. package/apps/studio/stickers/figjam-doodle/slice24.png +0 -0
  137. package/apps/studio/stickers/figjam-doodle/slice3.png +0 -0
  138. package/apps/studio/stickers/figjam-doodle/slice4.png +0 -0
  139. package/apps/studio/stickers/figjam-doodle/slice5.png +0 -0
  140. package/apps/studio/stickers/figjam-doodle/slice6.png +0 -0
  141. package/apps/studio/stickers/figjam-doodle/slice7.png +0 -0
  142. package/apps/studio/stickers/figjam-doodle/slice8.png +0 -0
  143. package/apps/studio/stickers/figjam-doodle/slice9.png +0 -0
  144. package/apps/studio/stickers/life-style/best.png +0 -0
  145. package/apps/studio/stickers/life-style/come-on.png +0 -0
  146. package/apps/studio/stickers/life-style/cut.png +0 -0
  147. package/apps/studio/stickers/life-style/detox.png +0 -0
  148. package/apps/studio/stickers/life-style/just.png +0 -0
  149. package/apps/studio/stickers/life-style/manifest.json +68 -0
  150. package/apps/studio/stickers/life-style/mirror.png +0 -0
  151. package/apps/studio/stickers/life-style/nice.png +0 -0
  152. package/apps/studio/stickers/life-style/objects.png +0 -0
  153. package/apps/studio/stickers/life-style/ok.png +0 -0
  154. package/apps/studio/stickers/life-style/queen.png +0 -0
  155. package/apps/studio/stickers/life-style/star.png +0 -0
  156. package/apps/studio/stickers/life-style/sun.png +0 -0
  157. package/apps/studio/stickers/life-style/water.png +0 -0
  158. package/apps/studio/stickers/life-style/yeah.png +0 -0
  159. package/apps/studio/stickers/life-style/you-can.png +0 -0
  160. package/apps/studio/stickers/opposing-thoughts/check-the-deets.png +0 -0
  161. package/apps/studio/stickers/opposing-thoughts/cut-it.png +0 -0
  162. package/apps/studio/stickers/opposing-thoughts/dope.png +0 -0
  163. package/apps/studio/stickers/opposing-thoughts/fresh.png +0 -0
  164. package/apps/studio/stickers/opposing-thoughts/hot.png +0 -0
  165. package/apps/studio/stickers/opposing-thoughts/idea.png +0 -0
  166. package/apps/studio/stickers/opposing-thoughts/keep-exploring.png +0 -0
  167. package/apps/studio/stickers/opposing-thoughts/killed-it.png +0 -0
  168. package/apps/studio/stickers/opposing-thoughts/love-it.png +0 -0
  169. package/apps/studio/stickers/opposing-thoughts/manifest.json +88 -0
  170. package/apps/studio/stickers/opposing-thoughts/not-sure.png +0 -0
  171. package/apps/studio/stickers/opposing-thoughts/ok.png +0 -0
  172. package/apps/studio/stickers/opposing-thoughts/pure-gold.png +0 -0
  173. package/apps/studio/stickers/opposing-thoughts/save-for-later.png +0 -0
  174. package/apps/studio/stickers/opposing-thoughts/steal-this.png +0 -0
  175. package/apps/studio/stickers/opposing-thoughts/take-a-peek.png +0 -0
  176. package/apps/studio/stickers/opposing-thoughts/this-or-that.png +0 -0
  177. package/apps/studio/stickers/opposing-thoughts/thoughts.png +0 -0
  178. package/apps/studio/stickers/opposing-thoughts/vibes.png +0 -0
  179. package/apps/studio/stickers/opposing-thoughts/winner.png +0 -0
  180. package/apps/studio/stickers/opposing-thoughts/wip.png +0 -0
  181. package/apps/studio/stickers/project-status/group-100.png +0 -0
  182. package/apps/studio/stickers/project-status/group-101.png +0 -0
  183. package/apps/studio/stickers/project-status/group-102.png +0 -0
  184. package/apps/studio/stickers/project-status/group-103.png +0 -0
  185. package/apps/studio/stickers/project-status/group-104.png +0 -0
  186. package/apps/studio/stickers/project-status/group-105.png +0 -0
  187. package/apps/studio/stickers/project-status/group-106.png +0 -0
  188. package/apps/studio/stickers/project-status/group-107.png +0 -0
  189. package/apps/studio/stickers/project-status/group-108.png +0 -0
  190. package/apps/studio/stickers/project-status/group-109.png +0 -0
  191. package/apps/studio/stickers/project-status/group-110.png +0 -0
  192. package/apps/studio/stickers/project-status/group-111.png +0 -0
  193. package/apps/studio/stickers/project-status/group-112.png +0 -0
  194. package/apps/studio/stickers/project-status/group-113.png +0 -0
  195. package/apps/studio/stickers/project-status/group-114.png +0 -0
  196. package/apps/studio/stickers/project-status/group-115.png +0 -0
  197. package/apps/studio/stickers/project-status/group-117.png +0 -0
  198. package/apps/studio/stickers/project-status/group-118.png +0 -0
  199. package/apps/studio/stickers/project-status/group-119.png +0 -0
  200. package/apps/studio/stickers/project-status/group-120.png +0 -0
  201. package/apps/studio/stickers/project-status/group-121.png +0 -0
  202. package/apps/studio/stickers/project-status/group-122.png +0 -0
  203. package/apps/studio/stickers/project-status/group-41.png +0 -0
  204. package/apps/studio/stickers/project-status/group-42.png +0 -0
  205. package/apps/studio/stickers/project-status/group-43.png +0 -0
  206. package/apps/studio/stickers/project-status/group-44.png +0 -0
  207. package/apps/studio/stickers/project-status/group-45.png +0 -0
  208. package/apps/studio/stickers/project-status/group-46.png +0 -0
  209. package/apps/studio/stickers/project-status/group-47.png +0 -0
  210. package/apps/studio/stickers/project-status/group-48.png +0 -0
  211. package/apps/studio/stickers/project-status/group-49.png +0 -0
  212. package/apps/studio/stickers/project-status/group-50.png +0 -0
  213. package/apps/studio/stickers/project-status/group-51.png +0 -0
  214. package/apps/studio/stickers/project-status/group-52.png +0 -0
  215. package/apps/studio/stickers/project-status/group-53.png +0 -0
  216. package/apps/studio/stickers/project-status/group-54.png +0 -0
  217. package/apps/studio/stickers/project-status/group-55.png +0 -0
  218. package/apps/studio/stickers/project-status/group-56.png +0 -0
  219. package/apps/studio/stickers/project-status/group-57.png +0 -0
  220. package/apps/studio/stickers/project-status/group-58.png +0 -0
  221. package/apps/studio/stickers/project-status/group-59.png +0 -0
  222. package/apps/studio/stickers/project-status/group-60.png +0 -0
  223. package/apps/studio/stickers/project-status/group-61.png +0 -0
  224. package/apps/studio/stickers/project-status/group-62.png +0 -0
  225. package/apps/studio/stickers/project-status/group-63.png +0 -0
  226. package/apps/studio/stickers/project-status/group-64.png +0 -0
  227. package/apps/studio/stickers/project-status/group-65.png +0 -0
  228. package/apps/studio/stickers/project-status/group-66.png +0 -0
  229. package/apps/studio/stickers/project-status/group-67.png +0 -0
  230. package/apps/studio/stickers/project-status/group-68.png +0 -0
  231. package/apps/studio/stickers/project-status/group-69.png +0 -0
  232. package/apps/studio/stickers/project-status/group-70.png +0 -0
  233. package/apps/studio/stickers/project-status/group-71.png +0 -0
  234. package/apps/studio/stickers/project-status/group-72.png +0 -0
  235. package/apps/studio/stickers/project-status/group-73.png +0 -0
  236. package/apps/studio/stickers/project-status/group-74.png +0 -0
  237. package/apps/studio/stickers/project-status/group-75.png +0 -0
  238. package/apps/studio/stickers/project-status/group-76.png +0 -0
  239. package/apps/studio/stickers/project-status/group-77.png +0 -0
  240. package/apps/studio/stickers/project-status/group-78.png +0 -0
  241. package/apps/studio/stickers/project-status/group-79.png +0 -0
  242. package/apps/studio/stickers/project-status/group-80.png +0 -0
  243. package/apps/studio/stickers/project-status/group-81.png +0 -0
  244. package/apps/studio/stickers/project-status/group-82.png +0 -0
  245. package/apps/studio/stickers/project-status/group-83.png +0 -0
  246. package/apps/studio/stickers/project-status/group-84.png +0 -0
  247. package/apps/studio/stickers/project-status/group-85.png +0 -0
  248. package/apps/studio/stickers/project-status/group-86.png +0 -0
  249. package/apps/studio/stickers/project-status/group-87.png +0 -0
  250. package/apps/studio/stickers/project-status/group-88.png +0 -0
  251. package/apps/studio/stickers/project-status/group-89.png +0 -0
  252. package/apps/studio/stickers/project-status/group-90.png +0 -0
  253. package/apps/studio/stickers/project-status/group-91.png +0 -0
  254. package/apps/studio/stickers/project-status/group-92.png +0 -0
  255. package/apps/studio/stickers/project-status/group-93.png +0 -0
  256. package/apps/studio/stickers/project-status/group-94.png +0 -0
  257. package/apps/studio/stickers/project-status/group-96.png +0 -0
  258. package/apps/studio/stickers/project-status/group-97.png +0 -0
  259. package/apps/studio/stickers/project-status/group-98.png +0 -0
  260. package/apps/studio/stickers/project-status/group-99.png +0 -0
  261. package/apps/studio/stickers/project-status/manifest.json +328 -0
  262. package/apps/studio/test/_helpers.ts +15 -2
  263. package/apps/studio/test/acp-bootstrap-brief.test.ts +11 -0
  264. package/apps/studio/test/acp-bridge.test.ts +34 -1
  265. package/apps/studio/test/acp-commands.test.ts +2 -1
  266. package/apps/studio/test/acp-env.test.ts +30 -0
  267. package/apps/studio/test/acp-plugin-bootstrap.test.ts +23 -57
  268. package/apps/studio/test/acp-session-plugins.test.ts +72 -1
  269. package/apps/studio/test/annotation-strokes-command.test.ts +17 -0
  270. package/apps/studio/test/annotations-layer.test.ts +174 -0
  271. package/apps/studio/test/asset-api.test.ts +3 -3
  272. package/apps/studio/test/canvas-edit.test.ts +17 -6
  273. package/apps/studio/test/canvas-origin-gate.test.ts +50 -0
  274. package/apps/studio/test/csp-canvas-shell.test.ts +11 -0
  275. package/apps/studio/test/csrf-write-guard.test.ts +23 -1
  276. package/apps/studio/test/design-setup-readiness.test.ts +155 -0
  277. package/apps/studio/test/dynamic-text-edit.test.ts +162 -0
  278. package/apps/studio/test/edit-source-command.test.ts +23 -0
  279. package/apps/studio/test/element-structural-edit.test.ts +154 -0
  280. package/apps/studio/test/fixtures/fake-claude-auth.mjs +13 -0
  281. package/apps/studio/test/footage-store.test.ts +100 -0
  282. package/apps/studio/test/generate-route.test.ts +106 -0
  283. package/apps/studio/test/git-branches.test.ts +97 -0
  284. package/apps/studio/test/github-api.test.ts +56 -1
  285. package/apps/studio/test/import-asset-browser.test.ts +64 -0
  286. package/apps/studio/test/import-asset-route.test.ts +71 -0
  287. package/apps/studio/test/import-asset.test.ts +399 -0
  288. package/apps/studio/test/import-brand-browser.test.ts +88 -0
  289. package/apps/studio/test/import-brand-route.test.ts +120 -0
  290. package/apps/studio/test/import-brand.test.ts +206 -0
  291. package/apps/studio/test/import-tokens.test.ts +722 -0
  292. package/apps/studio/test/input-router.test.ts +33 -0
  293. package/apps/studio/test/inspect-script-syntax.test.ts +53 -0
  294. package/apps/studio/test/media-commit-chain.test.ts +210 -0
  295. package/apps/studio/test/photo-bg-remove-validation.test.ts +71 -0
  296. package/apps/studio/test/photo-canvas-bundle.test.ts +61 -0
  297. package/apps/studio/test/photo-edit-api.test.ts +201 -0
  298. package/apps/studio/test/photo-filters.test.ts +149 -0
  299. package/apps/studio/test/photo-pipeline.test.ts +106 -0
  300. package/apps/studio/test/photo-taxonomy.test.ts +96 -0
  301. package/apps/studio/test/read-annotations.test.ts +164 -0
  302. package/apps/studio/test/readiness.test.ts +13 -11
  303. package/apps/studio/test/scaffold-design.test.ts +122 -0
  304. package/apps/studio/test/shell-importmap.test.ts +49 -0
  305. package/apps/studio/test/text-editability-stamp.test.ts +131 -0
  306. package/apps/studio/test/timeline-parse.test.ts +57 -0
  307. package/apps/studio/test/tool-palette-insert-anchor.test.ts +5 -3
  308. package/apps/studio/test/tour-overlay.test.tsx +16 -0
  309. package/apps/studio/test/undo-stack.test.ts +33 -0
  310. package/apps/studio/test/video-asset.test.ts +5 -5
  311. package/apps/studio/test/video-render-bridge.test.ts +23 -1
  312. package/apps/studio/text-caret.ts +239 -0
  313. package/apps/studio/tool-palette.tsx +49 -21
  314. package/apps/studio/ui-prefs.ts +130 -0
  315. package/apps/studio/undo-stack.ts +94 -2
  316. package/apps/studio/use-annotation-resize.tsx +4 -4
  317. package/apps/studio/use-canvas-media-drop.tsx +40 -1
  318. package/apps/studio/use-chrome-visibility.tsx +8 -2
  319. package/apps/studio/use-selection-set.tsx +21 -0
  320. package/apps/studio/video-comp.tsx +48 -7
  321. package/apps/studio/whats-new.json +149 -0
  322. package/apps/studio/ws.ts +5 -0
  323. package/cli/bin/maude.mjs +6 -6
  324. package/cli/commands/design.mjs +67 -3
  325. package/cli/lib/gitignore-block.mjs +2 -0
  326. package/cli/lib/pkg-root.mjs +107 -0
  327. package/cli/lib/pkg-root.test.mjs +79 -0
  328. package/cli/lib/reconstruct-toolset.test.mjs +194 -0
  329. package/cli/lib/update-check.mjs +8 -0
  330. package/package.json +13 -11
  331. package/plugins/design/dependencies.json +18 -0
  332. package/plugins/design/templates/_shell.html +1 -0
@@ -42,9 +42,15 @@ export function ChromeVisibilityProvider({
42
42
  children: ReactNode;
43
43
  initial?: Partial<ChromeVisibilityState>;
44
44
  }) {
45
+ // Defaults MUST match the app-shell's own default view-prefs (app.jsx
46
+ // MINIMAP_STORE / ZOOMCTL_STORE both default false) — otherwise a freshly
47
+ // loaded canvas shows the minimap/zoom before (or instead of, when the seed
48
+ // races the early inline-script `dgn:'loaded'`) the shell's OFF state is
49
+ // applied, so a user with both toggled off still sees them. `present` is a
50
+ // transient overlay, never persisted, so it stays false.
45
51
  const [state, setState] = useState<ChromeVisibilityState>({
46
- minimap: initial?.minimap ?? true,
47
- zoom: initial?.zoom ?? true,
52
+ minimap: initial?.minimap ?? false,
53
+ zoom: initial?.zoom ?? false,
48
54
  present: initial?.present ?? false,
49
55
  });
50
56
  const setChrome = useCallback((patch: Partial<ChromeVisibilityState>) => {
@@ -84,6 +84,27 @@ export interface Selection {
84
84
  * block child. Absent for a detached node / no parent. */
85
85
  parentDisplay?: string;
86
86
  parentFlexDirection?: string;
87
+ /** feature-photo-editor (Task 14) — set at selection resolution when the hit is
88
+ * a content-addressed photo. `artboard-img` = an `<img src="assets/<sha8>.<ext>">`
89
+ * authored in artboard TSX; `annotation-image` is threaded separately (the
90
+ * annotation model has no data-cd-id, so it never rides this DOM-selection
91
+ * path — see app.jsx's `edit-annotation-photo-request` handler). Absent for a
92
+ * non-photo element or an `<img>` whose src isn't content-addressed. */
93
+ photoKind?: 'artboard-img' | 'annotation-image';
94
+ /** The resolved `assets/<sha8>.<ext>` source, when `photoKind` is set — the key
95
+ * the Photo tab passes to `/_api/photo-edit`. */
96
+ photoAsset?: string;
97
+ }
98
+
99
+ /** feature-photo-editor — pull the content-addressed `assets/<sha8>.<ext>` out of
100
+ * an image src (relative, absolute, or embedded in an outerHTML excerpt). Returns
101
+ * null for a non-content-addressed src (external URL, SVG icon, data: URI) — those
102
+ * aren't editable by the sidecar-keyed photo pipeline. Exported so both the
103
+ * selection resolver and the Inspector's fallback derivation share one regex. */
104
+ export function photoAssetFromString(s: string | null | undefined): string | null {
105
+ if (!s) return null;
106
+ const m = /assets\/[0-9a-f]{8}\.[a-z0-9]+/i.exec(s);
107
+ return m ? m[0] : null;
87
108
  }
88
109
 
89
110
  interface SelectionSetValue {
@@ -116,6 +116,18 @@ function registry(): Map<string, CompEntry> {
116
116
  return w.__maudeVideoComps;
117
117
  }
118
118
 
119
+ // rca/issue-video-artboard-frame-reset-on-edit — the LAST live-preview frame
120
+ // per comp, hoisted above every remount boundary (module scope, mirrors
121
+ // canvas-lib.tsx's `liveViewport`). A canvas edit (ACP/agent/inspector/text)
122
+ // remounts the canvas subtree (canvas-comment-mount.tsx `key={attempt}`), and
123
+ // video-comp.tsx's own module survives that remount — so a fresh VideoComp
124
+ // mount can read the Timeline's last position here instead of always opening
125
+ // on the static `posterFrame`. A full page load clears it — correct; the
126
+ // shell re-seeds via the `timeline-comps` re-announce (app.jsx) once the new
127
+ // Player registers. Never touched during capture (`hideChrome()` forces the
128
+ // deterministic frame 0 / render-shim frame regardless of this mirror).
129
+ const liveTimelineFrame = new Map<string, number>();
130
+
119
131
  function clamp(n: number, lo: number, hi: number): number {
120
132
  return Math.max(lo, Math.min(hi, n));
121
133
  }
@@ -226,7 +238,7 @@ export interface VideoCompProps extends VideoCompMeta {
226
238
  /** Show the Player's own transport chrome in PREVIEW. Auto-hidden during
227
239
  * capture/export (the `?hide-chrome=1` shell flag). Default true. */
228
240
  controls?: boolean;
229
- /** Loop preview playback. Default true. */
241
+ /** Loop preview playback. Default false — the user opts in via the Timeline. */
230
242
  loop?: boolean;
231
243
  /**
232
244
  * Autoplay in preview. Default: play unless the viewer prefers reduced motion
@@ -281,7 +293,7 @@ export function VideoComp({
281
293
  id,
282
294
  inputProps,
283
295
  controls,
284
- loop = true,
296
+ loop = false,
285
297
  autoPlay,
286
298
  }: VideoCompProps) {
287
299
  const ref = useRef<PlayerRef | null>(null);
@@ -301,9 +313,15 @@ export function VideoComp({
301
313
  // autoplay; capture never autoplays (the shim drives frames). Opens on a
302
314
  // poster frame ~35% in so a paused comp shows content, not an empty frame 0.
303
315
  const play = (autoPlay ?? false) && !capturing;
304
- const posterFrame = capturing
316
+ const maxFrame = Math.max(0, durationInFrames - 1);
317
+ const posterFrame = capturing ? 0 : Math.min(maxFrame, Math.round(durationInFrames * 0.35));
318
+ // rca/issue-video-artboard-frame-reset-on-edit — reopen on the Timeline's
319
+ // last known frame for this comp (survives the remount that tore this
320
+ // instance down), not the fixed poster-frame formula. Falls back to
321
+ // posterFrame on a genuinely first-ever mount (nothing recorded yet).
322
+ const initialFrame = capturing
305
323
  ? 0
306
- : Math.min(Math.max(0, durationInFrames - 1), Math.round(durationInFrames * 0.35));
324
+ : clamp(liveTimelineFrame.get(compId) ?? posterFrame, 0, maxFrame);
307
325
 
308
326
  // Register with the seek bridge for the lifetime of this mount.
309
327
  useEffect(() => {
@@ -353,8 +371,10 @@ export function VideoComp({
353
371
  if (!p) return;
354
372
  try {
355
373
  if (m.dgn === 'timeline-seek' && typeof m.frame === 'number') {
374
+ const f = clamp(Math.round(m.frame), 0, Math.max(0, durationInFrames - 1));
356
375
  p.pause();
357
- p.seekTo(clamp(Math.round(m.frame), 0, Math.max(0, durationInFrames - 1)));
376
+ p.seekTo(f);
377
+ liveTimelineFrame.set(compId, f);
358
378
  } else if (m.dgn === 'timeline-play') {
359
379
  p.play();
360
380
  } else if (m.dgn === 'timeline-pause') {
@@ -382,7 +402,9 @@ export function VideoComp({
382
402
  if (!p) return;
383
403
  const onFrame = () => {
384
404
  try {
385
- postToShell({ dgn: 'timeline-frame', id: compId, frame: p.getCurrentFrame() });
405
+ const f = p.getCurrentFrame();
406
+ liveTimelineFrame.set(compId, f);
407
+ postToShell({ dgn: 'timeline-frame', id: compId, frame: f });
386
408
  } catch {
387
409
  /* ignore */
388
410
  }
@@ -397,6 +419,25 @@ export function VideoComp({
397
419
  };
398
420
  }, [compId]);
399
421
 
422
+ // iframe → shell: with loop off (now the default), the Player stops itself
423
+ // at the last frame — nothing else tells the Timeline panel's Play/Pause
424
+ // button to flip back to Play, so it reads "still playing" forever. Mirror
425
+ // the Player's own `ended` event so the shell can resync (rca/issue-video-
426
+ // artboard-loop-defaults-on).
427
+ useEffect(() => {
428
+ const p = ref.current;
429
+ if (!p) return;
430
+ const onEnded = () => postToShell({ dgn: 'timeline-ended', id: compId });
431
+ p.addEventListener('ended', onEnded);
432
+ return () => {
433
+ try {
434
+ p.removeEventListener('ended', onEnded);
435
+ } catch {
436
+ /* ignore */
437
+ }
438
+ };
439
+ }, [compId]);
440
+
400
441
  const style = useMemo(() => ({ width: '100%', height: '100%', display: 'block' as const }), []);
401
442
 
402
443
  return (
@@ -416,7 +457,7 @@ export function VideoComp({
416
457
  controls={showControls}
417
458
  loop={loopState}
418
459
  autoPlay={play}
419
- initialFrame={posterFrame}
460
+ initialFrame={initialFrame}
420
461
  // Clicking the artboard must NOT toggle playback — it selects elements
421
462
  // for editing (the whole point of a canvas). Play only via the explicit
422
463
  // transport button (Player controls / Timeline panel). Same for the
@@ -1,6 +1,155 @@
1
1
  {
2
2
  "$schema": "./whats-new.schema.json",
3
3
  "entries": [
4
+ {
5
+ "id": "bring-your-brand",
6
+ "version": "0.44.0",
7
+ "date": "2026-07-14",
8
+ "kind": "feature",
9
+ "title": "Bring your existing brand into Maude",
10
+ "summary": "Have a logo already? Upload it from the Quick setup checklist and Maude pulls out its color palette (and any recognizable font names) to seed a new design system — nothing is applied automatically, you confirm every choice during setup. Works with SVG logos; safety-checked before anything touches your project.",
11
+ "surface": "design-ui",
12
+ "tour": [
13
+ {
14
+ "target": "[data-testid=\"onboarding-bring-brand\"]",
15
+ "title": "Bring my existing brand",
16
+ "body": "Upload a logo SVG to seed a new design system's colors and fonts from it."
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "id": "inspector-designer-mode",
22
+ "version": "0.44.0",
23
+ "date": "2026-07-14",
24
+ "kind": "feature",
25
+ "title": "A Figma-friendly view of the CSS panel",
26
+ "summary": "The Inspector now speaks two languages. Advanced keeps the real CSS property names; Designer regroups the exact same controls into Figma-familiar sections — Fill, Stroke, Corner radius, Auto layout, Effects, Opacity, Text — and relabels the rows, so you can style without thinking in CSS. Flip between them from the small toggle in the panel's top-right corner, or set a default in Settings → Appearance. Same live edits underneath, and a value set in one view reads back correctly in the other. New in both views: Blur and Blend controls, plus a single 9-point pad for auto-layout alignment.",
27
+ "surface": "design-ui",
28
+ "tour": [
29
+ {
30
+ "target": "[data-tour=\"cp-mode\"]",
31
+ "title": "Advanced or Designer",
32
+ "body": "Toggle the CSS panel between raw property names and Figma-style vocabulary. Your choice is remembered — you can also set it in Settings → Appearance.",
33
+ "placement": "left",
34
+ "inspector": true,
35
+ "tab": "css",
36
+ "requireSelection": true
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "id": "watch-the-intro",
42
+ "version": "0.44.0",
43
+ "date": "2026-07-14",
44
+ "kind": "feature",
45
+ "title": "Watch the intro, right inside Maude",
46
+ "summary": "There's now a short product showreel (about a minute and a half) you can watch without leaving the app — Help → Watch the intro, or the same button on the welcome screen the first time you open Maude. It walks through the whole loop: a design system built from a conversation, the infinite canvas, pointing and commenting to direct an AI edit, live multiplayer, and shipping the result. Also lands as its own canvas (Maude Explainer) if you want to open it in the canvas browser like anything else.",
47
+ "surface": "design-ui",
48
+ "tour": [
49
+ {
50
+ "target": "[data-tour=\"help\"]",
51
+ "title": "Watch the intro",
52
+ "body": "Help → Watch the intro plays the full showreel without leaving Maude."
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "id": "bulk-media-insert",
58
+ "version": "0.44.0",
59
+ "date": "2026-07-14",
60
+ "kind": "feature",
61
+ "title": "Insert several photos at once",
62
+ "summary": "The media picker now supports multi-select — pick several photos (or a mix of photos, videos, and audio) in one go instead of one at a time. Choose whether they land inside your artboard or float on the canvas as annotations; the picker figures out the sensible default (video and audio always go on the canvas, since only images can drop into an artboard) and lets you switch. Dragging several files from Finder onto the canvas at once is also more reliable now — every dropped file lands, even in a large batch.",
63
+ "surface": "design-ui"
64
+ },
65
+ {
66
+ "id": "ai-video-generation",
67
+ "version": "0.44.0",
68
+ "date": "2026-07-14",
69
+ "kind": "feature",
70
+ "title": "Generate video clips for your reels",
71
+ "summary": "The BYOK generator now makes video. Bring your own Google key and generate a clip with Veo — a text prompt (\"slow drone push over an alpine lake at dawn\"), or seed it from a still you already generated so the clip matches your hero's look. The clip lands in your assets like any other footage, and Maude treats it as first-class: when you build a reel it watches the generated clip alongside your real footage and places it as a beat, so you can fill a missing shot without hand-sourcing one. Video takes a few minutes and runs in the background — you'll get the finished clip when it's ready. Add your key under File → Settings — AI generation (⌘,); it stays on your machine.",
72
+ "surface": "design-ui"
73
+ },
74
+ {
75
+ "id": "ai-audio-generation",
76
+ "version": "0.44.0",
77
+ "date": "2026-07-14",
78
+ "kind": "feature",
79
+ "title": "Generate music, voiceover & subtitles",
80
+ "summary": "The BYOK generator now does audio. Bring your own ElevenLabs key and generate a music bed, sound effects, or a text-to-speech voiceover — one key covers the whole stack — and the track lands in your assets ready to drop under a reel. Before you spend credits on a music or SFX track, Maude searches audio you've already made (and your own ElevenLabs history — re-downloading it costs nothing) and offers a match to reuse. Subtitles are free and need no key at all: `maude design transcribe` runs whisper.cpp locally on any clip and writes word-timed SRT/VTT captions. Local setup is one click — Settings → Subtitles → Download model — and Maude auto-converts video containers (no ffmpeg wrangling) and picks the model for you. You pick the subtitle engine — local whisper, ElevenLabs Scribe, or Groq — in Settings; it's an explicit choice, so Maude never silently switches to a paid cloud engine behind your back. In a reel, generated music/voiceover become layered audio tracks (the music ducks under the voice) and the subtitles become a caption track that renders right on the video. Add your keys under File → Settings — AI generation (⌘,); they're stored only on your machine.",
81
+ "surface": "design-ui"
82
+ },
83
+ {
84
+ "id": "ai-media-generation",
85
+ "version": "0.44.0",
86
+ "date": "2026-07-14",
87
+ "kind": "feature",
88
+ "title": "Generate images with your own AI key",
89
+ "summary": "Bring your own Google (Nano Banana) API key and generate images right inside Maude — and the image lands straight on your canvas, not in a dead-end dialog. Use the Generate action (⌘K → \"Generate with AI\"), the command line, or just ask in the chat panel (\"generate a hero image of a mountain lake\"); the finished picture drops onto the artboard you're looking at. Add your key under File → Settings — AI generation (⌘,); it's stored only on your machine, sent straight to the provider, and never touches a canvas or your git history. Want to change a generated image? Ask to edit it (\"make the sky purple\") for a fresh AI-edited version, or fine-tune it in the Photo tab — every result is a normal asset you can place, edit, and export. Audio and video generation are on the way.",
90
+ "surface": "design-ui",
91
+ "tour": [
92
+ {
93
+ "target": "[data-tour=\"menubar-file\"]",
94
+ "title": "AI generation settings",
95
+ "body": "Open File → Settings — AI generation (⌘,) to add your provider key, then Generate with AI from the ⌘K palette.",
96
+ "placement": "bottom"
97
+ }
98
+ ]
99
+ },
100
+ {
101
+ "id": "photo-editor",
102
+ "version": "0.43.0",
103
+ "date": "2026-07-10",
104
+ "kind": "feature",
105
+ "title": "Edit photos right on the canvas",
106
+ "summary": "A new Photo tab in the Inspector lets you tune any photo on your canvas — brightness, contrast, saturation, exposure, hue, sepia, grayscale, duotone, grain, pattern overlays, and vignette/reveal masks — with a live preview, whether it's an image you placed in an artboard or one you dropped/pasted onto the canvas. One click removes the background entirely (runs in your browser, no upload). Reach it from the Inspector's Photo tab or right-click \"Edit Photo…\". Every edit is also scriptable headlessly via /design:photo, so an agent can tune a photo without touching a slider.",
107
+ "surface": "design-ui",
108
+ "tour": [
109
+ {
110
+ "target": "[data-tour=\"inspector-tabs\"]",
111
+ "title": "Photo tab",
112
+ "body": "Select any photo on the canvas and a Photo tab appears here — adjustments, duotone, grain, pattern, masks, and one-click background removal.",
113
+ "placement": "bottom"
114
+ }
115
+ ]
116
+ },
117
+ {
118
+ "id": "footage-analysis-director-reel",
119
+ "version": "0.43.0",
120
+ "date": "2026-07-10",
121
+ "kind": "feature",
122
+ "title": "Turn a folder of clips into a cut — Maude watches your footage first",
123
+ "summary": "New /design:reel: point Maude at a folder of raw video (or the clips already on your canvas) and it actually WATCHES the footage first — pulling keyframes from each clip to find the good shots and read its character — then directs them like an editor into a graphics-laden cut with titles and transitions, ready to scrub on the Timeline and export to MP4. It can use several moments from one clip, orders them so they tell a story, and needs no extra install (frames are decoded in the browser, no ffmpeg). Analysis is vision-only; drop in a music track and it rides under the finished cut.",
124
+ "surface": "design-ui"
125
+ },
126
+ {
127
+ "id": "ssh-remote-publish-and-pr-flow",
128
+ "version": "0.43.0",
129
+ "date": "2026-07-10",
130
+ "kind": "feature",
131
+ "title": "Share to a protected branch — via a pull request",
132
+ "summary": "On a GitHub project, \"Add to Shared version\" now pushes your draft and opens a pull request into main, so it works even when main is protected and a direct push would be rejected — the pull-request link opens right from the dialog. Publishing and getting the latest now work on SSH remotes too, and a blue \"Get latest\" button appears in the dock the moment a teammate's work is ahead, so you're nudged to pull without hunting for it.",
133
+ "surface": "design-ui"
134
+ },
135
+ {
136
+ "id": "unified-text-editing",
137
+ "version": "0.43.0",
138
+ "date": "2026-07-10",
139
+ "kind": "feature",
140
+ "title": "Edit any text on the canvas, inline",
141
+ "summary": "Every kind of text on the canvas now edits the same predictable way — click to place a blinking caret, type, Enter to confirm, Shift+Enter for a new line — across an element's copy, shape and sticky labels, standalone text, and section titles. Text that's filled in from a list or variable (like cards built from data) is now editable in place too: double-click and change the words directly, and it's traced back to the right source. Only genuinely computed text still sends you to chat or /design:edit.",
142
+ "surface": "design-ui"
143
+ },
144
+ {
145
+ "id": "whiteboard-annotation-improvements",
146
+ "version": "0.43.0",
147
+ "date": "2026-07-10",
148
+ "kind": "feature",
149
+ "title": "Bulk resize, sticky authors & fun stickers",
150
+ "summary": "Select several stickies, shapes or images together and resize the whole group proportionally with live corner handles, instead of one at a time. Sticky notes now show who drew them with a name badge. Text editing got a real pass: a visible blinking caret everywhere you edit text, double-click places the caret where you clicked instead of selecting everything, hovering text shows a text cursor, and a new shape or sticky auto-focuses so you can start typing immediately. Also new: a searchable, FigJam-style sticker picker with genuinely fun (not emoji) bundled sticker packs, and /design:board now understands generation requests like \"make me a team sprint retro\" directly.",
151
+ "surface": "design-ui"
152
+ },
4
153
  {
5
154
  "id": "background-export-notification-center",
6
155
  "version": "0.42.0",
package/apps/studio/ws.ts CHANGED
@@ -222,6 +222,11 @@ export function createWs(
222
222
  // clients only — same privileged-data class as the rest of this feed.
223
223
  ctx.bus.on('export:job', (job: unknown) => broadcast({ type: 'export:job', payload: job }));
224
224
 
225
+ // feature-ai-media-generation (DDR-16x) — generation job queue state changes
226
+ // (queued → running → done/failed). Same privileged-data class + snapshot
227
+ // shape as export:job; the notification center reuses the export-center chrome.
228
+ ctx.bus.on('generate:job', (job: unknown) => broadcast({ type: 'generate:job', payload: job }));
229
+
225
230
  // HMR broadcaster — turns fs:any change events into `canvas-hmr` messages.
226
231
  // The iframe-side client (in _shell.html) decides reload strategy from `mode`.
227
232
  // Uses broadcastHmr so the segregated canvas origin's HMR-only sockets get it.
package/cli/bin/maude.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from 'node:fs';
3
- import { dirname, resolve } from 'node:path';
3
+ import { resolve } from 'node:path';
4
+ import { resolvePkgRoot } from '../lib/pkg-root.mjs';
4
5
  // maude — Maude CLI. Scaffold .ai workspace, run dev servers, manage config.
5
- import { fileURLToPath } from 'node:url';
6
6
  import { runUpdateCheck } from '../lib/update-check.mjs';
7
7
 
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
- const CLI_ROOT = resolve(__dirname, '..');
11
- const PKG_ROOT = resolve(CLI_ROOT, '..');
8
+ // DDR-166 T0b — real-disk resolution, safe inside a `bun build --compile`
9
+ // standalone binary (see pkg-root.mjs's own doc comment for why the previous
10
+ // `dirname(fileURLToPath(import.meta.url))` walk-up broke there).
11
+ const PKG_ROOT = resolvePkgRoot();
12
12
 
13
13
  const COMMANDS = {
14
14
  init: () => import('../commands/init.mjs'),
@@ -47,6 +47,51 @@ const BIN_VERBS = new Set([
47
47
  'chat-open',
48
48
  'ensure-browser',
49
49
  'fetch-asset',
50
+ // DDR-167 (Phase 3 / T10). `import-asset` is the LOCAL-file sibling of the
51
+ // URL-only `fetch-asset`: hardened SVG ingestion (allowlist DOM-sanitize +
52
+ // a real-browser execution canary). PDF import is wired but not yet
53
+ // available — see the DDR's addendum on why the planned rasterization
54
+ // mechanism doesn't work under browser automation.
55
+ 'import-asset',
56
+ // DDR-172 (Phase 3 / T11). `import-tokens` is the token-file sibling of
57
+ // `import-asset`: parses W3C design-tokens / Style-Dictionary JSON or raw
58
+ // CSS custom properties, maps recognized tokens onto the DS CSS-variable
59
+ // contract, and patches (theme-block-scoped) or scaffolds the target
60
+ // design system. CLI-only — no HTTP route, no in-app panel.
61
+ 'import-tokens',
62
+ // DDR-173 (Phase 3 / T12). `import-brand` extracts typed palette/font
63
+ // cues + a hardened logo asset from an already-DDR-167-sanitized SVG for
64
+ // `/design:setup-ds --from-brand` — never re-reads or re-sanitizes the
65
+ // original brand file (Decision 2: no parallel, ungated read path).
66
+ 'import-brand',
67
+ // feature-photo-editor (Stage G). `photo-adjust` is the thin non-browser
68
+ // parametric verb (curl → /_api/photo-edit). `photo-bg-remove` is the
69
+ // client-side @imgly ML harness (throwaway proof canvas + agent-browser
70
+ // attribute poll — see photo-bg-remove.sh's header for why).
71
+ 'photo-adjust',
72
+ 'photo-bg-remove',
73
+ // feature-footage-analysis-director. `ingest-footage` copies a folder of raw
74
+ // clips into assets/ (content-addressed, magic-byte sniffed). `probe-footage`
75
+ // decodes ONE clip in headless Chromium → keyframe PNGs for the footage-analyst's
76
+ // vision pass. Neither boots the dev server (probe is server-independent — it
77
+ // reads the clip as a file:// resource).
78
+ 'ingest-footage',
79
+ 'probe-footage',
80
+ // feature-ai-media-generation (Phase 0, DDR-16x). `generate` is the thin
81
+ // non-browser BYOK verb (curl → /_api/generate-jobs → poll → print the
82
+ // produced /assets/<sha8>.<ext>). The provider call happens server-side (the
83
+ // sidecar resolves the key from the keychain / ~/.config/maude/keys.json).
84
+ 'generate',
85
+ // feature-ai-media-generation (Phase 2, DDR-164). `transcribe` is the local,
86
+ // no-key subtitle verb: spawn whisper.cpp on an audio/video file → SRT/VTT via
87
+ // the shared captions.ts reflow. No dev server; whisper.cpp is a SOFT dep with
88
+ // ElevenLabs Scribe / Groq Whisper as the cloud fallback.
89
+ 'transcribe',
90
+ // feature-ai-media-generation (Task 2.5, DDR-164). `audio-search` is the
91
+ // reuse-before-you-pay verb: search the project's own generated audio + the
92
+ // user's ElevenLabs history before spending credits on a new music/SFX/VO
93
+ // track. Runs server-side (key resolved by the sidecar); needs a dev server.
94
+ 'audio-search',
50
95
  ]);
51
96
 
52
97
  // Bin verbs that boot the dev-server (directly, or by shelling into server-up.sh).
@@ -140,7 +185,8 @@ Dev-tooling (dispatch to the dev-server bash helpers — DDR-062):
140
185
  screenshot · server-up · prep · slug · bootstrap-check · runtime-health
141
186
  smoke · canvas-edit · handoff · asset-sweep · visual-sanity · fetch-asset
142
187
  draw-build · draw-proof · svg-optimize · to-lottie · read-annotations · annotate
143
- canvas-rects
188
+ canvas-rects · ingest-footage · probe-footage
189
+ generate · transcribe · audio-search
144
190
  Invoke the bundled helper of the same name. maude resolves it from its
145
191
  own package root and sets CLAUDE_PLUGIN_ROOT for the child; stdout,
146
192
  stderr, and exit code pass straight through (so command-substitution
@@ -453,7 +499,22 @@ async function runExport({ args }) {
453
499
  }
454
500
 
455
501
  const format = positional[0];
456
- const VALID_FORMATS = new Set(['png', 'pdf', 'svg', 'html', 'pptx', 'canva', 'zip']);
502
+ // Includes the temporal formats (DDR-148): mp4/webm/gif render through the
503
+ // capture spine, same POST /_api/export the UI ⌘E dialog uses. Video export
504
+ // is always artboard-scoped (the comp's registered meta drives fps/duration).
505
+ const VALID_FORMATS = new Set([
506
+ 'png',
507
+ 'pdf',
508
+ 'svg',
509
+ 'html',
510
+ 'pptx',
511
+ 'canva',
512
+ 'zip',
513
+ 'mp4',
514
+ 'webm',
515
+ 'gif',
516
+ ]);
517
+ const VIDEO_FORMATS = new Set(['mp4', 'webm', 'gif']);
457
518
  if (!format || !VALID_FORMATS.has(format)) {
458
519
  process.stderr.write(
459
520
  `maude design export: missing or unknown <format>. Try one of: ${Array.from(VALID_FORMATS).join(', ')}\n`
@@ -461,7 +522,10 @@ async function runExport({ args }) {
461
522
  process.exit(2);
462
523
  }
463
524
 
464
- const scope = flags.scope ?? 'canvas-as-separate';
525
+ // Video formats default to artboard scope (the only scope video supports —
526
+ // scope.ts resolves the artboard under the active canvas); other formats keep
527
+ // the canvas-as-separate default.
528
+ const scope = flags.scope ?? (VIDEO_FORMATS.has(format) ? 'artboard' : 'canvas-as-separate');
465
529
  const VALID_SCOPES = new Set(['selection', 'artboard', 'canvas-as-separate', 'project-raw']);
466
530
  if (!VALID_SCOPES.has(scope)) {
467
531
  process.stderr.write(`maude design export: unknown --scope "${scope}"\n`);
@@ -38,12 +38,14 @@ export function buildBlock(designRel = '.design') {
38
38
  `${root}/_preflight.json`,
39
39
  `${root}/_locator.json`, // regenerable slug→path index
40
40
  `${root}/_export-history.json`,
41
+ `${root}/_generate-history.json`, // AI-media generation job ledger (regenerable — feature-ai-media-generation, DDR-16x)
41
42
  // Per-machine / per-user dirs.
42
43
  `${root}/_export-jobs/`, // background-export job byte store (regenerable — feature-background-export-notification-center)
43
44
  `${root}/_state/`, // binary CRDT logs (regenerable from hub)
44
45
  `${root}/_history/`,
45
46
  `${root}/_trash/`, // soft-deleted canvases (recoverable locally — Phase 22 delete)
46
47
  `${root}/_draw/`, // draw-agent proof canvases (regenerable — Phase 25)
48
+ `${root}/_photo/`, // photo-bg-remove headless proof canvases (regenerable — feature-photo-editor)
47
49
  `${root}/_smoke/`, // batch-screenshot output (regenerable — DDR-021)
48
50
  `${root}/_canvas-state/`, // per-machine canvas scratch + camera (`*.view.json`, DDR-115)
49
51
  `${root}/_chat/`, // ACP transcripts (per-machine)
@@ -0,0 +1,107 @@
1
+ // pkg-root.mjs — real-disk `maude` package-root resolution, safe inside a
2
+ // `bun build --compile` standalone binary (DDR-166 T0b).
3
+ //
4
+ // `cli/bin/maude.mjs` originally computed PKG_ROOT via
5
+ // `dirname(fileURLToPath(import.meta.url))` walk-up — correct for the plain
6
+ // npm/node install, but the exact DDR-045 trap: inside a compiled binary,
7
+ // `import.meta.url` resolves to Bun's virtual filesystem (`/$bunfs/root` on
8
+ // POSIX, `B:/~BUN/root` on Windows), not the real on-disk location. Two
9
+ // dev-server releases (v0.18.0/v0.18.1) shipped broken because of this exact
10
+ // bug class before DDR-045 fixed it there. This module mirrors that fix
11
+ // (`apps/studio/paths.ts` `resolveDevServerRoot`) for the CLI, independently
12
+ // — not by importing paths.ts directly, to keep the CLI's compiled binary
13
+ // decoupled from the dev-server's module graph.
14
+
15
+ import { existsSync, realpathSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ function isVirtualBunfsPath(p) {
20
+ return p !== null && (p.startsWith('/$bunfs') || p.startsWith('B:/~BUN'));
21
+ }
22
+
23
+ /** True inside a `bun build --compile` standalone binary. Mirrors `apps/studio/paths.ts`'s `IS_COMPILED_BINARY`. */
24
+ export function isCompiledBinary() {
25
+ return isVirtualBunfsPath(getImportMetaDir());
26
+ }
27
+
28
+ function getImportMetaDir() {
29
+ try {
30
+ return dirname(fileURLToPath(import.meta.url));
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Anchor: `apps/studio/bin/screenshot.sh` is present in every real maude
38
+ * package root (dev tree, npm tarball, and the Tauri desktop `Resources/`
39
+ * bundle all ship it) and nowhere else — unique enough to identify the
40
+ * package root without also requiring `package.json` (npm tarballs can drop
41
+ * nested workspace `package.json` files, the exact false-negative DDR-045's
42
+ * own retro already documents for the dev-server's analogous check).
43
+ */
44
+ export function isPkgRoot(dir) {
45
+ // Both anchors, not just one: `apps/studio/bin/screenshot.sh` ALONE is a
46
+ // false-positive inside the Tauri desktop build — `stage-resources.mjs`
47
+ // stages a full working copy of `apps/studio` (dev-server + bin scripts)
48
+ // into `target/debug/apps/studio/` for the sidecar's own runtime
49
+ // resolution, but that staged copy has no `cli/` — walking up from a
50
+ // compiled `maude` binary placed at `target/debug/maude` matched THAT
51
+ // copy first and stopped one level too early (caught by live testing
52
+ // against the real Tauri build, not a synthetic path). `cli/commands/
53
+ // design.mjs` only exists in a genuine package root.
54
+ return (
55
+ existsSync(join(dir, 'apps', 'studio', 'bin', 'screenshot.sh')) &&
56
+ existsSync(join(dir, 'cli', 'commands', 'design.mjs'))
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Resolve maude's own package root — real disk path, safe inside a compiled
62
+ * binary. Priority: (0) explicit override, (1) dev-mode `import.meta.url`
63
+ * (plain `node`/`bun run cli/bin/maude.mjs`), (2) compiled-binary walk-up
64
+ * from `process.execPath` (matches the npm-install layout: binary at
65
+ * `@1agh/maude-<platform>/maude` walks up to `@1agh/maude/`, which ships
66
+ * `apps/studio/` via `package.json` `files`; matches the Tauri desktop layout
67
+ * too, where the bundled `maude` binary and the `Resources/apps/studio`
68
+ * bundle share a common ancestor within the walk-up depth).
69
+ */
70
+ export function resolvePkgRoot() {
71
+ const override = process.env.MAUDE_PKG_ROOT;
72
+ if (override && !isVirtualBunfsPath(override) && isPkgRoot(override)) return override;
73
+
74
+ const importDir = getImportMetaDir();
75
+ if (importDir && !isVirtualBunfsPath(importDir)) {
76
+ // Dev-mode: cli/bin/maude.mjs is 2 levels under the package root.
77
+ const candidate = join(importDir, '..', '..');
78
+ if (isPkgRoot(candidate)) return candidate;
79
+ }
80
+
81
+ // `process.execPath` is the path AS INVOKED — when launched through a
82
+ // symlink (DDR-166 T0b stages exactly this: sidecar.rs's narrow bin-link
83
+ // dir), it's the symlink's own path, not the real target, and walking up
84
+ // from there (e.g. ~/Library/Caches/…/bin-link/) never reaches the repo
85
+ // root. Dereference first. Falls back to the raw path if the file somehow
86
+ // doesn't exist (shouldn't happen — we ARE that running process).
87
+ let cur;
88
+ try {
89
+ cur = dirname(realpathSync(process.execPath));
90
+ } catch {
91
+ cur = dirname(process.execPath);
92
+ }
93
+ for (let i = 0; i < 10; i++) {
94
+ if (isPkgRoot(cur)) return cur;
95
+ const parent = dirname(cur);
96
+ if (parent === cur) break;
97
+ cur = parent;
98
+ }
99
+
100
+ // Final fallback for unanchored test contexts — callers should expect
101
+ // existsSync-based lookups to fail and surface a clear error, same
102
+ // contract as paths.ts's own resolver. Never join a VIRTUAL importDir here
103
+ // (`/$bunfs/root/../..` collapses to `/` via path.join — a real bug an
104
+ // earlier draft of this function had, caught by testing against an
105
+ // unanchored compile output before wiring this into Tauri).
106
+ return dirname(process.execPath);
107
+ }