cursedbelt 5.87.0 → 5.89.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/looks.css +4 -4
- package/dist/react/Toast.js +2 -2
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/VirtualCardGrid.d.ts +1 -1
- package/dist/react/VirtualCardGrid.js +1 -1
- package/dist/react/activity-bar/ActivityBar.d.ts +2 -2
- package/dist/react/activity-bar/ActivityBar.js +1 -1
- package/dist/react/activity-bar/activityBar.css +4 -4
- package/dist/react/analytics/AnalyticsPanel.d.ts +1 -1
- package/dist/react/analytics/AnalyticsPanel.js +1 -1
- package/dist/react/canvas-hud/CanvasMinimap.d.ts +1 -1
- package/dist/react/canvas-hud/HudControls.d.ts +2 -2
- package/dist/react/canvas-hud/alignment.d.ts +2 -2
- package/dist/react/canvas-hud/alignment.js +3 -3
- package/dist/react/canvas-hud/alignment.js.map +1 -1
- package/dist/react/canvas-hud/viewport.d.ts +1 -1
- package/dist/react/canvas-hud/viewport.js +1 -1
- package/dist/react/charts/BarChart.d.ts +2 -2
- package/dist/react/charts/HeatmapChart.d.ts +1 -1
- package/dist/react/charts/HeatmapChart.js +1 -1
- package/dist/react/charts/IntervalTimeline.d.ts +2 -2
- package/dist/react/charts/IntervalTimeline.d.ts.map +1 -1
- package/dist/react/charts/categorical.d.ts +11 -11
- package/dist/react/charts/categorical.js +9 -9
- package/dist/react/charts/categorical.js.map +1 -1
- package/dist/react/charts/ink.d.ts +5 -5
- package/dist/react/charts/ink.js +5 -5
- package/dist/react/charts/tokens.d.ts +1 -1
- package/dist/react/charts/tokens.js +1 -1
- package/dist/react/chat/Bubble.js +1 -1
- package/dist/react/chat/Bubble.js.map +1 -1
- package/dist/react/chat/DateMarker.js +1 -1
- package/dist/react/chat/MessageComposer.d.ts +1 -1
- package/dist/react/chat/types.d.ts +1 -1
- package/dist/react/chat/usePresence.d.ts +2 -2
- package/dist/react/chat/usePresence.js +1 -1
- package/dist/react/code-editor/markdownRules.js +1 -1
- package/dist/react/code-editor/markdownRules.js.map +1 -1
- package/dist/react/components/Badge.js +1 -1
- package/dist/react/components/Badge.js.map +1 -1
- package/dist/react/components/Button.js +5 -5
- package/dist/react/components/Button.js.map +1 -1
- package/dist/react/components/ChartTablePanel.js +1 -1
- package/dist/react/components/ChartTablePanel.js.map +1 -1
- package/dist/react/components/Checkbox.js +2 -2
- package/dist/react/components/Checkbox.js.map +1 -1
- package/dist/react/components/ChoiceGroup.d.ts +1 -1
- package/dist/react/components/ChoiceGroup.js +1 -1
- package/dist/react/components/CodeChip.d.ts +1 -1
- package/dist/react/components/CodeChip.js +1 -1
- package/dist/react/components/ColorSwatches.d.ts +10 -10
- package/dist/react/components/ColorSwatches.d.ts.map +1 -1
- package/dist/react/components/ColorSwatches.js +10 -10
- package/dist/react/components/ColorSwatches.js.map +1 -1
- package/dist/react/components/DataTable/DataTable.d.ts +2 -2
- package/dist/react/components/DataTable/DataTable.js +8 -8
- package/dist/react/components/DataTable/DataTable.js.map +1 -1
- package/dist/react/components/DataTable/SearchBox.js +2 -2
- package/dist/react/components/DataTable/SortHeader.js +1 -1
- package/dist/react/components/DataTable/SortHeader.js.map +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.js +1 -1
- package/dist/react/components/DropdownMenu.js +1 -1
- package/dist/react/components/Field.d.ts +1 -1
- package/dist/react/components/FormPreviewLayout.d.ts +1 -1
- package/dist/react/components/FormPreviewLayout.js +1 -1
- package/dist/react/components/Heat.d.ts +6 -6
- package/dist/react/components/Heat.js +1 -1
- package/dist/react/components/IconPicker.d.ts +8 -8
- package/dist/react/components/IconPicker.d.ts.map +1 -1
- package/dist/react/components/IconPicker.js +6 -6
- package/dist/react/components/IconPicker.js.map +1 -1
- package/dist/react/components/InfoHint.d.ts +1 -1
- package/dist/react/components/InfoHint.js +1 -1
- package/dist/react/components/InheritBadge.js +2 -2
- package/dist/react/components/ListToolbar.d.ts +1 -1
- package/dist/react/components/ListToolbar.js +1 -1
- package/dist/react/components/Menu.d.ts +1 -1
- package/dist/react/components/Menu.d.ts.map +1 -1
- package/dist/react/components/Modal.js +5 -5
- package/dist/react/components/Modal.js.map +1 -1
- package/dist/react/components/Page.d.ts +1 -1
- package/dist/react/components/Page.js +1 -1
- package/dist/react/components/PaneSplit.d.ts +1 -1
- package/dist/react/components/PathText.d.ts +1 -1
- package/dist/react/components/PathText.js +1 -1
- package/dist/react/components/Popover.d.ts +1 -1
- package/dist/react/components/PropertyList.d.ts +1 -1
- package/dist/react/components/PropertyList.js +1 -1
- package/dist/react/components/PropertyPicker.d.ts +2 -2
- package/dist/react/components/PropertyPicker.js +2 -2
- package/dist/react/components/RatingField.js +1 -1
- package/dist/react/components/RatingField.js.map +1 -1
- package/dist/react/components/SaveIndicator.d.ts +2 -2
- package/dist/react/components/SaveIndicator.js +2 -2
- package/dist/react/components/SearchInput.d.ts +1 -1
- package/dist/react/components/SearchInput.d.ts.map +1 -1
- package/dist/react/components/SecretInput.d.ts +1 -1
- package/dist/react/components/Section.d.ts +1 -1
- package/dist/react/components/SectionNav.d.ts +3 -3
- package/dist/react/components/SectionNav.d.ts.map +1 -1
- package/dist/react/components/SectionNav.js +2 -2
- package/dist/react/components/Slider.js +1 -1
- package/dist/react/components/StarRating.js +1 -1
- package/dist/react/components/SwatchPicker.d.ts +6 -6
- package/dist/react/components/SwatchPicker.d.ts.map +1 -1
- package/dist/react/components/SwatchPicker.js +3 -3
- package/dist/react/components/SwatchPicker.js.map +1 -1
- package/dist/react/components/Tooltip.js +1 -1
- package/dist/react/components/TreeView.js +1 -1
- package/dist/react/components/TreeView.js.map +1 -1
- package/dist/react/components/TruncatedText.d.ts +1 -1
- package/dist/react/components/TruncatedText.js +1 -1
- package/dist/react/components/UnstyledButton.d.ts +2 -2
- package/dist/react/components/UnstyledButton.js +2 -2
- package/dist/react/components/auth/AuthCard.js +1 -1
- package/dist/react/components/auth/AuthCard.js.map +1 -1
- package/dist/react/components/buildFreshness.css +2 -2
- package/dist/react/components/calendar/MonthGrid.js +2 -2
- package/dist/react/components/calendar/MonthGrid.js.map +1 -1
- package/dist/react/components/calendar/colorUtils.js +2 -2
- package/dist/react/components/dialog.js +1 -1
- package/dist/react/components/fieldErrorText.d.ts +1 -1
- package/dist/react/components/fieldErrorText.js +1 -1
- package/dist/react/components/paneSplit.css +1 -1
- package/dist/react/components/sharing/ShareEditor.d.ts +5 -1
- package/dist/react/components/sharing/ShareEditor.d.ts.map +1 -1
- package/dist/react/components/sharing/ShareEditor.js +2 -2
- package/dist/react/components/sharing/ShareEditor.js.map +1 -1
- package/dist/react/deep-link/links.d.ts +1 -1
- package/dist/react/deep-link/resume.js +1 -1
- package/dist/react/diff-viewer/DiffViewer.css +1 -1
- package/dist/react/file-tree/FileTree.d.ts +9 -9
- package/dist/react/file-tree/FileTree.js +13 -13
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/FolderViewMenus.d.ts +3 -3
- package/dist/react/file-tree/FolderViewMenus.d.ts.map +1 -1
- package/dist/react/file-tree/FolderViewMenus.js +1 -1
- package/dist/react/file-tree/FolderViewMenus.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +14 -14
- package/dist/react/file-tree/fileTreeSelection.d.ts +3 -3
- package/dist/react/file-tree/fileTreeSelection.js +4 -4
- package/dist/react/file-tree/folderView.d.ts +3 -3
- package/dist/react/file-tree/folderView.d.ts.map +1 -1
- package/dist/react/file-tree/folderView.js +3 -3
- package/dist/react/file-tree/folderView.js.map +1 -1
- package/dist/react/filter-rail/FilterRail.d.ts +3 -3
- package/dist/react/filter-rail/FilterRail.d.ts.map +1 -1
- package/dist/react/filter-rail/FilterRail.js +1 -1
- package/dist/react/filter-rail/RailSection.d.ts +1 -1
- package/dist/react/filter-rail/railResize.d.ts +1 -1
- package/dist/react/filter-rail/railResize.js +1 -1
- package/dist/react/folder-tree/FolderBreadcrumb.d.ts +1 -1
- package/dist/react/folder-tree/NewFolderControl.js +1 -1
- package/dist/react/hooks/keyboardDnd.d.ts +1 -1
- package/dist/react/hooks/keyboardDnd.d.ts.map +1 -1
- package/dist/react/hooks/keyboardDnd.js +2 -2
- package/dist/react/hooks/keyboardDnd.js.map +1 -1
- package/dist/react/hooks/pointerDnd.d.ts +2 -2
- package/dist/react/hooks/pointerDnd.d.ts.map +1 -1
- package/dist/react/hooks/pointerDnd.js +1 -1
- package/dist/react/hooks/usePointerDrag.d.ts +2 -2
- package/dist/react/icons.d.ts +16 -9
- package/dist/react/icons.d.ts.map +1 -1
- package/dist/react/icons.js +17 -10
- package/dist/react/icons.js.map +1 -1
- package/dist/react/layoutEngine/LayoutRenderer.js +1 -1
- package/dist/react/layoutEngine/LayoutRenderer.js.map +1 -1
- package/dist/react/lib/cn.d.ts +3 -3
- package/dist/react/lib/cn.js +4 -4
- package/dist/react/lib/field.d.ts +1 -1
- package/dist/react/lib/floatingContainer.d.ts +1 -1
- package/dist/react/lib/floatingContainer.js +1 -1
- package/dist/react/lib/floatingStyles.d.ts +1 -1
- package/dist/react/lib/floatingStyles.js +2 -2
- package/dist/react/lib/floatingStyles.js.map +1 -1
- package/dist/react/lib/hoverIntent.d.ts +6 -6
- package/dist/react/lib/hoverIntent.js +6 -6
- package/dist/react/lib/infoHint.d.ts +1 -1
- package/dist/react/lib/infoHint.js +1 -1
- package/dist/react/lib/overlayMotion.d.ts +1 -1
- package/dist/react/lib/overlayMotion.d.ts.map +1 -1
- package/dist/react/lib/overlayMotion.js +1 -1
- package/dist/react/lib/overlayMotion.js.map +1 -1
- package/dist/react/lib/press.d.ts +1 -1
- package/dist/react/lib/press.js +1 -1
- package/dist/react/lib/selectField.d.ts +2 -2
- package/dist/react/lib/selectField.d.ts.map +1 -1
- package/dist/react/lib/selectField.js +2 -2
- package/dist/react/lib/selectField.js.map +1 -1
- package/dist/react/look/AppearanceMenu.d.ts +1 -1
- package/dist/react/look/AppearanceMenu.js +1 -1
- package/dist/react/look/LookPicker.d.ts +3 -3
- package/dist/react/look/LookPicker.js +3 -3
- package/dist/react/look/lookStore.d.ts +1 -1
- package/dist/react/look/lookStore.js +4 -4
- package/dist/react/look/lookStore.js.map +1 -1
- package/dist/react/master-lock/MasterLockDialog.js +2 -2
- package/dist/react/master-lock/MasterLockDialog.js.map +1 -1
- package/dist/react/master-lock/MasterLockProvider.d.ts +1 -1
- package/dist/react/media/DenseGalleryShell.d.ts +3 -3
- package/dist/react/media/FocusViewer.js +3 -3
- package/dist/react/media/FocusViewer.js.map +1 -1
- package/dist/react/media/FrameGrabber.js +3 -3
- package/dist/react/media/FrameGrabber.js.map +1 -1
- package/dist/react/media/HoverScrubVideoThumb.d.ts +1 -1
- package/dist/react/media/HoverScrubVideoThumb.js +1 -1
- package/dist/react/media/HoverScrubVideoThumb.js.map +1 -1
- package/dist/react/media/MediaCell.d.ts +1 -1
- package/dist/react/media/MediaCell.js +1 -1
- package/dist/react/media/MediaResize.d.ts +1 -1
- package/dist/react/media/MediaThumb.d.ts +1 -1
- package/dist/react/media/PictureControls.d.ts +1 -1
- package/dist/react/media/PictureControls.js +2 -2
- package/dist/react/media/PictureControls.js.map +1 -1
- package/dist/react/media/PictureEditor.js +2 -2
- package/dist/react/media/PictureEditor.js.map +1 -1
- package/dist/react/media/PlayerStrip.d.ts +4 -4
- package/dist/react/media/PlayerStrip.d.ts.map +1 -1
- package/dist/react/media/PlayerStrip.js +2 -2
- package/dist/react/media/VideoPlayer.d.ts +3 -3
- package/dist/react/media/VideoPlayer.js +5 -5
- package/dist/react/media/VideoTurner.js +1 -1
- package/dist/react/media/VideoTurner.js.map +1 -1
- package/dist/react/media/VirtualizedRows.d.ts +1 -1
- package/dist/react/media/denseGallery.css +7 -7
- package/dist/react/media/focusViewer.css +2 -2
- package/dist/react/media/gifEncode.d.ts +4 -4
- package/dist/react/media/gifEncode.d.ts.map +1 -1
- package/dist/react/media/gifEncode.js +8 -8
- package/dist/react/media/gifEncode.js.map +1 -1
- package/dist/react/media/gifRecord.js +1 -1
- package/dist/react/media/gifRecord.js.map +1 -1
- package/dist/react/media/hlsSource.js +2 -2
- package/dist/react/media/hlsSource.js.map +1 -1
- package/dist/react/media/mediaResize.css +3 -3
- package/dist/react/media/pictureEdit.d.ts +1 -1
- package/dist/react/media/pictureEdit.js +4 -4
- package/dist/react/media/pictureEdit.js.map +1 -1
- package/dist/react/media/pictureEditor.css +6 -6
- package/dist/react/media/playerSpaceKey.js +3 -3
- package/dist/react/media/playerStrip.css +11 -11
- package/dist/react/media/posterFraming.d.ts +2 -2
- package/dist/react/media/posterFraming.d.ts.map +1 -1
- package/dist/react/media/posterFraming.js +4 -4
- package/dist/react/media/posterFraming.js.map +1 -1
- package/dist/react/media/sourceFault.d.ts +2 -2
- package/dist/react/media/sourceFault.js +1 -1
- package/dist/react/media/stageResize.d.ts +1 -1
- package/dist/react/media/stageResize.js +1 -1
- package/dist/react/media/surfaceGesture.d.ts +1 -1
- package/dist/react/media/surfaceGesture.js +1 -1
- package/dist/react/media/types.d.ts +1 -1
- package/dist/react/media/videoChapterEditor.css +1 -1
- package/dist/react/media/videoChapters.d.ts +1 -1
- package/dist/react/media/videoChapters.js +1 -1
- package/dist/react/media/videoCutEditor.css +2 -2
- package/dist/react/media/videoEnhance.d.ts +2 -2
- package/dist/react/media/videoEnhance.js +2 -2
- package/dist/react/media/videoPoster.d.ts +3 -3
- package/dist/react/media/videoPoster.d.ts.map +1 -1
- package/dist/react/media/videoPoster.js +4 -4
- package/dist/react/media/videoPoster.js.map +1 -1
- package/dist/react/media/videoRegion.d.ts +8 -8
- package/dist/react/media/videoRegion.js +8 -8
- package/dist/react/media-gallery/MediaGallery.d.ts +2 -2
- package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
- package/dist/react/media-gallery/MediaGallery.js +17 -17
- package/dist/react/media-gallery/MediaGallery.js.map +1 -1
- package/dist/react/media-gallery/MediaMetaEditor.js +1 -1
- package/dist/react/media-gallery/galleryDeepLink.d.ts +1 -1
- package/dist/react/media-gallery/galleryDeepLink.js +2 -2
- package/dist/react/media-gallery/galleryItemMedia.d.ts +1 -1
- package/dist/react/media-gallery/galleryItemMedia.js +1 -1
- package/dist/react/media-gallery/galleryReturn.d.ts +1 -1
- package/dist/react/media-gallery/galleryReturn.js +2 -2
- package/dist/react/media-gallery/mediaGallery.css +10 -10
- package/dist/react/media-gallery/mediaGalleryModel.d.ts +1 -1
- package/dist/react/media-gallery/mediaGalleryModel.js +1 -1
- package/dist/react/media-gallery/playbackPreferences.d.ts +3 -3
- package/dist/react/media-gallery/playbackPreferences.js +2 -2
- package/dist/react/media-gallery/playbackRun.d.ts +1 -1
- package/dist/react/media-gallery/playbackRun.js +1 -1
- package/dist/react/media-gallery/preferenceSync.js +1 -1
- package/dist/react/namespace/NamespaceTargetCapsule.d.ts +1 -1
- package/dist/react/nav/BreadcrumbTrail.d.ts +1 -1
- package/dist/react/nav/BreadcrumbTrail.js +1 -1
- package/dist/react/nav/Breadcrumbs.d.ts +1 -1
- package/dist/react/nav/Breadcrumbs.js +1 -1
- package/dist/react/nav/HeaderSearch.d.ts +3 -3
- package/dist/react/nav/HeaderSearch.d.ts.map +1 -1
- package/dist/react/nav/HeaderSearch.js +2 -2
- package/dist/react/nav/PlaceBar.d.ts +1 -1
- package/dist/react/nav/PlaceBar.js +2 -2
- package/dist/react/nav/SearchResults.d.ts +2 -2
- package/dist/react/nav/SearchResults.d.ts.map +1 -1
- package/dist/react/nav/SearchResults.js +1 -1
- package/dist/react/nav/TopBar.js +1 -1
- package/dist/react/nav/breadcrumbStore.js +1 -1
- package/dist/react/nav/navShared.d.ts +1 -1
- package/dist/react/palette/CommandPalette.js +1 -1
- package/dist/react/palette/CommandPalette.js.map +1 -1
- package/dist/react/pdf-viewer/PdfPage.js +1 -1
- package/dist/react/pdf-viewer/PdfPage.js.map +1 -1
- package/dist/react/spreadsheet/SpreadsheetGrid.d.ts +1 -1
- package/dist/react/spreadsheet/useGlideTheme.js +2 -2
- package/dist/react/workbook-viewer/WorkbookViewer.js +1 -1
- package/dist/react/workbook-viewer/WorkbookViewer.js.map +1 -1
- package/dist/styles-areas/notifications.css +1 -1
- package/dist/styles-static.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing/designAudit.d.ts +2 -2
- package/dist/testing/designAudit.js +3 -3
- package/dist/testing/designAudit.js.map +1 -1
- package/dist/testing/phoneRules.d.ts +4 -4
- package/dist/testing/phoneRules.js +4 -4
- package/dist/testing/phoneRules.js.map +1 -1
- package/dist/theme.css +10 -10
- package/package.json +1 -1
- package/scripts/attributesForwarded.ts +1 -1
- package/scripts/checkAreaStyles.ts +2 -2
- package/scripts/cssRules.ts +2 -2
- package/scripts/demoCoverage.ts +1 -1
- package/scripts/demoServer.ts +1 -1
- package/scripts/generateStaticStyles.ts +1 -1
- package/scripts/guardrailsEnforce.spec.ts +14 -14
- package/scripts/guardrailsEnforce.ts +25 -25
- package/scripts/paths.ts +1 -1
- package/scripts/styleAreas.ts +2 -2
- package/scripts/testTmpRoot.ts +1 -1
- package/src/buildIsStaged.spec.ts +1 -1
- package/src/iframesMakeNoRealRequest.spec.ts +1 -1
- package/src/looks.css +4 -4
- package/src/looks.spec.ts +3 -3
- package/src/namedSubpathsResolve.spec.ts +2 -2
- package/src/publishShape.spec.ts +1 -1
- package/src/react/Toast.spec.tsx +1 -1
- package/src/react/Toast.tsx +2 -2
- package/src/react/VirtualCardGrid.spec.tsx +1 -1
- package/src/react/VirtualCardGrid.tsx +3 -3
- package/src/react/activity-bar/ActivityBar.spec.tsx +2 -2
- package/src/react/activity-bar/ActivityBar.tsx +4 -4
- package/src/react/activity-bar/activityBar.css +4 -4
- package/src/react/analytics/AnalyticsPanel.tsx +1 -1
- package/src/react/canvas-hud/CanvasMinimap.tsx +1 -1
- package/src/react/canvas-hud/HudControls.spec.tsx +1 -1
- package/src/react/canvas-hud/HudControls.tsx +2 -2
- package/src/react/canvas-hud/alignment.spec.tsx +4 -4
- package/src/react/canvas-hud/alignment.ts +3 -3
- package/src/react/canvas-hud/viewport.spec.ts +1 -1
- package/src/react/canvas-hud/viewport.ts +1 -1
- package/src/react/charts/BarChart.tsx +2 -2
- package/src/react/charts/HeatmapChart.spec.ts +1 -1
- package/src/react/charts/HeatmapChart.tsx +1 -1
- package/src/react/charts/IntervalTimeline.color.spec.tsx +6 -6
- package/src/react/charts/IntervalTimeline.tsx +2 -2
- package/src/react/charts/Treemap.tsx +1 -1
- package/src/react/charts/accentCollision.spec.ts +1 -1
- package/src/react/charts/categorical.spec.ts +3 -3
- package/src/react/charts/categorical.ts +12 -12
- package/src/react/charts/ink.spec.ts +1 -1
- package/src/react/charts/ink.ts +5 -5
- package/src/react/charts/tokens.ts +1 -1
- package/src/react/chat/Bubble.tsx +1 -1
- package/src/react/chat/DateMarker.tsx +2 -2
- package/src/react/chat/MessageComposer.tsx +1 -1
- package/src/react/chat/PresenceRoster.tsx +2 -2
- package/src/react/chat/types.ts +1 -1
- package/src/react/chat/usePresence.ts +2 -2
- package/src/react/code-editor/markdownRules.ts +1 -1
- package/src/react/components/AvatarStack.spec.tsx +2 -2
- package/src/react/components/AvatarStack.tsx +2 -2
- package/src/react/components/Badge.capsule.spec.ts +6 -6
- package/src/react/components/Badge.tsx +1 -1
- package/src/react/components/Button.spec.tsx +1 -1
- package/src/react/components/Button.tsx +5 -5
- package/src/react/components/ChartTablePanel.tsx +1 -1
- package/src/react/components/Checkbox.spec.tsx +2 -2
- package/src/react/components/Checkbox.tsx +2 -2
- package/src/react/components/ChoiceGroup.tsx +1 -1
- package/src/react/components/CodeChip.spec.tsx +1 -1
- package/src/react/components/CodeChip.tsx +1 -1
- package/src/react/components/ColorSwatches.spec.tsx +9 -9
- package/src/react/components/ColorSwatches.tsx +16 -16
- package/src/react/components/CommentThread.tsx +1 -1
- package/src/react/components/ConnectionHealer.tsx +1 -1
- package/src/react/components/DataTable/DataTable.spec.tsx +2 -2
- package/src/react/components/DataTable/DataTable.tsx +10 -10
- package/src/react/components/DataTable/SearchBox.tsx +2 -2
- package/src/react/components/DataTable/SortHeader.tsx +1 -1
- package/src/react/components/DataTable/TableFilterBuilder.tsx +1 -1
- package/src/react/components/DataTable/selection.spec.tsx +1 -1
- package/src/react/components/DataTable/virtualize.spec.tsx +1 -1
- package/src/react/components/DropdownMenu.tsx +1 -1
- package/src/react/components/Field.tsx +1 -1
- package/src/react/components/FormPreviewLayout.tsx +1 -1
- package/src/react/components/Heat.spec.tsx +1 -1
- package/src/react/components/Heat.tsx +6 -6
- package/src/react/components/IconPicker.spec.tsx +1 -1
- package/src/react/components/IconPicker.tsx +9 -9
- package/src/react/components/InfoHint.spec.tsx +2 -2
- package/src/react/components/InfoHint.tsx +1 -1
- package/src/react/components/InheritBadge.tsx +2 -2
- package/src/react/components/IsolatedFrame.tsx +1 -1
- package/src/react/components/ListToolbar.tsx +1 -1
- package/src/react/components/Menu.tsx +1 -1
- package/src/react/components/Modal.tsx +5 -5
- package/src/react/components/Page.spec.tsx +1 -1
- package/src/react/components/Page.tsx +2 -2
- package/src/react/components/PaneSplit.tsx +1 -1
- package/src/react/components/PathText.tsx +1 -1
- package/src/react/components/PointerDrag.spec.tsx +1 -1
- package/src/react/components/Popover.spec.tsx +2 -2
- package/src/react/components/Popover.tsx +1 -1
- package/src/react/components/PropertyList.tsx +1 -1
- package/src/react/components/PropertyPicker.tsx +2 -2
- package/src/react/components/RatingField.tsx +1 -1
- package/src/react/components/SaveIndicator.tsx +2 -2
- package/src/react/components/SearchInput.tsx +1 -1
- package/src/react/components/SecretInput.tsx +1 -1
- package/src/react/components/Section.tsx +1 -1
- package/src/react/components/SectionNav.spec.tsx +3 -3
- package/src/react/components/SectionNav.tsx +3 -3
- package/src/react/components/SelectField.spec.tsx +1 -1
- package/src/react/components/Slider.tsx +1 -1
- package/src/react/components/StarRating.spec.tsx +4 -4
- package/src/react/components/StarRating.tsx +3 -3
- package/src/react/components/SwatchPicker.spec.tsx +4 -4
- package/src/react/components/SwatchPicker.tsx +9 -9
- package/src/react/components/Tooltip.spec.tsx +3 -3
- package/src/react/components/Tooltip.tsx +1 -1
- package/src/react/components/TreeView.tsx +1 -1
- package/src/react/components/TruncatedText.tsx +1 -1
- package/src/react/components/UnstyledButton.tsx +2 -2
- package/src/react/components/auth/AuthCard.tsx +1 -1
- package/src/react/components/buildFreshness.css +2 -2
- package/src/react/components/calendar/EventChip.spec.tsx +2 -2
- package/src/react/components/calendar/MonthGrid.tsx +2 -2
- package/src/react/components/calendar/colorUtils.ts +2 -2
- package/src/react/components/dialog.tsx +1 -1
- package/src/react/components/fieldErrorText.tsx +1 -1
- package/src/react/components/paneSplit.css +1 -1
- package/src/react/components/sharing/ShareEditor.spec.tsx +15 -0
- package/src/react/components/sharing/ShareEditor.tsx +14 -3
- package/src/react/deep-link/links.spec.ts +1 -1
- package/src/react/deep-link/links.ts +1 -1
- package/src/react/deep-link/resume.spec.tsx +1 -1
- package/src/react/deep-link/resume.ts +1 -1
- package/src/react/diff-viewer/DiffViewer.css +1 -1
- package/src/react/file-tree/FileTree.spec.tsx +13 -13
- package/src/react/file-tree/FileTree.tsx +22 -22
- package/src/react/file-tree/FolderViewMenus.tsx +3 -3
- package/src/react/file-tree/FolderViewPanels.tsx +1 -1
- package/src/react/file-tree/fileTree.css +14 -14
- package/src/react/file-tree/fileTreeGeometry.spec.ts +5 -5
- package/src/react/file-tree/fileTreeSelection.ts +4 -4
- package/src/react/file-tree/folderView.spec.ts +1 -1
- package/src/react/file-tree/folderView.ts +3 -3
- package/src/react/filter-rail/FilterRail.spec.tsx +1 -1
- package/src/react/filter-rail/FilterRail.tsx +4 -4
- package/src/react/filter-rail/RailSection.spec.tsx +2 -2
- package/src/react/filter-rail/RailSection.tsx +1 -1
- package/src/react/filter-rail/railResize.tsx +1 -1
- package/src/react/folder-tree/FolderBreadcrumb.tsx +1 -1
- package/src/react/folder-tree/NewFolderControl.tsx +1 -1
- package/src/react/hooks/keyboardDnd.spec.tsx +1 -1
- package/src/react/hooks/keyboardDnd.tsx +2 -2
- package/src/react/hooks/pointerDnd.spec.ts +3 -3
- package/src/react/hooks/pointerDnd.ts +2 -2
- package/src/react/hooks/useOutsidePress.spec.tsx +1 -1
- package/src/react/hooks/usePointerDrag.ts +2 -2
- package/src/react/icons.tsx +18 -11
- package/src/react/keep-awake/keepAwakeEngine.spec.ts +1 -1
- package/src/react/layoutEngine/LayoutConfigDialog.tsx +1 -1
- package/src/react/layoutEngine/LayoutRenderer.tsx +1 -1
- package/src/react/layoutEngine/gridClasses.spec.ts +3 -3
- package/src/react/lib/cn.spec.ts +1 -1
- package/src/react/lib/cn.ts +4 -4
- package/src/react/lib/directUpload.spec.ts +1 -1
- package/src/react/lib/field.ts +1 -1
- package/src/react/lib/floatingContainer.ts +1 -1
- package/src/react/lib/floatingStyles.spec.ts +1 -1
- package/src/react/lib/floatingStyles.ts +2 -2
- package/src/react/lib/hoverIntent.ts +6 -6
- package/src/react/lib/infoHint.ts +1 -1
- package/src/react/lib/overlayMotion.ts +1 -1
- package/src/react/lib/press.ts +1 -1
- package/src/react/lib/selectField.ts +2 -2
- package/src/react/lib/zodResolver.ts +1 -1
- package/src/react/look/AppearanceMenu.tsx +1 -1
- package/src/react/look/LookPicker.tsx +3 -3
- package/src/react/look/lookStore.ts +4 -4
- package/src/react/markdown/MarkdownDoc.spec.tsx +1 -1
- package/src/react/master-detail/MasterDetail.tsx +1 -1
- package/src/react/master-lock/MasterLockDialog.tsx +2 -2
- package/src/react/master-lock/MasterLockProvider.spec.tsx +1 -1
- package/src/react/master-lock/MasterLockProvider.tsx +1 -1
- package/src/react/media/DenseGalleryShell.tsx +3 -3
- package/src/react/media/FocusViewer.tsx +3 -3
- package/src/react/media/FrameGrabber.spec.tsx +2 -2
- package/src/react/media/FrameGrabber.tsx +3 -3
- package/src/react/media/HoverScrubVideoThumb.tsx +3 -3
- package/src/react/media/MediaCell.tsx +1 -1
- package/src/react/media/MediaResize.spec.tsx +4 -4
- package/src/react/media/MediaResize.tsx +1 -1
- package/src/react/media/MediaThumb.tsx +1 -1
- package/src/react/media/PictureControls.tsx +2 -2
- package/src/react/media/PictureEditor.tsx +2 -2
- package/src/react/media/PlayerStrip.tsx +9 -9
- package/src/react/media/VideoChapterEditor.tsx +1 -1
- package/src/react/media/VideoCutEditor.spec.tsx +1 -1
- package/src/react/media/VideoCutEditor.tsx +1 -1
- package/src/react/media/VideoPlayer.spec.tsx +7 -7
- package/src/react/media/VideoPlayer.tsx +6 -6
- package/src/react/media/VideoTurner.tsx +1 -1
- package/src/react/media/VirtualizedRows.tsx +1 -1
- package/src/react/media/denseGallery.css +7 -7
- package/src/react/media/focusViewer.css +2 -2
- package/src/react/media/focusViewer.css.spec.ts +1 -1
- package/src/react/media/gifEncode.spec.ts +2 -2
- package/src/react/media/gifEncode.ts +9 -9
- package/src/react/media/gifRecord.ts +1 -1
- package/src/react/media/hlsSource.recovery.spec.ts +2 -2
- package/src/react/media/hlsSource.spec.ts +1 -1
- package/src/react/media/hlsSource.ts +2 -2
- package/src/react/media/mediaResize.css +3 -3
- package/src/react/media/pictureEdit.spec.ts +5 -5
- package/src/react/media/pictureEdit.ts +4 -4
- package/src/react/media/pictureEditor.css +6 -6
- package/src/react/media/playerSpaceKey.ts +3 -3
- package/src/react/media/playerStrip.css +11 -11
- package/src/react/media/playerStrip.spec.tsx +17 -13
- package/src/react/media/posterFraming.spec.ts +4 -4
- package/src/react/media/posterFraming.ts +5 -5
- package/src/react/media/sourceFault.tsx +2 -2
- package/src/react/media/stageResize.ts +1 -1
- package/src/react/media/surfaceGesture.ts +1 -1
- package/src/react/media/types.ts +1 -1
- package/src/react/media/videoChapterEditor.css +1 -1
- package/src/react/media/videoChapters.spec.ts +1 -1
- package/src/react/media/videoChapters.ts +1 -1
- package/src/react/media/videoCutEditor.css +2 -2
- package/src/react/media/videoEnhance.spec.ts +1 -1
- package/src/react/media/videoEnhance.ts +2 -2
- package/src/react/media/videoPoster.spec.ts +3 -3
- package/src/react/media/videoPoster.ts +5 -5
- package/src/react/media/videoRegion.spec.ts +7 -7
- package/src/react/media/videoRegion.ts +8 -8
- package/src/react/media-gallery/GalleryTable.tsx +1 -1
- package/src/react/media-gallery/MediaGallery.spec.tsx +11 -11
- package/src/react/media-gallery/MediaGallery.tsx +23 -23
- package/src/react/media-gallery/MediaMetaEditor.tsx +1 -1
- package/src/react/media-gallery/galleryDeepLink.ts +2 -2
- package/src/react/media-gallery/galleryItemMedia.spec.tsx +1 -1
- package/src/react/media-gallery/galleryItemMedia.tsx +1 -1
- package/src/react/media-gallery/galleryReturn.ts +2 -2
- package/src/react/media-gallery/mediaGallery.css +10 -10
- package/src/react/media-gallery/mediaGalleryModel.ts +1 -1
- package/src/react/media-gallery/playbackPreferences.ts +3 -3
- package/src/react/media-gallery/playbackRun.spec.ts +1 -1
- package/src/react/media-gallery/playbackRun.ts +1 -1
- package/src/react/media-gallery/preferenceSync.ts +1 -1
- package/src/react/namespace/NamespaceTargetCapsule.tsx +1 -1
- package/src/react/nav/BreadcrumbTrail.tsx +1 -1
- package/src/react/nav/Breadcrumbs.tsx +1 -1
- package/src/react/nav/HeaderSearch.tsx +3 -3
- package/src/react/nav/PlaceBar.spec.tsx +4 -4
- package/src/react/nav/PlaceBar.tsx +4 -4
- package/src/react/nav/SearchResults.tsx +2 -2
- package/src/react/nav/TopBar.spec.tsx +1 -1
- package/src/react/nav/TopBar.tsx +1 -1
- package/src/react/nav/breadcrumbStore.ts +1 -1
- package/src/react/nav/breadcrumbSystem.spec.tsx +1 -1
- package/src/react/nav/navShared.tsx +1 -1
- package/src/react/palette/CommandPalette.spec.tsx +1 -1
- package/src/react/palette/CommandPalette.tsx +1 -1
- package/src/react/pdf-viewer/PdfPage.tsx +1 -1
- package/src/react/spreadsheet/SpreadsheetGrid.tsx +1 -1
- package/src/react/spreadsheet/useGlideTheme.ts +2 -2
- package/src/react/workbook-viewer/WorkbookViewer.tsx +1 -1
- package/src/shippedFilesAreTracked.spec.ts +2 -2
- package/src/styles-areas/notifications.css +1 -1
- package/src/styles-static.css +1 -1
- package/src/styles.css +1 -1
- package/src/stylesAreas.spec.ts +1 -1
- package/src/stylesStaticMatches.spec.ts +2 -2
- package/src/stylesUtilitiesMatches.spec.ts +4 -4
- package/src/testing/designAudit.ts +4 -4
- package/src/testing/phoneRules.spec.ts +1 -1
- package/src/testing/phoneRules.ts +7 -7
- package/src/theme.css +10 -10
- package/src/themeColorsResolve.spec.ts +5 -5
- package/src/themeDarkScopeAliases.spec.ts +1 -1
- package/src/verifyGraph.spec.ts +1 -1
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
swapsAxes,
|
|
19
19
|
} from './pictureEdit.js';
|
|
20
20
|
|
|
21
|
-
/** A
|
|
21
|
+
/** A gray field with one brighter pixel in the middle — the smallest thing with an edge. */
|
|
22
22
|
function spot(width: number, height: number, level = 128, peak = 200): Pixels {
|
|
23
23
|
const data = new Uint8ClampedArray(width * height * 4);
|
|
24
24
|
for (let i = 0; i < width * height; i += 1) {
|
|
@@ -156,7 +156,7 @@ describe('clampCrop', () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
describe('cropFromDrag', () => {
|
|
159
|
-
it('
|
|
159
|
+
it('normalizes a drag made right-to-left / bottom-to-top', () => {
|
|
160
160
|
expect(cropFromDrag({ x: 0.8, y: 0.9 }, { x: 0.2, y: 0.3 })).toEqual({
|
|
161
161
|
x: 0.2,
|
|
162
162
|
y: 0.3,
|
|
@@ -187,14 +187,14 @@ describe('applyAspect', () => {
|
|
|
187
187
|
expect(out.height).toBeLessThanOrEqual(rect.height + 1e-9);
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
it('keeps the framing
|
|
190
|
+
it('keeps the framing centered where it was', () => {
|
|
191
191
|
const rect = { x: 0.2, y: 0.2, width: 0.6, height: 0.6 };
|
|
192
192
|
const out = applyAspect(rect, 16 / 9, 1);
|
|
193
193
|
expect(out.x + out.width / 2).toBeCloseTo(rect.x + rect.width / 2, 5);
|
|
194
194
|
expect(out.y + out.height / 2).toBeCloseTo(rect.y + rect.height / 2, 5);
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
-
it('stays inside the picture even when
|
|
197
|
+
it('stays inside the picture even when centering would push it out', () => {
|
|
198
198
|
const out = applyAspect({ x: 0, y: 0.7, width: 1, height: 0.3 }, 1, 3);
|
|
199
199
|
expect(out.y).toBeGreaterThanOrEqual(0);
|
|
200
200
|
expect(out.y + out.height).toBeLessThanOrEqual(1 + 1e-9);
|
|
@@ -237,7 +237,7 @@ describe('sharpenPixels', () => {
|
|
|
237
237
|
it('CLAMPS at the edge rather than wrapping — the fringe defect', () => {
|
|
238
238
|
/*
|
|
239
239
|
* A picture that is dark on the left and bright on the right. Wrapping the blur would
|
|
240
|
-
* sample the bright side into the dark side's
|
|
240
|
+
* sample the bright side into the dark side's neighborhood, so the darkest column would
|
|
241
241
|
* be pushed DOWN hard and the picture would gain a fringe along its edge. With clamping,
|
|
242
242
|
* a flat run at the edge has no detail to recover and must come back unchanged.
|
|
243
243
|
*/
|
|
@@ -309,7 +309,7 @@ export function applyAspect(rect: CropRect, ratio: number | null, boxAspect: num
|
|
|
309
309
|
current > target
|
|
310
310
|
? { ...rect, width: rect.height * target }
|
|
311
311
|
: { ...rect, height: rect.width / target };
|
|
312
|
-
// Keep the rect
|
|
312
|
+
// Keep the rect centered on where it was, so applying a preset does not also move the framing.
|
|
313
313
|
const centred = {
|
|
314
314
|
...next,
|
|
315
315
|
x: rect.x + (rect.width - next.width) / 2,
|
|
@@ -344,7 +344,7 @@ export interface Pixels {
|
|
|
344
344
|
*
|
|
345
345
|
* 🔴 Edges CLAMP rather than wrap. A wrap samples the opposite side of the photograph into the
|
|
346
346
|
* blur, so every enhanced picture would gain a bright fringe along one edge and a dark one along
|
|
347
|
-
* the other — a defect that looks exactly like a scanning
|
|
347
|
+
* the other — a defect that looks exactly like a scanning artifact, which is the last thing this
|
|
348
348
|
* feature should be inventing.
|
|
349
349
|
*/
|
|
350
350
|
export function sharpenPixels(pixels: Pixels, enhance: PictureEnhance): void {
|
|
@@ -372,7 +372,7 @@ export function sharpenPixels(pixels: Pixels, enhance: PictureEnhance): void {
|
|
|
372
372
|
for (let c = 0; c < 3; c += 1) {
|
|
373
373
|
const original = data[p + c] as number;
|
|
374
374
|
const detail = original - (blurred[i * 3 + c] as number);
|
|
375
|
-
// Uint8ClampedArray clamps to 0…255 on assignment, which is exactly the
|
|
375
|
+
// Uint8ClampedArray clamps to 0…255 on assignment, which is exactly the behavior wanted:
|
|
376
376
|
// a strong setting on a bright highlight should flatten to white, not wrap to black.
|
|
377
377
|
data[p + c] = original + amount * detail;
|
|
378
378
|
}
|
|
@@ -445,7 +445,7 @@ export async function bakePicture(
|
|
|
445
445
|
ctx.filter = lightFilter(edit.light);
|
|
446
446
|
|
|
447
447
|
// Work in the turned frame: translate to the middle, rotate, mirror, then draw the source
|
|
448
|
-
//
|
|
448
|
+
// centered. Drawing the crop means picking the source rectangle — which is expressed in the
|
|
449
449
|
// TURNED picture's coordinates, so it is un-turned here rather than at the call site.
|
|
450
450
|
const crop = edit.crop ?? FULL_CROP;
|
|
451
451
|
const turnedW = swapsAxes(edit.turns) ? naturalHeight : naturalWidth;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
lazily imported from the gallery, so these bytes ride the gallery's chunk, while a utility
|
|
4
4
|
would land in whichever ENTRY stylesheet happened to scan it. `apps/family` had 89 bytes of
|
|
5
5
|
entry-stylesheet headroom when this was written, which is what makes that a rule here rather
|
|
6
|
-
than a preference. Every
|
|
6
|
+
than a preference. Every color is a theme token. */
|
|
7
7
|
|
|
8
8
|
.cbpe {
|
|
9
9
|
display: flex;
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
max-width: 100%;
|
|
37
37
|
max-height: 100%;
|
|
38
38
|
/* The turns and flips are a CSS transform on the element, so the box it is laid out in
|
|
39
|
-
must not clip the rotated result. `object-fit: contain` plus the flex
|
|
39
|
+
must not clip the rotated result. `object-fit: contain` plus the flex centering above is
|
|
40
40
|
what keeps a portrait picture whole inside a landscape frame. */
|
|
41
41
|
object-fit: contain;
|
|
42
42
|
transform-origin: center;
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
/* The before/after comparison the owner asked for by name for the sharpen — *"so long as we
|
|
217
217
|
don't confirm deleting the original until we review the enhanced output version"*. The
|
|
218
218
|
sharpened copy is a canvas laid over the picture and clipped to a seam the reader drags; it
|
|
219
|
-
|
|
219
|
+
centers itself the same way `.cbpe-frame` centers the `<img>`, so the two align exactly
|
|
220
220
|
without a second set of sizing rules. FrameGrabber's poster canvas rides the same selector. */
|
|
221
221
|
.cbpe-compare {
|
|
222
222
|
position: absolute;
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
/* The film, sized exactly like the `<img>` the picture editor puts in the same frame: contained,
|
|
267
|
-
|
|
267
|
+
centered, never overflowing the box it is previewed in. Written out here because `.cbpe-frame
|
|
268
268
|
img` cannot say it for an element that is not an image, and without it the preview is whatever
|
|
269
269
|
size the browser's default happens to make it — which for a phone clip is a postage stamp in
|
|
270
270
|
the middle of a black frame, i.e. the thing this dialog is for is invisible. */
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
|
|
301
301
|
/* Why there is no moving picture, said ON the frame that would otherwise be black — the owner's
|
|
302
302
|
*"I see a black screen"*, which he could only report as a black screen because the dialog said
|
|
303
|
-
nothing.
|
|
303
|
+
nothing. Centered over the ground rather than under the frame so it is read as being about the
|
|
304
304
|
picture, and `pointer-events: none` so it never eats a press meant for the film. */
|
|
305
305
|
.cbpe-frame-note {
|
|
306
306
|
position: absolute;
|
|
@@ -316,7 +316,7 @@
|
|
|
316
316
|
both themes — the same argument as `.cbpe-hint` above. */
|
|
317
317
|
color: rgb(255 255 255 / 0.82);
|
|
318
318
|
/* guardrails-ignore no-hardcoded-colors: the shadow that keeps this sentence readable over a
|
|
319
|
-
PALE frame of film — same argument as the
|
|
319
|
+
PALE frame of film — same argument as the color it sits on, two lines up. */
|
|
320
320
|
text-shadow: 0 1px 2px rgb(0 0 0 / 0.6);
|
|
321
321
|
pointer-events: none;
|
|
322
322
|
}
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
* Both are load-bearing and neither is obvious.
|
|
31
31
|
*
|
|
32
32
|
* A browser activates a focused button on the Space KEYUP, not the keydown — but it only
|
|
33
|
-
* does so if the keydown was not defaulted. So
|
|
33
|
+
* does so if the keydown was not defaulted. So canceling the keydown is what stops the
|
|
34
34
|
* button firing; a bubble-phase listener that runs after React's own handler is too late for
|
|
35
|
-
* anything that acts on keydown, and not
|
|
35
|
+
* anything that acts on keydown, and not canceling at all leaves BOTH behaviors running
|
|
36
36
|
* (pause AND next), which reads as "the space bar is random".
|
|
37
37
|
*
|
|
38
38
|
* `preventDefault` also stops the document scrolling a page down, which is what Space does
|
|
@@ -60,7 +60,7 @@ export interface PlayerKeyEvent {
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* `<input>` types that are a place to TYPE. Everything else an `<input>` can be — a
|
|
63
|
-
* checkbox, a radio, a range, a
|
|
63
|
+
* checkbox, a radio, a range, a color swatch, a file picker, a button — is a control the
|
|
64
64
|
* player page is entitled to take Space back from.
|
|
65
65
|
*
|
|
66
66
|
* An `<input>` with no `type` at all is a text field; `""` covers that.
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
z-index: var(--z-sticky);
|
|
35
35
|
display: grid;
|
|
36
36
|
/* "three distinct columns" — metadata, playback, utilities. The outer two are
|
|
37
|
-
`minmax(0, 1fr)` so they are equal and the
|
|
37
|
+
`minmax(0, 1fr)` so they are equal and the CENTER stays optically centered in the
|
|
38
38
|
window no matter how long a track title is. */
|
|
39
39
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
40
40
|
align-items: center;
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
height 120ms ease;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
/* Where the song IS, as a mark rather than as the end of a
|
|
147
|
+
/* Where the song IS, as a mark rather than as the end of a color — what a hand aims at
|
|
148
148
|
when the intent is "drag from here". */
|
|
149
149
|
.cbps__seekline-knob {
|
|
150
150
|
position: absolute;
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
* title is short.
|
|
263
263
|
*
|
|
264
264
|
* Sized to its content instead, the block is as wide as the song's name — so the delete,
|
|
265
|
-
*
|
|
265
|
+
* favorite and tag buttons beside it slide left and right by a hundred pixels every time
|
|
266
266
|
* the track changes. `DESIGN.md` §1: controls belong in fixed, predictable column tracks,
|
|
267
267
|
* and a destructive control that moves under a resting cursor is the worst case of not
|
|
268
268
|
* having one. It also makes the truncation boundary a property of the LAYOUT rather than of
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
/* DESIGN.md §4: secondary metadata never goes below 12px, and it is lightened rather than
|
|
288
|
-
|
|
288
|
+
grayed so it stays scannable against a near-black surface. */
|
|
289
289
|
.cbps__subtitle {
|
|
290
290
|
overflow: hidden;
|
|
291
291
|
text-overflow: ellipsis;
|
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
* DESIGN.md §5's 44px wins where the two disagree, which the owner anticipated.
|
|
313
313
|
*
|
|
314
314
|
* The BACKGROUND is what has to be transparent, not the target: `IconButton`'s ghost variant
|
|
315
|
-
* paints a `bg-muted` rectangle on hover, which is the
|
|
315
|
+
* paints a `bg-muted` rectangle on hover, which is the gray square the owner asked to have
|
|
316
316
|
* deleted. The hover affordance below is a soft circular glow instead, per "subtle, soft
|
|
317
317
|
* background glow circles appearing behind transparent containers".
|
|
318
318
|
*/
|
|
@@ -365,7 +365,7 @@
|
|
|
365
365
|
opacity: 0.4;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
/* ──
|
|
368
|
+
/* ── Center column ───────────────────────────────────────────────────────────────────── */
|
|
369
369
|
|
|
370
370
|
.cbps__centre {
|
|
371
371
|
display: flex;
|
|
@@ -389,7 +389,7 @@
|
|
|
389
389
|
* "Keep the Play/Pause toggle prominent, but scale down its massive gradient circular
|
|
390
390
|
* backdrop slightly to prevent it from overwhelming the adjacent buttons."
|
|
391
391
|
*
|
|
392
|
-
* It was 40px against 36px
|
|
392
|
+
* It was 40px against 36px neighbors; it is now 48px against 44px. Bigger in absolute
|
|
393
393
|
* terms and SMALLER relative to what surrounds it, which is what the sentence asks for.
|
|
394
394
|
*/
|
|
395
395
|
.cbps .cbps__play {
|
|
@@ -431,7 +431,7 @@
|
|
|
431
431
|
/*
|
|
432
432
|
* 🔴 42vw, measured rather than picked. The owner's band is "roughly 40% to 50% of the
|
|
433
433
|
* desktop window width", and where inside it this sits decides whether his TRACK TITLE is
|
|
434
|
-
* readable — because the outer columns are equal (that is what keeps the timeline
|
|
434
|
+
* readable — because the outer columns are equal (that is what keeps the timeline centered
|
|
435
435
|
* in the window), so each gets exactly (100 − N)/2 vw and the left one pays for the art,
|
|
436
436
|
* the three track actions and the gaps out of its share before the title sees a pixel.
|
|
437
437
|
*
|
|
@@ -478,7 +478,7 @@
|
|
|
478
478
|
* 🔴 A filled capsule, not bare text over whatever is behind it.
|
|
479
479
|
*
|
|
480
480
|
* It floats ABOVE the strip, which on a full-height page means it sits on top of the last
|
|
481
|
-
* row of whatever list the app is showing — and as plain
|
|
481
|
+
* row of whatever list the app is showing — and as plain centered text on a transparent
|
|
482
482
|
* background that is two strings drawn over each other, unreadable and looking like a
|
|
483
483
|
* rendering fault. Seen on the live music deployment on 2026-08-22: *"This track could not
|
|
484
484
|
* be played"* struck through a track title.
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
appearance: none;
|
|
540
540
|
width: 100%;
|
|
541
541
|
/* 🔴 The TOUCH TARGET, not the paint. A 6px line is a 6px hit area, and a thumb wants
|
|
542
|
-
~44. The track and thumb are drawn
|
|
542
|
+
~44. The track and thumb are drawn centered inside this box, so the control LOOKS 6px
|
|
543
543
|
tall and CATCHES a 2.75rem band. DESIGN.md §5, and the owner's *"I can't find the
|
|
544
544
|
slider to drag the song further left or right."* */
|
|
545
545
|
height: 2.75rem;
|
|
@@ -586,7 +586,7 @@
|
|
|
586
586
|
appearance: none;
|
|
587
587
|
width: 0.85rem;
|
|
588
588
|
height: 0.85rem;
|
|
589
|
-
/*
|
|
589
|
+
/* Centers the handle on a 6px track: (6px − 13.6px) / 2. */
|
|
590
590
|
margin-top: -0.24rem;
|
|
591
591
|
border: 0;
|
|
592
592
|
border-radius: 9999px;
|
|
@@ -119,7 +119,7 @@ describe("PlayerStrip — the controls", () => {
|
|
|
119
119
|
describe("PlayerStrip — the app's own actions", () => {
|
|
120
120
|
const action = (over: Record<string, unknown> = {}) => ({
|
|
121
121
|
key: "fav",
|
|
122
|
-
label: "
|
|
122
|
+
label: "Favorite",
|
|
123
123
|
icon: <svg />,
|
|
124
124
|
onClick: () => undefined,
|
|
125
125
|
...over,
|
|
@@ -128,7 +128,7 @@ describe("PlayerStrip — the app's own actions", () => {
|
|
|
128
128
|
test("track actions sit in the metadata column, beside what they act on", () => {
|
|
129
129
|
const { container } = render(<PlayerStrip {...base} trackActions={[action()]} />);
|
|
130
130
|
expect(container.querySelector(".cbps__meta .cbps__actions")).not.toBeNull();
|
|
131
|
-
expect(screen.getByRole("button", { name: "
|
|
131
|
+
expect(screen.getByRole("button", { name: "Favorite" })).toBeDefined();
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
test("utility actions sit on the right, with the volume", () => {
|
|
@@ -145,7 +145,7 @@ describe("PlayerStrip — the app's own actions", () => {
|
|
|
145
145
|
trackActions={[action({ active: true }), action({ key: "del", label: "Delete", danger: true })]}
|
|
146
146
|
/>,
|
|
147
147
|
);
|
|
148
|
-
const fav = screen.getByRole("button", { name: "
|
|
148
|
+
const fav = screen.getByRole("button", { name: "Favorite" });
|
|
149
149
|
expect(fav.getAttribute("aria-pressed")).toBe("true");
|
|
150
150
|
expect(fav.className).toContain("is-active");
|
|
151
151
|
expect(screen.getByRole("button", { name: "Delete" }).className).toContain("is-danger");
|
|
@@ -182,12 +182,16 @@ describe("PlayerStrip — the app's own actions", () => {
|
|
|
182
182
|
action({ key: "tag", label: "Tag", hint: "Add a tag to this track" }),
|
|
183
183
|
];
|
|
184
184
|
render(<PlayerStrip {...base} inlineActions={1} trackActions={hinted} />);
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
// ONE lookup of the trigger: once `pointerDown` opens the menu, Radix marks everything outside
|
|
186
|
+
// it `aria-hidden`, so a second `getByRole` for the trigger fails whenever the open lands first —
|
|
187
|
+
// which it did under the publish gate's load and nowhere else (forge task 3573).
|
|
188
|
+
const trigger = screen.getByRole("button", { name: "More actions for this track" });
|
|
189
|
+
fireEvent.pointerDown(trigger, { button: 0 });
|
|
190
|
+
fireEvent.click(trigger);
|
|
187
191
|
const menu = await screen.findByRole("menu", { hidden: true });
|
|
188
|
-
expect(within(menu).
|
|
192
|
+
expect(await within(menu).findByText("Keep")).toBeDefined();
|
|
189
193
|
expect(within(menu).queryByText("Save this track to your library")).toBeNull();
|
|
190
|
-
const info = within(menu).
|
|
194
|
+
const info = await within(menu).findByText("What these do");
|
|
191
195
|
await act(async () => {
|
|
192
196
|
fireEvent.click(info);
|
|
193
197
|
await new Promise((r) => setTimeout(r, 0));
|
|
@@ -268,7 +272,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
268
272
|
expect(svg).toContain("stroke-width: 2");
|
|
269
273
|
});
|
|
270
274
|
|
|
271
|
-
test("inactive controls are 50–60% of the foreground, not
|
|
275
|
+
test("inactive controls are 50–60% of the foreground, not gray", () => {
|
|
272
276
|
const pct = /color-mix\(in oklab, var\(--foreground\) (\d+)%/.exec(ruleBody(".cbps .cbps__icon"))?.[1];
|
|
273
277
|
expect(Number(pct)).toBeGreaterThanOrEqual(50);
|
|
274
278
|
expect(Number(pct)).toBeLessThanOrEqual(60);
|
|
@@ -284,7 +288,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
284
288
|
// The owner's 2026-08-12 report and the measured fix for it. A 2px line is a 2px hit
|
|
285
289
|
// area; the band is what a thumb actually lands on, and it has to WIN the press across
|
|
286
290
|
// the width or the control is grabbable nowhere. music's browser smoke measures the
|
|
287
|
-
// other half — that it does not swallow the play button's
|
|
291
|
+
// other half — that it does not swallow the play button's center.
|
|
288
292
|
const band = ruleBody(".cbps__seekline");
|
|
289
293
|
expect(band).toContain("height: 0.75rem");
|
|
290
294
|
// guardrails-ignore no-raw-zindex: asserting the declaration the rule above excuses —
|
|
@@ -294,7 +298,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
294
298
|
expect(ruleBody(".cbps__seekline-rail")).toContain("background: var(--border)");
|
|
295
299
|
});
|
|
296
300
|
|
|
297
|
-
test("the play disc is larger than its
|
|
301
|
+
test("the play disc is larger than its neighbors but not by much", () => {
|
|
298
302
|
// 3rem against 2.75rem — "keep it prominent, but scale down its massive gradient
|
|
299
303
|
// circular backdrop slightly to prevent it from overwhelming the adjacent buttons".
|
|
300
304
|
expect(ruleBody(".cbps .cbps__play")).toContain("width: 3rem");
|
|
@@ -311,7 +315,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
311
315
|
|
|
312
316
|
test("🔴 the track actions have a FIXED home, whatever the title's length", () => {
|
|
313
317
|
// Sized to its content, the text block is as wide as the song's name — so delete,
|
|
314
|
-
//
|
|
318
|
+
// favorite and tag slide by a hundred pixels every time the track changes. DESIGN.md
|
|
315
319
|
// §1, and a destructive control that moves under a resting cursor is its worst case.
|
|
316
320
|
expect(ruleBody(".cbps__text")).toContain("flex: 1 1 0");
|
|
317
321
|
expect(ruleBody(".cbps__actions")).toContain("flex: none");
|
|
@@ -334,7 +338,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
334
338
|
* in the red this slot reserves for "this is broken". A recoverable hiccup and a
|
|
335
339
|
* dead library looked identical, so the owner read a busy app as a broken one.
|
|
336
340
|
*
|
|
337
|
-
* The modifier redefines only the three
|
|
341
|
+
* The modifier redefines only the three color properties and inherits the geometry
|
|
338
342
|
* above it, which is what stops the two tones drifting into different shapes. And it
|
|
339
343
|
* is `--warning`, not a softer red: DESIGN.md §3 reserves `--danger` for a state the
|
|
340
344
|
* person has to act on.
|
|
@@ -348,7 +352,7 @@ describe("playerStrip.css — the layout the owner specified", () => {
|
|
|
348
352
|
expect(notice).not.toContain("position:");
|
|
349
353
|
});
|
|
350
354
|
|
|
351
|
-
test("no literal
|
|
355
|
+
test("no literal colors — theme tokens only", () => {
|
|
352
356
|
expect(/#[0-9a-fA-F]{3,8}\b/.test(css)).toBe(false);
|
|
353
357
|
expect(/\brgba?\(\s*\d/.test(css)).toBe(false);
|
|
354
358
|
});
|
|
@@ -59,7 +59,7 @@ describe('framedDrawRect', () => {
|
|
|
59
59
|
expect(rect.dw / rect.dh).toBeCloseTo(PORTRAIT.width / PORTRAIT.height, 5);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
test('a
|
|
62
|
+
test('a centered portrait shows the MIDDLE of the frame, not the top', () => {
|
|
63
63
|
const rect = framedDrawRect(PORTRAIT, BOX, DEFAULT_FRAMING);
|
|
64
64
|
expect(rect).not.toBeNull();
|
|
65
65
|
if (!rect) return;
|
|
@@ -103,14 +103,14 @@ describe('framedDrawRect', () => {
|
|
|
103
103
|
expect(under).toEqual(framedDrawRect(LANDSCAPE, BOX, DEFAULT_FRAMING));
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
test('zoom enlarges about the
|
|
106
|
+
test('zoom enlarges about the center when nothing is panned', () => {
|
|
107
107
|
const one = framedDrawRect(LANDSCAPE, BOX, DEFAULT_FRAMING);
|
|
108
108
|
const two = framedDrawRect(LANDSCAPE, BOX, { zoom: 2, panX: 0, panY: 0 });
|
|
109
109
|
expect(one).not.toBeNull();
|
|
110
110
|
expect(two).not.toBeNull();
|
|
111
111
|
if (!one || !two) return;
|
|
112
112
|
expect(two.dw).toBeCloseTo(one.dw * 2, 5);
|
|
113
|
-
// Same
|
|
113
|
+
// Same center point: the thing under the middle of the tile stays under it.
|
|
114
114
|
expect(two.dx + two.dw / 2).toBeCloseTo(one.dx + one.dw / 2, 5);
|
|
115
115
|
expect(two.dy + two.dh / 2).toBeCloseTo(one.dy + one.dh / 2, 5);
|
|
116
116
|
});
|
|
@@ -145,7 +145,7 @@ describe('pannedBy', () => {
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
describe('zoomedBy', () => {
|
|
148
|
-
test('re-
|
|
148
|
+
test('re-centers when it comes all the way back out', () => {
|
|
149
149
|
const framed = { zoom: 2, panX: 0.6, panY: -0.4 };
|
|
150
150
|
expect(zoomedBy(framed, 0.01)).toEqual(DEFAULT_FRAMING);
|
|
151
151
|
});
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
//
|
|
3
3
|
// `videoPoster.ts` (beside this) answers "which frame?"; this answers "which PART of that
|
|
4
4
|
// frame, at what magnification?". They are separate ideas: an automatic poster wants the most
|
|
5
|
-
// informative frame
|
|
5
|
+
// informative frame centered, and a person choosing one wants to push their grandmother's face
|
|
6
6
|
// into the middle of the card.
|
|
7
7
|
//
|
|
8
8
|
// ── 🔴 Why the framing is baked into the STORED bytes ────────────────────────────────
|
|
9
9
|
// A gallery tile paints its picture with `object-fit: cover`, so a poster whose shape differs
|
|
10
|
-
// from the tile's gets
|
|
10
|
+
// from the tile's gets center-cropped by the browser with no say from anyone — the owner's
|
|
11
11
|
// report, from `collections`, was a portrait video whose card showed somebody's midriff.
|
|
12
12
|
// Storing the poster ALREADY framed turns that silent crop into a decision: the browser's
|
|
13
13
|
// cover-crop becomes a no-op because the image it is handed is already the right shape.
|
|
@@ -38,7 +38,7 @@ export const POSTER_ASPECT = 16 / 9;
|
|
|
38
38
|
export interface PosterFraming {
|
|
39
39
|
/** 1 = the frame covers the tile exactly; above that, closer in. Below 1 is ignored. */
|
|
40
40
|
zoom: number;
|
|
41
|
-
/** −1…1 of the available overflow. 0 is
|
|
41
|
+
/** −1…1 of the available overflow. 0 is centered; an axis with no overflow ignores it. */
|
|
42
42
|
panX: number;
|
|
43
43
|
panY: number;
|
|
44
44
|
}
|
|
@@ -107,7 +107,7 @@ export const ZOOM_RANGE = { min: 1, max: 3, step: 0.01 } as const;
|
|
|
107
107
|
*
|
|
108
108
|
* `delta` is how far the POINTER moved, in the coordinates of the preview box. The picture
|
|
109
109
|
* follows the pointer, which is the only direction-of-travel a drag can have without feeling
|
|
110
|
-
* broken — and because pan is
|
|
110
|
+
* broken — and because pan is normalized to the overflow rather than to pixels, the conversion
|
|
111
111
|
* is the inverse of the offset `framedDrawRect` applies:
|
|
112
112
|
*
|
|
113
113
|
* dx = (W − dw)/2 − (panX × overflowX)/2 ⇒ Δpan = −2 × Δpixels / overflow
|
|
@@ -146,7 +146,7 @@ export function zoomedBy(framing: PosterFraming, factor: number): PosterFraming
|
|
|
146
146
|
if (!Number.isFinite(factor) || factor <= 0) return framing;
|
|
147
147
|
const zoom = Math.min(ZOOM_RANGE.max, Math.max(ZOOM_RANGE.min, framing.zoom * factor));
|
|
148
148
|
/*
|
|
149
|
-
* Zooming back out to 1 re-
|
|
149
|
+
* Zooming back out to 1 re-centers, because at zoom 1 there is no overflow for a pan to
|
|
150
150
|
* choose within — leaving a stale pan on the value means the next zoom-in jumps to a framing
|
|
151
151
|
* nobody asked for, which reads as the control having a memory of its own.
|
|
152
152
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ── 🔴 The report this exists for ───────────────────────────────────────────────────────
|
|
5
5
|
* Owner, 2026-09-17: *"make a 404 stop the media player spinner so I don't think it's going
|
|
6
|
-
* to eventually load."* One item in his
|
|
6
|
+
* to eventually load."* One item in his catalog has no ladder, so the very first request a
|
|
7
7
|
* player makes is answered honestly and immediately:
|
|
8
8
|
*
|
|
9
9
|
* GET …/u_fd668e6a….hls/master.m3u8 → 404 {"error":"not_found"} in 0.2 s
|
|
@@ -140,7 +140,7 @@ export async function probeSourceFault(url: string | undefined): Promise<SourceF
|
|
|
140
140
|
* assertion that reads the function asserts nothing.
|
|
141
141
|
*
|
|
142
142
|
* `subject` is the noun for the thing that failed — "This video", "This picture", the file's
|
|
143
|
-
* own name. It leads the sentence, so it is
|
|
143
|
+
* own name. It leads the sentence, so it is capitalized by the caller.
|
|
144
144
|
*/
|
|
145
145
|
export function sourceFaultMessage(fault: SourceFault, subject: string): string {
|
|
146
146
|
const status = fault.status != null ? ` (HTTP ${fault.status})` : '';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* How tall the person dragged the stage — the pure half, so the pointer
|
|
2
|
+
* How tall the person dragged the stage — the pure half, so the pointer math is testable
|
|
3
3
|
* without a pointer.
|
|
4
4
|
*
|
|
5
5
|
* Owner, 2026-08-25: *"allow dragging the corner to make it bigger viewing area while
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - `.vds-controls` covers the WHOLE player (`position: absolute; inset: 0`) but is
|
|
14
14
|
* `pointer-events: none`, so it is not the culprit;
|
|
15
15
|
* - `.vds-controls-spacer`, the middle band, is `pointer-events: none` too — which is why
|
|
16
|
-
* the
|
|
16
|
+
* the center of the picture has always worked;
|
|
17
17
|
* - 🔴 `.vds-video-layout .vds-controls-group` is **`pointer-events: auto`**, and there are
|
|
18
18
|
* three of them: the title strip across the top, the scrubber row, and the button bar.
|
|
19
19
|
* Each is a full-width `<div>`, so every pixel of those three bands — including the empty
|
package/src/react/media/types.ts
CHANGED
|
@@ -145,7 +145,7 @@ export interface MediaCatalogueViewerProps {
|
|
|
145
145
|
columns?: number;
|
|
146
146
|
/**
|
|
147
147
|
* Scroll container for the virtualized `rows` layout. Omit for window/page scroll
|
|
148
|
-
* (the default); pass the app's `ScrollArea` viewport element when the
|
|
148
|
+
* (the default); pass the app's `ScrollArea` viewport element when the catalog
|
|
149
149
|
* lives inside a bounded scroll region so only visible rows mount.
|
|
150
150
|
*/
|
|
151
151
|
scrollRoot?: HTMLElement | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Plain CSS, colocated with `VideoChapterEditor.tsx` and imported by it — the same
|
|
2
2
|
convention, and the same reason, as `videoCutEditor.css`: the editor is lazily imported
|
|
3
3
|
from the gallery, so these bytes ride the gallery's chunk instead of landing in whichever
|
|
4
|
-
ENTRY stylesheet happened to scan a utility class. Every
|
|
4
|
+
ENTRY stylesheet happened to scan a utility class. Every color is a theme token. */
|
|
5
5
|
|
|
6
6
|
.cbch {
|
|
7
7
|
display: flex;
|
|
@@ -81,7 +81,7 @@ describe('normalizeChapters', () => {
|
|
|
81
81
|
* The usual source is the playhead of a `<video>` whose metadata has not loaded. Every
|
|
82
82
|
* comparison against NaN is false, so leaving it in produces an array `sort` calls sorted
|
|
83
83
|
* and is not — and the de-duplication pass then compares each chapter to the wrong
|
|
84
|
-
*
|
|
84
|
+
* neighbor and starts dropping real ones.
|
|
85
85
|
*/
|
|
86
86
|
expect(
|
|
87
87
|
shape(normalizeChapters([at(40, 'Ending'), at(Number.NaN, 'Nowhere'), at(5, 'Intro')], 60)),
|
|
@@ -96,7 +96,7 @@ const cleanTitle = (title: unknown): string => (typeof title === 'string' ? titl
|
|
|
96
96
|
* A non-finite start (the usual source is a playhead read from a `<video>` whose metadata has
|
|
97
97
|
* not loaded, which reports `NaN`) drops its chapter instead of poisoning the sort: every
|
|
98
98
|
* comparison against `NaN` is false, which produces an array `sort` calls sorted and is not,
|
|
99
|
-
* and then the de-duplication pass below compares against the wrong
|
|
99
|
+
* and then the de-duplication pass below compares against the wrong neighbor.
|
|
100
100
|
*/
|
|
101
101
|
export function normalizeChapters(
|
|
102
102
|
chapters: readonly VideoChapter[],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Plain CSS, colocated with `VideoCutEditor.tsx` and imported by it — the same convention,
|
|
2
2
|
and the same reason, as `pictureEditor.css`: the editor is lazily imported from the
|
|
3
3
|
gallery, so these bytes ride the gallery's chunk instead of landing in whichever ENTRY
|
|
4
|
-
stylesheet happened to scan a utility class. Every
|
|
4
|
+
stylesheet happened to scan a utility class. Every color is a theme token. */
|
|
5
5
|
|
|
6
6
|
.cbvc {
|
|
7
7
|
display: flex;
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/* 🔴 The ✗ is always drawn, never revealed on hover. This control has no other affordance —
|
|
126
|
-
a bare
|
|
126
|
+
a bare colored block does not say it can be clicked — and a hover-only one is invisible on
|
|
127
127
|
every touch screen, which is the same invisible-affordance bug the resize grip was reported
|
|
128
128
|
for on 2026-08-25. It dims until hover instead. */
|
|
129
129
|
.cbvc-seg-x {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The enhancement
|
|
2
|
+
* The enhancement math — the parts that decide what a saved picture looks like, isolated
|
|
3
3
|
* from any DOM so they can be asserted.
|
|
4
4
|
*/
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* uses and for the same reason: an adjustment previewed by different code than the export is
|
|
11
11
|
* an adjustment that is eventually a lie. Here they are literally the same string.
|
|
12
12
|
*
|
|
13
|
-
* ── Why CSS filters rather than pixel
|
|
13
|
+
* ── Why CSS filters rather than pixel math ────────────────────────────────────────────
|
|
14
14
|
* `filter: brightness() contrast() saturate()` is GPU-composited, so it costs nothing on a
|
|
15
15
|
* playing 1080p video, and Canvas2D accepts the identical syntax on `ctx.filter`. A manual
|
|
16
|
-
* per-pixel loop would be equivalent
|
|
16
|
+
* per-pixel loop would be equivalent math, run on the CPU, at ~2 M pixels a frame — which is
|
|
17
17
|
* unusable live and pointless when the platform already does it.
|
|
18
18
|
*
|
|
19
19
|
* ── 🔴 Upscaling is HONEST here, and deliberately modest ───────────────────────────────
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
import { pickBestFrame, posterCandidateTimes, scoreFrame } from './videoPoster.js';
|
|
3
3
|
|
|
4
|
-
/** A solid block of one
|
|
4
|
+
/** A solid block of one color, as RGBA — the fades and flat frames real footage opens on. */
|
|
5
5
|
const flat = (r: number, g: number, b: number, pixels = 64): Uint8ClampedArray => {
|
|
6
6
|
const data = new Uint8ClampedArray(pixels * 4);
|
|
7
7
|
for (let i = 0; i < pixels; i++) {
|
|
@@ -62,14 +62,14 @@ describe('scoreFrame', () => {
|
|
|
62
62
|
expect(scoreFrame(flat(255, 255, 255)).score).toBe(0);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
test('a frame with actual content beats a flat mid-
|
|
65
|
+
test('a frame with actual content beats a flat mid-gray of the same brightness', () => {
|
|
66
66
|
const picture = scoreFrame(split(30, 210));
|
|
67
67
|
const grey = scoreFrame(flat(120, 120, 120));
|
|
68
68
|
expect(picture.contrast).toBeGreaterThan(grey.contrast);
|
|
69
69
|
expect(picture.score).toBeGreaterThan(grey.score);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
test('
|
|
72
|
+
test('color breaks the tie between two equally flat frames', () => {
|
|
73
73
|
expect(scoreFrame(flat(200, 40, 40)).colorfulness).toBeGreaterThan(
|
|
74
74
|
scoreFrame(flat(120, 120, 120)).colorfulness,
|
|
75
75
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ── Choosing a video's poster frame ──────────────────────────────────────────────
|
|
2
2
|
// Probe a video, score candidate frames, and encode the winner as an uploadable Blob.
|
|
3
3
|
//
|
|
4
|
-
// This exists because a gallery of videos with no posters is a gallery of
|
|
4
|
+
// This exists because a gallery of videos with no posters is a gallery of gray boxes —
|
|
5
5
|
// and the naive fix, "use the video's own URL as the tile image", is worse than none: the
|
|
6
6
|
// browser downloads the whole file to fail at decoding it as a picture. A poster is a
|
|
7
7
|
// 100 KB JPEG that a phone renders instantly.
|
|
@@ -23,7 +23,7 @@ export interface FrameQuality {
|
|
|
23
23
|
brightness: number;
|
|
24
24
|
/** Spread of luminance — the honest proxy for "there is something in this picture". */
|
|
25
25
|
contrast: number;
|
|
26
|
-
/** How much
|
|
26
|
+
/** How much color is present; separates a gray fade from a dim but real shot. */
|
|
27
27
|
colorfulness: number;
|
|
28
28
|
/** The single number `pickBestFrame` orders by. */
|
|
29
29
|
score: number;
|
|
@@ -51,10 +51,10 @@ export function posterCandidateTimes(durationSeconds: number, count = 5): number
|
|
|
51
51
|
* Score one decoded frame from its RGBA pixels.
|
|
52
52
|
*
|
|
53
53
|
* Contrast carries the weight because it is what actually distinguishes a picture from a
|
|
54
|
-
* fade: a black frame, a white flash and a flat
|
|
54
|
+
* fade: a black frame, a white flash and a flat gray all have near-zero spread whatever
|
|
55
55
|
* their brightness. Brightness then acts as a penalty at the extremes rather than as a
|
|
56
56
|
* reward — a correctly-exposed dark shot should still beat a blown-out one — and a little
|
|
57
|
-
*
|
|
57
|
+
* colorfulness breaks ties toward the frame a person would call "the picture of it".
|
|
58
58
|
*/
|
|
59
59
|
export function scoreFrame(rgba: Uint8ClampedArray | number[]): FrameQuality {
|
|
60
60
|
const pixels = Math.floor(rgba.length / 4);
|
|
@@ -83,7 +83,7 @@ export function scoreFrame(rgba: Uint8ClampedArray | number[]): FrameQuality {
|
|
|
83
83
|
const colorfulness = Math.min(1, chroma / pixels * 3);
|
|
84
84
|
|
|
85
85
|
// A frame this dark or this blown out is a fade, not a shot. Zero rather than a small
|
|
86
|
-
// number, so a clip that opens on ten seconds of black cannot win on
|
|
86
|
+
// number, so a clip that opens on ten seconds of black cannot win on colorfulness.
|
|
87
87
|
const usable = brightness > 0.04 && brightness < 0.97;
|
|
88
88
|
const exposure = 1 - Math.abs(brightness - 0.45) / 0.55;
|
|
89
89
|
const score = usable
|