dsh-drawai 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/README.md +96 -253
  2. package/lib/client.js +28 -7
  3. package/lib/index.js +474 -97
  4. package/lib/motion/asset-pack.js +109 -50
  5. package/lib/motion/assets.js +291 -291
  6. package/lib/motion/demo-assets-lock.js +10 -0
  7. package/lib/motion/mgstyles/vendor/README.md +28 -0
  8. package/lib/motion/mgstyles/vendor/modules/d3/LICENSE +13 -0
  9. package/lib/motion/mgstyles/vendor/modules/d3/dist/d3.min.js +2 -0
  10. package/lib/motion/mgstyles/vendor/modules/opentype.js/LICENSE +20 -0
  11. package/lib/motion/mgstyles/vendor/modules/opentype.js/dist/opentype.mjs +14460 -0
  12. package/lib/motion/mgstyles/vendor/modules/simplex-noise/LICENSE +21 -0
  13. package/lib/motion/mgstyles/vendor/modules/simplex-noise/dist/esm/simplex-noise.js +466 -0
  14. package/lib/motion/mgstyles/vendor/modules/topojson-client/LICENSE +13 -0
  15. package/lib/motion/mgstyles/vendor/modules/topojson-client/dist/topojson-client.min.js +2 -0
  16. package/lib/motion/preview.js +10 -3
  17. package/lib/motion/render.js +19 -1
  18. package/lib/motion/shotcraft/upstream/SKILL.md +286 -286
  19. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -54
  20. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -200
  21. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -45
  22. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -20
  23. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -65
  24. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -151
  25. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -129
  26. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -75
  27. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -40
  28. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -11
  29. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -10
  30. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -49
  31. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -210
  32. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -423
  33. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -170
  34. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -157
  35. package/lib/motion/shotcraft/upstream/demos/README.md +120 -120
  36. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -112
  37. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -647
  38. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -316
  39. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -82
  40. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -115
  41. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -243
  42. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +388 -388
  43. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -250
  44. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -305
  45. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -246
  46. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -390
  47. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -138
  48. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -542
  49. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -315
  50. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -299
  51. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -173
  52. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -348
  53. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -366
  54. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -279
  55. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -184
  56. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -390
  57. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -155
  58. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -303
  59. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -290
  60. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -278
  61. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -307
  62. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -215
  63. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -299
  64. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -232
  65. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -290
  66. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -273
  67. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -167
  68. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -536
  69. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -164
  70. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -175
  71. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -197
  72. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -241
  73. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -279
  74. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -230
  75. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -345
  76. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -275
  77. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -229
  78. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -312
  79. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -222
  80. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -295
  81. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -329
  82. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -315
  83. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -264
  84. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -260
  85. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -263
  86. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -123
  87. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -187
  88. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -175
  89. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -305
  90. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -350
  91. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -352
  92. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -218
  93. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -297
  94. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -157
  95. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -218
  96. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -202
  97. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -265
  98. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -457
  99. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -446
  100. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -249
  101. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -278
  102. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -332
  103. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -358
  104. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -201
  105. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -200
  106. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -208
  107. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -341
  108. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -389
  109. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -293
  110. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -183
  111. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -245
  112. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -222
  113. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -223
  114. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -217
  115. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -343
  116. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -357
  117. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -187
  118. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -333
  119. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -274
  120. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -270
  121. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -430
  122. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -252
  123. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -106
  124. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -288
  125. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -362
  126. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -168
  127. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -310
  128. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -261
  129. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -459
  130. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -294
  131. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -161
  132. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -248
  133. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -359
  134. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -227
  135. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -162
  136. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -248
  137. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -186
  138. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -355
  139. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -272
  140. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -262
  141. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -217
  142. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -285
  143. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -205
  144. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -178
  145. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -147
  146. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -143
  147. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -134
  148. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -217
  149. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -198
  150. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -221
  151. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -312
  152. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -261
  153. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -121
  154. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -153
  155. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -179
  156. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -140
  157. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -309
  158. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -191
  159. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -292
  160. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -135
  161. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -124
  162. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -255
  163. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -173
  164. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -225
  165. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -141
  166. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -137
  167. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -147
  168. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -409
  169. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -280
  170. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -271
  171. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -385
  172. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -312
  173. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -303
  174. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -77
  175. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -311
  176. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -307
  177. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -349
  178. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -255
  179. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -244
  180. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -217
  181. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -652
  182. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -153
  183. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -238
  184. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -324
  185. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -354
  186. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -101
  187. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -129
  188. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -297
  189. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -57
  190. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -155
  191. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -175
  192. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -153
  193. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -146
  194. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -226
  195. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -1
  196. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -193
  197. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -121
  198. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -199
  199. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -183
  200. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -257
  201. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -382
  202. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -111
  203. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -50
  204. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -95
  205. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -41
  206. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -52
  207. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -39
  208. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -39
  209. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -57
  210. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -51
  211. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -46
  212. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -52
  213. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -43
  214. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -42
  215. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -48
  216. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -52
  217. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -45
  218. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -46
  219. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -44
  220. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -63
  221. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -54
  222. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -58
  223. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -35
  224. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -53
  225. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -42
  226. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -50
  227. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -46
  228. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -56
  229. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -49
  230. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -50
  231. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -53
  232. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -53
  233. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -55
  234. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -52
  235. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -60
  236. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -57
  237. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -51
  238. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -41
  239. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -53
  240. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -60
  241. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -55
  242. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -57
  243. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -47
  244. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -37
  245. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -54
  246. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -36
  247. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -56
  248. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -56
  249. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -40
  250. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -60
  251. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -57
  252. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -73
  253. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -87
  254. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -55
  255. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -50
  256. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -59
  257. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -38
  258. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -38
  259. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -59
  260. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -39
  261. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -61
  262. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -52
  263. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -50
  264. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -66
  265. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -43
  266. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -38
  267. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -38
  268. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -56
  269. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -44
  270. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -38
  271. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -63
  272. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -57
  273. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -63
  274. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -58
  275. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -45
  276. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -55
  277. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -49
  278. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -42
  279. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -42
  280. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -45
  281. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -48
  282. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -55
  283. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -43
  284. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -41
  285. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -42
  286. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -59
  287. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -55
  288. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -65
  289. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -42
  290. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -64
  291. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -64
  292. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -54
  293. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -91
  294. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -51
  295. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -66
  296. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -38
  297. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -66
  298. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -58
  299. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -61
  300. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -64
  301. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -45
  302. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -41
  303. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -51
  304. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -42
  305. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -63
  306. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -57
  307. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -75
  308. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -59
  309. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -67
  310. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -60
  311. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -66
  312. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -40
  313. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -45
  314. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -62
  315. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -55
  316. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -37
  317. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -55
  318. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -50
  319. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -43
  320. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -55
  321. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -51
  322. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -51
  323. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -56
  324. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -49
  325. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -57
  326. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -36
  327. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -64
  328. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -47
  329. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -170
  330. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -157
  331. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -103
  332. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -44
  333. package/lib/motion/shotcraft/upstream/template/package.json +20 -20
  334. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -6
  335. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -17
  336. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -65
  337. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -48
  338. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -24
  339. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -192
  340. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -111
  341. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -150
  342. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -158
  343. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -468
  344. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -552
  345. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -411
  346. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -200
  347. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -324
  348. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +388 -388
  349. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -4
  350. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -7
  351. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -68
  352. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -1
  353. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -24
  354. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -74
  355. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -91
  356. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -213
  357. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -15
  358. package/lib/motion/style-frames.js +47 -12
  359. package/lib/motion-project.js +22 -3
  360. package/lib/motion-render-worker.js +31 -9
  361. package/lib/routing/router.js +12 -1
  362. package/lib/style-kernel.js +16 -6
  363. package/lib/video-trace-pool.js +128 -128
  364. package/package.json +1 -1
  365. package/LICENSE +0 -4175
@@ -1,446 +1,446 @@
1
- // theme-sweep-toggle —— 深浅模式扫场(第二轮重设计:video-shotcraft 发布周排期 · 昼 → 夜)
2
- //
3
- // 设计决定:
4
- // - 主体:video-shotcraft 的周视图制作排期(品牌轮由虚构日历 Solstice 换来,版式逐像素不变)——1680×912 浮窗,
5
- // 大字层级:64px 周标题、40px 日期数字、28px 事件标题;5 列 13 个制作日程(分镜 → 实拍 → 踩点 → 音效 → 渲染)、
6
- // 4 种日历色、一条"此刻"红线。顶栏标志 = 「镜刻」ShotcraftMark(昼夜各用亮 / 反白版)。
7
- // - look = custom(sand 的昼面 + 自定的夜面):昼 = 暖象牙舞台 + 左上斜射的阳光窗格光;
8
- // 夜 = 墨蓝舞台 + 右上月光 + 星点。主题切换不只换 UI 皮,连舞台的"时辰"一起换——
9
- // 但两版布局逐像素一致(同一个 <World> 组件、只换色板),读作"同一个 UI 就地换肤"。
10
- // - 因果:开场镜头贴近顶栏的大号日夜开关(zoom 2.0),指针按下 → 旋钮弹到右侧、太阳收光芒
11
- // 长出月牙 → 夜色边界紧接着**从开关那一侧**(右→左)15° 斜扫过全画面,同时相机拉开到全景,
12
- // 观众从"按了哪里"一路看到"整个世界入夜"。
13
- // - 边界 = 晨昏线:2px 白热核心线 + 亮侧一条随速度变宽的落日橙暮光带 + 夜侧冷蓝辉 + 亮侧柔影。
14
- // - 余波「灯亮了」:扫完后今天的日期胶囊、此刻线与窗口冷色轮廓光依次点亮(夜里才有的光)。
15
- //
16
- // 时间表(30fps,共 140f):
17
- // 0–8 预备:特写开关(zoom 2.0,极缓推 2.0→2.04),指针滑入开关
18
- // 8–18 按下:指针压 0.86、旋钮弹簧滑到右侧(damping 16,一次过冲),太阳 → 月牙;指针随后让开
19
- // 14–78 主动作:夜色斜边从右往左扫(64f,bezier(.3,0,.35,1)),~24f 扫过开关;
20
- // 10–40 相机拉开到全景(EASE.smooth),~40–50f 晨昏线过画面中线 = 昼夜同屏海报帧
21
- // 76–96 余波:开灯——今日胶囊 / 此刻线 / 窗口冷色轮廓光错峰亮起(EASE.out)
22
- // 96–140 hold:夜景定格,相机 1.0→1.02 极缓推、星点微闪
23
- import React from 'react';
24
- import { AbsoluteFill, useCurrentFrame } from 'remotion';
25
- import { EASE, FONT, Grain, bezier, mix, ramp, velocity } from '../../_fixtures/Polish';
26
- import { alpha, springAt } from '../../_fixtures/Look';
27
- import { BRAND, ShotcraftMark } from '../../_fixtures/Brand';
28
-
29
- export const THEME_SWEEP_TOGGLE_DURATION = 140;
30
-
31
- // ───────────── 时间轴 ─────────────
32
- const PTR_IN = 0;
33
- const CLICK = 8; // 指针按下 = 旋钮起跳
34
- const SWEEP0 = 14; // 夜色边界起扫(旋钮还在过冲回落,动作重叠)
35
- const SWEEP1 = 78;
36
- const CAM0 = 10; // 相机开始拉开
37
- const CAM1 = 40; // 全景先到位,晨昏线 ~44–50f 才过画面中线(昼夜同屏的海报帧)
38
- const LIGHTS = 76; // 开灯余波起点
39
-
40
- // ───────────── 世界几何(1920×1080 世界坐标,相机在其上推拉)─────────────
41
- const WIN = { x: 120, y: 84, w: 1680, h: 912, r: 28 };
42
- const TOP_H = 92;
43
- const HEAD_H = 112;
44
- const DAYS_H = 80;
45
- const GUT = 104; // 时间刻度列
46
- const COL_W = (WIN.w - GUT - 36) / 5;
47
- const H0 = 9; // 9:00 起
48
- const H1 = 16; // 16:00 止
49
- const HOUR = (WIN.h - TOP_H - HEAD_H - DAYS_H - 24) / (H1 - H0);
50
- const GRID_Y = TOP_H + HEAD_H + DAYS_H; // 窗口内坐标
51
- const TOG = { w: 132, h: 66 }; // 开关
52
- const TOG_X = WIN.w - 40 - TOG.w; // 窗口内
53
- const TOG_Y = (TOP_H - TOG.h) / 2;
54
- const TOG_C = { x: WIN.x + TOG_X + TOG.w / 2, y: WIN.y + TOG_Y + TOG.h / 2 }; // 世界坐标
55
- const T15 = Math.tan((15 * Math.PI) / 180);
56
-
57
- // ───────────── 两套主题(同布局,逐键手工映射色板,不做滤镜反相)─────────────
58
- type Hue = 'blue' | 'green' | 'amber' | 'rose';
59
- type Theme = {
60
- night: boolean;
61
- bg: [string, string, string];
62
- key: { x: number; y: number; c: string; a: number };
63
- win: string; win2: string; line: string; ink: string; ink2: string; ink3: string;
64
- accent: string; shadow: string; seg: string;
65
- track: string; trackOn: string; knob: string;
66
- ev: Record<Hue, { fill: string; bar: string; text: string; sub: string }>;
67
- };
68
-
69
- const DAY: Theme = {
70
- night: false,
71
- bg: ['#f4ede1', '#ece2d2', '#e0d3bf'],
72
- key: { x: 0.18, y: 0.0, c: '#fff8ea', a: 0.95 },
73
- win: '#fdfaf4', win2: '#f6f0e6', line: 'rgba(70,48,24,0.10)', ink: '#1f1a13', ink2: '#6b5f50', ink3: '#a49784',
74
- accent: '#e0552a', shadow: '#4a2f12', seg: '#efe7da',
75
- track: '#ece2d2', trackOn: '#2b3048', knob: '#ffffff',
76
- ev: {
77
- blue: { fill: '#e7eefb', bar: '#3d72de', text: '#1a3670', sub: '#5774a8' },
78
- green: { fill: '#e3f1e6', bar: '#2f9a5c', text: '#184d31', sub: '#4f7f62' },
79
- amber: { fill: '#fbeedb', bar: '#d98a1c', text: '#6a400f', sub: '#9b6c34' },
80
- rose: { fill: '#fbe6e7', bar: '#d6455c', text: '#76202e', sub: '#a5525f' },
81
- },
82
- };
83
-
84
- const NIGHT: Theme = {
85
- night: true,
86
- bg: ['#0e1426', '#090d1a', '#05070f'],
87
- key: { x: 0.86, y: 0.02, c: '#5b74c9', a: 0.4 },
88
- win: '#11151f', win2: '#161b28', line: 'rgba(170,190,240,0.10)', ink: '#eef1fa', ink2: '#9aa3ba', ink3: '#5f6882',
89
- accent: '#ff9450', shadow: '#000207', seg: '#1a2030',
90
- track: '#262c40', trackOn: '#262c40', knob: '#f4efe4',
91
- ev: {
92
- blue: { fill: 'rgba(96,140,255,0.15)', bar: '#6f9cff', text: '#dfe7ff', sub: '#8ea4d6' },
93
- green: { fill: 'rgba(72,205,135,0.13)', bar: '#52d38e', text: '#d8f5e5', sub: '#84b89b' },
94
- amber: { fill: 'rgba(255,172,72,0.14)', bar: '#ffb24c', text: '#ffeacf', sub: '#c9a479' },
95
- rose: { fill: 'rgba(255,104,134,0.14)', bar: '#ff718c', text: '#ffdee5', sub: '#c98b97' },
96
- },
97
- };
98
-
99
- const DAYS = [
100
- { d: 'MON', n: 14 },
101
- { d: 'TUE', n: 15 },
102
- { d: 'WED', n: 16 },
103
- { d: 'THU', n: 17 },
104
- { d: 'FRI', n: 18 },
105
- ];
106
- const TODAY = 2;
107
- const NOW = 13 + 40 / 60; // 13:40
108
-
109
- const EVENTS: { day: number; s: number; e: number; t: string; hue: Hue }[] = [
110
- { day: 0, s: 9.5, e: 10.75, t: 'Storyboard crit', hue: 'rose' },
111
- { day: 0, s: 12.5, e: 14.5, t: 'Shot list review', hue: 'blue' },
112
- { day: 1, s: 9, e: 10, t: 'Standup', hue: 'amber' },
113
- { day: 1, s: 10.75, e: 12.5, t: 'Page captures', hue: 'green' },
114
- { day: 1, s: 14, e: 15.25, t: 'Voiceover · Theo', hue: 'amber' },
115
- { day: 2, s: 9.25, e: 10.5, t: 'Launch sync', hue: 'blue' },
116
- { day: 2, s: 11, e: 12.75, t: 'Camera moves', hue: 'green' },
117
- { day: 2, s: 14.25, e: 15.75, t: 'Beat-sync pass', hue: 'rose' },
118
- { day: 3, s: 9.5, e: 11.25, t: 'SFX design', hue: 'amber' },
119
- { day: 3, s: 12.5, e: 13.5, t: 'Lunch & learn', hue: 'green' },
120
- { day: 3, s: 14.75, e: 16, t: 'Render review', hue: 'blue' },
121
- { day: 4, s: 10, e: 12.5, t: 'Final render', hue: 'green' },
122
- { day: 4, s: 13.5, e: 14.75, t: 'Ship the film', hue: 'rose' },
123
- ];
124
-
125
- const fmt = (h: number) => {
126
- const hh = Math.floor(h);
127
- const mm = Math.round((h - hh) * 60);
128
- const h12 = hh > 12 ? hh - 12 : hh;
129
- return `${h12}:${String(mm).padStart(2, '0')}`;
130
- };
131
-
132
- // 确定性哈希(星点)
133
- const hash = (n: number) => {
134
- const x = Math.sin(n * 91.345 + 47.853) * 43758.5453;
135
- return x - Math.floor(x);
136
- };
137
-
138
- // ───────────── 日 → 月 图标(太阳收光芒、遮罩圆滑入长出月牙)─────────────
139
- const SunMoon: React.FC<{ p: number; color: string; size: number }> = ({ p, color, size }) => {
140
- const id = React.useId().replace(/[^a-zA-Z0-9]/g, '');
141
- const rays = 1 - EASE.out(Math.min(1, p * 1.6));
142
- return (
143
- <svg width={size} height={size} viewBox="0 0 40 40" style={{ display: 'block', overflow: 'visible' }}>
144
- <defs>
145
- <mask id={`m${id}`}>
146
- <rect x={-10} y={-10} width={60} height={60} fill="#fff" />
147
- <circle cx={mix(44, 27, EASE.snappy(p))} cy={mix(-4, 13, EASE.snappy(p))} r={10.5} fill="#000" />
148
- </mask>
149
- </defs>
150
- <g transform={`rotate(${(p * 90).toFixed(2)} 20 20)`}>
151
- {Array.from({ length: 8 }, (_, i) => (
152
- <line key={i} x1={20} y1={4.5 - (1 - rays) * 4} x2={20} y2={8.5 - (1 - rays) * 6}
153
- stroke={color} strokeWidth={3} strokeLinecap="round" opacity={rays}
154
- transform={`rotate(${i * 45} 20 20)`} />
155
- ))}
156
- </g>
157
- <circle cx={20} cy={20} r={mix(7.5, 11, EASE.out(p))} fill={color} mask={`url(#m${id})`} />
158
- </svg>
159
- );
160
- };
161
-
162
- // ───────────── 整个世界(舞台 + 窗口),两版主题共用这一份布局 ─────────────
163
- const World: React.FC<{ th: Theme; knob: number; icon: number; lights: number; frame: number }> = ({ th, knob, icon, lights, frame }) => {
164
- const n = th.night;
165
- const [t, m, b] = th.bg;
166
- const todayLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2))) : 0;
167
- const nowLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2 - 0.5))) : 0;
168
- const rimLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2 - 1))) : 0;
169
- return (
170
- <div style={{ position: 'absolute', inset: 0 }}>
171
- {/* 舞台:外扩 260px,相机特写时右上角也有底 */}
172
- <div style={{ position: 'absolute', left: -260, top: -260, right: -260, bottom: -260, background: `linear-gradient(180deg, ${t} 0%, ${m} 55%, ${b} 100%)` }} />
173
- <div style={{
174
- position: 'absolute', left: -260, top: -260, right: -260, bottom: -260,
175
- background: `radial-gradient(ellipse 60% 64% at ${th.key.x * 100}% ${th.key.y * 100}%, ${alpha(th.key.c, th.key.a)} 0%, ${alpha(th.key.c, 0)} 72%)`,
176
- mixBlendMode: n ? 'screen' : 'normal',
177
- }} />
178
- {!n && (
179
- // 昼:左上斜射进来的窗格光(三道暖白光柱,极淡)
180
- <div style={{ position: 'absolute', inset: -260, overflow: 'hidden' }}>
181
- {[0, 1, 2].map((i) => (
182
- <div key={i} style={{
183
- position: 'absolute', left: 120 + i * 300, top: -300, width: 150, height: 2200,
184
- transform: 'rotate(-32deg)', transformOrigin: '0 0',
185
- background: 'linear-gradient(90deg, rgba(255,250,236,0) 0%, rgba(255,250,236,0.55) 50%, rgba(255,250,236,0) 100%)',
186
- opacity: 0.55 - i * 0.12,
187
- }} />
188
- ))}
189
- </div>
190
- )}
191
- {n && (
192
- // 夜:星点(窗口外的天空),确定性微闪
193
- <div style={{ position: 'absolute', inset: -260 }}>
194
- {Array.from({ length: 70 }, (_, i) => {
195
- const x = hash(i * 3.1) * 2440;
196
- const y = hash(i * 7.7 + 2) * 1600;
197
- const z = hash(i * 1.9 + 5);
198
- const tw = 0.55 + 0.45 * Math.sin(frame / (9 + z * 14) + i * 1.7);
199
- const s = 1.4 + z * 2.4;
200
- return <div key={i} style={{ position: 'absolute', left: x, top: y, width: s, height: s, borderRadius: '50%', background: '#dfe6ff', opacity: (0.25 + 0.6 * z) * tw, boxShadow: z > 0.75 ? '0 0 6px rgba(190,205,255,0.8)' : undefined }} />;
201
- })}
202
- </div>
203
- )}
204
- {/* 窗口落在"桌面"上的地面光 / 影 */}
205
- <div style={{
206
- position: 'absolute', left: WIN.x + 80, top: WIN.y + WIN.h - 40, width: WIN.w - 160, height: 140,
207
- background: n
208
- ? 'radial-gradient(ellipse 50% 50% at 50% 30%, rgba(90,120,220,0.20) 0%, rgba(90,120,220,0) 70%)'
209
- : 'radial-gradient(ellipse 50% 50% at 50% 30%, rgba(74,47,18,0.16) 0%, rgba(74,47,18,0) 70%)',
210
- filter: 'blur(14px)',
211
- }} />
212
-
213
- {/* 窗口 */}
214
- <div style={{
215
- position: 'absolute', left: WIN.x, top: WIN.y, width: WIN.w, height: WIN.h, borderRadius: WIN.r, overflow: 'hidden',
216
- background: th.win, fontFamily: FONT.sans, color: th.ink,
217
- boxShadow: n
218
- ? `inset 0 1px 0 rgba(255,255,255,0.07), 0 0 0 1px rgba(150,175,255,${(0.1 + 0.18 * rimLit).toFixed(3)}), 0 0 ${(60 * rimLit).toFixed(1)}px rgba(90,125,255,${(0.22 * rimLit).toFixed(3)}), 0 40px 90px -20px rgba(0,2,7,0.9), 0 12px 30px rgba(0,2,7,0.6)`
219
- : `inset 0 1px 0 rgba(255,255,255,0.9), 0 0 0 1px rgba(70,48,24,0.08), 0 40px 90px -24px rgba(74,47,18,0.32), 0 10px 26px -6px rgba(74,47,18,0.16)`,
220
- }}>
221
- {/* 顶栏 */}
222
- <div style={{ position: 'absolute', left: 0, top: 0, width: WIN.w, height: TOP_H, borderBottom: `1px solid ${th.line}`, display: 'flex', alignItems: 'center', padding: '0 40px', boxSizing: 'border-box' }}>
223
- <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
224
- {/* 「镜刻」标志:昼用墨框、夜用反白框,琥珀斜切不变(标志本身不加底块 / 发光) */}
225
- <ShotcraftMark size={44} tone={n ? 'dark' : 'light'} />
226
- <span style={{ fontFamily: BRAND.font, fontSize: 32, fontWeight: 700, letterSpacing: '0.03em' }}>{BRAND.name}</span>
227
- </div>
228
- <div style={{ marginLeft: 72, display: 'flex', gap: 4, padding: 5, borderRadius: 14, background: th.seg }}>
229
- {['Day', 'Week', 'Month'].map((s, i) => (
230
- <div key={s} style={{
231
- padding: '9px 22px', borderRadius: 10, fontSize: 24, fontWeight: i === 1 ? 650 : 500,
232
- color: i === 1 ? th.ink : th.ink2,
233
- background: i === 1 ? (n ? '#262d40' : '#ffffff') : 'transparent',
234
- boxShadow: i === 1 ? (n ? 'inset 0 1px 0 rgba(255,255,255,0.06)' : '0 1px 2px rgba(74,47,18,0.12), 0 2px 8px -2px rgba(74,47,18,0.12)') : 'none',
235
- }}>{s}</div>
236
- ))}
237
- </div>
238
- <div style={{ marginLeft: 'auto', marginRight: TOG.w + 28, fontSize: 24, color: th.ink2, fontWeight: 500 }}>Today</div>
239
- </div>
240
- {/* 日夜开关 */}
241
- <div style={{
242
- position: 'absolute', left: TOG_X, top: TOG_Y, width: TOG.w, height: TOG.h, borderRadius: TOG.h / 2,
243
- background: n ? th.track : `linear-gradient(90deg, ${th.trackOn} 0%, ${th.trackOn} ${(knob * 100).toFixed(1)}%, ${th.track} ${(knob * 100).toFixed(1)}%)`,
244
- boxShadow: n ? 'inset 0 2px 4px rgba(0,0,0,0.45), inset 0 0 0 1px rgba(255,255,255,0.06)' : 'inset 0 2px 4px rgba(74,47,18,0.16), inset 0 0 0 1px rgba(74,47,18,0.06)',
245
- overflow: 'hidden',
246
- }}>
247
- {/* 轨道里的小星(拨到夜时显出) */}
248
- {[0, 1, 2].map((i) => (
249
- <div key={i} style={{ position: 'absolute', left: 18 + i * 13, top: 18 + (i % 2) * 18, width: 4, height: 4, borderRadius: 2, background: '#e8edff', opacity: Math.min(1, Math.max(0, knob * 1.4 - 0.3)) * (0.5 + 0.2 * i) }} />
250
- ))}
251
- <div style={{
252
- position: 'absolute', top: 6, left: 6 + knob * (TOG.w - TOG.h), width: TOG.h - 12, height: TOG.h - 12, borderRadius: '50%',
253
- background: `linear-gradient(180deg, #ffffff, ${th.knob})`,
254
- boxShadow: '0 2px 4px rgba(0,0,0,0.22), 0 6px 14px -4px rgba(0,0,0,0.3)',
255
- display: 'flex', alignItems: 'center', justifyContent: 'center',
256
- }}>
257
- <SunMoon p={icon} color={icon > 0.5 ? '#3b4366' : '#f0a020'} size={34} />
258
- </div>
259
- </div>
260
-
261
- {/* 周标题 */}
262
- <div style={{ position: 'absolute', left: 40, top: TOP_H, height: HEAD_H, display: 'flex', alignItems: 'center', width: WIN.w - 80 }}>
263
- <span style={{ fontSize: 64, fontWeight: 760, letterSpacing: '-0.035em' }}>September 14 – 18</span>
264
- <span style={{ marginLeft: 26, fontSize: 26, fontWeight: 600, letterSpacing: '0.08em', color: th.ink3, transform: 'translateY(8px)' }}>WEEK 38</span>
265
- <span style={{ marginLeft: 'auto', fontSize: 26, color: th.ink2, fontWeight: 500 }}>Launch film · ships Friday</span>
266
- </div>
267
-
268
- {/* 星期 / 日期 */}
269
- {DAYS.map((d, i) => {
270
- const today = i === TODAY;
271
- return (
272
- <div key={d.d} style={{ position: 'absolute', left: GUT + i * COL_W, top: TOP_H + HEAD_H, width: COL_W, height: DAYS_H, display: 'flex', alignItems: 'center', gap: 14, paddingLeft: 14, boxSizing: 'border-box' }}>
273
- <span style={{ fontSize: 22, fontWeight: 650, letterSpacing: '0.1em', color: today ? th.accent : th.ink3 }}>{d.d}</span>
274
- <span style={{
275
- fontSize: 40, fontWeight: 720, letterSpacing: '-0.03em', fontVariantNumeric: 'tabular-nums',
276
- color: today ? '#ffffff' : th.ink,
277
- ...(today ? {
278
- background: th.accent, borderRadius: 30, padding: '2px 16px',
279
- boxShadow: n ? `0 0 ${(26 * todayLit).toFixed(1)}px ${alpha(th.accent, 0.6 * todayLit)}, 0 0 ${(70 * todayLit).toFixed(1)}px ${alpha(th.accent, 0.3 * todayLit)}` : `0 4px 12px -4px ${alpha(th.accent, 0.5)}`,
280
- } : {}),
281
- }}>{d.n}</span>
282
- </div>
283
- );
284
- })}
285
-
286
- {/* 时间网格 */}
287
- <div style={{ position: 'absolute', left: 0, top: GRID_Y, width: WIN.w, height: WIN.h - GRID_Y, borderTop: `1px solid ${th.line}` }}>
288
- {Array.from({ length: H1 - H0 + 1 }, (_, k) => (
289
- <React.Fragment key={k}>
290
- <div style={{ position: 'absolute', left: GUT, right: 36, top: k * HOUR, height: 1, background: th.line }} />
291
- <span style={{ position: 'absolute', left: 30, top: k * HOUR - 13, fontSize: 21, color: th.ink3, fontWeight: 500, fontVariantNumeric: 'tabular-nums' }}>
292
- {`${H0 + k > 12 ? H0 + k - 12 : H0 + k} ${H0 + k >= 12 ? 'PM' : 'AM'}`}
293
- </span>
294
- </React.Fragment>
295
- ))}
296
- {DAYS.map((_, i) => (
297
- <div key={i} style={{ position: 'absolute', left: GUT + i * COL_W, top: 0, bottom: 0, width: 1, background: th.line }} />
298
- ))}
299
- {/* 今天整列的极淡底色 */}
300
- <div style={{ position: 'absolute', left: GUT + TODAY * COL_W, top: 0, bottom: 0, width: COL_W, background: alpha(th.accent, n ? 0.04 : 0.035) }} />
301
- {/* 事件 */}
302
- {EVENTS.map((ev, i) => {
303
- const c = th.ev[ev.hue];
304
- const top = (ev.s - H0) * HOUR + 4;
305
- const h = (ev.e - ev.s) * HOUR - 8;
306
- return (
307
- <div key={i} style={{
308
- position: 'absolute', left: GUT + ev.day * COL_W + 8, top, width: COL_W - 16, height: h, borderRadius: 12,
309
- background: c.fill, overflow: 'hidden', boxSizing: 'border-box', padding: '12px 16px 0 22px',
310
- boxShadow: n ? `inset 0 0 0 1px ${alpha(c.bar, 0.18)}` : `inset 0 0 0 1px ${alpha(c.bar, 0.1)}`,
311
- }}>
312
- <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 6, background: c.bar, boxShadow: n ? `0 0 12px ${alpha(c.bar, 0.6)}` : 'none' }} />
313
- <div style={{ fontSize: 27, fontWeight: 650, letterSpacing: '-0.015em', color: c.text, whiteSpace: 'nowrap', lineHeight: 1.15 }}>{ev.t}</div>
314
- {h > 70 && <div style={{ fontSize: 21, color: c.sub, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>{fmt(ev.s)} – {fmt(ev.e)}</div>}
315
- </div>
316
- );
317
- })}
318
- {/* 此刻线 */}
319
- <div style={{ position: 'absolute', left: GUT, right: 36, top: (NOW - H0) * HOUR, height: 0 }}>
320
- <div style={{ position: 'absolute', left: 0, right: 0, top: -1, height: 2, background: alpha(th.accent, 0.35) }} />
321
- <div style={{
322
- position: 'absolute', left: TODAY * COL_W, width: COL_W, top: -1.5, height: 3, background: th.accent,
323
- boxShadow: n ? `0 0 ${(14 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.9 * nowLit)}, 0 0 ${(40 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.4 * nowLit)}` : 'none',
324
- }} />
325
- <div style={{
326
- position: 'absolute', left: TODAY * COL_W - 9, top: -9, width: 18, height: 18, borderRadius: 9, background: th.accent,
327
- boxShadow: n ? `0 0 0 ${(5 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.22)}, 0 0 ${(24 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.8 * nowLit)}` : `0 0 0 4px ${alpha(th.accent, 0.16)}`,
328
- }} />
329
- </div>
330
- </div>
331
- </div>
332
- </div>
333
- );
334
- };
335
-
336
- // macOS 指针
337
- const Pointer: React.FC<{ x: number; y: number; s: number; o: number }> = ({ x, y, s, o }) => (
338
- <svg width={30} height={30} viewBox="0 0 28 28" style={{
339
- position: 'absolute', left: x - 2, top: y - 1, opacity: o, overflow: 'visible',
340
- transformOrigin: '2px 1px', transform: `scale(${s.toFixed(3)})`,
341
- filter: 'drop-shadow(0 1px 1px rgba(20,14,8,0.35)) drop-shadow(0 4px 6px rgba(20,14,8,0.25))',
342
- }}>
343
- <path d="M2 1 L2 23 L8 17.5 L11.5 25 L15.5 23.2 L12 15.8 L20 15 Z" fill="#16171b" stroke="#ffffff" strokeWidth={1.6} strokeLinejoin="round" />
344
- </svg>
345
- );
346
-
347
- const SWEEP_EASE = bezier(0.3, 0, 0.35, 1); // 不对称 in-out:特写里起步(屏幕上已被 2× 放大显得很快),全景里匀称穿过、扫到左端软落
348
-
349
- export const ThemeSweepToggle: React.FC = () => {
350
- const frame = useCurrentFrame();
351
-
352
- // ── 相机:特写开关 → 拉开全景 → 极缓推 ──
353
- const camT = ramp(frame, CAM0, CAM1 - CAM0, EASE.smooth);
354
- const pre = ramp(frame, 0, CAM0, EASE.linear);
355
- const hold = ramp(frame, CAM1, THEME_SWEEP_TOGGLE_DURATION - CAM1, EASE.swift);
356
- const z = mix(2.0 + 0.04 * pre, 1.0, camT) + 0.02 * hold;
357
- const cx = mix(TOG_C.x - 300, 960, camT);
358
- const cy = mix(TOG_C.y + 150, 540, camT);
359
- const camStyle: React.CSSProperties = {
360
- position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, transformOrigin: '0 0',
361
- transform: `translate(${(960 - cx * z).toFixed(2)}px, ${(540 - cy * z).toFixed(2)}px) scale(${z.toFixed(4)})`,
362
- };
363
- const toScreen = (wx: number, wy: number) => ({ x: (wx - cx) * z + 960, y: (wy - cy) * z + 540 });
364
-
365
- // ── 开关:弹簧滑到右侧(一次过冲),图标 日→月 ──
366
- const knob = frame < CLICK ? 0 : springAt(frame, CLICK, { damping: 16, stiffness: 210 });
367
- const icon = ramp(frame, CLICK + 1, 12, EASE.out);
368
-
369
- // ── 指针:滑入开关 → 按下 → 拉开时淡出 ──
370
- const ptrT = ramp(frame, PTR_IN, CLICK - PTR_IN, EASE.out);
371
- const away = ramp(frame, CLICK + 6, 16, EASE.swift); // 点完让开,别挡住月亮
372
- const ptrX = mix(TOG_C.x + 150, TOG_C.x + 20, ptrT) + 60 * away;
373
- const ptrY = mix(TOG_C.y + 120, TOG_C.y + 8, ptrT) + Math.sin(ptrT * Math.PI) * 10 + 70 * away;
374
- const press = frame < CLICK - 1 ? 1 : mix(0.86, 1, ramp(frame, CLICK + 1, 6, EASE.out));
375
- const ptrO = 1 - ramp(frame, 22, 10, EASE.exit);
376
-
377
- // ── 扫场边界:世界坐标里的 15° 斜线 x(y) = p + y·tan15(顶端领先),夜在线的右侧 ──
378
- const pAt = (f: number) => mix(1960, -480, ramp(f, SWEEP0, SWEEP1 - SWEEP0, SWEEP_EASE));
379
- const p = pAt(frame);
380
- const v = Math.abs(velocity(pAt, frame)); // px/帧(世界)
381
- const streak = Math.min(1, v / 110);
382
- const sweeping = frame >= SWEEP0 && frame < SWEEP1;
383
- const night = frame >= SWEEP0;
384
- const dayGone = frame >= SWEEP1;
385
- const nightClip = dayGone ? 'none' : `polygon(${p - 400 * T15}px -400px, 2400px -400px, 2400px 1500px, ${p + 1500 * T15}px 1500px)`;
386
- const edgeO = ramp(frame, SWEEP0, 3, EASE.linear) * (1 - ramp(frame, SWEEP1 - 8, 8, EASE.linear));
387
-
388
- // 屏幕空间的晨昏多边形(给屏幕空间暗角分昼夜两层用)
389
- const a = toScreen(p - 400 * T15, -400);
390
- const b = toScreen(p + 1500 * T15, 1500);
391
- const screenNightClip = `polygon(${a.x}px ${a.y}px, 4000px ${a.y}px, 4000px ${b.y}px, ${b.x}px ${b.y}px)`;
392
- const screenDayClip = `polygon(-2000px ${a.y}px, ${a.x}px ${a.y}px, ${b.x}px ${b.y}px, -2000px ${b.y}px)`;
393
-
394
- const lights = ramp(frame, LIGHTS, 20, EASE.linear);
395
-
396
- // 边界几何(世界坐标):中心点 (xc, 540),竖直条绕中心逆时针转 15°
397
- const xc = p + 540 * T15;
398
- const duskW = 70 + 170 * streak;
399
- const coolW = 60 + 140 * streak;
400
-
401
- return (
402
- <AbsoluteFill style={{ background: NIGHT.bg[2], overflow: 'hidden' }}>
403
- <div style={camStyle}>
404
- {!dayGone && <World th={DAY} knob={knob} icon={icon} lights={0} frame={frame} />}
405
- {night && (
406
- <div style={{ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, clipPath: dayGone ? undefined : nightClip }}>
407
- <World th={NIGHT} knob={knob} icon={icon} lights={lights} frame={frame} />
408
- </div>
409
- )}
410
- {sweeping && (
411
- <div style={{ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, pointerEvents: 'none', opacity: edgeO }}>
412
- {/* 亮侧:幕布压过来的柔影 + 落日橙暮光带(随速度变宽) */}
413
- <div style={{
414
- position: 'absolute', left: xc - duskW, top: 540 - 1100, width: duskW, height: 2200,
415
- transformOrigin: '100% 50%', transform: 'rotate(-15deg)',
416
- background: `linear-gradient(90deg, rgba(255,150,70,0) 0%, rgba(255,150,70,${(0.06 + 0.08 * streak).toFixed(3)}) 60%, rgba(255,128,56,${(0.22 + 0.12 * streak).toFixed(3)}) 94%, rgba(60,30,30,0.22) 100%)`,
417
- }} />
418
- {/* 夜侧:冷蓝辉 */}
419
- <div style={{
420
- position: 'absolute', left: xc, top: 540 - 1100, width: coolW, height: 2200,
421
- transformOrigin: '0% 50%', transform: 'rotate(-15deg)',
422
- background: `linear-gradient(90deg, rgba(150,180,255,${(0.26 + 0.1 * streak).toFixed(3)}) 0%, rgba(90,120,255,0.1) 30%, rgba(90,120,255,0) 100%)`,
423
- }} />
424
- {/* 白热核心线 */}
425
- <div style={{
426
- position: 'absolute', left: xc - 1.5, top: 540 - 1100, width: 3, height: 2200,
427
- transformOrigin: '50% 50%', transform: 'rotate(-15deg)', background: '#fffaf0',
428
- boxShadow: '0 0 8px 1px rgba(255,236,210,0.9), 0 0 26px 4px rgba(255,170,90,0.5)',
429
- }} />
430
- </div>
431
- )}
432
- {/* 指针(世界坐标,随相机缩放) */}
433
- {ptrO > 0.01 && <Pointer x={ptrX} y={ptrY} s={1.15 * press} o={ptrO} />}
434
- </div>
435
-
436
- {/* 屏幕空间暗角(昼夜两层,按晨昏线分区),颗粒一层 */}
437
- {!dayGone && (
438
- <div style={{ position: 'absolute', inset: 0, clipPath: night ? screenDayClip : undefined, background: 'radial-gradient(ellipse 75% 75% at 50% 46%, rgba(74,47,18,0) 55%, rgba(74,47,18,0.16) 100%)' }} />
439
- )}
440
- {night && (
441
- <div style={{ position: 'absolute', inset: 0, clipPath: dayGone ? undefined : screenNightClip, background: 'radial-gradient(ellipse 72% 72% at 50% 46%, rgba(0,2,8,0) 45%, rgba(0,2,8,0.55) 100%)' }} />
442
- )}
443
- <Grain opacity={0.06} blend="overlay" />
444
- </AbsoluteFill>
445
- );
446
- };
1
+ // theme-sweep-toggle —— 深浅模式扫场(第二轮重设计:video-shotcraft 发布周排期 · 昼 → 夜)
2
+ //
3
+ // 设计决定:
4
+ // - 主体:video-shotcraft 的周视图制作排期(品牌轮由虚构日历 Solstice 换来,版式逐像素不变)——1680×912 浮窗,
5
+ // 大字层级:64px 周标题、40px 日期数字、28px 事件标题;5 列 13 个制作日程(分镜 → 实拍 → 踩点 → 音效 → 渲染)、
6
+ // 4 种日历色、一条"此刻"红线。顶栏标志 = 「镜刻」ShotcraftMark(昼夜各用亮 / 反白版)。
7
+ // - look = custom(sand 的昼面 + 自定的夜面):昼 = 暖象牙舞台 + 左上斜射的阳光窗格光;
8
+ // 夜 = 墨蓝舞台 + 右上月光 + 星点。主题切换不只换 UI 皮,连舞台的"时辰"一起换——
9
+ // 但两版布局逐像素一致(同一个 <World> 组件、只换色板),读作"同一个 UI 就地换肤"。
10
+ // - 因果:开场镜头贴近顶栏的大号日夜开关(zoom 2.0),指针按下 → 旋钮弹到右侧、太阳收光芒
11
+ // 长出月牙 → 夜色边界紧接着**从开关那一侧**(右→左)15° 斜扫过全画面,同时相机拉开到全景,
12
+ // 观众从"按了哪里"一路看到"整个世界入夜"。
13
+ // - 边界 = 晨昏线:2px 白热核心线 + 亮侧一条随速度变宽的落日橙暮光带 + 夜侧冷蓝辉 + 亮侧柔影。
14
+ // - 余波「灯亮了」:扫完后今天的日期胶囊、此刻线与窗口冷色轮廓光依次点亮(夜里才有的光)。
15
+ //
16
+ // 时间表(30fps,共 140f):
17
+ // 0–8 预备:特写开关(zoom 2.0,极缓推 2.0→2.04),指针滑入开关
18
+ // 8–18 按下:指针压 0.86、旋钮弹簧滑到右侧(damping 16,一次过冲),太阳 → 月牙;指针随后让开
19
+ // 14–78 主动作:夜色斜边从右往左扫(64f,bezier(.3,0,.35,1)),~24f 扫过开关;
20
+ // 10–40 相机拉开到全景(EASE.smooth),~40–50f 晨昏线过画面中线 = 昼夜同屏海报帧
21
+ // 76–96 余波:开灯——今日胶囊 / 此刻线 / 窗口冷色轮廓光错峰亮起(EASE.out)
22
+ // 96–140 hold:夜景定格,相机 1.0→1.02 极缓推、星点微闪
23
+ import React from 'react';
24
+ import { AbsoluteFill, useCurrentFrame } from 'remotion';
25
+ import { EASE, FONT, Grain, bezier, mix, ramp, velocity } from '../../_fixtures/Polish';
26
+ import { alpha, springAt } from '../../_fixtures/Look';
27
+ import { BRAND, ShotcraftMark } from '../../_fixtures/Brand';
28
+
29
+ export const THEME_SWEEP_TOGGLE_DURATION = 140;
30
+
31
+ // ───────────── 时间轴 ─────────────
32
+ const PTR_IN = 0;
33
+ const CLICK = 8; // 指针按下 = 旋钮起跳
34
+ const SWEEP0 = 14; // 夜色边界起扫(旋钮还在过冲回落,动作重叠)
35
+ const SWEEP1 = 78;
36
+ const CAM0 = 10; // 相机开始拉开
37
+ const CAM1 = 40; // 全景先到位,晨昏线 ~44–50f 才过画面中线(昼夜同屏的海报帧)
38
+ const LIGHTS = 76; // 开灯余波起点
39
+
40
+ // ───────────── 世界几何(1920×1080 世界坐标,相机在其上推拉)─────────────
41
+ const WIN = { x: 120, y: 84, w: 1680, h: 912, r: 28 };
42
+ const TOP_H = 92;
43
+ const HEAD_H = 112;
44
+ const DAYS_H = 80;
45
+ const GUT = 104; // 时间刻度列
46
+ const COL_W = (WIN.w - GUT - 36) / 5;
47
+ const H0 = 9; // 9:00 起
48
+ const H1 = 16; // 16:00 止
49
+ const HOUR = (WIN.h - TOP_H - HEAD_H - DAYS_H - 24) / (H1 - H0);
50
+ const GRID_Y = TOP_H + HEAD_H + DAYS_H; // 窗口内坐标
51
+ const TOG = { w: 132, h: 66 }; // 开关
52
+ const TOG_X = WIN.w - 40 - TOG.w; // 窗口内
53
+ const TOG_Y = (TOP_H - TOG.h) / 2;
54
+ const TOG_C = { x: WIN.x + TOG_X + TOG.w / 2, y: WIN.y + TOG_Y + TOG.h / 2 }; // 世界坐标
55
+ const T15 = Math.tan((15 * Math.PI) / 180);
56
+
57
+ // ───────────── 两套主题(同布局,逐键手工映射色板,不做滤镜反相)─────────────
58
+ type Hue = 'blue' | 'green' | 'amber' | 'rose';
59
+ type Theme = {
60
+ night: boolean;
61
+ bg: [string, string, string];
62
+ key: { x: number; y: number; c: string; a: number };
63
+ win: string; win2: string; line: string; ink: string; ink2: string; ink3: string;
64
+ accent: string; shadow: string; seg: string;
65
+ track: string; trackOn: string; knob: string;
66
+ ev: Record<Hue, { fill: string; bar: string; text: string; sub: string }>;
67
+ };
68
+
69
+ const DAY: Theme = {
70
+ night: false,
71
+ bg: ['#f4ede1', '#ece2d2', '#e0d3bf'],
72
+ key: { x: 0.18, y: 0.0, c: '#fff8ea', a: 0.95 },
73
+ win: '#fdfaf4', win2: '#f6f0e6', line: 'rgba(70,48,24,0.10)', ink: '#1f1a13', ink2: '#6b5f50', ink3: '#a49784',
74
+ accent: '#e0552a', shadow: '#4a2f12', seg: '#efe7da',
75
+ track: '#ece2d2', trackOn: '#2b3048', knob: '#ffffff',
76
+ ev: {
77
+ blue: { fill: '#e7eefb', bar: '#3d72de', text: '#1a3670', sub: '#5774a8' },
78
+ green: { fill: '#e3f1e6', bar: '#2f9a5c', text: '#184d31', sub: '#4f7f62' },
79
+ amber: { fill: '#fbeedb', bar: '#d98a1c', text: '#6a400f', sub: '#9b6c34' },
80
+ rose: { fill: '#fbe6e7', bar: '#d6455c', text: '#76202e', sub: '#a5525f' },
81
+ },
82
+ };
83
+
84
+ const NIGHT: Theme = {
85
+ night: true,
86
+ bg: ['#0e1426', '#090d1a', '#05070f'],
87
+ key: { x: 0.86, y: 0.02, c: '#5b74c9', a: 0.4 },
88
+ win: '#11151f', win2: '#161b28', line: 'rgba(170,190,240,0.10)', ink: '#eef1fa', ink2: '#9aa3ba', ink3: '#5f6882',
89
+ accent: '#ff9450', shadow: '#000207', seg: '#1a2030',
90
+ track: '#262c40', trackOn: '#262c40', knob: '#f4efe4',
91
+ ev: {
92
+ blue: { fill: 'rgba(96,140,255,0.15)', bar: '#6f9cff', text: '#dfe7ff', sub: '#8ea4d6' },
93
+ green: { fill: 'rgba(72,205,135,0.13)', bar: '#52d38e', text: '#d8f5e5', sub: '#84b89b' },
94
+ amber: { fill: 'rgba(255,172,72,0.14)', bar: '#ffb24c', text: '#ffeacf', sub: '#c9a479' },
95
+ rose: { fill: 'rgba(255,104,134,0.14)', bar: '#ff718c', text: '#ffdee5', sub: '#c98b97' },
96
+ },
97
+ };
98
+
99
+ const DAYS = [
100
+ { d: 'MON', n: 14 },
101
+ { d: 'TUE', n: 15 },
102
+ { d: 'WED', n: 16 },
103
+ { d: 'THU', n: 17 },
104
+ { d: 'FRI', n: 18 },
105
+ ];
106
+ const TODAY = 2;
107
+ const NOW = 13 + 40 / 60; // 13:40
108
+
109
+ const EVENTS: { day: number; s: number; e: number; t: string; hue: Hue }[] = [
110
+ { day: 0, s: 9.5, e: 10.75, t: 'Storyboard crit', hue: 'rose' },
111
+ { day: 0, s: 12.5, e: 14.5, t: 'Shot list review', hue: 'blue' },
112
+ { day: 1, s: 9, e: 10, t: 'Standup', hue: 'amber' },
113
+ { day: 1, s: 10.75, e: 12.5, t: 'Page captures', hue: 'green' },
114
+ { day: 1, s: 14, e: 15.25, t: 'Voiceover · Theo', hue: 'amber' },
115
+ { day: 2, s: 9.25, e: 10.5, t: 'Launch sync', hue: 'blue' },
116
+ { day: 2, s: 11, e: 12.75, t: 'Camera moves', hue: 'green' },
117
+ { day: 2, s: 14.25, e: 15.75, t: 'Beat-sync pass', hue: 'rose' },
118
+ { day: 3, s: 9.5, e: 11.25, t: 'SFX design', hue: 'amber' },
119
+ { day: 3, s: 12.5, e: 13.5, t: 'Lunch & learn', hue: 'green' },
120
+ { day: 3, s: 14.75, e: 16, t: 'Render review', hue: 'blue' },
121
+ { day: 4, s: 10, e: 12.5, t: 'Final render', hue: 'green' },
122
+ { day: 4, s: 13.5, e: 14.75, t: 'Ship the film', hue: 'rose' },
123
+ ];
124
+
125
+ const fmt = (h: number) => {
126
+ const hh = Math.floor(h);
127
+ const mm = Math.round((h - hh) * 60);
128
+ const h12 = hh > 12 ? hh - 12 : hh;
129
+ return `${h12}:${String(mm).padStart(2, '0')}`;
130
+ };
131
+
132
+ // 确定性哈希(星点)
133
+ const hash = (n: number) => {
134
+ const x = Math.sin(n * 91.345 + 47.853) * 43758.5453;
135
+ return x - Math.floor(x);
136
+ };
137
+
138
+ // ───────────── 日 → 月 图标(太阳收光芒、遮罩圆滑入长出月牙)─────────────
139
+ const SunMoon: React.FC<{ p: number; color: string; size: number }> = ({ p, color, size }) => {
140
+ const id = React.useId().replace(/[^a-zA-Z0-9]/g, '');
141
+ const rays = 1 - EASE.out(Math.min(1, p * 1.6));
142
+ return (
143
+ <svg width={size} height={size} viewBox="0 0 40 40" style={{ display: 'block', overflow: 'visible' }}>
144
+ <defs>
145
+ <mask id={`m${id}`}>
146
+ <rect x={-10} y={-10} width={60} height={60} fill="#fff" />
147
+ <circle cx={mix(44, 27, EASE.snappy(p))} cy={mix(-4, 13, EASE.snappy(p))} r={10.5} fill="#000" />
148
+ </mask>
149
+ </defs>
150
+ <g transform={`rotate(${(p * 90).toFixed(2)} 20 20)`}>
151
+ {Array.from({ length: 8 }, (_, i) => (
152
+ <line key={i} x1={20} y1={4.5 - (1 - rays) * 4} x2={20} y2={8.5 - (1 - rays) * 6}
153
+ stroke={color} strokeWidth={3} strokeLinecap="round" opacity={rays}
154
+ transform={`rotate(${i * 45} 20 20)`} />
155
+ ))}
156
+ </g>
157
+ <circle cx={20} cy={20} r={mix(7.5, 11, EASE.out(p))} fill={color} mask={`url(#m${id})`} />
158
+ </svg>
159
+ );
160
+ };
161
+
162
+ // ───────────── 整个世界(舞台 + 窗口),两版主题共用这一份布局 ─────────────
163
+ const World: React.FC<{ th: Theme; knob: number; icon: number; lights: number; frame: number }> = ({ th, knob, icon, lights, frame }) => {
164
+ const n = th.night;
165
+ const [t, m, b] = th.bg;
166
+ const todayLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2))) : 0;
167
+ const nowLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2 - 0.5))) : 0;
168
+ const rimLit = n ? EASE.out(Math.min(1, Math.max(0, lights * 2.2 - 1))) : 0;
169
+ return (
170
+ <div style={{ position: 'absolute', inset: 0 }}>
171
+ {/* 舞台:外扩 260px,相机特写时右上角也有底 */}
172
+ <div style={{ position: 'absolute', left: -260, top: -260, right: -260, bottom: -260, background: `linear-gradient(180deg, ${t} 0%, ${m} 55%, ${b} 100%)` }} />
173
+ <div style={{
174
+ position: 'absolute', left: -260, top: -260, right: -260, bottom: -260,
175
+ background: `radial-gradient(ellipse 60% 64% at ${th.key.x * 100}% ${th.key.y * 100}%, ${alpha(th.key.c, th.key.a)} 0%, ${alpha(th.key.c, 0)} 72%)`,
176
+ mixBlendMode: n ? 'screen' : 'normal',
177
+ }} />
178
+ {!n && (
179
+ // 昼:左上斜射进来的窗格光(三道暖白光柱,极淡)
180
+ <div style={{ position: 'absolute', inset: -260, overflow: 'hidden' }}>
181
+ {[0, 1, 2].map((i) => (
182
+ <div key={i} style={{
183
+ position: 'absolute', left: 120 + i * 300, top: -300, width: 150, height: 2200,
184
+ transform: 'rotate(-32deg)', transformOrigin: '0 0',
185
+ background: 'linear-gradient(90deg, rgba(255,250,236,0) 0%, rgba(255,250,236,0.55) 50%, rgba(255,250,236,0) 100%)',
186
+ opacity: 0.55 - i * 0.12,
187
+ }} />
188
+ ))}
189
+ </div>
190
+ )}
191
+ {n && (
192
+ // 夜:星点(窗口外的天空),确定性微闪
193
+ <div style={{ position: 'absolute', inset: -260 }}>
194
+ {Array.from({ length: 70 }, (_, i) => {
195
+ const x = hash(i * 3.1) * 2440;
196
+ const y = hash(i * 7.7 + 2) * 1600;
197
+ const z = hash(i * 1.9 + 5);
198
+ const tw = 0.55 + 0.45 * Math.sin(frame / (9 + z * 14) + i * 1.7);
199
+ const s = 1.4 + z * 2.4;
200
+ return <div key={i} style={{ position: 'absolute', left: x, top: y, width: s, height: s, borderRadius: '50%', background: '#dfe6ff', opacity: (0.25 + 0.6 * z) * tw, boxShadow: z > 0.75 ? '0 0 6px rgba(190,205,255,0.8)' : undefined }} />;
201
+ })}
202
+ </div>
203
+ )}
204
+ {/* 窗口落在"桌面"上的地面光 / 影 */}
205
+ <div style={{
206
+ position: 'absolute', left: WIN.x + 80, top: WIN.y + WIN.h - 40, width: WIN.w - 160, height: 140,
207
+ background: n
208
+ ? 'radial-gradient(ellipse 50% 50% at 50% 30%, rgba(90,120,220,0.20) 0%, rgba(90,120,220,0) 70%)'
209
+ : 'radial-gradient(ellipse 50% 50% at 50% 30%, rgba(74,47,18,0.16) 0%, rgba(74,47,18,0) 70%)',
210
+ filter: 'blur(14px)',
211
+ }} />
212
+
213
+ {/* 窗口 */}
214
+ <div style={{
215
+ position: 'absolute', left: WIN.x, top: WIN.y, width: WIN.w, height: WIN.h, borderRadius: WIN.r, overflow: 'hidden',
216
+ background: th.win, fontFamily: FONT.sans, color: th.ink,
217
+ boxShadow: n
218
+ ? `inset 0 1px 0 rgba(255,255,255,0.07), 0 0 0 1px rgba(150,175,255,${(0.1 + 0.18 * rimLit).toFixed(3)}), 0 0 ${(60 * rimLit).toFixed(1)}px rgba(90,125,255,${(0.22 * rimLit).toFixed(3)}), 0 40px 90px -20px rgba(0,2,7,0.9), 0 12px 30px rgba(0,2,7,0.6)`
219
+ : `inset 0 1px 0 rgba(255,255,255,0.9), 0 0 0 1px rgba(70,48,24,0.08), 0 40px 90px -24px rgba(74,47,18,0.32), 0 10px 26px -6px rgba(74,47,18,0.16)`,
220
+ }}>
221
+ {/* 顶栏 */}
222
+ <div style={{ position: 'absolute', left: 0, top: 0, width: WIN.w, height: TOP_H, borderBottom: `1px solid ${th.line}`, display: 'flex', alignItems: 'center', padding: '0 40px', boxSizing: 'border-box' }}>
223
+ <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
224
+ {/* 「镜刻」标志:昼用墨框、夜用反白框,琥珀斜切不变(标志本身不加底块 / 发光) */}
225
+ <ShotcraftMark size={44} tone={n ? 'dark' : 'light'} />
226
+ <span style={{ fontFamily: BRAND.font, fontSize: 32, fontWeight: 700, letterSpacing: '0.03em' }}>{BRAND.name}</span>
227
+ </div>
228
+ <div style={{ marginLeft: 72, display: 'flex', gap: 4, padding: 5, borderRadius: 14, background: th.seg }}>
229
+ {['Day', 'Week', 'Month'].map((s, i) => (
230
+ <div key={s} style={{
231
+ padding: '9px 22px', borderRadius: 10, fontSize: 24, fontWeight: i === 1 ? 650 : 500,
232
+ color: i === 1 ? th.ink : th.ink2,
233
+ background: i === 1 ? (n ? '#262d40' : '#ffffff') : 'transparent',
234
+ boxShadow: i === 1 ? (n ? 'inset 0 1px 0 rgba(255,255,255,0.06)' : '0 1px 2px rgba(74,47,18,0.12), 0 2px 8px -2px rgba(74,47,18,0.12)') : 'none',
235
+ }}>{s}</div>
236
+ ))}
237
+ </div>
238
+ <div style={{ marginLeft: 'auto', marginRight: TOG.w + 28, fontSize: 24, color: th.ink2, fontWeight: 500 }}>Today</div>
239
+ </div>
240
+ {/* 日夜开关 */}
241
+ <div style={{
242
+ position: 'absolute', left: TOG_X, top: TOG_Y, width: TOG.w, height: TOG.h, borderRadius: TOG.h / 2,
243
+ background: n ? th.track : `linear-gradient(90deg, ${th.trackOn} 0%, ${th.trackOn} ${(knob * 100).toFixed(1)}%, ${th.track} ${(knob * 100).toFixed(1)}%)`,
244
+ boxShadow: n ? 'inset 0 2px 4px rgba(0,0,0,0.45), inset 0 0 0 1px rgba(255,255,255,0.06)' : 'inset 0 2px 4px rgba(74,47,18,0.16), inset 0 0 0 1px rgba(74,47,18,0.06)',
245
+ overflow: 'hidden',
246
+ }}>
247
+ {/* 轨道里的小星(拨到夜时显出) */}
248
+ {[0, 1, 2].map((i) => (
249
+ <div key={i} style={{ position: 'absolute', left: 18 + i * 13, top: 18 + (i % 2) * 18, width: 4, height: 4, borderRadius: 2, background: '#e8edff', opacity: Math.min(1, Math.max(0, knob * 1.4 - 0.3)) * (0.5 + 0.2 * i) }} />
250
+ ))}
251
+ <div style={{
252
+ position: 'absolute', top: 6, left: 6 + knob * (TOG.w - TOG.h), width: TOG.h - 12, height: TOG.h - 12, borderRadius: '50%',
253
+ background: `linear-gradient(180deg, #ffffff, ${th.knob})`,
254
+ boxShadow: '0 2px 4px rgba(0,0,0,0.22), 0 6px 14px -4px rgba(0,0,0,0.3)',
255
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
256
+ }}>
257
+ <SunMoon p={icon} color={icon > 0.5 ? '#3b4366' : '#f0a020'} size={34} />
258
+ </div>
259
+ </div>
260
+
261
+ {/* 周标题 */}
262
+ <div style={{ position: 'absolute', left: 40, top: TOP_H, height: HEAD_H, display: 'flex', alignItems: 'center', width: WIN.w - 80 }}>
263
+ <span style={{ fontSize: 64, fontWeight: 760, letterSpacing: '-0.035em' }}>September 14 – 18</span>
264
+ <span style={{ marginLeft: 26, fontSize: 26, fontWeight: 600, letterSpacing: '0.08em', color: th.ink3, transform: 'translateY(8px)' }}>WEEK 38</span>
265
+ <span style={{ marginLeft: 'auto', fontSize: 26, color: th.ink2, fontWeight: 500 }}>Launch film · ships Friday</span>
266
+ </div>
267
+
268
+ {/* 星期 / 日期 */}
269
+ {DAYS.map((d, i) => {
270
+ const today = i === TODAY;
271
+ return (
272
+ <div key={d.d} style={{ position: 'absolute', left: GUT + i * COL_W, top: TOP_H + HEAD_H, width: COL_W, height: DAYS_H, display: 'flex', alignItems: 'center', gap: 14, paddingLeft: 14, boxSizing: 'border-box' }}>
273
+ <span style={{ fontSize: 22, fontWeight: 650, letterSpacing: '0.1em', color: today ? th.accent : th.ink3 }}>{d.d}</span>
274
+ <span style={{
275
+ fontSize: 40, fontWeight: 720, letterSpacing: '-0.03em', fontVariantNumeric: 'tabular-nums',
276
+ color: today ? '#ffffff' : th.ink,
277
+ ...(today ? {
278
+ background: th.accent, borderRadius: 30, padding: '2px 16px',
279
+ boxShadow: n ? `0 0 ${(26 * todayLit).toFixed(1)}px ${alpha(th.accent, 0.6 * todayLit)}, 0 0 ${(70 * todayLit).toFixed(1)}px ${alpha(th.accent, 0.3 * todayLit)}` : `0 4px 12px -4px ${alpha(th.accent, 0.5)}`,
280
+ } : {}),
281
+ }}>{d.n}</span>
282
+ </div>
283
+ );
284
+ })}
285
+
286
+ {/* 时间网格 */}
287
+ <div style={{ position: 'absolute', left: 0, top: GRID_Y, width: WIN.w, height: WIN.h - GRID_Y, borderTop: `1px solid ${th.line}` }}>
288
+ {Array.from({ length: H1 - H0 + 1 }, (_, k) => (
289
+ <React.Fragment key={k}>
290
+ <div style={{ position: 'absolute', left: GUT, right: 36, top: k * HOUR, height: 1, background: th.line }} />
291
+ <span style={{ position: 'absolute', left: 30, top: k * HOUR - 13, fontSize: 21, color: th.ink3, fontWeight: 500, fontVariantNumeric: 'tabular-nums' }}>
292
+ {`${H0 + k > 12 ? H0 + k - 12 : H0 + k} ${H0 + k >= 12 ? 'PM' : 'AM'}`}
293
+ </span>
294
+ </React.Fragment>
295
+ ))}
296
+ {DAYS.map((_, i) => (
297
+ <div key={i} style={{ position: 'absolute', left: GUT + i * COL_W, top: 0, bottom: 0, width: 1, background: th.line }} />
298
+ ))}
299
+ {/* 今天整列的极淡底色 */}
300
+ <div style={{ position: 'absolute', left: GUT + TODAY * COL_W, top: 0, bottom: 0, width: COL_W, background: alpha(th.accent, n ? 0.04 : 0.035) }} />
301
+ {/* 事件 */}
302
+ {EVENTS.map((ev, i) => {
303
+ const c = th.ev[ev.hue];
304
+ const top = (ev.s - H0) * HOUR + 4;
305
+ const h = (ev.e - ev.s) * HOUR - 8;
306
+ return (
307
+ <div key={i} style={{
308
+ position: 'absolute', left: GUT + ev.day * COL_W + 8, top, width: COL_W - 16, height: h, borderRadius: 12,
309
+ background: c.fill, overflow: 'hidden', boxSizing: 'border-box', padding: '12px 16px 0 22px',
310
+ boxShadow: n ? `inset 0 0 0 1px ${alpha(c.bar, 0.18)}` : `inset 0 0 0 1px ${alpha(c.bar, 0.1)}`,
311
+ }}>
312
+ <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 6, background: c.bar, boxShadow: n ? `0 0 12px ${alpha(c.bar, 0.6)}` : 'none' }} />
313
+ <div style={{ fontSize: 27, fontWeight: 650, letterSpacing: '-0.015em', color: c.text, whiteSpace: 'nowrap', lineHeight: 1.15 }}>{ev.t}</div>
314
+ {h > 70 && <div style={{ fontSize: 21, color: c.sub, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>{fmt(ev.s)} – {fmt(ev.e)}</div>}
315
+ </div>
316
+ );
317
+ })}
318
+ {/* 此刻线 */}
319
+ <div style={{ position: 'absolute', left: GUT, right: 36, top: (NOW - H0) * HOUR, height: 0 }}>
320
+ <div style={{ position: 'absolute', left: 0, right: 0, top: -1, height: 2, background: alpha(th.accent, 0.35) }} />
321
+ <div style={{
322
+ position: 'absolute', left: TODAY * COL_W, width: COL_W, top: -1.5, height: 3, background: th.accent,
323
+ boxShadow: n ? `0 0 ${(14 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.9 * nowLit)}, 0 0 ${(40 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.4 * nowLit)}` : 'none',
324
+ }} />
325
+ <div style={{
326
+ position: 'absolute', left: TODAY * COL_W - 9, top: -9, width: 18, height: 18, borderRadius: 9, background: th.accent,
327
+ boxShadow: n ? `0 0 0 ${(5 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.22)}, 0 0 ${(24 * nowLit).toFixed(1)}px ${alpha(th.accent, 0.8 * nowLit)}` : `0 0 0 4px ${alpha(th.accent, 0.16)}`,
328
+ }} />
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+ );
334
+ };
335
+
336
+ // macOS 指针
337
+ const Pointer: React.FC<{ x: number; y: number; s: number; o: number }> = ({ x, y, s, o }) => (
338
+ <svg width={30} height={30} viewBox="0 0 28 28" style={{
339
+ position: 'absolute', left: x - 2, top: y - 1, opacity: o, overflow: 'visible',
340
+ transformOrigin: '2px 1px', transform: `scale(${s.toFixed(3)})`,
341
+ filter: 'drop-shadow(0 1px 1px rgba(20,14,8,0.35)) drop-shadow(0 4px 6px rgba(20,14,8,0.25))',
342
+ }}>
343
+ <path d="M2 1 L2 23 L8 17.5 L11.5 25 L15.5 23.2 L12 15.8 L20 15 Z" fill="#16171b" stroke="#ffffff" strokeWidth={1.6} strokeLinejoin="round" />
344
+ </svg>
345
+ );
346
+
347
+ const SWEEP_EASE = bezier(0.3, 0, 0.35, 1); // 不对称 in-out:特写里起步(屏幕上已被 2× 放大显得很快),全景里匀称穿过、扫到左端软落
348
+
349
+ export const ThemeSweepToggle: React.FC = () => {
350
+ const frame = useCurrentFrame();
351
+
352
+ // ── 相机:特写开关 → 拉开全景 → 极缓推 ──
353
+ const camT = ramp(frame, CAM0, CAM1 - CAM0, EASE.smooth);
354
+ const pre = ramp(frame, 0, CAM0, EASE.linear);
355
+ const hold = ramp(frame, CAM1, THEME_SWEEP_TOGGLE_DURATION - CAM1, EASE.swift);
356
+ const z = mix(2.0 + 0.04 * pre, 1.0, camT) + 0.02 * hold;
357
+ const cx = mix(TOG_C.x - 300, 960, camT);
358
+ const cy = mix(TOG_C.y + 150, 540, camT);
359
+ const camStyle: React.CSSProperties = {
360
+ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, transformOrigin: '0 0',
361
+ transform: `translate(${(960 - cx * z).toFixed(2)}px, ${(540 - cy * z).toFixed(2)}px) scale(${z.toFixed(4)})`,
362
+ };
363
+ const toScreen = (wx: number, wy: number) => ({ x: (wx - cx) * z + 960, y: (wy - cy) * z + 540 });
364
+
365
+ // ── 开关:弹簧滑到右侧(一次过冲),图标 日→月 ──
366
+ const knob = frame < CLICK ? 0 : springAt(frame, CLICK, { damping: 16, stiffness: 210 });
367
+ const icon = ramp(frame, CLICK + 1, 12, EASE.out);
368
+
369
+ // ── 指针:滑入开关 → 按下 → 拉开时淡出 ──
370
+ const ptrT = ramp(frame, PTR_IN, CLICK - PTR_IN, EASE.out);
371
+ const away = ramp(frame, CLICK + 6, 16, EASE.swift); // 点完让开,别挡住月亮
372
+ const ptrX = mix(TOG_C.x + 150, TOG_C.x + 20, ptrT) + 60 * away;
373
+ const ptrY = mix(TOG_C.y + 120, TOG_C.y + 8, ptrT) + Math.sin(ptrT * Math.PI) * 10 + 70 * away;
374
+ const press = frame < CLICK - 1 ? 1 : mix(0.86, 1, ramp(frame, CLICK + 1, 6, EASE.out));
375
+ const ptrO = 1 - ramp(frame, 22, 10, EASE.exit);
376
+
377
+ // ── 扫场边界:世界坐标里的 15° 斜线 x(y) = p + y·tan15(顶端领先),夜在线的右侧 ──
378
+ const pAt = (f: number) => mix(1960, -480, ramp(f, SWEEP0, SWEEP1 - SWEEP0, SWEEP_EASE));
379
+ const p = pAt(frame);
380
+ const v = Math.abs(velocity(pAt, frame)); // px/帧(世界)
381
+ const streak = Math.min(1, v / 110);
382
+ const sweeping = frame >= SWEEP0 && frame < SWEEP1;
383
+ const night = frame >= SWEEP0;
384
+ const dayGone = frame >= SWEEP1;
385
+ const nightClip = dayGone ? 'none' : `polygon(${p - 400 * T15}px -400px, 2400px -400px, 2400px 1500px, ${p + 1500 * T15}px 1500px)`;
386
+ const edgeO = ramp(frame, SWEEP0, 3, EASE.linear) * (1 - ramp(frame, SWEEP1 - 8, 8, EASE.linear));
387
+
388
+ // 屏幕空间的晨昏多边形(给屏幕空间暗角分昼夜两层用)
389
+ const a = toScreen(p - 400 * T15, -400);
390
+ const b = toScreen(p + 1500 * T15, 1500);
391
+ const screenNightClip = `polygon(${a.x}px ${a.y}px, 4000px ${a.y}px, 4000px ${b.y}px, ${b.x}px ${b.y}px)`;
392
+ const screenDayClip = `polygon(-2000px ${a.y}px, ${a.x}px ${a.y}px, ${b.x}px ${b.y}px, -2000px ${b.y}px)`;
393
+
394
+ const lights = ramp(frame, LIGHTS, 20, EASE.linear);
395
+
396
+ // 边界几何(世界坐标):中心点 (xc, 540),竖直条绕中心逆时针转 15°
397
+ const xc = p + 540 * T15;
398
+ const duskW = 70 + 170 * streak;
399
+ const coolW = 60 + 140 * streak;
400
+
401
+ return (
402
+ <AbsoluteFill style={{ background: NIGHT.bg[2], overflow: 'hidden' }}>
403
+ <div style={camStyle}>
404
+ {!dayGone && <World th={DAY} knob={knob} icon={icon} lights={0} frame={frame} />}
405
+ {night && (
406
+ <div style={{ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, clipPath: dayGone ? undefined : nightClip }}>
407
+ <World th={NIGHT} knob={knob} icon={icon} lights={lights} frame={frame} />
408
+ </div>
409
+ )}
410
+ {sweeping && (
411
+ <div style={{ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080, pointerEvents: 'none', opacity: edgeO }}>
412
+ {/* 亮侧:幕布压过来的柔影 + 落日橙暮光带(随速度变宽) */}
413
+ <div style={{
414
+ position: 'absolute', left: xc - duskW, top: 540 - 1100, width: duskW, height: 2200,
415
+ transformOrigin: '100% 50%', transform: 'rotate(-15deg)',
416
+ background: `linear-gradient(90deg, rgba(255,150,70,0) 0%, rgba(255,150,70,${(0.06 + 0.08 * streak).toFixed(3)}) 60%, rgba(255,128,56,${(0.22 + 0.12 * streak).toFixed(3)}) 94%, rgba(60,30,30,0.22) 100%)`,
417
+ }} />
418
+ {/* 夜侧:冷蓝辉 */}
419
+ <div style={{
420
+ position: 'absolute', left: xc, top: 540 - 1100, width: coolW, height: 2200,
421
+ transformOrigin: '0% 50%', transform: 'rotate(-15deg)',
422
+ background: `linear-gradient(90deg, rgba(150,180,255,${(0.26 + 0.1 * streak).toFixed(3)}) 0%, rgba(90,120,255,0.1) 30%, rgba(90,120,255,0) 100%)`,
423
+ }} />
424
+ {/* 白热核心线 */}
425
+ <div style={{
426
+ position: 'absolute', left: xc - 1.5, top: 540 - 1100, width: 3, height: 2200,
427
+ transformOrigin: '50% 50%', transform: 'rotate(-15deg)', background: '#fffaf0',
428
+ boxShadow: '0 0 8px 1px rgba(255,236,210,0.9), 0 0 26px 4px rgba(255,170,90,0.5)',
429
+ }} />
430
+ </div>
431
+ )}
432
+ {/* 指针(世界坐标,随相机缩放) */}
433
+ {ptrO > 0.01 && <Pointer x={ptrX} y={ptrY} s={1.15 * press} o={ptrO} />}
434
+ </div>
435
+
436
+ {/* 屏幕空间暗角(昼夜两层,按晨昏线分区),颗粒一层 */}
437
+ {!dayGone && (
438
+ <div style={{ position: 'absolute', inset: 0, clipPath: night ? screenDayClip : undefined, background: 'radial-gradient(ellipse 75% 75% at 50% 46%, rgba(74,47,18,0) 55%, rgba(74,47,18,0.16) 100%)' }} />
439
+ )}
440
+ {night && (
441
+ <div style={{ position: 'absolute', inset: 0, clipPath: dayGone ? undefined : screenNightClip, background: 'radial-gradient(ellipse 72% 72% at 50% 46%, rgba(0,2,8,0) 45%, rgba(0,2,8,0.55) 100%)' }} />
442
+ )}
443
+ <Grain opacity={0.06} blend="overlay" />
444
+ </AbsoluteFill>
445
+ );
446
+ };