@epicbagel/saas-kit 1.19.0 → 1.21.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
@@ -60,6 +60,45 @@ does not import a voice provider. Stop and release audio tracks before setting
60
60
  }} />
61
61
  ```
62
62
 
63
+ ## Workflow assistant
64
+
65
+ `primitives/workflow-assistant` exports `WorkflowAssistant`, a controlled
66
+ conversation panel beside a working list. It uses `ChatThread`, `ChatInput`
67
+ and the same `voiceSession` contract as Chat. The panel is non-modal: it
68
+ leaves the page interactive, does not trap focus, and keeps the current
69
+ `context` visible in its header. On short screens the panel can scroll while
70
+ the header stays reachable.
71
+
72
+ Opening the launcher starts an inactive voice session. Minimizing preserves
73
+ the session, transcript and host-owned draft; the minimized launcher reports
74
+ the current voice state and keeps End available. `disabled`, `pending` or
75
+ `voiceSession.disabled` prevent a new call but never prevent reopening or
76
+ ending an active call. Activation moves keyboard focus to the panel heading;
77
+ minimizing returns it to the launcher. A host-driven open or scope update
78
+ does not move focus away from the page.
79
+
80
+ ```tsx
81
+ import { WorkflowAssistant } from "@epicbagel/saas-kit/primitives/workflow-assistant";
82
+
83
+ <WorkflowAssistant
84
+ open={open} onOpenChange={setOpen}
85
+ expanded={expanded} onExpandedChange={setExpanded}
86
+ title="Voice review" context={`Current list · ${items.length} items`}
87
+ messages={messages} value={draft} onChange={setDraft} onSend={sendRequest}
88
+ pending={pending} error={error}
89
+ voiceSession={{ state, muted, onMutedChange: setMuted,
90
+ onStart: startVoice, onEnd: endVoice }}
91
+ />
92
+ ```
93
+
94
+ The host owns authorization, list scope, messages, draft clearing, audio and
95
+ connection cleanup. When scope changes, the host must cancel or replace any
96
+ session that still refers to the previous list. Omit `voiceSession` for a
97
+ text-only conversation, omit `onExpandedChange` to hide the expand control,
98
+ and use `besideCornerAction` to leave space for an existing corner action.
99
+ Override panel actions with `labels`; minimized session status and End also
100
+ honor `voiceSession.labels`.
101
+
63
102
  ## API dictation
64
103
 
65
104
  Wrap the application in `DictationProvider` to use an authenticated transcription
@@ -244,6 +283,19 @@ public in 1.1.0, and these were the decisions about its props then:
244
283
  [])`: the draft and staged files stay, and a failure is reported without
245
284
  touching them. Suggestions are disabled while `pending`; pass none once
246
285
  they no longer apply.
286
+ - **Compact mobile suggestions (1.20.0).** Opt into `mobileCompactSuggestions`
287
+ to put the first reply in a shaded row inside the composer on phones
288
+ below 768px. Its button sends directly; the separate `+N more` button opens
289
+ a bottom sheet with every reply in full, wrapping onto as many lines as
290
+ needed. One reply needs no picker and no replies leave no row. Desktop
291
+ keeps the existing chips. The sheet uses `replySuggestionsLabel` as its
292
+ title; `replySuggestionsMoreLabel(count)` and `replySuggestionsCloseLabel`
293
+ replace the opener and close wording. These mobile picks preserve the
294
+ draft and files without focusing the textarea or opening the keyboard.
295
+ Pending sends, changed choices, a new latest message, or a move to desktop
296
+ dismiss the picker. Equivalent arrays from a refresh leave it open.
297
+ `ChatInput.header` provides the same inside-surface top slot for other
298
+ compositions; changing the header keeps its textarea mounted.
247
299
  - **Failed sends.** When `onSend` rejects, Chat puts the text back in the
248
300
  composer and calls `onSendError`; without one it reports the rejection
249
301
  through `reportError`, so a failure is never silent.