cursedbelt 5.112.1 → 5.113.1

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 (56) hide show
  1. package/dist/react/components/ErrorBoundary.d.ts.map +1 -1
  2. package/dist/react/components/ErrorBoundary.js +23 -13
  3. package/dist/react/components/ErrorBoundary.js.map +1 -1
  4. package/dist/react/lazy/staleChunk.d.ts +19 -0
  5. package/dist/react/lazy/staleChunk.d.ts.map +1 -1
  6. package/dist/react/lazy/staleChunk.js +49 -2
  7. package/dist/react/lazy/staleChunk.js.map +1 -1
  8. package/dist/react/media/CutTimeline.d.ts +31 -0
  9. package/dist/react/media/CutTimeline.d.ts.map +1 -0
  10. package/dist/react/media/CutTimeline.js +197 -0
  11. package/dist/react/media/CutTimeline.js.map +1 -0
  12. package/dist/react/media/VideoCutEditor.d.ts +32 -6
  13. package/dist/react/media/VideoCutEditor.d.ts.map +1 -1
  14. package/dist/react/media/VideoCutEditor.js +97 -63
  15. package/dist/react/media/VideoCutEditor.js.map +1 -1
  16. package/dist/react/media/cutTimelineMath.d.ts +80 -0
  17. package/dist/react/media/cutTimelineMath.d.ts.map +1 -0
  18. package/dist/react/media/cutTimelineMath.js +116 -0
  19. package/dist/react/media/cutTimelineMath.js.map +1 -0
  20. package/dist/react/media/useFrameRate.d.ts +34 -0
  21. package/dist/react/media/useFrameRate.d.ts.map +1 -0
  22. package/dist/react/media/useFrameRate.js +66 -0
  23. package/dist/react/media/useFrameRate.js.map +1 -0
  24. package/dist/react/media/videoCutEditor.css +249 -63
  25. package/dist/react/media-gallery/MediaGallery.d.ts +10 -1
  26. package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
  27. package/dist/react/media-gallery/MediaGallery.js +29 -7
  28. package/dist/react/media-gallery/MediaGallery.js.map +1 -1
  29. package/dist/react/media-gallery/galleryDeepLink.d.ts +14 -0
  30. package/dist/react/media-gallery/galleryDeepLink.d.ts.map +1 -1
  31. package/dist/react/media-gallery/galleryDeepLink.js +44 -5
  32. package/dist/react/media-gallery/galleryDeepLink.js.map +1 -1
  33. package/dist/react/media-gallery/index.d.ts +1 -1
  34. package/dist/react/media-gallery/index.d.ts.map +1 -1
  35. package/dist/react/media-gallery/index.js +1 -1
  36. package/dist/react/media-gallery/index.js.map +1 -1
  37. package/dist/react/media-gallery/types.d.ts +5 -0
  38. package/dist/react/media-gallery/types.d.ts.map +1 -1
  39. package/package.json +2 -1
  40. package/scripts/guardrails-baseline.json +1 -1
  41. package/src/react/components/ErrorBoundary.tsx +19 -11
  42. package/src/react/components/StaleDeployHealer.spec.tsx +27 -0
  43. package/src/react/lazy/staleChunk.spec.tsx +63 -1
  44. package/src/react/lazy/staleChunk.ts +55 -2
  45. package/src/react/media/CutTimeline.tsx +379 -0
  46. package/src/react/media/VideoCutEditor.spec.tsx +1 -1
  47. package/src/react/media/VideoCutEditor.tsx +277 -214
  48. package/src/react/media/cutTimelineMath.spec.ts +90 -0
  49. package/src/react/media/cutTimelineMath.ts +140 -0
  50. package/src/react/media/useFrameRate.ts +96 -0
  51. package/src/react/media/videoCutEditor.css +249 -63
  52. package/src/react/media-gallery/MediaGallery.tsx +38 -7
  53. package/src/react/media-gallery/galleryDeepLink.spec.tsx +87 -2
  54. package/src/react/media-gallery/galleryDeepLink.ts +58 -5
  55. package/src/react/media-gallery/index.ts +1 -0
  56. package/src/react/media-gallery/types.ts +5 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/react/components/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAIvF,OAAO,EAAwC,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAerG;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;IACxE,iEAAiE;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,qFAAqF;IACrF,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B;;;kFAG8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;CACxC;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAQD,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAItD,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;IAMlD,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS;CA0BpB;AA4CD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAGnD;AAED,MAAM,WAAW,sBAAsB;IACrC,gFAAgF;IAChF,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,+BAA+B,sBAAsB,gCAsCtF,CAAC"}
1
+ {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/react/components/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAIvF,OAAO,EAAwC,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAerG;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;IACxE,iEAAiE;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,qFAAqF;IACrF,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B;;;kFAG8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;CACxC;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAQD,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAItD,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;IAMlD,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS;CA0BpB;AA4CD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAGnD;AAED,MAAM,WAAW,sBAAsB;IACrC,gFAAgF;IAChF,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,+BAA+B,sBAAsB,gCA8CtF,CAAC"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Component, useEffect, useState } from 'react';
3
3
  import { Alert } from './Alert.js';
4
4
  import { Spinner } from './Spinner.js';
5
- import { claimStaleChunkReload, staleChunkReloadedRecently } from '../lazy/staleChunk.js';
5
+ import { claimStaleChunkReload, leavingFor, staleChunkReloadedRecently } from '../lazy/staleChunk.js';
6
6
  import { ConnectionHealer, isUnreachableError } from './ConnectionHealer.js';
7
7
  const keysChanged = (a, b) => {
8
8
  if (!a || !b)
@@ -129,18 +129,28 @@ export const StaleDeployHealer = ({ error, onRecover, ...rest }) => {
129
129
  useEffect(() => {
130
130
  if (typeof window === 'undefined')
131
131
  return;
132
- if (!claimStaleChunkReload()) {
133
- setExhausted(true);
134
- return;
135
- }
136
- // A tick of delay so the banner paints first — an instant reload on a slow
137
- // connection reads as a flicker with no explanation.
138
- const timer = setTimeout(() => {
139
- if (onRecover)
140
- onRecover();
141
- else
142
- window.location.reload();
143
- }, 400);
132
+ let timer;
133
+ const heal = () => {
134
+ if (!claimStaleChunkReload()) {
135
+ setExhausted(true);
136
+ return;
137
+ }
138
+ // A tick of delay so the banner paints first — an instant reload on a slow
139
+ // connection reads as a flicker with no explanation.
140
+ timer = setTimeout(() => {
141
+ if (onRecover)
142
+ onRecover();
143
+ else
144
+ window.location.reload();
145
+ }, 400);
146
+ };
147
+ // A chunk cut off by the page being left (Safari cancels its loads at `beforeunload`) is
148
+ // not stale, and a reload would pull the tab back from where it was going — `staleChunk.ts`.
149
+ const leaving = leavingFor();
150
+ if (leaving > 0)
151
+ timer = setTimeout(heal, leaving);
152
+ else
153
+ heal();
144
154
  return () => clearTimeout(timer);
145
155
  }, [onRecover]);
146
156
  if (exhausted)
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../../src/react/components/ErrorBoundary.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../../src/react/components/ErrorBoundary.tsx"],"names":[],"mappings":"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"}
@@ -27,6 +27,17 @@
27
27
  * because a chunk genuinely missing from the deployed build would otherwise reload
28
28
  * forever with nothing able to remember trying.
29
29
  *
30
+ * 🔴 A chunk cut off by the page being LEFT is not stale. Safari (WebKit) cancels the
31
+ * old document's in-flight loads the moment a document navigation begins, right after
32
+ * `beforeunload`, and every lazy import still on its way rejects "Importing a module
33
+ * script failed." — the same words a deleted chunk gets. Reloading then sends the tab
34
+ * back to the page it was leaving: measured 2026-11-04 on roms' stage, a `goto
35
+ * /settings` 0–300 ms after the library loaded was "interrupted by another navigation
36
+ * to /" in 9 of 12 WebKit runs (forge task 4425). So a failure inside
37
+ * {@link LEAVING_GRACE_MS} of `beforeunload` waits instead: a page that goes never
38
+ * runs the timer, and one still here after it (the navigation was canceled, or was a
39
+ * download) retries the chunk once before treating it as stale.
40
+ *
30
41
  * `guardrailsEnforce`'s `no-raw-lazy` is the WHETHER: a bare `React.lazy` in an app
31
42
  * is refused, so the next lazy view cannot quietly reopen this.
32
43
  */
@@ -34,12 +45,20 @@ import { type ComponentType, type LazyExoticComponent } from 'react';
34
45
  export declare const STALE_CHUNK_RELOADED_AT_KEY = "cursedbelt.staleChunkReloadAt";
35
46
  /** A reload within this long of the last one is a loop, not a stale page. */
36
47
  export declare const STALE_CHUNK_RELOAD_WINDOW_MS = 60000;
48
+ /** How long after `beforeunload` a failed chunk is read as the navigation's doing, not a deploy's. */
49
+ export declare const LEAVING_GRACE_MS = 10000;
37
50
  export interface StaleChunkEnv {
38
51
  storage: Pick<Storage, 'getItem' | 'setItem'> | null;
39
52
  reload: () => void;
40
53
  now: () => number;
54
+ /** When this page last heard `beforeunload` (0: never, or it came back from the bfcache since). */
55
+ leftAt?: () => number;
56
+ /** A timer — one that never fires on a page that has gone. */
57
+ sleep?: (ms: number) => Promise<void>;
41
58
  }
42
59
  export declare function browserEnv(): StaleChunkEnv;
60
+ /** How much longer a chunk failure now is the page being left (0: it is not being left). */
61
+ export declare function leavingFor(env?: StaleChunkEnv): number;
43
62
  /**
44
63
  * Claim this tab's one stale-chunk reload for the window: true when a reload may
45
64
  * happen now, and the claim is already recorded. False when one happened inside the
@@ -1 +1 @@
1
- {"version":3,"file":"staleChunk.d.ts","sourceRoot":"","sources":["../../../src/react/lazy/staleChunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAQ,MAAM,OAAO,CAAC;AAE3E,eAAO,MAAM,2BAA2B,kCAAkC,CAAC;AAE3E,6EAA6E;AAC7E,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,aAAa,CAQ1C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,aAA4B,GAAG,OAAO,CAShF;AAED,sGAAsG;AACtG,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,aAA4B,GAAG,OAAO,CASrF;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,GAAG,GAAE,MAAM,aAA0B,GACpC,MAAM,OAAO,CAAC,CAAC,CAAC,CAWlB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EACzD,IAAI,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,EACnC,GAAG,CAAC,EAAE,MAAM,aAAa,GACxB,mBAAmB,CAAC,CAAC,CAAC,CAExB"}
1
+ {"version":3,"file":"staleChunk.d.ts","sourceRoot":"","sources":["../../../src/react/lazy/staleChunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAQ,MAAM,OAAO,CAAC;AAE3E,eAAO,MAAM,2BAA2B,kCAAkC,CAAC;AAE3E,6EAA6E;AAC7E,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,sGAAsG;AACtG,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,mGAAmG;IACnG,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAqBD,wBAAgB,UAAU,IAAI,aAAa,CAQ1C;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,GAAG,GAAE,aAA4B,GAAG,MAAM,CAIpE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,aAA4B,GAAG,OAAO,CAShF;AAED,sGAAsG;AACtG,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,aAA4B,GAAG,OAAO,CASrF;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,GAAG,GAAE,MAAM,aAA0B,GACpC,MAAM,OAAO,CAAC,CAAC,CAAC,CAoBlB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EACzD,IAAI,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,EACnC,GAAG,CAAC,EAAE,MAAM,aAAa,GACxB,mBAAmB,CAAC,CAAC,CAAC,CAExB"}
@@ -27,6 +27,17 @@
27
27
  * because a chunk genuinely missing from the deployed build would otherwise reload
28
28
  * forever with nothing able to remember trying.
29
29
  *
30
+ * 🔴 A chunk cut off by the page being LEFT is not stale. Safari (WebKit) cancels the
31
+ * old document's in-flight loads the moment a document navigation begins, right after
32
+ * `beforeunload`, and every lazy import still on its way rejects "Importing a module
33
+ * script failed." — the same words a deleted chunk gets. Reloading then sends the tab
34
+ * back to the page it was leaving: measured 2026-11-04 on roms' stage, a `goto
35
+ * /settings` 0–300 ms after the library loaded was "interrupted by another navigation
36
+ * to /" in 9 of 12 WebKit runs (forge task 4425). So a failure inside
37
+ * {@link LEAVING_GRACE_MS} of `beforeunload` waits instead: a page that goes never
38
+ * runs the timer, and one still here after it (the navigation was canceled, or was a
39
+ * download) retries the chunk once before treating it as stale.
40
+ *
30
41
  * `guardrailsEnforce`'s `no-raw-lazy` is the WHETHER: a bare `React.lazy` in an app
31
42
  * is refused, so the next lazy view cannot quietly reopen this.
32
43
  */
@@ -34,6 +45,25 @@ import { lazy } from 'react';
34
45
  export const STALE_CHUNK_RELOADED_AT_KEY = 'cursedbelt.staleChunkReloadAt';
35
46
  /** A reload within this long of the last one is a loop, not a stale page. */
36
47
  export const STALE_CHUNK_RELOAD_WINDOW_MS = 60_000;
48
+ /** How long after `beforeunload` a failed chunk is read as the navigation's doing, not a deploy's. */
49
+ export const LEAVING_GRACE_MS = 10_000;
50
+ let leftAt = 0;
51
+ let watching = false;
52
+ /** Note when the page starts being left. Installed on import, so it hears the first navigation. */
53
+ function watchLeaving() {
54
+ if (watching || typeof window === 'undefined' || typeof window.addEventListener !== 'function')
55
+ return;
56
+ watching = true;
57
+ window.addEventListener('beforeunload', () => {
58
+ leftAt = Date.now();
59
+ });
60
+ // Back from the bfcache: the navigation that was leaving it is over.
61
+ window.addEventListener('pageshow', () => {
62
+ leftAt = 0;
63
+ });
64
+ }
65
+ watchLeaving();
66
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
37
67
  export function browserEnv() {
38
68
  let storage = null;
39
69
  try {
@@ -42,7 +72,14 @@ export function browserEnv() {
42
72
  catch {
43
73
  // Storage refused (blocked site data) — no memory, so no reload: the guard fails closed.
44
74
  }
45
- return { storage, reload: () => window.location.reload(), now: Date.now };
75
+ return { storage, reload: () => window.location.reload(), now: Date.now, leftAt: () => leftAt, sleep };
76
+ }
77
+ /** How much longer a chunk failure now is the page being left (0: it is not being left). */
78
+ export function leavingFor(env = browserEnv()) {
79
+ const at = env.leftAt?.() ?? 0;
80
+ if (at <= 0)
81
+ return 0;
82
+ return Math.max(0, at + LEAVING_GRACE_MS - env.now());
46
83
  }
47
84
  /**
48
85
  * Claim this tab's one stale-chunk reload for the window: true when a reload may
@@ -81,7 +118,17 @@ export function reloadOnStaleChunk(load, env = browserEnv) {
81
118
  return await load();
82
119
  }
83
120
  catch (error) {
84
- const current = env();
121
+ let current = env();
122
+ const wait = leavingFor(current);
123
+ if (wait > 0) {
124
+ await (current.sleep ?? sleep)(wait);
125
+ try {
126
+ return await load();
127
+ }
128
+ catch {
129
+ current = env();
130
+ }
131
+ }
85
132
  if (!claimStaleChunkReload(current))
86
133
  throw error;
87
134
  current.reload();
@@ -1 +1 @@
1
- {"version":3,"file":"staleChunk.js","sourceRoot":"","sources":["../../../src/react/lazy/staleChunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAgD,IAAI,EAAE,MAAM,OAAO,CAAC;AAE3E,MAAM,CAAC,MAAM,2BAA2B,GAAG,+BAA+B,CAAC;AAE3E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAQnD,MAAM,UAAU,UAAU;IACxB,IAAI,OAAO,GAA6B,IAAI,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,yFAAyF;IAC3F,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAqB,UAAU,EAAE;IACrE,IAAI,0BAA0B,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAChC,IAAI,CAAC;QACH,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,2BAA2B,CAAC,KAAK,KAAK,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,0BAA0B,CAAC,MAAqB,UAAU,EAAE;IAC1E,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5E,OAAO,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,GAAG,4BAA4B,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,kBAAkB,CAChC,IAAsB,EACtB,MAA2B,UAAU;IAErC,OAAO,KAAK,IAAI,EAAE;QAChB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC;gBAAE,MAAM,KAAK,CAAC;YACjD,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO,IAAI,OAAO,CAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAmC,EACnC,GAAyB;IAEzB,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"staleChunk.js","sourceRoot":"","sources":["../../../src/react/lazy/staleChunk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { type PlanSegment, type VideoCut } from 'cursedbelt-core/media/video-cuts';
3
+ /**
4
+ * The Trim page's timeline: a ruler, the clip's pieces, and a playhead, at any zoom from the
5
+ * whole clip down to one frame per 12 px (forge task 4311).
6
+ *
7
+ * The gestures are a map's and an NLE's, so nothing here has to be learned:
8
+ * · ⌘/Ctrl + wheel, or a trackpad pinch (which Chromium and Safari deliver as a ctrl-wheel),
9
+ * zooms around the POINTER; two fingers on a touch screen pinch the same way.
10
+ * · a plain wheel, or a drag anywhere on the track, pans.
11
+ * · a click on the ruler moves the playhead there, onto the frame under the pointer.
12
+ * · a click on a piece is the ✗ it always was — a drag that started on one never toggles it.
13
+ * The slider, ± and Fit do the same with no gesture at all (CNV-1: the wheel alone never zooms;
14
+ * CNV-3: a fit control on every canvas).
15
+ *
16
+ * 🔴 Drawn VIRTUALLY: only what is on screen is in the DOM, each piece clipped to the view. At
17
+ * 12 px a frame a two-hour 60 fps film is ten million pixels wide, which no browser will lay out
18
+ * as one element — so the track is always exactly the view, and the view is two numbers.
19
+ */
20
+ export interface CutTimelineProps {
21
+ duration: number;
22
+ fps: number;
23
+ playhead: number;
24
+ segments: readonly PlanSegment[];
25
+ onSeek: (seconds: number) => void;
26
+ onToggle: (segment: VideoCut) => void;
27
+ /** The transport, drawn at the start of the timeline's one-line toolbar (FRM-14). */
28
+ transport?: React.ReactNode;
29
+ }
30
+ export declare function CutTimeline({ duration, fps, playhead, segments, onSeek, onToggle, transport, ...passThrough }: CutTimelineProps): React.JSX.Element;
31
+ //# sourceMappingURL=CutTimeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CutTimeline.d.ts","sourceRoot":"","sources":["../../../src/react/media/CutTimeline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAe,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAmBhG;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IACjC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IACtC,qFAAqF;IACrF,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAeD,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,EAAE,gBAAgB,qBA4T/H"}
@@ -0,0 +1,197 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Maximize2, X, ZoomIn, ZoomOut } from 'lucide-react';
4
+ import { cutTimecode } from 'cursedbelt-core/media/video-cuts';
5
+ import { IconButton } from '../components/IconButton.js';
6
+ import { Button } from '../components/Button.js';
7
+ import { Slider } from '../components/Slider.js';
8
+ import { Tooltip } from '../components/Tooltip.js';
9
+ import { cn, RAW_BUTTON } from '../lib/cn.js';
10
+ import { clampViewStart, frameAt, frameStart, rulerLabel, rulerStep, sliderToZoom, snapToFrame, zoomAround, zoomBounds, zoomToSlider, } from './cutTimelineMath.js';
11
+ /** A drag shorter than this is a click — the slop every pointer library uses. */
12
+ const DRAG_SLOP_PX = 4;
13
+ /** The width drawn at before the track is measured. */
14
+ const NOMINAL_WIDTH_PX = 1000;
15
+ /** Frame ticks are drawn once frames are at least this far apart. */
16
+ const FRAME_TICK_MIN_PX = 6;
17
+ export function CutTimeline({ duration, fps, playhead, segments, onSeek, onToggle, transport, ...passThrough }) {
18
+ const trackRef = React.useRef(null);
19
+ const [measured, setWidth] = React.useState(0);
20
+ // Until the track has been measured (and in a DOM with no layout at all), draw at a nominal
21
+ // width rather than at zero — zero would put every piece off screen, and the first real
22
+ // measurement lands in a layout effect, before anything paints.
23
+ const width = measured > 0 ? measured : NOMINAL_WIDTH_PX;
24
+ const [view, setView] = React.useState({ start: 0, pps: null });
25
+ React.useLayoutEffect(() => {
26
+ const track = trackRef.current;
27
+ if (!track)
28
+ return;
29
+ const measure = () => setWidth(track.clientWidth);
30
+ measure();
31
+ const observer = new ResizeObserver(measure);
32
+ observer.observe(track);
33
+ return () => observer.disconnect();
34
+ }, []);
35
+ const bounds = zoomBounds(width, duration, fps);
36
+ const pps = Math.min(bounds.max, Math.max(bounds.fit, view.pps ?? bounds.fit));
37
+ const start = clampViewStart(view.start, pps, width, duration);
38
+ const span = pps > 0 ? width / pps : 0;
39
+ const canZoom = bounds.max > bounds.fit && bounds.fit > 0;
40
+ // The handlers below are bound once (the wheel has to be non-passive, so it is a native
41
+ // listener) and read the current numbers from here.
42
+ const now = React.useRef({ pps, start, width, duration, bounds });
43
+ now.current = { pps, start, width, duration, bounds };
44
+ const zoomTo = React.useCallback((next, anchorX) => {
45
+ const n = now.current;
46
+ setView(zoomAround({ start: n.start, pps: n.pps }, next, anchorX, n.width, n.duration, n.bounds));
47
+ }, []);
48
+ /** Zoom by a factor around the playhead when it is on screen, else around the middle. */
49
+ const zoomBy = (factor) => {
50
+ const x = playhead >= start && playhead <= start + span ? (playhead - start) * pps : width / 2;
51
+ zoomTo(pps * factor, x);
52
+ };
53
+ React.useEffect(() => {
54
+ const track = trackRef.current;
55
+ if (!track)
56
+ return;
57
+ const onWheel = (event) => {
58
+ event.preventDefault();
59
+ const n = now.current;
60
+ const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? n.width : 1;
61
+ if (event.ctrlKey || event.metaKey) {
62
+ const x = event.clientX - track.getBoundingClientRect().left;
63
+ zoomTo(n.pps * Math.exp(-event.deltaY * lines * 0.01), x);
64
+ return;
65
+ }
66
+ // A vertical wheel pans too: most mice have no other, and the page has nothing to scroll here.
67
+ const dx = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
68
+ setView({ pps: n.pps, start: clampViewStart(n.start + (dx * lines) / n.pps, n.pps, n.width, n.duration) });
69
+ };
70
+ track.addEventListener('wheel', onWheel, { passive: false });
71
+ return () => track.removeEventListener('wheel', onWheel);
72
+ }, [zoomTo]);
73
+ // Keep the playhead in view while it moves (playing, stepping, a seek from the cuts list) —
74
+ // a page-turn, as Resolve does, rather than a scroll that never lets the eye settle.
75
+ React.useEffect(() => {
76
+ const n = now.current;
77
+ if (n.width <= 0 || n.pps <= n.bounds.fit + 1e-9)
78
+ return;
79
+ const viewSpan = n.width / n.pps;
80
+ if (playhead < n.start || playhead > n.start + viewSpan) {
81
+ setView({ pps: n.pps, start: clampViewStart(playhead - viewSpan * 0.1, n.pps, n.width, n.duration) });
82
+ }
83
+ }, [playhead]);
84
+ /*
85
+ * Pointers. One pointer that moves past the slop pans; two pinch. One that does not move is a
86
+ * click: on the ruler it seeks, on a piece the piece's own button does its job.
87
+ */
88
+ const gesture = React.useRef({ pointers: new Map(), fromX: 0, fromStart: 0, dragged: false, pinch: null });
89
+ const onPointerDown = (event) => {
90
+ if (event.pointerType === 'mouse' && event.button !== 0)
91
+ return;
92
+ const g = gesture.current;
93
+ g.pointers.set(event.pointerId, event.clientX);
94
+ if (g.pointers.size === 1) {
95
+ g.fromX = event.clientX;
96
+ g.fromStart = start;
97
+ g.dragged = false;
98
+ g.pinch = null;
99
+ }
100
+ else if (g.pointers.size === 2) {
101
+ const [a, b] = [...g.pointers.values()];
102
+ g.pinch = { distance: Math.max(1, Math.abs(a - b)), pps };
103
+ g.dragged = true;
104
+ }
105
+ };
106
+ const onPointerMove = (event) => {
107
+ const g = gesture.current;
108
+ if (!g.pointers.has(event.pointerId))
109
+ return;
110
+ g.pointers.set(event.pointerId, event.clientX);
111
+ const left = event.currentTarget.getBoundingClientRect().left;
112
+ if (g.pinch && g.pointers.size >= 2) {
113
+ const [a, b] = [...g.pointers.values()];
114
+ zoomTo(g.pinch.pps * (Math.max(1, Math.abs(a - b)) / g.pinch.distance), (a + b) / 2 - left);
115
+ return;
116
+ }
117
+ const dx = event.clientX - g.fromX;
118
+ if (!g.dragged && Math.abs(dx) < DRAG_SLOP_PX)
119
+ return;
120
+ if (!g.dragged) {
121
+ g.dragged = true;
122
+ // Captured only once it is a drag: capturing on press would retarget the click of a
123
+ // piece's button to the track, and the ✗ would stop working.
124
+ event.currentTarget.setPointerCapture(event.pointerId);
125
+ }
126
+ const n = now.current;
127
+ setView({ pps: n.pps, start: clampViewStart(g.fromStart - dx / n.pps, n.pps, n.width, n.duration) });
128
+ };
129
+ const onPointerUp = (event) => {
130
+ const g = gesture.current;
131
+ if (!g.pointers.delete(event.pointerId))
132
+ return;
133
+ if (g.pointers.size > 0)
134
+ return;
135
+ g.pinch = null;
136
+ if (g.dragged || event.target.closest('.cbvc-seg'))
137
+ return;
138
+ const n = now.current;
139
+ const x = event.clientX - event.currentTarget.getBoundingClientRect().left;
140
+ onSeek(Math.min(n.duration, snapToFrame(n.start + x / n.pps, fps)));
141
+ };
142
+ // A drag that ended over a piece must not also toggle it.
143
+ const onClickCapture = (event) => {
144
+ if (gesture.current.dragged) {
145
+ event.preventDefault();
146
+ event.stopPropagation();
147
+ gesture.current.dragged = false;
148
+ }
149
+ };
150
+ const x = (seconds) => (seconds - start) * pps;
151
+ const end = start + span;
152
+ // The ruler: a labeled tick per step, and a tick per FRAME once frames are far enough apart.
153
+ const step = rulerStep(pps, fps);
154
+ const labels = [];
155
+ if (pps > 0 && duration > 0) {
156
+ // Counted in frames when the step is, so 0.1 s × 3 lands on frame 3 and not on 0.30000000000000004.
157
+ const stepFrames = Math.round(step.label * fps);
158
+ for (let k = Math.floor(start / step.label); k * step.label <= end + 1e-9; k += 1) {
159
+ const t = step.frames ? frameStart(k * stepFrames, fps) : k * step.label;
160
+ if (t >= start - 1e-9 && t <= duration + 1e-9)
161
+ labels.push(t);
162
+ }
163
+ }
164
+ const frameTicks = [];
165
+ if (pps / fps >= FRAME_TICK_MIN_PX) {
166
+ for (let k = frameAt(start, fps); frameStart(k, fps) <= Math.min(end, duration) + 1e-9; k += 1)
167
+ frameTicks.push(k);
168
+ }
169
+ return (_jsxs("div", { ...passThrough, className: 'cbvc-timeline', children: [_jsxs("div", { className: 'cbvc-tools', children: [transport, _jsxs("div", { className: 'cbvc-zoom', children: [_jsx(IconButton, { size: 'sm', label: 'Zoom out', tooltip: 'Zoom out (\u2318 or Ctrl + wheel, or pinch)', disabled: !canZoom || pps <= bounds.fit + 1e-9, onClick: () => zoomBy(0.5), children: _jsx(ZoomOut, { "aria-hidden": 'true' }) }), _jsx(Slider, { className: 'cbvc-zoom-slider', "aria-label": 'Zoom', "aria-valuetext": `${(pps / fps).toFixed(pps / fps < 1 ? 2 : 1)} px per frame`, min: 0, max: 1000, step: 1, disabled: !canZoom, value: Math.round(zoomToSlider(pps, bounds) * 1000), onChange: (value) => {
170
+ const next = sliderToZoom(value / 1000, bounds);
171
+ const x0 = playhead >= start && playhead <= end ? (playhead - start) * pps : width / 2;
172
+ zoomTo(next, x0);
173
+ } }), _jsx(IconButton, { size: 'sm', label: 'Zoom in', tooltip: 'Zoom in \u2014 down to single frames', disabled: !canZoom || pps >= bounds.max - 1e-9, onClick: () => zoomBy(2), children: _jsx(ZoomIn, { "aria-hidden": 'true' }) }), _jsxs(Button, { size: 'sm', variant: 'ghost', tooltip: 'Fit the whole clip in the timeline', disabled: view.pps === null || pps <= bounds.fit + 1e-9, onClick: () => setView({ start: 0, pps: null }), children: [_jsx(Maximize2, { "aria-hidden": 'true', size: 14 }), " Fit"] })] })] }), _jsxs("div", { ref: trackRef, className: 'cbvc-track', "data-px-per-second": pps, "data-view-start": start, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, onClickCapture: onClickCapture, children: [_jsxs("div", { className: 'cbvc-ruler', "aria-hidden": 'true', children: [frameTicks.map((k) => (_jsx("span", { className: 'cbvc-frame-tick', "data-frame": k, style: { left: `${x(frameStart(k, fps))}px` } }, `f${k}`))), labels.map((t) => (_jsx("span", { className: 'cbvc-tick', style: { left: `${x(t)}px` }, children: _jsx("span", { className: 'cbvc-tick-label', children: rulerLabel(t, fps, step.frames, duration) }) }, `l${t}`)))] }), _jsx("ul", { className: 'cbvc-strip', "aria-label": segments.length <= 1
174
+ ? 'The whole clip, uncut'
175
+ : `${segments.length} sections; ${segments.filter((segment) => segment.removed).length} marked for removal`, children: segments
176
+ .filter((segment) => segment.end > start && segment.start < end)
177
+ .map((segment) => {
178
+ // Clipped to the view (a pixel past each edge, so no border shows at the cut-off).
179
+ const left = Math.max(-1, x(segment.start));
180
+ const right = Math.min(width + 1, x(segment.end));
181
+ const words = segment.removed
182
+ ? `Keep ${cutTimecode(segment.start)} to ${cutTimecode(segment.end)} after all`
183
+ : `Remove ${cutTimecode(segment.start)} to ${cutTimecode(segment.end)}`;
184
+ return (_jsx("li", { className: 'cbvc-seg-item', style: { left: `${left}px`, width: `${Math.max(0, right - left)}px` }, children: _jsx(Tooltip, { content: words, children: _jsxs("button", {
185
+ // guardrails-ignore no-raw-action-button: a segment of the timeline, sized to
186
+ // its own duration — a `Button` carries padding and a min-width, which is
187
+ // exactly wrong for a control whose WIDTH is the information.
188
+ type: 'button', className: cn(RAW_BUTTON, `cbvc-seg${segment.removed ? ' is-removed' : ''}`), "aria-pressed": segment.removed, "aria-label": words, "data-start": segment.start, "data-end": segment.end, onClick: () => onToggle(segment), children: [_jsx(X, { className: 'cbvc-seg-x', "aria-hidden": 'true', size: 12 }), _jsx("span", { className: 'cbvc-seg-label', children: segment.removed ? 'Removed' : cutTimecode(segment.end - segment.start) })] }) }) }, segment.key));
189
+ }) }), playhead >= start - 1e-9 && playhead <= end + 1e-9 ? (_jsx("span", { className: 'cbvc-strip-head', style: { left: `${x(playhead)}px` } })) : null] }), pps > bounds.fit + 1e-9 ? (_jsxs("div", { className: 'cbvc-overview', "aria-hidden": 'true', onClick: (event) => {
190
+ const rect = event.currentTarget.getBoundingClientRect();
191
+ const t = ((event.clientX - rect.left) / rect.width) * duration;
192
+ setView({ pps, start: clampViewStart(t - span / 2, pps, width, duration) });
193
+ }, children: [segments
194
+ .filter((segment) => segment.removed)
195
+ .map((segment) => (_jsx("span", { className: 'cbvc-overview-cut', style: { left: `${(segment.start / duration) * 100}%`, width: `${((segment.end - segment.start) / duration) * 100}%` } }, segment.key))), _jsx("span", { className: 'cbvc-overview-window', style: { left: `${(start / duration) * 100}%`, width: `${Math.max(0.5, (span / duration) * 100)}%` } }), _jsx("span", { className: 'cbvc-overview-head', style: { left: `${(playhead / duration) * 100}%` } })] })) : null] }));
196
+ }
197
+ //# sourceMappingURL=CutTimeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CutTimeline.js","sourceRoot":"","sources":["../../../src/react/media/CutTimeline.tsx"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@ import { type VideoCut } from 'cursedbelt-core/media/video-cuts';
12
12
  * it uses ... It can then remove the parts of the video I don't want, save a new copy and I
13
13
  * can view it when it's done."*
14
14
  *
15
- * So this dialog never touches a byte. It produces a `VideoCut[]`, hands it to `onSubmit`,
15
+ * So this page never touches a byte. It produces a `VideoCut[]`, hands it to `onSubmit`,
16
16
  * and closes. Everything about what makes a list valid is `videoCuts.ts`, shared with the
17
17
  * server that executes it, so the dialog and the encoder cannot disagree about what was
18
18
  * asked for.
@@ -51,7 +51,28 @@ import { type VideoCut } from 'cursedbelt-core/media/video-cuts';
51
51
  * ONE `InfoHint` — the fleet's standard ⓘ, which costs a 16px glyph at rest and opens on
52
52
  * hover, focus or tap. That is the answer to *"tooltips or an info icon or something we
53
53
  * standardize can make it clear without taking up space when out of use"*: the explanation
54
- * exists, in one predictable place, and pays no permanent rent in the dialog.
54
+ * exists, in one predictable place, and pays no permanent rent on the page.
55
+ *
56
+ * ── A PAGE, with a zoomable timeline, down to the frame (owner, 2026-11-03) ───────────
57
+ * *"a full Trim page with a zoomable timeline, down to frame-by-frame selection."* It was a
58
+ * `size='lg'` modal with one fixed strip, so on a two-hour film a pixel was several seconds
59
+ * and the frame he wanted could not be marked at all. Now it is the shape of an NLE's trim
60
+ * view (YouTube Studio's editor, Resolve's cut page): the picture on top, the transport and a
61
+ * full-width timeline under it, the actions bottom-right.
62
+ * · It COVERS the viewport and has an address of its own when the app gives it one
63
+ * (`MediaGallery`'s `trimming`, `useGalleryDeepLink({ trim: true })`), so Back closes it
64
+ * and a reload keeps it. Underneath it is still the belt's dialog primitive — that is what
65
+ * traps focus, takes Escape and makes the gallery's own keys stand down (`openDialogs`).
66
+ * · The timeline zooms from the whole clip to one frame per 12 px (`CutTimeline`).
67
+ * · `,` and `.` step one frame, every split lands on a frame boundary, and the readout is
68
+ * `hh:mm:ss:ff`. The rate is the catalog's probe, else measured from the clip
69
+ * (`useFrameRate`), and an assumed rate is labeled as one.
70
+ *
71
+ * 🔴 Frame-accurate is a promise only because binary-server KEEPS it: its trim decodes and
72
+ * re-encodes every kept span (`trim=start:end` + `concat` into libx264 — `cutArgs`,
73
+ * `apps/binary-server/src/media/processing/ffmpegJobs.ts`), so a cut lands on the frame it
74
+ * names, not on the keyframe before it. The note under the timeline says so; a stream-copy
75
+ * trim would make that sentence false, and it must change with it.
55
76
  */
56
77
  export interface VideoCutEditorProps {
57
78
  /**
@@ -78,21 +99,26 @@ export interface VideoCutEditorProps {
78
99
  /**
79
100
  * The clip's length, when the app already knows it. The element's own metadata still wins
80
101
  * once it loads: a duration the catalog rounded to the second would put every mark a
81
- * fraction out, and the marks are the entire output of this dialog.
102
+ * fraction out, and the marks are the entire output of this page.
82
103
  */
83
104
  durationSeconds?: number | null;
105
+ /**
106
+ * Frames per second from the catalog's probe, when it has one. Without it the page measures
107
+ * the clip itself (`useFrameRate`) and, until it can, says the rate it draws with is assumed.
108
+ */
109
+ frameRate?: number | null;
84
110
  open?: boolean;
85
111
  onClose: () => void;
86
112
  /**
87
- * Send the marks. Resolving CLOSES the dialog; rejecting leaves it open with the marks
113
+ * Send the marks. Resolving CLOSES the page; rejecting leaves it open with the marks
88
114
  * intact, because the alternative is losing several minutes of careful scrubbing to one
89
115
  * failed request.
90
116
  */
91
117
  onSubmit: (cuts: VideoCut[]) => void | Promise<void>;
92
118
  /** Wording for the confirm button — apps queue this differently. */
93
119
  submitLabel?: string;
94
- /** Shown under the strip: what happens after Send, in the app's own words. */
120
+ /** Shown under the timeline: what happens after Send, in the app's own words. */
95
121
  note?: React.ReactNode;
96
122
  }
97
- export declare function VideoCutEditor({ src, hlsUrl, posterUrl, resolving, name, durationSeconds, open, onClose, onSubmit, submitLabel, note, ...passThrough }: VideoCutEditorProps): React.JSX.Element;
123
+ export declare function VideoCutEditor({ src, hlsUrl, posterUrl, resolving, name, durationSeconds, frameRate, open, onClose, onSubmit, submitLabel, note, ...passThrough }: VideoCutEditorProps): React.JSX.Element;
98
124
  //# sourceMappingURL=VideoCutEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"VideoCutEditor.d.ts","sourceRoot":"","sources":["../../../src/react/media/VideoCutEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAC9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,EAYL,KAAK,QAAQ,EACd,MAAM,kCAAkC,CAAC;AAI1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;OAQG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,MAAM,EACN,SAAS,EACT,SAAiB,EACjB,IAAI,EACJ,eAAe,EACf,IAAW,EACX,OAAO,EACP,QAAQ,EACR,WAAmC,EACnC,IAAI,EACJ,GAAG,WAAW,EACf,EAAE,mBAAmB,qBAoVrB"}
1
+ {"version":3,"file":"VideoCutEditor.d.ts","sourceRoot":"","sources":["../../../src/react/media/VideoCutEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAC9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAYL,KAAK,QAAQ,EACd,MAAM,kCAAkC,CAAC;AAO1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;OAQG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,MAAM,EACN,SAAS,EACT,SAAiB,EACjB,IAAI,EACJ,eAAe,EACf,SAAS,EACT,IAAW,EACX,OAAO,EACP,QAAQ,EACR,WAAmC,EACnC,IAAI,EACJ,GAAG,WAAW,EACf,EAAE,mBAAmB,qBAmXrB"}