stimeo-ui 0.9.0 → 0.11.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.
Files changed (162) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/dist/cable/index.d.ts +79 -14
  3. package/dist/cable/index.js +226 -43
  4. package/dist/cable/index.js.map +1 -1
  5. package/dist/controllers/accordion_controller.js.map +1 -1
  6. package/dist/controllers/alert_dialog_controller.js +17 -7
  7. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  8. package/dist/controllers/announcer_controller.js +1 -1
  9. package/dist/controllers/announcer_controller.js.map +1 -1
  10. package/dist/controllers/auto_submit_controller.js.map +1 -1
  11. package/dist/controllers/avatar_controller.js.map +1 -1
  12. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  13. package/dist/controllers/bulk_select_controller.js +2 -2
  14. package/dist/controllers/bulk_select_controller.js.map +1 -1
  15. package/dist/controllers/calendar_controller.js.map +1 -1
  16. package/dist/controllers/carousel_controller.js.map +1 -1
  17. package/dist/controllers/character_counter_controller.js.map +1 -1
  18. package/dist/controllers/clipboard_controller.js.map +1 -1
  19. package/dist/controllers/collapsible_controller.d.ts +1 -1
  20. package/dist/controllers/collapsible_controller.js +1 -1
  21. package/dist/controllers/collapsible_controller.js.map +1 -1
  22. package/dist/controllers/color_picker_controller.d.ts +1 -1
  23. package/dist/controllers/color_picker_controller.js.map +1 -1
  24. package/dist/controllers/combobox_controller.js.map +1 -1
  25. package/dist/controllers/command_palette_controller.js +15 -5
  26. package/dist/controllers/command_palette_controller.js.map +1 -1
  27. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  28. package/dist/controllers/confirm_controller.js +15 -5
  29. package/dist/controllers/confirm_controller.js.map +1 -1
  30. package/dist/controllers/context_menu_controller.d.ts +5 -4
  31. package/dist/controllers/context_menu_controller.js.map +1 -1
  32. package/dist/controllers/count_up_controller.js.map +1 -1
  33. package/dist/controllers/countdown_controller.js +1 -1
  34. package/dist/controllers/countdown_controller.js.map +1 -1
  35. package/dist/controllers/data_grid_controller.js +1 -1
  36. package/dist/controllers/data_grid_controller.js.map +1 -1
  37. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  38. package/dist/controllers/dialog_controller.js +15 -5
  39. package/dist/controllers/dialog_controller.js.map +1 -1
  40. package/dist/controllers/direct_upload_controller.d.ts +2 -2
  41. package/dist/controllers/direct_upload_controller.js +1 -5
  42. package/dist/controllers/direct_upload_controller.js.map +1 -1
  43. package/dist/controllers/dismissible_controller.js.map +1 -1
  44. package/dist/controllers/drawer_controller.d.ts +2 -2
  45. package/dist/controllers/drawer_controller.js +17 -7
  46. package/dist/controllers/drawer_controller.js.map +1 -1
  47. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  48. package/dist/controllers/flash_controller.js.map +1 -1
  49. package/dist/controllers/focus_controller.d.ts +5 -0
  50. package/dist/controllers/focus_controller.js +45 -7
  51. package/dist/controllers/focus_controller.js.map +1 -1
  52. package/dist/controllers/form_field_controller.js +1 -1
  53. package/dist/controllers/form_field_controller.js.map +1 -1
  54. package/dist/controllers/form_validation_controller.js.map +1 -1
  55. package/dist/controllers/frame_loading_controller.js.map +1 -1
  56. package/dist/controllers/highlight_controller.js.map +1 -1
  57. package/dist/controllers/input_mask_controller.js.map +1 -1
  58. package/dist/controllers/intersection_controller.d.ts +4 -3
  59. package/dist/controllers/intersection_controller.js.map +1 -1
  60. package/dist/controllers/lazy_frame_controller.d.ts +2 -2
  61. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  62. package/dist/controllers/listbox_controller.js.map +1 -1
  63. package/dist/controllers/masonry_controller.d.ts +3 -1
  64. package/dist/controllers/masonry_controller.js.map +1 -1
  65. package/dist/controllers/menu_controller.d.ts +1 -1
  66. package/dist/controllers/menu_controller.js.map +1 -1
  67. package/dist/controllers/menubar_controller.js +2 -2
  68. package/dist/controllers/menubar_controller.js.map +1 -1
  69. package/dist/controllers/meter_controller.d.ts +1 -1
  70. package/dist/controllers/meter_controller.js.map +1 -1
  71. package/dist/controllers/multi_select_controller.js +1 -1
  72. package/dist/controllers/multi_select_controller.js.map +1 -1
  73. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  74. package/dist/controllers/nested_form_controller.js.map +1 -1
  75. package/dist/controllers/number_input_controller.js.map +1 -1
  76. package/dist/controllers/otp_controller.js.map +1 -1
  77. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  78. package/dist/controllers/overflow_menu_controller.js +2 -2
  79. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  80. package/dist/controllers/pagination_controller.d.ts +1 -1
  81. package/dist/controllers/pagination_controller.js.map +1 -1
  82. package/dist/controllers/password_reveal_controller.d.ts +27 -2
  83. package/dist/controllers/password_reveal_controller.js +102 -3
  84. package/dist/controllers/password_reveal_controller.js.map +1 -1
  85. package/dist/controllers/password_strength_controller.d.ts +71 -17
  86. package/dist/controllers/password_strength_controller.js +287 -42
  87. package/dist/controllers/password_strength_controller.js.map +1 -1
  88. package/dist/controllers/pointer_drag_controller.d.ts +23 -5
  89. package/dist/controllers/pointer_drag_controller.js +64 -18
  90. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  91. package/dist/controllers/popover_controller.js.map +1 -1
  92. package/dist/controllers/portal_controller.d.ts +17 -4
  93. package/dist/controllers/portal_controller.js +32 -11
  94. package/dist/controllers/portal_controller.js.map +1 -1
  95. package/dist/controllers/preview_guard_controller.d.ts +22 -14
  96. package/dist/controllers/preview_guard_controller.js +180 -20
  97. package/dist/controllers/preview_guard_controller.js.map +1 -1
  98. package/dist/controllers/progress_controller.js.map +1 -1
  99. package/dist/controllers/radio_group_controller.js.map +1 -1
  100. package/dist/controllers/range_slider_controller.js.map +1 -1
  101. package/dist/controllers/rating_controller.js.map +1 -1
  102. package/dist/controllers/read_more_controller.js.map +1 -1
  103. package/dist/controllers/relative_time_controller.js.map +1 -1
  104. package/dist/controllers/reset_before_cache_controller.d.ts +1 -2
  105. package/dist/controllers/reset_before_cache_controller.js.map +1 -1
  106. package/dist/controllers/resizable_controller.d.ts +1 -1
  107. package/dist/controllers/resizable_controller.js +1 -1
  108. package/dist/controllers/resizable_controller.js.map +1 -1
  109. package/dist/controllers/roving_controller.d.ts +17 -4
  110. package/dist/controllers/roving_controller.js +127 -14
  111. package/dist/controllers/roving_controller.js.map +1 -1
  112. package/dist/controllers/scroll_area_controller.js.map +1 -1
  113. package/dist/controllers/scroll_restore_controller.d.ts +33 -11
  114. package/dist/controllers/scroll_restore_controller.js +127 -27
  115. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  116. package/dist/controllers/scroll_visibility_controller.d.ts +42 -10
  117. package/dist/controllers/scroll_visibility_controller.js +167 -14
  118. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  119. package/dist/controllers/scrollspy_controller.js.map +1 -1
  120. package/dist/controllers/separator_controller.js.map +1 -1
  121. package/dist/controllers/sidebar_controller.js +15 -5
  122. package/dist/controllers/sidebar_controller.js.map +1 -1
  123. package/dist/controllers/skeleton_controller.js.map +1 -1
  124. package/dist/controllers/slider_controller.js.map +1 -1
  125. package/dist/controllers/spinner_controller.js.map +1 -1
  126. package/dist/controllers/step_indicator_controller.js +0 -5
  127. package/dist/controllers/step_indicator_controller.js.map +1 -1
  128. package/dist/controllers/stick_to_bottom_controller.d.ts +26 -9
  129. package/dist/controllers/stick_to_bottom_controller.js +149 -28
  130. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  131. package/dist/controllers/submit_once_controller.js.map +1 -1
  132. package/dist/controllers/switch_controller.js.map +1 -1
  133. package/dist/controllers/tabs_controller.js.map +1 -1
  134. package/dist/controllers/tags_input_controller.js.map +1 -1
  135. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  136. package/dist/controllers/theme_controller.d.ts +32 -6
  137. package/dist/controllers/theme_controller.js +151 -40
  138. package/dist/controllers/theme_controller.js.map +1 -1
  139. package/dist/controllers/time_picker_controller.js.map +1 -1
  140. package/dist/controllers/toast_controller.d.ts +2 -2
  141. package/dist/controllers/toast_controller.js +2 -2
  142. package/dist/controllers/toast_controller.js.map +1 -1
  143. package/dist/controllers/toggle_group_controller.js.map +1 -1
  144. package/dist/controllers/toolbar_controller.js.map +1 -1
  145. package/dist/controllers/transition_controller.d.ts +11 -7
  146. package/dist/controllers/transition_controller.js +79 -15
  147. package/dist/controllers/transition_controller.js.map +1 -1
  148. package/dist/controllers/tree_view_controller.js.map +1 -1
  149. package/dist/index.d.ts +6 -3
  150. package/dist/index.js +1188 -318
  151. package/dist/index.js.map +1 -1
  152. package/dist/inspector/cli.d.ts +3 -4
  153. package/dist/inspector/cli.js +1 -0
  154. package/dist/inspector/cli.js.map +1 -1
  155. package/dist/inspector/cli_bin.js +6 -5
  156. package/dist/inspector/cli_bin.js.map +1 -1
  157. package/dist/inspector/examples.json +6 -6
  158. package/dist/inspector/manifest.json +15 -32
  159. package/dist/positioning/index.d.ts +18 -4
  160. package/dist/positioning/index.js +84 -25
  161. package/dist/positioning/index.js.map +1 -1
  162. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -7,6 +7,153 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
  While the version is `0.x`, the public API (the `stimeo--*` data attributes) may
8
8
  change between releases.
9
9
 
10
+ ## [0.11.0] - 2026-09-05
11
+
12
+ Minor release with no new components. Eight existing ones are reworked — the three
13
+ cable controllers (live-counter, presence, typing-indicator) plus pointer-drag,
14
+ portal, preview-guard, roving, and stick-to-bottom. Two of them changed a contract,
15
+ so read Removed before upgrading. The Inspector manifest stays on schema v12.
16
+
17
+ ### Removed
18
+
19
+ - preview-guard: the `mode` Value. `placeholder` alone decides the guard's form —
20
+ empty (the default) hides the element with `visibility`, and any other text takes
21
+ the place of its content.
22
+ - typing-indicator: the live region on the `status` target. It is a plain visible
23
+ slot now — drop its `aria-live`, seat a `stimeo--announcer` on the page, and set
24
+ `announceOneText` / `announceManyText`. `stimeo check` stops asking for the
25
+ live-region semantics too.
26
+
27
+ ### Added
28
+
29
+ - typing-indicator: `announceOneText` / `announceManyText` (`{name}` / `{names}` /
30
+ `{count}`), debounced through the shared announcer.
31
+ - pointer-drag: a `reset` action that returns the element to its origin — dropping
32
+ the committed follow offset and cancelling an in-flight drag.
33
+
34
+ ### Changed
35
+
36
+ - The cable controllers share one wire subscription per identifier (channel +
37
+ params), so a second widget on the same stream sends instead of waiting on a
38
+ confirmation that never comes. A caller joining a confirmed or refused
39
+ identifier is told so on the next microtask, and the wire is released when the
40
+ last one leaves.
41
+ - Cable identifier parameters are read by the controller, so an unreadable
42
+ `params` declaration falls back to the channel alone instead of stopping the
43
+ subscription from being created at all.
44
+ - An element moved within the page keeps what it was carrying: pointer-drag's
45
+ session and the attributes it lends its handles, portal's teleport,
46
+ preview-guard's guard, and roving's tab stop.
47
+ - A declaration that cannot be read falls back to its default — typing-indicator's
48
+ `timeout` / `throttle`, stick-to-bottom's `threshold`, and portal's `to`, which
49
+ now lands the node at the default destination instead of leaving it in place.
50
+ - A key that steers an IME conversion is left to the composition (pointer-drag,
51
+ roving), roving passes a modified `Home` / `End` through to the browser, and
52
+ pointer-drag leaves a press to a native control or editing surface inside the
53
+ handle.
54
+ - roving: an item that cannot take focus — `hidden`, natively `disabled`,
55
+ including through a `fieldset` — is neither a move target nor a tab-stop
56
+ candidate, and the horizontal arrow pair follows the writing direction.
57
+ - pointer-drag: the `touch-action` and `tabindex` lent to a handle are given back
58
+ when an element stops being one, and a handle that leaves the controller ends
59
+ its session in `cancel`.
60
+ - preview-guard: the rewind Turbo's snapshot needs runs on `turbo:before-cache`,
61
+ the inline `visibility` is leased so an authored declaration survives, and the
62
+ child markup a placeholder displaced is put back intact.
63
+ - Transient state is re-derived on connect rather than trusted from a restored
64
+ snapshot: the cable controllers' rejection hooks, stick-to-bottom's
65
+ `data-pinned` / `data-has-new`, and preview-guard's `data-preview-hidden`.
66
+ - A swap at runtime is followed rather than left behind: typing-indicator repaints
67
+ a `status` slot swapped in mid-conversation, stick-to-bottom moves its append
68
+ watch onto a `content` target that arrives or leaves and re-derives pinned from a
69
+ `threshold` changed on the element, and preview-guard re-forms a guard that is
70
+ already up to match a changed `placeholder`.
71
+
72
+ ### Fixed
73
+
74
+ - stick-to-bottom: a container connected without a box holds unpinned until the
75
+ layout arrives, so what arrives meanwhile is flagged rather than followed into a
76
+ box that cannot move.
77
+ - pointer-drag: an in-flight follow offset leaves the DOM on teardown, so a
78
+ re-inserted element does not read it back as a committed base.
79
+ - portal: an instance that has been replaced on the same element no longer rewinds
80
+ the teleport the live one holds.
81
+ - typing-indicator: a name carrying `$&`, `` $` ``, `$'` or `$$` is written into the
82
+ status copy literally instead of expanding into the template's own text.
83
+
84
+ ## [0.10.0] - 2026-08-31
85
+
86
+ Minor release with no new components. Eight existing ones are reworked — anchored,
87
+ focus, password-reveal, password-strength, scroll-restore, scroll-visibility,
88
+ theme, and transition — and a shared change to the focus trap reaches the six
89
+ modal overlays too. Most of them changed a contract, so read Removed and Changed
90
+ before upgrading. The Inspector manifest stays on schema v12.
91
+
92
+ ### Removed
93
+
94
+ - theme: the `mode` action param. Options declare their mode with
95
+ `data-value="light|dark|system"` — migrate every option or it can never become
96
+ the selection.
97
+ - password-strength: the live region on the `label` target. Drop its `aria-live`,
98
+ seat a `stimeo--announcer` on the page, and set `announceText`.
99
+ - focus: `[autofocus]` initial focus and the optional scroll lock, neither of
100
+ which was ever implemented. Initial focus is the `initial` target, else the
101
+ first focusable descendant, else the container.
102
+
103
+ ### Added
104
+
105
+ - password-strength: `announceText` (`{level}` / `{score}` / `{max}` / `{band}`),
106
+ a `setScore` action for a score computed outside the built-in heuristic, and a
107
+ `reconcile` event.
108
+ - `stimeo check` warns when a page uses password-strength with no
109
+ `stimeo--announcer`.
110
+
111
+ ### Changed
112
+
113
+ - alert-dialog, command-palette, confirm, dialog, drawer, sidebar, and focus:
114
+ background isolation walks from the element up to `<body>` and inerts each
115
+ ancestor's other children, so one nested below `<body>` isolates its own branch.
116
+ - transition: declare a token as a stage Value **or** author it as a standing
117
+ class, not both — a token already on the element is the consumer's and is never
118
+ applied or removed. `connect()` no longer strips half-applied stage classes.
119
+ - A half-applied state is rewound before Turbo caches the page, silently:
120
+ transition's stage classes, focus's `data-focus-trapped`, password-reveal's
121
+ revealed field, and everything password-strength derives from the field value.
122
+ - focus reads its declarations at fixed moments: only `trap` is watched, `auto`
123
+ and `inert` are read when the trap turns on, and `restore` when it turns off.
124
+ - A declaration that cannot be read falls back to its default instead of poisoning
125
+ the widget — anchored's `placement` / `offset` / `padding`, theme's `mode` /
126
+ `target`, scroll-visibility's `offset` / `root` / `focusSelector`,
127
+ password-strength's `minScore` / `levels`.
128
+ - Targets and declarations swapped in at runtime are followed: anchored's `anchor`
129
+ / `floating`, password-reveal's `input` / `toggle`, password-strength's targets,
130
+ theme's options, scroll-visibility's `element` / `offset` / `mode`, and
131
+ scroll-restore's `key` / `axis`.
132
+ - Events report transitions only — theme's `change` when `mode` or `resolved`
133
+ actually moved, scroll-visibility's `change` on a real visibility change
134
+ (connecting is silent), password-strength's announcement when the level changes.
135
+ A scroll with no vertical movement leaves scroll-visibility alone.
136
+ - Smaller contract shifts: password-strength's `data-strength` bands anchor both
137
+ ends of the declared scale (a level count other than four moves the boundaries)
138
+ and its `meter` target needs an accessible name; scroll-restore always restores
139
+ instantly (so `scroll-behavior: smooth` no longer animates it) and writes
140
+ nothing for an element it never restored and the reader never scrolled; theme writes
141
+ `aria-pressed` only on a button and passes modified `Home` / `End` through.
142
+
143
+ ### Fixed
144
+
145
+ - transition: `enter()` from `hidden` runs the CSS transition instead of popping
146
+ in, so `entered` arrives on the real terminal event rather than the safety
147
+ timeout.
148
+ - A control that owns focus is no longer hidden or stranded: scroll-visibility
149
+ holds the hide until it blurs and `toTop` focuses without scrolling, and focus's
150
+ state hook and events follow the controller's own record of trapping.
151
+ - Stored and measured state holds: scroll-restore keeps a saved offset that a
152
+ clamped restore would have overwritten and never republishes it under a new
153
+ `key`; password-reveal arms the `autoHide` it skipped on an already-revealed
154
+ field; anchored measures a target swapped in at runtime and writes no stale
155
+ placement after teardown.
156
+
10
157
  ## [0.9.0] - 2026-08-28
11
158
 
12
159
  Minor release with no new components. Ten existing ones are reworked —
@@ -783,6 +930,8 @@ Initial public alpha: 101 behavior-only, accessible Stimulus controllers driven
783
930
  by `data-*` attributes, shipping no CSS. Published to npm (with provenance) and
784
931
  RubyGems.
785
932
 
933
+ [0.11.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.11.0
934
+ [0.10.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.10.0
786
935
  [0.9.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.9.0
787
936
  [0.8.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.8.0
788
937
  [0.7.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.7.0
@@ -28,6 +28,8 @@ import { Controller, Application } from '@hotwired/stimulus';
28
28
  * bump is skipped and the broadcast applies the increment exactly once — set
29
29
  * `id` (any per-client string, e.g. `SecureRandom.uuid`) for optimistic UX.
30
30
  *
31
+ * `change` dispatches `{ count }`.
32
+ *
31
33
  * @remarks
32
34
  * Behavior only — the displayed number IS the state, and the **DOM is the
33
35
  * source of truth**: the server renders the initial count into the `value`
@@ -58,8 +60,8 @@ declare class LiveCounterController extends Controller<HTMLElement> {
58
60
  default: string;
59
61
  };
60
62
  params: {
61
- type: ObjectConstructor;
62
- default: {};
63
+ type: StringConstructor;
64
+ default: string;
63
65
  };
64
66
  id: {
65
67
  type: StringConstructor;
@@ -72,8 +74,15 @@ declare class LiveCounterController extends Controller<HTMLElement> {
72
74
  readonly valueTarget: HTMLElement;
73
75
  readonly triggerTargets: HTMLElement[];
74
76
  channelValue: string;
75
- paramsValue: Record<string, unknown>;
77
+ paramsValue: string;
76
78
  idValue: string;
79
+ /**
80
+ * Re-parses the identifier parameters when the declaration changes.
81
+ *
82
+ * A malformed declaration falls back to no parameters, so the identifier keeps
83
+ * naming the channel instead of the subscription never being created at all.
84
+ */
85
+ paramsValueChanged(): void;
77
86
  connect(): void;
78
87
  disconnect(): void;
79
88
  /** Late-added triggers (e.g. via a Turbo Stream) pick up the current gate. */
@@ -103,7 +112,7 @@ declare class LiveCounterController extends Controller<HTMLElement> {
103
112
  *
104
113
  * Roster convergence: a late joiner would otherwise see peers only as their
105
114
  * next heartbeats arrive, so on hearing a beacon from an *unknown* peer, each
106
- * client re-announces itself (throttled to {@link BEACON_THROTTLE_MS}) — the
115
+ * client re-announces itself (at most one such answer every 2s) — the
107
116
  * roster converges in one round-trip instead of one heartbeat period.
108
117
  *
109
118
  * Markup contract (identifier: `stimeo--presence`):
@@ -125,6 +134,9 @@ declare class LiveCounterController extends Controller<HTMLElement> {
125
134
  * { id: data["id"], name: data["name"], leaving: data["leaving"] })
126
135
  * end
127
136
  *
137
+ * `join` dispatches `{ id, name }`; `leave` dispatches `{ id }`; `change` dispatches
138
+ * `{ users }`.
139
+ *
128
140
  * @remarks
129
141
  * Behavior only — the dot/stack look is the consumer's CSS, keyed off the
130
142
  * `data-present` / `data-present-count` hooks. The roster counts and renders
@@ -160,8 +172,8 @@ declare class PresenceController extends Controller<HTMLElement> {
160
172
  default: string;
161
173
  };
162
174
  params: {
163
- type: ObjectConstructor;
164
- default: {};
175
+ type: StringConstructor;
176
+ default: string;
165
177
  };
166
178
  id: {
167
179
  type: StringConstructor;
@@ -188,11 +200,18 @@ declare class PresenceController extends Controller<HTMLElement> {
188
200
  readonly hasTemplateTarget: boolean;
189
201
  readonly templateTarget: HTMLTemplateElement;
190
202
  channelValue: string;
191
- paramsValue: Record<string, unknown>;
203
+ paramsValue: string;
192
204
  idValue: string;
193
205
  nameValue: string;
194
206
  heartbeatValue: number;
195
207
  timeoutValue: number;
208
+ /**
209
+ * Re-parses the identifier parameters when the declaration changes.
210
+ *
211
+ * A malformed declaration falls back to no parameters, so the identifier keeps
212
+ * naming the channel instead of the subscription never being created at all.
213
+ */
214
+ paramsValueChanged(): void;
196
215
  connect(): void;
197
216
  disconnect(): void;
198
217
  }
@@ -201,7 +220,7 @@ declare class PresenceController extends Controller<HTMLElement> {
201
220
  * Headless **typing indicator** — a *server-bound* behavior: a controller whose
202
221
  * state lives on the server stream, not in client memory. Typing in the composer
203
222
  * throttle-broadcasts a `typing` signal over an Action Cable channel; received
204
- * signals from *other* clients render "X is typing…" into a `status` live region
223
+ * signals from *other* clients render "X is typing…" into a visible `status` slot
205
224
  * and auto-clear after `timeout` ms of silence. The whole behavior is HTML + a
206
225
  * broadcast — no app JS, no client store, no reconciler. Ships in the opt-in
207
226
  * `stimeo-ui/cable` subpath (`@rails/actioncable` optional peer); the core stays
@@ -211,9 +230,10 @@ declare class PresenceController extends Controller<HTMLElement> {
211
230
  * <div data-controller="stimeo--typing-indicator"
212
231
  * data-stimeo--typing-indicator-channel-value="TypingChannel"
213
232
  * data-stimeo--typing-indicator-params-value='{"room":"chat_42"}'
214
- * data-stimeo--typing-indicator-name-value="Alice">
233
+ * data-stimeo--typing-indicator-name-value="Alice"
234
+ * data-stimeo--typing-indicator-announce-one-text-value="{name} is typing">
215
235
  * <textarea data-stimeo--typing-indicator-target="input"></textarea>
216
- * <p role="status" data-stimeo--typing-indicator-target="status"
236
+ * <p data-stimeo--typing-indicator-target="status"
217
237
  * data-one="%{name} is typing…" data-many="%{names} are typing…"></p>
218
238
  * </div>
219
239
  *
@@ -224,6 +244,14 @@ declare class PresenceController extends Controller<HTMLElement> {
224
244
  * { name: data["name"] })
225
245
  * end
226
246
  *
247
+ * The `status` target is a plain visible slot, not a live region: assistive tech is
248
+ * reached through the page's shared announcer instead, so a set that settles is sent
249
+ * there as `announceOneText` / `announceManyText` (`{name}` / `{names}` / `{count}`,
250
+ * debounced). Both default to empty, and an empty message announces nothing — a page
251
+ * that wants the indicator seen but not heard simply leaves them off.
252
+ *
253
+ * `change` dispatches `{ names }`.
254
+ *
227
255
  * @remarks
228
256
  * Behavior only — the indicator's look is the consumer's CSS, keyed off the
229
257
  * `data-typing` hook; the copy is localizable through the `data-one` /
@@ -237,7 +265,7 @@ declare class PresenceController extends Controller<HTMLElement> {
237
265
  * subscription publishes the `data-typing-indicator-rejected` hook (cleared on
238
266
  * `connect()` — rejection is transient server state), mirroring
239
267
  * `data-live-counter-rejected`. Typing state is transient by nature:
240
- * `connect()` resets the hook and the live region (a Turbo cache snapshot must
268
+ * `connect()` resets the hook and the status slot (a Turbo cache snapshot must
241
269
  * not resurrect a stale "X is typing…"), and re-population happens naturally
242
270
  * from the stream. The subscription, the per-typer timers, and the delegated
243
271
  * `input` listener are all released on `disconnect()` (Turbo navigation
@@ -252,8 +280,8 @@ declare class TypingIndicatorController extends Controller<HTMLElement> {
252
280
  default: string;
253
281
  };
254
282
  params: {
255
- type: ObjectConstructor;
256
- default: {};
283
+ type: StringConstructor;
284
+ default: string;
257
285
  };
258
286
  name: {
259
287
  type: StringConstructor;
@@ -267,15 +295,41 @@ declare class TypingIndicatorController extends Controller<HTMLElement> {
267
295
  type: NumberConstructor;
268
296
  default: number;
269
297
  };
298
+ announceOneText: {
299
+ type: StringConstructor;
300
+ default: string;
301
+ };
302
+ announceManyText: {
303
+ type: StringConstructor;
304
+ default: string;
305
+ };
270
306
  };
271
307
  static events: readonly ["change"];
272
308
  readonly hasStatusTarget: boolean;
273
309
  readonly statusTarget: HTMLElement;
274
310
  channelValue: string;
275
- paramsValue: Record<string, unknown>;
311
+ paramsValue: string;
276
312
  nameValue: string;
277
313
  timeoutValue: number;
278
314
  throttleValue: number;
315
+ announceOneTextValue: string;
316
+ announceManyTextValue: string;
317
+ /**
318
+ * Re-parses the identifier parameters when the declaration changes.
319
+ *
320
+ * A malformed declaration falls back to no parameters, so the identifier keeps
321
+ * naming the channel instead of the subscription never being created at all.
322
+ */
323
+ paramsValueChanged(): void;
324
+ /**
325
+ * Paints the current copy into a `status` target that arrived at runtime.
326
+ *
327
+ * No event: the set of typers did not change, only the element that displays it.
328
+ * Without this a region swapped in mid-conversation stays empty while `data-typing`
329
+ * still says someone is typing, leaving the state in the visual hook alone. An empty
330
+ * set needs no paint — a fresh region already shows it.
331
+ */
332
+ statusTargetConnected(): void;
279
333
  connect(): void;
280
334
  disconnect(): void;
281
335
  }
@@ -346,6 +400,17 @@ interface ConfirmedCableSubscription extends CableSubscription {
346
400
  * caller's own handler runs (so a handler reading `subscription.confirmed`
347
401
  * sees the post-transition state), and `received` passes straight through.
348
402
  *
403
+ * **Callers with the same identifier (channel + params) share one wire
404
+ * subscription.** The server confirms an identifier once and silently ignores a
405
+ * repeated `subscribe` for it, so a second wire subscription would never be
406
+ * confirmed — its sends would stay gated and the client would keep re-sending
407
+ * `subscribe` until the next reconnect. Instead every caller is a member of the
408
+ * identifier's one subscription: lifecycle callbacks and broadcasts fan out to all
409
+ * members, a member joining an already confirmed (or refused) identifier is told
410
+ * so on the next microtask — after its caller has stored the returned
411
+ * subscription, the way a callback from the wire would arrive — and the wire is
412
+ * unsubscribed when the last member leaves.
413
+ *
349
414
  * This is deliberately a *tracker*, not an automatic `perform()` gate: the
350
415
  * controllers must skip the local side effects that surround a send (an
351
416
  * optimistic DOM bump, a throttle timestamp) together with the send itself,