@epicbagel/saas-kit 1.24.0 → 1.25.1

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.
@@ -35,6 +35,25 @@ var DEFAULT_LABELS = {
35
35
  muteShort: "Mute",
36
36
  unmuteShort: "Unmute"
37
37
  };
38
+ var ACTION_LABEL = "hidden @min-[448px]/voice:inline";
39
+ function VoiceSessionAction({ label, icon, shortLabel, className, ...props }) {
40
+ return /* @__PURE__ */ jsxs(
41
+ Button,
42
+ {
43
+ type: "button",
44
+ variant: "outline",
45
+ size: "sm",
46
+ ...props,
47
+ "aria-label": label,
48
+ title: label,
49
+ className: cn("min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground", shortLabel && "@min-[448px]/voice:px-3", className),
50
+ children: [
51
+ icon,
52
+ shortLabel && /* @__PURE__ */ jsx("span", { "data-slot": "voice-session-action-label", className: ACTION_LABEL, children: shortLabel })
53
+ ]
54
+ }
55
+ );
56
+ }
38
57
  function VoiceSessionControls({
39
58
  state,
40
59
  onStart,
@@ -47,6 +66,7 @@ function VoiceSessionControls({
47
66
  audioLevels,
48
67
  labels,
49
68
  onWarm,
69
+ actions,
50
70
  size = "icon",
51
71
  className
52
72
  }) {
@@ -84,38 +104,25 @@ function VoiceSessionControls({
84
104
  /* @__PURE__ */ jsx("span", { role: "status", className: "min-w-0 flex-1 truncate text-sm font-medium text-primary", children: muted && state === "listening" ? copy.muted : copy[state] })
85
105
  ] }),
86
106
  /* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
87
- /* @__PURE__ */ jsxs(
88
- Button,
107
+ actions,
108
+ /* @__PURE__ */ jsx(
109
+ VoiceSessionAction,
89
110
  {
90
- type: "button",
91
- variant: "outline",
92
- size: "sm",
93
- className: "min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground @md/voice:px-3",
94
- "aria-label": muted ? copy.unmute : copy.mute,
95
- title: muted ? copy.unmute : copy.mute,
111
+ label: muted ? copy.unmute : copy.mute,
112
+ shortLabel: muted ? copy.unmuteShort : copy.muteShort,
113
+ icon: muted ? /* @__PURE__ */ jsx(Mic, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(MicOff, { "aria-hidden": "true" }),
96
114
  "aria-pressed": muted,
97
115
  disabled: waiting,
98
- onClick: () => onMutedChange(!muted),
99
- children: [
100
- muted ? /* @__PURE__ */ jsx(Mic, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(MicOff, { "aria-hidden": "true" }),
101
- /* @__PURE__ */ jsx("span", { "data-slot": "voice-session-action-label", className: "hidden @md/voice:inline", children: muted ? copy.unmuteShort : copy.muteShort })
102
- ]
116
+ onClick: () => onMutedChange(!muted)
103
117
  }
104
118
  ),
105
- /* @__PURE__ */ jsxs(
106
- Button,
119
+ /* @__PURE__ */ jsx(
120
+ VoiceSessionAction,
107
121
  {
108
- type: "button",
109
- variant: "outline",
110
- size: "sm",
111
- className: "min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground @md/voice:px-3",
112
- "aria-label": copy.end,
113
- title: copy.end,
114
- onClick: onEnd,
115
- children: [
116
- /* @__PURE__ */ jsx(Square, { "aria-hidden": "true", className: "fill-current" }),
117
- /* @__PURE__ */ jsx("span", { "data-slot": "voice-session-action-label", className: "hidden @md/voice:inline", children: copy.endShort })
118
- ]
122
+ label: copy.end,
123
+ shortLabel: copy.endShort,
124
+ icon: /* @__PURE__ */ jsx(Square, { "aria-hidden": "true", className: "fill-current" }),
125
+ onClick: onEnd
119
126
  }
120
127
  )
121
128
  ] })
@@ -137,6 +144,7 @@ function VoiceSessionControls({
137
144
  children: /* @__PURE__ */ jsx(AudioLines, { "aria-hidden": "true" })
138
145
  }
139
146
  ),
147
+ !active && actions,
140
148
  state === "error" && /* @__PURE__ */ jsx("span", { role: "alert", className: "basis-full text-xs text-destructive", children: error || copy.error })
141
149
  ]
142
150
  }
@@ -144,5 +152,6 @@ function VoiceSessionControls({
144
152
  }
145
153
 
146
154
  export {
155
+ VoiceSessionAction,
147
156
  VoiceSessionControls
148
157
  };
@@ -35,6 +35,25 @@ var DEFAULT_LABELS = {
35
35
  muteShort: "Mute",
36
36
  unmuteShort: "Unmute"
37
37
  };
38
+ var ACTION_LABEL = "hidden @min-[448px]/voice:inline";
39
+ function VoiceSessionAction({ label, icon, shortLabel, className, ...props }) {
40
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
41
+ _chunkJDEXZGIYcjs.Button,
42
+ {
43
+ type: "button",
44
+ variant: "outline",
45
+ size: "sm",
46
+ ...props,
47
+ "aria-label": label,
48
+ title: label,
49
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground", shortLabel && "@min-[448px]/voice:px-3", className),
50
+ children: [
51
+ icon,
52
+ shortLabel && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-slot": "voice-session-action-label", className: ACTION_LABEL, children: shortLabel })
53
+ ]
54
+ }
55
+ );
56
+ }
38
57
  function VoiceSessionControls({
39
58
  state,
40
59
  onStart,
@@ -47,6 +66,7 @@ function VoiceSessionControls({
47
66
  audioLevels,
48
67
  labels,
49
68
  onWarm,
69
+ actions,
50
70
  size = "icon",
51
71
  className
52
72
  }) {
@@ -84,38 +104,25 @@ function VoiceSessionControls({
84
104
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { role: "status", className: "min-w-0 flex-1 truncate text-sm font-medium text-primary", children: muted && state === "listening" ? copy.muted : copy[state] })
85
105
  ] }),
86
106
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
87
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
88
- _chunkJDEXZGIYcjs.Button,
107
+ actions,
108
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
109
+ VoiceSessionAction,
89
110
  {
90
- type: "button",
91
- variant: "outline",
92
- size: "sm",
93
- className: "min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground @md/voice:px-3",
94
- "aria-label": muted ? copy.unmute : copy.mute,
95
- title: muted ? copy.unmute : copy.mute,
111
+ label: muted ? copy.unmute : copy.mute,
112
+ shortLabel: muted ? copy.unmuteShort : copy.muteShort,
113
+ icon: muted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.MicOff, { "aria-hidden": "true" }),
96
114
  "aria-pressed": muted,
97
115
  disabled: waiting,
98
- onClick: () => onMutedChange(!muted),
99
- children: [
100
- muted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.MicOff, { "aria-hidden": "true" }),
101
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-slot": "voice-session-action-label", className: "hidden @md/voice:inline", children: muted ? copy.unmuteShort : copy.muteShort })
102
- ]
116
+ onClick: () => onMutedChange(!muted)
103
117
  }
104
118
  ),
105
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
106
- _chunkJDEXZGIYcjs.Button,
119
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
120
+ VoiceSessionAction,
107
121
  {
108
- type: "button",
109
- variant: "outline",
110
- size: "sm",
111
- className: "min-h-[44px] min-w-[44px] shrink-0 rounded-xl bg-background px-0 text-foreground @md/voice:px-3",
112
- "aria-label": copy.end,
113
- title: copy.end,
114
- onClick: onEnd,
115
- children: [
116
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Square, { "aria-hidden": "true", className: "fill-current" }),
117
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "data-slot": "voice-session-action-label", className: "hidden @md/voice:inline", children: copy.endShort })
118
- ]
122
+ label: copy.end,
123
+ shortLabel: copy.endShort,
124
+ icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Square, { "aria-hidden": "true", className: "fill-current" }),
125
+ onClick: onEnd
119
126
  }
120
127
  )
121
128
  ] })
@@ -137,6 +144,7 @@ function VoiceSessionControls({
137
144
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.AudioLines, { "aria-hidden": "true" })
138
145
  }
139
146
  ),
147
+ !active && actions,
140
148
  state === "error" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { role: "alert", className: "basis-full text-xs text-destructive", children: error || copy.error })
141
149
  ]
142
150
  }
@@ -145,4 +153,5 @@ function VoiceSessionControls({
145
153
 
146
154
 
147
155
 
148
- exports.VoiceSessionControls = VoiceSessionControls;
156
+
157
+ exports.VoiceSessionAction = VoiceSessionAction; exports.VoiceSessionControls = VoiceSessionControls;
@@ -10,7 +10,7 @@ var _chunk2BTDBZYNcjs = require('../chunk-2BTDBZYN.cjs');
10
10
  var _chunkPH2TV7XIcjs = require('../chunk-PH2TV7XI.cjs');
11
11
 
12
12
 
13
- var _chunkHETZNLKTcjs = require('../chunk-HETZNLKT.cjs');
13
+ var _chunkPNI354BYcjs = require('../chunk-PNI354BY.cjs');
14
14
 
15
15
 
16
16
 
@@ -489,7 +489,7 @@ function ChatInput({
489
489
  children: header
490
490
  }
491
491
  ),
492
- liveVoiceActive && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7LDJAXRXcjs.InputGroupAddon, { align: "block-start", className: "w-full cursor-default border-b border-border p-0 [.border-b]:pb-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkHETZNLKTcjs.VoiceSessionControls, { ...voiceSession }) }),
492
+ liveVoiceActive && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk7LDJAXRXcjs.InputGroupAddon, { align: "block-start", className: "w-full cursor-default border-b border-border p-0 [.border-b]:pb-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkPNI354BYcjs.VoiceSessionControls, { ...voiceSession }) }),
493
493
  attachments.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
494
494
  ChatAttachmentList,
495
495
  {
@@ -581,7 +581,7 @@ function ChatInput({
581
581
  )
582
582
  }
583
583
  ),
584
- voiceSession && !liveVoiceActive && !hasText && attachments.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkHETZNLKTcjs.VoiceSessionControls, { ...voiceSession, disabled: voiceSession.disabled || isDisabled }) : submitLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
584
+ voiceSession && !liveVoiceActive && !hasText && attachments.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkPNI354BYcjs.VoiceSessionControls, { ...voiceSession, disabled: voiceSession.disabled || isDisabled }) : submitLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
585
585
  _chunkJDEXZGIYcjs.Button,
586
586
  {
587
587
  type: "submit",
@@ -643,7 +643,7 @@ function VoiceHero({
643
643
  }) {
644
644
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col items-center gap-5 py-6", children: [
645
645
  voiceSession ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
646
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkHETZNLKTcjs.VoiceSessionControls, { ...voiceSession, size: "hero" }),
646
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkPNI354BYcjs.VoiceSessionControls, { ...voiceSession, size: "hero" }),
647
647
  voice && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
648
648
  _chunkPH2TV7XIcjs.VoiceDictationButton,
649
649
  {
@@ -3,6 +3,9 @@ import * as React$1 from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { VoiceSessionControlsProps } from './voice-session-controls.cjs';
5
5
  import { MarkdownComponents } from './markdown.cjs';
6
+ import './button.cjs';
7
+ import 'class-variance-authority/types';
8
+ import 'class-variance-authority';
6
9
  import 'react-markdown';
7
10
  import './rich-text.cjs';
8
11
 
@@ -3,6 +3,9 @@ import * as React$1 from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { VoiceSessionControlsProps } from './voice-session-controls.js';
5
5
  import { MarkdownComponents } from './markdown.js';
6
+ import './button.js';
7
+ import 'class-variance-authority/types';
8
+ import 'class-variance-authority';
6
9
  import 'react-markdown';
7
10
  import './rich-text.js';
8
11
 
@@ -10,7 +10,7 @@ import {
10
10
  } from "../chunk-ZGTPNSR7.js";
11
11
  import {
12
12
  VoiceSessionControls
13
- } from "../chunk-PL2462GA.js";
13
+ } from "../chunk-6O3DUMF7.js";
14
14
  import {
15
15
  Suggestion,
16
16
  Suggestions
@@ -2,11 +2,13 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkHETZNLKTcjs = require('../chunk-HETZNLKT.cjs');
5
+
6
+ var _chunkPNI354BYcjs = require('../chunk-PNI354BY.cjs');
6
7
  require('../chunk-NKWHDI4C.cjs');
7
8
  require('../chunk-JDEXZGIY.cjs');
8
9
  require('../chunk-KWLSBTB2.cjs');
9
10
  require('../chunk-2BSFYU22.cjs');
10
11
 
11
12
 
12
- exports.VoiceSessionControls = _chunkHETZNLKTcjs.VoiceSessionControls;
13
+
14
+ exports.VoiceSessionAction = _chunkPNI354BYcjs.VoiceSessionAction; exports.VoiceSessionControls = _chunkPNI354BYcjs.VoiceSessionControls;
@@ -1,4 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { ButtonProps } from './button.cjs';
4
+ import 'class-variance-authority/types';
5
+ import 'class-variance-authority';
2
6
 
3
7
  type VoiceSessionState = "disconnected" | "connecting" | "listening" | "thinking" | "speaking" | "reconnecting" | "error";
4
8
  interface VoiceAudioLevelsSource {
@@ -30,10 +34,24 @@ interface VoiceSessionControlsProps {
30
34
  * permission. It can fire several times; make it idempotent.
31
35
  */
32
36
  onWarm?: () => void;
37
+ /**
38
+ * Extra controls beside Mute and End (beside the start control between
39
+ * calls), such as a panel toggle. Pass `VoiceSessionAction`s so they match.
40
+ */
41
+ actions?: ReactNode;
33
42
  size?: "icon" | "hero";
34
43
  className?: string;
35
44
  }
45
+ interface VoiceSessionActionProps extends Omit<ButtonProps, "children" | "aria-label" | "title"> {
46
+ /** Full accessible name, also the tooltip. */
47
+ label: string;
48
+ icon: ReactNode;
49
+ /** Shown beside the icon on a wide strip; a narrow one shows only the icon. */
50
+ shortLabel?: string;
51
+ }
52
+ /** A 44px control styled as the strip's Mute and End. */
53
+ declare function VoiceSessionAction({ label, icon, shortLabel, className, ...props }: VoiceSessionActionProps): react_jsx_runtime.JSX.Element;
36
54
  /** Transport-independent voice controls. The conversation stays visible while speaking. */
37
- declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
55
+ declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, actions, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
38
56
 
39
- export { type VoiceAudioLevelsSource, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
57
+ export { type VoiceAudioLevelsSource, VoiceSessionAction, type VoiceSessionActionProps, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
@@ -1,4 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { ButtonProps } from './button.js';
4
+ import 'class-variance-authority/types';
5
+ import 'class-variance-authority';
2
6
 
3
7
  type VoiceSessionState = "disconnected" | "connecting" | "listening" | "thinking" | "speaking" | "reconnecting" | "error";
4
8
  interface VoiceAudioLevelsSource {
@@ -30,10 +34,24 @@ interface VoiceSessionControlsProps {
30
34
  * permission. It can fire several times; make it idempotent.
31
35
  */
32
36
  onWarm?: () => void;
37
+ /**
38
+ * Extra controls beside Mute and End (beside the start control between
39
+ * calls), such as a panel toggle. Pass `VoiceSessionAction`s so they match.
40
+ */
41
+ actions?: ReactNode;
33
42
  size?: "icon" | "hero";
34
43
  className?: string;
35
44
  }
45
+ interface VoiceSessionActionProps extends Omit<ButtonProps, "children" | "aria-label" | "title"> {
46
+ /** Full accessible name, also the tooltip. */
47
+ label: string;
48
+ icon: ReactNode;
49
+ /** Shown beside the icon on a wide strip; a narrow one shows only the icon. */
50
+ shortLabel?: string;
51
+ }
52
+ /** A 44px control styled as the strip's Mute and End. */
53
+ declare function VoiceSessionAction({ label, icon, shortLabel, className, ...props }: VoiceSessionActionProps): react_jsx_runtime.JSX.Element;
36
54
  /** Transport-independent voice controls. The conversation stays visible while speaking. */
37
- declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
55
+ declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, actions, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
38
56
 
39
- export { type VoiceAudioLevelsSource, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
57
+ export { type VoiceAudioLevelsSource, VoiceSessionAction, type VoiceSessionActionProps, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
@@ -1,12 +1,14 @@
1
1
  "use client";
2
2
  "use client";
3
3
  import {
4
+ VoiceSessionAction,
4
5
  VoiceSessionControls
5
- } from "../chunk-PL2462GA.js";
6
+ } from "../chunk-6O3DUMF7.js";
6
7
  import "../chunk-5OF6D3GH.js";
7
8
  import "../chunk-W326GKML.js";
8
9
  import "../chunk-G6SRSWXB.js";
9
10
  import "../chunk-GJ7S3L6R.js";
10
11
  export {
12
+ VoiceSessionAction,
11
13
  VoiceSessionControls
12
14
  };
@@ -2,7 +2,8 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkHETZNLKTcjs = require('../chunk-HETZNLKT.cjs');
5
+
6
+ var _chunkPNI354BYcjs = require('../chunk-PNI354BY.cjs');
6
7
  require('../chunk-NKWHDI4C.cjs');
7
8
 
8
9
 
@@ -34,6 +35,8 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
34
35
  context,
35
36
  description,
36
37
  summaries,
38
+ summariesOpen,
39
+ onSummariesOpenChange,
37
40
  voiceSession,
38
41
  pending,
39
42
  disabled,
@@ -44,13 +47,20 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
44
47
  }, ref) {
45
48
  const titleId = _react.useId.call(void 0, );
46
49
  const statusId = _react.useId.call(void 0, );
50
+ const summariesId = _react.useId.call(void 0, );
51
+ const [ownSummariesOpen, setOwnSummariesOpen] = _react.useState.call(void 0, false);
52
+ const showingSummaries = (_nullishCoalesce(summariesOpen, () => ( ownSummariesOpen))) && summaries.length > 0;
53
+ const setSummariesOpen = (next) => {
54
+ setOwnSummariesOpen(next);
55
+ _optionalChain([onSummariesOpenChange, 'optionalCall', _ => _(next)]);
56
+ };
47
57
  const launcher = _react.useRef.call(void 0, null);
48
58
  const heading = _react.useRef.call(void 0, null);
49
59
  const moveFocus = _react.useRef.call(void 0, false);
50
60
  _react.useEffect.call(void 0, () => {
51
61
  if (!moveFocus.current) return;
52
62
  moveFocus.current = false;
53
- _optionalChain([(open ? heading.current : launcher.current), 'optionalAccess', _ => _.focus, 'call', _2 => _2()]);
63
+ _optionalChain([(open ? heading.current : launcher.current), 'optionalAccess', _2 => _2.focus, 'call', _3 => _3()]);
54
64
  }, [open]);
55
65
  const toggle = (next) => {
56
66
  moveFocus.current = true;
@@ -58,7 +68,7 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
58
68
  };
59
69
  const startDisabled = pending || disabled || voiceSession.disabled;
60
70
  const active = voiceSession.state !== "disconnected" && voiceSession.state !== "error";
61
- const status = voiceSession.muted && voiceSession.state === "listening" ? _nullishCoalesce(_optionalChain([voiceSession, 'access', _3 => _3.labels, 'optionalAccess', _4 => _4.muted]), () => ( "Microphone muted")) : _nullishCoalesce(_optionalChain([voiceSession, 'access', _5 => _5.labels, 'optionalAccess', _6 => _6[voiceSession.state]]), () => ( SESSION_LABELS[voiceSession.state]));
71
+ const status = voiceSession.muted && voiceSession.state === "listening" ? _nullishCoalesce(_optionalChain([voiceSession, 'access', _4 => _4.labels, 'optionalAccess', _5 => _5.muted]), () => ( "Microphone muted")) : _nullishCoalesce(_optionalChain([voiceSession, 'access', _6 => _6.labels, 'optionalAccess', _7 => _7[voiceSession.state]]), () => ( SESSION_LABELS[voiceSession.state]));
62
72
  const unavailable = voiceSession.state === "error" && voiceSession.disabled;
63
73
  if (!open) return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
64
74
  "aside",
@@ -76,7 +86,7 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
76
86
  shape: "pill",
77
87
  className: "min-h-12 min-w-12 max-w-full gap-2 shadow-lg",
78
88
  disabled: startDisabled && !active && !unavailable,
79
- "aria-label": active ? title : unavailable ? "Voice review unavailable" : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _7 => _7.start]), () => ( "Start voice review")),
89
+ "aria-label": active ? title : unavailable ? "Voice review unavailable" : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _8 => _8.start]), () => ( "Start voice review")),
80
90
  "aria-expanded": false,
81
91
  "aria-describedby": active ? statusId : void 0,
82
92
  onClick: () => {
@@ -98,71 +108,92 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
98
108
  size: "icon",
99
109
  className: "min-h-12 min-w-12 bg-background shadow-lg",
100
110
  onClick: () => {
101
- _optionalChain([launcher, 'access', _8 => _8.current, 'optionalAccess', _9 => _9.focus, 'call', _10 => _10()]);
111
+ _optionalChain([launcher, 'access', _9 => _9.current, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]);
102
112
  voiceSession.onEnd();
103
113
  },
104
- "aria-label": _nullishCoalesce(_nullishCoalesce(_optionalChain([labels, 'optionalAccess', _11 => _11.end]), () => ( _optionalChain([voiceSession, 'access', _12 => _12.labels, 'optionalAccess', _13 => _13.end]))), () => ( "End voice chat")),
114
+ "aria-label": _nullishCoalesce(_nullishCoalesce(_optionalChain([labels, 'optionalAccess', _12 => _12.end]), () => ( _optionalChain([voiceSession, 'access', _13 => _13.labels, 'optionalAccess', _14 => _14.end]))), () => ( "End voice chat")),
105
115
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Square, { "aria-hidden": "true" })
106
116
  }
107
117
  )
108
118
  ]
109
119
  }
110
120
  );
111
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
112
- "aside",
113
- {
114
- ref,
115
- "data-slot": "workflow-assistant",
116
- "aria-labelledby": titleId,
117
- className: _chunkKWLSBTB2cjs.cn.call(void 0,
118
- "fixed inset-x-2 bottom-[calc(1rem+env(safe-area-inset-bottom))] z-40 flex min-h-0 flex-col overflow-y-auto overscroll-contain rounded-2xl border bg-background shadow-xl sm:inset-x-auto sm:right-6 sm:w-[28rem] md:right-8",
119
- expanded ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
120
- className
121
- ),
122
- children: [
123
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "sticky top-0 z-10 flex shrink-0 items-start gap-2 border-b bg-background p-3", children: [
124
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 flex-1", children: [
125
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold", children: title }),
126
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
121
+ return (
122
+ // On a phone the panel is a sheet docked to the bottom edge, like Drawer:
123
+ // its background runs under the home indicator (and a browser's floating
124
+ // toolbar) with its content kept above them. A floating card there left
125
+ // the page showing beneath it. From sm it is a card in the corner.
126
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
127
+ "aside",
128
+ {
129
+ ref,
130
+ "data-slot": "workflow-assistant",
131
+ "aria-labelledby": titleId,
132
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
133
+ "fixed inset-x-0 bottom-0 z-40 flex min-h-0 flex-col overflow-y-auto overscroll-contain rounded-t-[10px] border-t bg-background pb-[env(safe-area-inset-bottom)] shadow-[0_-8px_30px_rgb(0_0_0/0.12)]",
134
+ "sm:inset-x-auto sm:bottom-[calc(1rem+env(safe-area-inset-bottom))] sm:right-6 sm:w-[28rem] sm:rounded-2xl sm:border sm:pb-0 sm:shadow-xl md:right-8",
135
+ expanded && showingSummaries ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
136
+ className
137
+ ),
138
+ children: [
139
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "sticky top-0 z-10 flex shrink-0 items-start gap-2 border-b bg-background p-3", children: [
140
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 flex-1", children: [
141
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold outline-none", children: title }),
142
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
143
+ ] }),
144
+ onExpandedChange && showingSummaries && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
145
+ _chunkJDEXZGIYcjs.Button,
146
+ {
147
+ type: "button",
148
+ variant: "ghost",
149
+ size: "icon",
150
+ "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _15 => _15.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _16 => _16.expand]), () => ( "Expand panel")),
151
+ onClick: () => onExpandedChange(!expanded),
152
+ children: expanded ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Minimize2, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Maximize2, { "aria-hidden": "true" })
153
+ }
154
+ ),
155
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "ghost", size: "icon", "aria-label": _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _17 => _17.minimize]), () => ( "Minimize voice review")), onClick: () => toggle(false), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ChevronDown, { "aria-hidden": "true" }) })
127
156
  ] }),
128
- onExpandedChange && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
129
- _chunkJDEXZGIYcjs.Button,
157
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0", "data-slot": "workflow-assistant-voice", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
158
+ _chunkPNI354BYcjs.VoiceSessionControls,
130
159
  {
131
- type: "button",
132
- variant: "ghost",
133
- size: "icon",
134
- "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _14 => _14.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _15 => _15.expand]), () => ( "Expand panel")),
135
- onClick: () => onExpandedChange(!expanded),
136
- children: expanded ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Minimize2, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Maximize2, { "aria-hidden": "true" })
160
+ ...voiceSession,
161
+ disabled: Boolean(startDisabled),
162
+ size: "hero",
163
+ onEnd: () => {
164
+ _optionalChain([heading, 'access', _18 => _18.current, 'optionalAccess', _19 => _19.focus, 'call', _20 => _20()]);
165
+ voiceSession.onEnd();
166
+ },
167
+ actions: summaries.length > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
168
+ voiceSession.actions,
169
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
170
+ _chunkPNI354BYcjs.VoiceSessionAction,
171
+ {
172
+ label: showingSummaries ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _21 => _21.hideSummaries]), () => ( "Hide actions")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _22 => _22.showSummaries]), () => ( "Show actions")),
173
+ icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ListChecks, { "aria-hidden": "true" }),
174
+ "aria-expanded": showingSummaries,
175
+ "aria-controls": summariesId,
176
+ className: "aria-expanded:bg-muted",
177
+ onClick: () => setSummariesOpen(!showingSummaries)
178
+ }
179
+ )
180
+ ] }) : voiceSession.actions,
181
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, !active && "justify-center px-4 py-6 text-center")
137
182
  }
138
- ),
139
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "ghost", size: "icon", "aria-label": _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _16 => _16.minimize]), () => ( "Minimize voice review")), onClick: () => toggle(false), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ChevronDown, { "aria-hidden": "true" }) })
140
- ] }),
141
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0", "data-slot": "workflow-assistant-voice", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
142
- _chunkHETZNLKTcjs.VoiceSessionControls,
143
- {
144
- ...voiceSession,
145
- disabled: Boolean(startDisabled),
146
- size: "hero",
147
- onEnd: () => {
148
- _optionalChain([heading, 'access', _17 => _17.current, 'optionalAccess', _18 => _18.focus, 'call', _19 => _19()]);
149
- voiceSession.onEnd();
150
- },
151
- className: _chunkKWLSBTB2cjs.cn.call(void 0, !active && "justify-center px-4 py-6 text-center")
152
- }
153
- ) }),
154
- description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
155
- error && (voiceSession.state !== "error" || error !== voiceSession.error) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
156
- pending && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "status", className: "shrink-0 px-4 py-2 text-sm text-muted-foreground", children: "Applying your instructions\u2026" }),
157
- summaries.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { "aria-label": "Action summaries", className: "min-h-20 flex-1 overflow-y-auto border-t px-4 py-3", children: [
158
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
159
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "ol", { className: "space-y-3", "aria-live": "polite", "aria-relevant": "additions", children: [...summaries].reverse().map((summary) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "li", { className: "space-y-1 border-b pb-3 last:border-0 last:pb-0", children: [
160
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-xs text-muted-foreground", children: summary.context }),
161
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "whitespace-pre-wrap break-words text-sm", children: summary.body })
162
- ] }, summary.id)) })
163
- ] })
164
- ]
165
- }
183
+ ) }),
184
+ description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
185
+ error && (voiceSession.state !== "error" || error !== voiceSession.error) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
186
+ pending && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "status", className: "shrink-0 px-4 py-2 text-sm text-muted-foreground", children: "Applying your instructions\u2026" }),
187
+ summaries.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { id: summariesId, hidden: !showingSummaries, "aria-label": "Action summaries", className: "min-h-20 flex-1 overflow-y-auto border-t px-4 py-3", children: [
188
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
189
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "ol", { className: "space-y-3", "aria-live": "polite", "aria-relevant": "additions", children: [...summaries].reverse().map((summary) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "li", { className: "space-y-1 border-b pb-3 last:border-0 last:pb-0", children: [
190
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-xs text-muted-foreground", children: summary.context }),
191
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "whitespace-pre-wrap break-words text-sm", children: summary.body })
192
+ ] }, summary.id)) })
193
+ ] })
194
+ ]
195
+ }
196
+ )
166
197
  );
167
198
  });
168
199
 
@@ -2,6 +2,9 @@ import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { VoiceSessionControlsProps } from './voice-session-controls.cjs';
4
4
  import 'react/jsx-runtime';
5
+ import './button.cjs';
6
+ import 'class-variance-authority/types';
7
+ import 'class-variance-authority';
5
8
 
6
9
  interface WorkflowSummary {
7
10
  id: string;
@@ -18,8 +21,12 @@ interface WorkflowAssistantProps {
18
21
  /** Current host-owned scope. Keep it visible while the summaries scroll. */
19
22
  context: string;
20
23
  description?: ReactNode;
21
- /** Saved action receipts and clarification questions, newest last. Read-only. */
24
+ /** Saved action receipts and clarification questions, newest last. Read-only.
25
+ * Hidden until someone opens them: the voice already confirms each action. */
22
26
  summaries: WorkflowSummary[];
27
+ /** Whether the summaries show. Leave unset for the panel to keep it (closed at first). */
28
+ summariesOpen?: boolean;
29
+ onSummariesOpenChange?: (open: boolean) => void;
23
30
  /** Minimizing preserves this host-owned session; only End calls onEnd. */
24
31
  voiceSession: VoiceSessionControlsProps;
25
32
  pending?: boolean;
@@ -33,6 +40,8 @@ interface WorkflowAssistantProps {
33
40
  expand?: string;
34
41
  collapse?: string;
35
42
  end?: string;
43
+ showSummaries?: string;
44
+ hideSummaries?: string;
36
45
  };
37
46
  className?: string;
38
47
  }
@@ -2,6 +2,9 @@ import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { VoiceSessionControlsProps } from './voice-session-controls.js';
4
4
  import 'react/jsx-runtime';
5
+ import './button.js';
6
+ import 'class-variance-authority/types';
7
+ import 'class-variance-authority';
5
8
 
6
9
  interface WorkflowSummary {
7
10
  id: string;
@@ -18,8 +21,12 @@ interface WorkflowAssistantProps {
18
21
  /** Current host-owned scope. Keep it visible while the summaries scroll. */
19
22
  context: string;
20
23
  description?: ReactNode;
21
- /** Saved action receipts and clarification questions, newest last. Read-only. */
24
+ /** Saved action receipts and clarification questions, newest last. Read-only.
25
+ * Hidden until someone opens them: the voice already confirms each action. */
22
26
  summaries: WorkflowSummary[];
27
+ /** Whether the summaries show. Leave unset for the panel to keep it (closed at first). */
28
+ summariesOpen?: boolean;
29
+ onSummariesOpenChange?: (open: boolean) => void;
23
30
  /** Minimizing preserves this host-owned session; only End calls onEnd. */
24
31
  voiceSession: VoiceSessionControlsProps;
25
32
  pending?: boolean;
@@ -33,6 +40,8 @@ interface WorkflowAssistantProps {
33
40
  expand?: string;
34
41
  collapse?: string;
35
42
  end?: string;
43
+ showSummaries?: string;
44
+ hideSummaries?: string;
36
45
  };
37
46
  className?: string;
38
47
  }
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  "use client";
3
3
  import {
4
+ VoiceSessionAction,
4
5
  VoiceSessionControls
5
- } from "../chunk-PL2462GA.js";
6
+ } from "../chunk-6O3DUMF7.js";
6
7
  import "../chunk-5OF6D3GH.js";
7
8
  import {
8
9
  Button
@@ -13,9 +14,9 @@ import {
13
14
  import "../chunk-GJ7S3L6R.js";
14
15
 
15
16
  // src/primitives/workflow-assistant.tsx
16
- import { forwardRef, useEffect, useId, useRef } from "react";
17
- import { AudioLines, ChevronDown, Maximize2, Minimize2, Square } from "lucide-react";
18
- import { jsx, jsxs } from "react/jsx-runtime";
17
+ import { forwardRef, useEffect, useId, useRef, useState } from "react";
18
+ import { AudioLines, ChevronDown, ListChecks, Maximize2, Minimize2, Square } from "lucide-react";
19
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
20
  var SESSION_LABELS = {
20
21
  disconnected: "Voice chat",
21
22
  connecting: "Connecting\u2026",
@@ -34,6 +35,8 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
34
35
  context,
35
36
  description,
36
37
  summaries,
38
+ summariesOpen,
39
+ onSummariesOpenChange,
37
40
  voiceSession,
38
41
  pending,
39
42
  disabled,
@@ -44,6 +47,13 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
44
47
  }, ref) {
45
48
  const titleId = useId();
46
49
  const statusId = useId();
50
+ const summariesId = useId();
51
+ const [ownSummariesOpen, setOwnSummariesOpen] = useState(false);
52
+ const showingSummaries = (summariesOpen ?? ownSummariesOpen) && summaries.length > 0;
53
+ const setSummariesOpen = (next) => {
54
+ setOwnSummariesOpen(next);
55
+ onSummariesOpenChange?.(next);
56
+ };
47
57
  const launcher = useRef(null);
48
58
  const heading = useRef(null);
49
59
  const moveFocus = useRef(false);
@@ -108,61 +118,82 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
108
118
  ]
109
119
  }
110
120
  );
111
- return /* @__PURE__ */ jsxs(
112
- "aside",
113
- {
114
- ref,
115
- "data-slot": "workflow-assistant",
116
- "aria-labelledby": titleId,
117
- className: cn(
118
- "fixed inset-x-2 bottom-[calc(1rem+env(safe-area-inset-bottom))] z-40 flex min-h-0 flex-col overflow-y-auto overscroll-contain rounded-2xl border bg-background shadow-xl sm:inset-x-auto sm:right-6 sm:w-[28rem] md:right-8",
119
- expanded ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
120
- className
121
- ),
122
- children: [
123
- /* @__PURE__ */ jsxs("div", { className: "sticky top-0 z-10 flex shrink-0 items-start gap-2 border-b bg-background p-3", children: [
124
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
125
- /* @__PURE__ */ jsx("h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold", children: title }),
126
- /* @__PURE__ */ jsx("p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
121
+ return (
122
+ // On a phone the panel is a sheet docked to the bottom edge, like Drawer:
123
+ // its background runs under the home indicator (and a browser's floating
124
+ // toolbar) with its content kept above them. A floating card there left
125
+ // the page showing beneath it. From sm it is a card in the corner.
126
+ /* @__PURE__ */ jsxs(
127
+ "aside",
128
+ {
129
+ ref,
130
+ "data-slot": "workflow-assistant",
131
+ "aria-labelledby": titleId,
132
+ className: cn(
133
+ "fixed inset-x-0 bottom-0 z-40 flex min-h-0 flex-col overflow-y-auto overscroll-contain rounded-t-[10px] border-t bg-background pb-[env(safe-area-inset-bottom)] shadow-[0_-8px_30px_rgb(0_0_0/0.12)]",
134
+ "sm:inset-x-auto sm:bottom-[calc(1rem+env(safe-area-inset-bottom))] sm:right-6 sm:w-[28rem] sm:rounded-2xl sm:border sm:pb-0 sm:shadow-xl md:right-8",
135
+ expanded && showingSummaries ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
136
+ className
137
+ ),
138
+ children: [
139
+ /* @__PURE__ */ jsxs("div", { className: "sticky top-0 z-10 flex shrink-0 items-start gap-2 border-b bg-background p-3", children: [
140
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
141
+ /* @__PURE__ */ jsx("h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold outline-none", children: title }),
142
+ /* @__PURE__ */ jsx("p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
143
+ ] }),
144
+ onExpandedChange && showingSummaries && /* @__PURE__ */ jsx(
145
+ Button,
146
+ {
147
+ type: "button",
148
+ variant: "ghost",
149
+ size: "icon",
150
+ "aria-label": expanded ? labels?.collapse ?? "Reduce panel" : labels?.expand ?? "Expand panel",
151
+ onClick: () => onExpandedChange(!expanded),
152
+ children: expanded ? /* @__PURE__ */ jsx(Minimize2, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Maximize2, { "aria-hidden": "true" })
153
+ }
154
+ ),
155
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": labels?.minimize ?? "Minimize voice review", onClick: () => toggle(false), children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true" }) })
127
156
  ] }),
128
- onExpandedChange && /* @__PURE__ */ jsx(
129
- Button,
157
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", "data-slot": "workflow-assistant-voice", children: /* @__PURE__ */ jsx(
158
+ VoiceSessionControls,
130
159
  {
131
- type: "button",
132
- variant: "ghost",
133
- size: "icon",
134
- "aria-label": expanded ? labels?.collapse ?? "Reduce panel" : labels?.expand ?? "Expand panel",
135
- onClick: () => onExpandedChange(!expanded),
136
- children: expanded ? /* @__PURE__ */ jsx(Minimize2, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Maximize2, { "aria-hidden": "true" })
160
+ ...voiceSession,
161
+ disabled: Boolean(startDisabled),
162
+ size: "hero",
163
+ onEnd: () => {
164
+ heading.current?.focus();
165
+ voiceSession.onEnd();
166
+ },
167
+ actions: summaries.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
168
+ voiceSession.actions,
169
+ /* @__PURE__ */ jsx(
170
+ VoiceSessionAction,
171
+ {
172
+ label: showingSummaries ? labels?.hideSummaries ?? "Hide actions" : labels?.showSummaries ?? "Show actions",
173
+ icon: /* @__PURE__ */ jsx(ListChecks, { "aria-hidden": "true" }),
174
+ "aria-expanded": showingSummaries,
175
+ "aria-controls": summariesId,
176
+ className: "aria-expanded:bg-muted",
177
+ onClick: () => setSummariesOpen(!showingSummaries)
178
+ }
179
+ )
180
+ ] }) : voiceSession.actions,
181
+ className: cn(!active && "justify-center px-4 py-6 text-center")
137
182
  }
138
- ),
139
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": labels?.minimize ?? "Minimize voice review", onClick: () => toggle(false), children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true" }) })
140
- ] }),
141
- /* @__PURE__ */ jsx("div", { className: "shrink-0", "data-slot": "workflow-assistant-voice", children: /* @__PURE__ */ jsx(
142
- VoiceSessionControls,
143
- {
144
- ...voiceSession,
145
- disabled: Boolean(startDisabled),
146
- size: "hero",
147
- onEnd: () => {
148
- heading.current?.focus();
149
- voiceSession.onEnd();
150
- },
151
- className: cn(!active && "justify-center px-4 py-6 text-center")
152
- }
153
- ) }),
154
- description && /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
155
- error && (voiceSession.state !== "error" || error !== voiceSession.error) && /* @__PURE__ */ jsx("p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
156
- pending && /* @__PURE__ */ jsx("p", { role: "status", className: "shrink-0 px-4 py-2 text-sm text-muted-foreground", children: "Applying your instructions\u2026" }),
157
- summaries.length > 0 && /* @__PURE__ */ jsxs("section", { "aria-label": "Action summaries", className: "min-h-20 flex-1 overflow-y-auto border-t px-4 py-3", children: [
158
- /* @__PURE__ */ jsx("h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
159
- /* @__PURE__ */ jsx("ol", { className: "space-y-3", "aria-live": "polite", "aria-relevant": "additions", children: [...summaries].reverse().map((summary) => /* @__PURE__ */ jsxs("li", { className: "space-y-1 border-b pb-3 last:border-0 last:pb-0", children: [
160
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: summary.context }),
161
- /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-sm", children: summary.body })
162
- ] }, summary.id)) })
163
- ] })
164
- ]
165
- }
183
+ ) }),
184
+ description && /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
185
+ error && (voiceSession.state !== "error" || error !== voiceSession.error) && /* @__PURE__ */ jsx("p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
186
+ pending && /* @__PURE__ */ jsx("p", { role: "status", className: "shrink-0 px-4 py-2 text-sm text-muted-foreground", children: "Applying your instructions\u2026" }),
187
+ summaries.length > 0 && /* @__PURE__ */ jsxs("section", { id: summariesId, hidden: !showingSummaries, "aria-label": "Action summaries", className: "min-h-20 flex-1 overflow-y-auto border-t px-4 py-3", children: [
188
+ /* @__PURE__ */ jsx("h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
189
+ /* @__PURE__ */ jsx("ol", { className: "space-y-3", "aria-live": "polite", "aria-relevant": "additions", children: [...summaries].reverse().map((summary) => /* @__PURE__ */ jsxs("li", { className: "space-y-1 border-b pb-3 last:border-0 last:pb-0", children: [
190
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: summary.context }),
191
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-sm", children: summary.body })
192
+ ] }, summary.id)) })
193
+ ] })
194
+ ]
195
+ }
196
+ )
166
197
  );
167
198
  });
168
199
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicbagel/saas-kit",
3
- "version": "1.24.0",
3
+ "version": "1.25.1",
4
4
  "description": "Accessible React primitives, layout components and design tokens for SaaS apps, built on Radix UI and Tailwind CSS v4.",
5
5
  "license": "MIT",
6
6
  "keywords": [