@biunow/agent-biuart 0.0.0-stage → 0.1.1

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 (377) hide show
  1. package/bin/server.mjs +575 -0
  2. package/data/styles.json +533 -0
  3. package/engine/audit_clip.py +46 -0
  4. package/engine/clip.html +24 -0
  5. package/engine/clip.js +104 -0
  6. package/engine/clip_schema.json +19 -0
  7. package/engine/clips/t1_3b1b.js +203 -0
  8. package/engine/clips/t2_keynote_ui.js +347 -0
  9. package/engine/clips/t3_finance_chart.js +206 -0
  10. package/engine/clips/y1_kurzgesagt.js +183 -0
  11. package/engine/clips/y2_vox.js +150 -0
  12. package/engine/clips/y3_whiteboard.js +209 -0
  13. package/engine/clips/y4_storytime.js +194 -0
  14. package/engine/clips/y5_kinetic_type.js +134 -0
  15. package/engine/compare.py +19 -0
  16. package/engine/demos/_shared/hero/c1_0.png +0 -0
  17. package/engine/demos/_shared/hero/c1_1.png +0 -0
  18. package/engine/demos/_shared/hero/c1_2.png +0 -0
  19. package/engine/demos/_shared/hero/c3_line.png +0 -0
  20. package/engine/demos/_shared/hero.js +96 -0
  21. package/engine/demos/long_scroll/eras.js +23 -0
  22. package/engine/demos/long_scroll/frames/8bit_act/01.png +0 -0
  23. package/engine/demos/long_scroll/frames/8bit_act/02.png +0 -0
  24. package/engine/demos/long_scroll/frames/8bit_act/03.png +0 -0
  25. package/engine/demos/long_scroll/frames/8bit_act/04.png +0 -0
  26. package/engine/demos/long_scroll/frames/8bit_act/05.png +0 -0
  27. package/engine/demos/long_scroll/frames/8bit_act/06.png +0 -0
  28. package/engine/demos/long_scroll/frames/8bit_act/07.png +0 -0
  29. package/engine/demos/long_scroll/frames/8bit_act/08.png +0 -0
  30. package/engine/demos/long_scroll/frames/8bit_act/meta.json +69 -0
  31. package/engine/demos/long_scroll/frames/8bit_walk/01.png +0 -0
  32. package/engine/demos/long_scroll/frames/8bit_walk/02.png +0 -0
  33. package/engine/demos/long_scroll/frames/8bit_walk/03.png +0 -0
  34. package/engine/demos/long_scroll/frames/8bit_walk/04.png +0 -0
  35. package/engine/demos/long_scroll/frames/8bit_walk/05.png +0 -0
  36. package/engine/demos/long_scroll/frames/8bit_walk/06.png +0 -0
  37. package/engine/demos/long_scroll/frames/8bit_walk/07.png +0 -0
  38. package/engine/demos/long_scroll/frames/8bit_walk/08.png +0 -0
  39. package/engine/demos/long_scroll/frames/8bit_walk/meta.json +69 -0
  40. package/engine/demos/long_scroll/frames/egypt_dodge/01.png +0 -0
  41. package/engine/demos/long_scroll/frames/egypt_dodge/02.png +0 -0
  42. package/engine/demos/long_scroll/frames/egypt_dodge/03.png +0 -0
  43. package/engine/demos/long_scroll/frames/egypt_dodge/04.png +0 -0
  44. package/engine/demos/long_scroll/frames/egypt_dodge/05.png +0 -0
  45. package/engine/demos/long_scroll/frames/egypt_dodge/06.png +0 -0
  46. package/engine/demos/long_scroll/frames/egypt_dodge/07.png +0 -0
  47. package/engine/demos/long_scroll/frames/egypt_dodge/08.png +0 -0
  48. package/engine/demos/long_scroll/frames/egypt_dodge/meta.json +69 -0
  49. package/engine/demos/long_scroll/frames/egypt_walk8/01.png +0 -0
  50. package/engine/demos/long_scroll/frames/egypt_walk8/02.png +0 -0
  51. package/engine/demos/long_scroll/frames/egypt_walk8/03.png +0 -0
  52. package/engine/demos/long_scroll/frames/egypt_walk8/04.png +0 -0
  53. package/engine/demos/long_scroll/frames/egypt_walk8/05.png +0 -0
  54. package/engine/demos/long_scroll/frames/egypt_walk8/06.png +0 -0
  55. package/engine/demos/long_scroll/frames/egypt_walk8/07.png +0 -0
  56. package/engine/demos/long_scroll/frames/egypt_walk8/08.png +0 -0
  57. package/engine/demos/long_scroll/frames/egypt_walk8/meta.json +69 -0
  58. package/engine/demos/long_scroll/frames/monet_throw/01.png +0 -0
  59. package/engine/demos/long_scroll/frames/monet_throw/02.png +0 -0
  60. package/engine/demos/long_scroll/frames/monet_throw/03.png +0 -0
  61. package/engine/demos/long_scroll/frames/monet_throw/04.png +0 -0
  62. package/engine/demos/long_scroll/frames/monet_throw/05.png +0 -0
  63. package/engine/demos/long_scroll/frames/monet_throw/06.png +0 -0
  64. package/engine/demos/long_scroll/frames/monet_throw/07.png +0 -0
  65. package/engine/demos/long_scroll/frames/monet_throw/08.png +0 -0
  66. package/engine/demos/long_scroll/frames/monet_throw/meta.json +71 -0
  67. package/engine/demos/long_scroll/frames/monet_walk/01.png +0 -0
  68. package/engine/demos/long_scroll/frames/monet_walk/02.png +0 -0
  69. package/engine/demos/long_scroll/frames/monet_walk/03.png +0 -0
  70. package/engine/demos/long_scroll/frames/monet_walk/04.png +0 -0
  71. package/engine/demos/long_scroll/frames/monet_walk/05.png +0 -0
  72. package/engine/demos/long_scroll/frames/monet_walk/06.png +0 -0
  73. package/engine/demos/long_scroll/frames/monet_walk/07.png +0 -0
  74. package/engine/demos/long_scroll/frames/monet_walk/08.png +0 -0
  75. package/engine/demos/long_scroll/frames/monet_walk/meta.json +71 -0
  76. package/engine/demos/long_scroll/lib/acts.js +87 -0
  77. package/engine/demos/long_scroll/lib/crossing.js +463 -0
  78. package/engine/demos/long_scroll/lib/world.js +143 -0
  79. package/engine/demos/long_scroll/lib/xing2_ding.js +42 -0
  80. package/engine/demos/long_scroll/lib/xing2_jia.js +114 -0
  81. package/engine/demos/long_scroll/lib/xing2_yi.js +133 -0
  82. package/engine/demos/long_scroll/segments/02_egypt.js +234 -0
  83. package/engine/demos/long_scroll/segments/14_8bit.js +118 -0
  84. package/engine/demos/long_scroll/segments/28_monet.js +86 -0
  85. package/engine/demos/t1_3b1b/eras.js +11 -0
  86. package/engine/demos/t1_3b1b/t1_s1.js +101 -0
  87. package/engine/demos/t1_3b1b/t1_s2.js +74 -0
  88. package/engine/demos/t1_3b1b/t1_s3.js +65 -0
  89. package/engine/demos/t2_keynote_ui/eras.js +10 -0
  90. package/engine/demos/t2_keynote_ui/t2_s1.js +57 -0
  91. package/engine/demos/t2_keynote_ui/t2_s2.js +70 -0
  92. package/engine/demos/t2_keynote_ui/t2_s3.js +61 -0
  93. package/engine/demos/t3_finance_chart/eras.js +11 -0
  94. package/engine/demos/t3_finance_chart/t3_s1.js +48 -0
  95. package/engine/demos/t3_finance_chart/t3_s2.js +42 -0
  96. package/engine/demos/t3_finance_chart/t3_s3.js +51 -0
  97. package/engine/demos/y1_kurzgesagt/eras.js +10 -0
  98. package/engine/demos/y1_kurzgesagt/kurzgesagt.js +347 -0
  99. package/engine/demos/y2_vox/eras.js +10 -0
  100. package/engine/demos/y2_vox/vox.js +240 -0
  101. package/engine/demos/y3_whiteboard/eras.js +10 -0
  102. package/engine/demos/y3_whiteboard/whiteboard.js +162 -0
  103. package/engine/demos/y4_storytime/eras.js +14 -0
  104. package/engine/demos/y4_storytime/s1_desk.js +18 -0
  105. package/engine/demos/y4_storytime/s2_screen.js +82 -0
  106. package/engine/demos/y4_storytime/s3_react.js +39 -0
  107. package/engine/demos/y4_storytime/s4_wide.js +16 -0
  108. package/engine/demos/y4_storytime/s5_close.js +18 -0
  109. package/engine/demos/y4_storytime/story.js +25 -0
  110. package/engine/demos/y4_storytime/vo_env.js +2 -0
  111. package/engine/demos/y4_storytime/world.js +73 -0
  112. package/engine/demos/y5_kinetic_type/eras.js +16 -0
  113. package/engine/demos/y5_kinetic_type/k1_title.js +38 -0
  114. package/engine/demos/y5_kinetic_type/k2_complete.js +55 -0
  115. package/engine/demos/y5_kinetic_type/k3_chat.js +47 -0
  116. package/engine/demos/y5_kinetic_type/k4_agent.js +49 -0
  117. package/engine/demos/y5_kinetic_type/kinetic.js +52 -0
  118. package/engine/engine.js +277 -0
  119. package/engine/eras.js +60 -0
  120. package/engine/eras_gallery.js +115 -0
  121. package/engine/eras_style_studies.js +20 -0
  122. package/engine/examples/assets/K/347/272/277/345/233/276_/346/250/252/345/261/217.png +0 -0
  123. package/engine/examples/assets//347/273/210/347/253/257_for/345/276/252/347/216/257/346/261/202/345/222/214.png +0 -0
  124. package/engine/examples/assets//347/273/210/347/253/257_sum/346/261/202/345/222/214.png +0 -0
  125. package/engine/examples/assets//350/220/245/346/224/266/345/233/276_/347/253/226/345/261/217.png +0 -0
  126. package/engine/examples/assets//350/264/242/347/273/217/345/233/276/350/241/250_/346/250/252/345/261/217.png +0 -0
  127. package/engine/examples/t1_3b1b.json +19 -0
  128. package/engine/examples/t2_fullbleed_pulse.json +15 -0
  129. package/engine/examples/t2_keynote_ui.json +15 -0
  130. package/engine/examples/t3_finance_chart.json +29 -0
  131. package/engine/examples/y1_kurzgesagt.json +16 -0
  132. package/engine/examples/y2_vox.json +14 -0
  133. package/engine/examples/y3_whiteboard.json +17 -0
  134. package/engine/examples/y4_storytime.json +52 -0
  135. package/engine/examples/y5_kinetic_type.json +14 -0
  136. package/engine/index.html +42 -0
  137. package/engine/lib/THREE_LICENSE.txt +21 -0
  138. package/engine/lib/brush.js +149 -0
  139. package/engine/lib/camera.js +99 -0
  140. package/engine/lib/chart.js +152 -0
  141. package/engine/lib/collage.js +97 -0
  142. package/engine/lib/diagram.js +264 -0
  143. package/engine/lib/finish.js +70 -0
  144. package/engine/lib/fonts/AlfaSlabOne-400.woff +0 -0
  145. package/engine/lib/fonts/Anton-400.woff +0 -0
  146. package/engine/lib/fonts/ArchivoBlack-400.woff +0 -0
  147. package/engine/lib/fonts/Bangers-400.woff +0 -0
  148. package/engine/lib/fonts/Caveat-600.woff +0 -0
  149. package/engine/lib/fonts/Cinzel-400.woff +0 -0
  150. package/engine/lib/fonts/Cinzel-700.woff +0 -0
  151. package/engine/lib/fonts/CormorantGaramond-500.woff +0 -0
  152. package/engine/lib/fonts/CormorantGaramond-500i.woff +0 -0
  153. package/engine/lib/fonts/CormorantGaramond-700.woff +0 -0
  154. package/engine/lib/fonts/EBGaramond-400.woff +0 -0
  155. package/engine/lib/fonts/EBGaramond-400i.woff +0 -0
  156. package/engine/lib/fonts/EBGaramond-600.woff +0 -0
  157. package/engine/lib/fonts/Exo2-800i.woff +0 -0
  158. package/engine/lib/fonts/GochiHand-400.woff +0 -0
  159. package/engine/lib/fonts/IMFellEnglish-400.woff +0 -0
  160. package/engine/lib/fonts/IMFellEnglish-400i.woff +0 -0
  161. package/engine/lib/fonts/Inter-var.woff +0 -0
  162. package/engine/lib/fonts/JosefinSans-700.woff +0 -0
  163. package/engine/lib/fonts/Kalam-700.woff +0 -0
  164. package/engine/lib/fonts/LICENSES.md +40 -0
  165. package/engine/lib/fonts/LXGWWenKai-500.woff +0 -0
  166. package/engine/lib/fonts/LibreBaskerville-400.woff +0 -0
  167. package/engine/lib/fonts/LibreBaskerville-700.woff +0 -0
  168. package/engine/lib/fonts/LilitaOne-400.woff +0 -0
  169. package/engine/lib/fonts/Marcellus-400.woff +0 -0
  170. package/engine/lib/fonts/NotoSansSC-500.woff +0 -0
  171. package/engine/lib/fonts/NotoSansSC-700.woff +0 -0
  172. package/engine/lib/fonts/NotoSansSC-800.woff +0 -0
  173. package/engine/lib/fonts/NotoSansSC-900.woff +0 -0
  174. package/engine/lib/fonts/NotoSansSC-sub.woff +0 -0
  175. package/engine/lib/fonts/NotoSerifJP-600.woff +0 -0
  176. package/engine/lib/fonts/NotoSerifSC-sub.woff +0 -0
  177. package/engine/lib/fonts/OFL.txt +94 -0
  178. package/engine/lib/fonts/PlayfairDisplay-400i.woff +0 -0
  179. package/engine/lib/fonts/PlayfairDisplay-700.woff +0 -0
  180. package/engine/lib/fonts/PlayfairDisplay-700i.woff +0 -0
  181. package/engine/lib/fonts/Poppins-600.woff +0 -0
  182. package/engine/lib/fonts/Poppins-700.woff +0 -0
  183. package/engine/lib/fonts/Poppins-800.woff +0 -0
  184. package/engine/lib/fonts/PressStart2P-400.woff +0 -0
  185. package/engine/lib/fonts/Righteous-400.woff +0 -0
  186. package/engine/lib/fonts/RobotoCondensed-var.woff +0 -0
  187. package/engine/lib/fonts/Rye-400.woff +0 -0
  188. package/engine/lib/fonts/UnifrakturMaguntia-400.woff +0 -0
  189. package/engine/lib/fonts/cmunrm.woff +0 -0
  190. package/engine/lib/fonts/cmunti.woff +0 -0
  191. package/engine/lib/fonts/index.json +1 -0
  192. package/engine/lib/fonts.js +56 -0
  193. package/engine/lib/kit.js +91 -0
  194. package/engine/lib/media.js +118 -0
  195. package/engine/lib/medium.js +258 -0
  196. package/engine/lib/motion.js +111 -0
  197. package/engine/lib/paint.js +200 -0
  198. package/engine/lib/palettes.js +57 -0
  199. package/engine/lib/post.js +108 -0
  200. package/engine/lib/render.js +189 -0
  201. package/engine/lib/rig.js +390 -0
  202. package/engine/lib/rig_huashu.js +171 -0
  203. package/engine/lib/three.min.js +7 -0
  204. package/engine/lib/toon.js +186 -0
  205. package/engine/lib/typo.js +136 -0
  206. package/engine/lib/ui.js +141 -0
  207. package/engine/lib/util.js +92 -0
  208. package/engine/render.py +157 -0
  209. package/engine/scenes/01_cave.js +364 -0
  210. package/engine/scenes/02_egypt.js +524 -0
  211. package/engine/scenes/03_greek.js +448 -0
  212. package/engine/scenes/04_roman.js +555 -0
  213. package/engine/scenes/05_gothic.js +522 -0
  214. package/engine/scenes/06_renaissance.js +338 -0
  215. package/engine/scenes/08_impressionism.js +206 -0
  216. package/engine/scenes/09_postimp.js +183 -0
  217. package/engine/scenes/10_nouveau.js +334 -0
  218. package/engine/scenes/11_cubism.js +265 -0
  219. package/engine/scenes/12_bauhaus.js +214 -0
  220. package/engine/scenes/13_pop.js +269 -0
  221. package/engine/scenes/14_8bit.js +238 -0
  222. package/engine/scenes/15_raytrace.js +300 -0
  223. package/engine/scenes/16_2026.js +565 -0
  224. package/engine/scenes/17_ink.js +273 -0
  225. package/engine/scenes/18_klimt.js +227 -0
  226. package/engine/scenes/19_munch.js +153 -0
  227. package/engine/scenes/20_dunhuang.js +215 -0
  228. package/engine/scenes/21_kusama.js +124 -0
  229. package/engine/scenes/22_constructivism.js +205 -0
  230. package/engine/scenes/23_dali.js +221 -0
  231. package/engine/scenes/24_hopper.js +150 -0
  232. package/engine/scenes/25_ghibli.js +251 -0
  233. package/engine/scenes/26_vaporwave.js +191 -0
  234. package/engine/scenes/27_kirby.js +255 -0
  235. package/engine/scenes/28_monet.js +176 -0
  236. package/engine/scenes/29_seurat.js +104 -0
  237. package/engine/scenes/30_matisse.js +151 -0
  238. package/engine/scenes/31_haring.js +109 -0
  239. package/engine/scenes/32_rembrandt.js +217 -0
  240. package/engine/scenes/33_rubberhose.js +219 -0
  241. package/engine/scenes/34_shadowpuppet.js +195 -0
  242. package/engine/scenes/35_shinkai.js +197 -0
  243. package/engine/scenes/36_picasso_blue.js +123 -0
  244. package/engine/scenes/38_giotto.js +133 -0
  245. package/engine/scenes/39_piero.js +53 -0
  246. package/engine/scenes/40_raphael.js +96 -0
  247. package/engine/scenes/41_michelangelo.js +112 -0
  248. package/engine/scenes/42_guardi.js +70 -0
  249. package/engine/scenes/43_lorenzetti.js +145 -0
  250. package/engine/scenes/44_cortona.js +86 -0
  251. package/engine/scenes/45_renoir.js +86 -0
  252. package/engine/scenes/46_pissarro.js +82 -0
  253. package/engine/scenes/47_sisley.js +94 -0
  254. package/engine/scenes/48_gauguin.js +107 -0
  255. package/engine/scenes/49_chagall.js +60 -0
  256. package/engine/scenes/50_ma_yuan.js +131 -0
  257. package/engine/scenes/51_persian_miniature.js +149 -0
  258. package/engine/scenes/52_south_park.js +105 -0
  259. package/engine/scenes/53_pixar.js +60 -0
  260. package/engine/scenes/54_ghibli_lab.js +68 -0
  261. package/engine/scenes/55_dunhuang.js +161 -0
  262. package/engine/scenes/index.js +28 -0
  263. package/engine/spec_assets.py +76 -0
  264. package/engine/transitions.js +929 -0
  265. package/engine//346/227/266/344/273/243/344/275/234/350/200/205/350/247/204/350/214/203.md +49 -0
  266. package/lib/host.mjs +754 -0
  267. package/lib/render-bridge.mjs +543 -0
  268. package/lib/render-cli.mjs +98 -0
  269. package/lib/storyboard-lint.mjs +54 -0
  270. package/manifest.json +52 -0
  271. package/package.json +8 -4
  272. package/runtime/package.json +8 -0
  273. package/skills/biuart/SKILL.md +154 -0
  274. package/skills/biuart/references/01-/346/213/206/350/247/243.md +55 -0
  275. package/skills/biuart/references/02-/346/234/272/345/210/266.md +41 -0
  276. package/skills/biuart/references/03-/344/270/200/345/270/247/345/205/210/350/241/214/345/233/233/346/235/241/350/267/257/347/272/277.md +39 -0
  277. package/skills/biuart/references/04-/347/272/257/344/273/243/347/240/201/347/273/230/345/210/266.md +92 -0
  278. package/skills/biuart/references/05-/350/212/202/345/245/217/344/270/216/351/205/215/344/271/220.md +19 -0
  279. package/skills/biuart/references/06-/345/217/243/346/222/255/351/251/261/345/212/250/347/232/204/350/211/272/346/234/257/347/237/255/347/211/207.md +31 -0
  280. package/skills/biuart/references/07-/346/255/243/351/235/242/347/273/217/351/252/214.md +196 -0
  281. package/skills/biuart/references/08-/351/243/216/346/240/274/344/275/234/350/200/205/350/247/204/350/214/203.md +61 -0
  282. package/skills/biuart/references/09-/350/247/206/351/242/221/345/212/250/347/224/273/350/257/255/346/263/225.md +382 -0
  283. package/skills/biuart/references/10-/350/247/222/350/211/262.md +34 -0
  284. package/skills/biuart/references/11-/351/225/277/345/215/267/347/251/277/350/266/212/347/211/207.md +43 -0
  285. package/skills/biuart/references/12-/345/217/243/346/222/255/346/225/264/347/211/207/344/270/216/347/273/217/351/252/214/345/233/236/346/265/201.md +25 -0
  286. package/skills/biuart/references/13-/345/217/243/346/222/255/344/270/216/350/257/255/351/237/263/345/244/215/345/210/273.md +7 -0
  287. package/skills/biuart/references/15-/350/267/250/345/234/272/346/231/257/351/243/216/346/240/274/346/217/220/347/202/274.md +48 -0
  288. package/skills/biuart/references/16-/351/243/216/346/240/274/346/274/224/347/244/27295/345/210/206/345/215/217/350/256/256.md +103 -0
  289. package/skills/biuart/references/about-author.md +99 -0
  290. package/skills/biuart/references/capabilities.md +91 -0
  291. package/skills/biuart/references/compatibility.md +34 -0
  292. package/skills/biuart/references/images.md +97 -0
  293. package/skills/biuart/references/voice-model-review.md +18 -0
  294. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t1_3b1b.md +122 -0
  295. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t2_keynote_ui.md +117 -0
  296. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t3_finance_chart.md +116 -0
  297. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y1_kurzgesagt.md +193 -0
  298. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y2_vox.md +195 -0
  299. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y3_whiteboard.md +190 -0
  300. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y4_storytime.md +166 -0
  301. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y5_kinetic_type.md +158 -0
  302. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y6_presenter_explainer.md +24 -0
  303. package/skills/biuart/references//345/217/243/346/222/255API.md +125 -0
  304. package/skills/biuart/references//350/211/262/346/235/277.md +38 -0
  305. package/skills/biuart/references//350/247/222/350/211/262/347/262/276/347/273/206/345/272/246.md +61 -0
  306. package/skills/biuart/references//351/225/234/345/244/264/350/241/250/346/250/241/346/235/277.md +80 -0
  307. package/skills/biuart/references//351/225/234/345/244/264/350/257/255/350/250/200.md +73 -0
  308. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/01_cave.md +46 -0
  309. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/02_egypt.md +64 -0
  310. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/03_greek.md +50 -0
  311. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/04_roman.md +99 -0
  312. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/05_gothic.md +32 -0
  313. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/06_renaissance.md +41 -0
  314. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/08_impressionism.md +34 -0
  315. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/09_postimp.md +17 -0
  316. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/10_nouveau.md +31 -0
  317. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/11_cubism.md +30 -0
  318. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/12_bauhaus.md +30 -0
  319. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/13_pop.md +29 -0
  320. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/14_8bit.md +27 -0
  321. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/15_raytrace.md +32 -0
  322. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/16_2026.md +55 -0
  323. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/17_ink.md +50 -0
  324. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/18_klimt.md +37 -0
  325. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/19_munch.md +42 -0
  326. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/20_dunhuang.md +49 -0
  327. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/21_kusama.md +32 -0
  328. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/22_constructivism.md +35 -0
  329. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/23_dali.md +29 -0
  330. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/24_hopper.md +30 -0
  331. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/25_ghibli.md +55 -0
  332. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/26_vaporwave.md +27 -0
  333. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/27_kirby.md +30 -0
  334. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/28_monet.md +39 -0
  335. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/29_seurat.md +31 -0
  336. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/30_matisse.md +26 -0
  337. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/31_haring.md +22 -0
  338. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/32_rembrandt.md +29 -0
  339. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/33_rubberhose.md +31 -0
  340. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/34_shadowpuppet.md +40 -0
  341. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/35_shinkai.md +34 -0
  342. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/36_picasso_blue.md +25 -0
  343. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/37_rick_morty.md +48 -0
  344. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/38_giotto.md +67 -0
  345. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/39_piero.md +59 -0
  346. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/40_raphael.md +65 -0
  347. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/41_michelangelo.md +65 -0
  348. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/42_guardi.md +65 -0
  349. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/43_lorenzetti.md +69 -0
  350. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/44_cortona.md +66 -0
  351. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/45_renoir.md +66 -0
  352. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/46_pissarro.md +66 -0
  353. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/47_sisley.md +67 -0
  354. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/48_gauguin.md +66 -0
  355. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/49_chagall.md +59 -0
  356. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/50_ma_yuan.md +68 -0
  357. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/51_persian_miniature.md +66 -0
  358. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/52_south_park.md +65 -0
  359. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/53_pixar.md +61 -0
  360. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/55_dunhuang.md +41 -0
  361. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/INDEX.md +89 -0
  362. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/350/275/254/345/234/272_/350/277/201/347/247/273/346/265/213/350/257/225.md +53 -0
  363. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/207/215/345/201/232/350/277/233/345/272/246.md +37 -0
  364. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/237/263/350/275/250_/350/211/272/346/234/257/345/217/262/351/200/237/351/200/232.md +135 -0
  365. package/studio/app.js +380 -0
  366. package/studio/assert-panel-permit.mjs +67 -0
  367. package/studio/composer.css +465 -0
  368. package/studio/composer.js +480 -0
  369. package/studio/index.html +97 -0
  370. package/studio/optimize-prompt.mjs +84 -0
  371. package/studio/optimize-prompt.test.mjs +47 -0
  372. package/studio/recipes.mjs +87 -0
  373. package/studio/select-menu.js +450 -0
  374. package/studio/styles.css +71 -0
  375. package/studio/to-english-prompt.mjs +149 -0
  376. package/studio/to-english-prompt.test.mjs +72 -0
  377. package/README.md +0 -3
@@ -0,0 +1,161 @@
1
+ // 敦煌壁画(北朝:257 窟鹿王本生 · 285 窟西魏飞天):土红地上的白色走兽和锯齿山,奶白地上的青绿风叶和长飘带飞天;
2
+ // 不是「中国风插画」——它是干燥的泥壁画:矿物色粉上墙、铅白氧化发暗、墙皮龟裂起甲、盐碱泛白,线是红褐的铁线/兰叶描。
3
+ // 题材(新构图):上栏一位飞天托莲横空而过,风叶和散花从身边向后翻;下栏一只白鹿领着一只小鹿穿过三角形的山奔跑。
4
+ // 原作锚定(fetch_reference.py:257 窟鹿王本生、285 窟西魏飞天、428 窟北周飞天、盛唐 112 窟):
5
+ // 土红地 #92342d/#9b4745;白鹿 #efe6d2;青绿 #3e919c/#577f7a;奶白地 #e8dcc5;赭 #b89d7d;深褐线 #3a1812;
6
+ // 山=一排排锯齿三角、各自涂青/白/黑/绿;动物=平涂白身+极少内部线;飞天=水平横飞、下身化成一条飘到身后的长裙;
7
+ // 上下两栏之间有一道多色横带;窟顶下沿垂幔三角纹;整面墙龟裂、起甲、泛盐。
8
+ // 管线:墙底(泥+色差,缓存)→ 上栏奶白地+下栏土红地+多色分隔带+顶部菱格带+底部垂幔 → 锯齿山(缓存)
9
+ // → 动态:鹿群奔跑、风叶后掠、飞天、散花、尘埃 → 龟裂/起甲/盐碱覆盖层(盖在画上面,裂缝穿过人和鹿)→ 泥壁颗粒相乘。
10
+ SCENES['55_dunhuang'] = (() => {
11
+ const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, TAU = Math.PI * 2;
12
+ const C = { sky: '#e6d9bf', red: '#92342d', red2: '#7c2a24', white: '#efe6d2', teal: '#3e919c', teal2: '#1f5f6a', green: '#577f7a', ochre: '#c58a3e', ink: '#3a1812', line: '#6b2a1c', skin: '#e6cfae', flush: '#df8d74', dark: '#241512', lapis: '#35597c' };
13
+ const TOP = 64, SPLIT = 478, GROUND = 930, BOT = 986;
14
+ let base, over;
15
+ const sh = (h, k) => { const q = P.hex(h); return `rgb(${Math.round(q[0] * k)},${Math.round(q[1] * k)},${Math.round(q[2] * k)})`; };
16
+ const poly = (g, pts) => { g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.closePath(); };
17
+
18
+ // ---- 静态:墙与两栏 ----
19
+ function band(g) { // 顶部:菱格带(青/白/红/黑)
20
+ g.fillStyle = C.dark; g.fillRect(0, 0, W, TOP);
21
+ const cols = [C.teal, C.white, C.red, C.ochre];
22
+ for (let i = 0, x = 0; x < W + 60; i++, x += 56) { g.fillStyle = cols[i % 4]; poly(g, [[x, TOP / 2], [x + 28, 8], [x + 56, TOP / 2], [x + 28, TOP - 8]]); g.fill(); g.strokeStyle = C.dark; g.lineWidth = 2; g.stroke(); g.fillStyle = cols[(i + 2) % 4]; g.beginPath(); g.arc(x + 28, TOP / 2, 5, 0, TAU); g.fill(); }
23
+ }
24
+ function divider(g) { // 上下栏之间的多色横带+联珠
25
+ const rows = [[C.dark, 8], [C.white, 14], [C.teal, 20], [C.ochre, 14], [C.white, 12], [C.dark, 8]]; let y = SPLIT;
26
+ rows.forEach(([col, h]) => { g.fillStyle = col; g.fillRect(0, y, W, h); y += h; });
27
+ g.fillStyle = C.white; g.strokeStyle = C.dark; g.lineWidth = 1.6; for (let x = 14; x < W; x += 28) { g.beginPath(); g.arc(x, SPLIT + 8 + 14 + 10, 5, 0, TAU); g.fill(); g.stroke(); }
28
+ }
29
+ function drapery(g) { // 底部:垂幔三角纹,每片垂一粒珠
30
+ g.fillStyle = C.dark; g.fillRect(0, BOT, W, H - BOT);
31
+ const cols = [C.teal, C.ochre, C.white, C.red2, C.green];
32
+ for (let i = 0, x = -10; x < W + 80; i++, x += 84) { g.fillStyle = cols[i % 5]; poly(g, [[x, BOT], [x + 84, BOT], [x + 42, BOT + 70]]); g.fill(); g.strokeStyle = C.dark; g.lineWidth = 2.4; g.stroke();
33
+ g.fillStyle = cols[(i + 2) % 5]; poly(g, [[x + 20, BOT], [x + 64, BOT], [x + 42, BOT + 38]]); g.fill(); g.fillStyle = C.white; g.beginPath(); g.arc(x + 42, BOT + 82, 6, 0, TAU); g.fill(); g.stroke(); }
34
+ }
35
+ function mountains(g) { // 257 窟式的山:窄高、倾斜、参差的三角碎片,一排排叠在红地上,红地大面积露出来;每片一色,一侧压暗,内部几道斜向地层线
36
+ const r = rng(257), rows = [[790, 150, 250, [C.teal2, C.dark, C.green, C.lapis], 1.18], [905, 90, 160, [C.white, C.teal, C.ochre, C.white], 1.0]];
37
+ rows.forEach(([bs, h0, h1, pal, step], ri) => { let x = -60 + ri * 90; while (x < W + 100) { const w = 130 + r() * 150, hh = h0 + r() * (h1 - h0), c0 = pal[Math.floor(r() * pal.length)], ap = x + w * (0.5 + (r() - .5) * 1.5);
38
+ g.fillStyle = c0; poly(g, [[x, bs], [ap, bs - hh], [x + w, bs]]); g.fill();
39
+ g.fillStyle = sh(c0, 0.6); poly(g, [[ap, bs - hh], [x + w, bs], [ap - (ap - x) * 0.05, bs - hh * 0.0]]); g.fill();
40
+ g.save(); poly(g, [[x, bs], [ap, bs - hh], [x + w, bs]]); g.clip(); g.strokeStyle = 'rgba(58,24,18,.5)'; g.lineWidth = 2; for (let k = 1; k < 6; k++) { const yy = bs - hh * k / 6.4; g.beginPath(); g.moveTo(x - 10, yy + 26); g.lineTo(x + w + 10, yy - 14); g.stroke(); } g.restore();
41
+ g.strokeStyle = C.ink; g.lineWidth = 3; g.lineJoin = 'round'; poly(g, [[x, bs], [ap, bs - hh], [x + w, bs]]); g.stroke();
42
+ x += w * (step + r() * 0.25); } });
43
+ }
44
+ function ground(g) { // 山脚地面:赭土+青绿三叶草+碎石点
45
+ g.fillStyle = C.ochre; g.fillRect(0, GROUND, W, BOT - GROUND); g.fillStyle = C.ink; g.fillRect(0, GROUND - 3, W, 5);
46
+ const r = rng(11); for (let i = 0; i < 70; i++) { const x = r() * W, y = GROUND + 10 + r() * (BOT - GROUND - 20); g.strokeStyle = r() < 0.6 ? C.teal2 : C.ink; g.lineWidth = 2.2; g.beginPath(); g.moveTo(x, y); g.lineTo(x - 7, y - 12); g.moveTo(x, y); g.lineTo(x + 1, y - 15); g.moveTo(x, y); g.lineTo(x + 8, y - 11); g.stroke(); }
47
+ }
48
+ function build() {
49
+ base = P.canvas(); const g = base.getContext('2d');
50
+ g.drawImage(P.texture('dh_sky', C.sky, { scale: 0.003, amt: 22, grain: 8, seed: 4 }), 0, TOP, W, SPLIT - TOP, 0, TOP, W, SPLIT - TOP);
51
+ g.drawImage(P.texture('dh_red', C.red, { scale: 0.0034, amt: 26, grain: 9, dark: '#5a1d1a', seed: 9 }), 0, SPLIT, W, BOT - SPLIT, 0, SPLIT, W, BOT - SPLIT);
52
+ g.save(); g.beginPath(); g.rect(0, SPLIT + 76, W, GROUND - SPLIT - 76); g.clip(); mountains(g); g.restore();
53
+ // 红地上撒几只远处的小白兽剪影(257 窟满地走兽的感觉)
54
+ const rs = rng(77); for (let i = 0; i < 7; i++) { const x = 120 + i * 270 + rs() * 80, y = SPLIT + 120 + rs() * 90; g.fillStyle = 'rgba(239,230,210,.92)'; g.strokeStyle = C.ink; g.lineWidth = 2; g.beginPath(); g.ellipse(x, y, 26, 11, 0, 0, TAU); g.fill(); g.stroke(); g.beginPath(); g.ellipse(x + 26, y - 14, 8, 6, 0.3, 0, TAU); g.fill(); g.stroke(); [-14, 14].forEach(dx => { g.beginPath(); g.moveTo(x + dx, y + 8); g.lineTo(x + dx - 3, y + 26); g.stroke(); }); }
55
+ ground(g); band(g); divider(g); drapery(g);
56
+ // 上栏底部一圈淡淡的熏黑(油灯与岁月)
57
+ const gr = g.createLinearGradient(0, TOP, 0, SPLIT); gr.addColorStop(0, 'rgba(60,34,20,.26)'); gr.addColorStop(0.22, 'rgba(60,34,20,0)'); gr.addColorStop(1, 'rgba(60,34,20,.12)'); g.fillStyle = gr; g.fillRect(0, TOP, W, SPLIT - TOP);
58
+ // 覆盖层:龟裂、起甲露底、盐碱
59
+ over = P.canvas(); const o = over.getContext('2d'), r = rng(1999);
60
+ for (let k = 0; k < 18; k++) { let x = r() * W, y = TOP + r() * (BOT - TOP), a = r() * TAU; o.strokeStyle = 'rgba(30,14,10,.5)'; o.lineWidth = 1.2 + r() * 1.2; o.lineJoin = 'round'; o.beginPath(); o.moveTo(x, y);
61
+ const n = 10 + Math.floor(r() * 22); for (let i = 0; i < n; i++) { a += (r() - .5) * 0.9; x += Math.cos(a) * (12 + r() * 20); y += Math.sin(a) * (12 + r() * 20); o.lineTo(x, y); if (r() < 0.12) { o.stroke(); o.beginPath(); o.moveTo(x, y); a += (r() < 0.5 ? 1 : -1) * 0.9; } } o.stroke(); }
62
+ for (let k = 0; k < 22; k++) { const cx = r() * W, cy = TOP + r() * (BOT - TOP), n = 9 + Math.floor(r() * 6), R = 6 + r() * 22; o.fillStyle = r() < 0.6 ? 'rgba(206,176,136,.34)' : 'rgba(96,60,40,.26)'; o.beginPath();
63
+ for (let i = 0; i < n; i++) { const aa = i / n * TAU, rr = R * (0.45 + r() * 0.75); o.lineTo(cx + Math.cos(aa) * rr, cy + Math.sin(aa) * rr * 0.7); } o.closePath(); o.fill(); o.strokeStyle = 'rgba(40,20,12,.3)'; o.lineWidth = 1.4; o.stroke(); }
64
+ for (let k = 0; k < 260; k++) { o.fillStyle = `rgba(245,238,225,${0.1 + r() * 0.3})`; o.beginPath(); o.ellipse(r() * W, TOP + r() * (BOT - TOP), 1.5 + r() * 4.5, 1 + r() * 2.5, r() * 3, 0, TAU); o.fill(); }
65
+ }
66
+
67
+ // ---- 动态件 ----
68
+ const lineStroke = (c, w = 2.6) => { c.strokeStyle = C.line; c.lineWidth = w; c.lineJoin = 'round'; c.lineCap = 'round'; };
69
+ function ribbon(c, pts, w0, w1, fill, edge = C.line, hi = null) { // 变宽飘带:两侧沿法线展开,铁线描边
70
+ const L = [], R = [];
71
+ pts.forEach((p, i) => { const a = pts[Math.max(0, i - 1)], b = pts[Math.min(pts.length - 1, i + 1)], dx = b[0] - a[0], dy = b[1] - a[1], d = Math.hypot(dx, dy) || 1, nx = -dy / d, ny = dx / d, w = lerp(w0, w1, i / (pts.length - 1)) / 2; L.push([p[0] + nx * w, p[1] + ny * w]); R.push([p[0] - nx * w, p[1] - ny * w]); });
72
+ c.beginPath(); L.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); R.reverse().forEach(p => c.lineTo(p[0], p[1])); c.closePath(); c.fillStyle = fill; c.fill(); lineStroke(c, 2.2); c.strokeStyle = edge; c.stroke();
73
+ if (hi) { c.strokeStyle = hi; c.lineWidth = 2; c.beginPath(); pts.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); c.stroke(); }
74
+ }
75
+ function leaf(c, x, y, ang, len, col, col2, k) { // 西魏「风叶」:一撮向后翻卷的逗号形青绿叶,三尖
76
+ c.save(); c.translate(x, y); c.rotate(ang); c.scale(1, k);
77
+ c.beginPath(); c.moveTo(0, 0); c.bezierCurveTo(len * 0.25, -len * 0.5, len * 0.8, -len * 0.45, len, -len * 0.12); c.bezierCurveTo(len * 0.7, -len * 0.18, len * 0.5, -len * 0.1, len * 0.42, len * 0.06); c.bezierCurveTo(len * 0.3, len * 0.18, len * 0.1, len * 0.14, 0, 0); c.closePath();
78
+ c.fillStyle = col; c.fill(); lineStroke(c, 2); c.strokeStyle = col2; c.stroke();
79
+ c.strokeStyle = 'rgba(255,250,235,.8)'; c.lineWidth = 2.2; c.beginPath(); c.moveTo(len * 0.1, -len * 0.06); c.bezierCurveTo(len * 0.3, -len * 0.3, len * 0.6, -len * 0.3, len * 0.84, -len * 0.14); c.stroke();
80
+ c.restore();
81
+ }
82
+ function bloom(c, x, y, s, rot, col) { // 散花:四瓣白花,红心
83
+ c.save(); c.translate(x, y); c.rotate(rot); c.scale(s, s); c.fillStyle = col; lineStroke(c, 1.8 / s);
84
+ for (let i = 0; i < 4; i++) { c.save(); c.rotate(i * TAU / 4); c.beginPath(); c.ellipse(0, -9, 5.5, 9, 0, 0, TAU); c.fill(); c.stroke(); c.restore(); }
85
+ c.fillStyle = C.red; c.beginPath(); c.arc(0, 0, 3.2, 0, TAU); c.fill(); c.restore();
86
+ }
87
+ function lotus(c, x, y, s) {
88
+ c.save(); c.translate(x, y); c.scale(s, s); lineStroke(c, 2.2 / s);
89
+ [[-1.0, '#f1d9d0'], [1.0, '#f1d9d0'], [-0.5, '#f6e4dc'], [0.5, '#f6e4dc'], [0, '#fbeee8']].forEach(([a, col]) => { c.save(); c.rotate(a * 0.9 - 0.2); c.beginPath(); c.moveTo(0, 0); c.bezierCurveTo(-11, -14, -9, -32, 0, -40); c.bezierCurveTo(9, -32, 11, -14, 0, 0); c.fillStyle = col; c.fill(); c.stroke(); c.restore(); });
90
+ c.fillStyle = C.ochre; c.beginPath(); c.arc(0, -4, 5, 0, TAU); c.fill(); c.restore();
91
+ }
92
+ // 飞天(西魏式):水平横飞,头在前,披帛后扬,躯干与长裙是一条连贯的 S 形;脸小、眉眼细长、颊上一点红晕;红褐铁线勾边,肤色铅丹淡赭
93
+ function apsara(c, x, y, s, flip, t, ph = 0) {
94
+ c.save(); c.translate(x, y + Math.sin(t * 1.15 + ph) * 12); c.rotate(-0.07 + Math.sin(t * 0.9 + ph) * 0.03); c.scale(s * flip, s);
95
+ const w = (i, k) => Math.sin(t * 2.6 - i * 0.55 + ph + k), SK = '#eab894';
96
+ const sa = []; for (let i = 0; i <= 18; i++) sa.push([30 - i * 22, -30 - Math.sin(i * 0.22) * 34 + w(i, 0) * (3 + i * 1.5)]); ribbon(c, sa, 17, 3, C.teal, C.teal2, 'rgba(255,255,240,.75)');
97
+ const sb = []; for (let i = 0; i <= 12; i++) sb.push([24 - i * 20, 12 + w(i, 2) * (3 + i * 1.2) + i * 5]); ribbon(c, sb, 14, 3, C.ochre, C.line);
98
+ const sk = [[-16, 2]]; for (let i = 1; i <= 15; i++) sk.push([-16 - i * 23, 4 + Math.sin(i * 0.42 + t * 2.2 + ph) * (4 + i * 1.8) - i * 2.4]); // 长裙:从腰接出,越飘越尖
99
+ ribbon(c, sk, 52, 4, '#79b2a6', C.line, 'rgba(255,255,240,.5)'); c.strokeStyle = 'rgba(31,95,106,.7)'; c.lineWidth = 2; for (let k = -1; k <= 1; k += 2) { c.beginPath(); sk.slice(1, 13).forEach((p, i) => { const yy = p[1] + k * (12 - i * 0.8); i ? c.lineTo(p[0], yy) : c.moveTo(p[0], yy); }); c.stroke(); }
100
+ c.fillStyle = 'rgba(240,226,190,.8)'; for (let i = 2; i < 12; i += 2) { c.beginPath(); c.arc(sk[i][0], sk[i][1] - 2, 3.2, 0, TAU); c.fill(); }
101
+ ribbon(c, [[52, -28], [26, -18], [2, -6], [-22, 2]], 36, 30, SK, C.line); // 躯干:由肩向腰收
102
+ c.fillStyle = C.red; ribbon(c, [[-6, -14], [-8, -2], [-12, 10]], 10, 10, C.red, C.line); // 腰带
103
+ c.strokeStyle = C.teal; c.lineWidth = 4; c.beginPath(); c.arc(56, -34, 16, 0.4, 2.2); c.stroke(); c.fillStyle = C.ochre; c.beginPath(); c.arc(56, -17, 4, 0, TAU); c.fill(); lineStroke(c, 1.6); c.stroke(); // 璎珞
104
+ ribbon(c, [[40, -34], [24, -58], [0, -72]], 14, 10, SK, C.line); c.fillStyle = SK; c.beginPath(); c.arc(-6, -74, 7.5, 0, TAU); c.fill(); lineStroke(c, 2.2); c.stroke();
105
+ ribbon(c, [[52, -28], [84, -2 + w(0, 1) * 3], [112, -20]], 15, 11, SK, C.line); c.fillStyle = SK; c.beginPath(); c.arc(116, -22, 8, 0, TAU); c.fill(); lineStroke(c, 2.2); c.stroke(); lotus(c, 120, -26, 0.95);
106
+ const hr = []; for (let i = 0; i <= 8; i++) hr.push([62 - i * 12, -78 + i * 2.5 + w(i, 4) * (2 + i)]); ribbon(c, hr, 18, 3, C.dark, C.line); // 发尾后扬
107
+ c.fillStyle = SK; c.beginPath(); c.ellipse(80, -54, 20, 23, 0.12, 0, TAU); c.fill(); lineStroke(c, 2.6); c.stroke();
108
+ c.fillStyle = C.dark; c.beginPath(); c.moveTo(60, -58); c.bezierCurveTo(60, -86, 100, -88, 100, -60); c.bezierCurveTo(92, -72, 72, -72, 60, -58); c.fill(); c.beginPath(); c.arc(70, -86, 11, 0, TAU); c.fill(); c.strokeStyle = C.line; c.stroke();
109
+ c.fillStyle = C.ochre; [[78, -82], [86, -78], [92, -72]].forEach(([px, py]) => { c.beginPath(); c.arc(px, py, 2.8, 0, TAU); c.fill(); }); c.fillStyle = C.teal; c.beginPath(); c.arc(70, -97, 3.6, 0, TAU); c.fill();
110
+ c.fillStyle = 'rgba(223,120,96,.8)'; c.beginPath(); c.ellipse(92, -45, 7, 5.5, 0, 0, TAU); c.fill(); c.beginPath(); c.ellipse(68, -45, 6, 5, 0, 0, TAU); c.fill();
111
+ c.strokeStyle = C.line; c.lineWidth = 2.2; c.beginPath(); c.moveTo(80, -64); c.quadraticCurveTo(90, -69, 99, -62); c.moveTo(62, -63); c.quadraticCurveTo(68, -68, 77, -64); c.stroke();
112
+ c.lineWidth = 2.6; c.beginPath(); c.moveTo(83, -57); c.quadraticCurveTo(91, -60, 98, -56); c.moveTo(62, -57); c.quadraticCurveTo(69, -60, 76, -57); c.stroke(); c.fillStyle = C.dark; c.beginPath(); c.arc(91, -57, 2, 0, TAU); c.arc(69, -57, 1.8, 0, TAU); c.fill();
113
+ c.strokeStyle = C.line; c.lineWidth = 1.8; c.beginPath(); c.moveTo(82, -54); c.quadraticCurveTo(86, -46, 82, -43); c.stroke(); c.fillStyle = C.red; c.beginPath(); c.ellipse(80, -36, 4.6, 2.3, 0, 0, TAU); c.fill();
114
+ c.restore();
115
+ }
116
+ // 白鹿(鹿王本生式):侧身,身子是一整块白,只有轮廓与脊背一线赭点;奔跑用「腾空伸展—收拢」的两拍,前后腿成对错开
117
+ function deer(c, x, y, s, t, ph, col = C.white) {
118
+ const f = t * 5.2 + ph, g = Math.sin(f), jump = Math.max(0, Math.sin(f + 1.1)) * 20;
119
+ c.save(); c.translate(x, y - jump * s); c.rotate(-g * 0.03); c.scale(s, s); c.lineJoin = 'round'; c.lineCap = 'round';
120
+ const leg = (hx, hy, a, front) => { // a∈[-1,1]:前摆为正;前腿膝向前弯、后腿膝向后弯
121
+ const th = a * 0.95, kn = [hx + Math.sin(th) * 52, hy + Math.cos(th) * 52], th2 = th + (front ? -1 : 1) * (0.55 + 0.45 * (1 - a * (front ? 1 : -1)) * 0.5), fo = [kn[0] + Math.sin(th2 + (front ? 0.25 : -0.1)) * 56, kn[1] + Math.cos(th2) * 56];
122
+ ribbon(c, [[hx, hy], [kn[0], kn[1]], [fo[0], fo[1]]], 22, 8, col, C.line); c.fillStyle = C.ochre; c.beginPath(); c.ellipse(fo[0] + 3, fo[1] - 2, 8, 4.5, 0.2, 0, TAU); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke(); };
123
+ leg(-56, -78, -g * 0.9, false); leg(-40, -76, g * 0.9 + 0.2, false); leg(50, -80, g * 0.9, true); leg(64, -78, -g * 0.9 + 0.2, true);
124
+ c.beginPath(); c.moveTo(-90, -92); c.bezierCurveTo(-70, -132, 30, -138, 68, -114); c.bezierCurveTo(90, -126, 100, -150, 112, -172); c.bezierCurveTo(124, -190, 150, -190, 160, -174); c.bezierCurveTo(150, -158, 130, -156, 120, -146); c.bezierCurveTo(116, -118, 100, -90, 70, -76); c.bezierCurveTo(28, -56, -30, -56, -64, -66); c.bezierCurveTo(-84, -70, -98, -82, -90, -92); c.closePath();
125
+ c.fillStyle = col; c.fill(); lineStroke(c, 3.4); c.stroke();
126
+ c.fillStyle = C.ochre; for (let i = 0; i < 12; i++) { const q = i / 11, px = lerp(-70, 66, q), py = lerp(-100, -114, Math.sin(q * 2.2)) - 4; c.beginPath(); c.arc(px, py + (q > 0.8 ? (q - 0.8) * 40 : 0), 3.6, 0, TAU); c.fill(); }
127
+ c.strokeStyle = 'rgba(58,24,18,.55)'; c.lineWidth = 1.8; [[-22, -96, -8, -78], [10, -98, 22, -80], [-52, -90, -42, -74]].forEach(([a1, b1, a2, b2]) => { c.beginPath(); c.moveTo(a1, b1); c.quadraticCurveTo((a1 + a2) / 2 + 6, (b1 + b2) / 2, a2, b2); c.stroke(); });
128
+ c.fillStyle = col; lineStroke(c, 3); c.beginPath(); c.moveTo(110, -172); c.quadraticCurveTo(100, -204, 124, -214); c.quadraticCurveTo(124, -192, 124, -172); c.closePath(); c.fill(); c.stroke(); c.fillStyle = C.teal; c.beginPath(); c.moveTo(114, -184); c.quadraticCurveTo(112, -200, 121, -206); c.quadraticCurveTo(120, -194, 120, -184); c.fill();
129
+ c.strokeStyle = C.ochre; c.lineWidth = 5; c.beginPath(); c.moveTo(136, -186); c.quadraticCurveTo(148, -222, 176, -230); c.moveTo(152, -212); c.lineTo(150, -240); c.moveTo(160, -222); c.lineTo(184, -220); c.stroke();
130
+ c.fillStyle = C.ink; c.beginPath(); c.arc(138, -172, 3.4, 0, TAU); c.fill(); c.strokeStyle = C.red; c.lineWidth = 1.8; c.beginPath(); c.arc(138, -172, 6, 0, TAU); c.stroke(); c.fillStyle = C.ink; c.beginPath(); c.ellipse(158, -173, 3, 2.2, 0, 0, TAU); c.fill();
131
+ c.fillStyle = col; c.beginPath(); c.moveTo(-88, -94); c.quadraticCurveTo(-116, -108, -112, -80); c.quadraticCurveTo(-100, -86, -88, -84); c.fill(); lineStroke(c, 2.6); c.stroke();
132
+ c.restore();
133
+ }
134
+
135
+ function draw(c, lt) {
136
+ if (!base) build();
137
+ const t = clamp(lt, 0, 6);
138
+ c.drawImage(base, 0, 0);
139
+ // 上栏:风叶(远处小的在前面,带浅色;大的青绿深色)整体向后(左)翻,速度分层
140
+ const rl = rng(285);
141
+ for (let i = 0; i < 26; i++) {
142
+ const sp = 70 + rl() * 120, y0 = TOP + 40 + rl() * (SPLIT - TOP - 100), ln = 60 + rl() * 80, x0 = rl() * (W + 300), k = 0.8 + rl() * 0.4, rot = (rl() - .5) * 0.7, col = rl() < 0.62 ? C.teal : rl() < 0.8 ? C.green : '#c9a23a';
143
+ const x = ((x0 - t * sp) % (W + 300) + (W + 300)) % (W + 300) - 150, y = y0 + Math.sin(t * 1.3 + i) * 10;
144
+ leaf(c, x, y, Math.PI + rot + Math.sin(t * 1.1 + i) * 0.1, ln, col, i % 5 === 0 ? C.line : C.teal2, k);
145
+ }
146
+ apsara(c, 1060, 262, 1.72, 1, t, 0); // 主飞天
147
+ apsara(c, 560, 150, 0.82, -1, t, 2.2); // 远处的小飞天,反向飞
148
+ const rb = rng(31); for (let i = 0; i < 16; i++) { const sp = 60 + rb() * 90, x = (((rb() * (W + 200)) - t * sp * 0.9) % (W + 200) + W + 200) % (W + 200) - 100, y = TOP + 30 + ((rb() * (SPLIT - TOP - 80)) + t * 28 * (0.5 + rb())) % (SPLIT - TOP - 60); bloom(c, x, y, 0.8 + rb() * 0.7, t * (0.6 + rb()) + i, C.white); }
149
+ // 下栏:白鹿领队,小鹿稍后;前后各一个相位
150
+ const run = (t / 6), x1 = 360 + run * 1000, x2 = 160 + run * 940;
151
+ deer(c, x2, GROUND + 22, 0.95, t, 1.6, '#e8dcc2'); deer(c, x1, GROUND + 32, 1.5, t, 0);
152
+ // 奔跑扬起的尘(鹿蹄后面)
153
+ for (let i = 0; i < 12; i++) { const q = ((t * 1.6 + i / 12) % 1), px = x1 - 60 - q * 150 - i * 4, py = GROUND + 30 - q * 40 - (i % 3) * 6; c.fillStyle = `rgba(214,190,150,${(1 - q) * 0.55})`; c.beginPath(); c.arc(px, py, 6 + q * 18, 0, TAU); c.fill(); }
154
+ // 覆盖层+泥壁相乘
155
+ c.drawImage(over, 0, 0);
156
+ c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.42; c.drawImage(P.texture('dh_mud', '#d9c6a6', { scale: 0.006, amt: 30, grain: 14, dark: '#9a7a54', seed: 21 }), 0, 0); c.restore();
157
+ // 漂在光里的尘
158
+ const rd = rng(5); for (let i = 0; i < 40; i++) { const x = (rd() * W + t * (6 + rd() * 10)) % W, y = TOP + rd() * (BOT - TOP) + Math.sin(t * 0.6 + i) * 6; c.fillStyle = `rgba(255,244,214,${0.1 + rd() * 0.14})`; c.beginPath(); c.arc(x, y, 1 + rd() * 1.8, 0, TAU); c.fill(); }
159
+ }
160
+ return { draw };
161
+ })();
@@ -0,0 +1,28 @@
1
+ // 按 id 加载 scenes/<id>.js;按 transition.type 自动加载 transitions/<type>.js(同步 XHR,保证 engine 启动前全部注册)。
2
+ // 任何一个场景缺文件、语法错、没注册,或转场名在 TRANSITIONS 和 transitions/ 里都找不到 → 记进 window.__bootErrors,
3
+ // 引擎拒绝启动,render.py/qa.py 直接报错退出。
4
+ // (迁移测试 B 踩过:以前缺场景会画「待实现」占位照常渲,整段 1.2 秒灰底,靠帧差 0.0% 才发现;转场名写错会静默退成交叉淡化。)
5
+ (() => {
6
+ window.SCENES = window.SCENES || {}; window.__bootErrors = [];
7
+ const load = (url) => { const x = new XMLHttpRequest(); x.open('GET', url, false); try { x.send(); } catch (err) { return { err: `请求失败 ${err}` }; }
8
+ if (x.status !== 200) return { err: `${url} 不存在 (HTTP ${x.status})` };
9
+ try { (0, eval)(x.responseText + `\n//# sourceURL=${url}`); } catch (err) { return { err: `${url} 执行出错 ${err && err.stack || err}` }; } return {}; };
10
+ // 片子可以先加载自己的共用脚本(window.SCENE_LIBS,按顺序),场景文件目录 window.SCENE_DIR(默认 scenes)。
11
+ // 共用脚本里已经注册了 SCENES[id] 的段落,不再找单独的场景文件(demos/ 的示范片就是这么组织的)。
12
+ for (const f of window.SCENE_LIBS || []) { const r = load(f); if (r.err) __bootErrors.push(`共用脚本 ${r.err}`); }
13
+ const dir = window.SCENE_DIR || 'scenes';
14
+ for (const e of ERAS) {
15
+ if (!e.draw) { // eras 里内联了 draw 的段落不需要文件
16
+ const r = SCENES[e.id] ? {} : load(`${dir}/${e.id}.js`);
17
+ if (r.err) { __bootErrors.push(`${e.id}: ${r.err}`); continue; }
18
+ const sc = SCENES[e.id];
19
+ if (!sc || typeof sc.draw !== 'function') { __bootErrors.push(`${e.id}: 文件里没有 SCENES['${e.id}'] = { draw(c, lt, t) }`); continue; }
20
+ e.draw = sc.draw; if (sc.init) e.init = sc.init;
21
+ }
22
+ const ty = e.transition && e.transition.type;
23
+ if (ty && !TRANSITIONS[ty]) { // 项目自己的转场:放 transitions/<type>.js,往 TRANSITIONS 上挂同名函数
24
+ const r = load(`transitions/${ty}.js`);
25
+ if (r.err || !TRANSITIONS[ty]) __bootErrors.push(`${e.id}: 转场「${ty}」不在 transitions.js 里,也没有 transitions/${ty}.js(${r.err || '文件里没注册 TRANSITIONS.' + ty})`);
26
+ }
27
+ }
28
+ })();
@@ -0,0 +1,76 @@
1
+ """片段 spec 里的本地素材路径:找出来、核存在、改写成本地服务能取的 URL。render.py 用它(qa.py 可以照搬)。
2
+
3
+ 认这些字段(路径相对 spec 文件,也可写绝对路径;data: / http(s): 原样放行):
4
+ cues[].image 一张图(截图、照片、录像的一帧)
5
+ cues[].frames 帧序列:目录(按文件名排序取 .png/.jpg/.jpeg/.webp)或路径数组;配 cues[].fps(默认 30)
6
+ data.image 语法级的一张图(y1 用作中心物的图)
7
+ data.character y4 角色:预设名(neutral / bun / author)原样放行(y4 已停用代码画的角色,启动时会报错要帧库);目录 → 帧库(文件名去扩展名 = 姿势名);
8
+ 对象 {frames: {姿势: 路径} | 目录} 同理
9
+ 文件不存在 → FileNotFoundError(调用方拒绝渲染,不出空白帧)。
10
+ """
11
+ import urllib.parse
12
+ from pathlib import Path
13
+
14
+ FRAME_EXT = ('.png', '.jpg', '.jpeg', '.webp')
15
+ PRESETS = ('neutral', 'bun', 'author')
16
+
17
+
18
+ def _url(f, allowed):
19
+ allowed.add(str(f))
20
+ return '/__file__/' + urllib.parse.quote(str(f))
21
+
22
+
23
+ def _file(v, base, allowed, what):
24
+ if not v or str(v).startswith(('data:', 'http:', 'https:')):
25
+ return v
26
+ f = (base / v).resolve()
27
+ if not f.is_file():
28
+ raise FileNotFoundError(f'spec 里的{what}不存在:{v}(相对 {base})')
29
+ return _url(f, allowed)
30
+
31
+
32
+ def _dir_frames(v, base, what):
33
+ d = (base / v).resolve()
34
+ if not d.is_dir():
35
+ return None
36
+ files = sorted(p for p in d.iterdir() if p.suffix.lower() in FRAME_EXT)
37
+ if not files:
38
+ raise FileNotFoundError(f'spec 里的{what}目录是空的(没有 {"/".join(FRAME_EXT)}):{v}')
39
+ return files
40
+
41
+
42
+ def localize(spec, base, allowed):
43
+ """原地改写 spec。base = spec 所在目录;allowed 收集允许服务的绝对路径。"""
44
+ base = Path(base)
45
+ for q in spec.get('cues', []):
46
+ if q.get('image'):
47
+ q['image'] = _file(q['image'], base, allowed, '图片')
48
+ fr = q.get('frames')
49
+ if isinstance(fr, str):
50
+ files = _dir_frames(fr, base, '帧序列')
51
+ if files is None:
52
+ raise FileNotFoundError(f'spec 里的帧序列目录不存在:{fr}(相对 {base})')
53
+ q['frames'] = [_url(f, allowed) for f in files]
54
+ elif isinstance(fr, list):
55
+ if not fr:
56
+ raise FileNotFoundError('spec 里的 frames 是空数组')
57
+ q['frames'] = [_file(v, base, allowed, '视频帧') for v in fr]
58
+ data = spec.get('data') or {}
59
+ if data.get('image'):
60
+ data['image'] = _file(data['image'], base, allowed, '图片')
61
+ ch = data.get('character')
62
+ if isinstance(ch, str) and ch not in PRESETS:
63
+ files = _dir_frames(ch, base, '角色帧库')
64
+ if files is None:
65
+ raise FileNotFoundError(f'data.character 既不是预设({" / ".join(PRESETS)})也不是帧库目录:{ch}(相对 {base})')
66
+ data['character'] = {'frames': {f.stem: _url(f, allowed) for f in files}}
67
+ elif isinstance(ch, dict):
68
+ fr = ch.get('frames')
69
+ if isinstance(fr, str):
70
+ files = _dir_frames(fr, base, '角色帧库')
71
+ if files is None:
72
+ raise FileNotFoundError(f'data.character.frames 目录不存在:{fr}(相对 {base})')
73
+ ch['frames'] = {f.stem: _url(f, allowed) for f in files}
74
+ elif isinstance(fr, dict):
75
+ ch['frames'] = {k: _file(v, base, allowed, f'角色帧「{k}」') for k, v in fr.items()}
76
+ return spec