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,210 +0,0 @@
|
|
|
1
|
-
import { Crosshair, Maximize2, Minus, Plus } from 'lucide-react';
|
|
2
|
-
import { type PointerEvent as ReactPointerEvent, useCallback, useRef } from 'react';
|
|
3
|
-
import { IconButton } from '../components/IconButton';
|
|
4
|
-
import { Surface } from '../components/Surface';
|
|
5
|
-
import { cn } from '../lib/cn';
|
|
6
|
-
|
|
7
|
-
export interface CanvasRect {
|
|
8
|
-
x: number;
|
|
9
|
-
y: number;
|
|
10
|
-
width: number;
|
|
11
|
-
height: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface CanvasMinimapPoint {
|
|
15
|
-
x: number;
|
|
16
|
-
y: number;
|
|
17
|
-
/** A `--tone-*`/`--chart-*` CSS value. Defaults to the muted foreground. */
|
|
18
|
-
color?: string;
|
|
19
|
-
/** The focus node is drawn larger and on top. */
|
|
20
|
-
focus?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface CanvasNavHUDProps {
|
|
24
|
-
/** "Fit to canvas size" — frame the whole graph. Required: it is the escape
|
|
25
|
-
* hatch from being lost, and a canvas without one is the reported defect. */
|
|
26
|
-
onFit: () => void;
|
|
27
|
-
/** "Centre on the active focus node". Omit only if the canvas has no focus. */
|
|
28
|
-
onCenterFocus?: () => void;
|
|
29
|
-
/** Named in the centre control's hint, so it says WHAT it will centre on. */
|
|
30
|
-
focusLabel?: string;
|
|
31
|
-
/** Current zoom (1 = 100%). Renders the zoom cluster when `onZoomChange` is set. */
|
|
32
|
-
zoom?: number;
|
|
33
|
-
onZoomChange?: (zoom: number) => void;
|
|
34
|
-
/** Zoom bounds for the +/- controls. Default 0.1 – 4. */
|
|
35
|
-
zoomRange?: [number, number];
|
|
36
|
-
/** The whole graph's bounds, in world units. */
|
|
37
|
-
world?: CanvasRect;
|
|
38
|
-
/** What the user can currently see, in the same units. */
|
|
39
|
-
viewport?: CanvasRect;
|
|
40
|
-
/** Optional node dots for the radar. Keep it cheap — sample, don't map 10k nodes. */
|
|
41
|
-
points?: CanvasMinimapPoint[];
|
|
42
|
-
/** Click/drag the radar to jump. Receives world coordinates. */
|
|
43
|
-
onMinimapJump?: (point: { x: number; y: number }) => void;
|
|
44
|
-
/** Which corner the HUD floats in. Default `bottom-right`. */
|
|
45
|
-
corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
46
|
-
className?: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const CORNER_CLASS: Record<NonNullable<CanvasNavHUDProps['corner']>, string> = {
|
|
50
|
-
'top-left': 'top-3 left-3',
|
|
51
|
-
'top-right': 'top-3 right-3',
|
|
52
|
-
'bottom-left': 'bottom-3 left-3',
|
|
53
|
-
'bottom-right': 'bottom-3 right-3',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const MINIMAP_W = 132;
|
|
57
|
-
const MINIMAP_H = 88;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The floating navigation HUD every infinite canvas must have.
|
|
61
|
-
*
|
|
62
|
-
* `DESIGN.md` §7: *"Every infinite panning or zooming canvas must include an
|
|
63
|
-
* explicit floating Navigation HUD. The HUD must pack three essential controls: a
|
|
64
|
-
* 'Fit to Canvas Size' trigger, a 'Center Canvas on Active Focus Node' tracking
|
|
65
|
-
* action, and a low-opacity Radar Mini-Map positioned in a viewport corner
|
|
66
|
-
* indicating active coordinate bounds."*
|
|
67
|
-
*
|
|
68
|
-
* Nothing shared implemented any of the three — `HudPill` is a pill, not a HUD —
|
|
69
|
-
* so the family tree, the atlas, the orch lanes and fractals each had a different
|
|
70
|
-
* partial answer or none. This is the one implementation.
|
|
71
|
-
*
|
|
72
|
-
* It is deliberately presentational: it owns no camera. The canvas keeps its own
|
|
73
|
-
* transform and passes `world`/`viewport` in the SAME units, which is the only
|
|
74
|
-
* contract the radar needs and the reason this works for an SVG tree and a WebGL
|
|
75
|
-
* field alike. The controls sit in a `Surface` at `--z-sticky`, above the canvas
|
|
76
|
-
* and below every overlay (`DESIGN.md` §5's containment rule), and every one of
|
|
77
|
-
* them is a real 44×44 target on a coarse pointer via `IconButton`.
|
|
78
|
-
*/
|
|
79
|
-
export function CanvasNavHUD({
|
|
80
|
-
onFit,
|
|
81
|
-
onCenterFocus,
|
|
82
|
-
focusLabel,
|
|
83
|
-
zoom,
|
|
84
|
-
onZoomChange,
|
|
85
|
-
zoomRange = [0.1, 4],
|
|
86
|
-
world,
|
|
87
|
-
viewport,
|
|
88
|
-
points,
|
|
89
|
-
onMinimapJump,
|
|
90
|
-
corner = 'bottom-right',
|
|
91
|
-
className,
|
|
92
|
-
}: CanvasNavHUDProps) {
|
|
93
|
-
const svgRef = useRef<SVGSVGElement | null>(null);
|
|
94
|
-
const [minZoom, maxZoom] = zoomRange;
|
|
95
|
-
|
|
96
|
-
const jump = useCallback(
|
|
97
|
-
(e: ReactPointerEvent<SVGSVGElement>) => {
|
|
98
|
-
if (!onMinimapJump || !world) return;
|
|
99
|
-
const box = svgRef.current?.getBoundingClientRect();
|
|
100
|
-
if (!box || box.width === 0 || box.height === 0) return;
|
|
101
|
-
const fx = (e.clientX - box.left) / box.width;
|
|
102
|
-
const fy = (e.clientY - box.top) / box.height;
|
|
103
|
-
onMinimapJump({ x: world.x + fx * world.width, y: world.y + fy * world.height });
|
|
104
|
-
},
|
|
105
|
-
[onMinimapJump, world],
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const showRadar = Boolean(world && world.width > 0 && world.height > 0);
|
|
109
|
-
const scaleX = showRadar ? MINIMAP_W / (world as CanvasRect).width : 0;
|
|
110
|
-
const scaleY = showRadar ? MINIMAP_H / (world as CanvasRect).height : 0;
|
|
111
|
-
const toX = (x: number) => (x - (world as CanvasRect).x) * scaleX;
|
|
112
|
-
const toY = (y: number) => (y - (world as CanvasRect).y) * scaleY;
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<Surface
|
|
116
|
-
level='overlay'
|
|
117
|
-
pad='none'
|
|
118
|
-
className={cn(
|
|
119
|
-
'pointer-events-auto absolute z-[var(--z-sticky)] flex flex-col gap-2 rounded-lg p-2',
|
|
120
|
-
// Low-opacity by default so it never competes with the graph, full presence
|
|
121
|
-
// the moment the pointer or the keyboard is anywhere near it.
|
|
122
|
-
'opacity-70 transition-opacity duration-[var(--duration-fast)] hover:opacity-100 focus-within:opacity-100',
|
|
123
|
-
CORNER_CLASS[corner],
|
|
124
|
-
className,
|
|
125
|
-
)}
|
|
126
|
-
data-cb-canvas-hud=''
|
|
127
|
-
>
|
|
128
|
-
<div className='flex items-center gap-1'>
|
|
129
|
-
<IconButton size='sm' label='Fit to canvas' onClick={onFit}>
|
|
130
|
-
<Maximize2 />
|
|
131
|
-
</IconButton>
|
|
132
|
-
{onCenterFocus ? (
|
|
133
|
-
<IconButton
|
|
134
|
-
size='sm'
|
|
135
|
-
label={focusLabel ? `Centre on ${focusLabel}` : 'Centre on the focused item'}
|
|
136
|
-
onClick={onCenterFocus}
|
|
137
|
-
>
|
|
138
|
-
<Crosshair />
|
|
139
|
-
</IconButton>
|
|
140
|
-
) : null}
|
|
141
|
-
{onZoomChange ? (
|
|
142
|
-
<>
|
|
143
|
-
<IconButton
|
|
144
|
-
size='sm'
|
|
145
|
-
label='Zoom out'
|
|
146
|
-
disabled={zoom !== undefined && zoom <= minZoom}
|
|
147
|
-
onClick={() => onZoomChange(Math.max(minZoom, (zoom ?? 1) / 1.25))}
|
|
148
|
-
>
|
|
149
|
-
<Minus />
|
|
150
|
-
</IconButton>
|
|
151
|
-
<span className='min-w-10 text-center text-caption tabular-nums text-muted-foreground'>
|
|
152
|
-
{Math.round((zoom ?? 1) * 100)}%
|
|
153
|
-
</span>
|
|
154
|
-
<IconButton
|
|
155
|
-
size='sm'
|
|
156
|
-
label='Zoom in'
|
|
157
|
-
disabled={zoom !== undefined && zoom >= maxZoom}
|
|
158
|
-
onClick={() => onZoomChange(Math.min(maxZoom, (zoom ?? 1) * 1.25))}
|
|
159
|
-
>
|
|
160
|
-
<Plus />
|
|
161
|
-
</IconButton>
|
|
162
|
-
</>
|
|
163
|
-
) : null}
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
{showRadar ? (
|
|
167
|
-
<svg
|
|
168
|
-
ref={svgRef}
|
|
169
|
-
role={onMinimapJump ? 'button' : 'img'}
|
|
170
|
-
aria-label={onMinimapJump ? 'Radar — click to jump' : 'Radar'}
|
|
171
|
-
tabIndex={onMinimapJump ? 0 : undefined}
|
|
172
|
-
width={MINIMAP_W}
|
|
173
|
-
height={MINIMAP_H}
|
|
174
|
-
viewBox={`0 0 ${MINIMAP_W} ${MINIMAP_H}`}
|
|
175
|
-
onPointerDown={jump}
|
|
176
|
-
className={cn(
|
|
177
|
-
'rounded border border-border bg-surface-sunken',
|
|
178
|
-
onMinimapJump && 'cursor-pointer',
|
|
179
|
-
)}
|
|
180
|
-
>
|
|
181
|
-
<title>Radar</title>
|
|
182
|
-
{(points ?? []).map((p, i) => (
|
|
183
|
-
<circle
|
|
184
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: sampled dots have no stable identity
|
|
185
|
-
key={i}
|
|
186
|
-
cx={toX(p.x)}
|
|
187
|
-
cy={toY(p.y)}
|
|
188
|
-
r={p.focus ? 2.5 : 1.4}
|
|
189
|
-
fill={p.color ?? 'var(--muted-foreground)'}
|
|
190
|
-
opacity={p.focus ? 1 : 0.55}
|
|
191
|
-
/>
|
|
192
|
-
))}
|
|
193
|
-
{viewport ? (
|
|
194
|
-
<rect
|
|
195
|
-
x={toX(viewport.x)}
|
|
196
|
-
y={toY(viewport.y)}
|
|
197
|
-
width={Math.max(2, viewport.width * scaleX)}
|
|
198
|
-
height={Math.max(2, viewport.height * scaleY)}
|
|
199
|
-
fill='var(--accent)'
|
|
200
|
-
fillOpacity={0.12}
|
|
201
|
-
stroke='var(--accent)'
|
|
202
|
-
strokeWidth={1}
|
|
203
|
-
data-cb-hud-viewport=''
|
|
204
|
-
/>
|
|
205
|
-
) : null}
|
|
206
|
-
</svg>
|
|
207
|
-
) : null}
|
|
208
|
-
</Surface>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { fitLabel } from './fitLabel';
|
|
3
|
-
import { lodForNode, lodForZoom, lodShowsDetail, lodShowsText } from './lod';
|
|
4
|
-
|
|
5
|
-
// A deterministic measurer: 6px per character at fontSize 12, scaling linearly.
|
|
6
|
-
const measure = (t: string, size: number) => t.length * size * 0.5;
|
|
7
|
-
|
|
8
|
-
describe('fitLabel — DESIGN.md §5, in the order the clause states', () => {
|
|
9
|
-
test('a name that fits is left alone', () => {
|
|
10
|
-
const fit = fitLabel('Ada Lovelace', { width: 200, fontSize: 12, measure });
|
|
11
|
-
expect(fit.lines).toEqual(['Ada Lovelace']);
|
|
12
|
-
expect(fit.strategy).toBe('as-is');
|
|
13
|
-
expect(fit.overflow).toBe(false);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('middle names go before anything else does', () => {
|
|
17
|
-
// "Mary Anne Beth Sullivan" is 23 chars = 138px; "Mary Sullivan" is 13 = 78px.
|
|
18
|
-
const fit = fitLabel('Mary Anne Beth Sullivan', { width: 90, fontSize: 12, measure });
|
|
19
|
-
expect(fit.lines).toEqual(['Mary Sullivan']);
|
|
20
|
-
expect(fit.strategy).toBe('stripped');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('then a clean second line, never a mid-word cut', () => {
|
|
24
|
-
const fit = fitLabel('Bartholomew Wickersham', { width: 80, fontSize: 12, measure });
|
|
25
|
-
expect(fit.lines.length).toBe(2);
|
|
26
|
-
expect(fit.lines.join(' ')).toBe('Bartholomew Wickersham');
|
|
27
|
-
// The load-bearing property: nothing is chopped.
|
|
28
|
-
for (const line of fit.lines) expect(line.endsWith('…')).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('then the type scales down, in whole pixels', () => {
|
|
32
|
-
// 11 chars: 66px at 12, 55px at 10 — so it lands on 10 and stops there.
|
|
33
|
-
const fit = fitLabel('Bartholomew', { width: 58, fontSize: 12, measure });
|
|
34
|
-
expect(fit.strategy).toBe('scaled');
|
|
35
|
-
expect(Number.isInteger(fit.fontSize)).toBe(true);
|
|
36
|
-
expect(fit.fontSize).toBeLessThan(12);
|
|
37
|
-
expect(measure(fit.lines[0] as string, fit.fontSize)).toBeLessThanOrEqual(58);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('the last rung is a first name and a last initial', () => {
|
|
41
|
-
// "Alexander Wickersham" is 20 chars and will not scale into 62px at the 10px
|
|
42
|
-
// floor; "Alexander W." is 12 chars = 60px, which does.
|
|
43
|
-
const fit = fitLabel('Alexander Wickersham', { width: 62, fontSize: 12, measure, maxLines: 1 });
|
|
44
|
-
expect(fit.lines).toEqual(['Alexander W.']);
|
|
45
|
-
expect(fit.strategy).toBe('initials');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('a box too small for even that reports overflow, so the caller draws a glyph', () => {
|
|
49
|
-
const fit = fitLabel('Bartholomew Wickersham', { width: 4, fontSize: 12, measure });
|
|
50
|
-
expect(fit.overflow).toBe(true);
|
|
51
|
-
expect(fit.strategy).toBe('overflow');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('empty and zero-width inputs do not throw', () => {
|
|
55
|
-
expect(fitLabel('', { width: 100, fontSize: 12 }).lines).toEqual(['']);
|
|
56
|
-
expect(fitLabel('x', { width: 0, fontSize: 12 }).overflow).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('lodForZoom — DESIGN.md §7 draws the line at 50%', () => {
|
|
61
|
-
test('full detail at 100%, names only in between, glyphs below half', () => {
|
|
62
|
-
expect(lodForZoom(1)).toBe('full');
|
|
63
|
-
expect(lodForZoom(0.6)).toBe('compact');
|
|
64
|
-
expect(lodForZoom(0.49)).toBe('glyph');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('the 50% boundary itself still shows the name', () => {
|
|
68
|
-
expect(lodForZoom(0.5)).toBe('compact');
|
|
69
|
-
expect(lodShowsText(lodForZoom(0.5))).toBe(true);
|
|
70
|
-
expect(lodShowsDetail(lodForZoom(0.5))).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('a hovered or focused node keeps its label at any zoom', () => {
|
|
74
|
-
// "until zoomed in OR HOVERED over" — the half that keeps a glyph field usable.
|
|
75
|
-
expect(lodForNode(0.1, false)).toBe('glyph');
|
|
76
|
-
expect(lodForNode(0.1, true)).toBe('full');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('a nonsense zoom degrades to full detail rather than to nothing', () => {
|
|
80
|
-
expect(lodForZoom(Number.NaN)).toBe('full');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fitting a human name into a node box without a harsh cut.
|
|
3
|
-
*
|
|
4
|
-
* `DESIGN.md` §5: *"Node diagrams, map elements, or tree visualizations displaying
|
|
5
|
-
* user identities must never truncate names with harsh, broken text string cuts.
|
|
6
|
-
* Use responsive font scaling, wrap long strings onto a clean second line, or
|
|
7
|
-
* optimize middle-initial stripping to keep names human-readable."*
|
|
8
|
-
*
|
|
9
|
-
* That is three strategies in a stated order, and every canvas in the fleet was
|
|
10
|
-
* improvising one of them (family's `TreeCanvas` does its own; the atlas does
|
|
11
|
-
* none). This is the shared ladder, as a pure function with no DOM, so a `<text>`
|
|
12
|
-
* in SVG, a 2D canvas and an absolutely-positioned `<div>` all fit labels the same
|
|
13
|
-
* way and it can be tested without a browser.
|
|
14
|
-
*
|
|
15
|
-
* @see lodForZoom — below ~50% zoom the answer is not a smaller label, it is a glyph.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export type FitStrategy = 'as-is' | 'stripped' | 'wrapped' | 'scaled' | 'initials' | 'overflow';
|
|
19
|
-
|
|
20
|
-
export interface FitLabelOptions {
|
|
21
|
-
/** Usable width of the node box, in px. */
|
|
22
|
-
width: number;
|
|
23
|
-
/** The size the label would like to be, in px. */
|
|
24
|
-
fontSize: number;
|
|
25
|
-
/** How many lines the box can hold. Default 2 — the "clean second line". */
|
|
26
|
-
maxLines?: number;
|
|
27
|
-
/** Never scale below this. Default 10px, which is `DESIGN.md` §4's floor minus
|
|
28
|
-
* the two px a canvas label is allowed as a non-body-text glyph label. */
|
|
29
|
-
minFontSize?: number;
|
|
30
|
-
/**
|
|
31
|
-
* Width of `text` at `fontSize`. The default is a linear approximation
|
|
32
|
-
* (0.52em average advance, which is close for a UI sans at these sizes); a
|
|
33
|
-
* caller holding a real `CanvasRenderingContext2D` should pass `measureText`
|
|
34
|
-
* so the fit is exact.
|
|
35
|
-
*/
|
|
36
|
-
measure?: (text: string, fontSize: number) => number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface FittedLabel {
|
|
40
|
-
/** One entry per rendered line. Never empty. */
|
|
41
|
-
lines: string[];
|
|
42
|
-
/** The size to actually draw at. */
|
|
43
|
-
fontSize: number;
|
|
44
|
-
/** What the ladder had to do — surfaced so a caller can offer the full name in a
|
|
45
|
-
* hint exactly when something was dropped, rather than always or never. */
|
|
46
|
-
strategy: FitStrategy;
|
|
47
|
-
/** True when even the last rung does not fit. Draw a glyph, not an ellipsis. */
|
|
48
|
-
overflow: boolean;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const defaultMeasure = (text: string, fontSize: number) => text.length * fontSize * 0.52;
|
|
52
|
-
|
|
53
|
-
/** `Mary Anne Beth Sullivan` → `Mary Sullivan`; drops middles, keeps first + last. */
|
|
54
|
-
function stripMiddles(parts: string[]): string[] {
|
|
55
|
-
if (parts.length <= 2) return parts;
|
|
56
|
-
return [parts[0] as string, parts[parts.length - 1] as string];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** `Mary Sullivan` → `Mary S.` — the last rung before giving up on text entirely. */
|
|
60
|
-
function toInitialForm(parts: string[]): string {
|
|
61
|
-
if (parts.length === 0) return '';
|
|
62
|
-
if (parts.length === 1) return parts[0] as string;
|
|
63
|
-
const first = parts[0] as string;
|
|
64
|
-
const last = parts[parts.length - 1] as string;
|
|
65
|
-
return `${first} ${last.charAt(0).toUpperCase()}.`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Greedy word wrap into at most `maxLines`; returns null if it does not fit. */
|
|
69
|
-
function wrap(
|
|
70
|
-
words: string[],
|
|
71
|
-
width: number,
|
|
72
|
-
fontSize: number,
|
|
73
|
-
maxLines: number,
|
|
74
|
-
measure: (t: string, s: number) => number,
|
|
75
|
-
): string[] | null {
|
|
76
|
-
const lines: string[] = [];
|
|
77
|
-
let current = '';
|
|
78
|
-
for (const word of words) {
|
|
79
|
-
const candidate = current ? `${current} ${word}` : word;
|
|
80
|
-
if (measure(candidate, fontSize) <= width || !current) {
|
|
81
|
-
current = candidate;
|
|
82
|
-
} else {
|
|
83
|
-
lines.push(current);
|
|
84
|
-
current = word;
|
|
85
|
-
if (lines.length >= maxLines) return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (current) lines.push(current);
|
|
89
|
-
if (lines.length === 0 || lines.length > maxLines) return null;
|
|
90
|
-
return lines.every((l) => measure(l, fontSize) <= width) ? lines : null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Fit `text` into `width`, in `DESIGN.md` §5's stated order: as-is → drop middle
|
|
95
|
-
* names → wrap onto a second line → scale the type down → first name + last
|
|
96
|
-
* initial → give up and tell the caller to draw a glyph.
|
|
97
|
-
*/
|
|
98
|
-
export function fitLabel(text: string, options: FitLabelOptions): FittedLabel {
|
|
99
|
-
const {
|
|
100
|
-
width,
|
|
101
|
-
fontSize,
|
|
102
|
-
maxLines = 2,
|
|
103
|
-
minFontSize = 10,
|
|
104
|
-
measure = defaultMeasure,
|
|
105
|
-
} = options;
|
|
106
|
-
const clean = String(text ?? '').trim().replace(/\s+/g, ' ');
|
|
107
|
-
if (!clean) return { lines: [''], fontSize, strategy: 'as-is', overflow: false };
|
|
108
|
-
if (width <= 0) return { lines: [clean], fontSize, strategy: 'overflow', overflow: true };
|
|
109
|
-
|
|
110
|
-
if (measure(clean, fontSize) <= width) {
|
|
111
|
-
return { lines: [clean], fontSize, strategy: 'as-is', overflow: false };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const parts = clean.split(' ');
|
|
115
|
-
const stripped = stripMiddles(parts).join(' ');
|
|
116
|
-
if (stripped !== clean && measure(stripped, fontSize) <= width) {
|
|
117
|
-
return { lines: [stripped], fontSize, strategy: 'stripped', overflow: false };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (maxLines > 1) {
|
|
121
|
-
const wrapped = wrap(stripped.split(' '), width, fontSize, maxLines, measure);
|
|
122
|
-
if (wrapped) {
|
|
123
|
-
return {
|
|
124
|
-
lines: wrapped,
|
|
125
|
-
fontSize,
|
|
126
|
-
strategy: stripped === clean ? 'wrapped' : 'stripped',
|
|
127
|
-
overflow: false,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Scale down in whole pixels — a fractional ladder produces sizes that differ
|
|
133
|
-
// between two adjacent nodes by less than the eye can see, for no extra fit.
|
|
134
|
-
for (let size = fontSize - 1; size >= minFontSize; size--) {
|
|
135
|
-
const wrapped =
|
|
136
|
-
maxLines > 1
|
|
137
|
-
? wrap(stripped.split(' '), width, size, maxLines, measure)
|
|
138
|
-
: measure(stripped, size) <= width
|
|
139
|
-
? [stripped]
|
|
140
|
-
: null;
|
|
141
|
-
if (wrapped) return { lines: wrapped, fontSize: size, strategy: 'scaled', overflow: false };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const initials = toInitialForm(parts);
|
|
145
|
-
if (measure(initials, minFontSize) <= width) {
|
|
146
|
-
return { lines: [initials], fontSize: minFontSize, strategy: 'initials', overflow: false };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return { lines: [initials], fontSize: minFontSize, strategy: 'overflow', overflow: true };
|
|
150
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
CanvasNavHUD,
|
|
3
|
-
type CanvasMinimapPoint,
|
|
4
|
-
type CanvasNavHUDProps,
|
|
5
|
-
type CanvasRect,
|
|
6
|
-
} from './CanvasNavHUD';
|
|
7
|
-
export { fitLabel, type FitLabelOptions, type FittedLabel, type FitStrategy } from './fitLabel';
|
|
8
|
-
export {
|
|
9
|
-
type CanvasLod,
|
|
10
|
-
lodForNode,
|
|
11
|
-
lodForZoom,
|
|
12
|
-
lodShowsDetail,
|
|
13
|
-
lodShowsText,
|
|
14
|
-
type LodThresholds,
|
|
15
|
-
} from './lod';
|
package/src/react/canvas/lod.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adaptive level of detail for an infinite canvas.
|
|
3
|
-
*
|
|
4
|
-
* `DESIGN.md` §7: *"Large-scale interactive graphs … must not render full textual
|
|
5
|
-
* strings when zoomed out past 50%. Dynamically scale down information density:
|
|
6
|
-
* collapse names or details entirely, displaying only clean geometric glyph nodes
|
|
7
|
-
* or color-coded markers until zoomed in or hovered over."*
|
|
8
|
-
*
|
|
9
|
-
* `UX_STANDARDS.md` §3.1–§3.2 states the same thing as a principle — *"zooming out
|
|
10
|
-
* changes the DESIGN, not the scale"* — and nothing in the fleet implemented it, so
|
|
11
|
-
* every canvas shrinks its text into illegible grey fur instead. This is the shared
|
|
12
|
-
* decision, kept as a pure function because the interesting part is the THRESHOLDS
|
|
13
|
-
* and those must be identical in the tree, the atlas and the orch lanes.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* `full` — name plus its secondary line, portraits, badges.
|
|
18
|
-
* `compact` — the name only, at the node's own size.
|
|
19
|
-
* `glyph` — no text at all: a geometric marker, coloured by category. Labels
|
|
20
|
-
* come back on hover/focus, which is what keeps this navigable.
|
|
21
|
-
*/
|
|
22
|
-
export type CanvasLod = 'full' | 'compact' | 'glyph';
|
|
23
|
-
|
|
24
|
-
export interface LodThresholds {
|
|
25
|
-
/** At or above this zoom, everything is drawn. Default 0.75. */
|
|
26
|
-
full?: number;
|
|
27
|
-
/** At or above this zoom, the name survives. Default 0.5 — DESIGN.md §7's line. */
|
|
28
|
-
compact?: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** The level of detail a canvas should draw at `zoom` (1 = 100%). */
|
|
32
|
-
export function lodForZoom(zoom: number, thresholds: LodThresholds = {}): CanvasLod {
|
|
33
|
-
const full = thresholds.full ?? 0.75;
|
|
34
|
-
const compact = thresholds.compact ?? 0.5;
|
|
35
|
-
if (!Number.isFinite(zoom)) return 'full';
|
|
36
|
-
if (zoom >= full) return 'full';
|
|
37
|
-
if (zoom >= compact) return 'compact';
|
|
38
|
-
return 'glyph';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Whether a node at this level of detail should render any text at all. */
|
|
42
|
-
export const lodShowsText = (lod: CanvasLod): boolean => lod !== 'glyph';
|
|
43
|
-
|
|
44
|
-
/** Whether a node at this level of detail should render its secondary line. */
|
|
45
|
-
export const lodShowsDetail = (lod: CanvasLod): boolean => lod === 'full';
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A hovered or focused node always renders its label, whatever the zoom — that is
|
|
49
|
-
* the "until zoomed in OR HOVERED over" half of the clause, and it is the thing
|
|
50
|
-
* that keeps a glyph field usable rather than merely pretty.
|
|
51
|
-
*/
|
|
52
|
-
export const lodForNode = (zoom: number, active: boolean, thresholds?: LodThresholds): CanvasLod =>
|
|
53
|
-
active ? 'full' : lodForZoom(zoom, thresholds);
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { HeadModel } from 'cwip/seo';
|
|
2
|
-
import { createContext, type ReactNode, useContext, useEffect, useMemo } from 'react';
|
|
3
|
-
import { applyHeadToDocument } from './applyHeadToDocument';
|
|
4
|
-
|
|
5
|
-
export interface HeadProviderConfig {
|
|
6
|
-
// Applied under every route's model unless the route overrides the same field
|
|
7
|
-
// (route wins — see `useHead`).
|
|
8
|
-
default?: HeadModel;
|
|
9
|
-
siteUrl?: string;
|
|
10
|
-
titleTemplate?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const HeadContext = createContext<HeadProviderConfig>({});
|
|
14
|
-
|
|
15
|
-
export interface HeadProviderProps extends HeadProviderConfig {
|
|
16
|
-
children?: ReactNode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Provides the site-wide SEO defaults (base head + siteUrl + title template) that
|
|
20
|
-
// every `useHead` call merges under its own model. Mount once near the app root.
|
|
21
|
-
// Read the site-wide SEO config supplied by the nearest `HeadProvider` (siteUrl,
|
|
22
|
-
// title template, base head). Returns `{}` when no provider is mounted, so a
|
|
23
|
-
// consumer can fall back (e.g. to `window.location.origin`) without crashing.
|
|
24
|
-
export const useHeadConfig = (): HeadProviderConfig => useContext(HeadContext);
|
|
25
|
-
|
|
26
|
-
export const HeadProvider = ({ children, ...config }: HeadProviderProps) => {
|
|
27
|
-
const value = useMemo<HeadProviderConfig>(
|
|
28
|
-
() => ({
|
|
29
|
-
default: config.default,
|
|
30
|
-
siteUrl: config.siteUrl,
|
|
31
|
-
titleTemplate: config.titleTemplate,
|
|
32
|
-
}),
|
|
33
|
-
[config.default, config.siteUrl, config.titleTemplate],
|
|
34
|
-
);
|
|
35
|
-
return <HeadContext.Provider value={value}>{children}</HeadContext.Provider>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const mergeHead = (
|
|
39
|
-
base: HeadModel | undefined,
|
|
40
|
-
model: HeadModel,
|
|
41
|
-
titleTemplate?: string,
|
|
42
|
-
): HeadModel => ({
|
|
43
|
-
titleTemplate,
|
|
44
|
-
...base,
|
|
45
|
-
...model,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Reconciles `document.head` for the CURRENT route. Merges the provider's default
|
|
49
|
-
// head with `model` (model wins field-by-field), then applies it imperatively on
|
|
50
|
-
// mount/update. SSR-safe — a no-op when `document` is unavailable.
|
|
51
|
-
//
|
|
52
|
-
// Multi-caller semantics (last-writer-wins): if more than one component calls
|
|
53
|
-
// `useHead` while mounted at once, the effect that (re)runs LAST wins — its model
|
|
54
|
-
// fully replaces the previously-applied managed tags, because `applyHeadToDocument`
|
|
55
|
-
// removes any managed tag not present in the new render. Design routes so exactly
|
|
56
|
-
// one component per active route calls `useHead`; a nested component that also
|
|
57
|
-
// calls it will overwrite the route's head on every one of its own re-renders.
|
|
58
|
-
export const useHead = (model: HeadModel): void => {
|
|
59
|
-
const config = useContext(HeadContext);
|
|
60
|
-
const merged = mergeHead(config.default, model, config.titleTemplate);
|
|
61
|
-
const serialized = JSON.stringify(merged);
|
|
62
|
-
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
if (typeof document === 'undefined') return;
|
|
65
|
-
applyHeadToDocument(JSON.parse(serialized) as HeadModel);
|
|
66
|
-
}, [serialized]);
|
|
67
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { type HeadModel, renderHead, SEO_TAG_MARKER } from 'cwip/seo';
|
|
2
|
-
|
|
3
|
-
// Applies a HeadModel to a live document: sets `document.title` and upserts the
|
|
4
|
-
// managed <meta>/<link>/<script type="application/ld+json"> tags into <head>. Every
|
|
5
|
-
// tag it creates or updates carries SEO_TAG_MARKER so a later call can find and
|
|
6
|
-
// replace its own tags without ever touching app-authored markup, and so it can
|
|
7
|
-
// remove a managed tag that is no longer present in the new model. Idempotent:
|
|
8
|
-
// calling twice with the same model produces the same DOM, never duplicates.
|
|
9
|
-
//
|
|
10
|
-
// Built directly on cwip's `renderHead` output (a string of tags) so the client
|
|
11
|
-
// reconciler and every other delivery mechanism (prerender/edge/server) stay in
|
|
12
|
-
// sync on exactly what a HeadModel renders to — this module only does the DOM
|
|
13
|
-
// diffing, not the serialization.
|
|
14
|
-
export const applyHeadToDocument = (model: HeadModel, doc: Document = document): void => {
|
|
15
|
-
const html = renderHead(model, { marker: true });
|
|
16
|
-
const template = doc.createElement('template');
|
|
17
|
-
template.innerHTML = html;
|
|
18
|
-
const nextNodes = Array.from(template.content.childNodes).filter(
|
|
19
|
-
(node): node is Element => node.nodeType === 1,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
const title = nextNodes.find((node) => node.tagName === 'TITLE');
|
|
23
|
-
doc.title = title?.textContent ?? doc.title;
|
|
24
|
-
|
|
25
|
-
const previouslyManaged = Array.from(doc.head.querySelectorAll(`[${SEO_TAG_MARKER}]`));
|
|
26
|
-
const stillPresent = new Set<Element>();
|
|
27
|
-
|
|
28
|
-
for (const node of nextNodes) {
|
|
29
|
-
if (node.tagName === 'TITLE') continue; // handled via doc.title above
|
|
30
|
-
const match = previouslyManaged.find(
|
|
31
|
-
(existing) => !stillPresent.has(existing) && isSameTag(existing, node),
|
|
32
|
-
);
|
|
33
|
-
if (match) {
|
|
34
|
-
copyAttributes(match, node);
|
|
35
|
-
match.textContent = node.textContent;
|
|
36
|
-
stillPresent.add(match);
|
|
37
|
-
} else {
|
|
38
|
-
const clone = node.cloneNode(true) as Element;
|
|
39
|
-
doc.head.appendChild(clone);
|
|
40
|
-
stillPresent.add(clone);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
for (const existing of previouslyManaged) {
|
|
45
|
-
if (!stillPresent.has(existing)) existing.remove();
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Two tags are "the same" managed tag if they share a tag name and identifying key
|
|
50
|
-
// (meta name/property, link rel+href, or — for scripts/title — just the tag name,
|
|
51
|
-
// since a HeadModel carries at most one <title> and callers rarely emit more than
|
|
52
|
-
// one ld+json script; multiple ld+json blocks are matched positionally by falling
|
|
53
|
-
// through to a fresh append once earlier ones are claimed).
|
|
54
|
-
const isSameTag = (a: Element, b: Element): boolean => {
|
|
55
|
-
if (a.tagName !== b.tagName) return false;
|
|
56
|
-
if (a.tagName === 'META') {
|
|
57
|
-
return (
|
|
58
|
-
identity(a, 'name') === identity(b, 'name') &&
|
|
59
|
-
identity(a, 'property') === identity(b, 'property')
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
if (a.tagName === 'LINK') {
|
|
63
|
-
return identity(a, 'rel') === identity(b, 'rel') && identity(a, 'href') === identity(b, 'href');
|
|
64
|
-
}
|
|
65
|
-
return true; // SCRIPT (ld+json) — matched positionally among managed scripts
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const identity = (el: Element, attr: string): string => el.getAttribute(attr) ?? '';
|
|
69
|
-
|
|
70
|
-
const copyAttributes = (target: Element, source: Element): void => {
|
|
71
|
-
for (const attr of Array.from(target.attributes)) target.removeAttribute(attr.name);
|
|
72
|
-
for (const attr of Array.from(source.attributes)) target.setAttribute(attr.name, attr.value);
|
|
73
|
-
};
|
package/src/react/seo/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// cursedbelt/react/seo — a tiny, react-helmet-style head manager keyed to cwip's
|
|
2
|
-
// `HeadModel`. `HeadProvider` supplies site-wide defaults; a route calls `useHead`
|
|
3
|
-
// once to own `document.head` for as long as it's mounted. Dedicated subpath so an
|
|
4
|
-
// app that never touches SEO pays nothing for it.
|
|
5
|
-
export { applyHeadToDocument } from './applyHeadToDocument';
|
|
6
|
-
export {
|
|
7
|
-
HeadProvider,
|
|
8
|
-
type HeadProviderConfig,
|
|
9
|
-
type HeadProviderProps,
|
|
10
|
-
useHead,
|
|
11
|
-
useHeadConfig,
|
|
12
|
-
} from './HeadProvider';
|