cursedbelt 3.0.2 → 4.0.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 +111 -31
- 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/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/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
|
@@ -177,12 +177,52 @@
|
|
|
177
177
|
container-type: size;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/* 🔴 The turned picture surface is SIZED to the swapped panel, not merely bounded by it.
|
|
181
|
+
Owner, `ideas/collections-09-14.md`: *"If I rotate a portait style video 90 degrees it should
|
|
182
|
+
be changed to fit the wide screen panel … so I can watch it like it was recorded with wide
|
|
183
|
+
screen."* — a rotation on its own leaves the footage the size it already was.
|
|
184
|
+
|
|
185
|
+
These used to be `max-block-size`/`max-inline-size`, which can only SHRINK. Measured in
|
|
186
|
+
Chromium on 2026-09-16 against `demo`'s 9:16 clip in a 505×284 panel: the player gives its
|
|
187
|
+
`<video>` a 16:9 box of its own (284×160 there), `object-fit: contain` draws the portrait
|
|
188
|
+
film as a 90×160 pillar inside it, and the max-* pair had nothing to bind on — so the turn
|
|
189
|
+
painted 160×90 of film in a 505×284 panel, which is the tall thin picture in the middle of a
|
|
190
|
+
black stage that was reported. `e2e/video-turn.spec.ts` measures exactly that rectangle.
|
|
191
|
+
|
|
192
|
+
Width from the container's BLOCK size and height from its INLINE size — the axis swap, said
|
|
193
|
+
in the only units that can say it — so after the rotation the box is the panel again. The
|
|
194
|
+
fit itself is then `object-fit: contain`'s, which is what keeps this honest for footage whose
|
|
195
|
+
aspect is not the panel's inverse: a 16:9 clip turned on its end is letterboxed whole rather
|
|
196
|
+
than blown up and cropped, and a square one is left alone. A scale factor on the transform
|
|
197
|
+
cannot make that distinction — it does not know the film's shape — which is why the fit lives
|
|
198
|
+
on the box and only the rotation travels in `--cbmg-turn`. */
|
|
180
199
|
.cbmg-stage-turn[data-turned='true'] video,
|
|
181
200
|
.cbmg-stage-turn[data-turned='true'] .vds-poster img {
|
|
182
201
|
max-width: none;
|
|
183
202
|
max-height: none;
|
|
184
|
-
max-block-size:
|
|
185
|
-
max-inline-size:
|
|
203
|
+
max-block-size: none;
|
|
204
|
+
max-inline-size: none;
|
|
205
|
+
width: 100cqb;
|
|
206
|
+
height: 100cqi;
|
|
207
|
+
/* The player sets its own 16:9 on the element; an explicit pair of sides must win over it,
|
|
208
|
+
or the swap above is computed and then thrown away. */
|
|
209
|
+
aspect-ratio: auto;
|
|
210
|
+
object-fit: contain;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* 🔴 …and it has to be turned about the PANEL's centre, which means it has to sit at it first.
|
|
214
|
+
A rotation pivots on the element's own middle, so an element the player has left against the
|
|
215
|
+
left edge of its provider box lands a quarter of the panel off to one side — measured in
|
|
216
|
+
Chromium on 2026-09-16: a correctly sized 505×284 of film, painted at x=278 in a panel
|
|
217
|
+
starting at x=388, i.e. 110px of it outside the frame and a black band the same width down
|
|
218
|
+
the other edge. The wrapper above centres what IS its child; on the Vidstack path the child
|
|
219
|
+
is the player, and the element being turned is two boxes further in. Same reach as
|
|
220
|
+
`.vds-poster` / `.vds-controls` elsewhere in this file, and inert on the native stage, whose
|
|
221
|
+
`<video>` is already the wrapper's own flex-centred child. */
|
|
222
|
+
.cbmg-stage-turn[data-turned='true'] [data-media-provider] {
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
justify-content: center;
|
|
186
226
|
}
|
|
187
227
|
|
|
188
228
|
/* Inside the turned frame the stage element must stop claiming its own 70vh, or the flex
|
|
@@ -322,6 +362,63 @@
|
|
|
322
362
|
background: color-mix(in srgb, var(--accent-solid) 12%, transparent);
|
|
323
363
|
}
|
|
324
364
|
|
|
365
|
+
/* ── The tile the STAGE is showing ───────────────────────────────────────────────────────
|
|
366
|
+
*
|
|
367
|
+
* Owner, `ideas/collections-09-14.md`: *"I want to … still see the playing video in the list
|
|
368
|
+
* and nothing about the list changes as I pick other videos."* The strip under the stage used
|
|
369
|
+
* to drop the open file; it keeps it now and marks it here.
|
|
370
|
+
*
|
|
371
|
+
* 🔴 `::before`, where the selection tint is `::after`, so a tile that is BOTH ticked and
|
|
372
|
+
* playing draws both marks instead of one silently replacing the other. Neither pseudo is a
|
|
373
|
+
* `:first-child`, so `.cbmg-selectable > :first-child { height: 100% }` above is untouched.
|
|
374
|
+
*/
|
|
375
|
+
.cbmg-selectable.is-playing::before {
|
|
376
|
+
content: '';
|
|
377
|
+
position: absolute;
|
|
378
|
+
inset: 0;
|
|
379
|
+
/* guardrails-ignore no-raw-zindex: the same tile-local stacking the selection tint uses —
|
|
380
|
+
the mark over its own tile, inside the isolated `.cbmg-selectable` context. */
|
|
381
|
+
z-index: 1;
|
|
382
|
+
pointer-events: none;
|
|
383
|
+
border-radius: 8px;
|
|
384
|
+
/* Thicker than the selection's 2px: this one has to be findable while scrolling past it,
|
|
385
|
+
which is the whole reason the tile stays in the list. */
|
|
386
|
+
outline: 3px solid var(--accent-solid);
|
|
387
|
+
outline-offset: -3px;
|
|
388
|
+
background: color-mix(in srgb, var(--accent-solid) 10%, transparent);
|
|
389
|
+
}
|
|
390
|
+
/* A filled capsule with its own foreground token (DESIGN.md §3 — never a thin outline), at
|
|
391
|
+
the 12px floor (§4). `pointer-events: none` because the whole tile is one button and a
|
|
392
|
+
label that swallowed the click would make its corner dead. */
|
|
393
|
+
.cbmg-playing-chip {
|
|
394
|
+
position: absolute;
|
|
395
|
+
top: 6px;
|
|
396
|
+
left: 6px;
|
|
397
|
+
/* guardrails-ignore no-raw-zindex: tile-local stacking, over this tile's own mark. */
|
|
398
|
+
z-index: 2;
|
|
399
|
+
display: inline-flex;
|
|
400
|
+
align-items: center;
|
|
401
|
+
gap: 4px;
|
|
402
|
+
max-width: calc(100% - 12px);
|
|
403
|
+
padding: 3px 8px;
|
|
404
|
+
border-radius: 999px;
|
|
405
|
+
background: var(--accent-solid);
|
|
406
|
+
color: var(--accent-solid-foreground);
|
|
407
|
+
font-size: 12px;
|
|
408
|
+
font-weight: 600;
|
|
409
|
+
line-height: 1.2;
|
|
410
|
+
white-space: nowrap;
|
|
411
|
+
overflow: hidden;
|
|
412
|
+
text-overflow: ellipsis;
|
|
413
|
+
pointer-events: none;
|
|
414
|
+
}
|
|
415
|
+
/* The one corner collision: a document tile draws its type badge in this exact spot. The mark
|
|
416
|
+
wins for as long as it is there — the stage directly above is showing the file, and its
|
|
417
|
+
titlebar says what kind it is, so the badge is the redundant one of the two. */
|
|
418
|
+
.cbmg-selectable.is-playing .cbmg-doc-badge {
|
|
419
|
+
display: none;
|
|
420
|
+
}
|
|
421
|
+
|
|
325
422
|
.cbmg-bulkbar {
|
|
326
423
|
display: flex;
|
|
327
424
|
align-items: center;
|
|
@@ -356,7 +453,7 @@
|
|
|
356
453
|
frame grabber were each written inside ONE app's `detailActions`; these are the rules that
|
|
357
454
|
came with them. Plain CSS under `.cbmg-`, like the rest of this file. */
|
|
358
455
|
|
|
359
|
-
/* 🔴 The
|
|
456
|
+
/* 🔴 The Picture adjustment reaches the stage through a custom property set on the stage
|
|
360
457
|
anchor, NOT through a prop threaded into the player — the video stage is Vidstack's and is
|
|
361
458
|
several components deep, so a prop would make every consumer of the player primitive learn
|
|
362
459
|
about a control only the gallery offers.
|
|
@@ -544,6 +641,21 @@
|
|
|
544
641
|
box-shadow: inset 3px 0 0 0 var(--accent);
|
|
545
642
|
}
|
|
546
643
|
|
|
644
|
+
/* The row the stage is showing — the table's half of the grid tile's mark. Same shape as the
|
|
645
|
+
selected row so the two read as one family, and a step louder (the SOLID accent, a 4px bar)
|
|
646
|
+
because there is exactly one of these and it is the row the reader is looking for.
|
|
647
|
+
The name it is given is `.cbgd-sr-only` text in the name cell; `GalleryTable.tsx` says why a
|
|
648
|
+
visible capsule cannot live in a fixed-layout cell. */
|
|
649
|
+
.cbgt-row.is-playing {
|
|
650
|
+
background: color-mix(in srgb, var(--accent-solid) 12%, transparent);
|
|
651
|
+
box-shadow: inset 4px 0 0 0 var(--accent-solid);
|
|
652
|
+
}
|
|
653
|
+
/* …and it keeps that reading under the pointer, which the hover rule above would otherwise
|
|
654
|
+
take away by painting the plain hover fill over it. */
|
|
655
|
+
.cbgt-row.is-playing:hover {
|
|
656
|
+
background: color-mix(in srgb, var(--accent-solid) 18%, transparent);
|
|
657
|
+
}
|
|
658
|
+
|
|
547
659
|
/* Fixed tracks, declared once. `table-layout: fixed` means these widths are the layout. */
|
|
548
660
|
.cbgt-cell--check {
|
|
549
661
|
width: 36px;
|
|
@@ -566,6 +678,14 @@
|
|
|
566
678
|
.cbgt-cell--length {
|
|
567
679
|
width: 78px;
|
|
568
680
|
}
|
|
681
|
+
/* The exact byte count. Wider than the rounded Size beside it because the whole point of the
|
|
682
|
+
column is that nothing is dropped: a terabyte is 13 digits plus 4 separators, and a count
|
|
683
|
+
that truncated to "1,402,653,1…" would be exactly as useless for telling two files apart as
|
|
684
|
+
the rounded "1.4 GB" it exists to replace. `.cbgt-cell--num` already right-aligns it and
|
|
685
|
+
gives it tabular figures, which is what makes two counts comparable down a column. */
|
|
686
|
+
.cbgt-cell--bytes {
|
|
687
|
+
width: 132px;
|
|
688
|
+
}
|
|
569
689
|
/* The date is the widest of the three numeric columns: "21 Aug 2026" at 12px does not fit
|
|
570
690
|
the 96px the others use, and a DATE that truncates to "Aug 21, 2…" is a column that has
|
|
571
691
|
stopped answering the question it exists for. Deliberately generous — every locale this
|
|
@@ -731,7 +851,14 @@
|
|
|
731
851
|
/* ── Narrow screens ──────────────────────────────────────────────────────────────────────
|
|
732
852
|
*
|
|
733
853
|
* Kind and Length are the two columns a phone can lose without the row stopping being
|
|
734
|
-
* useful — the name, the size and the date are what the owner is scanning for.
|
|
854
|
+
* useful — the name, the size and the date are what the owner is scanning for.
|
|
855
|
+
*
|
|
856
|
+
* 🔴 The exact-bytes column goes with them, and it is the one that would hurt most to keep:
|
|
857
|
+
* it is the widest track in the table (132px), and these widths ARE the layout under
|
|
858
|
+
* `table-layout: fixed` — thumb + size + bytes + date already exceeds a 390px phone on their
|
|
859
|
+
* own, so keeping it would trade the file NAME for a digit count. The rounded Size stays, so
|
|
860
|
+
* the row still answers "how big"; comparing two counts to the byte is a desk instrument, and
|
|
861
|
+
* the desk is where the column is. */
|
|
735
862
|
@media (max-width: 720px) {
|
|
736
863
|
.cbgt-head th:first-child,
|
|
737
864
|
.cbgt-row td:first-child {
|
|
@@ -742,6 +869,7 @@
|
|
|
742
869
|
padding-right: 12px;
|
|
743
870
|
}
|
|
744
871
|
.cbgt-cell--kind,
|
|
872
|
+
.cbgt-cell--bytes,
|
|
745
873
|
.cbgt-cell--length {
|
|
746
874
|
display: none;
|
|
747
875
|
}
|
|
@@ -782,3 +910,51 @@
|
|
|
782
910
|
min-width: 0;
|
|
783
911
|
min-height: 0;
|
|
784
912
|
}
|
|
913
|
+
|
|
914
|
+
/*
|
|
915
|
+
* ── The DESCRIPTION, under the title on the stage ───────────────────────────────────────
|
|
916
|
+
*
|
|
917
|
+
* Owner, `ideas/collections-09-14.md`: *"In media galleries have an option for apps to tag
|
|
918
|
+
* media and aadd descriptions to media."*
|
|
919
|
+
*
|
|
920
|
+
* 🔴 It cannot ride the ` · ` facts line. `.cbgd-meta-line` is `white-space: nowrap` with an
|
|
921
|
+
* ellipsis because the SAME class captions every tile in the grid, so a sentence appended
|
|
922
|
+
* there is a sentence nobody can read past its first few words. These two rules are what let
|
|
923
|
+
* the titlebar's own wrapping flex row carry a second, wrapping line — the facts keep the
|
|
924
|
+
* nowrap behaviour they have always had, and the description is a block beneath them.
|
|
925
|
+
*
|
|
926
|
+
* The width cap is readability, not decoration: `DESIGN.md` §4 asks for a measure, and a
|
|
927
|
+
* description running the full width of a 1680px stage is one line of 200 characters.
|
|
928
|
+
*/
|
|
929
|
+
.cbmg-stage-facts {
|
|
930
|
+
display: block;
|
|
931
|
+
}
|
|
932
|
+
.cbmg-stage-description {
|
|
933
|
+
display: block;
|
|
934
|
+
/* Overrides the nowrap/ellipsis inherited from `.cbgd-meta-line` — see above. */
|
|
935
|
+
white-space: normal;
|
|
936
|
+
max-width: 68ch;
|
|
937
|
+
margin-top: 2px;
|
|
938
|
+
font-size: 12px;
|
|
939
|
+
line-height: 1.45;
|
|
940
|
+
color: var(--muted-foreground);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/*
|
|
944
|
+
* The tag/description editor's body — see `MediaMetaEditor.tsx`. Plain CSS in this module's
|
|
945
|
+
* own stylesheet rather than utilities on the elements, for the reason the rest of this file
|
|
946
|
+
* records: the sheet is already loaded wherever the gallery is, and the dialog is otherwise
|
|
947
|
+
* built entirely from design-system components that bring their own chrome.
|
|
948
|
+
*/
|
|
949
|
+
.cbmm {
|
|
950
|
+
display: flex;
|
|
951
|
+
flex-direction: column;
|
|
952
|
+
gap: 14px;
|
|
953
|
+
}
|
|
954
|
+
.cbmm-count {
|
|
955
|
+
display: flex;
|
|
956
|
+
align-items: center;
|
|
957
|
+
gap: 6px;
|
|
958
|
+
font-size: 13px;
|
|
959
|
+
color: var(--muted-foreground);
|
|
960
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { MediaGalleryItem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Tags and a description, expressed as a CHANGE rather than as a new value.
|
|
4
|
+
*
|
|
5
|
+
* ── Why a change and not a pair of fields ──────────────────────────────────────────────
|
|
6
|
+
* The owner's ask, `ideas/collections-09-14.md`, 2026-09-14: *"In media galleries have an
|
|
7
|
+
* option for apps to tag media and aadd descriptions to media."* The case he will actually
|
|
8
|
+
* hit is forty holiday photographs at once, and forty items do not share one tag list — each
|
|
9
|
+
* one has its own, and "the new tags" cannot be a single array without wiping thirty-nine
|
|
10
|
+
* lists that were never on screen.
|
|
11
|
+
*
|
|
12
|
+
* So the plural verb carries `addTags`/`removeTags`, which mean the same thing for one item
|
|
13
|
+
* as for forty, and a consumer writes ONE handler rather than a singular one plus a loop.
|
|
14
|
+
* That is the rule `tasks/Completed/240-*.md` states for the file tree's multi-select in as
|
|
15
|
+
* many words — *"One request for N ids, not N requests: the tree deliberately does not loop
|
|
16
|
+
* `onMove`, because a tree re-derived between each write is how a batch move half-lands."*
|
|
17
|
+
*
|
|
18
|
+
* 🔴 Everything here is case-INSENSITIVE and spelling-preserving. A vocabulary that mints
|
|
19
|
+
* `Bank`, `bank` and `BANK` as three tags is a vocabulary nothing can filter by, and the
|
|
20
|
+
* gallery's own chips would show all three. The first spelling seen wins; a tag that differs
|
|
21
|
+
* only in case from one already present is not an addition at all.
|
|
22
|
+
*/
|
|
23
|
+
export interface MediaMetaChange {
|
|
24
|
+
/** Tags to add to EVERY item in the set. Already trimmed, de-duplicated, never empty strings. */
|
|
25
|
+
addTags: string[];
|
|
26
|
+
/** Tags to remove from EVERY item in the set. Matched case-insensitively. */
|
|
27
|
+
removeTags: string[];
|
|
28
|
+
/**
|
|
29
|
+
* The new description, or `''` to clear it. `undefined` means "not part of this change" —
|
|
30
|
+
* which is what a bulk tag always sends, because one sentence cannot describe forty files
|
|
31
|
+
* and writing it onto all of them would delete thirty-nine that were never read.
|
|
32
|
+
*/
|
|
33
|
+
description?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Trim, drop the empties, drop case-insensitive duplicates keeping the first spelling. */
|
|
36
|
+
export declare function cleanTags(tags: readonly string[]): string[];
|
|
37
|
+
/**
|
|
38
|
+
* What it takes to get from `before` to `after` — the single-item editor's save, expressed in
|
|
39
|
+
* the same two lists the bulk one sends, so both paths arrive at a consumer as one shape.
|
|
40
|
+
*/
|
|
41
|
+
export declare function tagDiff(before: readonly string[] | undefined, after: readonly string[]): {
|
|
42
|
+
addTags: string[];
|
|
43
|
+
removeTags: string[];
|
|
44
|
+
};
|
|
45
|
+
/** Nothing to save. The editor's Save is disabled on this, so a no-op never reaches an app. */
|
|
46
|
+
export declare function isEmptyMetaChange(change: MediaMetaChange): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The merge a consumer would otherwise write itself — one item's row after the change lands.
|
|
49
|
+
*
|
|
50
|
+
* Exported for the same reason `fileTreeSelection.ts`'s rules are: the gallery persists
|
|
51
|
+
* nothing, so every app has to do this arithmetic, and four apps re-deriving "add these,
|
|
52
|
+
* remove those, keep the spelling" is four chances to mint a duplicate tag. `tags` comes back
|
|
53
|
+
* cleaned; `description` is omitted from the result when the change did not carry one, so a
|
|
54
|
+
* caller can spread the result over its row without clearing a description it never touched.
|
|
55
|
+
*/
|
|
56
|
+
export declare function applyMetaChange(item: Pick<MediaGalleryItem, 'tags' | 'description'>, change: MediaMetaChange): {
|
|
57
|
+
tags: string[];
|
|
58
|
+
description?: string;
|
|
59
|
+
};
|
|
60
|
+
/** Every tag on any of these items, cleaned and in first-seen order — the "Remove" vocabulary. */
|
|
61
|
+
export declare function tagsOn(items: readonly Pick<MediaGalleryItem, 'tags'>[]): string[];
|
|
62
|
+
//# sourceMappingURL=mediaMeta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaMeta.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/mediaMeta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAY3D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACrC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAS7C;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAMlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,aAAa,CAAC,EACpD,MAAM,EAAE,eAAe,GACtB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1C;AAED,kGAAkG;AAClG,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,CAEjF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Trim, drop the empties, drop case-insensitive duplicates keeping the first spelling. */
|
|
2
|
+
export function cleanTags(tags) {
|
|
3
|
+
const seen = new Set();
|
|
4
|
+
const out = [];
|
|
5
|
+
for (const raw of tags) {
|
|
6
|
+
const tag = raw.trim();
|
|
7
|
+
if (!tag)
|
|
8
|
+
continue;
|
|
9
|
+
const key = tag.toLowerCase();
|
|
10
|
+
if (seen.has(key))
|
|
11
|
+
continue;
|
|
12
|
+
seen.add(key);
|
|
13
|
+
out.push(tag);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What it takes to get from `before` to `after` — the single-item editor's save, expressed in
|
|
19
|
+
* the same two lists the bulk one sends, so both paths arrive at a consumer as one shape.
|
|
20
|
+
*/
|
|
21
|
+
export function tagDiff(before, after) {
|
|
22
|
+
const was = cleanTags(before ?? []);
|
|
23
|
+
const now = cleanTags(after);
|
|
24
|
+
const wasKeys = new Set(was.map((t) => t.toLowerCase()));
|
|
25
|
+
const nowKeys = new Set(now.map((t) => t.toLowerCase()));
|
|
26
|
+
return {
|
|
27
|
+
addTags: now.filter((tag) => !wasKeys.has(tag.toLowerCase())),
|
|
28
|
+
removeTags: was.filter((tag) => !nowKeys.has(tag.toLowerCase())),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Nothing to save. The editor's Save is disabled on this, so a no-op never reaches an app. */
|
|
32
|
+
export function isEmptyMetaChange(change) {
|
|
33
|
+
return (change.addTags.length === 0 &&
|
|
34
|
+
change.removeTags.length === 0 &&
|
|
35
|
+
change.description === undefined);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The merge a consumer would otherwise write itself — one item's row after the change lands.
|
|
39
|
+
*
|
|
40
|
+
* Exported for the same reason `fileTreeSelection.ts`'s rules are: the gallery persists
|
|
41
|
+
* nothing, so every app has to do this arithmetic, and four apps re-deriving "add these,
|
|
42
|
+
* remove those, keep the spelling" is four chances to mint a duplicate tag. `tags` comes back
|
|
43
|
+
* cleaned; `description` is omitted from the result when the change did not carry one, so a
|
|
44
|
+
* caller can spread the result over its row without clearing a description it never touched.
|
|
45
|
+
*/
|
|
46
|
+
export function applyMetaChange(item, change) {
|
|
47
|
+
const removed = new Set(change.removeTags.map((t) => t.toLowerCase()));
|
|
48
|
+
const kept = cleanTags(item.tags ?? []).filter((tag) => !removed.has(tag.toLowerCase()));
|
|
49
|
+
const tags = cleanTags([...kept, ...change.addTags]);
|
|
50
|
+
return change.description === undefined ? { tags } : { tags, description: change.description };
|
|
51
|
+
}
|
|
52
|
+
/** Every tag on any of these items, cleaned and in first-seen order — the "Remove" vocabulary. */
|
|
53
|
+
export function tagsOn(items) {
|
|
54
|
+
return cleanTags(items.flatMap((item) => item.tags ?? []));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=mediaMeta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaMeta.js","sourceRoot":"","sources":["../../../src/react/media-gallery/mediaMeta.ts"],"names":[],"mappings":"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"}
|
|
@@ -133,4 +133,81 @@ export declare const nextPlaybackEnd: (current: PlaybackEndBehavior) => Playback
|
|
|
133
133
|
export type GalleryViewMode = 'grid' | 'table';
|
|
134
134
|
export declare function readViewMode(): GalleryViewMode;
|
|
135
135
|
export declare function writeViewMode(mode: GalleryViewMode): void;
|
|
136
|
+
/**
|
|
137
|
+
* The level a player opens at, and whether it opens silent.
|
|
138
|
+
*
|
|
139
|
+
* ── The owner's spec, verbatim (`ideas/collections-09-14.md`, 2026-09-14) ───────────────
|
|
140
|
+
* *"Keep the media player volume where I set it after changing it during a session except if
|
|
141
|
+
* I refresh or first log in then continue starting it at mute. Then if I unmute it, keep that
|
|
142
|
+
* value as I change to other videos."*
|
|
143
|
+
*
|
|
144
|
+
* Three rules, and the middle one is the whole design:
|
|
145
|
+
*
|
|
146
|
+
* | fresh page load / first sign-in | muted, exactly as it is today |
|
|
147
|
+
* | he changes it, then opens another clip | that level, for the rest of the session |
|
|
148
|
+
* | he refreshes | muted again |
|
|
149
|
+
*
|
|
150
|
+
* ── 🔴 It is kept IN MEMORY. Not `localStorage`, and not `sessionStorage` either ────────
|
|
151
|
+
* The neighbours above are `localStorage` because they are ways of WORKING — how a folder
|
|
152
|
+
* plays through, how a stage is pinned — and a way of working survives a reload. Loudness is
|
|
153
|
+
* not: *"except if I refresh … continue starting it at mute"* is a request for this to be
|
|
154
|
+
* FORGOTTEN, so the only correct store is one that cannot outlive the page.
|
|
155
|
+
*
|
|
156
|
+
* 🔴 `sessionStorage` is NOT that store, however much its name suggests it. It is cleared
|
|
157
|
+
* when the TAB closes, not when the page loads — MDN, in as many words: *"the data survives
|
|
158
|
+
* page reloads and restores"*. Measured here 2026-09-16 in the repo's own Chromium (write,
|
|
159
|
+
* `page.reload()`, read: the value is still there), because the difference is invisible in a
|
|
160
|
+
* unit test and is precisely the row of the table above that he called out. A volume put
|
|
161
|
+
* there comes back LOUD on the one gesture he named as the one that must return to silence.
|
|
162
|
+
*
|
|
163
|
+
* A module-level `Map` dies with the document, which is what a person means by "this
|
|
164
|
+
* session". It is also why nothing here needs the `try` wrapping every reader above carries:
|
|
165
|
+
* there is no web storage to be denied by a sandboxed iframe or by Safari's private mode, and
|
|
166
|
+
* therefore no read that can throw the gallery render away over a remembered volume. The
|
|
167
|
+
* specs assert both stores stay untouched, so a later move onto one of them — which would
|
|
168
|
+
* silently restore loudness across a refresh — fails rather than passes.
|
|
169
|
+
*
|
|
170
|
+
* SSR-safe for the same reason: writes only ever happen in a browser event handler, so the
|
|
171
|
+
* map a server renders against is always empty and every server-rendered player starts muted.
|
|
172
|
+
*
|
|
173
|
+
* ── 🔴 Muted and the level are TWO facts ────────────────────────────────────────────────
|
|
174
|
+
* *"if I unmute it, keep that value"* — so a mute may not be stored as `volume: 0`. Pressing
|
|
175
|
+
* mute and pressing unmute has to leave the level where he put it, which it only does if the
|
|
176
|
+
* level survives the mute independently. Every media element already models these two
|
|
177
|
+
* separately; collapsing them here is what would break it.
|
|
178
|
+
*/
|
|
179
|
+
export interface SessionVolume {
|
|
180
|
+
/** 0–1: what it plays at, and what UNMUTING returns to. */
|
|
181
|
+
volume: number;
|
|
182
|
+
/** Silent — whatever level is underneath. */
|
|
183
|
+
muted: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* What a player opens at when the person has not touched the volume this session: full level,
|
|
187
|
+
* no sound. It is the level that matters — a restored `volume: 1` under a mute is what makes
|
|
188
|
+
* the control bar's unmute button behave the way it did before any of this existed.
|
|
189
|
+
*/
|
|
190
|
+
export declare const DEFAULT_SESSION_VOLUME: SessionVolume;
|
|
191
|
+
/**
|
|
192
|
+
* Where a session volume is kept — the CALLER names it, for the same reason
|
|
193
|
+
* `media/stageResize.ts` makes the remembered HEIGHT a keyed value: there is more than one
|
|
194
|
+
* player in this package, and one shared key would make setting the level on one of them
|
|
195
|
+
* silently reach into the other. The gallery stage and the shared `VideoPlayer` primitive an
|
|
196
|
+
* app mounts on a page of its own are two players.
|
|
197
|
+
*/
|
|
198
|
+
export declare const PLAYER_VOLUME_KEY = "cbvp:volume";
|
|
199
|
+
/** The gallery stage's own — both transports of it (Vidstack and the native `<video>`). */
|
|
200
|
+
export declare const STAGE_VOLUME_KEY = "cbmg:stage-volume";
|
|
201
|
+
/**
|
|
202
|
+
* What he set this session, or `null` for "he has not touched it".
|
|
203
|
+
*
|
|
204
|
+
* 🔴 `null` rather than {@link DEFAULT_SESSION_VOLUME}, and the distinction is load-bearing:
|
|
205
|
+
* a player that was never going to make a sound anyway (`autoPlay={false}`, which mutes
|
|
206
|
+
* nothing today) must keep behaving exactly as it did, and it only can if the caller can tell
|
|
207
|
+
* "nothing set" apart from "set to the default". Handing back the default here would silence
|
|
208
|
+
* every click-to-play consumer in the fleet on upgrade.
|
|
209
|
+
*/
|
|
210
|
+
export declare function readSessionVolume(key: string): SessionVolume | null;
|
|
211
|
+
/** Remember it for the rest of this PAGE. Never longer — see {@link SessionVolume}. */
|
|
212
|
+
export declare function writeSessionVolume(key: string, value: SessionVolume): void;
|
|
136
213
|
//# sourceMappingURL=playbackPreferences.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playbackPreferences.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/playbackPreferences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,wDAAwD;AACxD,MAAM,MAAM,mBAAmB;AAC7B,8EAA8E;AAC5E,MAAM;AACR,oCAAoC;GAClC,QAAQ;AACV,sFAAsF;GACpF,MAAM;AACR,wFAAwF;GACtF,KAAK;AACP,2FAA2F;GACzF,SAAS,CAAC;AAEd,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,EAMvD,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,mBAAmB,EACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAmB/C,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CASrD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAMpE;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,uCAAwC,CAAC;AAEzE,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMrD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAMtC;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAMnD;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAMpD;AAED,uEAAuE;AACvE,eAAO,MAAM,eAAe,GAAI,SAAS,mBAAmB,KAAG,mBAGlD,CAAC;AAEd;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAI/C,wBAAgB,YAAY,IAAI,eAAe,CAM9C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAMzD"}
|
|
1
|
+
{"version":3,"file":"playbackPreferences.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/playbackPreferences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,wDAAwD;AACxD,MAAM,MAAM,mBAAmB;AAC7B,8EAA8E;AAC5E,MAAM;AACR,oCAAoC;GAClC,QAAQ;AACV,sFAAsF;GACpF,MAAM;AACR,wFAAwF;GACtF,KAAK;AACP,2FAA2F;GACzF,SAAS,CAAC;AAEd,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,EAMvD,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,mBAAmB,EACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAmB/C,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CASrD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAMpE;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,uCAAwC,CAAC;AAEzE,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMrD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAMtC;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAMnD;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAMpD;AAED,uEAAuE;AACvE,eAAO,MAAM,eAAe,GAAI,SAAS,mBAAmB,KAAG,mBAGlD,CAAC;AAEd;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAI/C,wBAAgB,YAAY,IAAI,eAAe,CAM9C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAMzD;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAA0C,CAAC;AAEhF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAE/C,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAKpD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAKnE;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAO1E"}
|
|
@@ -210,4 +210,46 @@ export function writeViewMode(mode) {
|
|
|
210
210
|
// Unremembered is still usable this session.
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* What a player opens at when the person has not touched the volume this session: full level,
|
|
215
|
+
* no sound. It is the level that matters — a restored `volume: 1` under a mute is what makes
|
|
216
|
+
* the control bar's unmute button behave the way it did before any of this existed.
|
|
217
|
+
*/
|
|
218
|
+
export const DEFAULT_SESSION_VOLUME = { volume: 1, muted: true };
|
|
219
|
+
/**
|
|
220
|
+
* Where a session volume is kept — the CALLER names it, for the same reason
|
|
221
|
+
* `media/stageResize.ts` makes the remembered HEIGHT a keyed value: there is more than one
|
|
222
|
+
* player in this package, and one shared key would make setting the level on one of them
|
|
223
|
+
* silently reach into the other. The gallery stage and the shared `VideoPlayer` primitive an
|
|
224
|
+
* app mounts on a page of its own are two players.
|
|
225
|
+
*/
|
|
226
|
+
export const PLAYER_VOLUME_KEY = 'cbvp:volume';
|
|
227
|
+
/** The gallery stage's own — both transports of it (Vidstack and the native `<video>`). */
|
|
228
|
+
export const STAGE_VOLUME_KEY = 'cbmg:stage-volume';
|
|
229
|
+
/** Per key, for this document only. See {@link SessionVolume} for why it is not a web store. */
|
|
230
|
+
const sessionVolumes = new Map();
|
|
231
|
+
/**
|
|
232
|
+
* What he set this session, or `null` for "he has not touched it".
|
|
233
|
+
*
|
|
234
|
+
* 🔴 `null` rather than {@link DEFAULT_SESSION_VOLUME}, and the distinction is load-bearing:
|
|
235
|
+
* a player that was never going to make a sound anyway (`autoPlay={false}`, which mutes
|
|
236
|
+
* nothing today) must keep behaving exactly as it did, and it only can if the caller can tell
|
|
237
|
+
* "nothing set" apart from "set to the default". Handing back the default here would silence
|
|
238
|
+
* every click-to-play consumer in the fleet on upgrade.
|
|
239
|
+
*/
|
|
240
|
+
export function readSessionVolume(key) {
|
|
241
|
+
const held = sessionVolumes.get(key);
|
|
242
|
+
// A COPY: the caller hands this to a player that will go on changing it, and a shared
|
|
243
|
+
// object would let one surface's live state leak into the next one's starting value.
|
|
244
|
+
return held ? { ...held } : null;
|
|
245
|
+
}
|
|
246
|
+
/** Remember it for the rest of this PAGE. Never longer — see {@link SessionVolume}. */
|
|
247
|
+
export function writeSessionVolume(key, value) {
|
|
248
|
+
sessionVolumes.set(key, {
|
|
249
|
+
// Clamped here rather than at every call site: this lands on `HTMLMediaElement.volume`,
|
|
250
|
+
// which throws `IndexSizeError` outside 0–1.
|
|
251
|
+
volume: Math.min(1, Math.max(0, value.volume)),
|
|
252
|
+
muted: value.muted,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
213
255
|
//# sourceMappingURL=playbackPreferences.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playbackPreferences.js","sourceRoot":"","sources":["../../../src/react/media-gallery/playbackPreferences.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playbackPreferences.js","sourceRoot":"","sources":["../../../src/react/media-gallery/playbackPreferences.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FileCategory } from 'cwip/file-kind';
|
|
2
2
|
import type { ProcessingStatus } from 'cursedbelt-core/media';
|
|
3
|
+
import type { VideoChapter } from '../media/videoChapters';
|
|
3
4
|
/**
|
|
4
5
|
* Sort orders the gallery header offers.
|
|
5
6
|
*
|
|
@@ -82,6 +83,25 @@ export interface MediaGalleryItem extends MediaGalleryItemMedia {
|
|
|
82
83
|
durationSeconds?: number;
|
|
83
84
|
/** Free-form tags — shown in the meta line and as filter chips when any item has them. */
|
|
84
85
|
tags?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* What this file IS, in the owner's own words — shown under the title on the stage.
|
|
88
|
+
*
|
|
89
|
+
* 🔴 The owner's ask, `ideas/collections-09-14.md`, 2026-09-14: *"In media galleries have an
|
|
90
|
+
* option for apps to tag media and aadd descriptions to media."* Tags were already readable
|
|
91
|
+
* here and there was no description field at all, so half of that sentence had nowhere to
|
|
92
|
+
* land even for an app that stored one.
|
|
93
|
+
*
|
|
94
|
+
* Like {@link tags} it is READ freely: an item that carries one renders it whether or not
|
|
95
|
+
* the app can write one. Authoring both is a capability — pass
|
|
96
|
+
* {@link MediaGalleryProps.onSaveMeta} and the control appears.
|
|
97
|
+
*
|
|
98
|
+
* 🔴 Deliberately NOT on the tile caption. That caption is two lines by design (see
|
|
99
|
+
* `galleryTileLine`) and is already contested between the tags and the measurement; a
|
|
100
|
+
* sentence there would either be an ellipsis or a third line on every tile in the grid.
|
|
101
|
+
* The stage has the room, and the stage is where somebody is actually reading about one
|
|
102
|
+
* file rather than scanning forty.
|
|
103
|
+
*/
|
|
104
|
+
description?: string;
|
|
85
105
|
/** Download href when it differs from `url` (e.g. `?download=1` variants). */
|
|
86
106
|
downloadUrl?: string;
|
|
87
107
|
/**
|
|
@@ -104,6 +124,20 @@ export interface MediaGalleryItem extends MediaGalleryItemMedia {
|
|
|
104
124
|
flipX?: boolean;
|
|
105
125
|
flipY?: boolean;
|
|
106
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* The named sections of a video — YouTube chapters, stored by the app on its own row.
|
|
129
|
+
*
|
|
130
|
+
* 🔴 The owner's ask, 2026-09-15: *"allow putting markers on videos at points in time and
|
|
131
|
+
* giving them a title for the section for knowing what that part of the video is about, like
|
|
132
|
+
* youtube chapters."* Like {@link orientation}, this is metadata the app keeps beside the
|
|
133
|
+
* file rather than anything baked into it — a chapter costs no encode, applies the instant it
|
|
134
|
+
* is saved, and is free to be wrong.
|
|
135
|
+
*
|
|
136
|
+
* The stage passes them to the player, which draws the dividers on the scrub bar and fills
|
|
137
|
+
* its chapters menu (see `stagePlayableVideo`). Authoring them is a separate capability:
|
|
138
|
+
* pass {@link MediaGalleryProps.onSaveChapters} and the Sections control appears.
|
|
139
|
+
*/
|
|
140
|
+
chapters?: VideoChapter[];
|
|
107
141
|
/**
|
|
108
142
|
* Lazily resolve this item's media URLs the first time a tile or stage needs them.
|
|
109
143
|
* Called once per item id per gallery mount (cached); return `null` when the bytes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,WAAW,GACX,MAAM,GACN,UAAU,CAAC;AAEf;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAiB,SAAQ,qBAAqB;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACzE;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;CAC5D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
interface Violation {
|
|
3
|
+
file: string;
|
|
4
|
+
line: number;
|
|
5
|
+
snippet: string;
|
|
6
|
+
}
|
|
7
|
+
interface Rule {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
check: (filePath: string, source: string) => Violation[];
|
|
11
|
+
}
|
|
12
|
+
export declare function walk(dir: string, out?: string[]): string[];
|
|
13
|
+
export declare function setReimplementedNames(names: readonly string[]): void;
|
|
14
|
+
export declare const RULES: Record<string, Rule>;
|
|
15
|
+
export declare const ALLOWED_FILE_CATEGORIES: Set<string>;
|
|
16
|
+
export interface FileAllow {
|
|
17
|
+
rules: Set<string>;
|
|
18
|
+
category: string;
|
|
19
|
+
reason: string;
|
|
20
|
+
line: number;
|
|
21
|
+
}
|
|
22
|
+
/** Parse honored file-level allowances from a source file's header, plus warnings for any
|
|
23
|
+
* malformed/unrecognized directive (which is NOT honored — its violations still fire). */
|
|
24
|
+
export declare function parseFileAllows(source: string): {
|
|
25
|
+
allows: FileAllow[];
|
|
26
|
+
warnings: string[];
|
|
27
|
+
};
|
|
28
|
+
export type { Rule, Violation };
|
|
29
|
+
//# sourceMappingURL=guardrailsEnforce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrailsEnforce.d.ts","sourceRoot":"","sources":["../../scripts/guardrailsEnforce.ts"],"names":[],"mappings":";AAwKA,UAAU,SAAS;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,IAAI;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;CACzD;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CA6B9D;AAu2BD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEpE;AAweD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAkBtC,CAAC;AASF,eAAO,MAAM,uBAAuB,aAAkD,CAAC;AAGvF,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED;2FAC2F;AAC3F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA0C3F;AAED,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC"}
|