cursedbelt 1.0.0 → 2.3.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/client/index.js +1 -1
- package/dist/core/category/index.d.ts.map +1 -1
- package/dist/core/category/index.js +2 -1
- package/dist/core/category/index.js.map +1 -1
- package/dist/core/category/types.js +1 -1
- package/dist/core/category/types.js.map +1 -1
- package/dist/core/file-tree/fileTreeModel.d.ts +49 -0
- package/dist/core/file-tree/fileTreeModel.d.ts.map +1 -1
- package/dist/core/file-tree/fileTreeModel.js +84 -0
- package/dist/core/file-tree/fileTreeModel.js.map +1 -1
- package/dist/core/layout/index.js +1 -1
- package/dist/core/layout/index.js.map +1 -1
- package/dist/core/media/index.js +1 -1
- package/dist/core/media/index.js.map +1 -1
- package/dist/core/media/types.js +1 -1
- package/dist/core/media/types.js.map +1 -1
- package/dist/core/media/uploadLimits.js +1 -1
- package/dist/core/media/uploadLimits.js.map +1 -1
- package/dist/core/sharing/model.d.ts +1 -1
- package/dist/ctgr/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/activity-bar/ActivityBar.d.ts +17 -0
- package/dist/react/activity-bar/ActivityBar.d.ts.map +1 -1
- package/dist/react/activity-bar/ActivityBar.js.map +1 -1
- package/dist/react/analytics/index.d.ts +1 -1
- package/dist/react/analytics/index.js +1 -1
- package/dist/react/chat/MessageComposer.d.ts.map +1 -1
- package/dist/react/chat/MessageComposer.js +5 -4
- package/dist/react/chat/MessageComposer.js.map +1 -1
- package/dist/react/components/Button.d.ts.map +1 -1
- package/dist/react/components/Button.js +3 -2
- package/dist/react/components/Button.js.map +1 -1
- package/dist/react/components/Checkbox.d.ts +19 -3
- package/dist/react/components/Checkbox.d.ts.map +1 -1
- package/dist/react/components/Checkbox.js +29 -12
- package/dist/react/components/Checkbox.js.map +1 -1
- package/dist/react/components/CodeArea.d.ts.map +1 -1
- package/dist/react/components/CodeArea.js +2 -1
- package/dist/react/components/CodeArea.js.map +1 -1
- package/dist/react/components/Collapsible.d.ts.map +1 -1
- package/dist/react/components/Collapsible.js +2 -1
- package/dist/react/components/Collapsible.js.map +1 -1
- package/dist/react/components/ComboboxField.d.ts.map +1 -1
- package/dist/react/components/ComboboxField.js +3 -2
- package/dist/react/components/ComboboxField.js.map +1 -1
- package/dist/react/components/CopyButton.d.ts.map +1 -1
- package/dist/react/components/CopyButton.js +2 -1
- package/dist/react/components/CopyButton.js.map +1 -1
- package/dist/react/components/DataTable/ColumnChecklist.d.ts.map +1 -1
- package/dist/react/components/DataTable/ColumnChecklist.js +2 -1
- package/dist/react/components/DataTable/ColumnChecklist.js.map +1 -1
- package/dist/react/components/DataTable/Pagination.d.ts.map +1 -1
- package/dist/react/components/DataTable/Pagination.js +2 -1
- package/dist/react/components/DataTable/Pagination.js.map +1 -1
- package/dist/react/components/DataTable/SortHeader.d.ts.map +1 -1
- package/dist/react/components/DataTable/SortHeader.js +2 -1
- package/dist/react/components/DataTable/SortHeader.js.map +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts +61 -0
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -0
- package/dist/react/components/DataTable/{FilterBuilder.js → TableFilterBuilder.js} +26 -3
- package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -0
- package/dist/react/components/DataTable/Toolbar.d.ts +1 -1
- package/dist/react/components/DataTable/Toolbar.js +3 -3
- package/dist/react/components/DataTable/Toolbar.js.map +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.d.ts.map +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.js +2 -1
- package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
- package/dist/react/components/DataTable/index.d.ts +1 -1
- package/dist/react/components/DataTable/index.d.ts.map +1 -1
- package/dist/react/components/DataTable/index.js +4 -1
- package/dist/react/components/DataTable/index.js.map +1 -1
- package/dist/react/components/DragHandle.d.ts.map +1 -1
- package/dist/react/components/DragHandle.js +2 -1
- package/dist/react/components/DragHandle.js.map +1 -1
- package/dist/react/components/EmojiField.d.ts.map +1 -1
- package/dist/react/components/EmojiField.js +2 -1
- package/dist/react/components/EmojiField.js.map +1 -1
- package/dist/react/components/EventTimeline.d.ts +3 -2
- package/dist/react/components/EventTimeline.d.ts.map +1 -1
- package/dist/react/components/EventTimeline.js +3 -2
- package/dist/react/components/EventTimeline.js.map +1 -1
- package/dist/react/components/IconButton.d.ts +1 -1
- package/dist/react/components/IconButton.js +1 -1
- package/dist/react/components/InfoHint.d.ts.map +1 -1
- package/dist/react/components/InfoHint.js +5 -4
- package/dist/react/components/InfoHint.js.map +1 -1
- package/dist/react/components/InheritBadge.js +1 -1
- package/dist/react/components/InheritBadge.js.map +1 -1
- package/dist/react/components/InlineEditableText.d.ts.map +1 -1
- package/dist/react/components/InlineEditableText.js +2 -1
- package/dist/react/components/InlineEditableText.js.map +1 -1
- package/dist/react/components/MeterList.d.ts.map +1 -1
- package/dist/react/components/MeterList.js +2 -1
- package/dist/react/components/MeterList.js.map +1 -1
- package/dist/react/components/PathText.d.ts +2 -2
- package/dist/react/components/PathText.d.ts.map +1 -1
- package/dist/react/components/PathText.js +2 -1
- package/dist/react/components/PathText.js.map +1 -1
- package/dist/react/components/RatingField.d.ts.map +1 -1
- package/dist/react/components/RatingField.js +2 -1
- package/dist/react/components/RatingField.js.map +1 -1
- package/dist/react/components/SecretInput.js +3 -2
- package/dist/react/components/SecretInput.js.map +1 -1
- package/dist/react/components/SegmentedControl.d.ts.map +1 -1
- package/dist/react/components/SegmentedControl.js +2 -1
- package/dist/react/components/SegmentedControl.js.map +1 -1
- package/dist/react/components/StarRating.d.ts.map +1 -1
- package/dist/react/components/StarRating.js +9 -5
- package/dist/react/components/StarRating.js.map +1 -1
- package/dist/react/components/StatStrip.d.ts.map +1 -1
- package/dist/react/components/StatStrip.js +2 -1
- package/dist/react/components/StatStrip.js.map +1 -1
- package/dist/react/components/StatTile.d.ts.map +1 -1
- package/dist/react/components/StatTile.js +3 -2
- package/dist/react/components/StatTile.js.map +1 -1
- package/dist/react/components/Tabs.d.ts.map +1 -1
- package/dist/react/components/Tabs.js +2 -1
- package/dist/react/components/Tabs.js.map +1 -1
- package/dist/react/components/TruncatedText.d.ts +2 -1
- package/dist/react/components/TruncatedText.d.ts.map +1 -1
- package/dist/react/components/TruncatedText.js +2 -1
- package/dist/react/components/TruncatedText.js.map +1 -1
- package/dist/react/components/calendar/MonthGrid.d.ts.map +1 -1
- package/dist/react/components/calendar/MonthGrid.js +2 -1
- package/dist/react/components/calendar/MonthGrid.js.map +1 -1
- package/dist/react/components/calendar/WeekView.d.ts.map +1 -1
- package/dist/react/components/calendar/WeekView.js +2 -1
- package/dist/react/components/calendar/WeekView.js.map +1 -1
- package/dist/react/components/revealOnHover.d.ts.map +1 -1
- package/dist/react/components/revealOnHover.js +2 -1
- package/dist/react/components/revealOnHover.js.map +1 -1
- package/dist/react/file-tree/FileTree.d.ts +50 -1
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +234 -6
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +52 -0
- package/dist/react/filter-rail/FilterRail.d.ts.map +1 -1
- package/dist/react/filter-rail/FilterRail.js +5 -4
- package/dist/react/filter-rail/FilterRail.js.map +1 -1
- package/dist/react/folder-tree/FolderTreeRail.d.ts.map +1 -1
- package/dist/react/folder-tree/FolderTreeRail.js +2 -1
- package/dist/react/folder-tree/FolderTreeRail.js.map +1 -1
- package/dist/react/index.d.ts +5 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/layoutEngine/index.js +1 -1
- package/dist/react/layoutEngine/index.js.map +1 -1
- package/dist/react/lib/field.d.ts.map +1 -1
- package/dist/react/lib/field.js +3 -2
- package/dist/react/lib/field.js.map +1 -1
- package/dist/react/lib/floatingStyles.d.ts.map +1 -1
- package/dist/react/lib/floatingStyles.js +2 -1
- package/dist/react/lib/floatingStyles.js.map +1 -1
- package/dist/react/lib/tapTarget.d.ts +96 -0
- package/dist/react/lib/tapTarget.d.ts.map +1 -0
- package/dist/react/lib/tapTarget.js +96 -0
- package/dist/react/lib/tapTarget.js.map +1 -0
- package/dist/react/media/VideoTrimmer.d.ts +1 -1
- package/dist/react/media/VideoTrimmer.js +1 -1
- package/dist/react/media/denseGallery.css +3 -3
- package/dist/react/media/ffmpegWasm.js +2 -2
- package/dist/react/media/ffmpegWasm.js.map +1 -1
- package/dist/react/media/videoPlayer.css +42 -0
- package/dist/react/namespace/NamespaceTargetCapsule.d.ts +2 -2
- package/dist/react/namespace/NamespaceTargetCapsule.d.ts.map +1 -1
- package/dist/react/nav/BreadcrumbTrail.d.ts.map +1 -1
- package/dist/react/nav/BreadcrumbTrail.js +4 -3
- package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
- package/dist/react/nav/HubTileGrid.d.ts.map +1 -1
- package/dist/react/nav/HubTileGrid.js +2 -1
- package/dist/react/nav/HubTileGrid.js.map +1 -1
- package/dist/react/nav/SideNav.d.ts.map +1 -1
- package/dist/react/nav/SideNav.js +4 -3
- package/dist/react/nav/SideNav.js.map +1 -1
- package/dist/react/nav/SideNavCustomize.d.ts.map +1 -1
- package/dist/react/nav/SideNavCustomize.js +2 -1
- package/dist/react/nav/SideNavCustomize.js.map +1 -1
- package/dist/react/palette/CommandPalette.d.ts.map +1 -1
- package/dist/react/palette/CommandPalette.js +3 -2
- package/dist/react/palette/CommandPalette.js.map +1 -1
- package/dist/server/drivers/pooledControlStores.pg.test.js +1 -0
- package/dist/server/drivers/pooledControlStores.pg.test.js.map +1 -1
- package/dist/server/drivers/pooledIdentityStore.pg.test.js +1 -0
- package/dist/server/drivers/pooledIdentityStore.pg.test.js.map +1 -1
- package/dist/server/drivers/retentionPolicy.pg.test.js +1 -0
- package/dist/server/drivers/retentionPolicy.pg.test.js.map +1 -1
- package/dist/server/guard/argon2Binding.d.ts +1 -1
- package/dist/server/guard/argon2Binding.js +1 -1
- package/dist/server/incidents/store.d.ts +3 -3
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/master-lock/masterLock.d.ts +1 -1
- package/dist/server/media-bun/index.js +1 -1
- package/dist/server/media-bun/index.js.map +1 -1
- package/dist/server/paths/appDataHome.d.ts +20 -0
- package/dist/server/paths/appDataHome.d.ts.map +1 -0
- package/dist/server/paths/appDataHome.js +69 -0
- package/dist/server/paths/appDataHome.js.map +1 -0
- package/dist/server/retention/archive.d.ts +3 -4
- package/dist/server/retention/archive.d.ts.map +1 -1
- package/dist/server/retention/archive.js +20 -15
- package/dist/server/retention/archive.js.map +1 -1
- package/dist/server/retention/index.d.ts +1 -1
- package/dist/server/retention/index.js +1 -1
- package/dist/server/retention/policy.d.ts +2 -2
- package/dist/server/retention/policy.d.ts.map +1 -1
- package/dist/server/retention/policy.js +1 -1
- package/dist/server/retention/store.d.ts.map +1 -1
- package/dist/server/retention/store.js +27 -20
- package/dist/server/retention/store.js.map +1 -1
- package/dist/server/runtime/localHost.d.ts +10 -5
- package/dist/server/runtime/localHost.d.ts.map +1 -1
- package/dist/server/runtime/localHost.js +12 -8
- package/dist/server/runtime/localHost.js.map +1 -1
- package/dist/server/runtime/meterRollup.pg.test.js +1 -0
- package/dist/server/runtime/meterRollup.pg.test.js.map +1 -1
- package/dist/server/runtime/recipe.d.ts +4 -4
- package/dist/server/runtime/retentionJob.pg.test.js +1 -0
- package/dist/server/runtime/retentionJob.pg.test.js.map +1 -1
- package/dist/server/search/pgSearchProvider.pg.test.js +1 -0
- package/dist/server/search/pgSearchProvider.pg.test.js.map +1 -1
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.js +1 -1
- package/dist/styles-static.css +391 -0
- package/dist/styles.css +27 -0
- package/package.json +19 -167
- package/scripts/generateStaticStyles.ts +122 -0
- package/scripts/publicSurface.ts +455 -0
- package/scripts/testTmpRoot.ts +34 -5
- package/src/client/index.ts +1 -1
- package/src/core/category/index.ts +2 -1
- package/src/core/category/types.ts +1 -1
- package/src/core/file-tree/fileTreeModel.spec.ts +134 -0
- package/src/core/file-tree/fileTreeModel.ts +104 -0
- package/src/core/layout/index.ts +1 -1
- package/src/core/media/index.ts +1 -1
- package/src/core/media/types.ts +1 -1
- package/src/core/media/uploadLimits.ts +1 -1
- package/src/core/sharing/model.ts +1 -1
- package/src/ctgr/index.ts +1 -1
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/demoFixture.spec.ts +133 -0
- package/src/index.ts +1 -1
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +119 -0
- package/src/publicSurface.spec.ts +72 -0
- package/src/react/activity-bar/ActivityBar.tsx +17 -0
- package/src/react/analytics/index.ts +1 -1
- package/src/react/chat/MessageComposer.tsx +5 -4
- package/src/react/components/Button.tsx +3 -2
- package/src/react/components/Checkbox.spec.tsx +51 -0
- package/src/react/components/Checkbox.tsx +68 -26
- package/src/react/components/CodeArea.tsx +2 -1
- package/src/react/components/CodeChip.spec.tsx +2 -2
- package/src/react/components/Collapsible.tsx +2 -1
- package/src/react/components/ComboboxField.tsx +3 -2
- package/src/react/components/CopyButton.tsx +2 -1
- package/src/react/components/CopyableText.spec.tsx +1 -1
- package/src/react/components/DataTable/ColumnChecklist.tsx +2 -1
- package/src/react/components/DataTable/Pagination.tsx +2 -1
- package/src/react/components/DataTable/SortHeader.tsx +2 -1
- package/src/react/components/DataTable/{FilterBuilder.tsx → TableFilterBuilder.tsx} +33 -5
- package/src/react/components/DataTable/Toolbar.tsx +3 -3
- package/src/react/components/DataTable/cells/LinkCell.tsx +2 -1
- package/src/react/components/DataTable/index.ts +8 -1
- package/src/react/components/DragHandle.tsx +2 -1
- package/src/react/components/EmojiField.tsx +2 -1
- package/src/react/components/EventTimeline.tsx +3 -2
- package/src/react/components/FileLocation.spec.tsx +1 -1
- package/src/react/components/IconButton.tsx +1 -1
- package/src/react/components/InfoHint.tsx +5 -4
- package/src/react/components/InheritBadge.tsx +1 -1
- package/src/react/components/InlineEditableText.tsx +2 -1
- package/src/react/components/MeterList.tsx +2 -1
- package/src/react/components/PathText.tsx +4 -3
- package/src/react/components/RatingField.tsx +2 -1
- package/src/react/components/SecretInput.tsx +3 -3
- package/src/react/components/SegmentedControl.tsx +2 -2
- package/src/react/components/StarRating.spec.tsx +2 -2
- package/src/react/components/StarRating.tsx +9 -5
- package/src/react/components/StatStrip.tsx +2 -1
- package/src/react/components/StatTile.tsx +3 -3
- package/src/react/components/Tabs.spec.tsx +3 -2
- package/src/react/components/Tabs.tsx +2 -1
- package/src/react/components/TruncatedText.tsx +2 -1
- package/src/react/components/calendar/MonthGrid.spec.tsx +2 -1
- package/src/react/components/calendar/MonthGrid.tsx +2 -1
- package/src/react/components/calendar/WeekView.tsx +2 -1
- package/src/react/components/revealOnHover.ts +3 -2
- package/src/react/file-tree/FileTree.spec.tsx +248 -0
- package/src/react/file-tree/FileTree.tsx +304 -4
- package/src/react/file-tree/fileTree.css +52 -0
- package/src/react/file-tree/fileTreeGeometry.fixture.json +151 -0
- package/src/react/file-tree/fileTreeGeometry.spec.ts +33 -52
- package/src/react/filter-rail/FilterRail.spec.tsx +2 -1
- package/src/react/filter-rail/FilterRail.tsx +5 -4
- package/src/react/folder-tree/FolderTreeRail.tsx +2 -1
- package/src/react/index.ts +13 -3
- package/src/react/layoutEngine/index.ts +1 -1
- package/src/react/layoutEngine/types.ts +1 -1
- package/src/react/lib/field.ts +4 -2
- package/src/react/lib/floatingStyles.spec.ts +2 -1
- package/src/react/lib/floatingStyles.ts +3 -1
- package/src/react/lib/tapTarget.ts +100 -0
- package/src/react/media/MaskPainter.tsx +2 -2
- package/src/react/media/VideoTrimmer.tsx +1 -1
- package/src/react/media/denseGallery.css +3 -3
- package/src/react/media/ffmpegWasm.ts +2 -2
- package/src/react/media/videoPlayer.css +42 -0
- package/src/react/namespace/NamespaceTargetCapsule.tsx +2 -2
- package/src/react/nav/BreadcrumbTrail.tsx +4 -3
- package/src/react/nav/HubTileGrid.tsx +2 -1
- package/src/react/nav/SideNav.tsx +4 -3
- package/src/react/nav/SideNavCustomize.tsx +2 -1
- package/src/react/palette/CommandPalette.tsx +3 -2
- package/src/server/ctgr.ts +1 -1
- package/src/server/drivers/pooledControlStores.pg.test.ts +1 -0
- package/src/server/drivers/pooledIdentityStore.pg.test.ts +1 -0
- package/src/server/drivers/retentionPolicy.pg.test.ts +1 -0
- package/src/server/guard/argon2Binding.ts +1 -1
- package/src/server/incidents/store.ts +3 -3
- package/src/server/index.ts +1 -1
- package/src/server/master-lock/masterLock.spec.ts +18 -2
- package/src/server/master-lock/masterLock.ts +1 -1
- package/src/server/media-bun/image.spec.ts +1 -0
- package/src/server/media-bun/index.ts +1 -1
- package/src/server/media-bun/video.spec.ts +1 -0
- package/src/server/paths/appDataHome.spec.ts +87 -0
- package/src/server/paths/appDataHome.ts +84 -0
- package/src/server/retention/archive.spec.ts +26 -4
- package/src/server/retention/archive.ts +20 -15
- package/src/server/retention/index.ts +1 -1
- package/src/server/retention/policy.ts +2 -2
- package/src/server/retention/store.spec.ts +38 -10
- package/src/server/retention/store.ts +26 -19
- package/src/server/runtime/localHost.spec.ts +22 -3
- package/src/server/runtime/localHost.ts +12 -9
- package/src/server/runtime/meterRollup.pg.test.ts +1 -0
- package/src/server/runtime/recipe.ts +4 -4
- package/src/server/runtime/retentionJob.pg.test.ts +1 -0
- package/src/server/search/pgSearchProvider.pg.test.ts +1 -0
- package/src/server/storage/binaryServerProcessing.integration.spec.ts +6 -2
- package/src/server/storage/chunked.integration.spec.ts +5 -2
- package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +5 -2
- package/src/server/storage/files/adapter.photo.display.spec.ts +1 -0
- package/src/server/storage/imageThumbnail.spec.ts +1 -0
- package/src/server/storage/transcode.integration.spec.ts +5 -2
- package/src/server/storage/videoProcessor.posterOnly.spec.ts +1 -0
- package/src/shared/errors.ts +1 -1
- package/src/skippedTestsAreAnswered.spec.ts +215 -0
- package/src/styles-static.css +391 -0
- package/src/styles.css +27 -0
- package/src/stylesStaticMatches.spec.ts +134 -0
- package/src/tapTargetStylesheet.spec.ts +150 -0
- package/src/testTmpRoot.spec.ts +102 -0
- package/dist/core/duplicates/index.d.ts +0 -116
- package/dist/core/duplicates/index.d.ts.map +0 -1
- package/dist/core/duplicates/index.js +0 -178
- package/dist/core/duplicates/index.js.map +0 -1
- package/dist/core/namespace/helpers.d.ts +0 -14
- package/dist/core/namespace/helpers.d.ts.map +0 -1
- package/dist/core/namespace/helpers.js +0 -29
- package/dist/core/namespace/helpers.js.map +0 -1
- package/dist/core/namespace/index.d.ts +0 -4
- package/dist/core/namespace/index.d.ts.map +0 -1
- package/dist/core/namespace/index.js +0 -6
- package/dist/core/namespace/index.js.map +0 -1
- package/dist/core/namespace/memoryStore.d.ts +0 -19
- package/dist/core/namespace/memoryStore.d.ts.map +0 -1
- package/dist/core/namespace/memoryStore.js +0 -70
- package/dist/core/namespace/memoryStore.js.map +0 -1
- package/dist/core/namespace/types.d.ts +0 -44
- package/dist/core/namespace/types.d.ts.map +0 -1
- package/dist/core/namespace/types.js +0 -8
- package/dist/core/namespace/types.js.map +0 -1
- package/dist/game/core/DisposeRegistry.d.ts +0 -42
- package/dist/game/core/DisposeRegistry.d.ts.map +0 -1
- package/dist/game/core/DisposeRegistry.js +0 -67
- package/dist/game/core/DisposeRegistry.js.map +0 -1
- package/dist/game/core/drawBudget.d.ts +0 -26
- package/dist/game/core/drawBudget.d.ts.map +0 -1
- package/dist/game/core/drawBudget.js +0 -55
- package/dist/game/core/drawBudget.js.map +0 -1
- package/dist/game/core/fixedTimestep.d.ts +0 -38
- package/dist/game/core/fixedTimestep.d.ts.map +0 -1
- package/dist/game/core/fixedTimestep.js +0 -47
- package/dist/game/core/fixedTimestep.js.map +0 -1
- package/dist/game/core/index.d.ts +0 -9
- package/dist/game/core/index.d.ts.map +0 -1
- package/dist/game/core/index.js +0 -8
- package/dist/game/core/index.js.map +0 -1
- package/dist/game/core/inputCollector.d.ts +0 -82
- package/dist/game/core/inputCollector.d.ts.map +0 -1
- package/dist/game/core/inputCollector.js +0 -176
- package/dist/game/core/inputCollector.js.map +0 -1
- package/dist/game/core/resizeCanvas.d.ts +0 -19
- package/dist/game/core/resizeCanvas.d.ts.map +0 -1
- package/dist/game/core/resizeCanvas.js +0 -29
- package/dist/game/core/resizeCanvas.js.map +0 -1
- package/dist/game/react/index.d.ts +0 -3
- package/dist/game/react/index.d.ts.map +0 -1
- package/dist/game/react/index.js +0 -5
- package/dist/game/react/index.js.map +0 -1
- package/dist/game/react/useGameEngine.d.ts +0 -64
- package/dist/game/react/useGameEngine.d.ts.map +0 -1
- package/dist/game/react/useGameEngine.js +0 -78
- package/dist/game/react/useGameEngine.js.map +0 -1
- package/dist/react/canvas/CanvasNavHUD.d.ts +0 -64
- package/dist/react/canvas/CanvasNavHUD.d.ts.map +0 -1
- package/dist/react/canvas/CanvasNavHUD.js +0 -58
- package/dist/react/canvas/CanvasNavHUD.js.map +0 -1
- package/dist/react/canvas/fitLabel.d.ts +0 -53
- package/dist/react/canvas/fitLabel.d.ts.map +0 -1
- package/dist/react/canvas/fitLabel.js +0 -104
- package/dist/react/canvas/fitLabel.js.map +0 -1
- package/dist/react/canvas/index.d.ts +0 -4
- package/dist/react/canvas/index.d.ts.map +0 -1
- package/dist/react/canvas/index.js +0 -4
- package/dist/react/canvas/index.js.map +0 -1
- package/dist/react/canvas/lod.d.ts +0 -40
- package/dist/react/canvas/lod.d.ts.map +0 -1
- package/dist/react/canvas/lod.js +0 -37
- package/dist/react/canvas/lod.js.map +0 -1
- package/dist/react/components/DataTable/FilterBuilder.d.ts +0 -38
- package/dist/react/components/DataTable/FilterBuilder.d.ts.map +0 -1
- package/dist/react/components/DataTable/FilterBuilder.js.map +0 -1
- package/dist/react/seo/HeadProvider.d.ts +0 -14
- package/dist/react/seo/HeadProvider.d.ts.map +0 -1
- package/dist/react/seo/HeadProvider.js +0 -44
- package/dist/react/seo/HeadProvider.js.map +0 -1
- package/dist/react/seo/applyHeadToDocument.d.ts +0 -3
- package/dist/react/seo/applyHeadToDocument.d.ts.map +0 -1
- package/dist/react/seo/applyHeadToDocument.js +0 -66
- package/dist/react/seo/applyHeadToDocument.js.map +0 -1
- package/dist/react/seo/index.d.ts +0 -3
- package/dist/react/seo/index.d.ts.map +0 -1
- package/dist/react/seo/index.js +0 -7
- package/dist/react/seo/index.js.map +0 -1
- package/dist/react/timeline/ClipView.d.ts +0 -29
- package/dist/react/timeline/ClipView.d.ts.map +0 -1
- package/dist/react/timeline/ClipView.js +0 -53
- package/dist/react/timeline/ClipView.js.map +0 -1
- package/dist/react/timeline/Playhead.d.ts +0 -16
- package/dist/react/timeline/Playhead.d.ts.map +0 -1
- package/dist/react/timeline/Playhead.js +0 -26
- package/dist/react/timeline/Playhead.js.map +0 -1
- package/dist/react/timeline/TimeRuler.d.ts +0 -21
- package/dist/react/timeline/TimeRuler.d.ts.map +0 -1
- package/dist/react/timeline/TimeRuler.js +0 -31
- package/dist/react/timeline/TimeRuler.js.map +0 -1
- package/dist/react/timeline/TimelineSurface.d.ts +0 -29
- package/dist/react/timeline/TimelineSurface.d.ts.map +0 -1
- package/dist/react/timeline/TimelineSurface.js +0 -56
- package/dist/react/timeline/TimelineSurface.js.map +0 -1
- package/dist/react/timeline/TrackLane.d.ts +0 -31
- package/dist/react/timeline/TrackLane.d.ts.map +0 -1
- package/dist/react/timeline/TrackLane.js +0 -32
- package/dist/react/timeline/TrackLane.js.map +0 -1
- package/dist/react/timeline/index.d.ts +0 -6
- package/dist/react/timeline/index.d.ts.map +0 -1
- package/dist/react/timeline/index.js +0 -12
- package/dist/react/timeline/index.js.map +0 -1
- package/dist/server/private-media/index.d.ts +0 -7
- package/dist/server/private-media/index.d.ts.map +0 -1
- package/dist/server/private-media/index.js +0 -13
- package/dist/server/private-media/index.js.map +0 -1
- package/dist/server/seo/index.d.ts +0 -3
- package/dist/server/seo/index.d.ts.map +0 -1
- package/dist/server/seo/index.js +0 -6
- package/dist/server/seo/index.js.map +0 -1
- package/dist/server/seo/mountSeoRoutes.d.ts +0 -9
- package/dist/server/seo/mountSeoRoutes.d.ts.map +0 -1
- package/dist/server/seo/mountSeoRoutes.js +0 -19
- package/dist/server/seo/mountSeoRoutes.js.map +0 -1
- package/dist/server/seo/serveHeadInjectedIndex.d.ts +0 -9
- package/dist/server/seo/serveHeadInjectedIndex.d.ts.map +0 -1
- package/dist/server/seo/serveHeadInjectedIndex.js +0 -20
- package/dist/server/seo/serveHeadInjectedIndex.js.map +0 -1
- package/dist/upload.d.ts +0 -148
- package/dist/upload.d.ts.map +0 -1
- package/dist/upload.js +0 -159
- package/dist/upload.js.map +0 -1
- package/dist/verify/runtime/index.d.ts +0 -16
- package/dist/verify/runtime/index.d.ts.map +0 -1
- package/dist/verify/runtime/index.js +0 -16
- package/dist/verify/runtime/index.js.map +0 -1
- package/scripts/demoCheck.ts +0 -42
- package/src/core/duplicates/index.spec.ts +0 -116
- package/src/core/duplicates/index.ts +0 -203
- package/src/core/namespace/helpers.ts +0 -31
- package/src/core/namespace/index.ts +0 -12
- package/src/core/namespace/memoryStore.ts +0 -77
- package/src/core/namespace/namespace.spec.ts +0 -117
- package/src/core/namespace/types.ts +0 -53
- package/src/game/core/DisposeRegistry.spec.ts +0 -53
- package/src/game/core/DisposeRegistry.ts +0 -70
- package/src/game/core/drawBudget.spec.ts +0 -51
- package/src/game/core/drawBudget.ts +0 -75
- package/src/game/core/fixedTimestep.spec.ts +0 -91
- package/src/game/core/fixedTimestep.ts +0 -79
- package/src/game/core/index.ts +0 -16
- package/src/game/core/inputCollector.spec.ts +0 -29
- package/src/game/core/inputCollector.ts +0 -245
- package/src/game/core/resizeCanvas.ts +0 -31
- package/src/game/react/index.ts +0 -10
- package/src/game/react/useGameEngine.ts +0 -125
- package/src/react/canvas/CanvasNavHUD.spec.tsx +0 -62
- package/src/react/canvas/CanvasNavHUD.tsx +0 -210
- package/src/react/canvas/fitLabel.spec.ts +0 -82
- package/src/react/canvas/fitLabel.ts +0 -150
- package/src/react/canvas/index.ts +0 -15
- package/src/react/canvas/lod.ts +0 -53
- package/src/react/seo/HeadProvider.tsx +0 -67
- package/src/react/seo/applyHeadToDocument.ts +0 -73
- package/src/react/seo/index.ts +0 -12
- package/src/react/seo/seo.spec.tsx +0 -101
- package/src/react/timeline/ClipView.tsx +0 -146
- package/src/react/timeline/Playhead.tsx +0 -63
- package/src/react/timeline/TimeRuler.tsx +0 -85
- package/src/react/timeline/TimelineSurface.tsx +0 -157
- package/src/react/timeline/TrackLane.tsx +0 -87
- package/src/react/timeline/index.ts +0 -12
- package/src/react/timeline/timeline.spec.tsx +0 -175
- package/src/server/private-media/index.ts +0 -45
- package/src/server/seo/index.ts +0 -8
- package/src/server/seo/mountSeoRoutes.ts +0 -37
- package/src/server/seo/seo.spec.ts +0 -128
- package/src/server/seo/serveHeadInjectedIndex.ts +0 -36
- package/src/upload.spec.ts +0 -300
- package/src/upload.ts +0 -288
- package/src/verify/runtime/index.ts +0 -64
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
|
-
import { cleanup, render } from '@testing-library/react';
|
|
3
|
-
import type { HeadModel } from 'cwip/seo';
|
|
4
|
-
import { SEO_TAG_MARKER } from 'cwip/seo';
|
|
5
|
-
import { applyHeadToDocument } from './applyHeadToDocument';
|
|
6
|
-
import { HeadProvider, useHead } from './HeadProvider';
|
|
7
|
-
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
cleanup();
|
|
10
|
-
document.head.innerHTML = '';
|
|
11
|
-
document.title = '';
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe('applyHeadToDocument', () => {
|
|
15
|
-
test('sets document.title from a templated title', () => {
|
|
16
|
-
applyHeadToDocument({ title: 'Free monads', titleTemplate: '%s | Cursed Alchemy' });
|
|
17
|
-
expect(document.title).toBe('Free monads | Cursed Alchemy');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('upserts meta/link/jsonLd tags, all carrying the marker', () => {
|
|
21
|
-
const model: HeadModel = {
|
|
22
|
-
description: 'An article about free monads',
|
|
23
|
-
canonical: 'https://example.com/articles/free-monads',
|
|
24
|
-
openGraph: { title: 'Free monads', type: 'article' },
|
|
25
|
-
jsonLd: [{ '@type': 'Article', headline: 'Free monads' }],
|
|
26
|
-
};
|
|
27
|
-
applyHeadToDocument(model);
|
|
28
|
-
|
|
29
|
-
const description = document.head.querySelector('meta[name="description"]');
|
|
30
|
-
expect(description?.getAttribute('content')).toBe('An article about free monads');
|
|
31
|
-
expect(description?.hasAttribute(SEO_TAG_MARKER)).toBe(true);
|
|
32
|
-
|
|
33
|
-
const canonical = document.head.querySelector('link[rel="canonical"]');
|
|
34
|
-
expect(canonical?.getAttribute('href')).toBe('https://example.com/articles/free-monads');
|
|
35
|
-
|
|
36
|
-
const ogTitle = document.head.querySelector('meta[property="og:title"]');
|
|
37
|
-
expect(ogTitle?.getAttribute('content')).toBe('Free monads');
|
|
38
|
-
|
|
39
|
-
const script = document.head.querySelector('script[type="application/ld+json"]');
|
|
40
|
-
expect(script).not.toBeNull();
|
|
41
|
-
expect(JSON.parse(script?.textContent ?? '{}')).toEqual({
|
|
42
|
-
'@type': 'Article',
|
|
43
|
-
headline: 'Free monads',
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('re-applying the same model twice is idempotent (no duplicate tags)', () => {
|
|
48
|
-
const model: HeadModel = {
|
|
49
|
-
description: 'same description',
|
|
50
|
-
canonical: 'https://example.com/a',
|
|
51
|
-
};
|
|
52
|
-
applyHeadToDocument(model);
|
|
53
|
-
applyHeadToDocument(model);
|
|
54
|
-
expect(document.head.querySelectorAll('meta[name="description"]').length).toBe(1);
|
|
55
|
-
expect(document.head.querySelectorAll('link[rel="canonical"]').length).toBe(1);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('removes a previously-managed tag that is absent from the new model', () => {
|
|
59
|
-
applyHeadToDocument({ description: 'first', canonical: 'https://example.com/a' });
|
|
60
|
-
expect(document.head.querySelector('link[rel="canonical"]')).not.toBeNull();
|
|
61
|
-
|
|
62
|
-
applyHeadToDocument({ description: 'second' });
|
|
63
|
-
expect(document.head.querySelector('meta[name="description"]')?.getAttribute('content')).toBe(
|
|
64
|
-
'second',
|
|
65
|
-
);
|
|
66
|
-
expect(document.head.querySelector('link[rel="canonical"]')).toBeNull();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('never touches an app-authored (unmarked) tag', () => {
|
|
70
|
-
const appMeta = document.createElement('meta');
|
|
71
|
-
appMeta.setAttribute('name', 'viewport');
|
|
72
|
-
appMeta.setAttribute('content', 'width=device-width, initial-scale=1');
|
|
73
|
-
document.head.appendChild(appMeta);
|
|
74
|
-
|
|
75
|
-
applyHeadToDocument({ description: 'managed description' });
|
|
76
|
-
|
|
77
|
-
const viewport = document.head.querySelector('meta[name="viewport"]');
|
|
78
|
-
expect(viewport).toBe(appMeta);
|
|
79
|
-
expect(viewport?.hasAttribute(SEO_TAG_MARKER)).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('useHead', () => {
|
|
84
|
-
const Route = ({ model }: { model: HeadModel }) => {
|
|
85
|
-
useHead(model);
|
|
86
|
-
return null;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
test('applies the merged provider default + route model, route wins on overlap', async () => {
|
|
90
|
-
render(
|
|
91
|
-
<HeadProvider default={{ description: 'default description', titleTemplate: '%s | Site' }}>
|
|
92
|
-
<Route model={{ title: 'Route title', description: 'route description' }} />
|
|
93
|
-
</HeadProvider>,
|
|
94
|
-
);
|
|
95
|
-
// effects flush synchronously under @testing-library/react's act()
|
|
96
|
-
expect(document.title).toBe('Route title | Site');
|
|
97
|
-
expect(document.head.querySelector('meta[name="description"]')?.getAttribute('content')).toBe(
|
|
98
|
-
'route description',
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { clipLeftPx, clipWidthPx, pxToSeconds, type TimelineClip } from 'cwip/timeline';
|
|
2
|
-
import { Zap } from 'lucide-react';
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
import { useRef } from 'react';
|
|
5
|
-
import { formatTimecode } from '../../core/media';
|
|
6
|
-
import { usePointerDrag, type UsePointerDragOptions } from '../hooks/usePointerDrag';
|
|
7
|
-
import { cn } from '../lib/cn';
|
|
8
|
-
|
|
9
|
-
export type TrimEdge = 'start' | 'end';
|
|
10
|
-
|
|
11
|
-
export interface ClipViewProps {
|
|
12
|
-
clip: TimelineClip;
|
|
13
|
-
zoomScale: number;
|
|
14
|
-
selected: boolean;
|
|
15
|
-
onSelect: () => void;
|
|
16
|
-
/** Live preview during a body drag (raw target start, seconds). */
|
|
17
|
-
onMovePreview: (toStartSeconds: number, event: PointerEvent) => void;
|
|
18
|
-
onMoveCommit: (toStartSeconds: number, cancelled: boolean) => void;
|
|
19
|
-
/** Live preview during an edge drag (raw signed delta, seconds). */
|
|
20
|
-
onTrimPreview: (edge: TrimEdge, deltaSeconds: number, event: PointerEvent) => void;
|
|
21
|
-
onTrimCommit: (edge: TrimEdge, deltaSeconds: number, cancelled: boolean) => void;
|
|
22
|
-
/** Fallback color token when the clip carries none (a theme token name,
|
|
23
|
-
* resolved as `var(--<token>)` — e.g. "chart-1"). */
|
|
24
|
-
defaultColorToken?: string;
|
|
25
|
-
/** Extra content under the label (waveform strip, filmstrip). */
|
|
26
|
-
children?: ReactNode;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* One clip block: body drag repositions, edge handles trim, click selects.
|
|
31
|
-
* PRESENTATIONAL — it translates pixels to seconds and reports intent; all
|
|
32
|
-
* clamping/rippling/snapping is the owner's job (cwip/timeline ops), so the
|
|
33
|
-
* block never disagrees with the engine. Colors ride theme tokens via
|
|
34
|
-
* `color-mix`, never raw values.
|
|
35
|
-
*/
|
|
36
|
-
export function ClipView({
|
|
37
|
-
clip,
|
|
38
|
-
zoomScale,
|
|
39
|
-
selected,
|
|
40
|
-
onSelect,
|
|
41
|
-
onMovePreview,
|
|
42
|
-
onMoveCommit,
|
|
43
|
-
onTrimPreview,
|
|
44
|
-
onTrimCommit,
|
|
45
|
-
defaultColorToken = 'chart-1',
|
|
46
|
-
children,
|
|
47
|
-
}: ClipViewProps) {
|
|
48
|
-
const token = clip.colorToken ?? defaultColorToken;
|
|
49
|
-
const originStart = useRef(0);
|
|
50
|
-
|
|
51
|
-
const bodyDrag = usePointerDrag({
|
|
52
|
-
onStart: () => {
|
|
53
|
-
originStart.current = clip.timelineStart;
|
|
54
|
-
onSelect();
|
|
55
|
-
},
|
|
56
|
-
onMove: ({ dx, event }) =>
|
|
57
|
-
onMovePreview(Math.max(0, originStart.current + pxToSeconds(dx, zoomScale)), event),
|
|
58
|
-
onEnd: ({ dx, cancelled }) =>
|
|
59
|
-
onMoveCommit(Math.max(0, originStart.current + pxToSeconds(dx, zoomScale)), cancelled),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Both edge handles share one drag recipe, differing only by which edge they trim.
|
|
63
|
-
// `usePointerDrag` is a hook, so it's called directly at the top level (never inside a
|
|
64
|
-
// helper/closure — Rules of Hooks); `edgeDragOptions` builds the config, not the hook.
|
|
65
|
-
const edgeDragOptions = (edge: TrimEdge): UsePointerDragOptions => ({
|
|
66
|
-
onStart: () => onSelect(),
|
|
67
|
-
onMove: ({ dx, event }) => onTrimPreview(edge, pxToSeconds(dx, zoomScale), event),
|
|
68
|
-
onEnd: ({ dx, cancelled }) => onTrimCommit(edge, pxToSeconds(dx, zoomScale), cancelled),
|
|
69
|
-
});
|
|
70
|
-
const startDrag = usePointerDrag(edgeDragOptions('start'));
|
|
71
|
-
const endDrag = usePointerDrag(edgeDragOptions('end'));
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<button
|
|
75
|
-
// guardrails-ignore no-raw-action-button: a draggable/trimmable timeline-canvas
|
|
76
|
-
// primitive (positioned, pointer-drag-driven, custom color-mix styling via
|
|
77
|
-
// `style`) — not a generic action trigger, so it can't route through the
|
|
78
|
-
// Button/Tooltip abstraction without breaking the drag/trim contract.
|
|
79
|
-
type='button'
|
|
80
|
-
{...bodyDrag.handleProps}
|
|
81
|
-
onClick={(e) => {
|
|
82
|
-
e.stopPropagation(); // the lane behind also listens (deselect/seek)
|
|
83
|
-
onSelect();
|
|
84
|
-
}}
|
|
85
|
-
aria-label={`Clip ${clip.name}`}
|
|
86
|
-
aria-pressed={selected}
|
|
87
|
-
className={cn(
|
|
88
|
-
'group absolute inset-y-1 z-10 flex flex-col overflow-hidden rounded-md border p-0 text-left',
|
|
89
|
-
'cursor-grab appearance-none active:cursor-grabbing',
|
|
90
|
-
selected && 'ring-2 ring-accent-solid ring-offset-1 ring-offset-background',
|
|
91
|
-
)}
|
|
92
|
-
style={{
|
|
93
|
-
...bodyDrag.handleProps.style,
|
|
94
|
-
left: clipLeftPx(clip, zoomScale),
|
|
95
|
-
// 2px breathing room so butted clips stay visually distinct at high zoom
|
|
96
|
-
width: Math.max(8, clipWidthPx(clip, zoomScale) - 2),
|
|
97
|
-
borderColor: `var(--${token})`,
|
|
98
|
-
background: `color-mix(in srgb, var(--${token}) 30%, var(--background))`,
|
|
99
|
-
}}
|
|
100
|
-
>
|
|
101
|
-
{/* Media (filmstrip / waveform) fills the whole clip — labels ride ON TOP
|
|
102
|
-
as overlays so thumbnails read as pictures, not a sliver under a bar. */}
|
|
103
|
-
{children && <div className='pointer-events-none absolute inset-0'>{children}</div>}
|
|
104
|
-
<div className='pointer-events-none relative z-1 flex items-center gap-1 px-1.5 pt-0.5'>
|
|
105
|
-
<span className='truncate rounded bg-background/70 px-1 text-caption font-medium text-foreground'>
|
|
106
|
-
{clip.name}
|
|
107
|
-
</span>
|
|
108
|
-
{clip.speed !== 1 && (
|
|
109
|
-
<span className='inline-flex shrink-0 items-center gap-0.5 rounded bg-background/80 px-1 text-caption text-foreground'>
|
|
110
|
-
<Zap aria-hidden className='size-2.5' />
|
|
111
|
-
{clip.speed}x
|
|
112
|
-
</span>
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
<div className='pointer-events-none relative z-1 mt-auto w-fit rounded-tr bg-background/60 px-1.5 text-caption text-muted-foreground'>
|
|
116
|
-
{formatTimecode(clip.trackIn)}–{formatTimecode(clip.trackOut)}
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
{/* Trim handles — visible on hover/selection; hit area wider than the visual bar. */}
|
|
120
|
-
<div
|
|
121
|
-
{...startDrag.handleProps}
|
|
122
|
-
className={cn(
|
|
123
|
-
'absolute inset-y-0 -left-1 z-20 flex w-4 cursor-ew-resize items-stretch justify-start',
|
|
124
|
-
'opacity-0 transition-opacity group-hover:opacity-100',
|
|
125
|
-
selected && 'opacity-100',
|
|
126
|
-
)}
|
|
127
|
-
style={startDrag.handleProps.style}
|
|
128
|
-
aria-hidden
|
|
129
|
-
>
|
|
130
|
-
<div className='ml-1 w-1.5 rounded-sm bg-foreground/40' />
|
|
131
|
-
</div>
|
|
132
|
-
<div
|
|
133
|
-
{...endDrag.handleProps}
|
|
134
|
-
className={cn(
|
|
135
|
-
'absolute inset-y-0 -right-1 z-20 flex w-4 cursor-ew-resize items-stretch justify-end',
|
|
136
|
-
'opacity-0 transition-opacity group-hover:opacity-100',
|
|
137
|
-
selected && 'opacity-100',
|
|
138
|
-
)}
|
|
139
|
-
style={endDrag.handleProps.style}
|
|
140
|
-
aria-hidden
|
|
141
|
-
>
|
|
142
|
-
<div className='mr-1 w-1.5 rounded-sm bg-foreground/40' />
|
|
143
|
-
</div>
|
|
144
|
-
</button>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { pxToSeconds, secondsToPx } from 'cwip/timeline';
|
|
2
|
-
import { useRef } from 'react';
|
|
3
|
-
import { usePointerDrag } from '../hooks/usePointerDrag';
|
|
4
|
-
|
|
5
|
-
export interface PlayheadProps {
|
|
6
|
-
timeSeconds: number;
|
|
7
|
-
zoomScale: number;
|
|
8
|
-
/** Drag the head to scrub; omit for a display-only playhead. */
|
|
9
|
-
onScrub?: (seconds: number) => void;
|
|
10
|
-
onScrubStart?: () => void;
|
|
11
|
-
onScrubEnd?: () => void;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The vertical playhead overlay (line + grab knob). Positioned absolutely in a
|
|
16
|
-
* `relative` container spanning the timeline's full scroll width. The knob is
|
|
17
|
-
* the drag target; the line itself stays click-through so clips underneath
|
|
18
|
-
* remain selectable.
|
|
19
|
-
*/
|
|
20
|
-
export function Playhead({
|
|
21
|
-
timeSeconds,
|
|
22
|
-
zoomScale,
|
|
23
|
-
onScrub,
|
|
24
|
-
onScrubStart,
|
|
25
|
-
onScrubEnd,
|
|
26
|
-
}: PlayheadProps) {
|
|
27
|
-
const originSeconds = useRef(0);
|
|
28
|
-
const drag = usePointerDrag({
|
|
29
|
-
onStart: () => {
|
|
30
|
-
originSeconds.current = timeSeconds;
|
|
31
|
-
onScrubStart?.();
|
|
32
|
-
},
|
|
33
|
-
onMove: ({ dx }) => {
|
|
34
|
-
onScrub?.(Math.max(0, originSeconds.current + pxToSeconds(dx, zoomScale)));
|
|
35
|
-
},
|
|
36
|
-
onEnd: () => onScrubEnd?.(),
|
|
37
|
-
disabled: !onScrub,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<div
|
|
42
|
-
className='pointer-events-none absolute inset-y-0 z-20'
|
|
43
|
-
style={{ left: secondsToPx(timeSeconds, zoomScale) }}
|
|
44
|
-
>
|
|
45
|
-
<div className='absolute inset-y-0 w-px bg-destructive' />
|
|
46
|
-
{onScrub && (
|
|
47
|
-
<div
|
|
48
|
-
{...drag.handleProps}
|
|
49
|
-
role='slider'
|
|
50
|
-
aria-label='Playhead'
|
|
51
|
-
aria-valuemin={0}
|
|
52
|
-
aria-valuemax={Number.MAX_SAFE_INTEGER}
|
|
53
|
-
aria-valuenow={Math.round(timeSeconds)}
|
|
54
|
-
tabIndex={0}
|
|
55
|
-
className='pointer-events-auto absolute -top-1 -left-[11px] flex h-6 w-[23px] cursor-ew-resize items-start justify-center'
|
|
56
|
-
style={drag.handleProps.style}
|
|
57
|
-
>
|
|
58
|
-
<div className='h-3 w-3 rounded-full border border-background bg-destructive shadow' />
|
|
59
|
-
</div>
|
|
60
|
-
)}
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { pxToSeconds, rulerTicks, secondsToPx } from 'cwip/timeline';
|
|
2
|
-
import { useRef } from 'react';
|
|
3
|
-
import { formatTimecode } from '../../core/media';
|
|
4
|
-
import { usePointerDrag } from '../hooks/usePointerDrag';
|
|
5
|
-
import { cn } from '../lib/cn';
|
|
6
|
-
|
|
7
|
-
export interface TimeRulerProps {
|
|
8
|
-
/** Ruler length (seconds) — usually total duration + some tail slack. */
|
|
9
|
-
durationSeconds: number;
|
|
10
|
-
zoomScale: number;
|
|
11
|
-
/** Click/drag anywhere on the ruler seeks (a scrub). */
|
|
12
|
-
onScrub: (seconds: number) => void;
|
|
13
|
-
/** Fires on pointer-down / pointer-up of a ruler scrub so the owner can
|
|
14
|
-
* suppress live playback while the gesture owns the playhead (parity with the
|
|
15
|
-
* draggable playhead knob). */
|
|
16
|
-
onScrubStart?: () => void;
|
|
17
|
-
onScrubEnd?: () => void;
|
|
18
|
-
/** Current playhead time — announced as the slider value. */
|
|
19
|
-
currentTimeSeconds?: number;
|
|
20
|
-
className?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The clickable time ruler. Tick density self-adjusts with zoom (labels never
|
|
25
|
-
* crowd below ~60px apart); pointer down + drag scrubs continuously.
|
|
26
|
-
*/
|
|
27
|
-
export function TimeRuler({
|
|
28
|
-
durationSeconds,
|
|
29
|
-
zoomScale,
|
|
30
|
-
onScrub,
|
|
31
|
-
onScrubStart,
|
|
32
|
-
onScrubEnd,
|
|
33
|
-
currentTimeSeconds = 0,
|
|
34
|
-
className,
|
|
35
|
-
}: TimeRulerProps) {
|
|
36
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
37
|
-
|
|
38
|
-
const seekAtClientX = (clientX: number) => {
|
|
39
|
-
const rect = ref.current?.getBoundingClientRect();
|
|
40
|
-
if (!rect) return;
|
|
41
|
-
const seconds = pxToSeconds(clientX - rect.left, zoomScale);
|
|
42
|
-
onScrub(Math.min(durationSeconds, Math.max(0, seconds)));
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const drag = usePointerDrag({
|
|
46
|
-
onStart: (e) => {
|
|
47
|
-
onScrubStart?.();
|
|
48
|
-
seekAtClientX(e.clientX);
|
|
49
|
-
},
|
|
50
|
-
onMove: ({ event }) => seekAtClientX(event.clientX),
|
|
51
|
-
onEnd: () => onScrubEnd?.(),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const ticks = rulerTicks(durationSeconds, zoomScale);
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<div
|
|
58
|
-
ref={ref}
|
|
59
|
-
{...drag.handleProps}
|
|
60
|
-
className={cn(
|
|
61
|
-
'relative h-7 min-h-7 cursor-ew-resize select-none border-b border-border bg-muted/40',
|
|
62
|
-
className,
|
|
63
|
-
)}
|
|
64
|
-
style={{ ...drag.handleProps.style, width: secondsToPx(durationSeconds, zoomScale) }}
|
|
65
|
-
role='slider'
|
|
66
|
-
aria-label='Timeline position'
|
|
67
|
-
aria-valuemin={0}
|
|
68
|
-
aria-valuemax={Math.round(durationSeconds)}
|
|
69
|
-
aria-valuenow={Math.min(Math.round(durationSeconds), Math.round(currentTimeSeconds))}
|
|
70
|
-
tabIndex={0}
|
|
71
|
-
>
|
|
72
|
-
{ticks.map((t) => (
|
|
73
|
-
<div
|
|
74
|
-
key={t}
|
|
75
|
-
className='absolute top-0 h-full border-l border-border/70'
|
|
76
|
-
style={{ left: secondsToPx(t, zoomScale) }}
|
|
77
|
-
>
|
|
78
|
-
<span className='pl-1 text-caption leading-7 text-muted-foreground'>
|
|
79
|
-
{formatTimecode(t)}
|
|
80
|
-
</span>
|
|
81
|
-
</div>
|
|
82
|
-
))}
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
clampZoom,
|
|
3
|
-
secondsToPx,
|
|
4
|
-
type TimelineTrack,
|
|
5
|
-
totalDuration,
|
|
6
|
-
ZOOM_RANGE,
|
|
7
|
-
zoomAnchoredScrollLeft,
|
|
8
|
-
} from 'cwip/timeline';
|
|
9
|
-
import { Minus, Plus } from 'lucide-react';
|
|
10
|
-
import { type ReactNode, useEffect, useRef } from 'react';
|
|
11
|
-
import { IconButton } from '../components/IconButton';
|
|
12
|
-
import { ScrollArea } from '../components/ScrollArea';
|
|
13
|
-
import { Slider } from '../components/Slider';
|
|
14
|
-
import { Tooltip } from '../components/Tooltip';
|
|
15
|
-
import { cn } from '../lib/cn';
|
|
16
|
-
import { Playhead } from './Playhead';
|
|
17
|
-
import { TimeRuler } from './TimeRuler';
|
|
18
|
-
|
|
19
|
-
export interface TimelineSurfaceProps {
|
|
20
|
-
tracks: TimelineTrack[];
|
|
21
|
-
zoomScale: number;
|
|
22
|
-
currentTime: number;
|
|
23
|
-
onScrub: (seconds: number) => void;
|
|
24
|
-
onScrubStart?: () => void;
|
|
25
|
-
onScrubEnd?: () => void;
|
|
26
|
-
/** Zoom changes arrive pre-clamped; scroll is re-anchored on the playhead. */
|
|
27
|
-
onZoomChange?: (zoomScale: number) => void;
|
|
28
|
-
/** Renders each track's lane (TrackLane + ClipViews) — the owner wires ops. */
|
|
29
|
-
renderTrack: (track: TimelineTrack, durationSeconds: number) => ReactNode;
|
|
30
|
-
/** Extra tail beyond the last clip so there's room to drop/extend (default 10s). */
|
|
31
|
-
tailSeconds?: number;
|
|
32
|
-
/** Auto-scroll so the playhead stays in view as `currentTime` advances (e.g. during
|
|
33
|
-
* playback) — once it nears the viewport edge, the view pages forward to keep it
|
|
34
|
-
* visible, rather than scrolling every frame. */
|
|
35
|
-
followPlayhead?: boolean;
|
|
36
|
-
className?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* The composed timeline scroller: ruler + track lanes + playhead in one
|
|
41
|
-
* horizontally-scrolling keyboard-inset-safe surface (ScrollArea — no raw
|
|
42
|
-
* overflow), with zoom controls that keep the playhead visually anchored.
|
|
43
|
-
* Fully controlled; owns NO timeline state.
|
|
44
|
-
*/
|
|
45
|
-
export function TimelineSurface({
|
|
46
|
-
tracks,
|
|
47
|
-
zoomScale,
|
|
48
|
-
currentTime,
|
|
49
|
-
onScrub,
|
|
50
|
-
onScrubStart,
|
|
51
|
-
onScrubEnd,
|
|
52
|
-
onZoomChange,
|
|
53
|
-
renderTrack,
|
|
54
|
-
tailSeconds = 10,
|
|
55
|
-
followPlayhead = false,
|
|
56
|
-
className,
|
|
57
|
-
}: TimelineSurfaceProps) {
|
|
58
|
-
const scrollRef = useRef<HTMLDivElement | null>(null);
|
|
59
|
-
const durationSeconds = totalDuration(tracks) + tailSeconds;
|
|
60
|
-
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (!followPlayhead) return;
|
|
63
|
-
const el = scrollRef.current;
|
|
64
|
-
if (!el) return;
|
|
65
|
-
const playheadPx = secondsToPx(currentTime, zoomScale);
|
|
66
|
-
const viewportX = playheadPx - el.scrollLeft;
|
|
67
|
-
// Only page the view forward/back once the playhead nears an edge — scrolling every
|
|
68
|
-
// frame during playback would fight the user's ability to glance elsewhere.
|
|
69
|
-
const margin = el.clientWidth * 0.1;
|
|
70
|
-
if (viewportX < margin || viewportX > el.clientWidth - margin) {
|
|
71
|
-
el.scrollLeft = Math.max(0, playheadPx - el.clientWidth * 0.2);
|
|
72
|
-
}
|
|
73
|
-
}, [followPlayhead, currentTime, zoomScale]);
|
|
74
|
-
|
|
75
|
-
const changeZoom = (next: number) => {
|
|
76
|
-
if (!onZoomChange) return;
|
|
77
|
-
const clamped = clampZoom(next);
|
|
78
|
-
const el = scrollRef.current;
|
|
79
|
-
if (el) {
|
|
80
|
-
const anchorViewportPx = secondsToPx(currentTime, zoomScale) - el.scrollLeft;
|
|
81
|
-
// after the re-render at the new zoom, restore the playhead's viewport x
|
|
82
|
-
requestAnimationFrame(() => {
|
|
83
|
-
el.scrollLeft = zoomAnchoredScrollLeft(currentTime, anchorViewportPx, clamped);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
onZoomChange(clamped);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<div className={cn('flex flex-col', className)}>
|
|
91
|
-
{onZoomChange && (
|
|
92
|
-
<div className='flex items-center justify-end gap-2 border-b border-border px-2 py-1'>
|
|
93
|
-
<Tooltip content='Zoom out'>
|
|
94
|
-
<IconButton
|
|
95
|
-
label='Zoom out'
|
|
96
|
-
onClick={() => changeZoom(zoomScale / 1.4)}
|
|
97
|
-
disabled={zoomScale <= ZOOM_RANGE.min}
|
|
98
|
-
>
|
|
99
|
-
<Minus aria-hidden className='size-4' />
|
|
100
|
-
</IconButton>
|
|
101
|
-
</Tooltip>
|
|
102
|
-
<Slider
|
|
103
|
-
value={zoomScale}
|
|
104
|
-
min={ZOOM_RANGE.min}
|
|
105
|
-
max={ZOOM_RANGE.max}
|
|
106
|
-
// A coarse step of 1 would swallow the whole low end (0.05–1) in a single
|
|
107
|
-
// increment, making the zoomed-out-enough-to-fit-a-long-clip range
|
|
108
|
-
// unreachable by dragging — 0.5 keeps that end usable too.
|
|
109
|
-
step={0.5}
|
|
110
|
-
aria-label='Timeline zoom'
|
|
111
|
-
className='w-24'
|
|
112
|
-
onChange={changeZoom}
|
|
113
|
-
/>
|
|
114
|
-
<span className='w-14 text-center text-caption tabular-nums text-muted-foreground'>
|
|
115
|
-
{Math.round(zoomScale)} px/s
|
|
116
|
-
</span>
|
|
117
|
-
<Tooltip content='Zoom in'>
|
|
118
|
-
<IconButton
|
|
119
|
-
label='Zoom in'
|
|
120
|
-
onClick={() => changeZoom(zoomScale * 1.4)}
|
|
121
|
-
disabled={zoomScale >= ZOOM_RANGE.max}
|
|
122
|
-
>
|
|
123
|
-
<Plus aria-hidden className='size-4' />
|
|
124
|
-
</IconButton>
|
|
125
|
-
</Tooltip>
|
|
126
|
-
</div>
|
|
127
|
-
)}
|
|
128
|
-
<ScrollArea axis='x' space='none' scrollRef={scrollRef} className='grow'>
|
|
129
|
-
<div className='relative w-max min-w-full'>
|
|
130
|
-
{/* ruler is offset by the sticky lane-header width so times align */}
|
|
131
|
-
<div className='ml-28'>
|
|
132
|
-
<TimeRuler
|
|
133
|
-
durationSeconds={durationSeconds}
|
|
134
|
-
zoomScale={zoomScale}
|
|
135
|
-
currentTimeSeconds={currentTime}
|
|
136
|
-
onScrub={onScrub}
|
|
137
|
-
onScrubStart={onScrubStart}
|
|
138
|
-
onScrubEnd={onScrubEnd}
|
|
139
|
-
/>
|
|
140
|
-
</div>
|
|
141
|
-
{tracks.map((track) => renderTrack(track, durationSeconds))}
|
|
142
|
-
<div className='pointer-events-none absolute inset-y-0 left-28 right-0'>
|
|
143
|
-
<div className='relative h-full'>
|
|
144
|
-
<Playhead
|
|
145
|
-
timeSeconds={currentTime}
|
|
146
|
-
zoomScale={zoomScale}
|
|
147
|
-
onScrub={onScrub}
|
|
148
|
-
onScrubStart={onScrubStart}
|
|
149
|
-
onScrubEnd={onScrubEnd}
|
|
150
|
-
/>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
</ScrollArea>
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { pxToSeconds, secondsToPx, type TimelineTrack } from 'cwip/timeline';
|
|
2
|
-
import { AudioLines, Film } from 'lucide-react';
|
|
3
|
-
import type { DragEvent, ReactNode } from 'react';
|
|
4
|
-
import { useRef } from 'react';
|
|
5
|
-
import { cn } from '../lib/cn';
|
|
6
|
-
|
|
7
|
-
export const TRACK_LANE_HEIGHT_PX = 108;
|
|
8
|
-
|
|
9
|
-
export interface TrackLaneProps {
|
|
10
|
-
track: TimelineTrack;
|
|
11
|
-
zoomScale: number;
|
|
12
|
-
/** Lane runway width (seconds) — the surface passes one shared duration. */
|
|
13
|
-
durationSeconds: number;
|
|
14
|
-
/** Rendered `ClipView`s (the owner maps clips so it controls the wiring). */
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
/** Lane height in px (default {@link TRACK_LANE_HEIGHT_PX}). Owners can shrink
|
|
17
|
-
* a rarely-used lane (e.g. an empty audio track) and grow it once it holds a
|
|
18
|
-
* clip, so unused lanes don't waste vertical space. */
|
|
19
|
-
laneHeightPx?: number;
|
|
20
|
-
/** Click on empty runway — typically seek + deselect. */
|
|
21
|
-
onRunwayClick?: (seconds: number) => void;
|
|
22
|
-
/** A `dataTransfer` drop on the runway (asset from a library/palette). */
|
|
23
|
-
onDropData?: (payload: { atSeconds: number; dataTransfer: DataTransfer }) => void;
|
|
24
|
-
className?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* One track row: a sticky header (name + kind icon) and the clip runway. The
|
|
29
|
-
* runway is the `relative` positioning context `ClipView`s place themselves
|
|
30
|
-
* in, and the drop target for library assets (drop position is reported in
|
|
31
|
-
* SECONDS, scroll offset already accounted for via the runway's own rect).
|
|
32
|
-
*/
|
|
33
|
-
export function TrackLane({
|
|
34
|
-
track,
|
|
35
|
-
zoomScale,
|
|
36
|
-
durationSeconds,
|
|
37
|
-
children,
|
|
38
|
-
laneHeightPx = TRACK_LANE_HEIGHT_PX,
|
|
39
|
-
onRunwayClick,
|
|
40
|
-
onDropData,
|
|
41
|
-
className,
|
|
42
|
-
}: TrackLaneProps) {
|
|
43
|
-
const runwayRef = useRef<HTMLDivElement>(null);
|
|
44
|
-
|
|
45
|
-
const secondsAt = (clientX: number): number => {
|
|
46
|
-
const rect = runwayRef.current?.getBoundingClientRect();
|
|
47
|
-
if (!rect) return 0;
|
|
48
|
-
return Math.max(0, pxToSeconds(clientX - rect.left, zoomScale));
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const onDrop = (e: DragEvent) => {
|
|
52
|
-
if (!onDropData) return;
|
|
53
|
-
e.preventDefault();
|
|
54
|
-
onDropData({ atSeconds: secondsAt(e.clientX), dataTransfer: e.dataTransfer });
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div
|
|
59
|
-
className={cn('flex border-b border-border/60 transition-[min-height]', className)}
|
|
60
|
-
style={{ minHeight: laneHeightPx }}
|
|
61
|
-
>
|
|
62
|
-
<div className='sticky left-0 z-30 flex w-28 shrink-0 items-center gap-1.5 border-r border-border bg-muted/60 px-2 backdrop-blur-sm'>
|
|
63
|
-
{track.type === 'video' ? (
|
|
64
|
-
<Film aria-hidden className='size-3.5 text-muted-foreground' />
|
|
65
|
-
) : (
|
|
66
|
-
<AudioLines aria-hidden className='size-3.5 text-muted-foreground' />
|
|
67
|
-
)}
|
|
68
|
-
<span className='truncate text-xs text-foreground'>{track.name}</span>
|
|
69
|
-
</div>
|
|
70
|
-
{/* biome-ignore lint/a11y/useKeyWithClickEvents: seek-on-click pointer convenience — the ruler is the accessible seek control */}
|
|
71
|
-
{/* biome-ignore lint/a11y/noStaticElementInteractions: same — decorative runway, real controls live on the clips/ruler */}
|
|
72
|
-
<div
|
|
73
|
-
ref={runwayRef}
|
|
74
|
-
className='relative'
|
|
75
|
-
style={{ width: secondsToPx(durationSeconds, zoomScale) }}
|
|
76
|
-
onClick={(e) => onRunwayClick?.(secondsAt(e.clientX))}
|
|
77
|
-
onDragOver={(e) => {
|
|
78
|
-
if (onDropData) e.preventDefault(); // required for `drop` to fire
|
|
79
|
-
}}
|
|
80
|
-
onDrop={onDrop}
|
|
81
|
-
data-track-id={track.id}
|
|
82
|
-
>
|
|
83
|
-
{children}
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// cursedbelt/react/timeline — the generic multi-track timeline editor surface.
|
|
2
|
-
// Controlled/presentational components over the cwip/timeline engine: they
|
|
3
|
-
// translate pixels to seconds and report INTENT (select/move/trim/scrub); the
|
|
4
|
-
// owner applies cwip ops (clamping, ripple, snapping) and passes state back
|
|
5
|
-
// down. Reusable for any lane-based editor (video NLE, audio arrangement,
|
|
6
|
-
// schedule lanes) — the cursedshell video-editor plugin is the reference user.
|
|
7
|
-
|
|
8
|
-
export { ClipView, type ClipViewProps, type TrimEdge } from './ClipView';
|
|
9
|
-
export { Playhead, type PlayheadProps } from './Playhead';
|
|
10
|
-
export { TimelineSurface, type TimelineSurfaceProps } from './TimelineSurface';
|
|
11
|
-
export { TimeRuler, type TimeRulerProps } from './TimeRuler';
|
|
12
|
-
export { TRACK_LANE_HEIGHT_PX, TrackLane, type TrackLaneProps } from './TrackLane';
|