@ai-matrx/media 0.13.0 → 0.14.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-matrx/media
2
2
 
3
+ ## 0.14.0 — Read-aloud lives here (speech text, languages, button, language picker)
4
+
5
+ - `@ai-matrx/media/voices`: `parseMarkdownToText` (+ `normalizeSpeechAbbreviations`,
6
+ `normalizeSpeechBlanks`, `COMMON_ABBREVIATION_EXPANSIONS`, `SpeechPronunciation`) — THE one
7
+ markdown → spoken-text conversion (tables read as a sentence, initialisms letter by letter,
8
+ pronunciations), moved from matrx-frontend; `READ_ALOUD_LANGUAGES` / `readAloudLanguage` — the
9
+ website's 15 voice languages plus the extension's Persian, each with its own name.
10
+ - `@ai-matrx/media/react`: `ReadAloudButton` (one look for idle / connecting / playing / paused /
11
+ failed; the host's engine reports `status` and handles `onPress`) and
12
+ `ReadAloudLanguagePicker` (controlled quick picker).
13
+ - New sibling deps: `@ai-matrx/content-ir` (table + code-span rules), `@ai-matrx/tap-target`.
14
+ - Consumer action: replace an app copy of the speech parser / speaker button / language picker
15
+ with these; render `ReadAloudButton` inside a `TooltipProvider` (every app shell has one).
16
+
3
17
 
4
18
 
5
19
  ## 0.13.0 — DB types come from @ai-matrx/data/types
package/dist/react.cjs CHANGED
@@ -56,6 +56,8 @@ __export(react_exports, {
56
56
  PenToolIcon: () => PenToolIcon,
57
57
  PlayIcon: () => PlayIcon,
58
58
  PlusIcon: () => PlusIcon,
59
+ ReadAloudButton: () => ReadAloudButton,
60
+ ReadAloudLanguagePicker: () => ReadAloudLanguagePicker,
59
61
  SearchIcon: () => SearchIcon,
60
62
  Settings2Icon: () => Settings2Icon,
61
63
  Share2Icon: () => Share2Icon,
@@ -2531,4 +2533,122 @@ function MediaLightbox({
2531
2533
  }
2532
2534
  );
2533
2535
  }
2536
+
2537
+ // src/react/read-aloud-button.tsx
2538
+ var import_buttons = require("@ai-matrx/tap-target/buttons");
2539
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2540
+ function ReadAloudButton({
2541
+ status,
2542
+ onPress,
2543
+ error,
2544
+ disabled = false,
2545
+ variant,
2546
+ className,
2547
+ label = "Read aloud"
2548
+ }) {
2549
+ if (status === "playing") {
2550
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_buttons.PauseTapButton, { variant, onClick: onPress, disabled, ariaLabel: "Pause", className });
2551
+ }
2552
+ if (status === "error") {
2553
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2554
+ import_buttons.TriangleAlertTapButton,
2555
+ {
2556
+ variant,
2557
+ onClick: onPress,
2558
+ disabled,
2559
+ ariaLabel: error || "Read aloud failed. Press to try again.",
2560
+ className
2561
+ }
2562
+ );
2563
+ }
2564
+ const loading = status === "loading";
2565
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2566
+ import_buttons.Volume2TapButton,
2567
+ {
2568
+ variant,
2569
+ onClick: onPress,
2570
+ disabled: disabled || loading,
2571
+ ariaLabel: loading ? "Connecting\u2026" : status === "paused" ? "Resume" : label,
2572
+ className
2573
+ }
2574
+ );
2575
+ }
2576
+
2577
+ // src/react/read-aloud-language-picker.tsx
2578
+ var import_design_system = require("@ai-matrx/design-system");
2579
+ var import_lucide_react = require("lucide-react");
2580
+ var import_react17 = require("react");
2581
+
2582
+ // src/voices/read-aloud-languages.ts
2583
+ var READ_ALOUD_LANGUAGES = [
2584
+ { code: "en", englishName: "English", nativeName: "English" },
2585
+ { code: "es", englishName: "Spanish", nativeName: "Espa\xF1ol" },
2586
+ { code: "fr", englishName: "French", nativeName: "Fran\xE7ais" },
2587
+ { code: "de", englishName: "German", nativeName: "Deutsch" },
2588
+ { code: "it", englishName: "Italian", nativeName: "Italiano" },
2589
+ { code: "pt", englishName: "Portuguese", nativeName: "Portugu\xEAs" },
2590
+ { code: "zh", englishName: "Chinese", nativeName: "\u4E2D\u6587" },
2591
+ { code: "ja", englishName: "Japanese", nativeName: "\u65E5\u672C\u8A9E" },
2592
+ { code: "ko", englishName: "Korean", nativeName: "\uD55C\uAD6D\uC5B4" },
2593
+ { code: "ru", englishName: "Russian", nativeName: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
2594
+ { code: "hi", englishName: "Hindi", nativeName: "\u0939\u093F\u0928\u094D\u0926\u0940" },
2595
+ { code: "nl", englishName: "Dutch", nativeName: "Nederlands" },
2596
+ { code: "pl", englishName: "Polish", nativeName: "Polski" },
2597
+ { code: "sv", englishName: "Swedish", nativeName: "Svenska" },
2598
+ { code: "tr", englishName: "Turkish", nativeName: "T\xFCrk\xE7e" },
2599
+ { code: "fa", englishName: "Persian", nativeName: "\u0641\u0627\u0631\u0633\u06CC" }
2600
+ ];
2601
+ function readAloudLanguage(code) {
2602
+ return READ_ALOUD_LANGUAGES.find((l) => l.code === code) ?? READ_ALOUD_LANGUAGES[0];
2603
+ }
2604
+
2605
+ // src/react/read-aloud-language-picker.tsx
2606
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2607
+ function ReadAloudLanguagePicker({ value, onChange, className }) {
2608
+ const [open, setOpen] = (0, import_react17.useState)(false);
2609
+ const selected = readAloudLanguage(value);
2610
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system.Popover, { open, onOpenChange: setOpen, children: [
2611
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2612
+ import_design_system.Button,
2613
+ {
2614
+ variant: "ghost",
2615
+ size: "icon",
2616
+ className: cn("size-7 text-muted-foreground", className),
2617
+ title: `Voice language: ${selected.englishName}`,
2618
+ "aria-label": `Voice language, currently ${selected.englishName}`,
2619
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Languages, { className: "size-3.5" })
2620
+ }
2621
+ ) }),
2622
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system.PopoverContent, { className: "max-h-80 w-56 overflow-y-auto p-1", align: "end", children: [
2623
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "px-2.5 py-1.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Voice language" }),
2624
+ READ_ALOUD_LANGUAGES.map((option) => {
2625
+ const isSelected = option.code === selected.code;
2626
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2627
+ "button",
2628
+ {
2629
+ type: "button",
2630
+ role: "menuitemradio",
2631
+ "aria-checked": isSelected,
2632
+ onClick: () => {
2633
+ onChange(option.code);
2634
+ setOpen(false);
2635
+ },
2636
+ className: cn(
2637
+ "flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left transition-colors hover:bg-accent",
2638
+ isSelected && "bg-accent"
2639
+ ),
2640
+ children: [
2641
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "min-w-0 flex-1", children: [
2642
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "block text-sm", children: option.nativeName }),
2643
+ option.nativeName !== option.englishName ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "block text-[11px] text-muted-foreground", children: option.englishName }) : null
2644
+ ] }),
2645
+ isSelected ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react.Check, { className: "size-3.5 shrink-0 text-primary" }) : null
2646
+ ]
2647
+ },
2648
+ option.code
2649
+ );
2650
+ })
2651
+ ] })
2652
+ ] });
2653
+ }
2534
2654
  //# sourceMappingURL=react.cjs.map