cursedbelt 5.74.0 → 5.74.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/react/components/Page.d.ts.map +1 -1
  2. package/dist/react/components/Page.js +10 -1
  3. package/dist/react/components/Page.js.map +1 -1
  4. package/dist/react/media/DenseGalleryShell.d.ts.map +1 -1
  5. package/dist/react/media/DenseGalleryShell.js +73 -9
  6. package/dist/react/media/DenseGalleryShell.js.map +1 -1
  7. package/dist/react/media/HoverScrubVideoThumb.d.ts +11 -9
  8. package/dist/react/media/HoverScrubVideoThumb.d.ts.map +1 -1
  9. package/dist/react/media/HoverScrubVideoThumb.js +98 -25
  10. package/dist/react/media/HoverScrubVideoThumb.js.map +1 -1
  11. package/dist/react/media/denseGallery.css +103 -7
  12. package/dist/react/media-gallery/GalleryTable.js +7 -9
  13. package/dist/react/media-gallery/GalleryTable.js.map +1 -1
  14. package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
  15. package/dist/react/media-gallery/MediaGallery.js +19 -7
  16. package/dist/react/media-gallery/MediaGallery.js.map +1 -1
  17. package/dist/react/media-gallery/playbackPreferences.d.ts +2 -2
  18. package/dist/react/media-gallery/playbackPreferences.js +2 -2
  19. package/dist/styles-areas/core.css +8 -6
  20. package/dist/styles-utilities.css +8 -0
  21. package/package.json +1 -1
  22. package/src/react/components/Page.tsx +11 -1
  23. package/src/react/media/DenseGalleryShell.tsx +90 -6
  24. package/src/react/media/HoverScrubVideoThumb.spec.tsx +82 -0
  25. package/src/react/media/HoverScrubVideoThumb.tsx +116 -28
  26. package/src/react/media/denseGallery.css +103 -7
  27. package/src/react/media-gallery/GalleryTable.tsx +7 -9
  28. package/src/react/media-gallery/MediaGallery.tsx +18 -7
  29. package/src/react/media-gallery/playbackPreferences.ts +2 -2
  30. package/src/styles-areas/core.css +8 -6
  31. package/src/styles-utilities.css +8 -0
@@ -36,7 +36,7 @@ import type { DragEvent } from 'react';
36
36
  import { type ReactNode, useCallback, useEffect, useRef, useState } from 'react';
37
37
  import { CATEGORY_LABEL } from '../components/FileTypeIcon.js';
38
38
  import { useResizeObserver } from '../hooks/useResizeObserver.js';
39
- import { useCanHover, useScrubPreview } from '../media/HoverScrubVideoThumb.js';
39
+ import { useScrubPreview } from '../media/HoverScrubVideoThumb.js';
40
40
  import { releaseVideo, useInView, useSettled } from '../media/previewGate.js';
41
41
  import { createdLabel, type MediaCache, ThumbImg, useItemMedia } from './galleryItemMedia.js';
42
42
  import { galleryItemCategory } from './mediaGalleryModel.js';
@@ -393,8 +393,7 @@ function SortHeader({
393
393
  * tile and is written in Tailwind utilities; this module is deliberately Tailwind-free (see
394
394
  * `mediaGallery.css`'s opening paragraph — binary-server's inspector has no Tailwind build, so
395
395
  * a Tailwind component here renders unstyled in half the fleet). What the two DO share is the
396
- * arithmetic: `useScrubPreview` steps the same five offsets and `useCanHover` makes the same
397
- * coarse-pointer decision, both imported rather than re-derived.
396
+ * arithmetic: `useScrubPreview` steps the same five offsets, imported rather than re-derived.
398
397
  *
399
398
  * 🔴 It replaces the poster rather than sitting on top of it, because at 56×40 there is no room
400
399
  * for a stack — and it attaches the element only while previewing, so a table of four hundred
@@ -481,14 +480,13 @@ function GalleryTableRow({
481
480
  * to preview it."* Before this, neither half was true in table view.
482
481
  */
483
482
  const [hovering, setHovering] = useState(false);
484
- const canHover = useCanHover();
485
483
  const previewSrc = category === 'video' ? (media.streamUrl ?? media.url) : undefined;
486
- // `autoPreview` is suppressed on a coarse pointer exactly as the grid tile suppresses it, so
487
- // a phone never opens forty video connections under a finger. A deliberate hover still does.
488
- // 🔴 And it waits until the row is on screen and has SETTLED there (`previewGate.ts`), the
489
- // same gate the grid tile uses — a table scrolled past fast must not open a stream per row.
484
+ // `autoPreview` runs on a touch screen too, exactly as the grid tile's does (2026-10-10: the
485
+ // phone's "Preview all" lit up and nothing moved — `HoverScrubVideoThumb`'s `autoPlay` note).
486
+ // 🔴 It waits until the row is on screen and has SETTLED there (`previewGate.ts`), the same
487
+ // gate the grid tile uses — a table scrolled past fast must not open a stream per row.
490
488
  const thumbRef = useRef<HTMLButtonElement | null>(null);
491
- const autoReady = useSettled(useInView(thumbRef, previewSrc != null && autoPreview && canHover));
489
+ const autoReady = useSettled(useInView(thumbRef, previewSrc != null && autoPreview));
492
490
  const previewing = previewSrc != null && (autoReady || hovering);
493
491
 
494
492
  return (
@@ -86,7 +86,7 @@ import {
86
86
  type SourceFault,
87
87
  sourceFaultMessage,
88
88
  } from '../media/sourceFault.js';
89
- import { HoverScrubVideoThumb } from '../media/HoverScrubVideoThumb.js';
89
+ import { HoverScrubVideoThumb, useCanHover } from '../media/HoverScrubVideoThumb.js';
90
90
  import { isPlayPauseSpace } from '../media/playerSpaceKey.js';
91
91
  import {
92
92
  LIGHT_RANGE,
@@ -582,6 +582,15 @@ function TileShell({
582
582
  }) {
583
583
  const selected = selection?.selected === true;
584
584
  const quiet = selection !== undefined && !selection.armed;
585
+ /*
586
+ * 🔴 Draggable only where a pointer can HOVER. On iOS a press-and-hold on a draggable
587
+ * element lifts it for drag-and-drop, and a press-and-hold on a tile is the video PREVIEW
588
+ * there (owner, 2026-10-10: *"holding down on a thumbnail should make it start previewing the
589
+ * video"* — `HoverScrubVideoThumb`'s `held`). A phone has nowhere to drop a tile anyway: the
590
+ * folder tree is a closed drawer while the grid is on screen.
591
+ */
592
+ const canHover = useCanHover();
593
+ const dragHere = canHover ? drag : undefined;
585
594
  return (
586
595
  <div
587
596
  className={`cbmg-selectable${selected ? ' is-selected' : ''}${stageMark ? ' is-playing' : ''}${quiet ? ' cbmg-selectable--quiet' : ''}`}
@@ -595,11 +604,11 @@ function TileShell({
595
604
  // a drag, some treat the pointer-down as a press and never fire dragstart at all.
596
605
  // Dragging the wrapper also means the drag image is the whole tile, which is what a
597
606
  // person is aiming at.
598
- {...(drag
607
+ {...(dragHere
599
608
  ? {
600
609
  draggable: true,
601
- onDragStart: (event: DragEvent<HTMLDivElement>) => drag.onDragStart(event),
602
- onDragEnd: () => drag.onDragEnd?.(),
610
+ onDragStart: (event: DragEvent<HTMLDivElement>) => dragHere.onDragStart(event),
611
+ onDragEnd: () => dragHere.onDragEnd?.(),
603
612
  }
604
613
  : {})}
605
614
  >
@@ -796,8 +805,8 @@ function RawGalleryTile({
796
805
  video={video}
797
806
  onOpen={onOpen}
798
807
  previewMode='scrub'
799
- // The whole grid previewing at once, on request. The tile suppresses it on
800
- // coarse-pointer devices by itself, so a phone never plays ninety clips under a thumb.
808
+ // The whole grid previewing at once, on request — on a phone too; the tile's gate keeps
809
+ // it to the tiles on screen.
801
810
  autoPlay={autoPreview ?? false}
802
811
  // A stored turn turns the TILE too — otherwise the grid is a wall of sideways films
803
812
  // whose detail view is upright, which reads as the fix not having worked.
@@ -3224,7 +3233,9 @@ export function MediaGallery({
3224
3233
  // guardrails-ignore no-raw-action-button: back-to-grid navigation, styled by
3225
3234
  // the dense gallery's own plain CSS (plugin-media-gallery precedent).
3226
3235
  type='button'
3227
- className={cn(RAW_BUTTON, 'cbgd-order-btn is-icon')}
3236
+ // `cbgd-action-lead`: on a phone it leads the row (`denseGallery.css`), so the way
3237
+ // out is never on a folded line.
3238
+ className={cn(RAW_BUTTON, 'cbgd-order-btn is-icon cbgd-action-lead')}
3228
3239
  aria-label='Back to grid'
3229
3240
  onClick={() => select(null)}
3230
3241
  >
@@ -204,8 +204,8 @@ const THUMB_AUTOPLAY_KEY = 'cbmg:thumb-autoplay';
204
204
  * a toggle, which is a thing you turn on; a default-on preview is a thing you have to
205
205
  * discover in order to turn off.
206
206
  *
207
- * {@link HoverScrubVideoThumb} independently suppresses it on coarse-pointer devices, so a
208
- * phone never has ninety clips playing under the reader's thumb even with this on.
207
+ * On a phone it previews too (2026-10-10 — a pressed "Preview all" that moved nothing was the
208
+ * bug), and {@link HoverScrubVideoThumb}'s on-screen gate keeps it to the tiles in view.
209
209
  */
210
210
  export function readThumbAutoplay(): boolean {
211
211
  try {
@@ -15,7 +15,7 @@
15
15
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
16
16
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
17
17
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
18
- * 920 candidate(s).
18
+ * 922 candidate(s).
19
19
  *
20
20
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
21
21
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -38,11 +38,13 @@
38
38
  @source inline("[&_[data-icon]]:text-accent [&_[data-icon]]:text-destructive [&_[data-icon]]:text-success");
39
39
  @source inline("[&_[data-icon]]:text-warning [&_sup]:leading-none [&_svg]:shrink-0 [&_svg]:size-12");
40
40
  @source inline("[&_svg]:size-4 [&_svg]:size-5 [&_svg]:size-6 [--cbfk-sticky-top:-1rem]");
41
- @source inline("[-webkit-touch-callout:none] a absolute act active active:bg-accent active:scale-100");
42
- @source inline("active:scale-[var(--press-scale)] active:scale-[var(--press-scale-surface)] alert alert-icon");
43
- @source inline("align align-middle an animate animate-in animate-progress-indeterminate animate-pulse");
44
- @source inline("animate-spin aria aria-[invalid=true]:border-field-error aria-[invalid=true]:ring-field-error/30");
45
- @source inline("aria-disabled aria-disabled:active:scale-100 aria-disabled:cursor-not-allowed");
41
+ @source inline("[-webkit-touch-callout:none] [@media(orientation:landscape)_and_(max-height:500px)]:py-1.5");
42
+ @source inline("[@media(orientation:landscape)_and_(max-height:500px)]:text-h3 a absolute act active");
43
+ @source inline("active:bg-accent active:scale-100 active:scale-[var(--press-scale)]");
44
+ @source inline("active:scale-[var(--press-scale-surface)] alert alert-icon align align-middle an animate");
45
+ @source inline("animate-in animate-progress-indeterminate animate-pulse animate-spin aria");
46
+ @source inline("aria-[invalid=true]:border-field-error aria-[invalid=true]:ring-field-error/30 aria-disabled");
47
+ @source inline("aria-disabled:active:scale-100 aria-disabled:cursor-not-allowed");
46
48
  @source inline("aria-disabled:hover:bg-transparent aria-disabled:opacity-30 aria-disabled:opacity-50");
47
49
  @source inline("aria-disabled:pointer-events-none aria-pressed aria-pressed:bg-accent/10 aria-pressed:border");
48
50
  @source inline("aria-pressed:border-accent aria-pressed:text-accent auto auto-cols-[minmax(min-content,1fr)] b");
@@ -5451,6 +5451,14 @@
5451
5451
  width: calc(var(--spacing) * 3.5);
5452
5452
  height: calc(var(--spacing) * 3.5);
5453
5453
  }
5454
+ @media (orientation:landscape) and (max-height:500px) {
5455
+ .\[\@media\(orientation\:landscape\)_and_\(max-height\:500px\)\]\:py-1\.5 {
5456
+ padding-block: calc(var(--spacing) * 1.5);
5457
+ }
5458
+ .\[\@media\(orientation\:landscape\)_and_\(max-height\:500px\)\]\:text-h3 {
5459
+ font-size: clamp(1.125rem, 1rem + 0.5vw, 1.25rem);
5460
+ }
5461
+ }
5454
5462
  }
5455
5463
  @property --tw-translate-x {
5456
5464
  syntax: "*";