@ai-matrx/media 0.13.0 → 0.15.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/CHANGELOG.md +37 -0
- package/dist/react.cjs +201 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +59 -2
- package/dist/react.d.ts +59 -2
- package/dist/react.js +210 -1
- package/dist/react.js.map +1 -1
- package/dist/speech.cjs +1689 -0
- package/dist/speech.cjs.map +1 -0
- package/dist/speech.d.cts +565 -0
- package/dist/speech.d.ts +565 -0
- package/dist/speech.js +1683 -0
- package/dist/speech.js.map +1 -0
- package/dist/tts-config-JbF86KIt.d.cts +149 -0
- package/dist/tts-config-JbF86KIt.d.ts +149 -0
- package/dist/voices.cjs +481 -0
- package/dist/voices.cjs.map +1 -1
- package/dist/voices.d.cts +16 -47
- package/dist/voices.d.ts +16 -47
- package/dist/voices.js +489 -0
- package/dist/voices.js.map +1 -1
- package/package.json +17 -4
package/dist/react.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNode, ComponentProps } from 'react';
|
|
3
3
|
import { MediaRefLike, DurableSrc, MediaUploadOptions, MediaActionKind } from './index.cjs';
|
|
4
4
|
import { M as MediaUploadEntry } from './use-media-upload-v5Uq9Ruq.cjs';
|
|
5
|
+
import { Volume2TapButton } from '@ai-matrx/tap-target/buttons';
|
|
6
|
+
import { SpeakRequest, PlaybackItemStatus, PlaybackSnapshot, pausePlayback, resumePlayback, skipPlayback, removePlaybackItem, playPlaybackItem } from '@ai-matrx/media/speech';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* src/react/cn.ts — conditional class join with last-wins Tailwind conflict
|
|
@@ -342,4 +344,59 @@ interface MediaLightboxProps {
|
|
|
342
344
|
}
|
|
343
345
|
declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, autoPlay, playsInline, poster, playbackLabel, actions, hideUnavailableActions, headerExtra, className, }: MediaLightboxProps): react.JSX.Element | null;
|
|
344
346
|
|
|
345
|
-
|
|
347
|
+
type ReadAloudStatus = "idle" | "loading" | "playing" | "paused" | "error";
|
|
348
|
+
type TapVariant = ComponentProps<typeof Volume2TapButton>["variant"];
|
|
349
|
+
type ReadAloudButtonSize = "default" | "compact";
|
|
350
|
+
interface ReadAloudButtonProps {
|
|
351
|
+
status: ReadAloudStatus;
|
|
352
|
+
onPress: () => void;
|
|
353
|
+
/** The host's sentence for the person when `status` is "error". */
|
|
354
|
+
error?: string | null;
|
|
355
|
+
/** Nothing to read (empty text) or the host forbids it right now. */
|
|
356
|
+
disabled?: boolean;
|
|
357
|
+
variant?: TapVariant;
|
|
358
|
+
/**
|
|
359
|
+
* "compact" is the message-footer size: the 32px cell (28px pill) every chat footer control uses,
|
|
360
|
+
* instead of the 38px default box. It sets the geometry only; it wins over `variant`.
|
|
361
|
+
*/
|
|
362
|
+
size?: ReadAloudButtonSize;
|
|
363
|
+
className?: string;
|
|
364
|
+
/** Label for the idle state. Default "Read aloud". */
|
|
365
|
+
label?: string;
|
|
366
|
+
}
|
|
367
|
+
declare function ReadAloudButton({ status, onPress, error, disabled, variant: requestedVariant, size, className, label, }: ReadAloudButtonProps): react.JSX.Element;
|
|
368
|
+
|
|
369
|
+
declare function usePlaybackSnapshot(): PlaybackSnapshot;
|
|
370
|
+
type UseSpeechOptions = Omit<SpeakRequest, "text" | "sample"> & {
|
|
371
|
+
/** Re-adopt the queue's active item when it is speaking exactly this text. */
|
|
372
|
+
adoptText?: string;
|
|
373
|
+
};
|
|
374
|
+
declare function useSpeech({ adoptText, ...request }?: UseSpeechOptions): {
|
|
375
|
+
speak: (text: string) => string;
|
|
376
|
+
status: PlaybackItemStatus | null;
|
|
377
|
+
error: string | null;
|
|
378
|
+
itemId: string | null;
|
|
379
|
+
isMine: boolean;
|
|
380
|
+
pause: typeof pausePlayback;
|
|
381
|
+
resume: typeof resumePlayback;
|
|
382
|
+
skip: typeof skipPlayback;
|
|
383
|
+
remove: typeof removePlaybackItem;
|
|
384
|
+
playItem: typeof playPlaybackItem;
|
|
385
|
+
};
|
|
386
|
+
/** The queue's item status in the read-aloud button's vocabulary. */
|
|
387
|
+
declare function readAloudStatusOf(status: PlaybackItemStatus | null): ReadAloudStatus;
|
|
388
|
+
declare function useReadAloud(text: string, options?: UseSpeechOptions): {
|
|
389
|
+
status: ReadAloudStatus;
|
|
390
|
+
error: string | null;
|
|
391
|
+
onPress: () => void;
|
|
392
|
+
speak: (text: string) => string;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
interface ReadAloudLanguagePickerProps {
|
|
396
|
+
value: string;
|
|
397
|
+
onChange: (code: string) => void;
|
|
398
|
+
className?: string;
|
|
399
|
+
}
|
|
400
|
+
declare function ReadAloudLanguagePicker({ value, onChange, className }: ReadAloudLanguagePickerProps): react.JSX.Element;
|
|
401
|
+
|
|
402
|
+
export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PauseIcon, PenToolIcon, PlayIcon, PlusIcon, ReadAloudButton, type ReadAloudButtonProps, type ReadAloudButtonSize, ReadAloudLanguagePicker, type ReadAloudLanguagePickerProps, type ReadAloudStatus, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, type UseSpeechOptions, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, readAloudStatusOf, useMediaDropzone, usePlaybackSnapshot, useReadAloud, useSpeech };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNode, ComponentProps } from 'react';
|
|
3
3
|
import { MediaRefLike, DurableSrc, MediaUploadOptions, MediaActionKind } from './index.js';
|
|
4
4
|
import { M as MediaUploadEntry } from './use-media-upload-Dg2HSvTq.js';
|
|
5
|
+
import { Volume2TapButton } from '@ai-matrx/tap-target/buttons';
|
|
6
|
+
import { SpeakRequest, PlaybackItemStatus, PlaybackSnapshot, pausePlayback, resumePlayback, skipPlayback, removePlaybackItem, playPlaybackItem } from '@ai-matrx/media/speech';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* src/react/cn.ts — conditional class join with last-wins Tailwind conflict
|
|
@@ -342,4 +344,59 @@ interface MediaLightboxProps {
|
|
|
342
344
|
}
|
|
343
345
|
declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, autoPlay, playsInline, poster, playbackLabel, actions, hideUnavailableActions, headerExtra, className, }: MediaLightboxProps): react.JSX.Element | null;
|
|
344
346
|
|
|
345
|
-
|
|
347
|
+
type ReadAloudStatus = "idle" | "loading" | "playing" | "paused" | "error";
|
|
348
|
+
type TapVariant = ComponentProps<typeof Volume2TapButton>["variant"];
|
|
349
|
+
type ReadAloudButtonSize = "default" | "compact";
|
|
350
|
+
interface ReadAloudButtonProps {
|
|
351
|
+
status: ReadAloudStatus;
|
|
352
|
+
onPress: () => void;
|
|
353
|
+
/** The host's sentence for the person when `status` is "error". */
|
|
354
|
+
error?: string | null;
|
|
355
|
+
/** Nothing to read (empty text) or the host forbids it right now. */
|
|
356
|
+
disabled?: boolean;
|
|
357
|
+
variant?: TapVariant;
|
|
358
|
+
/**
|
|
359
|
+
* "compact" is the message-footer size: the 32px cell (28px pill) every chat footer control uses,
|
|
360
|
+
* instead of the 38px default box. It sets the geometry only; it wins over `variant`.
|
|
361
|
+
*/
|
|
362
|
+
size?: ReadAloudButtonSize;
|
|
363
|
+
className?: string;
|
|
364
|
+
/** Label for the idle state. Default "Read aloud". */
|
|
365
|
+
label?: string;
|
|
366
|
+
}
|
|
367
|
+
declare function ReadAloudButton({ status, onPress, error, disabled, variant: requestedVariant, size, className, label, }: ReadAloudButtonProps): react.JSX.Element;
|
|
368
|
+
|
|
369
|
+
declare function usePlaybackSnapshot(): PlaybackSnapshot;
|
|
370
|
+
type UseSpeechOptions = Omit<SpeakRequest, "text" | "sample"> & {
|
|
371
|
+
/** Re-adopt the queue's active item when it is speaking exactly this text. */
|
|
372
|
+
adoptText?: string;
|
|
373
|
+
};
|
|
374
|
+
declare function useSpeech({ adoptText, ...request }?: UseSpeechOptions): {
|
|
375
|
+
speak: (text: string) => string;
|
|
376
|
+
status: PlaybackItemStatus | null;
|
|
377
|
+
error: string | null;
|
|
378
|
+
itemId: string | null;
|
|
379
|
+
isMine: boolean;
|
|
380
|
+
pause: typeof pausePlayback;
|
|
381
|
+
resume: typeof resumePlayback;
|
|
382
|
+
skip: typeof skipPlayback;
|
|
383
|
+
remove: typeof removePlaybackItem;
|
|
384
|
+
playItem: typeof playPlaybackItem;
|
|
385
|
+
};
|
|
386
|
+
/** The queue's item status in the read-aloud button's vocabulary. */
|
|
387
|
+
declare function readAloudStatusOf(status: PlaybackItemStatus | null): ReadAloudStatus;
|
|
388
|
+
declare function useReadAloud(text: string, options?: UseSpeechOptions): {
|
|
389
|
+
status: ReadAloudStatus;
|
|
390
|
+
error: string | null;
|
|
391
|
+
onPress: () => void;
|
|
392
|
+
speak: (text: string) => string;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
interface ReadAloudLanguagePickerProps {
|
|
396
|
+
value: string;
|
|
397
|
+
onChange: (code: string) => void;
|
|
398
|
+
className?: string;
|
|
399
|
+
}
|
|
400
|
+
declare function ReadAloudLanguagePicker({ value, onChange, className }: ReadAloudLanguagePickerProps): react.JSX.Element;
|
|
401
|
+
|
|
402
|
+
export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PauseIcon, PenToolIcon, PlayIcon, PlusIcon, ReadAloudButton, type ReadAloudButtonProps, type ReadAloudButtonSize, ReadAloudLanguagePicker, type ReadAloudLanguagePickerProps, type ReadAloudStatus, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, type UseSpeechOptions, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, readAloudStatusOf, useMediaDropzone, usePlaybackSnapshot, useReadAloud, useSpeech };
|
package/dist/react.js
CHANGED
|
@@ -2475,6 +2475,209 @@ function MediaLightbox({
|
|
|
2475
2475
|
}
|
|
2476
2476
|
);
|
|
2477
2477
|
}
|
|
2478
|
+
|
|
2479
|
+
// src/react/read-aloud-button.tsx
|
|
2480
|
+
import { PauseTapButton, TriangleAlertTapButton, Volume2TapButton } from "@ai-matrx/tap-target/buttons";
|
|
2481
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2482
|
+
function ReadAloudButton({
|
|
2483
|
+
status,
|
|
2484
|
+
onPress,
|
|
2485
|
+
error,
|
|
2486
|
+
disabled = false,
|
|
2487
|
+
variant: requestedVariant,
|
|
2488
|
+
size = "default",
|
|
2489
|
+
className,
|
|
2490
|
+
label = "Read aloud"
|
|
2491
|
+
}) {
|
|
2492
|
+
const variant = size === "compact" ? "group" : requestedVariant;
|
|
2493
|
+
if (status === "playing") {
|
|
2494
|
+
return /* @__PURE__ */ jsx9(PauseTapButton, { variant, onClick: onPress, disabled, ariaLabel: "Pause", className });
|
|
2495
|
+
}
|
|
2496
|
+
if (status === "error") {
|
|
2497
|
+
return /* @__PURE__ */ jsx9(
|
|
2498
|
+
TriangleAlertTapButton,
|
|
2499
|
+
{
|
|
2500
|
+
variant,
|
|
2501
|
+
onClick: onPress,
|
|
2502
|
+
disabled,
|
|
2503
|
+
ariaLabel: error || "Read aloud failed. Press to try again.",
|
|
2504
|
+
className
|
|
2505
|
+
}
|
|
2506
|
+
);
|
|
2507
|
+
}
|
|
2508
|
+
const loading = status === "loading";
|
|
2509
|
+
return /* @__PURE__ */ jsx9(
|
|
2510
|
+
Volume2TapButton,
|
|
2511
|
+
{
|
|
2512
|
+
variant,
|
|
2513
|
+
onClick: onPress,
|
|
2514
|
+
disabled: disabled || loading,
|
|
2515
|
+
ariaLabel: loading ? "Connecting\u2026" : status === "paused" ? "Resume" : label,
|
|
2516
|
+
className
|
|
2517
|
+
}
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
// src/react/use-speech.ts
|
|
2522
|
+
import { useCallback as useCallback8, useState as useState10, useSyncExternalStore } from "react";
|
|
2523
|
+
import {
|
|
2524
|
+
getPlaybackSnapshot,
|
|
2525
|
+
pausePlayback,
|
|
2526
|
+
playPlaybackItem,
|
|
2527
|
+
removePlaybackItem,
|
|
2528
|
+
resumePlayback,
|
|
2529
|
+
skipPlayback,
|
|
2530
|
+
subscribePlayback,
|
|
2531
|
+
speak as speakText
|
|
2532
|
+
} from "@ai-matrx/media/speech";
|
|
2533
|
+
var cached = getPlaybackSnapshot();
|
|
2534
|
+
function subscribe(onChange) {
|
|
2535
|
+
return subscribePlayback((snapshot) => {
|
|
2536
|
+
cached = snapshot;
|
|
2537
|
+
onChange();
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
var read = () => cached;
|
|
2541
|
+
function usePlaybackSnapshot() {
|
|
2542
|
+
return useSyncExternalStore(subscribe, read, read);
|
|
2543
|
+
}
|
|
2544
|
+
function useSpeech({ adoptText, ...request } = {}) {
|
|
2545
|
+
const { items, currentId } = usePlaybackSnapshot();
|
|
2546
|
+
const [itemId, setItemId] = useState10(null);
|
|
2547
|
+
const adoptedId = !itemId && adoptText && currentId ? items.find(
|
|
2548
|
+
(i) => i.id === currentId && i.text === adoptText && (i.status === "playing" || i.status === "paused" || i.status === "loading")
|
|
2549
|
+
)?.id ?? null : null;
|
|
2550
|
+
const effectiveId = itemId ?? adoptedId;
|
|
2551
|
+
const { engine, voice, purpose, processMarkdown = true, label, dictionarySurfaceKey, language, speed } = request;
|
|
2552
|
+
const speak = useCallback8(
|
|
2553
|
+
(text) => {
|
|
2554
|
+
const { id } = speakText({
|
|
2555
|
+
text,
|
|
2556
|
+
engine,
|
|
2557
|
+
voice,
|
|
2558
|
+
purpose,
|
|
2559
|
+
processMarkdown,
|
|
2560
|
+
label,
|
|
2561
|
+
dictionarySurfaceKey,
|
|
2562
|
+
language,
|
|
2563
|
+
speed
|
|
2564
|
+
});
|
|
2565
|
+
setItemId(id);
|
|
2566
|
+
return id;
|
|
2567
|
+
},
|
|
2568
|
+
[engine, voice, purpose, processMarkdown, label, dictionarySurfaceKey, language, speed]
|
|
2569
|
+
);
|
|
2570
|
+
const item = effectiveId ? items.find((i) => i.id === effectiveId) : void 0;
|
|
2571
|
+
const status = item?.status ?? null;
|
|
2572
|
+
return {
|
|
2573
|
+
speak,
|
|
2574
|
+
status,
|
|
2575
|
+
error: item?.error ?? null,
|
|
2576
|
+
itemId,
|
|
2577
|
+
isMine: effectiveId !== null && effectiveId === currentId,
|
|
2578
|
+
pause: pausePlayback,
|
|
2579
|
+
resume: resumePlayback,
|
|
2580
|
+
skip: skipPlayback,
|
|
2581
|
+
remove: removePlaybackItem,
|
|
2582
|
+
playItem: playPlaybackItem
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
function readAloudStatusOf(status) {
|
|
2586
|
+
if (status === "playing") return "playing";
|
|
2587
|
+
if (status === "paused") return "paused";
|
|
2588
|
+
if (status === "loading" || status === "queued") return "loading";
|
|
2589
|
+
if (status === "error") return "error";
|
|
2590
|
+
return "idle";
|
|
2591
|
+
}
|
|
2592
|
+
function useReadAloud(text, options = {}) {
|
|
2593
|
+
const { speak, status, error, pause, resume } = useSpeech({ adoptText: text, ...options });
|
|
2594
|
+
const state = readAloudStatusOf(status);
|
|
2595
|
+
const onPress = useCallback8(() => {
|
|
2596
|
+
if (state === "loading") return;
|
|
2597
|
+
if (state === "playing") void pause();
|
|
2598
|
+
else if (state === "paused") void resume();
|
|
2599
|
+
else if (text.trim()) speak(text);
|
|
2600
|
+
}, [state, text, speak, pause, resume]);
|
|
2601
|
+
return { status: state, error, onPress, speak };
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
// src/react/read-aloud-language-picker.tsx
|
|
2605
|
+
import { Button, Popover, PopoverContent, PopoverTrigger } from "@ai-matrx/design-system";
|
|
2606
|
+
import { Check, Languages } from "lucide-react";
|
|
2607
|
+
import { useState as useState11 } from "react";
|
|
2608
|
+
|
|
2609
|
+
// src/voices/read-aloud-languages.ts
|
|
2610
|
+
var READ_ALOUD_LANGUAGES = [
|
|
2611
|
+
{ code: "en", englishName: "English", nativeName: "English" },
|
|
2612
|
+
{ code: "es", englishName: "Spanish", nativeName: "Espa\xF1ol" },
|
|
2613
|
+
{ code: "fr", englishName: "French", nativeName: "Fran\xE7ais" },
|
|
2614
|
+
{ code: "de", englishName: "German", nativeName: "Deutsch" },
|
|
2615
|
+
{ code: "it", englishName: "Italian", nativeName: "Italiano" },
|
|
2616
|
+
{ code: "pt", englishName: "Portuguese", nativeName: "Portugu\xEAs" },
|
|
2617
|
+
{ code: "zh", englishName: "Chinese", nativeName: "\u4E2D\u6587" },
|
|
2618
|
+
{ code: "ja", englishName: "Japanese", nativeName: "\u65E5\u672C\u8A9E" },
|
|
2619
|
+
{ code: "ko", englishName: "Korean", nativeName: "\uD55C\uAD6D\uC5B4" },
|
|
2620
|
+
{ code: "ru", englishName: "Russian", nativeName: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
|
|
2621
|
+
{ code: "hi", englishName: "Hindi", nativeName: "\u0939\u093F\u0928\u094D\u0926\u0940" },
|
|
2622
|
+
{ code: "nl", englishName: "Dutch", nativeName: "Nederlands" },
|
|
2623
|
+
{ code: "pl", englishName: "Polish", nativeName: "Polski" },
|
|
2624
|
+
{ code: "sv", englishName: "Swedish", nativeName: "Svenska" },
|
|
2625
|
+
{ code: "tr", englishName: "Turkish", nativeName: "T\xFCrk\xE7e" },
|
|
2626
|
+
{ code: "fa", englishName: "Persian", nativeName: "\u0641\u0627\u0631\u0633\u06CC" }
|
|
2627
|
+
];
|
|
2628
|
+
function readAloudLanguage(code) {
|
|
2629
|
+
return READ_ALOUD_LANGUAGES.find((l) => l.code === code) ?? READ_ALOUD_LANGUAGES[0];
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
// src/react/read-aloud-language-picker.tsx
|
|
2633
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2634
|
+
function ReadAloudLanguagePicker({ value, onChange, className }) {
|
|
2635
|
+
const [open, setOpen] = useState11(false);
|
|
2636
|
+
const selected = readAloudLanguage(value);
|
|
2637
|
+
return /* @__PURE__ */ jsxs7(Popover, { open, onOpenChange: setOpen, children: [
|
|
2638
|
+
/* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
|
|
2639
|
+
Button,
|
|
2640
|
+
{
|
|
2641
|
+
variant: "ghost",
|
|
2642
|
+
size: "icon",
|
|
2643
|
+
className: cn("size-7 text-muted-foreground", className),
|
|
2644
|
+
title: `Voice language: ${selected.englishName}`,
|
|
2645
|
+
"aria-label": `Voice language, currently ${selected.englishName}`,
|
|
2646
|
+
children: /* @__PURE__ */ jsx10(Languages, { className: "size-3.5" })
|
|
2647
|
+
}
|
|
2648
|
+
) }),
|
|
2649
|
+
/* @__PURE__ */ jsxs7(PopoverContent, { className: "max-h-80 w-56 overflow-y-auto p-1", align: "end", children: [
|
|
2650
|
+
/* @__PURE__ */ jsx10("div", { className: "px-2.5 py-1.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Voice language" }),
|
|
2651
|
+
READ_ALOUD_LANGUAGES.map((option) => {
|
|
2652
|
+
const isSelected = option.code === selected.code;
|
|
2653
|
+
return /* @__PURE__ */ jsxs7(
|
|
2654
|
+
"button",
|
|
2655
|
+
{
|
|
2656
|
+
type: "button",
|
|
2657
|
+
role: "menuitemradio",
|
|
2658
|
+
"aria-checked": isSelected,
|
|
2659
|
+
onClick: () => {
|
|
2660
|
+
onChange(option.code);
|
|
2661
|
+
setOpen(false);
|
|
2662
|
+
},
|
|
2663
|
+
className: cn(
|
|
2664
|
+
"flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left transition-colors hover:bg-accent",
|
|
2665
|
+
isSelected && "bg-accent"
|
|
2666
|
+
),
|
|
2667
|
+
children: [
|
|
2668
|
+
/* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1", children: [
|
|
2669
|
+
/* @__PURE__ */ jsx10("span", { className: "block text-sm", children: option.nativeName }),
|
|
2670
|
+
option.nativeName !== option.englishName ? /* @__PURE__ */ jsx10("span", { className: "block text-[11px] text-muted-foreground", children: option.englishName }) : null
|
|
2671
|
+
] }),
|
|
2672
|
+
isSelected ? /* @__PURE__ */ jsx10(Check, { className: "size-3.5 shrink-0 text-primary" }) : null
|
|
2673
|
+
]
|
|
2674
|
+
},
|
|
2675
|
+
option.code
|
|
2676
|
+
);
|
|
2677
|
+
})
|
|
2678
|
+
] })
|
|
2679
|
+
] });
|
|
2680
|
+
}
|
|
2478
2681
|
export {
|
|
2479
2682
|
AlertCircleIcon,
|
|
2480
2683
|
ArchiveIcon,
|
|
@@ -2511,6 +2714,8 @@ export {
|
|
|
2511
2714
|
PenToolIcon,
|
|
2512
2715
|
PlayIcon,
|
|
2513
2716
|
PlusIcon,
|
|
2717
|
+
ReadAloudButton,
|
|
2718
|
+
ReadAloudLanguagePicker,
|
|
2514
2719
|
SearchIcon,
|
|
2515
2720
|
Settings2Icon,
|
|
2516
2721
|
Share2Icon,
|
|
@@ -2523,6 +2728,10 @@ export {
|
|
|
2523
2728
|
VolumeXIcon,
|
|
2524
2729
|
XIcon,
|
|
2525
2730
|
cn,
|
|
2526
|
-
|
|
2731
|
+
readAloudStatusOf,
|
|
2732
|
+
useMediaDropzone,
|
|
2733
|
+
usePlaybackSnapshot,
|
|
2734
|
+
useReadAloud,
|
|
2735
|
+
useSpeech
|
|
2527
2736
|
};
|
|
2528
2737
|
//# sourceMappingURL=react.js.map
|