@appsbd/vue3-appsbd-ui 1.0.15 → 1.0.16

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.
@@ -89,21 +89,57 @@ a native file picker and routes the selection through `addFiles`) among other me
89
89
  Uses the browser's native Web Speech API (`SpeechRecognition`) — no extra dependency, no API key. A built-in mic
90
90
  button renders in the `right` area (before the `right` slot and the send button) whenever `showVoiceButton` is
91
91
  `true` (default).
92
- Clicking it (or calling `startRecording()`) swaps the whole composer row for a full-width recording bar — a live
93
- audio-reactive waveform (via `getUserMedia` + `AnalyserNode`) plus three controls:
94
92
 
95
- - **✕ cancel** — discards everything spoken this session, reverting `modelValue` to what it was before recording started.
96
- - **■ stop** — stops recording and keeps the transcribed text in the textarea (no submit).
97
- - **↑ submit** — stops recording, keeps the text, then fires `submit` immediately.
93
+ The composer layout never changes while recording, on every platform — no full-width recording bar swap: the
94
+ textarea stays visible **and stays fully editable** (typing/deleting mid-recording works normally) and fills with
95
+ the transcribed text live as it's recognized. The mic button shows a Google Assistant-style listening cue: its
96
+ circle fills with the theme's primary color (icon turns white to read against it), with a few rings pulsing
97
+ outward from behind it on a loop (`.apbd-chat-input__voice-ring`, staggered so they overlap continuously). Clicking
98
+ it (or calling `stopRecording()`) stops and keeps the text — there's no dedicated cancel button in this UI
99
+ (`cancelRecording()` is still available programmatically via a template ref), and the default send button (or
100
+ `submit()`/Enter generally) stops recording first when called mid-listening, then fires `submit` immediately.
98
101
 
99
- Recognized speech is appended to the existing `modelValue` (not replaced), one final phrase at a time — interim
100
- (still-speaking) text is not shown live.
102
+ On every platform, the textarea is refocused automatically the moment listening ends (stop, cancel, or a
103
+ voice-triggered submit) — the cursor lands right back in it so the user can keep typing without an extra tap.
104
+
105
+ Recognized speech is appended to the existing `modelValue` (not replaced), converting instantly as the user speaks
106
+ on every platform — interim results drive `modelValue` live so the text is already there before the user taps the
107
+ mic to stop, not just once they do (see below).
101
108
 
102
109
  A small language-code badge (e.g. `EN`, `BN`) sits on the mic button's corner. Clicking it opens an `AbPopover`
103
110
  with a searchable list of ~130 BCP-47 locales; picking one sets that recognition session's `SpeechRecognition.lang`
104
111
  and persists the choice to `localStorage` (key `apbd-chat-input-voice-lang`) so it's remembered next visit. This
105
112
  never touches the browser's own language/locale settings — only this component's own recognizer instance.
106
113
 
114
+ ### Voice punctuation
115
+
116
+ Two independent behaviors, each its own boolean prop, **both default `true`**:
117
+
118
+ - **`voicePunctuationCommands`** — saying a punctuation word inserts the actual mark, same idea as Google/Apple
119
+ dictation: English `"period"`/`"full stop"` → `.`, `"comma"` → `,`, `"question mark"` → `?`; Bengali
120
+ `"পূর্ণচ্ছেদ"` → `।`, `"কমা"` → `,`, `"প্রশ্নবোধক"` → `?`. Matched against the recognized transcript text itself
121
+ (not `voiceLang`), so it works regardless of which recognition language is selected. Matching uses
122
+ script-agnostic lookaround (not `\b`, which only understands ASCII letters — a no-op for Bengali) so a command
123
+ word only triggers standalone, never as a substring of a longer word (e.g. saying "দাঁড়িপাল্লা" doesn't trigger
124
+ on the "দাড়ি" inside it). `"দাড়ি"` itself (an earlier alternate trigger, same meaning as `"পূর্ণচ্ছেদ"`) was
125
+ dropped entirely rather than just boundary-guarded — it's also ordinary vocabulary (beard), so a sentence
126
+ actually about a beard would still lose that word every time. `"কমা"` keeps a similar, smaller risk (it's also
127
+ a common verb, "to decrease") — left as-is, since removing every word with *any* plausible double meaning
128
+ would leave nothing left to trigger on.
129
+ - **`voiceAutoPunctuate`** — when a new utterance is about to be appended onto existing text, a sentence-ending
130
+ mark is inserted first if the existing text doesn't already end with one — `.` normally, `।` when `voiceLang`
131
+ starts with `bn`. Applies between pauses (each per-utterance restart, see below), not mid-utterance.
132
+
133
+ Set either to `false` to disable it independently; both keep working with the default Web Speech transcript as-is
134
+ when off.
135
+
136
+ Both also auto-capitalize the sentence start they create: `voiceAutoPunctuate` capitalizes the first letter of a
137
+ new utterance right after it inserts a boundary mark (and always capitalizes the very first letter of the whole
138
+ message, regardless of this prop); `voicePunctuationCommands` capitalizes the letter right after any mark it
139
+ inserts mid-utterance (`"hello period how are you"` → `"Hello. How are you"`). Same script-agnostic approach as the
140
+ punctuation matching above — plain `toUpperCase()` on the matched letter, which is a no-op for scripts without a
141
+ case concept (Bengali, Arabic, CJK, …), so it needs no per-language list.
142
+
107
143
  Not supported in Firefox. On iOS, only Safari itself gets it — Chrome/Firefox/Edge/Opera on iOS run on the same
108
144
  underlying WebKit engine and expose the same `webkitSpeechRecognition` constructor, but iOS never grants the mic
109
145
  permission for it outside of Safari's own app (no permission prompt ever appears; `.start()` would otherwise fail
@@ -111,10 +147,28 @@ with a confusing, prompt-less "permission denied"), so these are detected via us
111
147
  too. The mic button still renders in all of these cases but is `disabled`, with a tooltip explaining why. Where it
112
148
  is supported, requires the browser's mic permission prompt to be accepted.
113
149
 
114
- On Android, the live waveform is skipped (bars stay flat) — recognition and transcription work normally. Android's
115
- mic hardware only allows one exclusive audio-capture session at a time; a second concurrent `getUserMedia` stream
116
- for the waveform would starve `SpeechRecognition` of audio (no error, no transcript, ever). This is detected via
117
- user-agent, not feature-tested.
150
+ Some browsers/builds hold back `isFinal` results until `.stop()` is called instead of firing them per pause segment
151
+ — waiting for that would mean the transcript only shows up once the user taps stop, not while they're actually
152
+ speaking (and on some builds it never reliably fires at all). `interimResults` is on by default to sidestep that,
153
+ converting speech to text instantly instead of only once recording stops — **except on Safari** (desktop and iOS
154
+ both), where it's kept `false`: Safari's engine has a bug where enabling it can fail to (re)start the recognizer
155
+ entirely, surfacing as a generic "Microphone permission denied" error indistinguishable from an actually-denied
156
+ permission. Safari still converts per finished phrase (not mid-utterance), just not keystroke-by-keystroke live.
157
+
158
+ `continuous`+`interimResults` together is also unreliable across browsers/builds in a way delta-tracking
159
+ `e.results` (via `resultIndex` or position) can't fully work around — it's been observed restating/duplicating
160
+ earlier words in a later result with no restart involved at all. So `recognition.continuous` is always `false` —
161
+ each internal session covers exactly one utterance, and a transparent onend-restart (also covers Safari's own
162
+ quirk of ending a `continuous: true` session early) picks the next utterance back up automatically, so it still
163
+ reads as continuous listening. Within one utterance's `onresult`, only the latest `e.results` entry is read — no
164
+ cross-event bookkeeping needed, since there's nothing earlier in that same array to conflict with.
165
+
166
+ Because the textarea stays editable during recording, `onresult` can't just privately accumulate a running
167
+ transcript either — that would silently clobber whatever the user just typed or deleted. Instead it reads the
168
+ current `modelValue`, strips back off the exact suffix it itself inserted last time (if that suffix is still there
169
+ untouched), and rebuilds from that stable base with the freshly recognized text appended; if the suffix isn't
170
+ there anymore, whatever's currently in the field — including the user's edit — becomes the new base instead of
171
+ being overwritten.
118
172
 
119
173
  ## Props
120
174
 
@@ -140,6 +194,8 @@ user-agent, not feature-tested.
140
194
  | `filePreviewHeight` | `Number` | `24` | Height (px) of each attached file's thumbnail/icon in the chip. |
141
195
  | `showFileName` | `Boolean` | `true` | Show/hide the filename text next to the chip thumbnail. |
142
196
  | `showVoiceButton` | `Boolean` | `true` | Show/hide the built-in mic button (see "Voice input" below). Set `false` to build custom voice UI via slots instead. |
197
+ | `voicePunctuationCommands` | `Boolean` | `true` | Say a punctuation word (e.g. "period", "পূর্ণচ্ছেদ") to insert the actual mark — see "Voice punctuation" above. |
198
+ | `voiceAutoPunctuate` | `Boolean` | `true` | Auto-insert a sentence-ending mark between voice utterances when one isn't already there — see "Voice punctuation" above. |
143
199
 
144
200
  ## Events
145
201
 
@@ -154,8 +210,8 @@ user-agent, not feature-tested.
154
210
  | `file-remove` | `FileEntry` | A file chip's remove button was clicked. |
155
211
  | `file-reject` | `{ file: File, reason: string }` | A file failed `maxFileSize`/`allowedExtensions` validation. A `useToast` error is also shown. |
156
212
  | `voice-start` | — | Voice recording started (mic button clicked or `startRecording()` called). |
157
- | `voice-stop` | — | Recording stopped normally (■ button, ↑ submit button, `stopRecording()`, or the browser ending it on its own) — transcribed text is kept in `modelValue`. |
158
- | `voice-cancel` | — | Recording was cancelled (✕ button or `cancelRecording()`) — `modelValue` is reverted to what it was before recording started. |
213
+ | `voice-stop` | — | Recording stopped normally (mic button tapped again, `stopRecording()`, or the browser ending it on its own) — transcribed text is kept in `modelValue`. |
214
+ | `voice-cancel` | — | Recording was cancelled via `cancelRecording()` (no dedicated UI button — call it programmatically via a template ref) — `modelValue` is reverted to what it was before recording started. |
159
215
  | `voice-error` | `Error \| SpeechRecognitionErrorEvent` | Mic permission was denied, or the recognition engine errored. A `useToast` error is also shown (except benign `no-speech`/`aborted` errors). |
160
216
 
161
217
  ## Slots
@@ -0,0 +1,112 @@
1
+ # AbVoiceButton — AI Reference
2
+
3
+ A standalone voice-to-text mic button — the same recognition engine and behavior as `AbChatInput`'s built-in mic,
4
+ with no composer attached. Use it to bolt voice input onto any existing input, form field, search box, or custom
5
+ UI via `v-model`; pick `AbChatInput` instead when you need the whole chat composer (textarea, file attachments,
6
+ add/more dropdown) with voice input built in.
7
+
8
+ ## Installation
9
+
10
+ Not globally registered — import it explicitly:
11
+
12
+ ```js
13
+ import { AbVoiceButton } from 'vue3-appsbd-ui';
14
+ ```
15
+
16
+ No extra peer deps beyond the library's own (`@lucide/vue` for the mic icon). Uses the browser's native Web
17
+ Speech API (`SpeechRecognition`) — no dependency, no API key.
18
+
19
+ ## Usage
20
+
21
+ ```vue
22
+ <template>
23
+ <AbVoiceButton v-model="message" />
24
+ <p>{{ message }}</p>
25
+ </template>
26
+
27
+ <script setup>
28
+ import { ref } from 'vue';
29
+ import { AbVoiceButton } from 'vue3-appsbd-ui';
30
+
31
+ const message = ref('');
32
+ </script>
33
+ ```
34
+
35
+ Pin the recognition language explicitly (there's no built-in language picker here, unlike `AbChatInput`'s mic
36
+ button — pass whichever locale your app already knows the user wants):
37
+
38
+ ```vue
39
+ <AbVoiceButton v-model="message" lang="bn-BD" />
40
+ ```
41
+
42
+ ## Behavior
43
+
44
+ Tapping the button (or calling `startRecording()`) starts listening; the mic icon stays the mic icon, but its
45
+ circle fills with the theme's primary color (`--#{prefix}theme`) and the icon turns white, with a few rings
46
+ pulsing outward from behind it on a loop — a Google Assistant-style listening cue, on every platform (same cue,
47
+ same architecture as `AbChatInput`'s built-in mic — this component just has no composer/textarea attached to it).
48
+ Tap it again (or call `stopRecording()`) to stop and keep the text.
49
+
50
+ Recognized speech merges into `modelValue` live as the user speaks, via one internal recognition session per
51
+ utterance (`continuous: false` + `interimResults: true`, restarted transparently between utterances) — on every
52
+ platform except Safari (desktop and iOS both), where `interimResults` is kept `false`: Safari's engine has a bug
53
+ where enabling it can fail to (re)start the recognizer entirely, surfacing as a generic "Microphone permission
54
+ denied" error indistinguishable from an actually-denied permission. Safari still converts per finished phrase, just
55
+ not keystroke-by-keystroke live. Reads the current `modelValue` on each update and replaces only the exact suffix
56
+ it last inserted itself, so external edits to `modelValue` between voice updates (e.g. the consumer's own code, or
57
+ a bound `<textarea>` the user is also typing into) are preserved rather than clobbered.
58
+
59
+ Not supported in Firefox. On iOS, only Safari itself gets it — Chrome/Firefox/Edge/Opera on iOS run on the same
60
+ underlying WebKit engine and expose the same `webkitSpeechRecognition` constructor, but iOS never grants the mic
61
+ permission for it outside of Safari's own app, so these are detected via user-agent and treated as unsupported too
62
+ (button `disabled`, tooltip explains why). Requires the browser's mic permission prompt to be accepted where
63
+ supported.
64
+
65
+ ### Voice punctuation
66
+
67
+ Two independent behaviors, each its own boolean prop, **both default `true`** (identical to `AbChatInput`'s):
68
+
69
+ - **`voicePunctuationCommands`** — saying a punctuation word inserts the actual mark: English `"period"`/
70
+ `"full stop"` → `.`, `"comma"` → `,`, `"question mark"` → `?`; Bengali `"পূর্ণচ্ছেদ"` → `।`, `"কমা"` → `,`,
71
+ `"প্রশ্নবোধক"` → `?`. Matched against the transcript text itself, independent of `lang`. Matching uses
72
+ script-agnostic Unicode lookaround (not `\b`, a no-op for Bengali script), so a command word only triggers
73
+ standalone, never as a substring of a longer word.
74
+ - **`voiceAutoPunctuate`** — a sentence-ending mark is inserted between voice utterances when the existing text
75
+ doesn't already end with one — `.` normally, `।` when `lang` (or the auto-detected language) starts with `bn`.
76
+
77
+ ## Props
78
+
79
+ | Name | Type | Default | Description |
80
+ | --- | --- | --- | --- |
81
+ | `modelValue` | `String` | `""` | Two-way-bound transcribed text. |
82
+ | `disabled` | `Boolean` | `false` | Disables the button. |
83
+ | `size` | `Number` | `40` | Button diameter in px. Icon and listening rings scale with it. |
84
+ | `lang` | `String` | `""` | BCP-47 recognition locale (e.g. `"bn-BD"`). Empty = auto-detect from `document.documentElement.lang` / `navigator.language`. |
85
+ | `voicePunctuationCommands` | `Boolean` | `true` | Say a punctuation word (e.g. "period", "পূর্ণচ্ছেদ") to insert the actual mark — see "Voice punctuation" above. |
86
+ | `voiceAutoPunctuate` | `Boolean` | `true` | Auto-insert a sentence-ending mark between voice utterances when one isn't already there — see "Voice punctuation" above. |
87
+
88
+ ## Events
89
+
90
+ | Event | Payload | Description |
91
+ | --- | --- | --- |
92
+ | `update:modelValue` | `string` | v-model sync. |
93
+ | `voice-start` | — | Voice recording started (mic tapped, or `startRecording()` called). |
94
+ | `voice-stop` | — | Recording stopped normally (mic tapped again, `stopRecording()`, or the browser ending it on its own) — transcribed text is kept in `modelValue`. |
95
+ | `voice-cancel` | — | Recording was cancelled via `cancelRecording()` — `modelValue` is reverted to what it was before recording started. |
96
+ | `voice-error` | `Error \| SpeechRecognitionErrorEvent` | Mic permission was denied, or the recognition engine errored. A `useToast` error is also shown (except benign `no-speech`/`aborted` errors). |
97
+
98
+ ## Slots
99
+
100
+ None.
101
+
102
+ ## v-model
103
+
104
+ Yes — `modelValue` + `update:modelValue` (string).
105
+
106
+ ## Exposed methods
107
+
108
+ | Method | Description |
109
+ | --- | --- |
110
+ | `startRecording()` | Starts voice input (same as tapping the button). No-op if unsupported/disabled/already recording. |
111
+ | `stopRecording()` | Stops recording, keeping the transcribed text (same as tapping the button again). |
112
+ | `cancelRecording()` | Stops recording and reverts `modelValue` to what it was before recording started. No dedicated UI for this — call it via a template ref. |
@@ -25,7 +25,8 @@
25
25
 
26
26
  - New `formatCurrency`/`formatAmount` utils + matching global config keys (`currency`, `currencyPosition`, `thousandSeparator`, `decimalSeparator`, `numberOfDecimals`, `round`) — optional to adopt for consumer-side number/currency formatting.
27
27
  - New `AbChatBox` component (wraps `AbChatInput` as a full chat UI) and new `AbScrollbar` exposed scroll methods.
28
- - `AbFormCheck` checked-state label color and `description-text` truncation CSS fixes; a WordPress `.is-flat-check` margin conflict fix.
28
+ - `AbFormCheck` checked-state label color and `description-text` truncation CSS fixes; a WordPress `.apbd-radio-input-container` margin conflict fix (covers all enhanced variants, not just `card-flat-check`).
29
+ - `AbSidebar` header no longer clamps to a fixed height and top-aligns its content instead of centering — lets multi-line header content grow the header instead of being clipped/centered.
29
30
  - Several `AbChatInput` voice-input (Safari/Android/iOS) and narrow-viewport layout fixes.
30
31
 
31
32
  ## [1.0.14] - 2026-08-19
package/AI_REFERENCE.md CHANGED
@@ -61,6 +61,7 @@
61
61
  | `AbChatInput` | — | ChatGPT-style auto-growing composer with an item-count-driven add/more dropdown, internal file attachment preview, and built-in Web Speech API voice input. | [.ai/ai_ref_AbChatInput.md](./.ai/ai_ref_AbChatInput.md) |
62
62
  | `AbChatInputMenuItem` | — | Presentational row (icon/name/description/action) for `AbChatInput`'s add/more dropdown. | [.ai/ai_ref_AbChatInputMenuItem.md](./.ai/ai_ref_AbChatInputMenuItem.md) |
63
63
  | `AbChatBox` | — | Full chat conversation container (header, message list, empty/typing states) built on `AbChatInput`; every part is slot-overridable. | [.ai/ai_ref_AbChatBox.md](./.ai/ai_ref_AbChatBox.md) |
64
+ | `AbVoiceButton` | — | Standalone Web Speech API voice-to-text mic button (v-model), no composer attached — same engine as `AbChatInput`'s built-in mic. | [.ai/ai_ref_AbVoiceButton.md](./.ai/ai_ref_AbVoiceButton.md) |
64
65
  | `AbDarkModeToggler` | — | Light / dark theme toggle (pairs with `useTheme`). | [.ai/ai_ref_AbDarkModeToggler.md](./.ai/ai_ref_AbDarkModeToggler.md) |
65
66
  | `useAlert` | — | Procedural API to trigger SweetAlert-based modal alerts, notifications, and confirmations. | [.ai/ai_ref_useAlert.md](./.ai/ai_ref_useAlert.md) |
66
67
  | `abEventBus` / `emitterObj` | — | Lightweight global event bus using `tiny-emitter`. | [.ai/ai_ref_abEventBus.md](./.ai/ai_ref_abEventBus.md) |
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to `@appsbd/vue3-appsbd-ui` are documented here. Format loosely follows [Keep a Changelog](https://keepachangelog.com/).
4
4
 
5
+ ## [1.0.16] - 2026-08-27
6
+
7
+ - **Fixed**: WordPress theme conflict — `AbFormCheck`'s enhanced (`.apbd-radio-input-container`) checkbox/radio inputs now reset `margin: 0` to counter WordPress's default form-control margin. (Previously targeted `.is-flat-check`, which only covers the `card-flat-check` variant and missed the other enhanced variants.)
8
+ - **Fixed**: `AbSidebar` header (`.apbd-sidebar-header`) no longer clamps to a fixed `min-height`/`max-height` (now `unset`) and aligns its content to the top (`align-items: start` instead of `center`) — the header can grow to fit multi-line content instead of clipping/centering it within a fixed band.
9
+ - **Changed**: `AbChatInput` voice recording on Android only — no longer swaps the composer for the full-width recording bar (every other platform keeps that unchanged: waveform + cancel/stop/submit buttons). On Android the textarea now stays visible and fully editable the whole time instead (typing/deleting mid-recording works normally) and fills with the transcribed text live, in place; the mic button gets a Google Assistant-style listening cue instead — its circle fills with the theme's primary color (icon turns white), with a few rings pulsing outward from behind it on a loop. Tap it to stop (keeps the text); the send button (and Enter) still submits immediately while listening by stopping first. There's no cancel button in this Android UI — `cancelRecording()` remains available programmatically via a template ref.
10
+ - **Fixed**: `AbChatInput` voice input on Android — text was only appearing once the user tapped stop, and could double up when it did (words repeating mid-recording, not just at the end). Android's `continuous`+`interimResults` combination turned out unreliable across builds in a way delta-tracking `e.results` (by `resultIndex` or by position) can't fully work around — it can restate/duplicate earlier words in a later result with no restart involved at all. Android now runs one utterance per internal recognition session (`continuous: false`) instead, relying on the existing transparent onend-restart (added earlier for a Safari quirk) to pick the next utterance back up automatically — so `onresult` only ever needs that session's latest result, with no cross-event bookkeeping to get wrong. Combined with the textarea staying editable during recording (see above), `onresult` now also reads the current text and swaps out only the exact suffix it last inserted, so a manual edit mid-recording is preserved instead of being silently overwritten.
11
+ - **Fixed**: `AbChatInput`'s Android listening cue (see above) — the solid circle and ring animation never actually rendered, in either light or dark mode. They referenced `--#{$prefix}apbd-primary`, a CSS variable that isn't defined anywhere in the project; with no fallback, `var()` resolves to nothing and the whole declaration is silently dropped. Now uses `--#{$prefix}theme` (the actual theme accent variable, defined per-skin for both modes), with a `#0d6efd` fallback for safety.
12
+ - **Changed**: `AbChatInput`'s Android listening rings scaled down slightly (max `2.4` → `1.7`) — the previous size read as too large next to the language badge and send button. The textarea also refocuses automatically now the moment listening ends, on every platform (stop, cancel, or a voice-triggered submit) — the cursor lands back in it immediately instead of requiring an extra tap.
13
+ - **Fixed**: `AbChatInput`'s mic button on touchscreens — its `:hover` background had no touch guard, so tapping it (e.g. to stop recording on Android) could leave a lingering gray circle behind, a well-known mobile quirk: touch has no real pointer to move away and clear `:hover` the way a mouse does. Wrapped in `@media (hover: hover)` so it only applies on devices with genuine hover capability.
14
+ - **Added**: `AbChatInput` `voicePunctuationCommands` and `voiceAutoPunctuate` props (both `Boolean`, default `true`, independently toggleable) for voice input punctuation. `voicePunctuationCommands`: saying a punctuation word inserts the actual mark — English `"period"`/`"full stop"` → `.`, `"comma"` → `,`, `"question mark"` → `?`; Bengali `"পূর্ণচ্ছেদ"` → `।`, `"কমা"` → `,`, `"প্রশ্নবোধক"` → `?` — matched against the transcript text itself, independent of the selected recognition language. `voiceAutoPunctuate`: a sentence-ending mark (`.`, or `।` when `voiceLang` starts with `bn`) is inserted between voice utterances (each final phrase on non-Android, each per-utterance restart on Android) whenever the existing text doesn't already end with one.
15
+ - **Fixed**: `AbChatInput`'s `voicePunctuationCommands` (see above) — command-word matching used `\b`, which only recognizes ASCII letters/digits as "word" characters and is effectively a no-op for Bengali script, so a Bengali trigger word could match as a substring inside an unrelated longer word (e.g. saying "দাঁড়িপাল্লা" — "scale/balance" — got mangled into "। পাল্লা"). Now uses script-agnostic lookaround instead, matching only when the word is actually standalone. `"দাড়ি"` was also dropped entirely as a trigger (not just boundary-guarded) — fixing the substring match doesn't fix it also being ordinary vocabulary (beard), so a sentence genuinely about a beard would still lose that word every time; `"পূর্ণচ্ছেদ"` (a purely technical term nobody says in normal conversation) remains as the sole Bengali trigger for `.`/`।`.
16
+ - **Added**: `AbVoiceButton` — a standalone voice-to-text mic button, the same recognition engine as `AbChatInput`'s built-in mic with no composer attached. `v-model` for the transcribed text; `voice-start`/`voice-stop`/`voice-cancel`/`voice-error` events; `startRecording()`/`stopRecording()`/`cancelRecording()` exposed methods. `size` (px, default `40`) and `lang` (BCP-47, auto-detected when unset — no built-in language picker here) props, plus the same `voicePunctuationCommands`/`voiceAutoPunctuate` props as `AbChatInput`. Unlike `AbChatInput`, the Google Assistant-style listening cue (solid theme-color circle + pulsing rings) and the live, per-utterance recognition architecture run on every platform here, not just Android — this component has no legacy full recording-bar behavior to preserve, so there was no reason to keep the two paths.
17
+ - **Changed**: `AbChatInput` voice recording — the Google Assistant-style listening cue (mic icon stays put with pulsing rings, textarea stays visible and live-editable the whole time) now runs on every platform, not just Android, matching `AbVoiceButton`'s architecture. The separate full-width recording bar (audio-reactive waveform + cancel/stop/submit buttons), previously kept for every platform except Android, is removed entirely — there's no dedicated cancel button in the UI anymore; `cancelRecording()` remains available programmatically via a template ref, and the default send button (or `submit()`/Enter) still stops-then-submits when used while listening.
18
+ - **Fixed**: `AbChatInput` voice-to-text not reliably converting speech on non-Android platforms — the old pipeline only ever committed text off a `SpeechRecognition` result's `isFinal` flag, which some browsers/builds hold back indefinitely instead of firing per pause segment, so speech could go untranscribed until — or even past — the point the user stopped recording. Every platform now runs the same per-utterance (`continuous: false`) + `interimResults: true` architecture already proven on Android, merging every recognized chunk into the text live instead of waiting on `isFinal`; the now-unused `getUserMedia`/`AnalyserNode` waveform pipeline (mic-hardware-exclusive, and irrelevant once the waveform UI it drove is gone) was removed along with it.
19
+ - **Fixed**: `AbChatInput` and `AbVoiceButton` mic button showing "Microphone permission denied" on Safari (desktop and iOS) after the `interimResults: true` change above — Safari's `SpeechRecognition` engine has a long-standing bug where turning on `interimResults` can fail to (re)start at all, surfacing as a generic `onerror('not-allowed')` indistinguishable from an actually-denied permission. Safari now keeps `interimResults: false` (final-only, its reliably-supported mode) while every other browser still gets live interim updates; `onresult`'s merge logic already handled final-only results correctly, so no other change was needed.
20
+ - **Fixed**: `AbChatInput` voice input transcribing sentences entirely lowercase (e.g. "hello how are you. hello" instead of "Hello how are you. Hello") — recognized speech was inserted as-is with no capitalization pass. Both existing punctuation props now also auto-capitalize the sentence they start: `voiceAutoPunctuate` capitalizes the first letter of a new utterance right after inserting a boundary mark (and the very first letter of the whole message unconditionally); `voicePunctuationCommands` capitalizes the letter right after any mark it inserts mid-utterance (`"hello period how are you"` → `"Hello. How are you"`). Applied only to the freshly recognized chunk, never to already-committed text, so it can't fight a manual edit made to an earlier sentence while still recording. Uses plain `toUpperCase()` on the matched letter — a safe no-op for scripts without letter case (Bengali, Arabic, CJK, …), so it needs no per-language list to work for any cased script.
21
+
5
22
  ## [1.0.15] - 2026-08-24
6
23
 
7
24
  - **Added**: `AbChatBox` — a full chat conversation container built on top of `AbChatInput`: header (title/subtitle/avatar, or fully custom via the `header`/`header-actions` slots), a scrollable message list with a default bubble renderer (overridable per-message via `message`/`message-avatar`), empty and typing states (`empty`/`typing` slots), and the composer docked at the bottom. `v-model:messages` holds the conversation, `v-model` holds the composer text; submitting auto-appends the user's message and fires `send`. Every `AbChatInput` prop, event, and slot (`left`/`right`/`send-button`/`menu-trigger`/`single-input`/`multiple-input`) is forwarded straight through to the inner composer.