@lalalic/markcut 1.2.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/AGENTS.md +40 -17
  2. package/README.md +7 -3
  3. package/SKILL.md +0 -2
  4. package/docs/edit-mode.md +1 -1
  5. package/docs/label-mode.md +6 -4
  6. package/docs/markdown-descriptive.md +34 -1
  7. package/docs/system-prompt-edit.md +19 -0
  8. package/package.json +1 -1
  9. package/src/config.mjs +8 -3
  10. package/src/descriptive/compiler.test.ts +42 -42
  11. package/src/descriptive/compiler.ts +41 -52
  12. package/src/descriptive/markdown.ts +8 -4
  13. package/src/descriptive/resolve.test.ts +14 -7
  14. package/src/descriptive/resolve.ts +148 -20
  15. package/src/player/browser.tsx +178 -54
  16. package/src/player/bundle/player.js +1149 -566
  17. package/src/player/components/EditControls.tsx +92 -0
  18. package/src/player/components/HeaderBar.tsx +60 -0
  19. package/src/player/components/LabelControls.tsx +367 -0
  20. package/src/player/components/SceneThumbnails.tsx +60 -0
  21. package/src/player/components/VariantBar.tsx +39 -0
  22. package/src/player/components/index.ts +5 -0
  23. package/src/player/pipeline.mjs +130 -66
  24. package/src/player/pipeline.ts +3 -1
  25. package/src/player/server-shared.mjs +5 -7
  26. package/src/player/server.mjs +336 -239
  27. package/src/render/cli.mjs +4 -6
  28. package/src/schema/index.ts +20 -23
  29. package/src/types/Audio.tsx +25 -33
  30. package/src/types/Component.tsx +18 -24
  31. package/src/types/Effect.tsx +31 -39
  32. package/src/types/Image.tsx +23 -30
  33. package/src/types/Include.tsx +70 -76
  34. package/src/types/Map.tsx +48 -44
  35. package/src/types/Rhythm.tsx +19 -27
  36. package/src/types/Video.tsx +40 -47
  37. package/src/utils/index.ts +23 -10
  38. package/src/vision/cli.mjs +6 -6
  39. package/templates/courseware/TEMPLATE.md +16 -10
  40. package/tests/fixtures/audio.json +4 -2
  41. package/tests/fixtures/basic.json +2 -1
  42. package/tests/fixtures/component-all.json +4 -2
  43. package/tests/fixtures/components.json +4 -2
  44. package/tests/fixtures/effects.json +12 -6
  45. package/tests/fixtures/full.json +8 -4
  46. package/tests/fixtures/map.json +2 -1
  47. package/tests/fixtures/md/dialogue.md +12 -0
  48. package/tests/fixtures/md/edge-cases.md +9 -0
  49. package/tests/fixtures/scenes.json +4 -2
  50. package/tests/fixtures/subtitle.json +6 -3
  51. package/tests/fixtures/subvideo.json +11 -6
  52. package/tests/fixtures/templates/courseware.md +61 -4
  53. package/tests/fixtures/tween-visual.json +2 -6
  54. package/tests/fixtures/video-series.json +6 -14
  55. package/tests/md-descriptive.test.ts +170 -0
  56. package/tests/render.test.ts +32 -16
  57. package/tests/schema.test.ts +6 -3
  58. package/tests/server.test.ts +9 -6
  59. package/tests/utils.ts +4 -4
  60. package/scripts/artlist-dl.mjs +0 -190
  61. package/src/player/label-server.mjs +0 -599
@@ -416,8 +416,8 @@ var require_react_production = __commonJS({
416
416
  exports.use = function(usable) {
417
417
  return ReactSharedInternals.H.use(usable);
418
418
  };
419
- exports.useActionState = function(action2, initialState, permalink) {
420
- return ReactSharedInternals.H.useActionState(action2, initialState, permalink);
419
+ exports.useActionState = function(action, initialState, permalink) {
420
+ return ReactSharedInternals.H.useActionState(action, initialState, permalink);
421
421
  };
422
422
  exports.useCallback = function(callback, deps) {
423
423
  return ReactSharedInternals.H.useCallback(callback, deps);
@@ -778,7 +778,7 @@ var require_scheduler = __commonJS({
778
778
  var require_react_dom_production = __commonJS({
779
779
  "node_modules/react-dom/cjs/react-dom.production.js"(exports) {
780
780
  "use strict";
781
- var React48 = require_react();
781
+ var React56 = require_react();
782
782
  function formatProdErrorMessage(code) {
783
783
  var url2 = "https://react.dev/errors/" + code;
784
784
  if (1 < arguments.length) {
@@ -818,7 +818,7 @@ var require_react_dom_production = __commonJS({
818
818
  implementation
819
819
  };
820
820
  }
821
- var ReactSharedInternals = React48.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
821
+ var ReactSharedInternals = React56.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
822
822
  function getCrossOriginStringAs(as, input) {
823
823
  if ("font" === as) return "";
824
824
  if ("string" === typeof input)
@@ -913,8 +913,8 @@ var require_react_dom_production = __commonJS({
913
913
  exports.unstable_batchedUpdates = function(fn, a2) {
914
914
  return fn(a2);
915
915
  };
916
- exports.useFormState = function(action2, initialState, permalink) {
917
- return ReactSharedInternals.H.useFormState(action2, initialState, permalink);
916
+ exports.useFormState = function(action, initialState, permalink) {
917
+ return ReactSharedInternals.H.useFormState(action, initialState, permalink);
918
918
  };
919
919
  exports.useFormStatus = function() {
920
920
  return ReactSharedInternals.H.useHostTransitionStatus();
@@ -954,8 +954,8 @@ var require_react_dom_client_production = __commonJS({
954
954
  "node_modules/react-dom/cjs/react-dom-client.production.js"(exports) {
955
955
  "use strict";
956
956
  var Scheduler = require_scheduler();
957
- var React48 = require_react();
958
- var ReactDOM2 = require_react_dom();
957
+ var React56 = require_react();
958
+ var ReactDOM3 = require_react_dom();
959
959
  function formatProdErrorMessage(code) {
960
960
  var url2 = "https://react.dev/errors/" + code;
961
961
  if (1 < arguments.length) {
@@ -1145,8 +1145,8 @@ var require_react_dom_client_production = __commonJS({
1145
1145
  return null;
1146
1146
  }
1147
1147
  var isArrayImpl = Array.isArray;
1148
- var ReactSharedInternals = React48.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
1149
- var ReactDOMSharedInternals = ReactDOM2.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
1148
+ var ReactSharedInternals = React56.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
1149
+ var ReactDOMSharedInternals = ReactDOM3.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
1150
1150
  var sharedNotPendingObject = {
1151
1151
  pending: false,
1152
1152
  data: null,
@@ -4323,8 +4323,8 @@ var require_react_dom_client_production = __commonJS({
4323
4323
  memoCache.index++;
4324
4324
  return updateQueue;
4325
4325
  }
4326
- function basicStateReducer(state, action2) {
4327
- return "function" === typeof action2 ? action2(state) : action2;
4326
+ function basicStateReducer(state, action) {
4327
+ return "function" === typeof action ? action(state) : action;
4328
4328
  }
4329
4329
  function updateReducer(reducer) {
4330
4330
  var hook = updateWorkInProgressHook();
@@ -4541,12 +4541,12 @@ var require_react_dom_client_production = __commonJS({
4541
4541
  }
4542
4542
  }
4543
4543
  function runActionStateAction(actionQueue, node) {
4544
- var action2 = node.action, payload = node.payload, prevState = actionQueue.state;
4544
+ var action = node.action, payload = node.payload, prevState = actionQueue.state;
4545
4545
  if (node.isTransition) {
4546
4546
  var prevTransition = ReactSharedInternals.T, currentTransition = {};
4547
4547
  ReactSharedInternals.T = currentTransition;
4548
4548
  try {
4549
- var returnValue = action2(prevState, payload), onStartTransitionFinish = ReactSharedInternals.S;
4549
+ var returnValue = action(prevState, payload), onStartTransitionFinish = ReactSharedInternals.S;
4550
4550
  null !== onStartTransitionFinish && onStartTransitionFinish(currentTransition, returnValue);
4551
4551
  handleActionReturnValue(actionQueue, node, returnValue);
4552
4552
  } catch (error49) {
@@ -4556,7 +4556,7 @@ var require_react_dom_client_production = __commonJS({
4556
4556
  }
4557
4557
  } else
4558
4558
  try {
4559
- prevTransition = action2(prevState, payload), handleActionReturnValue(actionQueue, node, prevTransition);
4559
+ prevTransition = action(prevState, payload), handleActionReturnValue(actionQueue, node, prevTransition);
4560
4560
  } catch (error$66) {
4561
4561
  onActionError(actionQueue, node, error$66);
4562
4562
  }
@@ -4597,7 +4597,7 @@ var require_react_dom_client_production = __commonJS({
4597
4597
  function actionStateReducer(oldState, newState) {
4598
4598
  return newState;
4599
4599
  }
4600
- function mountActionState(action2, initialStateProp) {
4600
+ function mountActionState(action, initialStateProp) {
4601
4601
  if (isHydrating) {
4602
4602
  var ssrFormState = workInProgressRoot.formState;
4603
4603
  if (null !== ssrFormState) {
@@ -4665,7 +4665,7 @@ var require_react_dom_client_production = __commonJS({
4665
4665
  JSCompiler_inline_result$jscomp$0 = {
4666
4666
  state: initialStateProp,
4667
4667
  dispatch: null,
4668
- action: action2,
4668
+ action,
4669
4669
  pending: null
4670
4670
  };
4671
4671
  JSCompiler_inline_result.queue = JSCompiler_inline_result$jscomp$0;
@@ -4677,14 +4677,14 @@ var require_react_dom_client_production = __commonJS({
4677
4677
  ssrFormState
4678
4678
  );
4679
4679
  JSCompiler_inline_result$jscomp$0.dispatch = ssrFormState;
4680
- JSCompiler_inline_result.memoizedState = action2;
4680
+ JSCompiler_inline_result.memoizedState = action;
4681
4681
  return [initialStateProp, ssrFormState, false];
4682
4682
  }
4683
- function updateActionState(action2) {
4683
+ function updateActionState(action) {
4684
4684
  var stateHook = updateWorkInProgressHook();
4685
- return updateActionStateImpl(stateHook, currentHook, action2);
4685
+ return updateActionStateImpl(stateHook, currentHook, action);
4686
4686
  }
4687
- function updateActionStateImpl(stateHook, currentStateHook, action2) {
4687
+ function updateActionStateImpl(stateHook, currentStateHook, action) {
4688
4688
  currentStateHook = updateReducerImpl(
4689
4689
  stateHook,
4690
4690
  currentStateHook,
@@ -4701,26 +4701,26 @@ var require_react_dom_client_production = __commonJS({
4701
4701
  else state = currentStateHook;
4702
4702
  currentStateHook = updateWorkInProgressHook();
4703
4703
  var actionQueue = currentStateHook.queue, dispatch = actionQueue.dispatch;
4704
- action2 !== currentStateHook.memoizedState && (currentlyRenderingFiber.flags |= 2048, pushSimpleEffect(
4704
+ action !== currentStateHook.memoizedState && (currentlyRenderingFiber.flags |= 2048, pushSimpleEffect(
4705
4705
  9,
4706
4706
  { destroy: void 0 },
4707
- actionStateActionEffect.bind(null, actionQueue, action2),
4707
+ actionStateActionEffect.bind(null, actionQueue, action),
4708
4708
  null
4709
4709
  ));
4710
4710
  return [state, dispatch, stateHook];
4711
4711
  }
4712
- function actionStateActionEffect(actionQueue, action2) {
4713
- actionQueue.action = action2;
4712
+ function actionStateActionEffect(actionQueue, action) {
4713
+ actionQueue.action = action;
4714
4714
  }
4715
- function rerenderActionState(action2) {
4715
+ function rerenderActionState(action) {
4716
4716
  var stateHook = updateWorkInProgressHook(), currentStateHook = currentHook;
4717
4717
  if (null !== currentStateHook)
4718
- return updateActionStateImpl(stateHook, currentStateHook, action2);
4718
+ return updateActionStateImpl(stateHook, currentStateHook, action);
4719
4719
  updateWorkInProgressHook();
4720
4720
  stateHook = stateHook.memoizedState;
4721
4721
  currentStateHook = updateWorkInProgressHook();
4722
4722
  var dispatch = currentStateHook.queue.dispatch;
4723
- currentStateHook.memoizedState = action2;
4723
+ currentStateHook.memoizedState = action;
4724
4724
  return [stateHook, dispatch, false];
4725
4725
  }
4726
4726
  function pushSimpleEffect(tag, inst, create, deps) {
@@ -4892,7 +4892,7 @@ var require_react_dom_client_production = __commonJS({
4892
4892
  }
4893
4893
  function noop2() {
4894
4894
  }
4895
- function startHostTransition(formFiber, pendingState, action2, formData) {
4895
+ function startHostTransition(formFiber, pendingState, action, formData) {
4896
4896
  if (5 !== formFiber.tag) throw Error(formatProdErrorMessage(476));
4897
4897
  var queue = ensureFormComponentIsStateful(formFiber).queue;
4898
4898
  startTransition(
@@ -4900,9 +4900,9 @@ var require_react_dom_client_production = __commonJS({
4900
4900
  queue,
4901
4901
  pendingState,
4902
4902
  sharedNotPendingObject,
4903
- null === action2 ? noop2 : function() {
4903
+ null === action ? noop2 : function() {
4904
4904
  requestFormReset$1(formFiber);
4905
- return action2(formData);
4905
+ return action(formData);
4906
4906
  }
4907
4907
  );
4908
4908
  }
@@ -4976,29 +4976,29 @@ var require_react_dom_client_production = __commonJS({
4976
4976
  provider = provider.return;
4977
4977
  }
4978
4978
  }
4979
- function dispatchReducerAction(fiber, queue, action2) {
4979
+ function dispatchReducerAction(fiber, queue, action) {
4980
4980
  var lane = requestUpdateLane();
4981
- action2 = {
4981
+ action = {
4982
4982
  lane,
4983
4983
  revertLane: 0,
4984
4984
  gesture: null,
4985
- action: action2,
4985
+ action,
4986
4986
  hasEagerState: false,
4987
4987
  eagerState: null,
4988
4988
  next: null
4989
4989
  };
4990
- isRenderPhaseUpdate(fiber) ? enqueueRenderPhaseUpdate(queue, action2) : (action2 = enqueueConcurrentHookUpdate(fiber, queue, action2, lane), null !== action2 && (scheduleUpdateOnFiber(action2, fiber, lane), entangleTransitionUpdate(action2, queue, lane)));
4990
+ isRenderPhaseUpdate(fiber) ? enqueueRenderPhaseUpdate(queue, action) : (action = enqueueConcurrentHookUpdate(fiber, queue, action, lane), null !== action && (scheduleUpdateOnFiber(action, fiber, lane), entangleTransitionUpdate(action, queue, lane)));
4991
4991
  }
4992
- function dispatchSetState(fiber, queue, action2) {
4992
+ function dispatchSetState(fiber, queue, action) {
4993
4993
  var lane = requestUpdateLane();
4994
- dispatchSetStateInternal(fiber, queue, action2, lane);
4994
+ dispatchSetStateInternal(fiber, queue, action, lane);
4995
4995
  }
4996
- function dispatchSetStateInternal(fiber, queue, action2, lane) {
4996
+ function dispatchSetStateInternal(fiber, queue, action, lane) {
4997
4997
  var update = {
4998
4998
  lane,
4999
4999
  revertLane: 0,
5000
5000
  gesture: null,
5001
- action: action2,
5001
+ action,
5002
5002
  hasEagerState: false,
5003
5003
  eagerState: null,
5004
5004
  next: null
@@ -5008,7 +5008,7 @@ var require_react_dom_client_production = __commonJS({
5008
5008
  var alternate = fiber.alternate;
5009
5009
  if (0 === fiber.lanes && (null === alternate || 0 === alternate.lanes) && (alternate = queue.lastRenderedReducer, null !== alternate))
5010
5010
  try {
5011
- var currentState = queue.lastRenderedState, eagerState = alternate(currentState, action2);
5011
+ var currentState = queue.lastRenderedState, eagerState = alternate(currentState, action);
5012
5012
  update.hasEagerState = true;
5013
5013
  update.eagerState = eagerState;
5014
5014
  if (objectIs(eagerState, currentState))
@@ -5016,18 +5016,18 @@ var require_react_dom_client_production = __commonJS({
5016
5016
  } catch (error49) {
5017
5017
  } finally {
5018
5018
  }
5019
- action2 = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
5020
- if (null !== action2)
5021
- return scheduleUpdateOnFiber(action2, fiber, lane), entangleTransitionUpdate(action2, queue, lane), true;
5019
+ action = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
5020
+ if (null !== action)
5021
+ return scheduleUpdateOnFiber(action, fiber, lane), entangleTransitionUpdate(action, queue, lane), true;
5022
5022
  }
5023
5023
  return false;
5024
5024
  }
5025
- function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action2) {
5026
- action2 = {
5025
+ function dispatchOptimisticSetState(fiber, throwIfDuringRender, queue, action) {
5026
+ action = {
5027
5027
  lane: 2,
5028
5028
  revertLane: requestTransitionLane(),
5029
5029
  gesture: null,
5030
- action: action2,
5030
+ action,
5031
5031
  hasEagerState: false,
5032
5032
  eagerState: null,
5033
5033
  next: null
@@ -5038,7 +5038,7 @@ var require_react_dom_client_production = __commonJS({
5038
5038
  throwIfDuringRender = enqueueConcurrentHookUpdate(
5039
5039
  fiber,
5040
5040
  queue,
5041
- action2,
5041
+ action,
5042
5042
  2
5043
5043
  ), null !== throwIfDuringRender && scheduleUpdateOnFiber(throwIfDuringRender, fiber, 2);
5044
5044
  }
@@ -9816,10 +9816,10 @@ var require_react_dom_client_production = __commonJS({
9816
9816
  }
9817
9817
  function extractEvents$1(dispatchQueue, domEventName, maybeTargetInst, nativeEvent, nativeEventTarget) {
9818
9818
  if ("submit" === domEventName && maybeTargetInst && maybeTargetInst.stateNode === nativeEventTarget) {
9819
- var action2 = coerceFormActionProp(
9819
+ var action = coerceFormActionProp(
9820
9820
  (nativeEventTarget[internalPropsKey] || null).action
9821
9821
  ), submitter = nativeEvent.submitter;
9822
- submitter && (domEventName = (domEventName = submitter[internalPropsKey] || null) ? coerceFormActionProp(domEventName.formAction) : submitter.getAttribute("formAction"), null !== domEventName && (action2 = domEventName, submitter = null));
9822
+ submitter && (domEventName = (domEventName = submitter[internalPropsKey] || null) ? coerceFormActionProp(domEventName.formAction) : submitter.getAttribute("formAction"), null !== domEventName && (action = domEventName, submitter = null));
9823
9823
  var event = new SyntheticEvent(
9824
9824
  "action",
9825
9825
  "action",
@@ -9842,22 +9842,22 @@ var require_react_dom_client_production = __commonJS({
9842
9842
  pending: true,
9843
9843
  data: formData,
9844
9844
  method: nativeEventTarget.method,
9845
- action: action2
9845
+ action
9846
9846
  },
9847
9847
  null,
9848
9848
  formData
9849
9849
  );
9850
9850
  }
9851
9851
  } else
9852
- "function" === typeof action2 && (event.preventDefault(), formData = submitter ? createFormDataWithSubmitter(nativeEventTarget, submitter) : new FormData(nativeEventTarget), startHostTransition(
9852
+ "function" === typeof action && (event.preventDefault(), formData = submitter ? createFormDataWithSubmitter(nativeEventTarget, submitter) : new FormData(nativeEventTarget), startHostTransition(
9853
9853
  maybeTargetInst,
9854
9854
  {
9855
9855
  pending: true,
9856
9856
  data: formData,
9857
9857
  method: nativeEventTarget.method,
9858
- action: action2
9858
+ action
9859
9859
  },
9860
- action2,
9860
+ action,
9861
9861
  formData
9862
9862
  ));
9863
9863
  },
@@ -12506,15 +12506,15 @@ var require_react_dom_client_production = __commonJS({
12506
12506
  if ("function" === typeof submitterOrAction)
12507
12507
  formProps || scheduleReplayQueueIfNeeded(i3);
12508
12508
  else if (formProps) {
12509
- var action2 = null;
12509
+ var action = null;
12510
12510
  if (submitterOrAction && submitterOrAction.hasAttribute("formAction"))
12511
12511
  if (form = submitterOrAction, formProps = submitterOrAction[internalPropsKey] || null)
12512
- action2 = formProps.formAction;
12512
+ action = formProps.formAction;
12513
12513
  else {
12514
12514
  if (null !== findInstanceBlockingTarget(form)) continue;
12515
12515
  }
12516
- else action2 = formProps.action;
12517
- "function" === typeof action2 ? i3[queuedTarget + 1] = action2 : (i3.splice(queuedTarget, 3), queuedTarget -= 3);
12516
+ else action = formProps.action;
12517
+ "function" === typeof action ? i3[queuedTarget + 1] = action : (i3.splice(queuedTarget, 3), queuedTarget -= 3);
12518
12518
  scheduleReplayQueueIfNeeded(i3);
12519
12519
  }
12520
12520
  }
@@ -12591,7 +12591,7 @@ var require_react_dom_client_production = __commonJS({
12591
12591
  0 === i3 && attemptExplicitHydrationTarget(target);
12592
12592
  }
12593
12593
  };
12594
- var isomorphicReactPackageVersion$jscomp$inline_1840 = React48.version;
12594
+ var isomorphicReactPackageVersion$jscomp$inline_1840 = React56.version;
12595
12595
  if ("19.2.5" !== isomorphicReactPackageVersion$jscomp$inline_1840)
12596
12596
  throw Error(
12597
12597
  formatProdErrorMessage(
@@ -12794,9 +12794,9 @@ var require_fast_deep_equal = __commonJS({
12794
12794
  });
12795
12795
 
12796
12796
  // src/player/browser.tsx
12797
- var React47 = __toESM(require_react(), 1);
12797
+ var React55 = __toESM(require_react(), 1);
12798
12798
  var import_client = __toESM(require_client(), 1);
12799
- var ReactDOM = __toESM(require_react_dom(), 1);
12799
+ var ReactDOM2 = __toESM(require_react_dom(), 1);
12800
12800
 
12801
12801
  // node_modules/remotion/dist/esm/index.mjs
12802
12802
  var esm_exports = {};
@@ -17159,16 +17159,16 @@ var resolveTrimProps = ({
17159
17159
  const trimAfterValue = trimAfter ?? endAt ?? void 0;
17160
17160
  return { trimBeforeValue, trimAfterValue };
17161
17161
  };
17162
- var durationReducer = (state, action2) => {
17163
- switch (action2.type) {
17162
+ var durationReducer = (state, action) => {
17163
+ switch (action.type) {
17164
17164
  case "got-duration": {
17165
- const absoluteSrc = getAbsoluteSrc(action2.src);
17166
- if (state[absoluteSrc] === action2.durationInSeconds) {
17165
+ const absoluteSrc = getAbsoluteSrc(action.src);
17166
+ if (state[absoluteSrc] === action.durationInSeconds) {
17167
17167
  return state;
17168
17168
  }
17169
17169
  return {
17170
17170
  ...state,
17171
- [absoluteSrc]: action2.durationInSeconds
17171
+ [absoluteSrc]: action.durationInSeconds
17172
17172
  };
17173
17173
  }
17174
17174
  default:
@@ -27936,15 +27936,84 @@ var forward2 = import_react110.forwardRef;
27936
27936
  var Thumbnail = forward2(ThumbnailFn);
27937
27937
 
27938
27938
  // src/entry.tsx
27939
- var React46 = __toESM(require_react(), 1);
27939
+ var React47 = __toESM(require_react(), 1);
27940
+
27941
+ // src/utils/component-import-map.ts
27942
+ var React8 = __toESM(require_react(), 1);
27943
+ var ReactDOM = __toESM(require_react_dom(), 1);
27944
+ function ensureSharedImportMap() {
27945
+ var _a3, _b, _c;
27946
+ if (typeof document === "undefined") return;
27947
+ if (document.querySelector("#rmtr-import-map")) return;
27948
+ const g2 = globalThis;
27949
+ g2.__remotionShared ?? (g2.__remotionShared = {});
27950
+ (_a3 = g2.__remotionShared)["react"] ?? (_a3["react"] = React8);
27951
+ (_b = g2.__remotionShared)["react-dom"] ?? (_b["react-dom"] = ReactDOM);
27952
+ (_c = g2.__remotionShared)["remotion"] ?? (_c["remotion"] = esm_exports);
27953
+ if (!g2.__nodeModuleStub) {
27954
+ const nodeModuleStubCode = `export function createRequire() { return () => ({ resolve: () => { throw new Error("node:module.createRequire is not available in the browser"); } }); }`;
27955
+ g2.__nodeModuleStub = URL.createObjectURL(
27956
+ new Blob([nodeModuleStubCode], { type: "application/javascript" })
27957
+ );
27958
+ }
27959
+ try {
27960
+ const imports = {};
27961
+ for (const spec of ["node:module", "node:fs", "node:path", "node:os"]) {
27962
+ imports[spec] = g2.__nodeModuleStub;
27963
+ }
27964
+ for (const [specifier, mod] of Object.entries(g2.__remotionShared)) {
27965
+ const lines = [
27966
+ `const _mod = globalThis.__remotionShared[${JSON.stringify(specifier)}];`
27967
+ ];
27968
+ for (const name of Object.keys(mod)) {
27969
+ if (name === "default") continue;
27970
+ lines.push(
27971
+ `const __${name} = _mod[${JSON.stringify(name)}];`,
27972
+ `export { __${name} as ${name} };`
27973
+ );
27974
+ }
27975
+ if ("default" in mod) lines.push("export default _mod.default;");
27976
+ imports[specifier] = URL.createObjectURL(
27977
+ new Blob([lines.join("\n")], { type: "application/javascript" })
27978
+ );
27979
+ }
27980
+ const blobJsx = new Blob(
27981
+ [
27982
+ `
27983
+ const R = globalThis.__remotionShared["react"];
27984
+ const { createElement, Fragment } = R;
27985
+ export { Fragment };
27986
+ export function jsx(type, props, key) {
27987
+ return createElement(type, key != null ? { ...props, key } : props);
27988
+ }
27989
+ export function jsxs(type, props, key) {
27990
+ return createElement(type, key != null ? { ...props, key } : props);
27991
+ }
27992
+ export function jsxDEV(type, props, key, isStaticChildren, source, self) {
27993
+ return createElement(type, key != null ? { ...props, key } : props);
27994
+ }
27995
+ `
27996
+ ],
27997
+ { type: "application/javascript" }
27998
+ );
27999
+ imports["react/jsx-runtime"] = URL.createObjectURL(blobJsx);
28000
+ const script = document.createElement("script");
28001
+ script.type = "importmap";
28002
+ script.id = "rmtr-import-map";
28003
+ script.textContent = JSON.stringify({ imports });
28004
+ document.head.appendChild(script);
28005
+ } catch (e) {
28006
+ console.warn("Failed to create shared module import map:", e);
28007
+ }
28008
+ }
27940
28009
 
27941
28010
  // src/context/index.tsx
27942
- var React9 = __toESM(require_react(), 1);
28011
+ var React15 = __toESM(require_react(), 1);
27943
28012
 
27944
28013
  // src/context/EventContext.tsx
27945
- var React8 = __toESM(require_react(), 1);
28014
+ var React9 = __toESM(require_react(), 1);
27946
28015
  var import_jsx_runtime56 = __toESM(require_jsx_runtime(), 1);
27947
- var EventContext = React8.createContext(null);
28016
+ var EventContext = React9.createContext(null);
27948
28017
  function createComponentProxy(target, setState) {
27949
28018
  return new Proxy(target, {
27950
28019
  set(obj, prop, value) {
@@ -27959,8 +28028,8 @@ function createComponentProxy(target, setState) {
27959
28028
  });
27960
28029
  }
27961
28030
  function EventProvider({ children }) {
27962
- const registryRef = React8.useRef(/* @__PURE__ */ new Map());
27963
- const register = React8.useCallback(
28031
+ const registryRef = React9.useRef(/* @__PURE__ */ new Map());
28032
+ const register = React9.useCallback(
27964
28033
  (id, setState) => {
27965
28034
  const target = {};
27966
28035
  const proxy = createComponentProxy(target, setState);
@@ -27968,10 +28037,10 @@ function EventProvider({ children }) {
27968
28037
  },
27969
28038
  []
27970
28039
  );
27971
- const unregister = React8.useCallback((id) => {
28040
+ const unregister = React9.useCallback((id) => {
27972
28041
  registryRef.current.delete(id);
27973
28042
  }, []);
27974
- const evaluate = React8.useCallback((code) => {
28043
+ const evaluate = React9.useCallback((code) => {
27975
28044
  const scope = {};
27976
28045
  for (const [id, reg] of registryRef.current) {
27977
28046
  scope[id] = reg.proxy;
@@ -27986,14 +28055,14 @@ function EventProvider({ children }) {
27986
28055
  console.warn(`Event evaluation failed: "${code}"`, err);
27987
28056
  }
27988
28057
  }, []);
27989
- const value = React8.useMemo(
28058
+ const value = React9.useMemo(
27990
28059
  () => ({ register, unregister, evaluate }),
27991
28060
  [register, unregister, evaluate]
27992
28061
  );
27993
28062
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(EventContext.Provider, { value, children });
27994
28063
  }
27995
28064
  function useEventContext() {
27996
- const ctx = React8.useContext(EventContext);
28065
+ const ctx = React9.useContext(EventContext);
27997
28066
  if (!ctx) {
27998
28067
  throw new Error("useEventContext must be used within an EventProvider");
27999
28068
  }
@@ -28017,8 +28086,8 @@ function useFrameEvents(on, durationInFrames) {
28017
28086
  const frame = useCurrentFrame();
28018
28087
  const { fps } = useVideoConfig();
28019
28088
  const { evaluate } = useEventContext();
28020
- const handledRef = React8.useRef(false);
28021
- React8.useEffect(() => {
28089
+ const handledRef = React9.useRef(false);
28090
+ React9.useEffect(() => {
28022
28091
  if (!on || durationInFrames <= 0) return;
28023
28092
  const targetFrame = resolveWhenToFrame(on.when, durationInFrames, fps);
28024
28093
  if (frame === targetFrame && !handledRef.current) {
@@ -28032,13 +28101,13 @@ function useFrameEvents(on, durationInFrames) {
28032
28101
  // src/context/index.tsx
28033
28102
  var import_jsx_runtime57 = __toESM(require_jsx_runtime(), 1);
28034
28103
  var DefaultContainer = ({ children, style: style2 }) => /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { style: { position: "absolute", inset: 0, ...style2 }, children });
28035
- var ComposeContext = React9.createContext({
28104
+ var ComposeContext = React15.createContext({
28036
28105
  Container: DefaultContainer
28037
28106
  });
28038
- var AudioContext2 = React9.createContext(null);
28107
+ var AudioContext2 = React15.createContext(null);
28039
28108
 
28040
28109
  // src/types/Folder.tsx
28041
- var React44 = __toESM(require_react(), 1);
28110
+ var React45 = __toESM(require_react(), 1);
28042
28111
 
28043
28112
  // node_modules/@remotion/transitions/dist/esm/index.mjs
28044
28113
  var import_react113 = __toESM(require_react(), 1);
@@ -30316,6 +30385,12 @@ function cssJS(css) {
30316
30385
  }
30317
30386
  return out;
30318
30387
  }
30388
+ function leafEnd(stream2) {
30389
+ if (typeof stream2.end === "number") return stream2.end;
30390
+ const s2 = stream2.start ?? 0;
30391
+ if (typeof stream2.duration === "number") return s2 + stream2.duration;
30392
+ return 0;
30393
+ }
30319
30394
  function getDurationInSeconds(stream2, update = true) {
30320
30395
  if (!stream2) return 0;
30321
30396
  if (stream2.type === "rhythm") {
@@ -30323,8 +30398,7 @@ function getDurationInSeconds(stream2, update = true) {
30323
30398
  }
30324
30399
  if (stream2.type === "include") {
30325
30400
  if (stream2.src) {
30326
- const last = stream2.actions?.[stream2.actions.length - 1];
30327
- const d2 = last?.end ?? 0;
30401
+ const d2 = leafEnd(stream2);
30328
30402
  if (update) stream2.durationInSeconds = d2;
30329
30403
  return d2;
30330
30404
  }
@@ -30344,8 +30418,7 @@ function getDurationInSeconds(stream2, update = true) {
30344
30418
  return total2;
30345
30419
  }
30346
30420
  if (!stream2.children?.length) {
30347
- const last = stream2.actions?.[stream2.actions.length - 1];
30348
- const d2 = last?.end ?? 0;
30421
+ const d2 = leafEnd(stream2);
30349
30422
  if (update) stream2.durationInSeconds = d2;
30350
30423
  return d2;
30351
30424
  }
@@ -30394,10 +30467,10 @@ function parseVTT(src) {
30394
30467
  }
30395
30468
 
30396
30469
  // src/types/Video.tsx
30397
- var React18 = __toESM(require_react(), 1);
30470
+ var React19 = __toESM(require_react(), 1);
30398
30471
 
30399
30472
  // src/types/FrameSyncStyle.tsx
30400
- var React16 = __toESM(require_react(), 1);
30473
+ var React18 = __toESM(require_react(), 1);
30401
30474
  var import_jsx_runtime77 = __toESM(require_jsx_runtime(), 1);
30402
30475
  function FrameSyncStyle({
30403
30476
  style: style2,
@@ -30406,7 +30479,7 @@ function FrameSyncStyle({
30406
30479
  const frame = useCurrentFrame();
30407
30480
  const { fps } = useVideoConfig();
30408
30481
  const hasAnimation = "animation" in style2;
30409
- const mergedStyle = React16.useMemo(() => {
30482
+ const mergedStyle = React18.useMemo(() => {
30410
30483
  if (!hasAnimation) return style2;
30411
30484
  const currentTime = frame / fps;
30412
30485
  return {
@@ -30437,60 +30510,57 @@ function resolveVideoSrc(src) {
30437
30510
  }
30438
30511
  function VideoLeaf({ stream: stream2 }) {
30439
30512
  const { fps } = useVideoConfig();
30440
- const audio2 = React18.useContext(AudioContext2);
30441
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
30513
+ const audio2 = React19.useContext(AudioContext2);
30514
+ const start = stream2.start ?? 0;
30515
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
30516
+ const totalDur = stream2.durationInSeconds ?? end;
30442
30517
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
30443
30518
  if (!stream2.src) return null;
30444
30519
  const resolvedSrc = resolveVideoSrc(stream2.src);
30445
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_jsx_runtime78.Fragment, { children: stream2.actions.map((a2) => {
30446
- const start = a2.start ?? 0;
30447
- const end = a2.end ?? start + 1;
30448
- const startFrom = a2.startFrom ?? 0;
30449
- const endAt = a2.endAt ?? stream2.durationInSeconds ?? end - start;
30450
- const volume = a2.volume ?? stream2.volume ?? 1;
30451
- const playbackRate = a2.loop ? 1 : Math.min(1, toPlaybackRate((endAt - startFrom) / (end - start)));
30452
- const actionStyle = cssJS(a2.style);
30453
- const hasAnimation = "animation" in actionStyle;
30454
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30455
- Sequence,
30456
- {
30457
- durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30458
- from: Math.floor(fps * start),
30459
- layout: "none",
30460
- showInTimeline: false,
30461
- children: hasAnimation ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(FrameSyncStyle, { style: actionStyle, children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30462
- OffthreadVideo,
30463
- {
30464
- src: resolvedSrc,
30465
- startFrom: Math.floor(startFrom * fps),
30466
- endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30467
- muted: volume === 0 || !!audio2?.foreground,
30468
- volume,
30469
- playbackRate,
30470
- showInTimeline: false,
30471
- style: { width: "100%", height: "100%" }
30472
- }
30473
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30474
- OffthreadVideo,
30475
- {
30476
- src: resolvedSrc,
30477
- startFrom: Math.floor(startFrom * fps),
30478
- endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30479
- muted: volume === 0 || !!audio2?.foreground,
30480
- volume,
30481
- playbackRate,
30482
- showInTimeline: false,
30483
- style: { width: "100%", height: "100%", ...actionStyle }
30484
- }
30485
- )
30486
- },
30487
- a2.id
30488
- );
30489
- }) });
30520
+ const startFrom = stream2.startFrom ?? 0;
30521
+ const endAt = stream2.endAt ?? totalDur;
30522
+ const volume = stream2.volume ?? 1;
30523
+ const playbackRate = stream2.loop ? 1 : Math.min(1, toPlaybackRate((endAt - startFrom) / (end - start)));
30524
+ const streamStyle = cssJS(stream2.style);
30525
+ const hasAnimation = "animation" in streamStyle;
30526
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30527
+ Sequence,
30528
+ {
30529
+ durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30530
+ from: Math.floor(fps * start),
30531
+ layout: "none",
30532
+ showInTimeline: false,
30533
+ children: hasAnimation ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(FrameSyncStyle, { style: streamStyle, children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30534
+ OffthreadVideo,
30535
+ {
30536
+ src: resolvedSrc,
30537
+ startFrom: Math.floor(startFrom * fps),
30538
+ endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30539
+ muted: volume === 0 || !!audio2?.foreground,
30540
+ volume,
30541
+ playbackRate,
30542
+ showInTimeline: false,
30543
+ style: { width: "100%", height: "100%" }
30544
+ }
30545
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
30546
+ OffthreadVideo,
30547
+ {
30548
+ src: resolvedSrc,
30549
+ startFrom: Math.floor(startFrom * fps),
30550
+ endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30551
+ muted: volume === 0 || !!audio2?.foreground,
30552
+ volume,
30553
+ playbackRate,
30554
+ showInTimeline: false,
30555
+ style: { width: "100%", height: "100%", ...streamStyle }
30556
+ }
30557
+ )
30558
+ }
30559
+ );
30490
30560
  }
30491
30561
 
30492
30562
  // src/types/Audio.tsx
30493
- var React19 = __toESM(require_react(), 1);
30563
+ var React25 = __toESM(require_react(), 1);
30494
30564
  var import_jsx_runtime79 = __toESM(require_jsx_runtime(), 1);
30495
30565
  function resolveAudioSrc(src) {
30496
30566
  if (/^(https?:|data:|blob:|file:|\/)/.test(src)) return src;
@@ -30499,44 +30569,41 @@ function resolveAudioSrc(src) {
30499
30569
  function AudioLeaf({ stream: stream2 }) {
30500
30570
  const { fps } = useVideoConfig();
30501
30571
  const environment = useRemotionEnvironment();
30502
- const ctx = React19.useContext(AudioContext2);
30503
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
30572
+ const ctx = React25.useContext(AudioContext2);
30573
+ const start = stream2.start ?? 0;
30574
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
30575
+ const totalDur = stream2.durationInSeconds ?? end;
30504
30576
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
30505
30577
  if (!stream2.src) return null;
30506
30578
  if (environment.isStudio) return null;
30507
30579
  const resolvedSrc = resolveAudioSrc(stream2.src);
30508
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_jsx_runtime79.Fragment, { children: stream2.actions.map((a2) => {
30509
- const start = a2.start ?? 0;
30510
- const end = a2.end ?? start + 1;
30511
- const startFrom = a2.startFrom ?? 0;
30512
- const endAt = a2.endAt ?? stream2.durationInSeconds ?? end - start;
30513
- const volume = a2.volume ?? stream2.volume ?? 1;
30514
- const playbackRate = a2.loop ? 1 : toPlaybackRate((endAt - startFrom) / (end - start));
30515
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
30516
- Sequence,
30517
- {
30518
- name: stream2.src ?? "audio",
30519
- durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30520
- from: Math.floor(fps * start),
30521
- layout: "none",
30522
- showInTimeline: false,
30523
- children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
30524
- Audio,
30525
- {
30526
- src: resolvedSrc,
30527
- startFrom: Math.floor(startFrom * fps),
30528
- endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30529
- muted: volume === 0 || !!ctx?.foreground,
30530
- volume,
30531
- loop: (a2.loop ?? 1) > 1,
30532
- playbackRate,
30533
- showInTimeline: false
30534
- }
30535
- )
30536
- },
30537
- a2.id
30538
- );
30539
- }) });
30580
+ const startFrom = stream2.startFrom ?? 0;
30581
+ const endAt = stream2.endAt ?? totalDur;
30582
+ const volume = stream2.volume ?? 1;
30583
+ const playbackRate = stream2.loop ? 1 : toPlaybackRate((endAt - startFrom) / (end - start));
30584
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
30585
+ Sequence,
30586
+ {
30587
+ name: stream2.src ?? "audio",
30588
+ durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30589
+ from: Math.floor(fps * start),
30590
+ layout: "none",
30591
+ showInTimeline: false,
30592
+ children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
30593
+ Audio,
30594
+ {
30595
+ src: resolvedSrc,
30596
+ startFrom: Math.floor(startFrom * fps),
30597
+ endAt: Math.floor(startFrom * fps) + Math.floor((endAt - startFrom) * fps / playbackRate),
30598
+ muted: volume === 0 || !!ctx?.foreground,
30599
+ volume,
30600
+ loop: (stream2.loop ?? 1) > 1,
30601
+ playbackRate,
30602
+ showInTimeline: false
30603
+ }
30604
+ )
30605
+ }
30606
+ );
30540
30607
  }
30541
30608
 
30542
30609
  // src/types/Image.tsx
@@ -30547,42 +30614,39 @@ function resolveImageSrc(src) {
30547
30614
  }
30548
30615
  function ImageLeaf({ stream: stream2 }) {
30549
30616
  const { fps } = useVideoConfig();
30550
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
30617
+ const start = stream2.start ?? 0;
30618
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
30619
+ const totalDur = stream2.durationInSeconds ?? end;
30551
30620
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
30552
30621
  if (!stream2.src) return null;
30553
30622
  const resolvedSrc = resolveImageSrc(stream2.src);
30554
- return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_jsx_runtime80.Fragment, { children: stream2.actions.map((a2) => {
30555
- const start = a2.start ?? 0;
30556
- const end = a2.end ?? start + 1;
30557
- return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
30558
- Sequence,
30559
- {
30560
- durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30561
- from: Math.floor(fps * start),
30562
- layout: "none",
30563
- children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(FrameSyncStyle, { style: cssJS(a2.style), children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
30564
- Img,
30565
- {
30566
- src: resolvedSrc,
30567
- style: {
30568
- width: "100%",
30569
- height: "100%",
30570
- objectFit: stream2.fit
30571
- },
30572
- onDragStart: (e) => {
30573
- e.stopPropagation();
30574
- return false;
30575
- }
30623
+ return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
30624
+ Sequence,
30625
+ {
30626
+ durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
30627
+ from: Math.floor(fps * start),
30628
+ layout: "none",
30629
+ children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(FrameSyncStyle, { style: cssJS(stream2.style), children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
30630
+ Img,
30631
+ {
30632
+ src: resolvedSrc,
30633
+ style: {
30634
+ width: "100%",
30635
+ height: "100%",
30636
+ objectFit: stream2.fit
30637
+ },
30638
+ onDragStart: (e) => {
30639
+ e.stopPropagation();
30640
+ return false;
30576
30641
  }
30577
- ) })
30578
- },
30579
- a2.id
30580
- );
30581
- }) });
30642
+ }
30643
+ ) })
30644
+ }
30645
+ );
30582
30646
  }
30583
30647
 
30584
30648
  // src/types/Component.tsx
30585
- var React27 = __toESM(require_react(), 1);
30649
+ var React30 = __toESM(require_react(), 1);
30586
30650
 
30587
30651
  // node_modules/react-jsx-parser/dist/react-jsx-parser.min.js
30588
30652
  var import_react125 = __toESM(require_react());
@@ -41616,11 +41680,11 @@ var JsxParser = class extends import_react125.default.Component {
41616
41680
  constructor() {
41617
41681
  super(...arguments);
41618
41682
  __publicField(this, "ParsedChildren", null);
41619
- __privateAdd(this, _parseJSX, (jsx68) => {
41683
+ __privateAdd(this, _parseJSX, (jsx76) => {
41620
41684
  const parser = Parser.extend(AcornJSX.default({
41621
41685
  autoCloseVoidElements: this.props.autoCloseVoidElements
41622
41686
  }));
41623
- const wrappedJsx = `<root>${jsx68}</root>`;
41687
+ const wrappedJsx = `<root>${jsx76}</root>`;
41624
41688
  let parsed = [];
41625
41689
  try {
41626
41690
  parsed = parser.parse(wrappedJsx, { ecmaVersion: "latest" });
@@ -41918,8 +41982,8 @@ var JsxParser = class extends import_react125.default.Component {
41918
41982
  });
41919
41983
  }
41920
41984
  render() {
41921
- const jsx68 = (this.props.jsx || "").trim().replace(/<!DOCTYPE([^>]*)>/g, "");
41922
- this.ParsedChildren = __privateGet(this, _parseJSX).call(this, jsx68);
41985
+ const jsx76 = (this.props.jsx || "").trim().replace(/<!DOCTYPE([^>]*)>/g, "");
41986
+ this.ParsedChildren = __privateGet(this, _parseJSX).call(this, jsx76);
41923
41987
  const className2 = [.../* @__PURE__ */ new Set(["jsx-parser", ...String(this.props.className).split(" ")])].filter(Boolean).join(" ");
41924
41988
  return this.props.renderInWrapper ? /* @__PURE__ */ import_react125.default.createElement("div", {
41925
41989
  className: className2
@@ -41955,7 +42019,7 @@ __publicField(JsxParser, "defaultProps", {
41955
42019
  var source_default = JsxParser;
41956
42020
 
41957
42021
  // src/utils/tween.ts
41958
- var React26 = __toESM(require_react(), 1);
42022
+ var React27 = __toESM(require_react(), 1);
41959
42023
  var EASING_MAP = {
41960
42024
  linear: void 0,
41961
42025
  ease: Easing.ease,
@@ -41976,16 +42040,16 @@ function hexToNumber(hex3) {
41976
42040
  function lerp(a2, b3, t) {
41977
42041
  return a2 + (b3 - a2) * t;
41978
42042
  }
41979
- function useTweenBindings(action2) {
42043
+ function useTweenBindings(action) {
41980
42044
  const frame = useCurrentFrame();
41981
- const actionDurationFrames = Math.max(1, Math.floor(((action2.end ?? 1) - (action2.start ?? 0)) * 30));
41982
- const cacheRef = React26.useRef(/* @__PURE__ */ new Map());
41983
- const prevFrameRef = React26.useRef(frame);
42045
+ const actionDurationFrames = Math.max(1, Math.floor(((action.end ?? 1) - (action.start ?? 0)) * 30));
42046
+ const cacheRef = React27.useRef(/* @__PURE__ */ new Map());
42047
+ const prevFrameRef = React27.useRef(frame);
41984
42048
  if (prevFrameRef.current !== frame) {
41985
42049
  cacheRef.current = /* @__PURE__ */ new Map();
41986
42050
  prevFrameRef.current = frame;
41987
42051
  }
41988
- const tween = React26.useCallback(
42052
+ const tween = React27.useCallback(
41989
42053
  (from, to, easing) => {
41990
42054
  const key = `${from},${to},${easing || "linear"}`;
41991
42055
  const cached2 = cacheRef.current.get(key);
@@ -42027,18 +42091,18 @@ function useTweenBindings(action2) {
42027
42091
  // src/types/Component.tsx
42028
42092
  var import_jsx_runtime81 = __toESM(require_jsx_runtime(), 1);
42029
42093
  function TweenedJsxParser({
42030
- jsx: jsx68,
42094
+ jsx: jsx76,
42031
42095
  components,
42032
42096
  data: data2,
42033
- action: action2
42097
+ action
42034
42098
  }) {
42035
- const tweenBindings = useTweenBindings(action2);
42099
+ const tweenBindings = useTweenBindings(action);
42036
42100
  return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(import_jsx_runtime81.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42037
42101
  source_default,
42038
42102
  {
42039
42103
  components,
42040
- bindings: React27.useMemo(() => ({ ...data2, ...tweenBindings }), [data2, tweenBindings]),
42041
- jsx: jsx68,
42104
+ bindings: React30.useMemo(() => ({ ...data2, ...tweenBindings }), [data2, tweenBindings]),
42105
+ jsx: jsx76,
42042
42106
  renderInWrapper: false,
42043
42107
  blacklistedAttrs: [],
42044
42108
  disableKeyGeneration: true,
@@ -42048,10 +42112,10 @@ function TweenedJsxParser({
42048
42112
  }
42049
42113
  function ComponentLeaf({ stream: stream2 }) {
42050
42114
  const { fps } = useVideoConfig();
42051
- const { components } = React27.useContext(ComposeContext);
42115
+ const { components } = React30.useContext(ComposeContext);
42052
42116
  const eventCtx = useEventContext();
42053
- const [eventState, setEventState] = React27.useState({});
42054
- React27.useLayoutEffect(() => {
42117
+ const [eventState, setEventState] = React30.useState({});
42118
+ React30.useLayoutEffect(() => {
42055
42119
  if (stream2.id) {
42056
42120
  eventCtx.register(
42057
42121
  stream2.id,
@@ -42060,42 +42124,39 @@ function ComponentLeaf({ stream: stream2 }) {
42060
42124
  return () => eventCtx.unregister(stream2.id);
42061
42125
  }
42062
42126
  }, [stream2.id, eventCtx]);
42063
- const bindings = React27.useMemo(
42127
+ const bindings = React30.useMemo(
42064
42128
  () => ({ ...components, ...stream2.data, ...eventState }),
42065
42129
  [stream2.data, components, eventState]
42066
42130
  );
42067
42131
  if (!stream2.jsx) return null;
42068
- return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(import_jsx_runtime81.Fragment, { children: stream2.actions.map((a2) => {
42069
- const start = a2.start ?? 0;
42070
- const end = a2.end ?? start + 1;
42071
- const durFrames = Math.max(1, Math.floor(fps * (end - start)));
42072
- return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42073
- Sequence,
42074
- {
42075
- durationInFrames: durFrames,
42076
- from: Math.floor(fps * start),
42077
- layout: "none",
42078
- children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42079
- EventAwareComponent,
42080
- {
42081
- jsx: stream2.jsx,
42082
- components,
42083
- data: bindings,
42084
- action: a2,
42085
- durFrames,
42086
- on: stream2.on
42087
- }
42088
- )
42089
- },
42090
- a2.id
42091
- );
42092
- }) });
42132
+ const start = stream2.start ?? 0;
42133
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
42134
+ const durFrames = Math.max(1, Math.floor(fps * (end - start)));
42135
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42136
+ Sequence,
42137
+ {
42138
+ durationInFrames: durFrames,
42139
+ from: Math.floor(fps * start),
42140
+ layout: "none",
42141
+ children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42142
+ EventAwareComponent,
42143
+ {
42144
+ jsx: stream2.jsx,
42145
+ components,
42146
+ data: bindings,
42147
+ action: { start, end },
42148
+ durFrames,
42149
+ on: stream2.on
42150
+ }
42151
+ )
42152
+ }
42153
+ );
42093
42154
  }
42094
42155
  function EventAwareComponent({
42095
- jsx: jsx68,
42156
+ jsx: jsx76,
42096
42157
  components,
42097
42158
  data: data2,
42098
- action: action2,
42159
+ action,
42099
42160
  durFrames,
42100
42161
  on
42101
42162
  }) {
@@ -42103,10 +42164,10 @@ function EventAwareComponent({
42103
42164
  return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
42104
42165
  TweenedJsxParser,
42105
42166
  {
42106
- jsx: jsx68,
42167
+ jsx: jsx76,
42107
42168
  components,
42108
42169
  data: data2,
42109
- action: action2
42170
+ action
42110
42171
  }
42111
42172
  );
42112
42173
  }
@@ -42120,43 +42181,40 @@ function resolveAudioSrc2(src) {
42120
42181
  function RhythmLeaf({ stream: stream2 }) {
42121
42182
  const { fps } = useVideoConfig();
42122
42183
  const environment = useRemotionEnvironment();
42123
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
42184
+ const start = stream2.start ?? 0;
42185
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
42186
+ const totalDur = stream2.durationInSeconds ?? end;
42124
42187
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
42125
42188
  if (!stream2.src || environment.isStudio) return null;
42126
42189
  const resolvedSrc = resolveAudioSrc2(stream2.src);
42127
- return /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(import_jsx_runtime82.Fragment, { children: stream2.actions.map((a2) => {
42128
- const start = a2.start ?? 0;
42129
- const end = a2.end ?? start + 1;
42130
- const volume = a2.volume ?? stream2.volume ?? 1;
42131
- return /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
42132
- Sequence,
42133
- {
42134
- name: stream2.src ?? "rhythm",
42135
- durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
42136
- from: Math.floor(fps * start),
42137
- layout: "none",
42138
- showInTimeline: false,
42139
- children: /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
42140
- Audio,
42141
- {
42142
- src: resolvedSrc,
42143
- muted: volume === 0,
42144
- volume,
42145
- loop: true,
42146
- showInTimeline: false
42147
- }
42148
- )
42149
- },
42150
- a2.id
42151
- );
42152
- }) });
42190
+ const volume = stream2.volume ?? 1;
42191
+ return /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
42192
+ Sequence,
42193
+ {
42194
+ name: stream2.src ?? "rhythm",
42195
+ durationInFrames: Math.max(1, Math.floor(fps * (end - start))),
42196
+ from: Math.floor(fps * start),
42197
+ layout: "none",
42198
+ showInTimeline: false,
42199
+ children: /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
42200
+ Audio,
42201
+ {
42202
+ src: resolvedSrc,
42203
+ muted: volume === 0,
42204
+ volume,
42205
+ loop: true,
42206
+ showInTimeline: false
42207
+ }
42208
+ )
42209
+ }
42210
+ );
42153
42211
  }
42154
42212
 
42155
42213
  // src/types/Map.tsx
42156
42214
  var import_react127 = __toESM(require_react(), 1);
42157
42215
 
42158
42216
  // node_modules/@vis.gl/react-google-maps/dist/index.modern.mjs
42159
- var React30 = __toESM(require_react(), 1);
42217
+ var React35 = __toESM(require_react(), 1);
42160
42218
  var import_react126 = __toESM(require_react(), 1);
42161
42219
  var import_react_dom3 = __toESM(require_react_dom(), 1);
42162
42220
  var import_fast_deep_equal = __toESM(require_fast_deep_equal(), 1);
@@ -42343,8 +42401,8 @@ function useMap3DInstances() {
42343
42401
  function useGoogleMapsApiLoader(props) {
42344
42402
  const { onLoad, onError, apiKey, version: version4, libraries = [], region, language, authReferrerPolicy, channel, solutionChannel, fetchAppCheckToken } = props;
42345
42403
  const [status, setStatus] = (0, import_react126.useState)(loadingStatus);
42346
- const [loadedLibraries, addLoadedLibrary] = (0, import_react126.useReducer)((loadedLibraries2, action2) => {
42347
- return loadedLibraries2[action2.name] ? loadedLibraries2 : Object.assign(Object.assign({}, loadedLibraries2), { [action2.name]: action2.value });
42404
+ const [loadedLibraries, addLoadedLibrary] = (0, import_react126.useReducer)((loadedLibraries2, action) => {
42405
+ return loadedLibraries2[action.name] ? loadedLibraries2 : Object.assign(Object.assign({}, loadedLibraries2), { [action.name]: action.value });
42348
42406
  }, {});
42349
42407
  const currentSerializedParams = (0, import_react126.useMemo)(() => {
42350
42408
  const params = {
@@ -43100,8 +43158,8 @@ function useMapsLibrary(name) {
43100
43158
  return (ctx === null || ctx === void 0 ? void 0 : ctx.loadedLibraries[name]) || null;
43101
43159
  }
43102
43160
  var _a;
43103
- var { useLayoutEffect: useLayoutEffect16, useRef: useRef31 } = React30;
43104
- var useBeforeEffect = (_a = React30.useInsertionEffect) !== null && _a !== void 0 ? _a : useLayoutEffect16;
43161
+ var { useLayoutEffect: useLayoutEffect16, useRef: useRef31 } = React35;
43162
+ var useBeforeEffect = (_a = React35.useInsertionEffect) !== null && _a !== void 0 ? _a : useLayoutEffect16;
43105
43163
  function forbiddenInRender() {
43106
43164
  throw new Error("useEffectEvent: invalid call during rendering.");
43107
43165
  }
@@ -44476,55 +44534,59 @@ Rectangle.displayName = "Rectangle";
44476
44534
 
44477
44535
  // src/types/Map.tsx
44478
44536
  var import_jsx_runtime83 = __toESM(require_jsx_runtime(), 1);
44479
- var GM_API_KEY = "AIzaSyC6x4jghg5ZggV5VTThu9JE4DwX9NlbN9U";
44537
+ function resolveApiKey(stream2) {
44538
+ if (stream2.googleMapsApiKey) return stream2.googleMapsApiKey;
44539
+ if (typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.GOOGLE_MAPS_API_KEY) {
44540
+ return process.env.GOOGLE_MAPS_API_KEY;
44541
+ }
44542
+ return "";
44543
+ }
44480
44544
  function MapLeaf({ stream: stream2 }) {
44481
44545
  const { fps } = useVideoConfig();
44482
44546
  const waypoints = stream2.waypoints ?? [];
44483
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
44547
+ const start = stream2.start ?? 0;
44548
+ const end = stream2.end ?? start + (stream2.duration ?? 1);
44549
+ const totalDur = stream2.durationInSeconds ?? end;
44550
+ const apiKey = resolveApiKey(stream2);
44484
44551
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
44485
44552
  if (waypoints.length === 0) return null;
44486
- return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(import_jsx_runtime83.Fragment, { children: stream2.actions?.map((a2, i3) => {
44487
- const start = a2.start ?? 0;
44488
- const end = a2.end ?? start + 1;
44489
- const durFrames = Math.max(1, Math.floor(fps * (end - start)));
44490
- const center = stream2.center ?? { lat: waypoints[0].lat, lng: waypoints[0].lng };
44491
- const zoom = stream2.zoom ?? 10;
44492
- const mapType = stream2.mapType ?? "roadmap";
44493
- const travelMode = stream2.travelMode ?? "DRIVING";
44494
- const markerEmoji = stream2.routeMarker ?? "\u{1F697}";
44495
- return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44496
- Sequence,
44497
- {
44498
- durationInFrames: durFrames,
44499
- from: Math.floor(fps * start),
44500
- layout: "none",
44501
- children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(APIProvider, { apiKey: GM_API_KEY, children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44502
- Map2,
44503
- {
44504
- mapId: String(stream2.id ?? i3),
44505
- defaultCenter: center,
44506
- defaultZoom: zoom,
44507
- defaultOptions: {
44508
- mapTypeId: mapType,
44509
- disableDefaultUI: true,
44510
- zoomControl: false
44511
- },
44512
- style: { width: "100%", height: "100%", position: "absolute" },
44513
- children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44514
- RouteWithMarker,
44515
- {
44516
- waypoints,
44517
- travelMode,
44518
- markerEmoji,
44519
- actionDuration: end - start
44520
- }
44521
- )
44522
- }
44523
- ) })
44524
- },
44525
- a2.id ?? i3
44526
- );
44527
- }) });
44553
+ const durFrames = Math.max(1, Math.floor(fps * (end - start)));
44554
+ const center = stream2.center ?? { lat: waypoints[0].lat, lng: waypoints[0].lng };
44555
+ const zoom = stream2.zoom ?? 10;
44556
+ const mapType = stream2.mapType ?? "roadmap";
44557
+ const travelMode = stream2.travelMode ?? "DRIVING";
44558
+ const markerEmoji = stream2.routeMarker ?? "\u{1F697}";
44559
+ return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44560
+ Sequence,
44561
+ {
44562
+ durationInFrames: durFrames,
44563
+ from: Math.floor(fps * start),
44564
+ layout: "none",
44565
+ children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(APIProvider, { apiKey, children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44566
+ Map2,
44567
+ {
44568
+ mapId: String(stream2.id ?? "map"),
44569
+ defaultCenter: center,
44570
+ defaultZoom: zoom,
44571
+ defaultOptions: {
44572
+ mapTypeId: mapType,
44573
+ disableDefaultUI: true,
44574
+ zoomControl: false
44575
+ },
44576
+ style: { width: "100%", height: "100%", position: "absolute" },
44577
+ children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
44578
+ RouteWithMarker,
44579
+ {
44580
+ waypoints,
44581
+ travelMode,
44582
+ markerEmoji,
44583
+ actionDuration: end - start
44584
+ }
44585
+ )
44586
+ }
44587
+ ) })
44588
+ }
44589
+ );
44528
44590
  }
44529
44591
  function RouteWithMarker({
44530
44592
  waypoints,
@@ -44647,7 +44709,7 @@ function getCurrentStep(leg, currentInSecond) {
44647
44709
  }
44648
44710
 
44649
44711
  // src/types/Include.tsx
44650
- var React39 = __toESM(require_react(), 1);
44712
+ var React41 = __toESM(require_react(), 1);
44651
44713
  var import_jsx_runtime84 = __toESM(require_jsx_runtime(), 1);
44652
44714
  var ASPECT_DIMS = {
44653
44715
  "16x9": { width: 1920, height: 1080 },
@@ -44666,10 +44728,10 @@ function resolveIncludeSrc(src) {
44666
44728
  return staticFile(src);
44667
44729
  }
44668
44730
  function useSubVideoRegistry(importsUrl) {
44669
- const [registry2, setRegistry] = React39.useState(null);
44670
- const [loaded, setLoaded] = React39.useState(false);
44671
- const handleRef = React39.useRef(null);
44672
- React39.useEffect(() => {
44731
+ const [registry2, setRegistry] = React41.useState(null);
44732
+ const [loaded, setLoaded] = React41.useState(false);
44733
+ const handleRef = React41.useRef(null);
44734
+ React41.useEffect(() => {
44673
44735
  if (!importsUrl) {
44674
44736
  setRegistry(null);
44675
44737
  setLoaded(true);
@@ -44701,18 +44763,21 @@ function useSubVideoRegistry(importsUrl) {
44701
44763
  }
44702
44764
  function IncludeLeaf({ stream: stream2 }) {
44703
44765
  const { fps: parentFps, width: parentWidth, height: parentHeight } = useVideoConfig();
44704
- const parentCompose = React39.useContext(ComposeContext);
44766
+ const parentCompose = React41.useContext(ComposeContext);
44705
44767
  const { Container } = parentCompose;
44706
- const parentAudio = React39.useContext(AudioContext2);
44707
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
44768
+ const parentAudio = React41.useContext(AudioContext2);
44769
+ const start = stream2.start ?? 0;
44770
+ const end = stream2.end ?? (stream2.durationInSeconds ?? start + (stream2.duration ?? 1));
44771
+ const totalDur = stream2.durationInSeconds ?? end;
44708
44772
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * parentFps)));
44709
- if (!stream2.actions?.length) return null;
44710
- const [externalData, setExternalData] = React39.useState(null);
44711
- const [loadError, setLoadError] = React39.useState(null);
44712
- const [handle] = React39.useState(
44773
+ const hasContent = !!stream2.src || (stream2.children?.length ?? 0) > 0;
44774
+ if (!hasContent) return null;
44775
+ const [externalData, setExternalData] = React41.useState(null);
44776
+ const [loadError, setLoadError] = React41.useState(null);
44777
+ const [handle] = React41.useState(
44713
44778
  () => stream2.src ? delayRender(`Loading include: ${stream2.src}`) : null
44714
44779
  );
44715
- React39.useEffect(() => {
44780
+ React41.useEffect(() => {
44716
44781
  if (!stream2.src || !handle) return;
44717
44782
  let active = true;
44718
44783
  const url2 = resolveIncludeSrc(stream2.src);
@@ -44736,18 +44801,18 @@ function IncludeLeaf({ stream: stream2 }) {
44736
44801
  active = false;
44737
44802
  };
44738
44803
  }, [stream2.src, handle]);
44739
- React39.useMemo(() => {
44804
+ React41.useMemo(() => {
44740
44805
  if (!stream2.src) {
44741
44806
  getDurationInSeconds(stream2, true);
44742
44807
  }
44743
44808
  }, [stream2, stream2.src]);
44744
- const subImportsUrl = React39.useMemo(() => {
44809
+ const subImportsUrl = React41.useMemo(() => {
44745
44810
  if (!externalData) return void 0;
44746
44811
  const root2 = externalData.root ?? externalData;
44747
44812
  return root2?.imports;
44748
44813
  }, [externalData]);
44749
44814
  const { registry: subRegistry, loaded: subRegistryLoaded } = useSubVideoRegistry(subImportsUrl);
44750
- const mergedCompose = React39.useMemo(() => {
44815
+ const mergedCompose = React41.useMemo(() => {
44751
44816
  const parentComponents = parentCompose.components;
44752
44817
  if (!subRegistry) return parentCompose;
44753
44818
  return {
@@ -44758,11 +44823,11 @@ function IncludeLeaf({ stream: stream2 }) {
44758
44823
  }
44759
44824
  };
44760
44825
  }, [parentCompose, subRegistry]);
44761
- const audioCtx = React39.useMemo(
44826
+ const audioCtx = React41.useMemo(
44762
44827
  () => ({ id: stream2.id, foreground: true, parent: parentAudio }),
44763
44828
  [stream2.id, parentAudio]
44764
44829
  );
44765
- const renderExternalContent = React39.useCallback(() => {
44830
+ const renderExternalContent = React41.useCallback(() => {
44766
44831
  if (!externalData) return null;
44767
44832
  if (loadError) {
44768
44833
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)("div", { style: { color: "#ff4444", fontSize: 24, padding: 40 }, children: [
@@ -44779,12 +44844,12 @@ function IncludeLeaf({ stream: stream2 }) {
44779
44844
  vj.bgm && /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Audio, { src: vj.bgm.src, volume: vj.bgm.baseVolume }),
44780
44845
  vj.scenes.map((scene2) => {
44781
44846
  const startFrame = Math.round((scene2.start ?? 0) * vjFps);
44782
- const durFrames = Math.round((scene2.duration ?? 1) * vjFps);
44847
+ const durFrames2 = Math.round((scene2.duration ?? 1) * vjFps);
44783
44848
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(
44784
44849
  Sequence,
44785
44850
  {
44786
44851
  from: startFrame,
44787
- durationInFrames: durFrames,
44852
+ durationInFrames: durFrames2,
44788
44853
  name: `${scene2.id}:${scene2.component}`,
44789
44854
  children: [
44790
44855
  /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(
@@ -44855,82 +44920,72 @@ function IncludeLeaf({ stream: stream2 }) {
44855
44920
  }
44856
44921
  );
44857
44922
  }, [externalData, loadError, parentFps, parentWidth, parentHeight]);
44858
- const renderAction = (a2) => {
44859
- const start = a2.start ?? 0;
44860
- const end = a2.end ?? (stream2.durationInSeconds ?? start + 1);
44861
- const dur = Math.max(0.1, end - start);
44862
- const durFrames = Math.max(1, Math.floor(parentFps * dur));
44863
- if (!stream2.src && stream2.children?.length) {
44864
- return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44865
- Sequence,
44866
- {
44867
- durationInFrames: durFrames,
44868
- from: Math.floor(parentFps * start),
44869
- layout: "none",
44870
- showInTimeline: false,
44871
- children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44872
- Container,
44873
- {
44874
- id: stream2.id,
44875
- type: "include",
44876
- style: {
44877
- ...cssJS(stream2.style),
44878
- width: parentWidth,
44879
- height: parentHeight,
44880
- overflow: "hidden",
44881
- position: "relative"
44882
- },
44883
- className: `include ${toClassName(stream2.id ?? "")}`,
44884
- children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(FolderLeaf, { stream: stream2 })
44885
- }
44886
- )
44923
+ const dur = Math.max(0.1, end - start);
44924
+ const durFrames = Math.max(1, Math.floor(parentFps * dur));
44925
+ let innerNode;
44926
+ if (!stream2.src && stream2.children?.length) {
44927
+ innerNode = /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44928
+ Container,
44929
+ {
44930
+ id: stream2.id,
44931
+ type: "include",
44932
+ style: {
44933
+ ...cssJS(stream2.style),
44934
+ width: parentWidth,
44935
+ height: parentHeight,
44936
+ overflow: "hidden",
44937
+ position: "relative"
44887
44938
  },
44888
- a2.id
44889
- );
44890
- }
44891
- return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44892
- Sequence,
44939
+ className: `include ${toClassName(stream2.id ?? "")}`,
44940
+ children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(FolderLeaf, { stream: stream2 })
44941
+ }
44942
+ );
44943
+ } else if (externalData || loadError) {
44944
+ innerNode = /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44945
+ "div",
44893
44946
  {
44894
- durationInFrames: durFrames,
44895
- from: Math.floor(parentFps * start),
44896
- layout: "none",
44897
- showInTimeline: false,
44898
- children: externalData || loadError ? /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44899
- "div",
44900
- {
44901
- style: {
44902
- width: parentWidth,
44903
- height: parentHeight,
44904
- overflow: "hidden",
44905
- position: "relative"
44906
- },
44907
- children: renderExternalContent()
44908
- }
44909
- ) : /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(
44910
- "div",
44911
- {
44912
- style: {
44913
- width: parentWidth,
44914
- height: parentHeight,
44915
- display: "flex",
44916
- alignItems: "center",
44917
- justifyContent: "center",
44918
- color: "#666",
44919
- fontSize: 20,
44920
- fontFamily: "monospace"
44921
- },
44922
- children: [
44923
- "Loading\u2026 ",
44924
- stream2.src
44925
- ]
44926
- }
44927
- )
44928
- },
44929
- a2.id
44947
+ style: {
44948
+ width: parentWidth,
44949
+ height: parentHeight,
44950
+ overflow: "hidden",
44951
+ position: "relative"
44952
+ },
44953
+ children: renderExternalContent()
44954
+ }
44930
44955
  );
44931
- };
44956
+ } else {
44957
+ innerNode = /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(
44958
+ "div",
44959
+ {
44960
+ style: {
44961
+ width: parentWidth,
44962
+ height: parentHeight,
44963
+ display: "flex",
44964
+ alignItems: "center",
44965
+ justifyContent: "center",
44966
+ color: "#666",
44967
+ fontSize: 20,
44968
+ fontFamily: "monospace"
44969
+ },
44970
+ children: [
44971
+ "Loading\u2026 ",
44972
+ stream2.src
44973
+ ]
44974
+ }
44975
+ );
44976
+ }
44977
+ const sequence = /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
44978
+ Sequence,
44979
+ {
44980
+ durationInFrames: durFrames,
44981
+ from: Math.floor(parentFps * start),
44982
+ layout: "none",
44983
+ showInTimeline: false,
44984
+ children: innerNode
44985
+ }
44986
+ );
44932
44987
  const needsNestedContext = subRegistry !== null && subRegistry !== parentCompose.components;
44933
- const inner2 = /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(AudioContext2.Provider, { value: audioCtx, children: stream2.actions.map(renderAction) });
44988
+ const inner2 = /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(AudioContext2.Provider, { value: audioCtx, children: sequence });
44934
44989
  if (needsNestedContext && subRegistryLoaded) {
44935
44990
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(ComposeContext.Provider, { value: mergedCompose, children: inner2 });
44936
44991
  }
@@ -44938,14 +44993,14 @@ function IncludeLeaf({ stream: stream2 }) {
44938
44993
  }
44939
44994
 
44940
44995
  // src/types/Scene.tsx
44941
- var React41 = __toESM(require_react(), 1);
44996
+ var React43 = __toESM(require_react(), 1);
44942
44997
  var import_jsx_runtime85 = __toESM(require_jsx_runtime(), 1);
44943
44998
  function SceneLeaf({ stream: stream2 }) {
44944
- const { Container } = React41.useContext(ComposeContext);
44945
- const parentAudio = React41.useContext(AudioContext2);
44999
+ const { Container } = React43.useContext(ComposeContext);
45000
+ const parentAudio = React43.useContext(AudioContext2);
44946
45001
  const totalDur = stream2.durationInSeconds ?? 1;
44947
45002
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * 30)));
44948
- const audioCtx = React41.useMemo(
45003
+ const audioCtx = React43.useMemo(
44949
45004
  () => ({ id: stream2.id, parent: parentAudio }),
44950
45005
  [stream2.id, parentAudio]
44951
45006
  );
@@ -44962,7 +45017,7 @@ function SceneLeaf({ stream: stream2 }) {
44962
45017
  }
44963
45018
 
44964
45019
  // src/types/Effect.tsx
44965
- var React43 = __toESM(require_react(), 1);
45020
+ var React44 = __toESM(require_react(), 1);
44966
45021
 
44967
45022
  // src/types/keyframes.ts
44968
45023
  function parseKeyframeData(data2) {
@@ -45355,47 +45410,42 @@ function EffectWrapper({
45355
45410
  }) {
45356
45411
  const frame = useCurrentFrame();
45357
45412
  const { fps, width, height } = useVideoConfig();
45358
- const totalDur = stream2.durationInSeconds ?? stream2.actions[0]?.end ?? 1;
45413
+ const startSec = stream2.start ?? 0;
45414
+ const endSec = stream2.end ?? startSec + (stream2.duration ?? 1);
45415
+ const totalDur = stream2.durationInSeconds ?? endSec;
45359
45416
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
45360
- const actions = stream2.actions ?? [];
45361
- const styles = React43.useMemo(() => {
45362
- const result = [];
45363
- for (const action2 of actions) {
45364
- const start = Math.ceil(action2.start * fps);
45365
- const end = Math.ceil(action2.end * fps);
45366
- const durationInFrames = end - start;
45367
- if (durationInFrames <= 0) continue;
45368
- const animation = stream2.animation;
45369
- const timingFn = stream2.animationTimingFunction;
45370
- const iterCount = stream2.animationIterationCount ?? 1;
45371
- const style2 = cssJS(action2.style) ?? {};
45372
- let currentFrame = frame;
45373
- if (iterCount > 0 && durationInFrames > 0) {
45374
- const iteration = Math.floor((frame - start) / durationInFrames);
45375
- if (iteration < iterCount) {
45376
- currentFrame = start + (frame - start) % durationInFrames;
45377
- }
45378
- }
45379
- if (currentFrame >= start && currentFrame < end) {
45380
- const actionFrame = currentFrame - start;
45381
- if (animation) {
45382
- const config2 = resolveAnimation(animation, stream2.customKeyframes);
45383
- if (config2) {
45384
- const animStyle = interpolateKeyframes(config2, actionFrame, {
45385
- fps,
45386
- durationInSeconds: durationInFrames / fps,
45387
- timingFunction: timingFn
45388
- });
45389
- if (animStyle) Object.assign(style2, animStyle);
45390
- }
45391
- }
45392
- if (Object.keys(style2).length > 0) {
45393
- result.push(style2);
45417
+ const styles = React44.useMemo(() => {
45418
+ const start = Math.ceil(startSec * fps);
45419
+ const end = Math.ceil(endSec * fps);
45420
+ const durationInFrames = end - start;
45421
+ if (durationInFrames <= 0) return [];
45422
+ const animation = stream2.animation;
45423
+ const timingFn = stream2.animationTimingFunction;
45424
+ const iterCount = stream2.animationIterationCount ?? 1;
45425
+ const style2 = cssJS(stream2.style) ?? {};
45426
+ let currentFrame = frame;
45427
+ if (iterCount > 0 && durationInFrames > 0) {
45428
+ const iteration = Math.floor((frame - start) / durationInFrames);
45429
+ if (iteration < iterCount) {
45430
+ currentFrame = start + (frame - start) % durationInFrames;
45431
+ }
45432
+ }
45433
+ if (currentFrame >= start && currentFrame < end) {
45434
+ const actionFrame = currentFrame - start;
45435
+ if (animation) {
45436
+ const config2 = resolveAnimation(animation, stream2.customKeyframes);
45437
+ if (config2) {
45438
+ const animStyle = interpolateKeyframes(config2, actionFrame, {
45439
+ fps,
45440
+ durationInSeconds: durationInFrames / fps,
45441
+ timingFunction: timingFn
45442
+ });
45443
+ if (animStyle) Object.assign(style2, animStyle);
45394
45444
  }
45395
45445
  }
45396
45446
  }
45397
- return result;
45398
- }, [frame, fps, actions, stream2.animation, stream2.animationTimingFunction, stream2.animationIterationCount, stream2.customKeyframes]);
45447
+ return Object.keys(style2).length > 0 ? [style2] : [];
45448
+ }, [frame, fps, startSec, endSec, stream2.animation, stream2.animationTimingFunction, stream2.animationIterationCount, stream2.customKeyframes, stream2.style]);
45399
45449
  if (styles.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_jsx_runtime86.Fragment, { children });
45400
45450
  return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
45401
45451
  "div",
@@ -45430,8 +45480,8 @@ var NotSeries = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
45430
45480
  NotSeries.Sequence = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_jsx_runtime87.Fragment, { children });
45431
45481
  function FolderLeaf({ stream: stream2 }) {
45432
45482
  const { fps, width, height } = useVideoConfig();
45433
- const { Container } = React44.useContext(ComposeContext);
45434
- const parentAudio = React44.useContext(AudioContext2);
45483
+ const { Container } = React45.useContext(ComposeContext);
45484
+ const parentAudio = React45.useContext(AudioContext2);
45435
45485
  const totalDur = stream2.durationInSeconds ?? 1;
45436
45486
  useFrameEvents(stream2.on, Math.max(1, Math.floor(totalDur * fps)));
45437
45487
  const isSeries = !!stream2.isSeries;
@@ -45443,11 +45493,11 @@ function FolderLeaf({ stream: stream2 }) {
45443
45493
  const seriesChildren = isSeries ? visibleChildren.filter((c3) => !c3.isBackground) : visibleChildren;
45444
45494
  const allAudio = seriesChildren.length > 0 && seriesChildren.every((c3) => c3.type === "audio");
45445
45495
  const effectiveTransition = allAudio ? void 0 : transition;
45446
- const TypedSeries = React44.useMemo(() => {
45496
+ const TypedSeries = React45.useMemo(() => {
45447
45497
  if (!isSeries) return NotSeries;
45448
45498
  return effectiveTransition ? TransitionSeries : Series;
45449
45499
  }, [isSeries, effectiveTransition]);
45450
- const transEl = React44.useMemo(() => {
45500
+ const transEl = React45.useMemo(() => {
45451
45501
  if (!isSeries || !effectiveTransition) return null;
45452
45502
  const presentation = TransitionPresets[transition]?.(
45453
45503
  transition === "clockWipe" ? { width, height } : void 0
@@ -45465,7 +45515,7 @@ function FolderLeaf({ stream: stream2 }) {
45465
45515
  const durFrames = Math.max(1, Math.floor(dur * fps));
45466
45516
  const SequenceWrap = TypedSeries.Sequence ?? Sequence;
45467
45517
  const isLeaf = child.type !== "folder" && child.type !== "root" && child.type !== "effect";
45468
- const childContent = isLeaf ? React44.createElement(Leaves[child.type] ?? (() => null), { stream: child }) : child.type === "effect" ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(EffectWrapper, { stream: child, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(FolderLeaf, { stream: child }) }) : React44.createElement(FolderLeaf, { stream: child });
45518
+ const childContent = isLeaf ? React45.createElement(Leaves[child.type] ?? (() => null), { stream: child }) : child.type === "effect" ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(EffectWrapper, { stream: child, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(FolderLeaf, { stream: child }) }) : React45.createElement(FolderLeaf, { stream: child });
45469
45519
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(SequenceWrap, { durationInFrames: durFrames, layout: "none", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
45470
45520
  Container,
45471
45521
  {
@@ -45481,7 +45531,7 @@ function FolderLeaf({ stream: stream2 }) {
45481
45531
  const dur = child.durationInSeconds ?? 0;
45482
45532
  const durFrames = Math.max(1, Math.floor(dur * fps));
45483
45533
  const isLeaf = child.type !== "folder" && child.type !== "root" && child.type !== "effect";
45484
- const childContent = isLeaf ? React44.createElement(Leaves[child.type] ?? (() => null), { stream: child }) : child.type === "effect" ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(EffectWrapper, { stream: child, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(FolderLeaf, { stream: child }) }) : React44.createElement(FolderLeaf, { stream: child });
45534
+ const childContent = isLeaf ? React45.createElement(Leaves[child.type] ?? (() => null), { stream: child }) : child.type === "effect" ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(EffectWrapper, { stream: child, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(FolderLeaf, { stream: child }) }) : React45.createElement(FolderLeaf, { stream: child });
45485
45535
  const wrapped = /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
45486
45536
  Container,
45487
45537
  {
@@ -45497,10 +45547,10 @@ function FolderLeaf({ stream: stream2 }) {
45497
45547
  }) });
45498
45548
  if (isSeries && transEl) {
45499
45549
  for (let i3 = 1; i3 < sequences.length; i3 += 2) {
45500
- sequences.splice(i3, 0, React44.cloneElement(transEl, { key: `t${i3}` }));
45550
+ sequences.splice(i3, 0, React45.cloneElement(transEl, { key: `t${i3}` }));
45501
45551
  }
45502
45552
  }
45503
- const audioCtx = React44.useMemo(
45553
+ const audioCtx = React45.useMemo(
45504
45554
  () => stream2.type !== "folder" ? { id: stream2.id, parent: parentAudio } : parentAudio,
45505
45555
  [stream2.id, stream2.type, parentAudio]
45506
45556
  );
@@ -45523,7 +45573,7 @@ function FolderLeaf({ stream: stream2 }) {
45523
45573
  }
45524
45574
 
45525
45575
  // src/types/Subtitle.tsx
45526
- var React45 = __toESM(require_react(), 1);
45576
+ var React46 = __toESM(require_react(), 1);
45527
45577
 
45528
45578
  // node_modules/remotion-subtitle/dist/index.esm.js
45529
45579
  var index_esm_exports = {};
@@ -45849,8 +45899,8 @@ function CueFrame({
45849
45899
  }) {
45850
45900
  const durationInFrames = Math.max(1, Math.floor((cue.endAt - cue.startFrom) * fps));
45851
45901
  const from = Math.floor(cue.startFrom * fps);
45852
- const captionText = React45.useMemo(() => supportHtml(cue.text), [cue.text]);
45853
- const textStyle = React45.useMemo(
45902
+ const captionText = React46.useMemo(() => supportHtml(cue.text), [cue.text]);
45903
+ const textStyle = React46.useMemo(
45854
45904
  () => ({
45855
45905
  ...DEFAULT_TEXT_STYLE,
45856
45906
  fontSize: subtitle.fontSize ?? DEFAULT_TEXT_STYLE.fontSize,
@@ -45863,12 +45913,12 @@ function CueFrame({
45863
45913
  }
45864
45914
  function SubtitleOverlay({ subtitle }) {
45865
45915
  const { fps } = useVideoConfig();
45866
- const [cues, setCues] = React45.useState(null);
45867
- const CaptionComponent = React45.useMemo(
45916
+ const [cues, setCues] = React46.useState(null);
45917
+ const CaptionComponent = React46.useMemo(
45868
45918
  () => resolveCaption(subtitle.type),
45869
45919
  [subtitle.type]
45870
45920
  );
45871
- React45.useEffect(() => {
45921
+ React46.useEffect(() => {
45872
45922
  const { src } = subtitle;
45873
45923
  if (src.includes("-->")) {
45874
45924
  setCues(parseVTT(src));
@@ -59686,17 +59736,6 @@ function date4(params) {
59686
59736
  config(en_default());
59687
59737
 
59688
59738
  // src/schema/index.ts
59689
- var action = external_exports.object({
59690
- id: external_exports.string().default(() => uid()),
59691
- start: external_exports.number().min(0).default(0).describe("seconds, relative to parent"),
59692
- end: external_exports.number().min(0).default(1).describe("seconds, relative to parent"),
59693
- startFrom: external_exports.number().optional().describe("trim seconds from source start"),
59694
- endAt: external_exports.number().optional().describe("trim seconds at source end"),
59695
- loop: external_exports.number().int().min(1).optional().describe(">1 = loop count"),
59696
- effectId: external_exports.string().optional(),
59697
- style: external_exports.string().optional().describe("inline css"),
59698
- volume: external_exports.number().min(0).max(1).optional()
59699
- });
59700
59739
  var eventSpec = external_exports.object({
59701
59740
  when: external_exports.string().describe("'start' | 'end' | '50%' | '2.5s'"),
59702
59741
  state: external_exports.string().describe("JS expression evaluated with registered components in scope")
@@ -59708,6 +59747,11 @@ var BaseShape = {
59708
59747
  style: external_exports.string().optional().describe("inline css"),
59709
59748
  visible: external_exports.boolean().default(true),
59710
59749
  isBackground: external_exports.boolean().optional(),
59750
+ start: external_exports.number().min(0).default(0).describe("seconds, relative to parent timeline (0 inside series parents)"),
59751
+ end: external_exports.number().min(0).optional().describe("seconds, relative to parent timeline \u2014 source of truth for duration"),
59752
+ duration: external_exports.number().min(0).optional().describe("convenience; normalized to end = start + duration when end is absent"),
59753
+ startFrom: external_exports.number().min(0).optional().describe("trim seconds from source start (video/audio)"),
59754
+ endAt: external_exports.number().min(0).optional().describe("trim seconds at source end (video/audio)"),
59711
59755
  durationInSeconds: external_exports.number().optional().describe("set by engine; do not edit by hand"),
59712
59756
  on: eventSpec.optional().describe("event that fires at a specific frame, mutating registered component state")
59713
59757
  };
@@ -59760,26 +59804,25 @@ var video = base.extend({
59760
59804
  playbackRate: external_exports.number().optional(),
59761
59805
  width: external_exports.number().default(1080),
59762
59806
  height: external_exports.number().default(1920),
59763
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59807
+ loop: external_exports.number().int().min(1).optional().describe(">1 = loop count")
59764
59808
  });
59765
59809
  var audio = base.extend({
59766
59810
  type: external_exports.literal("audio").default("audio"),
59767
59811
  src: external_exports.string().optional(),
59768
59812
  volume: external_exports.number().min(0).max(1).default(1),
59769
59813
  foreground: external_exports.boolean().optional().describe("ducks parent video audio while playing"),
59770
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59814
+ loop: external_exports.number().int().min(1).optional().describe(">1 = loop count"),
59815
+ speaker: external_exports.string().optional().describe("speaker name for multi-turn dialogue; set by resolveDialogue")
59771
59816
  });
59772
59817
  var image = base.extend({
59773
59818
  type: external_exports.literal("image").default("image"),
59774
59819
  src: external_exports.string().optional(),
59775
- fit: external_exports.enum(["contain", "cover", "fill"]).default("contain"),
59776
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59820
+ fit: external_exports.enum(["contain", "cover", "fill"]).default("contain")
59777
59821
  });
59778
59822
  var component = base.extend({
59779
59823
  type: external_exports.literal("component").default("component"),
59780
59824
  jsx: external_exports.string().describe("usage JSX expression compiled at runtime; tag names resolved from imports"),
59781
- data: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).optional().describe("extra variables (e.g. from ~~~md source code fences) available in JSX scope"),
59782
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59825
+ data: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).optional().describe("extra variables (e.g. from ~~~md source code fences) available in JSX scope")
59783
59826
  });
59784
59827
  var effect = base.extend({
59785
59828
  type: external_exports.literal("effect").default("effect"),
@@ -59787,23 +59830,20 @@ var effect = base.extend({
59787
59830
  animationTimingFunction: external_exports.enum(["linear", "ease", "ease-in", "ease-out", "ease-in-out"]).optional(),
59788
59831
  animationIterationCount: external_exports.number().default(1),
59789
59832
  customKeyframes: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.string())).optional().describe('inline keyframes: { "0": { opacity: "0" }, "100": { opacity: "1" } }'),
59790
- children: external_exports.array(external_exports.lazy(() => stream)).default(() => []),
59791
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59833
+ children: external_exports.array(external_exports.lazy(() => stream)).default(() => [])
59792
59834
  });
59793
59835
  var rhythm = base.extend({
59794
59836
  type: external_exports.literal("rhythm").default("rhythm"),
59795
59837
  src: external_exports.string().optional().describe("audio file for beat playback"),
59796
59838
  volume: external_exports.number().min(0).max(1).default(1),
59797
59839
  spots: external_exports.array(external_exports.number()).optional().describe("pre-computed beat timestamps in seconds"),
59798
- children: external_exports.array(external_exports.lazy(() => stream)).default(() => []),
59799
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59840
+ children: external_exports.array(external_exports.lazy(() => stream)).default(() => [])
59800
59841
  });
59801
59842
  var include = base.extend({
59802
59843
  type: external_exports.literal("include").default("include"),
59803
59844
  src: external_exports.string().optional().describe("path or URL to video JSON file (stream tree or scene-based)"),
59804
59845
  volume: external_exports.number().min(0).max(1).default(1),
59805
59846
  children: external_exports.array(external_exports.lazy(() => stream)).default(() => []),
59806
- actions: external_exports.array(action).min(1).default(() => [action.parse({})]),
59807
59847
  imports: external_exports.string().optional().describe("component bundle URL for sub-video (set by engine)")
59808
59848
  });
59809
59849
  var scene = base.extend({
@@ -59830,7 +59870,7 @@ var mapStream = base.extend({
59830
59870
  mapType: external_exports.enum(["roadmap", "satellite", "hybrid", "terrain"]).default("roadmap").describe("Google Maps style"),
59831
59871
  travelMode: external_exports.enum(["DRIVING", "WALKING", "BICYCLING", "TRANSIT"]).default("DRIVING").describe("Directions API travel mode"),
59832
59872
  routeMarker: external_exports.string().default("\u{1F697}").describe("emoji/character for the animated traveling marker"),
59833
- actions: external_exports.array(action).min(1).default(() => [action.parse({})])
59873
+ googleMapsApiKey: external_exports.string().optional().describe("injected by compiler from GOOGLE_MAPS_API_KEY env var")
59834
59874
  });
59835
59875
  var stream = external_exports.discriminatedUnion("type", [
59836
59876
  root,
@@ -59850,9 +59890,9 @@ var stream = external_exports.discriminatedUnion("type", [
59850
59890
  var import_jsx_runtime89 = __toESM(require_jsx_runtime(), 1);
59851
59891
  var DefaultContainer2 = ({ children, style: style2, className: className2 }) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: className2, style: { position: "absolute", inset: 0, ...style2 }, children });
59852
59892
  function useComponentRegistry(imports) {
59853
- const [registry2, setRegistry] = React46.useState(null);
59854
- const handleRef = React46.useRef(null);
59855
- React46.useEffect(() => {
59893
+ const [registry2, setRegistry] = React47.useState(null);
59894
+ const handleRef = React47.useRef(null);
59895
+ React47.useEffect(() => {
59856
59896
  if (!imports) {
59857
59897
  setRegistry(null);
59858
59898
  return;
@@ -59865,9 +59905,11 @@ function useComponentRegistry(imports) {
59865
59905
  if (!handleRef.current) {
59866
59906
  handleRef.current = delayRender("Loading component registry: " + imports);
59867
59907
  }
59908
+ const moduleUrl = /^(https?:|data:|blob:|\/)/.test(imports) ? imports : staticFile(imports);
59909
+ ensureSharedImportMap();
59868
59910
  import(
59869
59911
  /* webpackIgnore: true */
59870
- imports
59912
+ moduleUrl
59871
59913
  ).then((mod) => {
59872
59914
  setRegistry(mod.default ?? mod);
59873
59915
  if (handleRef.current) {
@@ -59889,7 +59931,7 @@ function useComponentRegistry(imports) {
59889
59931
  return registry2;
59890
59932
  }
59891
59933
  function MarkCut({ root: root2, compose, background = "#000" }) {
59892
- const parsed = React46.useMemo(() => {
59934
+ const parsed = React47.useMemo(() => {
59893
59935
  if (!root2) {
59894
59936
  return {
59895
59937
  id: "root",
@@ -59906,8 +59948,8 @@ function MarkCut({ root: root2, compose, background = "#000" }) {
59906
59948
  return root.parse(root2);
59907
59949
  }, [root2]);
59908
59950
  const registry2 = useComponentRegistry(parsed.imports);
59909
- React46.useMemo(() => getDurationInSeconds(parsed, true), [parsed]);
59910
- const value = React46.useMemo(
59951
+ React47.useMemo(() => getDurationInSeconds(parsed, true), [parsed]);
59952
+ const value = React47.useMemo(
59911
59953
  () => ({
59912
59954
  Container: compose?.Container ?? DefaultContainer2,
59913
59955
  onError: compose?.onError,
@@ -59922,11 +59964,452 @@ function MarkCut({ root: root2, compose, background = "#000" }) {
59922
59964
  ] }) }) });
59923
59965
  }
59924
59966
 
59967
+ // src/player/components/HeaderBar.tsx
59968
+ var React48 = __toESM(require_react(), 1);
59969
+ var import_jsx_runtime90 = __toESM(require_jsx_runtime(), 1);
59970
+ function HeaderBar({ mode, sceneInfo, editStatus, sseConnected }) {
59971
+ const handleClose = React48.useCallback(() => {
59972
+ navigator.sendBeacon("/api/shutdown", "{}");
59973
+ document.body.innerHTML = "<div style='display:flex;align-items:center;justify-content:center;height:100vh;background:#0a0a0a;color:#555;font-family:sans-serif;font-size:16px'>\u2B61 player closed \u2014 return to terminal</div>";
59974
+ }, []);
59975
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { id: "header", children: [
59976
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { style: { flex: 1, display: "flex", alignItems: "center", gap: 8 }, children: [
59977
+ mode === "label" && sceneInfo && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { id: "scene-info", children: sceneInfo }),
59978
+ mode === "edit" && editStatus && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { id: "edit-status", children: editStatus }),
59979
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
59980
+ "span",
59981
+ {
59982
+ id: "sse-indicator",
59983
+ title: sseConnected ? "Connected \u2014 auto-reload ready" : "Disconnected",
59984
+ style: {
59985
+ display: "inline-block",
59986
+ width: 8,
59987
+ height: 8,
59988
+ borderRadius: "50%",
59989
+ background: sseConnected ? "#4ade80" : "#555",
59990
+ flexShrink: 0
59991
+ }
59992
+ }
59993
+ )
59994
+ ] }),
59995
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("div", { id: "header-actions", children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("button", { id: "close-btn", title: "Close player and return to terminal", onClick: handleClose, children: "\u2715" }) })
59996
+ ] });
59997
+ }
59998
+
59999
+ // src/player/components/EditControls.tsx
60000
+ var React49 = __toESM(require_react(), 1);
60001
+ var import_jsx_runtime91 = __toESM(require_jsx_runtime(), 1);
60002
+ function EditControls({ onStatusChange, suppressReloadRef, currentTime, activeScene }) {
60003
+ const [busy, setBusy] = React49.useState(false);
60004
+ const inputRef = React49.useRef(null);
60005
+ const handleApplyEdit = React49.useCallback(
60006
+ async (text) => {
60007
+ if (!text || busy) return;
60008
+ setBusy(true);
60009
+ onStatusChange?.("\u231B editing...");
60010
+ if (inputRef.current) inputRef.current.value = "";
60011
+ if (suppressReloadRef) suppressReloadRef.current = true;
60012
+ try {
60013
+ const res = await fetch("/api/edit", {
60014
+ method: "POST",
60015
+ headers: { "Content-Type": "application/json" },
60016
+ body: JSON.stringify({ text, currentTime, activeScene })
60017
+ });
60018
+ const data2 = await res.json();
60019
+ if (res.ok) {
60020
+ const summary = data2.summary || (data2.output || "done").split("\n")[0].slice(0, 65);
60021
+ onStatusChange?.(summary);
60022
+ setTimeout(() => {
60023
+ if (suppressReloadRef) suppressReloadRef.current = false;
60024
+ window.dispatchEvent(new Event("refresh-player"));
60025
+ }, 4e3);
60026
+ } else {
60027
+ onStatusChange?.("\u274C " + (data2.error || "failed"));
60028
+ if (suppressReloadRef) suppressReloadRef.current = false;
60029
+ }
60030
+ } catch {
60031
+ onStatusChange?.("\u274C error");
60032
+ if (suppressReloadRef) suppressReloadRef.current = false;
60033
+ }
60034
+ setBusy(false);
60035
+ },
60036
+ [busy, onStatusChange, suppressReloadRef]
60037
+ );
60038
+ const handleKeyDown = React49.useCallback(
60039
+ (e) => {
60040
+ if (e.key === "Enter") {
60041
+ e.preventDefault();
60042
+ handleApplyEdit(e.target.value);
60043
+ }
60044
+ },
60045
+ [handleApplyEdit]
60046
+ );
60047
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { id: "bottom-bar", children: [
60048
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
60049
+ "input",
60050
+ {
60051
+ ref: inputRef,
60052
+ id: "edit-input",
60053
+ placeholder: "What should change? e.g. make text bigger",
60054
+ onKeyDown: handleKeyDown
60055
+ }
60056
+ ),
60057
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
60058
+ "button",
60059
+ {
60060
+ id: "edit-btn",
60061
+ title: "Apply edit",
60062
+ disabled: busy,
60063
+ onClick: () => handleApplyEdit(inputRef.current?.value || ""),
60064
+ children: "\u2728"
60065
+ }
60066
+ )
60067
+ ] });
60068
+ }
60069
+
60070
+ // src/player/components/LabelControls.tsx
60071
+ var React50 = __toESM(require_react(), 1);
60072
+ var import_jsx_runtime92 = __toESM(require_jsx_runtime(), 1);
60073
+ var VIDEO_EXT = {
60074
+ ".mov": 1,
60075
+ ".mp4": 1,
60076
+ ".avi": 1,
60077
+ ".mkv": 1,
60078
+ ".webm": 1,
60079
+ ".m4v": 1,
60080
+ ".wmv": 1
60081
+ };
60082
+ function escHtml(s2) {
60083
+ return s2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
60084
+ }
60085
+ function formatTime2(sec) {
60086
+ const mm = Math.floor(sec / 60);
60087
+ const ss = Math.floor(sec % 60);
60088
+ return mm + ":" + String(ss).padStart(2, "0");
60089
+ }
60090
+ function LabelControls({ playerRef, currentTime, onSceneChange }) {
60091
+ const [videoInfo, setVideoInfo] = React50.useState(null);
60092
+ const [labelData, setLabelData] = React50.useState({});
60093
+ const [currentSceneIdx, setCurrentSceneIdx] = React50.useState(0);
60094
+ const [selectedOverride, setSelectedOverride] = React50.useState(-1);
60095
+ const [inputText, setInputText] = React50.useState("");
60096
+ const [saving, setSaving] = React50.useState(false);
60097
+ const inputRef = React50.useRef(null);
60098
+ React50.useEffect(() => {
60099
+ fetch("/api/video-info").then((r) => r.json()).then((info2) => {
60100
+ setVideoInfo(info2);
60101
+ if (info2.scenes?.[0]) {
60102
+ onSceneChange?.(info2.scenes[0].name + " (0.0s)");
60103
+ }
60104
+ }).catch(() => {
60105
+ });
60106
+ fetch("/api/labels").then((r) => r.ok ? r.json() : null).then((tree) => {
60107
+ if (!tree) return;
60108
+ const root2 = tree.root || tree;
60109
+ const children = root2.children || [];
60110
+ const loaded = {};
60111
+ for (let i3 = 0; i3 < children.length; i3++) {
60112
+ const media = (children[i3].children || [])[0];
60113
+ if (media && media.userHints) {
60114
+ loaded[i3] = {
60115
+ overall: media.userHints.overall || "",
60116
+ timed: media.userHints.timed || {}
60117
+ };
60118
+ } else if (media && media.description) {
60119
+ loaded[i3] = { overall: media.description, timed: {} };
60120
+ }
60121
+ }
60122
+ setLabelData(loaded);
60123
+ }).catch(() => {
60124
+ });
60125
+ }, [onSceneChange]);
60126
+ const scenes = videoInfo?.scenes || [];
60127
+ const effectiveIdx = selectedOverride >= 0 ? selectedOverride : currentSceneIdx;
60128
+ const currentScene = scenes[effectiveIdx];
60129
+ React50.useEffect(() => {
60130
+ if (selectedOverride >= 0) return;
60131
+ for (let i3 = 0; i3 < scenes.length; i3++) {
60132
+ if (currentTime >= scenes[i3].start && currentTime < scenes[i3].end) {
60133
+ setCurrentSceneIdx(i3);
60134
+ break;
60135
+ }
60136
+ }
60137
+ }, [currentTime, scenes, selectedOverride]);
60138
+ React50.useEffect(() => {
60139
+ if (selectedOverride >= 0 && currentScene) {
60140
+ const scene2 = scenes[selectedOverride];
60141
+ if (scene2 && (currentTime < scene2.start || currentTime >= scene2.end)) {
60142
+ setSelectedOverride(-1);
60143
+ }
60144
+ }
60145
+ }, [currentTime, selectedOverride, scenes, currentScene]);
60146
+ React50.useEffect(() => {
60147
+ if (!currentScene) {
60148
+ onSceneChange?.(currentTime.toFixed(1) + "s");
60149
+ } else if (selectedOverride >= 0) {
60150
+ onSceneChange?.(currentScene.name + " (selected)");
60151
+ } else {
60152
+ onSceneChange?.(currentScene.name + " (" + currentTime.toFixed(1) + "s)");
60153
+ }
60154
+ }, [currentScene, currentTime, selectedOverride, onSceneChange]);
60155
+ const seekToScene = React50.useCallback(
60156
+ (index) => {
60157
+ setSelectedOverride(index);
60158
+ setCurrentSceneIdx(index);
60159
+ const scene2 = scenes[index];
60160
+ if (scene2 && playerRef.current) {
60161
+ const frame = Math.round(scene2.start * 30);
60162
+ playerRef.current.seekTo(frame);
60163
+ }
60164
+ },
60165
+ [scenes, playerRef]
60166
+ );
60167
+ React50.useEffect(() => {
60168
+ const entry = labelData[effectiveIdx];
60169
+ setInputText(entry && entry.overall || "");
60170
+ }, [effectiveIdx, labelData]);
60171
+ const saveLabel = React50.useCallback(() => {
60172
+ const text = inputText.trim();
60173
+ if (!text || saving) return;
60174
+ setSaving(true);
60175
+ const sceneIdx = effectiveIdx;
60176
+ const timeMs = Math.round(currentTime * 1e3);
60177
+ const scene2 = scenes[sceneIdx];
60178
+ const isImage = scene2 && scene2.mediaType === "image";
60179
+ const sceneStart = scene2 ? scene2.start * 1e3 : 0;
60180
+ const isOverall = isImage || timeMs - sceneStart < 1e3;
60181
+ setLabelData((prev) => {
60182
+ const next = { ...prev };
60183
+ if (!next[sceneIdx]) next[sceneIdx] = { overall: "", timed: {} };
60184
+ if (isOverall) {
60185
+ next[sceneIdx] = { ...next[sceneIdx], overall: text };
60186
+ } else {
60187
+ next[sceneIdx] = {
60188
+ ...next[sceneIdx],
60189
+ timed: { ...next[sceneIdx].timed, ["at_" + timeMs]: text }
60190
+ };
60191
+ }
60192
+ return next;
60193
+ });
60194
+ fetch("/api/labels", {
60195
+ method: "POST",
60196
+ headers: { "Content-Type": "application/json" },
60197
+ body: JSON.stringify({
60198
+ sceneIndex: sceneIdx,
60199
+ description: text,
60200
+ time: currentTime,
60201
+ overall: isOverall || void 0
60202
+ })
60203
+ }).then(() => {
60204
+ setInputText("");
60205
+ const toast = document.getElementById("saved-toast");
60206
+ if (toast) {
60207
+ toast.classList.add("show");
60208
+ setTimeout(() => toast.classList.remove("show"), 2e3);
60209
+ }
60210
+ }).catch(() => {
60211
+ }).finally(() => setSaving(false));
60212
+ }, [inputText, saving, effectiveIdx, currentTime, scenes]);
60213
+ const deleteTimed = React50.useCallback(
60214
+ (key) => {
60215
+ const sceneIdx = effectiveIdx;
60216
+ setLabelData((prev) => {
60217
+ const entry = prev[sceneIdx];
60218
+ if (!entry || !entry.timed[key]) return prev;
60219
+ const next = { ...prev };
60220
+ const nextEntry = { ...entry, timed: { ...entry.timed } };
60221
+ delete nextEntry.timed[key];
60222
+ next[sceneIdx] = nextEntry;
60223
+ return next;
60224
+ });
60225
+ fetch("/api/labels", {
60226
+ method: "POST",
60227
+ headers: { "Content-Type": "application/json" },
60228
+ body: JSON.stringify({
60229
+ sceneIndex: sceneIdx,
60230
+ description: "",
60231
+ removeTimed: key
60232
+ })
60233
+ }).catch(() => {
60234
+ });
60235
+ },
60236
+ [effectiveIdx]
60237
+ );
60238
+ const renderThumbnails = () => {
60239
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { id: "thumbnails", children: scenes.map((scene2, i3) => {
60240
+ const isActive = i3 === effectiveIdx;
60241
+ const entry = labelData[i3];
60242
+ const hasLabel = entry && (entry.overall || Object.keys(entry.timed).length > 0);
60243
+ let thumbContent;
60244
+ const ext = scene2.src.substring(scene2.src.lastIndexOf(".")).toLowerCase();
60245
+ const isVideo = !!VIDEO_EXT[ext];
60246
+ if (scene2.src && !isVideo) {
60247
+ thumbContent = /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("img", { src: scene2.src, alt: "", loading: "lazy" });
60248
+ } else if (scene2.src && isVideo) {
60249
+ thumbContent = /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
60250
+ "video",
60251
+ {
60252
+ src: scene2.src,
60253
+ muted: true,
60254
+ preload: "metadata",
60255
+ style: { width: "100%", height: "100%", objectFit: "cover" }
60256
+ }
60257
+ );
60258
+ } else {
60259
+ thumbContent = /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
60260
+ "div",
60261
+ {
60262
+ style: {
60263
+ width: "100%",
60264
+ height: "100%",
60265
+ display: "flex",
60266
+ alignItems: "center",
60267
+ justifyContent: "center",
60268
+ background: "rgba(255,255,255,.08)",
60269
+ color: "rgba(255,255,255,.3)",
60270
+ fontSize: 16,
60271
+ fontWeight: 600
60272
+ },
60273
+ children: (scene2.name || "S" + (i3 + 1)).slice(0, 2).toUpperCase()
60274
+ }
60275
+ );
60276
+ }
60277
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
60278
+ "div",
60279
+ {
60280
+ className: "thumb-item" + (isActive ? " active" : ""),
60281
+ "data-index": i3,
60282
+ onClick: () => seekToScene(i3),
60283
+ children: [
60284
+ thumbContent,
60285
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { className: "thumb-badge" + (hasLabel ? " has-label" : "") })
60286
+ ]
60287
+ },
60288
+ i3
60289
+ );
60290
+ }) });
60291
+ };
60292
+ const renderTimedLabels = () => {
60293
+ const entry = labelData[effectiveIdx];
60294
+ const timed = entry && entry.timed || {};
60295
+ const keys = Object.keys(timed).sort();
60296
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { id: "timed-labels", children: keys.map((k2) => {
60297
+ const sec = parseInt(k2.replace("at_", "")) / 1e3;
60298
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)("div", { className: "timed-label", "data-key": k2, children: [
60299
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("span", { className: "tl-time", children: formatTime2(sec) }),
60300
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("span", { className: "tl-text", dangerouslySetInnerHTML: { __html: escHtml(timed[k2]) } }),
60301
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("button", { className: "tl-del", title: "Remove", onClick: () => deleteTimed(k2), children: "\xD7" })
60302
+ ] }, k2);
60303
+ }) });
60304
+ };
60305
+ let sceneInfoText = currentTime.toFixed(1) + "s";
60306
+ if (currentScene) {
60307
+ if (selectedOverride >= 0) {
60308
+ sceneInfoText = currentScene.name + " (selected)";
60309
+ } else {
60310
+ sceneInfoText = currentScene.name + " (" + currentTime.toFixed(1) + "s)";
60311
+ }
60312
+ }
60313
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(import_jsx_runtime92.Fragment, { children: [
60314
+ renderThumbnails(),
60315
+ renderTimedLabels(),
60316
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { id: "saved-toast", children: "\u2713 Label saved" }),
60317
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)("div", { id: "bottom-bar", children: [
60318
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
60319
+ "input",
60320
+ {
60321
+ ref: inputRef,
60322
+ id: "label-input",
60323
+ placeholder: "Add label for current scene\u2026",
60324
+ value: inputText,
60325
+ onChange: (e) => setInputText(e.target.value),
60326
+ onKeyDown: (e) => {
60327
+ if (e.key === "Enter") {
60328
+ e.preventDefault();
60329
+ saveLabel();
60330
+ }
60331
+ }
60332
+ }
60333
+ ),
60334
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
60335
+ "button",
60336
+ {
60337
+ id: "label-btn",
60338
+ title: "Save label",
60339
+ disabled: saving || !inputText.trim(),
60340
+ onClick: saveLabel,
60341
+ children: "\u{1F4DD}"
60342
+ }
60343
+ )
60344
+ ] })
60345
+ ] });
60346
+ }
60347
+
60348
+ // src/player/components/SceneThumbnails.tsx
60349
+ var React51 = __toESM(require_react(), 1);
60350
+ var import_jsx_runtime93 = __toESM(require_jsx_runtime(), 1);
60351
+ function SceneThumbnails({ currentTime, onSeek }) {
60352
+ const [scenes, setScenes] = React51.useState([]);
60353
+ React51.useEffect(() => {
60354
+ fetch("/api/video-info").then((r) => r.json()).then((info2) => {
60355
+ if (info2.scenes) setScenes(info2.scenes);
60356
+ }).catch(() => {
60357
+ });
60358
+ }, []);
60359
+ if (scenes.length === 0) return null;
60360
+ let activeIdx = -1;
60361
+ for (let i3 = 0; i3 < scenes.length; i3++) {
60362
+ if (currentTime >= scenes[i3].start && currentTime < scenes[i3].end) {
60363
+ activeIdx = i3;
60364
+ break;
60365
+ }
60366
+ }
60367
+ return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)("div", { id: "scene-thumbnails", children: scenes.map((scene2, i3) => {
60368
+ const isActive = i3 === activeIdx;
60369
+ return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
60370
+ "span",
60371
+ {
60372
+ className: "scene-pill" + (isActive ? " active" : ""),
60373
+ onClick: () => onSeek?.(scene2.start),
60374
+ children: scene2.name || "Scene " + (i3 + 1)
60375
+ },
60376
+ i3
60377
+ );
60378
+ }) });
60379
+ }
60380
+
60381
+ // src/player/components/VariantBar.tsx
60382
+ var React54 = __toESM(require_react(), 1);
60383
+ var import_jsx_runtime94 = __toESM(require_jsx_runtime(), 1);
60384
+ function VariantBar() {
60385
+ const [variants, setVariants] = React54.useState([]);
60386
+ const currentVariant = (typeof window !== "undefined" ? window.VARIANT : null) || "default";
60387
+ React54.useEffect(() => {
60388
+ fetch("/api/video-info").then((r) => r.json()).then((info2) => {
60389
+ if (info2.variants && info2.variants.length > 1) {
60390
+ setVariants(info2.variants);
60391
+ }
60392
+ }).catch(() => {
60393
+ });
60394
+ }, []);
60395
+ if (variants.length <= 1) return null;
60396
+ return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("div", { id: "variant-bar", children: variants.map((v2) => /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
60397
+ "a",
60398
+ {
60399
+ href: `/${v2 === "default" ? "" : v2}`,
60400
+ className: "variant-link" + (v2 === currentVariant ? " active" : ""),
60401
+ children: v2
60402
+ },
60403
+ v2
60404
+ )) });
60405
+ }
60406
+
59925
60407
  // src/player/browser.tsx
60408
+ var import_jsx_runtime95 = __toESM(require_jsx_runtime(), 1);
59926
60409
  if (typeof window !== "undefined") {
59927
60410
  globalThis.__remotionShared = {
59928
- "react": React47,
59929
- "react-dom": ReactDOM,
60411
+ "react": React55,
60412
+ "react-dom": ReactDOM2,
59930
60413
  "remotion": esm_exports,
59931
60414
  "@remotion/player": { Player }
59932
60415
  };
@@ -59988,59 +60471,90 @@ if (typeof window !== "undefined") {
59988
60471
  }
59989
60472
  }
59990
60473
  function PlayerApp() {
59991
- const playerRef = React47.useRef(null);
59992
- const [ready, setReady] = React47.useState(false);
59993
- const [error49, setError] = React47.useState(null);
59994
- const [data2, setData] = React47.useState(null);
59995
- const [refreshKey, setRefreshKey] = React47.useState(0);
59996
- const [muted, setMuted] = React47.useState(false);
59997
- const [volume, setVolume] = React47.useState(1);
59998
- const seekAttemptedRef = React47.useRef(false);
59999
- const mountedRef = React47.useRef(true);
60474
+ const playerRef = React55.useRef(null);
60475
+ const [ready, setReady] = React55.useState(false);
60476
+ const [error49, setError] = React55.useState(null);
60477
+ const [data2, setData] = React55.useState(null);
60478
+ const [muted, setMuted] = React55.useState(false);
60479
+ const [volume, setVolume] = React55.useState(1);
60480
+ const mountedRef = React55.useRef(true);
60481
+ const currentFrameRef = React55.useRef(0);
60482
+ const [currentTime, setCurrentTime] = React55.useState(0);
60483
+ const [activeScene, setActiveScene] = React55.useState("");
60484
+ React55.useEffect(() => {
60485
+ fetch("/api/video-info").then((r) => r.json()).then((info2) => {
60486
+ if (info2.scenes) {
60487
+ window.__scenes = info2.scenes;
60488
+ }
60489
+ }).catch(() => {
60490
+ });
60491
+ }, []);
60492
+ React55.useEffect(() => {
60493
+ const scenes = window.__scenes;
60494
+ if (!scenes) return;
60495
+ let found = "";
60496
+ for (const s2 of scenes) {
60497
+ if (currentTime >= s2.start && currentTime < s2.end) {
60498
+ found = s2.name || "";
60499
+ break;
60500
+ }
60501
+ }
60502
+ setActiveScene(found);
60503
+ }, [currentTime]);
60504
+ const pendingSeekRef = React55.useRef(null);
60000
60505
  const urlParams = new URLSearchParams(typeof window !== "undefined" ? window.location.search : "");
60001
60506
  const autoPlay = urlParams.get("autoplay") === "true";
60002
60507
  const startAt = parseFloat(urlParams.get("start") || urlParams.get("t") || "0") || 0;
60003
60508
  const fps = data2?.fps ?? 30;
60004
60509
  const durationInSeconds = data2 ? getDurationInSeconds(data2, true) || 5 : 5;
60005
60510
  const durationInFrames = Math.max(1, Math.ceil(durationInSeconds * fps));
60006
- const loadData = React47.useCallback(() => {
60007
- setReady(false);
60511
+ const loadData = React55.useCallback((initial = false) => {
60512
+ if (initial) setReady(false);
60008
60513
  const variant = window.VARIANT || "default";
60009
60514
  const url2 = variant !== "default" ? `/api/video-data?variant=${variant}` : "/api/video-data";
60010
60515
  fetch(url2).then((r) => r.json()).then((json2) => {
60011
60516
  const root2 = json2.root || json2;
60012
60517
  setData(root2);
60013
- setReady(true);
60518
+ if (initial) setReady(true);
60014
60519
  }).catch((e) => setError(e.message));
60015
60520
  }, []);
60016
- React47.useEffect(() => {
60017
- loadData();
60018
- const handler = () => {
60019
- setRefreshKey((k2) => k2 + 1);
60020
- };
60021
- window.addEventListener("refresh-player", handler);
60521
+ React55.useEffect(() => {
60522
+ loadData(true);
60022
60523
  return () => {
60023
60524
  mountedRef.current = false;
60024
- window.removeEventListener("refresh-player", handler);
60025
60525
  };
60026
60526
  }, [loadData]);
60027
- React47.useEffect(() => {
60028
- if (refreshKey > 0) loadData();
60029
- }, [refreshKey, loadData]);
60030
- React47.useEffect(() => {
60031
- if (!ready || !data2 || seekAttemptedRef.current) return;
60032
- if (startAt > 0 && playerRef.current) {
60527
+ React55.useEffect(() => {
60528
+ const handler = () => {
60529
+ if (playerRef.current) {
60530
+ pendingSeekRef.current = playerRef.current.getCurrentFrame();
60531
+ }
60532
+ loadData(false);
60533
+ };
60534
+ window.addEventListener("refresh-player", handler);
60535
+ return () => window.removeEventListener("refresh-player", handler);
60536
+ }, [loadData]);
60537
+ React55.useEffect(() => {
60538
+ if (!ready || !data2 || !playerRef.current) return;
60539
+ const targetFrame = pendingSeekRef.current ?? Math.round(startAt * fps);
60540
+ if (targetFrame > 0) {
60033
60541
  const timer = setTimeout(() => {
60034
60542
  if (!mountedRef.current || !playerRef.current) return;
60035
- const frame = Math.round(startAt * fps);
60036
- playerRef.current.seekTo(frame);
60037
- seekAttemptedRef.current = true;
60543
+ playerRef.current.seekTo(targetFrame);
60544
+ pendingSeekRef.current = null;
60038
60545
  }, 100);
60039
60546
  return () => clearTimeout(timer);
60040
60547
  }
60041
- seekAttemptedRef.current = true;
60548
+ pendingSeekRef.current = null;
60042
60549
  }, [ready, data2, startAt, fps]);
60043
- React47.useEffect(() => {
60550
+ const handleFrameUpdate = React55.useCallback((frame) => {
60551
+ currentFrameRef.current = frame;
60552
+ setCurrentTime((prev) => {
60553
+ const newTime = frame / (data2?.fps ?? 30);
60554
+ return Math.abs(newTime - prev) > 0.5 ? newTime : prev;
60555
+ });
60556
+ }, [data2?.fps]);
60557
+ React55.useEffect(() => {
60044
60558
  if (!ready || !playerRef.current) return;
60045
60559
  const FWD_SECONDS = 5;
60046
60560
  const BACK_SECONDS = 5;
@@ -60150,55 +60664,124 @@ function PlayerApp() {
60150
60664
  window.addEventListener("keydown", onKey);
60151
60665
  return () => window.removeEventListener("keydown", onKey);
60152
60666
  }, [ready, data2, fps, durationInFrames, volume]);
60153
- React47.useEffect(() => {
60154
- if (!data2) return;
60155
- window.__remotionSeekTo = (timeInSeconds) => {
60156
- const frame = Math.round(timeInSeconds * fps);
60157
- playerRef.current?.seekTo(frame);
60667
+ const mode = (typeof window !== "undefined" ? window.MODE : null) || "preview";
60668
+ const [editStatus, setEditStatus] = React55.useState("");
60669
+ const [sseConnected, setSseConnected] = React55.useState(false);
60670
+ const [labelSceneInfo, setLabelSceneInfo] = React55.useState("");
60671
+ const suppressReloadRef = React55.useRef(false);
60672
+ React55.useEffect(() => {
60673
+ let evtSource = null;
60674
+ try {
60675
+ evtSource = new EventSource("/api/events");
60676
+ evtSource.onopen = () => setSseConnected(true);
60677
+ evtSource.onmessage = (e) => {
60678
+ try {
60679
+ const msg = JSON.parse(e.data);
60680
+ if (msg.type === "reload" && !suppressReloadRef.current) {
60681
+ window.dispatchEvent(new Event("refresh-player"));
60682
+ }
60683
+ } catch {
60684
+ }
60685
+ };
60686
+ evtSource.onerror = () => setSseConnected(false);
60687
+ } catch {
60688
+ setSseConnected(false);
60689
+ }
60690
+ return () => {
60691
+ evtSource?.close();
60692
+ setSseConnected(false);
60158
60693
  };
60159
- });
60694
+ }, []);
60160
60695
  if (error49) {
60161
- return React47.createElement("div", {
60162
- style: { color: "red", padding: 40, fontFamily: "sans-serif" }
60163
- }, "Error: " + error49);
60696
+ return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)("div", { style: { color: "red", padding: 40, fontFamily: "sans-serif" }, children: [
60697
+ "Error: ",
60698
+ error49
60699
+ ] });
60164
60700
  }
60165
60701
  if (!ready) {
60166
- return React47.createElement("div", {
60167
- style: { color: "#888", padding: 40, fontFamily: "sans-serif" }
60168
- }, "Loading...");
60702
+ return /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("div", { style: { color: "#888", padding: 40, fontFamily: "sans-serif" }, children: "Loading..." });
60169
60703
  }
60170
60704
  const width = data2.width || 1080;
60171
60705
  const height = data2.height || 1920;
60172
- return React47.createElement(
60706
+ return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
60173
60707
  "div",
60174
60708
  {
60175
- style: { width: "100%", height: "100%", background: "#000" }
60176
- },
60177
- React47.createElement(Player, {
60178
- ref: playerRef,
60179
- component: MarkCut,
60180
- inputProps: {
60181
- root: data2,
60182
- compose: {}
60709
+ style: {
60710
+ width: "100%",
60711
+ height: "100%",
60712
+ background: "#0a0a0a",
60713
+ display: "flex",
60714
+ flexDirection: "column",
60715
+ alignItems: "center"
60183
60716
  },
60184
- durationInFrames,
60185
- fps,
60186
- compositionWidth: width,
60187
- compositionHeight: height,
60188
- style: { width: "100%", height: "100%" },
60189
- controls: true,
60190
- showPlaybackRateControl: true,
60191
- allowFullscreen: true,
60192
- clickToPlay: false,
60193
- doubleClickToFullscreen: true,
60194
- autoPlay
60195
- })
60717
+ children: [
60718
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
60719
+ HeaderBar,
60720
+ {
60721
+ mode,
60722
+ editStatus,
60723
+ sseConnected,
60724
+ sceneInfo: mode === "label" ? labelSceneInfo : void 0
60725
+ }
60726
+ ),
60727
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(VariantBar, {}),
60728
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("div", { id: "player-frame", style: { flex: 1, width: "100%", maxWidth: 480, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
60729
+ Player,
60730
+ {
60731
+ ref: playerRef,
60732
+ component: MarkCut,
60733
+ inputProps: { root: data2, compose: {} },
60734
+ durationInFrames,
60735
+ fps,
60736
+ compositionWidth: width,
60737
+ compositionHeight: height,
60738
+ style: { width: "100%", height: "100%" },
60739
+ controls: true,
60740
+ showPlaybackRateControl: true,
60741
+ allowFullscreen: true,
60742
+ clickToPlay: false,
60743
+ doubleClickToFullscreen: true,
60744
+ autoPlay,
60745
+ onFrameUpdate: handleFrameUpdate
60746
+ }
60747
+ ) }),
60748
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
60749
+ SceneThumbnails,
60750
+ {
60751
+ currentTime,
60752
+ onSeek: (t) => {
60753
+ if (playerRef.current) {
60754
+ const frame = Math.round(t * (data2?.fps ?? 30));
60755
+ playerRef.current.seekTo(frame);
60756
+ }
60757
+ }
60758
+ }
60759
+ ),
60760
+ mode === "edit" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
60761
+ EditControls,
60762
+ {
60763
+ onStatusChange: setEditStatus,
60764
+ suppressReloadRef,
60765
+ currentTime,
60766
+ activeScene
60767
+ }
60768
+ ),
60769
+ mode === "label" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
60770
+ LabelControls,
60771
+ {
60772
+ playerRef,
60773
+ currentTime,
60774
+ onSceneChange: setLabelSceneInfo
60775
+ }
60776
+ )
60777
+ ]
60778
+ }
60196
60779
  );
60197
60780
  }
60198
60781
  var container2 = document.getElementById("root");
60199
60782
  if (container2) {
60200
60783
  const root2 = (0, import_client.createRoot)(container2);
60201
- root2.render(React47.createElement(PlayerApp));
60784
+ root2.render(React55.createElement(PlayerApp));
60202
60785
  }
60203
60786
  /*! Bundled license information:
60204
60787