@thomasfosterau/components 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +241 -56
- package/dist/canvas/VectorCanvas.svelte +320 -0
- package/dist/canvas/VectorCanvas.svelte.d.ts +105 -0
- package/dist/canvas/history.d.ts +105 -0
- package/dist/canvas/history.js +147 -0
- package/dist/canvas/index.d.ts +15 -0
- package/dist/canvas/index.js +14 -0
- package/dist/canvas/types.d.ts +91 -0
- package/dist/canvas/types.js +13 -0
- package/dist/canvas/utils.d.ts +66 -0
- package/dist/canvas/utils.js +114 -0
- package/dist/charts/Chart.svelte +300 -0
- package/dist/charts/Chart.svelte.d.ts +50 -0
- package/dist/charts/index.d.ts +11 -0
- package/dist/charts/index.js +10 -0
- package/dist/charts/types.d.ts +28 -0
- package/dist/charts/types.js +6 -0
- package/dist/citations/BookCitation.svelte +53 -0
- package/dist/citations/BookCitation.svelte.d.ts +41 -0
- package/dist/citations/ChapterCitation.svelte +38 -0
- package/dist/citations/ChapterCitation.svelte.d.ts +50 -0
- package/dist/citations/Citation.svelte +47 -0
- package/dist/citations/Citation.svelte.d.ts +24 -0
- package/dist/citations/CreditList.svelte +69 -0
- package/dist/citations/CreditList.svelte.d.ts +47 -0
- package/dist/citations/DOILink.svelte +28 -0
- package/dist/citations/DOILink.svelte.d.ts +19 -0
- package/dist/citations/Footnote.svelte +39 -0
- package/dist/citations/Footnote.svelte.d.ts +17 -0
- package/dist/citations/FootnoteList.svelte +36 -0
- package/dist/citations/FootnoteList.svelte.d.ts +21 -0
- package/dist/citations/ISBNLink.svelte +29 -0
- package/dist/citations/ISBNLink.svelte.d.ts +19 -0
- package/dist/citations/ISSNLink.svelte +29 -0
- package/dist/citations/ISSNLink.svelte.d.ts +19 -0
- package/dist/citations/Publisher.svelte +34 -0
- package/dist/citations/Publisher.svelte.d.ts +14 -0
- package/dist/citations/ReferenceList.svelte +53 -0
- package/dist/citations/ReferenceList.svelte.d.ts +21 -0
- package/dist/citations/ReportCitation.svelte +41 -0
- package/dist/citations/ReportCitation.svelte.d.ts +36 -0
- package/dist/citations/ScholarlyArticleCitation.svelte +64 -0
- package/dist/citations/ScholarlyArticleCitation.svelte.d.ts +41 -0
- package/dist/citations/ThesisCitation.svelte +35 -0
- package/dist/citations/ThesisCitation.svelte.d.ts +29 -0
- package/dist/citations/bibliography.svelte.d.ts +55 -0
- package/dist/citations/bibliography.svelte.js +78 -0
- package/dist/citations/citation.d.ts +71 -0
- package/dist/citations/citation.js +121 -0
- package/dist/citations/csl.d.ts +102 -0
- package/dist/citations/csl.js +20 -0
- package/dist/citations/index.d.ts +31 -0
- package/dist/citations/index.js +36 -0
- package/dist/citations/list-format.d.ts +34 -0
- package/dist/citations/list-format.js +40 -0
- package/dist/context.svelte.d.ts +46 -0
- package/dist/context.svelte.js +51 -0
- package/dist/data/ChatBubble.svelte +48 -0
- package/dist/data/ChatBubble.svelte.d.ts +33 -0
- package/dist/data/DataGrid.svelte +150 -0
- package/dist/data/DataGrid.svelte.d.ts +118 -0
- package/dist/data/DescriptionList.svelte +53 -0
- package/dist/data/DescriptionList.svelte.d.ts +26 -0
- package/dist/data/DescriptionRow.svelte +56 -0
- package/dist/data/DescriptionRow.svelte.d.ts +21 -0
- package/dist/data/DiffText.svelte +29 -0
- package/dist/data/DiffText.svelte.d.ts +19 -0
- package/dist/data/ItemList.svelte +80 -0
- package/dist/data/ItemList.svelte.d.ts +52 -0
- package/dist/data/LinkListItem.svelte +46 -0
- package/dist/data/LinkListItem.svelte.d.ts +21 -0
- package/dist/data/MessageThread.svelte +29 -0
- package/dist/data/MessageThread.svelte.d.ts +17 -0
- package/dist/data/Pagination.svelte +136 -0
- package/dist/data/Pagination.svelte.d.ts +79 -0
- package/dist/data/PaginationFooter.svelte +42 -0
- package/dist/data/PaginationFooter.svelte.d.ts +61 -0
- package/dist/data/ResultCount.svelte +27 -0
- package/dist/data/ResultCount.svelte.d.ts +18 -0
- package/dist/data/Table.svelte +151 -0
- package/dist/data/Table.svelte.d.ts +75 -0
- package/dist/data/TreeList.svelte +41 -0
- package/dist/data/TreeList.svelte.d.ts +17 -0
- package/dist/data/TreeListItem.svelte +29 -0
- package/dist/data/TreeListItem.svelte.d.ts +10 -0
- package/dist/data/description-context.d.ts +39 -0
- package/dist/data/description-context.js +36 -0
- package/dist/data/diff.d.ts +14 -0
- package/dist/data/diff.js +10 -0
- package/dist/data/index.d.ts +37 -0
- package/dist/data/index.js +37 -0
- package/dist/data/list-item.d.ts +30 -0
- package/dist/data/list-item.js +19 -0
- package/dist/data/pagination.d.ts +81 -0
- package/dist/data/pagination.js +79 -0
- package/dist/data/table-types.d.ts +71 -0
- package/dist/data/table-types.js +35 -0
- package/dist/feedback/ConfirmDialog.svelte +79 -0
- package/dist/feedback/ConfirmDialog.svelte.d.ts +82 -0
- package/dist/feedback/Dialog.svelte +47 -0
- package/dist/feedback/Dialog.svelte.d.ts +36 -0
- package/dist/feedback/DialogFrame.svelte +64 -0
- package/dist/feedback/DialogFrame.svelte.d.ts +23 -0
- package/dist/feedback/Drawer.svelte +43 -0
- package/dist/feedback/Drawer.svelte.d.ts +43 -0
- package/dist/feedback/LoadingBoundary.svelte +61 -0
- package/dist/feedback/LoadingBoundary.svelte.d.ts +34 -0
- package/dist/feedback/ProgressBar.svelte +46 -0
- package/dist/feedback/ProgressBar.svelte.d.ts +28 -0
- package/dist/feedback/SegmentBar.svelte +83 -0
- package/dist/feedback/SegmentBar.svelte.d.ts +37 -0
- package/dist/feedback/StatTile.svelte +77 -0
- package/dist/feedback/StatTile.svelte.d.ts +50 -0
- package/dist/feedback/Stats.svelte +18 -0
- package/dist/feedback/Stats.svelte.d.ts +16 -0
- package/dist/feedback/Steps.svelte +72 -0
- package/dist/feedback/Steps.svelte.d.ts +61 -0
- package/dist/feedback/Timeline.svelte +86 -0
- package/dist/feedback/Timeline.svelte.d.ts +55 -0
- package/dist/feedback/Toaster.svelte +78 -0
- package/dist/feedback/Toaster.svelte.d.ts +32 -0
- package/dist/feedback/index.d.ts +34 -0
- package/dist/feedback/index.js +29 -0
- package/dist/feedback/sizes.d.ts +41 -0
- package/dist/feedback/sizes.js +69 -0
- package/dist/feedback/toaster.d.ts +48 -0
- package/dist/feedback/toaster.js +29 -0
- package/dist/fields/Calendar.svelte +60 -0
- package/dist/fields/Calendar.svelte.d.ts +11 -0
- package/dist/fields/CalendarMonths.svelte +86 -0
- package/dist/fields/CalendarMonths.svelte.d.ts +30 -0
- package/dist/fields/Checkbox.svelte +33 -0
- package/dist/fields/Checkbox.svelte.d.ts +30 -0
- package/dist/fields/CheckboxGroup.svelte +41 -0
- package/dist/fields/CheckboxGroup.svelte.d.ts +40 -0
- package/dist/fields/Combobox.svelte +229 -0
- package/dist/fields/Combobox.svelte.d.ts +133 -0
- package/dist/fields/DateField.svelte +37 -0
- package/dist/fields/DateField.svelte.d.ts +20 -0
- package/dist/fields/DateInput.svelte +31 -0
- package/dist/fields/DateInput.svelte.d.ts +35 -0
- package/dist/fields/DatePicker.svelte +47 -0
- package/dist/fields/DatePicker.svelte.d.ts +25 -0
- package/dist/fields/DateRangePicker.svelte +70 -0
- package/dist/fields/DateRangePicker.svelte.d.ts +43 -0
- package/dist/fields/Dropzone.svelte +232 -0
- package/dist/fields/Dropzone.svelte.d.ts +103 -0
- package/dist/fields/FieldErrors.svelte +30 -0
- package/dist/fields/FieldErrors.svelte.d.ts +11 -0
- package/dist/fields/Fieldset.svelte +53 -0
- package/dist/fields/Fieldset.svelte.d.ts +27 -0
- package/dist/fields/FileInput.svelte +68 -0
- package/dist/fields/FileInput.svelte.d.ts +39 -0
- package/dist/fields/FormField.svelte +78 -0
- package/dist/fields/FormField.svelte.d.ts +29 -0
- package/dist/fields/ImageInput.svelte +87 -0
- package/dist/fields/ImageInput.svelte.d.ts +45 -0
- package/dist/fields/MultiSelect.svelte +112 -0
- package/dist/fields/MultiSelect.svelte.d.ts +75 -0
- package/dist/fields/NumberField.svelte +190 -0
- package/dist/fields/NumberField.svelte.d.ts +76 -0
- package/dist/fields/PasswordInput.svelte +95 -0
- package/dist/fields/PasswordInput.svelte.d.ts +58 -0
- package/dist/fields/PickerPopover.svelte +47 -0
- package/dist/fields/PickerPopover.svelte.d.ts +16 -0
- package/dist/fields/PinInput.svelte +83 -0
- package/dist/fields/PinInput.svelte.d.ts +66 -0
- package/dist/fields/PushToggle.svelte +93 -0
- package/dist/fields/PushToggle.svelte.d.ts +55 -0
- package/dist/fields/RadioGroup.svelte +88 -0
- package/dist/fields/RadioGroup.svelte.d.ts +41 -0
- package/dist/fields/RangeCalendar.svelte +51 -0
- package/dist/fields/RangeCalendar.svelte.d.ts +23 -0
- package/dist/fields/Rating.svelte +127 -0
- package/dist/fields/Rating.svelte.d.ts +86 -0
- package/dist/fields/SearchInput.svelte +58 -0
- package/dist/fields/SearchInput.svelte.d.ts +33 -0
- package/dist/fields/SegmentGroup.svelte +91 -0
- package/dist/fields/SegmentGroup.svelte.d.ts +20 -0
- package/dist/fields/Select.svelte +36 -0
- package/dist/fields/Select.svelte.d.ts +35 -0
- package/dist/fields/Slider.svelte +194 -0
- package/dist/fields/Slider.svelte.d.ts +100 -0
- package/dist/fields/TagsInput.svelte +256 -0
- package/dist/fields/TagsInput.svelte.d.ts +102 -0
- package/dist/fields/TextInput.svelte +26 -0
- package/dist/fields/TextInput.svelte.d.ts +36 -0
- package/dist/fields/Textarea.svelte +22 -0
- package/dist/fields/Textarea.svelte.d.ts +31 -0
- package/dist/fields/TimeField.svelte +30 -0
- package/dist/fields/TimeField.svelte.d.ts +19 -0
- package/dist/fields/Toggle.svelte +72 -0
- package/dist/fields/Toggle.svelte.d.ts +38 -0
- package/dist/fields/VideoInput.svelte +35 -0
- package/dist/fields/VideoInput.svelte.d.ts +46 -0
- package/dist/fields/calendar-props.d.ts +119 -0
- package/dist/fields/calendar-props.js +51 -0
- package/dist/fields/dates.d.ts +28 -0
- package/dist/fields/dates.js +26 -0
- package/dist/fields/field-types.d.ts +80 -0
- package/dist/fields/field-types.js +92 -0
- package/dist/fields/file-validation.d.ts +30 -0
- package/dist/fields/file-validation.js +51 -0
- package/dist/fields/index.d.ts +43 -0
- package/dist/fields/index.js +42 -0
- package/dist/fields/latest-search.svelte.d.ts +38 -0
- package/dist/fields/latest-search.svelte.js +85 -0
- package/dist/fields/number-format.d.ts +52 -0
- package/dist/fields/number-format.js +89 -0
- package/dist/fields/segmented-field.d.ts +128 -0
- package/dist/fields/segmented-field.js +52 -0
- package/dist/fields/slider-keys.d.ts +21 -0
- package/dist/fields/slider-keys.js +37 -0
- package/dist/formatting/Countdown.svelte +110 -0
- package/dist/formatting/Countdown.svelte.d.ts +35 -0
- package/dist/formatting/DateTime.svelte +35 -0
- package/dist/formatting/DateTime.svelte.d.ts +26 -0
- package/dist/formatting/Duration.svelte +18 -0
- package/dist/formatting/Duration.svelte.d.ts +20 -0
- package/dist/formatting/FormatList.svelte +31 -0
- package/dist/formatting/FormatList.svelte.d.ts +46 -0
- package/dist/formatting/Highlight.svelte +51 -0
- package/dist/formatting/Highlight.svelte.d.ts +19 -0
- package/dist/formatting/Number.svelte +35 -0
- package/dist/formatting/Number.svelte.d.ts +43 -0
- package/dist/formatting/RelativeTime.svelte +69 -0
- package/dist/formatting/RelativeTime.svelte.d.ts +26 -0
- package/dist/formatting/Year.svelte +39 -0
- package/dist/formatting/Year.svelte.d.ts +21 -0
- package/dist/formatting/duration.d.ts +44 -0
- package/dist/formatting/duration.js +125 -0
- package/dist/formatting/index.d.ts +15 -0
- package/dist/formatting/index.js +17 -0
- package/dist/forms/EntityForm.svelte +185 -0
- package/dist/forms/EntityForm.svelte.d.ts +66 -0
- package/dist/forms/FormActions.svelte +29 -0
- package/dist/forms/FormActions.svelte.d.ts +37 -0
- package/dist/forms/FormErrors.svelte +21 -0
- package/dist/forms/FormErrors.svelte.d.ts +16 -0
- package/dist/forms/HiddenField.svelte +22 -0
- package/dist/forms/HiddenField.svelte.d.ts +11 -0
- package/dist/forms/SearchForm.svelte +43 -0
- package/dist/forms/SearchForm.svelte.d.ts +36 -0
- package/dist/forms/entity-form.d.ts +12 -0
- package/dist/forms/entity-form.js +12 -0
- package/dist/forms/index.d.ts +14 -0
- package/dist/forms/index.js +14 -0
- package/dist/hotkeys/HotkeyBadge.svelte +39 -0
- package/dist/hotkeys/HotkeyBadge.svelte.d.ts +25 -0
- package/dist/hotkeys/HotkeyHelpDialog.svelte +68 -0
- package/dist/hotkeys/HotkeyHelpDialog.svelte.d.ts +35 -0
- package/dist/hotkeys/HotkeyProvider.svelte +92 -0
- package/dist/hotkeys/HotkeyProvider.svelte.d.ts +4 -0
- package/dist/hotkeys/context.svelte.d.ts +47 -0
- package/dist/hotkeys/context.svelte.js +72 -0
- package/dist/hotkeys/index.d.ts +28 -0
- package/dist/hotkeys/index.js +27 -0
- package/dist/hotkeys/match.d.ts +65 -0
- package/dist/hotkeys/match.js +135 -0
- package/dist/hotkeys/registry.svelte.d.ts +55 -0
- package/dist/hotkeys/registry.svelte.js +110 -0
- package/dist/hotkeys/types.d.ts +74 -0
- package/dist/hotkeys/types.js +18 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/infobox/Infobox.svelte +79 -0
- package/dist/infobox/Infobox.svelte.d.ts +33 -0
- package/dist/infobox/InfoboxHeader.svelte +35 -0
- package/dist/infobox/InfoboxHeader.svelte.d.ts +18 -0
- package/dist/infobox/InfoboxImage.svelte +38 -0
- package/dist/infobox/InfoboxImage.svelte.d.ts +21 -0
- package/dist/infobox/InfoboxRow.svelte +50 -0
- package/dist/infobox/InfoboxRow.svelte.d.ts +25 -0
- package/dist/infobox/InfoboxSection.svelte +24 -0
- package/dist/infobox/InfoboxSection.svelte.d.ts +12 -0
- package/dist/infobox/index.d.ts +21 -0
- package/dist/infobox/index.js +20 -0
- package/dist/infobox/types.d.ts +86 -0
- package/dist/infobox/types.js +27 -0
- package/dist/layout/AppShell.svelte +50 -0
- package/dist/layout/AppShell.svelte.d.ts +28 -0
- package/dist/layout/Breadcrumbs.svelte +52 -0
- package/dist/layout/Breadcrumbs.svelte.d.ts +18 -0
- package/dist/layout/Cluster.svelte +28 -0
- package/dist/layout/Cluster.svelte.d.ts +27 -0
- package/dist/layout/DetailLayout.svelte +50 -0
- package/dist/layout/DetailLayout.svelte.d.ts +19 -0
- package/dist/layout/FilterBar.svelte +40 -0
- package/dist/layout/FilterBar.svelte.d.ts +12 -0
- package/dist/layout/Fits.svelte +27 -0
- package/dist/layout/Fits.svelte.d.ts +13 -0
- package/dist/layout/Flow.svelte +46 -0
- package/dist/layout/Flow.svelte.d.ts +16 -0
- package/dist/layout/Footer.svelte +32 -0
- package/dist/layout/Footer.svelte.d.ts +12 -0
- package/dist/layout/Grid.svelte +23 -0
- package/dist/layout/Grid.svelte.d.ts +26 -0
- package/dist/layout/HStack.svelte +12 -0
- package/dist/layout/HStack.svelte.d.ts +34 -0
- package/dist/layout/Hero.svelte +76 -0
- package/dist/layout/Hero.svelte.d.ts +36 -0
- package/dist/layout/Main.svelte +30 -0
- package/dist/layout/Main.svelte.d.ts +15 -0
- package/dist/layout/Navbar.svelte +68 -0
- package/dist/layout/Navbar.svelte.d.ts +31 -0
- package/dist/layout/PageHeader.svelte +77 -0
- package/dist/layout/PageHeader.svelte.d.ts +33 -0
- package/dist/layout/Section.svelte +72 -0
- package/dist/layout/Section.svelte.d.ts +40 -0
- package/dist/layout/Sidebar.svelte +54 -0
- package/dist/layout/Sidebar.svelte.d.ts +27 -0
- package/dist/layout/Spacer.svelte +12 -0
- package/dist/layout/Spacer.svelte.d.ts +6 -0
- package/dist/layout/Tabs.svelte +85 -0
- package/dist/layout/Tabs.svelte.d.ts +42 -0
- package/dist/layout/VStack.svelte +12 -0
- package/dist/layout/VStack.svelte.d.ts +29 -0
- package/dist/layout/index.d.ts +38 -0
- package/dist/layout/index.js +38 -0
- package/dist/layout/space.d.ts +44 -0
- package/dist/layout/space.js +76 -0
- package/dist/layout/types.d.ts +52 -0
- package/dist/layout/types.js +27 -0
- package/dist/links/ContextLinks.svelte +28 -0
- package/dist/links/ContextLinks.svelte.d.ts +20 -0
- package/dist/links/EmailLink.svelte +26 -0
- package/dist/links/EmailLink.svelte.d.ts +20 -0
- package/dist/links/index.d.ts +7 -0
- package/dist/links/index.js +7 -0
- package/dist/media/Figure.svelte +26 -0
- package/dist/media/Figure.svelte.d.ts +12 -0
- package/dist/media/Image.svelte +76 -0
- package/dist/media/Image.svelte.d.ts +41 -0
- package/dist/media/Video.svelte +65 -0
- package/dist/media/Video.svelte.d.ts +29 -0
- package/dist/media/image-dimensions.d.ts +28 -0
- package/dist/media/image-dimensions.js +34 -0
- package/dist/media/index.d.ts +10 -0
- package/dist/media/index.js +10 -0
- package/dist/search/CommandPalette.svelte +257 -0
- package/dist/search/CommandPalette.svelte.d.ts +59 -0
- package/dist/search/index.d.ts +13 -0
- package/dist/search/index.js +13 -0
- package/dist/search/types.d.ts +40 -0
- package/dist/search/types.js +20 -0
- package/dist/seo/Head.svelte +190 -0
- package/dist/seo/Head.svelte.d.ts +86 -0
- package/dist/seo/JsonLd.svelte +30 -0
- package/dist/seo/JsonLd.svelte.d.ts +20 -0
- package/dist/seo/index.d.ts +18 -0
- package/dist/seo/index.js +17 -0
- package/dist/seo/json-ld.d.ts +33 -0
- package/dist/seo/json-ld.js +29 -0
- package/dist/seo/og-image.d.ts +72 -0
- package/dist/seo/og-image.js +82 -0
- package/dist/seo/types.d.ts +43 -0
- package/dist/seo/types.js +6 -0
- package/dist/theme.css +161 -0
- package/dist/tips/PopoverTip.svelte +80 -0
- package/dist/tips/PopoverTip.svelte.d.ts +33 -0
- package/dist/tips/Tip.svelte +91 -0
- package/dist/tips/Tip.svelte.d.ts +25 -0
- package/dist/tips/index.d.ts +15 -0
- package/dist/tips/index.js +14 -0
- package/dist/tips/tip-stack.d.ts +26 -0
- package/dist/tips/tip-stack.js +37 -0
- package/dist/tips/tip-store.svelte.d.ts +75 -0
- package/dist/tips/tip-store.svelte.js +158 -0
- package/dist/tips/types.d.ts +46 -0
- package/dist/tips/types.js +6 -0
- package/dist/ui/Accordion.svelte +75 -0
- package/dist/ui/Accordion.svelte.d.ts +50 -0
- package/dist/ui/AccordionItem.svelte +73 -0
- package/dist/ui/AccordionItem.svelte.d.ts +18 -0
- package/dist/ui/Alert.svelte +61 -0
- package/dist/ui/Alert.svelte.d.ts +37 -0
- package/dist/ui/Badge.svelte +39 -0
- package/dist/ui/Badge.svelte.d.ts +32 -0
- package/dist/ui/Button.svelte +44 -0
- package/dist/ui/Button.svelte.d.ts +29 -0
- package/dist/ui/ButtonGroup.svelte +24 -0
- package/dist/ui/ButtonGroup.svelte.d.ts +17 -0
- package/dist/ui/Card.svelte +49 -0
- package/dist/ui/Card.svelte.d.ts +44 -0
- package/dist/ui/Chip.svelte +58 -0
- package/dist/ui/Chip.svelte.d.ts +21 -0
- package/dist/ui/ConfirmButton.svelte +75 -0
- package/dist/ui/ConfirmButton.svelte.d.ts +48 -0
- package/dist/ui/ContextMenu.svelte +75 -0
- package/dist/ui/ContextMenu.svelte.d.ts +25 -0
- package/dist/ui/Disclosure.svelte +46 -0
- package/dist/ui/Disclosure.svelte.d.ts +22 -0
- package/dist/ui/Divider.svelte +47 -0
- package/dist/ui/Divider.svelte.d.ts +28 -0
- package/dist/ui/DropdownMenu.svelte +44 -0
- package/dist/ui/DropdownMenu.svelte.d.ts +30 -0
- package/dist/ui/EmptyState.svelte +37 -0
- package/dist/ui/EmptyState.svelte.d.ts +22 -0
- package/dist/ui/ExternalLink.svelte +38 -0
- package/dist/ui/ExternalLink.svelte.d.ts +34 -0
- package/dist/ui/Heading.svelte +58 -0
- package/dist/ui/Heading.svelte.d.ts +22 -0
- package/dist/ui/Link.svelte +34 -0
- package/dist/ui/Link.svelte.d.ts +20 -0
- package/dist/ui/LinkButton.svelte +51 -0
- package/dist/ui/LinkButton.svelte.d.ts +33 -0
- package/dist/ui/Menu.svelte +24 -0
- package/dist/ui/Menu.svelte.d.ts +13 -0
- package/dist/ui/MenuCheckboxItem.svelte +33 -0
- package/dist/ui/MenuCheckboxItem.svelte.d.ts +20 -0
- package/dist/ui/MenuItem.svelte +73 -0
- package/dist/ui/MenuItem.svelte.d.ts +21 -0
- package/dist/ui/MenuRadioGroup.svelte +25 -0
- package/dist/ui/MenuRadioGroup.svelte.d.ts +20 -0
- package/dist/ui/MenuRadioItem.svelte +20 -0
- package/dist/ui/MenuRadioItem.svelte.d.ts +15 -0
- package/dist/ui/MenuSeparator.svelte +27 -0
- package/dist/ui/MenuSeparator.svelte.d.ts +8 -0
- package/dist/ui/MenuSub.svelte +24 -0
- package/dist/ui/MenuSub.svelte.d.ts +15 -0
- package/dist/ui/Popover.svelte +63 -0
- package/dist/ui/Popover.svelte.d.ts +40 -0
- package/dist/ui/ShareButton.svelte +64 -0
- package/dist/ui/ShareButton.svelte.d.ts +39 -0
- package/dist/ui/Skeleton.svelte +43 -0
- package/dist/ui/Skeleton.svelte.d.ts +22 -0
- package/dist/ui/Spinner.svelte +41 -0
- package/dist/ui/Spinner.svelte.d.ts +35 -0
- package/dist/ui/Stack.svelte +36 -0
- package/dist/ui/Stack.svelte.d.ts +48 -0
- package/dist/ui/ThemeToggle.svelte +65 -0
- package/dist/ui/ThemeToggle.svelte.d.ts +24 -0
- package/dist/ui/ToggleButton.svelte +58 -0
- package/dist/ui/ToggleButton.svelte.d.ts +45 -0
- package/dist/ui/ToggleGroup.svelte +142 -0
- package/dist/ui/ToggleGroup.svelte.d.ts +86 -0
- package/dist/ui/Toolbar.svelte +40 -0
- package/dist/ui/Toolbar.svelte.d.ts +20 -0
- package/dist/ui/ToolbarButton.svelte +29 -0
- package/dist/ui/ToolbarButton.svelte.d.ts +21 -0
- package/dist/ui/ToolbarLink.svelte +21 -0
- package/dist/ui/ToolbarLink.svelte.d.ts +12 -0
- package/dist/ui/ToolbarSeparator.svelte +22 -0
- package/dist/ui/ToolbarSeparator.svelte.d.ts +12 -0
- package/dist/ui/ToolbarToggleGroup.svelte +66 -0
- package/dist/ui/ToolbarToggleGroup.svelte.d.ts +52 -0
- package/dist/ui/Tooltip.svelte +54 -0
- package/dist/ui/Tooltip.svelte.d.ts +35 -0
- package/dist/ui/VisuallyHidden.svelte +23 -0
- package/dist/ui/VisuallyHidden.svelte.d.ts +10 -0
- package/dist/ui/accordion-context.d.ts +26 -0
- package/dist/ui/accordion-context.js +23 -0
- package/dist/ui/colour-token.d.ts +41 -0
- package/dist/ui/colour-token.js +49 -0
- package/dist/ui/context-menu.d.ts +13 -0
- package/dist/ui/context-menu.js +13 -0
- package/dist/ui/index.d.ts +63 -0
- package/dist/ui/index.js +62 -0
- package/dist/ui/menu-context.d.ts +20 -0
- package/dist/ui/menu-context.js +22 -0
- package/dist/ui/menu-parts/CheckboxItemPart.svelte +26 -0
- package/dist/ui/menu-parts/CheckboxItemPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/ItemPart.svelte +29 -0
- package/dist/ui/menu-parts/ItemPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/RadioGroupPart.svelte +24 -0
- package/dist/ui/menu-parts/RadioGroupPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/RadioItemPart.svelte +22 -0
- package/dist/ui/menu-parts/RadioItemPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/SeparatorPart.svelte +16 -0
- package/dist/ui/menu-parts/SeparatorPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/SubPart.svelte +28 -0
- package/dist/ui/menu-parts/SubPart.svelte.d.ts +5 -0
- package/dist/ui/menu-parts/menu-parts.d.ts +104 -0
- package/dist/ui/menu-parts/menu-parts.js +31 -0
- package/dist/ui/menu-parts/parts.d.ts +12 -0
- package/dist/ui/menu-parts/parts.js +27 -0
- package/dist/ui/slug.d.ts +19 -0
- package/dist/ui/slug.js +25 -0
- package/dist/ui/toggle-classes.d.ts +39 -0
- package/dist/ui/toggle-classes.js +36 -0
- package/dist/ui/tones.d.ts +103 -0
- package/dist/ui/tones.js +240 -0
- package/package.json +41 -25
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte context plumbing for the hotkey registry, plus the declarative
|
|
3
|
+
* `registerHotkeys` helper that pages and shared page components call to
|
|
4
|
+
* contribute page-scoped actions.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { HotkeyRegistry, type HotkeyRegistryOptions } from './registry.svelte.ts';
|
|
9
|
+
import type { CommandAction, HotkeyScope } from './types.ts';
|
|
10
|
+
/** Raised by {@link getHotkeyRegistry} when no registry has been created above. */
|
|
11
|
+
export declare class HotkeyRegistryMissing extends Error {
|
|
12
|
+
readonly name = "HotkeyRegistryMissing";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create the registry and place it in context. Call once, from the root
|
|
16
|
+
* layout, during component initialisation.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createHotkeyRegistry(options: HotkeyRegistryOptions): HotkeyRegistry;
|
|
19
|
+
/**
|
|
20
|
+
* Read the registry from context, or `undefined` when none was created.
|
|
21
|
+
*
|
|
22
|
+
* This is the seam that lets `CommandPalette` work in an app that never
|
|
23
|
+
* mounted a registry: it falls back to its `items` prop. Components that
|
|
24
|
+
* cannot work without one (`HotkeyProvider`, `HotkeyHelpDialog`) call
|
|
25
|
+
* {@link getHotkeyRegistry} instead, so the failure is loud.
|
|
26
|
+
*/
|
|
27
|
+
export declare function tryGetHotkeyRegistry(): HotkeyRegistry | undefined;
|
|
28
|
+
/** Read the registry from context. Throws when none was created. */
|
|
29
|
+
export declare function getHotkeyRegistry(): HotkeyRegistry;
|
|
30
|
+
/**
|
|
31
|
+
* Declaratively register actions for the lifetime of the calling component.
|
|
32
|
+
* Registration is reactive: the `actions` thunk re-runs when its dependencies
|
|
33
|
+
* change (a resource's `canEdit` resolving, say), replacing the previous
|
|
34
|
+
* registration.
|
|
35
|
+
*
|
|
36
|
+
* ```svelte
|
|
37
|
+
* registerHotkeys(() =>
|
|
38
|
+
* post.canEdit
|
|
39
|
+
* ? [{ id: 'post.edit', label: 'Edit post', trigger: 'e', group: 'actions',
|
|
40
|
+
* run: () => goto(`/posts/${post.id}/edit`) }]
|
|
41
|
+
* : [],
|
|
42
|
+
* );
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function registerHotkeys(actions: () => readonly CommandAction[], options?: {
|
|
46
|
+
scope?: HotkeyScope | undefined;
|
|
47
|
+
}): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte context plumbing for the hotkey registry, plus the declarative
|
|
3
|
+
* `registerHotkeys` helper that pages and shared page components call to
|
|
4
|
+
* contribute page-scoped actions.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { getContext, setContext, untrack } from 'svelte';
|
|
9
|
+
import { HotkeyRegistry } from "./registry.svelte.js";
|
|
10
|
+
const CONTEXT_KEY = Symbol('@thomasfosterau/components:hotkey-registry');
|
|
11
|
+
/** Raised by {@link getHotkeyRegistry} when no registry has been created above. */
|
|
12
|
+
export class HotkeyRegistryMissing extends Error {
|
|
13
|
+
name = 'HotkeyRegistryMissing';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create the registry and place it in context. Call once, from the root
|
|
17
|
+
* layout, during component initialisation.
|
|
18
|
+
*/
|
|
19
|
+
export function createHotkeyRegistry(options) {
|
|
20
|
+
const registry = new HotkeyRegistry(options);
|
|
21
|
+
setContext(CONTEXT_KEY, registry);
|
|
22
|
+
return registry;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read the registry from context, or `undefined` when none was created.
|
|
26
|
+
*
|
|
27
|
+
* This is the seam that lets `CommandPalette` work in an app that never
|
|
28
|
+
* mounted a registry: it falls back to its `items` prop. Components that
|
|
29
|
+
* cannot work without one (`HotkeyProvider`, `HotkeyHelpDialog`) call
|
|
30
|
+
* {@link getHotkeyRegistry} instead, so the failure is loud.
|
|
31
|
+
*/
|
|
32
|
+
export function tryGetHotkeyRegistry() {
|
|
33
|
+
return getContext(CONTEXT_KEY);
|
|
34
|
+
}
|
|
35
|
+
/** Read the registry from context. Throws when none was created. */
|
|
36
|
+
export function getHotkeyRegistry() {
|
|
37
|
+
const registry = tryGetHotkeyRegistry();
|
|
38
|
+
if (!registry) {
|
|
39
|
+
throw new HotkeyRegistryMissing('HotkeyRegistry not found in context. ' +
|
|
40
|
+
'createHotkeyRegistry() must be called by an ancestor (the root layout).');
|
|
41
|
+
}
|
|
42
|
+
return registry;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Declaratively register actions for the lifetime of the calling component.
|
|
46
|
+
* Registration is reactive: the `actions` thunk re-runs when its dependencies
|
|
47
|
+
* change (a resource's `canEdit` resolving, say), replacing the previous
|
|
48
|
+
* registration.
|
|
49
|
+
*
|
|
50
|
+
* ```svelte
|
|
51
|
+
* registerHotkeys(() =>
|
|
52
|
+
* post.canEdit
|
|
53
|
+
* ? [{ id: 'post.edit', label: 'Edit post', trigger: 'e', group: 'actions',
|
|
54
|
+
* run: () => goto(`/posts/${post.id}/edit`) }]
|
|
55
|
+
* : [],
|
|
56
|
+
* );
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function registerHotkeys(actions, options = {}) {
|
|
60
|
+
const registry = getHotkeyRegistry();
|
|
61
|
+
const scope = options.scope ?? 'page';
|
|
62
|
+
$effect(() => {
|
|
63
|
+
// Track only the `actions` thunk's own reactive inputs. `register()`
|
|
64
|
+
// reads *and* writes the registry's `#registrations` `$state`; left
|
|
65
|
+
// tracked, that read-then-write makes the effect re-trigger itself
|
|
66
|
+
// forever (`effect_update_depth_exceeded`, which crashed hydration on
|
|
67
|
+
// every page). `untrack` isolates the register call so the effect re-runs
|
|
68
|
+
// only when `actions()` changes, not when the array it writes does.
|
|
69
|
+
const batch = actions();
|
|
70
|
+
return untrack(() => registry.register(scope, batch));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hotkeys — the registry that answers "what can the viewer do right now", and
|
|
3
|
+
* the two surfaces that read it back.
|
|
4
|
+
*
|
|
5
|
+
* An app creates one registry in its root layout (`createHotkeyRegistry`),
|
|
6
|
+
* renders `HotkeyProvider` once to mount the key listener, and registers
|
|
7
|
+
* actions from anywhere below with `registerHotkeys` — global ones from the
|
|
8
|
+
* layout, page ones from the page, torn down when it unmounts. `search/`'s
|
|
9
|
+
* `CommandPalette` and `HotkeyHelpDialog` then list exactly what is live.
|
|
10
|
+
*
|
|
11
|
+
* Everything user-facing is a prop: the registry stores the labels the app
|
|
12
|
+
* registered, and the two components take their own chrome copy
|
|
13
|
+
* (`title`, `closeLabel`, `commandLabel`, …) with English defaults.
|
|
14
|
+
*
|
|
15
|
+
* The family is re-exported from the root barrel and is also its own entry,
|
|
16
|
+
* `@thomasfosterau/components/hotkeys`, so a consumer can
|
|
17
|
+
* `import('@thomasfosterau/components/hotkeys')` to load `HotkeyHelpDialog`
|
|
18
|
+
* lazily, on the first `?`, instead of in its entry chunk.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export { HotkeyRegistryMissing, createHotkeyRegistry, getHotkeyRegistry, registerHotkeys, tryGetHotkeyRegistry, } from './context.svelte.ts';
|
|
23
|
+
export { HotkeyRegistry, type HotkeyRegistryOptions } from './registry.svelte.ts';
|
|
24
|
+
export { default as HotkeyBadge } from './HotkeyBadge.svelte';
|
|
25
|
+
export { default as HotkeyHelpDialog } from './HotkeyHelpDialog.svelte';
|
|
26
|
+
export { default as HotkeyProvider } from './HotkeyProvider.svelte';
|
|
27
|
+
export { SEQUENCE_TIMEOUT_MS, hasChordModifier, isApplePlatform, isEditableTarget, isSequenceKey, matchChord, matchSequence, triggerDisplay, type SequenceMatch, type TriggerDisplay, } from './match.ts';
|
|
28
|
+
export type { CommandAction, CommandActionGroup, HotkeyChord, HotkeyScope, HotkeySequence, HotkeyTrigger, RegisteredAction, } from './types.ts';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hotkeys — the registry that answers "what can the viewer do right now", and
|
|
3
|
+
* the two surfaces that read it back.
|
|
4
|
+
*
|
|
5
|
+
* An app creates one registry in its root layout (`createHotkeyRegistry`),
|
|
6
|
+
* renders `HotkeyProvider` once to mount the key listener, and registers
|
|
7
|
+
* actions from anywhere below with `registerHotkeys` — global ones from the
|
|
8
|
+
* layout, page ones from the page, torn down when it unmounts. `search/`'s
|
|
9
|
+
* `CommandPalette` and `HotkeyHelpDialog` then list exactly what is live.
|
|
10
|
+
*
|
|
11
|
+
* Everything user-facing is a prop: the registry stores the labels the app
|
|
12
|
+
* registered, and the two components take their own chrome copy
|
|
13
|
+
* (`title`, `closeLabel`, `commandLabel`, …) with English defaults.
|
|
14
|
+
*
|
|
15
|
+
* The family is re-exported from the root barrel and is also its own entry,
|
|
16
|
+
* `@thomasfosterau/components/hotkeys`, so a consumer can
|
|
17
|
+
* `import('@thomasfosterau/components/hotkeys')` to load `HotkeyHelpDialog`
|
|
18
|
+
* lazily, on the first `?`, instead of in its entry chunk.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export { HotkeyRegistryMissing, createHotkeyRegistry, getHotkeyRegistry, registerHotkeys, tryGetHotkeyRegistry, } from "./context.svelte.js";
|
|
23
|
+
export { HotkeyRegistry } from "./registry.svelte.js";
|
|
24
|
+
export { default as HotkeyBadge } from './HotkeyBadge.svelte';
|
|
25
|
+
export { default as HotkeyHelpDialog } from './HotkeyHelpDialog.svelte';
|
|
26
|
+
export { default as HotkeyProvider } from './HotkeyProvider.svelte';
|
|
27
|
+
export { SEQUENCE_TIMEOUT_MS, hasChordModifier, isApplePlatform, isEditableTarget, isSequenceKey, matchChord, matchSequence, triggerDisplay, } from "./match.js";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure key-matching helpers for the hotkey registry. No Svelte, no DOM
|
|
3
|
+
* mutation — just functions over `KeyboardEvent`s, trigger grammars and the
|
|
4
|
+
* sequence buffer, so the matching rules stay testable and `HotkeyProvider`
|
|
5
|
+
* stays thin.
|
|
6
|
+
*
|
|
7
|
+
* Trigger model:
|
|
8
|
+
*
|
|
9
|
+
* - **Modifier chords** (`'$mod+k'`) — held together. `$mod` is ⌘ on macOS
|
|
10
|
+
* and Ctrl elsewhere. Matched on their own; never buffered.
|
|
11
|
+
* - **Plain keys / sequences** (`'e'`, `'/'`, `['g', 'p']`) — pressed in
|
|
12
|
+
* order with no modifier held. A plain single key is just a sequence of
|
|
13
|
+
* length one, so matching is uniform.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { CommandAction, HotkeyTrigger, RegisteredAction } from './types.ts';
|
|
18
|
+
/** Sequence buffer entries expire after this many milliseconds. */
|
|
19
|
+
export declare const SEQUENCE_TIMEOUT_MS = 1000;
|
|
20
|
+
/** Whether the runtime platform treats `$mod` as ⌘ (macOS / iOS). */
|
|
21
|
+
export declare function isApplePlatform(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether a keyboard event's target is an editable surface — a form field or
|
|
24
|
+
* a contenteditable region (which covers a rich-text editor's root). Hotkeys
|
|
25
|
+
* without `allowInEditable` stay inert there so typing never fires actions.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isEditableTarget(target: EventTarget | null): boolean;
|
|
28
|
+
/** Whether an event carries a chord modifier (⌘ / Ctrl / Alt). */
|
|
29
|
+
export declare function hasChordModifier(event: KeyboardEvent): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether an event's key can participate in a sequence: a single printable
|
|
32
|
+
* character pressed without chord modifiers.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isSequenceKey(event: KeyboardEvent): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Find the action whose modifier chord matches the event. `actions` must
|
|
37
|
+
* already be in precedence order (page before global, later registrations
|
|
38
|
+
* first).
|
|
39
|
+
*/
|
|
40
|
+
export declare function matchChord(event: KeyboardEvent, actions: readonly RegisteredAction[]): CommandAction | undefined;
|
|
41
|
+
/** The result of `matchSequence`: the action a key buffer completes, and whether it could still grow into another. */
|
|
42
|
+
export interface SequenceMatch {
|
|
43
|
+
/** The action whose sequence the buffer completes, if any. */
|
|
44
|
+
exact: CommandAction | undefined;
|
|
45
|
+
/** Whether the buffer is a strict prefix of at least one sequence. */
|
|
46
|
+
isPrefix: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Match a candidate key buffer against the active plain-key triggers.
|
|
50
|
+
* `actions` must already be in precedence order.
|
|
51
|
+
*/
|
|
52
|
+
export declare function matchSequence(buffer: readonly string[], actions: readonly RegisteredAction[]): SequenceMatch;
|
|
53
|
+
/** A trigger split into the keys a `HotkeyBadge` renders, and whether they are pressed in turn or together. */
|
|
54
|
+
export interface TriggerDisplay {
|
|
55
|
+
/**
|
|
56
|
+
* Keys to render as `<kbd>` chips, in order. `'$mod'` is left un-resolved
|
|
57
|
+
* so the rendering component can substitute the platform-appropriate
|
|
58
|
+
* label.
|
|
59
|
+
*/
|
|
60
|
+
keys: readonly string[];
|
|
61
|
+
/** True when keys are pressed one after another rather than held together. */
|
|
62
|
+
sequential: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Project a trigger into its display form for help overlays and badges. */
|
|
65
|
+
export declare function triggerDisplay(trigger: HotkeyTrigger): TriggerDisplay;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure key-matching helpers for the hotkey registry. No Svelte, no DOM
|
|
3
|
+
* mutation — just functions over `KeyboardEvent`s, trigger grammars and the
|
|
4
|
+
* sequence buffer, so the matching rules stay testable and `HotkeyProvider`
|
|
5
|
+
* stays thin.
|
|
6
|
+
*
|
|
7
|
+
* Trigger model:
|
|
8
|
+
*
|
|
9
|
+
* - **Modifier chords** (`'$mod+k'`) — held together. `$mod` is ⌘ on macOS
|
|
10
|
+
* and Ctrl elsewhere. Matched on their own; never buffered.
|
|
11
|
+
* - **Plain keys / sequences** (`'e'`, `'/'`, `['g', 'p']`) — pressed in
|
|
12
|
+
* order with no modifier held. A plain single key is just a sequence of
|
|
13
|
+
* length one, so matching is uniform.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/** Sequence buffer entries expire after this many milliseconds. */
|
|
18
|
+
export const SEQUENCE_TIMEOUT_MS = 1000;
|
|
19
|
+
/** Whether the runtime platform treats `$mod` as ⌘ (macOS / iOS). */
|
|
20
|
+
export function isApplePlatform() {
|
|
21
|
+
// `globalThis.navigator` rather than the bare identifier: on a runtime
|
|
22
|
+
// without one, a direct reference would throw before the guard could run.
|
|
23
|
+
if (typeof globalThis.navigator === 'undefined')
|
|
24
|
+
return false;
|
|
25
|
+
return /mac|iphone|ipad|ipod/i.test(globalThis.navigator.platform);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether a keyboard event's target is an editable surface — a form field or
|
|
29
|
+
* a contenteditable region (which covers a rich-text editor's root). Hotkeys
|
|
30
|
+
* without `allowInEditable` stay inert there so typing never fires actions.
|
|
31
|
+
*/
|
|
32
|
+
export function isEditableTarget(target) {
|
|
33
|
+
if (!(target instanceof HTMLElement))
|
|
34
|
+
return false;
|
|
35
|
+
return target.matches('input, textarea, select') || target.isContentEditable;
|
|
36
|
+
}
|
|
37
|
+
/** Whether a trigger is in the modifier-chord form (`'$mod+…'`). */
|
|
38
|
+
function isModifierChord(trigger) {
|
|
39
|
+
return typeof trigger === 'string' && trigger.includes('+');
|
|
40
|
+
}
|
|
41
|
+
/** Normalise a plain-key trigger to its sequence form. `undefined` for a chord. */
|
|
42
|
+
function asSequence(trigger) {
|
|
43
|
+
if (typeof trigger === 'string') {
|
|
44
|
+
return isModifierChord(trigger) ? undefined : [trigger];
|
|
45
|
+
}
|
|
46
|
+
return trigger;
|
|
47
|
+
}
|
|
48
|
+
/** Whether the event matches a `'$mod+key'` chord. */
|
|
49
|
+
// Modifier matching is a flat run of guard comparisons (mod / alt / shift /
|
|
50
|
+
// cross-platform exclusion); each early return is one rule of the chord
|
|
51
|
+
// grammar, and extracting them would obscure the contract.
|
|
52
|
+
function eventMatchesChord(event, chord) {
|
|
53
|
+
const parts = chord.split('+');
|
|
54
|
+
const key = parts.at(-1) ?? '';
|
|
55
|
+
// A plain array, not a `Set`: a chord carries at most three modifiers, so
|
|
56
|
+
// three `includes` scans beat building a hashed collection on every
|
|
57
|
+
// keystroke.
|
|
58
|
+
const modifiers = parts.slice(0, -1);
|
|
59
|
+
const wantsMod = modifiers.includes('$mod');
|
|
60
|
+
const wantsAlt = modifiers.includes('alt');
|
|
61
|
+
const wantsShift = modifiers.includes('shift');
|
|
62
|
+
const apple = isApplePlatform();
|
|
63
|
+
const modPressed = apple ? event.metaKey : event.ctrlKey;
|
|
64
|
+
// The non-$mod modifier must not also be held, so e.g. Ctrl+K on macOS
|
|
65
|
+
// doesn't trigger a ⌘K hotkey.
|
|
66
|
+
const otherModPressed = apple ? event.ctrlKey : event.metaKey;
|
|
67
|
+
if (wantsMod !== modPressed || otherModPressed)
|
|
68
|
+
return false;
|
|
69
|
+
if (wantsAlt !== event.altKey)
|
|
70
|
+
return false;
|
|
71
|
+
if (wantsShift && !event.shiftKey)
|
|
72
|
+
return false;
|
|
73
|
+
return event.key.toLowerCase() === key.toLowerCase();
|
|
74
|
+
}
|
|
75
|
+
/** Whether an event carries a chord modifier (⌘ / Ctrl / Alt). */
|
|
76
|
+
export function hasChordModifier(event) {
|
|
77
|
+
return event.metaKey || event.ctrlKey || event.altKey;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether an event's key can participate in a sequence: a single printable
|
|
81
|
+
* character pressed without chord modifiers.
|
|
82
|
+
*/
|
|
83
|
+
export function isSequenceKey(event) {
|
|
84
|
+
return !hasChordModifier(event) && event.key.length === 1;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find the action whose modifier chord matches the event. `actions` must
|
|
88
|
+
* already be in precedence order (page before global, later registrations
|
|
89
|
+
* first).
|
|
90
|
+
*/
|
|
91
|
+
export function matchChord(event, actions) {
|
|
92
|
+
return actions.find(({ action }) => typeof action.trigger === 'string' &&
|
|
93
|
+
action.trigger.includes('+') &&
|
|
94
|
+
eventMatchesChord(event, action.trigger))?.action;
|
|
95
|
+
}
|
|
96
|
+
// A single-pass scan that classifies every trigger as exact-match / prefix /
|
|
97
|
+
// miss in one loop; splitting it into separate passes would be simpler per
|
|
98
|
+
// function but iterate the registrations three times per keystroke.
|
|
99
|
+
/**
|
|
100
|
+
* Match a candidate key buffer against the active plain-key triggers.
|
|
101
|
+
* `actions` must already be in precedence order.
|
|
102
|
+
*/
|
|
103
|
+
export function matchSequence(buffer, actions) {
|
|
104
|
+
let exact;
|
|
105
|
+
let isPrefix = false;
|
|
106
|
+
for (const { action } of actions) {
|
|
107
|
+
const trigger = action.trigger;
|
|
108
|
+
if (trigger === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
const sequence = asSequence(trigger);
|
|
111
|
+
if (sequence === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
if (sequence.length === buffer.length) {
|
|
114
|
+
if (exact === undefined && sequence.every((key, i) => key === buffer[i]))
|
|
115
|
+
exact = action;
|
|
116
|
+
}
|
|
117
|
+
else if (sequence.length > buffer.length && buffer.every((key, i) => key === sequence[i])) {
|
|
118
|
+
isPrefix = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { exact, isPrefix };
|
|
122
|
+
}
|
|
123
|
+
/** Project a trigger into its display form for help overlays and badges. */
|
|
124
|
+
export function triggerDisplay(trigger) {
|
|
125
|
+
if (typeof trigger === 'string') {
|
|
126
|
+
if (isModifierChord(trigger)) {
|
|
127
|
+
return {
|
|
128
|
+
keys: trigger.split('+').map((part) => (part === '$mod' ? part : part.toUpperCase())),
|
|
129
|
+
sequential: false,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return { keys: [trigger.toUpperCase()], sequential: false };
|
|
133
|
+
}
|
|
134
|
+
return { keys: trigger.map((key) => key.toUpperCase()), sequential: true };
|
|
135
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reactive hotkey / command registry.
|
|
3
|
+
*
|
|
4
|
+
* One instance lives in the app's Svelte context. The root layout registers
|
|
5
|
+
* `global` actions; pages register `page` actions that are torn down when the
|
|
6
|
+
* page unmounts. The registry is the single source of truth for three
|
|
7
|
+
* consumers:
|
|
8
|
+
*
|
|
9
|
+
* - `HotkeyProvider` — matches keyboard events against active triggers.
|
|
10
|
+
* - `CommandPalette` — lists palette-visible actions.
|
|
11
|
+
* - `HotkeyHelpDialog` — lists actions that carry triggers.
|
|
12
|
+
*
|
|
13
|
+
* Role gating happens here: actions registered with `requiresAdmin` are
|
|
14
|
+
* dropped from every read surface when the viewer is not an administrator, so
|
|
15
|
+
* non-admin users never see (or can fire) them.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { CommandAction, HotkeyScope, RegisteredAction } from './types.ts';
|
|
20
|
+
/** What `createHotkeyRegistry` needs from the app: the admin check and the dev flag. */
|
|
21
|
+
export interface HotkeyRegistryOptions {
|
|
22
|
+
/** Whether the current viewer is an administrator. Read lazily so it can be reactive. */
|
|
23
|
+
isAdmin: () => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether this is a dev build. When true, registering a batch whose trigger
|
|
26
|
+
* shadows an existing one logs a console warning. An app wires this to its
|
|
27
|
+
* bundler's dev flag (SvelteKit's `$app/environment` `dev`); defaults to
|
|
28
|
+
* `false`.
|
|
29
|
+
*/
|
|
30
|
+
dev?: boolean | undefined;
|
|
31
|
+
}
|
|
32
|
+
/** The reactive set of registered hotkey actions that `HotkeyProvider`, `CommandPalette` and `HotkeyHelpDialog` read; page registrations win over global ones. */
|
|
33
|
+
export declare class HotkeyRegistry {
|
|
34
|
+
#private;
|
|
35
|
+
/** Whether the command palette is open. Bound by `CommandPalette`. */
|
|
36
|
+
paletteOpen: boolean;
|
|
37
|
+
/** Whether the hotkey help overlay is open. Bound by `HotkeyHelpDialog`. */
|
|
38
|
+
helpOpen: boolean;
|
|
39
|
+
constructor(options: HotkeyRegistryOptions);
|
|
40
|
+
/**
|
|
41
|
+
* Register a batch of actions under a scope. Returns an unregister
|
|
42
|
+
* function; callers are expected to invoke it on unmount (the
|
|
43
|
+
* `registerHotkeys` helper wires this to `$effect` cleanup).
|
|
44
|
+
*/
|
|
45
|
+
register(scope: HotkeyScope, actions: readonly CommandAction[]): () => void;
|
|
46
|
+
/**
|
|
47
|
+
* Role-filtered active registrations in match-precedence order: page scope
|
|
48
|
+
* before global, later registrations first within a scope.
|
|
49
|
+
*/
|
|
50
|
+
readonly active: readonly RegisteredAction[];
|
|
51
|
+
/** Active actions that should appear in the command palette, in registration order. */
|
|
52
|
+
readonly paletteActions: readonly RegisteredAction[];
|
|
53
|
+
/** Active actions that carry a keyboard trigger, for the help overlay. */
|
|
54
|
+
readonly hotkeys: readonly RegisteredAction[];
|
|
55
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reactive hotkey / command registry.
|
|
3
|
+
*
|
|
4
|
+
* One instance lives in the app's Svelte context. The root layout registers
|
|
5
|
+
* `global` actions; pages register `page` actions that are torn down when the
|
|
6
|
+
* page unmounts. The registry is the single source of truth for three
|
|
7
|
+
* consumers:
|
|
8
|
+
*
|
|
9
|
+
* - `HotkeyProvider` — matches keyboard events against active triggers.
|
|
10
|
+
* - `CommandPalette` — lists palette-visible actions.
|
|
11
|
+
* - `HotkeyHelpDialog` — lists actions that carry triggers.
|
|
12
|
+
*
|
|
13
|
+
* Role gating happens here: actions registered with `requiresAdmin` are
|
|
14
|
+
* dropped from every read surface when the viewer is not an administrator, so
|
|
15
|
+
* non-admin users never see (or can fire) them.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Whether two triggers denote the same keystroke. A structural comparison
|
|
21
|
+
* rather than a JSON round-trip: a chord is a string and a sequence is an
|
|
22
|
+
* array, so the two forms never collide.
|
|
23
|
+
*/
|
|
24
|
+
function sameTrigger(self, that) {
|
|
25
|
+
return typeof self === 'string' || typeof that === 'string'
|
|
26
|
+
? self === that
|
|
27
|
+
: self.length === that.length && self.every((key, index) => key === that[index]);
|
|
28
|
+
}
|
|
29
|
+
/** A trigger rendered for the dev warning below. */
|
|
30
|
+
function formatTrigger(trigger) {
|
|
31
|
+
return typeof trigger === 'string' ? `"${trigger}"` : `["${trigger.join('", "')}"]`;
|
|
32
|
+
}
|
|
33
|
+
/** Dev-only console warning when a new registration shadows an existing trigger. */
|
|
34
|
+
function warnOnTriggerConflicts(added, existing) {
|
|
35
|
+
for (const entry of added) {
|
|
36
|
+
const trigger = entry.action.trigger;
|
|
37
|
+
if (trigger === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
const conflict = existing.find((other) => other.action.trigger !== undefined && sameTrigger(other.action.trigger, trigger));
|
|
40
|
+
if (conflict) {
|
|
41
|
+
// A dev-build diagnostic: it has to reach the browser console the
|
|
42
|
+
// developer is looking at, which is why this is the one `console` call
|
|
43
|
+
// in the package.
|
|
44
|
+
console.warn(`[hotkeys] Trigger ${formatTrigger(trigger)} of "${entry.action.id}" ` +
|
|
45
|
+
`shadows "${conflict.action.id}" (${conflict.scope}). ` +
|
|
46
|
+
`Page-scope and later registrations win.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Page scope sorts before global scope. */
|
|
51
|
+
const scopeRank = (entry) => (entry.scope === 'page' ? 0 : 1);
|
|
52
|
+
/**
|
|
53
|
+
* Match precedence: page scope first, then the most recent registration.
|
|
54
|
+
* A plain comparator — the composed `Order` this was ported from is the only
|
|
55
|
+
* thing that changed.
|
|
56
|
+
*/
|
|
57
|
+
const byMatchPrecedence = (a, b) => scopeRank(a) - scopeRank(b) || b.order - a.order;
|
|
58
|
+
/** Ascending registration order (earlier registrations first). */
|
|
59
|
+
const byOrder = (a, b) => a.order - b.order;
|
|
60
|
+
/** The reactive set of registered hotkey actions that `HotkeyProvider`, `CommandPalette` and `HotkeyHelpDialog` read; page registrations win over global ones. */
|
|
61
|
+
export class HotkeyRegistry {
|
|
62
|
+
#registrations = $state([]);
|
|
63
|
+
#nextOrder = 0;
|
|
64
|
+
#isAdmin;
|
|
65
|
+
#dev;
|
|
66
|
+
/** Whether the command palette is open. Bound by `CommandPalette`. */
|
|
67
|
+
paletteOpen = $state(false);
|
|
68
|
+
/** Whether the hotkey help overlay is open. Bound by `HotkeyHelpDialog`. */
|
|
69
|
+
helpOpen = $state(false);
|
|
70
|
+
constructor(options) {
|
|
71
|
+
this.#isAdmin = options.isAdmin;
|
|
72
|
+
this.#dev = options.dev ?? false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Register a batch of actions under a scope. Returns an unregister
|
|
76
|
+
* function; callers are expected to invoke it on unmount (the
|
|
77
|
+
* `registerHotkeys` helper wires this to `$effect` cleanup).
|
|
78
|
+
*/
|
|
79
|
+
register(scope, actions) {
|
|
80
|
+
const added = actions.map((action) => ({ scope, action, order: this.#nextOrder++ }));
|
|
81
|
+
if (this.#dev)
|
|
82
|
+
warnOnTriggerConflicts(added, this.#registrations);
|
|
83
|
+
this.#registrations = [...this.#registrations, ...added];
|
|
84
|
+
return () => {
|
|
85
|
+
// Remove by the stable monotonic `order`, NOT object identity. `$state`
|
|
86
|
+
// deep-proxies `#registrations`, so entries read back here are proxies
|
|
87
|
+
// that never `===` the raw objects captured in `added` — a `Set(added)`
|
|
88
|
+
// membership test would match nothing and remove nothing, leaking the
|
|
89
|
+
// batch. `registerHotkeys` wires this closure to `$effect` cleanup, so
|
|
90
|
+
// every reactive re-registration would otherwise accumulate duplicates.
|
|
91
|
+
const orders = new Set(added.map((entry) => entry.order));
|
|
92
|
+
this.#registrations = this.#registrations.filter((entry) => !orders.has(entry.order));
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Role-filtered active registrations in match-precedence order: page scope
|
|
97
|
+
* before global, later registrations first within a scope.
|
|
98
|
+
*/
|
|
99
|
+
active = $derived(this.#registrations
|
|
100
|
+
.filter((entry) => !entry.action.requiresAdmin || this.#isAdmin())
|
|
101
|
+
.toSorted(byMatchPrecedence));
|
|
102
|
+
/** Active actions that should appear in the command palette, in registration order. */
|
|
103
|
+
paletteActions = $derived(this.#registrations
|
|
104
|
+
.filter((entry) => (!entry.action.requiresAdmin || this.#isAdmin()) && entry.action.paletteVisible !== false)
|
|
105
|
+
.toSorted(byOrder));
|
|
106
|
+
/** Active actions that carry a keyboard trigger, for the help overlay. */
|
|
107
|
+
hotkeys = $derived(this.#registrations
|
|
108
|
+
.filter((entry) => (!entry.action.requiresAdmin || this.#isAdmin()) && entry.action.trigger !== undefined)
|
|
109
|
+
.toSorted(byOrder));
|
|
110
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registration contract shared by the hotkey registry and the command
|
|
3
|
+
* palette.
|
|
4
|
+
*
|
|
5
|
+
* A {@link CommandAction} is the unit of both systems: a labelled, runnable
|
|
6
|
+
* action that may carry a keyboard trigger (making it a hotkey) and may
|
|
7
|
+
* appear in the command palette (making it discoverable). Pages and the
|
|
8
|
+
* global shell register actions through the registry; the palette and the
|
|
9
|
+
* help overlay read the same registrations back, so there is exactly one
|
|
10
|
+
* source of truth for "what can the viewer do right now".
|
|
11
|
+
*
|
|
12
|
+
* Every optional member is declared `T | undefined`: the workspace compiles
|
|
13
|
+
* with `exactOptionalPropertyTypes`, and these objects are written as plain
|
|
14
|
+
* literals by application code that forwards its own optional values.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A single-keystroke trigger.
|
|
20
|
+
*
|
|
21
|
+
* Grammar: zero or more `+`-separated modifiers followed by a key, e.g.
|
|
22
|
+
* `'$mod+k'`, `'/'`, `'?'`, `'e'`. `$mod` resolves to ⌘ on macOS and Ctrl
|
|
23
|
+
* elsewhere. Shift is never written explicitly — shifted characters (like
|
|
24
|
+
* `?`) already arrive shifted in `KeyboardEvent.key`.
|
|
25
|
+
*/
|
|
26
|
+
export type HotkeyChord = string;
|
|
27
|
+
/**
|
|
28
|
+
* A multi-keystroke trigger pressed in order, e.g. `['g', 'p']`. Only plain
|
|
29
|
+
* (unmodified) printable keys participate in sequences.
|
|
30
|
+
*/
|
|
31
|
+
export type HotkeySequence = readonly string[];
|
|
32
|
+
/** A hotkey trigger: a chord pressed together (`'$mod+k'`) or a sequence pressed in turn (`['g', 'p']`). */
|
|
33
|
+
export type HotkeyTrigger = HotkeyChord | HotkeySequence;
|
|
34
|
+
/** The command-palette section a `CommandAction` is listed under. Drives section headings and ordering. */
|
|
35
|
+
export type CommandActionGroup = 'actions' | 'navigation';
|
|
36
|
+
/**
|
|
37
|
+
* Where a hotkey registration lives: `global` for the app shell, `page` for the mounted page. Page registrations are torn down when the
|
|
38
|
+
* registering page unmounts and win over global registrations on trigger
|
|
39
|
+
* conflicts.
|
|
40
|
+
*/
|
|
41
|
+
export type HotkeyScope = 'global' | 'page';
|
|
42
|
+
/** One action the hotkey registry and the command palette offer: an id, a label, what it runs and, optionally, its trigger. */
|
|
43
|
+
export interface CommandAction {
|
|
44
|
+
/** Stable unique id, e.g. `'nav.posts'`, `'posts.create'`. */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Human-readable label. An i18n app resolves its message at registration. */
|
|
47
|
+
label: string;
|
|
48
|
+
/** What runs on activation (navigate, focus, open a dialog, …). */
|
|
49
|
+
run: () => void | Promise<void>;
|
|
50
|
+
/** Palette section. */
|
|
51
|
+
group: CommandActionGroup;
|
|
52
|
+
/** Optional keyboard trigger. Actions without one are palette-only. */
|
|
53
|
+
trigger?: HotkeyTrigger | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* When true the registry drops the action entirely for non-admin viewers —
|
|
56
|
+
* it neither fires as a hotkey nor appears in the palette.
|
|
57
|
+
*/
|
|
58
|
+
requiresAdmin?: boolean | undefined;
|
|
59
|
+
/** Set false to keep an action hotkey-only (hidden from the palette). */
|
|
60
|
+
paletteVisible?: boolean | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Allow the trigger to fire while focus is inside an editable element
|
|
63
|
+
* (input / textarea / contenteditable). Reserved for the palette opener;
|
|
64
|
+
* everything else stays inert while typing.
|
|
65
|
+
*/
|
|
66
|
+
allowInEditable?: boolean | undefined;
|
|
67
|
+
}
|
|
68
|
+
/** A registration entry: an action plus the scope it was registered under. */
|
|
69
|
+
export interface RegisteredAction {
|
|
70
|
+
/** The scope it was registered under. */
|
|
71
|
+
scope: HotkeyScope;
|
|
72
|
+
/** The action as registered. */
|
|
73
|
+
action: CommandAction;
|
|
74
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registration contract shared by the hotkey registry and the command
|
|
3
|
+
* palette.
|
|
4
|
+
*
|
|
5
|
+
* A {@link CommandAction} is the unit of both systems: a labelled, runnable
|
|
6
|
+
* action that may carry a keyboard trigger (making it a hotkey) and may
|
|
7
|
+
* appear in the command palette (making it discoverable). Pages and the
|
|
8
|
+
* global shell register actions through the registry; the palette and the
|
|
9
|
+
* help overlay read the same registrations back, so there is exactly one
|
|
10
|
+
* source of truth for "what can the viewer do right now".
|
|
11
|
+
*
|
|
12
|
+
* Every optional member is declared `T | undefined`: the workspace compiles
|
|
13
|
+
* with `exactOptionalPropertyTypes`, and these objects are written as plain
|
|
14
|
+
* literals by application code that forwards its own optional values.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
export {};
|