@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.
- package/LICENSE +22 -0
- package/README.md +68 -2
- package/components.json +22 -0
- package/package.json +231 -4
- package/src/components/audio-player.tsx +549 -0
- package/src/components/button.tsx +13 -0
- package/src/components/chart.tsx +403 -0
- package/src/components/circuit.tsx +276 -0
- package/src/components/code-block/code-block.tsx +260 -0
- package/src/components/code-block/index.ts +18 -0
- package/src/components/code-block/languages.ts +311 -0
- package/src/components/code-block/styles.ts +39 -0
- package/src/components/code-block/tokenizer.ts +290 -0
- package/src/components/code-block/types.ts +38 -0
- package/src/components/code-block/utils.ts +39 -0
- package/src/components/code.tsx +48 -0
- package/src/components/cookie-consent.tsx +137 -0
- package/src/components/copy-button.tsx +44 -0
- package/src/components/country-dropdown.tsx +188 -0
- package/src/components/glow-loader-fast.tsx +32 -0
- package/src/components/glow-loader-mono.tsx +33 -0
- package/src/components/glow-loader-sm-fast.tsx +32 -0
- package/src/components/glow-loader-sm-mono.tsx +34 -0
- package/src/components/glow-loader-sm.tsx +32 -0
- package/src/components/glow-loader.tsx +32 -0
- package/src/components/glow-logo.tsx +32 -0
- package/src/components/gradient.tsx +80 -0
- package/src/components/index.ts +45 -0
- package/src/components/inline-sidebar.tsx +351 -0
- package/src/components/input-color.tsx +349 -0
- package/src/components/logo-base.tsx +213 -0
- package/src/components/logo-color.tsx +23 -0
- package/src/components/logo-full.tsx +71 -0
- package/src/components/logo-svg.tsx +23 -0
- package/src/components/menu-navigation.tsx +359 -0
- package/src/components/multi-select.tsx +721 -0
- package/src/components/pastel-gradient.tsx +25 -0
- package/src/components/pixel-symmetry.tsx +151 -0
- package/src/components/progress.tsx +28 -0
- package/src/components/scroll-more.tsx +131 -0
- package/src/components/select.tsx +85 -0
- package/src/components/shimmering-text.tsx +108 -0
- package/src/components/sidebar-light.tsx +102 -0
- package/src/components/sonner.tsx +38 -0
- package/src/components/theme-switcher-sidebar.tsx +56 -0
- package/src/components/theme-switcher-simple.tsx +110 -0
- package/src/components/theme-switcher.tsx +47 -0
- package/src/components/toggle-group.tsx +55 -0
- package/src/components/tooltip-button.tsx +91 -0
- package/src/components/tooltip.tsx +71 -0
- package/src/components/tree-view.tsx +499 -0
- package/src/components/unglow-loader.tsx +33 -0
- package/src/components/waveform.tsx +1632 -0
- package/src/components/zoomable-image.tsx +76 -0
- package/src/docs/api-docs.tsx +763 -0
- package/src/docs/callout.tsx +160 -0
- package/src/docs/code-group.tsx +215 -0
- package/src/docs/copy-menu.tsx +143 -0
- package/src/docs/doc-tabs.tsx +84 -0
- package/src/docs/index.ts +16 -0
- package/src/docs/steps.tsx +105 -0
- package/src/docs/table-of-contents.tsx +174 -0
- package/src/examples/guide-markdown.ts +129 -0
- package/src/examples/index.ts +1153 -0
- package/src/global.d.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +36 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/display-name.ts +15 -0
- package/src/lib/example-input.ts +130 -0
- package/src/lib/plans.ts +44 -0
- package/src/lib/safe-content.ts +137 -0
- package/src/lib/schema.ts +320 -0
- package/src/lib/storage.ts +108 -0
- package/src/markdown/docs-markdown-renderer.tsx +518 -0
- package/src/markdown/helpers.ts +95 -0
- package/src/markdown/index.ts +24 -0
- package/src/markdown/markdown-renderer.tsx +232 -0
- package/src/markdown/youtube-embed.tsx +43 -0
- package/src/og/image.ts +312 -0
- package/src/og/index.tsx +430 -0
- package/src/og/query.ts +26 -0
- package/src/shadcn/accordion.tsx +71 -0
- package/src/shadcn/alert-dialog.tsx +187 -0
- package/src/shadcn/alert.tsx +75 -0
- package/src/shadcn/aspect-ratio.tsx +22 -0
- package/src/shadcn/attachment.tsx +207 -0
- package/src/shadcn/avatar.tsx +106 -0
- package/src/shadcn/badge.tsx +51 -0
- package/src/shadcn/breadcrumb.tsx +124 -0
- package/src/shadcn/bubble.tsx +127 -0
- package/src/shadcn/button-group.tsx +87 -0
- package/src/shadcn/button.tsx +57 -0
- package/src/shadcn/calendar.tsx +219 -0
- package/src/shadcn/card.tsx +102 -0
- package/src/shadcn/carousel.tsx +242 -0
- package/src/shadcn/chart.tsx +372 -0
- package/src/shadcn/checkbox.tsx +28 -0
- package/src/shadcn/collapsible.tsx +19 -0
- package/src/shadcn/combobox.tsx +297 -0
- package/src/shadcn/command.tsx +194 -0
- package/src/shadcn/context-menu.tsx +270 -0
- package/src/shadcn/dialog.tsx +158 -0
- package/src/shadcn/direction.tsx +4 -0
- package/src/shadcn/drawer.tsx +227 -0
- package/src/shadcn/dropdown-menu.tsx +265 -0
- package/src/shadcn/empty.tsx +103 -0
- package/src/shadcn/field.tsx +238 -0
- package/src/shadcn/form.tsx +173 -0
- package/src/shadcn/hover-card.tsx +50 -0
- package/src/shadcn/index.ts +53 -0
- package/src/shadcn/input-group.tsx +156 -0
- package/src/shadcn/input-otp.tsx +84 -0
- package/src/shadcn/input.tsx +19 -0
- package/src/shadcn/item.tsx +201 -0
- package/src/shadcn/kbd.tsx +26 -0
- package/src/shadcn/label.tsx +19 -0
- package/src/shadcn/marker.tsx +70 -0
- package/src/shadcn/menubar.tsx +278 -0
- package/src/shadcn/message-scroller.tsx +131 -0
- package/src/shadcn/message.tsx +91 -0
- package/src/shadcn/native-select.tsx +60 -0
- package/src/shadcn/navigation-menu.tsx +167 -0
- package/src/shadcn/pagination.tsx +130 -0
- package/src/shadcn/popover.tsx +87 -0
- package/src/shadcn/progress.tsx +82 -0
- package/src/shadcn/radio-group.tsx +35 -0
- package/src/shadcn/resizable.tsx +49 -0
- package/src/shadcn/scroll-area.tsx +52 -0
- package/src/shadcn/select.tsx +200 -0
- package/src/shadcn/separator.tsx +22 -0
- package/src/shadcn/sheet.tsx +136 -0
- package/src/shadcn/sidebar.tsx +721 -0
- package/src/shadcn/skeleton.tsx +13 -0
- package/src/shadcn/slider.tsx +51 -0
- package/src/shadcn/sonner.tsx +49 -0
- package/src/shadcn/spinner.tsx +10 -0
- package/src/shadcn/switch.tsx +29 -0
- package/src/shadcn/table.tsx +115 -0
- package/src/shadcn/tabs.tsx +79 -0
- package/src/shadcn/textarea.tsx +17 -0
- package/src/shadcn/toggle-group.tsx +89 -0
- package/src/shadcn/toggle.tsx +44 -0
- package/src/shadcn/tooltip.tsx +63 -0
- package/src/theme/config.ts +48 -0
- package/src/theme/css/base.css +92 -0
- package/src/theme/css/bitboy.css +75 -0
- package/src/theme/css/dark.css +47 -0
- package/src/theme/css/designer.css +122 -0
- package/src/theme/css/fonts.css +33 -0
- package/src/theme/css/globals.css +14 -0
- package/src/theme/css/hackintosh.css +57 -0
- package/src/theme/css/llama.css +103 -0
- package/src/theme/css/safelist.css +39 -0
- package/src/theme/css/shadcn.css +77 -0
- package/src/theme/css/theme.css +216 -0
- package/src/theme/embed-theme.ts +146 -0
- package/src/theme/fonts/FiraCode-VF.woff2 +0 -0
- package/src/theme/fonts/Hack-Regular.woff2 +0 -0
- package/src/theme/fonts/LICENSE-FiraCode.txt +93 -0
- package/src/theme/fonts/LICENSE-Hack.txt +45 -0
- package/src/theme/fonts/LICENSE-MonaSans.txt +93 -0
- package/src/theme/fonts/Mona-Sans.woff2 +0 -0
- package/src/theme/index.ts +15 -0
- package/src/utils/cn.ts +3 -0
- 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
|
+
}
|