@prestyj/cli 5.28.0 → 5.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/assets/motion/THIRD-PARTY.md +14 -19
- package/assets/motion/bin/contact-sheet.mjs +9 -3
- package/assets/motion/bin/cues.mjs +337 -0
- package/assets/motion/bin/flash-check.mjs +314 -0
- package/assets/motion/bin/fonts.mjs +17 -13
- package/assets/motion/bin/library.mjs +61 -18
- package/assets/motion/bin/motion-blur.mjs +943 -0
- package/assets/motion/bin/motion-check.mjs +366 -9
- package/assets/motion/bin/music-fit.mjs +436 -0
- package/assets/motion/bin/pdf-extract.mjs +22 -10
- package/assets/motion/bin/reference-study.mjs +346 -0
- package/assets/motion/bin/score-synth.mjs +1052 -93
- package/assets/motion/fonts/finger-paint/OFL.txt +93 -0
- package/assets/motion/fonts/finger-paint/finger-paint-normal.woff2 +0 -0
- package/assets/motion/fonts/fonts.json +56 -0
- package/assets/motion/fonts/short-stack/OFL.txt +94 -0
- package/assets/motion/fonts/short-stack/short-stack-normal.woff2 +0 -0
- package/assets/motion/fonts/sora/OFL.txt +93 -0
- package/assets/motion/fonts/sora/sora-normal.woff2 +0 -0
- package/assets/motion/fonts/specimen.jpg +0 -0
- package/assets/motion/fonts/unbounded/OFL.txt +93 -0
- package/assets/motion/fonts/unbounded/unbounded-normal.woff2 +0 -0
- package/assets/motion/library/README.md +52 -14
- package/assets/motion/library/kit/moves.js +1981 -0
- package/assets/motion/library/library.json +232 -0
- package/assets/motion/library/pieces/camera-rig/meta.json +13 -0
- package/assets/motion/library/pieces/camera-rig/piece.html +153 -0
- package/assets/motion/library/pieces/camera-rig/preview.jpg +0 -0
- package/assets/motion/library/pieces/chain-knock/meta.json +13 -0
- package/assets/motion/library/pieces/chain-knock/piece.html +195 -0
- package/assets/motion/library/pieces/chain-knock/preview.jpg +0 -0
- package/assets/motion/library/pieces/gather-to-logo/meta.json +13 -0
- package/assets/motion/library/pieces/gather-to-logo/piece.html +159 -0
- package/assets/motion/library/pieces/gather-to-logo/preview.jpg +0 -0
- package/assets/motion/library/pieces/morph-carry/meta.json +13 -0
- package/assets/motion/library/pieces/morph-carry/piece.html +173 -0
- package/assets/motion/library/pieces/morph-carry/preview.jpg +0 -0
- package/assets/motion/library/pieces/one-shape-journey/meta.json +13 -0
- package/assets/motion/library/pieces/one-shape-journey/piece.html +195 -0
- package/assets/motion/library/pieces/one-shape-journey/preview.jpg +0 -0
- package/assets/motion/library/pieces/open-from-subject/meta.json +13 -0
- package/assets/motion/library/pieces/open-from-subject/piece.html +168 -0
- package/assets/motion/library/pieces/open-from-subject/preview.jpg +0 -0
- package/assets/motion/library/pieces/request-to-result/meta.json +13 -0
- package/assets/motion/library/pieces/request-to-result/piece.html +212 -0
- package/assets/motion/library/pieces/request-to-result/preview.jpg +0 -0
- package/assets/motion/library/pieces/scale-dive/meta.json +13 -0
- package/assets/motion/library/pieces/scale-dive/piece.html +321 -0
- package/assets/motion/library/pieces/scale-dive/preview.jpg +0 -0
- package/assets/motion/library/pieces/screen-replica-steps/meta.json +13 -0
- package/assets/motion/library/pieces/screen-replica-steps/piece.html +366 -0
- package/assets/motion/library/pieces/screen-replica-steps/preview.jpg +0 -0
- package/assets/motion/library/pieces/zoom-into-card/meta.json +13 -0
- package/assets/motion/library/pieces/zoom-into-card/piece.html +179 -0
- package/assets/motion/library/pieces/zoom-into-card/preview.jpg +0 -0
- package/assets/motion/library/sheets/diagram.jpg +0 -0
- package/assets/motion/library/sheets/frame.jpg +0 -0
- package/assets/motion/library/sheets/transition.jpg +0 -0
- package/assets/motion/library/sheets/ui.jpg +0 -0
- package/assets/motion/plugin.json +1 -1
- package/assets/motion/references/build-sheet.md +206 -0
- package/assets/motion/references/runtime/determinism-rules.md +3 -3
- package/assets/motion/references/runtime/gsap-easing-and-stagger.md +28 -28
- package/assets/motion/references/runtime/gsap.md +3 -3
- package/assets/motion/references/runtime/inputs-and-assets.md +21 -16
- package/assets/motion/references/runtime/lint-validate-inspect.md +3 -3
- package/assets/motion/references/runtime/minimal-composition.md +6 -0
- package/assets/motion/references/runtime/preview-render.md +3 -3
- package/assets/motion/skills/app-walkthrough/SKILL.md +66 -0
- package/assets/motion/skills/before-after/SKILL.md +53 -0
- package/assets/motion/skills/brand-kit/SKILL.md +17 -16
- package/assets/motion/skills/dev-tool-video/SKILL.md +57 -0
- package/assets/motion/skills/launch-video/SKILL.md +62 -0
- package/assets/motion/skills/match-reference/SKILL.md +58 -0
- package/assets/motion/skills/motion/SKILL.md +167 -98
- package/assets/motion/skills/source-ingest/SKILL.md +23 -15
- package/assets/motion/skills/website-video/SKILL.md +59 -0
- package/assets/skills/bulletproof/SKILL.md +36 -11
- package/assets/skills/bulletproof/references/agent-surface.md +19 -9
- package/assets/skills/bulletproof/references/audit-protocol.md +20 -5
- package/assets/skills/bulletproof/references/platform-playbooks.md +5 -4
- package/assets/skills/bulletproof/references/provenance.md +26 -1
- package/assets/skills/bulletproof/references/secure-defaults.md +6 -5
- package/assets/skills/bulletproof/references/supply-chain.md +21 -17
- package/assets/skills/bulletproof/references/threat-landscape.md +28 -26
- package/assets/skills/bulletproof/references/verification.md +2 -0
- package/assets/skills/clarify/SKILL.md +25 -16
- package/assets/skills/code-review/SKILL.md +71 -13
- package/assets/skills/code-review/references/agent-diffs.md +27 -0
- package/assets/skills/code-review/references/tests.md +19 -0
- package/assets/skills/compliance-guard/SKILL.md +20 -5
- package/assets/skills/compliance-guard/references/artifacts.md +1 -1
- package/assets/skills/compliance-guard/references/eu-uk.md +16 -16
- package/assets/skills/compliance-guard/references/lawsuit-vectors.md +5 -5
- package/assets/skills/compliance-guard/references/provenance.md +41 -2
- package/assets/skills/compliance-guard/references/sector-gates.md +3 -3
- package/assets/skills/compliance-guard/references/security-baseline.md +2 -2
- package/assets/skills/compliance-guard/references/trigger-map.md +5 -5
- package/assets/skills/compliance-guard/references/us.md +27 -21
- package/assets/skills/durable/SKILL.md +87 -79
- package/assets/skills/durable/references/agent-db-safety.md +69 -0
- package/assets/skills/durable/references/backups-and-runtime.md +19 -12
- package/assets/skills/durable/references/migrations-and-schema.md +13 -6
- package/assets/skills/evidence-led-ui/SKILL.md +69 -127
- package/assets/skills/evidence-led-ui/references/anti-defaults.md +107 -208
- package/assets/skills/evidence-led-ui/references/direction.md +124 -0
- package/assets/skills/evidence-led-ui/references/production-contract.md +8 -0
- package/assets/skills/evidence-led-ui/references/provenance.md +24 -1
- package/assets/skills/lean/SKILL.md +90 -71
- package/assets/skills/lean/references/memory-and-processes.md +3 -2
- package/assets/skills/lean/references/playbooks.md +37 -12
- package/assets/skills/refactoring/SKILL.md +24 -3
- package/assets/skills/refactoring/references/agent-pitfalls.md +4 -1
- package/assets/skills/refactoring/references/legacy.md +21 -0
- package/assets/skills/root-cause/SKILL.md +20 -10
- package/assets/skills/shared-language/SKILL.md +16 -14
- package/assets/skills/tdd/SKILL.md +27 -15
- package/dist/app-sidecar.js +203 -47
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.js +20 -28
- package/dist/cli.js.map +1 -1
- package/dist/core/acceptance-checks.d.ts +48 -0
- package/dist/core/acceptance-checks.js +144 -0
- package/dist/core/acceptance-checks.js.map +1 -0
- package/dist/core/agent-session.d.ts +119 -75
- package/dist/core/agent-session.js +561 -395
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +6 -5
- package/dist/core/agents.js +12 -3
- package/dist/core/agents.js.map +1 -1
- package/dist/core/ask-user.d.ts +90 -8
- package/dist/core/ask-user.js +124 -13
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/auth-providers.js +1 -1
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/autopilot-verdict.d.ts +5 -1
- package/dist/core/autopilot-verdict.js +30 -13
- package/dist/core/autopilot-verdict.js.map +1 -1
- package/dist/core/bundled-agents.js +1 -3
- package/dist/core/bundled-agents.js.map +1 -1
- package/dist/core/cache-diagnostics.d.ts +68 -0
- package/dist/core/cache-diagnostics.js +196 -0
- package/dist/core/cache-diagnostics.js.map +1 -0
- package/dist/core/cache-expiry.d.ts +87 -0
- package/dist/core/cache-expiry.js +111 -0
- package/dist/core/cache-expiry.js.map +1 -0
- package/dist/core/compaction/compactor.js +78 -48
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/plan-step-policy.d.ts +46 -0
- package/dist/core/compaction/plan-step-policy.js +57 -0
- package/dist/core/compaction/plan-step-policy.js.map +1 -0
- package/dist/core/destructive-git-guard.d.ts +90 -0
- package/dist/core/destructive-git-guard.js +871 -0
- package/dist/core/destructive-git-guard.js.map +1 -0
- package/dist/core/event-bus.d.ts +3 -0
- package/dist/core/event-bus.js +5 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/fast-apply-benchmark.d.ts +1 -1
- package/dist/core/fast-apply-benchmark.js +2 -2
- package/dist/core/fast-apply-benchmark.js.map +1 -1
- package/dist/core/injection-detect.d.ts +38 -0
- package/dist/core/injection-detect.js +232 -0
- package/dist/core/injection-detect.js.map +1 -0
- package/dist/core/keep-awake.d.ts +88 -0
- package/dist/core/keep-awake.js +251 -0
- package/dist/core/keep-awake.js.map +1 -0
- package/dist/core/mcp/client.d.ts +72 -0
- package/dist/core/mcp/client.js +264 -41
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/content.js +6 -2
- package/dist/core/mcp/content.js.map +1 -1
- package/dist/core/mcp/store.d.ts +6 -1
- package/dist/core/mcp/store.js +12 -1
- package/dist/core/mcp/store.js.map +1 -1
- package/dist/core/mcp/types.d.ts +18 -0
- package/dist/core/model-unavailable.d.ts +14 -0
- package/dist/core/model-unavailable.js +23 -0
- package/dist/core/model-unavailable.js.map +1 -0
- package/dist/core/node-debugger.d.ts +148 -0
- package/dist/core/node-debugger.js +642 -0
- package/dist/core/node-debugger.js.map +1 -0
- package/dist/core/nolan-context.d.ts +7 -5
- package/dist/core/nolan-context.js +106 -16
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-prompt.js +24 -21
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/package-threats.d.ts +18 -0
- package/dist/core/package-threats.js +168 -0
- package/dist/core/package-threats.js.map +1 -0
- package/dist/core/persistent-shell.d.ts +58 -6
- package/dist/core/persistent-shell.js +331 -49
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/process-manager.d.ts +14 -0
- package/dist/core/process-manager.js +61 -0
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/progress/git-xp.js +8 -14
- package/dist/core/progress/git-xp.js.map +1 -1
- package/dist/core/project-discovery.js +77 -26
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/semantic-search-benchmark.d.ts +1 -1
- package/dist/core/semantic-search-benchmark.js +2 -2
- package/dist/core/semantic-search-benchmark.js.map +1 -1
- package/dist/core/session-history.d.ts +12 -0
- package/dist/core/session-history.js +27 -0
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-manager.d.ts +13 -1
- package/dist/core/session-manager.js +38 -18
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-summary-index.d.ts +37 -0
- package/dist/core/session-summary-index.js +172 -0
- package/dist/core/session-summary-index.js.map +1 -0
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.js +10 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell-threats-popular-packages.d.ts +11 -0
- package/dist/core/shell-threats-popular-packages.js +675 -0
- package/dist/core/shell-threats-popular-packages.js.map +1 -0
- package/dist/core/shell-threats.d.ts +8 -0
- package/dist/core/shell-threats.js +186 -0
- package/dist/core/shell-threats.js.map +1 -0
- package/dist/core/skills.js +16 -4
- package/dist/core/skills.js.map +1 -1
- package/dist/core/stream-rules.d.ts +30 -0
- package/dist/core/stream-rules.js +151 -0
- package/dist/core/stream-rules.js.map +1 -0
- package/dist/core/subagent-manager.d.ts +23 -6
- package/dist/core/subagent-manager.js +25 -9
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-policy.js +1 -1
- package/dist/core/subagent-policy.js.map +1 -1
- package/dist/core/subagent-receipt.d.ts +54 -0
- package/dist/core/subagent-receipt.js +276 -0
- package/dist/core/subagent-receipt.js.map +1 -0
- package/dist/core/subagent-turn-record.d.ts +2 -0
- package/dist/core/subagent-turn-record.js.map +1 -1
- package/dist/core/test-impact.d.ts +73 -0
- package/dist/core/test-impact.js +467 -0
- package/dist/core/test-impact.js.map +1 -0
- package/dist/core/thinking-level.d.ts +1 -1
- package/dist/core/thinking-level.js +1 -1
- package/dist/core/thinking-level.js.map +1 -1
- package/dist/core/verification-gate.d.ts +2 -0
- package/dist/core/verification-gate.js +4 -0
- package/dist/core/verification-gate.js.map +1 -1
- package/dist/core/verification-snapshot.js +3 -5
- package/dist/core/verification-snapshot.js.map +1 -1
- package/dist/core/workspace-guard.d.ts +18 -7
- package/dist/core/workspace-guard.js +227 -60
- package/dist/core/workspace-guard.js.map +1 -1
- package/dist/core/worktree-setup.d.ts +23 -0
- package/dist/core/worktree-setup.js +128 -9
- package/dist/core/worktree-setup.js.map +1 -1
- package/dist/core/worktree.d.ts +20 -2
- package/dist/core/worktree.js +74 -31
- package/dist/core/worktree.js.map +1 -1
- package/dist/interactive.js +2 -1
- package/dist/interactive.js.map +1 -1
- package/dist/modes/json-mode.js +11 -2
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +36 -1
- package/dist/modes/subagent-worker-mode.js +100 -42
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/motion-agent/motion-agent.d.ts +7 -3
- package/dist/motion-agent/motion-agent.js +6 -8
- package/dist/motion-agent/motion-agent.js.map +1 -1
- package/dist/motion-agent/motion-prompt.d.ts +1 -1
- package/dist/motion-agent/motion-prompt.js +16 -25
- package/dist/motion-agent/motion-prompt.js.map +1 -1
- package/dist/motion-agent/motion-review-session.js +1 -1
- package/dist/motion-agent/motion-review-session.js.map +1 -1
- package/dist/motion-agent/motion-review.d.ts +10 -3
- package/dist/motion-agent/motion-review.js +15 -7
- package/dist/motion-agent/motion-review.js.map +1 -1
- package/dist/motion-agent/motion-studio-context.js +1 -1
- package/dist/motion-agent/motion-studio-context.js.map +1 -1
- package/dist/system-prompt.d.ts +2 -1
- package/dist/system-prompt.js +15 -4
- package/dist/system-prompt.js.map +1 -1
- package/dist/test-support/keep-alive.d.ts +14 -0
- package/dist/test-support/keep-alive.js +17 -0
- package/dist/test-support/keep-alive.js.map +1 -0
- package/dist/tools/ask-user.js +3 -3
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/bash-read-evidence.d.ts +10 -0
- package/dist/tools/bash-read-evidence.js +133 -0
- package/dist/tools/bash-read-evidence.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -1
- package/dist/tools/bash.js +179 -7
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/debug.d.ts +54 -0
- package/dist/tools/debug.js +233 -0
- package/dist/tools/debug.js.map +1 -0
- package/dist/tools/edit.js +13 -5
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/goals.d.ts +1 -1
- package/dist/tools/index.d.ts +25 -2
- package/dist/tools/index.js +48 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/prompt-hints.js +2 -0
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-tracker.d.ts +35 -2
- package/dist/tools/read-tracker.js +108 -11
- package/dist/tools/read-tracker.js.map +1 -1
- package/dist/tools/read.js +39 -7
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/skill.js +5 -0
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.js +44 -8
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +48 -8
- package/dist/tools/subagent-shared.js +75 -14
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent.d.ts +8 -2
- package/dist/tools/subagent.js +28 -10
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/task-output.js +3 -2
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.d.ts +1 -1
- package/dist/tools/task-send.js +15 -1
- package/dist/tools/task-send.js.map +1 -1
- package/dist/tools/tool-tiers.d.ts +2 -2
- package/dist/tools/tool-tiers.js +3 -2
- package/dist/tools/tool-tiers.js.map +1 -1
- package/dist/tools/truncate.d.ts +21 -0
- package/dist/tools/truncate.js +187 -0
- package/dist/tools/truncate.js.map +1 -1
- package/dist/tools/ui-adopt.js +2 -0
- package/dist/tools/ui-adopt.js.map +1 -1
- package/dist/tools/write.js +4 -3
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/App.d.ts +0 -4
- package/dist/ui/App.js +5 -28
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +1 -0
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/Footer.js +1 -1
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +1 -8
- package/dist/ui/hooks/useAgentLoop.js +1 -119
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/render.d.ts +2 -4
- package/dist/ui/render.js +3 -2
- package/dist/ui/render.js.map +1 -1
- package/dist/utils/git.d.ts +77 -0
- package/dist/utils/git.js +285 -21
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/github-ci.js +2 -1
- package/dist/utils/github-ci.js.map +1 -1
- package/dist/utils/github.js +11 -9
- package/dist/utils/github.js.map +1 -1
- package/dist/utils/image.d.ts +14 -0
- package/dist/utils/image.js +16 -0
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/process.d.ts +20 -0
- package/dist/utils/process.js +98 -0
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/text.d.ts +12 -0
- package/dist/utils/text.js +11 -0
- package/dist/utils/text.js.map +1 -1
- package/package.json +6 -6
- package/assets/motion/skills/mixkit-split-text-617/SKILL.md +0 -115
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-480.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-494.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-5.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-508.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6525.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6539.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6647.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6663.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6677.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6691.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6706.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6722.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6736.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6750.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6764.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6780.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6794.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6810.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6823.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6872.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6936.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-76.json +0 -1
- package/assets/motion/skills/mixkit-split-text-617/data/manifest.json +0 -495
- package/assets/motion/skills/mixkit-split-text-617/references/RECONSTRUCTION.md +0 -201
- package/assets/motion/skills/mixkit-split-text-617/references/VERIFICATION.md +0 -107
- package/assets/motion/skills/mixkit-split-text-617/tools/inspect_motion.py +0 -130
- package/assets/motion/skills/video-qa/SKILL.md +0 -66
- package/dist/core/ideal-review-subagent.d.ts +0 -42
- package/dist/core/ideal-review-subagent.js +0 -95
- package/dist/core/ideal-review-subagent.js.map +0 -1
- package/dist/core/ideal-review.d.ts +0 -82
- package/dist/core/ideal-review.js +0 -242
- package/dist/core/ideal-review.js.map +0 -1
- package/dist/motion-agent/motion-check-tool.d.ts +0 -21
- package/dist/motion-agent/motion-check-tool.js +0 -206
- package/dist/motion-agent/motion-check-tool.js.map +0 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Photosensitivity screen on decoded pixels against the WCAG 2.3.1 general and red flash
|
|
3
|
+
// thresholds. As in EA's open-source IRIS tool, an area's mean change only counts on frames
|
|
4
|
+
// where at least a quarter of the area changes, so moving content mostly cancels out rather
|
|
5
|
+
// than reading as flashing. IRIS applies this to the whole screen; here it applies to every
|
|
6
|
+
// WCAG 10° field (a third of the frame each way), so a flash filling part of the frame is
|
|
7
|
+
// caught too. A transition is a move of 0.1 relative luminance (or 20 red units) away from the
|
|
8
|
+
// last peak or valley, as WCAG defines it; more than six in any second fails. Renders above
|
|
9
|
+
// 60 fps are sampled at 60. A screen, not a certification.
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { createReadStream } from "node:fs";
|
|
12
|
+
import { stat } from "node:fs/promises";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { once } from "node:events";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
17
|
+
import { ffmpegBinary } from "./media-binaries.mjs";
|
|
18
|
+
|
|
19
|
+
/** Cells per side of the analysis grid; every aspect ratio maps onto it. */
|
|
20
|
+
export const GRID = 48;
|
|
21
|
+
/** Frames per analysed second; slower renders are repeated, so one second is always 60 frames. */
|
|
22
|
+
export const ANALYSIS_FPS = 60;
|
|
23
|
+
/** WCAG approximates the 10° field as 341×256 px of a 1024×768 screen: a third each way. */
|
|
24
|
+
const FIELD = GRID / 3;
|
|
25
|
+
const FIELD_CELLS = FIELD * FIELD;
|
|
26
|
+
/** A field's change counts once a quarter of it changes. */
|
|
27
|
+
const AREA = 0.25 * FIELD_CELLS;
|
|
28
|
+
const WINDOWS_PER_SIDE = GRID - FIELD + 1;
|
|
29
|
+
const WINDOWS = WINDOWS_PER_SIDE * WINDOWS_PER_SIDE;
|
|
30
|
+
/** More than three flashes (six opposing transitions) within any one second fails. */
|
|
31
|
+
const MAX_TRANSITIONS = 6;
|
|
32
|
+
/** Transitions per field within the trailing second never exceed one per analysed frame. */
|
|
33
|
+
const RING = 64;
|
|
34
|
+
const CELLS = GRID * GRID;
|
|
35
|
+
const FRAME_BYTES = CELLS * 3;
|
|
36
|
+
const CHANNELS = {
|
|
37
|
+
// Relative luminance 0–1: a 0.1 swing, unless even the darker state is 0.8 or brighter.
|
|
38
|
+
// A cell has changed once it moves by more than encoding noise (about 1/255 linear).
|
|
39
|
+
general: { threshold: 0.1, darkLimit: 0.8, noise: 0.004 },
|
|
40
|
+
// WCAG's red measure: (R − G − B) × 320 where R / (R + G + B) ≥ 0.8, else 0.
|
|
41
|
+
red: { threshold: 20, darkLimit: Infinity, noise: 1 },
|
|
42
|
+
};
|
|
43
|
+
/** sRGB to linear light, applied before averaging so mixed cells keep their real brightness. */
|
|
44
|
+
const LINEAR = "if(lte(val,10.31475),val/12.92,255*pow((val/255+0.055)/1.055,2.4))";
|
|
45
|
+
|
|
46
|
+
function createChannel({ threshold, darkLimit, noise }) {
|
|
47
|
+
const side = GRID + 1;
|
|
48
|
+
const changed = new Float64Array(side * side);
|
|
49
|
+
const change = new Float64Array(side * side);
|
|
50
|
+
const level = new Float64Array(side * side);
|
|
51
|
+
// Per field: the accumulated counted change, its last peak and valley, and the field's
|
|
52
|
+
// mean at each, for WCAG's "darker image" rule.
|
|
53
|
+
const signal = new Float64Array(WINDOWS);
|
|
54
|
+
const peakValue = new Float64Array(WINDOWS);
|
|
55
|
+
const peakLevel = new Float64Array(WINDOWS);
|
|
56
|
+
const valleyValue = new Float64Array(WINDOWS);
|
|
57
|
+
const valleyLevel = new Float64Array(WINDOWS);
|
|
58
|
+
/** 1 after a rise, -1 after a fall, 0 before the first transition. */
|
|
59
|
+
const direction = new Int8Array(WINDOWS);
|
|
60
|
+
const ring = new Int32Array(WINDOWS * RING);
|
|
61
|
+
const head = new Int32Array(WINDOWS);
|
|
62
|
+
const tail = new Int32Array(WINDOWS);
|
|
63
|
+
const failures = [];
|
|
64
|
+
let peak = 0;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
update(frame, previous, current) {
|
|
68
|
+
// Summed-area tables, so each field's totals cost four lookups.
|
|
69
|
+
for (let y = 0; y < GRID; y++) {
|
|
70
|
+
let rowChanged = 0;
|
|
71
|
+
let rowChange = 0;
|
|
72
|
+
let rowLevel = 0;
|
|
73
|
+
for (let x = 0; x < GRID; x++) {
|
|
74
|
+
const cell = y * GRID + x;
|
|
75
|
+
const delta = current[cell] - previous[cell];
|
|
76
|
+
if (Math.abs(delta) > noise) rowChanged++;
|
|
77
|
+
rowChange += delta;
|
|
78
|
+
rowLevel += current[cell];
|
|
79
|
+
const at = (y + 1) * side + x + 1;
|
|
80
|
+
changed[at] = changed[at - side] + rowChanged;
|
|
81
|
+
change[at] = change[at - side] + rowChange;
|
|
82
|
+
level[at] = level[at - side] + rowLevel;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let most = 0;
|
|
86
|
+
for (let wy = 0; wy < WINDOWS_PER_SIDE; wy++) {
|
|
87
|
+
for (let wx = 0; wx < WINDOWS_PER_SIDE; wx++) {
|
|
88
|
+
const window = wy * WINDOWS_PER_SIDE + wx;
|
|
89
|
+
const a = wy * side + wx;
|
|
90
|
+
const b = a + FIELD;
|
|
91
|
+
const c = a + FIELD * side;
|
|
92
|
+
const d = c + FIELD;
|
|
93
|
+
const area = changed[d] - changed[b] - changed[c] + changed[a];
|
|
94
|
+
const step = (change[d] - change[b] - change[c] + change[a]) / FIELD_CELLS;
|
|
95
|
+
const mean = (level[d] - level[b] - level[c] + level[a]) / FIELD_CELLS;
|
|
96
|
+
if (frame === 1) {
|
|
97
|
+
peakLevel[window] = mean - step;
|
|
98
|
+
valleyLevel[window] = mean - step;
|
|
99
|
+
}
|
|
100
|
+
const value = signal[window] + (area >= AREA ? step : 0);
|
|
101
|
+
signal[window] = value;
|
|
102
|
+
let transition = false;
|
|
103
|
+
if (direction[window] >= 0) {
|
|
104
|
+
if (value > peakValue[window]) {
|
|
105
|
+
peakValue[window] = value;
|
|
106
|
+
peakLevel[window] = mean;
|
|
107
|
+
} else if (peakValue[window] - value >= threshold) {
|
|
108
|
+
transition = Math.min(mean, peakLevel[window]) < darkLimit;
|
|
109
|
+
direction[window] = -1;
|
|
110
|
+
valleyValue[window] = value;
|
|
111
|
+
valleyLevel[window] = mean;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (direction[window] <= 0) {
|
|
115
|
+
if (value < valleyValue[window]) {
|
|
116
|
+
valleyValue[window] = value;
|
|
117
|
+
valleyLevel[window] = mean;
|
|
118
|
+
} else if (value - valleyValue[window] >= threshold) {
|
|
119
|
+
transition = Math.min(mean, valleyLevel[window]) < darkLimit;
|
|
120
|
+
direction[window] = 1;
|
|
121
|
+
peakValue[window] = value;
|
|
122
|
+
peakLevel[window] = mean;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const base = window * RING;
|
|
126
|
+
while (
|
|
127
|
+
tail[window] < head[window] &&
|
|
128
|
+
frame - ring[base + (tail[window] % RING)] >= ANALYSIS_FPS
|
|
129
|
+
)
|
|
130
|
+
tail[window]++;
|
|
131
|
+
if (transition) {
|
|
132
|
+
ring[base + (head[window] % RING)] = frame;
|
|
133
|
+
head[window]++;
|
|
134
|
+
}
|
|
135
|
+
const count = head[window] - tail[window];
|
|
136
|
+
if (count > most) most = count;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
peak = Math.max(peak, most);
|
|
140
|
+
if (most > MAX_TRANSITIONS) {
|
|
141
|
+
const start = Math.max(0, frame + 1 - ANALYSIS_FPS) / ANALYSIS_FPS;
|
|
142
|
+
const end = (frame + 1) / ANALYSIS_FPS;
|
|
143
|
+
const last = failures.at(-1);
|
|
144
|
+
if (last && start <= last.end) last.end = end;
|
|
145
|
+
else failures.push({ start, end });
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
summary() {
|
|
149
|
+
return {
|
|
150
|
+
maxFlashesPerSecond: peak / 2,
|
|
151
|
+
failures: failures.slice(0, 20).map(({ start, end }) => ({
|
|
152
|
+
start: Math.round(start * 100) / 100,
|
|
153
|
+
end: Math.round(end * 100) / 100,
|
|
154
|
+
})),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Pure analysis over linear-light RGB frames of GRID × GRID cells (3 bytes per cell). */
|
|
161
|
+
export function createFlashAnalyzer() {
|
|
162
|
+
const general = createChannel(CHANNELS.general);
|
|
163
|
+
const red = createChannel(CHANNELS.red);
|
|
164
|
+
let luminance = new Float64Array(CELLS);
|
|
165
|
+
let redness = new Float64Array(CELLS);
|
|
166
|
+
let previousLuminance = new Float64Array(CELLS);
|
|
167
|
+
let previousRedness = new Float64Array(CELLS);
|
|
168
|
+
let frames = 0;
|
|
169
|
+
return {
|
|
170
|
+
push(frame) {
|
|
171
|
+
if (!(frame instanceof Uint8Array) || frame.length !== FRAME_BYTES)
|
|
172
|
+
throw new Error(`Expected ${FRAME_BYTES}-byte analysis frames`);
|
|
173
|
+
for (let cell = 0; cell < CELLS; cell++) {
|
|
174
|
+
const r = frame[cell * 3] / 255;
|
|
175
|
+
const g = frame[cell * 3 + 1] / 255;
|
|
176
|
+
const b = frame[cell * 3 + 2] / 255;
|
|
177
|
+
luminance[cell] = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
178
|
+
const total = r + g + b;
|
|
179
|
+
redness[cell] = total > 0 && r / total >= 0.8 ? Math.max(0, (r - g - b) * 320) : 0;
|
|
180
|
+
}
|
|
181
|
+
if (frames > 0) {
|
|
182
|
+
general.update(frames, previousLuminance, luminance);
|
|
183
|
+
red.update(frames, previousRedness, redness);
|
|
184
|
+
}
|
|
185
|
+
[previousLuminance, luminance] = [luminance, previousLuminance];
|
|
186
|
+
[previousRedness, redness] = [redness, previousRedness];
|
|
187
|
+
frames++;
|
|
188
|
+
},
|
|
189
|
+
result() {
|
|
190
|
+
const generalSummary = general.summary();
|
|
191
|
+
const redSummary = red.summary();
|
|
192
|
+
return {
|
|
193
|
+
ok: frames >= 2 && !generalSummary.failures.length && !redSummary.failures.length,
|
|
194
|
+
method: "rendered-pixels",
|
|
195
|
+
standard: "WCAG 2.3.1 general and red flash thresholds",
|
|
196
|
+
analysisFps: ANALYSIS_FPS,
|
|
197
|
+
frames,
|
|
198
|
+
duration: frames / ANALYSIS_FPS,
|
|
199
|
+
general: generalSummary,
|
|
200
|
+
red: redSummary,
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Decode a video into linear-light analysis frames and return the flash report. */
|
|
207
|
+
export async function analyzeFlashes(file, signal) {
|
|
208
|
+
const binary = await ffmpegBinary();
|
|
209
|
+
const child = spawn(
|
|
210
|
+
binary,
|
|
211
|
+
[
|
|
212
|
+
"-hide_banner",
|
|
213
|
+
"-nostdin",
|
|
214
|
+
"-loglevel",
|
|
215
|
+
"error",
|
|
216
|
+
"-xerror",
|
|
217
|
+
"-protocol_whitelist",
|
|
218
|
+
"file,pipe",
|
|
219
|
+
"-i",
|
|
220
|
+
file,
|
|
221
|
+
"-map",
|
|
222
|
+
"0:v:0",
|
|
223
|
+
"-an",
|
|
224
|
+
"-vf",
|
|
225
|
+
[
|
|
226
|
+
// Linearise before any averaging: shrinking sRGB values first darkens fine detail
|
|
227
|
+
// by up to half the flash threshold.
|
|
228
|
+
"format=gbrp",
|
|
229
|
+
`lutrgb=r='${LINEAR}':g='${LINEAR}':b='${LINEAR}'`,
|
|
230
|
+
`scale=${GRID}:${GRID}:flags=area`,
|
|
231
|
+
`fps=${ANALYSIS_FPS}`,
|
|
232
|
+
"format=rgb24",
|
|
233
|
+
].join(","),
|
|
234
|
+
"-f",
|
|
235
|
+
"rawvideo",
|
|
236
|
+
"-pix_fmt",
|
|
237
|
+
"rgb24",
|
|
238
|
+
"pipe:1",
|
|
239
|
+
],
|
|
240
|
+
{ stdio: ["ignore", "pipe", "pipe"], windowsHide: true, signal },
|
|
241
|
+
);
|
|
242
|
+
let stderr = "";
|
|
243
|
+
child.stderr.setEncoding("utf8");
|
|
244
|
+
child.stderr.on("data", (text) => {
|
|
245
|
+
if (stderr.length < 16_000) stderr += text;
|
|
246
|
+
});
|
|
247
|
+
const closed = once(child, "close");
|
|
248
|
+
closed.catch(() => {});
|
|
249
|
+
const analyzer = createFlashAnalyzer();
|
|
250
|
+
const frame = new Uint8Array(FRAME_BYTES);
|
|
251
|
+
let filled = 0;
|
|
252
|
+
for await (const chunk of child.stdout) {
|
|
253
|
+
let offset = 0;
|
|
254
|
+
while (offset < chunk.length) {
|
|
255
|
+
const take = Math.min(FRAME_BYTES - filled, chunk.length - offset);
|
|
256
|
+
frame.set(chunk.subarray(offset, offset + take), filled);
|
|
257
|
+
filled += take;
|
|
258
|
+
offset += take;
|
|
259
|
+
if (filled === FRAME_BYTES) {
|
|
260
|
+
analyzer.push(frame);
|
|
261
|
+
filled = 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const [code] = await closed;
|
|
266
|
+
if (code !== 0)
|
|
267
|
+
throw new Error(`FFmpeg could not decode the video: ${stderr.trim().slice(-2000)}`);
|
|
268
|
+
if (filled !== 0) throw new Error("Decoded video ended mid-frame; flashing is unverified");
|
|
269
|
+
const result = analyzer.result();
|
|
270
|
+
if (result.frames < 2) throw new Error("Too few decoded frames; flashing is unverified");
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function videoHash(file, signal) {
|
|
275
|
+
const hash = createHash("sha256");
|
|
276
|
+
for await (const chunk of createReadStream(file, { signal })) hash.update(chunk);
|
|
277
|
+
return hash.digest("hex");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function main() {
|
|
281
|
+
const [video, ...extra] = process.argv.slice(2);
|
|
282
|
+
if (!video || extra.length) throw new Error("usage: flash-check.mjs <rendered-video>");
|
|
283
|
+
const file = resolve(video);
|
|
284
|
+
if (!(await stat(file)).isFile()) throw new Error("Expected a local rendered video file");
|
|
285
|
+
const controller = new AbortController();
|
|
286
|
+
const abort = () => controller.abort();
|
|
287
|
+
process.once("SIGTERM", abort);
|
|
288
|
+
process.once("SIGINT", abort);
|
|
289
|
+
const started = performance.now();
|
|
290
|
+
try {
|
|
291
|
+
const videoSha256 = await videoHash(file, controller.signal);
|
|
292
|
+
const result = await analyzeFlashes(file, controller.signal);
|
|
293
|
+
if ((await videoHash(file, controller.signal)) !== videoSha256)
|
|
294
|
+
throw new Error("Video changed during analysis; flashing is unverified");
|
|
295
|
+
process.stdout.write(
|
|
296
|
+
`${JSON.stringify({ ...result, file, videoSha256, elapsedMs: Math.round(performance.now() - started) })}\n`,
|
|
297
|
+
);
|
|
298
|
+
if (!result.ok) process.exitCode = 1;
|
|
299
|
+
} finally {
|
|
300
|
+
process.removeListener("SIGTERM", abort);
|
|
301
|
+
process.removeListener("SIGINT", abort);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
306
|
+
try {
|
|
307
|
+
await main();
|
|
308
|
+
} catch (error) {
|
|
309
|
+
process.stderr.write(
|
|
310
|
+
`Flash verification failed: ${error instanceof Error ? error.message : "invalid input"}\n`,
|
|
311
|
+
);
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
// node fonts.mjs add <project-dir> <Family> [<Family> ...]
|
|
8
8
|
//
|
|
9
9
|
// `add` copies each family's woff2 files and OFL.txt into
|
|
10
|
-
// <project>/assets/fonts/<family
|
|
11
|
-
// with an @font-face for every bundled
|
|
12
|
-
//
|
|
10
|
+
// <project>/assets/fonts/<family>/, rewrites <project>/assets/fonts/fonts.css,
|
|
11
|
+
// and returns `head`: a <style> block with an @font-face for every bundled
|
|
12
|
+
// family installed there, using project-relative paths. Paste `head` into the
|
|
13
|
+
// composition <head>. `hf check` only sees @font-face written in the page, so a
|
|
14
|
+
// linked fonts.css renders but is reported as missing fonts.
|
|
13
15
|
// Prints one JSON line: {"ok":true,...} or {"ok":false,"error":...}.
|
|
14
16
|
import { copyFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
15
17
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -23,15 +25,16 @@ function done(result, code = 0) {
|
|
|
23
25
|
}
|
|
24
26
|
const fail = (error) => done({ ok: false, error }, 1);
|
|
25
27
|
|
|
26
|
-
/** @returns {Promise<Array<{family:string,dir:string,license:string,files:Array<{file:string,style:string,weight:string,stretch:string|null}>}>>} */
|
|
28
|
+
/** @returns {Promise<Array<{family:string,dir:string,license:string,use?:string,files:Array<{file:string,style:string,weight:string,stretch:string|null}>}>>} */
|
|
27
29
|
async function manifest() {
|
|
28
30
|
return JSON.parse(await readFile(join(LIBRARY, "fonts.json"), "utf8"));
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
/** `base` is the font folder as seen from the file that holds the rule. */
|
|
34
|
+
function fontFace(entry, file, base = "") {
|
|
32
35
|
const lines = [
|
|
33
36
|
` font-family: "${entry.family}";`,
|
|
34
|
-
` src: url("${entry.dir}/${file.file}") format("woff2");`,
|
|
37
|
+
` src: url("${base}${entry.dir}/${file.file}") format("woff2");`,
|
|
35
38
|
` font-style: ${file.style};`,
|
|
36
39
|
` font-weight: ${file.weight};`,
|
|
37
40
|
];
|
|
@@ -52,6 +55,7 @@ if (command === "list") {
|
|
|
52
55
|
styles: f.files.map((file) => file.style),
|
|
53
56
|
weight: f.files[0]?.weight,
|
|
54
57
|
width: f.files[0]?.stretch ?? null,
|
|
58
|
+
...(f.use ? { use: f.use } : {}),
|
|
55
59
|
})),
|
|
56
60
|
});
|
|
57
61
|
}
|
|
@@ -93,15 +97,15 @@ for (const entry of fonts) {
|
|
|
93
97
|
);
|
|
94
98
|
if (present) installed.push(entry);
|
|
95
99
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
await writeFile(join(target, "fonts.css"), `${
|
|
100
|
+
const rules = (base) =>
|
|
101
|
+
installed.flatMap((entry) => entry.files.map((file) => fontFace(entry, file, base)));
|
|
102
|
+
const header =
|
|
103
|
+
"/* Generated by EZ Motion fonts.mjs. SIL OFL 1.1: licenses sit beside each family. */";
|
|
104
|
+
await writeFile(join(target, "fonts.css"), `${[header, ...rules("")].join("\n\n")}\n`);
|
|
101
105
|
|
|
102
106
|
done({
|
|
103
107
|
ok: true,
|
|
104
108
|
installed: installed.map((f) => f.family),
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
head: `<style>\n${[header, ...rules("assets/fonts/")].join("\n")}\n</style>`,
|
|
110
|
+
use: "Paste head into the composition <head>, replacing any earlier fonts block.",
|
|
107
111
|
});
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
// node library.mjs show <id>
|
|
9
9
|
// node library.mjs look <project-dir> <look-id>
|
|
10
10
|
// node library.mjs add <project-dir> <piece-id> [<piece-id> ...] [--force]
|
|
11
|
+
// node library.mjs kit <project-dir>
|
|
11
12
|
//
|
|
12
13
|
// `look` writes <project>/assets/looks/<id>.css (design tokens as CSS
|
|
13
14
|
// variables) and installs the look's fonts. `add` copies pieces into
|
|
14
15
|
// <project>/compositions/<id>.html, installs their fonts, and records
|
|
15
|
-
// third-party credits in CREDITS.md.
|
|
16
|
+
// third-party credits in CREDITS.md. `kit` installs the move kit
|
|
17
|
+
// (library/kit/moves.js) as <project>/assets/kit/moves.js; `add` does the
|
|
18
|
+
// same for pieces that require it. Every command prints one JSON line.
|
|
16
19
|
import { execFileSync } from "node:child_process";
|
|
17
20
|
import { appendFile, copyFile, mkdir, readFile, stat } from "node:fs/promises";
|
|
18
21
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -46,14 +49,29 @@ async function projectDir(arg) {
|
|
|
46
49
|
return dir;
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
/** Returns the installed families and fonts.mjs's page-ready <style> block. */
|
|
49
53
|
function installFonts(project, families) {
|
|
50
|
-
if (families.length === 0) return [];
|
|
51
|
-
const out = execFileSync(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
if (families.length === 0) return { installed: [], head: [] };
|
|
55
|
+
const out = execFileSync(
|
|
56
|
+
process.execPath,
|
|
57
|
+
[join(BIN, "fonts.mjs"), "add", project, ...families],
|
|
58
|
+
{
|
|
59
|
+
encoding: "utf8",
|
|
60
|
+
},
|
|
61
|
+
);
|
|
54
62
|
const result = JSON.parse(out);
|
|
55
63
|
if (!result.ok) fail(`Font install failed: ${result.error}`);
|
|
56
|
-
|
|
64
|
+
// Inline, not a linked fonts.css: `hf check` only sees @font-face written in the page.
|
|
65
|
+
return { installed: result.installed, head: [result.head] };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const KIT_HEAD = '<script src="assets/kit/moves.js"></script>';
|
|
69
|
+
|
|
70
|
+
/** Copies the move kit into the project; returns the <script> for the root head. */
|
|
71
|
+
async function installKit(project) {
|
|
72
|
+
await mkdir(join(project, "assets", "kit"), { recursive: true });
|
|
73
|
+
await copyFile(join(LIBRARY, "kit", "moves.js"), join(project, "assets", "kit", "moves.js"));
|
|
74
|
+
return KIT_HEAD;
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
const summary = (entry, type) => ({
|
|
@@ -74,7 +92,8 @@ if (command === "list") {
|
|
|
74
92
|
const which = rest[0] === "looks" || rest[0] === "pieces" ? rest[0] : undefined;
|
|
75
93
|
const kindIndex = rest.indexOf("--kind");
|
|
76
94
|
const kind = kindIndex >= 0 ? rest[kindIndex + 1] : undefined;
|
|
77
|
-
if (kind && !lib.kinds.includes(kind))
|
|
95
|
+
if (kind && !lib.kinds.includes(kind))
|
|
96
|
+
fail(`Unknown kind "${kind}". Kinds: ${lib.kinds.join(", ")}`);
|
|
78
97
|
const looks = which === "pieces" || kind ? [] : lib.looks.map((l) => summary(l, "look"));
|
|
79
98
|
const pieces =
|
|
80
99
|
which === "looks"
|
|
@@ -83,7 +102,8 @@ if (command === "list") {
|
|
|
83
102
|
// Contact sheets show many previews in one image read.
|
|
84
103
|
const sheets = {};
|
|
85
104
|
if (looks.length > 0) sheets.looks = join(LIBRARY, "sheets", "looks.jpg");
|
|
86
|
-
for (const k of new Set(pieces.map((p) => p.kind)))
|
|
105
|
+
for (const k of new Set(pieces.map((p) => p.kind)))
|
|
106
|
+
sheets[k] = join(LIBRARY, "sheets", `${k}.jpg`);
|
|
87
107
|
done({ ok: true, kinds: lib.kinds, sheets, looks, pieces });
|
|
88
108
|
}
|
|
89
109
|
|
|
@@ -118,7 +138,11 @@ if (command === "show") {
|
|
|
118
138
|
...(look ?? piece),
|
|
119
139
|
preview: join(dir, "preview.jpg"),
|
|
120
140
|
files: look
|
|
121
|
-
? {
|
|
141
|
+
? {
|
|
142
|
+
spec: join(dir, "look.md"),
|
|
143
|
+
tokens: join(dir, "tokens.css"),
|
|
144
|
+
sample: join(dir, "sample.html"),
|
|
145
|
+
}
|
|
122
146
|
: { source: join(dir, "piece.html") },
|
|
123
147
|
});
|
|
124
148
|
}
|
|
@@ -135,16 +159,26 @@ if (command === "look") {
|
|
|
135
159
|
ok: true,
|
|
136
160
|
look: look.id,
|
|
137
161
|
tokens: css,
|
|
138
|
-
fonts,
|
|
139
|
-
head: [
|
|
140
|
-
|
|
141
|
-
`<link rel="stylesheet" href="assets/looks/${look.id}.css" />`,
|
|
142
|
-
],
|
|
162
|
+
fonts: fonts.installed,
|
|
163
|
+
head: [...fonts.head, `<link rel="stylesheet" href="assets/looks/${look.id}.css" />`],
|
|
164
|
+
use: "Paste head into the composition <head>, replacing any earlier fonts block.",
|
|
143
165
|
root: `add class="look-${look.id}" to the root composition element`,
|
|
144
166
|
spec: join(LIBRARY, "looks", look.id, "look.md"),
|
|
145
167
|
});
|
|
146
168
|
}
|
|
147
169
|
|
|
170
|
+
if (command === "kit") {
|
|
171
|
+
const project = await projectDir(rest[0]);
|
|
172
|
+
const kit = await installKit(project);
|
|
173
|
+
done({
|
|
174
|
+
ok: true,
|
|
175
|
+
installed: join(project, "assets", "kit", "moves.js"),
|
|
176
|
+
kit,
|
|
177
|
+
use: "Put kit in the root index.html <head> after GSAP; scripts then use window.GGMotionKit.",
|
|
178
|
+
reference: join(LIBRARY, "README.md"),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
148
182
|
if (command === "add") {
|
|
149
183
|
const force = rest.includes("--force");
|
|
150
184
|
const [projectArg, ...ids] = rest.filter((a) => a !== "--force");
|
|
@@ -187,17 +221,26 @@ if (command === "add") {
|
|
|
187
221
|
let importmap;
|
|
188
222
|
if (pieces.some((p) => (p.requires ?? []).includes("three"))) {
|
|
189
223
|
const three = JSON.parse(
|
|
190
|
-
execFileSync(process.execPath, [join(BIN, "three.mjs"), "add", project], {
|
|
224
|
+
execFileSync(process.execPath, [join(BIN, "three.mjs"), "add", project], {
|
|
225
|
+
encoding: "utf8",
|
|
226
|
+
}),
|
|
191
227
|
);
|
|
192
228
|
if (!three.ok) fail(`Three.js install failed: ${three.error}`);
|
|
193
229
|
importmap = three.importmap;
|
|
194
230
|
}
|
|
231
|
+
// Kit pieces call window.GGMotionKit, loaded once by the root page.
|
|
232
|
+
const kit = pieces.some((p) => (p.requires ?? []).includes("kit"))
|
|
233
|
+
? await installKit(project)
|
|
234
|
+
: undefined;
|
|
195
235
|
done({
|
|
196
236
|
ok: true,
|
|
197
237
|
added,
|
|
198
238
|
kept,
|
|
199
|
-
fonts,
|
|
200
|
-
...(importmap
|
|
239
|
+
fonts: fonts.installed,
|
|
240
|
+
...(importmap
|
|
241
|
+
? { importmap, note: "put importmap in index.html <head> before any module script" }
|
|
242
|
+
: {}),
|
|
243
|
+
...(kit ? { kit, kitNote: "put kit in index.html <head> after GSAP" } : {}),
|
|
201
244
|
mount: pieces.map(
|
|
202
245
|
(p) =>
|
|
203
246
|
`<div id="${p.id}-clip" data-composition-id="${p.id}" data-composition-src="compositions/${p.id}.html" data-start="0" data-duration="${p.duration}" data-width="1920" data-height="1080" data-track-index="0"></div>`,
|
|
@@ -206,5 +249,5 @@ if (command === "add") {
|
|
|
206
249
|
}
|
|
207
250
|
|
|
208
251
|
fail(
|
|
209
|
-
"usage: library.mjs list [looks|pieces] [--kind k] | search <words> | show <id> | look <project> <look-id> | add <project> <piece-id>... [--force]",
|
|
252
|
+
"usage: library.mjs list [looks|pieces] [--kind k] | search <words> | show <id> | look <project> <look-id> | add <project> <piece-id>... [--force] | kit <project>",
|
|
210
253
|
);
|