svelte-incant 0.1.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.
Files changed (157) hide show
  1. package/README.md +75 -0
  2. package/dist/attachment.svelte.d.ts +8 -0
  3. package/dist/attachment.svelte.js +93 -0
  4. package/dist/combobox-example.svelte +155 -0
  5. package/dist/combobox-example.svelte.d.ts +3 -0
  6. package/dist/components/CodeBlock.svelte +213 -0
  7. package/dist/components/CodeBlock.svelte.d.ts +12 -0
  8. package/dist/components/header.svelte +155 -0
  9. package/dist/components/header.svelte.d.ts +3 -0
  10. package/dist/components/kbds.svelte +31 -0
  11. package/dist/components/kbds.svelte.d.ts +7 -0
  12. package/dist/components/ui/badge/badge.svelte +49 -0
  13. package/dist/components/ui/badge/badge.svelte.d.ts +32 -0
  14. package/dist/components/ui/badge/index.d.ts +2 -0
  15. package/dist/components/ui/badge/index.js +2 -0
  16. package/dist/components/ui/button/button.svelte +82 -0
  17. package/dist/components/ui/button/button.svelte.d.ts +64 -0
  18. package/dist/components/ui/button/index.d.ts +2 -0
  19. package/dist/components/ui/button/index.js +4 -0
  20. package/dist/components/ui/card/card-action.svelte +20 -0
  21. package/dist/components/ui/card/card-action.svelte.d.ts +5 -0
  22. package/dist/components/ui/card/card-content.svelte +15 -0
  23. package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
  24. package/dist/components/ui/card/card-description.svelte +20 -0
  25. package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
  26. package/dist/components/ui/card/card-footer.svelte +20 -0
  27. package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
  28. package/dist/components/ui/card/card-header.svelte +23 -0
  29. package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
  30. package/dist/components/ui/card/card-title.svelte +20 -0
  31. package/dist/components/ui/card/card-title.svelte.d.ts +5 -0
  32. package/dist/components/ui/card/card.svelte +23 -0
  33. package/dist/components/ui/card/card.svelte.d.ts +5 -0
  34. package/dist/components/ui/card/index.d.ts +8 -0
  35. package/dist/components/ui/card/index.js +10 -0
  36. package/dist/components/ui/command/command-dialog.svelte +40 -0
  37. package/dist/components/ui/command/command-dialog.svelte.d.ts +12 -0
  38. package/dist/components/ui/command/command-empty.svelte +17 -0
  39. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  40. package/dist/components/ui/command/command-group.svelte +30 -0
  41. package/dist/components/ui/command/command-group.svelte.d.ts +7 -0
  42. package/dist/components/ui/command/command-input.svelte +26 -0
  43. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  44. package/dist/components/ui/command/command-item.svelte +20 -0
  45. package/dist/components/ui/command/command-item.svelte.d.ts +4 -0
  46. package/dist/components/ui/command/command-link-item.svelte +20 -0
  47. package/dist/components/ui/command/command-link-item.svelte.d.ts +4 -0
  48. package/dist/components/ui/command/command-list.svelte +17 -0
  49. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  50. package/dist/components/ui/command/command-loading.svelte +7 -0
  51. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  52. package/dist/components/ui/command/command-separator.svelte +17 -0
  53. package/dist/components/ui/command/command-separator.svelte.d.ts +4 -0
  54. package/dist/components/ui/command/command-shortcut.svelte +20 -0
  55. package/dist/components/ui/command/command-shortcut.svelte.d.ts +5 -0
  56. package/dist/components/ui/command/command.svelte +28 -0
  57. package/dist/components/ui/command/command.svelte.d.ts +8 -0
  58. package/dist/components/ui/command/index.d.ts +12 -0
  59. package/dist/components/ui/command/index.js +14 -0
  60. package/dist/components/ui/dialog/dialog-close.svelte +7 -0
  61. package/dist/components/ui/dialog/dialog-close.svelte.d.ts +4 -0
  62. package/dist/components/ui/dialog/dialog-content.svelte +45 -0
  63. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +13 -0
  64. package/dist/components/ui/dialog/dialog-description.svelte +17 -0
  65. package/dist/components/ui/dialog/dialog-description.svelte.d.ts +4 -0
  66. package/dist/components/ui/dialog/dialog-footer.svelte +20 -0
  67. package/dist/components/ui/dialog/dialog-footer.svelte.d.ts +5 -0
  68. package/dist/components/ui/dialog/dialog-header.svelte +20 -0
  69. package/dist/components/ui/dialog/dialog-header.svelte.d.ts +5 -0
  70. package/dist/components/ui/dialog/dialog-overlay.svelte +20 -0
  71. package/dist/components/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
  72. package/dist/components/ui/dialog/dialog-portal.svelte +7 -0
  73. package/dist/components/ui/dialog/dialog-portal.svelte.d.ts +3 -0
  74. package/dist/components/ui/dialog/dialog-title.svelte +17 -0
  75. package/dist/components/ui/dialog/dialog-title.svelte.d.ts +4 -0
  76. package/dist/components/ui/dialog/dialog-trigger.svelte +7 -0
  77. package/dist/components/ui/dialog/dialog-trigger.svelte.d.ts +4 -0
  78. package/dist/components/ui/dialog/dialog.svelte +7 -0
  79. package/dist/components/ui/dialog/dialog.svelte.d.ts +3 -0
  80. package/dist/components/ui/dialog/index.d.ts +11 -0
  81. package/dist/components/ui/dialog/index.js +13 -0
  82. package/dist/components/ui/input/index.d.ts +2 -0
  83. package/dist/components/ui/input/index.js +4 -0
  84. package/dist/components/ui/input/input.svelte +52 -0
  85. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  86. package/dist/components/ui/kbd/index.d.ts +3 -0
  87. package/dist/components/ui/kbd/index.js +5 -0
  88. package/dist/components/ui/kbd/kbd-group.svelte +20 -0
  89. package/dist/components/ui/kbd/kbd-group.svelte.d.ts +5 -0
  90. package/dist/components/ui/kbd/kbd.svelte +25 -0
  91. package/dist/components/ui/kbd/kbd.svelte.d.ts +5 -0
  92. package/dist/components/ui/popover/index.d.ts +6 -0
  93. package/dist/components/ui/popover/index.js +8 -0
  94. package/dist/components/ui/popover/popover-close.svelte +7 -0
  95. package/dist/components/ui/popover/popover-close.svelte.d.ts +4 -0
  96. package/dist/components/ui/popover/popover-content.svelte +31 -0
  97. package/dist/components/ui/popover/popover-content.svelte.d.ts +10 -0
  98. package/dist/components/ui/popover/popover-portal.svelte +7 -0
  99. package/dist/components/ui/popover/popover-portal.svelte.d.ts +3 -0
  100. package/dist/components/ui/popover/popover-trigger.svelte +17 -0
  101. package/dist/components/ui/popover/popover-trigger.svelte.d.ts +4 -0
  102. package/dist/components/ui/popover/popover.svelte +7 -0
  103. package/dist/components/ui/popover/popover.svelte.d.ts +3 -0
  104. package/dist/components/ui/table/index.d.ts +9 -0
  105. package/dist/components/ui/table/index.js +11 -0
  106. package/dist/components/ui/table/table-body.svelte +20 -0
  107. package/dist/components/ui/table/table-body.svelte.d.ts +5 -0
  108. package/dist/components/ui/table/table-caption.svelte +20 -0
  109. package/dist/components/ui/table/table-caption.svelte.d.ts +5 -0
  110. package/dist/components/ui/table/table-cell.svelte +23 -0
  111. package/dist/components/ui/table/table-cell.svelte.d.ts +5 -0
  112. package/dist/components/ui/table/table-footer.svelte +20 -0
  113. package/dist/components/ui/table/table-footer.svelte.d.ts +5 -0
  114. package/dist/components/ui/table/table-head.svelte +23 -0
  115. package/dist/components/ui/table/table-head.svelte.d.ts +5 -0
  116. package/dist/components/ui/table/table-header.svelte +20 -0
  117. package/dist/components/ui/table/table-header.svelte.d.ts +5 -0
  118. package/dist/components/ui/table/table-row.svelte +23 -0
  119. package/dist/components/ui/table/table-row.svelte.d.ts +5 -0
  120. package/dist/components/ui/table/table.svelte +22 -0
  121. package/dist/components/ui/table/table.svelte.d.ts +5 -0
  122. package/dist/components/ui/tabs/index.d.ts +5 -0
  123. package/dist/components/ui/tabs/index.js +7 -0
  124. package/dist/components/ui/tabs/tabs-content.svelte +17 -0
  125. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  126. package/dist/components/ui/tabs/tabs-list.svelte +20 -0
  127. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  128. package/dist/components/ui/tabs/tabs-trigger.svelte +20 -0
  129. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  130. package/dist/components/ui/tabs/tabs.svelte +19 -0
  131. package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
  132. package/dist/components/ui/tooltip/index.d.ts +6 -0
  133. package/dist/components/ui/tooltip/index.js +8 -0
  134. package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
  135. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
  136. package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
  137. package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
  138. package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
  139. package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
  140. package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  141. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  142. package/dist/components/ui/tooltip/tooltip.svelte +7 -0
  143. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
  144. package/dist/focus.svelte +56 -0
  145. package/dist/focus.svelte.d.ts +13 -0
  146. package/dist/index.d.ts +5 -0
  147. package/dist/index.js +7 -0
  148. package/dist/overlay-component.svelte +19 -0
  149. package/dist/overlay-component.svelte.d.ts +6 -0
  150. package/dist/palette.svelte +132 -0
  151. package/dist/palette.svelte.d.ts +7 -0
  152. package/dist/palette.svelte.js +177 -0
  153. package/dist/shortcut.svelte +26 -0
  154. package/dist/shortcut.svelte.d.ts +8 -0
  155. package/dist/utils.d.ts +13 -0
  156. package/dist/utils.js +32 -0
  157. package/package.json +87 -0
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { Tabs as TabsPrimitive } from "bits-ui";
3
+ import { cn } from "../../../utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: TabsPrimitive.TriggerProps = $props();
10
+ </script>
11
+
12
+ <TabsPrimitive.Trigger
13
+ bind:ref
14
+ data-slot="tabs-trigger"
15
+ class={cn(
16
+ "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
17
+ className
18
+ )}
19
+ {...restProps}
20
+ />
@@ -0,0 +1,4 @@
1
+ import { Tabs as TabsPrimitive } from "bits-ui";
2
+ declare const TabsTrigger: import("svelte").Component<TabsPrimitive.TriggerProps, {}, "ref">;
3
+ type TabsTrigger = ReturnType<typeof TabsTrigger>;
4
+ export default TabsTrigger;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { Tabs as TabsPrimitive } from "bits-ui";
3
+ import { cn } from "../../../utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ value = $bindable(""),
8
+ class: className,
9
+ ...restProps
10
+ }: TabsPrimitive.RootProps = $props();
11
+ </script>
12
+
13
+ <TabsPrimitive.Root
14
+ bind:ref
15
+ bind:value
16
+ data-slot="tabs"
17
+ class={cn("flex flex-col gap-2", className)}
18
+ {...restProps}
19
+ />
@@ -0,0 +1,4 @@
1
+ import { Tabs as TabsPrimitive } from "bits-ui";
2
+ declare const Tabs: import("svelte").Component<TabsPrimitive.RootProps, {}, "ref" | "value">;
3
+ type Tabs = ReturnType<typeof Tabs>;
4
+ export default Tabs;
@@ -0,0 +1,6 @@
1
+ import Root from './tooltip.svelte';
2
+ import Trigger from './tooltip-trigger.svelte';
3
+ import Content from './tooltip-content.svelte';
4
+ import Provider from './tooltip-provider.svelte';
5
+ import Portal from './tooltip-portal.svelte';
6
+ export { Root, Trigger, Content, Provider, Portal, Root as Tooltip, Content as TooltipContent, Trigger as TooltipTrigger, Provider as TooltipProvider, Portal as TooltipPortal };
@@ -0,0 +1,8 @@
1
+ import Root from './tooltip.svelte';
2
+ import Trigger from './tooltip-trigger.svelte';
3
+ import Content from './tooltip-content.svelte';
4
+ import Provider from './tooltip-provider.svelte';
5
+ import Portal from './tooltip-portal.svelte';
6
+ export { Root, Trigger, Content, Provider, Portal,
7
+ //
8
+ Root as Tooltip, Content as TooltipContent, Trigger as TooltipTrigger, Provider as TooltipProvider, Portal as TooltipPortal };
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../utils.js';
4
+ import TooltipPortal from './tooltip-portal.svelte';
5
+ import type { ComponentProps } from 'svelte';
6
+ import type { WithoutChildrenOrChild } from '../../../utils.js';
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ sideOffset = 0,
12
+ side = 'top',
13
+ children,
14
+ arrowClasses,
15
+ portalProps,
16
+ ...restProps
17
+ }: TooltipPrimitive.ContentProps & {
18
+ arrowClasses?: string;
19
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof TooltipPortal>>;
20
+ } = $props();
21
+ </script>
22
+
23
+ <TooltipPortal {...portalProps}>
24
+ <TooltipPrimitive.Content
25
+ bind:ref
26
+ data-slot="tooltip-content"
27
+ {sideOffset}
28
+ {side}
29
+ class={cn(
30
+ 'z-50 w-fit origin-(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
31
+ className
32
+ )}
33
+ {...restProps}
34
+ >
35
+ {@render children?.()}
36
+ <TooltipPrimitive.Arrow>
37
+ {#snippet child({ props })}
38
+ <div
39
+ class={cn(
40
+ 'z-50 size-2.5 rotate-45 rounded-[2px] bg-primary',
41
+ 'data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]',
42
+ 'data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]',
43
+ 'data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2',
44
+ 'data-[side=left]:-translate-y-[calc(50%_-_3px)]',
45
+ arrowClasses
46
+ )}
47
+ {...props}
48
+ ></div>
49
+ {/snippet}
50
+ </TooltipPrimitive.Arrow>
51
+ </TooltipPrimitive.Content>
52
+ </TooltipPortal>
@@ -0,0 +1,11 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ import TooltipPortal from './tooltip-portal.svelte';
3
+ import type { ComponentProps } from 'svelte';
4
+ import type { WithoutChildrenOrChild } from '../../../utils.js';
5
+ type $$ComponentProps = TooltipPrimitive.ContentProps & {
6
+ arrowClasses?: string;
7
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof TooltipPortal>>;
8
+ };
9
+ declare const TooltipContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
10
+ type TooltipContent = ReturnType<typeof TooltipContent>;
11
+ export default TooltipContent;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+
4
+ let { ...restProps }: TooltipPrimitive.PortalProps = $props();
5
+ </script>
6
+
7
+ <TooltipPrimitive.Portal {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ declare const TooltipPortal: import("svelte").Component<TooltipPrimitive.PortalProps, {}, "">;
3
+ type TooltipPortal = ReturnType<typeof TooltipPortal>;
4
+ export default TooltipPortal;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+
4
+ let { ...restProps }: TooltipPrimitive.ProviderProps = $props();
5
+ </script>
6
+
7
+ <TooltipPrimitive.Provider {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ declare const TooltipProvider: import("svelte").Component<TooltipPrimitive.ProviderProps, {}, "">;
3
+ type TooltipProvider = ReturnType<typeof TooltipProvider>;
4
+ export default TooltipProvider;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+
4
+ let { ref = $bindable(null), ...restProps }: TooltipPrimitive.TriggerProps = $props();
5
+ </script>
6
+
7
+ <TooltipPrimitive.Trigger bind:ref data-slot="tooltip-trigger" {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ declare const TooltipTrigger: import("svelte").Component<TooltipPrimitive.TriggerProps, {}, "ref">;
3
+ type TooltipTrigger = ReturnType<typeof TooltipTrigger>;
4
+ export default TooltipTrigger;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+
4
+ let { open = $bindable(false), ...restProps }: TooltipPrimitive.RootProps = $props();
5
+ </script>
6
+
7
+ <TooltipPrimitive.Root bind:open {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ declare const Tooltip: import("svelte").Component<TooltipPrimitive.RootProps, {}, "open">;
3
+ type Tooltip = ReturnType<typeof Tooltip>;
4
+ export default Tooltip;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { shortcut } from './attachment.svelte';
4
+ import type { ClassValue } from 'svelte/elements';
5
+
6
+ let {
7
+ keys,
8
+ description,
9
+ element,
10
+ children,
11
+ after_focus,
12
+ class: className
13
+ }: {
14
+ keys: string | string[] | string[][];
15
+ description?: string;
16
+ element?: HTMLElement;
17
+ children: Snippet;
18
+ after_focus?: () => void;
19
+ class?: ClassValue;
20
+ } = $props();
21
+
22
+ let container: HTMLElement;
23
+
24
+ function focusChild() {
25
+ if (element) {
26
+ element.focus();
27
+ } else if (container) {
28
+ // Try to find the first focusable element
29
+ const focusable = container.querySelector<HTMLElement>(
30
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
31
+ );
32
+
33
+ if (focusable) {
34
+ focusable.focus();
35
+ } else {
36
+ // If no focusable element found, focus the container itself
37
+ container.focus();
38
+ }
39
+ }
40
+
41
+ after_focus?.();
42
+ }
43
+ </script>
44
+
45
+ <div
46
+ bind:this={container}
47
+ tabindex="-1"
48
+ class={className}
49
+ {@attach shortcut({
50
+ keys,
51
+ description: description,
52
+ action: focusChild
53
+ })}
54
+ >
55
+ {@render children()}
56
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassValue } from 'svelte/elements';
3
+ type $$ComponentProps = {
4
+ keys: string | string[] | string[][];
5
+ description?: string;
6
+ element?: HTMLElement;
7
+ children: Snippet;
8
+ after_focus?: () => void;
9
+ class?: ClassValue;
10
+ };
11
+ declare const Focus: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Focus = ReturnType<typeof Focus>;
13
+ export default Focus;
@@ -0,0 +1,5 @@
1
+ export { default as Palette } from './palette.svelte';
2
+ export { default as Shortcut } from './shortcut.svelte';
3
+ export { default as Focus } from './focus.svelte';
4
+ export { shortcut } from './attachment.svelte.js';
5
+ export { shortcuts } from './palette.svelte.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Simple index.ts to export available components
2
+ export { default as Palette } from './palette.svelte';
3
+ export { default as Shortcut } from './shortcut.svelte';
4
+ export { default as Focus } from './focus.svelte';
5
+ // Export functions
6
+ export { shortcut } from './attachment.svelte.js';
7
+ export { shortcuts } from './palette.svelte.js';
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { PressedKeys } from 'runed';
3
+ import Kbds from './components/kbds.svelte';
4
+
5
+ export type OverlayComponentProps = {
6
+ keys: string | string[] | string[][];
7
+ };
8
+
9
+ let { keys }: OverlayComponentProps = $props();
10
+
11
+ const pressed_keys = new PressedKeys();
12
+ const visible = $derived(pressed_keys.has('alt'));
13
+ </script>
14
+
15
+ {#if visible}
16
+ <div class="pointer-events-none absolute bottom-0 left-1/2 z-50 -translate-x-1/2 translate-y-2/3">
17
+ <Kbds {keys} />
18
+ </div>
19
+ {/if}
@@ -0,0 +1,6 @@
1
+ export type OverlayComponentProps = {
2
+ keys: string | string[] | string[][];
3
+ };
4
+ declare const OverlayComponent: import("svelte").Component<OverlayComponentProps, {}, "">;
5
+ type OverlayComponent = ReturnType<typeof OverlayComponent>;
6
+ export default OverlayComponent;
@@ -0,0 +1,132 @@
1
+ <script lang="ts" module>
2
+ export type PalettePosition =
3
+ | 'top-left'
4
+ | 'top-center'
5
+ | 'top-right'
6
+ | 'bottom-left'
7
+ | 'bottom-center'
8
+ | 'bottom-right'
9
+ | 'none';
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import { buttonVariants } from './components/ui/button';
14
+ import * as Dialog from './components/ui/dialog/index.js';
15
+ import * as Kbd from './components/ui/kbd/index.js';
16
+ import * as Table from './components/ui/table';
17
+ import * as Tooltip from './components/ui/tooltip';
18
+ import { Keyboard, ToggleLeft, ToggleRight } from '@lucide/svelte';
19
+ import { PressedKeys } from 'runed';
20
+ import { registry, slugify } from './palette.svelte.js';
21
+ import Shortcut from './shortcut.svelte';
22
+ import Kbds from './components/kbds.svelte';
23
+
24
+ let {
25
+ position = 'bottom-right'
26
+ }: {
27
+ position?: PalettePosition;
28
+ } = $props();
29
+
30
+ let open = $state(false);
31
+ let tooltip_open = $state(false);
32
+
33
+ const pressed_keys = new PressedKeys();
34
+ const all_keys = $derived(pressed_keys.all);
35
+
36
+ const filtered_shortcuts = $derived.by(() => {
37
+ const _all_keys = all_keys.filter((key) => ['?', '/', ' '].indexOf(key) === -1);
38
+ return registry.filteredShortcuts(_all_keys);
39
+ });
40
+
41
+ const positionClass = $derived.by(() => {
42
+ switch (position) {
43
+ case 'top-left':
44
+ return 'fixed left-4 top-4';
45
+ case 'top-center':
46
+ return 'fixed left-1/2 top-4 -translate-x-1/2';
47
+ case 'top-right':
48
+ return 'fixed right-4 top-4';
49
+ case 'bottom-left':
50
+ return 'fixed left-4 bottom-4';
51
+ case 'bottom-center':
52
+ return 'fixed left-1/2 bottom-4 -translate-x-1/2';
53
+ case 'bottom-right':
54
+ return 'fixed right-4 bottom-4';
55
+ case 'none':
56
+ return '';
57
+ }
58
+ });
59
+ </script>
60
+
61
+ <Shortcut keys={[['?'], ['/']]} description="Open shortcut palette" action={() => (open = !open)} />
62
+
63
+ <Tooltip.Provider delayDuration={0}>
64
+ <Tooltip.Root bind:open={tooltip_open}>
65
+ <Tooltip.Trigger
66
+ class={[buttonVariants({ size: 'icon-lg' }), positionClass, 'cursor-pointer']}
67
+ onclick={() => {
68
+ open = !open;
69
+ }}
70
+ >
71
+ <Keyboard />
72
+ </Tooltip.Trigger>
73
+ <Tooltip.Content>
74
+ Press <Kbd.Root>?</Kbd.Root>
75
+ </Tooltip.Content>
76
+ </Tooltip.Root>
77
+ </Tooltip.Provider>
78
+
79
+ <Dialog.Root bind:open>
80
+ <Dialog.Content>
81
+ <Dialog.Header>
82
+ <Dialog.Title>Keyboard Shortcuts</Dialog.Title>
83
+ <Dialog.Description class="my-8">
84
+ <p class="mb-4 text-sm text-muted-foreground">
85
+ Press any key to filter shortcuts containing that key. Matching keys will be highlighted
86
+ in green.
87
+ </p>
88
+ <Table.Root>
89
+ <Table.Header>
90
+ <Table.Row>
91
+ <Table.Head>Keys</Table.Head>
92
+ <Table.Head>Description</Table.Head>
93
+ <Table.Head class="text-right">Enabled</Table.Head>
94
+ </Table.Row>
95
+ </Table.Header>
96
+ <Table.Body>
97
+ {#each filtered_shortcuts as shortcut (slugify(shortcut.keys))}
98
+ <Table.Row>
99
+ <Table.Cell class="font-medium">
100
+ <Kbds keys={shortcut.keys} />
101
+ </Table.Cell>
102
+ <Table.Cell>{shortcut.description}</Table.Cell>
103
+ <Table.Cell class="text-right">
104
+ <button
105
+ class="inline-flex items-center justify-center rounded-md p-2 transition-colors hover:bg-accent hover:text-accent-foreground"
106
+ onclick={() => registry.toggle(shortcut.keys)}
107
+ aria-label={shortcut.enabled ? 'Disable shortcut' : 'Enable shortcut'}
108
+ >
109
+ {#if shortcut.enabled !== false}
110
+ <ToggleRight class="h-5 w-5 text-green-500" />
111
+ {:else}
112
+ <ToggleLeft class="h-5 w-5 text-muted-foreground" />
113
+ {/if}
114
+ </button>
115
+ </Table.Cell>
116
+ </Table.Row>
117
+ {:else}
118
+ <Table.Row>
119
+ <Table.Cell colspan={3} class="text-center py-4 text-sm text-muted-foreground">
120
+ No shortcuts containing
121
+ <Kbds keys={all_keys} />
122
+
123
+ .
124
+ </Table.Cell>
125
+ </Table.Row>
126
+ {/each}
127
+ </Table.Body>
128
+ </Table.Root>
129
+ </Dialog.Description>
130
+ </Dialog.Header>
131
+ </Dialog.Content>
132
+ </Dialog.Root>
@@ -0,0 +1,7 @@
1
+ export type PalettePosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'none';
2
+ type $$ComponentProps = {
3
+ position?: PalettePosition;
4
+ };
5
+ declare const Palette: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Palette = ReturnType<typeof Palette>;
7
+ export default Palette;
@@ -0,0 +1,177 @@
1
+ import { PressedKeys, activeElement } from 'runed';
2
+ class ShortcutRegistry {
3
+ shortcuts = $state({});
4
+ registeredCombos = $state(new Set());
5
+ pressedKeys = new PressedKeys();
6
+ cleanupCallbacks = new Map();
7
+ isListening = false;
8
+ constructor() { }
9
+ startListening() {
10
+ if (this.isListening)
11
+ return;
12
+ this.isListening = true;
13
+ $effect(() => {
14
+ this.syncKeyboardListeners();
15
+ });
16
+ }
17
+ normalizeKeys(keys) {
18
+ if (typeof keys === 'string') {
19
+ return [[keys]];
20
+ }
21
+ if (keys.length === 0) {
22
+ return [];
23
+ }
24
+ if (Array.isArray(keys[0])) {
25
+ return keys.map((combo) => this.sortCombo(combo));
26
+ }
27
+ return [this.sortCombo(keys)];
28
+ }
29
+ sortCombo(combo) {
30
+ return [...combo].sort((a, b) => a.localeCompare(b));
31
+ }
32
+ comboToString(combo) {
33
+ return combo.join('-');
34
+ }
35
+ slugify(keys) {
36
+ const normalized = this.normalizeKeys(keys);
37
+ return normalized
38
+ .map((combo) => this.comboToString(combo).toLowerCase().replace(/\s+/g, '-'))
39
+ .join('|');
40
+ }
41
+ checkCollision(keys, description) {
42
+ for (const combo of keys) {
43
+ const comboString = this.comboToString(combo);
44
+ if (this.registeredCombos.has(comboString)) {
45
+ console.warn(`Shortcut collision detected: "${comboString}" already registered${description ? ` (trying to register: "${description}")` : ''}`);
46
+ return true;
47
+ }
48
+ }
49
+ return false;
50
+ }
51
+ add(shortcut) {
52
+ this.startListening();
53
+ const normalizedKeys = this.normalizeKeys(shortcut.keys);
54
+ if (normalizedKeys.length === 0) {
55
+ console.warn('Cannot add shortcut with no keys');
56
+ return;
57
+ }
58
+ this.checkCollision(normalizedKeys, shortcut.description);
59
+ const slug = this.slugify(shortcut.keys);
60
+ for (const combo of normalizedKeys) {
61
+ const comboString = this.comboToString(combo);
62
+ this.registeredCombos.add(comboString);
63
+ }
64
+ this.shortcuts[slug] = {
65
+ ...shortcut,
66
+ keys: normalizedKeys,
67
+ enabled: shortcut.enabled ?? true
68
+ };
69
+ }
70
+ remove(keys) {
71
+ const slug = this.slugify(keys);
72
+ const shortcut = this.shortcuts[slug];
73
+ if (!shortcut) {
74
+ console.warn(`Shortcut not found for keys: ${JSON.stringify(keys)}`);
75
+ return;
76
+ }
77
+ for (const combo of shortcut.keys) {
78
+ const comboString = this.comboToString(combo);
79
+ this.registeredCombos.delete(comboString);
80
+ }
81
+ delete this.shortcuts[slug];
82
+ }
83
+ toggle(keys) {
84
+ const slug = this.slugify(keys);
85
+ if (this.shortcuts[slug]) {
86
+ this.shortcuts[slug].enabled = !this.shortcuts[slug].enabled;
87
+ }
88
+ }
89
+ getShortcuts() {
90
+ this.startListening();
91
+ return Object.values(this.shortcuts);
92
+ }
93
+ filteredShortcuts(pressedKeys) {
94
+ const allShortcuts = this.getShortcuts();
95
+ if (pressedKeys.length === 0) {
96
+ return allShortcuts;
97
+ }
98
+ const filteredPressedKeys = pressedKeys.filter((key) => ['?', '/', ' ', 'escape'].indexOf(key) === -1);
99
+ return allShortcuts.filter((shortcut) => {
100
+ return shortcut.keys.some((keyCombo) => keyCombo.some((key) => filteredPressedKeys.some((pressedKey) => key.toLowerCase() === pressedKey.toLowerCase())));
101
+ });
102
+ }
103
+ syncKeyboardListeners() {
104
+ for (const [slug, cleanup] of this.cleanupCallbacks) {
105
+ cleanup();
106
+ this.cleanupCallbacks.delete(slug);
107
+ }
108
+ for (const [slug, shortcut] of Object.entries(this.shortcuts)) {
109
+ const cleanup = this.setupKeyboardListener(shortcut);
110
+ this.cleanupCallbacks.set(slug, cleanup);
111
+ }
112
+ }
113
+ setupKeyboardListener(shortcut) {
114
+ for (const keyCombo of shortcut.keys) {
115
+ this.pressedKeys.onKeys(keyCombo, () => {
116
+ const target = activeElement.current;
117
+ const hasModifier = this.hasModifierKey(keyCombo);
118
+ if (shortcut.enabled && (hasModifier || !this.isTypingElement(target))) {
119
+ shortcut.action();
120
+ }
121
+ });
122
+ }
123
+ return () => { };
124
+ }
125
+ hasModifierKey(keys) {
126
+ const modifierKeys = ['control', 'ctrl', 'alt', 'meta', 'command', 'cmd'];
127
+ return keys.some((key) => modifierKeys.includes(key.toLowerCase()));
128
+ }
129
+ isTypingElement(element) {
130
+ if (!element)
131
+ return false;
132
+ const tagName = element.tagName.toLowerCase();
133
+ if (element.isContentEditable)
134
+ return true;
135
+ if (tagName === 'textarea')
136
+ return true;
137
+ if (tagName === 'input') {
138
+ const inputType = element.type.toLowerCase();
139
+ const textTypes = [
140
+ 'text',
141
+ 'password',
142
+ 'email',
143
+ 'search',
144
+ 'tel',
145
+ 'url',
146
+ 'number',
147
+ 'date',
148
+ 'datetime-local',
149
+ 'month',
150
+ 'time',
151
+ 'week'
152
+ ];
153
+ return textTypes.includes(inputType);
154
+ }
155
+ return false;
156
+ }
157
+ }
158
+ export const registry = new ShortcutRegistry();
159
+ export const shortcuts = registry.shortcuts;
160
+ export function add_shortcut(shortcut) {
161
+ registry.add(shortcut);
162
+ }
163
+ export function remove_shortcut(keys) {
164
+ registry.remove(keys);
165
+ }
166
+ export function toggle_shortcut(keys) {
167
+ registry.toggle(keys);
168
+ }
169
+ export function slugify(keys) {
170
+ return registry.slugify(keys);
171
+ }
172
+ export function isArrayOfArrays(keys) {
173
+ return Array.isArray(keys) && keys.length > 0 && Array.isArray(keys[0]);
174
+ }
175
+ export function normalizeKeys(keys) {
176
+ return registry.normalizeKeys(keys);
177
+ }
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { add_shortcut, remove_shortcut } from './palette.svelte.js';
4
+
5
+ let {
6
+ keys,
7
+ description,
8
+ action
9
+ }: {
10
+ keys: string | string[] | string[][];
11
+ description?: string;
12
+ action: () => void;
13
+ } = $props();
14
+
15
+ onMount(() => {
16
+ add_shortcut({
17
+ keys,
18
+ description,
19
+ action
20
+ });
21
+
22
+ return () => {
23
+ remove_shortcut(keys);
24
+ };
25
+ });
26
+ </script>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ keys: string | string[] | string[][];
3
+ description?: string;
4
+ action: () => void;
5
+ };
6
+ declare const Shortcut: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Shortcut = ReturnType<typeof Shortcut>;
8
+ export default Shortcut;