@epicbagel/saas-kit 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,20 @@ npm install @epicbagel/saas-kit
15
15
  Peer dependencies: `react` and `react-dom` (18 or 19), `lucide-react`
16
16
  (`>=0.400.0 <2`), `react-icons` (5) and `tailwindcss` (4.3 or later).
17
17
 
18
+ ## Page header actions
19
+
20
+ `PageHeaderActions` expands its actions on desktop and collapses them into
21
+ one menu on mobile. Use `presentation="menu"` to keep the same menu at every
22
+ width, giving long page titles more room. Its action order, nested menus,
23
+ disabled state, destructive treatment and callbacks stay the same.
24
+ `mobileLabel` and `mobileAriaLabel` name the menu trigger in either layout.
25
+
26
+ ```tsx
27
+ <PageHeader title="Senior Enterprise Account Executive · Example Company">
28
+ <PageHeaderActions presentation="menu" actions={actions} />
29
+ </PageHeader>
30
+ ```
31
+
18
32
  ## Live voice in Chat
19
33
 
20
34
  Pass a controlled `voiceSession` to `Chat` or `ChatInput` to show start, mute,
@@ -42,16 +56,20 @@ does not import a voice provider. Stop and release audio tracks before setting
42
56
  Wrap the application in `DictationProvider` to use an authenticated transcription
43
57
  API for every `VoiceDictationButton`, including buttons nested in chat, forms or
44
58
  another component library. The recorder opens beside its microphone on desktop
45
- and fills the screen below 768px, with elapsed time, LiveKit audio bars and
46
- Done/Cancel controls. The bars use the recording stream without opening a second
47
- microphone. Resizing keeps the current recording alive.
59
+ and fills the screen below 768px. Its roomy panel puts the title and close control
60
+ above a microphone, live audio bars and elapsed time, followed by the transcript
61
+ and Done/Cancel controls. The microphone and bars share the palette's first chart
62
+ colour; surfaces and buttons keep their normal theme. The bars use the recording
63
+ stream without opening a second microphone; native recognition has no meter and
64
+ shows no waveform. No device selector is shown because the capture API does not
65
+ offer device selection. Resizing keeps the current recording alive.
48
66
 
49
67
  For hidden/programmatic triggers, it uses the focused visible button as the
50
68
  desktop anchor. Hosts can pass an explicit `anchorRef` for a custom trigger.
51
69
  Without a visible anchor it opens full-screen. Nested and sibling dialogs keep
52
70
  their focus and cancellation behavior.
53
71
  The recorder runs until **Done**, finishes the last words, then commits the returned text once. Failed requests keep the
54
- take available for retry. Cancel stops microphone tracks, aborts the request and
72
+ take available for retry. Cancel and the header's close control stop microphone tracks, abort the request and
55
73
  ignores a late answer. The kit never persists audio or knows provider credentials.
56
74
 
57
75
  ```tsx
@@ -33,13 +33,14 @@ function MenuActions({ actions }) {
33
33
  return actions.map((action) => /* @__PURE__ */ jsxs(Fragment, { children: [
34
34
  action.separatorBefore && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
35
35
  action.items?.length ? /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
36
- /* @__PURE__ */ jsx(DropdownMenuSubTrigger, { disabled: action.disabled, children: /* @__PURE__ */ jsx(MenuActionContent, { action }) }),
36
+ /* @__PURE__ */ jsx(DropdownMenuSubTrigger, { disabled: action.disabled, title: action.title, children: /* @__PURE__ */ jsx(MenuActionContent, { action }) }),
37
37
  /* @__PURE__ */ jsx(DropdownMenuSubContent, { children: /* @__PURE__ */ jsx(MenuActions, { actions: action.items }) })
38
38
  ] }) : /* @__PURE__ */ jsx(
39
39
  DropdownMenuItem,
40
40
  {
41
41
  disabled: action.disabled,
42
42
  tone: action.tone,
43
+ title: action.title,
43
44
  onSelect: action.onSelect,
44
45
  className: action.description ? "items-start" : void 0,
45
46
  children: /* @__PURE__ */ jsx(MenuActionContent, { action })
@@ -49,6 +50,7 @@ function MenuActions({ actions }) {
49
50
  }
50
51
  function PageHeaderActions({
51
52
  actions,
53
+ presentation = "responsive",
52
54
  mobileLabel = "Actions",
53
55
  mobileAriaLabel,
54
56
  className
@@ -68,7 +70,7 @@ function PageHeaderActions({
68
70
  return rank[priorityFor(left.action)] - rank[priorityFor(right.action)] || left.index - right.index;
69
71
  }).map(({ action }) => action) : actions;
70
72
  return /* @__PURE__ */ jsxs("div", { className: cn("shrink-0", className), children: [
71
- /* @__PURE__ */ jsx(
73
+ presentation === "responsive" && /* @__PURE__ */ jsx(
72
74
  "div",
73
75
  {
74
76
  className: "hidden items-center gap-2 sm:flex",
@@ -110,7 +112,7 @@ function PageHeaderActions({
110
112
  )
111
113
  }
112
114
  ),
113
- /* @__PURE__ */ jsx("div", { className: "sm:hidden", "data-slot": "page-header-actions-mobile", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
115
+ /* @__PURE__ */ jsx("div", { className: presentation === "responsive" ? "sm:hidden" : void 0, "data-slot": "page-header-actions-mobile", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
114
116
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", variant: "outline", "aria-label": mobileAriaLabel, children: [
115
117
  mobileLabel,
116
118
  /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true" })
@@ -33,13 +33,14 @@ function MenuActions({ actions }) {
33
33
  return actions.map((action) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _react.Fragment, { children: [
34
34
  action.separatorBefore && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuSeparator, {}),
35
35
  _optionalChain([action, 'access', _ => _.items, 'optionalAccess', _2 => _2.length]) ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZAITUDYDcjs.DropdownMenuSub, { children: [
36
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuSubTrigger, { disabled: action.disabled, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MenuActionContent, { action }) }),
36
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuSubTrigger, { disabled: action.disabled, title: action.title, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MenuActionContent, { action }) }),
37
37
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuSubContent, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MenuActions, { actions: action.items }) })
38
38
  ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
39
39
  _chunkZAITUDYDcjs.DropdownMenuItem,
40
40
  {
41
41
  disabled: action.disabled,
42
42
  tone: action.tone,
43
+ title: action.title,
43
44
  onSelect: action.onSelect,
44
45
  className: action.description ? "items-start" : void 0,
45
46
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MenuActionContent, { action })
@@ -49,6 +50,7 @@ function MenuActions({ actions }) {
49
50
  }
50
51
  function PageHeaderActions({
51
52
  actions,
53
+ presentation = "responsive",
52
54
  mobileLabel = "Actions",
53
55
  mobileAriaLabel,
54
56
  className
@@ -68,7 +70,7 @@ function PageHeaderActions({
68
70
  return rank[priorityFor(left.action)] - rank[priorityFor(right.action)] || left.index - right.index;
69
71
  }).map(({ action }) => action) : actions;
70
72
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0", className), children: [
71
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
73
+ presentation === "responsive" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
72
74
  "div",
73
75
  {
74
76
  className: "hidden items-center gap-2 sm:flex",
@@ -110,7 +112,7 @@ function PageHeaderActions({
110
112
  )
111
113
  }
112
114
  ),
113
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "sm:hidden", "data-slot": "page-header-actions-mobile", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZAITUDYDcjs.DropdownMenu, { children: [
115
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: presentation === "responsive" ? "sm:hidden" : void 0, "data-slot": "page-header-actions-mobile", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZAITUDYDcjs.DropdownMenu, { children: [
114
116
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", size: "sm", variant: "outline", "aria-label": mobileAriaLabel, children: [
115
117
  mobileLabel,
116
118
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ChevronDown, { "aria-hidden": "true" })
@@ -118,40 +118,72 @@ function VoiceDictationButton({
118
118
  }
119
119
  };
120
120
  const content = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
121
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex min-w-0 items-start justify-between gap-3", !anchored && "w-full max-w-xl self-center"), children: [
122
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 space-y-1", children: [
123
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "font-medium", anchored ? "text-sm" : "text-lg"), children: title || "Voice dictation" }),
124
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { role: "status", className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
125
- busy ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.LoaderCircle, { "aria-hidden": "true", className: "size-3.5 animate-spin motion-reduce:animate-none" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { "aria-hidden": "true", className: "size-3.5" }),
121
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex min-w-0 shrink-0 items-start justify-between gap-4", !anchored && "w-full max-w-xl self-center"), children: [
122
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 space-y-2 pt-1", children: [
123
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "break-words text-xl font-semibold leading-tight", children: title || "Voice dictation" }),
124
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { role: "status", className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
125
+ busy && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.LoaderCircle, { "aria-hidden": "true", className: "size-4 shrink-0 animate-spin motion-reduce:animate-none" }),
126
126
  capture.state === "transcribing" ? "Finishing transcript\u2026" : capture.state === "requesting" ? "Starting dictation\u2026" : error ? "Recording not saved" : capture.state === "ready" ? "Ready" : caption
127
127
  ] })
128
128
  ] }),
129
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "time", { "aria-label": "Recording duration", dateTime: `PT${elapsed}S`, className: "pt-0.5 font-mono text-xs tabular-nums text-muted-foreground", children: [
130
- Math.floor(elapsed / 60).toString().padStart(2, "0"),
131
- ":",
132
- (elapsed % 60).toString().padStart(2, "0")
133
- ] })
129
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
130
+ _chunkJDEXZGIYcjs.Button,
131
+ {
132
+ type: "button",
133
+ variant: "ghost",
134
+ size: "icon",
135
+ shape: "circle",
136
+ "aria-label": "Cancel dictation",
137
+ className: "size-[44px] shrink-0 text-muted-foreground [&_svg]:size-5",
138
+ onClick: handleCancel,
139
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.X, { "aria-hidden": "true", className: "size-5" })
140
+ }
141
+ )
134
142
  ] }),
135
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex min-h-0 flex-col gap-3", !anchored && "w-full max-w-xl flex-1 justify-center self-center py-8 text-center"), children: [
136
- capture.audioLevels && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DictationWaveform, { source: capture.audioLevels, state: capture.state, className: !anchored ? "h-20" : void 0 }),
137
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { id: descriptionId, className: "text-xs text-muted-foreground", children: capture.state === "transcribing" ? "Finishing your last words\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Try recording again." : "Speak, then choose Done to add the text." }),
138
- displayTranscript && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "max-h-40 overflow-y-auto text-sm leading-relaxed", children: [
143
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex flex-col gap-5", !anchored && "w-full max-w-xl flex-1 justify-center self-center py-6"), children: [
144
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot": "dictation-meter", className: "flex min-w-0 items-center gap-3 py-5 text-[hsl(var(--chart-1))] sm:gap-5 sm:py-7", children: [
145
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
146
+ "div",
147
+ {
148
+ "aria-hidden": "true",
149
+ "data-slot": "dictation-microphone",
150
+ className: "flex size-[72px] shrink-0 items-center justify-center rounded-full bg-current/10 sm:size-[96px]",
151
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex size-[52px] items-center justify-center rounded-full bg-[hsl(var(--chart-1))] text-popover sm:size-[72px]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { className: "size-7 sm:size-9" }) })
152
+ }
153
+ ),
154
+ capture.audioLevels && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DictationWaveform, { source: capture.audioLevels, state: capture.state }),
155
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
156
+ "time",
157
+ {
158
+ "aria-label": "Recording duration",
159
+ dateTime: `PT${elapsed}S`,
160
+ className: "ml-auto shrink-0 font-mono text-xl tabular-nums text-muted-foreground sm:text-2xl",
161
+ children: [
162
+ Math.floor(elapsed / 60).toString().padStart(2, "0"),
163
+ ":",
164
+ (elapsed % 60).toString().padStart(2, "0")
165
+ ]
166
+ }
167
+ )
168
+ ] }),
169
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { id: descriptionId, className: "text-base leading-relaxed text-muted-foreground", children: capture.state === "transcribing" ? "Finishing your last words\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Try recording again." : "Speak, then choose Done to add the text." }),
170
+ displayTranscript && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "max-h-48 overflow-y-auto break-words text-base leading-relaxed", children: [
139
171
  transcript,
140
172
  interim && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "text-muted-foreground", children: [
141
173
  transcript ? " " : "",
142
174
  interim
143
175
  ] })
144
176
  ] }),
145
- error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "text-xs text-destructive", children: error }),
146
- !error && capture.previewError && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "status", className: "text-xs text-muted-foreground", children: capture.previewError })
177
+ error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "text-sm leading-relaxed text-destructive", children: error }),
178
+ !error && capture.previewError && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "status", className: "text-sm leading-relaxed text-muted-foreground", children: capture.previewError })
147
179
  ] }),
148
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "gap-2", anchored ? "flex flex-wrap items-center justify-end" : "mt-auto grid w-full max-w-xl grid-cols-2 gap-3 self-center"), children: [
149
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { ref: cancelControl, type: "button", variant: "ghost", size: "sm", className: anchored ? "min-h-[44px]" : "min-h-[48px]", onClick: handleCancel, children: "Cancel" }),
150
- capture.canRetry && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "outline", size: "sm", className: anchored ? "min-h-[44px]" : "order-first col-span-2 min-h-[48px]", onClick: () => {
180
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0 gap-3 pt-2", anchored ? "flex flex-wrap items-center justify-end" : "mt-auto grid w-full max-w-xl grid-cols-[auto_minmax(0,1fr)] self-center"), children: [
181
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { ref: cancelControl, type: "button", variant: "ghost", className: "min-h-[48px] px-5", onClick: handleCancel, children: "Cancel" }),
182
+ capture.canRetry && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "outline", className: _chunkKWLSBTB2cjs.cn.call(void 0, "min-h-[48px] px-5", !anchored && "order-first col-span-2"), onClick: () => {
151
183
  _optionalChain([cancelControl, 'access', _13 => _13.current, 'optionalAccess', _14 => _14.focus, 'call', _15 => _15({ preventScroll: true })]);
152
184
  void capture.start();
153
185
  }, children: "Record again" }),
154
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", size: "sm", className: anchored ? "min-h-[44px]" : "min-h-[48px]", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
186
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", className: "min-h-[48px] min-w-24 px-6", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
155
187
  ] })
156
188
  ] });
157
189
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
@@ -195,13 +227,13 @@ function VoiceDictationButton({
195
227
  "data-placement": "anchored",
196
228
  side: "top",
197
229
  align: "center",
198
- sideOffset: 8,
230
+ sideOffset: 12,
199
231
  collisionPadding: 12,
200
232
  avoidCollisions: true,
201
233
  onCloseAutoFocus: closeAutoFocus,
202
234
  onKeyDown,
203
235
  onEscapeKeyDown: (event) => event.stopPropagation(),
204
- className: "z-[200] flex max-h-[var(--radix-popover-content-available-height)] w-96 max-w-[calc(100vw-1.5rem)] flex-col gap-3 overflow-y-auto rounded-2xl border border-border bg-background p-4 text-foreground shadow-lg outline-hidden",
236
+ className: "z-[200] flex max-h-[var(--radix-popover-content-available-height)] w-[560px] max-w-[calc(100vw-24px)] flex-col gap-6 overflow-y-auto rounded-2xl border border-border bg-popover p-6 text-popover-foreground shadow-xl outline-hidden",
205
237
  children: content
206
238
  }
207
239
  ) })
@@ -221,7 +253,7 @@ function VoiceDictationButton({
221
253
  onCloseAutoFocus: closeAutoFocus,
222
254
  onKeyDown,
223
255
  onEscapeKeyDown: (event) => event.stopPropagation(),
224
- className: "fixed inset-0 z-[200] flex h-dvh w-screen flex-col gap-6 overflow-y-auto overscroll-contain bg-background px-6 pt-[max(1.5rem,env(safe-area-inset-top))] pb-[max(1.5rem,env(safe-area-inset-bottom))] text-foreground outline-hidden",
256
+ className: "fixed inset-0 z-[200] flex h-dvh w-screen flex-col gap-6 overflow-y-auto overscroll-contain bg-popover px-5 pt-[max(1.5rem,env(safe-area-inset-top))] pb-[max(1.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-hidden",
225
257
  children: [
226
258
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DialogPrimitive.Title, { className: "sr-only", children: "Voice dictation" }),
227
259
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DialogPrimitive.Description, { className: "sr-only", children: "Record your message, then choose Done to add the text. Cancel discards the recording." }),
@@ -234,7 +266,7 @@ function VoiceDictationButton({
234
266
  }
235
267
  var SILENCE = [];
236
268
  var silence = () => SILENCE;
237
- function DictationWaveform({ source, state, className }) {
269
+ function DictationWaveform({ source, state }) {
238
270
  const bands = _react.useSyncExternalStore.call(void 0, source.subscribe, source.getSnapshot, silence);
239
271
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
240
272
  _chunkNKWHDI4Ccjs.AgentAudioVisualizerBar,
@@ -245,7 +277,8 @@ function DictationWaveform({ source, state, className }) {
245
277
  barCount: 24,
246
278
  state: state === "recording" ? "listening" : state === "transcribing" ? "thinking" : state === "requesting" ? "connecting" : "disconnected",
247
279
  volumeBands: state === "recording" ? bands : void 0,
248
- className: _chunkKWLSBTB2cjs.cn.call(void 0, "h-9 w-full text-foreground", className)
280
+ className: "h-[64px] min-w-0 flex-1 gap-[3px] sm:h-[88px] sm:gap-[4px]",
281
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-h-[4px] max-w-[6px] flex-1 rounded-full bg-current/15 transition-colors duration-200 motion-reduce:transition-none data-[lk-highlighted=true]:bg-current" })
249
282
  }
250
283
  );
251
284
  }
@@ -23,7 +23,7 @@ import {
23
23
  } from "react";
24
24
  import * as DialogPrimitive from "@radix-ui/react-dialog";
25
25
  import * as PopoverPrimitive from "@radix-ui/react-popover";
26
- import { Mic, LoaderCircle } from "lucide-react";
26
+ import { Mic, LoaderCircle, X } from "lucide-react";
27
27
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
28
28
  function VoiceDictationButton({
29
29
  onCommit,
@@ -118,40 +118,72 @@ function VoiceDictationButton({
118
118
  }
119
119
  };
120
120
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
121
- /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-start justify-between gap-3", !anchored && "w-full max-w-xl self-center"), children: [
122
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
123
- /* @__PURE__ */ jsx("h2", { className: cn("font-medium", anchored ? "text-sm" : "text-lg"), children: title || "Voice dictation" }),
124
- /* @__PURE__ */ jsxs("span", { role: "status", className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
125
- busy ? /* @__PURE__ */ jsx(LoaderCircle, { "aria-hidden": "true", className: "size-3.5 animate-spin motion-reduce:animate-none" }) : /* @__PURE__ */ jsx(Mic, { "aria-hidden": "true", className: "size-3.5" }),
121
+ /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 shrink-0 items-start justify-between gap-4", !anchored && "w-full max-w-xl self-center"), children: [
122
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2 pt-1", children: [
123
+ /* @__PURE__ */ jsx("h2", { className: "break-words text-xl font-semibold leading-tight", children: title || "Voice dictation" }),
124
+ /* @__PURE__ */ jsxs("span", { role: "status", className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
125
+ busy && /* @__PURE__ */ jsx(LoaderCircle, { "aria-hidden": "true", className: "size-4 shrink-0 animate-spin motion-reduce:animate-none" }),
126
126
  capture.state === "transcribing" ? "Finishing transcript\u2026" : capture.state === "requesting" ? "Starting dictation\u2026" : error ? "Recording not saved" : capture.state === "ready" ? "Ready" : caption
127
127
  ] })
128
128
  ] }),
129
- /* @__PURE__ */ jsxs("time", { "aria-label": "Recording duration", dateTime: `PT${elapsed}S`, className: "pt-0.5 font-mono text-xs tabular-nums text-muted-foreground", children: [
130
- Math.floor(elapsed / 60).toString().padStart(2, "0"),
131
- ":",
132
- (elapsed % 60).toString().padStart(2, "0")
133
- ] })
129
+ /* @__PURE__ */ jsx(
130
+ Button,
131
+ {
132
+ type: "button",
133
+ variant: "ghost",
134
+ size: "icon",
135
+ shape: "circle",
136
+ "aria-label": "Cancel dictation",
137
+ className: "size-[44px] shrink-0 text-muted-foreground [&_svg]:size-5",
138
+ onClick: handleCancel,
139
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-5" })
140
+ }
141
+ )
134
142
  ] }),
135
- /* @__PURE__ */ jsxs("div", { className: cn("flex min-h-0 flex-col gap-3", !anchored && "w-full max-w-xl flex-1 justify-center self-center py-8 text-center"), children: [
136
- capture.audioLevels && /* @__PURE__ */ jsx(DictationWaveform, { source: capture.audioLevels, state: capture.state, className: !anchored ? "h-20" : void 0 }),
137
- /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-xs text-muted-foreground", children: capture.state === "transcribing" ? "Finishing your last words\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Try recording again." : "Speak, then choose Done to add the text." }),
138
- displayTranscript && /* @__PURE__ */ jsxs("p", { className: "max-h-40 overflow-y-auto text-sm leading-relaxed", children: [
143
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", !anchored && "w-full max-w-xl flex-1 justify-center self-center py-6"), children: [
144
+ /* @__PURE__ */ jsxs("div", { "data-slot": "dictation-meter", className: "flex min-w-0 items-center gap-3 py-5 text-[hsl(var(--chart-1))] sm:gap-5 sm:py-7", children: [
145
+ /* @__PURE__ */ jsx(
146
+ "div",
147
+ {
148
+ "aria-hidden": "true",
149
+ "data-slot": "dictation-microphone",
150
+ className: "flex size-[72px] shrink-0 items-center justify-center rounded-full bg-current/10 sm:size-[96px]",
151
+ children: /* @__PURE__ */ jsx("div", { className: "flex size-[52px] items-center justify-center rounded-full bg-[hsl(var(--chart-1))] text-popover sm:size-[72px]", children: /* @__PURE__ */ jsx(Mic, { className: "size-7 sm:size-9" }) })
152
+ }
153
+ ),
154
+ capture.audioLevels && /* @__PURE__ */ jsx(DictationWaveform, { source: capture.audioLevels, state: capture.state }),
155
+ /* @__PURE__ */ jsxs(
156
+ "time",
157
+ {
158
+ "aria-label": "Recording duration",
159
+ dateTime: `PT${elapsed}S`,
160
+ className: "ml-auto shrink-0 font-mono text-xl tabular-nums text-muted-foreground sm:text-2xl",
161
+ children: [
162
+ Math.floor(elapsed / 60).toString().padStart(2, "0"),
163
+ ":",
164
+ (elapsed % 60).toString().padStart(2, "0")
165
+ ]
166
+ }
167
+ )
168
+ ] }),
169
+ /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-base leading-relaxed text-muted-foreground", children: capture.state === "transcribing" ? "Finishing your last words\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Try recording again." : "Speak, then choose Done to add the text." }),
170
+ displayTranscript && /* @__PURE__ */ jsxs("p", { className: "max-h-48 overflow-y-auto break-words text-base leading-relaxed", children: [
139
171
  transcript,
140
172
  interim && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
141
173
  transcript ? " " : "",
142
174
  interim
143
175
  ] })
144
176
  ] }),
145
- error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-xs text-destructive", children: error }),
146
- !error && capture.previewError && /* @__PURE__ */ jsx("p", { role: "status", className: "text-xs text-muted-foreground", children: capture.previewError })
177
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm leading-relaxed text-destructive", children: error }),
178
+ !error && capture.previewError && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm leading-relaxed text-muted-foreground", children: capture.previewError })
147
179
  ] }),
148
- /* @__PURE__ */ jsxs("div", { className: cn("gap-2", anchored ? "flex flex-wrap items-center justify-end" : "mt-auto grid w-full max-w-xl grid-cols-2 gap-3 self-center"), children: [
149
- /* @__PURE__ */ jsx(Button, { ref: cancelControl, type: "button", variant: "ghost", size: "sm", className: anchored ? "min-h-[44px]" : "min-h-[48px]", onClick: handleCancel, children: "Cancel" }),
150
- capture.canRetry && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", className: anchored ? "min-h-[44px]" : "order-first col-span-2 min-h-[48px]", onClick: () => {
180
+ /* @__PURE__ */ jsxs("div", { className: cn("shrink-0 gap-3 pt-2", anchored ? "flex flex-wrap items-center justify-end" : "mt-auto grid w-full max-w-xl grid-cols-[auto_minmax(0,1fr)] self-center"), children: [
181
+ /* @__PURE__ */ jsx(Button, { ref: cancelControl, type: "button", variant: "ghost", className: "min-h-[48px] px-5", onClick: handleCancel, children: "Cancel" }),
182
+ capture.canRetry && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: cn("min-h-[48px] px-5", !anchored && "order-first col-span-2"), onClick: () => {
151
183
  cancelControl.current?.focus({ preventScroll: true });
152
184
  void capture.start();
153
185
  }, children: "Record again" }),
154
- /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", className: anchored ? "min-h-[44px]" : "min-h-[48px]", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
186
+ /* @__PURE__ */ jsx(Button, { type: "button", className: "min-h-[48px] min-w-24 px-6", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
155
187
  ] })
156
188
  ] });
157
189
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -195,13 +227,13 @@ function VoiceDictationButton({
195
227
  "data-placement": "anchored",
196
228
  side: "top",
197
229
  align: "center",
198
- sideOffset: 8,
230
+ sideOffset: 12,
199
231
  collisionPadding: 12,
200
232
  avoidCollisions: true,
201
233
  onCloseAutoFocus: closeAutoFocus,
202
234
  onKeyDown,
203
235
  onEscapeKeyDown: (event) => event.stopPropagation(),
204
- className: "z-[200] flex max-h-[var(--radix-popover-content-available-height)] w-96 max-w-[calc(100vw-1.5rem)] flex-col gap-3 overflow-y-auto rounded-2xl border border-border bg-background p-4 text-foreground shadow-lg outline-hidden",
236
+ className: "z-[200] flex max-h-[var(--radix-popover-content-available-height)] w-[560px] max-w-[calc(100vw-24px)] flex-col gap-6 overflow-y-auto rounded-2xl border border-border bg-popover p-6 text-popover-foreground shadow-xl outline-hidden",
205
237
  children: content
206
238
  }
207
239
  ) })
@@ -221,7 +253,7 @@ function VoiceDictationButton({
221
253
  onCloseAutoFocus: closeAutoFocus,
222
254
  onKeyDown,
223
255
  onEscapeKeyDown: (event) => event.stopPropagation(),
224
- className: "fixed inset-0 z-[200] flex h-dvh w-screen flex-col gap-6 overflow-y-auto overscroll-contain bg-background px-6 pt-[max(1.5rem,env(safe-area-inset-top))] pb-[max(1.5rem,env(safe-area-inset-bottom))] text-foreground outline-hidden",
256
+ className: "fixed inset-0 z-[200] flex h-dvh w-screen flex-col gap-6 overflow-y-auto overscroll-contain bg-popover px-5 pt-[max(1.5rem,env(safe-area-inset-top))] pb-[max(1.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-hidden",
225
257
  children: [
226
258
  /* @__PURE__ */ jsx(DialogPrimitive.Title, { className: "sr-only", children: "Voice dictation" }),
227
259
  /* @__PURE__ */ jsx(DialogPrimitive.Description, { className: "sr-only", children: "Record your message, then choose Done to add the text. Cancel discards the recording." }),
@@ -234,7 +266,7 @@ function VoiceDictationButton({
234
266
  }
235
267
  var SILENCE = [];
236
268
  var silence = () => SILENCE;
237
- function DictationWaveform({ source, state, className }) {
269
+ function DictationWaveform({ source, state }) {
238
270
  const bands = useSyncExternalStore(source.subscribe, source.getSnapshot, silence);
239
271
  return /* @__PURE__ */ jsx(
240
272
  AgentAudioVisualizerBar,
@@ -245,7 +277,8 @@ function DictationWaveform({ source, state, className }) {
245
277
  barCount: 24,
246
278
  state: state === "recording" ? "listening" : state === "transcribing" ? "thinking" : state === "requesting" ? "connecting" : "disconnected",
247
279
  volumeBands: state === "recording" ? bands : void 0,
248
- className: cn("h-9 w-full text-foreground", className)
280
+ className: "h-[64px] min-w-0 flex-1 gap-[3px] sm:h-[88px] sm:gap-[4px]",
281
+ children: /* @__PURE__ */ jsx("div", { className: "min-h-[4px] max-w-[6px] flex-1 rounded-full bg-current/15 transition-colors duration-200 motion-reduce:transition-none data-[lk-highlighted=true]:bg-current" })
249
282
  }
250
283
  );
251
284
  }
@@ -7,7 +7,7 @@
7
7
  var _chunk2BTDBZYNcjs = require('../chunk-2BTDBZYN.cjs');
8
8
 
9
9
 
10
- var _chunkQWUMIAYDcjs = require('../chunk-QWUMIAYD.cjs');
10
+ var _chunkPH2TV7XIcjs = require('../chunk-PH2TV7XI.cjs');
11
11
 
12
12
 
13
13
  var _chunkYMI3X2YLcjs = require('../chunk-YMI3X2YL.cjs');
@@ -545,7 +545,7 @@ function ChatInput({
545
545
  ) : null,
546
546
  (!helperText || compact) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-1" }),
547
547
  voice && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
548
- _chunkQWUMIAYDcjs.VoiceDictationButton,
548
+ _chunkPH2TV7XIcjs.VoiceDictationButton,
549
549
  {
550
550
  size: "icon",
551
551
  title: voiceTitle,
@@ -623,7 +623,7 @@ function VoiceHero({
623
623
  voiceSession ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
624
624
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYMI3X2YLcjs.VoiceSessionControls, { ...voiceSession, size: "hero" }),
625
625
  voice && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
626
- _chunkQWUMIAYDcjs.VoiceDictationButton,
626
+ _chunkPH2TV7XIcjs.VoiceDictationButton,
627
627
  {
628
628
  size: "icon",
629
629
  title: voiceTitle,
@@ -633,7 +633,7 @@ function VoiceHero({
633
633
  }
634
634
  )
635
635
  ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
636
- _chunkQWUMIAYDcjs.VoiceDictationButton,
636
+ _chunkPH2TV7XIcjs.VoiceDictationButton,
637
637
  {
638
638
  size: "lg",
639
639
  title: voiceTitle,
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-Z45YKVAM.js";
8
8
  import {
9
9
  VoiceDictationButton
10
- } from "../chunk-JKVVTFAP.js";
10
+ } from "../chunk-ZGTPNSR7.js";
11
11
  import {
12
12
  VoiceSessionControls
13
13
  } from "../chunk-KF4X37DP.js";
@@ -4,7 +4,7 @@
4
4
  var _chunk2HTYXBDUcjs = require('../chunk-2HTYXBDU.cjs');
5
5
 
6
6
 
7
- var _chunk6YXKFVZUcjs = require('../chunk-6YXKFVZU.cjs');
7
+ var _chunkAU5GUDOWcjs = require('../chunk-AU5GUDOW.cjs');
8
8
 
9
9
 
10
10
 
@@ -141,7 +141,7 @@ function Connection({
141
141
  ),
142
142
  children: [
143
143
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HTYXBDUcjs.StatusBadge, { tone: s.tone, children: _nullishCoalesce(statusLabel, () => ( s.label)) }),
144
- responsiveActions.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6YXKFVZUcjs.PageHeaderActions, { actions: responsiveActions, mobileLabel: "Actions" })
144
+ responsiveActions.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkAU5GUDOWcjs.PageHeaderActions, { actions: responsiveActions, mobileLabel: "Actions" })
145
145
  ]
146
146
  }
147
147
  ),
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-GMJWBGTJ.js";
5
5
  import {
6
6
  PageHeaderActions
7
- } from "../chunk-6S6GD43V.js";
7
+ } from "../chunk-2UYEMPOJ.js";
8
8
  import {
9
9
  Dialog,
10
10
  DialogContent,
@@ -2,10 +2,10 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunk6YXKFVZUcjs = require('../chunk-6YXKFVZU.cjs');
5
+ var _chunkAU5GUDOWcjs = require('../chunk-AU5GUDOW.cjs');
6
6
  require('../chunk-ZAITUDYD.cjs');
7
7
  require('../chunk-JDEXZGIY.cjs');
8
8
  require('../chunk-KWLSBTB2.cjs');
9
9
 
10
10
 
11
- exports.PageHeaderActions = _chunk6YXKFVZUcjs.PageHeaderActions;
11
+ exports.PageHeaderActions = _chunkAU5GUDOWcjs.PageHeaderActions;
@@ -10,7 +10,7 @@ interface PageHeaderAction {
10
10
  label: ReactNode;
11
11
  icon?: ReactNode;
12
12
  onSelect?: () => void;
13
- /** Optional nested actions. Desktop renders a menu button; mobile nests it inside Actions. */
13
+ /** Optional nested actions. Expanded layout renders a menu button; collapsed layout nests it inside Actions. */
14
14
  items?: PageHeaderAction[];
15
15
  /** Optional supporting copy shown when the action appears in a menu. */
16
16
  description?: ReactNode;
@@ -23,7 +23,7 @@ interface PageHeaderAction {
23
23
  * the button variant.
24
24
  */
25
25
  priority?: "primary" | "secondary" | "tertiary";
26
- /** Semantic treatment used in the collapsed mobile menu. */
26
+ /** Semantic treatment used in the collapsed menu. */
27
27
  tone?: "default" | "muted" | "destructive";
28
28
  /** Visually separates destructive or lower-priority actions in the menu. */
29
29
  separatorBefore?: boolean;
@@ -31,7 +31,9 @@ interface PageHeaderAction {
31
31
  }
32
32
  interface PageHeaderActionsProps {
33
33
  actions: PageHeaderAction[];
34
- /** Visible label for the collapsed mobile trigger. */
34
+ /** Responsive by default; menu keeps the same collapsed Actions menu at every width. */
35
+ presentation?: "responsive" | "menu";
36
+ /** Visible menu-trigger label, also used on desktop when presentation is menu. */
35
37
  mobileLabel?: ReactNode;
36
38
  /** Accessible label when `mobileLabel` is not plain text. */
37
39
  mobileAriaLabel?: string;
@@ -42,8 +44,9 @@ interface PageHeaderActionsProps {
42
44
  *
43
45
  * Desktop keeps every action visible for fast scanning. Mobile collapses the
44
46
  * same action definitions into one menu so page titles retain useful space.
47
+ * Use `presentation="menu"` to keep that menu at every viewport width.
45
48
  * Status and other non-interactive metadata should remain beside the title.
46
49
  */
47
- declare function PageHeaderActions({ actions, mobileLabel, mobileAriaLabel, className, }: PageHeaderActionsProps): react_jsx_runtime.JSX.Element | null;
50
+ declare function PageHeaderActions({ actions, presentation, mobileLabel, mobileAriaLabel, className, }: PageHeaderActionsProps): react_jsx_runtime.JSX.Element | null;
48
51
 
49
52
  export { type PageHeaderAction, PageHeaderActions, type PageHeaderActionsProps };
@@ -10,7 +10,7 @@ interface PageHeaderAction {
10
10
  label: ReactNode;
11
11
  icon?: ReactNode;
12
12
  onSelect?: () => void;
13
- /** Optional nested actions. Desktop renders a menu button; mobile nests it inside Actions. */
13
+ /** Optional nested actions. Expanded layout renders a menu button; collapsed layout nests it inside Actions. */
14
14
  items?: PageHeaderAction[];
15
15
  /** Optional supporting copy shown when the action appears in a menu. */
16
16
  description?: ReactNode;
@@ -23,7 +23,7 @@ interface PageHeaderAction {
23
23
  * the button variant.
24
24
  */
25
25
  priority?: "primary" | "secondary" | "tertiary";
26
- /** Semantic treatment used in the collapsed mobile menu. */
26
+ /** Semantic treatment used in the collapsed menu. */
27
27
  tone?: "default" | "muted" | "destructive";
28
28
  /** Visually separates destructive or lower-priority actions in the menu. */
29
29
  separatorBefore?: boolean;
@@ -31,7 +31,9 @@ interface PageHeaderAction {
31
31
  }
32
32
  interface PageHeaderActionsProps {
33
33
  actions: PageHeaderAction[];
34
- /** Visible label for the collapsed mobile trigger. */
34
+ /** Responsive by default; menu keeps the same collapsed Actions menu at every width. */
35
+ presentation?: "responsive" | "menu";
36
+ /** Visible menu-trigger label, also used on desktop when presentation is menu. */
35
37
  mobileLabel?: ReactNode;
36
38
  /** Accessible label when `mobileLabel` is not plain text. */
37
39
  mobileAriaLabel?: string;
@@ -42,8 +44,9 @@ interface PageHeaderActionsProps {
42
44
  *
43
45
  * Desktop keeps every action visible for fast scanning. Mobile collapses the
44
46
  * same action definitions into one menu so page titles retain useful space.
47
+ * Use `presentation="menu"` to keep that menu at every viewport width.
45
48
  * Status and other non-interactive metadata should remain beside the title.
46
49
  */
47
- declare function PageHeaderActions({ actions, mobileLabel, mobileAriaLabel, className, }: PageHeaderActionsProps): react_jsx_runtime.JSX.Element | null;
50
+ declare function PageHeaderActions({ actions, presentation, mobileLabel, mobileAriaLabel, className, }: PageHeaderActionsProps): react_jsx_runtime.JSX.Element | null;
48
51
 
49
52
  export { type PageHeaderAction, PageHeaderActions, type PageHeaderActionsProps };
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  PageHeaderActions
5
- } from "../chunk-6S6GD43V.js";
5
+ } from "../chunk-2UYEMPOJ.js";
6
6
  import "../chunk-V4LE2HWP.js";
7
7
  import "../chunk-W326GKML.js";
8
8
  import "../chunk-G6SRSWXB.js";
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkQWUMIAYDcjs = require('../chunk-QWUMIAYD.cjs');
5
+ var _chunkPH2TV7XIcjs = require('../chunk-PH2TV7XI.cjs');
6
6
  require('../chunk-NKWHDI4C.cjs');
7
7
  require('../chunk-JDEXZGIY.cjs');
8
8
  require('../chunk-KWLSBTB2.cjs');
@@ -10,4 +10,4 @@ require('../chunk-2BSFYU22.cjs');
10
10
  require('../chunk-7Q4EFCUI.cjs');
11
11
 
12
12
 
13
- exports.VoiceDictationButton = _chunkQWUMIAYDcjs.VoiceDictationButton;
13
+ exports.VoiceDictationButton = _chunkPH2TV7XIcjs.VoiceDictationButton;
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  VoiceDictationButton
5
- } from "../chunk-JKVVTFAP.js";
5
+ } from "../chunk-ZGTPNSR7.js";
6
6
  import "../chunk-5OF6D3GH.js";
7
7
  import "../chunk-W326GKML.js";
8
8
  import "../chunk-G6SRSWXB.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicbagel/saas-kit",
3
- "version": "1.16.0",
3
+ "version": "1.18.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": [