@bmsuisse/ui 0.15.0 → 0.16.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/index.js CHANGED
@@ -29,7 +29,16 @@ var buttonVariants = cva(
29
29
  // Tailwind theme (this package doesn't define them) — same pattern as AlertBox's
30
30
  // warning/info/success colors, which assume tokens the shared theme doesn't have.
31
31
  "swiss-primary": "bg-swiss-primary text-swiss-primary-foreground font-semibold shadow-sm hover:bg-swiss-primary-hover hover:-translate-y-px active:translate-y-px",
32
- "swiss-secondary": "border border-input bg-background font-semibold text-foreground shadow-sm hover:bg-accent hover:-translate-y-px active:translate-y-px"
32
+ "swiss-secondary": "border border-input bg-background font-semibold text-foreground shadow-sm hover:bg-accent hover:-translate-y-px active:translate-y-px",
33
+ // Violet "AI action" accent, in the same three weights as default/outline/ghost.
34
+ // Fixed Tailwind palette rather than a token — same choice as Badge's `warning`
35
+ // and AlertBox/StatusBadge's warning/info/success tones: the shared theme has no
36
+ // `ai` color, and unlike `swiss-primary` these should work in a consuming app with
37
+ // zero setup. Tints go through `violet-500/N` (not per-mode `violet-50`/`-950`
38
+ // shades) so one class list reads correctly on both light and dark surfaces.
39
+ ai: "bg-violet-600 text-white shadow-sm hover:bg-violet-600/90",
40
+ "ai-subtle": "border border-violet-500/30 bg-violet-500/10 text-violet-700 hover:bg-violet-500/20 dark:text-violet-300",
41
+ "ai-ghost": "text-violet-700 hover:bg-violet-500/10 dark:text-violet-300"
33
42
  },
34
43
  size: {
35
44
  default: "h-9 px-3",
@@ -2187,8 +2196,10 @@ var KpiCard = memo(function KpiCard2({
2187
2196
  progressLabel,
2188
2197
  segments,
2189
2198
  centerValue,
2199
+ background,
2190
2200
  testId
2191
2201
  }) {
2202
+ const bgStyle = background ? { background } : void 0;
2192
2203
  if (variant === "hero") {
2193
2204
  return /* @__PURE__ */ jsxs18(
2194
2205
  "div",
@@ -2196,8 +2207,8 @@ var KpiCard = memo(function KpiCard2({
2196
2207
  "data-testid": testId,
2197
2208
  className: "relative col-span-2 flex min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl p-4 md:p-3 lg:col-span-1 lg:max-w-[340px] lg:min-w-[260px]",
2198
2209
  style: {
2199
- background: "var(--color-primary)",
2200
- boxShadow: "0 2px 10px color-mix(in oklch, var(--color-primary) 18%, transparent)"
2210
+ background: background ?? "var(--color-primary)",
2211
+ boxShadow: `0 2px 10px color-mix(in oklch, ${background ?? "var(--color-primary)"} 18%, transparent)`
2201
2212
  },
2202
2213
  children: [
2203
2214
  /* @__PURE__ */ jsxs18("div", { className: "relative flex items-start justify-between", children: [
@@ -2241,6 +2252,7 @@ var KpiCard = memo(function KpiCard2({
2241
2252
  "div",
2242
2253
  {
2243
2254
  "data-testid": testId,
2255
+ style: bgStyle,
2244
2256
  className: cn(
2245
2257
  "flex h-full min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl border border-border bg-card p-3 transition-shadow md:p-2.5",
2246
2258
  href && "hover:border-primary/20 hover:shadow-md"
@@ -2282,7 +2294,7 @@ var KpiCard = memo(function KpiCard2({
2282
2294
  }
2283
2295
  if (variant === "donut") {
2284
2296
  const total = segments?.reduce((sum, s) => sum + s.value, 0) ?? 0;
2285
- return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
2297
+ return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
2286
2298
  /* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
2287
2299
  /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2288
2300
  Icon2 && /* @__PURE__ */ jsx35("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
@@ -2319,7 +2331,7 @@ var KpiCard = memo(function KpiCard2({
2319
2331
  const prefix = isSuffix ? null : parts?.[0];
2320
2332
  const number = isSuffix ? parts[0] : parts?.[1] ?? parts?.[0];
2321
2333
  const suffix = isSuffix ? parts[1] : null;
2322
- return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
2334
+ return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
2323
2335
  /* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
2324
2336
  /* @__PURE__ */ jsx35("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
2325
2337
  Icon2 && /* @__PURE__ */ jsx35("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx35(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
@@ -2700,10 +2712,326 @@ function SidebarNav({ children, className, ...rest }) {
2700
2712
  ] });
2701
2713
  }
2702
2714
 
2715
+ // src/patterns/ai/AiButton.tsx
2716
+ import { Sparkles } from "lucide-react";
2717
+ import { forwardRef as forwardRef17 } from "react";
2718
+ import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
2719
+ var AiButton = forwardRef17(
2720
+ ({ variant = "ai-subtle", loading = false, icon: Icon2 = Sparkles, disabled, children, ...props }, ref) => /* @__PURE__ */ jsxs23(
2721
+ Button,
2722
+ {
2723
+ ref,
2724
+ variant,
2725
+ disabled: disabled || loading,
2726
+ "aria-busy": loading || void 0,
2727
+ ...props,
2728
+ children: [
2729
+ loading ? /* @__PURE__ */ jsx41(LoadingSpinner, { size: "sm", className: "[&_svg]:text-current" }) : /* @__PURE__ */ jsx41(Icon2, { className: "h-4 w-4", "aria-hidden": "true" }),
2730
+ children
2731
+ ]
2732
+ }
2733
+ )
2734
+ );
2735
+ AiButton.displayName = "AiButton";
2736
+
2737
+ // src/patterns/ai/AiExplainButton.tsx
2738
+ import { RefreshCw, Sparkles as Sparkles2 } from "lucide-react";
2739
+ import { useState as useState12 } from "react";
2740
+ import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
2741
+ function AiExplainButton({
2742
+ onExplain,
2743
+ label = "Explain",
2744
+ title,
2745
+ align = "start",
2746
+ contentClassName = "w-80",
2747
+ size = "sm",
2748
+ ...props
2749
+ }) {
2750
+ const [open, setOpen] = useState12(false);
2751
+ const [loading, setLoading] = useState12(false);
2752
+ const [content, setContent] = useState12(null);
2753
+ const [error, setError] = useState12(null);
2754
+ const load = async () => {
2755
+ setLoading(true);
2756
+ setError(null);
2757
+ try {
2758
+ setContent(await onExplain());
2759
+ } catch (err) {
2760
+ setError(err instanceof Error ? err.message : String(err));
2761
+ } finally {
2762
+ setLoading(false);
2763
+ }
2764
+ };
2765
+ const handleOpenChange = (next) => {
2766
+ setOpen(next);
2767
+ if (next && content === null && error === null && !loading) void load();
2768
+ };
2769
+ return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: handleOpenChange, children: [
2770
+ /* @__PURE__ */ jsx42(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx42(AiButton, { size, ...props, children: label }) }),
2771
+ /* @__PURE__ */ jsxs24(PopoverContent, { align, className: contentClassName, children: [
2772
+ /* @__PURE__ */ jsxs24("p", { className: "mb-2 flex items-center gap-1.5 text-sm font-semibold", children: [
2773
+ /* @__PURE__ */ jsx42(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
2774
+ title ?? label
2775
+ ] }),
2776
+ loading ? /* @__PURE__ */ jsx42(LoadingSpinner, { size: "sm", label: "Thinking\u2026", className: "text-sm text-muted-foreground" }) : error ? /* @__PURE__ */ jsxs24("div", { className: "flex flex-col items-start gap-2", children: [
2777
+ /* @__PURE__ */ jsx42("div", { role: "alert", className: "w-full", children: /* @__PURE__ */ jsx42(AlertBox, { variant: "error", children: error }) }),
2778
+ /* @__PURE__ */ jsxs24(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void load(), children: [
2779
+ /* @__PURE__ */ jsx42(RefreshCw, { className: "h-4 w-4", "aria-hidden": "true" }),
2780
+ "Try again"
2781
+ ] })
2782
+ ] }) : /* @__PURE__ */ jsx42("div", { className: "max-h-80 overflow-y-auto text-sm leading-relaxed", children: content })
2783
+ ] })
2784
+ ] });
2785
+ }
2786
+
2787
+ // src/patterns/ai/VoiceInputButton.tsx
2788
+ import { Mic, MicOff } from "lucide-react";
2789
+ import { forwardRef as forwardRef18 } from "react";
2790
+
2791
+ // src/patterns/ai/useSpeechRecognition.ts
2792
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6, useState as useState13 } from "react";
2793
+ function getCtor() {
2794
+ if (typeof window === "undefined") return void 0;
2795
+ const w = window;
2796
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition;
2797
+ }
2798
+ function browserEngine() {
2799
+ const Ctor = getCtor();
2800
+ return Ctor ? new Ctor() : null;
2801
+ }
2802
+ function describeSpeechError(code) {
2803
+ switch (code) {
2804
+ case "aborted":
2805
+ return null;
2806
+ case "not-allowed":
2807
+ case "service-not-allowed":
2808
+ return "Microphone access is blocked. Allow it in the browser's site settings and try again.";
2809
+ case "audio-capture":
2810
+ return "No microphone was found.";
2811
+ case "no-speech":
2812
+ return "No speech was detected.";
2813
+ case "network":
2814
+ return "Speech recognition needs a network connection.";
2815
+ default:
2816
+ return `Speech recognition failed (${code}).`;
2817
+ }
2818
+ }
2819
+ function useSpeechRecognition({
2820
+ lang,
2821
+ onResult,
2822
+ onError,
2823
+ engine
2824
+ } = {}) {
2825
+ const [listening, setListening] = useState13(false);
2826
+ const [interim, setInterim] = useState13("");
2827
+ const [error, setError] = useState13(null);
2828
+ const recognitionRef = useRef6(null);
2829
+ const onResultRef = useRef6(onResult);
2830
+ const onErrorRef = useRef6(onError);
2831
+ const engineRef = useRef6(engine);
2832
+ onResultRef.current = onResult;
2833
+ onErrorRef.current = onError;
2834
+ engineRef.current = engine;
2835
+ const supported = engine !== void 0 || getCtor() !== void 0;
2836
+ const stop = useCallback2(() => {
2837
+ recognitionRef.current?.stop();
2838
+ }, []);
2839
+ const start = useCallback2(() => {
2840
+ if (recognitionRef.current) return;
2841
+ const recognition = engineRef.current ? engineRef.current() : browserEngine();
2842
+ if (!recognition) return;
2843
+ recognition.lang = lang ?? (typeof document !== "undefined" ? document.documentElement.lang : "") ?? "";
2844
+ if (!recognition.lang) recognition.lang = "en-US";
2845
+ recognition.continuous = true;
2846
+ recognition.interimResults = true;
2847
+ recognition.onresult = (event) => {
2848
+ let pending = "";
2849
+ for (let i = event.resultIndex; i < event.results.length; i += 1) {
2850
+ const result = event.results[i];
2851
+ if (!result) continue;
2852
+ if (result.isFinal) onResultRef.current?.(result[0].transcript.trim());
2853
+ else pending += result[0].transcript;
2854
+ }
2855
+ setInterim(pending);
2856
+ };
2857
+ recognition.onerror = (event) => {
2858
+ setError(event.error);
2859
+ onErrorRef.current?.(event.error);
2860
+ };
2861
+ recognition.onend = () => {
2862
+ recognitionRef.current = null;
2863
+ setListening(false);
2864
+ setInterim("");
2865
+ };
2866
+ recognitionRef.current = recognition;
2867
+ setError(null);
2868
+ setInterim("");
2869
+ setListening(true);
2870
+ recognition.start();
2871
+ }, [lang]);
2872
+ const toggle = useCallback2(() => {
2873
+ if (recognitionRef.current) stop();
2874
+ else start();
2875
+ }, [start, stop]);
2876
+ useEffect6(() => () => recognitionRef.current?.abort(), []);
2877
+ return { supported, listening, interim, error, start, stop, toggle };
2878
+ }
2879
+
2880
+ // src/patterns/ai/VoiceInputButton.tsx
2881
+ import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
2882
+ var VoiceInputButton = forwardRef18(
2883
+ ({
2884
+ onTranscript,
2885
+ onError,
2886
+ lang,
2887
+ engine,
2888
+ label,
2889
+ startLabel = "Start dictation",
2890
+ stopLabel = "Stop dictation",
2891
+ showInterim = false,
2892
+ size,
2893
+ disabled,
2894
+ className,
2895
+ ...props
2896
+ }, ref) => {
2897
+ const { supported, listening, interim, toggle } = useSpeechRecognition({
2898
+ lang,
2899
+ onResult: onTranscript,
2900
+ onError,
2901
+ engine
2902
+ });
2903
+ const unsupportedTitle = "Speech recognition isn't available in this browser";
2904
+ return (
2905
+ // `title` lives on the wrapper, not the button: buttonVariants sets
2906
+ // `disabled:pointer-events-none`, so a disabled button never shows its own.
2907
+ /* @__PURE__ */ jsxs25("span", { className: "inline-flex min-w-0 items-center gap-2", title: supported ? void 0 : unsupportedTitle, children: [
2908
+ /* @__PURE__ */ jsxs25(
2909
+ Button,
2910
+ {
2911
+ ref,
2912
+ type: "button",
2913
+ variant: "outline",
2914
+ size: size ?? (label ? "default" : "icon"),
2915
+ onClick: toggle,
2916
+ disabled: disabled || !supported,
2917
+ "aria-label": label ? void 0 : supported ? listening ? stopLabel : startLabel : unsupportedTitle,
2918
+ "aria-pressed": supported ? listening : void 0,
2919
+ className: cn(
2920
+ listening && "border-destructive bg-destructive/10 text-red-800 hover:bg-destructive/15 hover:text-red-800 dark:text-red-300 dark:hover:text-red-300",
2921
+ className
2922
+ ),
2923
+ ...props,
2924
+ children: [
2925
+ supported ? /* @__PURE__ */ jsx43(Mic, { className: cn("h-4 w-4", listening && "animate-pulse motion-reduce:animate-none"), "aria-hidden": "true" }) : /* @__PURE__ */ jsx43(MicOff, { className: "h-4 w-4", "aria-hidden": "true" }),
2926
+ label
2927
+ ]
2928
+ }
2929
+ ),
2930
+ showInterim && interim ? /* @__PURE__ */ jsx43("span", { "aria-live": "polite", className: "truncate text-sm text-muted-foreground italic", children: interim }) : null
2931
+ ] })
2932
+ );
2933
+ }
2934
+ );
2935
+ VoiceInputButton.displayName = "VoiceInputButton";
2936
+
2937
+ // src/patterns/ai/VoiceTranscript.tsx
2938
+ import { Undo2, Wand2 } from "lucide-react";
2939
+ import { useState as useState14 } from "react";
2940
+ import { jsx as jsx44, jsxs as jsxs26 } from "react/jsx-runtime";
2941
+ function VoiceTranscript({
2942
+ value,
2943
+ onChange,
2944
+ onTransform,
2945
+ transformLabel = "Transform with AI",
2946
+ placeholder = "Speak or type\u2026",
2947
+ lang,
2948
+ engine,
2949
+ rows = 4,
2950
+ disabled,
2951
+ className,
2952
+ ...props
2953
+ }) {
2954
+ const [loading, setLoading] = useState14(false);
2955
+ const [error, setError] = useState14(null);
2956
+ const [previous, setPrevious] = useState14(null);
2957
+ const append = (chunk) => {
2958
+ onChange(value ? `${value.trimEnd()} ${chunk}` : chunk);
2959
+ };
2960
+ const transform = async () => {
2961
+ if (!onTransform) return;
2962
+ const before = value;
2963
+ setLoading(true);
2964
+ setError(null);
2965
+ try {
2966
+ const next = await onTransform(before);
2967
+ setPrevious(before);
2968
+ onChange(next);
2969
+ } catch (err) {
2970
+ setError(err instanceof Error ? err.message : String(err));
2971
+ } finally {
2972
+ setLoading(false);
2973
+ }
2974
+ };
2975
+ const undo = () => {
2976
+ if (previous === null) return;
2977
+ onChange(previous);
2978
+ setPrevious(null);
2979
+ };
2980
+ return /* @__PURE__ */ jsxs26("div", { className: cn("flex flex-col gap-2", className), ...props, children: [
2981
+ /* @__PURE__ */ jsx44(
2982
+ Textarea,
2983
+ {
2984
+ value,
2985
+ onChange: (event) => onChange(event.target.value),
2986
+ placeholder,
2987
+ rows,
2988
+ disabled: disabled || loading,
2989
+ "aria-busy": loading || void 0
2990
+ }
2991
+ ),
2992
+ /* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-2", children: [
2993
+ /* @__PURE__ */ jsx44(
2994
+ VoiceInputButton,
2995
+ {
2996
+ onTranscript: append,
2997
+ onError: (code) => setError(describeSpeechError(code)),
2998
+ lang,
2999
+ engine,
3000
+ label: "Dictate",
3001
+ size: "sm",
3002
+ showInterim: true,
3003
+ disabled: disabled || loading
3004
+ }
3005
+ ),
3006
+ onTransform ? /* @__PURE__ */ jsx44(
3007
+ AiButton,
3008
+ {
3009
+ variant: "ai",
3010
+ size: "sm",
3011
+ icon: Wand2,
3012
+ loading,
3013
+ disabled: disabled || value.trim().length === 0,
3014
+ onClick: () => void transform(),
3015
+ children: transformLabel
3016
+ }
3017
+ ) : null,
3018
+ previous !== null && !loading ? /* @__PURE__ */ jsxs26(Button, { type: "button", variant: "ghost", size: "sm", onClick: undo, children: [
3019
+ /* @__PURE__ */ jsx44(Undo2, { className: "h-4 w-4", "aria-hidden": "true" }),
3020
+ "Undo"
3021
+ ] }) : null
3022
+ ] }),
3023
+ error ? (
3024
+ // AlertBox has no `role` prop, so the live region wraps it; same shape
3025
+ // a consuming app's own form errors take.
3026
+ /* @__PURE__ */ jsx44("div", { role: "alert", children: /* @__PURE__ */ jsx44(AlertBox, { variant: "error", children: error }) })
3027
+ ) : null
3028
+ ] });
3029
+ }
3030
+
2703
3031
  // src/patterns/stepper/Stepper.tsx
2704
- import { useEffect as useEffect6, useRef as useRef6 } from "react";
3032
+ import { useEffect as useEffect7, useRef as useRef7 } from "react";
2705
3033
  import { Check as Check5, Plus } from "lucide-react";
2706
- import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
3034
+ import { jsx as jsx45, jsxs as jsxs27 } from "react/jsx-runtime";
2707
3035
  function Stepper({
2708
3036
  step,
2709
3037
  maxStep,
@@ -2715,8 +3043,8 @@ function Stepper({
2715
3043
  scrollTestId = "stepper-scroll",
2716
3044
  newTestId
2717
3045
  }) {
2718
- const stepRefs = useRef6(/* @__PURE__ */ new Map());
2719
- useEffect6(() => {
3046
+ const stepRefs = useRef7(/* @__PURE__ */ new Map());
3047
+ useEffect7(() => {
2720
3048
  const activeStep = stepRefs.current.get(step);
2721
3049
  activeStep?.scrollIntoView({ block: "nearest", inline: "center" });
2722
3050
  }, [step]);
@@ -2724,19 +3052,19 @@ function Stepper({
2724
3052
  // onNew is pinned outside the horizontally-scrolling region below (a sibling of
2725
3053
  // it, not a child) so its own edge never scrolls off-screen along with the
2726
3054
  // later steps once the scroller's min-width doesn't fit the viewport.
2727
- /* @__PURE__ */ jsxs23("div", { className: "flex w-full shrink-0 items-center gap-2", children: [
2728
- /* @__PURE__ */ jsx41("nav", { "aria-label": navLabel, className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx41(
3055
+ /* @__PURE__ */ jsxs27("div", { className: "flex w-full shrink-0 items-center gap-2", children: [
3056
+ /* @__PURE__ */ jsx45("nav", { "aria-label": navLabel, className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx45(
2729
3057
  "div",
2730
3058
  {
2731
3059
  "data-testid": scrollTestId,
2732
3060
  className: "flex w-full items-center gap-0 overflow-x-auto px-2 py-1",
2733
- children: /* @__PURE__ */ jsx41("ol", { className: "flex min-w-[700px] flex-1 items-start", children: steps.map(({ n, label, phase }, i) => {
3061
+ children: /* @__PURE__ */ jsx45("ol", { className: "flex min-w-[700px] flex-1 items-start", children: steps.map(({ n, label, phase }, i) => {
2734
3062
  const done = step > n;
2735
3063
  const active = step === n;
2736
3064
  const navigable = n <= maxStep && n !== step;
2737
3065
  const last = i === steps.length - 1;
2738
- return /* @__PURE__ */ jsxs23("li", { className: cn("flex items-start", !last && "flex-1"), children: [
2739
- /* @__PURE__ */ jsxs23(
3066
+ return /* @__PURE__ */ jsxs27("li", { className: cn("flex items-start", !last && "flex-1"), children: [
3067
+ /* @__PURE__ */ jsxs27(
2740
3068
  "button",
2741
3069
  {
2742
3070
  ref: (element) => {
@@ -2752,17 +3080,17 @@ function Stepper({
2752
3080
  navigable ? "cursor-pointer hover:opacity-80" : "cursor-default"
2753
3081
  ),
2754
3082
  children: [
2755
- /* @__PURE__ */ jsx41(
3083
+ /* @__PURE__ */ jsx45(
2756
3084
  "div",
2757
3085
  {
2758
3086
  className: cn(
2759
3087
  "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[11px] font-bold transition-colors",
2760
3088
  done ? "bg-primary/15 text-primary" : active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
2761
3089
  ),
2762
- children: done ? /* @__PURE__ */ jsx41(Check5, { className: "h-3 w-3" }) : n
3090
+ children: done ? /* @__PURE__ */ jsx45(Check5, { className: "h-3 w-3" }) : n
2763
3091
  }
2764
3092
  ),
2765
- /* @__PURE__ */ jsx41(
3093
+ /* @__PURE__ */ jsx45(
2766
3094
  "span",
2767
3095
  {
2768
3096
  className: cn(
@@ -2772,11 +3100,11 @@ function Stepper({
2772
3100
  children: label
2773
3101
  }
2774
3102
  ),
2775
- /* @__PURE__ */ jsx41("span", { className: "text-[9px] tracking-[0.5px] text-muted-foreground/50 uppercase", children: phase })
3103
+ /* @__PURE__ */ jsx45("span", { className: "text-[9px] tracking-[0.5px] text-muted-foreground/50 uppercase", children: phase })
2776
3104
  ]
2777
3105
  }
2778
3106
  ),
2779
- !last && /* @__PURE__ */ jsx41(
3107
+ !last && /* @__PURE__ */ jsx45(
2780
3108
  "div",
2781
3109
  {
2782
3110
  className: cn(
@@ -2789,7 +3117,7 @@ function Stepper({
2789
3117
  }) })
2790
3118
  }
2791
3119
  ) }),
2792
- onNew && /* @__PURE__ */ jsxs23(
3120
+ onNew && /* @__PURE__ */ jsxs27(
2793
3121
  "button",
2794
3122
  {
2795
3123
  "data-testid": newTestId,
@@ -2797,7 +3125,7 @@ function Stepper({
2797
3125
  title: newLabel,
2798
3126
  className: "flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-[12px] font-semibold text-foreground transition-colors hover:bg-muted/50",
2799
3127
  children: [
2800
- /* @__PURE__ */ jsx41(Plus, { className: "h-3.5 w-3.5" }),
3128
+ /* @__PURE__ */ jsx45(Plus, { className: "h-3.5 w-3.5" }),
2801
3129
  newLabel
2802
3130
  ]
2803
3131
  }
@@ -2806,6 +3134,8 @@ function Stepper({
2806
3134
  );
2807
3135
  }
2808
3136
  export {
3137
+ AiButton,
3138
+ AiExplainButton,
2809
3139
  AlertBox,
2810
3140
  Badge,
2811
3141
  Button,
@@ -2905,14 +3235,18 @@ export {
2905
3235
  TooltipContent,
2906
3236
  TooltipProvider,
2907
3237
  TooltipTrigger,
3238
+ VoiceInputButton,
3239
+ VoiceTranscript,
2908
3240
  badgeVariants,
2909
3241
  buttonVariants,
2910
3242
  cn,
3243
+ describeSpeechError,
2911
3244
  getConfirmButtonPlacement,
2912
3245
  isWindowsPlatform,
2913
3246
  loadingSpinnerIconVariants,
2914
3247
  sheetVariants,
2915
3248
  useMediaQuery,
2916
- useSidebarCollapsed
3249
+ useSidebarCollapsed,
3250
+ useSpeechRecognition
2917
3251
  };
2918
3252
  //# sourceMappingURL=index.js.map