@lalalic/markcut 3.0.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 (34) hide show
  1. package/package.json +1 -1
  2. package/skills/markcut/SKILL.md +7 -0
  3. package/skills/markcut/docs/map-dynamic-camera.md +244 -0
  4. package/skills/markcut/docs/markdown-descriptive.md +2 -0
  5. package/src/descriptive/compiler.ts +41 -0
  6. package/src/descriptive/dsl.ts +42 -5
  7. package/src/descriptive/markdown.ts +5 -0
  8. package/src/descriptive/resolve.test.ts +5 -5
  9. package/src/descriptive/resolve.ts +51 -12
  10. package/src/player/bundle/player.js +448 -99
  11. package/src/player/pipeline.mjs +64 -13
  12. package/src/player/pipeline.ts +5 -4
  13. package/src/player/server.mjs +22 -42
  14. package/src/render/cli.mjs +54 -3
  15. package/src/render/validate-assets.mjs +140 -0
  16. package/src/schema/index.ts +56 -1
  17. package/src/spots/cli.mjs +266 -0
  18. package/src/types/Map.tsx +501 -127
  19. package/src/utils/tween.ts +49 -1
  20. package/tests/dsl.test.ts +43 -0
  21. package/tests/fixtures/map-dynamic.json +52 -0
  22. package/tests/fixtures/md/animate-diagrams.md +9 -7
  23. package/tests/fixtures/md/map-all-views.md +28 -0
  24. package/tests/md-descriptive.test.ts +58 -0
  25. package/tests/render.test.ts +1 -0
  26. package/tests/schema.test.ts +58 -1
  27. package/tests/validate-assets.test.ts +106 -0
  28. package/B] +0 -2
  29. package/tests/tmp/vision-1785081637127-video/videos/.normalized/segments/test-clip_0to3_seg_1100to3000.mp4 +0 -0
  30. package/tests/tmp/vision-1785081637127-video/videos/.normalized/test-clip_0to3.mp4 +0 -0
  31. package/tests/tmp/vision-1785081637127-video/videos/.normalized/test-clip_audio.mp3 +0 -0
  32. package/tests/tmp/vision-1785081637127-video/videos/metadata.json +0 -9
  33. package/tests/tmp/vision-1785081637127-video/videos/test-clip.mp4 +0 -0
  34. package/tests/tmp/vision-1785081637127-video/videos/test-clip.vtt +0 -5
@@ -239858,6 +239858,23 @@ function useTweenBindings(action) {
239858
239858
  );
239859
239859
  return { tween, interpolate };
239860
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
+ }
239861
239878
 
239862
239879
  // src/types/Component.tsx
239863
239880
  var import_jsx_runtime87 = __toESM(require_jsx_runtime(), 1);
@@ -242326,6 +242343,20 @@ var Rectangle = (0, import_react127.forwardRef)((props, ref2) => {
242326
242343
  return import_react127.default.createElement(import_react127.default.Fragment, null);
242327
242344
  });
242328
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
+ }
242329
242360
 
242330
242361
  // src/types/Map.tsx
242331
242362
  var import_jsx_runtime89 = __toESM(require_jsx_runtime(), 1);
@@ -242351,14 +242382,10 @@ function MapLeaf({ stream: stream3 }) {
242351
242382
  const end2 = stream3.end ?? start4 + (stream3.duration ?? 1);
242352
242383
  const totalDur = stream3.durationInSeconds ?? end2;
242353
242384
  const apiKey = resolveApiKey(stream3);
242385
+ const view = stream3.view ?? "route";
242354
242386
  useFrameEvents(stream3.on, Math.max(1, Math.floor(totalDur * fps)));
242355
- if (waypoints.length === 0) return null;
242387
+ if ((view === "route" || view === "cinematic") && waypoints.length === 0) return null;
242356
242388
  const durFrames = Math.max(1, Math.floor(fps * (end2 - start4)));
242357
- const center4 = stream3.center ?? { lat: waypoints[0].lat, lng: waypoints[0].lng };
242358
- const zoom2 = stream3.zoom ?? 10;
242359
- const mapType = stream3.mapType ?? "roadmap";
242360
- const travelMode = stream3.travelMode ?? "DRIVING";
242361
- const markerEmoji = stream3.routeMarker ?? "\u{1F697}";
242362
242389
  const mapLocale = import_react128.default.useMemo(
242363
242390
  () => resolveMapLocale(stream3.language, stream3.region),
242364
242391
  [stream3.language, stream3.region]
@@ -242366,7 +242393,7 @@ function MapLeaf({ stream: stream3 }) {
242366
242393
  const mapLoadHandleRef = import_react128.default.useRef(null);
242367
242394
  const mapLoadContinuedRef = import_react128.default.useRef(false);
242368
242395
  import_react128.default.useEffect(() => {
242369
- mapLoadHandleRef.current = delayRender("Waiting for map tiles to load...");
242396
+ mapLoadHandleRef.current = delayRender("Waiting for map to load...");
242370
242397
  mapLoadContinuedRef.current = false;
242371
242398
  const fallbackTimer = window.setTimeout(() => {
242372
242399
  if (!mapLoadContinuedRef.current && mapLoadHandleRef.current !== null) {
@@ -242383,63 +242410,369 @@ function MapLeaf({ stream: stream3 }) {
242383
242410
  mapLoadHandleRef.current = null;
242384
242411
  };
242385
242412
  }, [stream3.id, start4, end2]);
242386
- const handleTilesLoaded = import_react128.default.useCallback(() => {
242413
+ const handleMapReady = import_react128.default.useCallback(() => {
242387
242414
  if (!mapLoadContinuedRef.current && mapLoadHandleRef.current !== null) {
242388
242415
  continueRender(mapLoadHandleRef.current);
242389
242416
  mapLoadContinuedRef.current = true;
242390
242417
  }
242391
242418
  }, []);
242419
+ const use3d = view === "cinematic" && stream3.cinematic?.fallback === "none" && (stream3.cinematic.mode === "flyTo" || stream3.cinematic.mode === "orbit");
242392
242420
  return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242393
242421
  Sequence,
242394
242422
  {
242395
242423
  durationInFrames: durFrames,
242396
242424
  from: Math.floor(fps * start4),
242397
242425
  layout: "none",
242398
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242426
+ children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(
242399
242427
  APIProvider,
242400
242428
  {
242401
242429
  apiKey,
242402
242430
  language: mapLocale.language,
242403
242431
  region: mapLocale.region,
242404
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242405
- Map4,
242406
- {
242407
- mapId: String(stream3.id ?? "map"),
242408
- defaultCenter: center4,
242409
- defaultZoom: zoom2,
242410
- defaultOptions: {
242411
- mapTypeId: mapType,
242412
- disableDefaultUI: true,
242413
- zoomControl: false
242414
- },
242415
- onTilesLoaded: handleTilesLoaded,
242416
- style: { width: "100%", height: "100%", position: "absolute" },
242417
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242418
- RouteWithMarker,
242419
- {
242420
- waypoints,
242421
- travelMode,
242422
- markerEmoji,
242423
- actionDuration: end2 - start4
242424
- }
242425
- )
242426
- }
242427
- )
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
+ ]
242438
+ }
242439
+ )
242440
+ }
242441
+ );
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
242428
242568
  }
242429
242569
  )
242430
242570
  }
242431
242571
  );
242432
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
+ }
242433
242720
  function RouteWithMarker({
242434
242721
  waypoints,
242435
242722
  travelMode,
242436
242723
  markerEmoji,
242437
242724
  actionDuration
242438
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) {
242439
242773
  const map9 = useMap();
242440
242774
  const routesLibrary = useMapsLibrary("routes");
242441
242775
  const [leg, setLeg] = import_react128.default.useState(null);
242442
- const [routeIndex, setRouteIndex] = import_react128.default.useState(0);
242443
242776
  const handle2 = import_react128.default.useRef(null);
242444
242777
  import_react128.default.useEffect(() => {
242445
242778
  if (!routesLibrary || !map9 || waypoints.length < 2) return;
@@ -242455,7 +242788,6 @@ function RouteWithMarker({
242455
242788
  provideRouteAlternatives: false
242456
242789
  }).then((response) => {
242457
242790
  renderer15.setDirections(response);
242458
- setRouteIndex(0);
242459
242791
  setLeg(response.routes[0]?.legs[0] ?? null);
242460
242792
  if (handle2.current !== null) continueRender(handle2.current);
242461
242793
  }).catch(() => {
@@ -242465,30 +242797,7 @@ function RouteWithMarker({
242465
242797
  renderer15.setMap(null);
242466
242798
  };
242467
242799
  }, [routesLibrary, map9, waypoints, travelMode]);
242468
- import_react128.default.useEffect(() => {
242469
- setRouteIndex((prev2) => prev2);
242470
- }, [routeIndex]);
242471
- const position7 = useAnimatedPosition({ leg, actionDuration, waypoints });
242472
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
242473
- waypoints.map((wp, i5) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: wp, children: wp.label ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
242474
- "div",
242475
- {
242476
- style: {
242477
- background: "rgba(255,255,255,0.9)",
242478
- borderRadius: "4px",
242479
- padding: "2px 6px",
242480
- fontSize: "12px",
242481
- fontWeight: 700,
242482
- color: "#333",
242483
- whiteSpace: "nowrap",
242484
- position: "relative",
242485
- top: "-24px"
242486
- },
242487
- children: wp.label
242488
- }
242489
- ) : null }, i5)),
242490
- position7 ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(AdvancedMarker, { position: position7, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Pin, { glyphText: markerEmoji, scale: 4 }) }) : null
242491
- ] });
242800
+ return leg;
242492
242801
  }
242493
242802
  function useAnimatedPosition({
242494
242803
  leg,
@@ -242497,46 +242806,50 @@ function useAnimatedPosition({
242497
242806
  }) {
242498
242807
  const frame2 = useCurrentFrame();
242499
242808
  const { fps } = useVideoConfig();
242500
- return import_react128.default.useMemo(() => {
242501
- if (!leg || !leg.duration?.value) {
242502
- if (waypoints.length < 2) return null;
242503
- const t4 = Math.min(frame2 / (actionDuration * fps), 1);
242504
- const total = waypoints.length - 1;
242505
- const segI = Math.min(Math.floor(t4 * total), total - 1);
242506
- const segT = t4 * total - segI;
242507
- const a3 = waypoints[segI];
242508
- const b5 = waypoints[segI + 1];
242509
- if (!a3 || !b5) return null;
242510
- return {
242511
- lat: a3.lat + (b5.lat - a3.lat) * segT,
242512
- lng: a3.lng + (b5.lng - a3.lng) * segT
242513
- };
242514
- }
242515
- const currentInSecond = frame2 / fps * (leg.duration.value / actionDuration);
242516
- const { step: step3, elapsedInSeconds } = getCurrentStep(leg, currentInSecond);
242517
- if (!step3 || !step3.path) {
242518
- const t4 = Math.min(frame2 / (actionDuration * fps), 1);
242519
- const total = waypoints.length - 1;
242520
- const segI = Math.min(Math.floor(t4 * total), total - 1);
242521
- const segT = t4 * total - segI;
242522
- const a3 = waypoints[segI];
242523
- const b5 = waypoints[segI + 1];
242524
- if (!a3 || !b5) return null;
242525
- return {
242526
- lat: a3.lat + (b5.lat - a3.lat) * segT,
242527
- lng: a3.lng + (b5.lng - a3.lng) * segT
242528
- };
242529
- }
242530
- const stepElapsed = currentInSecond - elapsedInSeconds;
242531
- const stepProgress = stepElapsed / (step3.duration?.value ?? 1);
242532
- const pathIdx = Math.min(
242533
- Math.max(0, Math.floor(stepProgress * step3.path.length)),
242534
- step3.path.length - 1
242535
- );
242536
- const pt = step3.path[pathIdx];
242537
- if (!pt) return null;
242538
- return { lat: pt.lat(), lng: pt.lng() };
242539
- }, [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;
242540
242853
  }
242541
242854
  function getCurrentStep(leg, currentInSecond) {
242542
242855
  let elapsedInSeconds = 0;
@@ -257754,8 +258067,41 @@ var mapWaypoint = external_exports.object({
257754
258067
  label: external_exports.string().optional(),
257755
258068
  media: external_exports.string().optional().describe("image/video src for waypoint marker")
257756
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
+ });
257757
258102
  var mapStream = base.extend({
257758
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"),
257759
258105
  waypoints: external_exports.array(mapWaypoint).default(() => []),
257760
258106
  routeColor: external_exports.string().default("#4285F4"),
257761
258107
  routeWeight: external_exports.number().default(4),
@@ -257766,6 +258112,9 @@ var mapStream = base.extend({
257766
258112
  region: external_exports.string().optional().describe("Google Maps region code, e.g. CN"),
257767
258113
  travelMode: external_exports.enum(["DRIVING", "WALKING", "BICYCLING", "TRANSIT"]).default("DRIVING").describe("Directions API travel mode"),
257768
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"),
257769
258118
  googleMapsApiKey: external_exports.string().optional().describe("injected by compiler from GOOGLE_MAPS_API_KEY env var")
257770
258119
  });
257771
258120
  var stream2 = external_exports.discriminatedUnion("type", [