@inferencesh/ui 0.0.0-stage → 0.1.2

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 (165) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +68 -2
  3. package/components.json +22 -0
  4. package/package.json +231 -4
  5. package/src/components/audio-player.tsx +549 -0
  6. package/src/components/button.tsx +13 -0
  7. package/src/components/chart.tsx +403 -0
  8. package/src/components/circuit.tsx +276 -0
  9. package/src/components/code-block/code-block.tsx +260 -0
  10. package/src/components/code-block/index.ts +18 -0
  11. package/src/components/code-block/languages.ts +311 -0
  12. package/src/components/code-block/styles.ts +39 -0
  13. package/src/components/code-block/tokenizer.ts +290 -0
  14. package/src/components/code-block/types.ts +38 -0
  15. package/src/components/code-block/utils.ts +39 -0
  16. package/src/components/code.tsx +48 -0
  17. package/src/components/cookie-consent.tsx +137 -0
  18. package/src/components/copy-button.tsx +44 -0
  19. package/src/components/country-dropdown.tsx +188 -0
  20. package/src/components/glow-loader-fast.tsx +32 -0
  21. package/src/components/glow-loader-mono.tsx +33 -0
  22. package/src/components/glow-loader-sm-fast.tsx +32 -0
  23. package/src/components/glow-loader-sm-mono.tsx +34 -0
  24. package/src/components/glow-loader-sm.tsx +32 -0
  25. package/src/components/glow-loader.tsx +32 -0
  26. package/src/components/glow-logo.tsx +32 -0
  27. package/src/components/gradient.tsx +80 -0
  28. package/src/components/index.ts +45 -0
  29. package/src/components/inline-sidebar.tsx +351 -0
  30. package/src/components/input-color.tsx +349 -0
  31. package/src/components/logo-base.tsx +213 -0
  32. package/src/components/logo-color.tsx +23 -0
  33. package/src/components/logo-full.tsx +71 -0
  34. package/src/components/logo-svg.tsx +23 -0
  35. package/src/components/menu-navigation.tsx +359 -0
  36. package/src/components/multi-select.tsx +721 -0
  37. package/src/components/pastel-gradient.tsx +25 -0
  38. package/src/components/pixel-symmetry.tsx +151 -0
  39. package/src/components/progress.tsx +28 -0
  40. package/src/components/scroll-more.tsx +131 -0
  41. package/src/components/select.tsx +85 -0
  42. package/src/components/shimmering-text.tsx +108 -0
  43. package/src/components/sidebar-light.tsx +102 -0
  44. package/src/components/sonner.tsx +38 -0
  45. package/src/components/theme-switcher-sidebar.tsx +56 -0
  46. package/src/components/theme-switcher-simple.tsx +110 -0
  47. package/src/components/theme-switcher.tsx +47 -0
  48. package/src/components/toggle-group.tsx +55 -0
  49. package/src/components/tooltip-button.tsx +91 -0
  50. package/src/components/tooltip.tsx +71 -0
  51. package/src/components/tree-view.tsx +499 -0
  52. package/src/components/unglow-loader.tsx +33 -0
  53. package/src/components/waveform.tsx +1632 -0
  54. package/src/components/zoomable-image.tsx +76 -0
  55. package/src/docs/api-docs.tsx +763 -0
  56. package/src/docs/callout.tsx +160 -0
  57. package/src/docs/code-group.tsx +215 -0
  58. package/src/docs/copy-menu.tsx +143 -0
  59. package/src/docs/doc-tabs.tsx +84 -0
  60. package/src/docs/index.ts +16 -0
  61. package/src/docs/steps.tsx +105 -0
  62. package/src/docs/table-of-contents.tsx +174 -0
  63. package/src/examples/guide-markdown.ts +129 -0
  64. package/src/examples/index.ts +1153 -0
  65. package/src/global.d.ts +1 -0
  66. package/src/hooks/use-copy-to-clipboard.ts +36 -0
  67. package/src/hooks/use-mobile.ts +19 -0
  68. package/src/lib/display-name.ts +15 -0
  69. package/src/lib/example-input.ts +130 -0
  70. package/src/lib/plans.ts +44 -0
  71. package/src/lib/safe-content.ts +137 -0
  72. package/src/lib/schema.ts +320 -0
  73. package/src/lib/storage.ts +108 -0
  74. package/src/markdown/docs-markdown-renderer.tsx +518 -0
  75. package/src/markdown/helpers.ts +95 -0
  76. package/src/markdown/index.ts +24 -0
  77. package/src/markdown/markdown-renderer.tsx +232 -0
  78. package/src/markdown/youtube-embed.tsx +43 -0
  79. package/src/og/image.ts +312 -0
  80. package/src/og/index.tsx +430 -0
  81. package/src/og/query.ts +26 -0
  82. package/src/shadcn/accordion.tsx +71 -0
  83. package/src/shadcn/alert-dialog.tsx +187 -0
  84. package/src/shadcn/alert.tsx +75 -0
  85. package/src/shadcn/aspect-ratio.tsx +22 -0
  86. package/src/shadcn/attachment.tsx +207 -0
  87. package/src/shadcn/avatar.tsx +106 -0
  88. package/src/shadcn/badge.tsx +51 -0
  89. package/src/shadcn/breadcrumb.tsx +124 -0
  90. package/src/shadcn/bubble.tsx +127 -0
  91. package/src/shadcn/button-group.tsx +87 -0
  92. package/src/shadcn/button.tsx +57 -0
  93. package/src/shadcn/calendar.tsx +219 -0
  94. package/src/shadcn/card.tsx +102 -0
  95. package/src/shadcn/carousel.tsx +242 -0
  96. package/src/shadcn/chart.tsx +372 -0
  97. package/src/shadcn/checkbox.tsx +28 -0
  98. package/src/shadcn/collapsible.tsx +19 -0
  99. package/src/shadcn/combobox.tsx +297 -0
  100. package/src/shadcn/command.tsx +194 -0
  101. package/src/shadcn/context-menu.tsx +270 -0
  102. package/src/shadcn/dialog.tsx +158 -0
  103. package/src/shadcn/direction.tsx +4 -0
  104. package/src/shadcn/drawer.tsx +227 -0
  105. package/src/shadcn/dropdown-menu.tsx +265 -0
  106. package/src/shadcn/empty.tsx +103 -0
  107. package/src/shadcn/field.tsx +238 -0
  108. package/src/shadcn/form.tsx +173 -0
  109. package/src/shadcn/hover-card.tsx +50 -0
  110. package/src/shadcn/index.ts +53 -0
  111. package/src/shadcn/input-group.tsx +156 -0
  112. package/src/shadcn/input-otp.tsx +84 -0
  113. package/src/shadcn/input.tsx +19 -0
  114. package/src/shadcn/item.tsx +201 -0
  115. package/src/shadcn/kbd.tsx +26 -0
  116. package/src/shadcn/label.tsx +19 -0
  117. package/src/shadcn/marker.tsx +70 -0
  118. package/src/shadcn/menubar.tsx +278 -0
  119. package/src/shadcn/message-scroller.tsx +131 -0
  120. package/src/shadcn/message.tsx +91 -0
  121. package/src/shadcn/native-select.tsx +60 -0
  122. package/src/shadcn/navigation-menu.tsx +167 -0
  123. package/src/shadcn/pagination.tsx +130 -0
  124. package/src/shadcn/popover.tsx +87 -0
  125. package/src/shadcn/progress.tsx +82 -0
  126. package/src/shadcn/radio-group.tsx +35 -0
  127. package/src/shadcn/resizable.tsx +49 -0
  128. package/src/shadcn/scroll-area.tsx +52 -0
  129. package/src/shadcn/select.tsx +200 -0
  130. package/src/shadcn/separator.tsx +22 -0
  131. package/src/shadcn/sheet.tsx +136 -0
  132. package/src/shadcn/sidebar.tsx +721 -0
  133. package/src/shadcn/skeleton.tsx +13 -0
  134. package/src/shadcn/slider.tsx +51 -0
  135. package/src/shadcn/sonner.tsx +49 -0
  136. package/src/shadcn/spinner.tsx +10 -0
  137. package/src/shadcn/switch.tsx +29 -0
  138. package/src/shadcn/table.tsx +115 -0
  139. package/src/shadcn/tabs.tsx +79 -0
  140. package/src/shadcn/textarea.tsx +17 -0
  141. package/src/shadcn/toggle-group.tsx +89 -0
  142. package/src/shadcn/toggle.tsx +44 -0
  143. package/src/shadcn/tooltip.tsx +63 -0
  144. package/src/theme/config.ts +48 -0
  145. package/src/theme/css/base.css +92 -0
  146. package/src/theme/css/bitboy.css +75 -0
  147. package/src/theme/css/dark.css +47 -0
  148. package/src/theme/css/designer.css +122 -0
  149. package/src/theme/css/fonts.css +33 -0
  150. package/src/theme/css/globals.css +14 -0
  151. package/src/theme/css/hackintosh.css +57 -0
  152. package/src/theme/css/llama.css +103 -0
  153. package/src/theme/css/safelist.css +39 -0
  154. package/src/theme/css/shadcn.css +77 -0
  155. package/src/theme/css/theme.css +216 -0
  156. package/src/theme/embed-theme.ts +146 -0
  157. package/src/theme/fonts/FiraCode-VF.woff2 +0 -0
  158. package/src/theme/fonts/Hack-Regular.woff2 +0 -0
  159. package/src/theme/fonts/LICENSE-FiraCode.txt +93 -0
  160. package/src/theme/fonts/LICENSE-Hack.txt +45 -0
  161. package/src/theme/fonts/LICENSE-MonaSans.txt +93 -0
  162. package/src/theme/fonts/Mona-Sans.woff2 +0 -0
  163. package/src/theme/index.ts +15 -0
  164. package/src/utils/cn.ts +3 -0
  165. package/src/utils/index.ts +2 -0
@@ -0,0 +1,549 @@
1
+ "use client"
2
+
3
+ import { Slider as SliderPrimitive } from "@base-ui/react/slider"
4
+ import { PauseIcon, PlayIcon } from "lucide-react"
5
+ import {
6
+ ComponentProps,
7
+ createContext,
8
+ HTMLProps,
9
+ ReactNode,
10
+ RefObject,
11
+ useCallback,
12
+ useContext,
13
+ useEffect,
14
+ useMemo,
15
+ useRef,
16
+ useState
17
+ } from "react"
18
+
19
+ import { Button } from "../shadcn/button"
20
+ import { cn } from "cn"
21
+
22
+ enum ReadyState {
23
+ HAVE_NOTHING = 0,
24
+ HAVE_METADATA = 1,
25
+ HAVE_CURRENT_DATA = 2,
26
+ HAVE_FUTURE_DATA = 3,
27
+ HAVE_ENOUGH_DATA = 4,
28
+ }
29
+
30
+ enum NetworkState {
31
+ NETWORK_EMPTY = 0,
32
+ NETWORK_IDLE = 1,
33
+ NETWORK_LOADING = 2,
34
+ NETWORK_NO_SOURCE = 3,
35
+ }
36
+
37
+ function formatTime(seconds: number) {
38
+ const hrs = Math.floor(seconds / 3600)
39
+ const mins = Math.floor((seconds % 3600) / 60)
40
+ const secs = Math.floor(seconds % 60)
41
+
42
+ const formattedMins = mins < 10 ? `0${mins}` : mins
43
+ const formattedSecs = secs < 10 ? `0${secs}` : secs
44
+
45
+ return hrs > 0
46
+ ? `${hrs}:${formattedMins}:${formattedSecs}`
47
+ : `${mins}:${formattedSecs}`
48
+ }
49
+
50
+ interface AudioPlayerItem<TData = unknown> {
51
+ id: string | number
52
+ src: string
53
+ data?: TData
54
+ }
55
+
56
+ interface AudioPlayerApi<TData = unknown> {
57
+ ref: RefObject<HTMLAudioElement | null>
58
+ activeItem: AudioPlayerItem<TData> | null
59
+ duration: number | undefined
60
+ error: MediaError | null
61
+ isPlaying: boolean
62
+ isBuffering: boolean
63
+ isItemActive: (id: string | number | null) => boolean
64
+ setActiveItem: (item: AudioPlayerItem<TData> | null) => Promise<void>
65
+ play: (item?: AudioPlayerItem<TData> | null) => Promise<void>
66
+ pause: () => void
67
+ seek: (time: number) => void
68
+ }
69
+
70
+ const AudioPlayerContext = createContext<AudioPlayerApi<unknown> | null>(null)
71
+
72
+ export function useAudioPlayer<TData = unknown>(): AudioPlayerApi<TData> {
73
+ const api = useContext(AudioPlayerContext) as AudioPlayerApi<TData> | null
74
+ if (!api) {
75
+ throw new Error(
76
+ "useAudioPlayer cannot be called outside of AudioPlayerProvider"
77
+ )
78
+ }
79
+ return api
80
+ }
81
+
82
+ const AudioPlayerTimeContext = createContext<number | null>(null)
83
+
84
+ export const useAudioPlayerTime = () => {
85
+ const time = useContext(AudioPlayerTimeContext)
86
+ if (time === null) {
87
+ throw new Error(
88
+ "useAudioPlayerTime cannot be called outside of AudioPlayerProvider"
89
+ )
90
+ }
91
+ return time
92
+ }
93
+
94
+ export function AudioPlayerProvider<TData = unknown>({
95
+ children,
96
+ }: {
97
+ children: ReactNode
98
+ }) {
99
+ const audioRef = useRef<HTMLAudioElement>(null)
100
+ const itemRef = useRef<AudioPlayerItem<TData> | null>(null)
101
+ const playPromiseRef = useRef<Promise<void> | null>(null)
102
+ const [readyState, setReadyState] = useState<number>(0)
103
+ const [networkState, setNetworkState] = useState<number>(0)
104
+ const [time, setTime] = useState<number>(0)
105
+ const [duration, setDuration] = useState<number | undefined>(undefined)
106
+ const [error, setError] = useState<MediaError | null>(null)
107
+ const [activeItem, _setActiveItem] = useState<AudioPlayerItem<TData> | null>(
108
+ null
109
+ )
110
+ const [paused, setPaused] = useState(true)
111
+
112
+ const setActiveItem = useCallback(
113
+ async (item: AudioPlayerItem<TData> | null) => {
114
+ if (!audioRef.current) return
115
+
116
+ if (item?.id === itemRef.current?.id) {
117
+ return
118
+ }
119
+ itemRef.current = item
120
+ audioRef.current.pause()
121
+ audioRef.current.currentTime = 0
122
+ if (item === null) {
123
+ audioRef.current.removeAttribute("src")
124
+ } else {
125
+ audioRef.current.src = item.src
126
+ }
127
+ audioRef.current.load()
128
+ },
129
+ []
130
+ )
131
+
132
+ const play = useCallback(
133
+ async (item?: AudioPlayerItem<TData> | null) => {
134
+ if (!audioRef.current) return
135
+
136
+ if (playPromiseRef.current) {
137
+ try {
138
+ await playPromiseRef.current
139
+ } catch (error) {
140
+ console.error("Play promise error:", error)
141
+ }
142
+ }
143
+
144
+ if (item === undefined) {
145
+ const playPromise = audioRef.current.play()
146
+ playPromiseRef.current = playPromise
147
+ return playPromise
148
+ }
149
+ if (item?.id === activeItem?.id) {
150
+ const playPromise = audioRef.current.play()
151
+ playPromiseRef.current = playPromise
152
+ return playPromise
153
+ }
154
+
155
+ itemRef.current = item
156
+ if (!audioRef.current.paused) {
157
+ audioRef.current.pause()
158
+ }
159
+ audioRef.current.currentTime = 0
160
+ if (item === null) {
161
+ audioRef.current.removeAttribute("src")
162
+ } else {
163
+ audioRef.current.src = item.src
164
+ }
165
+ audioRef.current.load()
166
+ const playPromise = audioRef.current.play()
167
+ playPromiseRef.current = playPromise
168
+ return playPromise
169
+ },
170
+ [activeItem]
171
+ )
172
+
173
+ const pause = useCallback(async () => {
174
+ if (!audioRef.current) return
175
+
176
+ if (playPromiseRef.current) {
177
+ try {
178
+ await playPromiseRef.current
179
+ } catch (e) {
180
+ console.error(e)
181
+ }
182
+ }
183
+
184
+ audioRef.current?.pause()
185
+ playPromiseRef.current = null
186
+ }, [])
187
+
188
+ const seek = useCallback((time: number) => {
189
+ if (!audioRef.current) return
190
+ audioRef.current.currentTime = time
191
+ }, [])
192
+
193
+ const isItemActive = useCallback(
194
+ (id: string | number | null) => {
195
+ return activeItem?.id === id
196
+ },
197
+ [activeItem]
198
+ )
199
+
200
+ useAnimationFrame(() => {
201
+ if (audioRef.current) {
202
+ _setActiveItem(itemRef.current)
203
+ setReadyState(audioRef.current.readyState)
204
+ setNetworkState(audioRef.current.networkState)
205
+ setTime(audioRef.current.currentTime)
206
+ setDuration(audioRef.current.duration)
207
+ setPaused(audioRef.current.paused)
208
+ setError(audioRef.current.error)
209
+ }
210
+ })
211
+
212
+ const isPlaying = !paused
213
+ const isBuffering =
214
+ readyState < ReadyState.HAVE_FUTURE_DATA &&
215
+ networkState === NetworkState.NETWORK_LOADING
216
+
217
+ const api = useMemo<AudioPlayerApi<TData>>(
218
+ () => ({
219
+ ref: audioRef,
220
+ duration,
221
+ error,
222
+ isPlaying,
223
+ isBuffering,
224
+ activeItem,
225
+ isItemActive,
226
+ setActiveItem,
227
+ play,
228
+ pause,
229
+ seek,
230
+ }),
231
+ [
232
+ audioRef,
233
+ duration,
234
+ error,
235
+ isPlaying,
236
+ isBuffering,
237
+ activeItem,
238
+ isItemActive,
239
+ setActiveItem,
240
+ play,
241
+ pause,
242
+ seek,
243
+ ]
244
+ )
245
+
246
+ return (
247
+ <AudioPlayerContext.Provider value={api as AudioPlayerApi<unknown>}>
248
+ <AudioPlayerTimeContext.Provider value={time}>
249
+ <audio ref={audioRef} className="hidden" crossOrigin="anonymous" />
250
+ {children}
251
+ </AudioPlayerTimeContext.Provider>
252
+ </AudioPlayerContext.Provider>
253
+ )
254
+ }
255
+
256
+ export const AudioPlayerProgress = ({
257
+ ...otherProps
258
+ }: Omit<
259
+ ComponentProps<typeof SliderPrimitive.Root>,
260
+ "min" | "max" | "value"
261
+ >) => {
262
+ const player = useAudioPlayer()
263
+ const time = useAudioPlayerTime()
264
+ const wasPlayingRef = useRef(false)
265
+
266
+ return (
267
+ <SliderPrimitive.Root
268
+ {...otherProps}
269
+ value={[time]}
270
+ onValueChange={(vals, details) => {
271
+ const val = Array.isArray(vals) ? (vals as number[])[0] : (vals as number)
272
+ if (val !== undefined) player.seek(val)
273
+ otherProps.onValueChange?.(vals, details)
274
+ }}
275
+ min={0}
276
+ max={player.duration ?? 0}
277
+ step={otherProps.step || 0.25}
278
+ className={cn(
279
+ "group/player relative flex h-4 touch-none items-center select-none data-disabled:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
280
+ otherProps.className
281
+ )}
282
+ disabled={
283
+ player.duration === undefined ||
284
+ !Number.isFinite(player.duration) ||
285
+ Number.isNaN(player.duration)
286
+ }
287
+ >
288
+ <SliderPrimitive.Control
289
+ onPointerDown={(e) => {
290
+ wasPlayingRef.current = player.isPlaying
291
+ player.pause()
292
+ }}
293
+ onPointerUp={() => {
294
+ if (wasPlayingRef.current) {
295
+ player.play()
296
+ }
297
+ }}
298
+ onKeyDown={(e) => {
299
+ if (e.key === " ") {
300
+ e.preventDefault()
301
+ if (!player.isPlaying) {
302
+ player.play()
303
+ } else {
304
+ player.pause()
305
+ }
306
+ }
307
+ }}
308
+ className="relative flex h-4 w-full touch-none items-center select-none"
309
+ >
310
+ <SliderPrimitive.Track className="bg-muted relative h-[4px] w-full grow overflow-hidden rounded-full">
311
+ <SliderPrimitive.Indicator className="bg-primary absolute h-full" />
312
+ </SliderPrimitive.Track>
313
+ <SliderPrimitive.Thumb
314
+ className="relative flex h-0 w-0 items-center justify-center opacity-0 group-hover/player:opacity-100 focus-visible:opacity-100 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
315
+ data-slot="slider-thumb"
316
+ >
317
+ <div className="bg-foreground absolute size-3 rounded-full" />
318
+ </SliderPrimitive.Thumb>
319
+ </SliderPrimitive.Control>
320
+ </SliderPrimitive.Root>
321
+ )
322
+ }
323
+
324
+ export const AudioPlayerTime = ({
325
+ className,
326
+ ...otherProps
327
+ }: HTMLProps<HTMLSpanElement>) => {
328
+ const time = useAudioPlayerTime()
329
+ return (
330
+ <span
331
+ {...otherProps}
332
+ className={cn("text-muted-foreground text-sm tabular-nums", className)}
333
+ >
334
+ {formatTime(time)}
335
+ </span>
336
+ )
337
+ }
338
+
339
+ export const AudioPlayerDuration = ({
340
+ className,
341
+ ...otherProps
342
+ }: HTMLProps<HTMLSpanElement>) => {
343
+ const player = useAudioPlayer()
344
+ return (
345
+ <span
346
+ {...otherProps}
347
+ className={cn("text-muted-foreground text-sm tabular-nums", className)}
348
+ >
349
+ {player.duration !== null &&
350
+ player.duration !== undefined &&
351
+ !Number.isNaN(player.duration)
352
+ ? formatTime(player.duration)
353
+ : "--:--"}
354
+ </span>
355
+ )
356
+ }
357
+
358
+ interface SpinnerProps {
359
+ className?: string
360
+ }
361
+
362
+ function Spinner({ className }: SpinnerProps) {
363
+ return (
364
+ <div
365
+ className={cn(
366
+ "border-muted border-t-foreground size-3.5 animate-spin rounded-full border-2",
367
+ className
368
+ )}
369
+ role="status"
370
+ aria-label="Loading"
371
+ >
372
+ <span className="sr-only">Loading...</span>
373
+ </div>
374
+ )
375
+ }
376
+
377
+ interface PlayButtonProps extends React.ComponentProps<typeof Button> {
378
+ playing: boolean
379
+ onPlayingChange: (playing: boolean) => void
380
+ loading?: boolean
381
+ }
382
+
383
+ const PlayButton = ({
384
+ playing,
385
+ onPlayingChange,
386
+ className,
387
+ onClick,
388
+ loading,
389
+ ...otherProps
390
+ }: PlayButtonProps) => {
391
+ return (
392
+ <Button
393
+ {...otherProps}
394
+ onClick={(e) => {
395
+ onPlayingChange(!playing)
396
+ onClick?.(e)
397
+ }}
398
+ className={cn("relative", className)}
399
+ aria-label={playing ? "Pause" : "Play"}
400
+ type="button"
401
+ >
402
+ {playing ? (
403
+ <PauseIcon
404
+ className={cn("size-4", loading && "opacity-0")}
405
+ aria-hidden="true"
406
+ />
407
+ ) : (
408
+ <PlayIcon
409
+ className={cn("size-4", loading && "opacity-0")}
410
+ aria-hidden="true"
411
+ />
412
+ )}
413
+ {loading && (
414
+ <div className="absolute inset-0 flex items-center justify-center rounded-[inherit] backdrop-blur-xs">
415
+ <Spinner />
416
+ </div>
417
+ )}
418
+ </Button>
419
+ )
420
+ }
421
+
422
+ export interface AudioPlayerButtonProps<TData = unknown>
423
+ extends React.ComponentProps<typeof Button> {
424
+ item?: AudioPlayerItem<TData>
425
+ }
426
+
427
+ export function AudioPlayerButton<TData = unknown>({
428
+ item,
429
+ ...otherProps
430
+ }: AudioPlayerButtonProps<TData>) {
431
+ const player = useAudioPlayer<TData>()
432
+
433
+ if (!item) {
434
+ return (
435
+ <PlayButton
436
+ {...otherProps}
437
+ playing={player.isPlaying}
438
+ onPlayingChange={(shouldPlay) => {
439
+ if (shouldPlay) {
440
+ player.play()
441
+ } else {
442
+ player.pause()
443
+ }
444
+ }}
445
+ loading={player.isBuffering && player.isPlaying}
446
+ />
447
+ )
448
+ }
449
+
450
+ return (
451
+ <PlayButton
452
+ {...otherProps}
453
+ playing={player.isItemActive(item.id) && player.isPlaying}
454
+ onPlayingChange={(shouldPlay) => {
455
+ if (shouldPlay) {
456
+ player.play(item)
457
+ } else {
458
+ player.pause()
459
+ }
460
+ }}
461
+ loading={
462
+ player.isItemActive(item.id) && player.isBuffering && player.isPlaying
463
+ }
464
+ />
465
+ )
466
+ }
467
+
468
+ type Callback = (delta: number) => void
469
+
470
+ function useAnimationFrame(callback: Callback) {
471
+ const requestRef = useRef<number | null>(null)
472
+ const previousTimeRef = useRef<number | null>(null)
473
+ const callbackRef = useRef<Callback>(callback)
474
+
475
+ useEffect(() => {
476
+ callbackRef.current = callback
477
+ }, [callback])
478
+
479
+ useEffect(() => {
480
+ const animate = (time: number) => {
481
+ if (previousTimeRef.current !== null) {
482
+ const delta = time - previousTimeRef.current
483
+ callbackRef.current(delta)
484
+ }
485
+ previousTimeRef.current = time
486
+ requestRef.current = requestAnimationFrame(animate)
487
+ }
488
+
489
+ requestRef.current = requestAnimationFrame(animate)
490
+
491
+ return () => {
492
+ if (requestRef.current) cancelAnimationFrame(requestRef.current)
493
+ previousTimeRef.current = null
494
+ }
495
+ }, [])
496
+ }
497
+
498
+ export const exampleTracks = [
499
+ {
500
+ id: "0",
501
+ name: "II - 00",
502
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/00.mp3",
503
+ },
504
+ {
505
+ id: "1",
506
+ name: "II - 01",
507
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/01.mp3",
508
+ },
509
+ {
510
+ id: "2",
511
+ name: "II - 02",
512
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/02.mp3",
513
+ },
514
+ {
515
+ id: "3",
516
+ name: "II - 03",
517
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/03.mp3",
518
+ },
519
+ {
520
+ id: "4",
521
+ name: "II - 04",
522
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/04.mp3",
523
+ },
524
+ {
525
+ id: "5",
526
+ name: "II - 05",
527
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/05.mp3",
528
+ },
529
+ {
530
+ id: "6",
531
+ name: "II - 06",
532
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/06.mp3",
533
+ },
534
+ {
535
+ id: "7",
536
+ name: "II - 07",
537
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/07.mp3",
538
+ },
539
+ {
540
+ id: "8",
541
+ name: "II - 08",
542
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/08.mp3",
543
+ },
544
+ {
545
+ id: "9",
546
+ name: "II - 09",
547
+ url: "https://storage.googleapis.com/eleven-public-cdn/audio/ui-elevenlabs-io/09.mp3",
548
+ },
549
+ ]
@@ -0,0 +1,13 @@
1
+ import * as React from "react"
2
+
3
+ import { Button as ButtonBase } from "../shadcn/button"
4
+
5
+ export { buttonVariants } from "../shadcn/button"
6
+
7
+ // The shadcn button, and a button rendered as something else (a Link, an
8
+ // anchor) is told so: Base UI otherwise treats the element as a native
9
+ // <button>, writes type="button" on it and skips the button role and key
10
+ // handling. ui/shadcn/button.tsx stays as the CLI writes it.
11
+ export function Button({ nativeButton, render, ...props }: React.ComponentProps<typeof ButtonBase>) {
12
+ return <ButtonBase nativeButton={nativeButton ?? (render ? false : undefined)} render={render} {...props} />
13
+ }