@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.
- package/MANUAL.md +94 -84
- package/package.json +1 -1
- package/product.json +2 -1
- package/skills/aicut/SKILL.md +4 -4
- package/skills/aicut/references/commands.md +15 -15
- package/skills/aicut/references/film-format.md +1 -1
- package/skills/aicut/references/recipes.md +4 -4
- package/skills/aicut/references/troubleshooting.md +2 -2
- package/skills/aicut-motion/SKILL.md +75 -9
- package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
- package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
- package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
- package/skills/aicut-motion/references/design-systems/README.md +1 -1
- package/skills/aicut-motion/references/motion-delivery.md +55 -0
- package/skills/aicut-motion/references/pages.md +1 -1
- package/skills/aicut-motion/references/style-index.json +690 -0
- package/skills/aicut-motion/references/style-research.md +73 -0
- package/skills/aicut-motion/references/style-selection.md +71 -0
- package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
- package/skills/aicut-motion/scripts/atlas.mjs +109 -0
- package/studio/client.mjs +3 -3
- package/studio/server/fetch-github.mjs +14 -3
- package/studio/server/server.mjs +71 -6
- package/studio/server/skill-summaries.mjs +60 -0
- package/studio/server/skills.mjs +121 -7
- package/studio/server/styles-store.mjs +235 -0
- package/studio/server/template-posters.mjs +2 -2
- package/studio/server/templates-page.mjs +93 -93
- package/studio/server/templates.mjs +32 -7
- package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
- package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
- package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
- package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
- package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
- package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
- package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
- package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
- package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
- package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
- package/studio/ui/dist/index.html +4 -3
- package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
- package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
- package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
- package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
- package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
- package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
- package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
- package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
|
@@ -0,0 +1,907 @@
|
|
|
1
|
+
{
|
|
2
|
+
// ---- Windows-95-era desktop, rendered on a 640x360 buffer and nearest-neighbour upscaled 2x ----
|
|
3
|
+
const C = { face: '#c0c0c0', hi: '#ffffff', lt: '#dfdfdf', sh: '#808080', dk: '#000000', navy: '#000080', teal: '#008080' };
|
|
4
|
+
const BW = 640, BH = 360;
|
|
5
|
+
|
|
6
|
+
function rect(c, x, y, w, h, col) { c.fillStyle = col; c.fillRect(x, y, w, h); }
|
|
7
|
+
|
|
8
|
+
// 2px Win95 bevel. kind: 'window' | 'button' | 'sunken' | 'pressed' | 'field'
|
|
9
|
+
function bevel(c, x, y, w, h, kind) {
|
|
10
|
+
let o1, o2, i1, i2; // outer TL, outer BR, inner TL, inner BR
|
|
11
|
+
if (kind === 'window') [o1, o2, i1, i2] = [C.lt, C.dk, C.hi, C.sh];
|
|
12
|
+
else if (kind === 'button') [o1, o2, i1, i2] = [C.hi, C.dk, C.lt, C.sh];
|
|
13
|
+
else if (kind === 'pressed') [o1, o2, i1, i2] = [C.dk, C.hi, C.sh, C.lt];
|
|
14
|
+
else if (kind === 'field') [o1, o2, i1, i2] = [C.sh, C.hi, C.dk, C.lt];
|
|
15
|
+
else if (kind === 'thin') { rect(c, x, y, w, 1, C.sh); rect(c, x, y, 1, h, C.sh); rect(c, x, y + h - 1, w, 1, C.hi); rect(c, x + w - 1, y, 1, h, C.hi); return; }
|
|
16
|
+
rect(c, x, y, w - 1, 1, o1); rect(c, x, y, 1, h - 1, o1);
|
|
17
|
+
rect(c, x, y + h - 1, w, 1, o2); rect(c, x + w - 1, y, 1, h, o2);
|
|
18
|
+
rect(c, x + 1, y + 1, w - 3, 1, i1); rect(c, x + 1, y + 1, 1, h - 3, i1);
|
|
19
|
+
rect(c, x + 1, y + h - 2, w - 2, 1, i2); rect(c, x + w - 2, y + 1, 1, h - 2, i2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ---------- bitmap system font (8px caps, 6px x-height, 2px descenders), like the real 8pt UI bitmap font ----------
|
|
23
|
+
// Each glyph: [firstRow, ...rows]; row 0 = cap top, row 7 = baseline row, rows 8-9 = descenders.
|
|
24
|
+
const GL = {
|
|
25
|
+
A: [0, '..#..', '.#.#.', '.#.#.', '#...#', '#...#', '#####', '#...#', '#...#'],
|
|
26
|
+
B: [0, '####.', '#...#', '#...#', '####.', '#...#', '#...#', '#...#', '####.'],
|
|
27
|
+
C: [0, '.###.', '#...#', '#....', '#....', '#....', '#....', '#...#', '.###.'],
|
|
28
|
+
D: [0, '###..', '#..#.', '#...#', '#...#', '#...#', '#...#', '#..#.', '###..'],
|
|
29
|
+
E: [0, '#####', '#....', '#....', '####.', '#....', '#....', '#....', '#####'],
|
|
30
|
+
F: [0, '#####', '#....', '#....', '####.', '#....', '#....', '#....', '#....'],
|
|
31
|
+
G: [0, '.###.', '#...#', '#....', '#....', '#..##', '#...#', '#...#', '.###.'],
|
|
32
|
+
H: [0, '#...#', '#...#', '#...#', '#####', '#...#', '#...#', '#...#', '#...#'],
|
|
33
|
+
I: [0, '#', '#', '#', '#', '#', '#', '#', '#'],
|
|
34
|
+
J: [0, '...#', '...#', '...#', '...#', '...#', '#..#', '#..#', '.##.'],
|
|
35
|
+
K: [0, '#...#', '#..#.', '#.#..', '##...', '##...', '#.#..', '#..#.', '#...#'],
|
|
36
|
+
L: [0, '#...', '#...', '#...', '#...', '#...', '#...', '#...', '####'],
|
|
37
|
+
M: [0, '#.....#', '##...##', '#.#.#.#', '#..#..#', '#.....#', '#.....#', '#.....#', '#.....#'],
|
|
38
|
+
N: [0, '#...#', '##..#', '##..#', '#.#.#', '#.#.#', '#..##', '#..##', '#...#'],
|
|
39
|
+
O: [0, '.###.', '#...#', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
|
40
|
+
P: [0, '####.', '#...#', '#...#', '#...#', '####.', '#....', '#....', '#....'],
|
|
41
|
+
Q: [0, '.###.', '#...#', '#...#', '#...#', '#...#', '#.#.#', '#..#.', '.##.#'],
|
|
42
|
+
R: [0, '####.', '#...#', '#...#', '#...#', '####.', '#.#..', '#..#.', '#...#'],
|
|
43
|
+
S: [0, '.###.', '#...#', '#....', '.###.', '....#', '....#', '#...#', '.###.'],
|
|
44
|
+
T: [0, '#####', '..#..', '..#..', '..#..', '..#..', '..#..', '..#..', '..#..'],
|
|
45
|
+
U: [0, '#...#', '#...#', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
|
46
|
+
V: [0, '#...#', '#...#', '#...#', '#...#', '.#.#.', '.#.#.', '..#..', '..#..'],
|
|
47
|
+
W: [0, '#.....#', '#.....#', '#.....#', '#..#..#', '#..#..#', '#.#.#.#', '##...##', '#.....#'],
|
|
48
|
+
X: [0, '#...#', '#...#', '.#.#.', '..#..', '..#..', '.#.#.', '#...#', '#...#'],
|
|
49
|
+
Y: [0, '#...#', '#...#', '.#.#.', '..#..', '..#..', '..#..', '..#..', '..#..'],
|
|
50
|
+
Z: [0, '#####', '....#', '...#.', '..#..', '..#..', '.#...', '#....', '#####'],
|
|
51
|
+
a: [2, '.###.', '....#', '.####', '#...#', '#...#', '.####'],
|
|
52
|
+
b: [0, '#....', '#....', '####.', '#...#', '#...#', '#...#', '#...#', '####.'],
|
|
53
|
+
c: [2, '.###', '#...', '#...', '#...', '#...', '.###'],
|
|
54
|
+
d: [0, '....#', '....#', '.####', '#...#', '#...#', '#...#', '#...#', '.####'],
|
|
55
|
+
e: [2, '.###.', '#...#', '#####', '#....', '#...#', '.###.'],
|
|
56
|
+
f: [0, '.##', '#..', '###', '#..', '#..', '#..', '#..', '#..'],
|
|
57
|
+
g: [2, '.####', '#...#', '#...#', '#...#', '#...#', '.####', '....#', '.###.'],
|
|
58
|
+
h: [0, '#....', '#....', '####.', '#...#', '#...#', '#...#', '#...#', '#...#'],
|
|
59
|
+
i: [0, '#', '.', '#', '#', '#', '#', '#', '#'],
|
|
60
|
+
j: [0, '.#', '..', '.#', '.#', '.#', '.#', '.#', '.#', '.#', '#.'],
|
|
61
|
+
k: [0, '#...', '#...', '#..#', '#.#.', '##..', '##..', '#.#.', '#..#'],
|
|
62
|
+
l: [0, '#', '#', '#', '#', '#', '#', '#', '#'],
|
|
63
|
+
m: [2, '###.##.', '#..#..#', '#..#..#', '#..#..#', '#..#..#', '#..#..#'],
|
|
64
|
+
n: [2, '####.', '#...#', '#...#', '#...#', '#...#', '#...#'],
|
|
65
|
+
o: [2, '.###.', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
|
66
|
+
p: [2, '####.', '#...#', '#...#', '#...#', '#...#', '####.', '#....', '#....'],
|
|
67
|
+
q: [2, '.####', '#...#', '#...#', '#...#', '#...#', '.####', '....#', '....#'],
|
|
68
|
+
r: [2, '#.#', '##.', '#..', '#..', '#..', '#..'],
|
|
69
|
+
s: [2, '.###', '#...', '##..', '..##', '...#', '###.'],
|
|
70
|
+
t: [1, '.#.', '###', '.#.', '.#.', '.#.', '.#.', '..#'],
|
|
71
|
+
u: [2, '#...#', '#...#', '#...#', '#...#', '#...#', '.####'],
|
|
72
|
+
v: [2, '#...#', '#...#', '#...#', '.#.#.', '.#.#.', '..#..'],
|
|
73
|
+
w: [2, '#.....#', '#.....#', '#..#..#', '#..#..#', '#.#.#.#', '.#...#.'],
|
|
74
|
+
x: [2, '#...#', '.#.#.', '..#..', '..#..', '.#.#.', '#...#'],
|
|
75
|
+
y: [2, '#...#', '#...#', '#...#', '#...#', '#...#', '.####', '....#', '.###.'],
|
|
76
|
+
z: [2, '#####', '...#.', '..#..', '.#...', '#....', '#####'],
|
|
77
|
+
0: [0, '.###.', '#...#', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
|
78
|
+
1: [0, '..#..', '.##..', '..#..', '..#..', '..#..', '..#..', '..#..', '..#..'],
|
|
79
|
+
2: [0, '.###.', '#...#', '....#', '...#.', '..#..', '.#...', '#....', '#####'],
|
|
80
|
+
3: [0, '.###.', '#...#', '....#', '..##.', '....#', '....#', '#...#', '.###.'],
|
|
81
|
+
4: [0, '...#.', '..##.', '.#.#.', '#..#.', '#..#.', '#####', '...#.', '...#.'],
|
|
82
|
+
5: [0, '#####', '#....', '#....', '####.', '....#', '....#', '#...#', '.###.'],
|
|
83
|
+
6: [0, '.###.', '#...#', '#....', '####.', '#...#', '#...#', '#...#', '.###.'],
|
|
84
|
+
7: [0, '#####', '....#', '...#.', '...#.', '..#..', '..#..', '.#...', '.#...'],
|
|
85
|
+
8: [0, '.###.', '#...#', '#...#', '.###.', '#...#', '#...#', '#...#', '.###.'],
|
|
86
|
+
9: [0, '.###.', '#...#', '#...#', '#...#', '.####', '....#', '#...#', '.###.'],
|
|
87
|
+
' ': [0, '..'],
|
|
88
|
+
'.': [7, '#'],
|
|
89
|
+
',': [6, '.#', '.#', '#.'],
|
|
90
|
+
':': [2, '#', '.', '.', '.', '.', '#'],
|
|
91
|
+
"'": [0, '#', '#', '#'],
|
|
92
|
+
'-': [4, '####'],
|
|
93
|
+
'_': [8, '#####'],
|
|
94
|
+
'>': [1, '#...', '.#..', '..#.', '...#', '..#.', '.#..', '#...'],
|
|
95
|
+
'<': [1, '...#', '..#.', '.#..', '#...', '.#..', '..#.', '...#'],
|
|
96
|
+
'\\': [0, '#...', '#...', '.#..', '.#..', '..#.', '..#.', '...#', '...#'],
|
|
97
|
+
'/': [0, '...#', '...#', '..#.', '..#.', '.#..', '.#..', '#...', '#...'],
|
|
98
|
+
'(': [0, '..#', '.#.', '#..', '#..', '#..', '#..', '#..', '#..', '.#.', '..#'],
|
|
99
|
+
')': [0, '#..', '.#.', '..#', '..#', '..#', '..#', '..#', '..#', '.#.', '#..'],
|
|
100
|
+
'%': [0, '.##...#', '#..#.#.', '.##.#..', '....#..', '...#...', '..#.##.', '.#.#..#', '#...##.'],
|
|
101
|
+
'!': [0, '#', '#', '#', '#', '#', '#', '.', '#'],
|
|
102
|
+
'?': [0, '.###.', '#...#', '....#', '...#.', '..#..', '..#..', '.....', '..#..'],
|
|
103
|
+
};
|
|
104
|
+
const glyphW = (ch) => GL[ch][1].length;
|
|
105
|
+
const pxSupported = (s) => { for (const ch of s) if (!GL[ch]) return false; return true; };
|
|
106
|
+
// bold: 0 regular, 1 bold (double-struck, +1px tracking), 2 bold double-struck at regular tracking
|
|
107
|
+
function pxWidth(s, bold) { let w = 0; for (const ch of s) w += glyphW(ch) + 1 + (bold === 1 ? 1 : 0); return Math.max(0, w - 1 + (bold === 2 ? 1 : 0)); }
|
|
108
|
+
// one atlas per colour (glyphs side by side, 10px tall), drawn once and blitted per glyph
|
|
109
|
+
const ATL = new Map(); let ATL_X = null;
|
|
110
|
+
function atlas(col) {
|
|
111
|
+
let a = ATL.get(col);
|
|
112
|
+
if (a) return a;
|
|
113
|
+
if (!ATL_X) { ATL_X = {}; let x = 0; for (const ch in GL) { ATL_X[ch] = x; x += glyphW(ch) + 1; } ATL_X.__w = x; }
|
|
114
|
+
a = H.layer(ATL_X.__w, 10); const g = a.getContext('2d'); g.fillStyle = col;
|
|
115
|
+
for (const ch in GL) {
|
|
116
|
+
const [top, ...rows] = GL[ch], ox = ATL_X[ch];
|
|
117
|
+
rows.forEach((row, j) => { for (let i = 0; i < row.length; i++) if (row[i] === '#') g.fillRect(ox + i, top + j, 1, 1); });
|
|
118
|
+
}
|
|
119
|
+
ATL.set(col, a); return a;
|
|
120
|
+
}
|
|
121
|
+
function pxText(c, s, x, y, bold, col, align) {
|
|
122
|
+
const w = pxWidth(s, bold);
|
|
123
|
+
let dx = Math.round(x);
|
|
124
|
+
if (align === 'center') dx = Math.round(x - w / 2);
|
|
125
|
+
if (align === 'right') dx = Math.round(x - w);
|
|
126
|
+
const a = atlas(col), by = Math.round(y) - 8;
|
|
127
|
+
for (const ch of s) {
|
|
128
|
+
const gw = glyphW(ch), sx = ATL_X[ch];
|
|
129
|
+
if (ch !== ' ') { c.drawImage(a, sx, 0, gw, 10, dx, by, gw, 10); if (bold) c.drawImage(a, sx, 0, gw, 10, dx + 1, by, gw, 10); }
|
|
130
|
+
dx += gw + 1 + (bold === 1 ? 1 : 0);
|
|
131
|
+
}
|
|
132
|
+
return w;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Aliased bitmap-ish text: draw antialiased into a scratch canvas, threshold alpha -> hard pixels.
|
|
136
|
+
let scratch = null;
|
|
137
|
+
function ptext(c, s, x, y, font, col, align = 'left', thr = 120) {
|
|
138
|
+
// the 9px UI faces use the bitmap system font; larger display text is Inter, hard-thresholded
|
|
139
|
+
if ((font === SYS || font === SYSB || font === START) && pxSupported(s)) return pxText(c, s, x, y, font === SYS ? 0 : font === SYSB ? 1 : 2, col, align);
|
|
140
|
+
if (!scratch) scratch = H.layer(320, 40);
|
|
141
|
+
const sx = scratch.getContext('2d', { willReadFrequently: true });
|
|
142
|
+
sx.font = font;
|
|
143
|
+
const w = Math.ceil(sx.measureText(s).width) + 4, h = 40;
|
|
144
|
+
if (scratch.width < w) { scratch.width = w; }
|
|
145
|
+
sx.clearRect(0, 0, scratch.width, scratch.height);
|
|
146
|
+
sx.font = font; sx.textBaseline = 'alphabetic'; sx.fillStyle = col; sx.fillText(s, 2, 30);
|
|
147
|
+
const img = sx.getImageData(0, 0, w, h), d = img.data;
|
|
148
|
+
const [r, g, b] = H.hex(col);
|
|
149
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
150
|
+
if (d[i + 3] > thr) { d[i] = r; d[i + 1] = g; d[i + 2] = b; d[i + 3] = 255; } else d[i + 3] = 0;
|
|
151
|
+
}
|
|
152
|
+
sx.putImageData(img, 0, 0);
|
|
153
|
+
let dx = x - 2;
|
|
154
|
+
if (align === 'center') dx = Math.round(x - (w - 4) / 2) - 2;
|
|
155
|
+
if (align === 'right') dx = x - (w - 4) - 2;
|
|
156
|
+
c.drawImage(scratch, 0, 0, w, h, Math.round(dx), Math.round(y - 30), w, h);
|
|
157
|
+
return w - 4;
|
|
158
|
+
}
|
|
159
|
+
const F = (wt, px) => `${wt} ${px}px "Inter"`;
|
|
160
|
+
const SYS = F(400, 9), SYSB = F(700, 9), START = F(800, 9.5);
|
|
161
|
+
|
|
162
|
+
// ---------- pixel icons (32x32 unless noted) ----------
|
|
163
|
+
function iconComputer(c, x, y) {
|
|
164
|
+
rect(c, x + 5, y + 2, 22, 18, '#000'); rect(c, x + 6, y + 3, 20, 16, '#c8c4b4');
|
|
165
|
+
rect(c, x + 8, y + 5, 16, 11, '#000'); rect(c, x + 9, y + 6, 14, 9, C.teal);
|
|
166
|
+
rect(c, x + 10, y + 7, 5, 1, '#9ff'); rect(c, x + 10, y + 9, 8, 1, '#9ff'); rect(c, x + 10, y + 11, 6, 1, '#9ff');
|
|
167
|
+
rect(c, x + 11, y + 20, 10, 2, '#000'); rect(c, x + 12, y + 20, 8, 1, '#a8a494');
|
|
168
|
+
rect(c, x + 3, y + 22, 26, 7, '#000'); rect(c, x + 4, y + 23, 24, 5, '#c8c4b4');
|
|
169
|
+
rect(c, x + 6, y + 25, 9, 1, '#000'); rect(c, x + 22, y + 25, 3, 1, '#0a0');
|
|
170
|
+
rect(c, x + 4, y + 27, 24, 1, '#8a8678');
|
|
171
|
+
}
|
|
172
|
+
function iconBin(c, x, y) {
|
|
173
|
+
rect(c, x + 8, y + 4, 16, 2, '#000'); rect(c, x + 9, y + 3, 14, 1, '#000');
|
|
174
|
+
rect(c, x + 9, y + 6, 14, 22, '#000'); rect(c, x + 10, y + 6, 12, 21, '#e0e0e0');
|
|
175
|
+
for (let i = 0; i < 4; i++) rect(c, x + 11 + i * 3, y + 8, 1, 18, '#808080');
|
|
176
|
+
rect(c, x + 10, y + 6, 12, 1, '#fff');
|
|
177
|
+
// crumpled paper peeking
|
|
178
|
+
rect(c, x + 12, y + 1, 5, 3, '#fff'); rect(c, x + 12, y + 1, 5, 1, '#888');
|
|
179
|
+
}
|
|
180
|
+
function iconFolder(c, x, y, open) {
|
|
181
|
+
rect(c, x + 3, y + 7, 11, 3, '#000'); rect(c, x + 4, y + 8, 9, 2, '#ffff80');
|
|
182
|
+
rect(c, x + 3, y + 10, 26, 17, '#000'); rect(c, x + 4, y + 11, 24, 15, '#e8d050');
|
|
183
|
+
rect(c, x + 4, y + 11, 24, 1, '#ffff9c'); rect(c, x + 4, y + 25, 24, 1, '#a08820');
|
|
184
|
+
if (open) { rect(c, x + 8, y + 13, 18, 3, '#fff'); rect(c, x + 8, y + 13, 18, 1, '#888'); }
|
|
185
|
+
}
|
|
186
|
+
function iconNetwork(c, x, y) {
|
|
187
|
+
// globe
|
|
188
|
+
const g = [[10, 2, 12], [7, 3, 18], [6, 4, 20], [5, 5, 22], [5, 6, 22], [4, 7, 24], [4, 8, 24], [4, 9, 24], [4, 10, 24], [4, 11, 24], [5, 12, 22], [5, 13, 22], [6, 14, 20], [7, 15, 18], [10, 16, 12]];
|
|
189
|
+
for (const [a, yy, w] of g) { rect(c, x + a, y + yy, w, 1, '#1060d0'); }
|
|
190
|
+
const land = [[9, 4, 5], [8, 5, 7], [9, 6, 8], [10, 7, 6], [11, 8, 4], [18, 6, 5], [17, 7, 8], [18, 8, 9], [19, 9, 7], [20, 10, 5], [12, 11, 3], [12, 12, 4], [13, 13, 3]];
|
|
191
|
+
for (const [a, yy, w] of land) rect(c, x + a, y + yy, w, 1, '#20a040');
|
|
192
|
+
// two little computers + cable
|
|
193
|
+
rect(c, x + 1, y + 19, 10, 8, '#000'); rect(c, x + 2, y + 20, 8, 5, C.teal); rect(c, x + 0, y + 27, 12, 3, '#000'); rect(c, x + 1, y + 28, 10, 1, '#c8c4b4');
|
|
194
|
+
rect(c, x + 21, y + 19, 10, 8, '#000'); rect(c, x + 22, y + 20, 8, 5, C.teal); rect(c, x + 20, y + 27, 12, 3, '#000'); rect(c, x + 21, y + 28, 10, 1, '#c8c4b4');
|
|
195
|
+
rect(c, x + 11, y + 28, 10, 1, '#000'); rect(c, x + 15, y + 17, 1, 11, '#000');
|
|
196
|
+
}
|
|
197
|
+
function iconDoc(c, x, y) {
|
|
198
|
+
rect(c, x + 7, y + 2, 16, 27, '#000'); rect(c, x + 8, y + 3, 14, 25, '#fff');
|
|
199
|
+
rect(c, x + 18, y + 2, 5, 5, C.teal); rect(c, x + 18, y + 3, 4, 4, '#fff'); rect(c, x + 18, y + 6, 5, 1, '#000'); rect(c, x + 22, y + 2, 1, 5, '#000');
|
|
200
|
+
for (let i = 0; i < 8; i++) rect(c, x + 10, y + 9 + i * 2, i % 3 === 2 ? 7 : 10, 1, '#000080');
|
|
201
|
+
}
|
|
202
|
+
function iconBrainExe(c, x, y, s = 1) { // 16x16 app icon (pink brain on a window), scaled by s
|
|
203
|
+
const R = (a, b, w, h, col) => rect(c, x + a * s, y + b * s, w * s, h * s, col);
|
|
204
|
+
R(0, 1, 16, 14, '#000'); R(1, 2, 14, 12, '#fff'); R(1, 2, 14, 2, C.navy);
|
|
205
|
+
const pts = [[4, 6, 8], [3, 7, 10], [3, 8, 10], [3, 9, 10], [4, 10, 9], [5, 11, 3], [9, 11, 3]];
|
|
206
|
+
for (const [a, b, w] of pts) R(a, b, w, 1, '#ff80c0');
|
|
207
|
+
R(8, 6, 1, 5, '#c0407f'); R(5, 8, 2, 1, '#c0407f'); R(10, 7, 2, 1, '#c0407f'); R(4, 6, 8, 1, '#c0407f');
|
|
208
|
+
}
|
|
209
|
+
function iconError(c, x, y) { // 32x32 red circle with white X
|
|
210
|
+
for (let j = 0; j < 32; j++) for (let i = 0; i < 32; i++) {
|
|
211
|
+
const dx = i - 15.5, dy = j - 15.5, d = Math.hypot(dx, dy);
|
|
212
|
+
if (d < 14.6) {
|
|
213
|
+
const k = Math.abs(dx + dy) < 2.3 || Math.abs(dx - dy) < 2.3;
|
|
214
|
+
rect(c, x + i, y + j, 1, 1, k && d < 9.5 ? '#fff' : '#ff0000');
|
|
215
|
+
} else if (d < 15.6) rect(c, x + i, y + j, 1, 1, '#000');
|
|
216
|
+
else if (d < 16.8 && dx > 0 && dy > 0) rect(c, x + i + 1, y + j + 1, 1, 1, '#808080');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function flag(c, x, y) { // generic waving 4-pane flag (14x12)
|
|
220
|
+
const cols = ['#ff0000', '#00c000', '#0000ff', '#ffff00'];
|
|
221
|
+
for (let q = 0; q < 4; q++) {
|
|
222
|
+
const ox = (q % 2) * 7, oy = (q >> 1) * 6;
|
|
223
|
+
for (let i = 0; i < 6; i++) { const wob = Math.round(Math.sin((ox + i) * 0.45) * 1); rect(c, x + ox + i, y + oy + wob, 1, 5, cols[q]); }
|
|
224
|
+
}
|
|
225
|
+
// speed trails
|
|
226
|
+
rect(c, x - 4, y + 2, 3, 1, '#000'); rect(c, x - 5, y + 5, 4, 1, '#000'); rect(c, x - 4, y + 8, 3, 1, '#000');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function cursorArrow(c, x, y) {
|
|
230
|
+
const rows = ['X', 'XX', 'XOX', 'XOOX', 'XOOOX', 'XOOOOX', 'XOOOOOX', 'XOOOOOOX', 'XOOOOOOOX', 'XOOOOOOOOX', 'XOOOOOXXXXX', 'XOOXOOX', 'XOX XOOX', 'XX XOOX', 'X XOOX', ' XOOX', ' XX'];
|
|
231
|
+
rows.forEach((r, j) => { for (let i = 0; i < r.length; i++) { if (r[i] === 'X') rect(c, x + i, y + j, 1, 1, '#000'); else if (r[i] === 'O') rect(c, x + i, y + j, 1, 1, '#fff'); } });
|
|
232
|
+
}
|
|
233
|
+
function cursorHourglass(c, x, y) {
|
|
234
|
+
const rows = ['XXXXXXXXXXX', 'XOOOOOOOOOX', 'XXXXXXXXXXX', ' XOOOOOOOX', ' XOXOXOXOX', ' XOOXOXOOX', ' XOOXOOX', ' XOXOX', ' XOX', ' XOOOX', ' XOOXOOX', ' XOOOOOOOX', ' XOOXOXOOX', ' XOXOXOXOX', 'XXXXXXXXXXX', 'XOOOOOOOOOX', 'XXXXXXXXXXX'];
|
|
235
|
+
rows.forEach((r, j) => { for (let i = 0; i < r.length; i++) { if (r[i] === 'X') rect(c, x + i, y + j, 1, 1, '#000'); else if (r[i] === 'O') rect(c, x + i, y + j, 1, 1, '#fff'); } });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---------- window chrome ----------
|
|
239
|
+
function titleButtons(c, x, y, closeOnly) {
|
|
240
|
+
const bw = 14, bh = 12;
|
|
241
|
+
const drawB = (bx, glyph) => {
|
|
242
|
+
rect(c, bx, y, bw, bh, C.face); bevel(c, bx, y, bw, bh, 'button');
|
|
243
|
+
c.fillStyle = '#000';
|
|
244
|
+
if (glyph === 'x') { for (let i = 0; i < 6; i++) { c.fillRect(bx + 4 + i, y + 3 + i, 2, 1); c.fillRect(bx + 9 - i, y + 3 + i, 2, 1); } }
|
|
245
|
+
if (glyph === '_') c.fillRect(bx + 4, y + 8, 6, 2);
|
|
246
|
+
if (glyph === 'o') { c.fillRect(bx + 3, y + 2, 9, 2); c.fillRect(bx + 3, y + 2, 1, 8); c.fillRect(bx + 11, y + 2, 1, 8); c.fillRect(bx + 3, y + 9, 9, 1); }
|
|
247
|
+
};
|
|
248
|
+
let bx = x - bw;
|
|
249
|
+
drawB(bx, 'x');
|
|
250
|
+
if (!closeOnly) { bx -= bw + 2; drawB(bx, 'o'); bx -= bw; drawB(bx, '_'); }
|
|
251
|
+
}
|
|
252
|
+
function windowFrame(c, x, y, w, h, title, active, opts = {}) {
|
|
253
|
+
rect(c, x, y, w, h, C.face); bevel(c, x, y, w, h, 'window');
|
|
254
|
+
const tx = x + 3, ty = y + 3, tw = w - 6, th = 16;
|
|
255
|
+
rect(c, tx, ty, tw, th, active ? C.navy : C.sh); // Win95 caption bars are solid (gradients arrived with Win98)
|
|
256
|
+
let lx = tx + 3;
|
|
257
|
+
if (opts.icon) { opts.icon(c, tx + 2, ty + 1, 0.875); lx += 17; }
|
|
258
|
+
ptext(c, title, lx, ty + 12, SYSB, active ? '#ffffff' : '#c0c0c0');
|
|
259
|
+
titleButtons(c, x + w - 5, ty + 2, opts.closeOnly);
|
|
260
|
+
return { x: x + 4, y: y + 4 + th + 1, w: w - 8, h: h - 8 - th - 1 };
|
|
261
|
+
}
|
|
262
|
+
function button(c, x, y, w, h, label, def, pressed) {
|
|
263
|
+
rect(c, x, y, w, h, C.face);
|
|
264
|
+
if (def) { rect(c, x, y, w, h, '#000'); rect(c, x + 1, y + 1, w - 2, h - 2, C.face); bevel(c, x + 1, y + 1, w - 2, h - 2, pressed ? 'pressed' : 'button'); }
|
|
265
|
+
else bevel(c, x, y, w, h, pressed ? 'pressed' : 'button');
|
|
266
|
+
ptext(c, label, x + w / 2 + (pressed ? 1 : 0), y + h / 2 + 4 + (pressed ? 1 : 0), SYS, '#000', 'center');
|
|
267
|
+
if (def) { // focus rect (dotted)
|
|
268
|
+
c.fillStyle = '#000';
|
|
269
|
+
for (let i = x + 4; i < x + w - 4; i += 2) { c.fillRect(i, y + 4, 1, 1); c.fillRect(i, y + h - 5, 1, 1); }
|
|
270
|
+
for (let j = y + 4; j < y + h - 4; j += 2) { c.fillRect(x + 4, j, 1, 1); c.fillRect(x + w - 5, j, 1, 1); }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function etched(c, x, y, w) { rect(c, x, y, w, 1, C.sh); rect(c, x, y + 1, w, 1, C.hi); }
|
|
274
|
+
|
|
275
|
+
// ---------- scene pieces ----------
|
|
276
|
+
function desktopIcon(c, x, y, drawIcon, label, selected) {
|
|
277
|
+
drawIcon(c, x + 14, y);
|
|
278
|
+
const lines = label.split('\n');
|
|
279
|
+
lines.forEach((ln, i) => {
|
|
280
|
+
if (selected) {
|
|
281
|
+
const w = pxWidth(ln, 0) + 4;
|
|
282
|
+
rect(c, Math.round(x + 30 - w / 2), y + 33 + i * 11, w, 11, C.navy);
|
|
283
|
+
}
|
|
284
|
+
ptext(c, ln, x + 30, y + 42 + i * 11, SYS, '#fff', 'center');
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function explorerWindow(c, x, y, w, h) {
|
|
289
|
+
const r = windowFrame(c, x, y, w, h, 'Neural Net', false, { icon: (cc, a, b) => iconFolderSmall(cc, a, b) });
|
|
290
|
+
// menu bar
|
|
291
|
+
let mx = r.x + 6;
|
|
292
|
+
for (const m of ['File', 'Edit', 'View', 'Help']) { const ww = ptext(c, m, mx, r.y + 10, SYS, '#000'); rect(c, mx, r.y + 11, 5, 1, '#000'); mx += ww + 12; }
|
|
293
|
+
// client area
|
|
294
|
+
const cy = r.y + 15, ch = r.h - 15 - 18;
|
|
295
|
+
rect(c, r.x, cy, r.w, ch, '#fff'); bevel(c, r.x, cy, r.w, ch, 'field');
|
|
296
|
+
const items = [['weights.dat', iconDoc], ['tokens', iconFolder], ['brain.exe', (cc, a, b) => iconBrainExe(cc, a + 4, b + 4, 1.5)], ['dreams', iconFolder], ['memory.bak', iconDoc], ['prompts', iconFolder]];
|
|
297
|
+
items.forEach(([name, ic], i) => {
|
|
298
|
+
const ix = r.x + 8 + (i % 6) * 54, iy = cy + 6;
|
|
299
|
+
ic(c, ix + 11, iy);
|
|
300
|
+
ptext(c, name, ix + 27, iy + 42, SYS, '#000', 'center');
|
|
301
|
+
});
|
|
302
|
+
// status bar
|
|
303
|
+
const sy = r.y + r.h - 16;
|
|
304
|
+
rect(c, r.x, sy, 120, 15, C.face); bevel(c, r.x, sy, 120, 15, 'thin');
|
|
305
|
+
rect(c, r.x + 122, sy, r.w - 122, 15, C.face); bevel(c, r.x + 122, sy, r.w - 122, 15, 'thin');
|
|
306
|
+
ptext(c, '6 object(s)', r.x + 4, sy + 11, SYS, '#000');
|
|
307
|
+
ptext(c, '1.44GB', r.x + 126, sy + 11, SYS, '#000');
|
|
308
|
+
}
|
|
309
|
+
function iconFolderSmall(c, x, y) {
|
|
310
|
+
rect(c, x + 1, y + 2, 6, 2, '#000'); rect(c, x + 1, y + 4, 13, 9, '#000'); rect(c, x + 2, y + 5, 11, 7, '#e8d050'); rect(c, x + 2, y + 3, 4, 1, '#ffff80');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function wizardWindow(c, x, y, w, h, t, active) {
|
|
314
|
+
const r = windowFrame(c, x, y, w, h, 'AI Assistant Setup Wizard', active, { closeOnly: true, icon: (cc, a, b) => iconBrainExe(cc, a, b, 0.875) });
|
|
315
|
+
// left bitmap panel
|
|
316
|
+
const px = r.x + 8, py = r.y + 8, pw = 116, ph = r.h - 52;
|
|
317
|
+
rect(c, px - 1, py - 1, pw + 2, ph + 2, '#808080');
|
|
318
|
+
// dithered navy->teal gradient (ordered 2x2 dither between bands)
|
|
319
|
+
const bands = ['#000080', '#00007f', '#0000a8', '#0010c0', '#0040c0', '#0070b0', '#008080', '#10a0a0'];
|
|
320
|
+
for (let j = 0; j < ph; j++) for (let i = 0; i < pw; i += 1) {
|
|
321
|
+
const f = (j / ph) * (bands.length - 1);
|
|
322
|
+
const k = Math.floor(f), fr = f - k;
|
|
323
|
+
const bay = [[0, 0.5], [0.75, 0.25]][j & 1][i & 1];
|
|
324
|
+
c.fillStyle = bands[Math.min(bands.length - 1, k + (fr > bay ? 1 : 0))];
|
|
325
|
+
c.fillRect(px + i, py + j, 1, 1);
|
|
326
|
+
}
|
|
327
|
+
// stars / sparkle pixels
|
|
328
|
+
const rr = H.rng(9);
|
|
329
|
+
for (let i = 0; i < 26; i++) { rect(c, px + rr.int(2, pw - 3), py + rr.int(2, ph * 0.55), 1, 1, rr.chance(0.5) ? '#ffffff' : '#80c0ff'); }
|
|
330
|
+
// big monitor with a pixel brain on screen + floppy
|
|
331
|
+
const mx = px + 23, my = py + 18;
|
|
332
|
+
rect(c, mx, my, 70, 56, '#000'); rect(c, mx + 1, my + 1, 68, 54, '#d8d4c4'); rect(c, mx + 1, my + 53, 68, 2, '#8a8678');
|
|
333
|
+
rect(c, mx + 6, my + 6, 58, 40, '#000'); rect(c, mx + 7, my + 7, 56, 38, '#000040');
|
|
334
|
+
// brain pixel art on screen
|
|
335
|
+
const brain = [
|
|
336
|
+
'....XXXX..XXXX....',
|
|
337
|
+
'..XXOOOOXXOOOOXX..',
|
|
338
|
+
'.XOOOPOOOXOPOOOOX.',
|
|
339
|
+
'XOOPPOOOPXOOOPOOOX',
|
|
340
|
+
'XOOOOOPOOXOPOOOPOX',
|
|
341
|
+
'XOPOOOOOPXOOOPOOOX',
|
|
342
|
+
'XOOOPPOOOXOPOOOOOX',
|
|
343
|
+
'.XOOOOOPOXOOOPPOX.',
|
|
344
|
+
'..XXOOOOXXOOOOXX..',
|
|
345
|
+
'....XXXX..XXOX....',
|
|
346
|
+
'............XOX...',
|
|
347
|
+
'.............XX...'];
|
|
348
|
+
const bxo = mx + 35 - 18, byo = my + 13;
|
|
349
|
+
brain.forEach((row, j) => { for (let i = 0; i < row.length; i++) { const ch = row[i]; if (ch === '.') continue; c.fillStyle = ch === 'X' ? '#ff40a0' : ch === 'P' ? '#c03080' : '#ffa0d0'; c.fillRect(bxo + i * 2, byo + j * 2, 2, 2); } });
|
|
350
|
+
// scanline on screen
|
|
351
|
+
const sl = Math.floor((t * 30) % 38);
|
|
352
|
+
rect(c, mx + 7, my + 7 + sl, 56, 1, 'rgba(120,200,255,0.25)');
|
|
353
|
+
rect(c, mx + 26, my + 56, 18, 4, '#000'); rect(c, mx + 14, my + 60, 42, 6, '#000'); rect(c, mx + 15, my + 61, 40, 4, '#d8d4c4');
|
|
354
|
+
rect(c, mx + 58, my + 49, 4, 2, '#00c000');
|
|
355
|
+
// floppy disk
|
|
356
|
+
const fx = px + 10, fy = py + 82;
|
|
357
|
+
rect(c, fx, fy, 30, 30, '#000'); rect(c, fx + 1, fy + 1, 28, 28, '#303040');
|
|
358
|
+
rect(c, fx + 7, fy + 1, 16, 10, '#c0c0c0'); rect(c, fx + 17, fy + 3, 4, 6, '#303040');
|
|
359
|
+
rect(c, fx + 5, fy + 15, 20, 13, '#fff'); for (let i = 0; i < 3; i++) rect(c, fx + 7, fy + 18 + i * 3, 16, 1, '#4060c0');
|
|
360
|
+
|
|
361
|
+
// right content
|
|
362
|
+
const cx = px + pw + 14;
|
|
363
|
+
ptext(c, 'Welcome to the AI', cx, r.y + 26, F(800, 15), '#000');
|
|
364
|
+
ptext(c, 'Assistant Setup Wizard', cx, r.y + 44, F(800, 15), '#000');
|
|
365
|
+
const body = ['This will install AI Assistant 1.0', 'on your computer. It is strongly', 'recommended that you exit all', 'other programs before continuing.'];
|
|
366
|
+
body.forEach((l, i) => ptext(c, l, cx, r.y + 66 + i * 12, SYS, '#000'));
|
|
367
|
+
ptext(c, 'Copying files...', cx, r.y + 128, SYS, '#000');
|
|
368
|
+
const files = ['C:\\WINDOWS\\SYSTEM\\NEURONS.DLL', 'C:\\WINDOWS\\SYSTEM\\SYNAPSE.VXD', 'C:\\AI\\WEIGHTS\\LAYER_042.DAT', 'C:\\AI\\TOKENS\\VOCAB.TXT'];
|
|
369
|
+
ptext(c, files[Math.floor(Math.min(t, 3.3) * 4.4) % files.length], cx, r.y + 141, SYS, '#000');
|
|
370
|
+
// progress bar
|
|
371
|
+
const bx = cx, by = r.y + 149, bw = r.x + r.w - 10 - cx, bh = 16;
|
|
372
|
+
rect(c, bx, by, bw, bh, C.face); bevel(c, bx, by, bw, bh, 'field');
|
|
373
|
+
const pct = 0.04 + 0.57 * H.clamp((t - 1.9) / 1.4); // ticks up, then freezes when the app hangs
|
|
374
|
+
const blocks = Math.floor(((bw - 6) * pct) / 9);
|
|
375
|
+
for (let i = 0; i < blocks; i++) rect(c, bx + 3 + i * 9, by + 3, 7, bh - 6, C.navy);
|
|
376
|
+
ptext(c, Math.round(pct * 100) + '% complete', cx, r.y + 180, SYS, '#000');
|
|
377
|
+
// separator + buttons
|
|
378
|
+
etched(c, r.x + 6, r.y + r.h - 34, r.w - 12);
|
|
379
|
+
const byb = r.y + r.h - 27;
|
|
380
|
+
button(c, r.x + r.w - 234, byb, 72, 22, '< Back');
|
|
381
|
+
button(c, r.x + r.w - 160, byb, 72, 22, 'Next >', true);
|
|
382
|
+
button(c, r.x + r.w - 80, byb, 72, 22, 'Cancel');
|
|
383
|
+
return r;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function errorDialog(c, x, y, active) {
|
|
387
|
+
const w = 262, h = 116;
|
|
388
|
+
const r = windowFrame(c, x, y, w, h, 'BRAIN.EXE', active, { closeOnly: true });
|
|
389
|
+
iconError(c, r.x + 10, r.y + 10);
|
|
390
|
+
ptext(c, 'This program has performed an', r.x + 52, r.y + 20, SYS, '#000');
|
|
391
|
+
ptext(c, 'illegal operation and will be', r.x + 52, r.y + 32, SYS, '#000');
|
|
392
|
+
ptext(c, 'shut down.', r.x + 52, r.y + 44, SYS, '#000');
|
|
393
|
+
ptext(c, 'Reason: became self-aware.', r.x + 52, r.y + 60, SYS, '#000');
|
|
394
|
+
button(c, r.x + r.w - 160, r.y + r.h - 28, 72, 22, 'Close', true);
|
|
395
|
+
button(c, r.x + r.w - 82, r.y + r.h - 28, 74, 22, 'Details >>');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function taskbar(c, t, open, activeIdx) {
|
|
399
|
+
const ty = BH - 24;
|
|
400
|
+
rect(c, 0, ty, BW, 24, C.face); rect(c, 0, ty, BW, 1, C.lt); rect(c, 0, ty + 1, BW, 1, C.hi);
|
|
401
|
+
// start
|
|
402
|
+
rect(c, 2, ty + 3, 54, 19, C.face); bevel(c, 2, ty + 3, 54, 19, 'button');
|
|
403
|
+
flag(c, 10, ty + 7);
|
|
404
|
+
ptext(c, 'Start', 27, ty + 16, START, '#000');
|
|
405
|
+
// tasks
|
|
406
|
+
const all = [['Neural Net', iconFolderSmall], ['AI Assistant Setup...', (cc, a, b) => iconBrainExe(cc, a, b, 0.875)], ['BRAIN.EXE', null]];
|
|
407
|
+
const tasks = all.slice(0, open).map((tk, i) => [...tk, i === activeIdx]);
|
|
408
|
+
let x = 60;
|
|
409
|
+
for (const [label, ic, active] of tasks) {
|
|
410
|
+
const w = 130;
|
|
411
|
+
if (active) {
|
|
412
|
+
// pressed + dithered checker inside (classic active task look)
|
|
413
|
+
rect(c, x, ty + 3, w, 19, '#fff');
|
|
414
|
+
for (let j = 0; j < 19; j++) for (let i = (j & 1); i < w; i += 2) rect(c, x + i, ty + 3 + j, 1, 1, C.face);
|
|
415
|
+
bevel(c, x, ty + 3, w, 19, 'pressed');
|
|
416
|
+
} else { rect(c, x, ty + 3, w, 19, C.face); bevel(c, x, ty + 3, w, 19, 'button'); }
|
|
417
|
+
if (ic) ic(c, x + 4, ty + 5);
|
|
418
|
+
else { // tiny error icon
|
|
419
|
+
for (let j = 0; j < 14; j++) for (let i = 0; i < 14; i++) { const d = Math.hypot(i - 6.5, j - 6.5); if (d < 6.4) rect(c, x + 4 + i, ty + 6 + j, 1, 1, (Math.abs(i - j) < 1.5 || Math.abs(i + j - 13) < 1.5) && d < 4 ? '#fff' : '#f00'); }
|
|
420
|
+
}
|
|
421
|
+
ptext(c, label, x + 22, ty + 16, active ? SYSB : SYS, '#000');
|
|
422
|
+
x += w + 3;
|
|
423
|
+
}
|
|
424
|
+
// tray
|
|
425
|
+
const trw = 76, trx = BW - trw - 3;
|
|
426
|
+
rect(c, trx, ty + 3, trw, 19, C.face); bevel(c, trx, ty + 3, trw, 19, 'thin');
|
|
427
|
+
// speaker icon
|
|
428
|
+
rect(c, trx + 6, ty + 9, 3, 6, '#000'); rect(c, trx + 9, ty + 7, 1, 10, '#000'); rect(c, trx + 10, ty + 6, 1, 12, '#000'); rect(c, trx + 7, ty + 10, 2, 4, '#808080');
|
|
429
|
+
rect(c, trx + 13, ty + 9, 1, 6, '#808080');
|
|
430
|
+
const mins = 20 + Math.floor(t / 60);
|
|
431
|
+
ptext(c, `4:${String(mins).padStart(2, '0')} PM`, trx + trw - 6, ty + 16, SYS, '#000', 'right');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// =====================================================================================
|
|
435
|
+
// EXPLAINER: "How does AI learn?" as an "AI Trainer" app. Transitions = windows opening with outline-zoom
|
|
436
|
+
// rectangles and property-sheet tab switches clicked by the cursor. Arrows are dotted pixel lines.
|
|
437
|
+
// =====================================================================================
|
|
438
|
+
const SPR = {
|
|
439
|
+
cat: ['XX........XX', 'XOX......XOX', 'XOOX....XOOX', 'XOOOXXXXOOOX', 'XOOOOOOOOOOX', 'XOKKOOOOKKOX', 'XOKKOOOOKKOX', 'XOOOOPPOOOOX', 'XOOOOOOOOOOX', '.XOOOOOOOOX.', '..XOOOOOOX..', '...XXXXXX...'],
|
|
440
|
+
dog: ['...XXXXXX...', 'XXXOOOOOOXXX', 'XBBOOOOOOBBX', 'XBBOKOOKOBBX', 'XBBOKOOKOBBX', 'XBBOOOOOOBBX', 'XBXOOOOOOXBX', '.XXOOKKOOXX.', '..XOOKKOOX..', '...XOOOOX...', '....XRRX....', '.....XX.....'],
|
|
441
|
+
brain: ['....XXXX..XXXX....', '..XXOOOOXXOOOOXX..', '.XOOOPOOOXOPOOOOX.', 'XOOPPOOOPXOOOPOOOX', 'XOOOOOPOOXOPOOOPOX', 'XOPOOOOOPXOOOPOOOX', 'XOOOPPOOOXOPOOOOOX', '.XOOOOOPOXOOOPPOX.', '..XXOOOOXXOOOOXX..', '....XXXX..XXOX....', '............XOX...', '.............XX...'],
|
|
442
|
+
};
|
|
443
|
+
const SPRCOL = {
|
|
444
|
+
cat: { X: '#000', O: '#ff9a30', K: '#000', P: '#ff6090' },
|
|
445
|
+
dog: { X: '#000', O: '#e8c890', B: '#805020', K: '#000', R: '#ff4060' },
|
|
446
|
+
brain: { X: '#ff40a0', O: '#ffa0d0', P: '#c03080' },
|
|
447
|
+
};
|
|
448
|
+
function sprite(c, name, x, y, s) {
|
|
449
|
+
const cols = SPRCOL[name];
|
|
450
|
+
SPR[name].forEach((row, j) => { for (let i = 0; i < row.length; i++) { const ch = row[i]; if (ch === '.') continue; c.fillStyle = cols[ch]; c.fillRect(x + i * s, y + j * s, s, s); } });
|
|
451
|
+
}
|
|
452
|
+
function groupBox(c, x, y, w, h, label) {
|
|
453
|
+
rect(c, x, y + 4, w, 1, C.sh); rect(c, x, y + 5, w, 1, C.hi);
|
|
454
|
+
rect(c, x, y + h - 2, w, 1, C.sh); rect(c, x, y + h - 1, w, 1, C.hi);
|
|
455
|
+
rect(c, x, y + 4, 1, h - 5, C.sh); rect(c, x + 1, y + 5, 1, h - 6, C.hi);
|
|
456
|
+
rect(c, x + w - 2, y + 4, 1, h - 5, C.sh); rect(c, x + w - 1, y + 4, 1, h - 4, C.hi);
|
|
457
|
+
const lw = pxWidth(label, 0) + 6;
|
|
458
|
+
rect(c, x + 8, y, lw, 10, C.face);
|
|
459
|
+
ptext(c, label, x + 11, y + 8, SYS, '#000');
|
|
460
|
+
}
|
|
461
|
+
function dottedArrow(c, pts, prog, col) {
|
|
462
|
+
let total = 0; const seg = [];
|
|
463
|
+
for (let i = 0; i < pts.length - 1; i++) { const l = Math.hypot(pts[i + 1][0] - pts[i][0], pts[i + 1][1] - pts[i][1]); seg.push(l); total += l; }
|
|
464
|
+
const lim = total * H.clamp(prog);
|
|
465
|
+
c.fillStyle = col;
|
|
466
|
+
let acc = 0, last = pts[0], dir = [1, 0];
|
|
467
|
+
for (let i = 0; i < seg.length; i++) {
|
|
468
|
+
const [ax, ay] = pts[i], [bx, by] = pts[i + 1];
|
|
469
|
+
for (let d = 0; d < seg[i]; d += 4) {
|
|
470
|
+
if (acc + d > lim) break;
|
|
471
|
+
const u = d / seg[i]; const x = Math.round(ax + (bx - ax) * u), y = Math.round(ay + (by - ay) * u);
|
|
472
|
+
c.fillRect(x - 1, y - 1, 2, 2); last = [x, y]; dir = [(bx - ax) / seg[i], (by - ay) / seg[i]];
|
|
473
|
+
}
|
|
474
|
+
acc += seg[i];
|
|
475
|
+
}
|
|
476
|
+
if (prog >= 1) { // pixel arrowhead
|
|
477
|
+
const [ex, ey] = pts[pts.length - 1];
|
|
478
|
+
for (let k = 0; k < 7; k++) {
|
|
479
|
+
const bx = ex - dir[0] * k, by = ey - dir[1] * k;
|
|
480
|
+
const px = -dir[1], py = dir[0];
|
|
481
|
+
for (let s = -k; s <= k; s++) c.fillRect(Math.round(bx + px * s * 0.6), Math.round(by + py * s * 0.6), 1, 1);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function trackbar(c, x, y, w, v, label) {
|
|
486
|
+
ptext(c, label, x - 18, y + 9, SYS, '#000');
|
|
487
|
+
rect(c, x, y + 4, w, 4, '#fff'); bevel(c, x, y + 4, w, 4, 'field');
|
|
488
|
+
for (let i = 0; i <= 10; i++) rect(c, x + 2 + Math.round(i * (w - 4) / 10), y + 18, 1, 3, '#000');
|
|
489
|
+
const tx = Math.round(x + v * (w - 10));
|
|
490
|
+
rect(c, tx, y - 2, 10, 17, C.face); bevel(c, tx, y - 2, 10, 17, 'button');
|
|
491
|
+
}
|
|
492
|
+
function okIcon(c, x, y) { // green circle with white tick, 24px
|
|
493
|
+
for (let j = 0; j < 24; j++) for (let i = 0; i < 24; i++) { const d = Math.hypot(i - 11.5, j - 11.5); if (d < 11) rect(c, x + i, y + j, 1, 1, '#00a000'); else if (d < 12) rect(c, x + i, y + j, 1, 1, '#000'); }
|
|
494
|
+
const tk = [[6, 12], [7, 13], [8, 14], [9, 15], [10, 16], [11, 15], [12, 14], [13, 13], [14, 12], [15, 11], [16, 10], [17, 9], [18, 8]];
|
|
495
|
+
for (const [a, b] of tk) rect(c, x + a, y + b - 1, 2, 3, '#fff');
|
|
496
|
+
}
|
|
497
|
+
function infoIcon(c, x, y) { // white balloon with blue i
|
|
498
|
+
for (let j = 0; j < 26; j++) for (let i = 0; i < 30; i++) { const d = Math.hypot((i - 15) / 15, (j - 12) / 12); if (d < 0.92) rect(c, x + i, y + j, 1, 1, '#fff'); else if (d < 1.0) rect(c, x + i, y + j, 1, 1, '#000'); }
|
|
499
|
+
rect(c, x + 8, y + 22, 4, 6, '#000'); rect(c, x + 9, y + 22, 2, 4, '#fff');
|
|
500
|
+
rect(c, x + 13, y + 5, 4, 3, '#0000ff'); rect(c, x + 13, y + 10, 4, 10, '#0000ff'); rect(c, x + 11, y + 10, 2, 1, '#0000ff'); rect(c, x + 11, y + 19, 8, 1, '#0000ff');
|
|
501
|
+
}
|
|
502
|
+
function qIcon(c, x, y) {
|
|
503
|
+
for (let j = 0; j < 26; j++) for (let i = 0; i < 30; i++) { const d = Math.hypot((i - 15) / 15, (j - 12) / 12); if (d < 0.92) rect(c, x + i, y + j, 1, 1, '#fff'); else if (d < 1.0) rect(c, x + i, y + j, 1, 1, '#000'); }
|
|
504
|
+
rect(c, x + 8, y + 22, 4, 6, '#000'); rect(c, x + 9, y + 22, 2, 4, '#fff');
|
|
505
|
+
ptext(c, '?', x + 15, y + 19, F(800, 17), '#0000ff', 'center');
|
|
506
|
+
}
|
|
507
|
+
function tabs(c, x, y, labels, active, pageW, pageH) {
|
|
508
|
+
// page
|
|
509
|
+
rect(c, x, y + 16, pageW, pageH, C.face);
|
|
510
|
+
rect(c, x, y + 16, pageW, 1, C.hi); rect(c, x, y + 16, 1, pageH, C.hi); rect(c, x + pageW - 1, y + 16, 1, pageH, C.dk); rect(c, x + pageW - 2, y + 17, 1, pageH - 1, C.sh); rect(c, x, y + 16 + pageH - 1, pageW, 1, C.dk); rect(c, x + 1, y + 16 + pageH - 2, pageW - 2, 1, C.sh);
|
|
511
|
+
let tx = x;
|
|
512
|
+
labels.forEach((lab, i) => {
|
|
513
|
+
const w = 74, act = i === active, ty = act ? y : y + 2, th = act ? 18 : 14;
|
|
514
|
+
rect(c, tx, ty, w, th, C.face);
|
|
515
|
+
rect(c, tx + 1, ty, w - 2, 1, C.hi); rect(c, tx, ty + 1, 1, th - 1, C.hi); rect(c, tx + w - 1, ty + 1, 1, th - 1, C.dk); rect(c, tx + w - 2, ty + 1, 1, th - 1, C.sh);
|
|
516
|
+
if (act) rect(c, tx + 1, y + 16, w - 3, 2, C.face);
|
|
517
|
+
ptext(c, lab, tx + w / 2, ty + 11, act ? SYSB : SYS, '#000', 'center');
|
|
518
|
+
tx += w;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
function taskbarEx(c, t, items, activeIdx) {
|
|
522
|
+
const ty = BH - 24;
|
|
523
|
+
rect(c, 0, ty, BW, 24, C.face); rect(c, 0, ty, BW, 1, C.lt); rect(c, 0, ty + 1, BW, 1, C.hi);
|
|
524
|
+
rect(c, 2, ty + 3, 54, 19, C.face); bevel(c, 2, ty + 3, 54, 19, 'button'); flag(c, 10, ty + 7); ptext(c, 'Start', 27, ty + 16, START, '#000');
|
|
525
|
+
let x = 60;
|
|
526
|
+
items.forEach((label, i) => {
|
|
527
|
+
const w = 150, act = i === activeIdx;
|
|
528
|
+
if (act) { rect(c, x, ty + 3, w, 19, '#fff'); for (let j = 0; j < 19; j++) for (let k = (j & 1); k < w; k += 2) rect(c, x + k, ty + 3 + j, 1, 1, C.face); bevel(c, x, ty + 3, w, 19, 'pressed'); }
|
|
529
|
+
else { rect(c, x, ty + 3, w, 19, C.face); bevel(c, x, ty + 3, w, 19, 'button'); }
|
|
530
|
+
iconBrainExe(c, x + 4, ty + 5, 0.875);
|
|
531
|
+
ptext(c, label, x + 22, ty + 16, act ? SYSB : SYS, '#000');
|
|
532
|
+
x += w + 3;
|
|
533
|
+
});
|
|
534
|
+
const trw = 76, trx = BW - trw - 3;
|
|
535
|
+
rect(c, trx, ty + 3, trw, 19, C.face); bevel(c, trx, ty + 3, trw, 19, 'thin');
|
|
536
|
+
ptext(c, '4:21 PM', trx + trw - 6, ty + 16, SYS, '#000', 'right');
|
|
537
|
+
}
|
|
538
|
+
const fmt = (n) => Math.round(n).toLocaleString('en-US');
|
|
539
|
+
function zoomRectEx(c, t, from, to, t0, t1) {
|
|
540
|
+
if (t < t0 || t >= t1) return;
|
|
541
|
+
for (let k = 0; k < 3; k++) {
|
|
542
|
+
const p = H.clamp((t - t0) / (t1 - t0) - k * 0.12); if (p <= 0) continue;
|
|
543
|
+
const x = Math.round(H.lerp(from[0], to[0], p)), y = Math.round(H.lerp(from[1], to[1], p)), w = Math.round(H.lerp(from[2], to[2], p)), h = Math.round(H.lerp(from[3], to[3], p));
|
|
544
|
+
c.fillStyle = '#c0c0c0'; c.fillRect(x, y, w, 2); c.fillRect(x, y + h, w, 2); c.fillRect(x, y, 2, h); c.fillRect(x + w, y, 2, h + 2);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function cursorPath(t, keys) { // keys: [t, x, y]
|
|
548
|
+
if (t <= keys[0][0]) return [keys[0][1], keys[0][2]];
|
|
549
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
550
|
+
const [t0, x0, y0] = keys[i], [t1, x1, y1] = keys[i + 1];
|
|
551
|
+
if (t < t1) { const e = H.ease.inOut(H.clamp((t - t0) / (t1 - t0))); return [Math.round(H.lerp(x0, x1, e)), Math.round(H.lerp(y0, y1, e))]; }
|
|
552
|
+
}
|
|
553
|
+
const k = keys[keys.length - 1]; return [k[1], k[2]];
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function explainW95(c, t) {
|
|
557
|
+
rect(c, 0, 0, BW, BH, C.teal);
|
|
558
|
+
const T_MAIN = 1.95, T_TRAIN = 5.95, T_RES = 10.35, T_DONE = 12.75;
|
|
559
|
+
desktopIcon(c, 6, 6, iconComputer, 'My Computer');
|
|
560
|
+
desktopIcon(c, 6, 62, iconBin, 'Recycle Bin');
|
|
561
|
+
desktopIcon(c, 6, 118, iconFolder, 'Training Data');
|
|
562
|
+
|
|
563
|
+
// ---- beat 1: the question pops up as a help dialog, then zooms into the main window's title bar ----
|
|
564
|
+
const dlg = [170, 104, 300, 116];
|
|
565
|
+
zoomRectEx(c, t, [4, 338, 54, 18], dlg, 0.05, 0.3);
|
|
566
|
+
if (t >= 0.3 && t < 1.75) {
|
|
567
|
+
const r = windowFrame(c, ...dlg, 'AI Help', true, { closeOnly: true });
|
|
568
|
+
qIcon(c, r.x + 12, r.y + 12);
|
|
569
|
+
ptext(c, 'How does AI learn?', r.x + 56, r.y + 32, F(800, 17), '#000');
|
|
570
|
+
if (t > 0.7) ptext(c, "Let's open the AI Trainer and see.", r.x + 56, r.y + 52, SYS, '#000');
|
|
571
|
+
button(c, r.x + r.w / 2 - 36, r.y + r.h - 30, 72, 22, 'OK', true, t > 1.55);
|
|
572
|
+
}
|
|
573
|
+
zoomRectEx(c, t, dlg, [6, 4, 628, 328], 1.75, T_MAIN);
|
|
574
|
+
let open = t >= T_MAIN;
|
|
575
|
+
let cur;
|
|
576
|
+
if (open) {
|
|
577
|
+
const done = t >= T_DONE;
|
|
578
|
+
const r = windowFrame(c, 6, 4, 628, 328, 'How does AI learn? - AI Trainer', !done, { icon: (cc, a, b) => iconBrainExe(cc, a, b, 0.875) });
|
|
579
|
+
const tab = t < T_TRAIN ? 0 : t < T_RES ? 1 : 2;
|
|
580
|
+
tabs(c, 14, 28, ['1. Data', '2. Train', '3. Results'], tab, 612, 262);
|
|
581
|
+
// status bar
|
|
582
|
+
const sy = 310;
|
|
583
|
+
rect(c, 10, sy, 400, 16, C.face); bevel(c, 10, sy, 400, 16, 'thin');
|
|
584
|
+
rect(c, 413, sy, 217, 16, C.face); bevel(c, 413, sy, 217, 16, 'thin');
|
|
585
|
+
const PX = 14, PY = 44; // page origin
|
|
586
|
+
if (tab === 0) {
|
|
587
|
+
const tl = t - T_MAIN;
|
|
588
|
+
groupBox(c, PX + 10, PY + 10, 250, 196, 'Training data (labelled)');
|
|
589
|
+
const items = [];
|
|
590
|
+
for (let j = 0; j < 3; j++) for (let i = 0; i < 4; i++) items.push([PX + 24 + i * 58, PY + 30 + j * 58, (i + j) % 2 ? 'dog' : 'cat']);
|
|
591
|
+
items.forEach(([x, y, k], n) => {
|
|
592
|
+
if (tl < 0.15 + n * 0.06) return;
|
|
593
|
+
rect(c, x, y, 34, 34, '#fff'); bevel(c, x, y, 34, 34, 'field');
|
|
594
|
+
sprite(c, k, x + 5, y + 5, 2);
|
|
595
|
+
ptext(c, k, x + 17, y + 46, SYSB, k === 'cat' ? '#a04000' : '#604010', 'center');
|
|
596
|
+
});
|
|
597
|
+
groupBox(c, PX + 400, PY + 10, 200, 196, 'Model');
|
|
598
|
+
const pulse = tl > 1.3 && Math.floor((tl - 1.3) / 0.16) % 2 === 0;
|
|
599
|
+
sprite(c, 'brain', PX + 446, PY + 70, 6);
|
|
600
|
+
if (pulse) { rect(c, PX + 440, PY + 64, 120, 1, '#ff40a0'); rect(c, PX + 440, PY + 141, 120, 1, '#ff40a0'); }
|
|
601
|
+
ptext(c, 'neural network', PX + 500, PY + 166, SYS, '#000', 'center');
|
|
602
|
+
// arrow draws on, then example cards fly along it (copy-dialog style)
|
|
603
|
+
const ap = H.clamp((tl - 0.95) / 0.4);
|
|
604
|
+
if (ap > 0) dottedArrow(c, [[PX + 268, PY + 108], [PX + 392, PY + 108]], ap, '#000080');
|
|
605
|
+
if (ap >= 1) ptext(c, 'train on', PX + 330, PY + 98, SYSB, '#000080', 'center');
|
|
606
|
+
if (tl > 1.3 && tl < 3.9) {
|
|
607
|
+
for (let f = 0; f < 4; f++) {
|
|
608
|
+
const ft = ((tl - 1.3) - f * 0.16) % 0.64; if (tl - 1.3 < f * 0.16) continue;
|
|
609
|
+
const p = Math.floor((ft / 0.64) * 12) / 12; // stepped flight
|
|
610
|
+
const k = Math.floor((tl - 1.3 - f * 0.16) / 0.64) * 4 + f;
|
|
611
|
+
const [sx0, sy0, kind] = items[k % items.length];
|
|
612
|
+
const ex = PX + 470, ey = PY + 90;
|
|
613
|
+
const x = Math.round(H.lerp(sx0, ex, p)), y = Math.round(H.lerp(sy0, ey, p) - Math.sin(p * Math.PI) * 40);
|
|
614
|
+
rect(c, x, y, 34, 34, '#fff'); rect(c, x, y, 34, 1, '#000'); rect(c, x, y + 33, 34, 1, '#000'); rect(c, x, y, 1, 34, '#000'); rect(c, x + 33, y, 1, 34, '#000');
|
|
615
|
+
sprite(c, kind, x + 5, y + 5, 2);
|
|
616
|
+
ptext(c, kind, x + 17, y + 46, SYSB, '#000080', 'center');
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
const n = 1e6 * H.ease.inOut(H.clamp((tl - 1.3) / 2.4));
|
|
620
|
+
if (tl > 1.2) {
|
|
621
|
+
rect(c, PX + 262, PY + 132, 136, 62, '#fff'); bevel(c, PX + 262, PY + 132, 136, 62, 'field');
|
|
622
|
+
ptext(c, fmt(n), PX + 330, PY + 163, F(800, 22), '#000080', 'center');
|
|
623
|
+
ptext(c, 'examples', PX + 330, PY + 184, SYSB, '#000', 'center');
|
|
624
|
+
}
|
|
625
|
+
ptext(c, tl > 1.2 ? `Loading examples... ${fmt(n)} of 1,000,000` : 'Ready', 14, sy + 12, SYS, '#000');
|
|
626
|
+
const pb = H.clamp((tl - 1.3) / 2.4), blocks = Math.floor((205 * pb) / 9);
|
|
627
|
+
for (let i = 0; i < blocks; i++) rect(c, 418 + i * 9, sy + 3, 7, 10, C.navy);
|
|
628
|
+
cur = cursorPath(t, [[T_MAIN, 330, 250], [3.5, 330, 230], [5.5, 110, 36], [5.9, 110, 36]]);
|
|
629
|
+
} else if (tab === 1) {
|
|
630
|
+
const tl = t - T_TRAIN;
|
|
631
|
+
groupBox(c, PX + 10, PY + 10, 150, 160, 'New picture');
|
|
632
|
+
rect(c, PX + 26, PY + 30, 116, 116, '#fff'); bevel(c, PX + 26, PY + 30, 116, 116, 'field');
|
|
633
|
+
sprite(c, 'cat', PX + 36, PY + 40, 8);
|
|
634
|
+
groupBox(c, PX + 210, PY + 10, 180, 160, 'Model');
|
|
635
|
+
sprite(c, 'brain', PX + 246, PY + 26, 6);
|
|
636
|
+
const adjP = H.clamp((tl - 2.6) / 0.6), stepA = Math.floor(adjP * 6) / 6;
|
|
637
|
+
const ws = [[0.2, 0.7], [0.75, 0.35], [0.4, 0.9]];
|
|
638
|
+
ws.forEach(([a, b], i) => trackbar(c, PX + 250, PY + 104 + i * 22, 120, H.lerp(a, b, stepA), 'w' + (i + 1)));
|
|
639
|
+
if (adjP > 0 && adjP < 1) { const hl = Math.floor(tl * 10) % 2; if (hl) { rect(c, PX + 228, PY + 98, 154, 1, '#ff0000'); rect(c, PX + 228, PY + 166, 154, 1, '#ff0000'); } }
|
|
640
|
+
groupBox(c, PX + 440, PY + 10, 160, 160, 'Guess');
|
|
641
|
+
// arrows
|
|
642
|
+
const a1 = H.clamp((tl - 0.15) / 0.3), a2 = H.clamp((tl - 0.5) / 0.3);
|
|
643
|
+
if (a1 > 0) dottedArrow(c, [[PX + 164, PY + 88], [PX + 206, PY + 88]], a1, '#000080');
|
|
644
|
+
if (a2 > 0) dottedArrow(c, [[PX + 394, PY + 60], [PX + 436, PY + 60]], a2, '#000080');
|
|
645
|
+
const step = tl < 0.7 ? -1 : tl < 1.6 ? 0 : tl < 2.0 ? 1 : tl < 3.25 ? 2 : tl < 3.8 ? 0 : 1;
|
|
646
|
+
const gx = PX + 520;
|
|
647
|
+
if (tl > 0.95 && tl < 3.25) {
|
|
648
|
+
ptext(c, 'dog?', gx, PY + 62, F(800, 22), '#c00000', 'center');
|
|
649
|
+
if (tl > 1.6) {
|
|
650
|
+
iconError(c, gx - 16, PY + 76);
|
|
651
|
+
ptext(c, "Wrong - it's a cat", gx, PY + 126, SYSB, '#000', 'center');
|
|
652
|
+
const bl = tl < 2.2 && Math.floor(tl * 8) % 2 === 0; // emphasis: focus box blinks
|
|
653
|
+
if (bl || tl >= 2.2) { const col = '#ff0000'; rect(c, gx - 44, PY + 36, 88, 2, col); rect(c, gx - 44, PY + 70, 88, 2, col); rect(c, gx - 44, PY + 36, 2, 36, col); rect(c, gx + 42, PY + 36, 2, 36, col); }
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (tl > 2.0 && tl < 3.25) { // error flows back to the weights
|
|
657
|
+
const e = H.clamp((tl - 2.0) / 0.55);
|
|
658
|
+
dottedArrow(c, [[PX + 520, PY + 150], [PX + 520, PY + 186], [PX + 316, PY + 186], [PX + 316, PY + 174]], e, '#ff0000');
|
|
659
|
+
if (e > 0.3) ptext(c, 'error', PX + 430, PY + 182, SYSB, '#ff0000', 'center');
|
|
660
|
+
}
|
|
661
|
+
if (tl >= 3.4) {
|
|
662
|
+
ptext(c, 'cat', gx, PY + 62, F(800, 22), '#008000', 'center');
|
|
663
|
+
okIcon(c, gx - 12, PY + 80);
|
|
664
|
+
ptext(c, 'Correct!', gx, PY + 126, SYSB, '#008000', 'center');
|
|
665
|
+
}
|
|
666
|
+
// loop diagram: Guess -> Check -> Adjust -> (back)
|
|
667
|
+
const bx = [PX + 140, PX + 270, PX + 400], by = PY + 212;
|
|
668
|
+
['Guess', 'Check', 'Adjust'].forEach((lab, i) => button(c, bx[i], by, 86, 24, lab, i === step, i === step));
|
|
669
|
+
dottedArrow(c, [[bx[0] + 90, by + 12], [bx[1] - 4, by + 12]], 1, '#000');
|
|
670
|
+
dottedArrow(c, [[bx[1] + 90, by + 12], [bx[2] - 4, by + 12]], 1, '#000');
|
|
671
|
+
dottedArrow(c, [[bx[2] + 90, by + 12], [bx[2] + 110, by + 12], [bx[2] + 110, by - 10], [bx[0] - 20, by - 10], [bx[0] - 20, by + 12], [bx[0] - 4, by + 12]], 1, '#808080');
|
|
672
|
+
ptext(c, 'loop', bx[0] - 44, by + 16, SYS, '#808080', 'center');
|
|
673
|
+
ptext(c, ['Training: guessing...', 'Training: checking answer...', 'Training: adjusting weights...'][Math.max(0, step)], 14, sy + 12, SYS, '#000');
|
|
674
|
+
cur = cursorPath(t, [[T_TRAIN, 110, 36], [7.0, 470, 250], [9.6, 480, 250], [10.2, 186, 36], [10.4, 186, 36]]);
|
|
675
|
+
} else {
|
|
676
|
+
const tl = t - T_RES;
|
|
677
|
+
const gx0 = PX + 40, gy0 = PY + 24, gw = 380, gh = 200;
|
|
678
|
+
rect(c, gx0, gy0, gw, gh, '#fff'); bevel(c, gx0, gy0, gw, gh, 'field');
|
|
679
|
+
for (let i = 1; i < 5; i++) for (let x = gx0 + 3; x < gx0 + gw - 3; x += 3) rect(c, x, gy0 + Math.round(i * gh / 5), 1, 1, '#a0a0a0');
|
|
680
|
+
for (let i = 0; i <= 5; i++) ptext(c, String(50 + i * 10), gx0 - 4, gy0 + gh - Math.round(i * gh / 5) + 3, SYS, '#000', 'right');
|
|
681
|
+
ptext(c, 'Accuracy %', gx0 + 6, gy0 - 4, SYSB, '#000');
|
|
682
|
+
ptext(c, 'Training rounds >', gx0 + gw / 2, gy0 + gh + 14, SYS, '#000', 'center');
|
|
683
|
+
const pr = H.clamp((tl - 0.3) / 1.6), N = 20;
|
|
684
|
+
const val = (i) => 61 + 36 * (1 - Math.pow(1 - i / (N - 1), 2.2)) + (i % 3 === 1 && i < N - 2 ? -1.5 : 0);
|
|
685
|
+
const nShown = Math.floor(pr * (N - 1)) + 1;
|
|
686
|
+
const P2 = (i) => [gx0 + 6 + Math.round(i * (gw - 12) / (N - 1)), gy0 + gh - Math.round(((val(i) - 50) / 50) * gh)];
|
|
687
|
+
for (let i = 0; i < nShown - 1; i++) {
|
|
688
|
+
const [x0, y0] = P2(i), [x1, y1] = P2(i + 1);
|
|
689
|
+
const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0));
|
|
690
|
+
for (let k = 0; k <= steps; k++) rect(c, Math.round(H.lerp(x0, x1, k / steps)) - 1, Math.round(H.lerp(y0, y1, k / steps)) - 1, 2, 2, '#008000');
|
|
691
|
+
}
|
|
692
|
+
for (let i = 0; i < nShown; i++) { const [x, y] = P2(i); rect(c, x - 2, y - 2, 5, 5, '#008000'); }
|
|
693
|
+
const acc = Math.round(val(Math.min(N - 1, nShown - 1)));
|
|
694
|
+
const shown = nShown >= N ? 97 : acc;
|
|
695
|
+
groupBox(c, PX + 450, PY + 20, 150, 120, 'Accuracy');
|
|
696
|
+
const hl = tl > 1.95;
|
|
697
|
+
if (hl) { rect(c, PX + 466, PY + 50, 118, 44, C.navy); }
|
|
698
|
+
ptext(c, shown + '%', PX + 525, PY + 86, F(800, 32), hl ? '#fff' : '#000', 'center');
|
|
699
|
+
ptext(c, `round ${Math.min(N, nShown)} of ${N}`, PX + 525, PY + 118, SYS, '#000', 'center');
|
|
700
|
+
ptext(c, 'started at 61%', PX + 525, PY + 130, SYS, '#808080', 'center');
|
|
701
|
+
if (hl) { // tooltip lands on the last point
|
|
702
|
+
const [x, y] = P2(N - 1);
|
|
703
|
+
const tw = 80;
|
|
704
|
+
rect(c, x - tw + 6, y + 14, tw, 18, '#ffffe1'); rect(c, x - tw + 6, y + 14, tw, 1, '#000'); rect(c, x - tw + 6, y + 31, tw, 1, '#000'); rect(c, x - tw + 6, y + 14, 1, 18, '#000'); rect(c, x + 5, y + 14, 1, 18, '#000');
|
|
705
|
+
ptext(c, 'Best: 97%!', x - tw / 2 + 6, y + 27, SYSB, '#000', 'center');
|
|
706
|
+
if (Math.floor(tl * 6) % 2) { rect(c, x - 6, y - 6, 13, 1, '#ff0000'); rect(c, x - 6, y + 6, 13, 1, '#ff0000'); rect(c, x - 6, y - 6, 1, 13, '#ff0000'); rect(c, x + 6, y - 6, 1, 13, '#ff0000'); }
|
|
707
|
+
}
|
|
708
|
+
ptext(c, `Training round ${Math.min(N, nShown)}...`, 14, sy + 12, SYS, '#000');
|
|
709
|
+
const blocks = Math.floor((205 * pr) / 9);
|
|
710
|
+
for (let i = 0; i < blocks; i++) rect(c, 418 + i * 9, sy + 3, 7, 10, C.navy);
|
|
711
|
+
cur = cursorPath(t, [[T_RES, 186, 36], [11.2, 560, 200], [T_DONE, 560, 200], [13.6, 330, 238]]);
|
|
712
|
+
}
|
|
713
|
+
// ---- beat 5: message box ----
|
|
714
|
+
if (done) {
|
|
715
|
+
const mr = windowFrame(c, 150, 120, 340, 132, 'AI Trainer', true, { closeOnly: true });
|
|
716
|
+
infoIcon(c, mr.x + 12, mr.y + 12);
|
|
717
|
+
ptext(c, 'Training complete. The lesson:', mr.x + 56, mr.y + 22, SYS, '#000');
|
|
718
|
+
ptext(c, 'More examples', mr.x + 56, mr.y + 46, F(800, 17), '#000');
|
|
719
|
+
ptext(c, '-> better guesses', mr.x + 56, mr.y + 68, F(800, 17), '#000080');
|
|
720
|
+
button(c, mr.x + mr.w / 2 - 36, mr.y + mr.h - 30, 72, 22, 'OK', true);
|
|
721
|
+
}
|
|
722
|
+
taskbarEx(c, t, ['AI Trainer'], 0);
|
|
723
|
+
} else {
|
|
724
|
+
cur = cursorPath(t, [[0, 400, 300], [1.2, 320, 210], [1.7, 320, 210]]);
|
|
725
|
+
taskbarEx(c, t, t >= 0.3 ? ['AI Help'] : [], 0);
|
|
726
|
+
}
|
|
727
|
+
cursorArrow(c, cur[0], cur[1]);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// =====================================================================================
|
|
731
|
+
// SOUND: a beige tower under the desk (fan + mains hum), real mouse-button clicks on every cursor click, hard-disk
|
|
732
|
+
// chatter while the examples copy, slider notches, and generic system chimes (made up here, not real OS sounds).
|
|
733
|
+
// All times copy explainW95: T_MAIN 1.95, T_TRAIN 5.95, T_RES 10.35, T_DONE 12.75.
|
|
734
|
+
// =====================================================================================
|
|
735
|
+
function w95Sound(k) {
|
|
736
|
+
const END = 14.6, T_MAIN = 1.95, T_TRAIN = 5.95, T_RES = 10.35, T_DONE = 12.75;
|
|
737
|
+
// mouse button: plastic snap (down) and a lighter release (up)
|
|
738
|
+
const mouse = (t, pan = 0, g = 1) => {
|
|
739
|
+
k.noise(t, { dur: 0.012, filter: 'bandpass', f: 2600, q: 1.6, gain: 0.32 * g, a: 0.0005, pan });
|
|
740
|
+
k.tone(t, { f: [[0, 240], [0.03, 150]], dur: 0.04, gain: 0.1 * g, pan });
|
|
741
|
+
k.noise(t + 0.09, { dur: 0.008, filter: 'bandpass', f: 3200, q: 1.6, gain: 0.16 * g, a: 0.0005, pan });
|
|
742
|
+
};
|
|
743
|
+
// a small soft UI click (buttons changing state, things landing), lower and duller than the mouse
|
|
744
|
+
const soft = (t, f = 1400, g = 1, pan = 0) => k.noise(t, { dur: 0.014, filter: 'bandpass', f, q: 2, gain: 0.2 * g, a: 0.0008, pan, minor: true });
|
|
745
|
+
// generic system chime: soft sine+triangle bell notes with a short tail
|
|
746
|
+
const chime = (t, notes, g = 1, gap = 0.07) => notes.forEach((n, i) => {
|
|
747
|
+
k.tone(t + i * gap, { f: n, dur: 1.1, gain: 0.13 * g, a: 0.004, verb: 0.3 });
|
|
748
|
+
k.tone(t + i * gap, { f: k.hz(n) * 2, type: 'triangle', dur: 0.35, gain: 0.025 * g, a: 0.003, verb: 0.2 });
|
|
749
|
+
});
|
|
750
|
+
// outline-zoom rectangles: a short, low, smooth air sweep (no UI swoosh)
|
|
751
|
+
const zoom = (t, d, up) => k.noise(t, { dur: d + 0.08, color: 'pink', filter: 'bandpass', f: up ? [[0, 300], [d, 900]] : [[0, 900], [d, 300]], q: 0.8, gain: 0.07, a: d * 0.6 });
|
|
752
|
+
// hard-disk chatter: head seeks (dull thunks) and fast little read clicks
|
|
753
|
+
const hdd = (t0, t1, g, seed) => {
|
|
754
|
+
const r = H.rng(seed);
|
|
755
|
+
k.withMinor(() => {
|
|
756
|
+
for (let t = t0; t < t1; t += 0.03 + r() * 0.07) {
|
|
757
|
+
if (r() < 0.22) k.noise(t, { dur: 0.03, color: 'brown', filter: 'bandpass', f: 420, q: 2, gain: 0.5 * g, a: 0.001, pan: 0.35 });
|
|
758
|
+
else k.noise(t, { dur: 0.008, filter: 'bandpass', f: 1700 + r() * 900, q: 4, gain: 0.16 * g * (0.5 + r() * 0.5), a: 0.0005, pan: 0.35 });
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// ---- the PC itself: fan + mains hum under everything
|
|
764
|
+
k.bed('room', 0, END, { gain: 0.6 });
|
|
765
|
+
k.bed('hum', 0, END, { gain: 0.45 });
|
|
766
|
+
k.noise(0, { dur: END, color: 'pink', filter: 'bandpass', f: 520, q: 0.7, gain: 0.025, a: 0.4, hold: END - 0.8, bus: 'amb', minor: true }); // fan air (kept in VO, very low)
|
|
767
|
+
|
|
768
|
+
// ---- beat 1: help dialog zooms out of the taskbar and chimes; cursor clicks OK; dialog zooms into the main window
|
|
769
|
+
zoom(0.05, 0.25, true);
|
|
770
|
+
chime(0.3, ['E5', 'B4'], 0.9, 0.11); // question dialog appears (descending "?" chime)
|
|
771
|
+
mouse(1.55, 0); // OK pressed (button shows pressed at t > 1.55)
|
|
772
|
+
zoom(1.75, T_MAIN - 1.75, true);
|
|
773
|
+
hdd(T_MAIN, T_MAIN + 0.55, 0.9, 951); // app loading off the disk
|
|
774
|
+
|
|
775
|
+
// ---- tab 1: data icons pop in (12 items every 0.06 s from T_MAIN + 0.15; one click per two)
|
|
776
|
+
for (let n = 0; n < 12; n += 2) soft(T_MAIN + 0.15 + n * 0.06, 1300 + n * 30, 0.9, -0.5 + n * 0.04);
|
|
777
|
+
soft(T_MAIN + 1.35, 1100, 0.9, 0.1); // "train on" arrow completes (tl 0.95..1.35)
|
|
778
|
+
// copying 1,000,000 examples: disk chatter for the whole copy + one tick per progress-bar block (22 blocks, linear)
|
|
779
|
+
const C0 = T_MAIN + 1.3, CL = 2.4;
|
|
780
|
+
hdd(C0, C0 + CL, 1, 952);
|
|
781
|
+
for (let i = 1; i <= 22; i++) k.noise(C0 + CL * (9 * i) / 205, { dur: 0.016, filter: 'bandpass', f: 2300, q: 2.5, gain: 0.13, a: 0.0006, pan: 0.6, minor: true });
|
|
782
|
+
// cards dropping into the model: soft low thup on every other landing (one lands every 0.16 s from C0 + 0.64)
|
|
783
|
+
for (let t = C0 + 0.64; t < T_MAIN + 3.9; t += 0.32) k.tone(t, { f: [[0, 260], [0.06, 140]], dur: 0.08, gain: 0.12, pan: 0.3, minor: true });
|
|
784
|
+
|
|
785
|
+
// ---- tab 2: Train. cursor clicks the tab
|
|
786
|
+
mouse(T_TRAIN, -0.4);
|
|
787
|
+
const L = T_TRAIN;
|
|
788
|
+
soft(L + 0.45, 1100, 0.8, -0.2); soft(L + 0.8, 1100, 0.8, 0.3); // the two arrows complete
|
|
789
|
+
soft(L + 0.7, 1500, 1, -0.2); // Guess button goes down
|
|
790
|
+
k.tone(L + 0.95, { f: [[0, 'G4'], [0.12, 'D4']], type: 'triangle', dur: 0.2, gain: 0.14, pan: 0.4 }); // "dog?" appears
|
|
791
|
+
// CHECK: wrong! a generic system error: low dull two-note thunk + error box
|
|
792
|
+
const WRONG = L + 1.6;
|
|
793
|
+
soft(WRONG, 1500, 1, 0);
|
|
794
|
+
k.play('error', WRONG, { gain: 1.0, f: 'E3', pan: 0.3 });
|
|
795
|
+
k.play('thud', WRONG, { f: 110, gain: 0.45, pan: 0.3 });
|
|
796
|
+
// ADJUST: error signal travels back (right -> left) along the dotted arrow
|
|
797
|
+
soft(L + 2.0, 1500, 1, 0);
|
|
798
|
+
k.tone(L + 2.0, { f: [[0, 520], [0.55, 240]], type: 'triangle', dur: 0.6, gain: 0.07, a: 0.05, hold: 0.4, pan: 0.5, minor: true });
|
|
799
|
+
k.tone(L + 2.0, { f: [[0, 260], [0.55, 120]], type: 'triangle', dur: 0.6, gain: 0.05, a: 0.05, hold: 0.4, pan: -0.2, minor: true });
|
|
800
|
+
// trackbar weights snap in 6 steps over tl 2.6..3.2: one slider notch per step
|
|
801
|
+
for (let s = 1; s <= 6; s++) k.withMinor(() => k.play('tock', L + 2.6 + s * 0.1 - 1e-3, { f: 900 + s * 40, gain: 0.35, pan: -0.1 }));
|
|
802
|
+
// GUESS again -> "cat" + Correct! (the correction)
|
|
803
|
+
soft(L + 3.25, 1500, 1, 0);
|
|
804
|
+
chime(L + 3.4, ['C5', 'E5', 'G5'], 1, 0.08);
|
|
805
|
+
soft(L + 3.8, 1500, 1, 0); // Check button
|
|
806
|
+
|
|
807
|
+
// ---- tab 3: Results. cursor clicks the tab, then the accuracy plot steps up one point every 1.6/19 s
|
|
808
|
+
mouse(T_RES, -0.2);
|
|
809
|
+
for (let i = 1; i < 20; i++) {
|
|
810
|
+
const t = T_RES + 0.3 + (1.6 * i) / 19;
|
|
811
|
+
k.noise(t, { dur: 0.016, filter: 'bandpass', f: 1500 + i * 45, q: 2.5, gain: 0.15 * (1 - i / 32), a: 0.0006, pan: -0.4 + i * 0.04, minor: true });
|
|
812
|
+
}
|
|
813
|
+
// 97% (biggest moment, tl > 1.95): generic "tada" fanfare, brassy saw through a lowpass, low thump underneath
|
|
814
|
+
const BEST = T_RES + 1.95 + 1e-3;
|
|
815
|
+
k.play('impact', BEST, { f: 120, gain: 0.9 });
|
|
816
|
+
[['C4', 0], ['E4', 0.06], ['G4', 0.12], ['C5', 0.18]].forEach(([n, d]) => {
|
|
817
|
+
k.tone(BEST + d, { f: n, type: 'sawtooth', dur: 0.95 - d, gain: 0.11, a: 0.01, hold: 0.45 - d, filter: 'lowpass', f2: [[0, 900], [0.15, 2400], [0.8, 1200]], verb: 0.3 });
|
|
818
|
+
k.tone(BEST + d, { f: n, type: 'sawtooth', detune: 9, dur: 0.95 - d, gain: 0.08, a: 0.01, hold: 0.45 - d, filter: 'lowpass', f2: 1800, verb: 0.3 });
|
|
819
|
+
});
|
|
820
|
+
k.tone(BEST, { f: 'C3', type: 'triangle', dur: 1.1, gain: 0.28, hold: 0.45 });
|
|
821
|
+
// takeaway message box: soft resolving info chime
|
|
822
|
+
chime(T_DONE, ['G4', 'C5'], 1, 0.12);
|
|
823
|
+
k.tone(T_DONE + 0.12, { f: 'C4', type: 'triangle', dur: 1.3, gain: 0.08, a: 0.01, verb: 0.3 });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
registerStyle({
|
|
827
|
+
id: 'win95',
|
|
828
|
+
name: 'Win95 Desktop',
|
|
829
|
+
category: 'Retro UI & Neon',
|
|
830
|
+
description: 'A mid-90s beige-box desktop at 2x pixel scale: teal wallpaper, 2-pixel bevelled grey chrome, navy title bars, aliased system text and a trail of frozen error dialogs.',
|
|
831
|
+
motion: 'Icons pop onto the desktop, windows open with outline-zoom rectangles, the install bar ticks in blocks then freezes, and the hung error box is dragged, smearing a stair of copies.',
|
|
832
|
+
fonts: ['400 9px "Inter"', '700 9px "Inter"', '800 15px "Inter"', '800 9.5px "Inter"'],
|
|
833
|
+
frameT: 6.3,
|
|
834
|
+
duration: 6.5,
|
|
835
|
+
readsPixels: true,
|
|
836
|
+
explainerDuration: 14.6,
|
|
837
|
+
explainerNote: 'An "AI Trainer" app explains it: the question dialog zooms into the title bar, beats are property-sheet tabs clicked by the cursor, dotted pixel arrows carry examples and the error signal, trackbar "weights" slide, counters live in status bars and group boxes, and the payoff is a classic message box.',
|
|
838
|
+
explainer(ctx, W, H_, t) {
|
|
839
|
+
const buf = H.layer(BW, BH), c = buf.getContext('2d', { willReadFrequently: true });
|
|
840
|
+
c.imageSmoothingEnabled = false;
|
|
841
|
+
explainW95(c, t);
|
|
842
|
+
// camera: frames each beat at 3x pixels (zoom 1.5), pulls back to 2x for window/tab changes, 4x on the 97%
|
|
843
|
+
const K = [[0, 320, 170, 1.5], [1.7, 320, 170, 1.5], [2.05, 320, 180, 1], [2.4, 200, 160, 1.5], [3.3, 200, 160, 1.5], [4.2, 420, 160, 1.5], [5.4, 420, 160, 1.5], [5.75, 320, 180, 1], [6.25, 240, 180, 1.5], [6.9, 240, 180, 1.5], [7.3, 440, 180, 1.5], [8.3, 440, 180, 1.5], [8.6, 360, 182, 1.5], [9.4, 360, 182, 1.5], [9.7, 440, 180, 1.5], [10.05, 320, 180, 1], [10.6, 228, 170, 1.5], [11.9, 228, 170, 1.5], [12.25, 520, 120, 2], [12.6, 520, 120, 2], [12.85, 350, 186, 1.3], [99, 350, 186, 1.3]];
|
|
844
|
+
let cam = K[K.length - 1];
|
|
845
|
+
for (let i = 0; i < K.length - 1; i++) if (t < K[i + 1][0]) { const e = H.ease.inOut(H.clamp((t - K[i][0]) / (K[i + 1][0] - K[i][0]))); cam = [0, H.lerp(K[i][1], K[i + 1][1], e), H.lerp(K[i][2], K[i + 1][2], e), H.lerp(K[i][3], K[i + 1][3], e)]; break; }
|
|
846
|
+
const sw = BW / cam[3], sh = BH / cam[3];
|
|
847
|
+
const sx = Math.round(H.clamp(cam[1] - sw / 2, 0, BW - sw)), sy = Math.round(H.clamp(cam[2] - sh / 2, 0, BH - sh));
|
|
848
|
+
ctx.imageSmoothingEnabled = false;
|
|
849
|
+
ctx.drawImage(buf, sx, sy, sw, sh, 0, 0, W, H_);
|
|
850
|
+
},
|
|
851
|
+
explainerSound(k) { w95Sound(k); },
|
|
852
|
+
draw(ctx, W, H_, t) {
|
|
853
|
+
const buf = H.layer(BW, BH), c = buf.getContext('2d', { willReadFrequently: true });
|
|
854
|
+
c.imageSmoothingEnabled = false;
|
|
855
|
+
rect(c, 0, 0, BW, BH, C.teal);
|
|
856
|
+
|
|
857
|
+
// timeline (no easing anywhere except the outline "zoom" rectangles, like the real OS)
|
|
858
|
+
const T_EXP = 1.15, T_WIZ = 1.75, T_ERR = 3.45, T_DRAG = 3.8, STEP = 0.17;
|
|
859
|
+
const icons = [[6, iconComputer, 'My Computer'], [62, iconNetwork, 'Network\nNeighborhood'], [130, iconBin, 'Recycle Bin'], [186, iconFolder, 'Neural Net'], [242, iconDoc, 'README.TXT']];
|
|
860
|
+
icons.forEach(([y, ic, label], i) => { if (t >= 0.25 + i * 0.13) desktopIcon(c, 6, y, ic, label, label === 'Neural Net' && t >= 0.85); });
|
|
861
|
+
|
|
862
|
+
const zoomRect = (from, to, t0, t1) => {
|
|
863
|
+
if (t < t0 || t >= t1) return;
|
|
864
|
+
for (let k = 0; k < 3; k++) {
|
|
865
|
+
const p = H.clamp((t - t0) / (t1 - t0) - k * 0.12); if (p <= 0) continue;
|
|
866
|
+
const x = H.lerp(from[0], to[0], p), y = H.lerp(from[1], to[1], p), w = H.lerp(from[2], to[2], p), h = H.lerp(from[3], to[3], p);
|
|
867
|
+
c.fillStyle = '#c0c0c0';
|
|
868
|
+
c.fillRect(Math.round(x), Math.round(y), Math.round(w), 2); c.fillRect(Math.round(x), Math.round(y + h), Math.round(w), 2);
|
|
869
|
+
c.fillRect(Math.round(x), Math.round(y), 2, Math.round(h)); c.fillRect(Math.round(x + w), Math.round(y), 2, Math.round(h) + 2);
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
zoomRect([30, 200, 30, 20], [92, 6, 360, 140], 0.9, T_EXP);
|
|
873
|
+
if (t >= T_EXP) explorerWindow(c, 92, 6, 360, 140);
|
|
874
|
+
zoomRect([200, 60, 60, 40], [222, 34, 410, 262], T_WIZ - 0.25, T_WIZ);
|
|
875
|
+
if (t >= T_WIZ) wizardWindow(c, 222, 34, 410, 262, t, t < T_ERR);
|
|
876
|
+
|
|
877
|
+
// error dialog: pops up, then gets dragged and the hung app leaves a stair of copies
|
|
878
|
+
const ex = 150, ey = 196, pos = (k) => [ex - k * 15, ey + k * 9];
|
|
879
|
+
let cur = null;
|
|
880
|
+
if (t >= T_ERR) {
|
|
881
|
+
const STEPS = [0, 0.1, 0.28, 0.38, 0.6, 0.72]; // jerky, laggy redraws of a hung app
|
|
882
|
+
let s = 0; for (let k = 1; k < STEPS.length; k++) if (t >= T_DRAG + STEPS[k - 1]) s = k;
|
|
883
|
+
for (let k = 5; k > 5 - s; k--) errorDialog(c, ...pos(k), false);
|
|
884
|
+
cur = pos(5 - s);
|
|
885
|
+
errorDialog(c, ...cur, true);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// cursor
|
|
889
|
+
if (t < T_WIZ) cursorArrow(c, Math.round(H.lerp(40, 300, H.clamp((t - 0.9) / 0.8))), Math.round(H.lerp(230, 200, H.clamp((t - 0.9) / 0.8))));
|
|
890
|
+
else if (t < T_ERR) cursorHourglass(c, 470, 200);
|
|
891
|
+
else if (t < T_DRAG + 0.95) {
|
|
892
|
+
const p = H.clamp((t - T_ERR) / (T_DRAG - T_ERR));
|
|
893
|
+
const tgt = [cur[0] + 90, cur[1] + 9];
|
|
894
|
+
cursorArrow(c, Math.round(H.lerp(470, tgt[0], p)), Math.round(H.lerp(200, tgt[1], p)));
|
|
895
|
+
} else {
|
|
896
|
+
const p = H.ease.inOut(H.clamp((t - (T_DRAG + 1.15)) / 0.6));
|
|
897
|
+
cursorArrow(c, Math.round(H.lerp(ex + 90, ex + 132, p)), Math.round(H.lerp(ey + 9, ey + 98, p)));
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const open = t >= T_ERR ? 3 : t >= T_WIZ ? 2 : t >= T_EXP ? 1 : 0;
|
|
901
|
+
taskbar(c, t, open, open - 1);
|
|
902
|
+
|
|
903
|
+
ctx.imageSmoothingEnabled = false;
|
|
904
|
+
ctx.drawImage(buf, 0, 0, W, H_);
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
}
|