@tangle-network/agent-app 0.42.4 → 0.42.6

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 (48) hide show
  1. package/dist/{DesignCanvas-AOIVN3SJ.js → DesignCanvas-GUTCY763.js} +2 -2
  2. package/dist/{DesignCanvasEditor-QQNAZRJQ.js → DesignCanvasEditor-ZO4V7H6A.js} +4 -4
  3. package/dist/{TimelineEditor-Z7C6BFP5.js → TimelineEditor-SNOAGBV6.js} +2 -2
  4. package/dist/brand/index.d.ts +35 -0
  5. package/dist/brand/index.js +89 -0
  6. package/dist/brand/index.js.map +1 -0
  7. package/dist/{chunk-YYYXGLLQ.js → chunk-6JYDS34Y.js} +184 -54
  8. package/dist/chunk-6JYDS34Y.js.map +1 -0
  9. package/dist/{chunk-FJUOBNIY.js → chunk-A5Z53E3A.js} +3 -3
  10. package/dist/{chunk-V5XZKHCL.js → chunk-AFQXKJAE.js} +132 -2
  11. package/dist/chunk-AFQXKJAE.js.map +1 -0
  12. package/dist/{chunk-S6WS2B32.js → chunk-EX3LD5G6.js} +322 -122
  13. package/dist/chunk-EX3LD5G6.js.map +1 -0
  14. package/dist/{chunk-DTS5TZRN.js → chunk-MFI4ZM7W.js} +2 -2
  15. package/dist/chunk-MFI4ZM7W.js.map +1 -0
  16. package/dist/{chunk-YEWTVDDT.js → chunk-OVUQNKLM.js} +379 -323
  17. package/dist/chunk-OVUQNKLM.js.map +1 -0
  18. package/dist/chunk-ZN5J47UX.js +1 -0
  19. package/dist/chunk-ZN5J47UX.js.map +1 -0
  20. package/dist/{command-stack-CkjelfK_.d.ts → command-stack-CcWi_2bN.d.ts} +15 -0
  21. package/dist/design-canvas-react/engine.d.ts +2 -2
  22. package/dist/design-canvas-react/engine.js +5 -6
  23. package/dist/design-canvas-react/index.d.ts +61 -8
  24. package/dist/design-canvas-react/index.js +10 -9
  25. package/dist/design-canvas-react/lazy.d.ts +2 -2
  26. package/dist/design-canvas-react/lazy.js +1 -1
  27. package/dist/{lazy-BHHqS8-j.d.ts → lazy-2XBVp1_e.d.ts} +6 -2
  28. package/dist/sequences-react/index.d.ts +94 -2
  29. package/dist/sequences-react/index.js +12 -2
  30. package/dist/sequences-react/index.js.map +1 -1
  31. package/dist/studio/index.d.ts +1 -1
  32. package/dist/studio/index.js +1 -1
  33. package/dist/studio-react/index.js +1 -1
  34. package/dist/web-react/index.d.ts +73 -6
  35. package/dist/web-react/index.js +453 -210
  36. package/dist/web-react/index.js.map +1 -1
  37. package/package.json +14 -9
  38. package/dist/chunk-DTS5TZRN.js.map +0 -1
  39. package/dist/chunk-S6WS2B32.js.map +0 -1
  40. package/dist/chunk-SQV7NUEM.js +0 -136
  41. package/dist/chunk-SQV7NUEM.js.map +0 -1
  42. package/dist/chunk-V5XZKHCL.js.map +0 -1
  43. package/dist/chunk-YEWTVDDT.js.map +0 -1
  44. package/dist/chunk-YYYXGLLQ.js.map +0 -1
  45. /package/dist/{DesignCanvas-AOIVN3SJ.js.map → DesignCanvas-GUTCY763.js.map} +0 -0
  46. /package/dist/{DesignCanvasEditor-QQNAZRJQ.js.map → DesignCanvasEditor-ZO4V7H6A.js.map} +0 -0
  47. /package/dist/{TimelineEditor-Z7C6BFP5.js.map → TimelineEditor-SNOAGBV6.js.map} +0 -0
  48. /package/dist/{chunk-FJUOBNIY.js.map → chunk-A5Z53E3A.js.map} +0 -0
@@ -13,6 +13,24 @@ import {
13
13
  // src/sequences-react/components/TimelineEditor.tsx
14
14
  import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState3, useSyncExternalStore } from "react";
15
15
 
16
+ // src/sequences-react/contracts.ts
17
+ var DEFAULT_TIMELINE_LABELS = {
18
+ splitClip: "Split here",
19
+ splitClipAriaLabel: "Split clip at playhead",
20
+ addCaption: "Add caption",
21
+ addCaptionAriaLabel: "Add caption at playhead",
22
+ createExport: "Export\u2026",
23
+ emptyTitle: "This sequence has no tracks yet",
24
+ emptyBody: "Start from a template, drop in a clip, or hand it to the agent.",
25
+ emptyTemplateDoor: "Start from a template",
26
+ emptyClipDoor: "Add a clip",
27
+ emptyAgentDoor: "Ask the agent",
28
+ smallScreenTitle: "Best edited on a larger screen",
29
+ smallScreenBody: "The timeline needs room to scrub and trim. Open this cut on a tablet or desktop to edit.",
30
+ ghostVideoLane: "Video",
31
+ ghostCaptionLane: "Captions"
32
+ };
33
+
16
34
  // src/sequences-react/engine/command-stack.ts
17
35
  var COMMAND_HISTORY_LIMIT = 200;
18
36
  function createCommandStack(initial) {
@@ -1211,7 +1229,7 @@ function TimelineRuler({ fps, durationFrames, zoom, onScrub }) {
1211
1229
  {
1212
1230
  "data-timeline-ruler": true,
1213
1231
  className: "relative h-7 cursor-ew-resize select-none border-b border-[var(--border-default)] bg-[var(--bg-input)]",
1214
- style: { width: `${durationFrames * zoom}px` },
1232
+ style: { width: `${durationFrames * zoom}px`, touchAction: "none" },
1215
1233
  onPointerDown: handlePointerDown,
1216
1234
  onPointerMove: handlePointerMove,
1217
1235
  children: ticks.map((tick) => /* @__PURE__ */ jsx4(
@@ -1604,7 +1622,9 @@ function TimelineClipChip(props) {
1604
1622
  style: {
1605
1623
  left: `${geometry.left}px`,
1606
1624
  width: `${geometry.width}px`,
1607
- transform: shown.translateY !== 0 ? `translateY(${shown.translateY}px)` : void 0
1625
+ transform: shown.translateY !== 0 ? `translateY(${shown.translateY}px)` : void 0,
1626
+ // own the touch gesture so move/trim drags don't trigger lane scroll
1627
+ touchAction: "none"
1608
1628
  },
1609
1629
  onPointerDown: (event) => beginGesture(event, "move"),
1610
1630
  onPointerMove: updateGesture,
@@ -1750,6 +1770,12 @@ var CaptionPlusGlyph = glyph(
1750
1770
  /* @__PURE__ */ jsx6("path", { d: "M12 9v6M9 12h6" })
1751
1771
  ] })
1752
1772
  );
1773
+ var ExportGlyph = glyph(
1774
+ /* @__PURE__ */ jsxs4(Fragment2, { children: [
1775
+ /* @__PURE__ */ jsx6("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
1776
+ /* @__PURE__ */ jsx6("path", { d: "M12 3v12M8 7l4-4 4 4" })
1777
+ ] })
1778
+ );
1753
1779
 
1754
1780
  // src/sequences-react/components/TimelineTrackRow.tsx
1755
1781
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
@@ -1791,7 +1817,7 @@ function TimelineTrackRow(props) {
1791
1817
  "data-lane-kind": track.kind,
1792
1818
  "data-lane-locked": track.locked ? "true" : "false",
1793
1819
  className: `relative ${laneHeight} ${track.muted ? "opacity-60" : ""}`,
1794
- style: { width: `${sequenceDurationFrames * zoom}px` },
1820
+ style: { width: `${sequenceDurationFrames * zoom}px`, touchAction: "none" },
1795
1821
  onPointerDown: handleLanePointerDown,
1796
1822
  children: clips.map((clip) => /* @__PURE__ */ jsx7(
1797
1823
  TimelineClipChip,
@@ -1822,62 +1848,194 @@ function TimelineTrackRow(props) {
1822
1848
  ] });
1823
1849
  }
1824
1850
 
1851
+ // src/sequences-react/components/BrandMark.tsx
1852
+ import { lazy, Suspense } from "react";
1853
+ import { jsx as jsx8 } from "react/jsx-runtime";
1854
+ function MarkSpacer({ size }) {
1855
+ return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, style: { display: "inline-block", width: size, height: size } });
1856
+ }
1857
+ var LazyKnot = lazy(async () => {
1858
+ try {
1859
+ const mod = await import("./brand/index.js");
1860
+ return { default: mod.TangleKnot };
1861
+ } catch {
1862
+ return { default: MarkSpacer };
1863
+ }
1864
+ });
1865
+ function BrandMark({ size, className }) {
1866
+ return /* @__PURE__ */ jsx8(Suspense, { fallback: /* @__PURE__ */ jsx8(MarkSpacer, { size }), children: /* @__PURE__ */ jsx8(LazyKnot, { size, className }) });
1867
+ }
1868
+
1869
+ // src/sequences-react/components/TimelineEmptyState.tsx
1870
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1871
+ var DOOR_BUTTON = "flex min-h-[44px] items-center gap-2 rounded-md border border-[var(--border-default)] bg-[var(--bg-input)] px-3.5 py-2 text-xs font-medium text-[var(--text-secondary)] transition hover:border-[var(--brand-primary)] hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
1872
+ function TimelineEmptyState(props) {
1873
+ const labels = { ...DEFAULT_TIMELINE_LABELS, ...props.labels };
1874
+ const doors = [];
1875
+ if (props.onStartFromTemplate) {
1876
+ doors.push({ key: "template", label: labels.emptyTemplateDoor, primary: true, icon: FilmGlyph, onClick: props.onStartFromTemplate });
1877
+ }
1878
+ if (props.onAddClip) {
1879
+ doors.push({ key: "clip", label: labels.emptyClipDoor, primary: false, icon: CaptionPlusGlyph, onClick: props.onAddClip });
1880
+ }
1881
+ if (props.onAskAgent) {
1882
+ doors.push({ key: "agent", label: labels.emptyAgentDoor, primary: false, icon: AgentGlyph, onClick: props.onAskAgent });
1883
+ }
1884
+ return /* @__PURE__ */ jsxs6(
1885
+ "div",
1886
+ {
1887
+ "data-timeline-empty": true,
1888
+ className: "sticky left-0 flex min-h-[6rem] flex-col items-center justify-center gap-3 px-6 py-9 text-center",
1889
+ style: { width: "100%" },
1890
+ children: [
1891
+ /* @__PURE__ */ jsx9(BrandMark, { size: 28, className: "shrink-0 opacity-90" }),
1892
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
1893
+ /* @__PURE__ */ jsx9("p", { className: "text-sm font-semibold text-[var(--text-primary)]", children: labels.emptyTitle }),
1894
+ /* @__PURE__ */ jsx9("p", { className: "max-w-sm text-xs text-[var(--text-muted)]", children: labels.emptyBody })
1895
+ ] }),
1896
+ doors.length > 0 ? /* @__PURE__ */ jsx9("div", { className: "flex flex-wrap items-center justify-center gap-2 pt-1", children: doors.map(({ key, label, primary, icon: Icon, onClick }) => /* @__PURE__ */ jsxs6(
1897
+ "button",
1898
+ {
1899
+ type: "button",
1900
+ onClick,
1901
+ className: primary ? "flex min-h-[44px] items-center gap-2 rounded-md bg-[var(--brand-primary)] px-3.5 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" : DOOR_BUTTON,
1902
+ children: [
1903
+ /* @__PURE__ */ jsx9(Icon, { className: "h-4 w-4 shrink-0" }),
1904
+ label
1905
+ ]
1906
+ },
1907
+ key
1908
+ )) }) : null
1909
+ ]
1910
+ }
1911
+ );
1912
+ }
1913
+
1914
+ // src/sequences-react/components/TimelineGhostLanes.tsx
1915
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1916
+ function GhostLane({
1917
+ label,
1918
+ icon: Icon,
1919
+ height,
1920
+ laneWidth
1921
+ }) {
1922
+ return /* @__PURE__ */ jsxs7("div", { className: "flex border-b border-[var(--border-default)] last:border-b-0 opacity-60", children: [
1923
+ /* @__PURE__ */ jsxs7(
1924
+ "div",
1925
+ {
1926
+ className: `sticky left-0 z-10 flex w-36 shrink-0 items-center gap-2 border-r border-[var(--border-default)] bg-[var(--bg-input)] px-2.5 ${height}`,
1927
+ children: [
1928
+ /* @__PURE__ */ jsx10(Icon, { className: "h-3.5 w-3.5 shrink-0 text-[var(--text-muted)]" }),
1929
+ /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate text-xs font-medium text-[var(--text-muted)]", children: label })
1930
+ ]
1931
+ }
1932
+ ),
1933
+ /* @__PURE__ */ jsx10("div", { className: `${height}`, style: { width: `${laneWidth}px` } })
1934
+ ] });
1935
+ }
1936
+ function TimelineGhostLanes({ laneWidth, videoLabel, captionLabel }) {
1937
+ return /* @__PURE__ */ jsxs7("div", { "data-timeline-ghost-lanes": true, "aria-hidden": true, className: "pointer-events-none absolute inset-0", children: [
1938
+ /* @__PURE__ */ jsx10(GhostLane, { label: videoLabel, icon: FilmGlyph, height: "h-16", laneWidth }),
1939
+ /* @__PURE__ */ jsx10(GhostLane, { label: captionLabel, icon: CaptionGlyph, height: "h-9", laneWidth })
1940
+ ] });
1941
+ }
1942
+
1943
+ // src/sequences-react/components/TimelineSmallScreenGate.tsx
1944
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1945
+ function TimelineSmallScreenGate({ labels }) {
1946
+ const copy = { ...DEFAULT_TIMELINE_LABELS, ...labels };
1947
+ return /* @__PURE__ */ jsxs8(
1948
+ "div",
1949
+ {
1950
+ "data-timeline-small-screen": true,
1951
+ className: "flex h-full min-h-0 flex-col items-center justify-center gap-3 bg-[var(--bg-input)] px-8 py-12 text-center text-[var(--text-primary)] sm:hidden",
1952
+ children: [
1953
+ /* @__PURE__ */ jsx11(BrandMark, { size: 32, className: "shrink-0 opacity-90" }),
1954
+ /* @__PURE__ */ jsx11("p", { className: "text-base font-semibold", children: copy.smallScreenTitle }),
1955
+ /* @__PURE__ */ jsx11("p", { className: "max-w-xs text-sm text-[var(--text-muted)]", children: copy.smallScreenBody })
1956
+ ]
1957
+ }
1958
+ );
1959
+ }
1960
+
1825
1961
  // src/sequences-react/components/ZoomControl.tsx
1826
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1827
- function ZoomControl({ zoomMath, zoom, onZoomChange }) {
1962
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1963
+ function zoomPercent(zoom, fitZoom) {
1964
+ const base = fitZoom && fitZoom > 0 ? fitZoom : 1;
1965
+ return Math.round(zoom / base * 100);
1966
+ }
1967
+ var ZOOM_STEP_BUTTON = "flex h-7 w-7 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
1968
+ function ZoomControl({ zoomMath, zoom, onZoomChange, fitZoom }) {
1828
1969
  const sliderMin = zoomMath.zoomToSlider(zoomMath.minZoom);
1829
1970
  const sliderMax = zoomMath.zoomToSlider(zoomMath.maxZoom);
1830
1971
  const sliderStep = (sliderMax - sliderMin) / 100;
1831
1972
  const slider = zoomMath.zoomToSlider(zoom);
1973
+ const percent = zoomPercent(zoom, fitZoom);
1832
1974
  function setSlider(next) {
1833
1975
  const clamped = Math.max(sliderMin, Math.min(sliderMax, next));
1834
1976
  onZoomChange(zoomMath.sliderToZoom(clamped));
1835
1977
  }
1836
- return /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
1837
- /* @__PURE__ */ jsx8(
1978
+ return /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
1979
+ /* @__PURE__ */ jsx12(
1838
1980
  "button",
1839
1981
  {
1840
1982
  type: "button",
1841
1983
  "aria-label": "Zoom out",
1842
1984
  onClick: () => setSlider(slider - sliderStep * 10),
1843
- className: "flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]",
1985
+ className: ZOOM_STEP_BUTTON,
1844
1986
  children: "\u2212"
1845
1987
  }
1846
1988
  ),
1847
- /* @__PURE__ */ jsx8(
1989
+ /* @__PURE__ */ jsx12(
1848
1990
  "input",
1849
1991
  {
1850
1992
  type: "range",
1851
1993
  "aria-label": "Timeline zoom",
1994
+ "aria-valuetext": `${percent}%`,
1852
1995
  min: sliderMin,
1853
1996
  max: sliderMax,
1854
1997
  step: sliderStep,
1855
1998
  value: slider,
1856
1999
  onChange: (event) => setSlider(Number(event.target.value)),
1857
- className: "h-1 w-24 cursor-pointer accent-[var(--brand-primary)]"
2000
+ className: "hidden h-1 w-24 cursor-pointer accent-[var(--brand-primary)] sm:block"
1858
2001
  }
1859
2002
  ),
1860
- /* @__PURE__ */ jsx8(
2003
+ /* @__PURE__ */ jsx12(
1861
2004
  "button",
1862
2005
  {
1863
2006
  type: "button",
1864
2007
  "aria-label": "Zoom in",
1865
2008
  onClick: () => setSlider(slider + sliderStep * 10),
1866
- className: "flex h-6 w-6 items-center justify-center rounded border border-[var(--border-default)] text-sm leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)]",
2009
+ className: ZOOM_STEP_BUTTON,
1867
2010
  children: "+"
1868
2011
  }
2012
+ ),
2013
+ /* @__PURE__ */ jsxs9(
2014
+ "output",
2015
+ {
2016
+ "aria-hidden": true,
2017
+ title: fitZoom ? "Zoom (100% = fits the whole sequence)" : "Zoom",
2018
+ onClick: fitZoom ? () => onZoomChange(fitZoom) : void 0,
2019
+ className: `w-11 select-none text-right font-mono text-[11px] tabular-nums text-[var(--text-muted)] ${fitZoom ? "cursor-pointer hover:text-[var(--text-primary)]" : ""}`,
2020
+ children: [
2021
+ percent,
2022
+ "%"
2023
+ ]
2024
+ }
1869
2025
  )
1870
2026
  ] });
1871
2027
  }
1872
2028
 
1873
2029
  // src/sequences-react/components/TimelineEditor.tsx
1874
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2030
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1875
2031
  var SEQUENCE_MEDIA_DRAG_TYPE = "application/x-sequence-media";
1876
2032
  var HISTORY_MIRROR_LIMIT = 200;
1877
2033
  var MIN_ZOOM = 5e-3;
1878
2034
  var MAX_ZOOM = 24;
1879
2035
  var TRACK_HEADER_PX = 144;
1880
- var TRANSPORT_BUTTON = "flex h-7 w-7 items-center justify-center rounded border border-[var(--border-default)] text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] disabled:cursor-default disabled:opacity-40 disabled:hover:text-[var(--text-secondary)]";
2036
+ var TRANSPORT_BUTTON = "flex h-7 w-7 items-center justify-center rounded border border-[var(--border-default)] text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:opacity-40 disabled:hover:text-[var(--text-secondary)]";
2037
+ var EDIT_TOOL_BUTTON = "flex h-7 items-center gap-1.5 rounded border border-[var(--border-default)] px-2 text-xs font-medium text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:opacity-40 disabled:hover:text-[var(--text-secondary)]";
2038
+ var CLUSTER_DIVIDER = "mx-1 h-5 w-px shrink-0 bg-[var(--border-default)]";
1881
2039
  function mintClipId() {
1882
2040
  const uuid = globalThis.crypto && "randomUUID" in globalThis.crypto ? globalThis.crypto.randomUUID() : null;
1883
2041
  return `local-${uuid ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`;
@@ -1899,6 +2057,7 @@ function TimelineEditor(props) {
1899
2057
  const { canWrite, onApplyOperations } = props;
1900
2058
  const fps = props.timeline.sequence.fps;
1901
2059
  const durationFrames = props.timeline.sequence.durationFrames;
2060
+ const labels = useMemo3(() => ({ ...DEFAULT_TIMELINE_LABELS, ...props.labels }), [props.labels]);
1902
2061
  const stack = useMemo3(() => createCommandStack(props.timeline), []);
1903
2062
  const editorState = useSyncExternalStore(stack.subscribe, stack.getState, stack.getState);
1904
2063
  const timeline = editorState.timeline;
@@ -1951,13 +2110,16 @@ function TimelineEditor(props) {
1951
2110
  }, [props.frameProvider]);
1952
2111
  const zoomMath = useMemo3(() => createZoomMath({ minZoom: MIN_ZOOM, maxZoom: MAX_ZOOM }), []);
1953
2112
  const [zoom, setZoom] = useState3(1);
2113
+ const [fitZoom, setFitZoom] = useState3(void 0);
1954
2114
  const trackViewportRef = useRef3(null);
1955
2115
  useEffect3(() => {
1956
2116
  const viewport = trackViewportRef.current;
1957
2117
  if (!viewport) return;
1958
2118
  const laneWidth = viewport.clientWidth - TRACK_HEADER_PX;
1959
2119
  if (laneWidth <= 0) return;
1960
- setZoom(Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, laneWidth / durationFrames)));
2120
+ const fit = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, laneWidth / durationFrames));
2121
+ setFitZoom(fit);
2122
+ setZoom(fit);
1961
2123
  }, []);
1962
2124
  const [snapEnabled, setSnapEnabled] = useState3(true);
1963
2125
  const [activeSnapPoint, setActiveSnapPoint] = useState3(null);
@@ -2336,120 +2498,154 @@ function TimelineEditor(props) {
2336
2498
  );
2337
2499
  }
2338
2500
  const timelineWidth = timeline.sequence.durationFrames * zoom;
2339
- return /* @__PURE__ */ jsx9("div", { className: `flex h-full min-h-0 flex-col bg-[var(--bg-input)] text-[var(--text-primary)] ${props.className ?? ""}`, children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-0 flex-1", children: [
2340
- /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col", children: [
2341
- /* @__PURE__ */ jsx9(PreviewCanvas, { timeline, clock, frameProvider }),
2342
- /* @__PURE__ */ jsxs7("div", { className: "flex h-10 shrink-0 items-center gap-2 border-y border-[var(--border-default)] px-2", children: [
2343
- /* @__PURE__ */ jsx9(
2344
- "button",
2345
- {
2346
- type: "button",
2347
- "aria-label": isPlaying ? "Pause" : "Play",
2348
- onClick: togglePlayback,
2349
- className: TRANSPORT_BUTTON,
2350
- children: isPlaying ? /* @__PURE__ */ jsx9(PauseGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx9(PlayGlyph, { className: "h-3.5 w-3.5" })
2351
- }
2352
- ),
2353
- /* @__PURE__ */ jsxs7("span", { className: "font-mono text-xs tabular-nums text-[var(--text-secondary)]", children: [
2354
- formatTimecode(playheadFrame, fps),
2355
- /* @__PURE__ */ jsxs7("span", { className: "text-[var(--text-muted)]", children: [
2356
- " / ",
2357
- formatTimecode(timeline.sequence.durationFrames, fps)
2358
- ] })
2501
+ return /* @__PURE__ */ jsxs10("div", { className: `flex h-full min-h-0 flex-col bg-[var(--bg-input)] text-[var(--text-primary)] ${props.className ?? ""}`, children: [
2502
+ /* @__PURE__ */ jsx13(TimelineSmallScreenGate, { labels: props.labels }),
2503
+ /* @__PURE__ */ jsxs10("div", { className: "hidden min-h-0 flex-1 flex-col sm:flex lg:flex-row", children: [
2504
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
2505
+ /* @__PURE__ */ jsx13(PreviewCanvas, { timeline, clock, frameProvider }),
2506
+ /* @__PURE__ */ jsxs10("div", { "aria-label": "Timeline controls", className: "flex h-11 shrink-0 items-center gap-1.5 overflow-x-auto border-y border-[var(--border-default)] px-2", children: [
2507
+ /* @__PURE__ */ jsx13(
2508
+ "button",
2509
+ {
2510
+ type: "button",
2511
+ "aria-label": isPlaying ? "Pause" : "Play",
2512
+ onClick: togglePlayback,
2513
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-[var(--brand-primary)] text-[hsl(var(--primary-foreground))] transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2514
+ children: isPlaying ? /* @__PURE__ */ jsx13(PauseGlyph, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx13(PlayGlyph, { className: "h-4 w-4" })
2515
+ }
2516
+ ),
2517
+ /* @__PURE__ */ jsxs10("span", { className: "shrink-0 font-mono text-xs tabular-nums text-[var(--text-secondary)]", children: [
2518
+ formatTimecode(playheadFrame, fps),
2519
+ /* @__PURE__ */ jsxs10("span", { className: "text-[var(--text-muted)]", children: [
2520
+ " / ",
2521
+ formatTimecode(timeline.sequence.durationFrames, fps)
2522
+ ] })
2523
+ ] }),
2524
+ /* @__PURE__ */ jsx13("div", { className: CLUSTER_DIVIDER }),
2525
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": "Undo", disabled: !stack.canUndo() || !canWrite, onClick: undoLast, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx13(UndoGlyph, { className: "h-3.5 w-3.5" }) }),
2526
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": "Redo", disabled: !stack.canRedo() || !canWrite, onClick: redoLast, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx13(RedoGlyph, { className: "h-3.5 w-3.5" }) }),
2527
+ /* @__PURE__ */ jsx13(
2528
+ "button",
2529
+ {
2530
+ type: "button",
2531
+ "aria-label": "Toggle snapping",
2532
+ "aria-pressed": snapEnabled,
2533
+ onClick: () => setSnapEnabled((current) => !current),
2534
+ className: `${TRANSPORT_BUTTON} ${snapEnabled ? "border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]" : ""}`,
2535
+ children: /* @__PURE__ */ jsx13(MagnetGlyph, { className: "h-3.5 w-3.5" })
2536
+ }
2537
+ ),
2538
+ canWrite ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
2539
+ /* @__PURE__ */ jsx13("div", { className: CLUSTER_DIVIDER }),
2540
+ /* @__PURE__ */ jsxs10("button", { type: "button", "aria-label": labels.splitClipAriaLabel, disabled: !splittableClip, onClick: splitAtPlayhead, className: EDIT_TOOL_BUTTON, children: [
2541
+ /* @__PURE__ */ jsx13(ScissorsGlyph, { className: "h-3.5 w-3.5 shrink-0" }),
2542
+ /* @__PURE__ */ jsx13("span", { className: "hidden md:inline", children: labels.splitClip })
2543
+ ] }),
2544
+ /* @__PURE__ */ jsxs10("button", { type: "button", "aria-label": labels.addCaptionAriaLabel, onClick: addCaptionAtPlayhead, className: EDIT_TOOL_BUTTON, children: [
2545
+ /* @__PURE__ */ jsx13(CaptionPlusGlyph, { className: "h-3.5 w-3.5 shrink-0" }),
2546
+ /* @__PURE__ */ jsx13("span", { className: "hidden md:inline", children: labels.addCaption })
2547
+ ] })
2548
+ ] }) : null,
2549
+ /* @__PURE__ */ jsx13("div", { className: "min-w-2 flex-1" }),
2550
+ /* @__PURE__ */ jsx13(ZoomControl, { zoomMath, zoom, onZoomChange: setZoom, fitZoom }),
2551
+ props.onCreateExport ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
2552
+ /* @__PURE__ */ jsx13("div", { className: CLUSTER_DIVIDER }),
2553
+ /* @__PURE__ */ jsxs10(
2554
+ "button",
2555
+ {
2556
+ type: "button",
2557
+ "aria-label": labels.createExport,
2558
+ onClick: props.onCreateExport,
2559
+ className: "flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-[var(--brand-primary)] px-2.5 text-xs font-semibold text-[var(--brand-primary)] transition hover:bg-[hsl(var(--primary)/0.1)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2560
+ children: [
2561
+ /* @__PURE__ */ jsx13(ExportGlyph, { className: "h-3.5 w-3.5 shrink-0" }),
2562
+ /* @__PURE__ */ jsx13("span", { className: "hidden md:inline", children: labels.createExport })
2563
+ ]
2564
+ }
2565
+ )
2566
+ ] }) : null
2359
2567
  ] }),
2360
- /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-[var(--border-default)]" }),
2361
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Undo", disabled: !stack.canUndo() || !canWrite, onClick: undoLast, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx9(UndoGlyph, { className: "h-3.5 w-3.5" }) }),
2362
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Redo", disabled: !stack.canRedo() || !canWrite, onClick: redoLast, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx9(RedoGlyph, { className: "h-3.5 w-3.5" }) }),
2363
- /* @__PURE__ */ jsx9(
2364
- "button",
2365
- {
2366
- type: "button",
2367
- "aria-label": "Toggle snapping",
2368
- "aria-pressed": snapEnabled,
2369
- onClick: () => setSnapEnabled((current) => !current),
2370
- className: `${TRANSPORT_BUTTON} ${snapEnabled ? "border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]" : ""}`,
2371
- children: /* @__PURE__ */ jsx9(MagnetGlyph, { className: "h-3.5 w-3.5" })
2372
- }
2373
- ),
2374
- canWrite ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
2375
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Split clip at playhead", disabled: !splittableClip, onClick: splitAtPlayhead, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx9(ScissorsGlyph, { className: "h-3.5 w-3.5" }) }),
2376
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Add caption at playhead", onClick: addCaptionAtPlayhead, className: TRANSPORT_BUTTON, children: /* @__PURE__ */ jsx9(CaptionPlusGlyph, { className: "h-3.5 w-3.5" }) })
2568
+ commitError ? /* @__PURE__ */ jsxs10("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-[hsl(var(--destructive)/0.3)] bg-[hsl(var(--destructive)/0.1)] px-3 py-1.5 text-xs text-[var(--text-danger)]", role: "alert", children: [
2569
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate", children: commitError }),
2570
+ /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setCommitError(null), className: "shrink-0 underline-offset-2 hover:underline", children: "Dismiss" })
2377
2571
  ] }) : null,
2378
- /* @__PURE__ */ jsx9("div", { className: "flex-1" }),
2379
- /* @__PURE__ */ jsx9(ZoomControl, { zoomMath, zoom, onZoomChange: setZoom })
2380
- ] }),
2381
- commitError ? /* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-[hsl(var(--destructive)/0.3)] bg-[hsl(var(--destructive)/0.1)] px-3 py-1.5 text-xs text-[var(--text-danger)]", role: "alert", children: [
2382
- /* @__PURE__ */ jsx9("span", { className: "min-w-0 truncate", children: commitError }),
2383
- /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => setCommitError(null), className: "shrink-0 underline-offset-2 hover:underline", children: "Dismiss" })
2384
- ] }) : null,
2385
- props.renderAssetShelf ? /* @__PURE__ */ jsx9("div", { className: "shrink-0 border-b border-[var(--border-default)]", children: props.renderAssetShelf() }) : null,
2386
- /* @__PURE__ */ jsx9(
2387
- "div",
2388
- {
2389
- ref: trackViewportRef,
2390
- "data-timeline-tracks": true,
2391
- className: "relative max-h-60 min-h-[6rem] shrink-0 overflow-auto overscroll-x-contain",
2392
- onDragOver: handleTrackAreaDragOver,
2393
- onDrop: handleTrackAreaDrop,
2394
- children: /* @__PURE__ */ jsxs7("div", { className: "relative", style: { width: `${TRACK_HEADER_PX + timelineWidth}px`, minWidth: "100%" }, children: [
2395
- /* @__PURE__ */ jsxs7("div", { className: "sticky top-0 z-20 flex", children: [
2396
- /* @__PURE__ */ jsx9("div", { className: "sticky left-0 z-30 w-36 shrink-0 border-b border-r border-[var(--border-default)] bg-[var(--bg-input)]" }),
2397
- /* @__PURE__ */ jsx9(TimelineRuler, { fps, durationFrames: timeline.sequence.durationFrames, zoom, onScrub: (frame) => clock.seek(frame) })
2398
- ] }),
2399
- /* @__PURE__ */ jsxs7("div", { className: "relative", children: [
2400
- sortedTracks.length === 0 ? /* @__PURE__ */ jsxs7(
2401
- "div",
2402
- {
2403
- "data-timeline-empty": true,
2404
- className: "sticky left-0 flex min-h-[6rem] flex-col items-center justify-center gap-1.5 px-6 py-10 text-center",
2405
- style: { width: "100%" },
2406
- children: [
2407
- /* @__PURE__ */ jsx9(FilmGlyph, { className: "h-6 w-6 text-[var(--text-muted)]" }),
2408
- /* @__PURE__ */ jsx9("p", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "This sequence has no tracks yet" }),
2409
- /* @__PURE__ */ jsx9("p", { className: "max-w-xs text-xs text-[var(--text-muted)]", children: "Add a video, audio, or caption track to start placing clips." })
2410
- ]
2411
- }
2412
- ) : null,
2413
- sortedTracks.map((track) => /* @__PURE__ */ jsx9(
2414
- TimelineTrackRow,
2415
- {
2416
- track,
2417
- clips: clipsByTrack.get(track.id) ?? [],
2418
- fps,
2419
- zoom,
2420
- sequenceDurationFrames: timeline.sequence.durationFrames,
2421
- selectedClipIds: new Set(selectedClipIds),
2422
- tabbableClipId,
2423
- canWrite,
2424
- frameProvider,
2425
- snapMove,
2426
- snapEdge,
2427
- onSnapPointChange: setActiveSnapPoint,
2428
- onSelectClip: selectClip,
2429
- onRequestDeleteClip: deleteClip,
2430
- onFocusStepClip: focusStepClip,
2431
- onCommitMove: handleCommitMove,
2432
- onCommitTrim: handleCommitTrim,
2433
- onCommitText: handleCommitText,
2434
- onLaneSeek: (frame) => clock.seek(frame)
2435
- },
2436
- track.id
2437
- )),
2438
- /* @__PURE__ */ jsxs7("div", { className: "pointer-events-none absolute inset-y-0", style: { left: `${TRACK_HEADER_PX}px`, width: `${timelineWidth}px` }, children: [
2439
- /* @__PURE__ */ jsx9(TimelinePlayhead, { frame: playheadFrame, zoom }),
2440
- /* @__PURE__ */ jsx9(SnapIndicatorLine, { point: activeSnapPoint, zoom })
2572
+ props.renderAssetShelf ? /* @__PURE__ */ jsx13("div", { className: "shrink-0 border-b border-[var(--border-default)]", children: props.renderAssetShelf() }) : null,
2573
+ /* @__PURE__ */ jsx13(
2574
+ "div",
2575
+ {
2576
+ ref: trackViewportRef,
2577
+ "data-timeline-tracks": true,
2578
+ className: "relative max-h-60 min-h-[6rem] shrink-0 overflow-auto overscroll-x-contain",
2579
+ onDragOver: handleTrackAreaDragOver,
2580
+ onDrop: handleTrackAreaDrop,
2581
+ children: /* @__PURE__ */ jsxs10("div", { className: "relative", style: { width: `${TRACK_HEADER_PX + timelineWidth}px`, minWidth: "100%" }, children: [
2582
+ /* @__PURE__ */ jsxs10("div", { className: "sticky top-0 z-20 flex", children: [
2583
+ /* @__PURE__ */ jsx13("div", { className: "sticky left-0 z-30 w-36 shrink-0 border-b border-r border-[var(--border-default)] bg-[var(--bg-input)]" }),
2584
+ /* @__PURE__ */ jsx13(TimelineRuler, { fps, durationFrames: timeline.sequence.durationFrames, zoom, onScrub: (frame) => clock.seek(frame) })
2585
+ ] }),
2586
+ /* @__PURE__ */ jsxs10("div", { className: "relative", children: [
2587
+ sortedTracks.length === 0 ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
2588
+ /* @__PURE__ */ jsx13(
2589
+ TimelineGhostLanes,
2590
+ {
2591
+ laneWidth: timelineWidth,
2592
+ videoLabel: labels.ghostVideoLane,
2593
+ captionLabel: labels.ghostCaptionLane
2594
+ }
2595
+ ),
2596
+ /* @__PURE__ */ jsx13(
2597
+ TimelineEmptyState,
2598
+ {
2599
+ labels: props.labels,
2600
+ brandedExport: props.brandedExport,
2601
+ ...props.onStartFromTemplate ? { onStartFromTemplate: props.onStartFromTemplate } : {},
2602
+ ...props.onAddClip ? { onAddClip: props.onAddClip } : {},
2603
+ ...props.onAskAgent ? { onAskAgent: props.onAskAgent } : {}
2604
+ }
2605
+ )
2606
+ ] }) : null,
2607
+ sortedTracks.map((track) => /* @__PURE__ */ jsx13(
2608
+ TimelineTrackRow,
2609
+ {
2610
+ track,
2611
+ clips: clipsByTrack.get(track.id) ?? [],
2612
+ fps,
2613
+ zoom,
2614
+ sequenceDurationFrames: timeline.sequence.durationFrames,
2615
+ selectedClipIds: new Set(selectedClipIds),
2616
+ tabbableClipId,
2617
+ canWrite,
2618
+ frameProvider,
2619
+ snapMove,
2620
+ snapEdge,
2621
+ onSnapPointChange: setActiveSnapPoint,
2622
+ onSelectClip: selectClip,
2623
+ onRequestDeleteClip: deleteClip,
2624
+ onFocusStepClip: focusStepClip,
2625
+ onCommitMove: handleCommitMove,
2626
+ onCommitTrim: handleCommitTrim,
2627
+ onCommitText: handleCommitText,
2628
+ onLaneSeek: (frame) => clock.seek(frame)
2629
+ },
2630
+ track.id
2631
+ )),
2632
+ /* @__PURE__ */ jsxs10("div", { className: "pointer-events-none absolute inset-y-0", style: { left: `${TRACK_HEADER_PX}px`, width: `${timelineWidth}px` }, children: [
2633
+ /* @__PURE__ */ jsx13(TimelinePlayhead, { frame: playheadFrame, zoom }),
2634
+ /* @__PURE__ */ jsx13(SnapIndicatorLine, { point: activeSnapPoint, zoom })
2635
+ ] })
2441
2636
  ] })
2442
2637
  ] })
2443
- ] })
2444
- }
2445
- )
2446
- ] }),
2447
- props.renderSidePanel ? /* @__PURE__ */ jsx9("aside", { className: "flex w-80 shrink-0 flex-col overflow-hidden border-l border-[var(--border-default)]", children: props.renderSidePanel({ selectedClips, playheadFrame }) }) : null
2448
- ] }) });
2638
+ }
2639
+ )
2640
+ ] }),
2641
+ props.renderSidePanel ? /* @__PURE__ */ jsx13("aside", { className: "flex max-h-72 shrink-0 flex-col overflow-hidden border-t border-[var(--border-default)] lg:max-h-none lg:w-80 lg:border-l lg:border-t-0", children: props.renderSidePanel({ selectedClips, playheadFrame }) }) : null
2642
+ ] })
2643
+ ] });
2449
2644
  }
2450
2645
  var TimelineEditor_default = TimelineEditor;
2451
2646
 
2452
2647
  export {
2648
+ DEFAULT_TIMELINE_LABELS,
2453
2649
  COMMAND_HISTORY_LIMIT,
2454
2650
  createCommandStack,
2455
2651
  moveClipCommand,
@@ -2495,9 +2691,13 @@ export {
2495
2691
  TimelineRuler,
2496
2692
  TimelineClipChip,
2497
2693
  TimelineTrackRow,
2694
+ BrandMark,
2695
+ TimelineEmptyState,
2696
+ TimelineGhostLanes,
2697
+ TimelineSmallScreenGate,
2498
2698
  ZoomControl,
2499
2699
  SEQUENCE_MEDIA_DRAG_TYPE,
2500
2700
  TimelineEditor,
2501
2701
  TimelineEditor_default
2502
2702
  };
2503
- //# sourceMappingURL=chunk-S6WS2B32.js.map
2703
+ //# sourceMappingURL=chunk-EX3LD5G6.js.map