@k2b/ui 0.33.0 → 0.34.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 (240) hide show
  1. package/dist/base.css +14 -1
  2. package/dist/browser/actions/ContextMenu.js +13 -0
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/actions/Dropdown.js +15 -4
  5. package/dist/browser/actions/Dropdown.js.map +1 -1
  6. package/dist/browser/actions/SelectionToolbar.js +129 -0
  7. package/dist/browser/actions/SelectionToolbar.js.map +1 -0
  8. package/dist/browser/actions/index.js +1 -0
  9. package/dist/browser/actions/index.js.map +1 -1
  10. package/dist/browser/charts.js +6 -0
  11. package/dist/browser/charts.js.map +1 -0
  12. package/dist/browser/chat/ChatComposer.js +525 -407
  13. package/dist/browser/chat/ChatComposer.js.map +1 -1
  14. package/dist/browser/chat/chat-messages.js +4 -0
  15. package/dist/browser/chat/chat-messages.js.map +1 -1
  16. package/dist/browser/chat/composer-document.js +19 -0
  17. package/dist/browser/chat/composer-document.js.map +1 -1
  18. package/dist/browser/chat/index.js.map +1 -1
  19. package/dist/browser/content/Chart.js +8 -0
  20. package/dist/browser/content/Chart.js.map +1 -1
  21. package/dist/browser/content/FileGrid.js +231 -9
  22. package/dist/browser/content/FileGrid.js.map +1 -1
  23. package/dist/browser/content/FileTypeIcon.js +15 -0
  24. package/dist/browser/content/FileTypeIcon.js.map +1 -1
  25. package/dist/browser/content/FileView.js +11 -4
  26. package/dist/browser/content/FileView.js.map +1 -1
  27. package/dist/browser/content/MessageRow.js +73 -53
  28. package/dist/browser/content/MessageRow.js.map +1 -1
  29. package/dist/browser/content/ReactionBar.js +3 -1
  30. package/dist/browser/content/ReactionBar.js.map +1 -1
  31. package/dist/browser/content/calendar-day-scroll.js +4 -3
  32. package/dist/browser/content/calendar-day-scroll.js.map +1 -1
  33. package/dist/browser/content/chart-bar.js +476 -0
  34. package/dist/browser/content/chart-bar.js.map +1 -0
  35. package/dist/browser/content/file-list-messages.js +52 -0
  36. package/dist/browser/content/file-list-messages.js.map +1 -0
  37. package/dist/browser/content/index.js +1 -1
  38. package/dist/browser/content/index.js.map +1 -1
  39. package/dist/browser/feedback/Tooltip.js +31 -14
  40. package/dist/browser/feedback/Tooltip.js.map +1 -1
  41. package/dist/browser/inputs/ChoiceList.js +178 -0
  42. package/dist/browser/inputs/ChoiceList.js.map +1 -0
  43. package/dist/browser/inputs/ChoiceSlider.js +314 -0
  44. package/dist/browser/inputs/ChoiceSlider.js.map +1 -0
  45. package/dist/browser/inputs/ChoiceTiles.js +135 -0
  46. package/dist/browser/inputs/ChoiceTiles.js.map +1 -0
  47. package/dist/browser/inputs/ColorChoices.js +120 -0
  48. package/dist/browser/inputs/ColorChoices.js.map +1 -0
  49. package/dist/browser/inputs/QrScanner.js +95 -13
  50. package/dist/browser/inputs/QrScanner.js.map +1 -1
  51. package/dist/browser/inputs/TextInput.js +15 -13
  52. package/dist/browser/inputs/TextInput.js.map +1 -1
  53. package/dist/browser/inputs/index.js +4 -0
  54. package/dist/browser/inputs/index.js.map +1 -1
  55. package/dist/browser/inputs/qr-messages.js +2 -0
  56. package/dist/browser/inputs/qr-messages.js.map +1 -1
  57. package/dist/browser/internal/radio-keys.js +13 -0
  58. package/dist/browser/internal/radio-keys.js.map +1 -0
  59. package/dist/browser/layout/AnchoredPopover.js +180 -0
  60. package/dist/browser/layout/AnchoredPopover.js.map +1 -0
  61. package/dist/browser/layout/AppWorkspace.js +155 -20
  62. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  63. package/dist/browser/layout/DetailPanel.js +443 -333
  64. package/dist/browser/layout/DetailPanel.js.map +1 -1
  65. package/dist/browser/layout/MobileShell.js +21 -7
  66. package/dist/browser/layout/MobileShell.js.map +1 -1
  67. package/dist/browser/layout/Settings.js +283 -175
  68. package/dist/browser/layout/Settings.js.map +1 -1
  69. package/dist/browser/layout/SidebarRailHint.js +79 -0
  70. package/dist/browser/layout/SidebarRailHint.js.map +1 -0
  71. package/dist/browser/layout/SidebarSwitcher.js +79 -66
  72. package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
  73. package/dist/browser/layout/TabBar.js +18 -4
  74. package/dist/browser/layout/TabBar.js.map +1 -1
  75. package/dist/browser/layout/app-workspace-controller.js +11 -4
  76. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  77. package/dist/browser/layout/detail-panel-messages.js +4 -2
  78. package/dist/browser/layout/detail-panel-messages.js.map +1 -1
  79. package/dist/browser/layout/index.js +2 -1
  80. package/dist/browser/layout/index.js.map +1 -1
  81. package/dist/browser/layout/settings-messages.js +26 -2
  82. package/dist/browser/layout/settings-messages.js.map +1 -1
  83. package/dist/browser/surfaces/Avatar.js +2 -14
  84. package/dist/browser/surfaces/Avatar.js.map +1 -1
  85. package/dist/browser/surfaces/DotStrip.js +70 -0
  86. package/dist/browser/surfaces/DotStrip.js.map +1 -0
  87. package/dist/browser/surfaces/ProgressRing.js +3 -0
  88. package/dist/browser/surfaces/ProgressRing.js.map +1 -1
  89. package/dist/browser/surfaces/ResourceCard.js +125 -68
  90. package/dist/browser/surfaces/ResourceCard.js.map +1 -1
  91. package/dist/browser/surfaces/index.js +2 -0
  92. package/dist/browser/surfaces/index.js.map +1 -1
  93. package/dist/browser/surfaces/person-color.js +18 -0
  94. package/dist/browser/surfaces/person-color.js.map +1 -0
  95. package/dist/browser/surfaces/resource-messages.js +6 -2
  96. package/dist/browser/surfaces/resource-messages.js.map +1 -1
  97. package/dist/browser/widgets/Widget.js +3 -2
  98. package/dist/browser/widgets/Widget.js.map +1 -1
  99. package/dist/ssr/actions/ContextMenu.js +9 -0
  100. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  101. package/dist/ssr/actions/Dropdown.js +14 -4
  102. package/dist/ssr/actions/Dropdown.js.map +1 -1
  103. package/dist/ssr/actions/SelectionToolbar.js +104 -0
  104. package/dist/ssr/actions/SelectionToolbar.js.map +1 -0
  105. package/dist/ssr/actions/index.js +1 -0
  106. package/dist/ssr/actions/index.js.map +1 -1
  107. package/dist/ssr/charts.js +6 -0
  108. package/dist/ssr/charts.js.map +1 -0
  109. package/dist/ssr/chat/ChatComposer.js +132 -53
  110. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  111. package/dist/ssr/chat/chat-messages.js +4 -0
  112. package/dist/ssr/chat/chat-messages.js.map +1 -1
  113. package/dist/ssr/chat/composer-document.js +19 -0
  114. package/dist/ssr/chat/composer-document.js.map +1 -1
  115. package/dist/ssr/chat/index.js.map +1 -1
  116. package/dist/ssr/content/Chart.js +8 -0
  117. package/dist/ssr/content/Chart.js.map +1 -1
  118. package/dist/ssr/content/FileGrid.js +132 -5
  119. package/dist/ssr/content/FileGrid.js.map +1 -1
  120. package/dist/ssr/content/FileTypeIcon.js +15 -0
  121. package/dist/ssr/content/FileTypeIcon.js.map +1 -1
  122. package/dist/ssr/content/FileView.js +11 -4
  123. package/dist/ssr/content/FileView.js.map +1 -1
  124. package/dist/ssr/content/MessageRow.js +16 -6
  125. package/dist/ssr/content/MessageRow.js.map +1 -1
  126. package/dist/ssr/content/ReactionBar.js +3 -1
  127. package/dist/ssr/content/ReactionBar.js.map +1 -1
  128. package/dist/ssr/content/calendar-day-scroll.js +4 -3
  129. package/dist/ssr/content/calendar-day-scroll.js.map +1 -1
  130. package/dist/ssr/content/chart-bar.js +476 -0
  131. package/dist/ssr/content/chart-bar.js.map +1 -0
  132. package/dist/ssr/content/file-list-messages.js +52 -0
  133. package/dist/ssr/content/file-list-messages.js.map +1 -0
  134. package/dist/ssr/content/index.js +1 -1
  135. package/dist/ssr/content/index.js.map +1 -1
  136. package/dist/ssr/feedback/Tooltip.js +31 -14
  137. package/dist/ssr/feedback/Tooltip.js.map +1 -1
  138. package/dist/ssr/inputs/ChoiceList.js +95 -0
  139. package/dist/ssr/inputs/ChoiceList.js.map +1 -0
  140. package/dist/ssr/inputs/ChoiceSlider.js +191 -0
  141. package/dist/ssr/inputs/ChoiceSlider.js.map +1 -0
  142. package/dist/ssr/inputs/ChoiceTiles.js +61 -0
  143. package/dist/ssr/inputs/ChoiceTiles.js.map +1 -0
  144. package/dist/ssr/inputs/ColorChoices.js +60 -0
  145. package/dist/ssr/inputs/ColorChoices.js.map +1 -0
  146. package/dist/ssr/inputs/QrScanner.js +89 -10
  147. package/dist/ssr/inputs/QrScanner.js.map +1 -1
  148. package/dist/ssr/inputs/TextInput.js +13 -1
  149. package/dist/ssr/inputs/TextInput.js.map +1 -1
  150. package/dist/ssr/inputs/index.js +4 -0
  151. package/dist/ssr/inputs/index.js.map +1 -1
  152. package/dist/ssr/inputs/qr-messages.js +2 -0
  153. package/dist/ssr/inputs/qr-messages.js.map +1 -1
  154. package/dist/ssr/internal/radio-keys.js +13 -0
  155. package/dist/ssr/internal/radio-keys.js.map +1 -0
  156. package/dist/ssr/layout/AnchoredPopover.js +142 -0
  157. package/dist/ssr/layout/AnchoredPopover.js.map +1 -0
  158. package/dist/ssr/layout/AppWorkspace.js +154 -20
  159. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  160. package/dist/ssr/layout/DetailPanel.js +181 -108
  161. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  162. package/dist/ssr/layout/MobileShell.js +20 -6
  163. package/dist/ssr/layout/MobileShell.js.map +1 -1
  164. package/dist/ssr/layout/Settings.js +122 -45
  165. package/dist/ssr/layout/Settings.js.map +1 -1
  166. package/dist/ssr/layout/SidebarRailHint.js +79 -0
  167. package/dist/ssr/layout/SidebarRailHint.js.map +1 -0
  168. package/dist/ssr/layout/SidebarSwitcher.js +13 -3
  169. package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
  170. package/dist/ssr/layout/TabBar.js +11 -3
  171. package/dist/ssr/layout/TabBar.js.map +1 -1
  172. package/dist/ssr/layout/app-workspace-controller.js +11 -4
  173. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  174. package/dist/ssr/layout/detail-panel-messages.js +4 -2
  175. package/dist/ssr/layout/detail-panel-messages.js.map +1 -1
  176. package/dist/ssr/layout/index.js +2 -1
  177. package/dist/ssr/layout/index.js.map +1 -1
  178. package/dist/ssr/layout/settings-messages.js +26 -2
  179. package/dist/ssr/layout/settings-messages.js.map +1 -1
  180. package/dist/ssr/surfaces/Avatar.js +2 -14
  181. package/dist/ssr/surfaces/Avatar.js.map +1 -1
  182. package/dist/ssr/surfaces/DotStrip.js +31 -0
  183. package/dist/ssr/surfaces/DotStrip.js.map +1 -0
  184. package/dist/ssr/surfaces/ProgressRing.js +3 -0
  185. package/dist/ssr/surfaces/ProgressRing.js.map +1 -1
  186. package/dist/ssr/surfaces/ResourceCard.js +40 -15
  187. package/dist/ssr/surfaces/ResourceCard.js.map +1 -1
  188. package/dist/ssr/surfaces/index.js +2 -0
  189. package/dist/ssr/surfaces/index.js.map +1 -1
  190. package/dist/ssr/surfaces/person-color.js +18 -0
  191. package/dist/ssr/surfaces/person-color.js.map +1 -0
  192. package/dist/ssr/surfaces/resource-messages.js +6 -2
  193. package/dist/ssr/surfaces/resource-messages.js.map +1 -1
  194. package/dist/ssr/widgets/Widget.js +3 -2
  195. package/dist/ssr/widgets/Widget.js.map +1 -1
  196. package/dist/styles.css +1 -1
  197. package/dist/types/actions/ContextMenu.d.ts +5 -0
  198. package/dist/types/actions/Dropdown.d.ts +5 -0
  199. package/dist/types/actions/SelectionToolbar.d.ts +38 -0
  200. package/dist/types/actions/index.d.ts +2 -0
  201. package/dist/types/charts.d.ts +5 -0
  202. package/dist/types/chat/ChatComposer.d.ts +15 -0
  203. package/dist/types/chat/chat-messages.d.ts +2 -0
  204. package/dist/types/chat/composer-document.d.ts +5 -0
  205. package/dist/types/chat/index.d.ts +1 -1
  206. package/dist/types/content/Chart.d.ts +2 -1
  207. package/dist/types/content/FileGrid.d.ts +43 -3
  208. package/dist/types/content/FileTypeIcon.d.ts +10 -0
  209. package/dist/types/content/FileView.d.ts +5 -1
  210. package/dist/types/content/MessageRow.d.ts +2 -0
  211. package/dist/types/content/calendar-day-scroll.d.ts +2 -1
  212. package/dist/types/content/chart-bar.d.ts +60 -0
  213. package/dist/types/content/file-list-messages.d.ts +24 -0
  214. package/dist/types/content/index.d.ts +2 -2
  215. package/dist/types/feedback/Tooltip.d.ts +8 -2
  216. package/dist/types/inputs/ChoiceList.d.ts +28 -0
  217. package/dist/types/inputs/ChoiceSlider.d.ts +36 -0
  218. package/dist/types/inputs/ChoiceTiles.d.ts +25 -0
  219. package/dist/types/inputs/ColorChoices.d.ts +19 -0
  220. package/dist/types/inputs/index.d.ts +8 -0
  221. package/dist/types/inputs/qr-messages.d.ts +1 -0
  222. package/dist/types/internal/radio-keys.d.ts +5 -0
  223. package/dist/types/layout/AnchoredPopover.d.ts +31 -0
  224. package/dist/types/layout/AppWorkspace.d.ts +10 -0
  225. package/dist/types/layout/DetailPanel.d.ts +17 -0
  226. package/dist/types/layout/MobileShell.d.ts +5 -2
  227. package/dist/types/layout/Settings.d.ts +22 -0
  228. package/dist/types/layout/SidebarRailHint.d.ts +26 -0
  229. package/dist/types/layout/TabBar.d.ts +5 -0
  230. package/dist/types/layout/detail-panel-messages.d.ts +1 -0
  231. package/dist/types/layout/index.d.ts +3 -2
  232. package/dist/types/layout/settings-messages.d.ts +12 -0
  233. package/dist/types/surfaces/DotStrip.d.ts +22 -0
  234. package/dist/types/surfaces/ProgressRing.d.ts +2 -1
  235. package/dist/types/surfaces/ResourceCard.d.ts +9 -0
  236. package/dist/types/surfaces/index.d.ts +4 -1
  237. package/dist/types/surfaces/person-color.d.ts +10 -0
  238. package/dist/types/surfaces/resource-messages.d.ts +2 -0
  239. package/dist/types/widgets/Widget.d.ts +3 -2
  240. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/MessageRow.tsx"],"sourcesContent":["import { fileIcons } from \"@k2b/stdlib\";\nimport type { Token, Tokens } from \"marked\";\nimport { createMemo, createSignal, createUniqueId, For, type JSX, onCleanup, Show } from \"solid-js\";\nimport { Button } from \"../actions/Button\";\nimport { copyText } from \"../actions/CopyButton\";\nimport { Dropdown, type DropdownAction } from \"../actions/Dropdown\";\nimport { type GestureKind, GestureMenu, type GestureMenuAction } from \"../actions/GestureMenu\";\nimport { type ChatMessages, useChatMessages } from \"../chat/chat-messages\";\nimport { announce } from \"../feedback/announce\";\nimport { useLocale } from \"../intl/locale\";\nimport { type UiMessages, useUiMessages } from \"../intl/messages\";\nimport { Avatar } from \"../surfaces/Avatar\";\nimport { Kbd } from \"./Kbd\";\nimport { createSafeMarked, createSafeRenderer, escapeHtml } from \"./MarkdownView\";\nimport { ReactionBar, type ReactionBarItem } from \"./ReactionBar\";\n\n/** Messages by the same author within this time continue a group. */\nconst GROUP_WINDOW_MS = 5 * 60_000;\n/**\n * A message collapses to ten lines (`.k2b-message-row__text[data-collapsed]`) when its rendered text takes more lines\n * than this. `renderedLines` counts the fewest lines the text takes at any width, so a collapsed message always hides\n * some of it.\n */\nconst COLLAPSE_LINES = 14;\n/** Visible characters that take at least one line of a message, even at its widest. */\nconst LINE_CHARS = 100;\n/** Lines at the bottom of a collapsed message that fade out, as `.k2b-message-row__text[data-collapsed]` draws them. */\nconst FADE_LINES = 2;\nconst COPIED_MS = 2_000;\n/** Alt+click on these keeps its own meaning, such as downloading a link. */\nconst ALT_CLICK_IGNORE = \"a[href], button, input, textarea, select, summary, label, audio, video, [role='button'], [role='link']\";\n/** Links in messages may only use these schemes; other links show as their text. */\nconst LINK_PROTOCOLS = [\"https:\", \"http:\", \"mailto:\"] as const;\n/** Attachments open and load only from these schemes or from a relative URL. */\nconst ATTACHMENT_PROTOCOLS = new Set([\"https:\", \"http:\"]);\n/**\n * A picture may also show a file that is still uploading. A `blob:` URL runs in the page's origin when it is opened,\n * so links never use one.\n */\nconst PICTURE_PROTOCOLS = new Set([...ATTACHMENT_PROTOCOLS, \"blob:\"]);\n/** A grid shows this many images or videos; the last one tells how many more there are. */\nconst MEDIA_SHOWN = 4;\n/** A single image keeps its own aspect ratio within these bounds; beyond them it is cropped to fit. */\nconst MEDIA_RATIO_MIN = 0.5;\nconst MEDIA_RATIO_MAX = 3;\n\nexport type MessageRowAuthor = {\n /** Display name, shown at the start of a group and read before every message. */\n name: string;\n /** Picture URL. Without one, the avatar shows initials on the author's tint. */\n avatar?: string | null;\n /** Icon class shown instead of initials, for example for an automated author. */\n icon?: string;\n};\n\nexport type MessageRowStatus = \"pending\" | \"sent\" | \"failed\";\n\nexport type MessageRowAction = {\n id: string;\n /** Visible name of the menu entry. */\n label: string;\n /** Icon class, for example `ti ti-arrow-back-up`. */\n icon: string;\n /** One quiet sentence below the label that says what the entry does. */\n description?: string;\n onSelect: () => void;\n disabled?: boolean;\n variant?: \"danger\";\n /** A touch gesture that runs the entry too. Give each gesture to at most one entry. */\n gesture?: GestureKind;\n /**\n * Alt+click (Option+click on Apple devices) on the message runs the entry directly, and the entry shows that\n * shortcut. Give it to at most one entry.\n */\n altClick?: boolean;\n /** A key the application handles for the focused message, such as \"r\"; the entry shows it at its end. */\n shortcut?: string;\n /** In the phone sheet, runs while the tap still counts as the person's gesture, as clipboard writes require. */\n immediate?: boolean;\n};\n\nexport type MessageRowQuote = {\n /** Name of the quoted message's author. */\n author: string;\n /** Plain text of the quoted message. It shows at most two lines. */\n text: string;\n /** Opens the quoted message, for example by scrolling to it. Without it, the quote is not a control. */\n onSelect?: () => void;\n};\n\n/** Reaction labels are read by screen readers and shown in the names hint. */\nexport type MessageRowReaction = ReactionBarItem;\n\nexport type MessageRowThread = {\n /** Number of replies. */\n count: number;\n /** People who replied, most recent first; the bar shows up to three. */\n participants?: readonly MessageRowAuthor[];\n /** Visible time of the last reply, formatted by the caller. */\n lastReply?: string;\n lastReplyDateTime?: string | Date;\n onOpen: () => void;\n};\n\ntype MessageRowAttachmentTarget = {\n /** Opens the attachment, for example in a lightbox. Takes precedence over `href`. */\n onOpen?: () => void;\n /** Opens the attachment in a new tab. Absolute `https` or `http`, or relative. */\n href?: string;\n};\n\nexport type MessageRowMedia = MessageRowAttachmentTarget & {\n kind: \"image\" | \"video\";\n /** The image, or the poster frame of a video. Absolute `https` or `http`, relative, `blob`, or `data:image/`. */\n src: string;\n /** Describes the image or video for people who cannot see it. */\n alt: string;\n /** Stored pixel size. The row reserves the area from it, so nothing moves when the image loads. */\n width: number;\n height: number;\n /** Visible length of a video, for example \"0:42\". */\n duration?: string;\n};\n\nexport type MessageRowFile = MessageRowAttachmentTarget & {\n kind: \"file\";\n name: string;\n /** Second line, for example \"PDF · 412 KB\". */\n detail?: string;\n /** Media type, used with the name to choose the icon. */\n mediaType?: string;\n /** Icon class instead of the one chosen from the name. */\n icon?: string;\n};\n\nexport type MessageRowAttachment = MessageRowMedia | MessageRowFile;\n\n/** Where a message belongs, shown as a small chip under it, for example its thread in a timeline of several. */\nexport type MessageRowOrigin = {\n label: string;\n /** Icon class, for example `ti ti-hash`. */\n icon?: string;\n /** Opens where the message belongs. Without it, the chip is not a control. */\n onSelect?: () => void;\n /** Accessible name of the control when it must say more than the label, such as \"Open thread Updates\". */\n selectLabel?: string;\n};\n\nexport type MessageRowProgress = {\n /** What is happening, for example \"Writing\" or \"Searching the files\". */\n status: string;\n /** Called by \"Stop\". */\n onStop?: () => void;\n};\n\nexport type MessageRowProps = {\n author: MessageRowAuthor;\n /** Message text as Markdown. Raw HTML shows as text; links must be absolute and use `https`, `http`, or `mailto`. */\n text: string;\n /** Visible time, formatted by the caller, so server and browser render the same text. */\n time: string;\n /** Machine-readable time for the `<time>` element. */\n dateTime?: string | Date;\n /** The reader's own message: on the right in the accent tint, without avatar or name. */\n own?: boolean;\n /**\n * Starts a group with avatar, name, badge, and time. A continuation shows only its text; screen readers still hear\n * the author and time. Defaults to true; `startsMessageGroup` decides it from the previous message.\n */\n groupStart?: boolean;\n /** Shown next to the name at the start of a group, for example the kind of account. */\n badge?: JSX.Element;\n /** Send state. Sets a line of fixed height below the message: a clock, a check, or \"Not sent\" with \"Retry\". */\n status?: MessageRowStatus;\n /** Text shown in that line instead of the check once someone has read the message, for example \"Read by Nora\". */\n receipt?: string;\n /** Called by \"Retry\" while `status` is `\"failed\"`. */\n onRetry?: () => void;\n /** Called by \"Discard\" next to \"Retry\" while `status` is `\"failed\"`, so both answers to a failure sit where it shows. */\n onDiscard?: () => void;\n /**\n * The message's menu. A quiet \"…\" beside the bubble opens it while the message is hovered or focused, also from the\n * keyboard; right-click and a long press open the same menu. Showing \"…\" never moves the layout.\n */\n actions?: readonly MessageRowAction[];\n /** The message this one answers, shown above it. */\n quote?: MessageRowQuote;\n /** Marks the message as forwarded from somewhere else. */\n forwarded?: boolean;\n /** Marks the message as edited after it was sent. */\n edited?: boolean;\n /** Shows \"This message was deleted\" instead of the text, quote, attachments, previews, and reactions. */\n deleted?: boolean;\n /** Images and videos in a grid with areas reserved from their stored size, then files as chips. */\n attachments?: readonly MessageRowAttachment[];\n /** A preview of a link in the text, supplied with its final height. */\n linkPreview?: JSX.Element;\n /** A card for an element the message refers to, such as a task or a document, supplied with its final height. */\n card?: JSX.Element;\n /**\n * Reactions in a bar of fixed height. The bar shows whenever this is set, even to an empty list, so chips that come\n * and go never change the row. Only setting it where it was unset adds a line, so pass it on own messages and on\n * messages that have or had reactions, and leave it out on others' messages that never had one.\n */\n reactions?: readonly MessageRowReaction[];\n /** Called with a reaction's `key` when the reader presses its chip. Without it, the chips are not controls. */\n onToggleReaction?: (key: string) => void;\n /** Called by \"Add reaction\" in the reaction bar, with the button to anchor a picker to. */\n onAddReaction?: (anchor: HTMLElement) => void;\n /** A bar of fixed height that opens the replies to this message. */\n thread?: MessageRowThread;\n /** A chip of fixed height under the message that names where it belongs. */\n origin?: MessageRowOrigin;\n /** The message is still being written, by a person or an automated author alike: a status line with \"Stop\". */\n progress?: MessageRowProgress;\n class?: string;\n};\n\nexport type MessageSystemRowProps = {\n /** What happened, for example \"Nora added Tobias\". */\n children: JSX.Element;\n /** Visible time, formatted by the caller. */\n time?: string;\n dateTime?: string | Date;\n /** Icon class shown before the text. */\n icon?: string;\n class?: string;\n};\n\nexport type MessageEventRowProps = {\n /** What changed, for example \"Mara set the status to “In review”\". */\n children: JSX.Element;\n /** Icon class of the kind of change; a dot by default. */\n icon?: string;\n /** Where the change belongs, such as its thread, where several threads mix. */\n origin?: MessageRowOrigin;\n /** Visible time, formatted by the caller. */\n time?: string;\n dateTime?: string | Date;\n class?: string;\n};\n\nexport type MessageEventGroupProps = {\n /** What the closed group stands for, for example \"4 changes to Invitation and Light test\". */\n label: string;\n expanded: boolean;\n onToggle: () => void;\n /** Visible time or range, formatted by the caller, for example \"09:50–09:58\". */\n time?: string;\n /** The first event's time. */\n dateTime?: string | Date;\n class?: string;\n};\n\nexport type MessageGroupEntry = {\n /** Stable identity of the author. */\n author: string;\n at: Date | number | string;\n /** System rows never belong to a group. */\n system?: boolean;\n};\n\nconst timestamp = (value: Date | number | string) => (value instanceof Date ? value.getTime() : new Date(value).getTime());\n\n/**\n * Whether a message starts a new group: there is no previous message, either is a system row, the author changes, or\n * more than five minutes passed. Also start a group where you show a separator such as a new day.\n */\nexport const startsMessageGroup = (entry: MessageGroupEntry, previous?: MessageGroupEntry | null): boolean => {\n if (!previous || entry.system || previous.system || entry.author !== previous.author) return true;\n const gap = timestamp(entry.at) - timestamp(previous.at);\n return !(gap >= 0 && gap <= GROUP_WINDOW_MS);\n};\n\n/** Text that wraps: every `LINE_CHARS` visible characters of a line take at least one line. */\nconst wrappedLines = (text: string): number =>\n text.split(\"\\n\").reduce((lines, line) => lines + Math.ceil(line.trim().length / LINE_CHARS), 0);\n\n/** What inline tokens show: a link its label, never its destination. */\nconst inlineText = (tokens: readonly Token[]): string => {\n let text = \"\";\n for (const token of tokens) {\n if (token.type === \"br\") text += \"\\n\";\n else if (\"tokens\" in token && token.tokens) text += inlineText(token.tokens);\n else if (\"text\" in token) text += token.text;\n }\n return text;\n};\n\n/**\n * The fewest lines the rendered tokens take at any width. Code never wraps, so each of its lines is one line, blank\n * ones too, and its bar one more. Link destinations and reference definitions show nothing. Decided from the text\n * alone, so the row has its final height when it mounts.\n */\nconst renderedLines = (tokens: readonly Token[]): number => {\n let lines = 0;\n for (const token of tokens) {\n switch (token.type) {\n case \"code\":\n lines += token.text.split(\"\\n\").length + 1;\n break;\n case \"list\":\n for (const item of token.items) lines += Math.max(1, renderedLines(item.tokens));\n break;\n case \"table\":\n lines += token.rows.length + (token.header.some((cell: Tokens.TableCell) => cell.text.trim()) ? 1 : 0);\n break;\n case \"paragraph\":\n case \"heading\":\n case \"text\":\n lines += wrappedLines(token.tokens ? inlineText(token.tokens) : token.text);\n break;\n case \"hr\":\n lines += 1;\n break;\n case \"space\":\n case \"def\":\n break;\n default:\n if (\"tokens\" in token && token.tokens) lines += renderedLines(token.tokens);\n else if (\"text\" in token) lines += wrappedLines(token.text);\n }\n }\n return lines;\n};\n\n/** Messages link only to absolute URLs; a relative one would lead somewhere else on every page that shows it. */\nconst isAbsoluteUrl = (href: string): boolean => {\n try {\n return new URL(href).protocol !== \"\";\n } catch {\n return false;\n }\n};\n\nconst dateTimeOf = (value: string | Date | undefined): string | undefined => {\n if (value === undefined) return undefined;\n const date = value instanceof Date ? value : new Date(value);\n return Number.isFinite(date.getTime()) ? date.toISOString() : undefined;\n};\n\n/**\n * An attachment URL the row may load or open: absolute with an allowed scheme, or relative to the page. A picture may\n * also be a `blob:` URL or a `data:image/` URL, which an image element shows without running anything.\n */\nconst attachmentUrl = (url: string | undefined, picture = false): string | undefined => {\n if (!url) return undefined;\n if (picture && /^data:image\\//i.test(url)) return url;\n try {\n return (picture ? PICTURE_PROTOCOLS : ATTACHMENT_PROTOCOLS).has(new URL(url, \"https://relative.invalid/\").protocol) ? url : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The aspect ratio a single image or video reserves, from its stored size. */\nconst mediaRatio = (media: MessageRowMedia): number => {\n const ratio = media.width > 0 && media.height > 0 ? media.width / media.height : 4 / 3;\n return Math.min(MEDIA_RATIO_MAX, Math.max(MEDIA_RATIO_MIN, ratio));\n};\n\n/** The width of a single image or video: its stored width, at most the row's media height tall, never wider than the row. */\nconst mediaWidth = (media: MessageRowMedia): string => {\n const natural = Number.isFinite(media.width) && media.width > 0 ? `${media.width}px, ` : \"\";\n return `min(100%, ${natural}calc(var(--k2b-message-media-height) * ${mediaRatio(media)}))`;\n};\n\n/** Only the `ti-*` glyph of `fileIcons`, which may add colour utilities that `@k2b/ui` does not ship. */\nconst fileIcon = (file: MessageRowFile): string =>\n file.icon ??\n `ti ${fileIcons\n .getFileIcon({ name: file.name, type: \"file\", mimeType: file.mediaType })\n .split(/\\s+/)\n .filter((token) => token.startsWith(\"ti-\"))\n .join(\" \")}`;\n\n/** A link, a button, or neither, depending on what the caller passes. */\nfunction AttachmentTarget(\n props: MessageRowAttachmentTarget & {\n class: string;\n style?: JSX.CSSProperties;\n /** Names a picture; without a link or button around it, the element becomes the image. */\n label?: string;\n children: JSX.Element;\n },\n): JSX.Element {\n return (\n <Show\n when={props.onOpen}\n fallback={\n <Show\n when={attachmentUrl(props.href)}\n fallback={\n <Show\n when={props.label}\n fallback={\n <span class={props.class} style={props.style}>\n {props.children}\n </span>\n }\n >\n {(label) => (\n <span class={props.class} style={props.style} role=\"img\" aria-label={label()}>\n {props.children}\n </span>\n )}\n </Show>\n }\n >\n {(href) => (\n <a class={props.class} style={props.style} href={href()} target=\"_blank\" rel=\"noopener noreferrer\" aria-label={props.label}>\n {props.children}\n </a>\n )}\n </Show>\n }\n >\n {(open) => (\n <button type=\"button\" class={props.class} style={props.style} aria-label={props.label} onClick={() => open()()}>\n {props.children}\n </button>\n )}\n </Show>\n );\n}\n\n/**\n * Renders the safe Markdown of `MarkdownView` with line breaks kept, absolute links only, and a copy control above\n * every code block, and counts the fewest lines it takes. An edited message that does not collapse ends with the marker.\n */\nconst renderMessage = (\n text: string,\n locale: string,\n messages: UiMessages,\n chatMessages: ChatMessages,\n edited: boolean,\n): { html: string; lines: number } => {\n const renderer = createSafeRenderer({ allowImages: false, linkProtocols: LINK_PROTOCOLS, linkTarget: \"_blank\", locale });\n const renderLink = renderer.link.bind(renderer);\n renderer.link = (token: Tokens.Link) => (isAbsoluteUrl(token.href) ? renderLink(token) : renderer.parser.parseInline(token.tokens));\n const renderCode = renderer.code.bind(renderer);\n renderer.code = (token: Tokens.Code) => {\n const language = token.lang?.match(/^\\S{1,24}/)?.[0];\n const block = renderCode(token).replace(/^<pre>/, '<pre tabindex=\"0\">');\n return (\n '<div class=\"k2b-message-row__code\" data-gesture-ignore><div class=\"k2b-message-row__code-bar\">' +\n `<span class=\"k2b-message-row__code-label\">${escapeHtml(language ?? messages.code)}</span>` +\n '<button type=\"button\" class=\"k2b-message-row__copy\">' +\n `<span class=\"k2b-message-row__copy-idle\"><i class=\"ti ti-copy\" aria-hidden=\"true\"></i>${escapeHtml(messages.copy)}</span>` +\n `<span class=\"k2b-message-row__copy-done\"><i class=\"ti ti-check\" aria-hidden=\"true\"></i>${escapeHtml(messages.copied)}</span>` +\n `</button></div>${block}</div>`\n );\n };\n const marked = createSafeMarked(locale).setOptions({ breaks: true, renderer });\n const tokens = marked.lexer(text);\n const html = marked.parser(tokens);\n const lines = renderedLines(tokens);\n // A collapsed message would hide the end of its text, so its marker sits next to \"Show more\" instead.\n if (!edited || lines > COLLAPSE_LINES) return { html, lines };\n // The marker ends the last paragraph, so it takes no line of its own there.\n const marker = `<span class=\"k2b-message-row__edited\">${escapeHtml(chatMessages.messageEdited)}</span>`;\n const end = html.trimEnd();\n return { html: end.endsWith(\"</p>\") ? `${end.slice(0, -4)} ${marker}</p>` : `${html}<p>${marker}</p>`, lines };\n};\n\n/**\n * One message in a conversation with many people: own messages on the right, others on the left with avatar, name,\n * and time at the start of a group. Every part has its final height when the row mounts, so a virtualized list such\n * as `VirtualFeed` keeps its positions.\n */\nexport function MessageRow(props: MessageRowProps): JSX.Element {\n const messages = useUiMessages();\n const chatMessages = useChatMessages();\n const locale = useLocale();\n const textId = `k2b-message-${createUniqueId()}`;\n const [expanded, setExpanded] = createSignal(false);\n const rendered = createMemo(() => renderMessage(props.text, locale(), messages(), chatMessages(), Boolean(props.edited)));\n const collapsible = () => rendered().lines > COLLAPSE_LINES;\n const collapsed = () => collapsible() && !expanded();\n const groupStart = () => props.groupStart ?? true;\n const hasLine = () => props.progress !== undefined || props.status !== undefined || props.receipt !== undefined;\n const reactions = () => (props.deleted ? undefined : props.reactions);\n const hasFooter = () => hasLine() || reactions() !== undefined;\n const writing = () => props.progress !== undefined && !props.deleted && props.text.trim() === \"\";\n const hasBubble = () => props.deleted || writing() || props.text.trim() !== \"\";\n const content = () => !props.deleted;\n const media = createMemo(() =>\n content() ? (props.attachments ?? []).filter((attachment): attachment is MessageRowMedia => attachment.kind !== \"file\") : [],\n );\n const files = createMemo(() =>\n content() ? (props.attachments ?? []).filter((attachment): attachment is MessageRowFile => attachment.kind === \"file\") : [],\n );\n const shownMedia = () => media().slice(0, MEDIA_SHOWN);\n const hiddenMedia = () => media().length - shownMedia().length;\n let text: HTMLDivElement | undefined;\n let copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\n onCleanup(() => clearTimeout(copiedTimer));\n\n const copyCode = (event: MouseEvent) => {\n const button = (event.target as Element | null)?.closest?.<HTMLButtonElement>(\".k2b-message-row__copy\");\n if (!button || !text?.contains(button)) return;\n const code = button.closest(\".k2b-message-row__code\")?.querySelector(\"pre\")?.textContent ?? \"\";\n copyText(code).then(\n () => {\n for (const other of text?.querySelectorAll<HTMLElement>(\".k2b-message-row__copy[data-copied]\") ?? []) delete other.dataset.copied;\n button.dataset.copied = \"\";\n announce(messages().copied);\n clearTimeout(copiedTimer);\n copiedTimer = setTimeout(() => delete button.dataset.copied, COPIED_MS);\n },\n () => {},\n );\n };\n\n // A keyboard user who reaches a link or code block in the hidden or faded part of a collapsed message gets the whole\n // message. The collapsed text clips instead of scrolling, so the browser cannot scroll it to the target instead.\n const revealFocus = (event: FocusEvent) => {\n if (!text || !collapsed() || !(event.target instanceof Element)) return;\n const fade = text.getBoundingClientRect().bottom - FADE_LINES * Number.parseFloat(getComputedStyle(text).lineHeight);\n if (event.target.getBoundingClientRect().bottom > fade) setExpanded(true);\n };\n\n // The same entries serve \"…\", right-click, the keyboard, and a long press. Alt+click and keys show at the entry's end.\n // Each menu gets its own entries: a rendered shortcut is a DOM node and can live in only one menu.\n const entries = () =>\n (props.actions ?? []).map((entry): GestureMenuAction & Pick<Extract<DropdownAction, { action: () => void }>, \"immediate\"> => ({\n label: entry.label,\n icon: entry.icon,\n description: entry.description,\n disabled: entry.disabled,\n variant: entry.variant,\n gesture: entry.gesture,\n immediate: entry.immediate,\n trailing: entry.altClick ? <Kbd keys=\"alt+click\" /> : entry.shortcut ? <Kbd keys={entry.shortcut} /> : undefined,\n action: () => entry.onSelect(),\n }));\n const menuItems = createMemo(entries);\n const gestureItems = createMemo(entries);\n const hasMenu = () => (props.actions?.length ?? 0) > 0;\n\n // Alt+click (Option+click) on the message, not on a link or control inside it, runs the entry that asks for it.\n const altClick = (event: MouseEvent) => {\n if (!event.altKey || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey) return;\n const entry = props.actions?.find((action) => action.altClick && !action.disabled);\n const target = event.target instanceof Element ? event.target : null;\n if (!entry || target?.closest(ALT_CLICK_IGNORE)) return;\n event.preventDefault();\n entry.onSelect();\n };\n\n const more = () => (\n <Dropdown.Root\n items={menuItems()}\n label={chatMessages().messageActions}\n class=\"k2b-message-row__menu-root\"\n position={props.own ? \"bottom-right\" : \"bottom-left\"}\n phone=\"sheet\"\n >\n <Dropdown.Trigger iconOnly variant=\"ghost\" size=\"sm\" label={chatMessages().messageActions} class=\"k2b-message-row__menu\">\n <i class=\"ti ti-dots\" aria-hidden=\"true\" />\n </Dropdown.Trigger>\n </Dropdown.Root>\n );\n\n const header = () => (\n <>\n <span class=\"k2b-message-row__name\">{props.author.name}</span>\n <Show when={props.badge}>\n <span class=\"k2b-message-row__badge\">{props.badge}</span>\n </Show>\n <time class=\"k2b-message-row__time\" datetime={dateTimeOf(props.dateTime)}>\n {props.time}\n </time>\n </>\n );\n\n const quote = (value: MessageRowQuote) => (\n <>\n <span class=\"k2b-message-row__quote-author\">\n <span class=\"k2b-sr-only\">{chatMessages().messageReplyTo} </span>\n {value.author}\n </span>\n <span class=\"k2b-message-row__quote-text\">{value.text}</span>\n </>\n );\n\n /** The answered message on a quiet tint at the top of the bubble, or in its place without one. */\n const quoteBlock = () => (\n <Show when={content() && props.quote}>\n {(value) => (\n <Show when={value().onSelect} fallback={<div class=\"k2b-message-row__quote\">{quote(value())}</div>}>\n {(select) => (\n <button type=\"button\" class=\"k2b-message-row__quote\" onClick={() => select()()}>\n {quote(value())}\n </button>\n )}\n </Show>\n )}\n </Show>\n );\n\n /** The picture's description, then that it is a video and how long, then how many more the grid hides. */\n const mediaLabel = (item: MessageRowMedia, more: number) =>\n [\n item.alt,\n item.kind === \"video\" ? [chatMessages().messageVideo, item.duration].filter(Boolean).join(\" \") : \"\",\n more > 0 ? chatMessages().messageMoreAttachments({ count: more }) : \"\",\n ]\n .filter(Boolean)\n .join(\", \");\n\n const mediaItem = (item: MessageRowMedia, index: () => number) => {\n const more = () => (index() === shownMedia().length - 1 ? hiddenMedia() : 0);\n return (\n <AttachmentTarget\n class=\"k2b-message-row__media-item\"\n style={media().length === 1 ? { \"aspect-ratio\": String(mediaRatio(item)) } : undefined}\n label={mediaLabel(item, more())}\n onOpen={item.onOpen}\n href={item.href}\n >\n <img\n src={attachmentUrl(item.src, true)}\n alt=\"\"\n width={item.width > 0 ? item.width : undefined}\n height={item.height > 0 ? item.height : undefined}\n loading=\"lazy\"\n decoding=\"async\"\n />\n <Show when={item.kind === \"video\"}>\n <span class=\"k2b-message-row__media-play\" aria-hidden=\"true\">\n <i class=\"ti ti-player-play\" />\n </span>\n <Show when={item.duration}>\n <span class=\"k2b-message-row__media-duration\" aria-hidden=\"true\">\n {item.duration}\n </span>\n </Show>\n </Show>\n <Show when={more() > 0}>\n <span class=\"k2b-message-row__media-more\" aria-hidden=\"true\">\n +{more()}\n </span>\n </Show>\n </AttachmentTarget>\n );\n };\n\n const row = (\n // biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: Alt+click is a pointer shortcut for a menu entry that the keyboard reaches through \"…\".\n <div\n class={props.class ? `k2b-message-row ${props.class}` : \"k2b-message-row\"}\n onClick={altClick}\n data-own={props.own ? \"\" : undefined}\n data-group-start={groupStart() ? \"\" : undefined}\n data-status={props.status}\n data-media={media().length > 0 ? \"\" : undefined}\n >\n <Show when={!props.own}>\n <div class=\"k2b-message-row__gutter\">\n <Show when={groupStart()}>\n <span class=\"k2b-message-row__avatar\" aria-hidden=\"true\">\n <Avatar name={props.author.name} src={props.author.avatar} icon={props.author.icon} size=\"sm\" />\n </span>\n </Show>\n </div>\n </Show>\n <div class=\"k2b-message-row__body\">\n <Show\n when={groupStart()}\n fallback={\n <span class=\"k2b-sr-only\">\n {props.author.name}, {props.time}:{\" \"}\n </span>\n }\n >\n <Show\n when={!props.own}\n fallback={\n <div class=\"k2b-message-row__meta\">\n <span class=\"k2b-sr-only\">{props.author.name}, </span>\n <time class=\"k2b-message-row__time\" datetime={dateTimeOf(props.dateTime)}>\n {props.time}\n </time>\n </div>\n }\n >\n <div class=\"k2b-message-row__meta\">{header()}</div>\n </Show>\n </Show>\n <Show when={content() && props.forwarded}>\n <div class=\"k2b-message-row__marker\">\n <i class=\"ti ti-arrow-forward-up\" aria-hidden=\"true\" />\n <span>{chatMessages().messageForwarded}</span>\n </div>\n </Show>\n <Show when={!hasBubble()}>{quoteBlock()}</Show>\n <Show when={hasBubble()}>\n <div\n class=\"k2b-message-row__bubble\"\n data-deleted={props.deleted ? \"\" : undefined}\n aria-busy={props.progress && !props.deleted ? \"true\" : undefined}\n >\n {quoteBlock()}\n <Show\n when={!props.deleted}\n fallback={\n <span class=\"k2b-message-row__deleted\">\n <i class=\"ti ti-message-off\" aria-hidden=\"true\" />\n {chatMessages().messageDeleted}\n </span>\n }\n >\n <Show\n when={!writing()}\n fallback={\n <span class=\"k2b-message-row__writing\">\n <span class=\"k2b-chat-progress-dots\" aria-hidden=\"true\">\n <span />\n <span />\n <span />\n </span>\n </span>\n }\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: the native copy buttons inside the rendered Markdown are the targets; their clicks, including from the keyboard, bubble here. */}\n <div\n ref={text}\n id={textId}\n class=\"k2b-content-markdown k2b-message-row__text\"\n data-heading-scale=\"compact\"\n data-collapsed={collapsed() ? \"\" : undefined}\n onClick={copyCode}\n onFocusIn={revealFocus}\n innerHTML={rendered().html}\n />\n <Show when={collapsible()}>\n <div class=\"k2b-message-row__more-line\">\n <Button\n variant=\"text\"\n size=\"xs\"\n class=\"k2b-message-row__more\"\n aria-expanded={expanded() ? \"true\" : \"false\"}\n aria-controls={textId}\n onClick={() => setExpanded((value) => !value)}\n >\n {expanded() ? chatMessages().messageShowLess : chatMessages().messageShowMore}\n </Button>\n <Show when={props.edited}>\n <span class=\"k2b-message-row__edited\">{chatMessages().messageEdited}</span>\n </Show>\n </div>\n </Show>\n </Show>\n </Show>\n <Show when={hasMenu()}>{more()}</Show>\n </div>\n </Show>\n <Show when={hasMenu() && !hasBubble()}>{more()}</Show>\n {/* Without a bubble to end, the edit marker takes the bubble's place. */}\n <Show when={content() && props.edited && !hasBubble()}>\n <div class=\"k2b-message-row__marker\">\n <i class=\"ti ti-pencil\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__edited\">{chatMessages().messageEdited}</span>\n </div>\n </Show>\n <Show when={media().length > 0}>\n <div\n class=\"k2b-message-row__media\"\n role=\"group\"\n aria-label={chatMessages().attachments}\n data-count={Math.min(media().length, MEDIA_SHOWN)}\n style={media().length === 1 ? { width: mediaWidth(media()[0]!) } : undefined}\n >\n <For each={shownMedia()}>{(item, index) => mediaItem(item, index)}</For>\n </div>\n </Show>\n <Show when={files().length > 0}>\n <div class=\"k2b-message-row__files\">\n <For each={files()}>\n {(file) => (\n <AttachmentTarget class=\"k2b-message-row__file\" onOpen={file.onOpen} href={file.href}>\n <span class=\"k2b-message-row__file-icon\" aria-hidden=\"true\">\n <i class={fileIcon(file)} />\n </span>\n <span class=\"k2b-message-row__file-copy\">\n <span class=\"k2b-message-row__file-name\">{file.name}</span>\n <Show when={file.detail}>\n <span class=\"k2b-message-row__file-detail\">{file.detail}</span>\n </Show>\n </span>\n </AttachmentTarget>\n )}\n </For>\n </div>\n </Show>\n <Show when={content() && props.linkPreview}>\n <div class=\"k2b-message-row__slot\" data-gesture-ignore>\n {props.linkPreview}\n </div>\n </Show>\n <Show when={content() && props.card}>\n <div class=\"k2b-message-row__slot\" data-gesture-ignore>\n {props.card}\n </div>\n </Show>\n <Show when={props.thread}>\n {(thread) => (\n <button type=\"button\" class=\"k2b-message-row__thread\" onClick={() => thread().onOpen()}>\n <Show when={(thread().participants?.length ?? 0) > 0}>\n <span class=\"k2b-message-row__thread-people\" aria-hidden=\"true\">\n <For each={thread().participants?.slice(0, 3)}>\n {(person) => <Avatar name={person.name} src={person.avatar} icon={person.icon} size=\"xs\" />}\n </For>\n </span>\n </Show>\n <span class=\"k2b-message-row__thread-count\">{chatMessages().messageReplies({ count: thread().count })}</span>\n <Show when={thread().lastReply}>\n {(time) => (\n <time class=\"k2b-message-row__thread-time\" datetime={dateTimeOf(thread().lastReplyDateTime)}>\n {chatMessages().messageLastReply({ time: time() })}\n </time>\n )}\n </Show>\n </button>\n )}\n </Show>\n <Show when={props.origin}>{(origin) => <OriginChip origin={origin()} />}</Show>\n <Show when={hasFooter()}>\n <div class=\"k2b-message-row__footer\">\n <Show when={reactions()}>\n {(list) => <ReactionBar reactions={list()} onToggle={props.onToggleReaction} onAdd={props.onAddReaction} />}\n </Show>\n <Show when={hasLine()}>\n <div class=\"k2b-message-row__line\">\n <Show\n when={props.progress}\n fallback={\n <>\n <Show when={props.status === \"pending\"}>\n <i class=\"ti ti-clock\" aria-hidden=\"true\" />\n <span>{chatMessages().sending}</span>\n </Show>\n <Show when={props.status === \"failed\"}>\n <i class=\"ti ti-alert-circle\" aria-hidden=\"true\" />\n <span>{chatMessages().messageNotSent}</span>\n <Show when={props.onRetry}>\n {(retry) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => retry()()}>\n {chatMessages().messageRetry}\n </Button>\n )}\n </Show>\n <Show when={props.onDiscard}>\n {(discard) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => discard()()}>\n {chatMessages().messageDiscard}\n </Button>\n )}\n </Show>\n </Show>\n <Show when={props.status !== \"pending\" && props.status !== \"failed\"}>\n <Show\n when={props.receipt}\n fallback={\n <Show when={props.status === \"sent\"}>\n <i class=\"ti ti-check\" aria-hidden=\"true\" />\n <span class=\"k2b-sr-only\">{chatMessages().messageSent}</span>\n </Show>\n }\n >\n {(receipt) => (\n <>\n <i class=\"ti ti-checks\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__receipt\">{receipt()}</span>\n </>\n )}\n </Show>\n </Show>\n </>\n }\n >\n {(progress) => (\n <>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__line-text\">{progress().status}</span>\n <Show when={progress().onStop}>\n {(stop) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => stop()()}>\n {chatMessages().messageStop}\n </Button>\n )}\n </Show>\n </>\n )}\n </Show>\n </div>\n </Show>\n </div>\n </Show>\n </div>\n </div>\n );\n\n return (\n <Show when={hasMenu()} fallback={row}>\n {/* Out of the tab order: the keyboard reaches the same menu through \"…\", so a message adds no extra stop. */}\n <GestureMenu items={gestureItems()} label={chatMessages().messageMenu({ author: props.author.name, time: props.time })} tabIndex={-1}>\n {row}\n </GestureMenu>\n </Show>\n );\n}\n\n/** Where a row belongs, such as its thread; a control when it opens that place. */\nfunction OriginChip(props: { origin: MessageRowOrigin }): JSX.Element {\n return (\n <Show\n when={props.origin.onSelect}\n fallback={\n <span class=\"k2b-message-row__origin\">\n <Show when={props.origin.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.origin.label}\n </span>\n }\n >\n {(select) => (\n <button type=\"button\" class=\"k2b-message-row__origin\" aria-label={props.origin.selectLabel} onClick={() => select()()}>\n <Show when={props.origin.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.origin.label}\n </button>\n )}\n </Show>\n );\n}\n\n/**\n * A quiet line for a change to something the conversation is about, such as a task's new status: no bubble, aligned\n * with the messages' text, with its time at the end. It is no message: it never counts as unread.\n */\nexport function MessageEventRow(props: MessageEventRowProps): JSX.Element {\n return (\n <div class={props.class ? `k2b-message-event-row ${props.class}` : \"k2b-message-event-row\"}>\n <i class={props.icon ?? \"ti ti-point\"} aria-hidden=\"true\" />\n <span class=\"k2b-message-event-row__text\">{props.children}</span>\n <Show when={props.origin}>{(origin) => <OriginChip origin={origin()} />}</Show>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n </div>\n );\n}\n\n/** Several event lines in a row as one line that opens them, such as \"4 changes to Invitation and Light test\". */\nexport function MessageEventGroup(props: MessageEventGroupProps): JSX.Element {\n return (\n <button\n type=\"button\"\n class={props.class ? `k2b-message-event-row k2b-message-event-group ${props.class}` : \"k2b-message-event-row k2b-message-event-group\"}\n aria-expanded={props.expanded}\n onClick={() => props.onToggle()}\n >\n <i class=\"ti ti-history\" aria-hidden=\"true\" />\n <span class=\"k2b-message-event-row__text\">{props.label}</span>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n <i class={props.expanded ? \"ti ti-chevron-up\" : \"ti ti-chevron-down\"} aria-hidden=\"true\" />\n </button>\n );\n}\n\n/** A quiet, centered line for something that happened in the conversation, such as a person joining. */\nexport function MessageSystemRow(props: MessageSystemRowProps): JSX.Element {\n return (\n <div class={props.class ? `k2b-message-system-row ${props.class}` : \"k2b-message-system-row\"}>\n <Show when={props.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n <span>{props.children}</span>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/MessageRow.tsx"],"sourcesContent":["import { fileIcons } from \"@k2b/stdlib\";\nimport type { Token, Tokens } from \"marked\";\nimport { createMemo, createSignal, createUniqueId, For, type JSX, onCleanup, Show } from \"solid-js\";\nimport { Button } from \"../actions/Button\";\nimport { copyText } from \"../actions/CopyButton\";\nimport { Dropdown, type DropdownAction } from \"../actions/Dropdown\";\nimport { type GestureKind, GestureMenu, type GestureMenuAction } from \"../actions/GestureMenu\";\nimport { type ChatMessages, useChatMessages } from \"../chat/chat-messages\";\nimport { announce } from \"../feedback/announce\";\nimport { useLocale } from \"../intl/locale\";\nimport { type UiMessages, useUiMessages } from \"../intl/messages\";\nimport { Avatar } from \"../surfaces/Avatar\";\nimport { Kbd } from \"./Kbd\";\nimport { createSafeMarked, createSafeRenderer, escapeHtml } from \"./MarkdownView\";\nimport { ReactionBar, type ReactionBarItem } from \"./ReactionBar\";\n\n/** Messages by the same author within this time continue a group. */\nconst GROUP_WINDOW_MS = 5 * 60_000;\n/**\n * A message collapses to ten lines (`.k2b-message-row__text[data-collapsed]`) when its rendered text takes more lines\n * than this. `renderedLines` counts the fewest lines the text takes at any width, so a collapsed message always hides\n * some of it.\n */\nconst COLLAPSE_LINES = 14;\n/** Visible characters that take at least one line of a message, even at its widest. */\nconst LINE_CHARS = 100;\n/** Lines at the bottom of a collapsed message that fade out, as `.k2b-message-row__text[data-collapsed]` draws them. */\nconst FADE_LINES = 2;\nconst COPIED_MS = 2_000;\n/** Alt+click on these keeps its own meaning, such as downloading a link. */\nconst ALT_CLICK_IGNORE = \"a[href], button, input, textarea, select, summary, label, audio, video, [role='button'], [role='link']\";\n/** Links in messages may only use these schemes; other links show as their text. */\nconst LINK_PROTOCOLS = [\"https:\", \"http:\", \"mailto:\"] as const;\n/** Attachments open and load only from these schemes or from a relative URL. */\nconst ATTACHMENT_PROTOCOLS = new Set([\"https:\", \"http:\"]);\n/**\n * A picture may also show a file that is still uploading. A `blob:` URL runs in the page's origin when it is opened,\n * so links never use one.\n */\nconst PICTURE_PROTOCOLS = new Set([...ATTACHMENT_PROTOCOLS, \"blob:\"]);\n/** A grid shows this many images or videos; the last one tells how many more there are. */\nconst MEDIA_SHOWN = 4;\n/** A single image keeps its own aspect ratio within these bounds; beyond them it is cropped to fit. */\nconst MEDIA_RATIO_MIN = 0.5;\nconst MEDIA_RATIO_MAX = 3;\n\nexport type MessageRowAuthor = {\n /** Display name, shown at the start of a group and read before every message. */\n name: string;\n /** Picture URL. Without one, the avatar shows initials on the author's tint. */\n avatar?: string | null;\n /** Icon class shown instead of initials, for example for an automated author. */\n icon?: string;\n};\n\nexport type MessageRowStatus = \"pending\" | \"sent\" | \"failed\";\n\nexport type MessageRowAction = {\n id: string;\n /** Visible name of the menu entry. */\n label: string;\n /** Icon class, for example `ti ti-arrow-back-up`. */\n icon: string;\n /** One quiet sentence below the label that says what the entry does. */\n description?: string;\n onSelect: () => void;\n disabled?: boolean;\n variant?: \"danger\";\n /** A touch gesture that runs the entry too. Give each gesture to at most one entry. */\n gesture?: GestureKind;\n /**\n * Alt+click (Option+click on Apple devices) on the message runs the entry directly, and the entry shows that\n * shortcut. Give it to at most one entry.\n */\n altClick?: boolean;\n /** A key the application handles for the focused message, such as \"r\"; the entry shows it at its end. */\n shortcut?: string;\n /** In the phone sheet, runs while the tap still counts as the person's gesture, as clipboard writes require. */\n immediate?: boolean;\n};\n\nexport type MessageRowQuote = {\n /** Name of the quoted message's author. */\n author: string;\n /** Plain text of the quoted message. It shows at most two lines. */\n text: string;\n /** Opens the quoted message, for example by scrolling to it. Without it, the quote is not a control. */\n onSelect?: () => void;\n};\n\n/** Reaction labels are read by screen readers and shown in the names hint. */\nexport type MessageRowReaction = ReactionBarItem;\n\nexport type MessageRowThread = {\n /** Number of replies. */\n count: number;\n /** People who replied, most recent first; the bar shows up to three. */\n participants?: readonly MessageRowAuthor[];\n /** Visible time of the last reply, formatted by the caller. */\n lastReply?: string;\n lastReplyDateTime?: string | Date;\n onOpen: () => void;\n};\n\ntype MessageRowAttachmentTarget = {\n /** Opens the attachment, for example in a lightbox. Takes precedence over `href`. */\n onOpen?: () => void;\n /** Opens the attachment in a new tab. Absolute `https` or `http`, or relative. */\n href?: string;\n};\n\nexport type MessageRowMedia = MessageRowAttachmentTarget & {\n kind: \"image\" | \"video\";\n /** The image, or the poster frame of a video. Absolute `https` or `http`, relative, `blob`, or `data:image/`. */\n src: string;\n /** Describes the image or video for people who cannot see it. */\n alt: string;\n /** Stored pixel size. The row reserves the area from it, so nothing moves when the image loads. */\n width: number;\n height: number;\n /** Visible length of a video, for example \"0:42\". */\n duration?: string;\n};\n\nexport type MessageRowFile = MessageRowAttachmentTarget & {\n kind: \"file\";\n name: string;\n /** Second line, for example \"PDF · 412 KB\". */\n detail?: string;\n /** Media type, used with the name to choose the icon. */\n mediaType?: string;\n /** Icon class instead of the one chosen from the name. */\n icon?: string;\n};\n\nexport type MessageRowAttachment = MessageRowMedia | MessageRowFile;\n\n/** Where a message belongs, shown as a small chip under it, for example its thread in a timeline of several. */\nexport type MessageRowOrigin = {\n label: string;\n /** Icon class, for example `ti ti-hash`. */\n icon?: string;\n /** Opens where the message belongs. Without it, the chip is not a control. */\n onSelect?: () => void;\n /** Accessible name of the control when it must say more than the label, such as \"Open thread Updates\". */\n selectLabel?: string;\n};\n\nexport type MessageRowProgress = {\n /** What is happening, for example \"Writing\" or \"Searching the files\". */\n status: string;\n /** Called by \"Stop\". */\n onStop?: () => void;\n};\n\nexport type MessageRowProps = {\n author: MessageRowAuthor;\n /** Message text as Markdown. Raw HTML shows as text; links must be absolute and use `https`, `http`, or `mailto`. */\n text: string;\n /** Visible time, formatted by the caller, so server and browser render the same text. */\n time: string;\n /** Machine-readable time for the `<time>` element. */\n dateTime?: string | Date;\n /** The reader's own message: on the right in the accent tint, without avatar or name. */\n own?: boolean;\n /**\n * Starts a group with avatar, name, badge, and time. A continuation shows only its text; screen readers still hear\n * the author and time. Defaults to true; `startsMessageGroup` decides it from the previous message.\n */\n groupStart?: boolean;\n /** Shown next to the name at the start of a group, for example the kind of account. */\n badge?: JSX.Element;\n /** Send state. Sets a line of fixed height below the message: a clock, a check, or \"Not sent\" with \"Retry\". */\n status?: MessageRowStatus;\n /** Text shown in that line instead of the check once someone has read the message, for example \"Read by Nora\". */\n receipt?: string;\n /** Called by \"Retry\" while `status` is `\"failed\"`. */\n onRetry?: () => void;\n /** Called by \"Discard\" next to \"Retry\" while `status` is `\"failed\"`, so both answers to a failure sit where it shows. */\n onDiscard?: () => void;\n /**\n * The message's menu. A quiet \"…\" beside the bubble opens it while the message is hovered or focused, also from the\n * keyboard; right-click and a long press open the same menu. Showing \"…\" never moves the layout.\n */\n actions?: readonly MessageRowAction[];\n /** The message this one answers, shown above it. */\n quote?: MessageRowQuote;\n /** Marks the message as forwarded from somewhere else. */\n forwarded?: boolean;\n /** Marks the message as edited after it was sent. */\n edited?: boolean;\n /** Shows \"This message was deleted\" instead of the text, quote, attachments, previews, and reactions. */\n deleted?: boolean;\n /** Images and videos in a grid with areas reserved from their stored size, then files as chips. */\n attachments?: readonly MessageRowAttachment[];\n /** A preview of a link in the text, supplied with its final height. */\n linkPreview?: JSX.Element;\n /** A card for an element the message refers to, such as a task or a document, supplied with its final height. */\n card?: JSX.Element;\n /**\n * Reactions in a bar of fixed height. The bar shows whenever this is set, even to an empty list, so chips that come\n * and go never change the row. Only setting it where it was unset adds a line, so pass it on own messages and on\n * messages that have or had reactions, and leave it out on others' messages that never had one.\n */\n reactions?: readonly MessageRowReaction[];\n /** Called with a reaction's `key` when the reader presses its chip. Without it, the chips are not controls. */\n onToggleReaction?: (key: string) => void;\n /** Called by \"Add reaction\" in the reaction bar, with the button to anchor a picker to. */\n onAddReaction?: (anchor: HTMLElement) => void;\n /** A bar of fixed height that opens the replies to this message. */\n thread?: MessageRowThread;\n /** A chip of fixed height under the message that names where it belongs. */\n origin?: MessageRowOrigin;\n /** The message is still being written, by a person or an automated author alike: a status line with \"Stop\". */\n progress?: MessageRowProgress;\n class?: string;\n};\n\nexport type MessageSystemRowProps = {\n /** What happened, for example \"Nora added Tobias\". */\n children: JSX.Element;\n /** Visible time, formatted by the caller. */\n time?: string;\n dateTime?: string | Date;\n /** Icon class shown before the text. */\n icon?: string;\n class?: string;\n};\n\nexport type MessageEventRowProps = {\n /** What changed, for example \"Mara set the status to “In review”\". */\n children: JSX.Element;\n /** Icon class of the kind of change; a dot by default. */\n icon?: string;\n /** Where the change belongs, such as its thread, where several threads mix. */\n origin?: MessageRowOrigin;\n /** Opens what the change is about, such as the commented passage; the sentence becomes the link. */\n href?: string;\n /** Visible time, formatted by the caller. */\n time?: string;\n dateTime?: string | Date;\n class?: string;\n};\n\nexport type MessageEventGroupProps = {\n /** What the closed group stands for, for example \"4 changes to Invitation and Light test\". */\n label: string;\n expanded: boolean;\n onToggle: () => void;\n /** Visible time or range, formatted by the caller, for example \"09:50–09:58\". */\n time?: string;\n /** The first event's time. */\n dateTime?: string | Date;\n class?: string;\n};\n\nexport type MessageGroupEntry = {\n /** Stable identity of the author. */\n author: string;\n at: Date | number | string;\n /** System rows never belong to a group. */\n system?: boolean;\n};\n\nconst timestamp = (value: Date | number | string) => (value instanceof Date ? value.getTime() : new Date(value).getTime());\n\n/**\n * Whether a message starts a new group: there is no previous message, either is a system row, the author changes, or\n * more than five minutes passed. Also start a group where you show a separator such as a new day.\n */\nexport const startsMessageGroup = (entry: MessageGroupEntry, previous?: MessageGroupEntry | null): boolean => {\n if (!previous || entry.system || previous.system || entry.author !== previous.author) return true;\n const gap = timestamp(entry.at) - timestamp(previous.at);\n return !(gap >= 0 && gap <= GROUP_WINDOW_MS);\n};\n\n/** Text that wraps: every `LINE_CHARS` visible characters of a line take at least one line. */\nconst wrappedLines = (text: string): number =>\n text.split(\"\\n\").reduce((lines, line) => lines + Math.ceil(line.trim().length / LINE_CHARS), 0);\n\n/** What inline tokens show: a link its label, never its destination. */\nconst inlineText = (tokens: readonly Token[]): string => {\n let text = \"\";\n for (const token of tokens) {\n if (token.type === \"br\") text += \"\\n\";\n else if (\"tokens\" in token && token.tokens) text += inlineText(token.tokens);\n else if (\"text\" in token) text += token.text;\n }\n return text;\n};\n\n/**\n * The fewest lines the rendered tokens take at any width. Code never wraps, so each of its lines is one line, blank\n * ones too, and its bar one more. Link destinations and reference definitions show nothing. Decided from the text\n * alone, so the row has its final height when it mounts.\n */\nconst renderedLines = (tokens: readonly Token[]): number => {\n let lines = 0;\n for (const token of tokens) {\n switch (token.type) {\n case \"code\":\n lines += token.text.split(\"\\n\").length + 1;\n break;\n case \"list\":\n for (const item of token.items) lines += Math.max(1, renderedLines(item.tokens));\n break;\n case \"table\":\n lines += token.rows.length + (token.header.some((cell: Tokens.TableCell) => cell.text.trim()) ? 1 : 0);\n break;\n case \"paragraph\":\n case \"heading\":\n case \"text\":\n lines += wrappedLines(token.tokens ? inlineText(token.tokens) : token.text);\n break;\n case \"hr\":\n lines += 1;\n break;\n case \"space\":\n case \"def\":\n break;\n default:\n if (\"tokens\" in token && token.tokens) lines += renderedLines(token.tokens);\n else if (\"text\" in token) lines += wrappedLines(token.text);\n }\n }\n return lines;\n};\n\n/** Messages link only to absolute URLs; a relative one would lead somewhere else on every page that shows it. */\nconst isAbsoluteUrl = (href: string): boolean => {\n try {\n return new URL(href).protocol !== \"\";\n } catch {\n return false;\n }\n};\n\nconst dateTimeOf = (value: string | Date | undefined): string | undefined => {\n if (value === undefined) return undefined;\n const date = value instanceof Date ? value : new Date(value);\n return Number.isFinite(date.getTime()) ? date.toISOString() : undefined;\n};\n\n/**\n * An attachment URL the row may load or open: absolute with an allowed scheme, or relative to the page. A picture may\n * also be a `blob:` URL or a `data:image/` URL, which an image element shows without running anything.\n */\nconst attachmentUrl = (url: string | undefined, picture = false): string | undefined => {\n if (!url) return undefined;\n if (picture && /^data:image\\//i.test(url)) return url;\n try {\n return (picture ? PICTURE_PROTOCOLS : ATTACHMENT_PROTOCOLS).has(new URL(url, \"https://relative.invalid/\").protocol) ? url : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The aspect ratio a single image or video reserves, from its stored size. */\nconst mediaRatio = (media: MessageRowMedia): number => {\n const ratio = media.width > 0 && media.height > 0 ? media.width / media.height : 4 / 3;\n return Math.min(MEDIA_RATIO_MAX, Math.max(MEDIA_RATIO_MIN, ratio));\n};\n\n/** The width of a single image or video: its stored width, at most the row's media height tall, never wider than the row. */\nconst mediaWidth = (media: MessageRowMedia): string => {\n const natural = Number.isFinite(media.width) && media.width > 0 ? `${media.width}px, ` : \"\";\n return `min(100%, ${natural}calc(var(--k2b-message-media-height) * ${mediaRatio(media)}))`;\n};\n\n/** Only the `ti-*` glyph of `fileIcons`, which may add colour utilities that `@k2b/ui` does not ship. */\nconst fileIcon = (file: MessageRowFile): string =>\n file.icon ??\n `ti ${fileIcons\n .getFileIcon({ name: file.name, type: \"file\", mimeType: file.mediaType })\n .split(/\\s+/)\n .filter((token) => token.startsWith(\"ti-\"))\n .join(\" \")}`;\n\n/** A link, a button, or neither, depending on what the caller passes. */\nfunction AttachmentTarget(\n props: MessageRowAttachmentTarget & {\n class: string;\n style?: JSX.CSSProperties;\n /** Names a picture; without a link or button around it, the element becomes the image. */\n label?: string;\n children: JSX.Element;\n },\n): JSX.Element {\n return (\n <Show\n when={props.onOpen}\n fallback={\n <Show\n when={attachmentUrl(props.href)}\n fallback={\n <Show\n when={props.label}\n fallback={\n <span class={props.class} style={props.style}>\n {props.children}\n </span>\n }\n >\n {(label) => (\n <span class={props.class} style={props.style} role=\"img\" aria-label={label()}>\n {props.children}\n </span>\n )}\n </Show>\n }\n >\n {(href) => (\n <a class={props.class} style={props.style} href={href()} target=\"_blank\" rel=\"noopener noreferrer\" aria-label={props.label}>\n {props.children}\n </a>\n )}\n </Show>\n }\n >\n {(open) => (\n <button type=\"button\" class={props.class} style={props.style} aria-label={props.label} onClick={() => open()()}>\n {props.children}\n </button>\n )}\n </Show>\n );\n}\n\n/**\n * Renders the safe Markdown of `MarkdownView` with line breaks kept, absolute links only, and a copy control above\n * every code block, and counts the fewest lines it takes. An edited message that does not collapse ends with the marker.\n */\nconst renderMessage = (\n text: string,\n locale: string,\n messages: UiMessages,\n chatMessages: ChatMessages,\n edited: boolean,\n): { html: string; lines: number } => {\n const renderer = createSafeRenderer({ allowImages: false, linkProtocols: LINK_PROTOCOLS, linkTarget: \"_blank\", locale });\n const renderLink = renderer.link.bind(renderer);\n renderer.link = (token: Tokens.Link) => (isAbsoluteUrl(token.href) ? renderLink(token) : renderer.parser.parseInline(token.tokens));\n const renderCode = renderer.code.bind(renderer);\n renderer.code = (token: Tokens.Code) => {\n const language = token.lang?.match(/^\\S{1,24}/)?.[0];\n const block = renderCode(token).replace(/^<pre>/, '<pre tabindex=\"0\">');\n return (\n '<div class=\"k2b-message-row__code\" data-gesture-ignore><div class=\"k2b-message-row__code-bar\">' +\n `<span class=\"k2b-message-row__code-label\">${escapeHtml(language ?? messages.code)}</span>` +\n '<button type=\"button\" class=\"k2b-message-row__copy\">' +\n `<span class=\"k2b-message-row__copy-idle\"><i class=\"ti ti-copy\" aria-hidden=\"true\"></i>${escapeHtml(messages.copy)}</span>` +\n `<span class=\"k2b-message-row__copy-done\"><i class=\"ti ti-check\" aria-hidden=\"true\"></i>${escapeHtml(messages.copied)}</span>` +\n `</button></div>${block}</div>`\n );\n };\n const marked = createSafeMarked(locale).setOptions({ breaks: true, renderer });\n const tokens = marked.lexer(text);\n const html = marked.parser(tokens);\n const lines = renderedLines(tokens);\n // A collapsed message would hide the end of its text, so its marker sits next to \"Show more\" instead.\n if (!edited || lines > COLLAPSE_LINES) return { html, lines };\n // The marker ends the last paragraph, so it takes no line of its own there.\n const marker = `<span class=\"k2b-message-row__edited\">${escapeHtml(chatMessages.messageEdited)}</span>`;\n const end = html.trimEnd();\n return { html: end.endsWith(\"</p>\") ? `${end.slice(0, -4)} ${marker}</p>` : `${html}<p>${marker}</p>`, lines };\n};\n\n/**\n * One message in a conversation with many people: own messages on the right, others on the left with avatar, name,\n * and time at the start of a group. Every part has its final height when the row mounts, so a virtualized list such\n * as `VirtualFeed` keeps its positions.\n */\nexport function MessageRow(props: MessageRowProps): JSX.Element {\n const messages = useUiMessages();\n const chatMessages = useChatMessages();\n const locale = useLocale();\n const textId = `k2b-message-${createUniqueId()}`;\n const [expanded, setExpanded] = createSignal(false);\n const rendered = createMemo(() => renderMessage(props.text, locale(), messages(), chatMessages(), Boolean(props.edited)));\n const collapsible = () => rendered().lines > COLLAPSE_LINES;\n const collapsed = () => collapsible() && !expanded();\n const groupStart = () => props.groupStart ?? true;\n const hasLine = () => props.progress !== undefined || props.status !== undefined || props.receipt !== undefined;\n const reactions = () => (props.deleted ? undefined : props.reactions);\n const hasFooter = () => hasLine() || reactions() !== undefined;\n const writing = () => props.progress !== undefined && !props.deleted && props.text.trim() === \"\";\n const hasBubble = () => props.deleted || writing() || props.text.trim() !== \"\";\n const content = () => !props.deleted;\n const media = createMemo(() =>\n content() ? (props.attachments ?? []).filter((attachment): attachment is MessageRowMedia => attachment.kind !== \"file\") : [],\n );\n const files = createMemo(() =>\n content() ? (props.attachments ?? []).filter((attachment): attachment is MessageRowFile => attachment.kind === \"file\") : [],\n );\n const shownMedia = () => media().slice(0, MEDIA_SHOWN);\n const hiddenMedia = () => media().length - shownMedia().length;\n let text: HTMLDivElement | undefined;\n let copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\n onCleanup(() => clearTimeout(copiedTimer));\n\n const copyCode = (event: MouseEvent) => {\n const button = (event.target as Element | null)?.closest?.<HTMLButtonElement>(\".k2b-message-row__copy\");\n if (!button || !text?.contains(button)) return;\n const code = button.closest(\".k2b-message-row__code\")?.querySelector(\"pre\")?.textContent ?? \"\";\n copyText(code).then(\n () => {\n for (const other of text?.querySelectorAll<HTMLElement>(\".k2b-message-row__copy[data-copied]\") ?? []) delete other.dataset.copied;\n button.dataset.copied = \"\";\n announce(messages().copied);\n clearTimeout(copiedTimer);\n copiedTimer = setTimeout(() => delete button.dataset.copied, COPIED_MS);\n },\n () => {},\n );\n };\n\n // A keyboard user who reaches a link or code block in the hidden or faded part of a collapsed message gets the whole\n // message. The collapsed text clips instead of scrolling, so the browser cannot scroll it to the target instead.\n const revealFocus = (event: FocusEvent) => {\n if (!text || !collapsed() || !(event.target instanceof Element)) return;\n const fade = text.getBoundingClientRect().bottom - FADE_LINES * Number.parseFloat(getComputedStyle(text).lineHeight);\n if (event.target.getBoundingClientRect().bottom > fade) setExpanded(true);\n };\n\n // The same entries serve \"…\", right-click, the keyboard, and a long press. Alt+click and keys show at the entry's end.\n // Each menu gets its own entries: a rendered shortcut is a DOM node and can live in only one menu.\n const entries = () =>\n (props.actions ?? []).map((entry): GestureMenuAction & Pick<Extract<DropdownAction, { action: () => void }>, \"immediate\"> => ({\n label: entry.label,\n icon: entry.icon,\n description: entry.description,\n disabled: entry.disabled,\n variant: entry.variant,\n gesture: entry.gesture,\n immediate: entry.immediate,\n trailing: entry.altClick ? <Kbd keys=\"alt+click\" /> : entry.shortcut ? <Kbd keys={entry.shortcut} /> : undefined,\n action: () => entry.onSelect(),\n }));\n const menuItems = createMemo(entries);\n const gestureItems = createMemo(entries);\n const hasMenu = () => (props.actions?.length ?? 0) > 0;\n\n // Alt+click (Option+click) on the message, not on a link or control inside it, runs the entry that asks for it.\n const altClick = (event: MouseEvent) => {\n if (!event.altKey || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey) return;\n const entry = props.actions?.find((action) => action.altClick && !action.disabled);\n const target = event.target instanceof Element ? event.target : null;\n if (!entry || target?.closest(ALT_CLICK_IGNORE)) return;\n event.preventDefault();\n entry.onSelect();\n };\n\n const more = () => (\n <Dropdown.Root\n items={menuItems()}\n label={chatMessages().messageActions}\n class=\"k2b-message-row__menu-root\"\n position={props.own ? \"bottom-right\" : \"bottom-left\"}\n phone=\"sheet\"\n >\n <Dropdown.Trigger iconOnly variant=\"ghost\" size=\"sm\" label={chatMessages().messageActions} class=\"k2b-message-row__menu\">\n <i class=\"ti ti-dots\" aria-hidden=\"true\" />\n </Dropdown.Trigger>\n </Dropdown.Root>\n );\n\n const header = () => (\n <>\n <span class=\"k2b-message-row__name\">{props.author.name}</span>\n <Show when={props.badge}>\n <span class=\"k2b-message-row__badge\">{props.badge}</span>\n </Show>\n <time class=\"k2b-message-row__time\" datetime={dateTimeOf(props.dateTime)}>\n {props.time}\n </time>\n </>\n );\n\n const quote = (value: MessageRowQuote) => (\n <>\n <span class=\"k2b-message-row__quote-author\">\n <span class=\"k2b-sr-only\">{chatMessages().messageReplyTo} </span>\n {value.author}\n </span>\n <span class=\"k2b-message-row__quote-text\">{value.text}</span>\n </>\n );\n\n /** The answered message on a quiet tint at the top of the bubble, or in its place without one. */\n const quoteBlock = () => (\n <Show when={content() && props.quote}>\n {(value) => (\n <Show when={value().onSelect} fallback={<div class=\"k2b-message-row__quote\">{quote(value())}</div>}>\n {(select) => (\n <button type=\"button\" class=\"k2b-message-row__quote\" onClick={() => select()()}>\n {quote(value())}\n </button>\n )}\n </Show>\n )}\n </Show>\n );\n\n /** The picture's description, then that it is a video and how long, then how many more the grid hides. */\n const mediaLabel = (item: MessageRowMedia, more: number) =>\n [\n item.alt,\n item.kind === \"video\" ? [chatMessages().messageVideo, item.duration].filter(Boolean).join(\" \") : \"\",\n more > 0 ? chatMessages().messageMoreAttachments({ count: more }) : \"\",\n ]\n .filter(Boolean)\n .join(\", \");\n\n const mediaItem = (item: MessageRowMedia, index: () => number) => {\n const more = () => (index() === shownMedia().length - 1 ? hiddenMedia() : 0);\n return (\n <AttachmentTarget\n class=\"k2b-message-row__media-item\"\n style={media().length === 1 ? { \"aspect-ratio\": String(mediaRatio(item)) } : undefined}\n label={mediaLabel(item, more())}\n onOpen={item.onOpen}\n href={item.href}\n >\n <img\n src={attachmentUrl(item.src, true)}\n alt=\"\"\n width={item.width > 0 ? item.width : undefined}\n height={item.height > 0 ? item.height : undefined}\n loading=\"lazy\"\n decoding=\"async\"\n />\n <Show when={item.kind === \"video\"}>\n <span class=\"k2b-message-row__media-play\" aria-hidden=\"true\">\n <i class=\"ti ti-player-play\" />\n </span>\n <Show when={item.duration}>\n <span class=\"k2b-message-row__media-duration\" aria-hidden=\"true\">\n {item.duration}\n </span>\n </Show>\n </Show>\n <Show when={more() > 0}>\n <span class=\"k2b-message-row__media-more\" aria-hidden=\"true\">\n +{more()}\n </span>\n </Show>\n </AttachmentTarget>\n );\n };\n\n const row = (\n // biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: Alt+click is a pointer shortcut for a menu entry that the keyboard reaches through \"…\".\n <div\n class={props.class ? `k2b-message-row ${props.class}` : \"k2b-message-row\"}\n onClick={altClick}\n data-own={props.own ? \"\" : undefined}\n data-group-start={groupStart() ? \"\" : undefined}\n data-status={props.status}\n data-media={media().length > 0 ? \"\" : undefined}\n >\n <Show when={!props.own}>\n <div class=\"k2b-message-row__gutter\">\n <Show when={groupStart()}>\n <span class=\"k2b-message-row__avatar\" aria-hidden=\"true\">\n <Avatar name={props.author.name} src={props.author.avatar} icon={props.author.icon} size=\"sm\" />\n </span>\n </Show>\n </div>\n </Show>\n <div class=\"k2b-message-row__body\">\n <Show\n when={groupStart()}\n fallback={\n <span class=\"k2b-sr-only\">\n {props.author.name}, {props.time}:{\" \"}\n </span>\n }\n >\n <Show\n when={!props.own}\n fallback={\n <div class=\"k2b-message-row__meta\">\n <span class=\"k2b-sr-only\">{props.author.name}, </span>\n <time class=\"k2b-message-row__time\" datetime={dateTimeOf(props.dateTime)}>\n {props.time}\n </time>\n </div>\n }\n >\n <div class=\"k2b-message-row__meta\">{header()}</div>\n </Show>\n </Show>\n <Show when={content() && props.forwarded}>\n <div class=\"k2b-message-row__marker\">\n <i class=\"ti ti-arrow-forward-up\" aria-hidden=\"true\" />\n <span>{chatMessages().messageForwarded}</span>\n </div>\n </Show>\n <Show when={!hasBubble()}>{quoteBlock()}</Show>\n <Show when={hasBubble()}>\n <div\n class=\"k2b-message-row__bubble\"\n data-deleted={props.deleted ? \"\" : undefined}\n aria-busy={props.progress && !props.deleted ? \"true\" : undefined}\n >\n {quoteBlock()}\n <Show\n when={!props.deleted}\n fallback={\n <span class=\"k2b-message-row__deleted\">\n <i class=\"ti ti-message-off\" aria-hidden=\"true\" />\n {chatMessages().messageDeleted}\n </span>\n }\n >\n <Show\n when={!writing()}\n fallback={\n <span class=\"k2b-message-row__writing\">\n <span class=\"k2b-chat-progress-dots\" aria-hidden=\"true\">\n <span />\n <span />\n <span />\n </span>\n </span>\n }\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: the native copy buttons inside the rendered Markdown are the targets; their clicks, including from the keyboard, bubble here. */}\n <div\n ref={text}\n id={textId}\n class=\"k2b-content-markdown k2b-message-row__text\"\n data-heading-scale=\"compact\"\n data-collapsed={collapsed() ? \"\" : undefined}\n onClick={copyCode}\n onFocusIn={revealFocus}\n innerHTML={rendered().html}\n />\n <Show when={collapsible()}>\n <div class=\"k2b-message-row__more-line\">\n <Button\n variant=\"text\"\n size=\"xs\"\n class=\"k2b-message-row__more\"\n aria-expanded={expanded() ? \"true\" : \"false\"}\n aria-controls={textId}\n onClick={() => setExpanded((value) => !value)}\n >\n {expanded() ? chatMessages().messageShowLess : chatMessages().messageShowMore}\n </Button>\n <Show when={props.edited}>\n <span class=\"k2b-message-row__edited\">{chatMessages().messageEdited}</span>\n </Show>\n </div>\n </Show>\n </Show>\n </Show>\n <Show when={hasMenu()}>{more()}</Show>\n </div>\n </Show>\n <Show when={hasMenu() && !hasBubble()}>{more()}</Show>\n {/* Without a bubble to end, the edit marker takes the bubble's place. */}\n <Show when={content() && props.edited && !hasBubble()}>\n <div class=\"k2b-message-row__marker\">\n <i class=\"ti ti-pencil\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__edited\">{chatMessages().messageEdited}</span>\n </div>\n </Show>\n <Show when={media().length > 0}>\n <div\n class=\"k2b-message-row__media\"\n role=\"group\"\n aria-label={chatMessages().attachments}\n data-count={Math.min(media().length, MEDIA_SHOWN)}\n style={media().length === 1 ? { width: mediaWidth(media()[0]!) } : undefined}\n >\n <For each={shownMedia()}>{(item, index) => mediaItem(item, index)}</For>\n </div>\n </Show>\n <Show when={files().length > 0}>\n <div class=\"k2b-message-row__files\">\n <For each={files()}>\n {(file) => (\n <AttachmentTarget class=\"k2b-message-row__file\" onOpen={file.onOpen} href={file.href}>\n <span class=\"k2b-message-row__file-icon\" aria-hidden=\"true\">\n <i class={fileIcon(file)} />\n </span>\n <span class=\"k2b-message-row__file-copy\">\n <span class=\"k2b-message-row__file-name\">{file.name}</span>\n <Show when={file.detail}>\n <span class=\"k2b-message-row__file-detail\">{file.detail}</span>\n </Show>\n </span>\n </AttachmentTarget>\n )}\n </For>\n </div>\n </Show>\n <Show when={content() && props.linkPreview}>\n <div class=\"k2b-message-row__slot\" data-gesture-ignore>\n {props.linkPreview}\n </div>\n </Show>\n <Show when={content() && props.card}>\n <div class=\"k2b-message-row__slot\" data-gesture-ignore>\n {props.card}\n </div>\n </Show>\n <Show when={props.thread}>\n {(thread) => (\n <button type=\"button\" class=\"k2b-message-row__thread\" onClick={() => thread().onOpen()}>\n <Show when={(thread().participants?.length ?? 0) > 0}>\n <span class=\"k2b-message-row__thread-people\" aria-hidden=\"true\">\n <For each={thread().participants?.slice(0, 3)}>\n {(person) => <Avatar name={person.name} src={person.avatar} icon={person.icon} size=\"xs\" />}\n </For>\n </span>\n </Show>\n <span class=\"k2b-message-row__thread-count\">{chatMessages().messageReplies({ count: thread().count })}</span>\n <Show when={thread().lastReply}>\n {(time) => (\n <time class=\"k2b-message-row__thread-time\" datetime={dateTimeOf(thread().lastReplyDateTime)}>\n {chatMessages().messageLastReply({ time: time() })}\n </time>\n )}\n </Show>\n </button>\n )}\n </Show>\n <Show when={props.origin}>{(origin) => <OriginChip origin={origin()} />}</Show>\n <Show when={hasFooter()}>\n <div class=\"k2b-message-row__footer\">\n <Show when={reactions()}>\n {(list) => <ReactionBar reactions={list()} onToggle={props.onToggleReaction} onAdd={props.onAddReaction} />}\n </Show>\n <Show when={hasLine()}>\n <div class=\"k2b-message-row__line\">\n <Show\n when={props.progress}\n fallback={\n <>\n <Show when={props.status === \"pending\"}>\n <i class=\"ti ti-clock\" aria-hidden=\"true\" />\n <span>{chatMessages().sending}</span>\n </Show>\n <Show when={props.status === \"failed\"}>\n <i class=\"ti ti-alert-circle\" aria-hidden=\"true\" />\n <span>{chatMessages().messageNotSent}</span>\n <Show when={props.onRetry}>\n {(retry) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => retry()()}>\n {chatMessages().messageRetry}\n </Button>\n )}\n </Show>\n <Show when={props.onDiscard}>\n {(discard) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => discard()()}>\n {chatMessages().messageDiscard}\n </Button>\n )}\n </Show>\n </Show>\n <Show when={props.status !== \"pending\" && props.status !== \"failed\"}>\n <Show\n when={props.receipt}\n fallback={\n <Show when={props.status === \"sent\"}>\n <i class=\"ti ti-check\" aria-hidden=\"true\" />\n <span class=\"k2b-sr-only\">{chatMessages().messageSent}</span>\n </Show>\n }\n >\n {(receipt) => (\n <>\n <i class=\"ti ti-checks\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__receipt\">{receipt()}</span>\n </>\n )}\n </Show>\n </Show>\n </>\n }\n >\n {(progress) => (\n <>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n <span class=\"k2b-message-row__line-text\">{progress().status}</span>\n <Show when={progress().onStop}>\n {(stop) => (\n <Button variant=\"text\" size=\"xs\" onClick={() => stop()()}>\n {chatMessages().messageStop}\n </Button>\n )}\n </Show>\n </>\n )}\n </Show>\n </div>\n </Show>\n </div>\n </Show>\n </div>\n </div>\n );\n\n return (\n <Show when={hasMenu()} fallback={row}>\n {/* Out of the tab order: the keyboard reaches the same menu through \"…\", so a message adds no extra stop. */}\n <GestureMenu items={gestureItems()} label={chatMessages().messageMenu({ author: props.author.name, time: props.time })} tabIndex={-1}>\n {row}\n </GestureMenu>\n </Show>\n );\n}\n\n/** Where a row belongs, such as its thread; a control when it opens that place. */\nfunction OriginChip(props: { origin: MessageRowOrigin }): JSX.Element {\n return (\n <Show\n when={props.origin.onSelect}\n fallback={\n <span class=\"k2b-message-row__origin\">\n <Show when={props.origin.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.origin.label}\n </span>\n }\n >\n {(select) => (\n <button type=\"button\" class=\"k2b-message-row__origin\" aria-label={props.origin.selectLabel} onClick={() => select()()}>\n <Show when={props.origin.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.origin.label}\n </button>\n )}\n </Show>\n );\n}\n\n/**\n * A quiet line for a change to something the conversation is about, such as a task's new status: no bubble, aligned\n * with the messages' text, with its time at the end. It is no message: it never counts as unread.\n */\nexport function MessageEventRow(props: MessageEventRowProps): JSX.Element {\n return (\n <div class={props.class ? `k2b-message-event-row ${props.class}` : \"k2b-message-event-row\"}>\n <i class={props.icon ?? \"ti ti-point\"} aria-hidden=\"true\" />\n <Show when={props.href} fallback={<span class=\"k2b-message-event-row__text\">{props.children}</span>}>\n {(href) => (\n <a class=\"k2b-message-event-row__text k2b-message-event-row__link\" href={href()}>\n {props.children}\n </a>\n )}\n </Show>\n <Show when={props.origin}>{(origin) => <OriginChip origin={origin()} />}</Show>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n </div>\n );\n}\n\n/** Several event lines in a row as one line that opens them, such as \"4 changes to Invitation and Light test\". */\nexport function MessageEventGroup(props: MessageEventGroupProps): JSX.Element {\n return (\n <button\n type=\"button\"\n class={props.class ? `k2b-message-event-row k2b-message-event-group ${props.class}` : \"k2b-message-event-row k2b-message-event-group\"}\n aria-expanded={props.expanded}\n onClick={() => props.onToggle()}\n >\n <i class=\"ti ti-history\" aria-hidden=\"true\" />\n <span class=\"k2b-message-event-row__text\">{props.label}</span>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n <i class={props.expanded ? \"ti ti-chevron-up\" : \"ti ti-chevron-down\"} aria-hidden=\"true\" />\n </button>\n );\n}\n\n/** A quiet, centered line for something that happened in the conversation, such as a person joining. */\nexport function MessageSystemRow(props: MessageSystemRowProps): JSX.Element {\n return (\n <div class={props.class ? `k2b-message-system-row ${props.class}` : \"k2b-message-system-row\"}>\n <Show when={props.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n <span>{props.children}</span>\n <Show when={props.time}>\n <time datetime={dateTimeOf(props.dateTime)}>{props.time}</time>\n </Show>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,SAAS,QAAQ,aAAa;AAEvC,SAAS,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,EAAY,SAAS,EAAE,IAAI,QAAQ,UAAU;AACnG,SAAS,MAAM,QAAQ,mBAAmB;AAC1C,SAAS,QAAQ,QAAQ,uBAAuB;AAChD,SAAS,QAAQ,QAA6B,qBAAqB;AACnE,SAA2B,WAAW,QAAgC,wBAAwB;AAC9F,SAA4B,eAAe,QAAQ,uBAAuB;AAC1E,SAAS,QAAQ,QAAQ,sBAAsB;AAC/C,SAAS,SAAS,QAAQ,gBAAgB;AAC1C,SAA0B,aAAa,QAAQ,kBAAkB;AACjE,SAAS,MAAM,QAAQ,oBAAoB;AAC3C,SAAS,GAAG,QAAQ,OAAO;AAC3B,SAAS,gBAAgB,EAAE,kBAAkB,EAAE,UAAU,QAAQ,gBAAgB;AACjF,SAAS,WAAW,QAA8B,eAAe;;AAEjE;AACA,MAAM,eAAe,GAAG,CAAC,GAAG,MAAM;AAClC;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,EAAE;AACzB;AACA,MAAM,UAAU,GAAG,GAAG;AACtB;AACA,MAAM,UAAU,GAAG,CAAC;AACpB,MAAM,SAAS,GAAG,KAAK;AACvB;AACA,MAAM,gBAAgB,GAAG,wGAAwG;AACjI;AACA,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAU;AAC9D;AACA,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACzD;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,oBAAoB,EAAE,OAAO,CAAC,CAAC;AACrE;AACA,MAAM,WAAW,GAAG,CAAC;AACrB;AACA,MAAM,eAAe,GAAG,GAAG;AAC3B,MAAM,eAAe,GAAG,CAAC;;AA8CzB;;AA+CA;;AA+HA,MAAM,SAAS,GAAI,KAA6B,IAAM,KAAK,YAAY,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAE;;AAE1H;AACA;AACA;AACA;AACA,OAAO,MAAM,kBAAkB,GAAG,CAAC,KAAwB,EAAE,QAAmC,KAAc;EAC5G,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,OAAO,IAAI;EACjG,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;EACxD,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,eAAe,CAAC;AAC9C,CAAC;;AAED;AACA,MAAM,YAAY,GAAI,IAAY,IAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;;AAEjG;AACA,MAAM,UAAU,GAAI,MAAwB,IAAa;EACvD,IAAI,IAAI,GAAG,EAAE;EACb,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;IAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,KACjC,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KACxE,IAAI,MAAM,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,IAAI;EAC9C;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,MAAM,aAAa,GAAI,MAAwB,IAAa;EAC1D,IAAI,KAAK,GAAG,CAAC;EACb,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;IAC1B,QAAQ,KAAK,CAAC,IAAI;MAChB,KAAK,MAAM;QACT,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAC1C;MACF,KAAK,MAAM;QACT,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChF;MACF,KAAK,OAAO;QACV,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAE,IAAsB,IAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtG;MACF,KAAK,WAAW;MAChB,KAAK,SAAS;MACd,KAAK,MAAM;QACT,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3E;MACF,KAAK,IAAI;QACP,KAAK,IAAI,CAAC;QACV;MACF,KAAK,OAAO;MACZ,KAAK,KAAK;QACR;MACF;QACE,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KACvE,IAAI,MAAM,IAAI,KAAK,EAAE,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;IAC/D;EACF;EACA,OAAO,KAAK;AACd,CAAC;;AAED;AACA,MAAM,aAAa,GAAI,IAAY,IAAc;EAC/C,IAAI;IACF,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,EAAE;EACtC,CAAC,CAAC,MAAM;IACN,OAAO,KAAK;EACd;AACF,CAAC;AAED,MAAM,UAAU,GAAI,KAAgC,IAAyB;EAC3E,IAAI,KAAK,KAAK,SAAS,EAAE,OAAO,SAAS;EACzC,MAAM,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;EAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,SAAS;AACzE,CAAC;;AAED;AACA;AACA;AACA;AACA,MAAM,aAAa,GAAG,CAAC,GAAuB,EAAE,OAAO,GAAG,KAAK,KAAyB;EACtF,IAAI,CAAC,GAAG,EAAE,OAAO,SAAS;EAC1B,IAAI,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG;EACrD,IAAI;IACF,OAAO,CAAC,OAAO,GAAG,iBAAiB,GAAG,oBAAoB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,2BAA2B,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,SAAS;EACvI,CAAC,CAAC,MAAM;IACN,OAAO,SAAS;EAClB;AACF,CAAC;;AAED;AACA,MAAM,UAAU,GAAI,KAAsB,IAAa;EACrD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC;EACtF,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;AACpE,CAAC;;AAED;AACA,MAAM,UAAU,GAAI,KAAsB,IAAa;EACrD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,MAAM,GAAG,EAAE;EAC3F,OAAO,aAAa,OAAO,0CAA0C,UAAU,CAAC,KAAK,CAAC,IAAI;AAC5F,CAAC;;AAED;AACA,MAAM,QAAQ,GAAI,IAAoB,IACpC,IAAI,CAAC,IAAI,IACT,MAAM,SAAS,CACZ,WAAW,CAAC;EAAE,IAAI,EAAE,IAAI,CAAC,IAAI;EAAE,IAAI,EAAE,MAAM;EAAE,QAAQ,EAAE,IAAI,CAAC;AAAU,CAAC,CAAC,CACxE,KAAK,CAAC,KAAK,CAAC,CACZ,MAAM,CAAE,KAAK,IAAK,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAC1C,IAAI,CAAC,GAAG,CAAC,EAAE;;AAEhB;AACA,SAAS,gBAAgB,CACvB,KAMC,EACY;EACb,yBACG,IAAI;IAAA,IACH,IAAI;MAAA,OAAE,KAAK,CAAC,MAAM;IAAA;IAAA,IAClB,QAAQ;MAAA,yBACL,IAAI;QAAA,IACH,IAAI;UAAA,OAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;QAAA;QAAA,IAC/B,QAAQ;UAAA,yBACL,IAAI;YAAA,IACH,IAAI;cAAA,OAAE,KAAK,CAAC,KAAK;YAAA;YAAA,IACjB,QAAQ;cAAA,sDACO,KAAK,CAAC,KAAK,4BAAS,KAAK,CAAC,KAAK,YACzC,KAAK,CAAC,QAAQ;YAAA;YAAA,UAIjB,KAAK,oDACQ,KAAK,CAAC,KAAK,4BAAS,KAAK,CAAC,KAAK,yCAAyB,KAAK,CAAC,CAAC,0BACzE,KAAK,CAAC,QAAQ;UAElB;QAAA;QAAA,UAIH,IAAI,oDACM,KAAK,CAAC,KAAK,4BAAS,KAAK,CAAC,KAAK,mCAAQ,IAAI,CAAC,CAAC,uDAAwD,KAAK,CAAC,KAAK,0BACvH,KAAK,CAAC,QAAQ;MAElB;IAAA;IAAA,UAIH,IAAI,oDACyB,KAAK,CAAC,KAAK,4BAAS,KAAK,CAAC,KAAK,yCAAc,KAAK,CAAC,KAAK,0BAClF,KAAK,CAAC,QAAQ;EAElB;AAGP;;AAEA;AACA;AACA;AACA;AACA,MAAM,aAAa,GAAG,CACpB,IAAY,EACZ,MAAc,EACd,QAAoB,EACpB,YAA0B,EAC1B,MAAe,KACqB;EACpC,MAAM,QAAQ,GAAG,kBAAkB,CAAC;IAAE,WAAW,EAAE,KAAK;IAAE,aAAa,EAAE,cAAc;IAAE,UAAU,EAAE,QAAQ;IAAE;EAAO,CAAC,CAAC;EACxH,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;EAC/C,QAAQ,CAAC,IAAI,GAAI,KAAkB,IAAM,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAE;EACnI,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;EAC/C,QAAQ,CAAC,IAAI,GAAI,KAAkB,IAAK;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACvE,OACE,gGAAgG,GAChG,6CAA6C,UAAU,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,GAC3F,sDAAsD,GACtD,yFAAyF,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,GAC3H,0FAA0F,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,GAC9H,kBAAkB,KAAK,QAAQ;EAEnC,CAAC;EACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC;IAAE,MAAM,EAAE,IAAI;IAAE;EAAS,CAAC,CAAC;EAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;EACjC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;EAClC,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC;EACnC;EACA,IAAI,CAAC,MAAM,IAAI,KAAK,GAAG,cAAc,EAAE,OAAO;IAAE,IAAI;IAAE;EAAM,CAAC;EAC7D;EACA,MAAM,MAAM,GAAG,yCAAyC,UAAU,CAAC,YAAY,CAAC,aAAa,CAAC,SAAS;EACvG,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;EAC1B,OAAO;IAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,MAAM,MAAM;IAAE;EAAM,CAAC;AAChH,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS,UAAU,CAAC,KAAsB,EAAe;EAC9D,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC;EACtC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;EAC1B,MAAM,MAAM,GAAG,eAAe,cAAc,CAAC,CAAC,EAAE;EAChD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;EACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;EACzH,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,cAAc;EAC3D,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;EACpD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,IAAI;EACjD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;EAC/G,MAAM,SAAS,GAAG,MAAO,KAAK,CAAC,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC,SAAU;EACrE,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,KAAK,SAAS;EAC9D,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE;EAChG,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE;EAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO;EACpC,MAAM,KAAK,GAAG,UAAU,CAAC,MACvB,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,EAAE,MAAM,CAAE,UAAU,IAAoC,UAAU,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,EAC5H,CAAC;EACD,MAAM,KAAK,GAAG,UAAU,CAAC,MACvB,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,EAAE,MAAM,CAAE,UAAU,IAAmC,UAAU,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,EAC3H,CAAC;EACD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC;EACtD,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM;EAC9D,IAAI,IAAgC;EACpC,IAAI,WAAsD;EAE1D,SAAS,CAAC,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;EAE1C,MAAM,QAAQ,GAAI,KAAiB,IAAK;IACtC,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,EAAqB,OAAO,GAAsB,wBAAwB,CAAC;IACvG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,WAAW,IAAI,EAAE;IAC9F,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CACjB,MAAM;MACJ,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,gBAAgB,CAAc,qCAAqC,CAAC,IAAI,EAAE,EAAE,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM;MACjI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE;MAC1B,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;MAC3B,YAAY,CAAC,WAAW,CAAC;MACzB,WAAW,GAAG,UAAU,CAAC,MAAM,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;IACzE,CAAC,EACD,MAAM,CAAC,CACT,CAAC;EACH,CAAC;;EAED;EACA;EACA,MAAM,WAAW,GAAI,KAAiB,IAAK;IACzC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,YAAY,OAAO,CAAC,EAAE;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;IACpH,IAAI,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;EAC3E,CAAC;;EAED;EACA;EACA,MAAM,OAAO,GAAG,MACd,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,GAAG,CAAE,KAAK,KAA8F;IAC5H,KAAK,EAAE,KAAK,CAAC,KAAK;IAClB,IAAI,EAAE,KAAK,CAAC,IAAI;IAChB,WAAW,EAAE,KAAK,CAAC,WAAW;IAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;IACxB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,SAAS,EAAE,KAAK,CAAC,SAAS;IAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,qBAAI,GAAG;MAAC,IAAI;IAAA,KAAkB,KAAK,CAAC,QAAQ,qBAAI,GAAG;MAAA,IAAC,IAAI;QAAA,OAAE,KAAK,CAAC,QAAQ;MAAA;IAAA,KAAO,SAAS;IAChH,MAAM,EAAE,MAAM,KAAK,CAAC,QAAQ,CAAC;EAC/B,CAAC,CAAC,CAAC;EACL,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC;EACrC,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC;EACxC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC;;EAEtD;EACA,MAAM,QAAQ,GAAI,KAAiB,IAAK;IACtC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;IAC7F,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,CAAE,MAAM,IAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;IAClF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,YAAY,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI;IACpE,IAAI,CAAC,KAAK,IAAI,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE;IACjD,KAAK,CAAC,cAAc,CAAC,CAAC;IACtB,KAAK,CAAC,QAAQ,CAAC,CAAC;EAClB,CAAC;EAED,MAAM,IAAI,GAAG,wBACV,QAAQ,CAAC,IAAI;IAAA,IACZ,KAAK;MAAA,OAAE,SAAS,CAAC,CAAC;IAAA;IAAA,IAClB,KAAK;MAAA,OAAE,YAAY,CAAC,CAAC,CAAC,cAAc;IAAA;IAAA;IAAA,IAEpC,QAAQ;MAAA,OAAE,KAAK,CAAC,GAAG,GAAG,cAAc,GAAG,aAAa;IAAA;IACpD,KAAK;IAAA;MAAA,yBAEJ,QAAQ,CAAC,OAAO;QAAC,QAAQ;QAAC,OAAO;QAAS,IAAI;QAAA,IAAM,KAAK;UAAA,OAAE,YAAY,CAAC,CAAC,CAAC,cAAc;QAAA;QAAA;QAAA;UAAA;QAAA;MAAA;IAAA;EAAA,EAI5F;EAED,MAAM,MAAM,GAAG,+BAE0B,KAAK,CAAC,MAAM,CAAC,IAAI,sBACrD,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,KAAK;IAAA;IAAA;MAAA,+BACiB,KAAK,CAAC,KAAK;IAAA;EAAA,uDAEL,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,0BACrE,KAAK,CAAC,IAAI,GAGhB;EAED,MAAM,KAAK,GAAI,KAAsB,6BAGJ,YAAY,CAAC,CAAC,CAAC,cAAc,YACvD,KAAK,CAAC,MAAM,4BAE4B,KAAK,CAAC,IAAI,GAExD;;EAED;EACA,MAAM,UAAU,GAAG,wBAChB,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,sBAAO,CAAC,CAAC,OAAI,KAAK,CAAC,KAAK;IAAA;IAAA,UAChC,KAAK,sBACJ,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,KAAK,CAAC,CAAC,CAAC,QAAQ;MAAA;MAAA,IAAE,QAAQ;QAAA,+BAAuC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;MAAA;MAAA,UACvF,MAAM,6BAEH,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAElB;EAEJ,EAEJ;;EAED;EACA,MAAM,UAAU,GAAG,CAAC,IAAqB,EAAE,IAAY,KACrD,CACE,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EACnG,IAAI,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAAE,KAAK,EAAE;EAAK,CAAC,CAAC,GAAG,EAAE,CACvE,CACE,MAAM,CAAC,OAAO,CAAC,CACf,IAAI,CAAC,IAAI,CAAC;EAEf,MAAM,SAAS,GAAG,CAAC,IAAqB,EAAE,KAAmB,KAAK;IAChE,MAAM,IAAI,GAAG,MAAO,KAAK,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAE;IAC5E,yBACG,gBAAgB;MAAA;MAAA,IAEf,KAAK;QAAA,OAAE,kBAAK,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,MAAG;UAAE,cAAc,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,CAAC,GAAG,SAAS;MAAA;MAAA,IACtF,KAAK;QAAA,OAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;MAAA;MAAA,IAC/B,MAAM;QAAA,OAAE,IAAI,CAAC,MAAM;MAAA;MAAA,IACnB,IAAI;QAAA,OAAE,IAAI,CAAC,IAAI;MAAA;MAAA;QAAA,uDAGR,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,yCAE3B,IAAI,CAAC,KAAK,GAAG,CAAC,YAAG,IAAI,CAAC,KAAK,mBAAG,SAAS,2CACtC,IAAI,CAAC,MAAM,GAAG,CAAC,YAAG,IAAI,CAAC,MAAM,mBAAG,SAAS,oCAIlD,IAAI;UAAA,IAAC,IAAI;YAAA,OAAE,IAAI,CAAC,IAAI,KAAK,OAAO;UAAA;UAAA;YAAA,2CAI9B,IAAI;cAAA,IAAC,IAAI;gBAAA,OAAE,IAAI,CAAC,QAAQ;cAAA;cAAA;gBAAA,gCAEpB,IAAI,CAAC,QAAQ;cAAA;YAAA;UAAA;QAAA,sBAInB,IAAI;UAAA,IAAC,IAAI;YAAA,OAAE,IAAI,CAAC,CAAC,GAAG,CAAC;UAAA;UAAA;YAAA,gCAEhB,IAAI,CAAC,CAAC;UAAA;QAAA;MAAA;IAAA;EAKlB,CAAC;EAED,MAAM,GAAG,GACP;EAAA,wCAES,KAAK,CAAC,KAAK,GAAG,4BAAmB,KAAK,CAAC,KAAK,SAAE,GAAG,iBAAiB,sCAE/D,KAAK,CAAC,GAAG,GAAG,EAAE,YAAG,SAAS,qDAClB,UAAU,CAAC,CAAC,GAAG,EAAE,YAAG,SAAS,yDAClC,KAAK,CAAC,MAAM,+CACb,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,YAAG,SAAS,4CAE9C,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,CAAC,KAAK,CAAC,GAAG;IAAA;IAAA;MAAA,kDAEjB,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,UAAU,CAAC,CAAC;QAAA;QAAA;UAAA,kDAEnB,MAAM;YAAA,IAAC,IAAI;cAAA,OAAE,KAAK,CAAC,MAAM,CAAC,IAAI;YAAA;YAAA,IAAE,GAAG;cAAA,OAAE,KAAK,CAAC,MAAM,CAAC,MAAM;YAAA;YAAA,IAAE,IAAI;cAAA,OAAE,KAAK,CAAC,MAAM,CAAC,IAAI;YAAA;YAAE,IAAI;UAAA;QAAA;MAAA;IAAA;EAAA,gCAM7F,IAAI;IAAA,IACH,IAAI;MAAA,OAAE,UAAU,CAAC,CAAC;IAAA;IAAA,IAClB,QAAQ;MAAA,gCAEH,KAAK,CAAC,MAAM,CAAC,IAAI,YAAI,KAAK,CAAC,IAAI;IAAA;IAAA;MAAA,yBAInC,IAAI;QAAA,IACH,IAAI;UAAA,OAAE,CAAC,KAAK,CAAC,GAAG;QAAA;QAAA,IAChB,QAAQ;UAAA,gCAEuB,KAAK,CAAC,MAAM,CAAC,IAAI,uCACE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,0BACrE,KAAK,CAAC,IAAI;QAAA;QAAA;UAAA,gCAKmB,MAAM,CAAC,CAAC;QAAA;MAAA;IAAA;EAAA,gCAG/C,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,sBAAO,CAAC,CAAC,OAAI,KAAK,CAAC,SAAS;IAAA;IAAA;MAAA,gCAG7B,YAAY,CAAC,CAAC,CAAC,gBAAgB;IAAA;EAAA,gCAGzC,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,CAAC,SAAS,CAAC,CAAC;IAAA;IAAA;MAAA,OAAG,UAAU,CAAC,CAAC;IAAA;EAAA,gCACtC,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,SAAS,CAAC,CAAC;IAAA;IAAA;MAAA,sDAGL,KAAK,CAAC,OAAO,GAAG,EAAE,YAAG,SAAS,8CACjC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,YAAG,SAAS,0BAE/D,UAAU,CAAC,CAAC,8BACZ,IAAI;QAAA,IACH,IAAI;UAAA,OAAE,CAAC,KAAK,CAAC,OAAO;QAAA;QAAA,IACpB,QAAQ;UAAA,gCAGH,YAAY,CAAC,CAAC,CAAC,cAAc;QAAA;QAAA;UAAA,yBAIjC,IAAI;YAAA,IACH,IAAI;cAAA,OAAE,CAAC,OAAO,CAAC,CAAC;YAAA;YAAA,IAChB,QAAQ;cAAA;YAAA;YAAA;cAAA,sDAaF,MAAM,kDAGM,SAAS,CAAC,CAAC,GAAG,EAAE,YAAG,SAAS,iBAGjC,QAAQ,CAAC,CAAC,CAAC,IAAI,qBAE3B,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,WAAW,CAAC,CAAC;gBAAA;gBAAA;kBAAA,kDAEpB,MAAM;oBACL,OAAO;oBACP,IAAI;oBAAA;oBAAA;sBAAA,OAEW,QAAQ,CAAC,CAAC,GAAG,MAAM,GAAG,OAAO;oBAAA;oBAAA,iBAC7B,MAAM;oBACrB,OAAO,EAAE,MAAM,WAAW,CAAE,KAAK,IAAK,CAAC,KAAK,CAAC;oBAAA;sBAAA,OAE5C,QAAQ,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC,eAAe;oBAAA;kBAAA,gCAE9E,IAAI;oBAAA,IAAC,IAAI;sBAAA,OAAE,KAAK,CAAC,MAAM;oBAAA;oBAAA;sBAAA,gCACiB,YAAY,CAAC,CAAC,CAAC,aAAa;oBAAA;kBAAA;gBAAA;cAAA;YAAA;UAAA;QAAA;MAAA,gCAM5E,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,OAAO,CAAC,CAAC;QAAA;QAAA;UAAA,OAAG,IAAI,CAAC,CAAC;QAAA;MAAA;IAAA;EAAA,gCAGjC,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,sBAAO,CAAC,CAAC,OAAI,CAAC,SAAS,CAAC,CAAC;IAAA;IAAA;MAAA,OAAG,IAAI,CAAC,CAAC;IAAA;EAAA,gCAE7C,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,uBAAO,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,QAAI,CAAC,SAAS,CAAC,CAAC;IAAA;IAAA;MAAA,gCAGV,YAAY,CAAC,CAAC,CAAC,aAAa;IAAA;EAAA,gCAGtE,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;IAAA;IAAA;MAAA,6DAId,YAAY,CAAC,CAAC,CAAC,WAAW,wDAC1B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,4BAC1C,KAAK,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG;QAAE,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE;MAAE,CAAC,GAAG,SAAS,8BAE3E,GAAG;QAAA,IAAC,IAAI;UAAA,OAAE,UAAU,CAAC,CAAC;QAAA;QAAA,UAAG,CAAC,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,IAAI,EAAE,KAAK;MAAC;IAAA;EAAA,gCAGpE,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;IAAA;IAAA;MAAA,kDAEzB,GAAG;QAAA,IAAC,IAAI;UAAA,OAAE,KAAK,CAAC,CAAC;QAAA;QAAA,UACd,IAAI,sBACH,gBAAgB;UAAA;UAAA,IAA+B,MAAM;YAAA,OAAE,IAAI,CAAC,MAAM;UAAA;UAAA,IAAE,IAAI;YAAA,OAAE,IAAI,CAAC,IAAI;UAAA;UAAA;YAAA,yDAEtE,QAAQ,CAAC,IAAI,CAAC,2CAGkB,IAAI,CAAC,IAAI,8BAClD,IAAI;cAAA,IAAC,IAAI;gBAAA,OAAE,IAAI,CAAC,MAAM;cAAA;cAAA;gBAAA,gCACuB,IAAI,CAAC,MAAM;cAAA;YAAA;UAAA;QAAA;MAI9D;IAAA;EAAA,gCAIN,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,sBAAO,CAAC,CAAC,OAAI,KAAK,CAAC,WAAW;IAAA;IAAA;MAAA,gCAErC,KAAK,CAAC,WAAW;IAAA;EAAA,gCAGrB,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,sBAAO,CAAC,CAAC,OAAI,KAAK,CAAC,IAAI;IAAA;IAAA;MAAA,gCAE9B,KAAK,CAAC,IAAI;IAAA;EAAA,gCAGd,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,MAAM;IAAA;IAAA,UACpB,MAAM,+CAEH,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC;MAAA;MAAA;QAAA,kDAE/C,GAAG;UAAA,IAAC,IAAI;YAAA,OAAE,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;UAAA;UAAA,UACzC,MAAM,sBAAM,MAAM;YAAA,IAAC,IAAI;cAAA,OAAE,MAAM,CAAC,IAAI;YAAA;YAAA,IAAE,GAAG;cAAA,OAAE,MAAM,CAAC,MAAM;YAAA;YAAA,IAAE,IAAI;cAAA,OAAE,MAAM,CAAC,IAAI;YAAA;YAAE,IAAI;UAAA;QAAQ;MAAA;IAAA,cAIpD,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC;MAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAAM,CAAC,CAAC,8BACpG,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,MAAM,CAAC,CAAC,CAAC,SAAS;MAAA;MAAA,UAC1B,IAAI,wDACiD,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,0BACxF,YAAY,CAAC,CAAC,CAAC,gBAAgB,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;MAAE,CAAC,CAAC;IAErD;EAGN,gCAEF,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,MAAM;IAAA;IAAA,UAAI,MAAM,sBAAM,UAAU;MAAA,IAAC,MAAM;QAAA,OAAE,MAAM,CAAC,CAAC;MAAA;IAAA;EAAI,gCACtE,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,SAAS,CAAC,CAAC;IAAA;IAAA;MAAA,kDAElB,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,SAAS,CAAC,CAAC;QAAA;QAAA,UACnB,IAAI,sBAAM,WAAW;UAAA,IAAC,SAAS;YAAA,OAAE,IAAI,CAAC,CAAC;UAAA;UAAA,IAAE,QAAQ;YAAA,OAAE,KAAK,CAAC,gBAAgB;UAAA;UAAA,IAAE,KAAK;YAAA,OAAE,KAAK,CAAC,aAAa;UAAA;QAAA;MAAI,gCAE5G,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,OAAO,CAAC,CAAC;QAAA;QAAA;UAAA,kDAEhB,IAAI;YAAA,IACH,IAAI;cAAA,OAAE,KAAK,CAAC,QAAQ;YAAA;YAAA,IACpB,QAAQ;cAAA,0BAEH,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAA;gBAAA;kBAAA,kDAE7B,YAAY,CAAC,CAAC,CAAC,OAAO;gBAAA;cAAA,sBAE9B,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAAA;gBAAA;kBAAA,kDAE5B,YAAY,CAAC,CAAC,CAAC,cAAc,sBACnC,IAAI;oBAAA,IAAC,IAAI;sBAAA,OAAE,KAAK,CAAC,OAAO;oBAAA;oBAAA,UACrB,KAAK,sBACJ,MAAM;sBAAC,OAAO;sBAAQ,IAAI;sBAAM,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;sBAAA;wBAAA,OACtD,YAAY,CAAC,CAAC,CAAC,YAAY;sBAAA;oBAAA;kBAE/B,sBAEF,IAAI;oBAAA,IAAC,IAAI;sBAAA,OAAE,KAAK,CAAC,SAAS;oBAAA;oBAAA,UACvB,OAAO,sBACN,MAAM;sBAAC,OAAO;sBAAQ,IAAI;sBAAM,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;sBAAA;wBAAA,OACxD,YAAY,CAAC,CAAC,CAAC,cAAc;sBAAA;oBAAA;kBAEjC;gBAAA;cAAA,sBAGJ,IAAI;gBAAA,IAAC,IAAI;kBAAA,OAAE,kBAAK,CAAC,MAAM,KAAK,SAAS,OAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAAA;gBAAA;kBAAA,yBAChE,IAAI;oBAAA,IACH,IAAI;sBAAA,OAAE,KAAK,CAAC,OAAO;oBAAA;oBAAA,IACnB,QAAQ;sBAAA,yBACL,IAAI;wBAAA,IAAC,IAAI;0BAAA,OAAE,KAAK,CAAC,MAAM,KAAK,MAAM;wBAAA;wBAAA;0BAAA,kDAEN,YAAY,CAAC,CAAC,CAAC,WAAW;wBAAA;sBAAA;oBAAA;oBAAA,UAIvD,OAAO,+CAGmC,OAAO,CAAC,CAAC;kBAEpD;gBAAA;cAAA;YAAA;YAAA,UAMP,QAAQ,+CAGoC,QAAQ,CAAC,CAAC,CAAC,MAAM,sBAC1D,IAAI;cAAA,IAAC,IAAI;gBAAA,OAAE,QAAQ,CAAC,CAAC,CAAC,MAAM;cAAA;cAAA,UACzB,IAAI,sBACH,MAAM;gBAAC,OAAO;gBAAQ,IAAI;gBAAM,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;gBAAA;kBAAA,OACrD,YAAY,CAAC,CAAC,CAAC,WAAW;gBAAA;cAAA;YAE9B;UAGN;QAAA;MAAA;IAAA;EAAA,IAQhB;EAED,yBACG,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,OAAO,CAAC,CAAC;IAAA;IAAE,QAAQ,EAAE,GAAG;IAAA;MAAA,yBAEjC,WAAW;QAAA,IAAC,KAAK;UAAA,OAAE,YAAY,CAAC,CAAC;QAAA;QAAA,IAAE,KAAK;UAAA,OAAE,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;YAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI;YAAE,IAAI,EAAE,KAAK,CAAC;UAAK,CAAC,CAAC;QAAA;QAAE,QAAQ,EAAE,CAAC,CAAC;QAAA,UACjI;MAAG;IAAA;EAAA;AAIZ;;AAEA;AACA,SAAS,UAAU,CAAC,KAAmC,EAAe;EACpE,yBACG,IAAI;IAAA,IACH,IAAI;MAAA,OAAE,KAAK,CAAC,MAAM,CAAC,QAAQ;IAAA;IAAA,IAC3B,QAAQ;MAAA,kDAEH,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,KAAK,CAAC,MAAM,CAAC,IAAI;QAAA;QAAA,UAAI,IAAI,qDAAe,IAAI,CAAC,CAAC;MAAuB,cAChF,KAAK,CAAC,MAAM,CAAC,KAAK;IAAA;IAAA,UAIrB,MAAM,0DAC4D,KAAK,CAAC,MAAM,CAAC,WAAW,4CACvF,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,KAAK,CAAC,MAAM,CAAC,IAAI;MAAA;MAAA,UAAI,IAAI,qDAAe,IAAI,CAAC,CAAC;IAAuB,cAChF,KAAK,CAAC,MAAM,CAAC,KAAK;EAEtB;AAGP;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAAS,eAAe,CAAC,KAA2B,EAAe;EACxE,+CACc,KAAK,CAAC,KAAK,GAAG,kCAAyB,KAAK,CAAC,KAAK,SAAE,GAAG,uBAAuB,2CAC9E,KAAK,CAAC,IAAI,IAAI,aAAa,4CACpC,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,IAAI;IAAA;IAAA,IAAE,QAAQ;MAAA,gCAA6C,KAAK,CAAC,QAAQ;IAAA;IAAA,UACvF,IAAI,oDACqE,IAAI,CAAC,CAAC,0BAC5E,KAAK,CAAC,QAAQ;EAElB,gCAEF,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,MAAM;IAAA;IAAA,UAAI,MAAM,sBAAM,UAAU;MAAA,IAAC,MAAM;QAAA,OAAE,MAAM,CAAC,CAAC;MAAA;IAAA;EAAI,gCACtE,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,IAAI;IAAA;IAAA;MAAA,2DACJ,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,0BAAG,KAAK,CAAC,IAAI;IAAA;EAAA;AAI/D;;AAEA;AACA,OAAO,SAAS,iBAAiB,CAAC,KAA6B,EAAe;EAC5E,+CAGW,KAAK,CAAC,KAAK,GAAG,0DAAiD,KAAK,CAAC,KAAK,SAAE,GAAG,+CAA+C,oDACtH,KAAK,CAAC,QAAQ,0BAIc,KAAK,CAAC,KAAK,8BACrD,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,IAAI;IAAA;IAAA;MAAA,2DACJ,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,0BAAG,KAAK,CAAC,IAAI;IAAA;EAAA,6BAE/C,KAAK,CAAC,QAAQ,GAAG,kBAAkB,GAAG,oBAAoB;AAG1E;;AAEA;AACA,OAAO,SAAS,gBAAgB,CAAC,KAA4B,EAAe;EAC1E,+CACc,KAAK,CAAC,KAAK,GAAG,mCAA0B,KAAK,CAAC,KAAK,SAAE,GAAG,wBAAwB,qCACzF,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,IAAI;IAAA;IAAA,UAAI,IAAI,qDAAe,IAAI,CAAC,CAAC;EAAuB,cACnE,KAAK,CAAC,QAAQ,8BACpB,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,IAAI;IAAA;IAAA;MAAA,2DACJ,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,0BAAG,KAAK,CAAC,IAAI;IAAA;EAAA;AAI/D","ignoreList":[]}
@@ -85,7 +85,9 @@ export function ReactionBar(props) {
85
85
  };
86
86
  const dismiss = () => close();
87
87
  const escape = event => {
88
- if (event.key === "Escape") close();
88
+ if (event.key !== "Escape" || !tooltip.matches(":popover-open")) return;
89
+ event.preventDefault();
90
+ close();
89
91
  };
90
92
  /** Opens the names hint over a chip; false when the chip has no names to show. */
91
93
  const open = (chip, fromTouch) => {
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/ReactionBar.tsx"],"sourcesContent":["import { type Accessor, createEffect, createMemo, createSignal, For, type JSX, on, onCleanup, onMount, Show } from \"solid-js\";\nimport { IconButton } from \"../actions/Button\";\nimport { useChatMessages } from \"../chat/chat-messages\";\nimport { positionTooltipSurface } from \"../feedback/tooltip-position\";\nimport { ScrollArea } from \"../layout/ScrollArea\";\n\nexport type ReactionBarItem = {\n /** Stable identity of the reaction, passed to `onToggle`. */\n key: string;\n emoji: string;\n count: number;\n /** The reader reacted this way; the chip shows pressed. */\n own?: boolean;\n /** Who reacted, for example from `reactionPeople`. Read by screen readers and shown in the names hint. */\n label?: string;\n /** A change of the reader's own reaction is still being saved; the chip carries `aria-busy`. */\n busy?: boolean;\n};\nexport type ReactionBarProps = {\n reactions: readonly ReactionBarItem[];\n /** Called with a reaction's key when the reader presses its chip. Without it, the chips are not controls. */\n onToggle?: (key: string) => void;\n /** Called by \"Add reaction\" with the button to anchor a picker to, such as `EmojiPicker.Popover`. Without it there is no button. */\n onAdd?: (anchor: HTMLElement) => void;\n /** \"default\": 26 px chips (chat, item detail). \"compact\": 24 px chips (cards). */\n density?: \"default\" | \"compact\";\n /** \"scroll\" (default): one line of fixed height that scrolls sideways. \"wrap\": every chip shows, lines wrap. */\n overflow?: \"scroll\" | \"wrap\";\n /** Shows at most this many chips and then \"+n\", n being the number of further emoji. The shown chips do not scroll. */\n max?: number;\n /** Screen-reader text of \"+n\"; defaults to \"{n} more reactions\". */\n moreLabel?: (count: number) => string;\n /** \"always\" (default) or \"progressive\": shown on hover and focus with a mouse or trackpad (k2b-progressive-action, inside the caller's k2b-progressive-row), kept while its picker is open, hidden on touch. Its place stays reserved. */\n add?: \"always\" | \"progressive\";\n class?: string;\n};\n\nconst HOVER_MS = 250;\nconst HOLD_MS = 500;\nconst MOVE_TOLERANCE = 8;\n\nexport function ReactionBar(props: ReactionBarProps): JSX.Element {\n const chatMessages = useChatMessages();\n // A max uses the wrapping layout, so the shown chips, +n, and add button wrap together.\n const overflow = () => (props.max !== undefined ? \"wrap\" : (props.overflow ?? \"scroll\"));\n const shown = createMemo(() => (props.max === undefined ? props.reactions : props.reactions.slice(0, props.max)));\n const keys = createMemo(() => shown().map((reaction) => reaction.key));\n const byKey = createMemo(() => new Map(shown().map((reaction) => [reaction.key, reaction])));\n const more = () => props.reactions.length - shown().length;\n /** The reaction whose names the hint shows; it follows that reaction while the caller updates it. */\n const [hintKey, setHintKey] = createSignal<string>();\n const hinted = () => {\n const key = hintKey();\n const reaction = key === undefined ? undefined : byKey().get(key);\n return reaction?.label?.trim() ? reaction : undefined;\n };\n const hint = () => {\n const reaction = hinted();\n return reaction ? `${reaction.emoji} ${reaction.label}` : \"\";\n };\n const [touch, setTouch] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let surface: HTMLSpanElement | undefined;\n // The chip that holds focus. When it goes away, such as the reader's last reaction of its kind, focus moves to the\n // chip now in its place, else the one before it, else \"Add reaction\", so a keyboard user keeps their place.\n let focusedKey: string | undefined;\n createEffect(\n on(keys, (next, previous) => {\n const gone = focusedKey;\n if (!root || gone === undefined || next.includes(gone)) return;\n focusedKey = undefined;\n const active = document.activeElement;\n if (active && active !== document.body && root.contains(active)) return;\n const index = (previous ?? []).indexOf(gone);\n const target = next[Math.min(Math.max(index, 0), next.length - 1)];\n const chipElement =\n target === undefined ? null : root.querySelector<HTMLElement>(`button[data-reaction-key=\"${CSS.escape(target)}\"]`);\n (chipElement ?? root.querySelector<HTMLElement>(\".k2b-reaction-bar__add-button\"))?.focus();\n }),\n );\n\n onMount(() => {\n if (!root || !surface) return;\n const bar = root;\n const tooltip = surface;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let openChip: HTMLElement | undefined;\n let swallowClick = false;\n let press: { chip: HTMLElement; id: number; x: number; y: number; held: boolean; moved: boolean } | undefined;\n\n const chipOf = (target: EventTarget | null): HTMLElement | undefined => {\n const chip = target instanceof Element ? target.closest<HTMLElement>(\"[data-reaction-key]\") : null;\n return chip && chip.closest(\".k2b-reaction-bar\") === bar ? chip : undefined;\n };\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n const close = () => {\n clearTimer();\n openChip = undefined;\n try {\n tooltip.hidePopover();\n } catch {\n /* The surface may already be disconnected. */\n }\n document.removeEventListener(\"pointerdown\", dismiss, true);\n document.removeEventListener(\"keydown\", escape);\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n const dismiss = () => close();\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") close();\n };\n /** Opens the names hint over a chip; false when the chip has no names to show. */\n const open = (chip: HTMLElement, fromTouch: boolean): boolean => {\n clearTimer();\n const reaction = byKey().get(chip.dataset.reactionKey ?? \"\");\n if (!reaction?.label?.trim() || !chip.isConnected) return false;\n setHintKey(reaction.key);\n setTouch(fromTouch);\n if (!tooltip.matches(\":popover-open\")) {\n try {\n tooltip.showPopover();\n } catch {\n return false;\n }\n }\n openChip = chip;\n positionTooltipSurface(tooltip, chip, \"top\");\n document.addEventListener(\"pointerdown\", dismiss, true);\n document.addEventListener(\"keydown\", escape);\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return true;\n };\n // An open hint follows its reaction: new names show at once, and it closes when the reaction, its names, or its\n // chip go away.\n createEffect(\n on(hinted, (reaction) => {\n if (!openChip) return;\n if (!reaction || !openChip.isConnected) close();\n else positionTooltipSurface(tooltip, openChip, \"top\");\n }),\n );\n const delayed = (chip: HTMLElement) => {\n if (touch() && openChip) return;\n if (\n !byKey()\n .get(chip.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n ) {\n close();\n return;\n }\n clearTimer();\n if (openChip) open(chip, false);\n else timer = setTimeout(() => open(chip, false), HOVER_MS);\n };\n const enter = (event: PointerEvent) => {\n const chip = chipOf(event.target);\n if (chip && event.target === chip && event.pointerType !== \"touch\") delayed(chip);\n };\n const leave = (event: PointerEvent) => {\n if (event.target !== chipOf(event.target) || (touch() && openChip)) return;\n // Moving directly to another chip keeps an open hint available for its immediate update.\n const next = chipOf(event.relatedTarget);\n if (\n !next ||\n !byKey()\n .get(next.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n )\n close();\n };\n const focusIn = (event: FocusEvent) => {\n const chip = chipOf(event.target);\n focusedKey = chip instanceof HTMLButtonElement ? chip.dataset.reactionKey : undefined;\n if (chip?.matches(\":focus-visible\")) delayed(chip);\n };\n const focusOut = (event: FocusEvent) => {\n // Focus leaving for somewhere else ends the tracking; a removed chip reports no next target and keeps it.\n if (event.relatedTarget instanceof Node && !root?.contains(event.relatedTarget)) focusedKey = undefined;\n if (chipOf(event.target) && !chipOf(event.relatedTarget) && !(touch() && openChip)) close();\n };\n const resetSwallow = () => {\n swallowClick = false;\n document.removeEventListener(\"pointerdown\", resetSwallow, true);\n };\n const endPress = () => {\n clearTimer();\n press = undefined;\n document.removeEventListener(\"pointermove\", move, true);\n document.removeEventListener(\"pointerup\", up, true);\n document.removeEventListener(\"pointercancel\", cancel, true);\n };\n const down = (event: PointerEvent) => {\n resetSwallow();\n close();\n endPress();\n const chip = chipOf(event.target);\n if (!chip || (event.pointerType !== \"touch\" && event.pointerType !== \"pen\")) return;\n press = { chip, id: event.pointerId, x: event.clientX, y: event.clientY, held: false, moved: false };\n timer = setTimeout(() => {\n timer = undefined;\n if (!press || press.moved) return;\n // Only a hold that shows names takes the press; on a chip without names it stays an ordinary press.\n press.held = open(press.chip, true);\n }, HOLD_MS);\n document.addEventListener(\"pointermove\", move, true);\n document.addEventListener(\"pointerup\", up, true);\n document.addEventListener(\"pointercancel\", cancel, true);\n };\n const move = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (Math.hypot(event.clientX - press.x, event.clientY - press.y) > MOVE_TOLERANCE) {\n press.moved = true;\n clearTimer();\n }\n };\n const up = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (press.held) {\n swallowClick = true;\n // Some browsers send no click after a hold. Never consume the next real tap.\n document.addEventListener(\"pointerdown\", resetSwallow, true);\n } else if (!press.moved && !props.onToggle) open(press.chip, true);\n endPress();\n };\n const cancel = (event: PointerEvent) => {\n if (event.pointerId === press?.id) endPress();\n };\n const touchEnd = (event: TouchEvent) => {\n if (!swallowClick || !event.cancelable) return;\n // A cancelled touchend sends no click, so nothing is left to swallow.\n event.preventDefault();\n resetSwallow();\n };\n // Only the click of the held finger or pen is swallowed; a keyboard's click has no detail and always goes through.\n const click = (event: MouseEvent) => {\n if (!swallowClick || event.detail === 0) return;\n event.preventDefault();\n event.stopPropagation();\n resetSwallow();\n };\n const contextMenu = (event: MouseEvent) => {\n if (chipOf(event.target) && (press || swallowClick || (touch() && openChip))) event.preventDefault();\n };\n\n bar.addEventListener(\"pointerenter\", enter, true);\n bar.addEventListener(\"pointerleave\", leave, true);\n bar.addEventListener(\"focusin\", focusIn);\n bar.addEventListener(\"focusout\", focusOut);\n bar.addEventListener(\"pointerdown\", down);\n bar.addEventListener(\"touchend\", touchEnd, { passive: false });\n bar.addEventListener(\"click\", click, true);\n bar.addEventListener(\"contextmenu\", contextMenu);\n onCleanup(() => {\n close();\n endPress();\n resetSwallow();\n bar.removeEventListener(\"pointerenter\", enter, true);\n bar.removeEventListener(\"pointerleave\", leave, true);\n bar.removeEventListener(\"focusin\", focusIn);\n bar.removeEventListener(\"focusout\", focusOut);\n bar.removeEventListener(\"pointerdown\", down);\n bar.removeEventListener(\"touchend\", touchEnd);\n bar.removeEventListener(\"click\", click, true);\n bar.removeEventListener(\"contextmenu\", contextMenu);\n });\n });\n\n const chip = (key: string, reaction: Accessor<ReactionBarItem>) => {\n const label = (own: boolean) =>\n chatMessages().messageReaction({ emoji: reaction().emoji, count: reaction().count, names: reaction().label, own });\n return (\n <Show\n when={props.onToggle}\n fallback={\n <span\n class=\"k2b-reaction-bar__chip\"\n role=\"img\"\n aria-label={label(Boolean(reaction().own))}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n data-gesture-ignore\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </span>\n }\n >\n {(toggle) => (\n <button\n type=\"button\"\n class=\"k2b-reaction-bar__chip\"\n aria-pressed={reaction().own ? \"true\" : \"false\"}\n aria-label={label(false)}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n onClick={() => toggle()(key)}\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </button>\n )}\n </Show>\n );\n };\n const chips = () => <For each={keys()}>{(key) => <Show when={byKey().get(key)}>{(reaction) => chip(key, reaction)}</Show>}</For>;\n\n return (\n <div\n ref={root}\n class={props.class ? `k2b-reaction-bar ${props.class}` : \"k2b-reaction-bar\"}\n role=\"group\"\n aria-label={chatMessages().messageReactions}\n data-density={props.density ?? \"default\"}\n data-overflow={overflow()}\n data-add={props.onAdd ? (props.add ?? \"always\") : undefined}\n data-empty={props.reactions.length === 0 ? \"\" : undefined}\n >\n <Show when={overflow() === \"scroll\"} fallback={<div class=\"k2b-reaction-bar__list\">{chips()}</div>}>\n <Show when={shown().length > 0}>\n <ScrollArea orientation=\"horizontal\" class=\"k2b-reaction-bar__scroll\" tabindex={props.onToggle ? undefined : 0}>\n {chips()}\n </ScrollArea>\n </Show>\n </Show>\n <Show when={more() > 0}>\n <span class=\"k2b-reaction-bar__more\">\n <span aria-hidden=\"true\">+{more()}</span>\n <span class=\"k2b-sr-only\">{props.moreLabel?.(more()) ?? chatMessages().moreReactions({ count: more() })}</span>\n </span>\n </Show>\n <Show when={props.onAdd}>\n {(add) => (\n <span class={`k2b-reaction-bar__add${props.add === \"progressive\" ? \" k2b-progressive-action\" : \"\"}`}>\n <IconButton\n label={chatMessages().messageAddReaction}\n size=\"xs\"\n class=\"k2b-reaction-bar__add-button\"\n onClick={(event) => add()(event.currentTarget)}\n >\n <i class=\"ti ti-mood-plus\" aria-hidden=\"true\" />\n </IconButton>\n </span>\n )}\n </Show>\n <span\n ref={surface}\n class=\"k2b-tooltip k2b-reaction-bar__hint\"\n popover=\"manual\"\n role=\"tooltip\"\n aria-hidden=\"true\"\n data-touch={touch() ? \"\" : undefined}\n >\n {hint()}\n </span>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/ReactionBar.tsx"],"sourcesContent":["import { type Accessor, createEffect, createMemo, createSignal, For, type JSX, on, onCleanup, onMount, Show } from \"solid-js\";\nimport { IconButton } from \"../actions/Button\";\nimport { useChatMessages } from \"../chat/chat-messages\";\nimport { positionTooltipSurface } from \"../feedback/tooltip-position\";\nimport { ScrollArea } from \"../layout/ScrollArea\";\n\nexport type ReactionBarItem = {\n /** Stable identity of the reaction, passed to `onToggle`. */\n key: string;\n emoji: string;\n count: number;\n /** The reader reacted this way; the chip shows pressed. */\n own?: boolean;\n /** Who reacted, for example from `reactionPeople`. Read by screen readers and shown in the names hint. */\n label?: string;\n /** A change of the reader's own reaction is still being saved; the chip carries `aria-busy`. */\n busy?: boolean;\n};\nexport type ReactionBarProps = {\n reactions: readonly ReactionBarItem[];\n /** Called with a reaction's key when the reader presses its chip. Without it, the chips are not controls. */\n onToggle?: (key: string) => void;\n /** Called by \"Add reaction\" with the button to anchor a picker to, such as `EmojiPicker.Popover`. Without it there is no button. */\n onAdd?: (anchor: HTMLElement) => void;\n /** \"default\": 26 px chips (chat, item detail). \"compact\": 24 px chips (cards). */\n density?: \"default\" | \"compact\";\n /** \"scroll\" (default): one line of fixed height that scrolls sideways. \"wrap\": every chip shows, lines wrap. */\n overflow?: \"scroll\" | \"wrap\";\n /** Shows at most this many chips and then \"+n\", n being the number of further emoji. The shown chips do not scroll. */\n max?: number;\n /** Screen-reader text of \"+n\"; defaults to \"{n} more reactions\". */\n moreLabel?: (count: number) => string;\n /** \"always\" (default) or \"progressive\": shown on hover and focus with a mouse or trackpad (k2b-progressive-action, inside the caller's k2b-progressive-row), kept while its picker is open, hidden on touch. Its place stays reserved. */\n add?: \"always\" | \"progressive\";\n class?: string;\n};\n\nconst HOVER_MS = 250;\nconst HOLD_MS = 500;\nconst MOVE_TOLERANCE = 8;\n\nexport function ReactionBar(props: ReactionBarProps): JSX.Element {\n const chatMessages = useChatMessages();\n // A max uses the wrapping layout, so the shown chips, +n, and add button wrap together.\n const overflow = () => (props.max !== undefined ? \"wrap\" : (props.overflow ?? \"scroll\"));\n const shown = createMemo(() => (props.max === undefined ? props.reactions : props.reactions.slice(0, props.max)));\n const keys = createMemo(() => shown().map((reaction) => reaction.key));\n const byKey = createMemo(() => new Map(shown().map((reaction) => [reaction.key, reaction])));\n const more = () => props.reactions.length - shown().length;\n /** The reaction whose names the hint shows; it follows that reaction while the caller updates it. */\n const [hintKey, setHintKey] = createSignal<string>();\n const hinted = () => {\n const key = hintKey();\n const reaction = key === undefined ? undefined : byKey().get(key);\n return reaction?.label?.trim() ? reaction : undefined;\n };\n const hint = () => {\n const reaction = hinted();\n return reaction ? `${reaction.emoji} ${reaction.label}` : \"\";\n };\n const [touch, setTouch] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let surface: HTMLSpanElement | undefined;\n // The chip that holds focus. When it goes away, such as the reader's last reaction of its kind, focus moves to the\n // chip now in its place, else the one before it, else \"Add reaction\", so a keyboard user keeps their place.\n let focusedKey: string | undefined;\n createEffect(\n on(keys, (next, previous) => {\n const gone = focusedKey;\n if (!root || gone === undefined || next.includes(gone)) return;\n focusedKey = undefined;\n const active = document.activeElement;\n if (active && active !== document.body && root.contains(active)) return;\n const index = (previous ?? []).indexOf(gone);\n const target = next[Math.min(Math.max(index, 0), next.length - 1)];\n const chipElement =\n target === undefined ? null : root.querySelector<HTMLElement>(`button[data-reaction-key=\"${CSS.escape(target)}\"]`);\n (chipElement ?? root.querySelector<HTMLElement>(\".k2b-reaction-bar__add-button\"))?.focus();\n }),\n );\n\n onMount(() => {\n if (!root || !surface) return;\n const bar = root;\n const tooltip = surface;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let openChip: HTMLElement | undefined;\n let swallowClick = false;\n let press: { chip: HTMLElement; id: number; x: number; y: number; held: boolean; moved: boolean } | undefined;\n\n const chipOf = (target: EventTarget | null): HTMLElement | undefined => {\n const chip = target instanceof Element ? target.closest<HTMLElement>(\"[data-reaction-key]\") : null;\n return chip && chip.closest(\".k2b-reaction-bar\") === bar ? chip : undefined;\n };\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n const close = () => {\n clearTimer();\n openChip = undefined;\n try {\n tooltip.hidePopover();\n } catch {\n /* The surface may already be disconnected. */\n }\n document.removeEventListener(\"pointerdown\", dismiss, true);\n document.removeEventListener(\"keydown\", escape);\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n const dismiss = () => close();\n const escape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\" || !tooltip.matches(\":popover-open\")) return;\n event.preventDefault();\n close();\n };\n /** Opens the names hint over a chip; false when the chip has no names to show. */\n const open = (chip: HTMLElement, fromTouch: boolean): boolean => {\n clearTimer();\n const reaction = byKey().get(chip.dataset.reactionKey ?? \"\");\n if (!reaction?.label?.trim() || !chip.isConnected) return false;\n setHintKey(reaction.key);\n setTouch(fromTouch);\n if (!tooltip.matches(\":popover-open\")) {\n try {\n tooltip.showPopover();\n } catch {\n return false;\n }\n }\n openChip = chip;\n positionTooltipSurface(tooltip, chip, \"top\");\n document.addEventListener(\"pointerdown\", dismiss, true);\n document.addEventListener(\"keydown\", escape);\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return true;\n };\n // An open hint follows its reaction: new names show at once, and it closes when the reaction, its names, or its\n // chip go away.\n createEffect(\n on(hinted, (reaction) => {\n if (!openChip) return;\n if (!reaction || !openChip.isConnected) close();\n else positionTooltipSurface(tooltip, openChip, \"top\");\n }),\n );\n const delayed = (chip: HTMLElement) => {\n if (touch() && openChip) return;\n if (\n !byKey()\n .get(chip.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n ) {\n close();\n return;\n }\n clearTimer();\n if (openChip) open(chip, false);\n else timer = setTimeout(() => open(chip, false), HOVER_MS);\n };\n const enter = (event: PointerEvent) => {\n const chip = chipOf(event.target);\n if (chip && event.target === chip && event.pointerType !== \"touch\") delayed(chip);\n };\n const leave = (event: PointerEvent) => {\n if (event.target !== chipOf(event.target) || (touch() && openChip)) return;\n // Moving directly to another chip keeps an open hint available for its immediate update.\n const next = chipOf(event.relatedTarget);\n if (\n !next ||\n !byKey()\n .get(next.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n )\n close();\n };\n const focusIn = (event: FocusEvent) => {\n const chip = chipOf(event.target);\n focusedKey = chip instanceof HTMLButtonElement ? chip.dataset.reactionKey : undefined;\n if (chip?.matches(\":focus-visible\")) delayed(chip);\n };\n const focusOut = (event: FocusEvent) => {\n // Focus leaving for somewhere else ends the tracking; a removed chip reports no next target and keeps it.\n if (event.relatedTarget instanceof Node && !root?.contains(event.relatedTarget)) focusedKey = undefined;\n if (chipOf(event.target) && !chipOf(event.relatedTarget) && !(touch() && openChip)) close();\n };\n const resetSwallow = () => {\n swallowClick = false;\n document.removeEventListener(\"pointerdown\", resetSwallow, true);\n };\n const endPress = () => {\n clearTimer();\n press = undefined;\n document.removeEventListener(\"pointermove\", move, true);\n document.removeEventListener(\"pointerup\", up, true);\n document.removeEventListener(\"pointercancel\", cancel, true);\n };\n const down = (event: PointerEvent) => {\n resetSwallow();\n close();\n endPress();\n const chip = chipOf(event.target);\n if (!chip || (event.pointerType !== \"touch\" && event.pointerType !== \"pen\")) return;\n press = { chip, id: event.pointerId, x: event.clientX, y: event.clientY, held: false, moved: false };\n timer = setTimeout(() => {\n timer = undefined;\n if (!press || press.moved) return;\n // Only a hold that shows names takes the press; on a chip without names it stays an ordinary press.\n press.held = open(press.chip, true);\n }, HOLD_MS);\n document.addEventListener(\"pointermove\", move, true);\n document.addEventListener(\"pointerup\", up, true);\n document.addEventListener(\"pointercancel\", cancel, true);\n };\n const move = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (Math.hypot(event.clientX - press.x, event.clientY - press.y) > MOVE_TOLERANCE) {\n press.moved = true;\n clearTimer();\n }\n };\n const up = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (press.held) {\n swallowClick = true;\n // Some browsers send no click after a hold. Never consume the next real tap.\n document.addEventListener(\"pointerdown\", resetSwallow, true);\n } else if (!press.moved && !props.onToggle) open(press.chip, true);\n endPress();\n };\n const cancel = (event: PointerEvent) => {\n if (event.pointerId === press?.id) endPress();\n };\n const touchEnd = (event: TouchEvent) => {\n if (!swallowClick || !event.cancelable) return;\n // A cancelled touchend sends no click, so nothing is left to swallow.\n event.preventDefault();\n resetSwallow();\n };\n // Only the click of the held finger or pen is swallowed; a keyboard's click has no detail and always goes through.\n const click = (event: MouseEvent) => {\n if (!swallowClick || event.detail === 0) return;\n event.preventDefault();\n event.stopPropagation();\n resetSwallow();\n };\n const contextMenu = (event: MouseEvent) => {\n if (chipOf(event.target) && (press || swallowClick || (touch() && openChip))) event.preventDefault();\n };\n\n bar.addEventListener(\"pointerenter\", enter, true);\n bar.addEventListener(\"pointerleave\", leave, true);\n bar.addEventListener(\"focusin\", focusIn);\n bar.addEventListener(\"focusout\", focusOut);\n bar.addEventListener(\"pointerdown\", down);\n bar.addEventListener(\"touchend\", touchEnd, { passive: false });\n bar.addEventListener(\"click\", click, true);\n bar.addEventListener(\"contextmenu\", contextMenu);\n onCleanup(() => {\n close();\n endPress();\n resetSwallow();\n bar.removeEventListener(\"pointerenter\", enter, true);\n bar.removeEventListener(\"pointerleave\", leave, true);\n bar.removeEventListener(\"focusin\", focusIn);\n bar.removeEventListener(\"focusout\", focusOut);\n bar.removeEventListener(\"pointerdown\", down);\n bar.removeEventListener(\"touchend\", touchEnd);\n bar.removeEventListener(\"click\", click, true);\n bar.removeEventListener(\"contextmenu\", contextMenu);\n });\n });\n\n const chip = (key: string, reaction: Accessor<ReactionBarItem>) => {\n const label = (own: boolean) =>\n chatMessages().messageReaction({ emoji: reaction().emoji, count: reaction().count, names: reaction().label, own });\n return (\n <Show\n when={props.onToggle}\n fallback={\n <span\n class=\"k2b-reaction-bar__chip\"\n role=\"img\"\n aria-label={label(Boolean(reaction().own))}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n data-gesture-ignore\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </span>\n }\n >\n {(toggle) => (\n <button\n type=\"button\"\n class=\"k2b-reaction-bar__chip\"\n aria-pressed={reaction().own ? \"true\" : \"false\"}\n aria-label={label(false)}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n onClick={() => toggle()(key)}\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </button>\n )}\n </Show>\n );\n };\n const chips = () => <For each={keys()}>{(key) => <Show when={byKey().get(key)}>{(reaction) => chip(key, reaction)}</Show>}</For>;\n\n return (\n <div\n ref={root}\n class={props.class ? `k2b-reaction-bar ${props.class}` : \"k2b-reaction-bar\"}\n role=\"group\"\n aria-label={chatMessages().messageReactions}\n data-density={props.density ?? \"default\"}\n data-overflow={overflow()}\n data-add={props.onAdd ? (props.add ?? \"always\") : undefined}\n data-empty={props.reactions.length === 0 ? \"\" : undefined}\n >\n <Show when={overflow() === \"scroll\"} fallback={<div class=\"k2b-reaction-bar__list\">{chips()}</div>}>\n <Show when={shown().length > 0}>\n <ScrollArea orientation=\"horizontal\" class=\"k2b-reaction-bar__scroll\" tabindex={props.onToggle ? undefined : 0}>\n {chips()}\n </ScrollArea>\n </Show>\n </Show>\n <Show when={more() > 0}>\n <span class=\"k2b-reaction-bar__more\">\n <span aria-hidden=\"true\">+{more()}</span>\n <span class=\"k2b-sr-only\">{props.moreLabel?.(more()) ?? chatMessages().moreReactions({ count: more() })}</span>\n </span>\n </Show>\n <Show when={props.onAdd}>\n {(add) => (\n <span class={`k2b-reaction-bar__add${props.add === \"progressive\" ? \" k2b-progressive-action\" : \"\"}`}>\n <IconButton\n label={chatMessages().messageAddReaction}\n size=\"xs\"\n class=\"k2b-reaction-bar__add-button\"\n onClick={(event) => add()(event.currentTarget)}\n >\n <i class=\"ti ti-mood-plus\" aria-hidden=\"true\" />\n </IconButton>\n </span>\n )}\n </Show>\n <span\n ref={surface}\n class=\"k2b-tooltip k2b-reaction-bar__hint\"\n popover=\"manual\"\n role=\"tooltip\"\n aria-hidden=\"true\"\n data-touch={touch() ? \"\" : undefined}\n >\n {hint()}\n </span>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -97,15 +97,16 @@ export const wheelDelta = (axis, deltaX, deltaY, shiftKey) => {
97
97
  /**
98
98
  * The day start nearest to `position` in a strip of equal columns, or with `room` the nearer day edge at either end of
99
99
  * the visible room; ties keep the start. A `heading` of 1 or -1 only looks ahead or back, for a scroll that brought
100
- * something into view and must not slide back over it.
100
+ * something into view and must not slide back over it. The result is a whole pixel: a drawer can leave a fractional
101
+ * room, and WebKit drops the fraction of a scroll position, which would leave the strip short of the edge (#1713).
101
102
  */
102
103
  export const snapToColumn = (position, width, room, heading = 0) => {
103
104
  if (!(width > 0)) return position;
104
105
  // Half a pixel of slack keeps a strip already on an edge where it is.
105
106
  const edge = at => heading > 0 ? Math.ceil((at - 0.5) / width) : heading < 0 ? Math.floor((at + 0.5) / width) : Math.round(at / width);
106
107
  const start = edge(position) * width;
107
- if (room === undefined) return start;
108
+ if (room === undefined) return Math.round(start);
108
109
  const end = edge(position + room) * width - room;
109
- return Math.abs(end - position) < Math.abs(start - position) ? end : start;
110
+ return Math.round(Math.abs(end - position) < Math.abs(start - position) ? end : start);
110
111
  };
111
112
  //# sourceMappingURL=calendar-day-scroll.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/calendar-day-scroll.ts"],"sourcesContent":["/**\n * Scroll rules of the day and week views, free of the DOM so they can be tested.\n *\n * The day view runs down through the days and stops gently at each day start: within `STOP_ZONE` pixels of one the\n * wheel slows down, and a scroll that rests close to one glides onto it. The week view scrolls sideways through the\n * days; a wheel or trackpad gesture keeps the axis it started on, and a resting scroll glides to the nearest day start\n * or, with end space, day end at the edge of the visible room. A scroll no gesture made, such as a reveal, glides on in\n * its own direction, so what it brought into view stays there.\n */\n\n/** Pixels before and after a day start in which the wheel slows down. */\nexport const STOP_ZONE = 72;\n/** How much of a wheel step remains right at a day start. */\nexport const STOP_MIN_FACTOR = 0.16;\n/** A resting scroll this close to a day start glides onto it: a wheel or trackpad, and a finger that flings further. */\nexport const SETTLE_RADIUS = { wheel: 40, touch: 88 } as const;\n/** A pause between wheel events that starts a new gesture. */\nexport const GESTURE_GAP_MS = 150;\n\n/** A wheel delta in pixels, whatever unit the browser reports it in. */\nexport const wheelPixels = (delta: number, mode: number, page: number): number => delta * (mode === 1 ? 40 : mode === 2 ? page : 1);\n\n/** The index of the last stop at or before a position; `stops` are sorted. */\nexport const stopIndexAt = (stops: readonly number[], position: number): number => {\n let low = 0;\n let high = stops.length - 1;\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n if (stops[middle]! <= position + 2) low = middle;\n else high = middle - 1;\n }\n return Math.max(0, low);\n};\n\nexport const nearestStop = (stops: readonly number[], position: number): number | undefined => {\n if (stops.length === 0) return undefined;\n const index = stopIndexAt(stops, position);\n const candidates = [stops[index], stops[index + 1]].filter((value): value is number => value !== undefined);\n return candidates.reduce((best, value) => (Math.abs(value - position) < Math.abs(best - position) ? value : best), candidates[0]!);\n};\n\n/** How much of a wheel step moves the view at a position: less the closer it is to a stop, eased. */\nexport const stopFactor = (stops: readonly number[], position: number): number => {\n const stop = nearestStop(stops, position);\n if (stop === undefined) return 1;\n const distance = Math.abs(position - stop);\n if (distance >= STOP_ZONE) return 1;\n const share = distance / STOP_ZONE;\n return STOP_MIN_FACTOR + (1 - STOP_MIN_FACTOR) * share * share * (3 - 2 * share);\n};\n\n/**\n * Where a wheel step of `delta` pixels takes the view, braked near stops, or `undefined` when no stop lies within\n * reach and the browser scrolls as usual. The first stop at 0 brakes nothing: there is no day before it to leave.\n */\nexport const brakedScroll = (stops: readonly number[], position: number, delta: number): number | undefined => {\n if (delta === 0) return undefined;\n const direction = Math.sign(delta);\n const reach = [position - STOP_ZONE, position + delta + direction * STOP_ZONE].sort((left, right) => left - right);\n if (!stops.some((stop) => stop > 0 && stop >= reach[0]! && stop <= reach[1]!)) return undefined;\n let next = position;\n let left = Math.abs(delta);\n while (left > 0) {\n const step = Math.min(2, left);\n next += direction * step * stopFactor(stops, next);\n left -= step;\n }\n return next;\n};\n\n/** The stop a resting scroll glides onto, or `undefined` when it rests on one or too far from any. */\nexport const settleTarget = (stops: readonly number[], position: number, radius: number): number | undefined => {\n const stop = nearestStop(stops, position);\n if (stop === undefined) return undefined;\n const distance = Math.abs(position - stop);\n return distance > 0.5 && distance < radius ? stop : undefined;\n};\n\n/** How far a header has crossed its stop, 0 to 1, for the line that fills while a day start passes. */\nexport const stopCrossing = (stop: number, position: number): number => {\n const distance = Math.abs(stop - position);\n return distance < STOP_ZONE ? 1 - distance / STOP_ZONE : 0;\n};\n\nexport type WheelAxis = \"x\" | \"y\";\n\n/** The axis a wheel gesture takes from its first event: Shift turns a vertical wheel sideways. */\nexport const wheelAxis = (deltaX: number, deltaY: number, shiftKey: boolean): WheelAxis => {\n if (shiftKey && Math.abs(deltaY) > Math.abs(deltaX)) return \"x\";\n return Math.abs(deltaX) > Math.abs(deltaY) ? \"x\" : \"y\";\n};\n\n/** The sideways part of a wheel event on an axis, Shift turning the vertical delta into a sideways one. */\nexport const wheelDelta = (axis: WheelAxis, deltaX: number, deltaY: number, shiftKey: boolean): number => {\n if (axis === \"y\") return deltaY;\n return shiftKey && deltaX === 0 ? deltaY : deltaX;\n};\n\n/**\n * The day start nearest to `position` in a strip of equal columns, or with `room` the nearer day edge at either end of\n * the visible room; ties keep the start. A `heading` of 1 or -1 only looks ahead or back, for a scroll that brought\n * something into view and must not slide back over it.\n */\nexport const snapToColumn = (position: number, width: number, room?: number, heading = 0): number => {\n if (!(width > 0)) return position;\n // Half a pixel of slack keeps a strip already on an edge where it is.\n const edge = (at: number) =>\n heading > 0 ? Math.ceil((at - 0.5) / width) : heading < 0 ? Math.floor((at + 0.5) / width) : Math.round(at / width);\n const start = edge(position) * width;\n if (room === undefined) return start;\n const end = edge(position + room) * width - room;\n return Math.abs(end - position) < Math.abs(start - position) ? end : start;\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,MAAM,SAAS,GAAG,EAAE;AAC3B;AACA,OAAO,MAAM,eAAe,GAAG,IAAI;AACnC;AACA,OAAO,MAAM,aAAa,GAAG;EAAE,KAAK,EAAE,EAAE;EAAE,KAAK,EAAE;AAAG,CAAU;AAC9D;AACA,OAAO,MAAM,cAAc,GAAG,GAAG;;AAEjC;AACA,OAAO,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,IAAY,EAAE,IAAY,KAAa,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;;AAEnI;AACA,OAAO,MAAM,WAAW,GAAG,CAAC,KAAwB,EAAE,QAAgB,KAAa;EACjF,IAAI,GAAG,GAAG,CAAC;EACX,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;EAC3B,OAAO,GAAG,GAAG,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,CAAC,IAAK,QAAQ,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,KAC5C,IAAI,GAAG,MAAM,GAAG,CAAC;EACxB;EACA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC;AACzB,CAAC;AAED,OAAO,MAAM,WAAW,GAAG,CAAC,KAAwB,EAAE,QAAgB,KAAyB;EAC7F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,OAAO,SAAS;EACxC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC;EAC1C,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAE,KAAK,IAAsB,KAAK,KAAK,SAAS,CAAC;EAC3G,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,KAAM,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,KAAK,GAAG,IAAK,EAAE,UAAU,CAAC,CAAC,CAAE,CAAC;AACpI,CAAC;;AAED;AACA,OAAO,MAAM,UAAU,GAAG,CAAC,KAAwB,EAAE,QAAgB,KAAa;EAChF,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC;EACzC,IAAI,IAAI,KAAK,SAAS,EAAE,OAAO,CAAC;EAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;EAC1C,IAAI,QAAQ,IAAI,SAAS,EAAE,OAAO,CAAC;EACnC,MAAM,KAAK,GAAG,QAAQ,GAAG,SAAS;EAClC,OAAO,eAAe,GAAG,CAAC,CAAC,GAAG,eAAe,IAAI,KAAK,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAClF,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,MAAM,YAAY,GAAG,CAAC,KAAwB,EAAE,QAAgB,EAAE,KAAa,KAAyB;EAC7G,IAAI,KAAK,KAAK,CAAC,EAAE,OAAO,SAAS;EACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;EAClC,MAAM,KAAK,GAAG,CAAC,QAAQ,GAAG,SAAS,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC;EAClH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAE,IAAI,IAAK,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAE,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,OAAO,SAAS;EAC/F,IAAI,IAAI,GAAG,QAAQ;EACnB,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;EAC1B,OAAO,IAAI,GAAG,CAAC,EAAE;IACf,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,IAAI,IAAI,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC;IAClD,IAAI,IAAI,IAAI;EACd;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA,OAAO,MAAM,YAAY,GAAG,CAAC,KAAwB,EAAE,QAAgB,EAAE,MAAc,KAAyB;EAC9G,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC;EACzC,IAAI,IAAI,KAAK,SAAS,EAAE,OAAO,SAAS;EACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;EAC1C,OAAO,QAAQ,GAAG,GAAG,IAAI,QAAQ,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS;AAC/D,CAAC;;AAED;AACA,OAAO,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,QAAgB,KAAa;EACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;EAC1C,OAAO,QAAQ,GAAG,SAAS,GAAG,CAAC,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC5D,CAAC;AAID;AACA,OAAO,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,MAAc,EAAE,QAAiB,KAAgB;EACzF,IAAI,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG;EAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG;AACxD,CAAC;;AAED;AACA,OAAO,MAAM,UAAU,GAAG,CAAC,IAAe,EAAE,MAAc,EAAE,MAAc,EAAE,QAAiB,KAAa;EACxG,IAAI,IAAI,KAAK,GAAG,EAAE,OAAO,MAAM;EAC/B,OAAO,QAAQ,IAAI,MAAM,KAAK,CAAC,GAAG,MAAM,GAAG,MAAM;AACnD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAa,EAAE,OAAO,GAAG,CAAC,KAAa;EACnG,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,OAAO,QAAQ;EACjC;EACA,MAAM,IAAI,GAAI,EAAU,IACtB,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC;EACrH,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK;EACpC,IAAI,IAAI,KAAK,SAAS,EAAE,OAAO,KAAK;EACpC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI;EAChD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,GAAG,GAAG,KAAK;AAC5E,CAAC","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/calendar-day-scroll.ts"],"sourcesContent":["/**\n * Scroll rules of the day and week views, free of the DOM so they can be tested.\n *\n * The day view runs down through the days and stops gently at each day start: within `STOP_ZONE` pixels of one the\n * wheel slows down, and a scroll that rests close to one glides onto it. The week view scrolls sideways through the\n * days; a wheel or trackpad gesture keeps the axis it started on, and a resting scroll glides to the nearest day start\n * or, with end space, day end at the edge of the visible room. A scroll no gesture made, such as a reveal, glides on in\n * its own direction, so what it brought into view stays there.\n */\n\n/** Pixels before and after a day start in which the wheel slows down. */\nexport const STOP_ZONE = 72;\n/** How much of a wheel step remains right at a day start. */\nexport const STOP_MIN_FACTOR = 0.16;\n/** A resting scroll this close to a day start glides onto it: a wheel or trackpad, and a finger that flings further. */\nexport const SETTLE_RADIUS = { wheel: 40, touch: 88 } as const;\n/** A pause between wheel events that starts a new gesture. */\nexport const GESTURE_GAP_MS = 150;\n\n/** A wheel delta in pixels, whatever unit the browser reports it in. */\nexport const wheelPixels = (delta: number, mode: number, page: number): number => delta * (mode === 1 ? 40 : mode === 2 ? page : 1);\n\n/** The index of the last stop at or before a position; `stops` are sorted. */\nexport const stopIndexAt = (stops: readonly number[], position: number): number => {\n let low = 0;\n let high = stops.length - 1;\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n if (stops[middle]! <= position + 2) low = middle;\n else high = middle - 1;\n }\n return Math.max(0, low);\n};\n\nexport const nearestStop = (stops: readonly number[], position: number): number | undefined => {\n if (stops.length === 0) return undefined;\n const index = stopIndexAt(stops, position);\n const candidates = [stops[index], stops[index + 1]].filter((value): value is number => value !== undefined);\n return candidates.reduce((best, value) => (Math.abs(value - position) < Math.abs(best - position) ? value : best), candidates[0]!);\n};\n\n/** How much of a wheel step moves the view at a position: less the closer it is to a stop, eased. */\nexport const stopFactor = (stops: readonly number[], position: number): number => {\n const stop = nearestStop(stops, position);\n if (stop === undefined) return 1;\n const distance = Math.abs(position - stop);\n if (distance >= STOP_ZONE) return 1;\n const share = distance / STOP_ZONE;\n return STOP_MIN_FACTOR + (1 - STOP_MIN_FACTOR) * share * share * (3 - 2 * share);\n};\n\n/**\n * Where a wheel step of `delta` pixels takes the view, braked near stops, or `undefined` when no stop lies within\n * reach and the browser scrolls as usual. The first stop at 0 brakes nothing: there is no day before it to leave.\n */\nexport const brakedScroll = (stops: readonly number[], position: number, delta: number): number | undefined => {\n if (delta === 0) return undefined;\n const direction = Math.sign(delta);\n const reach = [position - STOP_ZONE, position + delta + direction * STOP_ZONE].sort((left, right) => left - right);\n if (!stops.some((stop) => stop > 0 && stop >= reach[0]! && stop <= reach[1]!)) return undefined;\n let next = position;\n let left = Math.abs(delta);\n while (left > 0) {\n const step = Math.min(2, left);\n next += direction * step * stopFactor(stops, next);\n left -= step;\n }\n return next;\n};\n\n/** The stop a resting scroll glides onto, or `undefined` when it rests on one or too far from any. */\nexport const settleTarget = (stops: readonly number[], position: number, radius: number): number | undefined => {\n const stop = nearestStop(stops, position);\n if (stop === undefined) return undefined;\n const distance = Math.abs(position - stop);\n return distance > 0.5 && distance < radius ? stop : undefined;\n};\n\n/** How far a header has crossed its stop, 0 to 1, for the line that fills while a day start passes. */\nexport const stopCrossing = (stop: number, position: number): number => {\n const distance = Math.abs(stop - position);\n return distance < STOP_ZONE ? 1 - distance / STOP_ZONE : 0;\n};\n\nexport type WheelAxis = \"x\" | \"y\";\n\n/** The axis a wheel gesture takes from its first event: Shift turns a vertical wheel sideways. */\nexport const wheelAxis = (deltaX: number, deltaY: number, shiftKey: boolean): WheelAxis => {\n if (shiftKey && Math.abs(deltaY) > Math.abs(deltaX)) return \"x\";\n return Math.abs(deltaX) > Math.abs(deltaY) ? \"x\" : \"y\";\n};\n\n/** The sideways part of a wheel event on an axis, Shift turning the vertical delta into a sideways one. */\nexport const wheelDelta = (axis: WheelAxis, deltaX: number, deltaY: number, shiftKey: boolean): number => {\n if (axis === \"y\") return deltaY;\n return shiftKey && deltaX === 0 ? deltaY : deltaX;\n};\n\n/**\n * The day start nearest to `position` in a strip of equal columns, or with `room` the nearer day edge at either end of\n * the visible room; ties keep the start. A `heading` of 1 or -1 only looks ahead or back, for a scroll that brought\n * something into view and must not slide back over it. The result is a whole pixel: a drawer can leave a fractional\n * room, and WebKit drops the fraction of a scroll position, which would leave the strip short of the edge (#1713).\n */\nexport const snapToColumn = (position: number, width: number, room?: number, heading = 0): number => {\n if (!(width > 0)) return position;\n // Half a pixel of slack keeps a strip already on an edge where it is.\n const edge = (at: number) =>\n heading > 0 ? Math.ceil((at - 0.5) / width) : heading < 0 ? Math.floor((at + 0.5) / width) : Math.round(at / width);\n const start = edge(position) * width;\n if (room === undefined) return Math.round(start);\n const end = edge(position + room) * width - room;\n return Math.round(Math.abs(end - position) < Math.abs(start - position) ? end : start);\n};\n"],"mappings":"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","ignoreList":[]}