cursedbelt 3.0.2 → 4.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.
- package/dist/react/components/ListToolbar.d.ts +94 -0
- package/dist/react/components/ListToolbar.d.ts.map +1 -0
- package/dist/react/components/ListToolbar.js +66 -0
- package/dist/react/components/ListToolbar.js.map +1 -0
- package/dist/react/file-tree/FileTree.d.ts +282 -3
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +799 -131
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +151 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts +187 -0
- package/dist/react/file-tree/fileTreeSelection.d.ts.map +1 -0
- package/dist/react/file-tree/fileTreeSelection.js +264 -0
- package/dist/react/file-tree/fileTreeSelection.js.map +1 -0
- package/dist/react/file-tree/index.d.ts +1 -0
- package/dist/react/file-tree/index.d.ts.map +1 -1
- package/dist/react/file-tree/index.js +8 -0
- package/dist/react/file-tree/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/form.d.ts +3 -0
- package/dist/react/lib/form.d.ts.map +1 -1
- package/dist/react/lib/form.js +43 -5
- package/dist/react/lib/form.js.map +1 -1
- package/dist/react/media/DenseGalleryShell.d.ts +19 -3
- package/dist/react/media/DenseGalleryShell.d.ts.map +1 -1
- package/dist/react/media/DenseGalleryShell.js +55 -15
- package/dist/react/media/DenseGalleryShell.js.map +1 -1
- package/dist/react/media/FocusViewer.d.ts.map +1 -1
- package/dist/react/media/FocusViewer.js +14 -14
- package/dist/react/media/FocusViewer.js.map +1 -1
- package/dist/react/media/FrameGrabber.d.ts +23 -0
- package/dist/react/media/FrameGrabber.d.ts.map +1 -1
- package/dist/react/media/FrameGrabber.js +67 -1
- package/dist/react/media/FrameGrabber.js.map +1 -1
- package/dist/react/media/MediaResize.d.ts +51 -19
- package/dist/react/media/MediaResize.d.ts.map +1 -1
- package/dist/react/media/MediaResize.js +20 -13
- package/dist/react/media/MediaResize.js.map +1 -1
- package/dist/react/media/VideoChapterEditor.d.ts +67 -0
- package/dist/react/media/VideoChapterEditor.d.ts.map +1 -0
- package/dist/react/media/VideoChapterEditor.js +148 -0
- package/dist/react/media/VideoChapterEditor.js.map +1 -0
- package/dist/react/media/VideoPlayer.d.ts +46 -4
- package/dist/react/media/VideoPlayer.d.ts.map +1 -1
- package/dist/react/media/VideoPlayer.js +124 -11
- package/dist/react/media/VideoPlayer.js.map +1 -1
- package/dist/react/media/VideoTurner.d.ts +15 -2
- package/dist/react/media/VideoTurner.d.ts.map +1 -1
- package/dist/react/media/VideoTurner.js +29 -2
- package/dist/react/media/VideoTurner.js.map +1 -1
- package/dist/react/media/denseGallery.css +104 -46
- package/dist/react/media/hlsSource.d.ts +57 -0
- package/dist/react/media/hlsSource.d.ts.map +1 -1
- package/dist/react/media/hlsSource.js +177 -1
- package/dist/react/media/hlsSource.js.map +1 -1
- package/dist/react/media/index.d.ts +2 -0
- package/dist/react/media/index.d.ts.map +1 -1
- package/dist/react/media/index.js +6 -0
- package/dist/react/media/index.js.map +1 -1
- package/dist/react/media/mediaResize.css +127 -54
- package/dist/react/media/pictureEditor.css +52 -6
- package/dist/react/media/videoChapterEditor.css +203 -0
- package/dist/react/media/videoChapters.d.ts +117 -0
- package/dist/react/media/videoChapters.d.ts.map +1 -0
- package/dist/react/media/videoChapters.js +95 -0
- package/dist/react/media/videoChapters.js.map +1 -0
- package/dist/react/media-gallery/GalleryTable.d.ts +52 -1
- package/dist/react/media-gallery/GalleryTable.d.ts.map +1 -1
- package/dist/react/media-gallery/GalleryTable.js +40 -9
- package/dist/react/media-gallery/GalleryTable.js.map +1 -1
- package/dist/react/media-gallery/MediaGallery.d.ts +107 -4
- package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
- package/dist/react/media-gallery/MediaGallery.js +484 -104
- package/dist/react/media-gallery/MediaGallery.js.map +1 -1
- package/dist/react/media-gallery/MediaMetaEditor.d.ts +48 -0
- package/dist/react/media-gallery/MediaMetaEditor.d.ts.map +1 -0
- package/dist/react/media-gallery/MediaMetaEditor.js +66 -0
- package/dist/react/media-gallery/MediaMetaEditor.js.map +1 -0
- package/dist/react/media-gallery/index.d.ts +2 -0
- package/dist/react/media-gallery/index.d.ts.map +1 -1
- package/dist/react/media-gallery/index.js +5 -0
- package/dist/react/media-gallery/index.js.map +1 -1
- package/dist/react/media-gallery/mediaGallery.css +180 -4
- package/dist/react/media-gallery/mediaMeta.d.ts +62 -0
- package/dist/react/media-gallery/mediaMeta.d.ts.map +1 -0
- package/dist/react/media-gallery/mediaMeta.js +56 -0
- package/dist/react/media-gallery/mediaMeta.js.map +1 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts +77 -0
- package/dist/react/media-gallery/playbackPreferences.d.ts.map +1 -1
- package/dist/react/media-gallery/playbackPreferences.js +42 -0
- package/dist/react/media-gallery/playbackPreferences.js.map +1 -1
- package/dist/react/media-gallery/types.d.ts +34 -0
- package/dist/react/media-gallery/types.d.ts.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts +29 -0
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -0
- package/dist/styles-areas/activity-bar.css +30 -0
- package/dist/styles-areas/analytics.css +672 -0
- package/dist/styles-areas/auth.css +154 -0
- package/dist/styles-areas/calendar.css +416 -0
- package/dist/styles-areas/chart-table.css +41 -0
- package/dist/styles-areas/charts.css +89 -0
- package/dist/styles-areas/chat.css +364 -0
- package/dist/styles-areas/clipboard.css +26 -0
- package/dist/styles-areas/code-editor.css +130 -0
- package/dist/styles-areas/companion-link.css +70 -0
- package/dist/styles-areas/core.css +2503 -0
- package/dist/styles-areas/dashboard-grid.css +84 -0
- package/dist/styles-areas/data-table.css +657 -0
- package/dist/styles-areas/deep-link.css +26 -0
- package/dist/styles-areas/diff-viewer.css +30 -0
- package/dist/styles-areas/disk-usage.css +262 -0
- package/dist/styles-areas/emoji.css +154 -0
- package/dist/styles-areas/fields.css +582 -0
- package/dist/styles-areas/file-tree.css +33 -0
- package/dist/styles-areas/filter-rail.css +235 -0
- package/dist/styles-areas/folder-tree.css +219 -0
- package/dist/styles-areas/keep-awake.css +26 -0
- package/dist/styles-areas/layout-engine.css +239 -0
- package/dist/styles-areas/markdown.css +87 -0
- package/dist/styles-areas/master-detail.css +239 -0
- package/dist/styles-areas/master-lock.css +26 -0
- package/dist/styles-areas/media-gallery.css +351 -0
- package/dist/styles-areas/media.css +658 -0
- package/dist/styles-areas/nav.css +488 -0
- package/dist/styles-areas/notifications.css +245 -0
- package/dist/styles-areas/overlays.css +133 -0
- package/dist/styles-areas/palette.css +140 -0
- package/dist/styles-areas/pdf-viewer.css +47 -0
- package/dist/styles-areas/problem-detail.css +97 -0
- package/dist/styles-areas/rich-text.css +167 -0
- package/dist/styles-areas/sharing.css +153 -0
- package/dist/styles-areas/spreadsheet.css +37 -0
- package/dist/styles-areas/stats.css +281 -0
- package/dist/styles-areas/test-report.css +133 -0
- package/dist/styles-areas/virtual.css +30 -0
- package/dist/styles-areas/wizard.css +381 -0
- package/dist/styles-areas/workbench.css +543 -0
- package/dist/styles-areas/workbook-viewer.css +44 -0
- package/dist/styles-static.css +34 -23
- package/dist/styles.css +34 -23
- package/package.json +130 -32
- package/scripts/checkDistExports.ts +67 -0
- package/scripts/cssRules.ts +125 -0
- package/scripts/fixtureAppCss.ts +344 -0
- package/scripts/generateAreaStyles.ts +253 -0
- package/scripts/guardrailsEnforce.spec.ts +62 -0
- package/scripts/guardrailsEnforce.ts +67 -3
- package/scripts/paths.ts +106 -0
- package/scripts/styleAreas.ts +585 -0
- package/scripts/verify.ts +5 -0
- package/src/barrelsReachNoOptionalPeer.spec.ts +117 -9
- package/src/docsMatchTheSplit.spec.ts +98 -0
- package/src/fixtureAppCss.spec.ts +177 -0
- package/src/namedSubpathsResolve.spec.ts +35 -0
- package/src/publishShape.spec.ts +67 -0
- package/src/react/components/ListToolbar.spec.tsx +172 -0
- package/src/react/components/ListToolbar.tsx +151 -0
- package/src/react/file-tree/FileTree.spec.tsx +1009 -0
- package/src/react/file-tree/FileTree.tsx +1459 -309
- package/src/react/file-tree/fileTree.css +151 -0
- package/src/react/file-tree/fileTreeSelection.spec.ts +327 -0
- package/src/react/file-tree/fileTreeSelection.ts +321 -0
- package/src/react/file-tree/index.ts +21 -0
- package/src/react/index.ts +1 -0
- package/src/react/lib/form.ts +51 -8
- package/src/react/markdown/MarkdownDoc.spec.tsx +1 -1
- package/src/react/media/DenseGalleryShell.tsx +108 -36
- package/src/react/media/FocusViewer.tsx +15 -8
- package/src/react/media/FrameGrabber.spec.tsx +175 -0
- package/src/react/media/FrameGrabber.tsx +67 -0
- package/src/react/media/MediaResize.spec.tsx +84 -14
- package/src/react/media/MediaResize.tsx +51 -19
- package/src/react/media/VideoChapterEditor.tsx +428 -0
- package/src/react/media/VideoPlayer.spec.tsx +111 -1
- package/src/react/media/VideoPlayer.tsx +158 -11
- package/src/react/media/VideoTurner.spec.tsx +67 -0
- package/src/react/media/VideoTurner.tsx +70 -15
- package/src/react/media/denseGallery.css +104 -46
- package/src/react/media/hlsSource.recovery.spec.ts +213 -0
- package/src/react/media/hlsSource.spec.ts +8 -0
- package/src/react/media/hlsSource.ts +189 -1
- package/src/react/media/index.ts +14 -0
- package/src/react/media/mediaResize.css +127 -54
- package/src/react/media/pictureEditor.css +52 -6
- package/src/react/media/videoChapterEditor.css +203 -0
- package/src/react/media/videoChapters.spec.ts +176 -0
- package/src/react/media/videoChapters.ts +181 -0
- package/src/react/media-gallery/GalleryTable.tsx +93 -3
- package/src/react/media-gallery/MediaGallery.spec.tsx +1156 -10
- package/src/react/media-gallery/MediaGallery.tsx +813 -95
- package/src/react/media-gallery/MediaMetaEditor.tsx +237 -0
- package/src/react/media-gallery/galleryTable.spec.ts +41 -1
- package/src/react/media-gallery/index.ts +7 -0
- package/src/react/media-gallery/mediaGallery.css +180 -4
- package/src/react/media-gallery/mediaMeta.spec.ts +95 -0
- package/src/react/media-gallery/mediaMeta.ts +101 -0
- package/src/react/media-gallery/playbackPreferences.spec.ts +172 -0
- package/src/react/media-gallery/playbackPreferences.ts +100 -0
- package/src/react/media-gallery/types.ts +34 -0
- package/src/shippedFilesAreTracked.spec.ts +69 -0
- package/src/styles-areas/activity-bar.css +30 -0
- package/src/styles-areas/analytics.css +672 -0
- package/src/styles-areas/auth.css +154 -0
- package/src/styles-areas/calendar.css +416 -0
- package/src/styles-areas/chart-table.css +41 -0
- package/src/styles-areas/charts.css +89 -0
- package/src/styles-areas/chat.css +364 -0
- package/src/styles-areas/clipboard.css +26 -0
- package/src/styles-areas/code-editor.css +130 -0
- package/src/styles-areas/companion-link.css +70 -0
- package/src/styles-areas/core.css +2503 -0
- package/src/styles-areas/dashboard-grid.css +84 -0
- package/src/styles-areas/data-table.css +657 -0
- package/src/styles-areas/deep-link.css +26 -0
- package/src/styles-areas/diff-viewer.css +30 -0
- package/src/styles-areas/disk-usage.css +262 -0
- package/src/styles-areas/emoji.css +154 -0
- package/src/styles-areas/fields.css +582 -0
- package/src/styles-areas/file-tree.css +33 -0
- package/src/styles-areas/filter-rail.css +235 -0
- package/src/styles-areas/folder-tree.css +219 -0
- package/src/styles-areas/keep-awake.css +26 -0
- package/src/styles-areas/layout-engine.css +239 -0
- package/src/styles-areas/markdown.css +87 -0
- package/src/styles-areas/master-detail.css +239 -0
- package/src/styles-areas/master-lock.css +26 -0
- package/src/styles-areas/media-gallery.css +351 -0
- package/src/styles-areas/media.css +658 -0
- package/src/styles-areas/nav.css +488 -0
- package/src/styles-areas/notifications.css +245 -0
- package/src/styles-areas/overlays.css +133 -0
- package/src/styles-areas/palette.css +140 -0
- package/src/styles-areas/pdf-viewer.css +47 -0
- package/src/styles-areas/problem-detail.css +97 -0
- package/src/styles-areas/rich-text.css +167 -0
- package/src/styles-areas/sharing.css +153 -0
- package/src/styles-areas/spreadsheet.css +37 -0
- package/src/styles-areas/stats.css +281 -0
- package/src/styles-areas/test-report.css +133 -0
- package/src/styles-areas/virtual.css +30 -0
- package/src/styles-areas/wizard.css +381 -0
- package/src/styles-areas/workbench.css +543 -0
- package/src/styles-areas/workbook-viewer.css +44 -0
- package/src/styles-static.css +34 -23
- package/src/styles.css +34 -23
- package/src/stylesAreas.spec.ts +247 -0
- package/src/stylesUtilitiesMatches.spec.ts +6 -77
- package/src/testFilesRunInParallel.spec.ts +79 -0
- package/src/typecheckCachesAreSeparate.spec.ts +128 -0
- package/src/verifyGraph.spec.ts +6 -0
|
@@ -21,13 +21,38 @@
|
|
|
21
21
|
* So the gesture is a hook and the affordances are components, and a third player gets both
|
|
22
22
|
* by calling them rather than by copying eighty lines that drift.
|
|
23
23
|
*
|
|
24
|
+
* ── 🔴 Both affordances live in a RAIL, below the picture ─────────────────────────────
|
|
25
|
+
* Owner, `ideas/collections-09-14.md`: *"Fix the drag to resize area to no longer overlap
|
|
26
|
+
* part of the horizontal play timeline area that shows what part of the video is playing. It
|
|
27
|
+
* should only be draggable when my mouse pointer is at the bottom right corner of the video
|
|
28
|
+
* player, just like mac windows for other apps work."*
|
|
29
|
+
*
|
|
30
|
+
* So every consumer renders exactly this, directly after the box that holds the picture:
|
|
31
|
+
*
|
|
32
|
+
* <div className='cbmr-rail'>
|
|
33
|
+
* <MediaResizeBar {...handle} label='…' />
|
|
34
|
+
* <MediaResizeCorner {...handle} label='…' />
|
|
35
|
+
* </div>
|
|
36
|
+
*
|
|
37
|
+
* and `MediaResize.spec.tsx` fails if a player stops doing so. The rail is a strip of chrome
|
|
38
|
+
* OUTSIDE the frame — a Mac window's grab zone is the window's own edge, not a patch of its
|
|
39
|
+
* content — which is what makes "nothing of it overlaps the control bar" a property of the
|
|
40
|
+
* layout rather than a gutter that has to be tuned against whatever the player draws. The
|
|
41
|
+
* geometry is proved in `e2e/resize-grip-rail.spec.ts`, in a real browser, at two viewports;
|
|
42
|
+
* happy-dom computes no layout, so nothing under `bun test` can see it.
|
|
43
|
+
*
|
|
24
44
|
* ── What each affordance is for ───────────────────────────────────────────────────────
|
|
25
|
-
* · {@link MediaResizeCorner} — the literal ask.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
45
|
+
* · {@link MediaResizeCorner} — the literal ask. The rail's bottom-right corner, 64px of
|
|
46
|
+
* reach, visible at rest, because that is the whole point of this pass.
|
|
47
|
+
* · {@link MediaResizeBar} — the rail's full-width edge. 🔴 **Coarse pointers only, and that
|
|
48
|
+
* is this pass's second decision.** *"It should only be draggable when my mouse pointer is
|
|
49
|
+
* at the bottom right corner"* is a verdict on the bar too: on a desktop there is now one
|
|
50
|
+
* drag surface and it is the corner, exactly like a window. The one argument that was ever
|
|
51
|
+
* made for keeping the bar beside the corner is about a THUMB — a corner is a small target
|
|
52
|
+
* on a phone, and on a portrait clip the picture's corner is empty letterboxing while the
|
|
53
|
+
* bottom edge is always where the picture ends — so it survives under `(pointer: coarse)`
|
|
54
|
+
* and nowhere else. It is still rendered by both players at every size; `mediaResize.css`
|
|
55
|
+
* decides where it is shown, because the input device is a media query and not a prop.
|
|
31
56
|
*
|
|
32
57
|
* Both drive the same {@link useMediaResize} state, so a viewer may render either or both
|
|
33
58
|
* and they can never disagree about how tall it is.
|
|
@@ -81,24 +106,31 @@ export interface MediaResizeAffordanceProps extends MediaResizeHandleProps {
|
|
|
81
106
|
style?: CSSProperties;
|
|
82
107
|
}
|
|
83
108
|
/**
|
|
84
|
-
* The corner grip — *"dragging the corner"*,
|
|
109
|
+
* The corner grip — *"dragging the corner"*, at the bottom-right of the rail under the
|
|
110
|
+
* picture.
|
|
85
111
|
*
|
|
86
|
-
* 🔴
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* to sit near one.
|
|
112
|
+
* 🔴 OUTSIDE the clipped frame since 2026-09-16, and the reason is in `mediaResize.css`'s
|
|
113
|
+
* header: keeping an overlay in that corner clear of a control bar means shortening the
|
|
114
|
+
* control bar, which costs the timeline the width it was complained about for and works only
|
|
115
|
+
* on a player this package styles. *"just like mac windows for other apps work"* — a window's
|
|
116
|
+
* grab zone is its outside corner, not a patch of its content, and the rail is that corner.
|
|
92
117
|
*/
|
|
93
118
|
export declare function MediaResizeCorner({ label, className, style, ...handle }: MediaResizeAffordanceProps): import("react").JSX.Element;
|
|
94
119
|
/**
|
|
95
|
-
* The bottom edge —
|
|
120
|
+
* The bottom edge — the rail's full-width bar.
|
|
121
|
+
*
|
|
122
|
+
* 🔴 **Shown on coarse pointers only** (`mediaResize.css`), which is the decision this
|
|
123
|
+
* component's existence turns on and is argued in this file's header: on a desktop the owner
|
|
124
|
+
* asked for one drag surface, in the corner, and the bar's own stated reason — a corner is a
|
|
125
|
+
* small target for a thumb, and a letterboxed picture's corner is empty black on a portrait
|
|
126
|
+
* clip while the bottom edge is always where the picture ends — is entirely about touch. It
|
|
127
|
+
* is rendered unconditionally so the two players cannot disagree about the markup; where it
|
|
128
|
+
* is SHOWN is a media query, because the input device is not something a prop can know.
|
|
96
129
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* the rest as invisible padding, so it reads as a hairline and is hit like a button.
|
|
130
|
+
* `DESIGN.md` §5 wants a 44px target: the bar is 4px of ink with the rest as invisible
|
|
131
|
+
* padding, so it reads as a hairline and is hit like a button. That padding stays INSIDE the
|
|
132
|
+
* rail — it used to be pulled back over the picture and the transport with `margin-block:
|
|
133
|
+
* -11px`, which is one of the overlaps the 2026-09-14 report is about.
|
|
102
134
|
*/
|
|
103
135
|
export declare function MediaResizeBar({ label, className, style, ...handle }: MediaResizeAffordanceProps): import("react").JSX.Element;
|
|
104
136
|
//# sourceMappingURL=MediaResize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaResize.d.ts","sourceRoot":"","sources":["../../../src/react/media/MediaResize.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MediaResize.d.ts","sourceRoot":"","sources":["../../../src/react/media/MediaResize.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,IAAI,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAG9E,OAAO,mBAAmB,CAAC;AAU3B,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC/D,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC7D,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,WAAW,EACX,OAAO,GACR,EAAE,qBAAqB,GAAG,gBAAgB,CAgG1C;AAyBD,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAuB,EACvB,SAAS,EACT,KAAK,EACL,GAAG,MAAM,EACV,EAAE,0BAA0B,+BAe5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAuB,EACvB,SAAS,EACT,KAAK,EACL,GAAG,MAAM,EACV,EAAE,0BAA0B,+BAY5B"}
|
|
@@ -116,26 +116,33 @@ function sliderAria(props, label) {
|
|
|
116
116
|
}
|
|
117
117
|
const TIP = 'Drag to resize the viewer · ↑↓ to nudge · double-click to fit automatically';
|
|
118
118
|
/**
|
|
119
|
-
* The corner grip — *"dragging the corner"*,
|
|
119
|
+
* The corner grip — *"dragging the corner"*, at the bottom-right of the rail under the
|
|
120
|
+
* picture.
|
|
120
121
|
*
|
|
121
|
-
* 🔴
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* to sit near one.
|
|
122
|
+
* 🔴 OUTSIDE the clipped frame since 2026-09-16, and the reason is in `mediaResize.css`'s
|
|
123
|
+
* header: keeping an overlay in that corner clear of a control bar means shortening the
|
|
124
|
+
* control bar, which costs the timeline the width it was complained about for and works only
|
|
125
|
+
* on a player this package styles. *"just like mac windows for other apps work"* — a window's
|
|
126
|
+
* grab zone is its outside corner, not a patch of its content, and the rail is that corner.
|
|
127
127
|
*/
|
|
128
128
|
export function MediaResizeCorner({ label = 'Viewer height', className, style, ...handle }) {
|
|
129
129
|
return (_jsx(Tooltip, { content: TIP, children: _jsx("div", { ...sliderAria(handle, label), className: `cbmr-corner${className ? ` ${className}` : ''}`, style: style, children: _jsxs("svg", { "aria-hidden": 'true', viewBox: '0 0 12 12', className: 'cbmr-corner-glyph', children: [_jsx("title", { children: "Resize" }), _jsx("path", { d: 'M11 3 L3 11 M11 7 L7 11' })] }) }) }));
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
|
-
* The bottom edge —
|
|
132
|
+
* The bottom edge — the rail's full-width bar.
|
|
133
133
|
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
134
|
+
* 🔴 **Shown on coarse pointers only** (`mediaResize.css`), which is the decision this
|
|
135
|
+
* component's existence turns on and is argued in this file's header: on a desktop the owner
|
|
136
|
+
* asked for one drag surface, in the corner, and the bar's own stated reason — a corner is a
|
|
137
|
+
* small target for a thumb, and a letterboxed picture's corner is empty black on a portrait
|
|
138
|
+
* clip while the bottom edge is always where the picture ends — is entirely about touch. It
|
|
139
|
+
* is rendered unconditionally so the two players cannot disagree about the markup; where it
|
|
140
|
+
* is SHOWN is a media query, because the input device is not something a prop can know.
|
|
141
|
+
*
|
|
142
|
+
* `DESIGN.md` §5 wants a 44px target: the bar is 4px of ink with the rest as invisible
|
|
143
|
+
* padding, so it reads as a hairline and is hit like a button. That padding stays INSIDE the
|
|
144
|
+
* rail — it used to be pulled back over the picture and the transport with `margin-block:
|
|
145
|
+
* -11px`, which is one of the overlaps the 2026-09-14 report is about.
|
|
139
146
|
*/
|
|
140
147
|
export function MediaResizeBar({ label = 'Viewer height', className, style, ...handle }) {
|
|
141
148
|
return (_jsx(Tooltip, { content: TIP, children: _jsx("div", { ...sliderAria(handle, label), className: `cbmr-bar${className ? ` ${className}` : ''}`, style: style, children: _jsx("span", { className: 'cbmr-bar-grip', "aria-hidden": 'true' }) }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaResize.js","sourceRoot":"","sources":["../../../src/react/media/MediaResize.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"MediaResize.js","sourceRoot":"","sources":["../../../src/react/media/MediaResize.tsx"],"names":[],"mappings":";AA4DA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,mBAAmB,CAAC;AAC3B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAuCvB,wDAAwD;AACxD,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,UAAU,EACV,WAAW,EACX,OAAO,GACe;IACtB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,GAAG,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACrF,OAAO,EAAE,CAAC;QACV,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO;QAClD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7C,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd;;;;;;;;;OASG;IACH,MAAM,YAAY,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,YAAY,CAAC;IAExE,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,KAAqC,EAAE,EAAE;QACxC,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC;QACnC,MAAM,WAAW,GAAG,YAAY,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;QAC7B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,MAAM,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE5C,MAAM,IAAI,GAAG,CAAC,SAAuB,EAAE,EAAE;YACvC,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,OAAO;YAC9B,SAAS,CAAC,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,OAAO,GAAG,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;QACvF,CAAC,CAAC;QACF,MAAM,GAAG,GAAG,GAAG,EAAE;YACf,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;YACzB,MAAM,CAAC,qBAAqB,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAChD,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YAChD,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC7C,MAAM,CAAC,mBAAmB,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACjD,iFAAiF;YACjF,8EAA8E;YAC9E,sBAAsB;YACtB,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;gBACpB,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBACtC,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,CAAC,gBAAgB,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;QAC9C,KAAK,CAAC,cAAc,EAAE,CAAC;IACzB,CAAC,EACD,CAAC,WAAW,EAAE,YAAY,EAAE,UAAU,CAAC,CACxC,CAAC;IAEF,yFAAyF;IACzF,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,KAAuC,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,mBAAmB,CAAC,YAAY,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QACnE,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC,EACD,CAAC,WAAW,EAAE,YAAY,EAAE,UAAU,CAAC,CACxC,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,YAAY,EACZ,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,YAAY,CAC3E,CAAC;IAEF,OAAO;QACL,MAAM;QACN,OAAO;QACP,MAAM,EAAE;YACN,QAAQ,EAAE,OAAO,IAAI,YAAY;YACjC,QAAQ;YACR,aAAa,EAAE,WAAW;YAC1B,SAAS,EAAE,WAAW;YACtB,aAAa,EAAE,WAAW;SAC3B;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAA6B,EAAE,KAAa;IAC9D,OAAO;QACL,IAAI,EAAE,QAAiB;QACvB,kBAAkB,EAAE,UAAmB;QACvC,YAAY,EAAE,KAAK;QACnB,eAAe,EAAE,YAAY;QAC7B,eAAe,EAAE,KAAK,CAAC,QAAQ;QAC/B,eAAe,EAAE,KAAK,CAAC,QAAQ;QAC/B,gBAAgB,EAAE,GAAG,KAAK,CAAC,QAAQ,cAAc;QACjD,QAAQ,EAAE,CAAC;QACX,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,aAAa,EAAE,KAAK,CAAC,aAAa;KACnC,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,6EAA6E,CAAC;AAU1F;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,KAAK,GAAG,eAAe,EACvB,SAAS,EACT,KAAK,EACL,GAAG,MAAM,EACkB;IAC3B,OAAO,CACL,KAAC,OAAO,IAAC,OAAO,EAAE,GAAG,YACnB,iBACM,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAC7B,SAAS,EAAE,cAAc,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAC3D,KAAK,EAAE,KAAK,YAEZ,8BAAiB,MAAM,EAAC,OAAO,EAAC,WAAW,EAAC,SAAS,EAAC,mBAAmB,aACvE,qCAAqB,EACrB,eAAM,CAAC,EAAC,yBAAyB,GAAG,IAChC,GACF,GACE,CACX,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,KAAK,GAAG,eAAe,EACvB,SAAS,EACT,KAAK,EACL,GAAG,MAAM,EACkB;IAC3B,OAAO,CACL,KAAC,OAAO,IAAC,OAAO,EAAE,GAAG,YACnB,iBACM,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAC7B,SAAS,EAAE,WAAW,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACxD,KAAK,EAAE,KAAK,YAEZ,eAAM,SAAS,EAAC,eAAe,iBAAa,MAAM,GAAG,GACjD,GACE,CACX,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import './videoChapterEditor.css';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { type VideoChapter } from './videoChapters';
|
|
4
|
+
/**
|
|
5
|
+
* Put markers on a video at points in time and NAME them — the dialog.
|
|
6
|
+
*
|
|
7
|
+
* ── The owner's ask, 2026-09-15, which is the whole scope ──────────────────────────────
|
|
8
|
+
* *"allow putting markers on videos at points in time and giving them a title for the section
|
|
9
|
+
* for knowing what that part of the video is about, like youtube chapters."*
|
|
10
|
+
*
|
|
11
|
+
* So this dialog never touches a byte and never queues anything. It produces a
|
|
12
|
+
* `VideoChapter[]`, hands it to `onSubmit`, and closes — everything about what makes a list
|
|
13
|
+
* valid is `videoChapters.ts`, shared with the player that draws the dividers, so the strip in
|
|
14
|
+
* here and the scrub bar out there cannot disagree about where a section starts.
|
|
15
|
+
*
|
|
16
|
+
* 🔴 The sibling of `VideoCutEditor`, and deliberately NOT part of it. The two gestures look
|
|
17
|
+
* identical on a timeline — click a point, do something to it — and have nothing else in
|
|
18
|
+
* common. A cut is a REQUEST that costs a full re-encode and comes back as a second file
|
|
19
|
+
* minutes or hours later; a chapter is metadata that applies the moment it is saved and is
|
|
20
|
+
* free to be wrong. One dialog holding both would mean naming a section and queueing an ffmpeg
|
|
21
|
+
* job through the same Send button.
|
|
22
|
+
*
|
|
23
|
+
* ── Why the name comes BEFORE the mark ────────────────────────────────────────────────
|
|
24
|
+
* The field is filled first and `◆ Mark here` is disabled until it holds something. The other
|
|
25
|
+
* order — mark now, name later — is how every list of "Chapter 3" ends up in a video nobody
|
|
26
|
+
* can navigate: the marker exists, it is already useful enough to leave alone, and the name is
|
|
27
|
+
* the part that gets skipped. Requiring it up front costs one keystroke's worth of order and
|
|
28
|
+
* makes an unnamed chapter unreachable rather than merely discouraged.
|
|
29
|
+
*
|
|
30
|
+
* ── Keeping it out of the way when it is not in use ───────────────────────────────────
|
|
31
|
+
* One `InfoHint` for the whole model, the fleet's standard ⓘ — a 16px glyph at rest that
|
|
32
|
+
* opens on hover, focus or tap. A line of instructions under the player is permanent rent paid
|
|
33
|
+
* by every visit after the first.
|
|
34
|
+
*/
|
|
35
|
+
export interface VideoChapterEditorProps {
|
|
36
|
+
/** The clip, as a URL a `<video>` can play. */
|
|
37
|
+
src: string;
|
|
38
|
+
/** Shown until the first frame decodes — a black box reads as a broken player. */
|
|
39
|
+
posterUrl?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The clip's length, when the app already knows it. The element's own metadata still wins
|
|
43
|
+
* once it loads: a duration the catalog rounded to the second would put the last chapter's
|
|
44
|
+
* section a fraction out, and could refuse a mark near the end that is perfectly legal.
|
|
45
|
+
*/
|
|
46
|
+
durationSeconds?: number | null;
|
|
47
|
+
/** The sections this video already has. Edited in place; nothing is saved until Save. */
|
|
48
|
+
chapters?: readonly VideoChapter[];
|
|
49
|
+
open?: boolean;
|
|
50
|
+
onClose: () => void;
|
|
51
|
+
/**
|
|
52
|
+
* Save the sections. Resolving CLOSES the dialog; rejecting leaves it open with the list
|
|
53
|
+
* intact, because the alternative is losing several minutes of careful naming to one failed
|
|
54
|
+
* request.
|
|
55
|
+
*
|
|
56
|
+
* An EMPTY list is a legitimate answer — it means "this video has no sections" and is how
|
|
57
|
+
* every chapter gets removed. Unlike a cut list, there is nothing expensive on the other
|
|
58
|
+
* side of it to refuse.
|
|
59
|
+
*/
|
|
60
|
+
onSubmit: (chapters: VideoChapter[]) => void | Promise<void>;
|
|
61
|
+
/** Wording for the confirm button — apps store this differently. */
|
|
62
|
+
submitLabel?: string;
|
|
63
|
+
/** Shown under the list: anything the app wants to say about where these are kept. */
|
|
64
|
+
note?: React.ReactNode;
|
|
65
|
+
}
|
|
66
|
+
export declare function VideoChapterEditor({ src, posterUrl, name, durationSeconds, chapters: initialChapters, open, onClose, onSubmit, submitLabel, note, }: VideoChapterEditorProps): React.JSX.Element;
|
|
67
|
+
//# sourceMappingURL=VideoChapterEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoChapterEditor.d.ts","sourceRoot":"","sources":["../../../src/react/media/VideoChapterEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,0BAA0B,CAAC;AAClC,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;;;;;OAQG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,SAAS,EACT,IAAI,EACJ,eAAe,EACf,QAAQ,EAAE,eAAe,EACzB,IAAW,EACX,OAAO,EACP,QAAQ,EACR,WAA6B,EAC7B,IAAI,GACL,EAAE,uBAAuB,qBAiVzB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import './videoChapterEditor.css';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Bookmark, Trash2 } from 'lucide-react';
|
|
5
|
+
import { cutTimecode } from 'cursedbelt-core/media/video-cuts';
|
|
6
|
+
import { ActionBar } from '../components/ActionBar';
|
|
7
|
+
import { Button } from '../components/Button';
|
|
8
|
+
import { InfoHint } from '../components/InfoHint';
|
|
9
|
+
import { Input } from '../components/Input';
|
|
10
|
+
import { Modal } from '../components/Modal';
|
|
11
|
+
import { chapterProblem, chapterSpans, normalizeChapters, } from './videoChapters';
|
|
12
|
+
export function VideoChapterEditor({ src, posterUrl, name, durationSeconds, chapters: initialChapters, open = true, onClose, onSubmit, submitLabel = 'Save sections', note, }) {
|
|
13
|
+
const videoRef = React.useRef(null);
|
|
14
|
+
const [duration, setDuration] = React.useState(durationSeconds && Number.isFinite(durationSeconds) ? durationSeconds : 0);
|
|
15
|
+
const [playhead, setPlayhead] = React.useState(0);
|
|
16
|
+
/*
|
|
17
|
+
* 🔴 Seeded from the prop SORTED but not normalized, and this is the bug that shape avoids:
|
|
18
|
+
* `normalizeChapters` needs a duration, the duration is frequently unknown at mount (the
|
|
19
|
+
* element has not read its metadata yet), and with `duration === 0` it returns an empty
|
|
20
|
+
* list. Normalizing here would therefore wipe every existing chapter of every video whose
|
|
21
|
+
* length the app did not already know — and since this is initial state, they would never
|
|
22
|
+
* come back. Normalization happens where a duration is real: on the strip, and on save.
|
|
23
|
+
*/
|
|
24
|
+
const [chapters, setChapters] = React.useState(() => [...(initialChapters ?? [])].sort((a, b) => a.startSeconds - b.startSeconds));
|
|
25
|
+
const [draftTitle, setDraftTitle] = React.useState('');
|
|
26
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
27
|
+
const [error, setError] = React.useState(null);
|
|
28
|
+
const spans = React.useMemo(() => chapterSpans(chapters, duration), [chapters, duration]);
|
|
29
|
+
/** What Save would send. Also what `isDirty` compares, so the two can never disagree. */
|
|
30
|
+
const saved = React.useMemo(() => normalizeChapters(chapters, duration), [chapters, duration]);
|
|
31
|
+
const baseline = React.useMemo(() => JSON.stringify(normalizeChapters(initialChapters ?? [], duration)), [initialChapters, duration]);
|
|
32
|
+
const isDirty = JSON.stringify(saved) !== baseline;
|
|
33
|
+
/**
|
|
34
|
+
* A row whose name has been cleared. It is REPORTED rather than dropped: `normalizeChapters`
|
|
35
|
+
* would quietly discard it on save, so somebody who emptied a field to retype it and then
|
|
36
|
+
* pressed Save would be told the save succeeded and lose the marker.
|
|
37
|
+
*/
|
|
38
|
+
const unnamed = chapters.filter((chapter) => chapter.title.trim().length === 0).length;
|
|
39
|
+
const pct = React.useCallback((seconds) => (duration > 0 ? Math.min(100, Math.max(0, (seconds / duration) * 100)) : 0), [duration]);
|
|
40
|
+
const seek = React.useCallback((seconds) => {
|
|
41
|
+
const video = videoRef.current;
|
|
42
|
+
if (!video || !Number.isFinite(seconds))
|
|
43
|
+
return;
|
|
44
|
+
video.currentTime = Math.max(0, seconds);
|
|
45
|
+
setPlayhead(Math.max(0, seconds));
|
|
46
|
+
}, []);
|
|
47
|
+
/** ◆ — name the section that starts at the playhead. */
|
|
48
|
+
const markHere = React.useCallback(() => {
|
|
49
|
+
const draft = { startSeconds: playhead, title: draftTitle };
|
|
50
|
+
const problem = chapterProblem(draft, chapters, duration);
|
|
51
|
+
if (problem) {
|
|
52
|
+
setError(problem);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setError(null);
|
|
56
|
+
setChapters((current) => [...current, { startSeconds: playhead, title: draftTitle.trim() }].sort((a, b) => a.startSeconds - b.startSeconds));
|
|
57
|
+
setDraftTitle('');
|
|
58
|
+
}, [playhead, draftTitle, chapters, duration]);
|
|
59
|
+
/** Rename in place. Keyed by the START, which is unique and does not move when a name does. */
|
|
60
|
+
const rename = React.useCallback((startSeconds, title) => {
|
|
61
|
+
setError(null);
|
|
62
|
+
setChapters((current) => current.map((chapter) => chapter.startSeconds === startSeconds ? { ...chapter, title } : chapter));
|
|
63
|
+
}, []);
|
|
64
|
+
const remove = React.useCallback((startSeconds) => {
|
|
65
|
+
setError(null);
|
|
66
|
+
setChapters((current) => current.filter((chapter) => chapter.startSeconds !== startSeconds));
|
|
67
|
+
}, []);
|
|
68
|
+
/*
|
|
69
|
+
* `Space` plays, and nothing else is bound — deliberately.
|
|
70
|
+
*
|
|
71
|
+
* 🔴 There is no key for "mark here". This dialog's primary control is a TEXT FIELD, which
|
|
72
|
+
* owns the keyboard the whole time somebody is using it: any letter bound on the container
|
|
73
|
+
* is a letter that either cannot be typed or fires twice depending on where focus happens to
|
|
74
|
+
* be. Enter in the field marks, which is the one shortcut that needs no arbitration.
|
|
75
|
+
*
|
|
76
|
+
* Bound on the DIALOG, not the window: the gallery has its own ⇧←/⇧→ and Space handling, and
|
|
77
|
+
* a global listener would keep firing after this closed. `Modal` traps focus, so the dialog
|
|
78
|
+
* really does have the keystrokes while it is open.
|
|
79
|
+
*/
|
|
80
|
+
const onKeyDown = (event) => {
|
|
81
|
+
const target = event.target;
|
|
82
|
+
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA'))
|
|
83
|
+
return;
|
|
84
|
+
if (event.key !== ' ')
|
|
85
|
+
return;
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
const video = videoRef.current;
|
|
88
|
+
if (!video)
|
|
89
|
+
return;
|
|
90
|
+
if (video.paused)
|
|
91
|
+
void video.play().catch(() => undefined);
|
|
92
|
+
else
|
|
93
|
+
video.pause();
|
|
94
|
+
};
|
|
95
|
+
const submit = async () => {
|
|
96
|
+
if (unnamed > 0) {
|
|
97
|
+
setError(unnamed === 1
|
|
98
|
+
? 'One section has no name. Name it, or remove it.'
|
|
99
|
+
: `${unnamed} sections have no name. Name them, or remove them.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setSubmitting(true);
|
|
103
|
+
setError(null);
|
|
104
|
+
try {
|
|
105
|
+
await onSubmit(saved);
|
|
106
|
+
onClose();
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
// 🔴 Left open, with the list intact. A failed request must not cost somebody the
|
|
110
|
+
// several minutes of scrubbing and naming that produced it.
|
|
111
|
+
setError(err instanceof Error ? err.message : 'That could not be saved. Nothing was changed.');
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
setSubmitting(false);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/** Why `◆ Mark here` is unavailable, as the sentence its tooltip shows. */
|
|
118
|
+
const markProblem = chapterProblem({ startSeconds: playhead, title: draftTitle }, chapters, duration);
|
|
119
|
+
const leadPct = spans.length > 0 ? pct(spans[0]?.startSeconds ?? 0) : 0;
|
|
120
|
+
return (_jsx(Modal, { open: open, onClose: onClose, title: name ? `Sections of ${name}` : 'Video sections', subtitle: 'Mark a point, name the part that starts there \u2014 the player shows them on the scrub bar.', size: 'lg',
|
|
121
|
+
// 🔴 The discard guard. This body holds text fields, so a backdrop click without it
|
|
122
|
+
// throws away every name typed since the dialog opened, with no prompt.
|
|
123
|
+
isDirty: isDirty, ...(error ? { problem: error } : {}), footer: _jsx(ActionBar, { primary: _jsxs(_Fragment, { children: [_jsx(Button, { variant: 'secondary', onClick: onClose, tooltip: 'Close without saving anything', children: "Cancel" }), _jsx(Button, { onClick: () => void submit(), disabled: submitting || unnamed > 0, tooltip: unnamed > 0
|
|
124
|
+
? 'Every section needs a name before this can be saved'
|
|
125
|
+
: 'Save these sections — nothing about the video itself is changed', children: submitting ? 'Saving…' : submitLabel })] }) }), children: _jsxs("fieldset", { className: 'cbch', onKeyDown: onKeyDown, "aria-label": 'Name the sections of this video. Type a name, then mark the point it starts at. Space plays and pauses.', children: [_jsx("video", { ref: videoRef, className: 'cbch-video', src: src, poster: posterUrl, controls: true, playsInline: true, preload: 'metadata', onLoadedMetadata: (event) => {
|
|
126
|
+
const seen = event.currentTarget.duration;
|
|
127
|
+
// The element's own metadata wins over anything the catalog rounded.
|
|
128
|
+
if (Number.isFinite(seen) && seen > 0)
|
|
129
|
+
setDuration(seen);
|
|
130
|
+
}, onTimeUpdate: (event) => setPlayhead(event.currentTarget.currentTime), onSeeked: (event) => setPlayhead(event.currentTarget.currentTime) }), _jsxs("div", { className: 'cbch-add', children: [_jsx("div", { className: 'cbch-add-field', children: _jsx(Input, { value: draftTitle, onChange: (event) => {
|
|
131
|
+
setDraftTitle(event.currentTarget.value);
|
|
132
|
+
setError(null);
|
|
133
|
+
}, onEnter: markHere, "aria-label": 'What this part of the video is about', placeholder: 'What is this part about?' }) }), _jsx(Button, { size: 'sm', variant: 'secondary', onClick: markHere, disabled: markProblem !== null, leftIcon: _jsx(Bookmark, { "aria-hidden": 'true', size: 14 }), tooltip: markProblem ?? `Start a section called "${draftTitle.trim()}" here`, children: "Mark here" }), _jsx("span", { className: 'cbch-playhead', children: cutTimecode(playhead) }), _jsxs(InfoHint, { title: 'Naming the parts of a video', align: 'right', children: [_jsxs("p", { children: ["Play or scrub to where a part begins, type what it is about, and press", ' ', _jsx("strong", { children: "Mark here" }), " (or ", _jsx("kbd", { children: "Enter" }), " in the box)."] }), _jsxs("p", { children: ["Each section runs until the next one starts. Click a section's time to jump there, edit a name in place, and ", _jsx("kbd", { children: "Space" }), " plays and pauses."] }), _jsx("p", { children: "Nothing here changes your video \u2014 these are names the player shows on the scrub bar and in its chapters menu." })] })] }), _jsxs("ul", { className: 'cbch-strip', "aria-label": spans.length === 0
|
|
134
|
+
? 'No sections yet — the whole clip is one unnamed part'
|
|
135
|
+
: `${spans.length} section${spans.length === 1 ? '' : 's'}`, children: [leadPct > 0 ? (_jsx("li", { className: 'cbch-lead', style: { width: `${leadPct}%` }, "aria-label": 'The opening, which has no section yet' })) : null, spans.map((span) => (_jsx("li", { className: 'cbch-span-item', style: { width: `${pct(span.endSeconds) - pct(span.startSeconds)}%` }, children: _jsx("button", {
|
|
136
|
+
// guardrails-ignore no-raw-action-button: a section of the timeline, sized to
|
|
137
|
+
// its own duration — a `Button` carries padding and a min-width, which is
|
|
138
|
+
// exactly wrong for a control whose WIDTH is the information.
|
|
139
|
+
type: 'button', className: 'cbch-span', "aria-label": `Jump to "${span.title}" at ${cutTimecode(span.startSeconds)}`, onClick: () => seek(span.startSeconds), children: _jsx("span", { className: 'cbch-span-label', children: span.title }) }) }, span.key))), _jsx("span", { className: 'cbch-strip-head', style: { left: `${pct(playhead)}%` } })] }), _jsx("p", { className: 'cbch-summary', children: duration > 0
|
|
140
|
+
? spans.length === 0
|
|
141
|
+
? `${cutTimecode(duration)} of video, in one unnamed part`
|
|
142
|
+
: `${spans.length} section${spans.length === 1 ? '' : 's'} across ${cutTimecode(duration)}`
|
|
143
|
+
: 'Reading the clip…' }), chapters.length === 0 ? (_jsx("p", { className: 'cbch-empty', children: "No sections yet. Scrub to where a part begins, name it, and mark it." })) : (_jsx("ul", { className: 'cbch-list', children: chapters.map((chapter) => (_jsxs("li", { className: 'cbch-row', children: [_jsxs("button", {
|
|
144
|
+
// guardrails-ignore no-raw-action-button: a plain-CSS row inside a lazily
|
|
145
|
+
// imported, deliberately Tailwind-free editor — see `videoCutEditor.css`.
|
|
146
|
+
type: 'button', className: 'cbch-row-goto', "aria-label": `Jump to ${cutTimecode(chapter.startSeconds)}`, onClick: () => seek(chapter.startSeconds), children: [_jsx(Bookmark, { "aria-hidden": 'true', size: 12 }), " ", cutTimecode(chapter.startSeconds)] }), _jsx("div", { className: 'cbch-row-title', children: _jsx(Input, { size: 'sm', value: chapter.title, onChange: (event) => rename(chapter.startSeconds, event.currentTarget.value), "aria-label": `Name of the section at ${cutTimecode(chapter.startSeconds)}`, "aria-invalid": chapter.title.trim().length === 0, placeholder: 'Name this section' }) }), _jsx(Button, { size: 'sm', variant: 'ghost', tooltip: 'Remove this section \u2014 the video keeps playing straight through', "aria-label": `Remove the section at ${cutTimecode(chapter.startSeconds)}`, onClick: () => remove(chapter.startSeconds), children: _jsx(Trash2, { "aria-hidden": 'true', size: 14 }) })] }, chapter.startSeconds.toFixed(3)))) })), note ? _jsx("div", { className: 'cbch-note', children: note }) : null] }) }));
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=VideoChapterEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoChapterEditor.js","sourceRoot":"","sources":["../../../src/react/media/VideoChapterEditor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ import { type MediaPlayerInstance, type MediaProviderAdapter } from '@vidstack/r
|
|
|
5
5
|
import { type DefaultVideoLayoutSlots } from '@vidstack/react/player/layouts/default';
|
|
6
6
|
import { type RefObject } from 'react';
|
|
7
7
|
import type { PlayableVideo } from 'cursedbelt-core/media';
|
|
8
|
+
import { type SessionVolume } from '../media-gallery/playbackPreferences';
|
|
9
|
+
import { type ChapteredVideo } from './videoChapters';
|
|
8
10
|
/**
|
|
9
11
|
* The reusable video player primitive. Consumes a {@link PlayableVideo} (URLs already
|
|
10
12
|
* resolved by a `cursedbelt-server/storage` source) and renders Vidstack's default layout —
|
|
@@ -19,16 +21,36 @@ import type { PlayableVideo } from 'cursedbelt-core/media';
|
|
|
19
21
|
* `<video>` element safe here — no canvas workaround. Scrub-preview thumbnails come from the
|
|
20
22
|
* source's WebVTT track (`vttUrl`); while derivatives are still being built, a thin progress
|
|
21
23
|
* bar rides the bottom edge of the surface (the original bytes stay playable behind it).
|
|
24
|
+
*
|
|
25
|
+
* Named sections (`chapters`) become a real `kind='chapters'` text track, which is what turns
|
|
26
|
+
* Vidstack's default layout from a bare bar into a YouTube-shaped one — dividers on the time
|
|
27
|
+
* slider, the current section's name above it, and a chapters menu. See {@link ChaptersTrack}.
|
|
22
28
|
*/
|
|
23
29
|
export interface VideoPlayerProps {
|
|
24
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The clip, with every URL already resolved — and its named sections, when the app carries
|
|
32
|
+
* any. {@link ChapteredVideo} is `PlayableVideo` plus `chapters`, so every existing source
|
|
33
|
+
* still satisfies this unchanged.
|
|
34
|
+
*/
|
|
35
|
+
video: ChapteredVideo;
|
|
25
36
|
/**
|
|
26
37
|
* Start playing on mount. **Defaults to `true`** — see {@link usePrefersReducedMotion} and
|
|
27
38
|
* the block on {@link AUTOPLAY_DEFAULT}.
|
|
28
39
|
*/
|
|
29
40
|
autoPlay?: boolean;
|
|
30
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Start silent. **Defaults to `true` whenever autoplay is in effect** (see below) — unless
|
|
43
|
+
* the person has already set a volume on this surface THIS SESSION, which wins over both.
|
|
44
|
+
*/
|
|
31
45
|
muted?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Where this surface's session volume is kept. Defaults to {@link PLAYER_VOLUME_KEY}.
|
|
48
|
+
*
|
|
49
|
+
* 🔴 Pass a different key for a player that is a DIFFERENT surface — the gallery stage
|
|
50
|
+
* passes `STAGE_VOLUME_KEY` — for the reason `media/stageResize.ts` keys the remembered
|
|
51
|
+
* height: one shared key makes two viewers on one page fight over a single number.
|
|
52
|
+
*/
|
|
53
|
+
volumeKey?: string;
|
|
32
54
|
className?: string;
|
|
33
55
|
/**
|
|
34
56
|
* Intrinsic aspect ratio (a CSS `aspect-ratio` value, e.g. `'16/9'` or `'4/3'`). Gives the
|
|
@@ -233,21 +255,41 @@ export declare const AUTOPLAY_DEFAULT = true;
|
|
|
233
255
|
* preference", which is the behavior everything had before this existed.
|
|
234
256
|
*/
|
|
235
257
|
/**
|
|
236
|
-
* The autoplay decision
|
|
258
|
+
* The autoplay decision — and, since 2026-09-16, how loud the clip opens.
|
|
237
259
|
*
|
|
238
260
|
* 🔴 It is extracted rather than inlined because nothing below a real browser can see the
|
|
239
261
|
* difference: `autoPlay` and `muted` are two props on one JSX element, and swapping them,
|
|
240
262
|
* dropping the reduced-motion term, or letting `muted` default independently all typecheck
|
|
241
263
|
* and render identically under happy-dom. The same reasoning `posterFraming.ts` and
|
|
242
264
|
* `mediaGalleryModel.ts` already apply to their own decisions.
|
|
265
|
+
*
|
|
266
|
+
* ── 🔴 Where `session` sits in the precedence, and why ──────────────────────────────────
|
|
267
|
+
* Owner, `ideas/collections-09-14.md`: *"Keep the media player volume where I set it after
|
|
268
|
+
* changing it during a session except if I refresh or first log in then continue starting it
|
|
269
|
+
* at mute. Then if I unmute it, keep that value as I change to other videos."*
|
|
270
|
+
*
|
|
271
|
+
* · an explicit `muted` prop still wins — an app that says "silent" means silent
|
|
272
|
+
* · then what he set THIS SESSION ({@link readSessionVolume}, `null` until he touches it)
|
|
273
|
+
* · then the old rule: muted follows autoPlay, because an unmuted autoplay is refused
|
|
274
|
+
*
|
|
275
|
+
* A `null` session is therefore byte-for-byte the behavior every consumer had before this
|
|
276
|
+
* existed, which is what makes "a refresh starts at mute" true rather than merely intended:
|
|
277
|
+
* a fresh tab has no session entry, so a fresh tab takes the third line.
|
|
278
|
+
*
|
|
279
|
+
* `volume` is returned separately from `muted` on purpose — see {@link SessionVolume}. A mute
|
|
280
|
+
* stored as `volume: 0` would make the control bar's unmute button return to silence, and
|
|
281
|
+
* *"if I unmute it, keep that value"* is the sentence that forbids it.
|
|
243
282
|
*/
|
|
244
283
|
export declare function resolveAutoplay(opts: {
|
|
245
284
|
autoPlay?: boolean;
|
|
246
285
|
muted?: boolean;
|
|
247
286
|
reducedMotion: boolean;
|
|
287
|
+
/** What he set this session on this surface, or `null`/absent for "he has not". */
|
|
288
|
+
session?: SessionVolume | null;
|
|
248
289
|
}): {
|
|
249
290
|
autoPlay: boolean;
|
|
250
291
|
muted: boolean;
|
|
292
|
+
volume: number;
|
|
251
293
|
};
|
|
252
294
|
/**
|
|
253
295
|
* 🔴 Make the picture follow the scrub bar WHILE it is dragged, not only on release.
|
|
@@ -325,7 +367,7 @@ export declare function useLiveScrub(hostRef: RefObject<HTMLElement | null>, pla
|
|
|
325
367
|
*/
|
|
326
368
|
export declare function preferBundledHls(provider: MediaProviderAdapter | null): void;
|
|
327
369
|
export declare function usePrefersReducedMotion(): boolean;
|
|
328
|
-
export declare function VideoPlayer({ video, autoPlay, muted, className, aspectRatio, onEnded, loop, liveScrub, smallLayoutWhen, onStep, canStep, fullscreenOnDoubleClick, }: VideoPlayerProps): import("react").JSX.Element;
|
|
370
|
+
export declare function VideoPlayer({ video, autoPlay, muted, volumeKey, className, aspectRatio, onEnded, loop, liveScrub, smallLayoutWhen, onStep, canStep, fullscreenOnDoubleClick, }: VideoPlayerProps): import("react").JSX.Element;
|
|
329
371
|
/**
|
|
330
372
|
* The processing state for a video surface: a thin progress bar pinned across the bottom edge
|
|
331
373
|
* while derivatives build (indeterminate shimmer until real progress arrives), or a compact
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/media/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,iDAAiD,CAAC;AACzD,OAAO,yDAAyD,CAAC;AACjE,OAAO,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/media/VideoPlayer.tsx"],"names":[],"mappings":"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"}
|