mergestory 0.0.0-stage → 1.0.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 (474) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +48 -2
  4. package/assets/kokoro-plugin/package-lock.json +1139 -0
  5. package/dist/analyzer/analyze.d.ts +8 -0
  6. package/dist/analyzer/analyze.js +328 -0
  7. package/dist/analyzer/components.d.ts +18 -0
  8. package/dist/analyzer/components.js +266 -0
  9. package/dist/analyzer/dependencies.d.ts +13 -0
  10. package/dist/analyzer/dependencies.js +349 -0
  11. package/dist/analyzer/evidence.d.ts +15 -0
  12. package/dist/analyzer/evidence.js +73 -0
  13. package/dist/analyzer/hunks.d.ts +21 -0
  14. package/dist/analyzer/hunks.js +88 -0
  15. package/dist/analyzer/imports.d.ts +46 -0
  16. package/dist/analyzer/imports.js +87 -0
  17. package/dist/analyzer/index.d.ts +14 -0
  18. package/dist/analyzer/index.js +16 -0
  19. package/dist/analyzer/languages/go.d.ts +2 -0
  20. package/dist/analyzer/languages/go.js +76 -0
  21. package/dist/analyzer/languages/index.d.ts +18 -0
  22. package/dist/analyzer/languages/index.js +72 -0
  23. package/dist/analyzer/languages/javascript.d.ts +2 -0
  24. package/dist/analyzer/languages/javascript.js +336 -0
  25. package/dist/analyzer/languages/jvm.d.ts +2 -0
  26. package/dist/analyzer/languages/jvm.js +157 -0
  27. package/dist/analyzer/languages/others.d.ts +2 -0
  28. package/dist/analyzer/languages/others.js +40 -0
  29. package/dist/analyzer/languages/php.d.ts +2 -0
  30. package/dist/analyzer/languages/php.js +81 -0
  31. package/dist/analyzer/languages/python.d.ts +2 -0
  32. package/dist/analyzer/languages/python.js +180 -0
  33. package/dist/analyzer/languages/ruby.d.ts +2 -0
  34. package/dist/analyzer/languages/ruby.js +99 -0
  35. package/dist/analyzer/languages/rust.d.ts +2 -0
  36. package/dist/analyzer/languages/rust.js +164 -0
  37. package/dist/analyzer/languages/shared.d.ts +35 -0
  38. package/dist/analyzer/languages/shared.js +43 -0
  39. package/dist/analyzer/languages/types.d.ts +78 -0
  40. package/dist/analyzer/languages/types.js +2 -0
  41. package/dist/analyzer/pack-format.d.ts +27 -0
  42. package/dist/analyzer/pack-format.js +139 -0
  43. package/dist/analyzer/pack.d.ts +26 -0
  44. package/dist/analyzer/pack.js +498 -0
  45. package/dist/analyzer/relationships.d.ts +17 -0
  46. package/dist/analyzer/relationships.js +34 -0
  47. package/dist/analyzer/risk-rules.d.ts +287 -0
  48. package/dist/analyzer/risk-rules.js +373 -0
  49. package/dist/analyzer/risks.d.ts +36 -0
  50. package/dist/analyzer/risks.js +480 -0
  51. package/dist/analyzer/roles.d.ts +11 -0
  52. package/dist/analyzer/roles.js +280 -0
  53. package/dist/analyzer/scan.d.ts +26 -0
  54. package/dist/analyzer/scan.js +157 -0
  55. package/dist/analyzer/signals.d.ts +20 -0
  56. package/dist/analyzer/signals.js +51 -0
  57. package/dist/analyzer/symbol-changes.d.ts +11 -0
  58. package/dist/analyzer/symbol-changes.js +101 -0
  59. package/dist/analyzer/symbol-types.d.ts +17 -0
  60. package/dist/analyzer/symbol-types.js +9 -0
  61. package/dist/analyzer/symbols.d.ts +4 -0
  62. package/dist/analyzer/symbols.js +16 -0
  63. package/dist/analyzer/trivial.d.ts +13 -0
  64. package/dist/analyzer/trivial.js +137 -0
  65. package/dist/analyzer/util.d.ts +6 -0
  66. package/dist/analyzer/util.js +24 -0
  67. package/dist/cli/commands/authoring.d.ts +9 -0
  68. package/dist/cli/commands/authoring.js +98 -0
  69. package/dist/cli/commands/cache.d.ts +4 -0
  70. package/dist/cli/commands/cache.js +37 -0
  71. package/dist/cli/commands/doctor.d.ts +5 -0
  72. package/dist/cli/commands/doctor.js +37 -0
  73. package/dist/cli/commands/generate.d.ts +8 -0
  74. package/dist/cli/commands/generate.js +69 -0
  75. package/dist/cli/commands/jobs.d.ts +8 -0
  76. package/dist/cli/commands/jobs.js +70 -0
  77. package/dist/cli/commands/publish.d.ts +8 -0
  78. package/dist/cli/commands/publish.js +120 -0
  79. package/dist/cli/commands/serve.d.ts +4 -0
  80. package/dist/cli/commands/serve.js +69 -0
  81. package/dist/cli/commands/setup.d.ts +14 -0
  82. package/dist/cli/commands/setup.js +108 -0
  83. package/dist/cli/commands/shared.d.ts +18 -0
  84. package/dist/cli/commands/shared.js +24 -0
  85. package/dist/cli/files.d.ts +26 -0
  86. package/dist/cli/files.js +98 -0
  87. package/dist/cli/format.d.ts +55 -0
  88. package/dist/cli/format.js +295 -0
  89. package/dist/cli/inputs.d.ts +23 -0
  90. package/dist/cli/inputs.js +96 -0
  91. package/dist/cli/io.d.ts +19 -0
  92. package/dist/cli/io.js +31 -0
  93. package/dist/cli/main.d.ts +2 -0
  94. package/dist/cli/main.js +15 -0
  95. package/dist/cli/options.d.ts +105 -0
  96. package/dist/cli/options.js +324 -0
  97. package/dist/cli/program.d.ts +12 -0
  98. package/dist/cli/program.js +146 -0
  99. package/dist/cli/runtime.d.ts +59 -0
  100. package/dist/cli/runtime.js +83 -0
  101. package/dist/cli/style.d.ts +13 -0
  102. package/dist/cli/style.js +27 -0
  103. package/dist/core/binaries.d.ts +7 -0
  104. package/dist/core/binaries.js +33 -0
  105. package/dist/core/binary-paths.d.ts +5 -0
  106. package/dist/core/binary-paths.js +92 -0
  107. package/dist/core/cache.d.ts +14 -0
  108. package/dist/core/cache.js +43 -0
  109. package/dist/core/child-env.d.ts +9 -0
  110. package/dist/core/child-env.js +61 -0
  111. package/dist/core/concurrency.d.ts +13 -0
  112. package/dist/core/concurrency.js +58 -0
  113. package/dist/core/config.d.ts +223 -0
  114. package/dist/core/config.js +454 -0
  115. package/dist/core/context.d.ts +39 -0
  116. package/dist/core/context.js +44 -0
  117. package/dist/core/cost.d.ts +46 -0
  118. package/dist/core/cost.js +108 -0
  119. package/dist/core/diagram-metrics.d.ts +57 -0
  120. package/dist/core/diagram-metrics.js +63 -0
  121. package/dist/core/diff-view.d.ts +44 -0
  122. package/dist/core/diff-view.js +136 -0
  123. package/dist/core/errors.d.ts +47 -0
  124. package/dist/core/errors.js +94 -0
  125. package/dist/core/ids.d.ts +11 -0
  126. package/dist/core/ids.js +36 -0
  127. package/dist/core/json.d.ts +8 -0
  128. package/dist/core/json.js +17 -0
  129. package/dist/core/languages.d.ts +19 -0
  130. package/dist/core/languages.js +292 -0
  131. package/dist/core/logger.d.ts +29 -0
  132. package/dist/core/logger.js +68 -0
  133. package/dist/core/net.d.ts +16 -0
  134. package/dist/core/net.js +80 -0
  135. package/dist/core/path-policy.d.ts +20 -0
  136. package/dist/core/path-policy.js +202 -0
  137. package/dist/core/paths.d.ts +18 -0
  138. package/dist/core/paths.js +53 -0
  139. package/dist/core/posix.d.ts +12 -0
  140. package/dist/core/posix.js +50 -0
  141. package/dist/core/process.d.ts +22 -0
  142. package/dist/core/process.js +117 -0
  143. package/dist/core/providers.d.ts +204 -0
  144. package/dist/core/providers.js +2 -0
  145. package/dist/core/redact.d.ts +11 -0
  146. package/dist/core/redact.js +70 -0
  147. package/dist/core/render-plan.d.ts +307 -0
  148. package/dist/core/render-plan.js +7 -0
  149. package/dist/core/retry.d.ts +54 -0
  150. package/dist/core/retry.js +116 -0
  151. package/dist/core/run-folder.d.ts +74 -0
  152. package/dist/core/run-folder.js +129 -0
  153. package/dist/core/secrets.d.ts +56 -0
  154. package/dist/core/secrets.js +301 -0
  155. package/dist/core/storyboard-facts.d.ts +22 -0
  156. package/dist/core/storyboard-facts.js +61 -0
  157. package/dist/core/storyboard-schema.d.ts +1279 -0
  158. package/dist/core/storyboard-schema.js +342 -0
  159. package/dist/core/text.d.ts +43 -0
  160. package/dist/core/text.js +94 -0
  161. package/dist/core/types.d.ts +459 -0
  162. package/dist/core/types.js +17 -0
  163. package/dist/doctor/check.d.ts +6 -0
  164. package/dist/doctor/check.js +18 -0
  165. package/dist/doctor/deps.d.ts +2 -0
  166. package/dist/doctor/deps.js +90 -0
  167. package/dist/doctor/format.d.ts +9 -0
  168. package/dist/doctor/format.js +19 -0
  169. package/dist/doctor/github.d.ts +2 -0
  170. package/dist/doctor/github.js +13 -0
  171. package/dist/doctor/home.d.ts +2 -0
  172. package/dist/doctor/home.js +26 -0
  173. package/dist/doctor/index.d.ts +18 -0
  174. package/dist/doctor/index.js +81 -0
  175. package/dist/doctor/llm.d.ts +2 -0
  176. package/dist/doctor/llm.js +32 -0
  177. package/dist/doctor/publish.d.ts +2 -0
  178. package/dist/doctor/publish.js +16 -0
  179. package/dist/doctor/s3.d.ts +2 -0
  180. package/dist/doctor/s3.js +125 -0
  181. package/dist/doctor/speech.d.ts +2 -0
  182. package/dist/doctor/speech.js +112 -0
  183. package/dist/doctor/tools.d.ts +5 -0
  184. package/dist/doctor/tools.js +58 -0
  185. package/dist/doctor/types.d.ts +92 -0
  186. package/dist/doctor/types.js +2 -0
  187. package/dist/doctor/youtube.d.ts +2 -0
  188. package/dist/doctor/youtube.js +73 -0
  189. package/dist/github/client.d.ts +108 -0
  190. package/dist/github/client.js +499 -0
  191. package/dist/github/errors.d.ts +37 -0
  192. package/dist/github/errors.js +155 -0
  193. package/dist/github/index.d.ts +13 -0
  194. package/dist/github/index.js +9 -0
  195. package/dist/github/linked-issues.d.ts +37 -0
  196. package/dist/github/linked-issues.js +131 -0
  197. package/dist/github/model.d.ts +81 -0
  198. package/dist/github/model.js +114 -0
  199. package/dist/github/notify.d.ts +27 -0
  200. package/dist/github/notify.js +118 -0
  201. package/dist/github/pr-url.d.ts +27 -0
  202. package/dist/github/pr-url.js +122 -0
  203. package/dist/github/token.d.ts +16 -0
  204. package/dist/github/token.js +69 -0
  205. package/dist/index.d.ts +29 -0
  206. package/dist/index.js +19 -0
  207. package/dist/jobs/input-rules.d.ts +20 -0
  208. package/dist/jobs/input-rules.js +100 -0
  209. package/dist/jobs/model.d.ts +47 -0
  210. package/dist/jobs/model.js +18 -0
  211. package/dist/jobs/runner.d.ts +43 -0
  212. package/dist/jobs/runner.js +165 -0
  213. package/dist/jobs/store.d.ts +67 -0
  214. package/dist/jobs/store.js +314 -0
  215. package/dist/jobs/view.d.ts +48 -0
  216. package/dist/jobs/view.js +111 -0
  217. package/dist/jobs/worker.d.ts +24 -0
  218. package/dist/jobs/worker.js +47 -0
  219. package/dist/llm/anthropic.d.ts +11 -0
  220. package/dist/llm/anthropic.js +225 -0
  221. package/dist/llm/index.d.ts +10 -0
  222. package/dist/llm/index.js +27 -0
  223. package/dist/llm/openai-compatible.d.ts +33 -0
  224. package/dist/llm/openai-compatible.js +378 -0
  225. package/dist/llm/prices.d.ts +26 -0
  226. package/dist/llm/prices.js +41 -0
  227. package/dist/llm/shared.d.ts +30 -0
  228. package/dist/llm/shared.js +115 -0
  229. package/dist/narration/assemble.d.ts +36 -0
  230. package/dist/narration/assemble.js +282 -0
  231. package/dist/narration/audio.d.ts +61 -0
  232. package/dist/narration/audio.js +301 -0
  233. package/dist/narration/captions.d.ts +37 -0
  234. package/dist/narration/captions.js +195 -0
  235. package/dist/narration/chunking.d.ts +6 -0
  236. package/dist/narration/chunking.js +46 -0
  237. package/dist/narration/convert.d.ts +9 -0
  238. package/dist/narration/convert.js +90 -0
  239. package/dist/narration/elevenlabs.d.ts +5 -0
  240. package/dist/narration/elevenlabs.js +53 -0
  241. package/dist/narration/http-errors.d.ts +14 -0
  242. package/dist/narration/http-errors.js +74 -0
  243. package/dist/narration/index.d.ts +21 -0
  244. package/dist/narration/index.js +22 -0
  245. package/dist/narration/kokoro-install.d.ts +30 -0
  246. package/dist/narration/kokoro-install.js +284 -0
  247. package/dist/narration/kokoro-model.d.ts +54 -0
  248. package/dist/narration/kokoro-model.js +168 -0
  249. package/dist/narration/kokoro-plugin.d.ts +78 -0
  250. package/dist/narration/kokoro-plugin.js +245 -0
  251. package/dist/narration/kokoro.d.ts +22 -0
  252. package/dist/narration/kokoro.js +231 -0
  253. package/dist/narration/openai.d.ts +4 -0
  254. package/dist/narration/openai.js +54 -0
  255. package/dist/narration/pricing.d.ts +8 -0
  256. package/dist/narration/pricing.js +17 -0
  257. package/dist/narration/remote-speech.d.ts +53 -0
  258. package/dist/narration/remote-speech.js +100 -0
  259. package/dist/narration/say.d.ts +28 -0
  260. package/dist/narration/say.js +178 -0
  261. package/dist/narration/select.d.ts +27 -0
  262. package/dist/narration/select.js +80 -0
  263. package/dist/narration/silent.d.ts +2 -0
  264. package/dist/narration/silent.js +18 -0
  265. package/dist/narration/speakable.d.ts +20 -0
  266. package/dist/narration/speakable.js +398 -0
  267. package/dist/narration/synthesize.d.ts +25 -0
  268. package/dist/narration/synthesize.js +220 -0
  269. package/dist/narration/wav.d.ts +37 -0
  270. package/dist/narration/wav.js +170 -0
  271. package/dist/pipeline/authoring.d.ts +8 -0
  272. package/dist/pipeline/authoring.js +117 -0
  273. package/dist/pipeline/commands.d.ts +9 -0
  274. package/dist/pipeline/commands.js +21 -0
  275. package/dist/pipeline/fit-duration.d.ts +41 -0
  276. package/dist/pipeline/fit-duration.js +150 -0
  277. package/dist/pipeline/index.d.ts +9 -0
  278. package/dist/pipeline/index.js +9 -0
  279. package/dist/pipeline/inputs.d.ts +42 -0
  280. package/dist/pipeline/inputs.js +171 -0
  281. package/dist/pipeline/plan.d.ts +8 -0
  282. package/dist/pipeline/plan.js +85 -0
  283. package/dist/pipeline/planned-egress.d.ts +21 -0
  284. package/dist/pipeline/planned-egress.js +43 -0
  285. package/dist/pipeline/prepare.d.ts +26 -0
  286. package/dist/pipeline/prepare.js +84 -0
  287. package/dist/pipeline/presentation.d.ts +26 -0
  288. package/dist/pipeline/presentation.js +110 -0
  289. package/dist/pipeline/publish-run.d.ts +71 -0
  290. package/dist/pipeline/publish-run.js +295 -0
  291. package/dist/pipeline/run.d.ts +7 -0
  292. package/dist/pipeline/run.js +196 -0
  293. package/dist/pipeline/storyboard-choice.d.ts +34 -0
  294. package/dist/pipeline/storyboard-choice.js +129 -0
  295. package/dist/pipeline/types.d.ts +76 -0
  296. package/dist/pipeline/types.js +2 -0
  297. package/dist/publish/access-probe.d.ts +55 -0
  298. package/dist/publish/access-probe.js +249 -0
  299. package/dist/publish/bundle.d.ts +20 -0
  300. package/dist/publish/bundle.js +57 -0
  301. package/dist/publish/comment.d.ts +10 -0
  302. package/dist/publish/comment.js +184 -0
  303. package/dist/publish/content-types.d.ts +15 -0
  304. package/dist/publish/content-types.js +41 -0
  305. package/dist/publish/index.d.ts +18 -0
  306. package/dist/publish/index.js +30 -0
  307. package/dist/publish/keys.d.ts +10 -0
  308. package/dist/publish/keys.js +30 -0
  309. package/dist/publish/local.d.ts +33 -0
  310. package/dist/publish/local.js +143 -0
  311. package/dist/publish/privacy.d.ts +20 -0
  312. package/dist/publish/privacy.js +21 -0
  313. package/dist/publish/s3/bucket.d.ts +22 -0
  314. package/dist/publish/s3/bucket.js +82 -0
  315. package/dist/publish/s3/client.d.ts +33 -0
  316. package/dist/publish/s3/client.js +134 -0
  317. package/dist/publish/s3/failures.d.ts +22 -0
  318. package/dist/publish/s3/failures.js +110 -0
  319. package/dist/publish/s3/index.d.ts +5 -0
  320. package/dist/publish/s3/index.js +6 -0
  321. package/dist/publish/s3/links.d.ts +38 -0
  322. package/dist/publish/s3/links.js +60 -0
  323. package/dist/publish/s3/protected-links.d.ts +29 -0
  324. package/dist/publish/s3/protected-links.js +67 -0
  325. package/dist/publish/s3/publisher.d.ts +32 -0
  326. package/dist/publish/s3/publisher.js +123 -0
  327. package/dist/publish/s3/settings.d.ts +29 -0
  328. package/dist/publish/s3/settings.js +64 -0
  329. package/dist/publish/s3/upload.d.ts +29 -0
  330. package/dist/publish/s3/upload.js +145 -0
  331. package/dist/publish/severity.d.ts +9 -0
  332. package/dist/publish/severity.js +18 -0
  333. package/dist/publish/text.d.ts +23 -0
  334. package/dist/publish/text.js +94 -0
  335. package/dist/publish/types.d.ts +67 -0
  336. package/dist/publish/types.js +12 -0
  337. package/dist/publish/vtt.d.ts +9 -0
  338. package/dist/publish/vtt.js +43 -0
  339. package/dist/publish/watch-page.d.ts +4 -0
  340. package/dist/publish/watch-page.js +463 -0
  341. package/dist/publish/youtube/credentials.d.ts +33 -0
  342. package/dist/publish/youtube/credentials.js +162 -0
  343. package/dist/publish/youtube/description.d.ts +56 -0
  344. package/dist/publish/youtube/description.js +197 -0
  345. package/dist/publish/youtube/google-api.d.ts +95 -0
  346. package/dist/publish/youtube/google-api.js +339 -0
  347. package/dist/publish/youtube/index.d.ts +7 -0
  348. package/dist/publish/youtube/index.js +8 -0
  349. package/dist/publish/youtube/loopback.d.ts +13 -0
  350. package/dist/publish/youtube/loopback.js +144 -0
  351. package/dist/publish/youtube/oauth.d.ts +68 -0
  352. package/dist/publish/youtube/oauth.js +159 -0
  353. package/dist/publish/youtube/prompt.d.ts +27 -0
  354. package/dist/publish/youtube/prompt.js +94 -0
  355. package/dist/publish/youtube/publisher.d.ts +38 -0
  356. package/dist/publish/youtube/publisher.js +346 -0
  357. package/dist/publish/youtube/scopes.d.ts +32 -0
  358. package/dist/publish/youtube/scopes.js +55 -0
  359. package/dist/publish/youtube/sign-in.d.ts +37 -0
  360. package/dist/publish/youtube/sign-in.js +137 -0
  361. package/dist/publish/youtube/upload.d.ts +43 -0
  362. package/dist/publish/youtube/upload.js +210 -0
  363. package/dist/render/browser.d.ts +23 -0
  364. package/dist/render/browser.js +219 -0
  365. package/dist/render/chromium-renderer.d.ts +56 -0
  366. package/dist/render/chromium-renderer.js +519 -0
  367. package/dist/render/ffmpeg.d.ts +55 -0
  368. package/dist/render/ffmpeg.js +212 -0
  369. package/dist/render/fonts.d.ts +10 -0
  370. package/dist/render/fonts.js +42 -0
  371. package/dist/render/index.d.ts +9 -0
  372. package/dist/render/index.js +11 -0
  373. package/dist/render/page.d.ts +46 -0
  374. package/dist/render/page.js +123 -0
  375. package/dist/render/playwright.d.ts +3 -0
  376. package/dist/render/playwright.js +6 -0
  377. package/dist/render/runtime-bundle.d.ts +2 -0
  378. package/dist/render/runtime-bundle.js +72 -0
  379. package/dist/runtime/runtime.js +1626 -0
  380. package/dist/server/auth.d.ts +17 -0
  381. package/dist/server/auth.js +58 -0
  382. package/dist/server/http.d.ts +43 -0
  383. package/dist/server/http.js +119 -0
  384. package/dist/server/index.d.ts +46 -0
  385. package/dist/server/index.js +310 -0
  386. package/dist/server/queue.d.ts +30 -0
  387. package/dist/server/queue.js +76 -0
  388. package/dist/server/schema.d.ts +103 -0
  389. package/dist/server/schema.js +121 -0
  390. package/dist/server/source.d.ts +17 -0
  391. package/dist/server/source.js +100 -0
  392. package/dist/sources/assemble.d.ts +86 -0
  393. package/dist/sources/assemble.js +117 -0
  394. package/dist/sources/diff-parser.d.ts +14 -0
  395. package/dist/sources/diff-parser.js +84 -0
  396. package/dist/sources/git.d.ts +28 -0
  397. package/dist/sources/git.js +108 -0
  398. package/dist/sources/github.d.ts +20 -0
  399. package/dist/sources/github.js +192 -0
  400. package/dist/sources/hunk-selection.d.ts +25 -0
  401. package/dist/sources/hunk-selection.js +46 -0
  402. package/dist/sources/index.d.ts +23 -0
  403. package/dist/sources/index.js +62 -0
  404. package/dist/sources/limits.d.ts +27 -0
  405. package/dist/sources/limits.js +22 -0
  406. package/dist/sources/local.d.ts +34 -0
  407. package/dist/sources/local.js +555 -0
  408. package/dist/sources/reader.d.ts +13 -0
  409. package/dist/sources/reader.js +45 -0
  410. package/dist/sources/remote.d.ts +13 -0
  411. package/dist/sources/remote.js +41 -0
  412. package/dist/sources/repo-context.d.ts +34 -0
  413. package/dist/sources/repo-context.js +144 -0
  414. package/dist/sources/text.d.ts +25 -0
  415. package/dist/sources/text.js +71 -0
  416. package/dist/storyboard/authoring.d.ts +108 -0
  417. package/dist/storyboard/authoring.js +196 -0
  418. package/dist/storyboard/claims.d.ts +35 -0
  419. package/dist/storyboard/claims.js +318 -0
  420. package/dist/storyboard/coderef.d.ts +28 -0
  421. package/dist/storyboard/coderef.js +118 -0
  422. package/dist/storyboard/generate.d.ts +25 -0
  423. package/dist/storyboard/generate.js +194 -0
  424. package/dist/storyboard/heuristic-code.d.ts +91 -0
  425. package/dist/storyboard/heuristic-code.js +524 -0
  426. package/dist/storyboard/heuristic-context.d.ts +42 -0
  427. package/dist/storyboard/heuristic-context.js +102 -0
  428. package/dist/storyboard/heuristic-facts.d.ts +106 -0
  429. package/dist/storyboard/heuristic-facts.js +415 -0
  430. package/dist/storyboard/heuristic-overview.d.ts +4 -0
  431. package/dist/storyboard/heuristic-overview.js +151 -0
  432. package/dist/storyboard/heuristic-review.d.ts +10 -0
  433. package/dist/storyboard/heuristic-review.js +158 -0
  434. package/dist/storyboard/heuristic-scenes.d.ts +8 -0
  435. package/dist/storyboard/heuristic-scenes.js +357 -0
  436. package/dist/storyboard/heuristic-shared.d.ts +43 -0
  437. package/dist/storyboard/heuristic-shared.js +136 -0
  438. package/dist/storyboard/heuristic.d.ts +15 -0
  439. package/dist/storyboard/heuristic.js +324 -0
  440. package/dist/storyboard/identifiers.d.ts +51 -0
  441. package/dist/storyboard/identifiers.js +195 -0
  442. package/dist/storyboard/index.d.ts +10 -0
  443. package/dist/storyboard/index.js +10 -0
  444. package/dist/storyboard/prompts.d.ts +16 -0
  445. package/dist/storyboard/prompts.js +61 -0
  446. package/dist/storyboard/run.d.ts +82 -0
  447. package/dist/storyboard/run.js +332 -0
  448. package/dist/storyboard/text.d.ts +16 -0
  449. package/dist/storyboard/text.js +103 -0
  450. package/dist/storyboard/types.d.ts +47 -0
  451. package/dist/storyboard/types.js +2 -0
  452. package/dist/storyboard/urls.d.ts +15 -0
  453. package/dist/storyboard/urls.js +83 -0
  454. package/dist/storyboard/validate.d.ts +8 -0
  455. package/dist/storyboard/validate.js +525 -0
  456. package/dist/storyboard/visuals.d.ts +23 -0
  457. package/dist/storyboard/visuals.js +324 -0
  458. package/dist/version.d.ts +2 -0
  459. package/dist/version.js +3 -0
  460. package/dist/visuals/code.d.ts +68 -0
  461. package/dist/visuals/code.js +385 -0
  462. package/dist/visuals/highlight.d.ts +17 -0
  463. package/dist/visuals/highlight.js +85 -0
  464. package/dist/visuals/image.d.ts +12 -0
  465. package/dist/visuals/image.js +96 -0
  466. package/dist/visuals/index.d.ts +10 -0
  467. package/dist/visuals/index.js +10 -0
  468. package/dist/visuals/layout.d.ts +89 -0
  469. package/dist/visuals/layout.js +506 -0
  470. package/dist/visuals/plan.d.ts +33 -0
  471. package/dist/visuals/plan.js +511 -0
  472. package/dist/visuals/timeline.d.ts +31 -0
  473. package/dist/visuals/timeline.js +96 -0
  474. package/package.json +74 -4
@@ -0,0 +1,89 @@
1
+ import { type DiagramFonts } from '../core/diagram-metrics.ts';
2
+ import type { ArchitectureResolved } from '../core/render-plan.ts';
3
+ import type { Visual } from '../core/storyboard-schema.ts';
4
+ export type ArchitectureVisual = Extract<Visual, {
5
+ type: 'architecture';
6
+ }>;
7
+ export type GlyphWeight = 500 | 600 | 700;
8
+ export interface TextFont {
9
+ /** Font size in px. */
10
+ px: number;
11
+ weight: GlyphWeight;
12
+ /** Letter spacing in em (the node label uses a slightly tight -0.015). */
13
+ spacingEm?: number;
14
+ }
15
+ /** Width in px of a single line of text. */
16
+ export declare function textWidth(text: string, font: TextFont): number;
17
+ /** Width of the widest piece of `text` that cannot be broken at a space, hyphen or slash. */
18
+ export declare function longestSegment(text: string, font: TextFont): number;
19
+ /**
20
+ * Number of lines `text` takes in a box `width` px wide: lines break at spaces and after hyphens and
21
+ * slashes, and a piece that is wider than the box breaks anywhere (the template sets
22
+ * overflow-wrap: anywhere). Empty text takes no lines.
23
+ */
24
+ export declare function countLines(text: string, width: number, font: TextFont): number;
25
+ /**
26
+ * The smallest box width, from `from` up to `to`, in which `text` takes at most `maxLines` lines. If
27
+ * there is none, the widest (the text is then cut, as a last resort).
28
+ */
29
+ export declare function widthForLines(text: string, font: TextFont, maxLines: number, from: number, to: number): number;
30
+ export declare const MIN_NODE_WIDTH = 260;
31
+ export declare const MIN_NODE_HEIGHT = 96;
32
+ export declare const MAX_NODE_WIDTH = 480;
33
+ export declare const MAX_LABEL_LINES = 2;
34
+ export declare const MAX_DETAIL_LINES = 2;
35
+ export interface NodeSize {
36
+ w: number;
37
+ h: number;
38
+ }
39
+ export interface NodeEstimate extends NodeSize {
40
+ labelLines: number;
41
+ detailLines: number;
42
+ }
43
+ export interface NodeSizeOptions {
44
+ /** Widest the card may become before text wraps; default MAX_NODE_WIDTH. */
45
+ maxWidth?: number;
46
+ fonts?: DiagramFonts;
47
+ }
48
+ /**
49
+ * Node size from its text: wide enough for the label and detail on one line each, up to `maxWidth`,
50
+ * then wrapped (label up to two lines, detail up to two lines) and tall enough for what wraps. A
51
+ * card is never narrower than its longest word, so names are not broken in the middle.
52
+ */
53
+ export declare function estimateNodeSize(label: string, detail: string | null, options?: NodeSizeOptions): NodeEstimate;
54
+ export declare const MAX_EDGE_LINES = 2;
55
+ export interface EdgeLabelBox {
56
+ /** Pill width in layout pixels. */
57
+ w: number;
58
+ /** Pill height in layout pixels. */
59
+ h: number;
60
+ lines: number;
61
+ }
62
+ /**
63
+ * Size of an edge label pill: one line if it fits `wrapAt`, else wrapped (at most two lines). The pill
64
+ * is never narrower than its longest word.
65
+ */
66
+ export declare function edgeLabelSize(label: string, fonts: DiagramFonts, wrapAt: number): EdgeLabelBox;
67
+ export type Placed = Pick<ArchitectureResolved, 'width' | 'height' | 'nodes' | 'edges'>;
68
+ /** The same layout with its two axes swapped (left to right becomes top to bottom). */
69
+ export declare function transpose(placed: Placed): Placed;
70
+ /**
71
+ * Cut a left to right layout into two rows, one under the other, so a long chain of ranks is not
72
+ * squeezed into one thin strip. The ranks are split where the two rows come out equally long.
73
+ * Edges inside a row keep their route; an edge between the rows goes down (or up) out of its
74
+ * node, along the channel between the rows and into the other node. Returns null when there are
75
+ * fewer than three ranks, or when an arrow between the rows would have to pass through a card.
76
+ */
77
+ export declare function foldRows(placed: Placed): Placed | null;
78
+ export interface ArchitectureLayout extends Placed {
79
+ /** The direction the diagram was laid out in; the other one when that fits much better. */
80
+ direction: 'LR' | 'TB';
81
+ }
82
+ /**
83
+ * Lay out an architecture diagram. Cards are sized from their text and the whole diagram is zoomed
84
+ * to the stage (see solve). A diagram that stays too small in the direction it was drawn in is also
85
+ * tried the other way round and turned when that is clearly better. Edges that name an unknown node
86
+ * are dropped and repeated node ids keep their first definition, so a slightly wrong storyboard
87
+ * still draws.
88
+ */
89
+ export declare function layoutArchitecture(visual: ArchitectureVisual): ArchitectureLayout;
@@ -0,0 +1,506 @@
1
+ /**
2
+ * Architecture diagram layout. Node and edge positions are computed here, in Node, so the browser
3
+ * runtime only draws them. Nodes are sized from their real text (label 30 px bold, detail 22 px)
4
+ * so the template never has to cut a name short, and the spacing is tuned for a 1920x1080 frame.
5
+ */
6
+ import dagre from '@dagrejs/dagre';
7
+ import { BASE_FONTS, DETAIL_LINE_HEIGHT, DIAGRAM_AREA, EDGE_LABEL_MAX, EDGE_LINE_HEIGHT, EDGE_PILL_HEIGHT_EM, EDGE_PILL_PAD_EM, EDGE_PILL_VPAD_EM, EDGE_WRAP, fitScale, fontsForScale, LABEL_LINE_HEIGHT, LABEL_SPACING_EM, NODE_CHROME, NODE_PAD_Y, NODE_TEXT_GAP, } from '../core/diagram-metrics.js';
8
+ /**
9
+ * Advance widths of Inter (the video font) in thousandths of an em, for the printable ASCII
10
+ * characters (space to tilde) at three weights. Kerning is ignored, which makes the estimate
11
+ * very slightly wide: a good side to err on.
12
+ */
13
+ const ADVANCE_TABLE = {
14
+ 500: '267 304 494 639 646 993 653 313 369 369 521 667 303 462 303 370 646 415 616 627 656 603 630 571 629 630 303 315 667 667 667 527 982 709 657 733 722 603 589 748 745 272 575 688 565 913 756 767 642 769 648 646 653 740 709 1003 701 696 641 369 370 369 477 463 337 568 618 577 618 587 379 620 602 252 252 559 252 888 602 604 618 618 387 539 340 602 575 829 557 575 559 440 346 440 667',
15
+ 600: '252 321 523 644 650 1004 663 326 373 373 540 673 319 465 319 379 660 423 623 636 666 612 640 576 640 640 319 329 673 673 673 543 999 728 659 737 722 605 588 749 746 277 580 703 565 922 759 769 645 773 652 650 660 736 728 1020 720 713 652 373 379 373 481 469 351 574 624 583 624 591 389 626 612 262 262 569 262 900 612 609 624 624 397 549 353 612 587 839 569 588 566 455 359 455 673',
16
+ 700: '237 338 552 649 655 1016 672 339 377 377 559 679 334 468 334 388 674 431 630 646 676 622 649 582 651 649 334 343 679 679 679 560 1016 747 662 740 722 607 587 751 747 281 584 719 565 932 762 771 648 777 657 655 667 732 747 1038 738 731 664 377 388 377 487 476 365 581 630 588 630 596 398 632 623 271 271 580 271 913 623 613 630 630 407 560 366 623 600 850 580 602 573 469 372 469 679',
17
+ };
18
+ /** Punctuation outside ASCII that labels use often, with its advance per weight (same order). */
19
+ const EXTRA_CHARS = '…·’‘“”–—×−•';
20
+ const EXTRA_TABLE = {
21
+ 500: '910 303 277 277 474 471 500 1000 667 667 533',
22
+ 600: '956 319 294 294 507 501 500 1000 673 673 503',
23
+ 700: '1002 334 311 311 540 532 500 1000 679 679 474',
24
+ };
25
+ const parseTable = (table) => table.split(' ').map(Number);
26
+ const ADVANCES = {
27
+ 500: parseTable(ADVANCE_TABLE[500]),
28
+ 600: parseTable(ADVANCE_TABLE[600]),
29
+ 700: parseTable(ADVANCE_TABLE[700]),
30
+ };
31
+ const EXTRAS = {
32
+ 500: parseTable(EXTRA_TABLE[500]),
33
+ 600: parseTable(EXTRA_TABLE[600]),
34
+ 700: parseTable(EXTRA_TABLE[700]),
35
+ };
36
+ /** Width of one character in thousandths of an em. Unknown characters count as an average letter. */
37
+ function advanceOf(ch, weight) {
38
+ const code = ch.codePointAt(0) ?? 0;
39
+ if (code >= 32 && code <= 126)
40
+ return ADVANCES[weight][code - 32] ?? 600;
41
+ const extra = EXTRA_CHARS.indexOf(ch);
42
+ if (extra >= 0)
43
+ return EXTRAS[weight][extra] ?? 600;
44
+ return code >= 0x2e80 ? 1000 : 600;
45
+ }
46
+ /** Width in px of a single line of text. */
47
+ export function textWidth(text, font) {
48
+ let total = 0;
49
+ let count = 0;
50
+ for (const ch of text) {
51
+ total += advanceOf(ch, font.weight);
52
+ count += 1;
53
+ }
54
+ return (total / 1000 + count * (font.spacingEm ?? 0)) * font.px;
55
+ }
56
+ /**
57
+ * Pieces of text that the browser does not split further without need: words, cut after a hyphen
58
+ * or a slash the way a line break may fall in "src/weather-api/index.ts".
59
+ */
60
+ function segmentsOf(text) {
61
+ const out = [];
62
+ for (const word of text.split(/\s+/).filter(Boolean)) {
63
+ const pieces = word.split(/(?<=[-/])(?=\S)/);
64
+ pieces.forEach((piece, i) => out.push({ text: piece, spaceBefore: i === 0 && out.length > 0 }));
65
+ }
66
+ return out;
67
+ }
68
+ /** Width of the widest piece of `text` that cannot be broken at a space, hyphen or slash. */
69
+ export function longestSegment(text, font) {
70
+ return Math.max(0, ...segmentsOf(text).map((segment) => textWidth(segment.text, font)));
71
+ }
72
+ /**
73
+ * Number of lines `text` takes in a box `width` px wide: lines break at spaces and after hyphens and
74
+ * slashes, and a piece that is wider than the box breaks anywhere (the template sets
75
+ * overflow-wrap: anywhere). Empty text takes no lines.
76
+ */
77
+ export function countLines(text, width, font) {
78
+ const segments = segmentsOf(text);
79
+ if (segments.length === 0)
80
+ return 0;
81
+ const room = Math.max(1, width);
82
+ const space = textWidth(' ', font);
83
+ let lines = 1;
84
+ let used = 0;
85
+ for (const segment of segments) {
86
+ const w = textWidth(segment.text, font);
87
+ const gap = segment.spaceBefore && used > 0 ? space : 0;
88
+ if (used > 0 && used + gap + w <= room) {
89
+ used += gap + w;
90
+ continue;
91
+ }
92
+ if (used > 0)
93
+ lines += 1;
94
+ if (w <= room) {
95
+ used = w;
96
+ }
97
+ else {
98
+ const rows = Math.ceil(w / room);
99
+ lines += rows - 1;
100
+ used = w - (rows - 1) * room;
101
+ }
102
+ }
103
+ return lines;
104
+ }
105
+ /**
106
+ * The smallest box width, from `from` up to `to`, in which `text` takes at most `maxLines` lines. If
107
+ * there is none, the widest (the text is then cut, as a last resort).
108
+ */
109
+ export function widthForLines(text, font, maxLines, from, to) {
110
+ for (let width = Math.ceil(from); width < to; width += 4) {
111
+ if (countLines(text, width, font) <= maxLines)
112
+ return width;
113
+ }
114
+ return Math.ceil(to);
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Node sizes
118
+ // ---------------------------------------------------------------------------
119
+ export const MIN_NODE_WIDTH = 260;
120
+ export const MIN_NODE_HEIGHT = 96;
121
+ export const MAX_NODE_WIDTH = 480;
122
+ export const MAX_LABEL_LINES = 2;
123
+ export const MAX_DETAIL_LINES = 2;
124
+ /** Extra room kept beside every line of text, as a share of its width plus a few pixels. */
125
+ const TEXT_SLACK = 1.02;
126
+ const TEXT_SLACK_PX = 6;
127
+ const LAYOUT_MARGIN = 28;
128
+ /**
129
+ * Node size from its text: wide enough for the label and detail on one line each, up to `maxWidth`,
130
+ * then wrapped (label up to two lines, detail up to two lines) and tall enough for what wraps. A
131
+ * card is never narrower than its longest word, so names are not broken in the middle.
132
+ */
133
+ export function estimateNodeSize(label, detail, options = {}) {
134
+ const fonts = options.fonts ?? BASE_FONTS;
135
+ const labelFont = { px: fonts.label, weight: 700, spacingEm: LABEL_SPACING_EM };
136
+ const detailFont = { px: fonts.detail, weight: 500 };
137
+ const labelText = label.trim();
138
+ const detailText = detail?.trim() ?? '';
139
+ const widest = Math.max(textWidth(labelText, labelFont), detailText ? textWidth(detailText, detailFont) : 0);
140
+ const longestWord = Math.max(longestSegment(labelText, labelFont), longestSegment(detailText, detailFont));
141
+ const minText = MIN_NODE_WIDTH - NODE_CHROME;
142
+ const hardMax = MAX_NODE_WIDTH - NODE_CHROME;
143
+ const floor = Math.min(hardMax, Math.max(minText, Math.ceil(longestWord * TEXT_SLACK) + TEXT_SLACK_PX));
144
+ const ceiling = Math.max(floor, (options.maxWidth ?? MAX_NODE_WIDTH) - NODE_CHROME);
145
+ const single = Math.ceil(widest * TEXT_SLACK) + TEXT_SLACK_PX;
146
+ // One line each if the card may be that wide; otherwise as narrow as keeps every part within its
147
+ // line limit, so text is only cut when even the widest card cannot hold it.
148
+ const needed = Math.max(widthForLines(labelText, labelFont, MAX_LABEL_LINES, floor, hardMax), detailText ? widthForLines(detailText, detailFont, MAX_DETAIL_LINES, floor, hardMax) : 0);
149
+ const text = Math.min(hardMax, Math.max(floor, needed, Math.min(ceiling, single)));
150
+ // Count lines against a slightly smaller box than the real one, so a line is never a pixel short.
151
+ const room = text - 2;
152
+ const labelLines = Math.min(MAX_LABEL_LINES, Math.max(1, countLines(labelText, room, labelFont)));
153
+ const detailLines = detailText ? Math.min(MAX_DETAIL_LINES, countLines(detailText, room, detailFont)) : 0;
154
+ const content = labelLines * fonts.label * LABEL_LINE_HEIGHT +
155
+ (detailLines > 0 ? NODE_TEXT_GAP + detailLines * fonts.detail * DETAIL_LINE_HEIGHT : 0);
156
+ return {
157
+ w: Math.round(text + NODE_CHROME),
158
+ h: Math.max(MIN_NODE_HEIGHT, Math.ceil(2 * NODE_PAD_Y + content)),
159
+ labelLines,
160
+ detailLines,
161
+ };
162
+ }
163
+ export const MAX_EDGE_LINES = 2;
164
+ /**
165
+ * Size of an edge label pill: one line if it fits `wrapAt`, else wrapped (at most two lines). The pill
166
+ * is never narrower than its longest word.
167
+ */
168
+ export function edgeLabelSize(label, fonts, wrapAt) {
169
+ const font = { px: fonts.edge, weight: 600 };
170
+ const pad = 2 * EDGE_PILL_PAD_EM * fonts.edge;
171
+ const text = label.trim();
172
+ const longestWord = longestSegment(text, font);
173
+ const floor = Math.max(wrapAt, Math.ceil(longestWord + pad) + 4);
174
+ const single = textWidth(text, font) + pad;
175
+ if (single <= floor) {
176
+ return { w: Math.ceil(single), h: Math.ceil(EDGE_PILL_HEIGHT_EM * fonts.edge), lines: 1 };
177
+ }
178
+ // Two lines as narrow as the floor allows; wider only if the text would not fit two lines at all.
179
+ const wrap = widthForLines(text, font, MAX_EDGE_LINES, floor - pad - 2, EDGE_LABEL_MAX - pad - 2) + pad + 2;
180
+ const lines = Math.min(MAX_EDGE_LINES, Math.max(2, countLines(text, wrap - pad - 2, font)));
181
+ const vertical = 2 * EDGE_PILL_VPAD_EM * fonts.edge;
182
+ return { w: Math.ceil(wrap), h: Math.ceil(lines * EDGE_LINE_HEIGHT * fonts.edge + vertical), lines };
183
+ }
184
+ const round1 = (value) => Math.round(value * 10) / 10;
185
+ // ---------------------------------------------------------------------------
186
+ // Folding a long chain into two rows
187
+ // ---------------------------------------------------------------------------
188
+ /** A diagram that needs a zoom below this is cut into two rows if that helps. */
189
+ const FOLD_BELOW_SCALE = 0.8;
190
+ /** Two rows are used when they allow a zoom at least this much larger than one. */
191
+ const FOLD_GAIN = 1.25;
192
+ /** Space between the two rows: room for the arrows that join them, and their labels. */
193
+ const FOLD_GAP = 150;
194
+ /** Room a row needs above its nodes (the status pill) and below them (edge labels). */
195
+ const ROW_TOP = 30;
196
+ const ROW_BOTTOM = 22;
197
+ /** Distance between arrows that share the channel between the rows. */
198
+ const CHANNEL_STEP = 26;
199
+ /** Space kept between an arrow and a card it passes, in px. */
200
+ const CLEARANCE = 10;
201
+ /** Whether a horizontal or vertical segment from a to b touches a rectangle grown by `pad`. */
202
+ function segmentHitsRect(a, b, rect, pad) {
203
+ const left = rect.x - rect.w / 2 - pad;
204
+ const right = rect.x + rect.w / 2 + pad;
205
+ const top = rect.y - rect.h / 2 - pad;
206
+ const bottom = rect.y + rect.h / 2 + pad;
207
+ return (Math.max(Math.min(a.x, b.x), left) <= Math.min(Math.max(a.x, b.x), right) &&
208
+ Math.max(Math.min(a.y, b.y), top) <= Math.min(Math.max(a.y, b.y), bottom));
209
+ }
210
+ /** The same layout with its two axes swapped (left to right becomes top to bottom). */
211
+ export function transpose(placed) {
212
+ return {
213
+ width: placed.height,
214
+ height: placed.width,
215
+ nodes: placed.nodes.map((n) => ({ ...n, x: n.y, y: n.x, w: n.h, h: n.w })),
216
+ edges: placed.edges.map((e) => ({ ...e, points: e.points.map((p) => ({ x: p.y, y: p.x })) })),
217
+ };
218
+ }
219
+ /**
220
+ * Cut a left to right layout into two rows, one under the other, so a long chain of ranks is not
221
+ * squeezed into one thin strip. The ranks are split where the two rows come out equally long.
222
+ * Edges inside a row keep their route; an edge between the rows goes down (or up) out of its
223
+ * node, along the channel between the rows and into the other node. Returns null when there are
224
+ * fewer than three ranks, or when an arrow between the rows would have to pass through a card.
225
+ */
226
+ export function foldRows(placed) {
227
+ const ranks = new Map();
228
+ placed.nodes.forEach((n, i) => {
229
+ const key = Math.round(n.x);
230
+ ranks.set(key, [...(ranks.get(key) ?? []), i]);
231
+ });
232
+ const keys = [...ranks.keys()].sort((a, b) => a - b);
233
+ if (keys.length < 3)
234
+ return null;
235
+ const extent = (members) => {
236
+ let left = Number.POSITIVE_INFINITY;
237
+ let right = Number.NEGATIVE_INFINITY;
238
+ let top = Number.POSITIVE_INFINITY;
239
+ let bottom = Number.NEGATIVE_INFINITY;
240
+ for (const i of members) {
241
+ const n = placed.nodes[i];
242
+ left = Math.min(left, n.x - n.w / 2);
243
+ right = Math.max(right, n.x + n.w / 2);
244
+ top = Math.min(top, n.y - n.h / 2 - ROW_TOP);
245
+ bottom = Math.max(bottom, n.y + n.h / 2 + ROW_BOTTOM);
246
+ }
247
+ return { left, right, top, bottom };
248
+ };
249
+ const membersOf = (rankKeys) => rankKeys.flatMap((k) => ranks.get(k) ?? []);
250
+ let split = 1;
251
+ let narrowest = Number.POSITIVE_INFINITY;
252
+ for (let m = 1; m < keys.length; m++) {
253
+ const a = extent(membersOf(keys.slice(0, m)));
254
+ const b = extent(membersOf(keys.slice(m)));
255
+ const longest = Math.max(a.right - a.left, b.right - b.left);
256
+ if (longest < narrowest) {
257
+ narrowest = longest;
258
+ split = m;
259
+ }
260
+ }
261
+ const firstRow = new Set(membersOf(keys.slice(0, split)));
262
+ const a = extent([...firstRow]);
263
+ const b = extent(membersOf(keys.slice(split)));
264
+ const dx1 = LAYOUT_MARGIN - a.left;
265
+ const dy1 = LAYOUT_MARGIN - a.top;
266
+ const dx2 = LAYOUT_MARGIN - b.left;
267
+ // The second row starts FOLD_GAP below the lowest edge of the first one.
268
+ const dy2 = dy1 + a.bottom + FOLD_GAP - b.top;
269
+ const rowOf = new Map();
270
+ const nodes = placed.nodes.map((n, i) => {
271
+ const row = firstRow.has(i) ? 1 : 2;
272
+ rowOf.set(n.id, row);
273
+ return { ...n, x: round1(n.x + (row === 1 ? dx1 : dx2)), y: round1(n.y + (row === 1 ? dy1 : dy2)) };
274
+ });
275
+ const byId = new Map(nodes.map((n) => [n.id, n]));
276
+ const channel = dy1 + a.bottom + FOLD_GAP / 2;
277
+ const crossing = placed.edges.filter((e) => rowOf.get(e.from) !== rowOf.get(e.to));
278
+ const edges = placed.edges.map((edge) => {
279
+ const rowFrom = rowOf.get(edge.from);
280
+ const rowTo = rowOf.get(edge.to);
281
+ if (rowFrom === rowTo) {
282
+ const dx = rowFrom === 1 ? dx1 : dx2;
283
+ const dy = rowFrom === 1 ? dy1 : dy2;
284
+ return { ...edge, points: edge.points.map((p) => ({ x: round1(p.x + dx), y: round1(p.y + dy) })) };
285
+ }
286
+ const from = byId.get(edge.from);
287
+ const to = byId.get(edge.to);
288
+ const k = crossing.indexOf(edge);
289
+ const y = channel + (k - (crossing.length - 1) / 2) * CHANNEL_STEP;
290
+ const down = rowFrom === 1;
291
+ const out = down ? from.y + from.h / 2 : from.y - from.h / 2;
292
+ const into = down ? to.y - to.h / 2 : to.y + to.h / 2;
293
+ return {
294
+ ...edge,
295
+ points: [
296
+ { x: round1(from.x), y: round1(out) },
297
+ { x: round1(from.x), y: round1(y) },
298
+ { x: round1(to.x), y: round1(y) },
299
+ { x: round1(to.x), y: round1(into) },
300
+ ],
301
+ };
302
+ });
303
+ // A fold is only good when no arrow between the rows runs through a card on its way.
304
+ for (const edge of edges) {
305
+ if (rowOf.get(edge.from) === rowOf.get(edge.to))
306
+ continue;
307
+ for (let i = 0; i + 1 < edge.points.length; i++) {
308
+ const a = edge.points[i];
309
+ const b = edge.points[i + 1];
310
+ for (const n of nodes) {
311
+ if (n.id === edge.from || n.id === edge.to)
312
+ continue;
313
+ if (segmentHitsRect(a, b, n, CLEARANCE))
314
+ return null;
315
+ }
316
+ }
317
+ }
318
+ const right = Math.max(...nodes.map((n) => n.x + n.w / 2));
319
+ const bottom = Math.max(...nodes.map((n) => n.y + n.h / 2 + ROW_BOTTOM));
320
+ return { width: Math.ceil(right + LAYOUT_MARGIN), height: Math.ceil(bottom + LAYOUT_MARGIN), nodes, edges };
321
+ }
322
+ /**
323
+ * The layout in one piece, or in two rows (two columns for a top to bottom diagram) when it would
324
+ * have to be drawn small and the two-row version can be drawn clearly larger.
325
+ */
326
+ function foldIfSmall(direction, placed) {
327
+ const scale = fitScale(placed.width, placed.height);
328
+ if (scale >= FOLD_BELOW_SCALE || placed.nodes.length < 4)
329
+ return placed;
330
+ const horizontal = direction === 'LR';
331
+ const folded = foldRows(horizontal ? placed : transpose(placed));
332
+ if (!folded)
333
+ return placed;
334
+ const result = horizontal ? folded : transpose(folded);
335
+ return fitScale(result.width, result.height) > scale * FOLD_GAIN ? result : placed;
336
+ }
337
+ /** Space between nodes of one rank, and between ranks, in layout pixels. */
338
+ const NODE_SEP = { LR: 88, TB: 80 };
339
+ const RANK_SEP = { LR: 84, TB: 96 };
340
+ /** The most extra space between nodes of one rank that is added to use a stage that is mostly empty. */
341
+ const MAX_EXTRA_NODE_SEP = 80;
342
+ /** One dagre run with fixed node widths and fonts. */
343
+ function place(nodes, edges, options) {
344
+ const { direction, maxNodeWidth, fonts } = options;
345
+ const horizontal = direction === 'LR';
346
+ const graph = new dagre.graphlib.Graph({ multigraph: true });
347
+ graph.setGraph({
348
+ rankdir: horizontal ? 'LR' : 'TB',
349
+ nodesep: NODE_SEP[direction] + (options.extraNodeSep ?? 0),
350
+ ranksep: RANK_SEP[direction],
351
+ edgesep: 24,
352
+ marginx: LAYOUT_MARGIN,
353
+ marginy: LAYOUT_MARGIN,
354
+ ranker: 'network-simplex',
355
+ });
356
+ graph.setDefaultEdgeLabel(() => ({}));
357
+ for (const [id, node] of nodes) {
358
+ const size = estimateNodeSize(node.label, node.detail, { maxWidth: maxNodeWidth, fonts });
359
+ graph.setNode(id, { width: size.w, height: size.h });
360
+ }
361
+ edges.forEach((edge, index) => {
362
+ const label = (edge.label ?? '').trim();
363
+ const box = label ? edgeLabelSize(label, fonts, EDGE_WRAP[direction]) : null;
364
+ // Keep a little room around the pill so arrows show on both sides of it.
365
+ graph.setEdge(edge.from, edge.to, box ? { width: box.w + 28, height: box.h + 14, labelpos: 'c' } : {}, String(index));
366
+ });
367
+ dagre.layout(graph);
368
+ const box = graph.graph();
369
+ const placedNodes = [];
370
+ for (const [id, node] of nodes) {
371
+ const placed = graph.node(id);
372
+ placedNodes.push({
373
+ id,
374
+ label: node.label,
375
+ detail: node.detail,
376
+ kind: node.kind,
377
+ status: node.status,
378
+ beat: node.beat,
379
+ x: round1(placed.x),
380
+ y: round1(placed.y),
381
+ w: placed.width,
382
+ h: placed.height,
383
+ });
384
+ }
385
+ const byId = new Map(placedNodes.map((n) => [n.id, n]));
386
+ const placedEdges = edges.map((edge, index) => {
387
+ const layoutEdge = graph.edge(edge.from, edge.to, String(index));
388
+ let points = (layoutEdge?.points ?? []).map((p) => ({
389
+ x: round1(p.x),
390
+ y: round1(p.y),
391
+ }));
392
+ if (points.length < 2) {
393
+ // Fall back to a straight line between the node centres.
394
+ const from = byId.get(edge.from);
395
+ const to = byId.get(edge.to);
396
+ points = [
397
+ { x: from.x, y: from.y },
398
+ { x: to.x, y: to.y },
399
+ ];
400
+ }
401
+ return {
402
+ from: edge.from,
403
+ to: edge.to,
404
+ label: edge.label,
405
+ status: edge.status,
406
+ beat: edge.beat,
407
+ points,
408
+ };
409
+ });
410
+ return foldIfSmall(direction, {
411
+ width: Math.ceil(box.width ?? 0),
412
+ height: Math.ceil(box.height ?? 0),
413
+ nodes: placedNodes,
414
+ edges: placedEdges,
415
+ });
416
+ }
417
+ /** Widest a card may be, tried in order: roomy first, then narrower so more text wraps. */
418
+ const WIDTH_STEPS = [MAX_NODE_WIDTH, 400, 340];
419
+ /** A diagram that needs a zoom below this is tried again with narrower cards. */
420
+ const COMFORTABLE_SCALE = 0.92;
421
+ /** Narrower cards are used when they allow a zoom this much larger. */
422
+ const NARROWER_CARDS_GAIN = 1.04;
423
+ const MAX_FONT_ROUNDS = 12;
424
+ /** The font rounds stop when the zoom changes by less than this. */
425
+ const SCALE_SETTLED = 0.004;
426
+ /** A diagram that still needs a zoom below this is also tried in the other direction. */
427
+ const TRY_OTHER_DIRECTION = 0.8;
428
+ /** The other direction is used when it allows a zoom this much larger. */
429
+ const OTHER_DIRECTION_GAIN = 1.12;
430
+ /**
431
+ * Lay out in one direction. A diagram that would have to shrink on screen gets narrower cards (more
432
+ * wrapping) and larger layout fonts, so text stays legible; the fonts and the zoom are settled
433
+ * together, because larger fonts make the diagram larger.
434
+ */
435
+ function solve(nodes, edges, direction) {
436
+ let best = null;
437
+ for (const maxNodeWidth of WIDTH_STEPS) {
438
+ let options = { direction, maxNodeWidth, fonts: BASE_FONTS };
439
+ let placed = place(nodes, edges, options);
440
+ let scale = fitScale(placed.width, placed.height);
441
+ for (let round = 0; round < MAX_FONT_ROUNDS && scale < 1; round++) {
442
+ // Aim a little low: a slightly larger font than needed is safe, a smaller one would overflow.
443
+ options = { direction, maxNodeWidth, fonts: fontsForScale(scale * 0.97) };
444
+ placed = place(nodes, edges, options);
445
+ const next = fitScale(placed.width, placed.height);
446
+ const settled = Math.abs(next - scale) < SCALE_SETTLED;
447
+ scale = next;
448
+ if (settled)
449
+ break;
450
+ }
451
+ // Narrower cards (more wrapping) are only worth it for a clear gain in size.
452
+ if (!best || scale > best.scale * NARROWER_CARDS_GAIN)
453
+ best = { placed, scale, options };
454
+ if (scale >= COMFORTABLE_SCALE)
455
+ break;
456
+ }
457
+ return best;
458
+ }
459
+ /**
460
+ * Use free space on the stage: when the zoom is set by the length of the diagram, the nodes of a
461
+ * rank are moved apart across it, so the diagram does not sit as a thin strip in a mostly empty frame.
462
+ */
463
+ function spread(solved, nodes, edges) {
464
+ const { placed, scale, options } = solved;
465
+ const horizontal = options.direction === 'LR';
466
+ const area = horizontal ? DIAGRAM_AREA.h : DIAGRAM_AREA.w;
467
+ const used = horizontal ? placed.height : placed.width;
468
+ const slack = (area - 2 * DIAGRAM_AREA.margin) / scale - used;
469
+ const ranks = new Map();
470
+ for (const node of placed.nodes) {
471
+ const key = Math.round(horizontal ? node.x : node.y);
472
+ ranks.set(key, (ranks.get(key) ?? 0) + 1);
473
+ }
474
+ const widest = Math.max(1, ...ranks.values());
475
+ if (widest < 2 || slack < 60)
476
+ return solved;
477
+ const extra = Math.min(MAX_EXTRA_NODE_SEP, (slack * 0.35) / (widest - 1));
478
+ const spreadOptions = { ...options, extraNodeSep: Math.round(extra) };
479
+ const wider = place(nodes, edges, spreadOptions);
480
+ const next = fitScale(wider.width, wider.height);
481
+ return next >= scale - 0.002 ? { placed: wider, scale: next, options: spreadOptions } : solved;
482
+ }
483
+ /**
484
+ * Lay out an architecture diagram. Cards are sized from their text and the whole diagram is zoomed
485
+ * to the stage (see solve). A diagram that stays too small in the direction it was drawn in is also
486
+ * tried the other way round and turned when that is clearly better. Edges that name an unknown node
487
+ * are dropped and repeated node ids keep their first definition, so a slightly wrong storyboard
488
+ * still draws.
489
+ */
490
+ export function layoutArchitecture(visual) {
491
+ const direction = visual.direction === 'TB' ? 'TB' : 'LR';
492
+ const nodes = new Map();
493
+ for (const node of visual.nodes)
494
+ if (!nodes.has(node.id))
495
+ nodes.set(node.id, node);
496
+ const edges = visual.edges.filter((edge) => nodes.has(edge.from) && nodes.has(edge.to));
497
+ let best = solve(nodes, edges, direction);
498
+ if (best.scale < TRY_OTHER_DIRECTION && nodes.size > 1) {
499
+ const other = solve(nodes, edges, direction === 'LR' ? 'TB' : 'LR');
500
+ if (other.scale > best.scale * OTHER_DIRECTION_GAIN)
501
+ best = other;
502
+ }
503
+ best = spread(best, nodes, edges);
504
+ return { ...best.placed, direction: best.options.direction };
505
+ }
506
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Render plan builder: turns a validated storyboard, the timeline and the repository into a
3
+ * self-contained RenderPlan. Code is read from the repository here, never taken from the storyboard,
4
+ * and every string that reaches the screen is masked first.
5
+ */
6
+ import { type Config } from '../core/config.ts';
7
+ import type { RunContext } from '../core/context.ts';
8
+ import type { RenderPlan } from '../core/render-plan.ts';
9
+ import { type Storyboard } from '../core/storyboard-schema.ts';
10
+ import type { Analysis, CaptionCue, ChangeSet, ContextInputs, RepoInfo, RepoReader, Timeline } from '../core/types.ts';
11
+ export interface BuildPlanInput {
12
+ storyboard: Storyboard;
13
+ timeline: Timeline;
14
+ cues: CaptionCue[];
15
+ changeSet: ChangeSet;
16
+ analysis: Analysis;
17
+ inputs: ContextInputs;
18
+ reader: RepoReader;
19
+ config: Config;
20
+ }
21
+ /** Rows shown in the changed-files visual; the rest is summarised as a count. */
22
+ export declare const MAX_FILE_ROWS = 16;
23
+ export declare const OUTRO_TAGLINE = "Every change has a story.";
24
+ export declare const OUTRO_DISCLAIMER = "This video explains the change. It does not replace code review.";
25
+ /** "owner/name", or the repository name. Never a filesystem path. */
26
+ export declare function repoLabelFor(repo: RepoInfo): string;
27
+ /** "PR #42", or the head branch for a local run. */
28
+ export declare function changeLabelFor(changeSet: ChangeSet): string;
29
+ /**
30
+ * Build the render plan: quality preset, theme, labels, captions, chapters, one planned scene per
31
+ * storyboard scene (code, diagrams and screenshots resolved) and the end card.
32
+ */
33
+ export declare function buildRenderPlan(input: BuildPlanInput, ctx: RunContext): Promise<RenderPlan>;