@joewinke/jatui 0.1.31 → 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 (239) 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/collapse.js +29 -15
  5. package/dist/actions/floating.d.ts +53 -0
  6. package/dist/actions/floating.js +158 -0
  7. package/dist/actions/listNav.js +137 -108
  8. package/dist/actions/railNav.js +89 -72
  9. package/dist/actions/staggerEntrance.d.ts +28 -0
  10. package/dist/actions/staggerEntrance.js +71 -0
  11. package/dist/actions/tooltip.d.ts +28 -0
  12. package/dist/actions/tooltip.js +186 -0
  13. package/dist/assets/sounds/README.md +47 -0
  14. package/dist/assets/sounds/error.d.ts +1 -0
  15. package/dist/assets/sounds/error.js +4 -0
  16. package/dist/assets/sounds/notify.d.ts +1 -0
  17. package/dist/assets/sounds/notify.js +4 -0
  18. package/dist/assets/sounds/pop.d.ts +1 -0
  19. package/dist/assets/sounds/pop.js +4 -0
  20. package/dist/assets/sounds/send.d.ts +1 -0
  21. package/dist/assets/sounds/send.js +4 -0
  22. package/dist/assets/sounds/success.d.ts +1 -0
  23. package/dist/assets/sounds/success.js +4 -0
  24. package/dist/assets/sounds/toggle.d.ts +1 -0
  25. package/dist/assets/sounds/toggle.js +4 -0
  26. package/dist/components/AnimatedNumber.svelte +112 -0
  27. package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
  28. package/dist/components/Badge.svelte +75 -4
  29. package/dist/components/Badge.svelte.d.ts +6 -1
  30. package/dist/components/Button.svelte +196 -21
  31. package/dist/components/Button.svelte.d.ts +10 -1
  32. package/dist/components/Carousel.svelte +265 -0
  33. package/dist/components/Carousel.svelte.d.ts +21 -0
  34. package/dist/components/ChipInput.svelte +63 -3
  35. package/dist/components/Collapse.svelte +70 -14
  36. package/dist/components/Collapse.svelte.d.ts +11 -0
  37. package/dist/components/ColorSelector.svelte +21 -24
  38. package/dist/components/CommandPalette.svelte +475 -0
  39. package/dist/components/CommandPalette.svelte.d.ts +3 -0
  40. package/dist/components/ContextMenu.svelte +9 -73
  41. package/dist/components/CopyButton.svelte +156 -0
  42. package/dist/components/CopyButton.svelte.d.ts +12 -0
  43. package/dist/components/HoldToConfirm.svelte +183 -0
  44. package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
  45. package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
  46. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
  47. package/dist/components/PasswordMeter.svelte +87 -0
  48. package/dist/components/PasswordMeter.svelte.d.ts +8 -0
  49. package/dist/components/PinInput.svelte +186 -0
  50. package/dist/components/PinInput.svelte.d.ts +13 -0
  51. package/dist/components/QuantityStepper.svelte +185 -0
  52. package/dist/components/QuantityStepper.svelte.d.ts +12 -0
  53. package/dist/components/Skeleton.svelte +123 -0
  54. package/dist/components/Skeleton.svelte.d.ts +12 -0
  55. package/dist/components/SpeechForm.svelte +37 -0
  56. package/dist/components/StatusBadge.svelte +31 -3
  57. package/dist/components/StatusBadge.svelte.d.ts +0 -5
  58. package/dist/components/Tabs.svelte +270 -0
  59. package/dist/components/Tabs.svelte.d.ts +15 -0
  60. package/dist/components/TextInput.svelte +138 -38
  61. package/dist/components/TextInput.svelte.d.ts +9 -1
  62. package/dist/components/VoicePlayer.svelte +51 -0
  63. package/dist/components/basic/Alert.svelte +36 -0
  64. package/dist/components/basic/Alert.svelte.d.ts +9 -0
  65. package/dist/components/basic/Breadcrumb.svelte +27 -0
  66. package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
  67. package/dist/components/basic/ButtonGroup.svelte +15 -0
  68. package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
  69. package/dist/components/basic/Checkbox.svelte +85 -0
  70. package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
  71. package/dist/components/basic/Empty.svelte +17 -0
  72. package/dist/components/basic/Empty.svelte.d.ts +8 -0
  73. package/dist/components/basic/Kbd.svelte +16 -0
  74. package/dist/components/basic/Kbd.svelte.d.ts +8 -0
  75. package/dist/components/basic/Pagination.svelte +104 -0
  76. package/dist/components/basic/Pagination.svelte.d.ts +12 -0
  77. package/dist/components/basic/RadioGroup.svelte +71 -0
  78. package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
  79. package/dist/components/basic/Separator.svelte +19 -0
  80. package/dist/components/basic/Separator.svelte.d.ts +8 -0
  81. package/dist/components/basic/Slider.svelte +72 -0
  82. package/dist/components/basic/Slider.svelte.d.ts +17 -0
  83. package/dist/components/basic/Switch.svelte +88 -0
  84. package/dist/components/basic/Switch.svelte.d.ts +14 -0
  85. package/dist/components/basic/Toggle.svelte +43 -0
  86. package/dist/components/basic/Toggle.svelte.d.ts +14 -0
  87. package/dist/components/graph/GraphCanvas.svelte +103 -0
  88. package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
  89. package/dist/components/graph/JatuiNode.svelte +128 -0
  90. package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
  91. package/dist/components/graph/theme.d.ts +27 -0
  92. package/dist/components/graph/theme.js +136 -0
  93. package/dist/components/markup.js +62 -59
  94. package/dist/components/messaging/MessageItem.svelte +92 -0
  95. package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
  96. package/dist/components/messaging/TypingIndicator.svelte +63 -0
  97. package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
  98. package/dist/components/pipeline/PipelineColumn.svelte +44 -3
  99. package/dist/components/progress/ProgressBar.svelte +189 -0
  100. package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
  101. package/dist/components/progress/ProgressRing.svelte +138 -0
  102. package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
  103. package/dist/components/progress/Steps.svelte +195 -0
  104. package/dist/components/progress/Steps.svelte.d.ts +14 -0
  105. package/dist/components/session-nav/transcriptModel.js +66 -49
  106. package/dist/components/shell/NavigationMenu.svelte +422 -0
  107. package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
  108. package/dist/components/shell/Sidebar.svelte +276 -0
  109. package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
  110. package/dist/components/shell/SidebarGroup.svelte +180 -0
  111. package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
  112. package/dist/components/shell/SidebarItem.svelte +170 -0
  113. package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
  114. package/dist/components/shell/shell.d.ts +30 -0
  115. package/dist/components/shell/shell.js +65 -0
  116. package/dist/components/table/DataTable.svelte +364 -0
  117. package/dist/components/table/DataTable.svelte.d.ts +4 -0
  118. package/dist/components/table/flexRender.d.ts +31 -0
  119. package/dist/components/table/flexRender.js +32 -0
  120. package/dist/components/table/types.d.ts +28 -0
  121. package/dist/components/table/types.js +1 -0
  122. package/dist/components/toast/ToastHost.svelte +21 -0
  123. package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
  124. package/dist/components/toast/ToastItem.svelte +287 -0
  125. package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
  126. package/dist/data/emojis.js +4 -4
  127. package/dist/dnd/autoScroll.js +12 -10
  128. package/dist/dnd/createDndBoard.svelte.js +110 -76
  129. package/dist/dnd/createDndBoard.test.js +161 -85
  130. package/dist/dnd/draggable.js +23 -15
  131. package/dist/dnd/dropTarget.js +29 -15
  132. package/dist/dnd/index.d.ts +0 -1
  133. package/dist/dnd/index.js +0 -1
  134. package/dist/dnd/types.d.ts +0 -9
  135. package/dist/dnd/types.js +14 -2
  136. package/dist/index.d.ts +60 -6
  137. package/dist/index.js +73 -6
  138. package/dist/showcase/AnimatedBeam.svelte +183 -0
  139. package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
  140. package/dist/showcase/AuroraDrift.svelte +74 -0
  141. package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
  142. package/dist/showcase/BentoCard.svelte +50 -0
  143. package/dist/showcase/BentoCard.svelte.d.ts +14 -0
  144. package/dist/showcase/BentoGrid.svelte +43 -0
  145. package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
  146. package/dist/showcase/BorderBeam.svelte +79 -0
  147. package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
  148. package/dist/showcase/ClipWipe.svelte +82 -0
  149. package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
  150. package/dist/showcase/Confetti.svelte +71 -0
  151. package/dist/showcase/Confetti.svelte.d.ts +10 -0
  152. package/dist/showcase/CursorSpotlight.svelte +85 -0
  153. package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
  154. package/dist/showcase/DeviceMock.svelte +186 -0
  155. package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
  156. package/dist/showcase/Dock.svelte +123 -0
  157. package/dist/showcase/Dock.svelte.d.ts +17 -0
  158. package/dist/showcase/FlipCard.svelte +103 -0
  159. package/dist/showcase/FlipCard.svelte.d.ts +10 -0
  160. package/dist/showcase/FocusReveal.svelte +83 -0
  161. package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
  162. package/dist/showcase/Globe.svelte +202 -0
  163. package/dist/showcase/Globe.svelte.d.ts +16 -0
  164. package/dist/showcase/KenBurns.svelte +105 -0
  165. package/dist/showcase/KenBurns.svelte.d.ts +13 -0
  166. package/dist/showcase/Marquee.svelte +89 -0
  167. package/dist/showcase/Marquee.svelte.d.ts +9 -0
  168. package/dist/showcase/ParallaxTilt.svelte +98 -0
  169. package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
  170. package/dist/showcase/ScrambleText.svelte +98 -0
  171. package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
  172. package/dist/showcase/ShineSweep.svelte +81 -0
  173. package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
  174. package/dist/showcase/Terminal.svelte +230 -0
  175. package/dist/showcase/Terminal.svelte.d.ts +18 -0
  176. package/dist/showcase/TextSplitReveal.svelte +146 -0
  177. package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
  178. package/dist/showcase/TextWave.svelte +55 -0
  179. package/dist/showcase/TextWave.svelte.d.ts +8 -0
  180. package/dist/showcase/TypewriterText.svelte +130 -0
  181. package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
  182. package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
  183. package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
  184. package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
  185. package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
  186. package/dist/showcase/backdrop/Metaballs.svelte +116 -0
  187. package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
  188. package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
  189. package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
  190. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
  191. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
  192. package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
  193. package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
  194. package/dist/showcase/generative/intent.d.ts +7 -0
  195. package/dist/showcase/generative/intent.js +67 -0
  196. package/dist/showcase/generative/manifest.d.ts +9 -0
  197. package/dist/showcase/generative/manifest.js +97 -0
  198. package/dist/showcase/generative/types.d.ts +48 -0
  199. package/dist/showcase/generative/types.js +8 -0
  200. package/dist/showcase/index.d.ts +36 -0
  201. package/dist/showcase/index.js +46 -0
  202. package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
  203. package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
  204. package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
  205. package/dist/showcase/scroll/scrollProgress.js +65 -0
  206. package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
  207. package/dist/showcase/scroll/scrollReveal.js +70 -0
  208. package/dist/stores/commandPalette.svelte.d.ts +24 -0
  209. package/dist/stores/commandPalette.svelte.js +82 -0
  210. package/dist/stores/confirmDialog.svelte.js +5 -5
  211. package/dist/stores/inputDialog.svelte.js +5 -5
  212. package/dist/stores/toast.svelte.d.ts +28 -0
  213. package/dist/stores/toast.svelte.js +33 -0
  214. package/dist/styles/motion.css +46 -0
  215. package/dist/styles/tokens.css +31 -0
  216. package/dist/transitions/glide.d.ts +31 -0
  217. package/dist/transitions/glide.js +58 -0
  218. package/dist/types/booking.js +4 -4
  219. package/dist/types/comments.js +2 -2
  220. package/dist/types/messaging.js +3 -3
  221. package/dist/types/pipeline.js +2 -2
  222. package/dist/utils/currency.js +4 -2
  223. package/dist/utils/daisyuiColors.js +27 -25
  224. package/dist/utils/dateFormatters.d.ts +20 -0
  225. package/dist/utils/dateFormatters.js +65 -44
  226. package/dist/utils/googleMapsLoader.js +100 -46
  227. package/dist/utils/mentionParser.js +30 -30
  228. package/dist/utils/phone.js +27 -21
  229. package/dist/utils/phoneFormat.js +9 -9
  230. package/dist/utils/sound.d.ts +6 -0
  231. package/dist/utils/sound.js +113 -0
  232. package/dist/utils/splitText.d.ts +25 -0
  233. package/dist/utils/splitText.js +94 -0
  234. package/dist/utils/taskUtils.js +1 -1
  235. package/dist/utils/themeWipe.d.ts +21 -0
  236. package/dist/utils/themeWipe.js +94 -0
  237. package/dist/utils/viewTransition.d.ts +23 -0
  238. package/dist/utils/viewTransition.js +75 -0
  239. package/package.json +56 -4
@@ -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;
@@ -59,7 +59,25 @@
59
59
  stage.droppableIn !== false
60
60
  );
61
61
 
62
- const flipDuration = $derived(drag?.flipDurationMs ?? 200);
62
+ const flipDuration = $derived(drag?.flipDurationMs ?? 300); // var(--motion-move) = 300ms
63
+
64
+ // Track which item was just dropped to play a spring settle pulse
65
+ let settledItemId = $state<string | number | null>(null);
66
+ let settleTimer: ReturnType<typeof setTimeout> | null = null;
67
+
68
+ function handleFinalize(e: CustomEvent) {
69
+ onDndFinalize?.(e);
70
+ // Find the dropped item (first item if array changed, or use event detail)
71
+ const droppedItem = e.detail?.items?.[0];
72
+ if (droppedItem?.id !== undefined) {
73
+ if (settleTimer) clearTimeout(settleTimer);
74
+ settledItemId = droppedItem.id;
75
+ // Spring settle lasts ~400ms (single overshoot)
76
+ settleTimer = setTimeout(() => {
77
+ settledItemId = null;
78
+ }, 400);
79
+ }
80
+ }
63
81
  </script>
64
82
 
65
83
  <div
@@ -119,11 +137,14 @@
119
137
  dropTargetClasses: drag?.dropTargetClass ? [drag.dropTargetClass] : []
120
138
  }}
121
139
  onconsider={(e) => onDndConsider?.(e)}
122
- onfinalize={(e) => onDndFinalize?.(e)}
140
+ onfinalize={handleFinalize}
123
141
  class="flex flex-col gap-2 min-h-8 flex-1"
124
142
  >
125
143
  {#each items as item (item.id)}
126
- <div animate:flip={{ duration: flipDuration }}>
144
+ <div
145
+ animate:flip={{ duration: flipDuration, easing: (t) => t }}
146
+ class={settledItemId === item.id ? 'pipeline-card-settle' : ''}
147
+ >
127
148
  {#if card}
128
149
  {@render card({ item, stage })}
129
150
  {:else}
@@ -161,3 +182,23 @@
161
182
  </div>
162
183
  {/if}
163
184
  </div>
185
+
186
+ <style>
187
+ /* Spring settle pulse — single overshoot scale on drop (gesture-release, sanctioned). */
188
+ .pipeline-card-settle > :global(*) {
189
+ animation: pipeline-settle 400ms var(--motion-spring) forwards;
190
+ transform-origin: center;
191
+ }
192
+
193
+ @keyframes pipeline-settle {
194
+ 0% { transform: scale(1); }
195
+ 40% { transform: scale(1.02); }
196
+ 100% { transform: scale(1); }
197
+ }
198
+
199
+ @media (prefers-reduced-motion: reduce) {
200
+ .pipeline-card-settle > :global(*) {
201
+ animation: none;
202
+ }
203
+ }
204
+ </style>
@@ -0,0 +1,189 @@
1
+ <script lang="ts">
2
+ interface Segment {
3
+ value: number;
4
+ color?: string;
5
+ }
6
+
7
+ interface Props {
8
+ value?: number;
9
+ indeterminate?: boolean;
10
+ segments?: Segment[];
11
+ color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
12
+ size?: 'xs' | 'sm' | 'md' | 'lg';
13
+ label?: string;
14
+ showLabel?: boolean;
15
+ class?: string;
16
+ }
17
+
18
+ let {
19
+ value = 0,
20
+ indeterminate = false,
21
+ segments,
22
+ color = 'primary',
23
+ size = 'md',
24
+ label,
25
+ showLabel = false,
26
+ class: className = ''
27
+ }: Props = $props();
28
+
29
+ const clampedValue = $derived(Math.min(100, Math.max(0, value)));
30
+
31
+ const heightMap: Record<NonNullable<Props['size']>, string> = {
32
+ xs: '4px',
33
+ sm: '6px',
34
+ md: '8px',
35
+ lg: '12px'
36
+ };
37
+
38
+ const colorMap: Record<NonNullable<Props['color']>, string> = {
39
+ primary: 'oklch(var(--p))',
40
+ secondary: 'oklch(var(--s))',
41
+ accent: 'oklch(var(--a))',
42
+ success: 'oklch(var(--su))',
43
+ warning: 'oklch(var(--wa))',
44
+ error: 'oklch(var(--er))',
45
+ info: 'oklch(var(--in))'
46
+ };
47
+
48
+ const trackHeight = $derived(heightMap[size]);
49
+ const fillColor = $derived(colorMap[color]);
50
+ </script>
51
+
52
+ <div class="progress-wrap {className}" aria-label={label ?? 'Progress'}>
53
+ {#if label && showLabel}
54
+ <div class="progress-label">
55
+ <span>{label}</span>
56
+ {#if !indeterminate}
57
+ <span class="progress-pct">{clampedValue}%</span>
58
+ {/if}
59
+ </div>
60
+ {/if}
61
+
62
+ <div
63
+ class="progress-track"
64
+ style="height:{trackHeight}"
65
+ role="progressbar"
66
+ aria-valuenow={indeterminate ? undefined : clampedValue}
67
+ aria-valuemin={0}
68
+ aria-valuemax={100}
69
+ aria-label={label ?? 'Progress'}
70
+ >
71
+ {#if indeterminate}
72
+ <div class="progress-indeterminate" style="background:{fillColor}"></div>
73
+ {:else if segments && segments.length > 0}
74
+ <div class="progress-segments">
75
+ {#each segments as seg, i}
76
+ <div
77
+ class="progress-segment"
78
+ class:progress-segment--filled={i < value}
79
+ style="--seg-color:{seg.color ? seg.color : fillColor}"
80
+ ></div>
81
+ {/each}
82
+ </div>
83
+ {:else}
84
+ <div
85
+ class="progress-fill"
86
+ style="width:{clampedValue}%;background:{fillColor}"
87
+ ></div>
88
+ {/if}
89
+ </div>
90
+ </div>
91
+
92
+ <style>
93
+ .progress-wrap {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 0.375rem;
97
+ width: 100%;
98
+ }
99
+
100
+ .progress-label {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ font-size: 0.8125rem;
105
+ color: oklch(var(--bc) / 0.7);
106
+ }
107
+
108
+ .progress-pct {
109
+ font-variant-numeric: tabular-nums;
110
+ font-size: 0.75rem;
111
+ }
112
+
113
+ .progress-track {
114
+ position: relative;
115
+ width: 100%;
116
+ border-radius: 9999px;
117
+ background: oklch(var(--b3) / 1);
118
+ overflow: hidden;
119
+ }
120
+
121
+ /* Determinate fill — gentle glide spring on width change */
122
+ .progress-fill {
123
+ height: 100%;
124
+ border-radius: 9999px;
125
+ transition: width 0.9s var(--motion-glide);
126
+ }
127
+
128
+ /* Indeterminate sweep */
129
+ .progress-indeterminate {
130
+ position: absolute;
131
+ inset: 0;
132
+ width: 40%;
133
+ border-radius: 9999px;
134
+ animation: progress-slide 1.4s var(--motion-glide) infinite;
135
+ }
136
+
137
+ @keyframes progress-slide {
138
+ 0% {
139
+ transform: translateX(-100%);
140
+ }
141
+ 100% {
142
+ transform: translateX(320%);
143
+ }
144
+ }
145
+
146
+ /* Segment loader */
147
+ .progress-segments {
148
+ display: flex;
149
+ height: 100%;
150
+ gap: 3px;
151
+ padding: 0 2px;
152
+ }
153
+
154
+ .progress-segment {
155
+ flex: 1;
156
+ height: 100%;
157
+ border-radius: 9999px;
158
+ background: oklch(var(--b1) / 1);
159
+ overflow: hidden;
160
+ position: relative;
161
+ }
162
+
163
+ .progress-segment::after {
164
+ content: '';
165
+ display: block;
166
+ height: 100%;
167
+ background: var(--seg-color);
168
+ transform: scaleX(0);
169
+ transform-origin: left;
170
+ transition: transform 0.35s var(--motion-glide);
171
+ }
172
+
173
+ .progress-segment--filled::after {
174
+ transform: scaleX(1);
175
+ }
176
+
177
+ @media (prefers-reduced-motion: reduce) {
178
+ .progress-fill {
179
+ transition: none;
180
+ }
181
+ .progress-indeterminate {
182
+ animation: none;
183
+ transform: translateX(140%);
184
+ }
185
+ .progress-segment::after {
186
+ transition: none;
187
+ }
188
+ }
189
+ </style>
@@ -0,0 +1,17 @@
1
+ interface Segment {
2
+ value: number;
3
+ color?: string;
4
+ }
5
+ interface Props {
6
+ value?: number;
7
+ indeterminate?: boolean;
8
+ segments?: Segment[];
9
+ color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
10
+ size?: 'xs' | 'sm' | 'md' | 'lg';
11
+ label?: string;
12
+ showLabel?: boolean;
13
+ class?: string;
14
+ }
15
+ declare const ProgressBar: import("svelte").Component<Props, {}, "">;
16
+ type ProgressBar = ReturnType<typeof ProgressBar>;
17
+ export default ProgressBar;
@@ -0,0 +1,138 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ value?: number;
4
+ size?: number;
5
+ strokeWidth?: number;
6
+ color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
7
+ label?: string | null;
8
+ showValue?: boolean;
9
+ class?: string;
10
+ }
11
+
12
+ let {
13
+ value = 0,
14
+ size = 80,
15
+ strokeWidth = 6,
16
+ color = 'primary',
17
+ label = null,
18
+ showValue = true,
19
+ class: className = ''
20
+ }: Props = $props();
21
+
22
+ const clampedValue = $derived(Math.min(100, Math.max(0, value)));
23
+
24
+ const r = $derived((size - strokeWidth) / 2);
25
+ const circumference = $derived(2 * Math.PI * r);
26
+ const offset = $derived(circumference * (1 - clampedValue / 100));
27
+ const cx = $derived(size / 2);
28
+ const cy = $derived(size / 2);
29
+
30
+ const colorMap: Record<NonNullable<Props['color']>, string> = {
31
+ primary: 'oklch(var(--p))',
32
+ secondary: 'oklch(var(--s))',
33
+ accent: 'oklch(var(--a))',
34
+ success: 'oklch(var(--su))',
35
+ warning: 'oklch(var(--wa))',
36
+ error: 'oklch(var(--er))',
37
+ info: 'oklch(var(--in))'
38
+ };
39
+
40
+ const strokeColor = $derived(colorMap[color]);
41
+ const fontSize = $derived(Math.round(size * 0.2));
42
+ </script>
43
+
44
+ <div
45
+ class="progress-ring {className}"
46
+ style="width:{size}px;height:{size}px"
47
+ role="progressbar"
48
+ aria-valuenow={clampedValue}
49
+ aria-valuemin={0}
50
+ aria-valuemax={100}
51
+ aria-label={label ?? 'Progress'}
52
+ >
53
+ <!-- Rotate -90deg so fill starts from 12 o'clock -->
54
+ <svg viewBox="0 0 {size} {size}" style="transform:rotate(-90deg)">
55
+ <!-- Track -->
56
+ <circle
57
+ {cx}
58
+ {cy}
59
+ r={r}
60
+ fill="none"
61
+ stroke="oklch(var(--b3) / 1)"
62
+ stroke-width={strokeWidth}
63
+ />
64
+ <!-- Progress arc -->
65
+ <circle
66
+ {cx}
67
+ {cy}
68
+ r={r}
69
+ fill="none"
70
+ stroke={strokeColor}
71
+ stroke-width={strokeWidth}
72
+ stroke-linecap="round"
73
+ stroke-dasharray={circumference}
74
+ stroke-dashoffset={offset}
75
+ class="ring-prog"
76
+ />
77
+ </svg>
78
+
79
+ {#if showValue || label}
80
+ <div class="ring-label" style="font-size:{fontSize}px">
81
+ {#if showValue}
82
+ <span class="ring-value">{clampedValue}%</span>
83
+ {/if}
84
+ {#if label}
85
+ <span class="ring-sublabel">{label}</span>
86
+ {/if}
87
+ </div>
88
+ {/if}
89
+ </div>
90
+
91
+ <style>
92
+ .progress-ring {
93
+ position: relative;
94
+ display: inline-flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ flex-shrink: 0;
98
+ }
99
+
100
+ .progress-ring svg {
101
+ position: absolute;
102
+ inset: 0;
103
+ width: 100%;
104
+ height: 100%;
105
+ }
106
+
107
+ .ring-prog {
108
+ transition: stroke-dashoffset 0.9s var(--motion-glide);
109
+ }
110
+
111
+ .ring-label {
112
+ display: flex;
113
+ flex-direction: column;
114
+ align-items: center;
115
+ justify-content: center;
116
+ line-height: 1.1;
117
+ color: oklch(var(--bc) / 1);
118
+ pointer-events: none;
119
+ z-index: 1;
120
+ }
121
+
122
+ .ring-value {
123
+ font-variant-numeric: tabular-nums;
124
+ font-weight: 600;
125
+ }
126
+
127
+ .ring-sublabel {
128
+ font-size: 0.625em;
129
+ color: oklch(var(--bc) / 0.6);
130
+ margin-top: 0.125em;
131
+ }
132
+
133
+ @media (prefers-reduced-motion: reduce) {
134
+ .ring-prog {
135
+ transition: none;
136
+ }
137
+ }
138
+ </style>
@@ -0,0 +1,12 @@
1
+ interface Props {
2
+ value?: number;
3
+ size?: number;
4
+ strokeWidth?: number;
5
+ color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
6
+ label?: string | null;
7
+ showValue?: boolean;
8
+ class?: string;
9
+ }
10
+ declare const ProgressRing: import("svelte").Component<Props, {}, "">;
11
+ type ProgressRing = ReturnType<typeof ProgressRing>;
12
+ export default ProgressRing;