@epicbagel/saas-kit 1.24.0 → 1.25.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.
@@ -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,10 +108,10 @@ 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
  )
@@ -116,7 +126,7 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
116
126
  "aria-labelledby": titleId,
117
127
  className: _chunkKWLSBTB2cjs.cn.call(void 0,
118
128
  "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)]",
129
+ expanded && showingSummaries ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
120
130
  className
121
131
  ),
122
132
  children: [
@@ -125,36 +135,50 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
125
135
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold", children: title }),
126
136
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
127
137
  ] }),
128
- onExpandedChange && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
138
+ onExpandedChange && showingSummaries && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
129
139
  _chunkJDEXZGIYcjs.Button,
130
140
  {
131
141
  type: "button",
132
142
  variant: "ghost",
133
143
  size: "icon",
134
- "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _14 => _14.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _15 => _15.expand]), () => ( "Expand panel")),
144
+ "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _15 => _15.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _16 => _16.expand]), () => ( "Expand panel")),
135
145
  onClick: () => onExpandedChange(!expanded),
136
146
  children: expanded ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Minimize2, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Maximize2, { "aria-hidden": "true" })
137
147
  }
138
148
  ),
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" }) })
149
+ /* @__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" }) })
140
150
  ] }),
141
151
  /* @__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,
152
+ _chunkPNI354BYcjs.VoiceSessionControls,
143
153
  {
144
154
  ...voiceSession,
145
155
  disabled: Boolean(startDisabled),
146
156
  size: "hero",
147
157
  onEnd: () => {
148
- _optionalChain([heading, 'access', _17 => _17.current, 'optionalAccess', _18 => _18.focus, 'call', _19 => _19()]);
158
+ _optionalChain([heading, 'access', _18 => _18.current, 'optionalAccess', _19 => _19.focus, 'call', _20 => _20()]);
149
159
  voiceSession.onEnd();
150
160
  },
161
+ actions: summaries.length > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
162
+ voiceSession.actions,
163
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
164
+ _chunkPNI354BYcjs.VoiceSessionAction,
165
+ {
166
+ label: showingSummaries ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _21 => _21.hideSummaries]), () => ( "Hide actions")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _22 => _22.showSummaries]), () => ( "Show actions")),
167
+ icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ListChecks, { "aria-hidden": "true" }),
168
+ "aria-expanded": showingSummaries,
169
+ "aria-controls": summariesId,
170
+ className: "aria-expanded:bg-muted",
171
+ onClick: () => setSummariesOpen(!showingSummaries)
172
+ }
173
+ )
174
+ ] }) : voiceSession.actions,
151
175
  className: _chunkKWLSBTB2cjs.cn.call(void 0, !active && "justify-center px-4 py-6 text-center")
152
176
  }
153
177
  ) }),
154
178
  description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
155
179
  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
180
  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: [
181
+ 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: [
158
182
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
159
183
  /* @__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
184
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-xs text-muted-foreground", children: summary.context }),
@@ -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);
@@ -116,7 +126,7 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
116
126
  "aria-labelledby": titleId,
117
127
  className: cn(
118
128
  "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)]",
129
+ expanded && showingSummaries ? "h-[calc(100dvh-6rem)]" : "max-h-[min(36rem,70dvh)]",
120
130
  className
121
131
  ),
122
132
  children: [
@@ -125,7 +135,7 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
125
135
  /* @__PURE__ */ jsx("h2", { ref: heading, tabIndex: -1, id: titleId, className: "text-sm font-semibold", children: title }),
126
136
  /* @__PURE__ */ jsx("p", { title: context, className: "mt-1 truncate text-xs text-muted-foreground", children: context })
127
137
  ] }),
128
- onExpandedChange && /* @__PURE__ */ jsx(
138
+ onExpandedChange && showingSummaries && /* @__PURE__ */ jsx(
129
139
  Button,
130
140
  {
131
141
  type: "button",
@@ -148,13 +158,27 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
148
158
  heading.current?.focus();
149
159
  voiceSession.onEnd();
150
160
  },
161
+ actions: summaries.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
162
+ voiceSession.actions,
163
+ /* @__PURE__ */ jsx(
164
+ VoiceSessionAction,
165
+ {
166
+ label: showingSummaries ? labels?.hideSummaries ?? "Hide actions" : labels?.showSummaries ?? "Show actions",
167
+ icon: /* @__PURE__ */ jsx(ListChecks, { "aria-hidden": "true" }),
168
+ "aria-expanded": showingSummaries,
169
+ "aria-controls": summariesId,
170
+ className: "aria-expanded:bg-muted",
171
+ onClick: () => setSummariesOpen(!showingSummaries)
172
+ }
173
+ )
174
+ ] }) : voiceSession.actions,
151
175
  className: cn(!active && "justify-center px-4 py-6 text-center")
152
176
  }
153
177
  ) }),
154
178
  description && /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 py-3 text-sm text-muted-foreground", children: description }),
155
179
  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
180
  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: [
181
+ 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: [
158
182
  /* @__PURE__ */ jsx("h3", { className: "mb-3 text-xs font-medium text-muted-foreground", children: "Action summaries" }),
159
183
  /* @__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
184
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: summary.context }),
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.0",
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": [