cursedbelt 5.47.0 → 5.48.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.
@@ -622,34 +622,72 @@
622
622
  overflow: hidden;
623
623
  background: var(--background);
624
624
  }
625
- /* Fullscreen-within-the-plugin — hide the rail and related grid, let the stage grow to
626
- fill the whole plugin container instead of a fixed 80vh cap. */
625
+ /*
626
+ * ── 🔴 Full window: the picture as big as the WINDOW, the files still below it ──────────
627
+ *
628
+ * Owner, 2026-10-02: *"I do not want the video size limited by anything other than the size
629
+ * of the window. This may mean I have to scroll to see anything under the video at all …
630
+ * and that is ok."* So the detail view covers the whole window (`fixed`, over the app's own
631
+ * toolstrip, sidebar and activity column — none of which this module could otherwise reach),
632
+ * stays the scroll container the related grid virtualises against, and the frame is exactly
633
+ * one window tall and wide. The stage letterboxes inside it as it always does, which is what
634
+ * keeps the aspect ratio and every edge of the picture on screen.
635
+ *
636
+ * `--z-overlay`, not higher: a menu or tooltip opened from here is `--z-popover`, and a
637
+ * dialog portalled to <body> comes later in the document at the same layer, so both still
638
+ * draw over it.
639
+ *
640
+ * Until 2026-10-02 this filled the plugin's container instead, and hid the related grid.
641
+ */
627
642
  .cbgd-watch-full {
628
- overflow: hidden;
643
+ position: fixed;
644
+ inset: 0;
645
+ z-index: var(--z-overlay, 1000);
646
+ height: auto;
629
647
  }
630
648
  .cbgd-watch-full .cbgd-watch-top {
631
- display: flex;
632
- flex-direction: column;
633
- flex: 1;
634
- min-height: 0;
635
- height: 100%;
636
- }
637
- .cbgd-watch-full .cbgd-watch-main {
638
- flex: 1;
639
- min-height: 0;
649
+ display: block;
650
+ padding: 0;
640
651
  }
641
- .cbgd-watch-full .cbgd-watch-player {
642
- flex: 1;
643
- min-height: 0;
652
+ /* Pinning a band that is the whole window would leave nothing to scroll into view. */
653
+ .cbgd-watch-sticky.cbgd-watch-full .cbgd-watch-top {
654
+ position: static;
655
+ max-height: none;
656
+ overflow: visible;
644
657
  }
645
658
  .cbgd-watch-full .cbgd-watch-player-box {
646
- max-width: 100%;
647
- height: 100%;
659
+ width: 100%;
660
+ max-width: none;
661
+ height: 100dvh;
662
+ }
663
+ .cbgd-watch-full .cbgd-watch-titlebar {
664
+ padding-inline: 10px;
648
665
  }
649
- .cbgd-watch-full .cbgd-watch-rail,
650
666
  .cbgd-watch-full .cbgd-related {
667
+ padding-top: 6px;
668
+ }
669
+ .cbgd-watch-full .cbgd-watch-rail {
651
670
  display: none;
652
671
  }
672
+ /* The way out for a stage with no control bar of its own (a picture, a document, the native
673
+ `<video controls>`): pinned to the window's corner, quiet until it is wanted. A Vidstack
674
+ video carries its toggle in its own bar instead — `stageOffersFullStageToggle`. */
675
+ .cbgd-full-exit {
676
+ position: fixed;
677
+ top: 12px;
678
+ right: 12px;
679
+ opacity: 0.6;
680
+ transition: opacity 120ms ease;
681
+ }
682
+ /* Solid, because what is under it is a picture of any colour. */
683
+ .cbgd-full-exit .cbgd-order-btn {
684
+ background: var(--background);
685
+ color: var(--foreground);
686
+ }
687
+ .cbgd-full-exit:hover,
688
+ .cbgd-full-exit:focus-within {
689
+ opacity: 1;
690
+ }
653
691
  /*
654
692
  * An image stage (photo detail) — a letterbox of CONSTANT height.
655
693
  *
@@ -23,7 +23,8 @@
23
23
  /* Vidstack sizes its icons through `.vds-icon`; the pair only needs to match the seek
24
24
  buttons beside them, which are 80% of the play button. */
25
25
  .cbvp-step .vds-icon,
26
- .cbvp-section .vds-icon {
26
+ .cbvp-section .vds-icon,
27
+ .cbvp-fill-window .vds-icon {
27
28
  width: var(--media-button-icon-size, 80%);
28
29
  height: var(--media-button-icon-size, 80%);
29
30
  }
@@ -358,7 +358,7 @@ describe('MediaGallery detail view', () => {
358
358
  expect(getByRole('button', { name: 'Open beach.jpg' })).toBeDefined();
359
359
  // Download + fullscreen affordances.
360
360
  expect(getByRole('link', { name: /download/i }).getAttribute('href')).toBe('/files/img1');
361
- expect(getByRole('button', { name: /fullscreen/i })).toBeDefined();
361
+ expect(getByRole('button', { name: 'Full window' })).toBeDefined();
362
362
  // Back to grid returns to browse.
363
363
  fireEvent.click(getByRole('button', { name: 'Back to grid' }));
364
364
  expect(container.querySelector('.cbgd-root')).not.toBeNull();
@@ -1770,7 +1770,7 @@ describe('🔴 double-clicking the stage', () => {
1770
1770
  fireEvent.doubleClick(frameOf(view.container));
1771
1771
  expect(isFull(view.container)).toBe(true);
1772
1772
  // The titlebar's own control agrees — one state, two ways into it.
1773
- expect(view.getByRole('button', { name: /Shrink/ }).getAttribute('aria-pressed')).toBe('true');
1773
+ expect(view.getByRole('button', { name: 'Full window' }).getAttribute('aria-pressed')).toBe('true');
1774
1774
 
1775
1775
  fireEvent.doubleClick(frameOf(view.container));
1776
1776
  expect(isFull(view.container)).toBe(false);
@@ -1786,7 +1786,7 @@ describe('🔴 double-clicking the stage', () => {
1786
1786
  test('🔴 …but a video DOES leave the shell fullscreen, because the player stood down', () => {
1787
1787
  const view = render(<MediaGallery items={ITEMS} nativeVideoStage />);
1788
1788
  fireEvent.click(view.getByRole('button', { name: 'Open clip.mp4' }));
1789
- fireEvent.click(view.getByRole('button', { name: /Fullscreen/ }));
1789
+ fireEvent.click(view.getByRole('button', { name: 'Full window' }));
1790
1790
  expect(isFull(view.container)).toBe(true);
1791
1791
  fireEvent.doubleClick(frameOf(view.container));
1792
1792
  expect(isFull(view.container)).toBe(false);
@@ -2386,7 +2386,7 @@ describe('MediaGallery — the toolbar is icons, and the words are its names', (
2386
2386
  'Picture',
2387
2387
  'Delete this item',
2388
2388
  'Back to grid',
2389
- 'Fullscreen',
2389
+ 'Full window',
2390
2390
  ]) {
2391
2391
  expect(view.getByRole('button', { name }), `no control named "${name}"`).toBeDefined();
2392
2392
  }
@@ -1359,6 +1359,8 @@ function GalleryStage({
1359
1359
  onStep,
1360
1360
  canStep,
1361
1361
  fullscreenOnDoubleClick,
1362
+ fillWindow,
1363
+ onFillWindowChange,
1362
1364
  }: {
1363
1365
  item: MediaGalleryItem;
1364
1366
  mediaCache: MediaCache;
@@ -1377,6 +1379,9 @@ function GalleryStage({
1377
1379
  canStep?: boolean;
1378
1380
  /** False while the SHELL is fullscreen — see `VideoPlayerProps.fullscreenOnDoubleClick`. */
1379
1381
  fullscreenOnDoubleClick?: boolean;
1382
+ /** The shell's full-window state, toggled from the player's own bar — see `fillWindowSlots`. */
1383
+ fillWindow?: boolean;
1384
+ onFillWindowChange?: (next: boolean) => void;
1380
1385
  }) {
1381
1386
  const category = galleryItemCategory(item);
1382
1387
  const { media, resolving, refresh } = useItemMedia(item, mediaCache);
@@ -1444,6 +1449,7 @@ function GalleryStage({
1444
1449
  {...(repeat ? {} : { onEnded })}
1445
1450
  {...(onStep ? { onStep, canStep: canStep !== false } : {})}
1446
1451
  {...(fullscreenOnDoubleClick === false ? { fullscreenOnDoubleClick: false } : {})}
1452
+ {...(onFillWindowChange ? { fillWindow: fillWindow === true, onFillWindowChange } : {})}
1447
1453
  />
1448
1454
  </React.Suspense>
1449
1455
  );
@@ -2719,6 +2725,10 @@ export function MediaGallery({
2719
2725
  `⤢ Fullscreen` is what a double-click means on it. Owner, 2026-09-11:
2720
2726
  *"Doubleclicking an image to toggle full screen is not working at all."* */
2721
2727
  stageOwnsDoubleClick={category === 'video'}
2728
+ /* A Vidstack video carries its own full-window toggle in its control bar, so the
2729
+ shell's floating way out would be a second one over the same picture. The native
2730
+ `<video controls>` stage has no bar of ours, so it keeps the shell's. */
2731
+ stageOffersFullStageToggle={category === 'video' && !nativeVideoStage}
2722
2732
  title={selected.name}
2723
2733
  meta={
2724
2734
  // The date joins the line rather than replacing what was there — beside the title is
@@ -2767,6 +2777,8 @@ export function MediaGallery({
2767
2777
  screen video should change back … to non-full screen view") has exactly one
2768
2778
  meaning. `DenseGalleryDetail` handles it from the frame instead. */
2769
2779
  fullscreenOnDoubleClick={!fullStage}
2780
+ fillWindow={fullStage}
2781
+ onFillWindowChange={setFullStage}
2770
2782
  repeat={runRepeatsOne(playbackEnd)}
2771
2783
  // 🔴 `advance`, not `step(1)`. The arrows wrap and the RUN does not: `next`
2772
2784
  // stops after the last item, `all` starts over, `shuffle` draws from the pass.