stimeo-ui 0.17.0 → 0.18.1

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 (299) hide show
  1. package/CHANGELOG.md +136 -2
  2. package/README.md +19 -10
  3. package/dist/cable/index.d.ts +11 -9
  4. package/dist/cable/index.js +22 -14
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.js +14 -6
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.js +145 -118
  10. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  11. package/dist/controllers/announcer_controller.d.ts +5 -3
  12. package/dist/controllers/announcer_controller.js +113 -17
  13. package/dist/controllers/announcer_controller.js.map +1 -1
  14. package/dist/controllers/auto_submit_controller.js +185 -60
  15. package/dist/controllers/auto_submit_controller.js.map +1 -1
  16. package/dist/controllers/avatar_controller.js +55 -43
  17. package/dist/controllers/avatar_controller.js.map +1 -1
  18. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  19. package/dist/controllers/bulk_select_controller.d.ts +3 -3
  20. package/dist/controllers/bulk_select_controller.js +64 -50
  21. package/dist/controllers/bulk_select_controller.js.map +1 -1
  22. package/dist/controllers/calendar_controller.d.ts +15 -12
  23. package/dist/controllers/calendar_controller.js +55 -53
  24. package/dist/controllers/calendar_controller.js.map +1 -1
  25. package/dist/controllers/carousel_controller.d.ts +9 -5
  26. package/dist/controllers/carousel_controller.js +124 -86
  27. package/dist/controllers/carousel_controller.js.map +1 -1
  28. package/dist/controllers/character_counter_controller.d.ts +27 -6
  29. package/dist/controllers/character_counter_controller.js +291 -95
  30. package/dist/controllers/character_counter_controller.js.map +1 -1
  31. package/dist/controllers/checkbox_controller.js +6 -0
  32. package/dist/controllers/checkbox_controller.js.map +1 -1
  33. package/dist/controllers/clipboard_controller.js +118 -27
  34. package/dist/controllers/clipboard_controller.js.map +1 -1
  35. package/dist/controllers/collapsible_controller.d.ts +1 -1
  36. package/dist/controllers/collapsible_controller.js +52 -49
  37. package/dist/controllers/collapsible_controller.js.map +1 -1
  38. package/dist/controllers/color_picker_controller.js +29 -7
  39. package/dist/controllers/color_picker_controller.js.map +1 -1
  40. package/dist/controllers/combobox_controller.d.ts +1 -1
  41. package/dist/controllers/combobox_controller.js +210 -71
  42. package/dist/controllers/combobox_controller.js.map +1 -1
  43. package/dist/controllers/command_palette_controller.d.ts +9 -9
  44. package/dist/controllers/command_palette_controller.js +290 -140
  45. package/dist/controllers/command_palette_controller.js.map +1 -1
  46. package/dist/controllers/conditional_fields_controller.js +145 -58
  47. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  48. package/dist/controllers/confirm_controller.js +145 -118
  49. package/dist/controllers/confirm_controller.js.map +1 -1
  50. package/dist/controllers/context_menu_controller.d.ts +49 -14
  51. package/dist/controllers/context_menu_controller.js +138 -73
  52. package/dist/controllers/context_menu_controller.js.map +1 -1
  53. package/dist/controllers/count_up_controller.js.map +1 -1
  54. package/dist/controllers/countdown_controller.d.ts +2 -2
  55. package/dist/controllers/countdown_controller.js +109 -21
  56. package/dist/controllers/countdown_controller.js.map +1 -1
  57. package/dist/controllers/currency_input_controller.d.ts +75 -11
  58. package/dist/controllers/currency_input_controller.js +446 -72
  59. package/dist/controllers/currency_input_controller.js.map +1 -1
  60. package/dist/controllers/data_grid_controller.d.ts +10 -8
  61. package/dist/controllers/data_grid_controller.js +134 -28
  62. package/dist/controllers/data_grid_controller.js.map +1 -1
  63. package/dist/controllers/date_range_picker_controller.d.ts +12 -9
  64. package/dist/controllers/date_range_picker_controller.js +93 -72
  65. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  66. package/dist/controllers/dialog_controller.d.ts +4 -5
  67. package/dist/controllers/dialog_controller.js +145 -118
  68. package/dist/controllers/dialog_controller.js.map +1 -1
  69. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  70. package/dist/controllers/direct_upload_controller.js +24 -14
  71. package/dist/controllers/direct_upload_controller.js.map +1 -1
  72. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  73. package/dist/controllers/dirty_form_controller.js +68 -31
  74. package/dist/controllers/dirty_form_controller.js.map +1 -1
  75. package/dist/controllers/dismissible_controller.js +66 -46
  76. package/dist/controllers/dismissible_controller.js.map +1 -1
  77. package/dist/controllers/drawer_controller.js +145 -118
  78. package/dist/controllers/drawer_controller.js.map +1 -1
  79. package/dist/controllers/dropdown_controller.js +76 -63
  80. package/dist/controllers/dropdown_controller.js.map +1 -1
  81. package/dist/controllers/editable_controller.js +200 -62
  82. package/dist/controllers/editable_controller.js.map +1 -1
  83. package/dist/controllers/empty_state_controller.js +13 -6
  84. package/dist/controllers/empty_state_controller.js.map +1 -1
  85. package/dist/controllers/file_dropzone_controller.d.ts +3 -0
  86. package/dist/controllers/file_dropzone_controller.js +55 -48
  87. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  88. package/dist/controllers/filter_controller.js +9 -2
  89. package/dist/controllers/filter_controller.js.map +1 -1
  90. package/dist/controllers/flash_controller.d.ts +4 -4
  91. package/dist/controllers/flash_controller.js +66 -33
  92. package/dist/controllers/flash_controller.js.map +1 -1
  93. package/dist/controllers/focus_controller.d.ts +3 -3
  94. package/dist/controllers/focus_controller.js +39 -14
  95. package/dist/controllers/focus_controller.js.map +1 -1
  96. package/dist/controllers/form_field_controller.d.ts +2 -1
  97. package/dist/controllers/form_field_controller.js +52 -43
  98. package/dist/controllers/form_field_controller.js.map +1 -1
  99. package/dist/controllers/form_validation_controller.d.ts +3 -3
  100. package/dist/controllers/form_validation_controller.js +18 -11
  101. package/dist/controllers/form_validation_controller.js.map +1 -1
  102. package/dist/controllers/frame_loading_controller.d.ts +13 -12
  103. package/dist/controllers/frame_loading_controller.js +55 -48
  104. package/dist/controllers/frame_loading_controller.js.map +1 -1
  105. package/dist/controllers/highlight_controller.d.ts +5 -4
  106. package/dist/controllers/highlight_controller.js.map +1 -1
  107. package/dist/controllers/hover_card_controller.d.ts +4 -1
  108. package/dist/controllers/hover_card_controller.js +67 -61
  109. package/dist/controllers/hover_card_controller.js.map +1 -1
  110. package/dist/controllers/idle_controller.d.ts +6 -5
  111. package/dist/controllers/idle_controller.js +10 -6
  112. package/dist/controllers/idle_controller.js.map +1 -1
  113. package/dist/controllers/input_mask_controller.d.ts +31 -13
  114. package/dist/controllers/input_mask_controller.js +434 -24
  115. package/dist/controllers/input_mask_controller.js.map +1 -1
  116. package/dist/controllers/intersection_controller.js +3 -0
  117. package/dist/controllers/intersection_controller.js.map +1 -1
  118. package/dist/controllers/lazy_frame_controller.d.ts +3 -2
  119. package/dist/controllers/lazy_frame_controller.js +15 -3
  120. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  121. package/dist/controllers/listbox_controller.d.ts +10 -7
  122. package/dist/controllers/listbox_controller.js +260 -79
  123. package/dist/controllers/listbox_controller.js.map +1 -1
  124. package/dist/controllers/local_time_controller.d.ts +8 -6
  125. package/dist/controllers/local_time_controller.js +48 -13
  126. package/dist/controllers/local_time_controller.js.map +1 -1
  127. package/dist/controllers/masonry_controller.js +11 -4
  128. package/dist/controllers/masonry_controller.js.map +1 -1
  129. package/dist/controllers/menu_controller.d.ts +24 -5
  130. package/dist/controllers/menu_controller.js +138 -82
  131. package/dist/controllers/menu_controller.js.map +1 -1
  132. package/dist/controllers/menubar_controller.d.ts +49 -7
  133. package/dist/controllers/menubar_controller.js +65 -19
  134. package/dist/controllers/menubar_controller.js.map +1 -1
  135. package/dist/controllers/meter_controller.d.ts +13 -20
  136. package/dist/controllers/meter_controller.js +270 -24
  137. package/dist/controllers/meter_controller.js.map +1 -1
  138. package/dist/controllers/multi_select_controller.d.ts +19 -16
  139. package/dist/controllers/multi_select_controller.js +272 -95
  140. package/dist/controllers/multi_select_controller.js.map +1 -1
  141. package/dist/controllers/navigation_menu_controller.d.ts +19 -5
  142. package/dist/controllers/navigation_menu_controller.js +67 -19
  143. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  144. package/dist/controllers/nested_form_controller.js +98 -64
  145. package/dist/controllers/nested_form_controller.js.map +1 -1
  146. package/dist/controllers/network_status_controller.js +70 -52
  147. package/dist/controllers/network_status_controller.js.map +1 -1
  148. package/dist/controllers/number_input_controller.d.ts +24 -6
  149. package/dist/controllers/number_input_controller.js +344 -94
  150. package/dist/controllers/number_input_controller.js.map +1 -1
  151. package/dist/controllers/optimistic_controller.d.ts +8 -5
  152. package/dist/controllers/optimistic_controller.js +29 -11
  153. package/dist/controllers/optimistic_controller.js.map +1 -1
  154. package/dist/controllers/otp_controller.d.ts +35 -4
  155. package/dist/controllers/otp_controller.js +502 -104
  156. package/dist/controllers/otp_controller.js.map +1 -1
  157. package/dist/controllers/overflow_indicator_controller.js +6 -0
  158. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  159. package/dist/controllers/overflow_menu_controller.d.ts +10 -9
  160. package/dist/controllers/overflow_menu_controller.js +136 -21
  161. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  162. package/dist/controllers/pagination_controller.d.ts +3 -2
  163. package/dist/controllers/pagination_controller.js +47 -14
  164. package/dist/controllers/pagination_controller.js.map +1 -1
  165. package/dist/controllers/password_reveal_controller.d.ts +5 -4
  166. package/dist/controllers/password_reveal_controller.js +52 -49
  167. package/dist/controllers/password_reveal_controller.js.map +1 -1
  168. package/dist/controllers/password_strength_controller.d.ts +1 -1
  169. package/dist/controllers/password_strength_controller.js +13 -6
  170. package/dist/controllers/password_strength_controller.js.map +1 -1
  171. package/dist/controllers/persist_controller.js +48 -128
  172. package/dist/controllers/persist_controller.js.map +1 -1
  173. package/dist/controllers/pointer_drag_controller.d.ts +20 -8
  174. package/dist/controllers/pointer_drag_controller.js +255 -24
  175. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  176. package/dist/controllers/popover_controller.d.ts +9 -5
  177. package/dist/controllers/popover_controller.js +91 -65
  178. package/dist/controllers/popover_controller.js.map +1 -1
  179. package/dist/controllers/portal_controller.js +6 -0
  180. package/dist/controllers/portal_controller.js.map +1 -1
  181. package/dist/controllers/preview_guard_controller.js +14 -3
  182. package/dist/controllers/preview_guard_controller.js.map +1 -1
  183. package/dist/controllers/progress_controller.d.ts +9 -19
  184. package/dist/controllers/progress_controller.js +271 -27
  185. package/dist/controllers/progress_controller.js.map +1 -1
  186. package/dist/controllers/radio_group_controller.d.ts +11 -8
  187. package/dist/controllers/radio_group_controller.js +167 -80
  188. package/dist/controllers/radio_group_controller.js.map +1 -1
  189. package/dist/controllers/range_slider_controller.d.ts +4 -3
  190. package/dist/controllers/range_slider_controller.js +27 -7
  191. package/dist/controllers/range_slider_controller.js.map +1 -1
  192. package/dist/controllers/rating_controller.js +93 -57
  193. package/dist/controllers/rating_controller.js.map +1 -1
  194. package/dist/controllers/read_more_controller.js +10 -6
  195. package/dist/controllers/read_more_controller.js.map +1 -1
  196. package/dist/controllers/reading_progress_controller.js +22 -5
  197. package/dist/controllers/reading_progress_controller.js.map +1 -1
  198. package/dist/controllers/relative_time_controller.d.ts +4 -4
  199. package/dist/controllers/relative_time_controller.js +75 -11
  200. package/dist/controllers/relative_time_controller.js.map +1 -1
  201. package/dist/controllers/reset_on_restore_controller.d.ts +3 -3
  202. package/dist/controllers/reset_on_restore_controller.js +6 -0
  203. package/dist/controllers/reset_on_restore_controller.js.map +1 -1
  204. package/dist/controllers/resizable_controller.d.ts +3 -0
  205. package/dist/controllers/resizable_controller.js +55 -22
  206. package/dist/controllers/resizable_controller.js.map +1 -1
  207. package/dist/controllers/roving_controller.d.ts +3 -3
  208. package/dist/controllers/roving_controller.js +10 -4
  209. package/dist/controllers/roving_controller.js.map +1 -1
  210. package/dist/controllers/scroll_area_controller.js +129 -88
  211. package/dist/controllers/scroll_area_controller.js.map +1 -1
  212. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  213. package/dist/controllers/scroll_restore_controller.js +14 -2
  214. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  215. package/dist/controllers/scroll_visibility_controller.js +93 -60
  216. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  217. package/dist/controllers/scrollspy_controller.js +11 -5
  218. package/dist/controllers/scrollspy_controller.js.map +1 -1
  219. package/dist/controllers/separator_controller.d.ts +1 -1
  220. package/dist/controllers/separator_controller.js +59 -43
  221. package/dist/controllers/separator_controller.js.map +1 -1
  222. package/dist/controllers/sidebar_controller.d.ts +1 -1
  223. package/dist/controllers/sidebar_controller.js +150 -122
  224. package/dist/controllers/sidebar_controller.js.map +1 -1
  225. package/dist/controllers/skeleton_controller.js +55 -48
  226. package/dist/controllers/skeleton_controller.js.map +1 -1
  227. package/dist/controllers/slider_controller.d.ts +2 -1
  228. package/dist/controllers/slider_controller.js +27 -6
  229. package/dist/controllers/slider_controller.js.map +1 -1
  230. package/dist/controllers/smart_sticky_header_controller.d.ts +1 -1
  231. package/dist/controllers/smart_sticky_header_controller.js +13 -5
  232. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  233. package/dist/controllers/sortable_controller.d.ts +4 -4
  234. package/dist/controllers/sortable_controller.js +27 -14
  235. package/dist/controllers/sortable_controller.js.map +1 -1
  236. package/dist/controllers/spinner_controller.js +55 -48
  237. package/dist/controllers/spinner_controller.js.map +1 -1
  238. package/dist/controllers/step_indicator_controller.d.ts +4 -4
  239. package/dist/controllers/step_indicator_controller.js +6 -0
  240. package/dist/controllers/step_indicator_controller.js.map +1 -1
  241. package/dist/controllers/stepper_controller.d.ts +2 -3
  242. package/dist/controllers/stepper_controller.js +6 -0
  243. package/dist/controllers/stepper_controller.js.map +1 -1
  244. package/dist/controllers/stick_to_bottom_controller.d.ts +3 -1
  245. package/dist/controllers/stick_to_bottom_controller.js +19 -8
  246. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  247. package/dist/controllers/sticky_observer_controller.js +49 -43
  248. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  249. package/dist/controllers/submit_once_controller.d.ts +2 -1
  250. package/dist/controllers/submit_once_controller.js +145 -85
  251. package/dist/controllers/submit_once_controller.js.map +1 -1
  252. package/dist/controllers/switch_controller.d.ts +7 -4
  253. package/dist/controllers/switch_controller.js +172 -28
  254. package/dist/controllers/switch_controller.js.map +1 -1
  255. package/dist/controllers/tabs_controller.d.ts +7 -4
  256. package/dist/controllers/tabs_controller.js +6 -1
  257. package/dist/controllers/tabs_controller.js.map +1 -1
  258. package/dist/controllers/tags_input_controller.d.ts +4 -3
  259. package/dist/controllers/tags_input_controller.js +157 -28
  260. package/dist/controllers/tags_input_controller.js.map +1 -1
  261. package/dist/controllers/textarea_autosize_controller.d.ts +1 -1
  262. package/dist/controllers/textarea_autosize_controller.js +40 -19
  263. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  264. package/dist/controllers/theme_controller.d.ts +3 -2
  265. package/dist/controllers/theme_controller.js +10 -4
  266. package/dist/controllers/theme_controller.js.map +1 -1
  267. package/dist/controllers/time_picker_controller.d.ts +38 -5
  268. package/dist/controllers/time_picker_controller.js +192 -54
  269. package/dist/controllers/time_picker_controller.js.map +1 -1
  270. package/dist/controllers/toast_controller.d.ts +4 -5
  271. package/dist/controllers/toast_controller.js +58 -27
  272. package/dist/controllers/toast_controller.js.map +1 -1
  273. package/dist/controllers/toggle_group_controller.d.ts +2 -3
  274. package/dist/controllers/toggle_group_controller.js +158 -56
  275. package/dist/controllers/toggle_group_controller.js.map +1 -1
  276. package/dist/controllers/toolbar_controller.d.ts +8 -6
  277. package/dist/controllers/toolbar_controller.js +150 -21
  278. package/dist/controllers/toolbar_controller.js.map +1 -1
  279. package/dist/controllers/tooltip_controller.d.ts +4 -1
  280. package/dist/controllers/tooltip_controller.js.map +1 -1
  281. package/dist/controllers/transition_controller.js.map +1 -1
  282. package/dist/controllers/tree_view_controller.d.ts +1 -1
  283. package/dist/controllers/tree_view_controller.js +134 -25
  284. package/dist/controllers/tree_view_controller.js.map +1 -1
  285. package/dist/index.d.ts +4 -1
  286. package/dist/index.js +2515 -1315
  287. package/dist/index.js.map +1 -1
  288. package/dist/inspector/cli.d.ts +25 -12
  289. package/dist/inspector/cli.js +21 -3
  290. package/dist/inspector/cli.js.map +1 -1
  291. package/dist/inspector/cli_bin.d.ts +1 -1
  292. package/dist/inspector/cli_bin.js +62 -8
  293. package/dist/inspector/cli_bin.js.map +1 -1
  294. package/dist/inspector/examples.json +13 -13
  295. package/dist/inspector/manifest.json +19 -5
  296. package/dist/positioning/index.d.ts +9 -8
  297. package/dist/positioning/index.js +6 -0
  298. package/dist/positioning/index.js.map +1 -1
  299. package/package.json +11 -10
package/CHANGELOG.md CHANGED
@@ -7,7 +7,140 @@ 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
- ## [Unreleased]
10
+ ## [0.18.1] - 2026-10-31
11
+
12
+ Patch release focused on keyboard ownership, focus handoff, masked selections,
13
+ and Inspector compatibility.
14
+
15
+ ### Changed
16
+
17
+ - Inspector host contracts can admit the `<input>` types a controller composes
18
+ with instead of replacing (`inputTypes`); any other `<input>` on such a host is
19
+ still reported as `invalid-host`. The manifest carries this as `schemaVersion`
20
+ 17. Update Inspector engines and the VS Code extension together with their
21
+ manifest; an older engine reading it may report an admitted input as
22
+ `invalid-host`.
23
+ - Refresh pinned build and test tooling: Biome 2.6.3, happy-dom 21.0.4,
24
+ tsup 9.0.0, and Rollup 4.66.0.
25
+
26
+ ### Fixed
27
+
28
+ - command-palette and editable: respect keyboard events already handled by another
29
+ listener, including global shortcuts and F2.
30
+ - multi-select: close on Tab from a chip and keep the list closed when that Tab
31
+ reaches the input.
32
+ - popover: close when focus reaches an unrelated control after first falling to
33
+ the document body, while preserving the destination focus.
34
+ - dismissible: release focus from hidden content when no fallback can receive
35
+ focus.
36
+ - input-mask: preserve selected text when a single invalid typed character would
37
+ replace it.
38
+ - pointer-drag: cancel active sessions when a handle loses its target declaration
39
+ and restore the fallback handle when no declared handles remain.
40
+
41
+ ## [0.18.0] - 2026-10-22
42
+
43
+ Minor release with no new components. IME composition, Turbo morphs, and form
44
+ submission now behave consistently across the input components, and several
45
+ contracts change, so read Changed before upgrading. The Inspector manifest moves
46
+ to schema v16; update `stimeo check` and the VS Code extension together and run
47
+ `stimeo check` before deploying.
48
+
49
+ ### Added
50
+
51
+ - time-picker: `amLabel` and `pmLabel` Values set the meridiem text and its
52
+ speech, and `hour12ZeroBased` shows and enters a twelve-hour clock's hours as
53
+ 0–11, as `午前` / `午後` time is written. The submitted value stays on the
54
+ 24-hour clock.
55
+
56
+ ### Changed
57
+
58
+ - **Breaking** input-mask: a synchronized hidden field carries a unique internal
59
+ DOM name; native FormData and native / Turbo submission restore the original
60
+ name, and removing the source submits an empty entry. Find managed fields by id
61
+ or the pairing attribute, use native FormData, and call
62
+ `prepareInputMaskForm(form)` before reading FormData from a new detached form.
63
+ input-mask clears only the raw value it wrote.
64
+ - **Breaking** otp: a pattern drop held for a composition is reported as
65
+ `reconcile` before the edit's own `change` / `complete` / `invalid`, and a
66
+ reconnect under another `pattern` drops what it rejects as `reconcile`. Use the
67
+ edit events for submission.
68
+ - **Breaking** number-input: a range or step change made during IME composition
69
+ applies when the user confirms, reported as `change`; a morph that changes
70
+ neither holds nothing.
71
+ - **Breaking** currency-input: actions and events act only on the first `display`
72
+ target, and a display already focused when the controller connects starts
73
+ formatting at its next ordinary edit or composition end. Render the initial
74
+ hidden and accessible values on the server. `stimeo check` warns about extra
75
+ displays.
76
+ - **Breaking** tabs, calendar, date-range-picker, slider, range-slider,
77
+ color-picker, multi-select, and tags-input read the writing direction from their
78
+ layout targets instead of the root; carousel gains a `pickerList` and
79
+ navigation-menu a `list` target.
80
+ - **Breaking** dirty-form and optimistic accept only the terminal event matching
81
+ the latest start's source and token, and only `success: true` succeeds. For a
82
+ custom save, call `markClean` or dispatch the paired events with `success`.
83
+ - **Breaking** local-time and relative-time read `datetime` strictly: a clock
84
+ without a zone is UTC (relative-time read it as local time), and an incomplete
85
+ or invalid value keeps the current display.
86
+ - **Breaking** presence and typing-indicator templates use `{count}`, `{name}`,
87
+ and `{names}` instead of `%{…}`.
88
+ - **Breaking** modified `Home` / `End` / `PageUp` / `PageDown` go to the browser
89
+ in widgets without a dedicated binding; pointer-drag cancels a keyboard grab
90
+ when focus leaves the handle; time-picker accepts digits typed with `Shift`.
91
+ - **Breaking** progress, meter, pointer-drag, countdown, overflow-menu, listbox,
92
+ submit-once, clipboard, announcer, and currency-input change their ownership
93
+ markers (pointer-drag: `data-stimeo--pointer-drag-tabindex-loan`, listbox:
94
+ `owns-label`); re-render persisted HTML from authored markup. progress and meter
95
+ restore the authored `aria-valuetext` when the template is cleared or the
96
+ controller disconnects.
97
+ - **Breaking** menubar, navigation-menu, menu, and context-menu treat an entry
98
+ inside an `aria-disabled="true"` ancestor as disabled (it stays reachable with
99
+ the arrow keys), and with nested entries refuse an action when any entry holding
100
+ its target is disabled. A click is judged when it reaches the component, so a
101
+ handler inside the component that disables its entry during the click keeps the
102
+ open or close it started; a menubar top it leaves natively `disabled` then has
103
+ its menu closed again, without a `close` event.
104
+ - **Breaking** pointer-drag: `reset()` returns the inline `translate` to the
105
+ origin only when `follow` is on; otherwise it cancels the gesture in flight and
106
+ leaves the author's `translate` alone.
107
+ - toggle-group writes `aria-pressed`, time-picker `aria-valuenow`, and radio-group
108
+ its arrival and release `tabindex` only when the value changes.
109
+ - context-menu: the region contract drops `aria-haspopup`; give the region a role
110
+ and name that fit its content and advertise the key with
111
+ `aria-keyshortcuts="Shift+F10"`.
112
+ - `stimeo check` reports `missing-announcer` for the `stimeo-ui/cable`
113
+ controllers (live-counter, presence, typing-indicator), and the
114
+ `stimeo:install` generator writes commented-out pins and registrations for the
115
+ opt-in `stimeo-ui/cable` and `stimeo-ui/positioning` entry points.
116
+
117
+ ### Removed
118
+
119
+ - character-counter: the `announceTextValueChanged()` callback is gone from the
120
+ shipped types; pending announcements still read the current `announceText`.
121
+
122
+ ### Fixed
123
+
124
+ - IME composition no longer sticks after a page write, a move, or a Turbo morph
125
+ in auto-submit, character-counter, combobox, command-palette, currency-input,
126
+ editable, input-mask, multi-select, number-input, otp, tags-input, and toolbar.
127
+ A morph leaves a field mid-composition alone, and a focused draft survives
128
+ morphs and reconnection.
129
+ - switch, otp, currency-input, and input-mask keep their hidden form values right
130
+ across Turbo morphs and removal.
131
+ - State events and reconciliation stay right when a `change` listener disconnects
132
+ the controller or the page edits state during a repair.
133
+ - Components release what an earlier connection registered when connected twice,
134
+ and keep authored text, ARIA, and tab stops through restores.
135
+ - relative-time follows live `datetime` changes; template substitution leaves
136
+ inherited names such as `{constructor}` unchanged.
137
+ - menubar, navigation-menu, menu, and context-menu refuse a `hidden`, natively
138
+ `disabled`, or `aria-disabled` entry on every route — an explicit element, any
139
+ event, or hover — as a click on it already was; `context-menu#activate` takes
140
+ the optional event.
141
+ - The MCP server accepts JSON-RPC batches in sessions that negotiated protocol
142
+ version 2025-03-26; other versions, and batches before `initialize`, are still
143
+ rejected.
11
144
 
12
145
  ## [0.17.0] - 2026-10-04
13
146
 
@@ -1307,7 +1440,8 @@ Initial public alpha: 101 behavior-only, accessible Stimulus controllers driven
1307
1440
  by `data-*` attributes, shipping no CSS. Published to npm (with provenance) and
1308
1441
  RubyGems.
1309
1442
 
1310
- [Unreleased]: https://github.com/taiyaky/stimeo-ui/compare/v0.17.0...HEAD
1443
+ [0.18.1]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.18.1
1444
+ [0.18.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.18.0
1311
1445
  [0.17.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.17.0
1312
1446
  [0.16.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.16.0
1313
1447
  [0.15.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.15.0
package/README.md CHANGED
@@ -15,9 +15,9 @@
15
15
  owns the look entirely.
16
16
 
17
17
  - Lean by design: the **core** needs only `@hotwired/stimulus` at runtime (kept
18
- external in the build). The opt-in `stimeo-ui/positioning` module is the one
19
- exception — it uses `@floating-ui/dom` (an optional **peer dependency**; see the
20
- Peer dependencies note below).
18
+ external in the build). Two opt-in subpaths each add one optional **peer
19
+ dependency**: `stimeo-ui/positioning` (`@floating-ui/dom`) and `stimeo-ui/cable`
20
+ (`@rails/actioncable`); see the Peer dependencies note below.
21
21
  - Accessibility first: every controller follows the relevant WAI-ARIA APG pattern
22
22
  and the related WCAG 2.2 AA criteria.
23
23
  - Public controller identifiers use the `stimeo--` namespace (e.g.
@@ -36,8 +36,12 @@ bin/rails generate stimeo:install
36
36
  ```
37
37
 
38
38
  The generator vendors the prebuilt JS into `vendor/javascript/stimeo/`, pins
39
- `stimeo-ui` in `config/importmap.rb`, and registers all controllers with your
40
- Stimulus application. Then drive components from HTML alone:
39
+ `stimeo-ui` in `config/importmap.rb`, and registers the core controllers with your
40
+ Stimulus application (`app/javascript/controllers/stimeo.js`). It writes the opt-in
41
+ `stimeo-ui/positioning` and `stimeo-ui/cable` entry points into those two files
42
+ commented out; to enable one, uncomment its lines. The cable lines include the pin
43
+ for its `@rails/actioncable` peer; positioning also needs a pin for
44
+ `@floating-ui/dom`. Then drive components from HTML alone:
41
45
 
42
46
  ```erb
43
47
  <div data-controller="stimeo--dropdown">
@@ -58,9 +62,12 @@ import { Application } from "@hotwired/stimulus";
58
62
  import { registerStimeo } from "stimeo-ui";
59
63
 
60
64
  const application = Application.start();
61
- registerStimeo(application); // registers every stimeo--* controller
65
+ registerStimeo(application); // registers every controller of the core entry point
62
66
  ```
63
67
 
68
+ The opt-in `stimeo-ui/positioning` and `stimeo-ui/cable` entry points register
69
+ theirs with `registerPositioning` / `registerCable`.
70
+
64
71
  Need only a few controllers? Import them individually from
65
72
  `stimeo-ui/controllers/*` and register them under your own identifiers. Each
66
73
  file loads on its own, yet the coordination between components — which layer
@@ -73,7 +80,8 @@ and peer departure decisions; generated ARIA ids stay distinct across copies too
73
80
 
74
81
  - **Peer dependencies:** `@hotwired/stimulus` (always), `@floating-ui/dom` (only
75
82
  if you use the opt-in `stimeo-ui/positioning` module — tooltips, popovers, etc.
76
- work without it via the default flow layout).
83
+ work without it via the default flow layout), and `@rails/actioncable` (only if
84
+ you use the opt-in `stimeo-ui/cable` module).
77
85
  - **No CSS is shipped.** Style the components yourself; controllers only toggle
78
86
  ARIA state and `data-*` hooks.
79
87
 
@@ -118,8 +126,9 @@ keep the rules on everywhere else. For Biome:
118
126
  The `eslint-plugin-jsx-a11y` equivalents are
119
127
  `no-noninteractive-element-to-interactive-role`, `no-redundant-roles`,
120
128
  `prefer-tag-over-role`, `interactive-supports-focus`, and
121
- `no-noninteractive-tabindex`. These components' real accessibility is exercised
122
- with axe-core and real screen readers in this project's own test suite.
129
+ `no-noninteractive-tabindex`. This repository's tests check these components
130
+ with axe-core and with a virtual screen reader for speech order; neither stands
131
+ in for a real screen reader on your own pages.
123
132
 
124
133
  ## Composing components
125
134
 
@@ -279,7 +288,7 @@ or in `.mcp.json` (Claude Code) / `.cursor/mcp.json` (Cursor):
279
288
  It exposes four read-only tools — `stimeo_check` (validate a source string),
280
289
  `stimeo_catalog`, `stimeo_controller` (one controller's full contract,
281
290
  accessibility requirements included), and `stimeo_example` (verified example
282
- markup: the official catalog demo under [`examples/`](examples/), bundled at
291
+ markup: the controller's demo under [`examples/`](examples/), bundled at
283
292
  build time and guaranteed to pass the checker) — plus MCP resources
284
293
  (`stimeo://manifest`, `stimeo://examples/<id>`) for preloading context without
285
294
  a tool round-trip. The server reads only its bundled manifest and example
@@ -145,7 +145,7 @@ declare class LiveCounterController extends Controller<HTMLElement> {
145
145
  * data-stimeo--presence-channel-value="PresenceChannel"
146
146
  * data-stimeo--presence-params-value='{"room":"doc_7"}'
147
147
  * data-stimeo--presence-id-value="17" data-stimeo--presence-name-value="Alice">
148
- * <span data-stimeo--presence-target="count" data-other="%{count} viewing"></span>
148
+ * <span data-stimeo--presence-target="count" data-other="{count} viewing"></span>
149
149
  * <ul data-stimeo--presence-target="list"></ul>
150
150
  * <template data-stimeo--presence-target="template">
151
151
  * <li><span data-presence-name></span></li>
@@ -169,7 +169,7 @@ declare class LiveCounterController extends Controller<HTMLElement> {
169
169
  * `template` pair, one clone per peer is appended (elements marked
170
170
  * `data-presence-name` receive the name; the clone root is tagged
171
171
  * `data-presence-id`), and the `count` target renders through localizable
172
- * `data-zero` / `data-one` / `data-other` templates (`%{count}`). Richer
172
+ * `data-zero` / `data-one` / `data-other` templates (`{count}`). Richer
173
173
  * per-user rendering (avatars, links) belongs to the consumer via the `join` /
174
174
  * `leave` / `change` events or a server-rendered Turbo Stream. A `template` holds
175
175
  * one root element: the clone appended for a peer, and the node removed when the
@@ -189,9 +189,10 @@ declare class LiveCounterController extends Controller<HTMLElement> {
189
189
  * whatever a Turbo cache snapshot preserved (hooks + rendered clones), renders
190
190
  * the known-empty count (the `data-present*` hooks stay absent until the first
191
191
  * beacon), and the stream re-populates; a `count` or `list` target swapped in by a
192
- * Stream or a morph arrives empty and is drawn from the roster as it connects; the subscription, heartbeat interval,
193
- * per-peer expiry timers, and the `pagehide` listener are all released on
194
- * `disconnect()` (Turbo navigation included).
192
+ * Stream or a morph arrives empty and is drawn from the roster as it connects;
193
+ * the subscription, heartbeat interval, per-peer expiry timers, and the
194
+ * `pagehide` listener are all released on `disconnect()` (Turbo navigation
195
+ * included).
195
196
  *
196
197
  * The live resources follow the declaration; Values changed together move them
197
198
  * once, after the batch, and nothing moves before `connect()` or after
@@ -316,7 +317,7 @@ declare class PresenceController extends Controller<HTMLElement> {
316
317
  * data-stimeo--typing-indicator-announce-one-text-value="{name} is typing">
317
318
  * <textarea data-stimeo--typing-indicator-target="input"></textarea>
318
319
  * <p data-stimeo--typing-indicator-target="status"
319
- * data-one="%{name} is typing…" data-many="%{names} are typing…"></p>
320
+ * data-one="{name} is typing…" data-many="{names} are typing…"></p>
320
321
  * </div>
321
322
  *
322
323
  * Server contract (a trivial rebroadcast channel):
@@ -337,8 +338,8 @@ declare class PresenceController extends Controller<HTMLElement> {
337
338
  * @remarks
338
339
  * Behavior only — the indicator's look is the consumer's CSS, keyed off the
339
340
  * `data-typing` hook; the copy is localizable through the `data-one` /
340
- * `data-many` templates (`%{name}` / `%{names}` / `%{count}`, terse English
341
- * fallback — the same channel design as `stimeo--sortable`). The own echo is
341
+ * `data-many` templates (`{name}` / `{names}` / `{count}`), with terse English
342
+ * as the fallback. The own echo is
342
343
  * dropped by comparing the broadcast `name` against `name` (a same-name guard,
343
344
  * not authentication — identity belongs to the server). Sending tracks the full
344
345
  * subscription lifecycle (via the shared confirmation-aware subscription):
@@ -563,4 +564,5 @@ declare const cableControllers: {
563
564
  */
564
565
  declare function registerCable(application: Application): void;
565
566
 
566
- export { type CableConsumer, type CableSubscription, type CableSubscriptionMixin, type ConfirmedCableSubscription, LiveCounterController, PresenceController, TypingIndicatorController, cableControllers, createConfirmedSubscription, getCableConsumer, registerCable, setCableConsumer };
567
+ export { LiveCounterController, PresenceController, TypingIndicatorController, cableControllers, createConfirmedSubscription, getCableConsumer, registerCable, setCableConsumer };
568
+ export type { CableConsumer, CableSubscription, CableSubscriptionMixin, ConfirmedCableSubscription };
@@ -1,6 +1,13 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
  import { createConsumer } from '@rails/actioncable';
3
3
 
4
+ function fillTemplate(template, values) {
5
+ return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
6
+ const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
7
+ return replacement === void 0 ? match : String(replacement);
8
+ });
9
+ }
10
+
4
11
  function announce(message, options = {}) {
5
12
  const text = message.trim();
6
13
  if (text.length === 0) return;
@@ -10,12 +17,6 @@ function announce(message, options = {}) {
10
17
  })
11
18
  );
12
19
  }
13
- function fillTemplate(template, values) {
14
- return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
15
- const replacement = values[name];
16
- return replacement === void 0 ? match : String(replacement);
17
- });
18
- }
19
20
 
20
21
  var TOKEN_BODY = /\d[\d.,'’’    _]*\d|\d/;
21
22
  var SIGN_OPENER = /[\s([]/;
@@ -97,6 +98,9 @@ var MicrotaskCoalescer = class {
97
98
  this.#queued = false;
98
99
  this.#generation += 1;
99
100
  }
101
+ get pending() {
102
+ return this.#queued;
103
+ }
100
104
  schedule() {
101
105
  if (!this.#active || this.#queued) return;
102
106
  this.#queued = true;
@@ -138,6 +142,9 @@ var MorphRenderWatcher = class {
138
142
  schedule() {
139
143
  this.#pass.schedule();
140
144
  }
145
+ get pending() {
146
+ return this.#pass.pending;
147
+ }
141
148
  disconnect() {
142
149
  this.#listeners.dispose();
143
150
  this.#pass.cancel();
@@ -601,6 +608,7 @@ var PresenceController = class _PresenceController extends Controller {
601
608
  #pendingBeacon = null;
602
609
  #announceId = null;
603
610
  #connected = false;
611
+ #cycleListeners = new ListenerSet();
604
612
  #speakerKey = "";
605
613
  #speakerId = "";
606
614
  channelValueChanged() {
@@ -666,10 +674,10 @@ var PresenceController = class _PresenceController extends Controller {
666
674
  received: (data) => this.#onReceived(data)
667
675
  });
668
676
  this.#armHeartbeat();
669
- window.addEventListener("pagehide", this.#onPageHide);
677
+ this.#cycleListeners.add(window, "pagehide", this.#onPageHide);
670
678
  }
671
679
  #close() {
672
- window.removeEventListener("pagehide", this.#onPageHide);
680
+ this.#cycleListeners.dispose();
673
681
  this.#sendLeaveNotice(this.#releaseSpeaker());
674
682
  this.#subscription?.unsubscribe();
675
683
  this.#subscription = null;
@@ -797,7 +805,7 @@ ${this.#speakerId}`;
797
805
  #countMessage(count) {
798
806
  const templates = this.countTarget.dataset;
799
807
  const template = (count === 0 ? templates.zero : count === 1 ? templates.one : templates.other) ?? templates.other;
800
- return template ? template.replace("%{count}", String(count)) : String(count);
808
+ return template ? fillTemplate(template, { count }) : String(count);
801
809
  }
802
810
  #drawList() {
803
811
  if (!this.hasListTarget) return;
@@ -877,7 +885,6 @@ var TRANSIENT3 = new TransientHooks({
877
885
  });
878
886
  var DEFAULT_TIMEOUT = 3e3;
879
887
  var DEFAULT_THROTTLE = 2e3;
880
- var fillTokens = (template, values) => template.replace(/%\{(name|names|count)\}/g, (match, token) => values[token] ?? match);
881
888
  var TypingIndicatorController = class _TypingIndicatorController extends Controller {
882
889
  #numbers = new NumberValueReader();
883
890
  static targets = ["input", "status"];
@@ -905,6 +912,7 @@ var TypingIndicatorController = class _TypingIndicatorController extends Control
905
912
  #lastSentAt = 0;
906
913
  #announceId = null;
907
914
  #connected = false;
915
+ #listeners = new ListenerSet();
908
916
  channelValueChanged() {
909
917
  this.#follow.schedule();
910
918
  }
@@ -927,14 +935,14 @@ var TypingIndicatorController = class _TypingIndicatorController extends Control
927
935
  connect() {
928
936
  this.#connected = true;
929
937
  this.#reset();
930
- this.element.addEventListener("input", this.#onInput);
938
+ this.#listeners.add(this.element, "input", this.#onInput);
931
939
  this.#follow.activate();
932
940
  this.#open();
933
941
  }
934
942
  disconnect() {
935
943
  this.#connected = false;
936
944
  this.#follow.cancel();
937
- this.element.removeEventListener("input", this.#onInput);
945
+ this.#listeners.dispose();
938
946
  this.#close();
939
947
  this.#reset();
940
948
  }
@@ -1034,10 +1042,10 @@ var TypingIndicatorController = class _TypingIndicatorController extends Control
1034
1042
  if (names.length === 1) {
1035
1043
  const template2 = this.statusTarget.dataset.one;
1036
1044
  const name = names[0] ?? "";
1037
- return template2 ? fillTokens(template2, { name }) : `${name} is typing\u2026`;
1045
+ return template2 ? fillTemplate(template2, { name }) : `${name} is typing\u2026`;
1038
1046
  }
1039
1047
  const template = this.statusTarget.dataset.many;
1040
- return template ? fillTokens(template, { names: joined, count: String(names.length) }) : `${joined} are typing\u2026`;
1048
+ return template ? fillTemplate(template, { names: joined, count: names.length }) : `${joined} are typing\u2026`;
1041
1049
  }
1042
1050
  get #safeTimeout() {
1043
1051
  return this.#numbers.read(