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
|
@@ -14,6 +14,37 @@
|
|
|
14
14
|
* {@link FileTreeProps.allowReorder}, and everything that made it good — the gesture, the
|
|
15
15
|
* spring-open, the typeahead, the in-place rename — is here unchanged.
|
|
16
16
|
*
|
|
17
|
+
* ── 🔴 The graduation verdict, 2026-09-13: this one stays, and it grew ──────────────────
|
|
18
|
+
* `cursedesk` has a `FileTree.tsx` of its own — the same ancestor, kept in step by hand ever
|
|
19
|
+
* since. The pair was compared to decide which should be the shared one, and this is: it is
|
|
20
|
+
* the app's tree with the workbook/sheet/note/merge vocabulary generalised away, and it has
|
|
21
|
+
* external drags, `canDrop`, `acceptsDrop`, reordering, filtering, access marks, counts and
|
|
22
|
+
* the three render slots, none of which the app's has.
|
|
23
|
+
*
|
|
24
|
+
* But the app's carried SIX behaviours this did not, every one of them general, and adopting
|
|
25
|
+
* this tree without them would have been a regression on a 200-row library rather than a
|
|
26
|
+
* graduation. They are here now and each says so where it lives:
|
|
27
|
+
*
|
|
28
|
+
* 1. **Reveal** — the selected row is scrolled into view, and a shut folder holding it is
|
|
29
|
+
* marked and scrolled to instead of being forced open (see the effect below).
|
|
30
|
+
* 2. **The path is marked** — `data-ancestor` on the folders between the root and the
|
|
31
|
+
* selection, `data-holds-open` on the shut one holding it (`openPath`).
|
|
32
|
+
* 3. **Escape cancels a drag** (`beginDrag`), which `pointercancel` never did.
|
|
33
|
+
* 4. **⌥→ / ⌥← move the focused row** (`resolveKeyboardMove`) — the keyboard's half of the
|
|
34
|
+
* drag, and the only half touch has.
|
|
35
|
+
* 5. **The drag chip says where it is going**, not only what is held (`describeDrop`).
|
|
36
|
+
* 6. **The click a finished drag leaves behind is swallowed** (`draggedJustNow`), and the
|
|
37
|
+
* keyboard comes back from a rename instead of being dropped on `<body>`.
|
|
38
|
+
*
|
|
39
|
+
* Two more joined them because they were the same gap wearing a different hat: `onRowMenu`
|
|
40
|
+
* (Shift+F10 / the Menu key, since the tool shelf is deliberately not a tab stop) and
|
|
41
|
+
* `aria-posinset`/`aria-setsize`.
|
|
42
|
+
*
|
|
43
|
+
* What did NOT come across is the app's vocabulary — workbooks, merge-into-a-tab, notes,
|
|
44
|
+
* derived extensions, `activeNoteId`/`activeMediaId` as separate props. A feature the app
|
|
45
|
+
* needs and a design system should not have is an argument for a prop, not for a copy: the
|
|
46
|
+
* three claimants collapse into one `selectedId`, and a kind-specific glyph is `renderIcon`.
|
|
47
|
+
*
|
|
17
48
|
* ── Pointer events, not HTML5 drag-and-drop, for INTERNAL drags ─────────────────────────
|
|
18
49
|
* The ghost image is a translucent screenshot nobody asked for, `dragover` fires at the
|
|
19
50
|
* mercy of the browser, and touch does not have HTML5 dnd at all. The drag only *starts*
|
|
@@ -37,6 +68,7 @@ import type {
|
|
|
37
68
|
} from 'react';
|
|
38
69
|
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
39
70
|
import {
|
|
71
|
+
describeDrop,
|
|
40
72
|
descendantIdsOf,
|
|
41
73
|
type DropOffer,
|
|
42
74
|
type FileTreeDrop,
|
|
@@ -46,6 +78,7 @@ import {
|
|
|
46
78
|
flattenFileTree,
|
|
47
79
|
matchTypeahead,
|
|
48
80
|
resolveDrop,
|
|
81
|
+
resolveKeyboardMove,
|
|
49
82
|
} from '../../core/file-tree/fileTreeModel';
|
|
50
83
|
import { Input } from '../components/Input';
|
|
51
84
|
import { Tooltip } from '../components/Tooltip';
|
|
@@ -159,6 +192,22 @@ export interface FileTreeProps {
|
|
|
159
192
|
onDraftCommit?: (name: string) => void;
|
|
160
193
|
onDraftCancel?: () => void;
|
|
161
194
|
|
|
195
|
+
/**
|
|
196
|
+
* The focused row's own menu, reached by **Shift+F10** or the **Menu** key — the keyboard's
|
|
197
|
+
* route to whatever {@link renderTools} draws.
|
|
198
|
+
*
|
|
199
|
+
* 🔴 The shelf is deliberately NOT a tab stop of its own (that would be one extra stop per
|
|
200
|
+
* row, which is what the roving-tabindex rule above exists to prevent), so without this the
|
|
201
|
+
* ⋯ menu on every row is unreachable without a pointer. Shift+F10 and the Menu key are what
|
|
202
|
+
* every desktop application answers a context menu on, so it is not a shortcut anybody has
|
|
203
|
+
* to be told about.
|
|
204
|
+
*
|
|
205
|
+
* The point is handed in viewport coordinates — the focused row's bottom-right corner —
|
|
206
|
+
* which is what a popover anchors to. Wire it to the SAME call the shelf's own button
|
|
207
|
+
* makes, so the two cannot drift into meaning different things.
|
|
208
|
+
*/
|
|
209
|
+
onRowMenu?: (node: FileTreeNode, at: { x: number; y: number }) => void;
|
|
210
|
+
|
|
162
211
|
/** Trailing controls for a row — the tool shelf. Quiet at rest, never invisible. */
|
|
163
212
|
renderTools?: (node: FileTreeNode) => ReactNode;
|
|
164
213
|
/** Leading glyph. Defaults to a folder/file mark. */
|
|
@@ -276,6 +325,7 @@ export function FileTree({
|
|
|
276
325
|
draft,
|
|
277
326
|
onDraftCommit,
|
|
278
327
|
onDraftCancel,
|
|
328
|
+
onRowMenu,
|
|
279
329
|
renderTools,
|
|
280
330
|
renderIcon,
|
|
281
331
|
renderAccessory,
|
|
@@ -295,6 +345,17 @@ export function FileTree({
|
|
|
295
345
|
/** Folders this drag opened by hovering, to shut again if it never drops. */
|
|
296
346
|
const sprung = useRef<string[]>([]);
|
|
297
347
|
const typeahead = useRef({ word: '', at: 0 });
|
|
348
|
+
/**
|
|
349
|
+
* Swallow the `click` a finished drag leaves behind.
|
|
350
|
+
*
|
|
351
|
+
* 🔴 A pointerdown and a pointerup on the same row still produce a click even when the
|
|
352
|
+
* pointer travelled the length of the list in between — and with `setPointerCapture` the
|
|
353
|
+
* click is delivered to the row the gesture STARTED on whatever it ended over. So without
|
|
354
|
+
* this, dropping a row into a folder also selects the row that was dropped, which in a
|
|
355
|
+
* consumer that navigates on select means the drag takes you somewhere. The drag is the
|
|
356
|
+
* gesture; the click is debris from it.
|
|
357
|
+
*/
|
|
358
|
+
const draggedJustNow = useRef(false);
|
|
298
359
|
|
|
299
360
|
const [ownCollapsed, setOwnCollapsed] = useState<readonly string[]>([]);
|
|
300
361
|
const collapsed = collapsedProp ?? ownCollapsed;
|
|
@@ -310,6 +371,15 @@ export function FileTree({
|
|
|
310
371
|
const [focusedId, setFocusedId] = useState<string | null>(null);
|
|
311
372
|
/** The row an EXTERNAL drag is over, drawn with the same marker an internal one uses. */
|
|
312
373
|
const [externalOverId, setExternalOverId] = useState<string | null>(null);
|
|
374
|
+
/**
|
|
375
|
+
* The last thing a KEYBOARD move said, for the live region.
|
|
376
|
+
*
|
|
377
|
+
* A pointer drag states its refusal on the chip while the row is still in the air; ⌥← on a
|
|
378
|
+
* top-level row has no chip and nowhere else to say "it is already at the top level", so a
|
|
379
|
+
* keyboard route with no voice would be a control that silently does nothing — the exact
|
|
380
|
+
* reading of "broken" this tree's whole drop design exists to avoid.
|
|
381
|
+
*/
|
|
382
|
+
const [notice, setNotice] = useState('');
|
|
313
383
|
|
|
314
384
|
const shown = useMemo(() => (filter ? filterFileTree(nodes, filter) : nodes), [nodes, filter]);
|
|
315
385
|
/*
|
|
@@ -321,6 +391,52 @@ export function FileTree({
|
|
|
321
391
|
[shown, collapsed, filter],
|
|
322
392
|
);
|
|
323
393
|
|
|
394
|
+
/**
|
|
395
|
+
* The folders between the root and the selected row, and the shut one HOLDING it.
|
|
396
|
+
*
|
|
397
|
+
* A tree with most of its folders shut says nothing about where you are, and the one state
|
|
398
|
+
* where that matters most is the state where the selected row is not drawn at all. So the
|
|
399
|
+
* path is marked (`data-ancestor`) and the folder actually holding the selection is marked
|
|
400
|
+
* more strongly (`data-holds-open`) — which is also the thing the reveal below scrolls to
|
|
401
|
+
* when the row itself does not exist on screen.
|
|
402
|
+
*
|
|
403
|
+
* Computed over the whole tree rather than over `rows`, because a shut folder's children
|
|
404
|
+
* are not in `rows` — which is exactly the case this is for.
|
|
405
|
+
*/
|
|
406
|
+
const openPath = useMemo(() => {
|
|
407
|
+
const path = new Set<string>();
|
|
408
|
+
let holder: string | null = null;
|
|
409
|
+
if (selectedId === null) return { path, holder };
|
|
410
|
+
const walk = (list: readonly FileTreeNode[], ancestors: string[]): boolean => {
|
|
411
|
+
for (const node of list) {
|
|
412
|
+
if (node.id === selectedId) {
|
|
413
|
+
for (const id of ancestors) path.add(id);
|
|
414
|
+
holder = ancestors.at(-1) ?? null;
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
if (node.children && walk(node.children, [...ancestors, node.id])) return true;
|
|
418
|
+
}
|
|
419
|
+
return false;
|
|
420
|
+
};
|
|
421
|
+
walk(shown, []);
|
|
422
|
+
return { path, holder };
|
|
423
|
+
}, [shown, selectedId]);
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* How many siblings a row has, by parent — `aria-setsize`, without an O(n²) scan per row.
|
|
427
|
+
*
|
|
428
|
+
* Every sibling of a visible row is itself visible (a shut folder hides its children, never
|
|
429
|
+
* its peers), so counting the rows on screen is counting the level.
|
|
430
|
+
*/
|
|
431
|
+
const setSizes = useMemo(() => {
|
|
432
|
+
const counts = new Map<string, number>();
|
|
433
|
+
for (const row of rows) {
|
|
434
|
+
const key = row.parentId ?? '';
|
|
435
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
436
|
+
}
|
|
437
|
+
return counts;
|
|
438
|
+
}, [rows]);
|
|
439
|
+
|
|
324
440
|
const toggle = useCallback(
|
|
325
441
|
(id: string) => {
|
|
326
442
|
const set = new Set(collapsed);
|
|
@@ -340,6 +456,51 @@ export function FileTree({
|
|
|
340
456
|
[collapsed, setCollapsed],
|
|
341
457
|
);
|
|
342
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Bring the selected row into view — the explorer's *reveal*.
|
|
461
|
+
*
|
|
462
|
+
* 🔴 Every other cue for "where am I" is worth nothing in the one case that matters most:
|
|
463
|
+
* a long list where the selected row is eight hundred pixels above the fold. A consumer
|
|
464
|
+
* changes `selectedId` from a command palette, a tab, a Back button, a deep link or a
|
|
465
|
+
* recents list — all of them **without the pointer ever going near the tree** — so nothing
|
|
466
|
+
* was scrolling it. `cursedesk`'s tree has had this since it was written and this one did
|
|
467
|
+
* not, which would have made adopting it a regression on a 200-row library.
|
|
468
|
+
*
|
|
469
|
+
* Three deliberate limits, each of which VS Code gets right or wrong depending on a setting:
|
|
470
|
+
*
|
|
471
|
+
* 1. 🔴 **A shut folder stays shut.** Its `data-holds-open` marker is scrolled to instead.
|
|
472
|
+
* Auto-expanding is what `explorer.autoReveal` does and it is the behaviour people turn
|
|
473
|
+
* off, because it silently undoes a collapse they chose — and `collapsed` here is often
|
|
474
|
+
* a CONTROLLED value the consumer persists, so expanding would write it open for good.
|
|
475
|
+
* 2. **Only when the row is not already visible.** A list that jumps a few pixels every
|
|
476
|
+
* time you click a row you can already see reads as a bug.
|
|
477
|
+
* 3. **Never mid-drag.** Scrolling the list under a drag moves the drop target out from
|
|
478
|
+
* under the pointer.
|
|
479
|
+
*/
|
|
480
|
+
/*
|
|
481
|
+
* 🔴 The dependency list is deliberately neither what this reads nor a superset of it.
|
|
482
|
+
*
|
|
483
|
+
* `selectedId` is not read in the body at all — the row is found through the DOM, because
|
|
484
|
+
* `data-holds-open` is the answer precisely when the selected row has none — but it is the
|
|
485
|
+
* one thing whose CHANGE should reveal. And `drag` IS read, as a guard rather than a
|
|
486
|
+
* trigger: adding it would reveal the moment a drag ends, which is limit 3 above.
|
|
487
|
+
*/
|
|
488
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `selectedId` is the trigger and not a read; `drag` is a guard and not a trigger — see just above.
|
|
489
|
+
useEffect(() => {
|
|
490
|
+
const scroller = scrollRef.current;
|
|
491
|
+
if (!scroller || drag?.active) return;
|
|
492
|
+
const row =
|
|
493
|
+
scroller.querySelector<HTMLElement>('.cbft-row[data-selected]') ??
|
|
494
|
+
scroller.querySelector<HTMLElement>('.cbft-row[data-holds-open]');
|
|
495
|
+
if (!row?.scrollIntoView) return;
|
|
496
|
+
const box = row.getBoundingClientRect();
|
|
497
|
+
const frame = scroller.getBoundingClientRect();
|
|
498
|
+
if (box.top >= frame.top && box.bottom <= frame.bottom) return;
|
|
499
|
+
// `nearest` on both axes: the least movement that makes the row visible, and no sideways
|
|
500
|
+
// travel in a list that does not scroll sideways.
|
|
501
|
+
row.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
502
|
+
}, [selectedId]);
|
|
503
|
+
|
|
343
504
|
// ── The drag ──────────────────────────────────────────────────────────────────────────
|
|
344
505
|
|
|
345
506
|
const stopAutoScroll = useCallback(() => {
|
|
@@ -373,6 +534,10 @@ export function FileTree({
|
|
|
373
534
|
(commit: boolean, state: DragState | null) => {
|
|
374
535
|
stopAutoScroll();
|
|
375
536
|
stopSpring();
|
|
537
|
+
// Whatever happens next, a gesture that actually travelled owes the row a swallowed
|
|
538
|
+
// click — see `draggedJustNow`. Set for a cancelled drag too: the click arrives either
|
|
539
|
+
// way, and Escape landing you on a different row would be its own surprise.
|
|
540
|
+
if (state?.active) draggedJustNow.current = true;
|
|
376
541
|
if (commit && state?.active && state.offer.target && onMove) {
|
|
377
542
|
onMove(state.id, state.offer.target);
|
|
378
543
|
sprung.current = [];
|
|
@@ -468,13 +633,32 @@ export function FileTree({
|
|
|
468
633
|
target.removeEventListener('pointermove', move);
|
|
469
634
|
target.removeEventListener('pointerup', up);
|
|
470
635
|
target.removeEventListener('pointercancel', cancel);
|
|
636
|
+
window.removeEventListener('keydown', abort);
|
|
471
637
|
endDrag(commit, state);
|
|
472
638
|
};
|
|
473
639
|
const up = finish(true);
|
|
474
640
|
const cancel = finish(false);
|
|
641
|
+
/*
|
|
642
|
+
* 🔴 Escape puts the row back — the way out of a gesture you did not mean to start.
|
|
643
|
+
*
|
|
644
|
+
* Every drag surface has it and this one did not: the only way to abandon a drag here
|
|
645
|
+
* was to find somewhere the tree refuses and release there, which is a thing you have
|
|
646
|
+
* to already understand the rules to do. `pointercancel` fires when the SYSTEM takes
|
|
647
|
+
* the pointer away, never when a person changes their mind.
|
|
648
|
+
*
|
|
649
|
+
* On `window` rather than the row, because a pointer capture is in force and the
|
|
650
|
+
* keyboard's focus is wherever it was before the press — quite possibly not this tree.
|
|
651
|
+
* It is removed by `finish` on every exit path, including this one.
|
|
652
|
+
*/
|
|
653
|
+
const abort = (keyEvent: KeyboardEvent) => {
|
|
654
|
+
if (keyEvent.key !== 'Escape') return;
|
|
655
|
+
keyEvent.preventDefault();
|
|
656
|
+
cancel();
|
|
657
|
+
};
|
|
475
658
|
target.addEventListener('pointermove', move);
|
|
476
659
|
target.addEventListener('pointerup', up);
|
|
477
660
|
target.addEventListener('pointercancel', cancel);
|
|
661
|
+
window.addEventListener('keydown', abort);
|
|
478
662
|
},
|
|
479
663
|
[
|
|
480
664
|
allowReorder,
|
|
@@ -512,6 +696,33 @@ export function FileTree({
|
|
|
512
696
|
if (id) rowRefs.current.get(id)?.focus();
|
|
513
697
|
}, []);
|
|
514
698
|
|
|
699
|
+
/*
|
|
700
|
+
* ── The keyboard comes back from a rename ─────────────────────────────────────────────
|
|
701
|
+
*
|
|
702
|
+
* Owner, of the tree this one was ported from: *"I clicked a file, pressed enter, changed
|
|
703
|
+
* the title, pressed enter, and it saved. Then I pressed enter and it should have started
|
|
704
|
+
* renaming again but didn't. I think the focus gets lost on saving a rename."*
|
|
705
|
+
*
|
|
706
|
+
* The word "lost" is literal: committing unmounts the `<input>` that had focus, and a
|
|
707
|
+
* browser hands focus to `<body>` when that happens. `<body>` is not in the tree, so the
|
|
708
|
+
* next Enter goes to the application — which has no answer for it — rather than to the row
|
|
709
|
+
* that was just renamed. Every key this tree defines is gone until something is clicked.
|
|
710
|
+
*
|
|
711
|
+
* 🔴 **Only when focus was actually lost.** Blur commits too, so the ordinary way to end a
|
|
712
|
+
* rename is to click something else, and stealing focus back would undo that click.
|
|
713
|
+
* `document.body` (or nothing at all) is the signature of the unmount; anything else means
|
|
714
|
+
* a person is already somewhere on purpose.
|
|
715
|
+
*/
|
|
716
|
+
const wasRenaming = useRef<string | null>(null);
|
|
717
|
+
useEffect(() => {
|
|
718
|
+
const previous = wasRenaming.current;
|
|
719
|
+
wasRenaming.current = renamingId ?? null;
|
|
720
|
+
if (!previous || renamingId) return;
|
|
721
|
+
const active = document.activeElement;
|
|
722
|
+
if (active && active !== document.body) return;
|
|
723
|
+
focusRow(previous);
|
|
724
|
+
}, [renamingId, focusRow]);
|
|
725
|
+
|
|
515
726
|
const onKeyDown = useCallback(
|
|
516
727
|
(event: React.KeyboardEvent<HTMLElement>, row: FileTreeRow) => {
|
|
517
728
|
if (renamingId) return;
|
|
@@ -520,6 +731,53 @@ export function FileTree({
|
|
|
520
731
|
const next = rows[Math.min(rows.length - 1, Math.max(0, index + delta))];
|
|
521
732
|
if (next) focusRow(next.node.id);
|
|
522
733
|
};
|
|
734
|
+
|
|
735
|
+
/*
|
|
736
|
+
* The keyboard's route to the row's own menu — see {@link FileTreeProps.onRowMenu}.
|
|
737
|
+
* Before the switch, because `ContextMenu` and Shift+F10 are not arrow keys and the
|
|
738
|
+
* shelf is deliberately not a tab stop that could answer them itself.
|
|
739
|
+
*/
|
|
740
|
+
if (onRowMenu && (event.key === 'ContextMenu' || (event.shiftKey && event.key === 'F10'))) {
|
|
741
|
+
event.preventDefault();
|
|
742
|
+
const box = rowRefs.current.get(row.node.id)?.getBoundingClientRect();
|
|
743
|
+
onRowMenu(row.node, { x: box?.right ?? 0, y: box?.bottom ?? 0 });
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/*
|
|
748
|
+
* ⌥→ and ⌥← MOVE the focused row — the keyboard's half of the drag, and the only half
|
|
749
|
+
* that exists on touch (a press-and-drag there scrolls the list, so the gesture is
|
|
750
|
+
* deliberately not offered). See `resolveKeyboardMove` for what the two directions mean
|
|
751
|
+
* and why there is no ⌥↑/⌥↓.
|
|
752
|
+
*
|
|
753
|
+
* 🔴 Before the arrow cases below, so one key never means two things. Everything else
|
|
754
|
+
* in this handler is bare-key, which is what keeps ⇧⏎/⌘F and the rest free for the app.
|
|
755
|
+
*/
|
|
756
|
+
if (
|
|
757
|
+
onMove &&
|
|
758
|
+
event.altKey &&
|
|
759
|
+
!event.metaKey &&
|
|
760
|
+
!event.ctrlKey &&
|
|
761
|
+
(event.key === 'ArrowLeft' || event.key === 'ArrowRight')
|
|
762
|
+
) {
|
|
763
|
+
event.preventDefault();
|
|
764
|
+
const offer = resolveKeyboardMove({
|
|
765
|
+
nodes: shown,
|
|
766
|
+
rows,
|
|
767
|
+
row,
|
|
768
|
+
direction: event.key === 'ArrowRight' ? 'in' : 'out',
|
|
769
|
+
...(lockedReason ? { lockedReason } : {}),
|
|
770
|
+
...(canDrop ? { canDrop } : {}),
|
|
771
|
+
});
|
|
772
|
+
if (offer.target) {
|
|
773
|
+
setNotice(describeDrop(shown, offer.target));
|
|
774
|
+
onMove(row.node.id, offer.target);
|
|
775
|
+
} else {
|
|
776
|
+
setNotice(offer.reason ?? '');
|
|
777
|
+
}
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
|
|
523
781
|
switch (event.key) {
|
|
524
782
|
case 'ArrowDown':
|
|
525
783
|
event.preventDefault();
|
|
@@ -617,7 +875,21 @@ export function FileTree({
|
|
|
617
875
|
focusRow((rows[found] as FileTreeRow).node.id);
|
|
618
876
|
}
|
|
619
877
|
},
|
|
620
|
-
[
|
|
878
|
+
[
|
|
879
|
+
canDrop,
|
|
880
|
+
focusRow,
|
|
881
|
+
lockedReason,
|
|
882
|
+
onDelete,
|
|
883
|
+
onMove,
|
|
884
|
+
onOpen,
|
|
885
|
+
onRenameStart,
|
|
886
|
+
onRowMenu,
|
|
887
|
+
onSelect,
|
|
888
|
+
renamingId,
|
|
889
|
+
rows,
|
|
890
|
+
shown,
|
|
891
|
+
toggle,
|
|
892
|
+
],
|
|
621
893
|
);
|
|
622
894
|
|
|
623
895
|
// ── External drags (Finder files, gallery tiles) ──────────────────────────────────────
|
|
@@ -825,6 +1097,11 @@ export function FileTree({
|
|
|
825
1097
|
role='treeitem'
|
|
826
1098
|
aria-selected={isSelected}
|
|
827
1099
|
aria-level={row.depth + 1}
|
|
1100
|
+
/* A screen reader says "3 of 7" only if it is told the level's size. Both
|
|
1101
|
+
come from `shown`, so a filtered tree counts what it drew rather than
|
|
1102
|
+
what it hid. */
|
|
1103
|
+
aria-posinset={row.index + 1}
|
|
1104
|
+
aria-setsize={setSizes.get(row.parentId ?? '') ?? 1}
|
|
828
1105
|
{...(row.expandable ? { 'aria-expanded': row.expanded } : {})}
|
|
829
1106
|
tabIndex={
|
|
830
1107
|
node.id === (focusedId ?? selectedId ?? rows[0]?.node.id) ? 0 : -1
|
|
@@ -839,6 +1116,13 @@ export function FileTree({
|
|
|
839
1116
|
data-dragged={isDragged || undefined}
|
|
840
1117
|
data-into={isDropTarget || undefined}
|
|
841
1118
|
data-marker={marker ?? undefined}
|
|
1119
|
+
/* On the way to the selection, and — when it is shut — the one HOLDING it.
|
|
1120
|
+
See `openPath`: with the selected row not drawn at all, this bar is the
|
|
1121
|
+
only thing in the tree that says where you are. */
|
|
1122
|
+
data-ancestor={openPath.path.has(node.id) || undefined}
|
|
1123
|
+
data-holds-open={
|
|
1124
|
+
(row.expandable && !row.expanded && openPath.holder === node.id) || undefined
|
|
1125
|
+
}
|
|
842
1126
|
data-access={node.access && node.access !== 'normal' ? node.access : undefined}
|
|
843
1127
|
style={{ '--cbft-depth': row.depth } as React.CSSProperties}
|
|
844
1128
|
onPointerDown={(event) => {
|
|
@@ -847,6 +1131,11 @@ export function FileTree({
|
|
|
847
1131
|
}}
|
|
848
1132
|
onClick={(event) => {
|
|
849
1133
|
if (inTools(event.target)) return;
|
|
1134
|
+
// The click a finished drag left behind — see `draggedJustNow`.
|
|
1135
|
+
if (draggedJustNow.current) {
|
|
1136
|
+
draggedJustNow.current = false;
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
850
1139
|
onSelect(node);
|
|
851
1140
|
}}
|
|
852
1141
|
onDoubleClick={(event) => {
|
|
@@ -1033,6 +1322,11 @@ export function FileTree({
|
|
|
1033
1322
|
* The chip that follows the cursor. There is no browser drag image with pointer
|
|
1034
1323
|
* events, so this is the only thing saying the gesture is working — and it is also
|
|
1035
1324
|
* where a refusal is stated, in words, while the row is still in the air.
|
|
1325
|
+
*
|
|
1326
|
+
* 🔴 It says WHERE IT IS GOING, not only what is held (2026-09-13). "Into Projects" or
|
|
1327
|
+
* "To the top level" is the answer to the question a drag actually asks, and it is the
|
|
1328
|
+
* difference between a cue that is decoration and one that stops a wrong drop before
|
|
1329
|
+
* the pointer is lifted. The name alone tells you the gesture started, which you knew.
|
|
1036
1330
|
*/}
|
|
1037
1331
|
{drag?.active ? (
|
|
1038
1332
|
<div
|
|
@@ -1041,12 +1335,18 @@ export function FileTree({
|
|
|
1041
1335
|
style={{ left: `${drag.x}px`, top: `${drag.y}px` }}
|
|
1042
1336
|
aria-hidden='true'
|
|
1043
1337
|
>
|
|
1044
|
-
{
|
|
1338
|
+
<span className='cbft-chip-name'>{nameOf(shown, drag.id)}</span>
|
|
1339
|
+
{/* The refusal wins the slot: while something cannot land, "why not" outranks
|
|
1340
|
+
"where to", and there is never both to say. */}
|
|
1341
|
+
<span className='cbft-chip-where'>
|
|
1342
|
+
{drag.offer.reason ?? describeDrop(shown, drag.offer.target)}
|
|
1343
|
+
</span>
|
|
1045
1344
|
</div>
|
|
1046
1345
|
) : null}
|
|
1047
|
-
{/* The same words, for a screen reader, which cannot see the chip
|
|
1346
|
+
{/* The same words, for a screen reader, which cannot see the chip — and, between drags,
|
|
1347
|
+
whatever the last keyboard move said. See `notice`. */}
|
|
1048
1348
|
<span className='cbft-sr' role='status' aria-live='polite'>
|
|
1049
|
-
{drag?.active ? (drag.offer.reason ??
|
|
1349
|
+
{drag?.active ? (drag.offer.reason ?? describeDrop(shown, drag.offer.target)) : notice}
|
|
1050
1350
|
</span>
|
|
1051
1351
|
</div>
|
|
1052
1352
|
);
|
|
@@ -333,6 +333,39 @@
|
|
|
333
333
|
outline-offset: -2px;
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
/*
|
|
337
|
+
* ── 🔴 Where you are, when the row you are on is not drawn ──────────────────────────────
|
|
338
|
+
*
|
|
339
|
+
* A tree with most of its folders shut says nothing about where you are, and the state where
|
|
340
|
+
* that matters most is the one where the selected row is not on screen at all. So the folders
|
|
341
|
+
* BETWEEN the root and the selection are weighted a step, and the shut folder actually
|
|
342
|
+
* holding it takes a mark of its own.
|
|
343
|
+
*
|
|
344
|
+
* Weight rather than colour for the ancestors: they are context, and three tinted rows down a
|
|
345
|
+
* sidebar would compete with the one neutral fill that says *this is where you are*. 550 is
|
|
346
|
+
* `file-tree-geometry`'s `maxWeight` — the ceiling the "use thinner fonts" note put on this
|
|
347
|
+
* tree, and the reason this is not 600.
|
|
348
|
+
*/
|
|
349
|
+
.cbft-row[data-ancestor] .cbft-name {
|
|
350
|
+
font-weight: 550;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/*
|
|
354
|
+
* The shut folder holding the selection. A 2px bar on the inside edge — the same language the
|
|
355
|
+
* activity rail uses for the view you are in, so a person learns it once — and deliberately
|
|
356
|
+
* NOT the selected row's fill: this folder is not what is selected, it is what the selection
|
|
357
|
+
* is inside. The reveal scrolls to this element when the selected row does not exist.
|
|
358
|
+
*/
|
|
359
|
+
.cbft-row[data-holds-open]::before {
|
|
360
|
+
content: '';
|
|
361
|
+
position: absolute;
|
|
362
|
+
inset-block: 2px;
|
|
363
|
+
inset-inline-start: 0;
|
|
364
|
+
inline-size: 2px;
|
|
365
|
+
border-radius: 999px;
|
|
366
|
+
background: var(--accent-solid, var(--accent));
|
|
367
|
+
}
|
|
368
|
+
|
|
336
369
|
/* The row that is in the air — still drawn, so the tree does not jump, but clearly lifted. */
|
|
337
370
|
.cbft-row[data-dragged] {
|
|
338
371
|
opacity: 0.4;
|
|
@@ -615,6 +648,25 @@
|
|
|
615
648
|
|
|
616
649
|
/* ── The drag chip ───────────────────────────────────────────────────────────────────── */
|
|
617
650
|
|
|
651
|
+
/*
|
|
652
|
+
* Two parts, not one string: what is held, and where it is going. The destination is drawn a
|
|
653
|
+
* step quieter because the name is what identifies the chip and the destination is what you
|
|
654
|
+
* are checking — and because a refusal takes the same slot, where the colour below is what
|
|
655
|
+
* carries the "no".
|
|
656
|
+
*/
|
|
657
|
+
.cbft-chip-name {
|
|
658
|
+
font-weight: 500;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.cbft-chip-where {
|
|
662
|
+
margin-inline-start: 0.375rem;
|
|
663
|
+
color: var(--muted-foreground);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.cbft-chip[data-refused] .cbft-chip-where {
|
|
667
|
+
color: inherit;
|
|
668
|
+
}
|
|
669
|
+
|
|
618
670
|
.cbft-chip {
|
|
619
671
|
position: fixed;
|
|
620
672
|
/* Offset off the cursor so it never covers the row being aimed at. */
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": [
|
|
3
|
+
"THE SIDEBAR FILE TREE, AS NUMBERS — the design this tree implements, committed HERE.",
|
|
4
|
+
"",
|
|
5
|
+
"🔴 WHY IT IS A FILE IN THIS REPO AND NOT A LOOKUP. Until 2026-09-13 the spec beside this",
|
|
6
|
+
"file read these numbers out of a checkout in the owner's home directory, and skipped its",
|
|
7
|
+
"cross-repo assertions whenever that directory was missing. cursedbelt is PUBLISHED: a",
|
|
8
|
+
"package whose suite reaches into one person's private tree passes for them and is silently",
|
|
9
|
+
"half-skipped for everybody else. A test that quietly does nothing is worse than no test.",
|
|
10
|
+
"",
|
|
11
|
+
"So the design is vendored. Every assertion in `fileTreeGeometry.spec.ts` now runs",
|
|
12
|
+
"unconditionally, on any machine, with no fleet beside the checkout.",
|
|
13
|
+
"",
|
|
14
|
+
"🔴 The numbers are asserted against THIS tree's implementation, never against themselves.",
|
|
15
|
+
"A test that read `true` out of this file and then asserted `true` would pass on a tree that",
|
|
16
|
+
"does neither thing. Change a value here and the stylesheet reds until it follows.",
|
|
17
|
+
"",
|
|
18
|
+
"The same design is implemented a second time in cursed-embeddable-spreadsheet, which is a",
|
|
19
|
+
"separate published package and cannot depend on this one. Keeping the two in step is a",
|
|
20
|
+
"human step at the point the design changes: edit this file, then both stylesheets."
|
|
21
|
+
],
|
|
22
|
+
"geometry": {
|
|
23
|
+
"$comment": "Pixels. Same meaning either side; the prefixes differ (--cbft-* / --cs-tree-*).",
|
|
24
|
+
"row": 22,
|
|
25
|
+
"pad": 3,
|
|
26
|
+
"inset": 8,
|
|
27
|
+
"glyph": 16,
|
|
28
|
+
"indent": 8,
|
|
29
|
+
"gap": 6,
|
|
30
|
+
"guideOffset": 4
|
|
31
|
+
},
|
|
32
|
+
"scrollbar": {
|
|
33
|
+
"$comment": [
|
|
34
|
+
"🔴 THE TREE'S OWN SCROLLBAR, added 2026-09-11. Owner: \"The scrollbar should be very thin",
|
|
35
|
+
"in the file tree and against the right edge. Currently it has space between the scrolling",
|
|
36
|
+
"marker and the edge. File tree changes need to be done in ces too.\"",
|
|
37
|
+
"",
|
|
38
|
+
"Two separate defects, one per repo, and neither is visible from the other side:",
|
|
39
|
+
"",
|
|
40
|
+
" - ces had a GLOBAL rule, `.cs-root ::-webkit-scrollbar { width: 11px }` with the thumb",
|
|
41
|
+
" carrying `border: 3px solid var(--cs-bg)`. That border IS the space he is describing:",
|
|
42
|
+
" a 3px ring of page background on each side leaves a 5px marker floating in an 11px",
|
|
43
|
+
" gutter. Correct for a document scroller, far too loud down a 22px-row tree.",
|
|
44
|
+
" - cursedbelt's `.cbft-scroll` declared nothing at all, so it drew the platform default",
|
|
45
|
+
" — ~15px on macOS with the thumb inset by the engine.",
|
|
46
|
+
"",
|
|
47
|
+
"And in cursedbelt there was a third, which is the 'against the right edge' half: the tree",
|
|
48
|
+
"sat inside `FilterRail`'s `px-2` body, so the scroller's own edge was 8px short of the",
|
|
49
|
+
"rail's. A filling section now bleeds that padding back out (`-mx-2`) and the tree supplies",
|
|
50
|
+
"its own `inset` for the ROWS — which is why `inset` above is unchanged and the row text",
|
|
51
|
+
"still lines up under the section heading.",
|
|
52
|
+
"",
|
|
53
|
+
"🔴 `thumbInset: 0` is the assertion that matters and the one most likely to be undone by",
|
|
54
|
+
"somebody 'tidying' the thumb with a border or a margin to round it off. The marker touches",
|
|
55
|
+
"the edge; that is the whole request."
|
|
56
|
+
],
|
|
57
|
+
"width": 6,
|
|
58
|
+
"thumbInset": 0
|
|
59
|
+
},
|
|
60
|
+
"invariants": {
|
|
61
|
+
"$comment": [
|
|
62
|
+
"The relationships, which are what actually break. A number can be changed on purpose;",
|
|
63
|
+
"these cannot be broken on purpose, and each one has a symptom nothing else reports."
|
|
64
|
+
],
|
|
65
|
+
"indent === 8": "VS Code's own tree step, and a MEASUREMENT the owner made on screen rather than a value derived from anything here — so nothing may recompute it. It was briefly `glyph` (16) on a reading of \"the subdir and file icons should start at the right edge of the parent folder's arrow icon … bumped over by 1 icon space\", which is literally true only at one-glyph-per-step. He saw that and said: \"I want vscode 8px style so it matches how theirs looks.\" In VS Code a child's glyph box starts at the MIDDLE of its parent's, not past its edge.",
|
|
66
|
+
"indent is INDEPENDENT of glyph": "They were coupled for one day and must not be recoupled. What makes 8px read as VS Code is the single glyph column, not the step: the old four-track layout spent 44px of chrome before every name, so 8px of step was invisible beside it; at 22px of chrome the step is the thing you see.",
|
|
67
|
+
"no density or pointer block overrides indent": "cb had 12px under `@media (pointer: coarse)` and ces 12px under `[data-density=comfortable]`. A step that is 8 on a desktop and 12 on a phone is not one look. Row height and padding DO vary — those are about how much room a row is given; the step is about reading the hierarchy and reads the same everywhere.",
|
|
68
|
+
"guideOffset === indent / 2": "The indentation guide is painted down the MIDDLE of each indent step. Break it and every guide line sits in the gutter between two levels instead of under the chevron it connects to.",
|
|
69
|
+
"gap === 6": "One character at 13px, and VS Code's own figure — owner: \"a gap about the size of a single character\", and \"the gap is the same for files from their icons and dirs from their arrows.\" The second clause is why the tree has ONE glyph column rather than a chevron track plus an icon track.",
|
|
70
|
+
"row === glyph + pad * 2": "The row's height is its line box plus its padding, and the line box is set to the glyph. Break it and the row's height becomes a function of the host app's inherited line-height — the same component drawing a different height in each app.",
|
|
71
|
+
"scrollbar.width is declared by the TREE, not inherited": "Both repos draw a scrollbar somewhere up the tree — ces globally on `.cs-root`, cursedbelt by platform default — and both were wrong here for opposite reasons. A tree whose gutter is inherited is a tree whose gutter changes when somebody restyles a dialog.",
|
|
72
|
+
"scrollbar.thumbInset === 0": "The marker touches the right edge. A `border` or a `margin` on the thumb is how the space comes back: ces's global thumb carries `border: 3px solid var(--cs-bg)` on purpose for document scrollers, and the tree must override it rather than inherit it. Owner: \"against the right edge … Currently it has space between the scrolling marker and the edge.\"",
|
|
73
|
+
"nothing may pad the SCROLLER horizontally": "The scrollbar is painted at the scroll container's border-box edge, so any ancestor padding moves it inward — which is what `FilterRail`'s `px-2` was doing. Indentation is the ROW's job (`inset`), never the scroller's."
|
|
74
|
+
},
|
|
75
|
+
"typography": {
|
|
76
|
+
"$comment": [
|
|
77
|
+
"Owner, 2026-09-10: \"use thinner fonts and less bright font in dark mode. It looks too",
|
|
78
|
+
"glaring.\" Both halves are needed: the colour is the brightness, and antialiasing is the",
|
|
79
|
+
"WEIGHT — on a dark ground macOS stem-darkening renders 13px type visibly heavier than",
|
|
80
|
+
"the same weight on white, so dimming alone leaves fat letterforms in a quieter grey.",
|
|
81
|
+
"",
|
|
82
|
+
"🔴 The two repos already differed on the second half, and each keeps its own answer:",
|
|
83
|
+
"ces has carried `-webkit-font-smoothing: antialiased` on `.cs-root` since the beginning,",
|
|
84
|
+
"so a tree-scoped copy there would be inert and is deliberately absent — its \"thinner\" is",
|
|
85
|
+
"the weight ladder alone. cursedbelt's framework sets no base, so its tree declares one",
|
|
86
|
+
"under `:where(.dark) .cbft`. Each repo's test asserts ITS arrangement; this field records",
|
|
87
|
+
"the intent, not a string to grep for.",
|
|
88
|
+
"",
|
|
89
|
+
"The dark foreground is expressed as a target rather than a literal because the two apps",
|
|
90
|
+
"theme from different palettes; each derives its own value near it. VS Code's dark themes",
|
|
91
|
+
"all sit close to #cccccc for this surface while keeping full white for the selected row."
|
|
92
|
+
],
|
|
93
|
+
"restWeight": 400,
|
|
94
|
+
"maxWeight": 550,
|
|
95
|
+
"darkForegroundTarget": "#cbd5e1",
|
|
96
|
+
"darkFontSmoothing": "antialiased",
|
|
97
|
+
"selectedRowKeepsFullForeground": true
|
|
98
|
+
},
|
|
99
|
+
"behaviour": {
|
|
100
|
+
"$comment": [
|
|
101
|
+
"Not numbers — the two things a row DOES that both trees must agree on. Added",
|
|
102
|
+
"2026-09-11 from the owner's batch: \"Make them use an arrow icon next to a dr even when",
|
|
103
|
+
"the dir is empty. For meda files show a thumbnail instead of the file type icon to the",
|
|
104
|
+
"left of the name.\"",
|
|
105
|
+
"",
|
|
106
|
+
"🔴 These are asserted against the IMPLEMENTATION on each side, not compared flag to",
|
|
107
|
+
"flag. A test that read `true` here and asserted `true` would pass on a tree that does",
|
|
108
|
+
"neither. So: flip a flag below and the repo whose code still does the old thing goes",
|
|
109
|
+
"red, which is the only arrangement where this file can refuse anything."
|
|
110
|
+
],
|
|
111
|
+
"everyFolderDrawsATwisty": {
|
|
112
|
+
"value": true,
|
|
113
|
+
"why": [
|
|
114
|
+
"The chevron says FOLDER, not OCCUPIED — VS Code's own rule, and it opens an empty",
|
|
115
|
+
"folder onto nothing rather than hiding the control.",
|
|
116
|
+
"",
|
|
117
|
+
"Both trees defaulted to `children.length > 0` and it was wrong twice. For a LAZY tree",
|
|
118
|
+
"it is a trap door: a row whose contents load only while it is open has no children",
|
|
119
|
+
"while shut, so it loses its caret and can never be reopened — measured in",
|
|
120
|
+
"apps/collections, which had to pass an explicit `expandable` to escape it. For an",
|
|
121
|
+
"EAGER tree it draws a brand-new empty folder as a leaf, so the one mark that says",
|
|
122
|
+
"'this is a place' is missing exactly while the folder is being filled.",
|
|
123
|
+
"",
|
|
124
|
+
"An explicit opt-out survives on each side (cb: `expandable: false`) for a row that is",
|
|
125
|
+
"a folder in name and can never hold anything."
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"mediaRowDrawsItsThumbnail": {
|
|
129
|
+
"value": true,
|
|
130
|
+
"why": [
|
|
131
|
+
"A folder of photographs is a column of identical camera glyphs: the glyph box costs",
|
|
132
|
+
"16px and says nothing. The thumbnail is the only thing that tells one row from the",
|
|
133
|
+
"next without opening it.",
|
|
134
|
+
"",
|
|
135
|
+
"🔴 THREE constraints, each of which has already cost something somewhere in the fleet:",
|
|
136
|
+
" 1. It takes the SAME glyph box, not a new track and not a taller row. `row === glyph",
|
|
137
|
+
" + pad * 2` above is what keeps a tree scannable; a row that grows per file is not.",
|
|
138
|
+
" 2. It falls back to the type icon ONCE per URL and never retries. A derived",
|
|
139
|
+
" thumbnail's ABSENCE is the normal 'not rendered yet' state, so a broken-image",
|
|
140
|
+
" glyph is a lie about the archive (apps/family shipped and reverted exactly that on",
|
|
141
|
+
" 2026-08-14), and re-pointing `src` from inside `onError` is an infinite request",
|
|
142
|
+
" loop with every row in view retrying at once.",
|
|
143
|
+
" 3. It is a THUMBNAIL, never the original. This is a 16px box; a 300-row folder",
|
|
144
|
+
" pointed at full-size objects is 300 full downloads to draw a rail.",
|
|
145
|
+
"",
|
|
146
|
+
"A FOLDER's glyph box is its twisty and no picture may displace it — the row would lose",
|
|
147
|
+
"the only control that opens it."
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|