@epicbagel/saas-kit 1.21.0 → 1.23.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.
@@ -2,11 +2,11 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkYMI3X2YLcjs = require('../chunk-YMI3X2YL.cjs');
5
+ var _chunkHETZNLKTcjs = require('../chunk-HETZNLKT.cjs');
6
6
  require('../chunk-NKWHDI4C.cjs');
7
7
  require('../chunk-JDEXZGIY.cjs');
8
8
  require('../chunk-KWLSBTB2.cjs');
9
9
  require('../chunk-2BSFYU22.cjs');
10
10
 
11
11
 
12
- exports.VoiceSessionControls = _chunkYMI3X2YLcjs.VoiceSessionControls;
12
+ exports.VoiceSessionControls = _chunkHETZNLKTcjs.VoiceSessionControls;
@@ -22,10 +22,18 @@ interface VoiceSessionControlsProps {
22
22
  /** Optional external audio store, so level changes only re-render these controls. */
23
23
  audioLevels?: VoiceAudioLevelsSource;
24
24
  labels?: Partial<Record<VoiceSessionState | "start" | "end" | "mute" | "unmute" | "muted" | "retry" | "endShort" | "muteShort" | "unmuteShort" | "transcriptPlaceholder", string>>;
25
+ /**
26
+ * Called when someone is about to start (the start control is hovered,
27
+ * pressed or focused), before the click lands. Hosts load their voice
28
+ * transport here so starting doesn't wait for it. It must never touch the
29
+ * microphone: capture belongs to `onStart`, so warming never prompts for
30
+ * permission. It can fire several times; make it idempotent.
31
+ */
32
+ onWarm?: () => void;
25
33
  size?: "icon" | "hero";
26
34
  className?: string;
27
35
  }
28
36
  /** Transport-independent voice controls. The conversation stays visible while speaking. */
29
- declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
37
+ declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
30
38
 
31
39
  export { type VoiceAudioLevelsSource, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
@@ -22,10 +22,18 @@ interface VoiceSessionControlsProps {
22
22
  /** Optional external audio store, so level changes only re-render these controls. */
23
23
  audioLevels?: VoiceAudioLevelsSource;
24
24
  labels?: Partial<Record<VoiceSessionState | "start" | "end" | "mute" | "unmute" | "muted" | "retry" | "endShort" | "muteShort" | "unmuteShort" | "transcriptPlaceholder", string>>;
25
+ /**
26
+ * Called when someone is about to start (the start control is hovered,
27
+ * pressed or focused), before the click lands. Hosts load their voice
28
+ * transport here so starting doesn't wait for it. It must never touch the
29
+ * microphone: capture belongs to `onStart`, so warming never prompts for
30
+ * permission. It can fire several times; make it idempotent.
31
+ */
32
+ onWarm?: () => void;
25
33
  size?: "icon" | "hero";
26
34
  className?: string;
27
35
  }
28
36
  /** Transport-independent voice controls. The conversation stays visible while speaking. */
29
- declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
37
+ declare function VoiceSessionControls({ state, onStart, onEnd, muted, onMutedChange, disabled, error, volumeBands, audioLevels, labels, onWarm, size, className, }: VoiceSessionControlsProps): react_jsx_runtime.JSX.Element;
30
38
 
31
39
  export { type VoiceAudioLevelsSource, VoiceSessionControls, type VoiceSessionControlsProps, type VoiceSessionState };
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  VoiceSessionControls
5
- } from "../chunk-KF4X37DP.js";
5
+ } from "../chunk-PL2462GA.js";
6
6
  import "../chunk-5OF6D3GH.js";
7
7
  import "../chunk-W326GKML.js";
8
8
  import "../chunk-G6SRSWXB.js";
@@ -2,32 +2,7 @@
2
2
  "use client";
3
3
 
4
4
 
5
-
6
- var _chunk4YJ6DGPLcjs = require('../chunk-4YJ6DGPL.cjs');
7
- require('../chunk-2BTDBZYN.cjs');
8
- require('../chunk-PH2TV7XI.cjs');
9
- require('../chunk-YMI3X2YL.cjs');
10
- require('../chunk-M4V7OJNF.cjs');
11
- require('../chunk-B765DSRG.cjs');
12
- require('../chunk-23IETCLV.cjs');
13
- require('../chunk-DMPSKGFM.cjs');
14
- require('../chunk-NWDPH2Z7.cjs');
15
- require('../chunk-Z2H4LJVG.cjs');
16
- require('../chunk-7LDJAXRX.cjs');
17
- require('../chunk-2HV2KKOE.cjs');
18
- require('../chunk-ZLEM2SAU.cjs');
19
- require('../chunk-KENBU3NV.cjs');
20
- require('../chunk-ELPV44S6.cjs');
21
- require('../chunk-2BHI5ZWJ.cjs');
22
- require('../chunk-ZAITUDYD.cjs');
23
- require('../chunk-U2NES7VR.cjs');
24
- require('../chunk-V4YWTRLD.cjs');
25
- require('../chunk-373W6VAD.cjs');
26
- require('../chunk-W5HPHEE7.cjs');
27
- require('../chunk-FYRQFEU4.cjs');
28
- require('../chunk-2KFZQJH5.cjs');
29
- require('../chunk-PQUSJEXL.cjs');
30
- require('../chunk-WABCLRBR.cjs');
5
+ var _chunkHETZNLKTcjs = require('../chunk-HETZNLKT.cjs');
31
6
  require('../chunk-NKWHDI4C.cjs');
32
7
 
33
8
 
@@ -36,10 +11,6 @@ var _chunkJDEXZGIYcjs = require('../chunk-JDEXZGIY.cjs');
36
11
 
37
12
  var _chunkKWLSBTB2cjs = require('../chunk-KWLSBTB2.cjs');
38
13
  require('../chunk-2BSFYU22.cjs');
39
- require('../chunk-4LXN6GLZ.cjs');
40
- require('../chunk-7Q4EFCUI.cjs');
41
- require('../chunk-R35FT53G.cjs');
42
- require('../chunk-H2UFRLLN.cjs');
43
14
 
44
15
  // src/primitives/workflow-assistant.tsx
45
16
  var _react = require('react');
@@ -62,15 +33,11 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
62
33
  title = "Voice review",
63
34
  context,
64
35
  description,
65
- messages,
66
- value,
67
- onChange,
68
- onSend,
36
+ summaries,
69
37
  voiceSession,
70
38
  pending,
71
39
  disabled,
72
40
  error,
73
- placeholder = "Describe an action\u2026",
74
41
  besideCornerAction = false,
75
42
  labels,
76
43
  className
@@ -89,9 +56,10 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
89
56
  moveFocus.current = true;
90
57
  onOpenChange(next);
91
58
  };
92
- const startDisabled = pending || disabled || _optionalChain([voiceSession, 'optionalAccess', _3 => _3.disabled]);
93
- const active = voiceSession && voiceSession.state !== "disconnected" && voiceSession.state !== "error";
94
- const status = voiceSession && (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])));
59
+ const startDisabled = pending || disabled || voiceSession.disabled;
60
+ 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]));
62
+ const unavailable = voiceSession.state === "error" && voiceSession.disabled;
95
63
  if (!open) return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
96
64
  "aside",
97
65
  {
@@ -107,17 +75,17 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
107
75
  type: "button",
108
76
  shape: "pill",
109
77
  className: "min-h-12 min-w-12 max-w-full gap-2 shadow-lg",
110
- disabled: startDisabled && !active,
111
- "aria-label": active ? title : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _8 => _8.start]), () => ( "Start voice review")),
78
+ disabled: startDisabled && !active && !unavailable,
79
+ "aria-label": active ? title : unavailable ? "Voice review unavailable" : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _7 => _7.start]), () => ( "Start voice review")),
112
80
  "aria-expanded": false,
113
81
  "aria-describedby": active ? statusId : void 0,
114
82
  onClick: () => {
115
83
  toggle(true);
116
- if (!active && !startDisabled) _optionalChain([voiceSession, 'optionalAccess', _9 => _9.onStart, 'call', _10 => _10()]);
84
+ if (!active && !startDisabled) voiceSession.onStart();
117
85
  },
118
86
  children: [
119
87
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.AudioLines, { "aria-hidden": "true", className: "shrink-0" }),
120
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { id: statusId, role: active ? "status" : void 0, className: "truncate", children: active ? status : title })
88
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { id: statusId, role: active ? "status" : void 0, className: "truncate", children: active ? status : unavailable ? "Voice unavailable" : title })
121
89
  ]
122
90
  }
123
91
  ),
@@ -130,10 +98,10 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
130
98
  size: "icon",
131
99
  className: "min-h-12 min-w-12 bg-background shadow-lg",
132
100
  onClick: () => {
133
- _optionalChain([launcher, 'access', _11 => _11.current, 'optionalAccess', _12 => _12.focus, 'call', _13 => _13()]);
101
+ _optionalChain([launcher, 'access', _8 => _8.current, 'optionalAccess', _9 => _9.focus, 'call', _10 => _10()]);
134
102
  voiceSession.onEnd();
135
103
  },
136
- "aria-label": _nullishCoalesce(_nullishCoalesce(_optionalChain([labels, 'optionalAccess', _14 => _14.end]), () => ( _optionalChain([voiceSession, 'access', _15 => _15.labels, 'optionalAccess', _16 => _16.end]))), () => ( "End voice chat")),
104
+ "aria-label": _nullishCoalesce(_nullishCoalesce(_optionalChain([labels, 'optionalAccess', _11 => _11.end]), () => ( _optionalChain([voiceSession, 'access', _12 => _12.labels, 'optionalAccess', _13 => _13.end]))), () => ( "End voice chat")),
137
105
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Square, { "aria-hidden": "true" })
138
106
  }
139
107
  )
@@ -163,38 +131,36 @@ var WorkflowAssistant = _react.forwardRef.call(void 0, function WorkflowAssistan
163
131
  type: "button",
164
132
  variant: "ghost",
165
133
  size: "icon",
166
- "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _17 => _17.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _18 => _18.expand]), () => ( "Expand panel")),
134
+ "aria-label": expanded ? _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _14 => _14.collapse]), () => ( "Reduce panel")) : _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _15 => _15.expand]), () => ( "Expand panel")),
167
135
  onClick: () => onExpandedChange(!expanded),
168
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" })
169
137
  }
170
138
  ),
171
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "ghost", size: "icon", "aria-label": _nullishCoalesce(_optionalChain([labels, 'optionalAccess', _19 => _19.minimize]), () => ( "Minimize voice review")), onClick: () => toggle(false), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ChevronDown, { "aria-hidden": "true" }) })
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" }) })
172
140
  ] }),
173
- description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0 px-4 py-2 text-xs text-muted-foreground", children: description }),
174
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
175
- _chunk4YJ6DGPLcjs.ChatThread,
176
- {
177
- messages,
178
- pending,
179
- maxHeight: expanded ? "60dvh" : "32dvh",
180
- className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex-1 px-3", messages.length > 0 || pending ? "min-h-24" : "min-h-0")
181
- }
182
- ),
183
- error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
184
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "shrink-0 p-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
185
- _chunk4YJ6DGPLcjs.ChatInput,
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,
186
143
  {
187
- value,
188
- onChange,
189
- onSend,
190
- disabled,
191
- pending,
192
- placeholder,
193
- size: "sm",
194
- voice: false,
195
- voiceSession
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")
196
152
  }
197
- ) })
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
+ ] })
198
164
  ]
199
165
  }
200
166
  );
@@ -1,31 +1,30 @@
1
1
  import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { ChatMessage } from './chat.cjs';
4
3
  import { VoiceSessionControlsProps } from './voice-session-controls.cjs';
5
4
  import 'react/jsx-runtime';
6
- import './markdown.cjs';
7
- import 'react-markdown';
8
- import './rich-text.cjs';
9
5
 
6
+ interface WorkflowSummary {
7
+ id: string;
8
+ /** Scope at the time of the action, which may differ from the current list. */
9
+ context: string;
10
+ body: string;
11
+ }
10
12
  interface WorkflowAssistantProps {
11
13
  open: boolean;
12
14
  onOpenChange: (open: boolean) => void;
13
15
  expanded?: boolean;
14
16
  onExpandedChange?: (expanded: boolean) => void;
15
17
  title?: string;
16
- /** Current host-owned scope. Keep it visible while the transcript scrolls. */
18
+ /** Current host-owned scope. Keep it visible while the summaries scroll. */
17
19
  context: string;
18
20
  description?: ReactNode;
19
- messages: ChatMessage[];
20
- value: string;
21
- onChange: (value: string) => void;
22
- onSend: (value: string) => void;
21
+ /** Saved action receipts and clarification questions, newest last. Read-only. */
22
+ summaries: WorkflowSummary[];
23
23
  /** Minimizing preserves this host-owned session; only End calls onEnd. */
24
- voiceSession?: VoiceSessionControlsProps;
24
+ voiceSession: VoiceSessionControlsProps;
25
25
  pending?: boolean;
26
26
  disabled?: boolean;
27
27
  error?: string | null;
28
- placeholder?: string;
29
28
  /** Leave room for an existing corner action such as New. */
30
29
  besideCornerAction?: boolean;
31
30
  labels?: {
@@ -37,8 +36,8 @@ interface WorkflowAssistantProps {
37
36
  };
38
37
  className?: string;
39
38
  }
40
- /** A non-modal workflow conversation: the underlying list stays usable.
39
+ /** Voice-only workflow controls: the underlying list stays usable.
41
40
  * The host owns scope, authorization, persistence and the voice connection. */
42
41
  declare const WorkflowAssistant: React.ForwardRefExoticComponent<WorkflowAssistantProps & React.RefAttributes<HTMLElement>>;
43
42
 
44
- export { WorkflowAssistant, type WorkflowAssistantProps };
43
+ export { WorkflowAssistant, type WorkflowAssistantProps, type WorkflowSummary };
@@ -1,31 +1,30 @@
1
1
  import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { ChatMessage } from './chat.js';
4
3
  import { VoiceSessionControlsProps } from './voice-session-controls.js';
5
4
  import 'react/jsx-runtime';
6
- import './markdown.js';
7
- import 'react-markdown';
8
- import './rich-text.js';
9
5
 
6
+ interface WorkflowSummary {
7
+ id: string;
8
+ /** Scope at the time of the action, which may differ from the current list. */
9
+ context: string;
10
+ body: string;
11
+ }
10
12
  interface WorkflowAssistantProps {
11
13
  open: boolean;
12
14
  onOpenChange: (open: boolean) => void;
13
15
  expanded?: boolean;
14
16
  onExpandedChange?: (expanded: boolean) => void;
15
17
  title?: string;
16
- /** Current host-owned scope. Keep it visible while the transcript scrolls. */
18
+ /** Current host-owned scope. Keep it visible while the summaries scroll. */
17
19
  context: string;
18
20
  description?: ReactNode;
19
- messages: ChatMessage[];
20
- value: string;
21
- onChange: (value: string) => void;
22
- onSend: (value: string) => void;
21
+ /** Saved action receipts and clarification questions, newest last. Read-only. */
22
+ summaries: WorkflowSummary[];
23
23
  /** Minimizing preserves this host-owned session; only End calls onEnd. */
24
- voiceSession?: VoiceSessionControlsProps;
24
+ voiceSession: VoiceSessionControlsProps;
25
25
  pending?: boolean;
26
26
  disabled?: boolean;
27
27
  error?: string | null;
28
- placeholder?: string;
29
28
  /** Leave room for an existing corner action such as New. */
30
29
  besideCornerAction?: boolean;
31
30
  labels?: {
@@ -37,8 +36,8 @@ interface WorkflowAssistantProps {
37
36
  };
38
37
  className?: string;
39
38
  }
40
- /** A non-modal workflow conversation: the underlying list stays usable.
39
+ /** Voice-only workflow controls: the underlying list stays usable.
41
40
  * The host owns scope, authorization, persistence and the voice connection. */
42
41
  declare const WorkflowAssistant: React.ForwardRefExoticComponent<WorkflowAssistantProps & React.RefAttributes<HTMLElement>>;
43
42
 
44
- export { WorkflowAssistant, type WorkflowAssistantProps };
43
+ export { WorkflowAssistant, type WorkflowAssistantProps, type WorkflowSummary };
@@ -1,33 +1,8 @@
1
1
  "use client";
2
2
  "use client";
3
3
  import {
4
- ChatInput,
5
- ChatThread
6
- } from "../chunk-C23Q4FRM.js";
7
- import "../chunk-Z45YKVAM.js";
8
- import "../chunk-ZGTPNSR7.js";
9
- import "../chunk-KF4X37DP.js";
10
- import "../chunk-IJG2B3Y2.js";
11
- import "../chunk-IW4ZIXXG.js";
12
- import "../chunk-OUZSIA5G.js";
13
- import "../chunk-36LNA4WM.js";
14
- import "../chunk-H4XH2PPU.js";
15
- import "../chunk-GA5SX7FE.js";
16
- import "../chunk-QTPPNK6F.js";
17
- import "../chunk-6FC65AZQ.js";
18
- import "../chunk-WHDHUYS6.js";
19
- import "../chunk-VY5U4VGV.js";
20
- import "../chunk-INNGGLMZ.js";
21
- import "../chunk-4HWCNRK4.js";
22
- import "../chunk-V4LE2HWP.js";
23
- import "../chunk-ZRLUUZHK.js";
24
- import "../chunk-F366BHWH.js";
25
- import "../chunk-EIY5LLSP.js";
26
- import "../chunk-NBUWGRQO.js";
27
- import "../chunk-CYDICVH2.js";
28
- import "../chunk-42RTTHND.js";
29
- import "../chunk-QMA3WVB7.js";
30
- import "../chunk-3OWGL4Q2.js";
4
+ VoiceSessionControls
5
+ } from "../chunk-PL2462GA.js";
31
6
  import "../chunk-5OF6D3GH.js";
32
7
  import {
33
8
  Button
@@ -36,10 +11,6 @@ import {
36
11
  cn
37
12
  } from "../chunk-G6SRSWXB.js";
38
13
  import "../chunk-GJ7S3L6R.js";
39
- import "../chunk-ZWZAAGQF.js";
40
- import "../chunk-RPSRCB5O.js";
41
- import "../chunk-PIU5SSUV.js";
42
- import "../chunk-CS7DM3NV.js";
43
14
 
44
15
  // src/primitives/workflow-assistant.tsx
45
16
  import { forwardRef, useEffect, useId, useRef } from "react";
@@ -62,15 +33,11 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
62
33
  title = "Voice review",
63
34
  context,
64
35
  description,
65
- messages,
66
- value,
67
- onChange,
68
- onSend,
36
+ summaries,
69
37
  voiceSession,
70
38
  pending,
71
39
  disabled,
72
40
  error,
73
- placeholder = "Describe an action\u2026",
74
41
  besideCornerAction = false,
75
42
  labels,
76
43
  className
@@ -89,9 +56,10 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
89
56
  moveFocus.current = true;
90
57
  onOpenChange(next);
91
58
  };
92
- const startDisabled = pending || disabled || voiceSession?.disabled;
93
- const active = voiceSession && voiceSession.state !== "disconnected" && voiceSession.state !== "error";
94
- const status = voiceSession && (voiceSession.muted && voiceSession.state === "listening" ? voiceSession.labels?.muted ?? "Microphone muted" : voiceSession.labels?.[voiceSession.state] ?? SESSION_LABELS[voiceSession.state]);
59
+ const startDisabled = pending || disabled || voiceSession.disabled;
60
+ const active = voiceSession.state !== "disconnected" && voiceSession.state !== "error";
61
+ const status = voiceSession.muted && voiceSession.state === "listening" ? voiceSession.labels?.muted ?? "Microphone muted" : voiceSession.labels?.[voiceSession.state] ?? SESSION_LABELS[voiceSession.state];
62
+ const unavailable = voiceSession.state === "error" && voiceSession.disabled;
95
63
  if (!open) return /* @__PURE__ */ jsxs(
96
64
  "aside",
97
65
  {
@@ -107,17 +75,17 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
107
75
  type: "button",
108
76
  shape: "pill",
109
77
  className: "min-h-12 min-w-12 max-w-full gap-2 shadow-lg",
110
- disabled: startDisabled && !active,
111
- "aria-label": active ? title : labels?.start ?? "Start voice review",
78
+ disabled: startDisabled && !active && !unavailable,
79
+ "aria-label": active ? title : unavailable ? "Voice review unavailable" : labels?.start ?? "Start voice review",
112
80
  "aria-expanded": false,
113
81
  "aria-describedby": active ? statusId : void 0,
114
82
  onClick: () => {
115
83
  toggle(true);
116
- if (!active && !startDisabled) voiceSession?.onStart();
84
+ if (!active && !startDisabled) voiceSession.onStart();
117
85
  },
118
86
  children: [
119
87
  /* @__PURE__ */ jsx(AudioLines, { "aria-hidden": "true", className: "shrink-0" }),
120
- /* @__PURE__ */ jsx("span", { id: statusId, role: active ? "status" : void 0, className: "truncate", children: active ? status : title })
88
+ /* @__PURE__ */ jsx("span", { id: statusId, role: active ? "status" : void 0, className: "truncate", children: active ? status : unavailable ? "Voice unavailable" : title })
121
89
  ]
122
90
  }
123
91
  ),
@@ -170,31 +138,29 @@ var WorkflowAssistant = forwardRef(function WorkflowAssistant2({
170
138
  ),
171
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" }) })
172
140
  ] }),
173
- description && /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 py-2 text-xs text-muted-foreground", children: description }),
174
- /* @__PURE__ */ jsx(
175
- ChatThread,
141
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", "data-slot": "workflow-assistant-voice", children: /* @__PURE__ */ jsx(
142
+ VoiceSessionControls,
176
143
  {
177
- messages,
178
- pending,
179
- maxHeight: expanded ? "60dvh" : "32dvh",
180
- className: cn("flex-1 px-3", messages.length > 0 || pending ? "min-h-24" : "min-h-0")
181
- }
182
- ),
183
- error && /* @__PURE__ */ jsx("p", { role: "alert", className: "shrink-0 px-4 py-2 text-sm text-destructive", children: error }),
184
- /* @__PURE__ */ jsx("div", { className: "shrink-0 p-2", children: /* @__PURE__ */ jsx(
185
- ChatInput,
186
- {
187
- value,
188
- onChange,
189
- onSend,
190
- disabled,
191
- pending,
192
- placeholder,
193
- size: "sm",
194
- voice: false,
195
- voiceSession
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")
196
152
  }
197
- ) })
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
+ ] })
198
164
  ]
199
165
  }
200
166
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicbagel/saas-kit",
3
- "version": "1.21.0",
3
+ "version": "1.23.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": [