luckiest-co 1.0.32 → 1.0.33
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/commands/plan.md +1 -0
- package/package.json +1 -1
- package/skills/luckiest-plan/SKILL.md +1 -0
- package/skills/luckiest-research/CHANGELOG.md +5 -0
- package/skills/luckiest-research/SKILL.md +8 -4
- package/skills/luckiest-video-studio/ATTRIBUTION.md +11 -0
- package/skills/luckiest-video-studio/CHANGELOG.md +56 -0
- package/skills/luckiest-video-studio/SKILL.md +35 -7
- package/skills/luckiest-video-studio/references/audio.md +61 -10
- package/skills/luckiest-video-studio/references/lessons.md +44 -0
- package/skills/luckiest-video-studio/references/step-2-plan.md +32 -1
- package/skills/luckiest-video-studio/references/step-3-compose.md +28 -0
- package/skills/luckiest-video-studio/references/step-4-deliver.md +24 -1
- package/skills/luckiest-video-studio/references/storyboard.md +24 -3
- package/skills/luckiest-video-studio/scripts/qa.mjs +167 -5
- package/skills/luckiest-video-studio/scripts/qa.test.mjs +99 -1
- package/skills/luckiest-video-studio/scripts/render-scenes.mjs +196 -71
- package/skills/luckiest-video-studio/scripts/render-scenes.test.mjs +109 -0
- package/skills/luckiest-video-studio/scripts/sfx-cues.mjs +116 -0
- package/skills/luckiest-video-studio/scripts/sfx-cues.test.mjs +76 -0
- package/skills/luckiest-video-studio/scripts/stamp-format.mjs +26 -0
- package/skills/luckiest-video-studio/scripts/storyboard.schema.json +23 -0
- package/skills/luckiest-video-studio/templates/logo-line-draw.html +12 -5
- package/skills/luckiest-video-studio-art-director/ATTRIBUTION.md +1 -0
- package/skills/luckiest-video-studio-art-director/CHANGELOG.md +8 -0
- package/skills/luckiest-video-studio-art-director/SKILL.md +18 -5
- package/skills/luckiest-video-studio-cards/CHANGELOG.md +18 -0
- package/skills/luckiest-video-studio-cards/SKILL.md +22 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-agenda.html +17 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-bigtype.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-board.html +20 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-checklist.html +27 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-compare.html +40 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-deck.html +26 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-dossier.html +28 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-filmstrip.html +36 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-grid6.html +24 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-indexset.html +34 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-maplegend.html +39 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-matrix.html +35 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-numlist.html +26 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-outline.html +26 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-phases.html +34 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-quad.html +41 -10
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-roadmap.html +66 -17
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-stepper.html +42 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-toc.html +26 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-vtimeline.html +35 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-bracket.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-circle.html +21 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-classified.html +25 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-collage.html +27 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-correction.html +19 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-crt.html +15 -4
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-cutout.html +18 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-fullbleed.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-highlight.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-indexcard.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-map.html +20 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-markerbox.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-numeral.html +21 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-split.html +25 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-stacked.html +26 -10
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-stamp.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-ticker.html +26 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-tornstrip.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-annotated.html +29 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-arc.html +25 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-axis.html +34 -14
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-bar.html +17 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-bracket.html +24 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-calendar.html +24 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-callout.html +24 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-columns.html +27 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-corner.html +22 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-dots.html +27 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-engraving.html +30 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-footnote.html +20 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-frac.html +24 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-heat.html +22 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-isotype.html +29 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ledger.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-left.html +27 -10
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-magnifier.html +21 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-map.html +21 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-meter.html +26 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-multiplier.html +27 -12
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-odo.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pack.html +41 -17
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-photo.html +18 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pie.html +25 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-postage.html +24 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pricetag.html +25 -11
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-quote.html +21 -8
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ranking.html +25 -10
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-receipt.html +24 -12
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ribbon.html +34 -12
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-semi.html +22 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-spotlight.html +23 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-stamp.html +24 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-steps.html +30 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-tape.html +33 -12
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-torn.html +22 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-trend.html +23 -9
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-vs.html +30 -10
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-window.html +38 -25
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat.html +23 -4
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-timeline.html +41 -6
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-label.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-bracket.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-caption.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-circle.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-definition.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-footnote.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-magnify.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-pin.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-pullquote.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-spotlight.html +15 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-sticky.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-tag.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-underline.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lower-third.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-badge.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-barslab.html +26 -7
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-bracket.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-clapper.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-highlight.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-indextab.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-rail.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-ribbon.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-stacked.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-stamp.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-sticky.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-typecard.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/style.json +1 -1
- package/skills/luckiest-video-studio-cards/packs/01-paper-collage/tokens.css +18 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-agenda.html +23 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-beforeafter.html +30 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-bento.html +34 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-bigleft.html +28 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-callouts.html +36 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-cardrow5.html +17 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-checklist.html +11 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-cycle.html +30 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-dashboard.html +32 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-deck2.html +21 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-deck3.html +18 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-duallist.html +24 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-featurecompare.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-featuregrid.html +25 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-flashcards.html +22 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-flow.html +23 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-funnel.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-gantt.html +22 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-grid6.html +17 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-grid9.html +15 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-iconlist.html +18 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-journey.html +41 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-kpirow.html +24 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-layers.html +20 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-legend.html +19 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-levels.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-logowall.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-mappoints.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-matrix.html +24 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-milestones.html +38 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-mindmap.html +35 -12
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-nested.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-network.html +76 -31
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-notifs.html +21 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-numlist.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-orgchart.html +36 -16
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-outline.html +17 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-phases.html +23 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pipeline.html +25 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pricing.html +36 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-profiles.html +20 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-proscons.html +21 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pyramid.html +17 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-quad.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-quotegrid.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-ranked.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-roadmap.html +27 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-spectrum.html +29 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-split3.html +24 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-spotlightcard.html +34 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-statgrid.html +17 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-steps.html +25 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-summary.html +22 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tiercompare.html +29 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-timeline.html +26 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-toc.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tree.html +40 -15
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tricards.html +22 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-venn.html +34 -9
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-vstable.html +24 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ascend.html +10 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-aurora-bars.html +12 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-beam.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-boxed.html +26 -13
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-breath.html +14 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-chaptercard.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-column-light.html +17 -9
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-constellation.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-countdown.html +10 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-curtain.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-dawn.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-depth.html +18 -12
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-divider.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-drift-grid.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-duotone.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-eclipse.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-emblem.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-flare-sweep.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-glow-rule.html +22 -13
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-gradient-word.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-gridfloor.html +16 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-halo.html +16 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-horizon.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-index.html +25 -9
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-kinetic.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-lensflare.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-liquid.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-marquee.html +34 -25
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-mesh.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-monolith.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-node-title.html +17 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-numeral.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-orb.html +12 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-orbit.html +9 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-particles.html +7 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-pill.html +7 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-portal.html +14 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-prism.html +15 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-question.html +14 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-quoteopen.html +11 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-reveal-mask.html +6 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ribbon.html +12 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ripple.html +8 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-searchbar.html +15 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-slab.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-sphere-top.html +12 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-split-reveal.html +24 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-splitpanel.html +27 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-spotlight-cone.html +16 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-spotlight.html +10 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-stack.html +17 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-stacked-words.html +14 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-starfield.html +7 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-sweep.html +11 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-tunnel.html +14 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-type-scale.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-typed.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-underline-grow.html +13 -1
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-waveform.html +11 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-wordmark.html +11 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-annot.html +22 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-beforeafter.html +23 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bigpercent.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bracketstat.html +13 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bubble.html +7 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-cbars.html +8 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-clockface.html +8 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-column.html +12 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-compass.html +7 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-counterpill.html +11 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-curve.html +22 -10
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-delta.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-dial.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-donut.html +15 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-dotgrid.html +14 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-figure.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-fillshape.html +11 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-fraction.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-funnel.html +24 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-growtharrow.html +18 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-heatcell.html +8 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-isotype.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-layered.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-mega.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-meter.html +9 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-milestone.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-money.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-multiplier.html +12 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-multiring.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-needle.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-nodes.html +50 -9
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-odometer.html +9 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-orbitmoons.html +21 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-orbitstat.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-pair.html +18 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-percentile.html +11 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-pictobar.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-polar.html +12 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-quotestat.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-radialbars.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-range.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-rank.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-ratio.html +10 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-ring.html +10 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-risewall.html +18 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-score.html +11 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-segbar.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-semigauge.html +9 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-sparkline.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-spectrum.html +9 -2
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-split.html +14 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-stackedbars.html +9 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-tickerstat.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-tiles.html +18 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-timer.html +14 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-trendline.html +18 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-underscore.html +9 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-versus.html +18 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-waffle.html +18 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-waterfall.html +9 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-badge.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-brace.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-bracket.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-bubble.html +16 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-byline.html +17 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-callout.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-category.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-checkmark.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-chipstat.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-comparemini.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-coord.html +26 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-counter.html +17 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-crosshair.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-dashed.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-definition.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-deltachip.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-eyebrow.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-fact.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-flag.html +20 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-formula.html +17 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-hash.html +17 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-highlightbox.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-iconchip.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-info.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-infocard.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-keycap.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-keyword.html +12 -3
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-leader.html +16 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-legend.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-measure.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-meterchip.html +16 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-metric.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-micropie.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-minibar.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-node.html +28 -18
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-note.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-numbercallout.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-percent.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-pointer.html +31 -22
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-price.html +20 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-progress.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-pulse.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-qmark.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-quote.html +16 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-rating.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-ribbon.html +19 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-ringmark.html +27 -17
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-search.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-sparkchip.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-spec.html +25 -13
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-status.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-step.html +16 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tag.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-takeaway.html +13 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tcode.html +18 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-timestamp.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-toast.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tooltip.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-underline.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-warning.html +16 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-avatar.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-badgecard.html +12 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-barslide.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-bracket.html +20 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-brandbar.html +13 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-bug.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-capsule.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-chapterbar.html +12 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-chip.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cinemastrip.html +20 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cite.html +12 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-clockname.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-coordbar.html +16 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-corner.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cornerframe.html +16 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-countbar.html +13 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dividername.html +26 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dotline.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-doublecard.html +12 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dual.html +12 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-edgebar.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-flipcard.html +15 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-geopin.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-glasscard.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-gradient.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-gradientbar.html +12 -4
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-handlebar.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-infochips.html +11 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-lensbar.html +25 -12
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-live.html +12 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-location.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-logolock.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-micname.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-minicard.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-minimal.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-nameplate3d.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-neonframe.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-neonpill.html +13 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-nowplaying.html +15 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-orbitbadge.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-pill.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-portraitframe.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-progressname.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-quote.html +15 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ratingname.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ribbon.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-sidebracket.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-slidetab.html +15 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-social.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-spotlightname.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-stacked.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-statline.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-tagrow.html +14 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-thinstrip.html +18 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ticker.html +29 -7
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-titlebar.html +19 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-trust.html +16 -8
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-typedname.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-underbar.html +13 -5
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-waveband.html +14 -6
- package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/tokens.css +18 -8
- package/skills/luckiest-video-studio-cards/packs/_blueprint/cards/tier1/t1-stat.html +4 -2
- package/skills/luckiest-video-studio-cards/packs/_blueprint/cards/tier2/t2-list.html +5 -2
- package/skills/luckiest-video-studio-cards/packs/_blueprint/tokens.css +12 -4
- package/skills/luckiest-video-studio-cards/references/card-contract.md +15 -1
- package/skills/luckiest-video-studio-cards/scripts/qa-cards.mjs +14 -8
- package/skills/luckiest-video-studio-cards/templates/glass-popout/index.html +35 -11
- package/skills/luckiest-video-studio-cards/templates/glass-popout/meta.json +6 -1
- package/skills/luckiest-video-studio-cards/templates/graph-paper/index.html +15 -10
- package/skills/luckiest-video-studio-cards/templates/graph-paper/meta.json +7 -2
- package/skills/luckiest-video-studio-motion/ATTRIBUTION.md +4 -0
- package/skills/luckiest-video-studio-motion/CHANGELOG.md +9 -0
- package/skills/luckiest-video-studio-motion/SKILL.md +7 -3
- package/skills/luckiest-video-studio-motion/references/vocabulary.md +21 -0
- package/skills/luckiest-video-studio-motion/templates/spring.js +50 -0
- package/skills/luckiest-video-studio-motion/templates/spring.test.mjs +51 -0
- package/skills/luckiest-video-studio-recreate/ATTRIBUTION.md +2 -0
- package/skills/luckiest-video-studio-recreate/CHANGELOG.md +11 -0
- package/skills/luckiest-video-studio-recreate/SKILL.md +46 -7
- package/skills/luckiest-video-studio-reel/CHANGELOG.md +6 -0
- package/skills/luckiest-video-studio-reel/SKILL.md +9 -4
- package/skills/luckiest-video-studio-reel/templates/reel.html +36 -18
- package/skills/luckiest-video-studio-reference/CHANGELOG.md +4 -0
- package/skills/luckiest-video-studio-reference/SKILL.md +10 -3
- package/skills/luckiest-video-studio-review/ATTRIBUTION.md +4 -0
- package/skills/luckiest-video-watcher/ATTRIBUTION.md +37 -0
- package/skills/luckiest-video-watcher/CHANGELOG.md +70 -0
- package/skills/luckiest-video-watcher/LICENSE-claude-video +21 -0
- package/skills/luckiest-video-watcher/SKILL.md +181 -0
- package/skills/luckiest-video-watcher/references/watch-guide.md +159 -0
- package/skills/luckiest-video-watcher/scripts/config.py +174 -0
- package/skills/luckiest-video-watcher/scripts/download.py +207 -0
- package/skills/luckiest-video-watcher/scripts/frames.py +758 -0
- package/skills/luckiest-video-watcher/scripts/gemini.py +162 -0
- package/skills/luckiest-video-watcher/scripts/local_whisperx.py +147 -0
- package/skills/luckiest-video-watcher/scripts/runtime.py +35 -0
- package/skills/luckiest-video-watcher/scripts/setup.py +338 -0
- package/skills/luckiest-video-watcher/scripts/transcribe.py +113 -0
- package/skills/luckiest-video-watcher/scripts/watch.py +430 -0
- package/skills/luckiest-video-watcher/scripts/whisper.py +425 -0
|
@@ -14,6 +14,7 @@ this file and re-renders only what it touched. Schema:
|
|
|
14
14
|
"mode": "demo",
|
|
15
15
|
"tone": "polished",
|
|
16
16
|
"format": { "width": 1920, "height": 1080, "fps": 30 },
|
|
17
|
+
"formats": ["landscape", "vertical", "square"],
|
|
17
18
|
"hyperframes": "0.8.84",
|
|
18
19
|
"music": { "file": "audio/bed.mp3", "volume": 0.3, "license": "user supplied" },
|
|
19
20
|
"scenes": [
|
|
@@ -27,16 +28,23 @@ this file and re-renders only what it touched. Schema:
|
|
|
27
28
|
|
|
28
29
|
| Field | Meaning |
|
|
29
30
|
|---|---|
|
|
30
|
-
| `format` |
|
|
31
|
+
| `format` | Master size and frame rate. Landscape 1920x1080, vertical 1080x1920, square 1080x1080. Scenes are designed and reviewed at this size. |
|
|
32
|
+
| `formats` | Optional. Render the same scenes once per listed format: `landscape` 1920x1080, `vertical` 1080x1920, `square` 1080x1080, all at `format.fps`. Motion and logo scenes re-lay out for each size (see "Build once, render every format" in `step-3-compose.md`); they are never cropped. Only demo footage is cropped, around `scenes[].focus`. Omit it to render `format` alone. |
|
|
31
33
|
| `hyperframes` | Pinned HyperFrames version so a re-render months later looks the same. |
|
|
32
|
-
| `music` | Optional bed mixed under the joined video, looped and faded out. Licensed files only. |
|
|
34
|
+
| `music` | Optional bed mixed under the joined video, looped and faded out. Licensed files only. `offset` skips into the track and `at` starts it in the video, so a chosen kick lands on a chosen moment: a kick 10.4s into the track on a cut at 3.0s is `"offset": 7.4, "at": 0` or `"offset": 10.4, "at": 3`. |
|
|
35
|
+
| `facts` | Every number, price, and id the video shows, as short phrases: `["3 unpaid invoices, $4,792.44 total", "Tom Becker INV-0039, 5 days late"]`. Write them in Step 2 from the project's real data. `qa.mjs facts` fails any on-screen number with two or more digits, a currency sign, a percent, or an id that no fact contains, and any em dash on screen. Add decorative numbers (a timecode, a year) here too. |
|
|
36
|
+
| `loudness` | Integrated loudness of `final.mp4` in LUFS, default `-14` (the streaming norm), two-pass with a -1.2 dBTP ceiling. `false` skips it. |
|
|
37
|
+
| `sfx` | Optional `{ "file": "audio/sfx.wav", "volume": 1 }` from `scripts/sfx-cues.mjs`, mixed under the joined video at 0 s. Cue times are global, so re-run the mixer after a scene's length changes. |
|
|
33
38
|
| `scenes[].id` | Lowercase and hyphens. Stable: edits refer to scenes by id. |
|
|
34
39
|
| `scenes[].kind` | `motion` (HyperFrames scene), `logo` (HyperFrames logo recipe), `demo` (footage). |
|
|
35
40
|
| `scenes[].composition` | For motion and logo, the sub-composition under `composition/`. Default `compositions/<id>.html`. |
|
|
36
41
|
| `scenes[].source`, `sourceStart` | For demo, the footage file and where to start in it. |
|
|
42
|
+
| `scenes[].focus` | For demo, `{ "x": 0.5, "y": 0.5 }` as fractions of the source frame: the point kept in view when the footage is cropped to a different shape (a 16:9 screen recording in a vertical cut). Set it on the cursor, the face, or the UI the line talks about. |
|
|
37
43
|
| `scenes[].variables` | Copy, colors, and file paths the composition reads. Keep editable values here, not hardcoded in HTML. |
|
|
38
44
|
| `scenes[].assets` | Other files the scene depends on (logo SVG, shared CSS). Changing one re-renders the scene. |
|
|
45
|
+
| `scenes[].states` | For a one-shape morph scene, the state list: `{ t, label, shape, cursor? }` per state, local times. See "One shape, never cut" in `step-2-plan.md`. |
|
|
39
46
|
| `scenes[].line` | One line on what the scene says or shows. Goes into `TIMING.md`. |
|
|
47
|
+
| `scenes[].hold` | Optional. Seconds into the scene where `--stills` captures its hold frame. Default 0.5s before the cut. |
|
|
40
48
|
|
|
41
49
|
## Rendering
|
|
42
50
|
|
|
@@ -45,6 +53,7 @@ node <skill-dir>/scripts/render-scenes.mjs <run-dir> # render
|
|
|
45
53
|
node <skill-dir>/scripts/render-scenes.mjs <run-dir> --scene stat # force one scene
|
|
46
54
|
node <skill-dir>/scripts/render-scenes.mjs <run-dir> --quality draft # fast pass while iterating
|
|
47
55
|
node <skill-dir>/scripts/render-scenes.mjs <run-dir> --no-join # clips only
|
|
56
|
+
node <skill-dir>/scripts/render-scenes.mjs <run-dir> --stills # hold-frame PNGs per format, no render
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
The script hashes each scene's JSON, its composition or footage, and its `assets`.
|
|
@@ -53,6 +62,16 @@ scenes and leaves the rest as they are. Every clip is normalized to the same siz
|
|
|
53
62
|
frame rate, and a stereo audio track, so the join is a lossless concat. It writes
|
|
54
63
|
`clips/NN-id.mp4`, `final.mp4`, and `TIMING.md`.
|
|
55
64
|
|
|
65
|
+
With `formats`, each format gets its own folder and file: `clips/<format>/NN-id.mp4`
|
|
66
|
+
and `final-<format>.mp4`, each with its own cache, so adding a format renders only
|
|
67
|
+
that format. A landscape format reuses the clips of a finished single-format 16:9
|
|
68
|
+
render at the same `--quality`, so adding formats after the master is done renders
|
|
69
|
+
only the new sizes (demo footage re-crops, which is ffmpeg only). For motion and logo scenes it writes a stamped copy of the composition
|
|
70
|
+
next to the original (`compositions/<id>.<format>.html`, root `data-width`,
|
|
71
|
+
`data-height`, and `data-format` set, plus `:root{--w;--h;--u}`), renders it, and
|
|
72
|
+
deletes the copy. A scene that sizes itself with those tokens re-lays out; one
|
|
73
|
+
hardcoded to 1920x1080 px gets clipped, which `qa.mjs` catches.
|
|
74
|
+
|
|
56
75
|
It runs `hyperframes` when it is on the PATH, otherwise `npx hyperframes@<pinned
|
|
57
76
|
version>`. Before the first `npx` run in a session, tell the user it downloads
|
|
58
77
|
HyperFrames from npm and ask.
|
|
@@ -75,4 +94,6 @@ HyperFrames from npm and ask.
|
|
|
75
94
|
| "Make the demo part shorter" | `duration` or `sourceStart` of that demo scene |
|
|
76
95
|
| "Swap scenes 2 and 4" | Reorder `scenes`; clips are reused and renamed, nothing re-renders |
|
|
77
96
|
| "Different logo in the outro" | Replace the file listed in that scene's `assets` |
|
|
78
|
-
| "Vertical version" |
|
|
97
|
+
| "Vertical version" | Add `"vertical"` to `formats`; existing formats keep their clips and only the new one renders |
|
|
98
|
+
| "Square too" | Add `"square"` to `formats` |
|
|
99
|
+
| "The crop cuts off the button" | Move that demo scene's `focus` toward the button, then `--scene <id>` |
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
// node qa.mjs beatsync <talk-dir> plan anchors against the transcript words
|
|
13
13
|
// node qa.mjs beatgrid <video> --bpm 128 [--phase 0] [--tol 0.035] cuts on the beat or half beat
|
|
14
14
|
// node qa.mjs face <run-dir> [--max 18] cards covering the speaker's face (scene faceRect)
|
|
15
|
+
// node qa.mjs avsync <video> [--expect 1.5,4.2] [--duration 20] audio present, length right, hits within one frame
|
|
16
|
+
// node qa.mjs loop <video> [--max 6] last frame must match the first, so the piece loops
|
|
17
|
+
// node qa.mjs facts <run-dir> on-screen numbers not in storyboard facts, em dashes on screen
|
|
18
|
+
// node qa.mjs safe <run-dir> [--max 0.004] [--stills] text or logo edges in the margins each format's platforms cover
|
|
15
19
|
// node qa.mjs judge <new.mp4> <old.mp4> [--out dir] [--cmd "claude -p --model sonnet"] [--verdicts A,B]
|
|
16
20
|
// new vs old, asked twice with the order swapped; the new one is kept only if it wins both
|
|
17
21
|
//
|
|
@@ -22,6 +26,7 @@ import { readFileSync, readdirSync, existsSync, statSync, mkdirSync, writeFileSy
|
|
|
22
26
|
import { join, resolve, extname } from "node:path";
|
|
23
27
|
import { spawnSync } from "node:child_process";
|
|
24
28
|
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { FORMATS, SAFE } from "./stamp-format.mjs";
|
|
25
30
|
|
|
26
31
|
// ---------- shared ----------
|
|
27
32
|
|
|
@@ -68,12 +73,15 @@ export function scanTokens(html, tokens = DEFAULT_TOKENS) {
|
|
|
68
73
|
|
|
69
74
|
// ---------- cross-fade ----------
|
|
70
75
|
|
|
71
|
-
// Selectors whose CSS makes them cover the whole frame.
|
|
76
|
+
// Selectors whose CSS makes them cover the whole frame: 100%, the size tokens, or the root's own data-width/data-height in px.
|
|
72
77
|
export function fullFrameSelectors(html) {
|
|
78
|
+
const root = html.match(/<div\b[^>]*\bdata-composition-id\b[^>]*>/i)?.[0] ?? "";
|
|
79
|
+
const [w, h] = ["width", "height"].map((d, i) => root.match(new RegExp(`data-${d}\\s*=\\s*["']?(\\d+)`))?.[1] ?? [1920, 1080][i]);
|
|
80
|
+
const fullW = new RegExp(`width:(100%|var\\(--w\\)|${w}px)`), fullH = new RegExp(`height:(100%|var\\(--h\\)|${h}px)`);
|
|
73
81
|
const sels = new Set();
|
|
74
82
|
for (const m of html.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
|
75
83
|
const body = m[2].replace(/\s/g, "");
|
|
76
|
-
const full = /inset:0(;|$)/.test(body) || (
|
|
84
|
+
const full = /inset:0(;|$)/.test(body) || (fullW.test(body) && fullH.test(body));
|
|
77
85
|
if (full) for (const s of m[1].split(",")) { const t = s.trim().split(/[\s>]+/).pop(); if (/^[.#][\w-]+$/.test(t)) sels.add(t); }
|
|
78
86
|
}
|
|
79
87
|
return sels;
|
|
@@ -237,6 +245,79 @@ export function beatGrid(cuts, { bpm, phase = 0, tol = 0.035 }) {
|
|
|
237
245
|
return { ok: rows.every((r) => r.ok), off: rows.filter((r) => !r.ok), cuts: rows.length };
|
|
238
246
|
}
|
|
239
247
|
|
|
248
|
+
// ---------- avsync ----------
|
|
249
|
+
|
|
250
|
+
// Times where the audio's short-window energy jumps (a hit, a click, a whoosh's attack).
|
|
251
|
+
export function audioOnsets(video, { rate = 8000, win = 0.005, rise = 4, floor = 0.02 } = {}) {
|
|
252
|
+
const r = ff(["-v", "error", "-i", video, "-vn", "-ac", "1", "-ar", String(rate), "-f", "s16le", "-"]);
|
|
253
|
+
if (r.status !== 0) throw new Error(`ffmpeg failed on ${video}: ${r.stderr}`);
|
|
254
|
+
const pcm = new Int16Array(r.stdout.buffer, r.stdout.byteOffset, r.stdout.length >> 1);
|
|
255
|
+
const n = Math.round(rate * win), e = [];
|
|
256
|
+
for (let o = 0; o + n <= pcm.length; o += n) { let s = 0; for (let i = o; i < o + n; i++) s += (pcm[i] / 32768) ** 2; e.push(Math.sqrt(s / n)); }
|
|
257
|
+
const back = Math.round(0.05 / win), onsets = [];
|
|
258
|
+
for (let i = 1; i < e.length; i++) {
|
|
259
|
+
let prev = 0; for (let j = Math.max(0, i - back); j < i; j++) prev += e[j];
|
|
260
|
+
prev /= Math.max(1, Math.min(back, i));
|
|
261
|
+
// ponytail: energy-rise onsets, good for hits on a quiet-ish bed; under a dense mix pass the SFX stem instead.
|
|
262
|
+
if (e[i] > floor && e[i] > rise * prev && !(onsets.length && i * win - onsets.at(-1) < 0.1)) onsets.push(Number((i * win).toFixed(3)));
|
|
263
|
+
}
|
|
264
|
+
return onsets;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// The final MP4 has an audio stream, the expected length, and each expected hit lands within one frame.
|
|
268
|
+
export function avSync(video, { expect = [], duration } = {}) {
|
|
269
|
+
const { fps, duration: vdur } = probe(video), tol = 1 / fps;
|
|
270
|
+
const r = spawnSync("ffprobe", ["-v", "error", "-select_streams", "a:0", "-show_entries", "stream=duration", "-of", "json", video], { encoding: "utf8" });
|
|
271
|
+
const a = JSON.parse(r.stdout || "{}").streams?.[0];
|
|
272
|
+
if (!a) return { ok: false, audio: false, hits: [] };
|
|
273
|
+
const problems = [], adur = Number(a.duration);
|
|
274
|
+
// ponytail: two frames of slack for the encoder's audio padding.
|
|
275
|
+
if (Number.isFinite(adur) && Math.abs(adur - vdur) > 2 * tol) problems.push(`audio ${adur.toFixed(3)}s vs video ${vdur.toFixed(3)}s`);
|
|
276
|
+
if (duration != null && Math.abs(vdur - duration) > tol) problems.push(`video ${vdur.toFixed(3)}s, expected ${duration}s`);
|
|
277
|
+
const on = expect.length ? audioOnsets(video) : [];
|
|
278
|
+
const hits = expect.map((t) => {
|
|
279
|
+
const near = on.reduce((b, o) => (b == null || Math.abs(o - t) < Math.abs(b - t) ? o : b), null);
|
|
280
|
+
const offset = near == null ? null : Number((near - t).toFixed(3));
|
|
281
|
+
return { t, onset: near, offset, ok: offset != null && Math.abs(offset) <= tol };
|
|
282
|
+
});
|
|
283
|
+
return { ok: problems.length === 0 && hits.every((h) => h.ok), audio: true, problems, hits, tolerance: Number(tol.toFixed(3)) };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ---------- loop ----------
|
|
287
|
+
|
|
288
|
+
// A looping piece (a state-list morph) must end on its first frame, or the repeat shows a jump.
|
|
289
|
+
export function loopCheck(video, { max = 6 } = {}) {
|
|
290
|
+
const frames = grayFrames(video);
|
|
291
|
+
const diff = meanDiff(frames[0], frames.at(-1));
|
|
292
|
+
return { ok: diff <= max, diff: Number(diff.toFixed(2)), frames: frames.length };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ---------- facts ----------
|
|
296
|
+
|
|
297
|
+
const ENTITIES = { amp: "&", nbsp: " ", mdash: "\u2014", ndash: "\u2013", lt: "<", gt: ">", quot: '"', "#39": "'", "#8212": "\u2014" };
|
|
298
|
+
|
|
299
|
+
// What a viewer can read: markup, scripts, styles, and comments removed. Template contents stay (sub-compositions).
|
|
300
|
+
export const visibleText = (html) => html
|
|
301
|
+
.replace(/<!--[\s\S]*?-->/g, " ").replace(/<(script|style)\b[\s\S]*?<\/\1>/gi, " ")
|
|
302
|
+
.replace(/<[^>]+>/g, " ").replace(/&(#?\w+);/g, (m, e) => ENTITIES[e] ?? m);
|
|
303
|
+
|
|
304
|
+
// ponytail: single digits pass unchecked (step numbers, "1 click"); currency, percents, ids, and 2+ digit numbers are checked.
|
|
305
|
+
const NUMBERS = /[A-Z]{2,}-\d+|[$€£]\s?\d[\d,]*(?:\.\d+)?|\d[\d,]*(?:\.\d+)?\s?%|\d{1,3}(?:,\d{3})+(?:\.\d+)?|\d{2,}(?:\.\d+)?|\d\.\d+/g;
|
|
306
|
+
|
|
307
|
+
// texts: [{ where, text }]. A number passes when it appears inside one of the story facts.
|
|
308
|
+
export function scanFacts(texts, facts = []) {
|
|
309
|
+
const norm = (x) => x.replace(/\s/g, "");
|
|
310
|
+
const known = facts.map(norm);
|
|
311
|
+
const found = [];
|
|
312
|
+
for (const { where, text } of texts) {
|
|
313
|
+
for (const token of text.match(NUMBERS) ?? []) {
|
|
314
|
+
if (!known.some((f) => f.includes(norm(token)))) found.push({ where, rule: "not-a-fact", token: token.trim() });
|
|
315
|
+
}
|
|
316
|
+
if (text.includes("\u2014")) found.push({ where, rule: "em-dash", token: "\u2014" });
|
|
317
|
+
}
|
|
318
|
+
return found;
|
|
319
|
+
}
|
|
320
|
+
|
|
240
321
|
// ---------- face ----------
|
|
241
322
|
|
|
242
323
|
// Crop a 0-1 rect [x, y, w, h] out of a w x h gray frame.
|
|
@@ -250,7 +331,8 @@ export const crop = (f, w, h, [x, y, cw, ch]) => {
|
|
|
250
331
|
// A card over the face changes those pixels; the clip alone does not.
|
|
251
332
|
export function faceCover(runDir, { max = 18, w = 160, h = 90 } = {}) {
|
|
252
333
|
const sb = JSON.parse(readFileSync(join(runDir, "storyboard.json"), "utf8"));
|
|
253
|
-
|
|
334
|
+
// With formats, faceRect matches the landscape cut (16:9 footage is not cropped there).
|
|
335
|
+
const final = join(runDir, existsSync(join(runDir, "final.mp4")) ? "final.mp4" : "final-landscape.mp4"), covered = [];
|
|
254
336
|
let t0 = 0, checked = 0;
|
|
255
337
|
for (const s of sb.scenes) {
|
|
256
338
|
if (s.faceRect && s.source && existsSync(join(runDir, s.source))) {
|
|
@@ -268,6 +350,71 @@ export function faceCover(runDir, { max = 18, w = 160, h = 90 } = {}) {
|
|
|
268
350
|
return { ok: covered.length === 0, covered, checked };
|
|
269
351
|
}
|
|
270
352
|
|
|
353
|
+
// ---------- safe ----------
|
|
354
|
+
|
|
355
|
+
// Share of sharp-edge pixels in each margin the format's platforms cover (SAFE in stamp-format.mjs).
|
|
356
|
+
// Text and logos are sharp edges; a solid or gradient background has none.
|
|
357
|
+
// ponytail: pixels, not DOM boxes; a full-bleed photo or texture in a motion scene also trips it, so check a hit by eye.
|
|
358
|
+
export function marginInk(frame, w, h, [t, b, l, r], { edge = 48 } = {}) {
|
|
359
|
+
// Each band stops one row short of the safe edge, so a line drawn exactly on the edge (a HUD corner) is not counted.
|
|
360
|
+
const bands = { top: [0, 0, w, Math.round(t * h) - 1], bottom: [0, h - Math.round(b * h) + 1, w, h], left: [0, 0, Math.round(l * w) - 1, h], right: [w - Math.round(r * w) + 1, 0, w, h] };
|
|
361
|
+
const out = {};
|
|
362
|
+
for (const [side, [x0, y0, x1, y1]] of Object.entries(bands)) {
|
|
363
|
+
let hits = 0, n = 0;
|
|
364
|
+
for (let y = y0; y < Math.min(y1, h - 1); y++) for (let x = x0; x < Math.min(x1, w - 1); x++) {
|
|
365
|
+
const i = y * w + x;
|
|
366
|
+
if (Math.abs(frame[i] - frame[i + 1]) + Math.abs(frame[i] - frame[i + w]) > edge) hits++;
|
|
367
|
+
n++;
|
|
368
|
+
}
|
|
369
|
+
out[side] = n ? hits / n : 0;
|
|
370
|
+
}
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Samples every motion and logo scene of each format's final, twice a second. Demo footage is skipped:
|
|
375
|
+
// a screen recording fills the frame by design. With stills, reads the hold-frame PNGs that
|
|
376
|
+
// `render-scenes.mjs --stills` wrote instead, so the check needs no render.
|
|
377
|
+
export function safeAreas(runDir, { max = 0.004, every = 0.5, stills = false } = {}) {
|
|
378
|
+
const sb = JSON.parse(readFileSync(join(runDir, "storyboard.json"), "utf8"));
|
|
379
|
+
const named = Object.keys(FORMATS).find((k) => FORMATS[k][0] === sb.format.width && FORMATS[k][1] === sb.format.height);
|
|
380
|
+
const runs = Array.isArray(sb.formats) ? sb.formats.map((f) => [f, `final-${f}.mp4`]) : named ? [[named, "final.mp4"]] : [];
|
|
381
|
+
const hits = [];
|
|
382
|
+
let checked = 0;
|
|
383
|
+
if (stills) {
|
|
384
|
+
for (const [fmt] of runs) {
|
|
385
|
+
const [W, H] = FORMATS[fmt], w = W / 4, h = H / 4;
|
|
386
|
+
sb.scenes.forEach((s, i) => {
|
|
387
|
+
if (s.kind === "demo") return;
|
|
388
|
+
const png = join(runDir, "stills", Array.isArray(sb.formats) ? fmt : "", `${String(i + 1).padStart(2, "0")}-${s.id}.png`);
|
|
389
|
+
if (!existsSync(png)) { hits.push({ format: fmt, scene: s.id, missing: png }); return; }
|
|
390
|
+
const [f] = grayFrames(png, w, h);
|
|
391
|
+
checked++;
|
|
392
|
+
for (const [side, ink] of Object.entries(marginInk(f, w, h, SAFE[fmt]))) {
|
|
393
|
+
if (ink > max) hits.push({ format: fmt, scene: s.id, side, ink: Number(ink.toFixed(4)) });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return { ok: hits.length === 0, hits, checked };
|
|
398
|
+
}
|
|
399
|
+
for (const [fmt, file] of runs) {
|
|
400
|
+
const video = join(runDir, file), [W, H] = FORMATS[fmt], w = W / 4, h = H / 4;
|
|
401
|
+
if (!existsSync(video)) { hits.push({ format: fmt, missing: file }); continue; }
|
|
402
|
+
let t0 = 0;
|
|
403
|
+
for (const s of sb.scenes) {
|
|
404
|
+
for (let t = 0.25; s.kind !== "demo" && t < s.duration; t += every) {
|
|
405
|
+
const [f] = grayFrames(video, w, h, { start: t0 + t, duration: 0.05 });
|
|
406
|
+
if (!f) continue;
|
|
407
|
+
checked++;
|
|
408
|
+
for (const [side, ink] of Object.entries(marginInk(f, w, h, SAFE[fmt]))) {
|
|
409
|
+
if (ink > max) hits.push({ format: fmt, scene: s.id, t: Number((t0 + t).toFixed(2)), side, ink: Number(ink.toFixed(4)) });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
t0 += s.duration;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return { ok: hits.length === 0, hits, checked };
|
|
416
|
+
}
|
|
417
|
+
|
|
271
418
|
// ---------- judge ----------
|
|
272
419
|
|
|
273
420
|
// Pass 1 shows new as A, old as B. Pass 2 swaps them. Keep new only if it wins both,
|
|
@@ -300,7 +447,7 @@ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.ur
|
|
|
300
447
|
if (gate === "judge" && rest[0] && !rest[0].startsWith("--")) rest.unshift("--old", rest.shift());
|
|
301
448
|
const opt = (n, d) => { const i = rest.indexOf(n); return i >= 0 ? rest[i + 1] : d; };
|
|
302
449
|
const num = (n, d) => Number(opt(n, d));
|
|
303
|
-
if (!gate || !target) { console.error("Usage: qa.mjs <tokens|crossfade|motion|legibility|pops|deadframes|presence|beatsync|beatgrid|face|judge> <path> [options]"); process.exit(1); }
|
|
450
|
+
if (!gate || !target) { console.error("Usage: qa.mjs <tokens|crossfade|motion|legibility|pops|deadframes|presence|beatsync|beatgrid|face|avsync|loop|facts|safe|judge> <path> [options]"); process.exit(1); }
|
|
304
451
|
let result;
|
|
305
452
|
if (gate === "tokens") {
|
|
306
453
|
const tokens = opt("--tokens") ? JSON.parse(readFileSync(opt("--tokens"), "utf8")) : DEFAULT_TOKENS;
|
|
@@ -331,7 +478,22 @@ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.ur
|
|
|
331
478
|
const { fps } = probe(target);
|
|
332
479
|
result = beatGrid(cutTimes(grayFrames(target), fps), { bpm: num("--bpm"), phase: num("--phase", 0), tol: num("--tol", 0.035) });
|
|
333
480
|
} else if (gate === "face") result = faceCover(target, { max: num("--max", 18) });
|
|
334
|
-
else if (gate === "
|
|
481
|
+
else if (gate === "avsync") {
|
|
482
|
+
const expect = (opt("--expect", "") || "").split(",").filter(Boolean).map(Number);
|
|
483
|
+
result = avSync(target, { expect, duration: opt("--duration") != null ? num("--duration") : undefined });
|
|
484
|
+
} else if (gate === "loop") result = loopCheck(target, { max: num("--max", 6) });
|
|
485
|
+
else if (gate === "safe") result = safeAreas(target, { max: num("--max", 0.004), stills: rest.includes("--stills") });
|
|
486
|
+
else if (gate === "facts") {
|
|
487
|
+
const sb = JSON.parse(readFileSync(join(target, "storyboard.json"), "utf8"));
|
|
488
|
+
const comp = join(target, "composition");
|
|
489
|
+
const texts = existsSync(comp) ? htmlFiles(comp).map((f) => ({ where: f, text: visibleText(readFileSync(f, "utf8")) })) : [];
|
|
490
|
+
for (const sc of sb.scenes ?? []) {
|
|
491
|
+
const vals = [sc.line, ...Object.values(sc.variables ?? {})].filter((v) => typeof v === "string");
|
|
492
|
+
texts.push({ where: `storyboard.json#${sc.id}`, text: vals.join("\n") });
|
|
493
|
+
}
|
|
494
|
+
const found = scanFacts(texts, sb.facts ?? []);
|
|
495
|
+
result = { ok: found.length === 0, found, facts: (sb.facts ?? []).length };
|
|
496
|
+
} else if (gate === "judge") {
|
|
335
497
|
const old = opt("--old"), out = opt("--out", "qa-judge");
|
|
336
498
|
if (!old) { console.error("judge needs <new.mp4> <old.mp4>"); process.exit(1); }
|
|
337
499
|
mkdirSync(out, { recursive: true });
|
|
@@ -9,7 +9,8 @@ import { spawnSync } from "node:child_process";
|
|
|
9
9
|
import {
|
|
10
10
|
scanTokens, scanCrossfades, scanMotion, legibility, findPops, deadFrames, presence,
|
|
11
11
|
beatSync, findAnchor, cutTimes, beatGrid, grayFrames, probe,
|
|
12
|
-
faceCover, pairVerdict, parseWinner,
|
|
12
|
+
faceCover, pairVerdict, parseWinner, audioOnsets, avSync, visibleText, scanFacts, loopCheck,
|
|
13
|
+
fullFrameSelectors, safeAreas,
|
|
13
14
|
} from "./qa.mjs";
|
|
14
15
|
|
|
15
16
|
const dir = mkdtempSync(join(tmpdir(), "qa-gates-"));
|
|
@@ -150,3 +151,100 @@ test("judge CLI: pending without a verdict, keeps or rejects with verdicts, fake
|
|
|
150
151
|
assert.equal(cli("judge", n, o, "--out", out, "--verdicts", "A,A").status, 1);
|
|
151
152
|
assert.equal(cli("judge", n, o, "--out", out, "--cmd", "echo WINNER: A").status, 1); // first-position bias
|
|
152
153
|
});
|
|
154
|
+
|
|
155
|
+
// Gray video with a 50 ms tone burst starting at each time in `hits`.
|
|
156
|
+
function withHits(name, hits, d = 2) {
|
|
157
|
+
const out = join(dir, name);
|
|
158
|
+
const expr = hits.length ? hits.map((h) => `between(t,${h},${h + 0.05})`).join("+") : "0";
|
|
159
|
+
const r = spawnSync("ffmpeg", ["-v", "error", "-y", "-f", "lavfi", "-i", `color=c=gray:size=160x90:rate=30:duration=${d}`,
|
|
160
|
+
"-f", "lavfi", "-i", `aevalsrc='0.8*(${expr})*sin(2*PI*1000*t)':s=48000:d=${d}`,
|
|
161
|
+
"-c:v", "libx264", "-crf", "12", "-c:a", "aac", "-shortest", out]);
|
|
162
|
+
assert.equal(r.status, 0, r.stderr?.toString());
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
test("avsync: hits on time pass; a hit 3 frames late, no audio, or the wrong length fail", () => {
|
|
167
|
+
const clip = withHits("hits.mp4", [0.5, 1.2]);
|
|
168
|
+
const on = audioOnsets(clip);
|
|
169
|
+
assert.ok(on.some((t) => Math.abs(t - 0.5) < 1 / 30) && on.some((t) => Math.abs(t - 1.2) < 1 / 30), JSON.stringify(on));
|
|
170
|
+
assert.equal(avSync(clip, { expect: [0.5, 1.2], duration: 2 }).ok, true);
|
|
171
|
+
const late = avSync(clip, { expect: [0.5, 1.1] });
|
|
172
|
+
assert.equal(late.ok, false);
|
|
173
|
+
assert.equal(late.hits.filter((h) => !h.ok).length, 1);
|
|
174
|
+
assert.equal(avSync(clip, { duration: 3 }).ok, false);
|
|
175
|
+
const silent = avSync(video("noaudio.mp4", [["gray", 1]]));
|
|
176
|
+
assert.equal(silent.ok, false);
|
|
177
|
+
assert.equal(silent.audio, false);
|
|
178
|
+
assert.equal(cli("avsync", clip, "--expect", "0.5,1.1").status, 1);
|
|
179
|
+
assert.equal(cli("avsync", clip, "--expect", "0.5,1.2", "--duration", "2").status, 0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("facts: on-screen numbers must be story facts; code numbers and single digits are ignored; em dashes fail", () => {
|
|
183
|
+
const html = `<style>.t{font-size:84px;letter-spacing:-0.02em}</style>
|
|
184
|
+
<template><div class="t" data-start="1.25">3 unpaid, $4,792.44 total</div><p>INV-0043 opened twice</p></template>
|
|
185
|
+
<script>tl.to(".t", { y: 120, duration: 0.35 }, 2.5);</script>`;
|
|
186
|
+
assert.equal(visibleText(html).replace(/\s+/g, " ").trim(), "3 unpaid, $4,792.44 total INV-0043 opened twice");
|
|
187
|
+
const facts = ["3 unpaid invoices, $4,792.44 total", "Maya Chen INV-0043 $781.94"];
|
|
188
|
+
assert.deepEqual(scanFacts([{ where: "a.html", text: visibleText(html) }], facts), []);
|
|
189
|
+
const drift = scanFacts([{ where: "b.html", text: "Total $4,792.40, up 12% since INV-0044" }], facts);
|
|
190
|
+
assert.deepEqual(drift.map((f) => f.token), ["$4,792.40", "12%", "INV-0044"]);
|
|
191
|
+
assert.deepEqual(scanFacts([{ where: "c.html", text: "Fast \u2014 and free" }], facts).map((f) => f.rule), ["em-dash"]);
|
|
192
|
+
assert.deepEqual(scanFacts([{ where: "d.html", text: visibleText("<p>Fast — free</p>") }], facts).map((f) => f.rule), ["em-dash"]);
|
|
193
|
+
const run = join(dir, "factsrun");
|
|
194
|
+
mkdirSync(join(run, "composition/compositions"), { recursive: true });
|
|
195
|
+
writeFileSync(join(run, "composition/compositions/hook.html"), "<p>Paid $2,860.50</p>");
|
|
196
|
+
writeFileSync(join(run, "storyboard.json"), JSON.stringify({ facts: ["Nair Dental $2,860.50"], scenes: [{ id: "hook", kind: "motion", duration: 2, line: "Paid $2,860.50", variables: { value: "3x" } }] }));
|
|
197
|
+
assert.equal(cli("facts", run).status, 0);
|
|
198
|
+
writeFileSync(join(run, "composition/compositions/hook.html"), "<p>Paid $2,680.50</p>");
|
|
199
|
+
const r = cli("facts", run);
|
|
200
|
+
assert.equal(r.status, 1);
|
|
201
|
+
assert.match(r.stdout, /2,680\.50/);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("loop: last frame matching the first passes, a different end frame fails", () => {
|
|
205
|
+
const loops = video("loops.mp4", [["0x3050a0", 0.5], ["0xa05030", 0.5], ["0x3050a0", 0.5]]);
|
|
206
|
+
assert.equal(loopCheck(loops).ok, true);
|
|
207
|
+
const ends = video("ends.mp4", [["0x3050a0", 0.5], ["0xa05030", 1]]);
|
|
208
|
+
assert.equal(loopCheck(ends).ok, false);
|
|
209
|
+
assert.equal(cli("loop", ends).status, 1);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("crossfade: a full-frame layer is found at the composition's own size, not only 1920x1080", () => {
|
|
213
|
+
const html = `<div data-composition-id="m" data-width="1080" data-height="1920"></div><style>.bg{width:1080px;height:1920px}.v{width:var(--w);height:var(--h)}.wide{width:1920px;height:1080px}</style>`;
|
|
214
|
+
assert.deepEqual([...fullFrameSelectors(html)], [".bg", ".v"]);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("safe: sharp content in vertical's covered bottom fails, the same content in the safe area passes", () => {
|
|
218
|
+
// A 320x180 test pattern (sharp text and bars) on a plain vertical frame.
|
|
219
|
+
const place = (name, y) => {
|
|
220
|
+
const run = join(dir, name);
|
|
221
|
+
mkdirSync(run, { recursive: true });
|
|
222
|
+
const r = spawnSync("ffmpeg", ["-v", "error", "-y", "-f", "lavfi", "-i", "color=c=0xf3efe6:size=1080x1920:rate=30:duration=1", "-f", "lavfi", "-i", "testsrc2=size=320x180:rate=30:duration=1",
|
|
223
|
+
"-filter_complex", `[0][1]overlay=x=380:y=${y},format=yuv420p`, "-c:v", "libx264", "-crf", "12", join(run, "final-vertical.mp4")]);
|
|
224
|
+
assert.equal(r.status, 0, r.stderr?.toString());
|
|
225
|
+
writeFileSync(join(run, "storyboard.json"), JSON.stringify({ format: { width: 1920, height: 1080, fps: 30 }, formats: ["vertical"], scenes: [{ id: "end", kind: "motion", duration: 1 }] }));
|
|
226
|
+
return run;
|
|
227
|
+
};
|
|
228
|
+
const low = safeAreas(place("safe-low", 1500));
|
|
229
|
+
assert.equal(low.ok, false);
|
|
230
|
+
assert.ok(low.hits.every((h) => h.side === "bottom" && h.format === "vertical"), JSON.stringify(low.hits));
|
|
231
|
+
assert.equal(safeAreas(place("safe-mid", 700)).ok, true);
|
|
232
|
+
assert.equal(cli("safe", join(dir, "safe-low")).status, 1);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("safe --stills reads the hold-frame PNGs from render-scenes --stills, no final needed", () => {
|
|
236
|
+
const place = (name, y) => {
|
|
237
|
+
const run = join(dir, name);
|
|
238
|
+
mkdirSync(join(run, "stills/vertical"), { recursive: true });
|
|
239
|
+
const r = spawnSync("ffmpeg", ["-v", "error", "-y", "-f", "lavfi", "-i", "color=c=0xf3efe6:size=1080x1920", "-f", "lavfi", "-i", "testsrc2=size=320x180",
|
|
240
|
+
"-filter_complex", `[0][1]overlay=x=380:y=${y}`, "-frames:v", "1", join(run, "stills/vertical/01-end.png")]);
|
|
241
|
+
assert.equal(r.status, 0, r.stderr?.toString());
|
|
242
|
+
writeFileSync(join(run, "storyboard.json"), JSON.stringify({ format: { width: 1920, height: 1080, fps: 30 }, formats: ["vertical"], scenes: [{ id: "end", kind: "motion", duration: 1 }] }));
|
|
243
|
+
return run;
|
|
244
|
+
};
|
|
245
|
+
const low = safeAreas(place("stills-low", 1500), { stills: true });
|
|
246
|
+
assert.equal(low.ok, false);
|
|
247
|
+
assert.deepEqual(low.hits.map((h) => h.side), ["bottom"]);
|
|
248
|
+
assert.equal(safeAreas(place("stills-mid", 700), { stills: true }).ok, true);
|
|
249
|
+
assert.equal(cli("safe", join(dir, "stills-low"), "--stills").status, 1);
|
|
250
|
+
});
|