@lalalic/markcut 2.9.0 → 3.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 (36) hide show
  1. package/package.json +1 -1
  2. package/skills/markcut/SKILL.md +7 -0
  3. package/skills/markcut/docs/components.md +45 -2
  4. package/skills/markcut/docs/map-dynamic-camera.md +244 -0
  5. package/skills/markcut/docs/markdown-descriptive.md +7 -2
  6. package/src/components/Markdown.tsx +138 -24
  7. package/src/components/Mermaid.tsx +223 -22
  8. package/src/context/EventContext.tsx +3 -0
  9. package/src/descriptive/compiler.ts +105 -29
  10. package/src/descriptive/dsl.ts +42 -5
  11. package/src/descriptive/markdown.ts +23 -0
  12. package/src/descriptive/resolve.test.ts +5 -5
  13. package/src/descriptive/resolve.ts +51 -12
  14. package/src/player/bundle/player.js +751 -143
  15. package/src/player/pipeline.mjs +130 -32
  16. package/src/player/pipeline.ts +5 -4
  17. package/src/player/server.mjs +22 -42
  18. package/src/render/cli.mjs +54 -3
  19. package/src/render/validate-assets.mjs +140 -0
  20. package/src/schema/index.ts +58 -2
  21. package/src/spots/cli.mjs +266 -0
  22. package/src/types/Component.tsx +27 -1
  23. package/src/types/Effect.tsx +13 -6
  24. package/src/types/Folder.tsx +1 -1
  25. package/src/types/Map.tsx +501 -127
  26. package/src/utils/index.ts +14 -2
  27. package/src/utils/tween.ts +49 -1
  28. package/tests/dsl.test.ts +43 -0
  29. package/tests/fixtures/map-dynamic.json +52 -0
  30. package/tests/fixtures/md/animate-diagrams.md +42 -0
  31. package/tests/fixtures/md/electricity-grow.md +130 -0
  32. package/tests/fixtures/md/map-all-views.md +28 -0
  33. package/tests/md-descriptive.test.ts +58 -0
  34. package/tests/render.test.ts +1 -0
  35. package/tests/schema.test.ts +58 -1
  36. package/tests/validate-assets.test.ts +106 -0
@@ -122,7 +122,7 @@ var require_react_production = __commonJS({
122
122
  function cloneAndReplaceKey(oldElement, newKey) {
123
123
  return ReactElement(oldElement.type, newKey, oldElement.props);
124
124
  }
125
- function isValidElement(object4) {
125
+ function isValidElement2(object4) {
126
126
  return "object" === typeof object4 && null !== object4 && object4.$$typeof === REACT_ELEMENT_TYPE;
127
127
  }
128
128
  function escape3(key) {
@@ -189,7 +189,7 @@ var require_react_production = __commonJS({
189
189
  if (invokeCallback)
190
190
  return callback = callback(children2), invokeCallback = "" === nameSoFar ? "." + getElementKey(children2, 0) : nameSoFar, isArrayImpl(callback) ? (escapedPrefix = "", null != invokeCallback && (escapedPrefix = invokeCallback.replace(userProvidedKeyEscapeRegex, "$&/") + "/"), mapIntoArray(callback, array5, escapedPrefix, "", function(c5) {
191
191
  return c5;
192
- })) : null != callback && (isValidElement(callback) && (callback = cloneAndReplaceKey(
192
+ })) : null != callback && (isValidElement2(callback) && (callback = cloneAndReplaceKey(
193
193
  callback,
194
194
  escapedPrefix + (null == callback.key || children2 && children2.key === callback.key ? "" : ("" + callback.key).replace(
195
195
  userProvidedKeyEscapeRegex,
@@ -298,7 +298,7 @@ var require_react_production = __commonJS({
298
298
  }) || [];
299
299
  },
300
300
  only: function(children2) {
301
- if (!isValidElement(children2))
301
+ if (!isValidElement2(children2))
302
302
  throw Error(
303
303
  "React.Children.only expected to receive a single React element child."
304
304
  );
@@ -385,7 +385,7 @@ var require_react_production = __commonJS({
385
385
  exports2.forwardRef = function(render8) {
386
386
  return { $$typeof: REACT_FORWARD_REF_TYPE, render: render8 };
387
387
  };
388
- exports2.isValidElement = isValidElement;
388
+ exports2.isValidElement = isValidElement2;
389
389
  exports2.lazy = function(ctor) {
390
390
  return {
391
391
  $$typeof: REACT_LAZY_TYPE,
@@ -225416,10 +225416,100 @@ var mermaid_default = mermaid;
225416
225416
  // src/components/Mermaid.tsx
225417
225417
  var import_jsx_runtime60 = __toESM(require_jsx_runtime(), 1);
225418
225418
  var initialized = false;
225419
- function Mermaid({ children: children2, source = children2, theme = "dark", className: className2 }) {
225419
+ function extractText(x7) {
225420
+ if (typeof x7 === "string") return x7;
225421
+ if (Array.isArray(x7)) return x7.map(extractText).join("");
225422
+ if (x7 && typeof x7 === "object" && "props" in x7) {
225423
+ return extractText(x7.props?.children);
225424
+ }
225425
+ return "";
225426
+ }
225427
+ function findNodeGroup(svg4, name2) {
225428
+ const byId = svg4.querySelector(`[id*="-${CSS.escape(name2)}-"]`);
225429
+ if (byId) {
225430
+ let el = byId;
225431
+ while (el && el.tagName !== "g") el = el.parentElement;
225432
+ return el || byId;
225433
+ }
225434
+ for (const t4 of svg4.querySelectorAll("text")) {
225435
+ const text10 = t4.textContent?.trim() ?? "";
225436
+ if (text10.startsWith(name2)) {
225437
+ let el = t4;
225438
+ while (el && el.tagName !== "g") el = el.parentElement;
225439
+ return el || t4;
225440
+ }
225441
+ }
225442
+ for (const t4 of svg4.querySelectorAll("title")) {
225443
+ if ((t4.textContent?.trim() ?? "").startsWith(name2) && t4.parentElement) {
225444
+ return t4.parentElement;
225445
+ }
225446
+ }
225447
+ return null;
225448
+ }
225449
+ function applyEdgeAnimation(svg4, spec) {
225450
+ if (!spec) return;
225451
+ const allEdges = Array.from(svg4.querySelectorAll(
225452
+ 'path[id*="-L_"]'
225453
+ ));
225454
+ if (spec === true) {
225455
+ for (const path5 of allEdges) {
225456
+ path5.classList.add("edge-animated");
225457
+ }
225458
+ return;
225459
+ }
225460
+ for (const pattern of spec) {
225461
+ const match3 = pattern.match(/^(\w+)\s*->\s*(\w+)$/);
225462
+ if (!match3) continue;
225463
+ const source = match3[1];
225464
+ const target = match3[2];
225465
+ const suffix = `L_${source}_${target}_`;
225466
+ for (const path5 of allEdges) {
225467
+ if (path5.id.includes(suffix)) {
225468
+ path5.classList.add("edge-animated");
225469
+ }
225470
+ }
225471
+ }
225472
+ }
225473
+ function Mermaid({
225474
+ children: children2,
225475
+ source: sourceProp,
225476
+ theme = "dark",
225477
+ className: className2,
225478
+ style: style4,
225479
+ highlight,
225480
+ animateEdges
225481
+ }) {
225420
225482
  const ref2 = React8.useRef(null);
225421
- const [handle2] = React8.useState(() => delayRender("Mermaid rendering"));
225483
+ const renderedRef = React8.useRef(false);
225484
+ const styleRef = React8.useRef(null);
225485
+ React8.useEffect(() => {
225486
+ if (!styleRef.current) {
225487
+ const el = document.createElement("style");
225488
+ el.textContent = `
225489
+ @keyframes mermaid-edge-flow {
225490
+ to { stroke-dashoffset: -24; }
225491
+ }
225492
+ .edge-animated {
225493
+ stroke-dasharray: 8 6 !important;
225494
+ animation: mermaid-edge-flow 0.5s linear infinite !important;
225495
+ }
225496
+ `;
225497
+ document.head.appendChild(el);
225498
+ styleRef.current = el;
225499
+ }
225500
+ return () => {
225501
+ if (styleRef.current) {
225502
+ styleRef.current.remove();
225503
+ styleRef.current = null;
225504
+ }
225505
+ };
225506
+ }, []);
225507
+ const source = React8.useMemo(
225508
+ () => extractText(sourceProp ?? children2),
225509
+ [sourceProp, children2]
225510
+ );
225422
225511
  React8.useEffect(() => {
225512
+ let cancelled = false;
225423
225513
  if (!source || !ref2.current) return;
225424
225514
  if (!initialized) {
225425
225515
  mermaid_default.initialize({
@@ -225431,52 +225521,186 @@ function Mermaid({ children: children2, source = children2, theme = "dark", clas
225431
225521
  }
225432
225522
  const id39 = "mmd-" + Math.random().toString(36).slice(2, 10);
225433
225523
  mermaid_default.render(id39, source).then((result) => {
225434
- if (ref2.current) ref2.current.innerHTML = result.svg;
225435
- continueRender(handle2);
225524
+ if (cancelled || !ref2.current) return;
225525
+ ref2.current.innerHTML = result.svg;
225526
+ const svg4 = ref2.current.querySelector("svg");
225527
+ if (svg4) {
225528
+ svg4.removeAttribute("width");
225529
+ svg4.removeAttribute("height");
225530
+ svg4.style.width = "100%";
225531
+ svg4.style.height = "100%";
225532
+ if (highlight) {
225533
+ const names = Array.isArray(highlight) ? highlight : [highlight];
225534
+ for (const name2 of names) {
225535
+ const node3 = findNodeGroup(svg4, name2);
225536
+ if (node3) node3.classList.add("highlight");
225537
+ }
225538
+ }
225539
+ applyEdgeAnimation(svg4, animateEdges);
225540
+ }
225541
+ renderedRef.current = true;
225436
225542
  }).catch((err) => {
225543
+ if (cancelled) return;
225437
225544
  console.error("Mermaid error:", err);
225438
225545
  if (ref2.current) {
225439
225546
  ref2.current.innerHTML = `<div style="color:#f87171;padding:1em;border:2px dashed #f87171;border-radius:8px;font-family:monospace;font-size:14px;">
225440
225547
  <strong>\u26A0 Mermaid Error</strong><br/>${String(err).replace(/</g, "&lt;").replace(/>/g, "&gt;")}
225441
225548
  </div>`;
225442
225549
  }
225443
- continueRender(handle2);
225444
225550
  });
225445
- }, [source, theme, handle2]);
225446
- return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
225447
- "div",
225448
- {
225449
- ref: ref2,
225450
- className: className2
225551
+ return () => {
225552
+ cancelled = true;
225553
+ };
225554
+ }, [source, theme]);
225555
+ React8.useEffect(() => {
225556
+ const svg4 = ref2.current?.querySelector("svg");
225557
+ if (!svg4) return;
225558
+ svg4.querySelectorAll(".highlight").forEach((el) => {
225559
+ el.classList.remove("highlight");
225560
+ });
225561
+ const names = Array.isArray(highlight) ? highlight : highlight ? [highlight] : [];
225562
+ for (const name2 of names) {
225563
+ const node3 = findNodeGroup(svg4, name2);
225564
+ if (node3) {
225565
+ node3.classList.add("highlight");
225566
+ }
225451
225567
  }
225452
- );
225568
+ }, [highlight]);
225569
+ React8.useEffect(() => {
225570
+ const svg4 = ref2.current?.querySelector("svg");
225571
+ if (!svg4) return;
225572
+ svg4.querySelectorAll(".edge-animated").forEach((el) => {
225573
+ el.classList.remove("edge-animated");
225574
+ });
225575
+ applyEdgeAnimation(svg4, animateEdges);
225576
+ }, [animateEdges]);
225577
+ const containerStyle3 = {
225578
+ display: "flex",
225579
+ justifyContent: "center",
225580
+ alignItems: "center",
225581
+ width: "100%",
225582
+ height: "100%",
225583
+ ...style4
225584
+ };
225585
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { ref: ref2, className: className2, style: containerStyle3 });
225453
225586
  }
225454
225587
 
225455
225588
  // src/components/Markdown.tsx
225456
225589
  var import_jsx_runtime61 = __toESM(require_jsx_runtime(), 1);
225457
- function Markdown2({ children: children2, source = children2, className: className2, plugins, components: components3 }) {
225458
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: className2, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
225590
+ var ListCtx = React9.createContext([]);
225591
+ function OrderedList({
225592
+ children: children2,
225593
+ ...props
225594
+ }) {
225595
+ const hl = React9.useContext(ListCtx);
225596
+ const items = React9.Children.toArray(children2).filter(
225597
+ (c5) => React9.isValidElement(c5) && c5.type === "li"
225598
+ );
225599
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("ol", { ...props, children: items.map((child, i5) => {
225600
+ if (hl.includes(i5)) {
225601
+ return React9.cloneElement(child, { className: "highlight-list-item", key: child.key });
225602
+ }
225603
+ return child;
225604
+ }) });
225605
+ }
225606
+ function UnorderedList({
225607
+ children: children2,
225608
+ ...props
225609
+ }) {
225610
+ const hl = React9.useContext(ListCtx);
225611
+ const items = React9.Children.toArray(children2).filter(
225612
+ (c5) => React9.isValidElement(c5) && c5.type === "li"
225613
+ );
225614
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("ul", { ...props, children: items.map((child, i5) => {
225615
+ if (hl.includes(i5)) {
225616
+ return React9.cloneElement(child, { className: "highlight-list-item", key: child.key });
225617
+ }
225618
+ return child;
225619
+ }) });
225620
+ }
225621
+ function Markdown2({
225622
+ children: children2,
225623
+ source = children2,
225624
+ className: className2,
225625
+ plugins,
225626
+ components: propComponents,
225627
+ highlight
225628
+ }) {
225629
+ const hl = React9.useMemo(() => highlight ?? [], [highlight]);
225630
+ React9.useEffect(() => {
225631
+ const id39 = "markcut-markdown-defaults";
225632
+ if (document.getElementById(id39)) return;
225633
+ const el = document.createElement("style");
225634
+ el.id = id39;
225635
+ el.textContent = `
225636
+ .highlight-list-item {
225637
+ background: rgba(255, 215, 0, 0.15);
225638
+ border-left: 3px solid #ffd700;
225639
+ padding-left: 8px;
225640
+ border-radius: 0 4px 4px 0;
225641
+ }
225642
+ .slide {
225643
+ display: flex;
225644
+ flex-direction: column;
225645
+ align-items: center;
225646
+ justify-content: center;
225647
+ padding: 24px;
225648
+ text-align: center;
225649
+ }
225650
+ .slide h1 { font-size: 2em; margin: 0.4em 0; font-weight: 700; }
225651
+ .slide h2 { font-size: 1.6em; margin: 0.35em 0; font-weight: 600; }
225652
+ .slide h3 { font-size: 1.3em; margin: 0.3em 0; font-weight: 600; }
225653
+ .slide p { margin: 0.6em 0; line-height: 1.6; }
225654
+ .slide ul, .slide ol { margin: 0.5em 0; padding-left: 1.5em; text-align: left; }
225655
+ .slide li { margin: 0.3em 0; }
225656
+ .slide blockquote {
225657
+ margin: 0.6em 0;
225658
+ padding: 0.4em 1em;
225659
+ border-left: 3px solid rgba(255,255,255,.3);
225660
+ font-style: italic;
225661
+ opacity: .85;
225662
+ }
225663
+ .slide code {
225664
+ background: rgba(255,255,255,.08);
225665
+ padding: 0.15em 0.4em;
225666
+ border-radius: 4px;
225667
+ font-size: 0.9em;
225668
+ }
225669
+ .slide pre { margin: 0.6em 0; text-align: left; width: 100%; }
225670
+ .slide a { color: #4a9eff; text-decoration: none; }
225671
+ .slide a:hover { text-decoration: underline; }
225672
+ `;
225673
+ document.head.appendChild(el);
225674
+ return () => {
225675
+ document.getElementById(id39)?.remove();
225676
+ };
225677
+ }, []);
225678
+ const mergedComponents = React9.useMemo(
225679
+ () => ({
225680
+ ...propComponents,
225681
+ ol: OrderedList,
225682
+ ul: UnorderedList,
225683
+ pre: ({ children: preChildren }) => {
225684
+ const code4 = React9.Children.toArray(preChildren)[0];
225685
+ if (code4?.props?.className === "language-mermaid") {
225686
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Mermaid, { source: String(code4.props.children) });
225687
+ }
225688
+ if (propComponents?.pre) {
225689
+ return propComponents.pre({ children: preChildren });
225690
+ }
225691
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("pre", { children: preChildren });
225692
+ }
225693
+ }),
225694
+ [propComponents]
225695
+ );
225696
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ListCtx.Provider, { value: hl, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: className2, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
225459
225697
  Markdown,
225460
225698
  {
225461
225699
  remarkPlugins: React9.useMemo(() => [remarkGfm, ...plugins || []], [plugins]),
225462
- components: React9.useMemo(
225463
- () => ({
225464
- ...components3,
225465
- pre: ({ children: children3 }) => {
225466
- const code4 = React9.Children.toArray(children3)[0];
225467
- if (code4?.props?.className === "language-mermaid") {
225468
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Mermaid, { source: String(code4.props.children) });
225469
- } else if (components3.pre) {
225470
- return components3.pre({ children: children3 });
225471
- }
225472
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("pre", { children: children3 });
225473
- }
225474
- }),
225475
- [components3]
225476
- ),
225700
+ components: mergedComponents,
225477
225701
  children: source
225478
225702
  }
225479
- ) });
225703
+ ) }) });
225480
225704
  }
225481
225705
 
225482
225706
  // src/utils/component-import-map.ts
@@ -225588,6 +225812,8 @@ function EventProvider({ children: children2 }) {
225588
225812
  const keys4 = Object.keys(scope);
225589
225813
  const vals = Object.values(scope);
225590
225814
  try {
225815
+ const codeChars = Array.from(code4).map((c5) => c5.charCodeAt(0));
225816
+ console.log(`EventContext.debug: keys=${JSON.stringify(keys4)}, codeLen=${code4.length}, codeChars=${JSON.stringify(codeChars)}, codeStr=${JSON.stringify(code4)}`);
225591
225817
  const fn3 = new Function(...keys4, code4);
225592
225818
  fn3(...vals);
225593
225819
  console.info(`Event evaluation succeeded: "${code4}"`);
@@ -227900,7 +228126,13 @@ var clockWipe = (props) => {
227900
228126
 
227901
228127
  // src/utils/index.ts
227902
228128
  function uid() {
227903
- return Math.random().toString(36).slice(2, 10);
228129
+ const raw = Math.random().toString(36).slice(2, 10);
228130
+ const first3 = raw[0];
228131
+ if (/^[0-9]/.test(first3)) {
228132
+ const letter = String.fromCharCode(97 + Math.floor(Math.random() * 26));
228133
+ return letter + raw;
228134
+ }
228135
+ return raw;
227904
228136
  }
227905
228137
  var KEBAB = /[^a-zA-Z0-9_-]+/g;
227906
228138
  function toClassName(s3) {
@@ -227966,7 +228198,7 @@ function getDurationInSeconds(stream3, update2 = true) {
227966
228198
  for (const child of stream3.children) {
227967
228199
  getDurationInSeconds(child, update2);
227968
228200
  }
227969
- const visible = stream3.children.filter((c5) => !c5.isBackground);
228201
+ const visible = stream3.type === "effect" ? stream3.children : stream3.children.filter((c5) => !c5.isBackground);
227970
228202
  if (stream3.isSeries) {
227971
228203
  const overlap = stream3.transition ? stream3.transitionTime ?? 0.5 : 0;
227972
228204
  for (let i5 = 0; i5 < visible.length; i5++) {
@@ -239626,6 +239858,23 @@ function useTweenBindings(action) {
239626
239858
  );
239627
239859
  return { tween, interpolate };
239628
239860
  }
239861
+ function resolveTween(frame2, fps, spec, start4, end2, fallback) {
239862
+ if (typeof spec === "number") return spec;
239863
+ const tween = spec?.__tween;
239864
+ if (!tween || tween.length < 2) return fallback;
239865
+ const from2 = Number(tween[0]);
239866
+ const to = Number(tween[1]);
239867
+ if (!Number.isFinite(from2) || !Number.isFinite(to)) return fallback;
239868
+ const easingName = typeof tween[2] === "string" ? tween[2] : void 0;
239869
+ const easingFn = easingName ? EASING_MAP[easingName] : void 0;
239870
+ const startF = Math.max(0, Math.floor(start4 * fps));
239871
+ const endF = Math.max(startF + 1, Math.floor(end2 * fps));
239872
+ return interpolate(frame2, [startF, endF], [from2, to], {
239873
+ extrapolateLeft: "clamp",
239874
+ extrapolateRight: "clamp",
239875
+ easing: easingFn
239876
+ });
239877
+ }
239629
239878
 
239630
239879
  // src/types/Component.tsx
239631
239880
  var import_jsx_runtime87 = __toESM(require_jsx_runtime(), 1);
@@ -239667,7 +239916,30 @@ function ComponentLeaf({ stream: stream3 }) {
239667
239916
  () => ({ ...components3, ...stream3.data, ...eventState }),
239668
239917
  [stream3.data, components3, eventState]
239669
239918
  );
239670
- if (!stream3.jsx) return null;
239919
+ if (!stream3.jsx) {
239920
+ const start5 = stream3.start ?? 0;
239921
+ const end3 = stream3.end ?? start5 + (stream3.duration ?? 1);
239922
+ const durFrames2 = Math.max(1, Math.floor(fps * (end3 - start5)));
239923
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
239924
+ Sequence,
239925
+ {
239926
+ durationInFrames: durFrames2,
239927
+ from: Math.floor(fps * start5),
239928
+ layout: "none",
239929
+ children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
239930
+ EventAwareComponent,
239931
+ {
239932
+ jsx: "",
239933
+ components: components3,
239934
+ data: bindings,
239935
+ action: { start: start5, end: end3 },
239936
+ durFrames: durFrames2,
239937
+ on: stream3.on
239938
+ }
239939
+ )
239940
+ }
239941
+ );
239942
+ }
239671
239943
  const start4 = stream3.start ?? 0;
239672
239944
  const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
239673
239945
  const durFrames = Math.max(1, Math.floor(fps * (end2 - start4)));
@@ -239700,6 +239972,7 @@ function EventAwareComponent({
239700
239972
  on: on3
239701
239973
  }) {
239702
239974
  useFrameEvents(on3, durFrames);
239975
+ if (!jsx84) return null;
239703
239976
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
239704
239977
  TweenedJsxParser,
239705
239978
  {
@@ -242070,6 +242343,20 @@ var Rectangle = (0, import_react127.forwardRef)((props, ref2) => {
242070
242343
  return import_react127.default.createElement(import_react127.default.Fragment, null);
242071
242344
  });
242072
242345
  Rectangle.displayName = "Rectangle";
242346
+ function useMap3D(id39 = null) {
242347
+ const apiContext = (0, import_react127.useContext)(APIProviderContext);
242348
+ const map3dContext = (0, import_react127.useContext)(GoogleMaps3DContext);
242349
+ if (apiContext === null) {
242350
+ logErrorOnce("useMap3D(): failed to retrieve APIProviderContext. Make sure that the <APIProvider> component exists and that the component you are calling `useMap3D()` from is a child of the <APIProvider>.");
242351
+ return null;
242352
+ }
242353
+ const { map3dInstances } = apiContext;
242354
+ if (id39 !== null)
242355
+ return map3dInstances[id39] || null;
242356
+ if (map3dContext === null || map3dContext === void 0 ? void 0 : map3dContext.map3d)
242357
+ return map3dContext.map3d;
242358
+ return map3dInstances["default"] || null;
242359
+ }
242073
242360
 
242074
242361
  // src/types/Map.tsx
242075
242362
  var import_jsx_runtime89 = __toESM(require_jsx_runtime(), 1);
@@ -242095,14 +242382,10 @@ function MapLeaf({ stream: stream3 }) {
242095
242382
  const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242096
242383
  const totalDur = stream3.durationInSeconds ?? end2;
242097
242384
  const apiKey = resolveApiKey(stream3);
242385
+ const view = stream3.view ?? "route";
242098
242386
  useFrameEvents(stream3.on, Math.max(1, Math.floor(totalDur * fps)));
242099
- if (waypoints.length === 0) return null;
242387
+ if ((view === "route" || view === "cinematic") && waypoints.length === 0) return null;
242100
242388
  const durFrames = Math.max(1, Math.floor(fps * (end2 - start4)));
242101
- const center4 = stream3.center ?? { lat: waypoints[0].lat, lng: waypoints[0].lng };
242102
- const zoom2 = stream3.zoom ?? 10;
242103
- const mapType = stream3.mapType ?? "roadmap";
242104
- const travelMode = stream3.travelMode ?? "DRIVING";
242105
- const markerEmoji = stream3.routeMarker ?? "\u{1F697}";
242106
242389
  const mapLocale = import_react128.default.useMemo(
242107
242390
  () => resolveMapLocale(stream3.language, stream3.region),
242108
242391
  [stream3.language, stream3.region]
@@ -242110,7 +242393,7 @@ function MapLeaf({ stream: stream3 }) {
242110
242393
  const mapLoadHandleRef = import_react128.default.useRef(null);
242111
242394
  const mapLoadContinuedRef = import_react128.default.useRef(false);
242112
242395
  import_react128.default.useEffect(() => {
242113
- mapLoadHandleRef.current = delayRender("Waiting for map tiles to load...");
242396
+ mapLoadHandleRef.current = delayRender("Waiting for map to load...");
242114
242397
  mapLoadContinuedRef.current = false;
242115
242398
  const fallbackTimer = window.setTimeout(() => {
242116
242399
  if (!mapLoadContinuedRef.current && mapLoadHandleRef.current !== null) {
@@ -242127,63 +242410,369 @@ function MapLeaf({ stream: stream3 }) {
242127
242410
  mapLoadHandleRef.current = null;
242128
242411
  };
242129
242412
  }, [stream3.id, start4, end2]);
242130
- const handleTilesLoaded = import_react128.default.useCallback(() => {
242413
+ const handleMapReady = import_react128.default.useCallback(() => {
242131
242414
  if (!mapLoadContinuedRef.current && mapLoadHandleRef.current !== null) {
242132
242415
  continueRender(mapLoadHandleRef.current);
242133
242416
  mapLoadContinuedRef.current = true;
242134
242417
  }
242135
242418
  }, []);
242419
+ const use3d = view === "cinematic" && stream3.cinematic?.fallback === "none" && (stream3.cinematic.mode === "flyTo" || stream3.cinematic.mode === "orbit");
242136
242420
  return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242137
242421
  Sequence,
242138
242422
  {
242139
242423
  durationInFrames: durFrames,
242140
242424
  from: Math.floor(fps * start4),
242141
242425
  layout: "none",
242142
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242426
+ children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(
242143
242427
  APIProvider,
242144
242428
  {
242145
242429
  apiKey,
242146
242430
  language: mapLocale.language,
242147
242431
  region: mapLocale.region,
242148
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242149
- Map4,
242150
- {
242151
- mapId: String(stream3.id ?? "map"),
242152
- defaultCenter: center4,
242153
- defaultZoom: zoom2,
242154
- defaultOptions: {
242155
- mapTypeId: mapType,
242156
- disableDefaultUI: true,
242157
- zoomControl: false
242158
- },
242159
- onTilesLoaded: handleTilesLoaded,
242160
- style: { width: "100%", height: "100%", position: "absolute" },
242161
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242162
- RouteWithMarker,
242163
- {
242164
- waypoints,
242165
- travelMode,
242166
- markerEmoji,
242167
- actionDuration: end2 - start4
242168
- }
242169
- )
242170
- }
242171
- )
242432
+ children: [
242433
+ view === "overview" && /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(OverviewMap, { stream: stream3, onTilesLoaded: handleMapReady }),
242434
+ view === "cinematic" && (use3d ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(CinematicMap3D, { stream: stream3, onReady: handleMapReady }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(CinematicMap, { stream: stream3, onTilesLoaded: handleMapReady })),
242435
+ view === "streetview" && /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(StreetViewLeaf, { stream: stream3, onPanoReady: handleMapReady }),
242436
+ view === "route" && /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(RouteMap, { stream: stream3, onTilesLoaded: handleMapReady })
242437
+ ]
242172
242438
  }
242173
242439
  )
242174
242440
  }
242175
242441
  );
242176
242442
  }
242443
+ function OverviewMap({
242444
+ stream: stream3,
242445
+ onTilesLoaded
242446
+ }) {
242447
+ const { fps } = useVideoConfig();
242448
+ const frame2 = useCurrentFrame();
242449
+ const start4 = stream3.start ?? 0;
242450
+ const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242451
+ const fallbackCenter = stream3.center ?? { lat: 37.7749, lng: -122.4194 };
242452
+ const center4 = {
242453
+ lat: resolveTween(frame2, fps, stream3.camera?.center?.lat, start4, end2, fallbackCenter.lat),
242454
+ lng: resolveTween(frame2, fps, stream3.camera?.center?.lng, start4, end2, fallbackCenter.lng)
242455
+ };
242456
+ const zoom2 = resolveTween(frame2, fps, stream3.camera?.zoom, start4, end2, stream3.zoom ?? 10);
242457
+ const heading3 = resolveTween(frame2, fps, stream3.camera?.heading, start4, end2, 0);
242458
+ const tilt = resolveTween(frame2, fps, stream3.camera?.tilt, start4, end2, 0);
242459
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242460
+ Map4,
242461
+ {
242462
+ mapId: String(stream3.id ?? "map-overview"),
242463
+ center: center4,
242464
+ zoom: zoom2,
242465
+ heading: heading3,
242466
+ tilt,
242467
+ mapTypeId: stream3.mapType ?? "roadmap",
242468
+ disableDefaultUI: true,
242469
+ zoomControl: false,
242470
+ onTilesLoaded,
242471
+ style: { width: "100%", height: "100%", position: "absolute" }
242472
+ }
242473
+ );
242474
+ }
242475
+ function RouteMap({
242476
+ stream: stream3,
242477
+ onTilesLoaded
242478
+ }) {
242479
+ const { fps } = useVideoConfig();
242480
+ const frame2 = useCurrentFrame();
242481
+ const waypoints = stream3.waypoints ?? [];
242482
+ const start4 = stream3.start ?? 0;
242483
+ const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242484
+ const fallbackCenter = stream3.center ?? { lat: waypoints[0].lat, lng: waypoints[0].lng };
242485
+ const center4 = {
242486
+ lat: resolveTween(frame2, fps, stream3.camera?.center?.lat, start4, end2, fallbackCenter.lat),
242487
+ lng: resolveTween(frame2, fps, stream3.camera?.center?.lng, start4, end2, fallbackCenter.lng)
242488
+ };
242489
+ const zoom2 = resolveTween(frame2, fps, stream3.camera?.zoom, start4, end2, stream3.zoom ?? 10);
242490
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242491
+ Map4,
242492
+ {
242493
+ mapId: String(stream3.id ?? "map-route"),
242494
+ center: center4,
242495
+ zoom: zoom2,
242496
+ mapTypeId: stream3.mapType ?? "roadmap",
242497
+ disableDefaultUI: true,
242498
+ zoomControl: false,
242499
+ onTilesLoaded,
242500
+ style: { width: "100%", height: "100%", position: "absolute" },
242501
+ children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242502
+ RouteWithMarker,
242503
+ {
242504
+ waypoints,
242505
+ travelMode: stream3.travelMode ?? "DRIVING",
242506
+ markerEmoji: stream3.routeMarker ?? "\u{1F697}",
242507
+ actionDuration: end2 - start4
242508
+ }
242509
+ )
242510
+ }
242511
+ );
242512
+ }
242513
+ function CinematicMap({
242514
+ stream: stream3,
242515
+ onTilesLoaded
242516
+ }) {
242517
+ const { fps } = useVideoConfig();
242518
+ const frame2 = useCurrentFrame();
242519
+ const waypoints = stream3.waypoints ?? [];
242520
+ const start4 = stream3.start ?? 0;
242521
+ const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242522
+ const actionDuration = Math.max(0.1, end2 - start4);
242523
+ const mode = stream3.cinematic?.mode ?? "flyAlong";
242524
+ const headingFollow = stream3.cinematic?.headingFollow ?? true;
242525
+ const leg = useRouteLeg(waypoints, stream3.travelMode ?? "DRIVING");
242526
+ const seconds2 = frame2 / fps;
242527
+ const pos = routePositionAt(leg, waypoints, actionDuration, seconds2);
242528
+ const lookahead2 = routePositionAt(leg, waypoints, actionDuration, seconds2 + 0.4);
242529
+ const first3 = waypoints[0];
242530
+ const fallbackCenter = stream3.center ?? first3;
242531
+ let center4;
242532
+ if (mode === "flyAlong" && pos) {
242533
+ center4 = pos;
242534
+ } else if (mode === "flyTo") {
242535
+ center4 = {
242536
+ lat: resolveTween(frame2, fps, stream3.camera?.center?.lat, start4, end2, first3.lat),
242537
+ lng: resolveTween(frame2, fps, stream3.camera?.center?.lng, start4, end2, first3.lng)
242538
+ };
242539
+ } else {
242540
+ center4 = fallbackCenter;
242541
+ }
242542
+ let heading3 = resolveTween(frame2, fps, stream3.camera?.heading, start4, end2, 0);
242543
+ if (headingFollow && pos && lookahead2) {
242544
+ heading3 = bearing(pos, lookahead2);
242545
+ }
242546
+ const tilt = resolveTween(frame2, fps, stream3.cinematic?.tilt, start4, end2, 45);
242547
+ const zoom2 = resolveTween(frame2, fps, stream3.camera?.zoom, start4, end2, stream3.zoom ?? 13);
242548
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242549
+ Map4,
242550
+ {
242551
+ mapId: String(stream3.id ?? "map-cinematic"),
242552
+ center: center4,
242553
+ zoom: zoom2,
242554
+ heading: heading3,
242555
+ tilt,
242556
+ mapTypeId: stream3.mapType ?? "roadmap",
242557
+ disableDefaultUI: true,
242558
+ zoomControl: false,
242559
+ onTilesLoaded,
242560
+ style: { width: "100%", height: "100%", position: "absolute" },
242561
+ children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242562
+ RouteWithMarker,
242563
+ {
242564
+ waypoints,
242565
+ travelMode: stream3.travelMode ?? "DRIVING",
242566
+ markerEmoji: stream3.routeMarker ?? "\u{1F697}",
242567
+ actionDuration
242568
+ }
242569
+ )
242570
+ }
242571
+ );
242572
+ }
242573
+ function CinematicMap3D({
242574
+ stream: stream3,
242575
+ onReady
242576
+ }) {
242577
+ const { fps } = useVideoConfig();
242578
+ const frame2 = useCurrentFrame();
242579
+ const waypoints = stream3.waypoints ?? [];
242580
+ const start4 = stream3.start ?? 0;
242581
+ const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242582
+ const actionDuration = Math.max(0.1, end2 - start4);
242583
+ const first3 = waypoints[0] ?? { lat: 37.7749, lng: -122.4194 };
242584
+ const map3dRef = import_react128.default.useRef(null);
242585
+ const map3d = useMap3D();
242586
+ const leg = useRouteLeg(waypoints, stream3.travelMode ?? "DRIVING");
242587
+ const range3 = resolveTween(frame2, fps, stream3.cinematic?.range, start4, end2, 2e3);
242588
+ const tilt = resolveTween(frame2, fps, stream3.cinematic?.tilt, start4, end2, 60);
242589
+ const roll = resolveTween(frame2, fps, stream3.cinematic?.roll, start4, end2, 0);
242590
+ const heading3 = resolveTween(frame2, fps, stream3.camera?.heading, start4, end2, 0);
242591
+ const center4 = {
242592
+ lat: resolveTween(frame2, fps, stream3.camera?.center?.lat, start4, end2, first3.lat),
242593
+ lng: resolveTween(frame2, fps, stream3.camera?.center?.lng, start4, end2, first3.lng),
242594
+ altitude: stream3.cinematic?.altitude ?? 100
242595
+ };
242596
+ const seconds2 = frame2 / fps;
242597
+ const pos = routePositionAt(leg, waypoints, actionDuration, seconds2);
242598
+ const legPath = import_react128.default.useMemo(() => {
242599
+ if (!leg) return null;
242600
+ const pts2 = [];
242601
+ for (const step3 of leg.steps ?? []) {
242602
+ for (const p4 of step3.path ?? []) pts2.push({ lat: p4.lat(), lng: p4.lng(), altitude: 0 });
242603
+ }
242604
+ return pts2.length ? pts2 : null;
242605
+ }, [leg]);
242606
+ import_react128.default.useEffect(() => {
242607
+ if (!map3d || !legPath) return;
242608
+ const el = document.createElement("gmp-polyline-3d");
242609
+ el.coordinates = legPath;
242610
+ el.strokeColor = stream3.routeColor ?? "#4285F4";
242611
+ el.strokeWidth = stream3.routeWeight ?? 4;
242612
+ map3d.appendChild(el);
242613
+ return () => {
242614
+ el.remove();
242615
+ };
242616
+ }, [map3d, legPath, stream3.routeColor, stream3.routeWeight]);
242617
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242618
+ Map3D,
242619
+ {
242620
+ ref: map3dRef,
242621
+ mode: stream3.mapType === "hybrid" ? "HYBRID" : "SATELLITE",
242622
+ center: center4,
242623
+ range: range3,
242624
+ heading: heading3,
242625
+ tilt,
242626
+ roll,
242627
+ onSteadyChange: onReady,
242628
+ onAnimationEnd: onReady,
242629
+ style: { width: "100%", height: "100%", position: "absolute" },
242630
+ children: pos ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Marker3D, { position: { lat: pos.lat, lng: pos.lng, altitude: 0 } }) : null
242631
+ }
242632
+ );
242633
+ }
242634
+ function StreetViewLeaf({
242635
+ stream: stream3,
242636
+ onPanoReady
242637
+ }) {
242638
+ const containerRef = import_react128.default.useRef(null);
242639
+ const svLibrary = useMapsLibrary("streetView");
242640
+ const [pano, setPano] = import_react128.default.useState(null);
242641
+ const { fps } = useVideoConfig();
242642
+ const frame2 = useCurrentFrame();
242643
+ const start4 = stream3.start ?? 0;
242644
+ const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242645
+ const sv = stream3.streetView;
242646
+ import_react128.default.useEffect(() => {
242647
+ if (!svLibrary || !containerRef.current) return;
242648
+ const pan2 = new svLibrary.StreetViewPanorama(containerRef.current, {
242649
+ disableDefaultUI: true
242650
+ });
242651
+ if (sv?.pano) {
242652
+ pan2.setPano(sv.pano);
242653
+ } else if (sv?.location) {
242654
+ pan2.setPosition(sv.location);
242655
+ } else if (sv?.route?.length) {
242656
+ pan2.setPosition(sv.route[0]);
242657
+ }
242658
+ if (sv?.pov) {
242659
+ pan2.setPov({
242660
+ heading: typeof sv.pov.heading === "number" ? sv.pov.heading : 0,
242661
+ pitch: typeof sv.pov.pitch === "number" ? sv.pov.pitch : 0
242662
+ });
242663
+ }
242664
+ if (typeof sv?.zoom === "number") {
242665
+ pan2.setZoom(sv.zoom);
242666
+ }
242667
+ setPano(pan2);
242668
+ return () => {
242669
+ pan2.setVisible(false);
242670
+ };
242671
+ }, [svLibrary, stream3.id, sv?.pano, sv?.location, sv?.route, sv?.pov, sv?.zoom]);
242672
+ import_react128.default.useEffect(() => {
242673
+ if (!pano) return;
242674
+ const pov = pano.getPov();
242675
+ const heading3 = resolveTween(frame2, fps, sv?.pov?.heading, start4, end2, pov.heading);
242676
+ const pitch = resolveTween(frame2, fps, sv?.pov?.pitch, start4, end2, pov.pitch);
242677
+ pano.setPov({ heading: heading3, pitch });
242678
+ const zoom2 = resolveTween(frame2, fps, sv?.zoom, start4, end2, pano.getZoom() ?? 0);
242679
+ pano.setZoom(zoom2);
242680
+ let movedPosition = false;
242681
+ if (sv?.route && sv.route.length > 1) {
242682
+ const t4 = Math.min(Math.max(frame2 / fps / Math.max(0.1, end2 - start4), 0), 1);
242683
+ const total = sv.route.length - 1;
242684
+ const segI = Math.min(Math.floor(t4 * total), total - 1);
242685
+ const segT = t4 * total - segI;
242686
+ const a3 = sv.route[segI];
242687
+ const b5 = sv.route[segI + 1];
242688
+ pano.setPosition({
242689
+ lat: a3.lat + (b5.lat - a3.lat) * segT,
242690
+ lng: a3.lng + (b5.lng - a3.lng) * segT
242691
+ });
242692
+ movedPosition = true;
242693
+ }
242694
+ const handle2 = delayRender(`Street View frame ${frame2}`);
242695
+ let done = false;
242696
+ const finish = () => {
242697
+ if (done) return;
242698
+ done = true;
242699
+ continueRender(handle2);
242700
+ onPanoReady();
242701
+ };
242702
+ const graceMs = movedPosition ? 1200 : 400;
242703
+ const onPano = () => setTimeout(finish, graceMs);
242704
+ const lPano = pano.addListener("pano_changed", onPano);
242705
+ const fallback = setTimeout(finish, movedPosition ? 3e3 : 600);
242706
+ return () => {
242707
+ google.maps.event.removeListener(lPano);
242708
+ clearTimeout(fallback);
242709
+ finish();
242710
+ };
242711
+ }, [pano, frame2, fps, start4, end2, sv?.route, sv?.pov, sv?.zoom, onPanoReady]);
242712
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242713
+ "div",
242714
+ {
242715
+ ref: containerRef,
242716
+ style: { width: "100%", height: "100%", position: "absolute" }
242717
+ }
242718
+ );
242719
+ }
242177
242720
  function RouteWithMarker({
242178
242721
  waypoints,
242179
242722
  travelMode,
242180
242723
  markerEmoji,
242181
242724
  actionDuration
242182
242725
  }) {
242726
+ const leg = useRouteLeg(waypoints, travelMode);
242727
+ const position7 = useAnimatedPosition({ leg, actionDuration, waypoints });
242728
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
242729
+ waypoints.map((wp, i5) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: wp, children: wp.media ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242730
+ "div",
242731
+ {
242732
+ style: {
242733
+ width: 44,
242734
+ height: 44,
242735
+ borderRadius: 6,
242736
+ overflow: "hidden",
242737
+ border: "2px solid #fff",
242738
+ boxShadow: "0 1px 4px rgba(0,0,0,0.45)",
242739
+ background: "#fff",
242740
+ position: "relative",
242741
+ top: "-24px"
242742
+ },
242743
+ children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242744
+ "img",
242745
+ {
242746
+ src: wp.media,
242747
+ alt: "",
242748
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
242749
+ }
242750
+ )
242751
+ }
242752
+ ) : wp.label ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242753
+ "div",
242754
+ {
242755
+ style: {
242756
+ background: "rgba(255,255,255,0.9)",
242757
+ borderRadius: "4px",
242758
+ padding: "2px 6px",
242759
+ fontSize: "12px",
242760
+ fontWeight: 700,
242761
+ color: "#333",
242762
+ whiteSpace: "nowrap",
242763
+ position: "relative",
242764
+ top: "-24px"
242765
+ },
242766
+ children: wp.label
242767
+ }
242768
+ ) : null }, i5)),
242769
+ position7 ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: position7, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Pin, { glyphText: markerEmoji, scale: 4 }) }) : null
242770
+ ] });
242771
+ }
242772
+ function useRouteLeg(waypoints, travelMode) {
242183
242773
  const map9 = useMap();
242184
242774
  const routesLibrary = useMapsLibrary("routes");
242185
242775
  const [leg, setLeg] = import_react128.default.useState(null);
242186
- const [routeIndex, setRouteIndex] = import_react128.default.useState(0);
242187
242776
  const handle2 = import_react128.default.useRef(null);
242188
242777
  import_react128.default.useEffect(() => {
242189
242778
  if (!routesLibrary || !map9 || waypoints.length < 2) return;
@@ -242199,7 +242788,6 @@ function RouteWithMarker({
242199
242788
  provideRouteAlternatives: false
242200
242789
  }).then((response) => {
242201
242790
  renderer15.setDirections(response);
242202
- setRouteIndex(0);
242203
242791
  setLeg(response.routes[0]?.legs[0] ?? null);
242204
242792
  if (handle2.current !== null) continueRender(handle2.current);
242205
242793
  }).catch(() => {
@@ -242209,30 +242797,7 @@ function RouteWithMarker({
242209
242797
  renderer15.setMap(null);
242210
242798
  };
242211
242799
  }, [routesLibrary, map9, waypoints, travelMode]);
242212
- import_react128.default.useEffect(() => {
242213
- setRouteIndex((prev2) => prev2);
242214
- }, [routeIndex]);
242215
- const position7 = useAnimatedPosition({ leg, actionDuration, waypoints });
242216
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
242217
- waypoints.map((wp, i5) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: wp, children: wp.label ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242218
- "div",
242219
- {
242220
- style: {
242221
- background: "rgba(255,255,255,0.9)",
242222
- borderRadius: "4px",
242223
- padding: "2px 6px",
242224
- fontSize: "12px",
242225
- fontWeight: 700,
242226
- color: "#333",
242227
- whiteSpace: "nowrap",
242228
- position: "relative",
242229
- top: "-24px"
242230
- },
242231
- children: wp.label
242232
- }
242233
- ) : null }, i5)),
242234
- position7 ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: position7, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Pin, { glyphText: markerEmoji, scale: 4 }) }) : null
242235
- ] });
242800
+ return leg;
242236
242801
  }
242237
242802
  function useAnimatedPosition({
242238
242803
  leg,
@@ -242241,46 +242806,50 @@ function useAnimatedPosition({
242241
242806
  }) {
242242
242807
  const frame2 = useCurrentFrame();
242243
242808
  const { fps } = useVideoConfig();
242244
- return import_react128.default.useMemo(() => {
242245
- if (!leg || !leg.duration?.value) {
242246
- if (waypoints.length < 2) return null;
242247
- const t4 = Math.min(frame2 / (actionDuration * fps), 1);
242248
- const total = waypoints.length - 1;
242249
- const segI = Math.min(Math.floor(t4 * total), total - 1);
242250
- const segT = t4 * total - segI;
242251
- const a3 = waypoints[segI];
242252
- const b5 = waypoints[segI + 1];
242253
- if (!a3 || !b5) return null;
242254
- return {
242255
- lat: a3.lat + (b5.lat - a3.lat) * segT,
242256
- lng: a3.lng + (b5.lng - a3.lng) * segT
242257
- };
242258
- }
242259
- const currentInSecond = frame2 / fps * (leg.duration.value / actionDuration);
242260
- const { step: step3, elapsedInSeconds } = getCurrentStep(leg, currentInSecond);
242261
- if (!step3 || !step3.path) {
242262
- const t4 = Math.min(frame2 / (actionDuration * fps), 1);
242263
- const total = waypoints.length - 1;
242264
- const segI = Math.min(Math.floor(t4 * total), total - 1);
242265
- const segT = t4 * total - segI;
242266
- const a3 = waypoints[segI];
242267
- const b5 = waypoints[segI + 1];
242268
- if (!a3 || !b5) return null;
242269
- return {
242270
- lat: a3.lat + (b5.lat - a3.lat) * segT,
242271
- lng: a3.lng + (b5.lng - a3.lng) * segT
242272
- };
242273
- }
242274
- const stepElapsed = currentInSecond - elapsedInSeconds;
242275
- const stepProgress = stepElapsed / (step3.duration?.value ?? 1);
242276
- const pathIdx = Math.min(
242277
- Math.max(0, Math.floor(stepProgress * step3.path.length)),
242278
- step3.path.length - 1
242279
- );
242280
- const pt = step3.path[pathIdx];
242281
- if (!pt) return null;
242282
- return { lat: pt.lat(), lng: pt.lng() };
242283
- }, [leg, frame2, fps, actionDuration, waypoints]);
242809
+ return import_react128.default.useMemo(
242810
+ () => routePositionAt(leg, waypoints, actionDuration, frame2 / fps),
242811
+ [leg, frame2, fps, actionDuration, waypoints]
242812
+ );
242813
+ }
242814
+ function routePositionAt(leg, waypoints, actionDuration, seconds2) {
242815
+ const linearFallback = () => {
242816
+ if (waypoints.length < 2) return null;
242817
+ const t4 = Math.min(seconds2 / actionDuration, 1);
242818
+ const total = waypoints.length - 1;
242819
+ const segI = Math.min(Math.floor(t4 * total), total - 1);
242820
+ const segT = t4 * total - segI;
242821
+ const a3 = waypoints[segI];
242822
+ const b5 = waypoints[segI + 1];
242823
+ if (!a3 || !b5) return null;
242824
+ return { lat: a3.lat + (b5.lat - a3.lat) * segT, lng: a3.lng + (b5.lng - a3.lng) * segT };
242825
+ };
242826
+ if (!leg || !leg.duration?.value) {
242827
+ return linearFallback();
242828
+ }
242829
+ const currentInSecond = seconds2 * (leg.duration.value / actionDuration);
242830
+ const { step: step3, elapsedInSeconds } = getCurrentStep(leg, currentInSecond);
242831
+ if (!step3 || !step3.path) {
242832
+ return linearFallback();
242833
+ }
242834
+ const stepElapsed = currentInSecond - elapsedInSeconds;
242835
+ const stepProgress = stepElapsed / (step3.duration?.value ?? 1);
242836
+ const pathIdx = Math.min(
242837
+ Math.max(0, Math.floor(stepProgress * step3.path.length)),
242838
+ step3.path.length - 1
242839
+ );
242840
+ const pt = step3.path[pathIdx];
242841
+ if (!pt) return null;
242842
+ return { lat: pt.lat(), lng: pt.lng() };
242843
+ }
242844
+ function bearing(a3, b5) {
242845
+ const toRad = (d4) => d4 * Math.PI / 180;
242846
+ const toDeg = (d4) => d4 * 180 / Math.PI;
242847
+ const lat1 = toRad(a3.lat);
242848
+ const lat2 = toRad(b5.lat);
242849
+ const dLng = toRad(b5.lng - a3.lng);
242850
+ const y7 = Math.sin(dLng) * Math.cos(lat2);
242851
+ const x7 = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng);
242852
+ return (toDeg(Math.atan2(y7, x7)) + 360) % 360;
242284
242853
  }
242285
242854
  function getCurrentStep(leg, currentInSecond) {
242286
242855
  let elapsedInSeconds = 0;
@@ -242999,15 +243568,17 @@ function EffectWrapper({
242999
243568
  const end2 = Math.ceil(endSec * fps);
243000
243569
  const durationInFrames = end2 - start4;
243001
243570
  if (durationInFrames <= 0) return [];
243571
+ const animDurationSec = stream3.animationDurationSeconds ?? stream3.durationInSeconds ?? durationInFrames / fps;
243572
+ const animDurationFrames = Math.ceil(animDurationSec * fps);
243002
243573
  const animation2 = stream3.animation;
243003
243574
  const timingFn = stream3.animationTimingFunction;
243004
243575
  const iterCount = stream3.animationIterationCount ?? 1;
243005
243576
  const style4 = cssJS(stream3.style) ?? {};
243006
243577
  let currentFrame = frame2;
243007
- if (iterCount > 0 && durationInFrames > 0) {
243008
- const iteration = Math.floor((frame2 - start4) / durationInFrames);
243578
+ if (iterCount > 0 && animDurationFrames > 0) {
243579
+ const iteration = Math.floor((frame2 - start4) / animDurationFrames);
243009
243580
  if (iteration < iterCount) {
243010
- currentFrame = start4 + (frame2 - start4) % durationInFrames;
243581
+ currentFrame = start4 + (frame2 - start4) % animDurationFrames;
243011
243582
  }
243012
243583
  }
243013
243584
  if (currentFrame >= start4 && currentFrame < end2) {
@@ -243017,7 +243588,7 @@ function EffectWrapper({
243017
243588
  if (config4) {
243018
243589
  const animStyle = interpolateKeyframes(config4, actionFrame, {
243019
243590
  fps,
243020
- durationInSeconds: durationInFrames / fps,
243591
+ durationInSeconds: animDurationSec,
243021
243592
  timingFunction: timingFn
243022
243593
  });
243023
243594
  if (animStyle) Object.assign(style4, animStyle);
@@ -243025,7 +243596,7 @@ function EffectWrapper({
243025
243596
  }
243026
243597
  }
243027
243598
  return Object.keys(style4).length > 0 ? [style4] : [];
243028
- }, [frame2, fps, startSec, endSec, stream3.animation, stream3.animationTimingFunction, stream3.animationIterationCount, stream3.customKeyframes, stream3.style]);
243599
+ }, [frame2, fps, startSec, endSec, stream3.animation, stream3.animationTimingFunction, stream3.animationIterationCount, stream3.customKeyframes, stream3.style, stream3.animationDurationSeconds, stream3.durationInSeconds]);
243029
243600
  if (styles7.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(import_jsx_runtime92.Fragment, { children: children2 });
243030
243601
  return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
243031
243602
  "div",
@@ -243070,7 +243641,7 @@ function FolderLeaf({ stream: stream3 }) {
243070
243641
  const isRoot = stream3.id === "root";
243071
243642
  const visibleChildren = stream3.children.filter((c5) => c5.visible !== false);
243072
243643
  const bgChildren = visibleChildren.filter((c5) => c5.isBackground);
243073
- const seriesChildren = isSeries ? visibleChildren.filter((c5) => !c5.isBackground) : visibleChildren;
243644
+ const seriesChildren = visibleChildren.filter((c5) => !c5.isBackground);
243074
243645
  const allAudio = seriesChildren.length > 0 && seriesChildren.every((c5) => c5.type === "audio");
243075
243646
  const effectiveTransition = allAudio ? void 0 : transition2;
243076
243647
  const TypedSeries = React47.useMemo(() => {
@@ -257457,11 +258028,12 @@ var image3 = base.extend({
257457
258028
  var component2 = base.extend({
257458
258029
  type: external_exports.literal("component").default("component"),
257459
258030
  jsx: external_exports.string().describe("usage JSX expression compiled at runtime; tag names resolved from imports"),
257460
- 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")
258031
+ data: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("extra variables (e.g. from ~~~md source code fences) available in JSX scope")
257461
258032
  });
257462
258033
  var effect = base.extend({
257463
258034
  type: external_exports.literal("effect").default("effect"),
257464
258035
  animation: external_exports.string().optional().describe("builtin keyframe name or 'custom'"),
258036
+ animationDurationSeconds: external_exports.number().optional().describe("animation duration (separate from wrapper durationInSeconds which getDurationInSeconds may overwrite)"),
257465
258037
  animationTimingFunction: external_exports.enum(["linear", "ease", "ease-in", "ease-out", "ease-in-out"]).optional(),
257466
258038
  animationIterationCount: external_exports.number().default(1),
257467
258039
  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" } }'),
@@ -257495,8 +258067,41 @@ var mapWaypoint = external_exports.object({
257495
258067
  label: external_exports.string().optional(),
257496
258068
  media: external_exports.string().optional().describe("image/video src for waypoint marker")
257497
258069
  });
258070
+ var tweenSpec = external_exports.object({
258071
+ __tween: external_exports.array(external_exports.union([external_exports.number(), external_exports.string()]))
258072
+ });
258073
+ var tweenableNumber = external_exports.union([external_exports.number(), tweenSpec]);
258074
+ var mapCamera = external_exports.object({
258075
+ zoom: tweenableNumber.optional(),
258076
+ center: external_exports.object({ lat: tweenableNumber, lng: tweenableNumber }).optional(),
258077
+ heading: tweenableNumber.optional(),
258078
+ tilt: tweenableNumber.optional()
258079
+ });
258080
+ var mapCinematic = external_exports.object({
258081
+ mode: external_exports.enum(["flyAlong", "flyTo", "orbit"]).optional().describe("camera move; default flyAlong"),
258082
+ followRoute: external_exports.boolean().optional().describe("center follows the animated marker; default true"),
258083
+ headingFollow: external_exports.boolean().optional().describe("heading = route bearing (forward is up); default true"),
258084
+ tilt: tweenableNumber.optional().describe("0 = top-down reveal, 45 = chase; tween(0,45) = tilt reveal; default 45"),
258085
+ range: tweenableNumber.optional().describe("3D camera distance (m); tween(8000,300) = fly-to"),
258086
+ altitude: external_exports.number().optional().describe("3D center altitude (m)"),
258087
+ roll: tweenableNumber.optional().describe("3D bank (deg); default 0"),
258088
+ fallback: external_exports.enum(["2d", "none"]).optional().describe("'2d' renders safe 2D chase; 'none' opts into experimental Map3D; default '2d'")
258089
+ });
258090
+ var mapStreetView = external_exports.object({
258091
+ pano: external_exports.string().optional().describe("explicit panorama id (deterministic)"),
258092
+ location: external_exports.object({ lat: external_exports.number(), lng: external_exports.number() }).optional().describe("else nearest-pano search"),
258093
+ route: external_exports.array(external_exports.object({ lat: external_exports.number(), lng: external_exports.number() })).optional().describe("walk/drive path (nearest pano per stop)"),
258094
+ radius: external_exports.number().optional().describe("nearest-pano search radius (m); default 50"),
258095
+ source: external_exports.enum(["default", "outdoor", "indoor"]).optional().describe("default 'default'"),
258096
+ pov: external_exports.object({
258097
+ heading: tweenableNumber.optional().describe("tween(200,320) = pan"),
258098
+ pitch: tweenableNumber.optional().describe("tween(0,-10) = tilt sweep")
258099
+ }).optional(),
258100
+ zoom: tweenableNumber.optional().describe("street view field-of-view zoom; tween(0,1) = dolly-zoom feel")
258101
+ });
257498
258102
  var mapStream = base.extend({
257499
258103
  type: external_exports.literal("map").default("map"),
258104
+ view: external_exports.enum(["overview", "route", "cinematic", "streetview"]).default("route").describe("camera experience: static/dolly overview, animated route, cinematic flyover, immersive street view"),
257500
258105
  waypoints: external_exports.array(mapWaypoint).default(() => []),
257501
258106
  routeColor: external_exports.string().default("#4285F4"),
257502
258107
  routeWeight: external_exports.number().default(4),
@@ -257507,6 +258112,9 @@ var mapStream = base.extend({
257507
258112
  region: external_exports.string().optional().describe("Google Maps region code, e.g. CN"),
257508
258113
  travelMode: external_exports.enum(["DRIVING", "WALKING", "BICYCLING", "TRANSIT"]).default("DRIVING").describe("Directions API travel mode"),
257509
258114
  routeMarker: external_exports.string().default("\u{1F697}").describe("emoji/character for the animated traveling marker"),
258115
+ camera: mapCamera.optional().describe("generic camera tween (dolly/pan/tilt)"),
258116
+ cinematic: mapCinematic.optional().describe("cinematic camera behavior"),
258117
+ streetView: mapStreetView.optional().describe("immersive street view config"),
257510
258118
  googleMapsApiKey: external_exports.string().optional().describe("injected by compiler from GOOGLE_MAPS_API_KEY env var")
257511
258119
  });
257512
258120
  var stream2 = external_exports.discriminatedUnion("type", [