@ai-matrx/media 0.14.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/dist/react.d.cts CHANGED
@@ -3,6 +3,7 @@ import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNod
3
3
  import { MediaRefLike, DurableSrc, MediaUploadOptions, MediaActionKind } from './index.cjs';
4
4
  import { M as MediaUploadEntry } from './use-media-upload-v5Uq9Ruq.cjs';
5
5
  import { Volume2TapButton } from '@ai-matrx/tap-target/buttons';
6
+ import { SpeakRequest, PlaybackItemStatus, PlaybackSnapshot, pausePlayback, resumePlayback, skipPlayback, removePlaybackItem, playPlaybackItem } from '@ai-matrx/media/speech';
6
7
 
7
8
  /**
8
9
  * src/react/cn.ts — conditional class join with last-wins Tailwind conflict
@@ -345,6 +346,7 @@ declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, aut
345
346
 
346
347
  type ReadAloudStatus = "idle" | "loading" | "playing" | "paused" | "error";
347
348
  type TapVariant = ComponentProps<typeof Volume2TapButton>["variant"];
349
+ type ReadAloudButtonSize = "default" | "compact";
348
350
  interface ReadAloudButtonProps {
349
351
  status: ReadAloudStatus;
350
352
  onPress: () => void;
@@ -353,11 +355,42 @@ interface ReadAloudButtonProps {
353
355
  /** Nothing to read (empty text) or the host forbids it right now. */
354
356
  disabled?: boolean;
355
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;
356
363
  className?: string;
357
364
  /** Label for the idle state. Default "Read aloud". */
358
365
  label?: string;
359
366
  }
360
- declare function ReadAloudButton({ status, onPress, error, disabled, variant, className, label, }: ReadAloudButtonProps): react.JSX.Element;
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
+ };
361
394
 
362
395
  interface ReadAloudLanguagePickerProps {
363
396
  value: string;
@@ -366,4 +399,4 @@ interface ReadAloudLanguagePickerProps {
366
399
  }
367
400
  declare function ReadAloudLanguagePicker({ value, onChange, className }: ReadAloudLanguagePickerProps): react.JSX.Element;
368
401
 
369
- 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, ReadAloudLanguagePicker, type ReadAloudLanguagePickerProps, type ReadAloudStatus, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, useMediaDropzone };
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
@@ -3,6 +3,7 @@ import react__default, { SVGProps, HTMLAttributes, InputHTMLAttributes, ReactNod
3
3
  import { MediaRefLike, DurableSrc, MediaUploadOptions, MediaActionKind } from './index.js';
4
4
  import { M as MediaUploadEntry } from './use-media-upload-Dg2HSvTq.js';
5
5
  import { Volume2TapButton } from '@ai-matrx/tap-target/buttons';
6
+ import { SpeakRequest, PlaybackItemStatus, PlaybackSnapshot, pausePlayback, resumePlayback, skipPlayback, removePlaybackItem, playPlaybackItem } from '@ai-matrx/media/speech';
6
7
 
7
8
  /**
8
9
  * src/react/cn.ts — conditional class join with last-wins Tailwind conflict
@@ -345,6 +346,7 @@ declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, aut
345
346
 
346
347
  type ReadAloudStatus = "idle" | "loading" | "playing" | "paused" | "error";
347
348
  type TapVariant = ComponentProps<typeof Volume2TapButton>["variant"];
349
+ type ReadAloudButtonSize = "default" | "compact";
348
350
  interface ReadAloudButtonProps {
349
351
  status: ReadAloudStatus;
350
352
  onPress: () => void;
@@ -353,11 +355,42 @@ interface ReadAloudButtonProps {
353
355
  /** Nothing to read (empty text) or the host forbids it right now. */
354
356
  disabled?: boolean;
355
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;
356
363
  className?: string;
357
364
  /** Label for the idle state. Default "Read aloud". */
358
365
  label?: string;
359
366
  }
360
- declare function ReadAloudButton({ status, onPress, error, disabled, variant, className, label, }: ReadAloudButtonProps): react.JSX.Element;
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
+ };
361
394
 
362
395
  interface ReadAloudLanguagePickerProps {
363
396
  value: string;
@@ -366,4 +399,4 @@ interface ReadAloudLanguagePickerProps {
366
399
  }
367
400
  declare function ReadAloudLanguagePicker({ value, onChange, className }: ReadAloudLanguagePickerProps): react.JSX.Element;
368
401
 
369
- 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, ReadAloudLanguagePicker, type ReadAloudLanguagePickerProps, type ReadAloudStatus, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, useMediaDropzone };
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
@@ -2484,10 +2484,12 @@ function ReadAloudButton({
2484
2484
  onPress,
2485
2485
  error,
2486
2486
  disabled = false,
2487
- variant,
2487
+ variant: requestedVariant,
2488
+ size = "default",
2488
2489
  className,
2489
2490
  label = "Read aloud"
2490
2491
  }) {
2492
+ const variant = size === "compact" ? "group" : requestedVariant;
2491
2493
  if (status === "playing") {
2492
2494
  return /* @__PURE__ */ jsx9(PauseTapButton, { variant, onClick: onPress, disabled, ariaLabel: "Pause", className });
2493
2495
  }
@@ -2516,10 +2518,93 @@ function ReadAloudButton({
2516
2518
  );
2517
2519
  }
2518
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
+
2519
2604
  // src/react/read-aloud-language-picker.tsx
2520
2605
  import { Button, Popover, PopoverContent, PopoverTrigger } from "@ai-matrx/design-system";
2521
2606
  import { Check, Languages } from "lucide-react";
2522
- import { useState as useState10 } from "react";
2607
+ import { useState as useState11 } from "react";
2523
2608
 
2524
2609
  // src/voices/read-aloud-languages.ts
2525
2610
  var READ_ALOUD_LANGUAGES = [
@@ -2547,7 +2632,7 @@ function readAloudLanguage(code) {
2547
2632
  // src/react/read-aloud-language-picker.tsx
2548
2633
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2549
2634
  function ReadAloudLanguagePicker({ value, onChange, className }) {
2550
- const [open, setOpen] = useState10(false);
2635
+ const [open, setOpen] = useState11(false);
2551
2636
  const selected = readAloudLanguage(value);
2552
2637
  return /* @__PURE__ */ jsxs7(Popover, { open, onOpenChange: setOpen, children: [
2553
2638
  /* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
@@ -2643,6 +2728,10 @@ export {
2643
2728
  VolumeXIcon,
2644
2729
  XIcon,
2645
2730
  cn,
2646
- useMediaDropzone
2731
+ readAloudStatusOf,
2732
+ useMediaDropzone,
2733
+ usePlaybackSnapshot,
2734
+ useReadAloud,
2735
+ useSpeech
2647
2736
  };
2648
2737
  //# sourceMappingURL=react.js.map