@konce-pt/react 0.11.2 → 0.14.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 (382) hide show
  1. package/README.md +128 -64
  2. package/dist/accordion/accordion.d.ts +14 -6
  3. package/dist/accordion/accordion.js +25 -17
  4. package/dist/accordion/llms.txt +22 -10
  5. package/dist/alert/alert.d.ts +2 -2
  6. package/dist/alert/alert.js +3 -2
  7. package/dist/alert/llms.txt +10 -9
  8. package/dist/app-shell/app-shell.d.ts +14 -14
  9. package/dist/app-shell/app-shell.js +22 -15
  10. package/dist/app-shell/llms.txt +16 -15
  11. package/dist/auth/auth-types.d.ts +6 -6
  12. package/dist/auth/auth.d.ts +2 -2
  13. package/dist/auth/auth.js +24 -15
  14. package/dist/auth/llms.txt +12 -11
  15. package/dist/autocomplete/autocomplete.d.ts +19 -6
  16. package/dist/autocomplete/autocomplete.js +32 -11
  17. package/dist/autocomplete/llms.txt +19 -5
  18. package/dist/avatar/avatar.d.ts +6 -6
  19. package/dist/avatar/avatar.js +6 -5
  20. package/dist/avatar/llms.txt +10 -10
  21. package/dist/avatar-group/avatar-group.d.ts +4 -4
  22. package/dist/avatar-group/avatar-group.js +4 -3
  23. package/dist/avatar-group/llms.txt +8 -8
  24. package/dist/backdrop/backdrop.d.ts +7 -26
  25. package/dist/backdrop/backdrop.js +32 -27
  26. package/dist/backdrop/llms.txt +27 -27
  27. package/dist/badge/badge.d.ts +3 -3
  28. package/dist/badge/badge.js +1 -1
  29. package/dist/badge/llms.txt +11 -11
  30. package/dist/bottom-sheet/bottom-sheet.d.ts +8 -3
  31. package/dist/bottom-sheet/bottom-sheet.js +5 -4
  32. package/dist/bottom-sheet/llms.txt +11 -8
  33. package/dist/breadcrumb/breadcrumb.d.ts +8 -2
  34. package/dist/breadcrumb/breadcrumb.js +11 -3
  35. package/dist/breadcrumb/llms.txt +10 -2
  36. package/dist/button/button.d.ts +3 -3
  37. package/dist/button/button.js +10 -9
  38. package/dist/button/llms.txt +27 -19
  39. package/dist/button-group/button-group.d.ts +18 -5
  40. package/dist/button-group/button-group.js +54 -6
  41. package/dist/button-group/llms.txt +27 -18
  42. package/dist/card/card.d.ts +8 -8
  43. package/dist/card/card.js +10 -9
  44. package/dist/card/llms.txt +20 -20
  45. package/dist/carousel/carousel.d.ts +6 -6
  46. package/dist/carousel/carousel.js +8 -5
  47. package/dist/carousel/llms.txt +10 -10
  48. package/dist/charts/chart-legend.d.ts +3 -3
  49. package/dist/charts/chart-legend.js +2 -1
  50. package/dist/charts/chart-tooltip.d.ts +3 -3
  51. package/dist/charts/chart-tooltip.js +1 -1
  52. package/dist/charts/chart.d.ts +4 -3
  53. package/dist/charts/chart.js +18 -10
  54. package/dist/charts/llms.txt +10 -7
  55. package/dist/charts-entry/public-api.js +2 -2
  56. package/dist/chat/chat.d.ts +17 -10
  57. package/dist/chat/chat.js +77 -23
  58. package/dist/chat/llms.txt +39 -15
  59. package/dist/chat-composer/chat-composer.d.ts +20 -7
  60. package/dist/chat-composer/chat-composer.js +29 -7
  61. package/dist/chat-composer/llms.txt +20 -11
  62. package/dist/chat-dock/chat-dock.d.ts +11 -4
  63. package/dist/chat-dock/chat-dock.js +10 -6
  64. package/dist/chat-dock/llms.txt +15 -14
  65. package/dist/chat-entry/public-api.js +1 -1
  66. package/dist/chat-message/chat-message.d.ts +12 -5
  67. package/dist/chat-message/chat-message.js +19 -8
  68. package/dist/chat-message/llms.txt +16 -15
  69. package/dist/chat-typing/chat-typing.d.ts +2 -2
  70. package/dist/chat-typing/chat-typing.js +4 -3
  71. package/dist/chat-typing/llms.txt +6 -6
  72. package/dist/checkbox/checkbox.d.ts +13 -6
  73. package/dist/checkbox/checkbox.js +31 -5
  74. package/dist/checkbox/llms.txt +26 -11
  75. package/dist/chip/chip.d.ts +4 -4
  76. package/dist/chip/chip.js +3 -2
  77. package/dist/chip/llms.txt +11 -11
  78. package/dist/chips-input/chips-input.d.ts +6 -4
  79. package/dist/chips-input/chips-input.js +47 -12
  80. package/dist/chips-input/llms.txt +18 -8
  81. package/dist/clock/clock.d.ts +11 -6
  82. package/dist/clock/clock.js +38 -8
  83. package/dist/clock/llms.txt +12 -8
  84. package/dist/color-picker/color-picker.d.ts +9 -5
  85. package/dist/color-picker/color-picker.js +37 -18
  86. package/dist/color-picker/llms.txt +8 -5
  87. package/dist/confirm/confirm-container.d.ts +3 -3
  88. package/dist/confirm/confirm-container.js +6 -5
  89. package/dist/confirm/confirm-store.d.ts +4 -4
  90. package/dist/confirm/confirm-store.js +11 -5
  91. package/dist/confirm/llms.txt +8 -7
  92. package/dist/context-menu/context-menu.d.ts +1 -1
  93. package/dist/context-menu/context-menu.js +37 -14
  94. package/dist/context-menu/llms.txt +10 -7
  95. package/dist/data-table/data-table-types.d.ts +5 -5
  96. package/dist/data-table/data-table.d.ts +37 -5
  97. package/dist/data-table/data-table.js +426 -119
  98. package/dist/data-table/export-csv.d.ts +3 -3
  99. package/dist/data-table/export-csv.js +8 -10
  100. package/dist/data-table/llms.txt +127 -79
  101. package/dist/data-view/data-view.d.ts +13 -5
  102. package/dist/data-view/data-view.js +68 -6
  103. package/dist/data-view/llms.txt +29 -10
  104. package/dist/date-range/date-range.d.ts +20 -8
  105. package/dist/date-range/date-range.js +86 -63
  106. package/dist/date-range/llms.txt +32 -18
  107. package/dist/datepicker/datepicker.d.ts +22 -8
  108. package/dist/datepicker/datepicker.js +103 -71
  109. package/dist/datepicker/llms.txt +36 -22
  110. package/dist/dialog/dialog.d.ts +4 -4
  111. package/dist/dialog/dialog.js +32 -11
  112. package/dist/dialog/llms.txt +13 -10
  113. package/dist/divider/divider.d.ts +7 -1
  114. package/dist/divider/divider.js +3 -2
  115. package/dist/divider/llms.txt +4 -3
  116. package/dist/drawer/drawer.d.ts +9 -4
  117. package/dist/drawer/drawer.js +5 -4
  118. package/dist/drawer/llms.txt +13 -10
  119. package/dist/empty/empty.d.ts +2 -2
  120. package/dist/empty/empty.js +1 -1
  121. package/dist/empty/llms.txt +9 -9
  122. package/dist/fab/fab.d.ts +1 -1
  123. package/dist/fab/fab.js +1 -1
  124. package/dist/fab/llms.txt +10 -6
  125. package/dist/fieldset/fieldset.d.ts +18 -4
  126. package/dist/fieldset/fieldset.js +12 -6
  127. package/dist/fieldset/llms.txt +26 -9
  128. package/dist/file-upload/file-upload.d.ts +8 -2
  129. package/dist/file-upload/file-upload.js +22 -11
  130. package/dist/file-upload/llms.txt +12 -9
  131. package/dist/form-field/form-field-context.d.ts +4 -4
  132. package/dist/form-field/form-field.d.ts +12 -4
  133. package/dist/form-field/form-field.js +27 -6
  134. package/dist/form-field/llms.txt +25 -11
  135. package/dist/galleria/galleria.d.ts +3 -3
  136. package/dist/galleria/galleria.js +9 -6
  137. package/dist/galleria/llms.txt +7 -7
  138. package/dist/grid/col.d.ts +4 -4
  139. package/dist/grid/col.js +2 -2
  140. package/dist/grid/flex.d.ts +3 -3
  141. package/dist/grid/flex.js +1 -1
  142. package/dist/grid/grid.d.ts +7 -7
  143. package/dist/grid/grid.js +3 -3
  144. package/dist/grid/layout.d.ts +2 -2
  145. package/dist/grid/layout.js +2 -2
  146. package/dist/grid/llms.txt +15 -15
  147. package/dist/grid-entry/public-api.js +1 -1
  148. package/dist/i18n/i18n-context.d.ts +6 -6
  149. package/dist/i18n/i18n-context.js +27 -5
  150. package/dist/icon/icon-registry.d.ts +3 -3
  151. package/dist/icon/icon-registry.js +3 -3
  152. package/dist/icon/icon.d.ts +4 -4
  153. package/dist/icon/icon.js +10 -7
  154. package/dist/icon/llms.txt +13 -13
  155. package/dist/icon-button/icon-button.d.ts +2 -2
  156. package/dist/icon-button/icon-button.js +1 -1
  157. package/dist/icon-button/llms.txt +9 -5
  158. package/dist/icons-entry/public-api.d.ts +2 -2
  159. package/dist/icons-entry/public-api.js +3 -3
  160. package/dist/image/image.js +5 -5
  161. package/dist/image/llms.txt +13 -13
  162. package/dist/input/input.d.ts +18 -5
  163. package/dist/input/input.js +22 -6
  164. package/dist/input/llms.txt +25 -11
  165. package/dist/input-mask/input-mask.d.ts +7 -2
  166. package/dist/input-mask/input-mask.js +17 -43
  167. package/dist/input-mask/llms.txt +8 -5
  168. package/dist/input-number/input-number.d.ts +11 -9
  169. package/dist/input-number/input-number.js +25 -21
  170. package/dist/input-number/llms.txt +17 -14
  171. package/dist/input-otp/input-otp.d.ts +18 -5
  172. package/dist/input-otp/input-otp.js +48 -24
  173. package/dist/input-otp/llms.txt +18 -7
  174. package/dist/internal/calendar-focus.d.ts +1 -1
  175. package/dist/internal/calendar-focus.js +3 -3
  176. package/dist/internal/cn.d.ts +1 -1
  177. package/dist/internal/mask.d.ts +14 -0
  178. package/dist/internal/mask.js +62 -0
  179. package/dist/internal/number-group.d.ts +22 -0
  180. package/dist/internal/number-group.js +53 -0
  181. package/dist/internal/option-key.d.ts +6 -0
  182. package/dist/internal/option-key.js +8 -0
  183. package/dist/internal/option-nav.d.ts +4 -4
  184. package/dist/internal/option-nav.js +5 -5
  185. package/dist/internal/overlay-container.js +1 -1
  186. package/dist/internal/overlay.d.ts +3 -3
  187. package/dist/internal/overlay.js +40 -10
  188. package/dist/internal/password-strength.d.ts +6 -0
  189. package/dist/internal/password-strength.js +74 -0
  190. package/dist/internal/roving.d.ts +2 -2
  191. package/dist/internal/roving.js +3 -3
  192. package/dist/internal/scroll-lock.d.ts +6 -0
  193. package/dist/internal/scroll-lock.js +35 -0
  194. package/dist/internal/slot-children.d.ts +13 -0
  195. package/dist/internal/slot-children.js +31 -0
  196. package/dist/internal/slot.d.ts +3 -3
  197. package/dist/internal/slot.js +2 -2
  198. package/dist/internal/step.d.ts +12 -0
  199. package/dist/internal/step.js +31 -0
  200. package/dist/internal/tween.d.ts +5 -5
  201. package/dist/internal/tween.js +7 -8
  202. package/dist/internal/types.d.ts +2 -2
  203. package/dist/internal/use-breakpoint.d.ts +2 -2
  204. package/dist/internal/use-breakpoint.js +4 -4
  205. package/dist/internal/use-controllable-state.d.ts +5 -0
  206. package/dist/internal/use-controllable-state.js +25 -4
  207. package/dist/internal/use-id.d.ts +2 -2
  208. package/dist/internal/use-id.js +2 -2
  209. package/dist/internal/use-modal-focus.d.ts +2 -1
  210. package/dist/internal/use-modal-focus.js +14 -3
  211. package/dist/knob/knob.d.ts +7 -4
  212. package/dist/knob/knob.js +27 -22
  213. package/dist/knob/llms.txt +18 -11
  214. package/dist/listbox/listbox.d.ts +8 -6
  215. package/dist/listbox/listbox.js +37 -17
  216. package/dist/listbox/llms.txt +12 -9
  217. package/dist/loader/llms.txt +56 -0
  218. package/dist/loader/loader.d.ts +49 -0
  219. package/dist/loader/loader.js +68 -0
  220. package/dist/map/llms.txt +30 -23
  221. package/dist/map/map-create-dialog.d.ts +4 -4
  222. package/dist/map/map-create-dialog.js +36 -28
  223. package/dist/map/map-hours-field.d.ts +4 -4
  224. package/dist/map/map-hours-field.js +8 -7
  225. package/dist/map/map-image-field.d.ts +2 -2
  226. package/dist/map/map-image-field.js +6 -5
  227. package/dist/map/map-legend.d.ts +4 -4
  228. package/dist/map/map-legend.js +3 -2
  229. package/dist/map/map-poi-card.d.ts +3 -3
  230. package/dist/map/map-poi-card.js +10 -9
  231. package/dist/map/map-search.d.ts +17 -6
  232. package/dist/map/map-search.js +70 -43
  233. package/dist/map/map.d.ts +25 -13
  234. package/dist/map/map.js +86 -57
  235. package/dist/map/ports.d.ts +9 -9
  236. package/dist/map/ports.js +6 -6
  237. package/dist/map-entry/public-api.js +2 -2
  238. package/dist/megamenu/llms.txt +18 -7
  239. package/dist/megamenu/megamenu.d.ts +12 -6
  240. package/dist/megamenu/megamenu.js +23 -14
  241. package/dist/mention/llms.txt +15 -15
  242. package/dist/mention/mention.d.ts +5 -5
  243. package/dist/mention/mention.js +2 -2
  244. package/dist/mention/mentions.d.ts +1 -1
  245. package/dist/mention/mentions.js +1 -1
  246. package/dist/menu/llms.txt +12 -12
  247. package/dist/menu/menu-item.d.ts +4 -4
  248. package/dist/menu/menu-keys.d.ts +2 -2
  249. package/dist/menu/menu-keys.js +5 -5
  250. package/dist/menu/menu-rows.js +1 -0
  251. package/dist/menu/menu.d.ts +2 -2
  252. package/dist/menu/menu.js +15 -8
  253. package/dist/menubar/llms.txt +19 -8
  254. package/dist/menubar/menubar.d.ts +11 -5
  255. package/dist/menubar/menubar.js +23 -11
  256. package/dist/meter-group/llms.txt +12 -12
  257. package/dist/meter-group/meter-group.d.ts +2 -2
  258. package/dist/meter-group/meter-group.js +2 -1
  259. package/dist/motion/motion-context.d.ts +22 -0
  260. package/dist/motion/motion-context.js +28 -0
  261. package/dist/order-list/llms.txt +24 -9
  262. package/dist/order-list/order-list.d.ts +12 -4
  263. package/dist/order-list/order-list.js +123 -25
  264. package/dist/paginator/llms.txt +8 -8
  265. package/dist/paginator/paginator.js +7 -4
  266. package/dist/panel/llms.txt +25 -13
  267. package/dist/panel/panel.d.ts +11 -3
  268. package/dist/panel/panel.js +12 -7
  269. package/dist/password/llms.txt +19 -7
  270. package/dist/password/password.d.ts +2 -2
  271. package/dist/password/password.js +27 -16
  272. package/dist/peek/llms.txt +11 -11
  273. package/dist/peek/peek.d.ts +6 -6
  274. package/dist/peek/peek.js +41 -19
  275. package/dist/pick-list/llms.txt +35 -12
  276. package/dist/pick-list/pick-list.d.ts +12 -4
  277. package/dist/pick-list/pick-list.js +128 -24
  278. package/dist/popover/llms.txt +13 -10
  279. package/dist/popover/popover.d.ts +6 -4
  280. package/dist/popover/popover.js +14 -7
  281. package/dist/progress/llms.txt +17 -17
  282. package/dist/progress/progress.d.ts +7 -7
  283. package/dist/progress/progress.js +7 -6
  284. package/dist/public-api.d.ts +16 -4
  285. package/dist/public-api.js +13 -7
  286. package/dist/radio-group/llms.txt +16 -6
  287. package/dist/radio-group/radio-group.d.ts +12 -3
  288. package/dist/radio-group/radio-group.js +23 -4
  289. package/dist/rating/llms.txt +20 -13
  290. package/dist/rating/rating-pointer.d.ts +16 -0
  291. package/dist/rating/rating-pointer.js +26 -0
  292. package/dist/rating/rating.d.ts +6 -6
  293. package/dist/rating/rating.js +77 -12
  294. package/dist/rich-text/lists.d.ts +2 -2
  295. package/dist/rich-text/lists.js +7 -7
  296. package/dist/rich-text/llms.txt +35 -12
  297. package/dist/rich-text/rich-text.d.ts +13 -14
  298. package/dist/rich-text/rich-text.js +311 -142
  299. package/dist/rich-text/sanitize.d.ts +11 -2
  300. package/dist/rich-text/sanitize.js +80 -32
  301. package/dist/rich-text/tables.d.ts +3 -3
  302. package/dist/rich-text/tables.js +3 -3
  303. package/dist/roadmap/llms.txt +23 -17
  304. package/dist/roadmap/roadmap.d.ts +6 -6
  305. package/dist/roadmap/roadmap.js +102 -25
  306. package/dist/roadmap-entry/public-api.js +2 -2
  307. package/dist/scroll-top/llms.txt +6 -6
  308. package/dist/scroll-top/scroll-top.d.ts +1 -1
  309. package/dist/scroll-top/scroll-top.js +2 -1
  310. package/dist/select/llms.txt +22 -12
  311. package/dist/select/select.d.ts +10 -8
  312. package/dist/select/select.js +87 -47
  313. package/dist/shared/types.d.ts +22 -1
  314. package/dist/shared/types.js +9 -1
  315. package/dist/sidenav/llms.txt +9 -9
  316. package/dist/sidenav/sidenav.d.ts +2 -3
  317. package/dist/sidenav/sidenav.js +5 -4
  318. package/dist/skeleton/llms.txt +3 -3
  319. package/dist/skeleton/skeleton.d.ts +1 -1
  320. package/dist/skeleton/skeleton.js +1 -1
  321. package/dist/slider/llms.txt +34 -12
  322. package/dist/slider/slider-drag.d.ts +26 -0
  323. package/dist/slider/slider-drag.js +79 -0
  324. package/dist/slider/slider.d.ts +8 -5
  325. package/dist/slider/slider.js +38 -8
  326. package/dist/speech/llms.txt +97 -0
  327. package/dist/speech/speech-button.d.ts +44 -0
  328. package/dist/speech/speech-button.js +86 -0
  329. package/dist/speech/speech-wave.d.ts +19 -0
  330. package/dist/speech/speech-wave.js +16 -0
  331. package/dist/speech/use-speech-recognition.d.ts +81 -0
  332. package/dist/speech/use-speech-recognition.js +179 -0
  333. package/dist/speed-dial/llms.txt +14 -8
  334. package/dist/speed-dial/speed-dial.d.ts +1 -1
  335. package/dist/speed-dial/speed-dial.js +3 -2
  336. package/dist/spinner/llms.txt +7 -7
  337. package/dist/spinner/spinner.d.ts +1 -1
  338. package/dist/spinner/spinner.js +2 -1
  339. package/dist/split-button/llms.txt +21 -15
  340. package/dist/split-button/split-button.d.ts +2 -2
  341. package/dist/split-button/split-button.js +13 -6
  342. package/dist/splitter/llms.txt +11 -11
  343. package/dist/splitter/splitter.d.ts +3 -3
  344. package/dist/splitter/splitter.js +25 -10
  345. package/dist/stepper/llms.txt +35 -13
  346. package/dist/stepper/stepper.d.ts +6 -6
  347. package/dist/stepper/stepper.js +60 -13
  348. package/dist/switch/llms.txt +15 -5
  349. package/dist/switch/switch.d.ts +10 -3
  350. package/dist/switch/switch.js +18 -4
  351. package/dist/switch-group/llms.txt +11 -8
  352. package/dist/switch-group/switch-group.d.ts +6 -4
  353. package/dist/switch-group/switch-group.js +9 -7
  354. package/dist/tabs/llms.txt +12 -9
  355. package/dist/tabs/tabs.d.ts +5 -5
  356. package/dist/tabs/tabs.js +36 -20
  357. package/dist/textarea/llms.txt +23 -12
  358. package/dist/textarea/textarea.d.ts +19 -6
  359. package/dist/textarea/textarea.js +22 -7
  360. package/dist/timeline/llms.txt +4 -4
  361. package/dist/timeline/timeline.d.ts +1 -1
  362. package/dist/timeline/timeline.js +1 -1
  363. package/dist/toast/llms.txt +50 -17
  364. package/dist/toast/toast-container.d.ts +9 -3
  365. package/dist/toast/toast-container.js +18 -9
  366. package/dist/toast/toast-store.d.ts +42 -2
  367. package/dist/toast/toast-store.js +36 -1
  368. package/dist/toolbar/llms.txt +7 -7
  369. package/dist/toolbar/toolbar.d.ts +3 -3
  370. package/dist/toolbar/toolbar.js +2 -2
  371. package/dist/tooltip/llms.txt +14 -12
  372. package/dist/tooltip/tooltip.d.ts +5 -4
  373. package/dist/tooltip/tooltip.js +100 -41
  374. package/dist/tree/llms.txt +25 -11
  375. package/dist/tree/tree-keys.d.ts +5 -5
  376. package/dist/tree/tree-keys.js +7 -7
  377. package/dist/tree/tree.d.ts +17 -5
  378. package/dist/tree/tree.js +62 -20
  379. package/dist/update/llms.txt +54 -0
  380. package/dist/update/update.d.ts +49 -0
  381. package/dist/update/update.js +82 -0
  382. package/package.json +18 -15
@@ -1,21 +1,25 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useRef, useState, } from 'react';
4
+ import { KPT_CHAT_TYPING_THROTTLE_MS } from '@konce-pt/chat';
3
5
  import { cn } from "../internal/cn.js";
4
6
  import { useControllableState } from "../internal/use-controllable-state.js";
5
7
  import { useKptMessages } from "../i18n/i18n-context.js";
6
8
  import { KptIcon } from "../icon/icon.js";
7
9
  import { KptIconButton } from "../icon-button/icon-button.js";
10
+ import { KptSpeechButton } from "../speech/speech-button.js";
11
+ import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
8
12
  /**
9
- * Pole pisania wiadomości — autorosnące, z załącznikami i paskiem cytatu.
13
+ * Pole pisania wiadomości - autorosnące, z załącznikami i paskiem cytatu.
10
14
  *
11
15
  * `Enter` wysyła, `Shift+Enter` łamie linię (`enterToSend={false}` odwraca układ).
12
- * Przy `offline` przycisk zmienia etykietę na kolejkowanie — wiadomość dalej da się napisać
16
+ * Przy `offline` przycisk zmienia etykietę na kolejkowanie - wiadomość dalej da się napisać
13
17
  * i oddać aplikacji, bo to ona decyduje, co zrobić z kolejką.
14
18
  *
15
19
  * @example
16
20
  * <KptChatComposer allowAttachments onSend={(payload) => send(payload)} />
17
21
  */
18
- export function KptChatComposer({ value, defaultValue = '', onValueChange, placeholder = '', disabled = false, offline = false, allowAttachments = false, maxRows = 6, enterToSend = true, replyTo, defaultReplyTo = null, onReplyToChange, onSend, onTyping, locale, dictionary, className, ...rest }) {
22
+ export function KptChatComposer({ value, defaultValue = '', onValueChange, placeholder = '', disabled = false, offline = false, allowAttachments = false, maxRows = 6, enterToSend = true, speech = false, speechLang, speechColor, replyTo, defaultReplyTo = null, onReplyToChange, onSend, onTyping, locale, dictionary, className, ...rest }) {
19
23
  const { t } = useKptMessages({ locale, dictionary });
20
24
  const [text, setText] = useControllableState({
21
25
  value,
@@ -30,7 +34,13 @@ export function KptChatComposer({ value, defaultValue = '', onValueChange, place
30
34
  const [files, setFiles] = useState([]);
31
35
  const fieldRef = useRef(null);
32
36
  const pickerRef = useRef(null);
33
- const canSend = !disabled && (text.trim().length > 0 || files.length > 0);
37
+ // Dyktowanie: tekst zatwierdzony wchodzi przez `onChange` pola - rośnie ono i zgłasza "piszę"
38
+ // jak przy klawiaturze. Bez `speech` kontroler nie tworzy sesji.
39
+ const mic = useKptSpeechRecognition({ enabled: speech, target: fieldRef, lang: speechLang, disabled });
40
+ /** Kiedy ostatnio zgłoszono "piszę" - dławienie `onTyping`. */
41
+ const lastTyping = useRef(0);
42
+ // W trakcie dyktowania słowa mogą być jeszcze tylko tymczasowe - wysyłka i tak je zatwierdzi.
43
+ const canSend = !disabled && (text.trim().length > 0 || files.length > 0 || mic.listening);
34
44
  const sendLabel = offline ? t('chat.queue') : t('chat.send');
35
45
  /**
36
46
  * Wysokość liczona od treści. Zerowanie przed odczytem `scrollHeight` jest konieczne:
@@ -48,7 +58,14 @@ export function KptChatComposer({ value, defaultValue = '', onValueChange, place
48
58
  const submit = () => {
49
59
  if (!canSend)
50
60
  return;
51
- onSend?.({ text: text.trim(), files, replyTo: quote });
61
+ // Tekst tymczasowy dyktowania widać w polu, ale nie ma go w stanie - `flush()` zatwierdza go
62
+ // synchronicznie przez zdarzenie `input`, więc DOM ma go od razu, a stan dopiero po renderze.
63
+ // Dlatego treść do wysyłki czytamy z pola: wysyła się dokładnie to, co widać.
64
+ mic.flush();
65
+ const current = (fieldRef.current?.value ?? text).trim();
66
+ if (!current && !files.length)
67
+ return;
68
+ onSend?.({ text: current, files, replyTo: quote });
52
69
  setText('');
53
70
  setFiles([]);
54
71
  setQuote(null);
@@ -76,6 +93,11 @@ export function KptChatComposer({ value, defaultValue = '', onValueChange, place
76
93
  } }), _jsx(KptIconButton, { size: "sm", disabled: disabled, "aria-label": t('chat.attach'), onClick: () => pickerRef.current?.click(), children: _jsx(KptIcon, { name: "paperclip" }) })] })) : null, _jsx("textarea", { ref: fieldRef, className: "kpt-chat-composer__field", rows: 1, style: { '--_max-rows': maxRows }, value: text, disabled: disabled, placeholder: placeholder || t('chat.placeholder'), "aria-label": placeholder || t('chat.placeholder'), onChange: (event) => {
77
94
  setText(event.currentTarget.value);
78
95
  grow(event.currentTarget);
79
- onTyping?.();
80
- }, onKeyDown: onKeyDown }), _jsx(KptIconButton, { variant: "filled", disabled: !canSend, "aria-label": sendLabel, onClick: submit, children: _jsx(KptIcon, { name: offline ? 'clock' : 'send' }) })] })] }));
96
+ // "Piszę" przy pierwszym znaku i potem najwyżej raz na KPT_CHAT_TYPING_THROTTLE_MS - nie przy każdym.
97
+ const now = Date.now();
98
+ if (now - lastTyping.current >= KPT_CHAT_TYPING_THROTTLE_MS) {
99
+ lastTyping.current = now;
100
+ onTyping?.();
101
+ }
102
+ }, onKeyDown: onKeyDown }), speech ? (_jsx(KptSpeechButton, { listeningColor: speechColor, speech: mic, size: "sm", unsupported: "hide", disabled: disabled, hostProps: { className: 'kpt-chat-composer__speech' } })) : null, _jsx(KptIconButton, { variant: "filled", disabled: !canSend, "aria-label": sendLabel, onClick: submit, children: _jsx(KptIcon, { name: offline ? 'clock' : 'send' }) })] })] }));
81
103
  }
@@ -1,12 +1,12 @@
1
1
  # KptChatComposer (@konce-pt/react/chat)
2
2
 
3
- The message field — auto-growing, with attachments and a quote strip.
3
+ The message field - auto-growing, with attachments and a quote strip.
4
4
  Import: `import { KptChatComposer } from '@konce-pt/react/chat';`
5
5
 
6
6
  ## DOM
7
7
  <div class="kpt-chat-composer-host">
8
- <div class="kpt-chat-composer__reply">…the quoted message…</div>
9
- <ul class="kpt-chat-composer__files">…picked files…</ul>
8
+ <div class="kpt-chat-composer__reply">...the quoted message...</div>
9
+ <ul class="kpt-chat-composer__files">...picked files...</ul>
10
10
  <div class="kpt-chat-composer">
11
11
  <input type="file" class="kpt-chat-composer__picker" hidden />
12
12
  <!-- KptIconButton: attach -->
@@ -16,26 +16,26 @@ Import: `import { KptChatComposer } from '@konce-pt/react/chat';`
16
16
  </div>
17
17
 
18
18
  ## Props
19
- - `value` / `defaultValue` / `onValueChange` — the text
20
- - `replyTo` / `defaultReplyTo` / `onReplyToChange` — `KptChatReplyRef | null`
19
+ - `value` / `defaultValue` / `onValueChange` - the text
20
+ - `replyTo` / `defaultReplyTo` / `onReplyToChange` - `KptChatReplyRef | null`
21
21
  - `placeholder`, `disabled`, `offline`, `allowAttachments`
22
22
  - `maxRows`: number (default 6), `enterToSend`: boolean (default true)
23
- - `onSend(payload)` — `{ text, files, replyTo }`; the text is trimmed
24
- - `onTyping()` — every keystroke, for the "is typing" signal
23
+ - `onSend(payload)` - `{ text, files, replyTo }`; the text is trimmed
24
+ - `onTyping()` - every keystroke, for the "is typing" signal
25
25
  - `className`, `ref` and every other `<div>` prop go to the host
26
26
 
27
27
  ## Enter sends, Shift+Enter breaks the line
28
- `enterToSend={false}` swaps the two. A keystroke arriving mid-composition (an IME) never sends —
28
+ `enterToSend={false}` swaps the two. A keystroke arriving mid-composition (an IME) never sends -
29
29
  `isComposing` is checked, otherwise picking a candidate would fire the message.
30
30
 
31
31
  ## The height comes from the content
32
32
  The field grows with the text and then scrolls. The script sets `height` to `scrollHeight`, having
33
- zeroed it first — without that reset the field can only grow, because `scrollHeight` never drops
33
+ zeroed it first - without that reset the field can only grow, because `scrollHeight` never drops
34
34
  below the height already set. The ceiling is CSS: `--_max-rows`.
35
35
 
36
36
  ## Offline does not lock the field
37
37
  The button changes its label to queueing and the payload still reaches `onSend`. What to do with a
38
- queue is the application's decision — the component has no transport to hold it back with.
38
+ queue is the application's decision - the component has no transport to hold it back with.
39
39
 
40
40
  ## Examples
41
41
  <KptChatComposer allowAttachments onSend={send} />
@@ -51,6 +51,15 @@ The field `--kpt-color-surface-variant` + `--kpt-radius-lg`, the top edge `--kpt
51
51
  the quote strip `--kpt-color-primary`.
52
52
 
53
53
  ## Accessibility
54
- The textarea carries an `aria-label` equal to the placeholder — a field without a label is
54
+ The textarea carries an `aria-label` equal to the placeholder - a field without a label is
55
55
  invisible to a screen reader. Both icon buttons are labelled from the dictionary. It is not a
56
56
  form control: no `touched`/`invalid` state, and the value disappears on send.
57
+
58
+ ## Dictation
59
+ `speech`: boolean = false puts a microphone between the field and the send button (`KptSpeechButton`);
60
+ `speechLang` sets the language and `speechColor` the listening colour (theme colour by default). Words appear in the field live. Enter or "Send" during dictation
61
+ commits what is visible (`flush()`) and stops listening, so exactly the visible text is sent - the send
62
+ button is enabled while listening even if only interim words exist. Final text comes through the
63
+ field's `onChange`: the field grows and `onTyping` fires as with a keyboard.
64
+
65
+ <KptChatComposer speech onSend={({ text }) => askAgent(text)} />
@@ -1,5 +1,6 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
2
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ import { type KptEnterMotion } from '@konce-pt/motion';
3
4
  export type KptChatDockPosition = 'bottom-end' | 'bottom-start';
4
5
  export interface KptChatDockProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
5
6
  open?: boolean;
@@ -15,17 +16,23 @@ export interface KptChatDockProps extends Omit<ComponentPropsWithRef<'div'>, 'ch
15
16
  width?: string;
16
17
  /**
17
18
  * Odsunięcie od rogu ekranu (dowolna długość CSS), wspólne dla obu osi.
18
- * Domyślnie `--kpt-space-6`. Przydaje się, gdy w tym samym rogu stoi już inny widget —
19
+ * Domyślnie `--kpt-space-6`. Przydaje się, gdy w tym samym rogu stoi już inny widget -
19
20
  * `KptScrollTop` przykleja się dokładnie tam samo i bez tego oba się nakładają.
20
21
  */
21
22
  offset?: string;
22
23
  children?: ReactNode;
24
+ /**
25
+ * Ruch otwarcia: `spring` - okno wyrasta z przycisku (kapsuła rośnie w panel na sprężynie),
26
+ * `fade` - panel wjeżdża i się przenika, `none` - bez ruchu. Bez wartości bierze `enter`
27
+ * z `KptMotionProvider` (domyślnie `spring`); `enabled={false}` i systemowe "ogranicz ruch" wyłączają go zawsze.
28
+ */
29
+ enterMotion?: KptEnterMotion;
23
30
  }
24
31
  /**
25
- * Pływający czat w rogu ekranu — FAB z licznikiem nieprzeczytanych i panel z rozmową.
32
+ * Pływający czat w rogu ekranu - FAB z licznikiem nieprzeczytanych i panel z rozmową.
26
33
  *
27
34
  * Zawartość panelu jest dzieckiem, więc dock nie wie nic o wiadomościach: wkłada się do niego
28
- * `KptChat` i `KptChatComposer` (albo cokolwiek innego). Sam FAB to `KptFab` — dock go używa,
35
+ * `KptChat` i `KptChatComposer` (albo cokolwiek innego). Sam FAB to `KptFab` - dock go używa,
29
36
  * nie zastępuje.
30
37
  *
31
38
  * Poniżej `--kpt-breakpoint-sm` panel wchodzi na pełny ekran: 22,5 rem szerokości na telefonie
@@ -37,4 +44,4 @@ export interface KptChatDockProps extends Omit<ComponentPropsWithRef<'div'>, 'ch
37
44
  * <KptChatComposer onSend={send} />
38
45
  * </KptChatDock>
39
46
  */
40
- export declare function KptChatDock({ open, defaultOpen, onOpenChange, position, unread, header, icon, width, offset, locale, dictionary, className, children, ...rest }: Readonly<KptChatDockProps>): import("react").JSX.Element;
47
+ export declare function KptChatDock({ open, defaultOpen, onOpenChange, position, unread, header, icon, width, offset, enterMotion, locale, dictionary, className, children, ...rest }: Readonly<KptChatDockProps>): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useEffect, useRef, } from 'react';
3
4
  import { cn } from "../internal/cn.js";
@@ -7,11 +8,13 @@ import { KptBadge } from "../badge/badge.js";
7
8
  import { KptFab } from "../fab/fab.js";
8
9
  import { KptIcon } from "../icon/icon.js";
9
10
  import { KptIconButton } from "../icon-button/icon-button.js";
11
+ import { resolveEnterMotion } from '@konce-pt/motion';
12
+ import { useKptMotion } from "../motion/motion-context.js";
10
13
  /**
11
- * Pływający czat w rogu ekranu — FAB z licznikiem nieprzeczytanych i panel z rozmową.
14
+ * Pływający czat w rogu ekranu - FAB z licznikiem nieprzeczytanych i panel z rozmową.
12
15
  *
13
16
  * Zawartość panelu jest dzieckiem, więc dock nie wie nic o wiadomościach: wkłada się do niego
14
- * `KptChat` i `KptChatComposer` (albo cokolwiek innego). Sam FAB to `KptFab` — dock go używa,
17
+ * `KptChat` i `KptChatComposer` (albo cokolwiek innego). Sam FAB to `KptFab` - dock go używa,
15
18
  * nie zastępuje.
16
19
  *
17
20
  * Poniżej `--kpt-breakpoint-sm` panel wchodzi na pełny ekran: 22,5 rem szerokości na telefonie
@@ -23,7 +26,8 @@ import { KptIconButton } from "../icon-button/icon-button.js";
23
26
  * <KptChatComposer onSend={send} />
24
27
  * </KptChatDock>
25
28
  */
26
- export function KptChatDock({ open, defaultOpen = false, onOpenChange, position = 'bottom-end', unread = 0, header = '', icon = 'message-circle', width = '22.5rem', offset = '', locale, dictionary, className, children, ...rest }) {
29
+ export function KptChatDock({ open, defaultOpen = false, onOpenChange, position = 'bottom-end', unread = 0, header = '', icon = 'message-circle', width = '22.5rem', offset = '', enterMotion, locale, dictionary, className, children, ...rest }) {
30
+ const motion = resolveEnterMotion(enterMotion, useKptMotion());
27
31
  const { t } = useKptMessages({ locale, dictionary });
28
32
  const [isOpen, setOpen] = useControllableState({
29
33
  value: open,
@@ -33,7 +37,7 @@ export function KptChatDock({ open, defaultOpen = false, onOpenChange, position
33
37
  const panelRef = useRef(null);
34
38
  const triggerRef = useRef(null);
35
39
  /*
36
- * Fokus ląduje na polu pisania, jeśli w panelu jest — po to się czat otwiera.
40
+ * Fokus ląduje na polu pisania, jeśli w panelu jest - po to się czat otwiera.
37
41
  * Bez pola bierze pierwszy element interaktywny, a w ostateczności sam panel.
38
42
  */
39
43
  useEffect(() => {
@@ -54,7 +58,7 @@ export function KptChatDock({ open, defaultOpen = false, onOpenChange, position
54
58
  if (!isOpen)
55
59
  return;
56
60
  setOpen(false);
57
- // Fokus wraca na FAB — po zamknięciu panelu nie może zostać na elemencie,
61
+ // Fokus wraca na FAB - po zamknięciu panelu nie może zostać na elemencie,
58
62
  // który przed chwilą zniknął z drzewa dostępności.
59
63
  triggerRef.current?.querySelector('button')?.focus();
60
64
  };
@@ -63,5 +67,5 @@ export function KptChatDock({ open, defaultOpen = false, onOpenChange, position
63
67
  close();
64
68
  };
65
69
  const fab = (_jsx(KptFab, { "aria-label": isOpen ? t('chat.close') : t('chat.open'), onClick: () => setOpen(!isOpen), children: _jsx(KptIcon, { name: isOpen ? 'x' : icon }) }));
66
- return (_jsxs("div", { className: cn('kpt-chat-dock-host', className), onKeyDown: onKeyDown, ...rest, children: [_jsxs("div", { ref: panelRef, className: cn('kpt-chat-dock__panel', isOpen && 'is-open'), "data-position": position, role: "dialog", tabIndex: -1, style: { '--_width': width, ...(offset ? { '--_offset': offset } : {}) }, "aria-label": header || t('chat.open'), "aria-hidden": isOpen ? undefined : true, inert: !isOpen, children: [_jsxs("header", { className: "kpt-chat-dock__header", children: [_jsx("span", { className: "kpt-chat-dock__title", children: header }), _jsx(KptIconButton, { size: "sm", "aria-label": t('chat.close'), onClick: close, children: _jsx(KptIcon, { name: "x" }) })] }), _jsx("div", { className: "kpt-chat-dock__content", children: children })] }), _jsx("span", { ref: triggerRef, className: "kpt-chat-dock__trigger", "data-position": position, style: offset ? { '--_offset': offset } : undefined, children: unread > 0 ? (_jsx(KptBadge, { value: unread, variant: "danger", size: "sm", overlay: true, "aria-label": t('chat.unreadBadge', { count: unread }), children: fab })) : (fab) })] }));
70
+ return (_jsxs("div", { className: cn('kpt-chat-dock-host', className), onKeyDown: onKeyDown, ...rest, children: [_jsxs("div", { ref: panelRef, className: cn('kpt-chat-dock__panel', isOpen && 'is-open'), "data-position": position, "data-motion": motion, role: "dialog", tabIndex: -1, style: { '--_width': width, ...(offset ? { '--_offset': offset } : {}) }, "aria-label": header || t('chat.open'), "aria-hidden": isOpen ? undefined : true, inert: !isOpen, children: [_jsxs("header", { className: "kpt-chat-dock__header", children: [_jsx("span", { className: "kpt-chat-dock__title", children: header }), _jsx(KptIconButton, { size: "sm", "aria-label": t('chat.close'), onClick: close, children: _jsx(KptIcon, { name: "x" }) })] }), _jsx("div", { className: "kpt-chat-dock__content", children: children })] }), _jsx("span", { ref: triggerRef, className: "kpt-chat-dock__trigger", "data-position": position, style: offset ? { '--_offset': offset } : undefined, children: unread > 0 ? (_jsx(KptBadge, { value: unread, variant: "danger", size: "sm", overlay: true, "aria-label": t('chat.unreadBadge', { count: unread }), children: fab })) : (fab) })] }));
67
71
  }
@@ -1,6 +1,6 @@
1
1
  # KptChatDock (@konce-pt/react/chat)
2
2
 
3
- A floating chat in the corner of the screen — a FAB with an unread badge plus a panel holding the
3
+ A floating chat in the corner of the screen - a FAB with an unread badge plus a panel holding the
4
4
  conversation.
5
5
  Import: `import { KptChatDock } from '@konce-pt/react/chat';`
6
6
 
@@ -11,31 +11,32 @@ Import: `import { KptChatDock } from '@konce-pt/react/chat';`
11
11
  <span class="kpt-chat-dock__title">Support</span>
12
12
  <!-- KptIconButton: close -->
13
13
  </header>
14
- <div class="kpt-chat-dock__content">…children…</div>
14
+ <div class="kpt-chat-dock__content">...children...</div>
15
15
  </div>
16
16
  <span class="kpt-chat-dock__trigger" data-position="bottom-end">
17
- <!-- KptBadge overlay → KptFab -->
17
+ <!-- KptBadge overlay -> KptFab -->
18
18
  </span>
19
19
  </div>
20
20
 
21
21
  ## Props
22
- - `open` / `defaultOpen` / `onOpenChange` — works controlled and uncontrolled
22
+ - `open` / `defaultOpen` / `onOpenChange` - works controlled and uncontrolled
23
23
  - `position`: `'bottom-end' | 'bottom-start'` (logical, so RTL flips the corner on its own)
24
- - `unread`: number — 0 hides the badge; `header`: string
25
- - `icon`: string (default `message-circle`) — the closed FAB; open always shows a cross
26
- - `width`: string (default `22.5rem`) — above `--kpt-breakpoint-sm`
27
- - `offset`: string — distance from the corner, both axes (default `--kpt-space-6`)
28
- - `children` — the panel content
24
+ - `unread`: number - 0 hides the badge; `header`: string
25
+ - `icon`: string (default `message-circle`) - the closed FAB; open always shows a cross
26
+ - `width`: string (default `22.5rem`) - above `--kpt-breakpoint-sm`
27
+ - `offset`: string - distance from the corner, both axes (default `--kpt-space-6`)
28
+ - `enterMotion`: `'spring' | 'fade' | 'none'` - `spring` grows the window out of the button, `fade` slides it in (default from `KptMotionProvider`, else `spring`)
29
+ - `children` - the panel content
29
30
  - `className`, `ref` and every other `<div>` prop go to the host
30
31
 
31
32
  ## The FAB is KptFab, not a copy of it
32
- The dock composes the existing components — `KptFab`, `KptBadge`, `KptIconButton` — so a change to
33
+ The dock composes the existing components - `KptFab`, `KptBadge`, `KptIconButton` - so a change to
33
34
  the FAB reaches the chat as well. The content is `children`, which means the dock knows nothing
34
35
  about messages: put `KptChat` and `KptChatComposer` inside, or anything else.
35
36
 
36
37
  ## Sharing the corner with KptScrollTop
37
38
  `KptScrollTop` pins itself to the same spot with the same `--kpt-z-overlay`, and it would swallow the
38
- chat FAB whole — only the unread badge would stick out. The stylesheet resolves it by itself: when a
39
+ chat FAB whole - only the unread badge would stick out. The stylesheet resolves it by itself: when a
39
40
  `bottom-end` dock is on the page, the scroll-top button steps **aside**, not up, because the
40
41
  conversation panel stands above the FAB. It applies to `bottom-end` only and measures from the
41
42
  default offset, so an application that sets `offset` positions the scroll-top button itself.
@@ -45,7 +46,7 @@ default offset, so an application that sets `offset` positions the scroll-top bu
45
46
  Dropped in the middle of an application's layout the dock takes no space in the flow.
46
47
 
47
48
  ## Closed is not removed
48
- The panel stays in the DOM with `visibility: hidden` and a delayed transition — a transition
49
+ The panel stays in the DOM with `visibility: hidden` and a delayed transition - a transition
49
50
  cannot animate out of `display: none`. It also carries `inert` and `aria-hidden` while closed, so
50
51
  a hidden panel never catches the tab order.
51
52
 
@@ -55,7 +56,7 @@ a hidden panel never catches the tab order.
55
56
  <KptChatComposer onSend={send} />
56
57
  </KptChatDock>
57
58
 
58
- <KptChatDock open={open} onOpenChange={setOpen} position="bottom-start" width="26rem">…</KptChatDock>
59
+ <KptChatDock open={open} onOpenChange={setOpen} position="bottom-start" width="26rem">...</KptChatDock>
59
60
 
60
61
  ## i18n
61
62
  `open`, `close`, `unreadBadge` (`{count}`) from the `chat` namespace.
@@ -65,7 +66,7 @@ The panel `--kpt-radius-lg` + `--kpt-elevation-3`, the corner offsets `--kpt-spa
65
66
  the stack `--kpt-z-overlay`.
66
67
 
67
68
  ## Accessibility
68
- Opening moves focus to the composer inside the panel — that is what the chat is opened for; with no
69
+ Opening moves focus to the composer inside the panel - that is what the chat is opened for; with no
69
70
  field it takes the first interactive element. `Escape` closes and returns focus to the FAB, because
70
71
  focus must not stay on an element that just left the accessibility tree. Below 640px the panel goes
71
72
  full-screen.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * @konce-pt/react/chat — rozmowa. Osobny subpath, bo dock i logika listy są ciężkie
2
+ * @konce-pt/react/chat - rozmowa. Osobny subpath, bo dock i logika listy są ciężkie
3
3
  * względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
4
4
  */
5
5
  export { KptChat } from "../chat/chat.js";
@@ -1,7 +1,8 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import { type KptEnterMotion } from '@konce-pt/motion';
2
3
  import type { KptChatAttachment, KptChatAuthor, KptChatGroupPosition, KptChatMessage as KptChatMessageModel, KptChatReaction, KptChatReactionOption, KptChatReplyRef, KptChatStatus } from '@konce-pt/chat';
3
4
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
- /** Wygląd bąbla. `system` nie rysuje bąbla w ogóle — to komunikat, nie wypowiedź. */
5
+ /** Wygląd bąbla. `system` nie rysuje bąbla w ogóle - to komunikat, nie wypowiedź. */
5
6
  export type KptChatMessageVariant = 'neutral' | 'primary' | 'solid' | 'ghost' | 'system';
6
7
  export interface KptChatMessageProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
7
8
  /** Cała wiadomość naraz. Pojedyncze propsy poniżej mają nad nią priorytet. */
@@ -29,15 +30,21 @@ export interface KptChatMessageProps extends Omit<ComponentPropsWithRef<'div'>,
29
30
  reactionOptions?: readonly KptChatReactionOption[];
30
31
  onRetry?: () => void;
31
32
  onReply?: () => void;
32
- /** Wybrana reakcja (emoji). Biblioteka niczego nie dopisuje do modelu — robi to aplikacja. */
33
+ /** Wybrana reakcja (emoji). Biblioteka niczego nie dopisuje do modelu - robi to aplikacja. */
33
34
  onReact?: (emoji: string) => void;
34
- /** Kliknięcie w cytat — aplikacja może przewinąć listę do oryginału. */
35
+ /** Kliknięcie w cytat - aplikacja może przewinąć listę do oryginału. */
35
36
  onQuoted?: (quote: KptChatReplyRef) => void;
36
37
  /** Treść bąbla. Wygrywa z `text`, więc bąbel przyjmuje cokolwiek. */
37
38
  children?: ReactNode;
39
+ /**
40
+ * Ruch pojawiania się (reakcja, zmiana statusu): `spring` - wyrasta z miejsca, z którego przychodzi, na sprężynie;
41
+ * `fade` - tylko się przenika; `none` - bez ruchu. Bez wartości bierze `enter` z `KptMotionProvider`
42
+ * (domyślnie `spring`); `enabled={false}` i systemowe "ogranicz ruch" wyłączają go zawsze.
43
+ */
44
+ enterMotion?: KptEnterMotion;
38
45
  }
39
46
  /**
40
- * Pojedyncza wiadomość czatu — bąbel z treścią, godziną, statusem wysyłki i opcjonalnym
47
+ * Pojedyncza wiadomość czatu - bąbel z treścią, godziną, statusem wysyłki i opcjonalnym
41
48
  * awatarem, cytatem, załącznikami i reakcjami.
42
49
  *
43
50
  * Działa na dwa sposoby: dostaje gotowy obiekt przez `message` (tak używa go `KptChat`)
@@ -50,4 +57,4 @@ export interface KptChatMessageProps extends Omit<ComponentPropsWithRef<'div'>,
50
57
  * <KptChatMessage message={msg} align="end" position="last" />
51
58
  * <KptChatMessage align="end" status="failed" onRetry={resend}>Nie poszło</KptChatMessage>
52
59
  */
53
- export declare function KptChatMessage({ message, author, text, time, attachments, reactions, replyTo, status, align, variant, position, system, showAuthor, showAvatar, showMeta, tail, actions, reactionOptions, onRetry, onReply, onReact, onQuoted, children, locale, dictionary, className, ...rest }: Readonly<KptChatMessageProps>): import("react").JSX.Element;
60
+ export declare function KptChatMessage({ message, author, text, time, attachments, reactions, replyTo, status, align, variant, position, system, showAuthor, showAvatar, showMeta, tail, actions, reactionOptions, enterMotion, ref, onRetry, onReply, onReact, onQuoted, children, locale, dictionary, className, ...rest }: Readonly<KptChatMessageProps>): import("react").JSX.Element;
@@ -1,11 +1,15 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useEffect, useRef, useState } from 'react';
4
+ import { resolveEnterMotion } from '@konce-pt/motion';
3
5
  import { KPT_CHAT_REACTIONS, canRetry, toDate } from '@konce-pt/chat';
4
6
  import { cn } from "../internal/cn.js";
5
7
  import { useKptMessages } from "../i18n/i18n-context.js";
6
8
  import { KptAvatar } from "../avatar/avatar.js";
7
9
  import { KptIcon } from "../icon/icon.js";
8
10
  import { KptIconButton } from "../icon-button/icon-button.js";
11
+ import { useKptMotion } from "../motion/motion-context.js";
12
+ import { composeRefs } from "../internal/compose-refs.js";
9
13
  /** Ikona statusu wysyłki. `read` używa tej samej ikony co `delivered`, różni je kolor. */
10
14
  const STATUS_ICONS = {
11
15
  sending: 'clock',
@@ -15,7 +19,7 @@ const STATUS_ICONS = {
15
19
  failed: 'alert-triangle',
16
20
  };
17
21
  /**
18
- * Pojedyncza wiadomość czatu — bąbel z treścią, godziną, statusem wysyłki i opcjonalnym
22
+ * Pojedyncza wiadomość czatu - bąbel z treścią, godziną, statusem wysyłki i opcjonalnym
19
23
  * awatarem, cytatem, załącznikami i reakcjami.
20
24
  *
21
25
  * Działa na dwa sposoby: dostaje gotowy obiekt przez `message` (tak używa go `KptChat`)
@@ -28,14 +32,21 @@ const STATUS_ICONS = {
28
32
  * <KptChatMessage message={msg} align="end" position="last" />
29
33
  * <KptChatMessage align="end" status="failed" onRetry={resend}>Nie poszło</KptChatMessage>
30
34
  */
31
- export function KptChatMessage({ message = null, author = null, text = null, time = null, attachments = null, reactions = null, replyTo = null, status = null, align = 'start', variant = null, position = 'single', system = false, showAuthor = false, showAvatar = false, showMeta = true, tail = true, actions = false, reactionOptions = KPT_CHAT_REACTIONS, onRetry, onReply, onReact, onQuoted, children, locale, dictionary, className, ...rest }) {
35
+ export function KptChatMessage({ message = null, author = null, text = null, time = null, attachments = null, reactions = null, replyTo = null, status = null, align = 'start', variant = null, position = 'single', system = false, showAuthor = false, showAvatar = false, showMeta = true, tail = true, actions = false, reactionOptions = KPT_CHAT_REACTIONS, enterMotion, ref, onRetry, onReply, onReact, onQuoted, children, locale, dictionary, className, ...rest }) {
32
36
  const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
33
37
  const rowRef = useRef(null);
34
38
  /** Czy wybierak reakcji jest otwarty. */
35
39
  const [pickerOpen, setPickerOpen] = useState(false);
40
+ const motion = resolveEnterMotion(enterMotion, useKptMotion());
41
+ const hostRef = useRef(null);
42
+ // Reakcje i status wskakują dopiero po pierwszym renderze - wczytana historia się nie rusza.
43
+ // Klasa idzie wprost na element: osobny stan dałby drugi przebieg renderu tylko po to.
44
+ useEffect(() => {
45
+ hostRef.current?.classList.add('is-live');
46
+ }, []);
36
47
  // Kliknięcie obok zamyka wybierak. Sam `onBlur` nie wystarcza: w Safari kliknięcie w przycisk
37
48
  // nie przenosi na niego fokusu, więc wybierak zostawałby otwarty na zawsze. Faza przechwytywania
38
- // i `pointerdown` (nie `click`) — własne `onClick` opcji zdąży się wykonać przed zamknięciem.
49
+ // i `pointerdown` (nie `click`) - własne `onClick` opcji zdąży się wykonać przed zamknięciem.
39
50
  useEffect(() => {
40
51
  if (!pickerOpen)
41
52
  return;
@@ -75,7 +86,7 @@ export function KptChatMessage({ message = null, author = null, text = null, tim
75
86
  const statusIcon = resolvedStatus ? STATUS_ICONS[resolvedStatus] : null;
76
87
  const retryable = canRetry(resolvedStatus);
77
88
  /*
78
- * Etykieta dla czytnika ekranu. Bez niej z bąbla płynie sama treść — bez nadawcy,
89
+ * Etykieta dla czytnika ekranu. Bez niej z bąbla płynie sama treść - bez nadawcy,
79
90
  * godziny i informacji, że wiadomość się nie wysłała.
80
91
  */
81
92
  const ariaLabel = isSystem
@@ -84,9 +95,9 @@ export function KptChatMessage({ message = null, author = null, text = null, tim
84
95
  .filter(Boolean)
85
96
  .join(', ');
86
97
  if (isSystem) {
87
- return (_jsx("div", { className: cn('kpt-chat-message-host', className), role: "note", "aria-label": ariaLabel, ...rest, children: _jsx("p", { className: "kpt-chat-message__system", children: resolvedText }) }));
98
+ return (_jsx("div", { ref: composeRefs(hostRef, ref), className: cn('kpt-chat-message-host', className), role: "note", "aria-label": ariaLabel, "data-motion": motion, ...rest, children: _jsx("p", { className: "kpt-chat-message__system", children: resolvedText }) }));
88
99
  }
89
- return (_jsx("div", { className: cn('kpt-chat-message-host', className), role: "article", "aria-label": ariaLabel, ...rest, children: _jsxs("div", { ref: rowRef, className: "kpt-chat-message__row", "data-align": align, children: [align === 'start' ? (_jsx("span", { className: "kpt-chat-message__avatar", "aria-hidden": "true", children: showAvatar ? (_jsx(KptAvatar, { size: "sm", src: resolvedAuthor?.avatar ?? null, name: resolvedAuthor?.name ?? '' })) : null })) : null, _jsxs("div", { className: "kpt-chat-message__stack", children: [showAuthor && resolvedAuthor ? (_jsx("span", { className: "kpt-chat-message__author", children: resolvedAuthor.name })) : null, _jsxs("div", { className: "kpt-chat-message__bubble", "data-variant": resolvedVariant, "data-align": align, "data-position": position, "data-status": resolvedStatus ?? undefined, "data-tail": hasTail ? '' : undefined, children: [resolvedReplyTo ? (_jsxs("button", { type: "button", className: "kpt-chat-message__quote", onClick: () => onQuoted?.(resolvedReplyTo), children: [_jsx("span", { className: "kpt-chat-message__quote-author", children: resolvedReplyTo.author }), _jsx("span", { className: "kpt-chat-message__quote-text", children: resolvedReplyTo.excerpt })] })) : null, resolvedText ? _jsx("p", { className: "kpt-chat-message__text", children: resolvedText }) : null, children, resolvedAttachments.length ? (_jsx("ul", { className: "kpt-chat-message__files", children: resolvedAttachments.map((file) => (_jsxs("li", { className: "kpt-chat-message__file", children: [file.thumbnail ? (_jsx("img", { className: "kpt-chat-message__file-thumb", src: file.thumbnail, alt: file.name })) : (_jsx(KptIcon, { name: "paperclip" })), _jsx("a", { className: "kpt-chat-message__file-name", href: file.url, download: file.url ? file.name : undefined, children: file.name }), file.size ? (_jsx("span", { className: "kpt-chat-message__file-size", children: fileSize(file.size) })) : null] }, file.id))) })) : null, showMeta ? (_jsxs("span", { className: "kpt-chat-message__meta", children: [timeLabel ? (_jsx("time", { className: "kpt-chat-message__time", dateTime: sentAt?.toISOString(), children: timeLabel })) : null, statusIcon ? (_jsx(KptIcon, { className: "kpt-chat-message__status", name: statusIcon })) : null] })) : null, resolvedReactions.length ? (_jsx("ul", { className: "kpt-chat-message__reactions", "data-align": align, children: resolvedReactions.map((reaction) => (_jsxs("li", { className: "kpt-chat-message__reaction", "data-reacted": reaction.reacted ? '' : undefined, children: [_jsx("span", { "aria-hidden": "true", children: reaction.emoji }), _jsx("span", { className: "kpt-chat-message__reaction-count", children: reaction.count })] }, reaction.emoji))) })) : null] })] }), _jsx("span", { className: "kpt-chat-message__actions", "data-open": pickerOpen ? '' : undefined, children: retryable ? (_jsx(KptIconButton, { hostProps: { className: 'kpt-chat-message__retry' }, size: "sm", "aria-label": t('chat.retry'), onClick: () => onRetry?.(), children: _jsx(KptIcon, { name: "refresh" }) })) : actions ? (_jsxs(_Fragment, { children: [_jsx(KptIconButton, { size: "sm", "aria-label": t('chat.replyTo', { author: resolvedAuthor?.name ?? '' }), onClick: () => onReply?.(), children: _jsx(KptIcon, { name: "corner-up-left" }) }), _jsxs("span", { className: "kpt-chat-message__react", onKeyDown: (event) => {
100
+ return (_jsx("div", { ref: composeRefs(hostRef, ref), className: cn('kpt-chat-message-host', className), role: "article", "aria-label": ariaLabel, "data-motion": motion, ...rest, children: _jsxs("div", { ref: rowRef, className: "kpt-chat-message__row", "data-align": align, children: [align === 'start' ? (_jsx("span", { className: "kpt-chat-message__avatar", "aria-hidden": "true", children: showAvatar ? (_jsx(KptAvatar, { size: "sm", src: resolvedAuthor?.avatar ?? null, name: resolvedAuthor?.name ?? '' })) : null })) : null, _jsxs("div", { className: "kpt-chat-message__stack", children: [showAuthor && resolvedAuthor ? (_jsx("span", { className: "kpt-chat-message__author", children: resolvedAuthor.name })) : null, _jsxs("div", { className: "kpt-chat-message__bubble", "data-variant": resolvedVariant, "data-align": align, "data-position": position, "data-status": resolvedStatus ?? undefined, "data-tail": hasTail ? '' : undefined, children: [resolvedReplyTo ? (_jsxs("button", { type: "button", className: "kpt-chat-message__quote", onClick: () => onQuoted?.(resolvedReplyTo), children: [_jsx("span", { className: "kpt-chat-message__quote-author", children: resolvedReplyTo.author }), _jsx("span", { className: "kpt-chat-message__quote-text", children: resolvedReplyTo.excerpt })] })) : null, resolvedText ? _jsx("p", { className: "kpt-chat-message__text", children: resolvedText }) : null, children, resolvedAttachments.length ? (_jsx("ul", { className: "kpt-chat-message__files", children: resolvedAttachments.map((file) => (_jsxs("li", { className: "kpt-chat-message__file", children: [file.thumbnail ? (_jsx("img", { className: "kpt-chat-message__file-thumb", src: file.thumbnail, alt: file.name })) : (_jsx(KptIcon, { name: "paperclip" })), _jsx("a", { className: "kpt-chat-message__file-name", href: file.url, download: file.url ? file.name : undefined, children: file.name }), file.size ? (_jsx("span", { className: "kpt-chat-message__file-size", children: fileSize(file.size) })) : null] }, file.id))) })) : null, showMeta ? (_jsxs("span", { className: "kpt-chat-message__meta", children: [timeLabel ? (_jsx("time", { className: "kpt-chat-message__time", dateTime: sentAt?.toISOString(), children: timeLabel })) : null, statusIcon ? (_jsx(KptIcon, { className: "kpt-chat-message__status", name: statusIcon }, statusIcon)) : null] })) : null, resolvedReactions.length ? (_jsx("ul", { className: "kpt-chat-message__reactions", "data-align": align, children: resolvedReactions.map((reaction) => (_jsxs("li", { className: "kpt-chat-message__reaction", "data-reacted": reaction.reacted ? '' : undefined, children: [_jsx("span", { "aria-hidden": "true", children: reaction.emoji }), _jsx("span", { className: "kpt-chat-message__reaction-count", children: _jsx("span", { className: "kpt-chat-message__reaction-digit", children: reaction.count }, reaction.count) })] }, reaction.emoji))) })) : null] })] }), _jsx("span", { className: "kpt-chat-message__actions", "data-open": pickerOpen ? '' : undefined, children: retryable ? (_jsx(KptIconButton, { hostProps: { className: 'kpt-chat-message__retry' }, size: "sm", "aria-label": t('chat.retry'), onClick: () => onRetry?.(), children: _jsx(KptIcon, { name: "refresh" }) })) : actions ? (_jsxs(_Fragment, { children: [_jsx(KptIconButton, { size: "sm", "aria-label": t('chat.replyTo', { author: resolvedAuthor?.name ?? '' }), onClick: () => onReply?.(), children: _jsx(KptIcon, { name: "corner-up-left" }) }), _jsxs("span", { className: "kpt-chat-message__react", onKeyDown: (event) => {
90
101
  if (event.key === 'Escape')
91
102
  setPickerOpen(false);
92
103
  }, onBlur: (event) => {
@@ -94,7 +105,7 @@ export function KptChatMessage({ message = null, author = null, text = null, tim
94
105
  if (!event.currentTarget.contains(event.relatedTarget))
95
106
  setPickerOpen(false);
96
107
  }, children: [_jsx(KptIconButton, { size: "sm", "aria-label": t('chat.react'), "aria-expanded": pickerOpen, onClick: () => setPickerOpen((open) => !open), children: _jsx(KptIcon, { name: "mood-smile" }) }), pickerOpen ? (_jsx("span", { className: "kpt-chat-message__picker", role: "group", "aria-label": t('chat.react'), children: reactionOptions.map((option) => {
97
- const label = t('chat.react' + option.kind[0].toUpperCase() + option.kind.slice(1));
108
+ const label = option.label ?? t('chat.react' + option.kind[0].toUpperCase() + option.kind.slice(1));
98
109
  const reacted = resolvedReactions.some((item) => item.emoji === option.emoji && item.reacted);
99
110
  return (_jsx("button", { type: "button", className: "kpt-chat-message__pick", "aria-label": label, title: label, "aria-pressed": reacted, onClick: () => {
100
111
  onReact?.(option.emoji);
@@ -102,7 +113,7 @@ export function KptChatMessage({ message = null, author = null, text = null, tim
102
113
  }, children: _jsx("span", { "aria-hidden": "true", children: option.emoji }) }, option.kind));
103
114
  }) })) : null] })] })) : null })] }) }));
104
115
  }
105
- /** Rozmiar załącznika w kB/MB — surowe bajty nic nie mówią przy pliku obok nazwy. */
116
+ /** Rozmiar załącznika w kB/MB - surowe bajty nic nie mówią przy pliku obok nazwy. */
106
117
  function fileSize(bytes) {
107
118
  if (bytes < 1024)
108
119
  return bytes + ' B';
@@ -7,41 +7,42 @@ Import: `import { KptChatMessage } from '@konce-pt/react/chat';`
7
7
  ## DOM
8
8
  <div class="kpt-chat-message-host" role="article" aria-label="Anna, 09:22, Read">
9
9
  <div class="kpt-chat-message__row" data-align="start">
10
- <span class="kpt-chat-message__avatar">…KptAvatar…</span>
10
+ <span class="kpt-chat-message__avatar">...KptAvatar...</span>
11
11
  <div class="kpt-chat-message__stack">
12
12
  <span class="kpt-chat-message__author">Anna Kowalska</span>
13
13
  <div class="kpt-chat-message__bubble"
14
14
  data-variant="neutral" data-align="start" data-position="last" data-tail>
15
- <button class="kpt-chat-message__quote">…</button>
16
- <p class="kpt-chat-message__text">…</p>
17
- <ul class="kpt-chat-message__files">…</ul>
18
- <span class="kpt-chat-message__meta"><time>09:22</time>…</span>
19
- <ul class="kpt-chat-message__reactions">…</ul>
15
+ <button class="kpt-chat-message__quote">...</button>
16
+ <p class="kpt-chat-message__text">...</p>
17
+ <ul class="kpt-chat-message__files">...</ul>
18
+ <span class="kpt-chat-message__meta"><time>09:22</time>...</span>
19
+ <ul class="kpt-chat-message__reactions">...</ul>
20
20
  </div>
21
21
  </div>
22
- <span class="kpt-chat-message__actions">…KptIconButton, .kpt-chat-message__picker…</span>
22
+ <span class="kpt-chat-message__actions">...KptIconButton, .kpt-chat-message__picker...</span>
23
23
  </div>
24
24
  </div>
25
25
 
26
26
  ## Props
27
- - `message`: `KptChatMessage` — the whole object; the single props below win over it
27
+ - `message`: `KptChatMessage` - the whole object; the single props below win over it
28
28
  - `author`, `text`, `time`, `status`, `attachments`, `reactions`, `replyTo`
29
29
  - `align`: `'start' | 'end'`; `variant`: `'neutral' | 'primary' | 'solid' | 'ghost' | 'system'`
30
30
  - `position`: `'single' | 'first' | 'middle' | 'last'`; `system`: boolean
31
31
  - `showAuthor`, `showAvatar`, `showMeta` (default true), `tail` (default true), `actions`
32
- - `reactionOptions`: `readonly KptChatReactionOption[]` (default `KPT_CHAT_REACTIONS`) — the picker set
32
+ - `reactionOptions`: `readonly KptChatReactionOption[]` (default `KPT_CHAT_REACTIONS`) - the picker set
33
+ - `enterMotion`: `'spring' | 'fade' | 'none'` - how a new reaction and a status change appear (`KptChat` passes its own; see its doc)
33
34
  - `onRetry`, `onReply`: `() => void`; `onReact`: `(emoji: string) => void`; `onQuoted`: `(quote) => void`
34
- - `children` — bubble content; it renders next to `text`, so the bubble takes anything
35
+ - `children` - bubble content; it renders next to `text`, so the bubble takes anything
35
36
  - `className`, `ref` and every other `<div>` prop go to the host
36
37
 
37
38
  ## The default variant follows the alignment
38
39
  Your own message gets the full `solid`, someone else's gets `neutral`. The theme is monochrome, so
39
- the difference between bubbles is lightness, not hue — `--kpt-color-primary-subtle` has exactly the
40
+ the difference between bubbles is lightness, not hue - `--kpt-color-primary-subtle` has exactly the
40
41
  lightness of `--kpt-color-muted`, which would make the two sides indistinguishable. For a quieter
41
42
  list set `variant="primary"` by hand.
42
43
 
43
44
  ## Grouping is what the shape is made of
44
- `position` decides which corner collapses and which bubble wears the tail — inside a group the
45
+ `position` decides which corner collapses and which bubble wears the tail - inside a group the
45
46
  corner on the sender's side drops to `--kpt-radius-sm`. `KptChat` gets all of it from
46
47
  `buildChatRows()`; set it by hand only when you build the list yourself. The tail is skipped for
47
48
  `ghost` and `system`, which have no background to cut it out of.
@@ -49,7 +50,7 @@ corner on the sender's side drops to `--kpt-radius-sm`. `KptChat` gets all of it
49
50
  ## An unsent message must be able to say so
50
51
  `status="failed"` outlines the bubble and puts a retry arrow **outside** it, in the action column
51
52
  on the outer side of the list. `KptChat` also keeps the meta row visible on such a bubble even in
52
- the middle of a group — the arrow with nothing to explain it would be a mystery.
53
+ the middle of a group - the arrow with nothing to explain it would be a mystery.
53
54
  Statuses: `sending` (clock), `sent` (check), `delivered` (double check), `read` (the same icon in
54
55
  the accent colour) and `failed`.
55
56
 
@@ -64,10 +65,10 @@ is formatted by `Intl` in the active locale.
64
65
 
65
66
  ## Tokens
66
67
  `--kpt-color-muted` (incoming) / `--kpt-color-primary` + `--kpt-color-on-primary` (own),
67
- failed `--kpt-color-danger-subtle` + `--kpt-color-danger-border` — the failed rule also resets the
68
+ failed `--kpt-color-danger-subtle` + `--kpt-color-danger-border` - the failed rule also resets the
68
69
  text colour, or a white `solid` label would land on pale pink.
69
70
 
70
71
  ## Accessibility
71
- The host is an `article` labelled with author, time and status — content alone would say nothing
72
+ The host is an `article` labelled with author, time and status - content alone would say nothing
72
73
  about who wrote it or whether it was sent. The status icon is `aria-hidden`; the meaning travels in
73
74
  the label. A system notice is a `note`, never an `article`.
@@ -8,13 +8,13 @@ export interface KptChatTypingProps extends Omit<ComponentPropsWithRef<'div'>, '
8
8
  label?: string;
9
9
  }
10
10
  /**
11
- * Wskaźnik pisania — trzy kropki i informacja, kto pisze.
11
+ * Wskaźnik pisania - trzy kropki i informacja, kto pisze.
12
12
  *
13
13
  * Pusta lista autorów nie renderuje niczego, więc można go trzymać w drzewie na stałe
14
14
  * i sterować wyłącznie danymi.
15
15
  *
16
16
  * Element jest `role="status"` z `aria-live="polite"`: czytnik ogłasza wejście i zniknięcie
17
- * wskaźnika, ale nie przerywa czytania wiadomości. Sama animacja kropek nic nie mówi —
17
+ * wskaźnika, ale nie przerywa czytania wiadomości. Sama animacja kropek nic nie mówi -
18
18
  * informację niesie etykieta.
19
19
  *
20
20
  * @example
@@ -1,14 +1,15 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { cn } from "../internal/cn.js";
3
4
  import { useKptMessages } from "../i18n/i18n-context.js";
4
5
  /**
5
- * Wskaźnik pisania — trzy kropki i informacja, kto pisze.
6
+ * Wskaźnik pisania - trzy kropki i informacja, kto pisze.
6
7
  *
7
8
  * Pusta lista autorów nie renderuje niczego, więc można go trzymać w drzewie na stałe
8
9
  * i sterować wyłącznie danymi.
9
10
  *
10
11
  * Element jest `role="status"` z `aria-live="polite"`: czytnik ogłasza wejście i zniknięcie
11
- * wskaźnika, ale nie przerywa czytania wiadomości. Sama animacja kropek nic nie mówi —
12
+ * wskaźnika, ale nie przerywa czytania wiadomości. Sama animacja kropek nic nie mówi -
12
13
  * informację niesie etykieta.
13
14
  *
14
15
  * @example
@@ -19,7 +20,7 @@ export function KptChatTyping({ authors = [], label = '', locale, dictionary, cl
19
20
  if (authors.length === 0 && !label)
20
21
  return null;
21
22
  /*
22
- * Jedna osoba jest wymieniona z imienia, przy większej liczbie liczy się głowy —
23
+ * Jedna osoba jest wymieniona z imienia, przy większej liczbie liczy się głowy -
23
24
  * lista nazwisk rozjeżdża wiersz i i tak nikt jej nie czyta.
24
25
  */
25
26
  const text = label ||