@joewinke/jatui 0.1.33 → 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.
Files changed (209) hide show
  1. package/CLAUDE.md +22 -0
  2. package/dist/actions/attention.d.ts +24 -0
  3. package/dist/actions/attention.js +94 -0
  4. package/dist/actions/floating.d.ts +53 -0
  5. package/dist/actions/floating.js +158 -0
  6. package/dist/actions/staggerEntrance.d.ts +28 -0
  7. package/dist/actions/staggerEntrance.js +71 -0
  8. package/dist/actions/tooltip.d.ts +28 -0
  9. package/dist/actions/tooltip.js +186 -0
  10. package/dist/assets/sounds/README.md +47 -0
  11. package/dist/assets/sounds/error.d.ts +1 -0
  12. package/dist/assets/sounds/error.js +4 -0
  13. package/dist/assets/sounds/notify.d.ts +1 -0
  14. package/dist/assets/sounds/notify.js +4 -0
  15. package/dist/assets/sounds/pop.d.ts +1 -0
  16. package/dist/assets/sounds/pop.js +4 -0
  17. package/dist/assets/sounds/send.d.ts +1 -0
  18. package/dist/assets/sounds/send.js +4 -0
  19. package/dist/assets/sounds/success.d.ts +1 -0
  20. package/dist/assets/sounds/success.js +4 -0
  21. package/dist/assets/sounds/toggle.d.ts +1 -0
  22. package/dist/assets/sounds/toggle.js +4 -0
  23. package/dist/components/AnimatedNumber.svelte +112 -0
  24. package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
  25. package/dist/components/Badge.svelte +75 -4
  26. package/dist/components/Badge.svelte.d.ts +6 -1
  27. package/dist/components/Button.svelte +196 -21
  28. package/dist/components/Button.svelte.d.ts +10 -1
  29. package/dist/components/Carousel.svelte +265 -0
  30. package/dist/components/Carousel.svelte.d.ts +21 -0
  31. package/dist/components/ChipInput.svelte +63 -3
  32. package/dist/components/Collapse.svelte +70 -14
  33. package/dist/components/Collapse.svelte.d.ts +11 -0
  34. package/dist/components/ColorSelector.svelte +21 -24
  35. package/dist/components/CommandPalette.svelte +475 -0
  36. package/dist/components/CommandPalette.svelte.d.ts +3 -0
  37. package/dist/components/ContextMenu.svelte +9 -73
  38. package/dist/components/CopyButton.svelte +156 -0
  39. package/dist/components/CopyButton.svelte.d.ts +12 -0
  40. package/dist/components/HoldToConfirm.svelte +183 -0
  41. package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
  42. package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
  43. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
  44. package/dist/components/PasswordMeter.svelte +87 -0
  45. package/dist/components/PasswordMeter.svelte.d.ts +8 -0
  46. package/dist/components/PinInput.svelte +186 -0
  47. package/dist/components/PinInput.svelte.d.ts +13 -0
  48. package/dist/components/QuantityStepper.svelte +185 -0
  49. package/dist/components/QuantityStepper.svelte.d.ts +12 -0
  50. package/dist/components/Skeleton.svelte +123 -0
  51. package/dist/components/Skeleton.svelte.d.ts +12 -0
  52. package/dist/components/SpeechForm.svelte +37 -0
  53. package/dist/components/StatusBadge.svelte +31 -3
  54. package/dist/components/StatusBadge.svelte.d.ts +0 -5
  55. package/dist/components/Tabs.svelte +270 -0
  56. package/dist/components/Tabs.svelte.d.ts +15 -0
  57. package/dist/components/TextInput.svelte +138 -38
  58. package/dist/components/TextInput.svelte.d.ts +9 -1
  59. package/dist/components/VoicePlayer.svelte +51 -0
  60. package/dist/components/basic/Alert.svelte +36 -0
  61. package/dist/components/basic/Alert.svelte.d.ts +9 -0
  62. package/dist/components/basic/Breadcrumb.svelte +27 -0
  63. package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
  64. package/dist/components/basic/ButtonGroup.svelte +15 -0
  65. package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
  66. package/dist/components/basic/Checkbox.svelte +85 -0
  67. package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
  68. package/dist/components/basic/Empty.svelte +17 -0
  69. package/dist/components/basic/Empty.svelte.d.ts +8 -0
  70. package/dist/components/basic/Kbd.svelte +16 -0
  71. package/dist/components/basic/Kbd.svelte.d.ts +8 -0
  72. package/dist/components/basic/Pagination.svelte +104 -0
  73. package/dist/components/basic/Pagination.svelte.d.ts +12 -0
  74. package/dist/components/basic/RadioGroup.svelte +71 -0
  75. package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
  76. package/dist/components/basic/Separator.svelte +19 -0
  77. package/dist/components/basic/Separator.svelte.d.ts +8 -0
  78. package/dist/components/basic/Slider.svelte +72 -0
  79. package/dist/components/basic/Slider.svelte.d.ts +17 -0
  80. package/dist/components/basic/Switch.svelte +88 -0
  81. package/dist/components/basic/Switch.svelte.d.ts +14 -0
  82. package/dist/components/basic/Toggle.svelte +43 -0
  83. package/dist/components/basic/Toggle.svelte.d.ts +14 -0
  84. package/dist/components/graph/GraphCanvas.svelte +103 -0
  85. package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
  86. package/dist/components/graph/JatuiNode.svelte +128 -0
  87. package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
  88. package/dist/components/graph/theme.d.ts +27 -0
  89. package/dist/components/graph/theme.js +136 -0
  90. package/dist/components/messaging/MessageItem.svelte +92 -0
  91. package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
  92. package/dist/components/messaging/TypingIndicator.svelte +63 -0
  93. package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
  94. package/dist/components/pipeline/PipelineColumn.svelte +44 -3
  95. package/dist/components/progress/ProgressBar.svelte +189 -0
  96. package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
  97. package/dist/components/progress/ProgressRing.svelte +138 -0
  98. package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
  99. package/dist/components/progress/Steps.svelte +195 -0
  100. package/dist/components/progress/Steps.svelte.d.ts +14 -0
  101. package/dist/components/shell/NavigationMenu.svelte +422 -0
  102. package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
  103. package/dist/components/shell/Sidebar.svelte +276 -0
  104. package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
  105. package/dist/components/shell/SidebarGroup.svelte +180 -0
  106. package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
  107. package/dist/components/shell/SidebarItem.svelte +170 -0
  108. package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
  109. package/dist/components/shell/shell.d.ts +30 -0
  110. package/dist/components/shell/shell.js +65 -0
  111. package/dist/components/table/DataTable.svelte +364 -0
  112. package/dist/components/table/DataTable.svelte.d.ts +4 -0
  113. package/dist/components/table/flexRender.d.ts +31 -0
  114. package/dist/components/table/flexRender.js +32 -0
  115. package/dist/components/table/types.d.ts +28 -0
  116. package/dist/components/table/types.js +1 -0
  117. package/dist/components/toast/ToastHost.svelte +21 -0
  118. package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
  119. package/dist/components/toast/ToastItem.svelte +287 -0
  120. package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
  121. package/dist/index.d.ts +59 -0
  122. package/dist/index.js +67 -0
  123. package/dist/showcase/AnimatedBeam.svelte +183 -0
  124. package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
  125. package/dist/showcase/AuroraDrift.svelte +74 -0
  126. package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
  127. package/dist/showcase/BentoCard.svelte +50 -0
  128. package/dist/showcase/BentoCard.svelte.d.ts +14 -0
  129. package/dist/showcase/BentoGrid.svelte +43 -0
  130. package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
  131. package/dist/showcase/BorderBeam.svelte +79 -0
  132. package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
  133. package/dist/showcase/ClipWipe.svelte +82 -0
  134. package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
  135. package/dist/showcase/Confetti.svelte +71 -0
  136. package/dist/showcase/Confetti.svelte.d.ts +10 -0
  137. package/dist/showcase/CursorSpotlight.svelte +85 -0
  138. package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
  139. package/dist/showcase/DeviceMock.svelte +186 -0
  140. package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
  141. package/dist/showcase/Dock.svelte +123 -0
  142. package/dist/showcase/Dock.svelte.d.ts +17 -0
  143. package/dist/showcase/FlipCard.svelte +103 -0
  144. package/dist/showcase/FlipCard.svelte.d.ts +10 -0
  145. package/dist/showcase/FocusReveal.svelte +83 -0
  146. package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
  147. package/dist/showcase/Globe.svelte +202 -0
  148. package/dist/showcase/Globe.svelte.d.ts +16 -0
  149. package/dist/showcase/KenBurns.svelte +105 -0
  150. package/dist/showcase/KenBurns.svelte.d.ts +13 -0
  151. package/dist/showcase/Marquee.svelte +89 -0
  152. package/dist/showcase/Marquee.svelte.d.ts +9 -0
  153. package/dist/showcase/ParallaxTilt.svelte +98 -0
  154. package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
  155. package/dist/showcase/ScrambleText.svelte +98 -0
  156. package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
  157. package/dist/showcase/ShineSweep.svelte +81 -0
  158. package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
  159. package/dist/showcase/Terminal.svelte +230 -0
  160. package/dist/showcase/Terminal.svelte.d.ts +18 -0
  161. package/dist/showcase/TextSplitReveal.svelte +146 -0
  162. package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
  163. package/dist/showcase/TextWave.svelte +55 -0
  164. package/dist/showcase/TextWave.svelte.d.ts +8 -0
  165. package/dist/showcase/TypewriterText.svelte +130 -0
  166. package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
  167. package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
  168. package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
  169. package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
  170. package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
  171. package/dist/showcase/backdrop/Metaballs.svelte +116 -0
  172. package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
  173. package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
  174. package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
  175. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
  176. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
  177. package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
  178. package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
  179. package/dist/showcase/generative/intent.d.ts +7 -0
  180. package/dist/showcase/generative/intent.js +67 -0
  181. package/dist/showcase/generative/manifest.d.ts +9 -0
  182. package/dist/showcase/generative/manifest.js +97 -0
  183. package/dist/showcase/generative/types.d.ts +48 -0
  184. package/dist/showcase/generative/types.js +8 -0
  185. package/dist/showcase/index.d.ts +36 -0
  186. package/dist/showcase/index.js +46 -0
  187. package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
  188. package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
  189. package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
  190. package/dist/showcase/scroll/scrollProgress.js +65 -0
  191. package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
  192. package/dist/showcase/scroll/scrollReveal.js +70 -0
  193. package/dist/stores/commandPalette.svelte.d.ts +24 -0
  194. package/dist/stores/commandPalette.svelte.js +82 -0
  195. package/dist/stores/toast.svelte.d.ts +28 -0
  196. package/dist/stores/toast.svelte.js +33 -0
  197. package/dist/styles/motion.css +46 -0
  198. package/dist/styles/tokens.css +31 -0
  199. package/dist/transitions/glide.d.ts +31 -0
  200. package/dist/transitions/glide.js +58 -0
  201. package/dist/utils/sound.d.ts +6 -0
  202. package/dist/utils/sound.js +113 -0
  203. package/dist/utils/splitText.d.ts +25 -0
  204. package/dist/utils/splitText.js +94 -0
  205. package/dist/utils/themeWipe.d.ts +21 -0
  206. package/dist/utils/themeWipe.js +94 -0
  207. package/dist/utils/viewTransition.d.ts +23 -0
  208. package/dist/utils/viewTransition.js +75 -0
  209. package/package.json +50 -3
@@ -0,0 +1,103 @@
1
+ <script lang="ts">
2
+ import '@xyflow/svelte/dist/style.css'
3
+
4
+ import { SvelteFlow, Background, Controls, MiniMap, type Node, type Edge } from '@xyflow/svelte'
5
+ import JatuiNode from './JatuiNode.svelte'
6
+ import { watchAndApplyTheme } from './theme.js'
7
+
8
+ import type { NodeEventWithPointer } from '@xyflow/svelte'
9
+
10
+ interface Props {
11
+ /** Flow nodes — bindable for interactive graphs. */
12
+ nodes: Node[]
13
+ /** Flow edges — bindable for interactive graphs. */
14
+ edges: Edge[]
15
+ /** When true, disables pan/zoom/selection interactions. */
16
+ readonly?: boolean
17
+ /** Fit the graph to the viewport on first render. */
18
+ fitView?: boolean
19
+ /** Show the minimap panel. */
20
+ minimap?: boolean
21
+ /** Fired when the user clicks a node. */
22
+ onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent>
23
+ /** Height of the canvas container (CSS value). Defaults to 100%. */
24
+ height?: string
25
+ }
26
+
27
+ let {
28
+ nodes = $bindable([]),
29
+ edges = $bindable([]),
30
+ readonly = false,
31
+ fitView = true,
32
+ minimap = false,
33
+ onNodeClick,
34
+ height = '100%',
35
+ }: Props = $props()
36
+
37
+ const nodeTypes = { jatuiNode: JatuiNode }
38
+
39
+ let flowEl: HTMLDivElement | null = $state(null)
40
+ let stopWatch: (() => void) | null = null
41
+
42
+ $effect(() => {
43
+ if (!flowEl) return
44
+ stopWatch?.()
45
+ stopWatch = watchAndApplyTheme(flowEl)
46
+ return () => {
47
+ stopWatch?.()
48
+ stopWatch = null
49
+ }
50
+ })
51
+ </script>
52
+
53
+ <div class="graph-canvas" style:height bind:this={flowEl}>
54
+ <SvelteFlow
55
+ {nodes}
56
+ {edges}
57
+ {nodeTypes}
58
+ {fitView}
59
+ nodesDraggable={!readonly}
60
+ nodesConnectable={!readonly}
61
+ elementsSelectable={!readonly}
62
+ panOnDrag={!readonly}
63
+ zoomOnScroll={!readonly}
64
+ onnodeclick={onNodeClick as NodeEventWithPointer<MouseEvent | TouchEvent> | undefined}
65
+ fitViewOptions={{ padding: 0.2 }}
66
+ attributionPosition="bottom-right"
67
+ >
68
+ <Background gap={16} size={1} />
69
+ <Controls showLock={false} />
70
+ {#if minimap}
71
+ <MiniMap />
72
+ {/if}
73
+ </SvelteFlow>
74
+ </div>
75
+
76
+ <style>
77
+ .graph-canvas {
78
+ width: 100%;
79
+ position: relative;
80
+ /* Contain the xyflow absolute-positioned layers */
81
+ overflow: hidden;
82
+ border-radius: var(--jatui-radius-box);
83
+ border: var(--jatui-border-w) solid var(--color-base-300, oklch(0.9 0 0));
84
+ background: var(--color-base-100, oklch(0.98 0 0));
85
+ }
86
+
87
+ /* Strip xyflow's built-in node box-shadow (house rule: no at-rest shadows) */
88
+ :global(.svelte-flow .svelte-flow__node) {
89
+ box-shadow: none !important;
90
+ filter: none !important;
91
+ }
92
+
93
+ /* Edge animation uses opacity+stroke transitions, not layout — allowed */
94
+ :global(.svelte-flow .svelte-flow__edge path) {
95
+ transition: stroke var(--motion-fast) var(--motion-glide);
96
+ }
97
+
98
+ @media (prefers-reduced-motion: reduce) {
99
+ :global(.svelte-flow .svelte-flow__edge path) {
100
+ transition: none;
101
+ }
102
+ }
103
+ </style>
@@ -0,0 +1,22 @@
1
+ import '@xyflow/svelte/dist/style.css';
2
+ import { type Node, type Edge } from '@xyflow/svelte';
3
+ import type { NodeEventWithPointer } from '@xyflow/svelte';
4
+ interface Props {
5
+ /** Flow nodes — bindable for interactive graphs. */
6
+ nodes: Node[];
7
+ /** Flow edges — bindable for interactive graphs. */
8
+ edges: Edge[];
9
+ /** When true, disables pan/zoom/selection interactions. */
10
+ readonly?: boolean;
11
+ /** Fit the graph to the viewport on first render. */
12
+ fitView?: boolean;
13
+ /** Show the minimap panel. */
14
+ minimap?: boolean;
15
+ /** Fired when the user clicks a node. */
16
+ onNodeClick?: NodeEventWithPointer<MouseEvent | TouchEvent>;
17
+ /** Height of the canvas container (CSS value). Defaults to 100%. */
18
+ height?: string;
19
+ }
20
+ declare const GraphCanvas: import("svelte").Component<Props, {}, "nodes" | "edges">;
21
+ type GraphCanvas = ReturnType<typeof GraphCanvas>;
22
+ export default GraphCanvas;
@@ -0,0 +1,128 @@
1
+ <script lang="ts">
2
+ import { Handle, Position } from '@xyflow/svelte'
3
+ import type { NodeProps } from '@xyflow/svelte'
4
+ import type { NodeStatus } from './theme.js'
5
+
6
+ interface JatuiNodeData {
7
+ label?: string
8
+ status?: NodeStatus
9
+ /** Extra text below the label — e.g. type or description */
10
+ sublabel?: string
11
+ }
12
+
13
+ type Props = NodeProps & { data: JatuiNodeData }
14
+
15
+ let { data, selected = false, sourcePosition = Position.Bottom, targetPosition = Position.Top }: Props = $props()
16
+
17
+ const STATUS_TINT: Record<NodeStatus, string> = {
18
+ default: '',
19
+ primary: 'bg-primary/10 border-primary text-primary',
20
+ secondary: 'bg-secondary/10 border-secondary text-secondary',
21
+ info: 'bg-info/10 border-info text-info',
22
+ success: 'bg-success/10 border-success text-success',
23
+ warning: 'bg-warning/10 border-warning text-warning',
24
+ error: 'bg-error/10 border-error text-error',
25
+ }
26
+
27
+ const STATUS_DOT: Record<NodeStatus, string> = {
28
+ default: '',
29
+ primary: 'bg-primary',
30
+ secondary: 'bg-secondary',
31
+ info: 'bg-info',
32
+ success: 'bg-success',
33
+ warning: 'bg-warning',
34
+ error: 'bg-error',
35
+ }
36
+
37
+ const status = $derived(data?.status ?? 'default')
38
+ const tintClass = $derived(STATUS_TINT[status])
39
+ const dotClass = $derived(STATUS_DOT[status])
40
+ </script>
41
+
42
+ <Handle type="target" position={targetPosition} />
43
+
44
+ <div
45
+ class="jatui-node"
46
+ class:jatui-node--selected={selected}
47
+ class:jatui-node--tinted={status !== 'default'}
48
+ >
49
+ <div class="jatui-node__inner {tintClass}">
50
+ {#if status !== 'default'}
51
+ <span class="jatui-node__dot {dotClass}" aria-hidden="true"></span>
52
+ {/if}
53
+ <span class="jatui-node__label">{data?.label ?? ''}</span>
54
+ {#if data?.sublabel}
55
+ <span class="jatui-node__sublabel">{data.sublabel}</span>
56
+ {/if}
57
+ </div>
58
+ </div>
59
+
60
+ <Handle type="source" position={sourcePosition} />
61
+
62
+ <style>
63
+ .jatui-node {
64
+ min-width: 120px;
65
+ max-width: 240px;
66
+ }
67
+
68
+ .jatui-node__inner {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--jatui-space-sm);
72
+ padding: var(--jatui-space-base) var(--jatui-space-lg);
73
+ background: var(--color-base-200, oklch(0.96 0 0));
74
+ border: var(--jatui-border-w) solid var(--color-base-300, oklch(0.9 0 0));
75
+ border-radius: var(--jatui-radius-box);
76
+ transition:
77
+ border-color var(--motion-fast) var(--motion-glide),
78
+ background var(--motion-fast) var(--motion-glide);
79
+ cursor: default;
80
+ user-select: none;
81
+ }
82
+
83
+ /* Tinted nodes: bg-X/10 and border-X classes applied via class binding */
84
+ .jatui-node--tinted .jatui-node__inner {
85
+ /* colors come from DaisyUI utility classes in the markup */
86
+ }
87
+
88
+ /* Selected ring — uses primary without shadows (house rule) */
89
+ .jatui-node--selected .jatui-node__inner {
90
+ border-color: var(--color-primary, oklch(0.6 0.2 264));
91
+ }
92
+
93
+ .jatui-node__dot {
94
+ flex-shrink: 0;
95
+ width: 6px;
96
+ height: 6px;
97
+ border-radius: 50%;
98
+ }
99
+
100
+ .jatui-node__label {
101
+ font-size: 0.8125rem; /* 13px */
102
+ font-weight: 500;
103
+ color: var(--color-base-content, currentColor);
104
+ line-height: 1.3;
105
+ word-break: break-word;
106
+ }
107
+
108
+ /* Tinted label inherits color from the tint utility class text-* */
109
+ .jatui-node--tinted .jatui-node__inner .jatui-node__label {
110
+ color: inherit;
111
+ }
112
+
113
+ .jatui-node__sublabel {
114
+ display: block;
115
+ font-size: 0.6875rem; /* 11px */
116
+ color: var(--color-base-content, currentColor);
117
+ opacity: 0.55;
118
+ line-height: 1.3;
119
+ margin-top: 2px;
120
+ }
121
+
122
+ /* Reduced motion: strip transitions */
123
+ @media (prefers-reduced-motion: reduce) {
124
+ .jatui-node__inner {
125
+ transition: none;
126
+ }
127
+ }
128
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { NodeProps } from '@xyflow/svelte';
2
+ import type { NodeStatus } from './theme.js';
3
+ interface JatuiNodeData {
4
+ label?: string;
5
+ status?: NodeStatus;
6
+ /** Extra text below the label — e.g. type or description */
7
+ sublabel?: string;
8
+ }
9
+ type Props = NodeProps & {
10
+ data: JatuiNodeData;
11
+ };
12
+ declare const JatuiNode: import("svelte").Component<Props, {}, "">;
13
+ type JatuiNode = ReturnType<typeof JatuiNode>;
14
+ export default JatuiNode;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * GraphCanvas theme bridge — maps DaisyUI --color-* custom properties onto
3
+ * xyflow CSS vars so the graph follows the active DaisyUI theme at runtime.
4
+ *
5
+ * Call applyXyflowTheme(el) after mount and whenever the theme changes.
6
+ * el should be the .svelte-flow wrapper element.
7
+ */
8
+ /**
9
+ * Status colors available for JatuiNode status-tinting.
10
+ * Returns a map of status → resolved CSS color string.
11
+ */
12
+ export type NodeStatus = 'default' | 'info' | 'success' | 'warning' | 'error' | 'primary' | 'secondary';
13
+ export declare function getStatusColor(status: NodeStatus): {
14
+ bg: string;
15
+ border: string;
16
+ text: string;
17
+ };
18
+ /**
19
+ * Apply DaisyUI-derived xyflow CSS vars to the flow container element.
20
+ * Safe to call multiple times — just re-sets the vars.
21
+ */
22
+ export declare function applyXyflowTheme(el: HTMLElement): void;
23
+ /**
24
+ * Setup watcher: re-applies theme whenever DaisyUI theme changes.
25
+ * Returns cleanup fn.
26
+ */
27
+ export declare function watchAndApplyTheme(el: HTMLElement): () => void;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * GraphCanvas theme bridge — maps DaisyUI --color-* custom properties onto
3
+ * xyflow CSS vars so the graph follows the active DaisyUI theme at runtime.
4
+ *
5
+ * Call applyXyflowTheme(el) after mount and whenever the theme changes.
6
+ * el should be the .svelte-flow wrapper element.
7
+ */
8
+ import { getDaisyUIColor, watchThemeChanges } from '../../utils/daisyuiColors.js';
9
+ /** All xyflow CSS vars we override, plus the fallback xyflow var they replace. */
10
+ var THEME_MAP = [
11
+ // Edges
12
+ ['--xy-edge-stroke', 'var(--color-base-content, #b1b1b7)'],
13
+ ['--xy-edge-stroke-selected', 'var(--color-primary, #555)'],
14
+ ['--xy-connectionline-stroke', 'var(--color-base-content, #b1b1b7)'],
15
+ // Background canvas
16
+ ['--xy-background-color', 'var(--color-base-100, transparent)'],
17
+ ['--xy-background-pattern-dots-color', 'var(--color-base-300, #91919a)'],
18
+ ['--xy-background-pattern-lines-color', 'var(--color-base-200, #eee)'],
19
+ ['--xy-background-pattern-cross-color', 'var(--color-base-300, #e2e2e2)'],
20
+ // Node defaults
21
+ ['--xy-node-color', 'var(--color-base-content, #333)'],
22
+ ['--xy-node-border', '1px solid var(--color-base-300, #bbb)'],
23
+ ['--xy-node-border-selected', '1px solid var(--color-primary, #555)'],
24
+ ['--xy-node-background-color', 'var(--color-base-200, #fff)'],
25
+ // Handle
26
+ ['--xy-handle-background-color', 'var(--color-base-content, #333)'],
27
+ ['--xy-handle-border-color', 'var(--color-base-100, #fff)'],
28
+ // Minimap
29
+ ['--xy-minimap-background-color', 'var(--color-base-100, #fff)'],
30
+ ['--xy-minimap-mask-background-color', 'color-mix(in oklch, var(--color-base-200, #f0f0f0) 60%, transparent)'],
31
+ ['--xy-minimap-node-background-color', 'var(--color-base-200, #e2e2e2)'],
32
+ // Selection box
33
+ ['--xy-selection-background-color', 'color-mix(in oklch, var(--color-primary, #6366f1) 10%, transparent)'],
34
+ ['--xy-selection-border', '1px dotted color-mix(in oklch, var(--color-primary, #6366f1) 50%, transparent)'],
35
+ // Attribution
36
+ ['--xy-attribution-background-color', 'color-mix(in oklch, var(--color-base-100, #fff) 80%, transparent)'],
37
+ ];
38
+ export function getStatusColor(status) {
39
+ switch (status) {
40
+ case 'primary':
41
+ return {
42
+ bg: getDaisyUIColor('primary'),
43
+ border: getDaisyUIColor('primary'),
44
+ text: getDaisyUIColor('primary-content'),
45
+ };
46
+ case 'secondary':
47
+ return {
48
+ bg: getDaisyUIColor('secondary'),
49
+ border: getDaisyUIColor('secondary'),
50
+ text: getDaisyUIColor('secondary-content'),
51
+ };
52
+ case 'info':
53
+ return {
54
+ bg: getDaisyUIColor('info'),
55
+ border: getDaisyUIColor('info'),
56
+ text: getDaisyUIColor('info-content'),
57
+ };
58
+ case 'success':
59
+ return {
60
+ bg: getDaisyUIColor('success'),
61
+ border: getDaisyUIColor('success'),
62
+ text: getDaisyUIColor('success-content'),
63
+ };
64
+ case 'warning':
65
+ return {
66
+ bg: getDaisyUIColor('warning'),
67
+ border: getDaisyUIColor('warning'),
68
+ text: getDaisyUIColor('warning-content'),
69
+ };
70
+ case 'error':
71
+ return {
72
+ bg: getDaisyUIColor('error'),
73
+ border: getDaisyUIColor('error'),
74
+ text: getDaisyUIColor('error-content'),
75
+ };
76
+ default:
77
+ return { bg: '', border: '', text: '' };
78
+ }
79
+ }
80
+ /**
81
+ * Apply DaisyUI-derived xyflow CSS vars to the flow container element.
82
+ * Safe to call multiple times — just re-sets the vars.
83
+ */
84
+ export function applyXyflowTheme(el) {
85
+ for (var _i = 0, THEME_MAP_1 = THEME_MAP; _i < THEME_MAP_1.length; _i++) {
86
+ var prop = THEME_MAP_1[_i][0];
87
+ // We set the vars as CSS custom properties on the element itself.
88
+ // xyflow reads them from .svelte-flow context, so setting on the wrapper is correct.
89
+ var cssVarName = prop.replace('--xy-', '--xy-'); // identity — keep readable
90
+ // Read the DaisyUI value dynamically then set
91
+ var mappedValue = resolveThemeValue(prop);
92
+ el.style.setProperty(cssVarName, mappedValue);
93
+ }
94
+ }
95
+ function resolveThemeValue(prop) {
96
+ var _a, _b;
97
+ // Map the xyflow prop name to the DaisyUI color resolution
98
+ var propToColor = {
99
+ '--xy-edge-stroke': function () { return getDaisyUIColor('base-content'); },
100
+ '--xy-edge-stroke-selected': function () { return getDaisyUIColor('primary'); },
101
+ '--xy-connectionline-stroke': function () { return getDaisyUIColor('base-content'); },
102
+ '--xy-background-color': function () { return getDaisyUIColor('base-100'); },
103
+ '--xy-background-pattern-dots-color': function () { return getDaisyUIColor('base-300'); },
104
+ '--xy-background-pattern-lines-color': function () { return getDaisyUIColor('base-200'); },
105
+ '--xy-background-pattern-cross-color': function () { return getDaisyUIColor('base-300'); },
106
+ '--xy-node-color': function () { return getDaisyUIColor('base-content'); },
107
+ '--xy-node-border': function () { return "1px solid ".concat(getDaisyUIColor('base-300')); },
108
+ '--xy-node-border-selected': function () { return "1px solid ".concat(getDaisyUIColor('primary')); },
109
+ '--xy-node-background-color': function () { return getDaisyUIColor('base-200'); },
110
+ '--xy-handle-background-color': function () { return getDaisyUIColor('base-content'); },
111
+ '--xy-handle-border-color': function () { return getDaisyUIColor('base-100'); },
112
+ '--xy-minimap-background-color': function () { return getDaisyUIColor('base-100'); },
113
+ '--xy-minimap-mask-background-color': function () {
114
+ return "color-mix(in oklch, ".concat(getDaisyUIColor('base-200'), " 60%, transparent)");
115
+ },
116
+ '--xy-minimap-node-background-color': function () { return getDaisyUIColor('base-200'); },
117
+ '--xy-selection-background-color': function () {
118
+ return "color-mix(in oklch, ".concat(getDaisyUIColor('primary'), " 10%, transparent)");
119
+ },
120
+ '--xy-selection-border': function () {
121
+ return "1px dotted color-mix(in oklch, ".concat(getDaisyUIColor('primary'), " 50%, transparent)");
122
+ },
123
+ '--xy-attribution-background-color': function () {
124
+ return "color-mix(in oklch, ".concat(getDaisyUIColor('base-100'), " 80%, transparent)");
125
+ },
126
+ };
127
+ return (_b = (_a = propToColor[prop]) === null || _a === void 0 ? void 0 : _a.call(propToColor)) !== null && _b !== void 0 ? _b : '';
128
+ }
129
+ /**
130
+ * Setup watcher: re-applies theme whenever DaisyUI theme changes.
131
+ * Returns cleanup fn.
132
+ */
133
+ export function watchAndApplyTheme(el) {
134
+ applyXyflowTheme(el);
135
+ return watchThemeChanges(function () { return applyXyflowTheme(el); });
136
+ }
@@ -21,6 +21,8 @@
21
21
  import MessageAttachment from './MessageAttachment.svelte'
22
22
  import EmojiSelector from './EmojiSelector.svelte'
23
23
 
24
+ import type { Snippet } from 'svelte'
25
+
24
26
  interface Props {
25
27
  message: Message
26
28
  isOwn?: boolean
@@ -43,6 +45,10 @@
43
45
  onopenthread?: (data: { messageId: string }) => void
44
46
  onquotereply?: (data: { messageId: string }) => void
45
47
  onreactionchange?: () => void
48
+ /** Enable swipe-to-reveal on coarse pointers (touch). Defaults to false. */
49
+ swipeActions?: boolean
50
+ /** Snippet rendered in the revealed action zone (right side, behind the item). */
51
+ swipeContent?: Snippet
46
52
  }
47
53
 
48
54
  let {
@@ -64,6 +70,8 @@
64
70
  onopenthread,
65
71
  onquotereply,
66
72
  onreactionchange,
73
+ swipeActions = false,
74
+ swipeContent,
67
75
  }: Props = $props()
68
76
 
69
77
  let isEditing = $state(false)
@@ -72,6 +80,66 @@
72
80
  let showEmojiSelector = $state(false)
73
81
  let emojiSelectorPosition = $state({ x: 0, y: 0 })
74
82
 
83
+ // Swipe-to-reveal state (coarse pointer / touch only)
84
+ const SWIPE_THRESHOLD = 96
85
+ const RUBBER_BAND_LIMIT = 130
86
+ let swipeOpen = $state(false)
87
+ let swipeDragging = $state(false)
88
+ let swipeOffset = $state(0)
89
+ let swipeStartX = 0
90
+ let swipeStartOpen = false
91
+
92
+ function isCoarsePointer(e: PointerEvent) {
93
+ return e.pointerType === 'touch' || e.pointerType === 'pen'
94
+ }
95
+
96
+ function swipeRubberBand(raw: number): number {
97
+ if (raw >= 0) return 0
98
+ const abs = Math.abs(raw)
99
+ if (abs <= SWIPE_THRESHOLD) return raw
100
+ const over = abs - SWIPE_THRESHOLD
101
+ const damped = SWIPE_THRESHOLD + over * (1 - over / (RUBBER_BAND_LIMIT * 2))
102
+ return -Math.min(damped, RUBBER_BAND_LIMIT)
103
+ }
104
+
105
+ function onSwipePointerDown(e: PointerEvent) {
106
+ if (!swipeActions || !swipeContent || !isCoarsePointer(e)) return
107
+ swipeStartX = e.clientX
108
+ swipeStartOpen = swipeOpen
109
+ swipeDragging = true
110
+ ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)
111
+ }
112
+
113
+ function onSwipePointerMove(e: PointerEvent) {
114
+ if (!swipeDragging) return
115
+ const baseOffset = swipeStartOpen ? -SWIPE_THRESHOLD : 0
116
+ const raw = baseOffset + (e.clientX - swipeStartX)
117
+ swipeOffset = swipeRubberBand(raw)
118
+ }
119
+
120
+ function onSwipePointerUp(e: PointerEvent) {
121
+ if (!swipeDragging) return
122
+ swipeDragging = false
123
+ const baseOffset = swipeStartOpen ? -SWIPE_THRESHOLD : 0
124
+ const delta = baseOffset + (e.clientX - swipeStartX)
125
+ // Velocity hint: fast leftward swipe counts even without crossing threshold
126
+ const velocity = e.clientX - swipeStartX
127
+ if (delta < -SWIPE_THRESHOLD / 2 || velocity < -30) {
128
+ swipeOpen = true
129
+ } else {
130
+ swipeOpen = false
131
+ }
132
+ swipeOffset = 0
133
+ }
134
+
135
+ const swipeTranslateX = $derived(
136
+ swipeDragging
137
+ ? swipeOffset
138
+ : swipeOpen
139
+ ? -SWIPE_THRESHOLD
140
+ : 0
141
+ )
142
+
75
143
  function formatTime(dateString: string): string {
76
144
  const date = new Date(dateString)
77
145
  const now = new Date()
@@ -182,6 +250,27 @@
182
250
  }
183
251
  </script>
184
252
 
253
+ <!-- Swipe wrapper: always rendered; overflow-hidden clips the action zone -->
254
+ <div
255
+ class="swipe-wrapper relative overflow-hidden"
256
+ class:rounded={swipeActions && !!swipeContent}
257
+ >
258
+ <!-- Action zone — only present when swipe is enabled -->
259
+ {#if swipeActions && swipeContent}
260
+ <div class="swipe-actions absolute inset-y-0 right-0 flex items-center pr-2 gap-1" aria-hidden={!swipeOpen}>
261
+ {@render swipeContent()}
262
+ </div>
263
+ {/if}
264
+
265
+ <!-- Draggable layer (swipe) or passthrough div -->
266
+ <div
267
+ style={swipeActions && swipeContent ? `transform: translateX(${swipeTranslateX}px); transition: ${swipeDragging ? 'none' : 'transform var(--motion-move) var(--motion-spring)'};` : undefined}
268
+ onpointerdown={swipeActions && swipeContent ? onSwipePointerDown : undefined}
269
+ onpointermove={swipeActions && swipeContent ? onSwipePointerMove : undefined}
270
+ onpointerup={swipeActions && swipeContent ? onSwipePointerUp : undefined}
271
+ onpointercancel={swipeActions && swipeContent ? onSwipePointerUp : undefined}
272
+ >
273
+
185
274
  <div
186
275
  class="group flex gap-3 px-2 py-1 rounded relative transition-colors w-full text-left cursor-pointer {message.isPinned ? 'bg-warning/5 hover:bg-warning/10' : 'hover:bg-base-200'}"
187
276
  class:pt-2={!isConsecutive}
@@ -328,6 +417,9 @@
328
417
  </div>
329
418
  </div>
330
419
 
420
+ </div><!-- /draggable layer -->
421
+ </div><!-- /swipe-wrapper -->
422
+
331
423
  <!-- Emoji Selector -->
332
424
  {#if showEmojiSelector && currentUserId}
333
425
  <EmojiSelector
@@ -1,4 +1,5 @@
1
1
  import type { Message, ReactionSummary, StandardEmoji, CustomEmoji, ReactionCallbacks, AttachmentCallbacks } from '../../types/messaging';
2
+ import type { Snippet } from 'svelte';
2
3
  interface Props {
3
4
  message: Message;
4
5
  isOwn?: boolean;
@@ -35,6 +36,10 @@ interface Props {
35
36
  messageId: string;
36
37
  }) => void;
37
38
  onreactionchange?: () => void;
39
+ /** Enable swipe-to-reveal on coarse pointers (touch). Defaults to false. */
40
+ swipeActions?: boolean;
41
+ /** Snippet rendered in the revealed action zone (right side, behind the item). */
42
+ swipeContent?: Snippet;
38
43
  }
39
44
  declare const MessageItem: import("svelte").Component<Props, {}, "">;
40
45
  type MessageItem = ReturnType<typeof MessageItem>;
@@ -0,0 +1,63 @@
1
+ <!--
2
+ TypingIndicator — three-dot phase-offset bounce (kinetics: Typing Indicator).
3
+ Infinite loop is sanctioned for functional status indicators.
4
+ Reduced-motion: static dots + label remain visible.
5
+ -->
6
+
7
+ <script lang="ts">
8
+ interface Props {
9
+ users?: string[]
10
+ class?: string
11
+ }
12
+
13
+ let { users = [], class: cls = '' }: Props = $props()
14
+
15
+ const label = $derived(
16
+ users.length === 0
17
+ ? 'typing…'
18
+ : users.length === 1
19
+ ? `${users[0]} is typing…`
20
+ : users.length === 2
21
+ ? `${users[0]} and ${users[1]} are typing…`
22
+ : `${users[0]} and ${users.length - 1} others are typing…`
23
+ )
24
+ </script>
25
+
26
+ <div class="typing-indicator inline-flex items-center gap-1.5 {cls}" aria-label={label} role="status">
27
+ <span class="dot" aria-hidden="true"></span>
28
+ <span class="dot" style="animation-delay: 0.16s" aria-hidden="true"></span>
29
+ <span class="dot" style="animation-delay: 0.32s" aria-hidden="true"></span>
30
+ {#if users.length > 0}
31
+ <span class="text-xs text-base-content/60 ml-0.5">{label}</span>
32
+ {/if}
33
+ </div>
34
+
35
+ <style>
36
+ .dot {
37
+ width: 4px;
38
+ height: 4px;
39
+ border-radius: 50%;
40
+ background-color: oklch(from var(--color-base-content, currentColor) l c h / 0.6);
41
+ animation: typing-bounce 1.2s ease-in-out infinite;
42
+ display: inline-block;
43
+ flex-shrink: 0;
44
+ }
45
+
46
+ @keyframes typing-bounce {
47
+ 0%, 60%, 100% {
48
+ transform: translateY(0);
49
+ opacity: 0.4;
50
+ }
51
+ 30% {
52
+ transform: translateY(-6px);
53
+ opacity: 1;
54
+ }
55
+ }
56
+
57
+ @media (prefers-reduced-motion: reduce) {
58
+ .dot {
59
+ animation: none;
60
+ opacity: 0.6;
61
+ }
62
+ }
63
+ </style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ users?: string[];
3
+ class?: string;
4
+ }
5
+ declare const TypingIndicator: import("svelte").Component<Props, {}, "">;
6
+ type TypingIndicator = ReturnType<typeof TypingIndicator>;
7
+ export default TypingIndicator;