@autoaiai/aicut 0.1.7 → 0.1.13

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 (306) hide show
  1. package/MANUAL.md +94 -84
  2. package/package.json +1 -1
  3. package/product.json +2 -1
  4. package/skills/aicut/SKILL.md +4 -4
  5. package/skills/aicut/references/commands.md +15 -15
  6. package/skills/aicut/references/film-format.md +1 -1
  7. package/skills/aicut/references/recipes.md +4 -4
  8. package/skills/aicut/references/troubleshooting.md +2 -2
  9. package/skills/aicut-motion/SKILL.md +75 -9
  10. package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
  11. package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
  12. package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
  13. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
  14. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
  15. package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
  16. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
  17. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
  18. package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
  19. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
  20. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
  21. package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
  22. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
  23. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
  24. package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
  25. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
  26. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
  27. package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
  28. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
  29. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
  30. package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
  31. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
  32. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
  33. package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
  34. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
  35. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
  36. package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
  37. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
  38. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
  39. package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
  40. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
  41. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
  42. package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
  43. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
  44. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
  45. package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
  46. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
  47. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
  48. package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
  49. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
  50. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
  51. package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
  52. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
  53. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
  54. package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
  55. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
  56. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
  57. package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
  58. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
  59. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
  60. package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
  61. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
  62. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
  63. package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
  64. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
  65. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
  66. package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
  67. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
  68. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
  69. package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
  70. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
  71. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
  72. package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
  73. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
  74. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
  75. package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
  76. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
  77. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
  78. package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
  79. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
  80. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
  81. package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
  82. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
  83. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
  84. package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
  85. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
  86. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
  87. package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
  88. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
  89. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
  90. package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
  91. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
  92. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
  93. package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
  94. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
  95. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
  96. package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
  97. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
  98. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
  99. package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
  100. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
  101. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
  102. package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
  103. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
  104. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
  105. package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
  106. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
  107. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
  108. package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
  109. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
  110. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
  111. package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
  112. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
  113. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
  114. package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
  115. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
  116. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
  117. package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
  118. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
  119. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
  120. package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
  121. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
  122. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
  123. package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
  124. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
  125. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
  126. package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
  127. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
  128. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
  129. package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
  130. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
  131. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
  132. package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
  133. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
  134. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
  135. package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
  136. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
  137. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
  138. package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
  139. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
  140. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
  141. package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
  142. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
  143. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
  144. package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
  145. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
  146. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
  147. package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
  148. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
  149. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
  150. package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
  151. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
  152. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
  153. package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
  154. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
  155. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
  156. package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
  157. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
  158. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
  159. package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
  160. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
  161. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
  162. package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
  163. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
  164. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
  165. package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
  166. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
  167. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
  168. package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
  169. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
  170. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
  171. package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
  172. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
  173. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
  174. package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
  175. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
  176. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
  177. package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
  178. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
  179. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
  180. package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
  181. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
  182. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
  183. package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
  184. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
  185. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
  186. package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
  187. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
  188. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
  189. package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
  190. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
  191. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
  192. package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
  193. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
  194. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
  195. package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
  196. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
  197. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
  198. package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
  199. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
  200. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
  201. package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
  202. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
  203. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
  204. package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
  205. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
  206. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
  207. package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
  208. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
  209. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
  210. package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
  211. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
  212. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
  213. package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
  214. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
  215. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
  216. package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
  217. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
  218. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
  219. package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
  220. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
  221. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
  222. package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
  223. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
  224. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
  225. package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
  226. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
  227. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
  228. package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
  229. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
  230. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
  231. package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
  232. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
  233. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
  234. package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
  235. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
  236. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
  237. package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
  238. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
  239. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
  240. package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
  241. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
  242. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
  243. package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
  244. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
  245. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
  246. package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
  247. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
  248. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
  249. package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
  250. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
  251. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
  252. package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
  253. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
  254. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
  255. package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
  256. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
  257. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
  258. package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
  259. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
  260. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
  261. package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
  262. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
  263. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
  264. package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
  265. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
  266. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
  267. package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
  268. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
  269. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
  270. package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
  271. package/skills/aicut-motion/references/design-systems/README.md +1 -1
  272. package/skills/aicut-motion/references/motion-delivery.md +55 -0
  273. package/skills/aicut-motion/references/pages.md +1 -1
  274. package/skills/aicut-motion/references/style-index.json +690 -0
  275. package/skills/aicut-motion/references/style-research.md +73 -0
  276. package/skills/aicut-motion/references/style-selection.md +71 -0
  277. package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
  278. package/skills/aicut-motion/scripts/atlas.mjs +109 -0
  279. package/studio/client.mjs +3 -3
  280. package/studio/server/fetch-github.mjs +14 -3
  281. package/studio/server/server.mjs +71 -6
  282. package/studio/server/skill-summaries.mjs +60 -0
  283. package/studio/server/skills.mjs +121 -7
  284. package/studio/server/styles-store.mjs +235 -0
  285. package/studio/server/template-posters.mjs +2 -2
  286. package/studio/server/templates-page.mjs +93 -93
  287. package/studio/server/templates.mjs +32 -7
  288. package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
  289. package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
  290. package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
  291. package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
  292. package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
  293. package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
  294. package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
  295. package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
  296. package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
  297. package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
  298. package/studio/ui/dist/index.html +4 -3
  299. package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
  300. package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
  301. package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
  302. package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
  303. package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
  304. package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
  305. package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
  306. package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
@@ -0,0 +1,524 @@
1
+ {
2
+ // ---------- DMG handheld: 160x144 four-shade LCD, LCD pixel grid + drop shadows + ghosting, inside the bezel ----------
3
+ const LW = 160, LH = 144;
4
+ const SHADES = ['#a4ba30', '#7f9c22', '#3e6a2c', '#14381a'].map((h) => H.hex(h)); // lightest pulled toward DMG olive
5
+
6
+ const HERO_A = [
7
+ '.....33333......',
8
+ '....3222223.....',
9
+ '...322222223....',
10
+ '...3333333333...',
11
+ '...31100003.....',
12
+ '...31000303.....',
13
+ '...31000003.....',
14
+ '....300003......',
15
+ '....3333333.....',
16
+ '...322222223....',
17
+ '..32222222223...',
18
+ '..30322222303...',
19
+ '...333333333....',
20
+ '...3223.3223....',
21
+ '...3223.3223....',
22
+ '..33333.33333...',
23
+ ];
24
+ const HERO_B = HERO_A.slice(0, 13).concat(['...3223..3223...', '..3223....3223..', '.33333....33333.']);
25
+ const BOT = [
26
+ '.......3........',
27
+ '......303.......',
28
+ '.......3........',
29
+ '..33333333333...',
30
+ '.3000000000003..',
31
+ '.3033333333303..',
32
+ '.3033033033303..',
33
+ '.3033033033303..',
34
+ '.3033300033303..',
35
+ '.3033333333303..',
36
+ '.3000000000003..',
37
+ '..33333333333...',
38
+ '....3211123.....',
39
+ '...321111123....',
40
+ '....3333333.....',
41
+ '................',
42
+ ];
43
+ const TREE = [
44
+ '.....333333.....',
45
+ '...3311111133...',
46
+ '..311001111113..',
47
+ '.31100111111213.',
48
+ '.31101111112213.',
49
+ '3111111111122213',
50
+ '3111111111222213',
51
+ '3121111112222223',
52
+ '3122111122222223',
53
+ '.31222222222223.',
54
+ '.33222222222233.',
55
+ '..333222222333..',
56
+ '....33322333....',
57
+ '......3223......',
58
+ '......3223......',
59
+ '.....332233.....',
60
+ ];
61
+
62
+ // shared LCD + case pipeline (used by both draw and the explainer)
63
+ const BX = 212, BY = 18, BW = 856, BH = 690, SC = 4;
64
+ const SX = BX + (BW - LW * SC) / 2 + 20, SY = BY + 70;
65
+ const bezPath = (ctx, inset) => { // bezel (grey-violet) with the big bottom-right radius
66
+ const x = BX + inset, y = BY + inset, w = BW - inset * 2, h = BH - inset * 2, r1 = 22 - inset * 0.5, r2 = 120 - inset;
67
+ ctx.beginPath(); ctx.moveTo(x + r1, y); ctx.lineTo(x + w - r1, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r1);
68
+ ctx.lineTo(x + w, y + h - r2); ctx.quadraticCurveTo(x + w, y + h, x + w - r2, y + h); ctx.lineTo(x + r1, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r1);
69
+ ctx.lineTo(x, y + r1); ctx.quadraticCurveTo(x, y, x + r1, y); ctx.closePath();
70
+ };
71
+ // static case + bezel + label + LED + LCD recess, built once per canvas size
72
+ let CASE = null;
73
+ function caseLayer(W, H_) {
74
+ if (CASE && CASE.w === W && CASE.h === H_) return CASE.cv;
75
+ const cv = H.layer(W, H_), ctx = cv.getContext('2d');
76
+ // off-white ABS: diagonal light, low-frequency unevenness and a fine moulded stipple
77
+ const img = ctx.createImageData(W, H_), d = img.data, nz = H.makeNoise(9), rr = H.rng(31);
78
+ const c0 = H.hex('#dcd8d0'), c1 = H.hex('#b9b4aa');
79
+ for (let y = 0, p = 0; y < H_; y++) for (let x = 0; x < W; x++, p++) {
80
+ const g = H.clamp((x / W) * 0.55 + (y / H_) * 0.45), lo = nz.fbm2(x * 0.004, y * 0.004, 3) * 0.035;
81
+ const st = (rr() - 0.5) * 0.035 + nz.n2(x * 0.6, y * 0.6) * 0.02;
82
+ const m = 1 + lo + st, i = p * 4;
83
+ d[i] = (c0[0] + (c1[0] - c0[0]) * g) * m; d[i + 1] = (c0[1] + (c1[1] - c0[1]) * g) * m; d[i + 2] = (c0[2] + (c1[2] - c0[2]) * g) * m; d[i + 3] = 255;
84
+ }
85
+ ctx.putImageData(img, 0, 0);
86
+ for (let i = 0; i < 7000; i++) { const r0 = H.rng(i); const x = r0() * W, y = r0() * H_; ctx.fillStyle = `rgba(${r0() < 0.5 ? '255,255,255' : '60,50,40'},0.06)`; ctx.fillRect(x, y, 2, 2); }
87
+ // soft top-left key light on the flanks, falling to shade bottom-right
88
+ const kl = ctx.createRadialGradient(60, 40, 0, 60, 40, 900); kl.addColorStop(0, 'rgba(255,252,245,0.22)'); kl.addColorStop(1, 'rgba(255,252,245,0)');
89
+ ctx.fillStyle = kl; ctx.fillRect(0, 0, W, H_);
90
+ const sh = ctx.createLinearGradient(W * 0.5, H_ * 0.4, W, H_); sh.addColorStop(0, 'rgba(70,60,50,0)'); sh.addColorStop(1, 'rgba(70,60,50,0.14)');
91
+ ctx.fillStyle = sh; ctx.fillRect(0, 0, W, H_);
92
+ // bezel: cast shadow onto the case, then the grey-violet face
93
+ ctx.save(); ctx.shadowColor = 'rgba(60,50,40,0.35)'; ctx.shadowBlur = 10; ctx.shadowOffsetY = 3; bezPath(ctx, 0); ctx.fillStyle = '#6f6d7f'; ctx.fill(); ctx.restore();
94
+ const bg2 = ctx.createLinearGradient(0, BY, 0, BY + 700); bg2.addColorStop(0, '#7b798c'); bg2.addColorStop(1, '#5e5c6e');
95
+ bezPath(ctx, 3); ctx.fillStyle = bg2; ctx.fill();
96
+ // matte print texture + top-left light / bottom-right shade on the bezel face
97
+ ctx.save(); bezPath(ctx, 3); ctx.clip();
98
+ const bl = ctx.createLinearGradient(BX, BY, BX + BW, BY + BH); bl.addColorStop(0, 'rgba(255,255,255,0.07)'); bl.addColorStop(0.5, 'rgba(255,255,255,0)'); bl.addColorStop(1, 'rgba(10,8,20,0.12)');
99
+ ctx.fillStyle = bl; ctx.fillRect(BX, BY, BW, BH);
100
+ const r2 = H.rng(77);
101
+ for (let i = 0; i < 9000; i++) { ctx.fillStyle = r2() < 0.5 ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,10,0.07)'; ctx.fillRect(BX + r2() * BW, BY + r2() * BH, 1, 1); }
102
+ ctx.restore();
103
+ // moulded edge: lit along the top/left, shaded along the bottom/right
104
+ const eg = ctx.createLinearGradient(BX, BY, BX + BW * 0.6, BY + BH); eg.addColorStop(0, 'rgba(255,255,255,0.28)'); eg.addColorStop(0.55, 'rgba(255,255,255,0.05)'); eg.addColorStop(1, 'rgba(20,15,35,0.35)');
105
+ ctx.save(); bezPath(ctx, 1); ctx.strokeStyle = eg; ctx.lineWidth = 2; ctx.stroke(); ctx.restore();
106
+ ctx.save(); bezPath(ctx, 3); ctx.strokeStyle = 'rgba(255,255,255,0.12)'; ctx.lineWidth = 1.5; ctx.stroke(); ctx.restore();
107
+ // label lines + DOT MATRIX text
108
+ const ly = BY + 40;
109
+ ctx.fillStyle = '#7a1f52'; ctx.fillRect(BX + 34, ly - 6, 200, 3); ctx.fillRect(BX + BW - 34 - 200, ly - 6, 200, 3);
110
+ ctx.fillStyle = '#26276a'; ctx.fillRect(BX + 34, ly + 1, 200, 3); ctx.fillRect(BX + BW - 34 - 200, ly + 1, 200, 3);
111
+ ctx.font = 'italic 700 13px "Inter"'; ctx.fillStyle = '#c9c7d6'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
112
+ ctx.fillText('DOT MATRIX WITH NEURAL SOUND', BX + BW / 2, ly - 1);
113
+ // battery LED
114
+ const ledX = BX + 52, ledY = BY + 250;
115
+ const lg = ctx.createRadialGradient(ledX, ledY, 0, ledX, ledY, 18); lg.addColorStop(0, 'rgba(255,60,60,0.7)'); lg.addColorStop(1, 'rgba(255,60,60,0)');
116
+ ctx.fillStyle = lg; ctx.fillRect(ledX - 18, ledY - 18, 36, 36);
117
+ ctx.fillStyle = '#ff5050'; ctx.beginPath(); ctx.arc(ledX, ledY, 5, 0, 7); ctx.fill();
118
+ ctx.fillStyle = 'rgba(255,255,255,0.7)'; ctx.beginPath(); ctx.arc(ledX - 1.5, ledY - 1.5, 1.6, 0, 7); ctx.fill();
119
+ ctx.font = '600 11px "Inter"'; ctx.fillStyle = '#c9c7d6'; ctx.fillText('BATTERY', ledX, ledY + 22);
120
+ // LCD recess: dark well with a 1px deeper lip, lit lower/right rim where the bezel steps down
121
+ const rx = SX - 6, ry = SY - 6, rw = LW * SC + 12, rh = LH * SC + 12;
122
+ ctx.fillStyle = 'rgba(255,255,255,0.16)'; ctx.fillRect(rx, ry + rh, rw + 1, 1); ctx.fillRect(rx + rw, ry, 1, rh + 1);
123
+ ctx.fillStyle = 'rgba(20,18,30,0.35)'; ctx.fillRect(rx - 1, ry - 1, rw + 1, 1); ctx.fillRect(rx - 1, ry - 1, 1, rh + 1);
124
+ ctx.fillStyle = '#3d3c48'; ctx.fillRect(rx, ry, rw, rh);
125
+ ctx.strokeStyle = '#2a2933'; ctx.lineWidth = 1; ctx.strokeRect(rx + 0.5, ry + 0.5, rw - 1, rh - 1);
126
+ CASE = { w: W, h: H_, cv };
127
+ return cv;
128
+ }
129
+
130
+ function gbCompose(ctx, W, H_, t, renderWorld) {
131
+ // ghosting: blend current frame with two previous frames (slow LCD response)
132
+ const F0 = renderWorld(t), F1 = renderWorld(t - 0.1), F2 = renderWorld(t - 0.2);
133
+ const ink = new Float32Array(LW * LH);
134
+ for (let i = 0; i < ink.length; i++) { const a = F0[i], b = Math.max(a, F1[i]), c = Math.max(a, F2[i]); ink[i] = a * 0.7 + b * 0.2 + c * 0.1; }
135
+
136
+ // ---------- case + bezel (cached) ----------
137
+ ctx.drawImage(caseLayer(W, H_), 0, 0);
138
+
139
+ // ---------- LCD panel ----------
140
+ const PW = LW * SC, PH = LH * SC;
141
+ const img = ctx.createImageData(PW, PH), D = img.data;
142
+ const paper = H.hex('#a9bd3a'), sh = H.hex('#8ea424').map((q) => q * 0.55);
143
+ // per-cell colours: the cell itself and the gap around it (mostly backing colour, picks a bit of the pixel)
144
+ const N = LW * LH, cr = new Float32Array(N), cg = new Float32Array(N), cb = new Float32Array(N);
145
+ for (let k = 0; k < N; k++) { const v = ink[k], i = Math.min(2, Math.floor(v)), f = v - i, A = SHADES[i], B = SHADES[i + 1]; cr[k] = A[0] + (B[0] - A[0]) * f; cg[k] = A[1] + (B[1] - A[1]) * f; cb[k] = A[2] + (B[2] - A[2]) * f; }
146
+ // backlight-less falloff + diagonal reflective sheen (+3% top-left -> -3% bottom-right), separable
147
+ const colT = new Float32Array(PW), rowT = new Float32Array(PH);
148
+ for (let x = 0; x < PW; x++) colT[x] = -0.08 * ((x / PW - 0.5) ** 2 * 4) - 0.03 * (x / PW);
149
+ for (let y = 0; y < PH; y++) rowT[y] = 1.03 - 0.08 * ((y / PH - 0.5) ** 2 * 4) - 0.03 * (y / PH);
150
+ for (let y = 0; y < PH; y++) {
151
+ const ly2 = (y / SC) | 0, cy = y % SC, syp = y - 2, sy2 = syp >= 0 ? ((syp / SC) | 0) * LW : -1;
152
+ for (let x = 0; x < PW; x++) {
153
+ const lx = (x / SC) | 0, k = ly2 * LW + lx, v = ink[k];
154
+ let r = cr[k], g = cg[k], b = cb[k];
155
+ if (x % SC === SC - 1 || cy === SC - 1) { r = paper[0] + (r - paper[0]) * 0.45; g = paper[1] + (g - paper[1]) * 0.45; b = paper[2] + (b - paper[2]) * 0.45; }
156
+ // drop shadow of the liquid-crystal layer onto the reflective backing (offset down-right ~2 px)
157
+ const sxp = x - 2;
158
+ if (sxp >= 0 && sy2 >= 0) { const vs = ink[sy2 + ((sxp / SC) | 0)]; if (vs > v) { const m = Math.min(0.3, (vs - v) * 0.11); r += (sh[0] - r) * m; g += (sh[1] - g) * m; b += (sh[2] - b) * m; } }
159
+ const f = colT[x] + rowT[y], i = (y * PW + x) * 4;
160
+ D[i] = r * f; D[i + 1] = g * f; D[i + 2] = b * f; D[i + 3] = 255;
161
+ }
162
+ }
163
+ ctx.putImageData(img, SX, SY);
164
+ // inner shadow (panel sits below the glass) + glass sheen
165
+ ctx.save();
166
+ const ish = ctx.createLinearGradient(0, SY, 0, SY + 26); ish.addColorStop(0, 'rgba(20,30,10,0.35)'); ish.addColorStop(1, 'rgba(20,30,10,0)');
167
+ ctx.fillStyle = ish; ctx.fillRect(SX, SY, PW, 26);
168
+ const ish2 = ctx.createLinearGradient(SX, 0, SX + 20, 0); ish2.addColorStop(0, 'rgba(20,30,10,0.3)'); ish2.addColorStop(1, 'rgba(20,30,10,0)');
169
+ ctx.fillStyle = ish2; ctx.fillRect(SX, SY, 20, PH);
170
+ // glare across the cover, kept inside the bezel
171
+ bezPath(ctx, 3); ctx.clip();
172
+ ctx.globalCompositeOperation = 'screen';
173
+ ctx.beginPath(); ctx.moveTo(BX + 120, BY); ctx.lineTo(BX + 420, BY); ctx.lineTo(BX + 120, BY + 520); ctx.lineTo(BX - 40, BY + 520); ctx.closePath();
174
+ const gl = ctx.createLinearGradient(BX, BY, BX + 300, BY + 300); gl.addColorStop(0, 'rgba(255,255,255,0.10)'); gl.addColorStop(1, 'rgba(255,255,255,0)');
175
+ ctx.fillStyle = gl; ctx.fill();
176
+ ctx.restore();
177
+ }
178
+
179
+ // ================= EXPLAINER: "How does AI learn?" as a handheld monster-battle RPG =================
180
+ const GB_CAT = ['3.........3', '33.......33', '313.....313', '31133333113', '31111111113', '31131113113', '31111211113', '30113131103', '.311111113.', '..3333333..'];
181
+ const GB_DOG = ['..3333333..', '.300000003.', '32300000323', '32303030323', '32300000323', '32300300323', '.330000033.', '..3001003..', '...30003...', '....333....'];
182
+ const GB_ARW = ['3....', '33...', '323..', '3223.', '323..', '33...', '3....'];
183
+ function gbExplainer(ctx, W, H_, t) {
184
+ const tlx = H.layer(LW, 24).getContext('2d', { willReadFrequently: true });
185
+ const renderWorld = (tt) => {
186
+ const S = new Uint8Array(LW * LH);
187
+ const tq = Math.floor(tt * 60) / 60;
188
+ const pr = (a, b) => H.clamp((tq - a) / (b - a));
189
+ const put = (x, y, v) => { x |= 0; y |= 0; if (x >= 0 && y >= 0 && x < LW && y < LH) S[y * LW + x] = v; };
190
+ const rect = (x, y, w, h, v) => { for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) put(x + i, y + j, v); };
191
+ const spr = (rows, ox, oy, flip, sc = 1) => rows.forEach((row, j) => [...row].forEach((c, i) => { if (c >= '0' && c <= '3') for (let a = 0; a < sc; a++) for (let b = 0; b < sc; b++) put(ox + (flip ? row.length - 1 - i : i) * sc + a, oy + j * sc + b, +c); }));
192
+ const stamp = (str, x, y, v = 3, size = 8) => {
193
+ tlx.clearRect(0, 0, LW, 24); tlx.font = size + 'px "Press Start 2P"'; tlx.textBaseline = 'top'; tlx.fillStyle = '#000'; tlx.fillText(str, 0, 0);
194
+ const hh = size + 2, d = tlx.getImageData(0, 0, LW, hh).data;
195
+ for (let j = 0; j < hh; j++) for (let i = 0; i < LW; i++) if (d[(j * LW + i) * 4 + 3] > 110) put(x + i, y + j, v);
196
+ };
197
+ const tbox = (x, y, w, h) => { rect(x, y, w, h, 0); rect(x, y, w, 1, 3); rect(x, y + h - 1, w, 1, 3); rect(x, y, 1, h, 3); rect(x + w - 1, y, 1, h, 3); rect(x + 2, y + 2, w - 4, 1, 3); rect(x + 2, y + h - 3, w - 4, 1, 3); rect(x + 2, y + 2, 1, h - 4, 3); rect(x + w - 3, y + 2, 1, h - 4, 3); };
198
+ const typed = (str, a, cps = 30) => str.slice(0, Math.max(0, Math.floor((tq - a) * cps)));
199
+ const fmt = (n) => String(Math.floor(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
200
+ const remap = (m) => { for (let i = 0; i < S.length; i++) S[i] = m[S[i]]; };
201
+ const grass = () => { for (let y = 0; y < LH; y++) for (let x = 0; x < LW; x++) { const h = ((x * 73856093) ^ (y * 19349663)) >>> 0; S[y * LW + x] = (h % 29 === 0) ? 2 : (h % 13 === 0 ? 0 : 1); } };
202
+ const header = () => { tbox(0, 0, LW, 16); stamp('HOW DOES AI LEARN?', 8, 4); };
203
+
204
+ if (tq < 2.35) { // ---------- BEAT 1: the question as a text box, which then becomes the header ----------
205
+ grass();
206
+ rect(56, 40, 48, 40, 0); for (let x = 56; x < 104; x++) { put(x, 40, 3); put(x, 79, 3); } // a little clearing
207
+ spr(BOT, 64, 44 + Math.round(Math.sin(tq * 4) * 2), false, 2);
208
+ const up = Math.floor(pr(1.55, 1.95) * 4) / 4; // window layer slides up in 8-px steps
209
+ const by = Math.round(H.lerp(98, 0, up) / 8) * 8, bh = up > 0 ? 16 : 46;
210
+ tbox(0, by, LW, up >= 1 ? 16 : bh);
211
+ if (up === 0) { stamp(typed('HOW DOES', 0.2, 20), 8, by + 8, 3, 16); stamp(typed('AI LEARN?', 0.6, 20), 8, by + 26, 3, 16); }
212
+ else stamp('HOW DOES AI LEARN?', 8, by + 4);
213
+ // door-style palette fade to white into the next scene
214
+ const fo = [[0, 1, 2, 3], [0, 0, 1, 2], [0, 0, 0, 1], [0, 0, 0, 0]];
215
+ remap(fo[H.clamp(Math.floor((tq - 2.1) / 0.06) + 1, 0, 3)]);
216
+ return S;
217
+ }
218
+ if (tq < 6.5) { // ---------- BEAT 2: labelled cards walk the arrow path into CHIP (the model) ----------
219
+ rect(0, 0, LW, LH, 0);
220
+ for (let y = 20; y < 100; y++) for (let x = 0; x < LW; x++) if ((x + y) % 4 === 0 && y % 2 === 0) put(x, y, 1); // floor tiles
221
+ // arrow-tile path
222
+ for (let x = 0; x < 112; x += 8) spr(GB_ARW, x + ((Math.floor(tq * 8)) % 8 < 4 ? 0 : 1), 80, false);
223
+ rect(0, 89, 116, 1, 2);
224
+ stamp('TRAINING DATA', 4, 91, 3);
225
+ // the model: CHIP at 2x on a pad, label landing under it
226
+ rect(112, 74, 44, 6, 2); rect(112, 74, 44, 1, 3);
227
+ const gulp = Math.floor((tq - 2.95) / 0.4) >= 0 && ((tq - 2.95) % 0.4) < 0.07 && tq < 6.0;
228
+ spr(BOT, 118, 40 + (gulp ? -2 : 0), true, 2);
229
+ tbox(112, 20, 46, 16); stamp('MODEL', 115, 24);
230
+ // cards
231
+ for (let i = 0; i < 9; i++) {
232
+ const k = (tq - (2.55 + i * 0.4)) / 1.8; if (k < 0 || k >= 1) continue;
233
+ const x = Math.round(-28 + k * 150), isCat = i % 3 !== 1;
234
+ tbox(x, 44, 28, 22); spr(isCat ? GB_CAT : GB_DOG, x + 9, 50, false);
235
+ rect(x, 66, 28, 11, 3); stamp(isCat ? 'CAT' : 'DOG', x + 2, 68, 0);
236
+ }
237
+ // counter text box
238
+ tbox(0, 100, LW, 44); stamp('EXAMPLES SEEN', 8, 106); stamp(fmt(1e6 * Math.pow(pr(2.8, 5.8), 2.2)), 8, 120, 3, 16);
239
+ header();
240
+ const fi = [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 2], [0, 1, 2, 3]];
241
+ remap(fi[H.clamp(Math.floor((tq - 2.35) / 0.06), 0, 3)]);
242
+ // battle intro: flashes, then striped wipe
243
+ if (tq > 6.0 && tq < 6.25 && Math.floor((tq - 6.0) / 0.05) % 2 === 0) remap([3, 2, 1, 0]);
244
+ if (tq >= 6.25) { const k = pr(6.25, 6.5); for (let y = 0; y < LH; y++) { const w = Math.round(k * LW); if (Math.floor(y / 4) % 2) rect(0, y, w, 1, 3); else rect(LW - w, y, w, 1, 3); } }
245
+ return S;
246
+ }
247
+ if (tq < 10.55) { // ---------- BEAT 3: battle — GUESS / CHECK / ADJUST ----------
248
+ rect(0, 0, LW, LH, 0);
249
+ const phase = tq < 7.4 ? 0 : tq < 8.3 ? 1 : tq < 9.4 ? 2 : 0;
250
+ const shake = tq > 7.4 && tq < 7.7 ? (Math.floor(tq * 30) % 2 ? 2 : -2) : 0;
251
+ // enemy: the picture (a cat) on its platform + name box
252
+ for (let x = -24; x <= 24; x++) { const y = Math.round(Math.sqrt(Math.max(0, 1 - (x / 24) ** 2)) * 5); put(118 + x, 48 + y, 2); put(118 + x, 48 - y, 2); }
253
+ if (!(tq > 7.4 && tq < 7.8 && Math.floor(tq * 15) % 2)) spr(GB_CAT, 107 + shake, 26, false, 2);
254
+ tbox(4, 20, 70, 16); stamp('NEW PIC', 10, 24);
255
+ // player: CHIP (the model) + weights panel
256
+ for (let x = -28; x <= 28; x++) { const y = Math.round(Math.sqrt(Math.max(0, 1 - (x / 28) ** 2)) * 5); put(34 + x, 96 + y, 2); put(34 + x, 96 - y, 2); }
257
+ spr(BOT, 18, 66 + Math.round(Math.sin(tq * 4)), false, 2);
258
+ tbox(70, 56, 90, 48); stamp('MODEL', 76, 60);
259
+ const adj = Math.floor(pr(8.55, 9.15) * 4) / 4;
260
+ [[0.3, 0.8], [0.85, 0.45], [0.5, 0.95]].forEach(([a, b], i) => { const v = H.lerp(a, b, adj), y = 72 + i * 10; stamp('W' + (i + 1), 76, y - 1, 3); rect(94, y, 60, 4, 3); rect(95, y + 1, Math.round(58 * v), 2, adj > 0 && adj < 1 && Math.floor(tq * 15) % 2 ? 1 : 0); });
261
+ // error trail: dotted line from the picture back to the model
262
+ if (tq > 8.3 && tq < 9.4) { const k = pr(8.3, 8.75); for (let s2 = 0; s2 < k; s2 += 0.04) { const x = Math.round(H.lerp(104, 50, s2)), y = Math.round(H.lerp(44, 66, s2) - Math.sin(s2 * Math.PI) * 14); rect(x, y, 2, 2, 3); } }
263
+ // message box + command menu
264
+ tbox(0, 104, 98, 40); tbox(96, 104, 64, 40);
265
+ ['GUESS', 'CHECK', 'ADJST'].forEach((nm, i) => stamp(nm, 110, 110 + i * 11));
266
+ if (Math.floor(tq * 4) % 2 === 0 || true) spr(['3...', '33..', '333.', '33..', '3...'], 102, 111 + phase * 11, false);
267
+ let m1 = '', m2 = '';
268
+ if (tq < 7.4) { m1 = 'CHIP SAYS'; m2 = typed('DOG?', 6.75, 12); }
269
+ else if (tq < 8.3) { m1 = 'WRONG!'; m2 = "IT'S A CAT"; }
270
+ else if (tq < 9.4) { m1 = 'ERROR!'; m2 = 'ADJUSTING'; }
271
+ else { m1 = 'CHIP SAYS'; m2 = typed('CAT!', 9.55, 12); }
272
+ stamp(m1, 6, 110); const big = m2 === 'DOG?' || m2 === 'CAT!' || m2.length < 4; stamp(m2, 6, big ? 122 : 126, 3, big ? 16 : 8);
273
+ if (tq > 9.9) { spr(['......33', '.....33.', '33..33..', '.3333...', '..33....'], 76, 126, false, 2); }
274
+ header();
275
+ if (tq > 7.4 && tq < 7.48) remap([3, 2, 1, 0]); // hit flash
276
+ const fo = [[0, 1, 2, 3], [0, 0, 1, 2], [0, 0, 0, 1], [0, 0, 0, 0]];
277
+ if (tq > 10.3) remap(fo[H.clamp(Math.floor((tq - 10.3) / 0.06) + 1, 0, 3)]);
278
+ if (tq < 6.65) { const k = 1 - pr(6.5, 6.65); for (let y = 0; y < LH; y++) { const w = Math.round(k * LW); if (Math.floor(y / 4) % 2) rect(0, y, w, 1, 3); else rect(LW - w, y, w, 1, 3); } }
279
+ return S;
280
+ }
281
+ // ---------- BEAT 4 + 5: level-up stats screen, then the takeaway ----------
282
+ rect(0, 0, LW, LH, 0);
283
+ const p = pr(10.8, 11.9), val = Math.round(61 + 36 * H.ease.out(p));
284
+ tbox(0, 18, LW, 86);
285
+ stamp('ACCURACY', 8, 26);
286
+ const box97 = p >= 1 && (Math.floor(tq * 6) % 2 === 0 || tq > 12.9);
287
+ if (box97) rect(6, 38, 66, 22, 3);
288
+ stamp(val + '%', 8, 41, box97 ? 0 : 3, 16);
289
+ stamp('61%>' + val + '%', 8, 64);
290
+ stamp('EXP', 8, 78); rect(36, 78, 36, 7, 3); rect(37, 79, Math.round(34 * H.ease.out(p)), 5, 1);
291
+ if (p >= 1) stamp('LEVEL UP!', 8, 90);
292
+ // bar chart of rounds
293
+ for (let y = 30; y <= 96; y++) put(84, y, 3); for (let x = 84; x < 154; x++) put(x, 96, 3);
294
+ for (let i = 0; i < 8; i++) { const g = H.clamp(p * 8 - i); if (g <= 0) break; const h = Math.round(((97 - 36 * Math.exp(-i / 2.5)) / 100) * 60 * g); rect(88 + i * 8, 96 - h, 6, h, i === 7 && p >= 1 ? 3 : 2); }
295
+ if (p >= 1) for (let k = 0; k < 4; k++) { const a = k * 1.57 + tq * 4, x = Math.round(150 + Math.cos(a) * 6), y = Math.round(34 + Math.sin(a) * 6); put(x, y, 3); put(x + 1, y, 2); }
296
+ tbox(0, 104, LW, 40); stamp('TRAINING', 8, 112); stamp('ROUNDS: ' + String(Math.round(H.ease.out(p) * 500)), 8, 126);
297
+ if (tq > 13.0) { // takeaway: full-screen ending card, one big word per line
298
+ tbox(0, 16, LW, 128);
299
+ stamp(typed('MORE', 13.0, 30), 48, 26, 3, 16); stamp(typed('EXAMPLES', 13.1, 30), 16, 46, 3, 16);
300
+ if (tq > 13.35) spr(['3333333', '.32223.', '..323..', '...3...'], 76, 68, false, 2);
301
+ stamp(typed('BETTER', 13.4, 30), 32, 84, 3, 16); stamp(typed('GUESSES!', 13.55, 30), 16, 104, 3, 16);
302
+ }
303
+ header();
304
+ const fi = [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 2], [0, 1, 2, 3]];
305
+ remap(fi[H.clamp(Math.floor((tq - 10.55) / 0.06), 0, 3)]);
306
+ return S;
307
+ };
308
+ gbCompose(ctx, W, H_, t, renderWorld);
309
+ }
310
+
311
+ // ================= SOUND: the handheld's own 4 channels. Two square pulses for text blips, menus and jingles, the
312
+ // wave channel for bass, and the crunchy noise channel for hits, doors and the battle intro. Mono-ish (small pans),
313
+ // like the one tiny speaker. Times copied from gbExplainer (it runs on a 60 fps clock: tq = floor(t*60)/60).
314
+ function gbSound(k) {
315
+ const END = 14.5;
316
+ const pu = (t, f, dur, gain, pan = 0, minor) => k.tone(t, { f, type: 'square', dur, gain, a: 0.001, hold: dur * 0.65, pan, minor });
317
+ const wv = (t, f, dur, gain, pan = 0) => k.tone(t, { f, type: 'triangle', dur, gain, a: 0.002, hold: dur * 0.6, pan });
318
+ const nz = (t, dur, gain, o) => k.noise(t, Object.assign({ dur, filter: 'lowpass', f: 3000, gain, rate: 0.22, a: 0.001 }, o)); // LFSR-ish crunch
319
+ // dialog text: one low blip per printed letter
320
+ const text = (t0, n, cps, pan = 0) => { for (let i = 0; i < n; i++) pu(t0 + i / cps, i % 2 ? 'A4' : 'D5', 0.03, 0.045, pan, true); };
321
+ // door / palette fade: falling noise + pulse step down (out), rising (in)
322
+ const door = (t, out) => { nz(t, 0.2, 0.12, { f: out ? [[0, 3000], [0.2, 600]] : [[0, 600], [0.2, 3000]] }); pu(t, out ? 'C5' : 'G4', 0.06, 0.05); pu(t + 0.06, out ? 'G4' : 'C5', 0.08, 0.05); };
323
+
324
+ // ---- music: overworld while the data walks in, battle theme for the loop, light theme on the stats screen
325
+ k.music('chip', 2.53, 6.0, { bpm: 132, gain: 0.4, chords: [['G3', 'B3', 'D4', 'G4'], ['E3', 'G3', 'B3', 'E4']] });
326
+ k.music('chip', 6.65, 10.3, { bpm: 168, gain: 0.45, chords: [['E3', 'G3', 'B3', 'E4'], ['C3', 'E3', 'G3', 'C4'], ['D3', 'F#3', 'A3', 'D4']] });
327
+ k.music('chip', 10.73, 11.85, { bpm: 132, gain: 0.3, chords: [['C4', 'E4', 'G4', 'C5']] });
328
+ k.music('chip', 12.9, END, { bpm: 132, gain: 0.3, chords: [['F3', 'A3', 'C4', 'F4'], ['C4', 'E4', 'G4', 'C5']] });
329
+
330
+ // ---- BEAT 1: question prints (0.2: 8 chars at 20 cps; 0.6: 9 chars), window steps up into the header (1.65-1.95)
331
+ text(0.2, 8, 20, -0.1); text(0.6, 9, 20, -0.1);
332
+ ['C4', 'E4', 'G4', 'C5'].forEach((n, i) => pu(1.65 + i * 0.1, n, 0.05, 0.05, 0, true));
333
+ door(2.1, true); // fade to white 2.1-2.28
334
+ door(2.35, false); // fade in 2.35-2.53
335
+
336
+ // ---- BEAT 2: CHIP gulps every 0.4 s from 2.95 (2-px hop), counter 2.8-5.8, lands on 1,000,000
337
+ for (let i = 0; i < 8; i++) { const t = 2.95 + i * 0.4, b = 330 * Math.pow(2, i / 12); k.tone(t, { f: [[0, b], [0.06, b * 1.5]], type: 'square', dur: 0.07, gain: 0.04, a: 0.001, hold: 0.04, pan: 0.3, minor: true }); }
338
+ for (let t = 2.85; t < 5.75;) { nz(t, 0.02, 0.07, { f: 6000, rate: 0.5, pan: -0.2, minor: true }); t += H.lerp(0.25, 1 / 12, (t - 2.8) / 3.0); }
339
+ pu(5.8, 'G5', 0.08, 0.05); pu(5.88, 'C6', 0.2, 0.05); wv(5.8, 'C4', 0.28, 0.1); // item-get blip
340
+ // battle intro: 5 inverted flashes (6.0-6.25, every 0.05) then stripes wipe (6.25-6.65)
341
+ for (let i = 0; i < 5; i++) { pu(6.0 + i * 0.05, i % 2 ? 'E5' : 'B5', 0.04, 0.05); }
342
+ for (let i = 0; i < 8; i++) pu(6.25 + i * 0.05, 200 * Math.pow(2, i / 4), 0.045, 0.045); // stripes sweep up
343
+ nz(6.25, 0.4, 0.1, { f: [[0, 800], [0.4, 4000]], a: 0.2 });
344
+
345
+ // ---- BEAT 3: "CHIP SAYS DOG?" (6.75, 12 cps)
346
+ text(6.75, 4, 12, 0.1);
347
+ // CHECK (7.4): cursor, hit flash, shake and the cat blinking -> damage crunch + low falling pulse
348
+ pu(7.4, 'A4', 0.03, 0.05, 0.2, true);
349
+ nz(7.4, 0.35, 0.34, { f: [[0, 4000], [0.3, 700]] });
350
+ k.tone(7.4, { f: [[0, 700], [0.28, 110]], type: 'square', dur: 0.3, gain: 0.1, a: 0.001, hold: 0.22, pan: 0.3 }); wv(7.4, 'E2', 0.3, 0.2);
351
+ pu(7.75, 'D3', 0.14, 0.07); pu(7.9, 'A2', 0.25, 0.07); // "WRONG! IT'S A CAT" buzzer
352
+ // ADJUST (8.3): cursor, dotted error trail 8.3-8.75, weight bars step in 4 levels (8.7, 8.85, 9.0, 9.15)
353
+ pu(8.3, 'A4', 0.03, 0.05, 0.2, true);
354
+ k.withMinor(() => { for (let i = 0; i < 6; i++) { const f = i / 5; pu(8.3 + f * 0.45, 900 * Math.pow(2, -f * 1.2), 0.04, 0.04, 0.4 - f * 0.6); } });
355
+ ['C5', 'E5', 'G5', 'C6'].forEach((n, i) => pu(8.7 + i * 0.15, n, i === 3 ? 0.14 : 0.06, 0.05, 0.2, true));
356
+ // GUESS again (9.4), "CAT!" (9.55, 12 cps), check sprite (9.9): the correction
357
+ pu(9.4, 'A4', 0.03, 0.05, 0.2, true);
358
+ text(9.55, 4, 12, 0.1);
359
+ ['C5', 'E5', 'G5'].forEach((n, i) => pu(9.9 + i * 0.06, n, 0.06, 0.05, -0.2)); pu(10.08, 'C6', 0.25, 0.045, -0.2); wv(9.9, 'C3', 0.35, 0.11);
360
+ door(10.3, true); door(10.55, false);
361
+
362
+ // ---- BEAT 4: EXP bar fills 10.8-11.9 (ease-out): the classic rising EXP-gain sweep, % ticks slowing as it settles
363
+ k.tone(10.8, { f: [[0, 300], [0.4, 900], [1.1, 1250]], type: 'square', dur: 1.1, gain: 0.035, a: 0.01, hold: 1.0, pan: -0.2 });
364
+ { let last = 61, lastT = -1; for (let f = Math.ceil(10.8 * 60); f < 11.88 * 60; f++) { const t = f / 60, v = Math.round(61 + 36 * H.ease.out(H.clamp((t - 10.8) / 1.1))); if (v > last && t - lastT >= 1 / 12) { nz(t, 0.02, 0.07, { f: 6000, rate: 0.5, minor: true }); last = v; lastT = t; } } }
365
+ // 97% + LEVEL UP! (11.9): the biggest hit. level-up fanfare on both pulses over the wave bass, noise crash
366
+ const L = 11.9;
367
+ nz(L, 0.3, 0.16, { f: [[0, 5000], [0.3, 1500]] });
368
+ [['C5', 0.1], ['C5', 0.1], ['C5', 0.1], ['G5', 0.55]].forEach(([n, d], i) => { const t = L + [0, 0.11, 0.22, 0.33][i]; pu(t, n, d, 0.085); pu(t, k.hz(n) * 0.75, d, 0.05); });
369
+ wv(L, 'C3', 0.3, 0.28); wv(L + 0.33, 'G3', 0.55, 0.28);
370
+ ['E6', 'G6', 'C6', 'E6', 'G6', 'C6'].forEach((n, i) => pu(L + 0.4 + i * 0.07, n, 0.04, 0.02, 0.5, true)); // sparkles orbiting
371
+ // ---- BEAT 5: takeaway card types (MORE 13.0, EXAMPLES 13.1, BETTER 13.4, GUESSES! 13.55, all 30 cps), lands 13.82
372
+ k.withMinor(() => { for (let t = 13.0; t < 13.8; t += 1 / 15) pu(t, Math.round((t - 13) * 15) % 2 ? 'A4' : 'D5', 0.03, 0.045); });
373
+ pu(13.35, 'G4', 0.05, 0.04); // the down-arrow lands
374
+ const TAKE = 13.82;
375
+ pu(TAKE, 'B4', 0.1, 0.04); pu(TAKE + 0.1, 'C5', 0.6, 0.04); pu(TAKE + 0.1, 'G4', 0.6, 0.022); wv(TAKE, 'C3', 0.75, 0.12);
376
+ }
377
+
378
+ registerStyle({
379
+ id: 'gameboy',
380
+ name: 'Pocket Monochrome',
381
+ category: 'Retro Digital',
382
+ description: 'An original DMG handheld frame: 160x144 pixels in exactly four pea-soup greens, a visible LCD pixel grid with each pixel casting a faint shadow on the reflective backing, motion ghosting, and the purple-grey bezel with its striped DOT MATRIX label.',
383
+ motion: 'A handheld boot: the logo scrolls down one LCD pixel per frame and smears, the palette steps to white, the overworld steps back in through the 4 shades, the hero walks on 1 px per frame with a 2-frame cycle, and the window pops up and prints text.',
384
+ duration: 6.2,
385
+ fonts: ['8px "Press Start 2P"', '16px "Press Start 2P"', 'italic 700 13px "Inter"', '600 11px "Inter"', '800 30px "Inter"'],
386
+ frameT: 6.0,
387
+ readsPixels: true,
388
+ explainerDuration: 14.5,
389
+ explainerNote: 'Explains as a handheld monster-battle RPG in four LCD shades: the question text box slides up the window layer into a header, palette fades act as door transitions, labelled cards march an arrow-tile path into CHIP the model, a flash-and-stripes battle intro starts a GUESS/CHECK/ADJUST fight (hit flash, dotted error trail, weight bars), and a LEVEL UP stats screen counts accuracy to an inverted 97%.',
390
+ explainer(ctx, W, H_, t) { gbExplainer(ctx, W, H_, t); },
391
+ explainerSound(k) { gbSound(k); },
392
+ draw(ctx, W, H_, t) {
393
+ // ---------- LCD world renderer: returns shade indices (0..3) at time tt ----------
394
+ const textLayer = H.layer(LW, 16), tlx = textLayer.getContext('2d', { willReadFrequently: true });
395
+ const renderWorld = (tt) => {
396
+ const S = new Uint8Array(LW * LH);
397
+ const put = (x, y, v) => { x |= 0; y |= 0; if (x >= 0 && y >= 0 && x < LW && y < LH) S[y * LW + x] = v; };
398
+ const rect = (x, y, w, h, v) => { for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) put(x + i, y + j, v); };
399
+ const spr = (rows, ox, oy, flip) => rows.forEach((row, j) => [...row].forEach((c, i) => { if (c >= '0' && c <= '3') put(ox + (flip ? 15 - i : i), oy + j, +c); }));
400
+ const tq = Math.floor(tt * 60) / 60; // 60 Hz game tick
401
+ const stamp = (str, x, y, v) => {
402
+ tlx.clearRect(0, 0, LW, 16); tlx.font = '8px "Press Start 2P"'; tlx.textBaseline = 'top'; tlx.fillStyle = '#000'; tlx.fillText(str, 0, 0);
403
+ const d = tlx.getImageData(0, 0, LW, 10).data;
404
+ for (let j = 0; j < 10; j++) for (let i = 0; i < LW; i++) if (d[(j * LW + i) * 4 + 3] > 110) put(x + i, y + j, v);
405
+ };
406
+ const remap = (m) => { for (let i = 0; i < S.length; i++) S[i] = m[S[i]]; };
407
+ // ---------- boot sequence ----------
408
+ if (tt < 2.0) {
409
+ if (tt >= 0.15) {
410
+ const ly = Math.min(60, -10 + Math.floor((tq - 0.15) * 66));
411
+ stamp('POCKET AI', 44, ly, 3);
412
+ if (ly >= 60) { rect(44, ly + 11, 72, 1, 2); stamp('\u00ae', 119, ly - 5, 2); }
413
+ }
414
+ const fadeOut = [[0, 1, 2, 3], [0, 0, 1, 2], [0, 0, 0, 1], [0, 0, 0, 0]];
415
+ remap(fadeOut[H.clamp(Math.floor((tt - 1.75) / 0.07) + 1, 0, 3)]);
416
+ return S;
417
+ }
418
+ const rnd = H.rng(144);
419
+ // grass base with sparse tufts
420
+ for (let y = 0; y < LH; y++) for (let x = 0; x < LW; x++) {
421
+ const h = ((x * 73856093) ^ (y * 19349663)) >>> 0;
422
+ put(x, y, (h % 29 === 0) ? 2 : (h % 13 === 0 ? 0 : 1));
423
+ }
424
+ for (let k = 0; k < 46; k++) { const x = rnd.int(0, LW - 3), y = rnd.int(0, 96); put(x, y, 2); put(x + 2, y, 2); put(x + 1, y - 1, 2); }
425
+ // dirt path (from lab door down/left), with pebble dither
426
+ const pathCells = [];
427
+ for (let y = 50; y < 96; y++) for (let x = 84; x < 100; x++) pathCells.push([x, y]);
428
+ for (let y = 66; y < 82; y++) for (let x = 20; x < 100; x++) pathCells.push([x, y]);
429
+ pathCells.forEach(([x, y]) => put(x, y, ((x * 7 + y * 13) % 17 === 0) ? 1 : 0));
430
+ // path edge lines
431
+ for (let x = 20; x < 84; x++) { put(x, 65, 2); put(x, 82, 2); }
432
+ for (let y = 50; y < 66; y++) { put(83, y, 2); put(100, y, 2); }
433
+ for (let y = 82; y < 96; y++) { put(83, y, 2); put(100, y, 2); }
434
+ // pond with animated wave glints
435
+ for (let y = 40; y < 64; y++) for (let x = 4; x < 64; x++) {
436
+ const dx = (x - 33) / 27, dy = (y - 52) / 10;
437
+ if (dx * dx + dy * dy > 1) continue;
438
+ const edge = dx * dx + dy * dy > 0.82;
439
+ let v = edge ? 3 : 2;
440
+ if (!edge && ((x + Math.floor(tt * 4) * 2 + (y % 4) * 5) % 12 < 4) && y % 3 === 1) v = 1; if (!edge && ((x + Math.floor(tt * 4) * 2) % 24 === 0) && y % 3 === 1) v = 0;
441
+ put(x, y, v);
442
+ }
443
+ // tall grass patch (right)
444
+ for (let ty = 0; ty < 3; ty++) for (let tx = 0; tx < 4; tx++) {
445
+ const ox = 112 + tx * 8, oy = 66 + ty * 8;
446
+ const sway = Math.floor(tt * 3 + tx + ty) % 2;
447
+ [[1, 6], [2, 3], [3, 6], [5, 6], [6, 2 + sway], [7, 6]].forEach(([a, b]) => { for (let q = b; q < 8; q++) put(ox + a, oy + q, q === b ? 3 : 2); });
448
+ }
449
+ // flowers
450
+ [[10, 88], [22, 92], [48, 87], [70, 44], [106, 50], [146, 58], [62, 92], [140, 92], [34, 86], [6, 70]].forEach(([x, y], i) => {
451
+ const bloom = (Math.floor(tt * 2) + i) % 2;
452
+ put(x, y - 1, 0); put(x - 1, y, 0); put(x + 1, y, 0); put(x, y + 1, 0); put(x, y, bloom ? 3 : 2);
453
+ });
454
+ // lab building (with roof dish = "AI lab")
455
+ const lx = 70, ly = 14, lw = 46, lh = 36;
456
+ rect(lx, ly + 10, lw, lh - 10, 0);
457
+ for (let y = ly; y < ly + 12; y++) { const inset = Math.max(0, 11 - (y - ly)) >> 2; rect(lx - 2 + inset, y, lw + 4 - inset * 2, 1, (y - ly) % 3 === 0 ? 3 : 2); }
458
+ rect(lx - 3, ly + 12, lw + 6, 1, 3);
459
+ for (let y = ly + 12; y < ly + lh; y++) { put(lx, y, 3); put(lx + lw - 1, y, 3); }
460
+ rect(lx, ly + lh - 1, lw, 1, 3);
461
+ for (let y = ly + 16; y < ly + lh - 1; y += 4) for (let x = lx + 1; x < lx + lw - 1; x++) if ((x + y) % 7 === 0) put(x, y, 1);
462
+ // windows
463
+ [[lx + 5, ly + 17], [lx + 31, ly + 17]].forEach(([wx, wy]) => { rect(wx, wy, 10, 8, 3); rect(wx + 1, wy + 1, 8, 6, 2); put(wx + 2, wy + 2, 0); put(wx + 3, wy + 2, 0); put(wx + 2, wy + 3, 0); rect(wx + 5, wy, 1, 8, 3); });
464
+ // door
465
+ rect(lx + 18, ly + 22, 10, 14, 3); rect(lx + 19, ly + 23, 8, 12, 2); put(lx + 25, ly + 29, 0);
466
+ // sign above door: chip glyph
467
+ rect(lx + 17, ly + 14, 12, 6, 3); rect(lx + 18, ly + 15, 10, 4, 0); rect(lx + 21, ly + 16, 4, 2, 3);
468
+ for (let k = 0; k < 3; k++) { put(lx + 20 + k * 2, ly + 14, 0); put(lx + 20 + k * 2, ly + 19, 0); }
469
+ // dish on roof
470
+ const dx0 = lx + 36, dy0 = ly - 6;
471
+ for (let a = 0; a < 10; a++) { put(dx0 + a, dy0 + Math.round(((a - 4.5) ** 2) / 6), 3); }
472
+ rect(dx0 + 4, dy0 + 3, 2, 6, 3);
473
+ const blink = Math.floor(tt * 3) % 2; put(dx0 + 4, dy0 - 2, blink ? 3 : 1); put(dx0 + 5, dy0 - 2, blink ? 3 : 1);
474
+ // signal arcs from dish
475
+ if (blink) for (let a = -0.9; a < 0.9; a += 0.08) { put(dx0 + 4 + Math.sin(a) * 6, dy0 - 3 - Math.cos(a) * 5, 2); }
476
+ // fence
477
+ for (let x = 2; x < 66; x++) { put(x, 32, 3); put(x, 36, 3); }
478
+ for (let x = 2; x < 66; x += 6) { rect(x, 30, 2, 9, 3); put(x, 30, 2); }
479
+ // trees: border top + left
480
+ for (let x = -8; x < LW; x += 16) if (x < 60 || x > 120) spr(TREE, x, -6, false);
481
+ spr(TREE, 0, 12, false); spr(TREE, 16, 10, true); spr(TREE, 142, 14, false); spr(TREE, 144, 36, true);
482
+ spr(TREE, 124, 30, false);
483
+ // signpost
484
+ rect(126, 50, 12, 8, 3); rect(127, 51, 10, 6, 0); for (let x = 128; x < 136; x += 2) put(x, 53, 2); for (let x = 129; x < 135; x += 2) put(x, 55, 2); rect(131, 58, 2, 5, 3);
485
+ // hero walking right along the path + robot hovering
486
+ const walkD = H.clamp((tq - 2.3) * 60, 0, 80), walking = walkD > 0 && walkD < 80;
487
+ const walkX = -16 + Math.floor(walkD);
488
+ const frame = walking ? Math.floor(tq * 8) % 2 : 1;
489
+ // shadows
490
+ for (let i = 3; i < 13; i++) put(walkX + i, 82, 2);
491
+ spr(frame ? HERO_A : HERO_B, Math.round(walkX), 66 + (frame ? 0 : -1), false);
492
+ const bx = 84, by = 58 + Math.round(Math.sin(tq * 4) * 2.5);
493
+ for (let i = 4; i < 11; i++) put(bx + i, 80, (i % 2) ? 2 : 1);
494
+ spr(BOT, bx, by, true);
495
+ // heart emote over the robot
496
+ const HEART = ['.33.33.', '3003003', '3000003', '.30003.', '..303..', '...3...'];
497
+ if (tt >= 3.7 && Math.floor(tt * 2) % 2 === 0) HEART.forEach((row, j2) => [...row].forEach((c, i2) => { if (c !== '.') put(bx + 4 + i2, by - 8 + j2, +c); }));
498
+
499
+ // ---------- text box ----------
500
+ const tby = 98;
501
+ const fadeIn = [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 2], [0, 1, 2, 3]];
502
+ if (tt < 3.75) { remap(fadeIn[H.clamp(Math.floor((tt - 2.0) / 0.08), 0, 3)]); return S; }
503
+ rect(0, tby, LW, LH - tby, 0);
504
+ rect(0, tby, LW, 1, 3); rect(0, LH - 1, LW, 1, 3); rect(0, tby, 1, LH - tby, 3); rect(LW - 1, tby, 1, LH - tby, 3);
505
+ rect(2, tby + 2, LW - 4, 1, 3); rect(2, LH - 3, LW - 4, 1, 3); rect(2, tby + 2, 1, LH - tby - 4, 3); rect(LW - 3, tby + 2, 1, LH - tby - 4, 3);
506
+ // corner pips
507
+ [[3, tby + 3], [LW - 4, tby + 3], [3, LH - 4], [LW - 4, LH - 4]].forEach(([x, y]) => put(x, y, 2));
508
+ const lines = ['MEET CHIP,', 'YOUR AI PAL!'];
509
+ const total = lines.join('').length;
510
+ let budget = Math.floor(Math.max(0, tt - 3.8) * 30);
511
+ const text = (str, x, y) => {
512
+ tlx.clearRect(0, 0, LW, 16); tlx.font = '8px "Press Start 2P"'; tlx.textBaseline = 'top'; tlx.fillStyle = '#000'; tlx.fillText(str, 0, 0);
513
+ const d = tlx.getImageData(0, 0, LW, 10).data;
514
+ for (let j = 0; j < 10; j++) for (let i = 0; i < LW; i++) if (d[(j * LW + i) * 4 + 3] > 110) put(x + i, y + j, 3);
515
+ };
516
+ lines.forEach((ln, i) => { const s = ln.slice(0, Math.max(0, budget)); budget -= ln.length; text(s, 10, tby + 12 + i * 16); });
517
+ if (budget >= 0 && Math.floor(tt * 2.5) % 2 === 0) { const ax = LW - 14, ay = LH - 12; rect(ax, ay, 7, 1, 3); rect(ax + 1, ay + 1, 5, 1, 3); rect(ax + 2, ay + 2, 3, 1, 3); put(ax + 3, ay + 3, 3); }
518
+ return S;
519
+ };
520
+
521
+ gbCompose(ctx, W, H_, t, renderWorld);
522
+ },
523
+ });
524
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 59,
3
+ "name": "Pocket Monochrome",
4
+ "nameZh": "掌机单色像素",
5
+ "family": "Retro Digital",
6
+ "familyZh": "复古数字",
7
+ "description": "An original DMG handheld frame: 160x144 pixels in exactly four pea-soup greens, a visible LCD pixel grid with each pixel casting a faint shadow on the reflective backing, motion ghosting, and the purple-grey bezel with its striped DOT MATRIX label.",
8
+ "motionNotes": "Motion: A handheld boot: the logo scrolls down one LCD pixel per frame and smears, the palette steps to white, the overworld steps back in through the 4 shades, the hero walks on 1 px per frame with a 2-frame cycle, and the window pops up and prints text.",
9
+ "explainerNotes": "Explainer: Explains as a handheld monster-battle RPG in four LCD shades: the question text box slides up the window layer into a header, palette fades act as door transitions, labelled cards march an arrow-tile path into CHIP the model, a flash-and-stripes battle intro starts a GUESS/CHECK/ADJUST fight (hit flash, dotted error trail, weight bars), and a LEVEL UP stats screen counts accuracy to an inverted 97%.",
10
+ "code": "code.js",
11
+ "registeredAs": "gameboy",
12
+ "source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
13
+ }