@elabs-ai/components-ai 5.4.0 → 5.5.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.
- package/README.md +4 -6
- package/dist/_lazy-engine-boundary.js +1 -1
- package/dist/_persona-rive.js.map +1 -1
- package/dist/_streamdown-i18n.js +2 -2
- package/dist/agent.js +2 -2
- package/dist/asset-preview.js +5 -5
- package/dist/attachments.js +1 -1
- package/dist/audio-player.js +1 -2
- package/dist/{chunk-7RL6KZDF.js → chunk-24M6XWVJ.js} +25 -17
- package/dist/chunk-24M6XWVJ.js.map +1 -0
- package/dist/{chunk-VJZJWNEC.js → chunk-3KXIWGHW.js} +3 -3
- package/dist/{chunk-TWPHAJNM.js → chunk-3UHR3RLZ.js} +12 -6
- package/dist/chunk-3UHR3RLZ.js.map +1 -0
- package/dist/{chunk-V4DORGWJ.js → chunk-5AMSNG2T.js} +2 -2
- package/dist/chunk-D7BZMRI3.js +2 -0
- package/dist/chunk-D7BZMRI3.js.map +1 -0
- package/dist/{chunk-PQBF6ZIQ.js → chunk-DI645WR4.js} +4 -4
- package/dist/{chunk-NCL2UZKI.js → chunk-FFA7OPEH.js} +2 -2
- package/dist/{chunk-34CQ2VHM.js → chunk-FO44KY7Y.js} +10 -7
- package/dist/chunk-FO44KY7Y.js.map +1 -0
- package/dist/{chunk-RB7G65JC.js → chunk-GP7ZZCID.js} +7 -5
- package/dist/chunk-GP7ZZCID.js.map +1 -0
- package/dist/{chunk-6PB2MXCI.js → chunk-HLODND65.js} +2 -2
- package/dist/{chunk-ADOYAGGK.js → chunk-IFVO7LQJ.js} +2 -2
- package/dist/{chunk-2XOIG5TG.js → chunk-JSEV46SH.js} +18 -12
- package/dist/chunk-JSEV46SH.js.map +1 -0
- package/dist/{chunk-CZP3RHC4.js → chunk-LY7O4BMP.js} +2 -2
- package/dist/{chunk-AOOIAFDT.js → chunk-OCZNA5SO.js} +2 -2
- package/dist/{chunk-S3ISPW3A.js → chunk-PWQPVOZJ.js} +1 -1
- package/dist/chunk-PWQPVOZJ.js.map +1 -0
- package/dist/chunk-RLV64EWK.js +41 -0
- package/dist/chunk-RLV64EWK.js.map +1 -0
- package/dist/chunk-SSES3F4Q.js +126 -0
- package/dist/chunk-SSES3F4Q.js.map +1 -0
- package/dist/{chunk-3XNPMDZY.js → chunk-TBCMCF5O.js} +9 -56
- package/dist/chunk-TBCMCF5O.js.map +1 -0
- package/dist/{chunk-UHLVPAHG.js → chunk-WXUXFDJQ.js} +2 -2
- package/dist/{chunk-X47QMM6K.js → chunk-YLN7E4OV.js} +2 -2
- package/dist/code-block.js +1 -1
- package/dist/diff-view.js +2 -2
- package/dist/gallery.js +1 -1
- package/dist/generated-image.js +9 -0
- package/dist/generated-image.js.map +1 -0
- package/dist/grouped-parts.js +6 -6
- package/dist/image.js +4 -4
- package/dist/index.d.ts +92 -150
- package/dist/index.js +67 -65
- package/dist/markdown-view.js +3 -3
- package/dist/message.js +3 -3
- package/dist/model-provider-logo.js +1 -1
- package/dist/persona.js +2 -2
- package/dist/queue.js +1 -1
- package/dist/reasoning.js +3 -3
- package/dist/sandbox.js +3 -3
- package/dist/tool.js +2 -2
- package/package.json +8 -13
- package/schemas/a2ui-surface.v1.schema.json +1 -1
- package/src/__contract__/{image.contract.test.tsx → attachments.contract.test.tsx} +7 -7
- package/src/__contract__/generated-image.contract.test.tsx +49 -0
- package/src/_lazy-boundary-conformance.ts +12 -18
- package/src/_lazy-engine-boundary.tsx +1 -2
- package/src/_lazy-mermaid-absent.test.ts +2 -2
- package/src/_media-fixtures.ts +47 -0
- package/src/_persona-rive.tsx +1 -2
- package/src/a2ui/catalog.source.json +1 -1
- package/src/asset-preview.stories.tsx +12 -0
- package/src/asset-preview.test.tsx +14 -0
- package/src/asset-preview.tsx +4 -2
- package/src/attachments.stories.tsx +147 -0
- package/src/attachments.test.tsx +104 -0
- package/src/attachments.tsx +28 -20
- package/src/audio-player.stories.tsx +10 -45
- package/src/audio-player.test.tsx +166 -61
- package/src/audio-player.tsx +175 -266
- package/src/chat.stories.tsx +75 -3
- package/src/code-block.test.tsx +32 -0
- package/src/code-block.tsx +22 -5
- package/src/gallery.tsx +13 -77
- package/src/generated-image.stories.tsx +67 -0
- package/src/generated-image.test.tsx +107 -0
- package/src/generated-image.tsx +57 -0
- package/src/image.tsx +2 -115
- package/src/index.ts +1 -0
- package/src/model-provider-logo.tsx +20 -18
- package/src/queue.test.tsx +24 -0
- package/src/queue.tsx +8 -5
- package/dist/_audio-player-media-chrome.js +0 -85
- package/dist/_audio-player-media-chrome.js.map +0 -1
- package/dist/chunk-2XOIG5TG.js.map +0 -1
- package/dist/chunk-34CQ2VHM.js.map +0 -1
- package/dist/chunk-3XNPMDZY.js.map +0 -1
- package/dist/chunk-7RL6KZDF.js.map +0 -1
- package/dist/chunk-NSCLRHES.js +0 -102
- package/dist/chunk-NSCLRHES.js.map +0 -1
- package/dist/chunk-RB7G65JC.js.map +0 -1
- package/dist/chunk-S3ISPW3A.js.map +0 -1
- package/dist/chunk-TWPHAJNM.js.map +0 -1
- package/dist/chunk-V6E5DYZD.js +0 -80
- package/dist/chunk-V6E5DYZD.js.map +0 -1
- package/src/_audio-player-media-chrome.tsx +0 -233
- package/src/image.stories.tsx +0 -67
- package/src/image.test.tsx +0 -105
- /package/dist/{chunk-VJZJWNEC.js.map → chunk-3KXIWGHW.js.map} +0 -0
- /package/dist/{chunk-V4DORGWJ.js.map → chunk-5AMSNG2T.js.map} +0 -0
- /package/dist/{chunk-PQBF6ZIQ.js.map → chunk-DI645WR4.js.map} +0 -0
- /package/dist/{chunk-NCL2UZKI.js.map → chunk-FFA7OPEH.js.map} +0 -0
- /package/dist/{chunk-6PB2MXCI.js.map → chunk-HLODND65.js.map} +0 -0
- /package/dist/{chunk-ADOYAGGK.js.map → chunk-IFVO7LQJ.js.map} +0 -0
- /package/dist/{chunk-CZP3RHC4.js.map → chunk-LY7O4BMP.js.map} +0 -0
- /package/dist/{chunk-AOOIAFDT.js.map → chunk-OCZNA5SO.js.map} +0 -0
- /package/dist/{chunk-UHLVPAHG.js.map → chunk-WXUXFDJQ.js.map} +0 -0
- /package/dist/{chunk-X47QMM6K.js.map → chunk-YLN7E4OV.js.map} +0 -0
package/src/audio-player.tsx
CHANGED
|
@@ -1,191 +1,146 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
MediaPlayer,
|
|
5
|
+
MediaPlayerControls,
|
|
6
|
+
MediaPlayerElement,
|
|
7
|
+
MediaPlayerError,
|
|
8
|
+
MediaPlayerLoading,
|
|
9
|
+
MediaPlayerMuteButton,
|
|
10
|
+
MediaPlayerPlayButton,
|
|
11
|
+
MediaPlayerSeekButton,
|
|
12
|
+
MediaPlayerTime,
|
|
13
|
+
MediaPlayerTimeSlider,
|
|
14
|
+
MediaPlayerVolumeSlider,
|
|
15
|
+
useLocale,
|
|
16
|
+
useMediaPlayer,
|
|
17
|
+
type MediaPlayerButtonProps,
|
|
18
|
+
type MediaPlayerControlsProps,
|
|
19
|
+
type MediaPlayerElementProps,
|
|
20
|
+
type MediaPlayerProps,
|
|
21
|
+
type MediaPlayerTimeProps,
|
|
22
|
+
type MediaPlayerTimeSliderProps,
|
|
23
|
+
type MediaPlayerVolumeSliderProps,
|
|
24
|
+
} from "@elabs-ai/components-ui";
|
|
5
25
|
import type { Experimental_SpeechResult as SpeechResult } from "ai";
|
|
6
|
-
import {
|
|
7
|
-
import type { ComponentProps, ComponentType, HTMLAttributes } from "react";
|
|
8
|
-
import { lazy, Suspense } from "react";
|
|
26
|
+
import type { ComponentProps, HTMLAttributes, Ref } from "react";
|
|
9
27
|
|
|
10
|
-
|
|
28
|
+
/**
|
|
29
|
+
* `AudioPlayer*` — the chat-facing names for spoken agent replies, as thin
|
|
30
|
+
* presets over ui's `MediaPlayer*` compound parts (ADR 0041). Each preset
|
|
31
|
+
* emits the SHARED slot of the ui part it wraps (`media-player*`, spelled out
|
|
32
|
+
* so this module declares what it renders); the former `audio-player*`
|
|
33
|
+
* selectors are gone (maintainer decision 2026-09-23, ADR 0041 §7). The ui
|
|
34
|
+
* part underneath owns the behaviour, labels and styling.
|
|
35
|
+
*/
|
|
11
36
|
|
|
12
37
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* `_audio-player-media-chrome.tsx`. See ADR 0019 and `pnpm heavy-deps:check`.
|
|
17
|
-
*
|
|
18
|
-
* All ten parts resolve from the SAME module specifier, so they share one chunk:
|
|
19
|
-
* once `AudioPlayer` has loaded it, the controls inside it resolve from cache.
|
|
20
|
-
*
|
|
21
|
-
* Issue #101: `media-chrome` is an OPTIONAL peer, so no PUBLIC export's type
|
|
22
|
-
* may structurally reference `media-chrome/react`'s own types (previously
|
|
23
|
-
* `ComponentProps<typeof MediaController>` and nine siblings, imported as
|
|
24
|
-
* TYPE-only above) — doing so names `media-chrome/react`'s module specifier in
|
|
25
|
-
* this package's generated root `.d.ts` and hands a `skipLibCheck: false`
|
|
26
|
-
* consumer who has correctly omitted the peer a `TS2307` just for importing
|
|
27
|
-
* the barrel — not just for using `AudioPlayer`. `AudioPlayerPartProps` below
|
|
28
|
-
* is an OWNED base type: `media-chrome/react`'s wrapper
|
|
29
|
-
* (`media-chrome/react`'s `createComponent`, from the `ce-la-react` helper it
|
|
30
|
-
* is built on) types every part as `Omit<HTMLAttributes<I>, …> &
|
|
31
|
-
* Partial<the element's own instance members>` — i.e. ordinary HTML attributes
|
|
32
|
-
* plus a handful of optional custom attributes. `AudioPlayerPartProps` itself
|
|
33
|
-
* stays ordinary HTML attributes only; the PRIMITIVE-typed (`string` /
|
|
34
|
-
* `boolean` / `number`) extra instance members of `<media-controller>` and
|
|
35
|
-
* `<media-seek-forward-button>` — the two elements measured by the round-2
|
|
36
|
-
* validator — are mirrored one-by-one on `AudioPlayerProps` and
|
|
37
|
-
* `AudioPlayerSeekForwardButtonProps` below (each a single declaration line
|
|
38
|
-
* referencing nothing peer-owned, the same technique `seekOffset` already
|
|
39
|
-
* used). What stays narrowed to `AudioPlayerPartProps` alone are the
|
|
40
|
-
* PEER/COMPLEX-typed members (`MediaStore`, `AttributeTokenList`,
|
|
41
|
-
* `MediaTooltip`, `TooltipPlacement`, DOM-element and `HTMLMediaElement`
|
|
42
|
-
* callbacks, methods) and every member of the other eight parts — restoring
|
|
43
|
-
* those would re-import the peer type graph this fix exists to avoid. See the
|
|
44
|
-
* CHANGELOG's "Breaking (types)" entry for the exact narrowed set.
|
|
45
|
-
* `AudioPlayerPartProps` is therefore NOT a full replica of media-chrome's
|
|
46
|
-
* surface, only the base every part actually uses and re-exports (the same
|
|
47
|
-
* scoping `TerminalColorTheme` uses for xterm's `ITheme` in
|
|
48
|
-
* `packages/terminal/src/interactive-terminal.tsx`, also issue #101). The ten
|
|
49
|
-
* conformance assertions at the bottom of `_audio-player-media-chrome.tsx`
|
|
50
|
-
* (which still imports the real peer types — that module is reached only
|
|
51
|
-
* through `lazy()` and never sits in the barrel's declaration graph) prove
|
|
52
|
-
* every owned type here stays assignable to its real media-chrome counterpart;
|
|
53
|
-
* a peer version bump that narrows a prop incompatibly fails
|
|
54
|
-
* `pnpm --filter @elabs-ai/components-ai typecheck` locally instead of
|
|
55
|
-
* reaching a consumer as silent drift.
|
|
56
|
-
*
|
|
57
|
-
* Deliberately no `ref` field: `ComponentProps<typeof MediaController>` (and
|
|
58
|
-
* its nine siblings) included one via `RefAttributes<I>`, but `RefObject<T>`'s
|
|
59
|
-
* mutable `current` makes ref types INVARIANT in `T` — a `Ref<HTMLElement>`
|
|
60
|
-
* can never be assignable to a real element's own `Ref<MediaController>` (the
|
|
61
|
-
* custom element subclass carries private/extra members `HTMLElement` lacks),
|
|
62
|
-
* so keeping it would make every owned type fail its own conformance
|
|
63
|
-
* assertion below. This IS a real-world break for a React 19 consumer: none
|
|
64
|
-
* of the ten parts this package renders is wrapped in `forwardRef`, but
|
|
65
|
-
* `MediaController` (and its siblings) are genuine `forwardRef` components,
|
|
66
|
-
* and React 19's ref-as-prop carries a `ref` straight through an ordinary
|
|
67
|
-
* function component's `{...props}` spread to the real underlying element —
|
|
68
|
-
* see the CHANGELOG's "Breaking (types)" entry for the measured detail. It is
|
|
69
|
-
* inert only under React 18.
|
|
38
|
+
* @deprecated The shared base of the former custom-element parts. Each part now
|
|
39
|
+
* has its own prop type (`AudioPlayerPlayButtonProps`, …); removed in the next
|
|
40
|
+
* major.
|
|
70
41
|
*/
|
|
71
42
|
export type AudioPlayerPartProps = HTMLAttributes<HTMLElement>;
|
|
72
43
|
|
|
73
|
-
type MediaChromeModule = typeof import("./_audio-player-media-chrome");
|
|
74
|
-
|
|
75
|
-
const lazyPart = <P,>(pick: (module: MediaChromeModule) => ComponentType<P>) =>
|
|
76
|
-
lazy(() => import("./_audio-player-media-chrome").then((m) => ({ default: pick(m) })));
|
|
77
|
-
|
|
78
44
|
/**
|
|
79
|
-
* The
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* against the `.d.ts` leak this fix exists to prevent. Deliberately EXCLUDED:
|
|
84
|
-
* `audio` (already narrowed out — `_audio-player-media-chrome.tsx`'s
|
|
85
|
-
* conformance assertion Omits it from the real `ComponentProps`, since this
|
|
86
|
-
* package renders its own `<AudioPlayerElement>` instead of passing one
|
|
87
|
-
* through), and every PEER/COMPLEX-typed member (`mediaStore`, `hotkeys`
|
|
88
|
-
* (`AttributeTokenList`), `media`/`fullscreenElement` (DOM elements),
|
|
89
|
-
* `mediaStateReceivers`, `associatedElementSubscriptions`, every
|
|
90
|
-
* `HTMLMediaElement` callback and every method) — those stay narrowed to
|
|
91
|
-
* `AudioPlayerPartProps` alone; restoring them would re-import the peer type
|
|
92
|
-
* graph. See `_audio-player-media-chrome.tsx`'s
|
|
93
|
-
* `_AudioPlayerPropsConformance` assertion, which fails typecheck if this
|
|
94
|
-
* interface ever drifts wider than the real element's own props.
|
|
95
|
-
*
|
|
96
|
-
* Doc comments below are inferred from each member's name and media-chrome's
|
|
97
|
-
* public attribute-naming convention (`no*` = boolean opt-out of the
|
|
98
|
-
* corresponding default-on behavior) — media-chrome ships no per-property doc
|
|
99
|
-
* comments in its own `.d.ts` or README to copy from. Treat them as a reading
|
|
100
|
-
* aid, not an authoritative spec; verify against media-chrome's source before
|
|
101
|
-
* relying on exact semantics.
|
|
45
|
+
* The former custom-element controller attributes. Kept so existing
|
|
46
|
+
* call sites typecheck; `noHotkeys`/`keyboardControl` still map onto
|
|
47
|
+
* `keyboardShortcuts`, every other member is accepted and ignored. All are
|
|
48
|
+
* removed in the next major.
|
|
102
49
|
*/
|
|
103
|
-
|
|
104
|
-
/**
|
|
50
|
+
interface AudioPlayerLegacyProps {
|
|
51
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
105
52
|
autohide?: string;
|
|
106
|
-
/**
|
|
53
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
107
54
|
autohideOverControls?: boolean;
|
|
108
|
-
/**
|
|
55
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
109
56
|
breakpoints?: string;
|
|
110
|
-
/**
|
|
57
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
111
58
|
breakpointsComputed?: boolean;
|
|
112
|
-
/**
|
|
59
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
113
60
|
defaultDuration?: number;
|
|
114
|
-
/**
|
|
61
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
115
62
|
defaultStreamType?: string;
|
|
116
|
-
/**
|
|
63
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
117
64
|
defaultSubtitles?: boolean;
|
|
118
|
-
/**
|
|
65
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
119
66
|
gesturesDisabled?: boolean;
|
|
120
|
-
/**
|
|
67
|
+
/** @deprecated Use `keyboardShortcuts`. `false` turns the player's keyboard map off. */
|
|
121
68
|
keyboardControl?: boolean;
|
|
122
|
-
/**
|
|
69
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
123
70
|
keysUsed?: string;
|
|
124
|
-
/**
|
|
71
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
125
72
|
liveEdgeOffset?: number;
|
|
126
|
-
/**
|
|
73
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
127
74
|
noAutohide?: boolean;
|
|
128
|
-
/**
|
|
75
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
129
76
|
noAutoSeekToLive?: boolean;
|
|
130
|
-
/**
|
|
77
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
131
78
|
noDefaultStore?: boolean;
|
|
132
|
-
/**
|
|
79
|
+
/** @deprecated Use `keyboardShortcuts={false}`. */
|
|
133
80
|
noHotkeys?: boolean;
|
|
134
|
-
/**
|
|
81
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
135
82
|
noMutedPref?: boolean;
|
|
136
|
-
/**
|
|
83
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
137
84
|
noSubtitlesLangPref?: boolean;
|
|
138
|
-
/**
|
|
85
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
139
86
|
noVolumePref?: boolean;
|
|
140
|
-
/**
|
|
87
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
141
88
|
resolvedLang?: string;
|
|
142
|
-
/**
|
|
89
|
+
/** @deprecated Ignored (legacy controller attribute); removed in the next major. */
|
|
143
90
|
userInteractive?: boolean;
|
|
144
91
|
}
|
|
145
92
|
|
|
146
|
-
|
|
93
|
+
export interface AudioPlayerProps extends Omit<MediaPlayerProps, "kind">, AudioPlayerLegacyProps {}
|
|
147
94
|
|
|
148
|
-
/**
|
|
149
|
-
|
|
150
|
-
* once the `media-chrome` load has genuinely failed, never while it is still
|
|
151
|
-
* pending (that stays `Skeleton`, via the `Suspense fallback` below). A
|
|
152
|
-
* loading skeleton left in place after a settled failure reads as "still
|
|
153
|
-
* loading, forever" (loading-states.md) — this is a terminal state, so it
|
|
154
|
-
* gets a real (if compact) notice instead, sized to the same `h-10` slot so
|
|
155
|
-
* nothing shifts when the boundary trips.
|
|
156
|
-
*/
|
|
157
|
-
const AudioPlayerMissing = ({ className, error }: { className?: string; error: unknown }) => {
|
|
95
|
+
/** Replaces the controls with the compact error panel once playback has terminally failed. */
|
|
96
|
+
function AudioPlayerErrorRung() {
|
|
158
97
|
const { t } = useLocale();
|
|
159
|
-
const
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
"flex h-10 w-full items-center gap-2 rounded-md border border-dashed border-border bg-muted/30 px-3 text-caption text-muted-foreground",
|
|
164
|
-
className,
|
|
165
|
-
)}
|
|
166
|
-
role={isPeerMissing ? "status" : "alert"}
|
|
167
|
-
>
|
|
168
|
-
<VolumeOffIcon aria-hidden="true" className="size-4 shrink-0" />
|
|
169
|
-
<span className="truncate">
|
|
170
|
-
{isPeerMissing
|
|
171
|
-
? t("ai.error.engineMissingBody", {
|
|
172
|
-
feature: t("ai.audioPlayer.feature"),
|
|
173
|
-
packages: "media-chrome",
|
|
174
|
-
})
|
|
175
|
-
: t("ai.audioPlayer.renderError")}
|
|
176
|
-
</span>
|
|
177
|
-
</div>
|
|
178
|
-
);
|
|
179
|
-
};
|
|
98
|
+
const { state } = useMediaPlayer();
|
|
99
|
+
if (!state.error) return null;
|
|
100
|
+
return <MediaPlayerError title={t("ai.audioPlayer.renderError")} />;
|
|
101
|
+
}
|
|
180
102
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The player root (`MediaPlayer kind="audio"`): transparent, so put it in the
|
|
105
|
+
* bubble or card that is its surface. Compose `AudioPlayerElement` plus an
|
|
106
|
+
* `AudioPlayerControlBar` inside.
|
|
107
|
+
*/
|
|
108
|
+
export const AudioPlayer = ({
|
|
109
|
+
keyboardShortcuts,
|
|
110
|
+
noHotkeys,
|
|
111
|
+
keyboardControl,
|
|
112
|
+
children,
|
|
113
|
+
// Accepted and ignored (former legacy controller attributes).
|
|
114
|
+
autohide: _autohide,
|
|
115
|
+
autohideOverControls: _autohideOverControls,
|
|
116
|
+
breakpoints: _breakpoints,
|
|
117
|
+
breakpointsComputed: _breakpointsComputed,
|
|
118
|
+
defaultDuration: _defaultDuration,
|
|
119
|
+
defaultStreamType: _defaultStreamType,
|
|
120
|
+
defaultSubtitles: _defaultSubtitles,
|
|
121
|
+
gesturesDisabled: _gesturesDisabled,
|
|
122
|
+
keysUsed: _keysUsed,
|
|
123
|
+
liveEdgeOffset: _liveEdgeOffset,
|
|
124
|
+
noAutohide: _noAutohide,
|
|
125
|
+
noAutoSeekToLive: _noAutoSeekToLive,
|
|
126
|
+
noDefaultStore: _noDefaultStore,
|
|
127
|
+
noMutedPref: _noMutedPref,
|
|
128
|
+
noSubtitlesLangPref: _noSubtitlesLangPref,
|
|
129
|
+
noVolumePref: _noVolumePref,
|
|
130
|
+
resolvedLang: _resolvedLang,
|
|
131
|
+
userInteractive: _userInteractive,
|
|
132
|
+
...props
|
|
133
|
+
}: AudioPlayerProps) => (
|
|
134
|
+
<MediaPlayer
|
|
135
|
+
kind="audio"
|
|
136
|
+
data-slot="media-player"
|
|
137
|
+
keyboardShortcuts={(keyboardShortcuts ?? true) && !noHotkeys && keyboardControl !== false}
|
|
138
|
+
{...props}
|
|
184
139
|
>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
</
|
|
140
|
+
{children}
|
|
141
|
+
<MediaPlayerLoading />
|
|
142
|
+
<AudioPlayerErrorRung />
|
|
143
|
+
</MediaPlayer>
|
|
189
144
|
);
|
|
190
145
|
|
|
191
146
|
export type AudioPlayerElementProps = Omit<ComponentProps<"audio">, "src"> &
|
|
@@ -198,160 +153,114 @@ export type AudioPlayerElementProps = Omit<ComponentProps<"audio">, "src"> &
|
|
|
198
153
|
}
|
|
199
154
|
);
|
|
200
155
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
156
|
+
/**
|
|
157
|
+
* The `<audio>` element. Pass a URL as `src`, or an AI SDK speech result's
|
|
158
|
+
* `audio` as `data` (converted to a base64 data URL).
|
|
159
|
+
*/
|
|
160
|
+
export const AudioPlayerElement = ({ ref, ...props }: AudioPlayerElementProps) => {
|
|
161
|
+
const { src, data, ...rest } = props as typeof props & {
|
|
162
|
+
src?: string;
|
|
163
|
+
data?: SpeechResult["audio"];
|
|
164
|
+
};
|
|
165
|
+
return (
|
|
166
|
+
<MediaPlayerElement
|
|
167
|
+
data-slot="media-player-element"
|
|
168
|
+
{...(rest as MediaPlayerElementProps)}
|
|
169
|
+
ref={ref as Ref<HTMLMediaElement> | undefined}
|
|
170
|
+
src={data ? `data:${data.mediaType};base64,${data.base64}` : src}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
212
174
|
|
|
213
|
-
|
|
214
|
-
(m) => m.AudioPlayerControlBar,
|
|
215
|
-
);
|
|
175
|
+
export type AudioPlayerControlBarProps = MediaPlayerControlsProps;
|
|
216
176
|
|
|
217
177
|
export const AudioPlayerControlBar = (props: AudioPlayerControlBarProps) => (
|
|
218
|
-
<
|
|
219
|
-
<Suspense fallback={null}>
|
|
220
|
-
<AudioPlayerControlBarImpl data-slot="audio-player-control-bar" {...props} />
|
|
221
|
-
</Suspense>
|
|
222
|
-
</LazyEngineBoundary>
|
|
178
|
+
<MediaPlayerControls data-slot="media-player-controls" {...props} />
|
|
223
179
|
);
|
|
224
180
|
|
|
225
|
-
export type AudioPlayerPlayButtonProps =
|
|
226
|
-
|
|
227
|
-
const AudioPlayerPlayButtonImpl = lazyPart<AudioPlayerPlayButtonProps>(
|
|
228
|
-
(m) => m.AudioPlayerPlayButton,
|
|
229
|
-
);
|
|
181
|
+
export type AudioPlayerPlayButtonProps = MediaPlayerButtonProps;
|
|
230
182
|
|
|
231
183
|
export const AudioPlayerPlayButton = (props: AudioPlayerPlayButtonProps) => (
|
|
232
|
-
<
|
|
233
|
-
<Suspense fallback={null}>
|
|
234
|
-
<AudioPlayerPlayButtonImpl data-slot="audio-player-play-button" {...props} />
|
|
235
|
-
</Suspense>
|
|
236
|
-
</LazyEngineBoundary>
|
|
184
|
+
<MediaPlayerPlayButton data-slot="media-player-play-button" {...props} />
|
|
237
185
|
);
|
|
238
186
|
|
|
239
|
-
|
|
240
|
-
|
|
187
|
+
/** Props shared by the two seek presets. */
|
|
188
|
+
interface AudioPlayerSeekPresetProps extends MediaPlayerButtonProps {
|
|
189
|
+
/** Seconds to skip, as a positive distance; the button sets the direction. Default 10. */
|
|
190
|
+
offset?: number;
|
|
191
|
+
/** @deprecated Use `offset`. */
|
|
241
192
|
seekOffset?: number;
|
|
242
193
|
}
|
|
243
194
|
|
|
244
|
-
|
|
245
|
-
(m) => m.AudioPlayerSeekBackwardButton,
|
|
246
|
-
);
|
|
195
|
+
export type AudioPlayerSeekBackwardButtonProps = AudioPlayerSeekPresetProps;
|
|
247
196
|
|
|
248
|
-
export const AudioPlayerSeekBackwardButton = (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
197
|
+
export const AudioPlayerSeekBackwardButton = ({
|
|
198
|
+
offset,
|
|
199
|
+
seekOffset,
|
|
200
|
+
...props
|
|
201
|
+
}: AudioPlayerSeekBackwardButtonProps) => (
|
|
202
|
+
<MediaPlayerSeekButton
|
|
203
|
+
data-slot="media-player-seek-button"
|
|
204
|
+
{...props}
|
|
205
|
+
offset={-Math.abs(offset ?? seekOffset ?? 10)}
|
|
206
|
+
/>
|
|
254
207
|
);
|
|
255
208
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
* (R2) — same technique and same inferred-from-naming caveat as
|
|
259
|
-
* `AudioPlayerProps` above. Deliberately still narrowed out:
|
|
260
|
-
* `keysUsed` (here typed `string[]`, unlike the controller's own `string`
|
|
261
|
-
* member of the same name — an array is not primitive) and the DOM/method
|
|
262
|
-
* members `disable`/`enable`/`handleClick`/`tooltipEl`/`tooltipPlacement`.
|
|
263
|
-
*/
|
|
264
|
-
export interface AudioPlayerSeekForwardButtonProps extends AudioPlayerPartProps {
|
|
265
|
-
/** Whether the button is disabled. */
|
|
266
|
-
disabled?: boolean;
|
|
267
|
-
/** ID of the `<media-controller>` element this button controls, when it is not an ancestor. */
|
|
209
|
+
export interface AudioPlayerSeekForwardButtonProps extends AudioPlayerSeekPresetProps {
|
|
210
|
+
/** @deprecated Ignored (legacy attribute); removed in the next major. */
|
|
268
211
|
mediaController?: string;
|
|
269
|
-
/**
|
|
212
|
+
/** @deprecated Ignored (legacy attribute); removed in the next major. */
|
|
270
213
|
mediaCurrentTime?: number;
|
|
271
|
-
/**
|
|
272
|
-
seekOffset?: number;
|
|
273
|
-
/** Suppresses the built-in hover tooltip on this button. */
|
|
214
|
+
/** @deprecated Ignored (legacy attribute); removed in the next major. */
|
|
274
215
|
noTooltip?: boolean;
|
|
275
|
-
/**
|
|
216
|
+
/** @deprecated Ignored (legacy attribute); removed in the next major. */
|
|
276
217
|
preventClick?: boolean;
|
|
277
218
|
}
|
|
278
219
|
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
220
|
+
export const AudioPlayerSeekForwardButton = ({
|
|
221
|
+
offset,
|
|
222
|
+
seekOffset,
|
|
223
|
+
mediaController: _mediaController,
|
|
224
|
+
mediaCurrentTime: _mediaCurrentTime,
|
|
225
|
+
noTooltip: _noTooltip,
|
|
226
|
+
preventClick: _preventClick,
|
|
227
|
+
...props
|
|
228
|
+
}: AudioPlayerSeekForwardButtonProps) => (
|
|
229
|
+
<MediaPlayerSeekButton
|
|
230
|
+
data-slot="media-player-seek-button"
|
|
231
|
+
{...props}
|
|
232
|
+
offset={Math.abs(offset ?? seekOffset ?? 10)}
|
|
233
|
+
/>
|
|
289
234
|
);
|
|
290
235
|
|
|
291
|
-
export type AudioPlayerTimeDisplayProps =
|
|
292
|
-
|
|
293
|
-
const AudioPlayerTimeDisplayImpl = lazyPart<AudioPlayerTimeDisplayProps>(
|
|
294
|
-
(m) => m.AudioPlayerTimeDisplay,
|
|
295
|
-
);
|
|
236
|
+
export type AudioPlayerTimeDisplayProps = Omit<MediaPlayerTimeProps, "mode">;
|
|
296
237
|
|
|
238
|
+
/** The current playback position. */
|
|
297
239
|
export const AudioPlayerTimeDisplay = (props: AudioPlayerTimeDisplayProps) => (
|
|
298
|
-
<
|
|
299
|
-
<Suspense fallback={null}>
|
|
300
|
-
<AudioPlayerTimeDisplayImpl data-slot="audio-player-time-display" {...props} />
|
|
301
|
-
</Suspense>
|
|
302
|
-
</LazyEngineBoundary>
|
|
240
|
+
<MediaPlayerTime data-slot="media-player-time" {...props} mode="current" />
|
|
303
241
|
);
|
|
304
242
|
|
|
305
|
-
export type AudioPlayerTimeRangeProps =
|
|
306
|
-
|
|
307
|
-
const AudioPlayerTimeRangeImpl = lazyPart<AudioPlayerTimeRangeProps>((m) => m.AudioPlayerTimeRange);
|
|
243
|
+
export type AudioPlayerTimeRangeProps = MediaPlayerTimeSliderProps;
|
|
308
244
|
|
|
309
245
|
export const AudioPlayerTimeRange = (props: AudioPlayerTimeRangeProps) => (
|
|
310
|
-
<
|
|
311
|
-
<Suspense fallback={null}>
|
|
312
|
-
<AudioPlayerTimeRangeImpl data-slot="audio-player-time-range" {...props} />
|
|
313
|
-
</Suspense>
|
|
314
|
-
</LazyEngineBoundary>
|
|
246
|
+
<MediaPlayerTimeSlider data-slot="media-player-time-slider" {...props} />
|
|
315
247
|
);
|
|
316
248
|
|
|
317
|
-
export type AudioPlayerDurationDisplayProps =
|
|
318
|
-
|
|
319
|
-
const AudioPlayerDurationDisplayImpl = lazyPart<AudioPlayerDurationDisplayProps>(
|
|
320
|
-
(m) => m.AudioPlayerDurationDisplay,
|
|
321
|
-
);
|
|
249
|
+
export type AudioPlayerDurationDisplayProps = Omit<MediaPlayerTimeProps, "mode">;
|
|
322
250
|
|
|
251
|
+
/** The total duration. */
|
|
323
252
|
export const AudioPlayerDurationDisplay = (props: AudioPlayerDurationDisplayProps) => (
|
|
324
|
-
<
|
|
325
|
-
<Suspense fallback={null}>
|
|
326
|
-
<AudioPlayerDurationDisplayImpl data-slot="audio-player-duration-display" {...props} />
|
|
327
|
-
</Suspense>
|
|
328
|
-
</LazyEngineBoundary>
|
|
253
|
+
<MediaPlayerTime data-slot="media-player-time" {...props} mode="duration" />
|
|
329
254
|
);
|
|
330
255
|
|
|
331
|
-
export type AudioPlayerMuteButtonProps =
|
|
332
|
-
|
|
333
|
-
const AudioPlayerMuteButtonImpl = lazyPart<AudioPlayerMuteButtonProps>(
|
|
334
|
-
(m) => m.AudioPlayerMuteButton,
|
|
335
|
-
);
|
|
256
|
+
export type AudioPlayerMuteButtonProps = MediaPlayerButtonProps;
|
|
336
257
|
|
|
337
258
|
export const AudioPlayerMuteButton = (props: AudioPlayerMuteButtonProps) => (
|
|
338
|
-
<
|
|
339
|
-
<Suspense fallback={null}>
|
|
340
|
-
<AudioPlayerMuteButtonImpl data-slot="audio-player-mute-button" {...props} />
|
|
341
|
-
</Suspense>
|
|
342
|
-
</LazyEngineBoundary>
|
|
259
|
+
<MediaPlayerMuteButton data-slot="media-player-mute-button" {...props} />
|
|
343
260
|
);
|
|
344
261
|
|
|
345
|
-
export type AudioPlayerVolumeRangeProps =
|
|
346
|
-
|
|
347
|
-
const AudioPlayerVolumeRangeImpl = lazyPart<AudioPlayerVolumeRangeProps>(
|
|
348
|
-
(m) => m.AudioPlayerVolumeRange,
|
|
349
|
-
);
|
|
262
|
+
export type AudioPlayerVolumeRangeProps = MediaPlayerVolumeSliderProps;
|
|
350
263
|
|
|
351
264
|
export const AudioPlayerVolumeRange = (props: AudioPlayerVolumeRangeProps) => (
|
|
352
|
-
<
|
|
353
|
-
<Suspense fallback={null}>
|
|
354
|
-
<AudioPlayerVolumeRangeImpl data-slot="audio-player-volume-range" {...props} />
|
|
355
|
-
</Suspense>
|
|
356
|
-
</LazyEngineBoundary>
|
|
265
|
+
<MediaPlayerVolumeSlider data-slot="media-player-volume-slider" {...props} />
|
|
357
266
|
);
|