@k2b/ui 0.17.0 → 0.19.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 (228) hide show
  1. package/README.md +3 -2
  2. package/dist/browser/actions/ContextMenu.js +3 -1
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/actions/Dropdown.js +91 -46
  5. package/dist/browser/actions/Dropdown.js.map +1 -1
  6. package/dist/browser/actions/FilterChip.js +4 -1
  7. package/dist/browser/actions/FilterChip.js.map +1 -1
  8. package/dist/browser/chat/ChatComposer.js +1311 -565
  9. package/dist/browser/chat/ChatComposer.js.map +1 -1
  10. package/dist/browser/chat/ChatPrimitives.js +9 -6
  11. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  12. package/dist/browser/chat/chat-behavior.js +9 -0
  13. package/dist/browser/chat/chat-behavior.js.map +1 -1
  14. package/dist/browser/chat/index.js.map +1 -1
  15. package/dist/browser/chat/types.js.map +1 -1
  16. package/dist/browser/content/Calendar.js +579 -533
  17. package/dist/browser/content/Calendar.js.map +1 -1
  18. package/dist/browser/content/FileView.js +62 -36
  19. package/dist/browser/content/FileView.js.map +1 -1
  20. package/dist/browser/content/Lightbox.js +76 -40
  21. package/dist/browser/content/Lightbox.js.map +1 -1
  22. package/dist/browser/content/MarkdownView.js +9 -3
  23. package/dist/browser/content/MarkdownView.js.map +1 -1
  24. package/dist/browser/content/PdfPreview.js +146 -79
  25. package/dist/browser/content/PdfPreview.js.map +1 -1
  26. package/dist/browser/content/Timeline.js +1038 -0
  27. package/dist/browser/content/Timeline.js.map +1 -0
  28. package/dist/browser/content/VideoPlayer.js +299 -0
  29. package/dist/browser/content/VideoPlayer.js.map +1 -0
  30. package/dist/browser/content/chart-inspection.js +7 -2
  31. package/dist/browser/content/chart-inspection.js.map +1 -1
  32. package/dist/browser/content/file-view-preview.js +2 -2
  33. package/dist/browser/content/file-view-preview.js.map +1 -1
  34. package/dist/browser/content/index.js +3 -0
  35. package/dist/browser/content/index.js.map +1 -1
  36. package/dist/browser/content/markdown-links.js +178 -0
  37. package/dist/browser/content/markdown-links.js.map +1 -0
  38. package/dist/browser/content/timeline-model.js +579 -0
  39. package/dist/browser/content/timeline-model.js.map +1 -0
  40. package/dist/browser/feedback/dialog-core.js +26 -14
  41. package/dist/browser/feedback/dialog-core.js.map +1 -1
  42. package/dist/browser/inputs/ChoiceChips.js +66 -0
  43. package/dist/browser/inputs/ChoiceChips.js.map +1 -0
  44. package/dist/browser/inputs/ChoiceGroups.js +53 -12
  45. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  46. package/dist/browser/inputs/FileDropTarget.js +129 -0
  47. package/dist/browser/inputs/FileDropTarget.js.map +1 -0
  48. package/dist/browser/inputs/FileInputs.js +84 -43
  49. package/dist/browser/inputs/FileInputs.js.map +1 -1
  50. package/dist/browser/inputs/SignatureInput.js +558 -0
  51. package/dist/browser/inputs/SignatureInput.js.map +1 -0
  52. package/dist/browser/inputs/emoji/EmojiPicker.js +841 -0
  53. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -0
  54. package/dist/browser/inputs/emoji/emoji-data.js +1903 -0
  55. package/dist/browser/inputs/emoji/emoji-data.js.map +1 -0
  56. package/dist/browser/inputs/emoji/emoji-index.js +209 -0
  57. package/dist/browser/inputs/emoji/emoji-index.js.map +1 -0
  58. package/dist/browser/inputs/emoji/emoji-messages.js +64 -0
  59. package/dist/browser/inputs/emoji/emoji-messages.js.map +1 -0
  60. package/dist/browser/inputs/file-drop.js +338 -0
  61. package/dist/browser/inputs/file-drop.js.map +1 -0
  62. package/dist/browser/inputs/index.js +6 -0
  63. package/dist/browser/inputs/index.js.map +1 -1
  64. package/dist/browser/inputs/markdown/MarkdownEditor.js +72 -78
  65. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  66. package/dist/browser/inputs/markdown/actions.js +48 -2
  67. package/dist/browser/inputs/markdown/actions.js.map +1 -1
  68. package/dist/browser/inputs/markdown/code-zone.js +38 -2
  69. package/dist/browser/inputs/markdown/code-zone.js.map +1 -1
  70. package/dist/browser/inputs/signature-messages.js +39 -0
  71. package/dist/browser/inputs/signature-messages.js.map +1 -0
  72. package/dist/browser/inputs/signature.js +208 -0
  73. package/dist/browser/inputs/signature.js.map +1 -0
  74. package/dist/browser/internal/render-error.js +49 -0
  75. package/dist/browser/internal/render-error.js.map +1 -0
  76. package/dist/browser/intl/messages.js +205 -4
  77. package/dist/browser/intl/messages.js.map +1 -1
  78. package/dist/browser/layout/AppWorkspace.js +483 -218
  79. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  80. package/dist/browser/layout/FloatingWindow.js +4 -1
  81. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  82. package/dist/browser/layout/Navigation.js +274 -197
  83. package/dist/browser/layout/Navigation.js.map +1 -1
  84. package/dist/browser/layout/SidebarItemPreview.js +5 -1
  85. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  86. package/dist/browser/layout/index.js.map +1 -1
  87. package/dist/browser/layout/navigation-model.js +2 -0
  88. package/dist/browser/layout/navigation-model.js.map +1 -1
  89. package/dist/browser/surfaces/NoticeCard.js +47 -44
  90. package/dist/browser/surfaces/NoticeCard.js.map +1 -1
  91. package/dist/browser/surfaces/ResourceCard.js +212 -0
  92. package/dist/browser/surfaces/ResourceCard.js.map +1 -0
  93. package/dist/browser/surfaces/index.js +1 -0
  94. package/dist/browser/surfaces/index.js.map +1 -1
  95. package/dist/licenses/emojibase-data.txt +21 -0
  96. package/dist/ssr/actions/ContextMenu.js +3 -1
  97. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  98. package/dist/ssr/actions/Dropdown.js +49 -12
  99. package/dist/ssr/actions/Dropdown.js.map +1 -1
  100. package/dist/ssr/actions/FilterChip.js +4 -1
  101. package/dist/ssr/actions/FilterChip.js.map +1 -1
  102. package/dist/ssr/chat/ChatComposer.js +823 -221
  103. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  104. package/dist/ssr/chat/ChatPrimitives.js +9 -6
  105. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  106. package/dist/ssr/chat/chat-behavior.js +9 -0
  107. package/dist/ssr/chat/chat-behavior.js.map +1 -1
  108. package/dist/ssr/chat/index.js.map +1 -1
  109. package/dist/ssr/chat/types.js.map +1 -1
  110. package/dist/ssr/content/Calendar.js +183 -142
  111. package/dist/ssr/content/Calendar.js.map +1 -1
  112. package/dist/ssr/content/FileView.js +30 -6
  113. package/dist/ssr/content/FileView.js.map +1 -1
  114. package/dist/ssr/content/Lightbox.js +19 -5
  115. package/dist/ssr/content/Lightbox.js.map +1 -1
  116. package/dist/ssr/content/MarkdownView.js +9 -3
  117. package/dist/ssr/content/MarkdownView.js.map +1 -1
  118. package/dist/ssr/content/PdfPreview.js +77 -36
  119. package/dist/ssr/content/PdfPreview.js.map +1 -1
  120. package/dist/ssr/content/Timeline.js +804 -0
  121. package/dist/ssr/content/Timeline.js.map +1 -0
  122. package/dist/ssr/content/VideoPlayer.js +228 -0
  123. package/dist/ssr/content/VideoPlayer.js.map +1 -0
  124. package/dist/ssr/content/chart-inspection.js +7 -2
  125. package/dist/ssr/content/chart-inspection.js.map +1 -1
  126. package/dist/ssr/content/file-view-preview.js +2 -2
  127. package/dist/ssr/content/file-view-preview.js.map +1 -1
  128. package/dist/ssr/content/index.js +3 -0
  129. package/dist/ssr/content/index.js.map +1 -1
  130. package/dist/ssr/content/markdown-links.js +178 -0
  131. package/dist/ssr/content/markdown-links.js.map +1 -0
  132. package/dist/ssr/content/timeline-model.js +579 -0
  133. package/dist/ssr/content/timeline-model.js.map +1 -0
  134. package/dist/ssr/feedback/dialog-core.js +26 -14
  135. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  136. package/dist/ssr/inputs/ChoiceChips.js +66 -0
  137. package/dist/ssr/inputs/ChoiceChips.js.map +1 -0
  138. package/dist/ssr/inputs/ChoiceGroups.js +12 -4
  139. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  140. package/dist/ssr/inputs/FileDropTarget.js +103 -0
  141. package/dist/ssr/inputs/FileDropTarget.js.map +1 -0
  142. package/dist/ssr/inputs/FileInputs.js +49 -12
  143. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  144. package/dist/ssr/inputs/SignatureInput.js +404 -0
  145. package/dist/ssr/inputs/SignatureInput.js.map +1 -0
  146. package/dist/ssr/inputs/emoji/EmojiPicker.js +551 -0
  147. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -0
  148. package/dist/ssr/inputs/emoji/emoji-data.js +1903 -0
  149. package/dist/ssr/inputs/emoji/emoji-data.js.map +1 -0
  150. package/dist/ssr/inputs/emoji/emoji-index.js +209 -0
  151. package/dist/ssr/inputs/emoji/emoji-index.js.map +1 -0
  152. package/dist/ssr/inputs/emoji/emoji-messages.js +64 -0
  153. package/dist/ssr/inputs/emoji/emoji-messages.js.map +1 -0
  154. package/dist/ssr/inputs/file-drop.js +338 -0
  155. package/dist/ssr/inputs/file-drop.js.map +1 -0
  156. package/dist/ssr/inputs/index.js +6 -0
  157. package/dist/ssr/inputs/index.js.map +1 -1
  158. package/dist/ssr/inputs/markdown/MarkdownEditor.js +14 -2
  159. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  160. package/dist/ssr/inputs/markdown/actions.js +48 -2
  161. package/dist/ssr/inputs/markdown/actions.js.map +1 -1
  162. package/dist/ssr/inputs/markdown/code-zone.js +38 -2
  163. package/dist/ssr/inputs/markdown/code-zone.js.map +1 -1
  164. package/dist/ssr/inputs/signature-messages.js +39 -0
  165. package/dist/ssr/inputs/signature-messages.js.map +1 -0
  166. package/dist/ssr/inputs/signature.js +208 -0
  167. package/dist/ssr/inputs/signature.js.map +1 -0
  168. package/dist/ssr/internal/render-error.js +49 -0
  169. package/dist/ssr/internal/render-error.js.map +1 -0
  170. package/dist/ssr/intl/messages.js +205 -4
  171. package/dist/ssr/intl/messages.js.map +1 -1
  172. package/dist/ssr/layout/AppWorkspace.js +264 -47
  173. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  174. package/dist/ssr/layout/FloatingWindow.js +4 -1
  175. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  176. package/dist/ssr/layout/Navigation.js +169 -122
  177. package/dist/ssr/layout/Navigation.js.map +1 -1
  178. package/dist/ssr/layout/SidebarItemPreview.js +5 -1
  179. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  180. package/dist/ssr/layout/index.js.map +1 -1
  181. package/dist/ssr/layout/navigation-model.js +2 -0
  182. package/dist/ssr/layout/navigation-model.js.map +1 -1
  183. package/dist/ssr/surfaces/NoticeCard.js +17 -12
  184. package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
  185. package/dist/ssr/surfaces/ResourceCard.js +115 -0
  186. package/dist/ssr/surfaces/ResourceCard.js.map +1 -0
  187. package/dist/ssr/surfaces/index.js +1 -0
  188. package/dist/ssr/surfaces/index.js.map +1 -1
  189. package/dist/styles.css +1 -1
  190. package/dist/types/actions/Dropdown.d.ts +13 -1
  191. package/dist/types/actions/FilterChip.d.ts +5 -0
  192. package/dist/types/chat/ChatComposer.d.ts +24 -1
  193. package/dist/types/chat/ChatPrimitives.d.ts +1 -1
  194. package/dist/types/chat/chat-behavior.d.ts +15 -0
  195. package/dist/types/chat/index.d.ts +1 -1
  196. package/dist/types/chat/types.d.ts +34 -0
  197. package/dist/types/content/Calendar.d.ts +28 -8
  198. package/dist/types/content/Lightbox.d.ts +8 -0
  199. package/dist/types/content/Timeline.d.ts +49 -0
  200. package/dist/types/content/VideoPlayer.d.ts +41 -0
  201. package/dist/types/content/index.d.ts +6 -2
  202. package/dist/types/content/markdown-links.d.ts +66 -0
  203. package/dist/types/content/timeline-model.d.ts +148 -0
  204. package/dist/types/feedback/dialog-core.d.ts +1 -1
  205. package/dist/types/inputs/ChoiceChips.d.ts +20 -0
  206. package/dist/types/inputs/ChoiceGroups.d.ts +11 -2
  207. package/dist/types/inputs/FileDropTarget.d.ts +23 -0
  208. package/dist/types/inputs/FileInputs.d.ts +8 -0
  209. package/dist/types/inputs/SignatureInput.d.ts +22 -0
  210. package/dist/types/inputs/emoji/EmojiPicker.d.ts +42 -0
  211. package/dist/types/inputs/emoji/emoji-data.d.ts +2 -0
  212. package/dist/types/inputs/emoji/emoji-index.d.ts +109 -0
  213. package/dist/types/inputs/emoji/emoji-messages.d.ts +30 -0
  214. package/dist/types/inputs/file-drop.d.ts +80 -0
  215. package/dist/types/inputs/index.d.ts +12 -0
  216. package/dist/types/inputs/markdown/actions.d.ts +4 -0
  217. package/dist/types/inputs/markdown/code-zone.d.ts +7 -0
  218. package/dist/types/inputs/signature-messages.d.ts +11 -0
  219. package/dist/types/inputs/signature.d.ts +72 -0
  220. package/dist/types/internal/render-error.d.ts +13 -0
  221. package/dist/types/intl/messages.d.ts +101 -0
  222. package/dist/types/layout/AppWorkspace.d.ts +38 -0
  223. package/dist/types/layout/SidebarItemPreview.d.ts +2 -0
  224. package/dist/types/layout/index.d.ts +2 -2
  225. package/dist/types/layout/navigation-model.d.ts +18 -0
  226. package/dist/types/surfaces/ResourceCard.d.ts +31 -0
  227. package/dist/types/surfaces/index.d.ts +2 -0
  228. package/package.json +5 -4
@@ -1,9 +1,10 @@
1
1
  import { template as _$template } from "solid-js/web";
2
2
  import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
- import { memo as _$memo } from "solid-js/web";
3
+ import { className as _$className } from "solid-js/web";
4
4
  import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
5
5
  import { setAttribute as _$setAttribute } from "solid-js/web";
6
6
  import { effect as _$effect } from "solid-js/web";
7
+ import { memo as _$memo } from "solid-js/web";
7
8
  import { createComponent as _$createComponent } from "solid-js/web";
8
9
  import { getNextElement as _$getNextElement } from "solid-js/web";
9
10
  import { getNextMarker as _$getNextMarker } from "solid-js/web";
@@ -15,12 +16,16 @@ var _tmpl$ = /*#__PURE__*/_$template(`<span class=k2b-content-lightbox__counter
15
16
  _tmpl$4 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__nav data-direction=previous><i class="ti ti-chevron-left"aria-hidden=true>`),
16
17
  _tmpl$5 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__nav data-direction=next><i class="ti ti-chevron-right"aria-hidden=true>`),
17
18
  _tmpl$6 = /*#__PURE__*/_$template(`<div class=k2b-content-lightbox__dots role=group>`),
18
- _tmpl$7 = /*#__PURE__*/_$template(`<dialog class=k2b-content-lightbox><div class=k2b-content-lightbox__bar><div class=k2b-content-lightbox__meta><!$><!/><!$><!/></div><div class=k2b-content-lightbox__actions><!$><!/><button type=button class=k2b-content-lightbox__button><i class="ti ti-x"aria-hidden=true></i><span class=k2b-content-lightbox__button-label></span></button></div></div><div class=k2b-content-lightbox__stage></div><!$><!/><!$><!/>`),
19
- _tmpl$8 = /*#__PURE__*/_$template(`<img class=k2b-content-lightbox__image>`),
20
- _tmpl$9 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__dot>`);
19
+ _tmpl$7 = /*#__PURE__*/_$template(`<dialog class=k2b-content-lightbox><div class=k2b-content-lightbox__bar><div class=k2b-content-lightbox__meta><!$><!/><!$><!/></div><div class=k2b-content-lightbox__actions><!$><!/><!$><!/><button type=button class=k2b-content-lightbox__button><i class="ti ti-x"aria-hidden=true></i><span class=k2b-content-lightbox__button-label></span></button></div></div><div class=k2b-content-lightbox__stage></div><!$><!/><!$><!/>`),
20
+ _tmpl$8 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__button><i aria-hidden=true></i><span class=k2b-content-lightbox__button-label>`),
21
+ _tmpl$9 = /*#__PURE__*/_$template(`<img class=k2b-content-lightbox__image>`),
22
+ _tmpl$0 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__dot>`);
21
23
  import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js";
22
24
  import { returnFocus, ringOnReturn } from "../internal/focus-return";
23
25
  import { useUiMessages } from "../intl/messages";
26
+
27
+ /** One more thing to do with the shown image, beside Download: a short label, its icon class, and what it does. */
28
+
24
29
  /**
25
30
  * Minimal, accessible lightbox using native <dialog>.
26
31
  * Supports keyboard navigation, touch swipe gestures, and screen readers.
@@ -70,8 +75,13 @@ export default function Lightbox(props) {
70
75
  props.onClose();
71
76
  };
72
77
 
78
+ // A key belongs to the Lightbox while focus is inside it, or on the page with no other dialog open. A dialog opened
79
+ // from one of its actions keeps its own keys: Escape closes that dialog, and arrows stay in its fields.
80
+ const ownsKey = target => target instanceof Node && (dialogRef.contains(target) || target === document.body && Array.from(document.querySelectorAll("dialog[open]")).every(dialog => dialog === dialogRef));
81
+
73
82
  // Keyboard navigation
74
83
  const handleKeyDown = e => {
84
+ if (!ownsKey(e.target)) return;
75
85
  switch (e.key) {
76
86
  case "Escape":
77
87
  // Handled here, so WebKit does not light the returned focus for this key.
@@ -147,14 +157,16 @@ export default function Lightbox(props) {
147
157
  _el$14 = _el$3.nextSibling,
148
158
  _el$21 = _el$14.firstChild,
149
159
  [_el$22, _co$5] = _$getNextMarker(_el$21.nextSibling),
150
- _el$18 = _el$22.nextSibling,
160
+ _el$23 = _el$22.nextSibling,
161
+ [_el$24, _co$6] = _$getNextMarker(_el$23.nextSibling),
162
+ _el$18 = _el$24.nextSibling,
151
163
  _el$19 = _el$18.firstChild,
152
164
  _el$20 = _el$19.nextSibling,
153
- _el$23 = _el$2.nextSibling,
154
- _el$27 = _el$23.nextSibling,
155
- [_el$28, _co$6] = _$getNextMarker(_el$27.nextSibling),
156
- _el$29 = _el$28.nextSibling,
157
- [_el$30, _co$7] = _$getNextMarker(_el$29.nextSibling);
165
+ _el$25 = _el$2.nextSibling,
166
+ _el$29 = _el$25.nextSibling,
167
+ [_el$30, _co$7] = _$getNextMarker(_el$29.nextSibling),
168
+ _el$31 = _el$30.nextSibling,
169
+ [_el$32, _co$8] = _$getNextMarker(_el$31.nextSibling);
158
170
  _el$.$$touchend = handleTouchEnd;
159
171
  _el$.$$touchstart = handleTouchStart;
160
172
  _el$.addEventListener("cancel", handleCancel);
@@ -188,6 +200,30 @@ export default function Lightbox(props) {
188
200
  return _el$0;
189
201
  }
190
202
  }), _el$13, _co$4);
203
+ _$insert(_el$14, _$createComponent(For, {
204
+ get each() {
205
+ return current()?.actions ?? [];
206
+ },
207
+ children: action => (() => {
208
+ var _el$33 = _$getNextElement(_tmpl$8),
209
+ _el$34 = _el$33.firstChild,
210
+ _el$35 = _el$34.nextSibling;
211
+ _el$33.$$click = () => action.onClick();
212
+ _$insert(_el$35, () => action.label);
213
+ _$effect(_p$ => {
214
+ var _v$5 = action.label,
215
+ _v$6 = action.icon;
216
+ _v$5 !== _p$.e && _$setAttribute(_el$33, "aria-label", _p$.e = _v$5);
217
+ _v$6 !== _p$.t && _$className(_el$34, _p$.t = _v$6);
218
+ return _p$;
219
+ }, {
220
+ e: undefined,
221
+ t: undefined
222
+ });
223
+ _$runHydrationEvents();
224
+ return _el$33;
225
+ })()
226
+ }), _el$22, _co$5);
191
227
  _$insert(_el$14, _$createComponent(Show, {
192
228
  get when() {
193
229
  return current()?.downloadUrl;
@@ -209,27 +245,27 @@ export default function Lightbox(props) {
209
245
  });
210
246
  return _el$15;
211
247
  }
212
- }), _el$22, _co$5);
248
+ }), _el$24, _co$6);
213
249
  _el$18.$$click = close;
214
250
  _$insert(_el$20, () => messages().close);
215
- _$insert(_el$23, _$createComponent(Show, {
251
+ _$insert(_el$25, _$createComponent(Show, {
216
252
  get when() {
217
253
  return current();
218
254
  },
219
255
  children: image => (() => {
220
- var _el$31 = _$getNextElement(_tmpl$8);
221
- _$setAttribute(_el$31, "draggable", false);
256
+ var _el$36 = _$getNextElement(_tmpl$9);
257
+ _$setAttribute(_el$36, "draggable", false);
222
258
  _$effect(_p$ => {
223
- var _v$5 = image().src,
224
- _v$6 = image().alt ?? "";
225
- _v$5 !== _p$.e && _$setAttribute(_el$31, "src", _p$.e = _v$5);
226
- _v$6 !== _p$.t && _$setAttribute(_el$31, "alt", _p$.t = _v$6);
259
+ var _v$7 = image().src,
260
+ _v$8 = image().alt ?? "";
261
+ _v$7 !== _p$.e && _$setAttribute(_el$36, "src", _p$.e = _v$7);
262
+ _v$8 !== _p$.t && _$setAttribute(_el$36, "alt", _p$.t = _v$8);
227
263
  return _p$;
228
264
  }, {
229
265
  e: undefined,
230
266
  t: undefined
231
267
  });
232
- return _el$31;
268
+ return _el$36;
233
269
  })()
234
270
  }));
235
271
  _$insert(_el$, _$createComponent(Show, {
@@ -238,53 +274,53 @@ export default function Lightbox(props) {
238
274
  },
239
275
  get children() {
240
276
  return [(() => {
241
- var _el$24 = _$getNextElement(_tmpl$4);
242
- _el$24.$$click = prev;
243
- _$effect(() => _$setAttribute(_el$24, "aria-label", messages().previousImage));
277
+ var _el$26 = _$getNextElement(_tmpl$4);
278
+ _el$26.$$click = prev;
279
+ _$effect(() => _$setAttribute(_el$26, "aria-label", messages().previousImage));
244
280
  _$runHydrationEvents();
245
- return _el$24;
281
+ return _el$26;
246
282
  })(), (() => {
247
- var _el$25 = _$getNextElement(_tmpl$5);
248
- _el$25.$$click = next;
249
- _$effect(() => _$setAttribute(_el$25, "aria-label", messages().nextImage));
283
+ var _el$27 = _$getNextElement(_tmpl$5);
284
+ _el$27.$$click = next;
285
+ _$effect(() => _$setAttribute(_el$27, "aria-label", messages().nextImage));
250
286
  _$runHydrationEvents();
251
- return _el$25;
287
+ return _el$27;
252
288
  })()];
253
289
  }
254
- }), _el$28, _co$6);
290
+ }), _el$30, _co$7);
255
291
  _$insert(_el$, _$createComponent(Show, {
256
292
  get when() {
257
293
  return _$memo(() => !!isMultiple())() && props.images.length <= 10;
258
294
  },
259
295
  get children() {
260
- var _el$26 = _$getNextElement(_tmpl$6);
261
- _$insert(_el$26, _$createComponent(For, {
296
+ var _el$28 = _$getNextElement(_tmpl$6);
297
+ _$insert(_el$28, _$createComponent(For, {
262
298
  get each() {
263
299
  return props.images;
264
300
  },
265
301
  children: (_, i) => (() => {
266
- var _el$32 = _$getNextElement(_tmpl$9);
267
- _el$32.$$click = () => setIndex(i());
302
+ var _el$37 = _$getNextElement(_tmpl$0);
303
+ _el$37.$$click = () => setIndex(i());
268
304
  _$effect(_p$ => {
269
- var _v$7 = index() === i() ? "true" : undefined,
270
- _v$8 = messages().goToImage({
305
+ var _v$9 = index() === i() ? "true" : undefined,
306
+ _v$0 = messages().goToImage({
271
307
  index: i() + 1
272
308
  });
273
- _v$7 !== _p$.e && _$setAttribute(_el$32, "aria-current", _p$.e = _v$7);
274
- _v$8 !== _p$.t && _$setAttribute(_el$32, "aria-label", _p$.t = _v$8);
309
+ _v$9 !== _p$.e && _$setAttribute(_el$37, "aria-current", _p$.e = _v$9);
310
+ _v$0 !== _p$.t && _$setAttribute(_el$37, "aria-label", _p$.t = _v$0);
275
311
  return _p$;
276
312
  }, {
277
313
  e: undefined,
278
314
  t: undefined
279
315
  });
280
316
  _$runHydrationEvents();
281
- return _el$32;
317
+ return _el$37;
282
318
  })()
283
319
  }));
284
- _$effect(() => _$setAttribute(_el$26, "aria-label", messages().imageNavigation));
285
- return _el$26;
320
+ _$effect(() => _$setAttribute(_el$28, "aria-label", messages().imageNavigation));
321
+ return _el$28;
286
322
  }
287
- }), _el$30, _co$7);
323
+ }), _el$32, _co$8);
288
324
  _$effect(_p$ => {
289
325
  var _v$3 = messages().imageLightbox,
290
326
  _v$4 = messages().closeLightbox;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/Lightbox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, onCleanup, onMount, Show } from \"solid-js\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\nexport type LightboxImage = {\n src: string;\n alt?: string;\n downloadUrl?: string;\n};\n\ntype LightboxProps = {\n images: LightboxImage[];\n initialIndex?: number;\n onClose: () => void;\n};\n\n/**\n * Minimal, accessible lightbox using native <dialog>.\n * Supports keyboard navigation, touch swipe gestures, and screen readers.\n */\nexport default function Lightbox(props: LightboxProps) {\n const messages = useUiMessages();\n const clampIndex = (value: number, images = props.images): number =>\n images.length === 0 ? 0 : Math.max(0, Math.min(value, images.length - 1));\n const [index, setIndex] = createSignal(clampIndex(props.initialIndex ?? 0));\n let dialogRef!: HTMLDialogElement;\n let opener: HTMLElement | null = null;\n let openerRing = true;\n\n createEffect(\n (previous: { images: LightboxImage[]; initialIndex: number | undefined }) => {\n const images = props.images;\n const initialIndex = props.initialIndex;\n setIndex((currentIndex) => {\n if (initialIndex !== previous.initialIndex) return clampIndex(initialIndex ?? 0, images);\n const previousImage = previous.images[currentIndex];\n const retained = previousImage ? images.findIndex((image) => image === previousImage || image.src === previousImage.src) : -1;\n return retained >= 0 ? retained : clampIndex(currentIndex, images);\n });\n return { images, initialIndex };\n },\n { images: props.images, initialIndex: props.initialIndex },\n );\n\n // Touch gesture tracking\n let touchStartX = 0;\n let touchStartY = 0;\n const SWIPE_THRESHOLD = 50;\n\n const current = () => props.images[index()];\n const isMultiple = () => props.images.length > 1;\n const prev = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i - 1 + props.images.length) % props.images.length);\n };\n const next = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i + 1) % props.images.length);\n };\n\n const close = () => {\n dialogRef.close();\n returnFocus(opener, openerRing);\n props.onClose();\n };\n\n // Keyboard navigation\n const handleKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case \"Escape\":\n // Handled here, so WebKit does not light the returned focus for this key.\n e.preventDefault();\n close();\n break;\n case \"ArrowLeft\":\n prev();\n break;\n case \"ArrowRight\":\n next();\n break;\n }\n };\n\n // Touch handlers for swipe gestures\n const handleTouchStart = (e: TouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n }\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n const touch = e.changedTouches[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n\n // Only trigger swipe if horizontal movement is dominant\n if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > SWIPE_THRESHOLD) {\n if (deltaX > 0) {\n prev();\n } else {\n next();\n }\n }\n };\n\n // Click on backdrop closes lightbox\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === dialogRef) {\n close();\n }\n };\n\n // The browser cancels the dialog itself, as on the Android back gesture;\n // closing here still returns focus and tells the owner.\n const handleCancel = (e: Event) => {\n e.preventDefault();\n close();\n };\n\n // Registration and teardown both live in onMount: onCleanup also runs when an\n // SSR render is disposed, where `document` does not exist.\n onMount(() => {\n opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n openerRing = ringOnReturn(opener);\n // The native dialog would return focus with a ring of the browser's choosing.\n opener?.blur();\n dialogRef.showModal();\n document.addEventListener(\"keydown\", handleKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", handleKeyDown));\n });\n\n return (\n <dialog\n ref={dialogRef}\n class=\"k2b-content-lightbox\"\n onMouseDown={handleBackdropClick}\n onCancel={handleCancel}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n aria-label={messages().imageLightbox}\n >\n {/* Top bar */}\n <div class=\"k2b-content-lightbox__bar\">\n <div class=\"k2b-content-lightbox__meta\">\n <Show when={isMultiple()}>\n <span class=\"k2b-content-lightbox__counter\" aria-live=\"polite\">\n {index() + 1} / {props.images.length}\n </span>\n </Show>\n <Show when={current()?.alt}>\n <div class=\"k2b-content-lightbox__caption\">\n <div class=\"k2b-content-lightbox__caption-text\">{current()?.alt}</div>\n </div>\n </Show>\n </div>\n\n <div class=\"k2b-content-lightbox__actions\">\n <Show when={current()?.downloadUrl}>\n <a href={current()!.downloadUrl} download=\"\" class=\"k2b-content-lightbox__button\" aria-label={messages().downloadImage}>\n <i class=\"ti ti-download\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().download}</span>\n </a>\n </Show>\n <button type=\"button\" onClick={close} class=\"k2b-content-lightbox__button\" aria-label={messages().closeLightbox}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().close}</span>\n </button>\n </div>\n </div>\n\n <div class=\"k2b-content-lightbox__stage\">\n <Show when={current()}>\n {(image) => <img src={image().src} alt={image().alt ?? \"\"} class=\"k2b-content-lightbox__image\" draggable={false} />}\n </Show>\n </div>\n\n <Show when={isMultiple()}>\n <button\n type=\"button\"\n onClick={prev}\n class=\"k2b-content-lightbox__nav\"\n data-direction=\"previous\"\n aria-label={messages().previousImage}\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </button>\n <button type=\"button\" onClick={next} class=\"k2b-content-lightbox__nav\" data-direction=\"next\" aria-label={messages().nextImage}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </button>\n </Show>\n\n <Show when={isMultiple() && props.images.length <= 10}>\n <div class=\"k2b-content-lightbox__dots\" role=\"group\" aria-label={messages().imageNavigation}>\n <For each={props.images}>\n {(_, i) => (\n <button\n type=\"button\"\n onClick={() => setIndex(i())}\n class=\"k2b-content-lightbox__dot\"\n aria-current={index() === i() ? \"true\" : undefined}\n aria-label={messages().goToImage({ index: i() + 1 })}\n />\n )}\n </For>\n </div>\n </Show>\n </dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,QAAQ,UAAU;AACpF,SAAS,WAAW,EAAE,YAAY,QAAQ,0BAA0B;AACpE,SAAS,aAAa,QAAQ,kBAAkB;AAchD;AACA;AACA;AACA;AACA,eAAe,SAAS,QAAQ,CAAC,KAAoB,EAAE;EACrD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,KACtD,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;EAC3E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;EAC3E,IAAI,SAA6B;EACjC,IAAI,MAA0B,GAAG,IAAI;EACrC,IAAI,UAAU,GAAG,IAAI;EAErB,YAAY,CACT,QAAuE,IAAK;IAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;IAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY;IACvC,QAAQ,CAAE,YAAY,IAAK;MACzB,IAAI,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,OAAO,UAAU,CAAC,YAAY,IAAI,CAAC,EAAE,MAAM,CAAC;MACxF,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC;MACnD,MAAM,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC,SAAS,CAAE,KAAK,IAAK,KAAK,KAAK,aAAa,IAAI,KAAK,CAAC,GAAG,KAAK,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;MAC7H,OAAO,QAAQ,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,CAAC,YAAY,EAAE,MAAM,CAAC;IACpE,CAAC,CAAC;IACF,OAAO;MAAE,MAAM;MAAE;IAAa,CAAC;EACjC,CAAC,EACD;IAAE,MAAM,EAAE,KAAK,CAAC,MAAM;IAAE,YAAY,EAAE,KAAK,CAAC;EAAa,CAC3D,CAAC;;EAED;EACA,IAAI,WAAW,GAAG,CAAC;EACnB,IAAI,WAAW,GAAG,CAAC;EACnB,MAAM,eAAe,GAAG,EAAE;EAE1B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;EAC3C,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;EAChD,MAAM,IAAI,GAAG,MAAM;IACjB,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE;IACnB,QAAQ,CAAE,CAAC,IAAK,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;EACtE,CAAC;EACD,MAAM,IAAI,GAAG,MAAM;IACjB,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE;IACnB,QAAQ,CAAE,CAAC,IAAK,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;EAChD,CAAC;EAED,MAAM,KAAK,GAAG,MAAM;IAClB,SAAS,CAAC,KAAK,CAAC,CAAC;IACjB,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC;IAC/B,KAAK,CAAC,OAAO,CAAC,CAAC;EACjB,CAAC;;EAED;EACA,MAAM,aAAa,GAAI,CAAgB,IAAK;IAC1C,QAAQ,CAAC,CAAC,GAAG;MACX,KAAK,QAAQ;QACX;QACA,CAAC,CAAC,cAAc,CAAC,CAAC;QAClB,KAAK,CAAC,CAAC;QACP;MACF,KAAK,WAAW;QACd,IAAI,CAAC,CAAC;QACN;MACF,KAAK,YAAY;QACf,IAAI,CAAC,CAAC;QACN;IACJ;EACF,CAAC;;EAED;EACA,MAAM,gBAAgB,GAAI,CAAa,IAAK;IAC1C,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1B,IAAI,KAAK,EAAE;MACT,WAAW,GAAG,KAAK,CAAC,OAAO;MAC3B,WAAW,GAAG,KAAK,CAAC,OAAO;IAC7B;EACF,CAAC;EAED,MAAM,cAAc,GAAI,CAAa,IAAK;IACxC,MAAM,KAAK,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE;IAEZ,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,WAAW;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,WAAW;;IAE1C;IACA,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,eAAe,EAAE;MAC7E,IAAI,MAAM,GAAG,CAAC,EAAE;QACd,IAAI,CAAC,CAAC;MACR,CAAC,MAAM;QACL,IAAI,CAAC,CAAC;MACR;IACF;EACF,CAAC;;EAED;EACA,MAAM,mBAAmB,GAAI,CAAa,IAAK;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE;MAC1B,KAAK,CAAC,CAAC;IACT;EACF,CAAC;;EAED;EACA;EACA,MAAM,YAAY,GAAI,CAAQ,IAAK;IACjC,CAAC,CAAC,cAAc,CAAC,CAAC;IAClB,KAAK,CAAC,CAAC;EACT,CAAC;;EAED;EACA;EACA,OAAO,CAAC,MAAM;IACZ,MAAM,GAAG,QAAQ,CAAC,aAAa,YAAY,WAAW,GAAG,QAAQ,CAAC,aAAa,GAAG,IAAI;IACtF,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC;IACjC;IACA,MAAM,EAAE,IAAI,CAAC,CAAC;IACd,SAAS,CAAC,SAAS,CAAC,CAAC;IACrB,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC;IACnD,SAAS,CAAC,MAAM,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;EACzE,CAAC,CAAC;EAEF;IAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;IAAA,kBAOgB,cAAc;IAAA,oBADZ,gBAAgB;IAAA,gCADpB,YAAY;IAAA,mBADT,mBAAmB;IAAA,YAF3B,SAAS;IAAA,mDAAT,SAAS;IAAA,kCAWT,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,UAAU,CAAC,CAAC;MAAA;MAAA;QAAA;UAAA;UAAA;UAAA;UAAA;UAAA;QAAA,sBAEnB,KAAK,CAAC,CAAC,GAAG,CAAC;QAAA,sBAAK,KAAK,CAAC,MAAM,CAAC,MAAM;QAAA;MAAA;IAAA;IAAA,kCAGvC,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,OAAO,CAAC,CAAC,EAAE,GAAG;MAAA;MAAA;QAAA;UAAA;QAAA,sBAE2B,OAAO,CAAC,CAAC,EAAE,GAAG;QAAA;MAAA;IAAA;IAAA,mCAMlE,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,OAAO,CAAC,CAAC,EAAE,WAAW;MAAA;MAAA;QAAA;UAAA;UAAA;QAAA,uBAGoB,QAAQ,CAAC,CAAC,CAAC,QAAQ;QAAA;UAAA,UAF9D,OAAO,CAAC,CAAC,CAAE,WAAW;YAAA,OAA+D,QAAQ,CAAC,CAAC,CAAC,aAAa;UAAA;UAAA;UAAA;QAAA;UAAA;UAAA;QAAA;QAAA;MAAA;IAAA;IAAA,iBAKzF,KAAK;IAAA,uBAEgB,QAAQ,CAAC,CAAC,CAAC,KAAK;IAAA,mCAMrE,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,OAAO,CAAC,CAAC;MAAA;MAAA,UACjB,KAAK;QAAA;QAAA,oCAAmG,KAAK;QAAA;UAAA,WAAzF,KAAK,CAAC,CAAC,CAAC,GAAG;YAAA,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE;UAAA;UAAA;UAAA;QAAA;UAAA;UAAA;QAAA;QAAA;MAAA;IAA0D;IAAA,iCAItH,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,UAAU,CAAC,CAAC;MAAA;MAAA;QAAA;UAAA;UAAA,iBAGX,IAAI;UAAA,oDAGD,QAAQ,CAAC,CAAC,CAAC,aAAa;UAAA;UAAA;QAAA;UAAA;UAAA,iBAIP,IAAI;UAAA,oDAAsE,QAAQ,CAAC,CAAC,CAAC,SAAS;UAAA;UAAA;QAAA;MAAA;IAAA;IAAA,iCAK9H,IAAI;MAAA,IAAC,IAAI;QAAA,OAAE,yBAAU,CAAC,CAAC,OAAI,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE;MAAA;MAAA;QAAA;QAAA,mCAEhD,GAAG;UAAA,IAAC,IAAI;YAAA,OAAE,KAAK,CAAC,MAAM;UAAA;UAAA,UACpB,CAAC,CAAC,EAAE,CAAC;YAAA;YAAA,iBAGO,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAAA;cAAA,WAEd,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,SAAS;gBAAA,OACtC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;kBAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG;gBAAE,CAAC,CAAC;cAAA;cAAA;cAAA;YAAA;cAAA;cAAA;YAAA;YAAA;YAAA;UAAA;QAEvD;QAAA,oDAV4D,QAAQ,CAAC,CAAC,CAAC,eAAe;QAAA;MAAA;IAAA;IAAA;MAAA,WArDjF,QAAQ,CAAC,CAAC,CAAC,aAAa;QAAA,OAwBuD,QAAQ,CAAC,CAAC,CAAC,aAAa;MAAA;MAAA;MAAA;IAAA;MAAA;MAAA;IAAA;IAAA;IAAA;EAAA;AA6CzH;AAAC","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/Lightbox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, onCleanup, onMount, Show } from \"solid-js\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\n/** One more thing to do with the shown image, beside Download: a short label, its icon class, and what it does. */\nexport type LightboxAction = {\n label: string;\n icon: string;\n onClick: () => void;\n};\n\nexport type LightboxImage = {\n src: string;\n alt?: string;\n downloadUrl?: string;\n /** Actions for this image, shown before Download in the same style. */\n actions?: readonly LightboxAction[];\n};\n\ntype LightboxProps = {\n images: LightboxImage[];\n initialIndex?: number;\n onClose: () => void;\n};\n\n/**\n * Minimal, accessible lightbox using native <dialog>.\n * Supports keyboard navigation, touch swipe gestures, and screen readers.\n */\nexport default function Lightbox(props: LightboxProps) {\n const messages = useUiMessages();\n const clampIndex = (value: number, images = props.images): number =>\n images.length === 0 ? 0 : Math.max(0, Math.min(value, images.length - 1));\n const [index, setIndex] = createSignal(clampIndex(props.initialIndex ?? 0));\n let dialogRef!: HTMLDialogElement;\n let opener: HTMLElement | null = null;\n let openerRing = true;\n\n createEffect(\n (previous: { images: LightboxImage[]; initialIndex: number | undefined }) => {\n const images = props.images;\n const initialIndex = props.initialIndex;\n setIndex((currentIndex) => {\n if (initialIndex !== previous.initialIndex) return clampIndex(initialIndex ?? 0, images);\n const previousImage = previous.images[currentIndex];\n const retained = previousImage ? images.findIndex((image) => image === previousImage || image.src === previousImage.src) : -1;\n return retained >= 0 ? retained : clampIndex(currentIndex, images);\n });\n return { images, initialIndex };\n },\n { images: props.images, initialIndex: props.initialIndex },\n );\n\n // Touch gesture tracking\n let touchStartX = 0;\n let touchStartY = 0;\n const SWIPE_THRESHOLD = 50;\n\n const current = () => props.images[index()];\n const isMultiple = () => props.images.length > 1;\n const prev = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i - 1 + props.images.length) % props.images.length);\n };\n const next = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i + 1) % props.images.length);\n };\n\n const close = () => {\n dialogRef.close();\n returnFocus(opener, openerRing);\n props.onClose();\n };\n\n // A key belongs to the Lightbox while focus is inside it, or on the page with no other dialog open. A dialog opened\n // from one of its actions keeps its own keys: Escape closes that dialog, and arrows stay in its fields.\n const ownsKey = (target: EventTarget | null) =>\n target instanceof Node &&\n (dialogRef.contains(target) ||\n (target === document.body && Array.from(document.querySelectorAll(\"dialog[open]\")).every((dialog) => dialog === dialogRef)));\n\n // Keyboard navigation\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!ownsKey(e.target)) return;\n switch (e.key) {\n case \"Escape\":\n // Handled here, so WebKit does not light the returned focus for this key.\n e.preventDefault();\n close();\n break;\n case \"ArrowLeft\":\n prev();\n break;\n case \"ArrowRight\":\n next();\n break;\n }\n };\n\n // Touch handlers for swipe gestures\n const handleTouchStart = (e: TouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n }\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n const touch = e.changedTouches[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n\n // Only trigger swipe if horizontal movement is dominant\n if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > SWIPE_THRESHOLD) {\n if (deltaX > 0) {\n prev();\n } else {\n next();\n }\n }\n };\n\n // Click on backdrop closes lightbox\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === dialogRef) {\n close();\n }\n };\n\n // The browser cancels the dialog itself, as on the Android back gesture;\n // closing here still returns focus and tells the owner.\n const handleCancel = (e: Event) => {\n e.preventDefault();\n close();\n };\n\n // Registration and teardown both live in onMount: onCleanup also runs when an\n // SSR render is disposed, where `document` does not exist.\n onMount(() => {\n opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n openerRing = ringOnReturn(opener);\n // The native dialog would return focus with a ring of the browser's choosing.\n opener?.blur();\n dialogRef.showModal();\n document.addEventListener(\"keydown\", handleKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", handleKeyDown));\n });\n\n return (\n <dialog\n ref={dialogRef}\n class=\"k2b-content-lightbox\"\n onMouseDown={handleBackdropClick}\n onCancel={handleCancel}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n aria-label={messages().imageLightbox}\n >\n {/* Top bar */}\n <div class=\"k2b-content-lightbox__bar\">\n <div class=\"k2b-content-lightbox__meta\">\n <Show when={isMultiple()}>\n <span class=\"k2b-content-lightbox__counter\" aria-live=\"polite\">\n {index() + 1} / {props.images.length}\n </span>\n </Show>\n <Show when={current()?.alt}>\n <div class=\"k2b-content-lightbox__caption\">\n <div class=\"k2b-content-lightbox__caption-text\">{current()?.alt}</div>\n </div>\n </Show>\n </div>\n\n <div class=\"k2b-content-lightbox__actions\">\n <For each={current()?.actions ?? []}>\n {(action) => (\n <button type=\"button\" onClick={() => action.onClick()} class=\"k2b-content-lightbox__button\" aria-label={action.label}>\n <i class={action.icon} aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{action.label}</span>\n </button>\n )}\n </For>\n <Show when={current()?.downloadUrl}>\n <a href={current()!.downloadUrl} download=\"\" class=\"k2b-content-lightbox__button\" aria-label={messages().downloadImage}>\n <i class=\"ti ti-download\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().download}</span>\n </a>\n </Show>\n <button type=\"button\" onClick={close} class=\"k2b-content-lightbox__button\" aria-label={messages().closeLightbox}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().close}</span>\n </button>\n </div>\n </div>\n\n <div class=\"k2b-content-lightbox__stage\">\n <Show when={current()}>\n {(image) => <img src={image().src} alt={image().alt ?? \"\"} class=\"k2b-content-lightbox__image\" draggable={false} />}\n </Show>\n </div>\n\n <Show when={isMultiple()}>\n <button\n type=\"button\"\n onClick={prev}\n class=\"k2b-content-lightbox__nav\"\n data-direction=\"previous\"\n aria-label={messages().previousImage}\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </button>\n <button type=\"button\" onClick={next} class=\"k2b-content-lightbox__nav\" data-direction=\"next\" aria-label={messages().nextImage}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </button>\n </Show>\n\n <Show when={isMultiple() && props.images.length <= 10}>\n <div class=\"k2b-content-lightbox__dots\" role=\"group\" aria-label={messages().imageNavigation}>\n <For each={props.images}>\n {(_, i) => (\n <button\n type=\"button\"\n onClick={() => setIndex(i())}\n class=\"k2b-content-lightbox__dot\"\n aria-current={index() === i() ? \"true\" : undefined}\n aria-label={messages().goToImage({ index: i() + 1 })}\n />\n )}\n </For>\n </div>\n </Show>\n </dialog>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -8,6 +8,7 @@ var _tmpl$ = /*#__PURE__*/_$template(`<div>`);
8
8
  import { Marked, Renderer } from "marked";
9
9
  import { useLocale } from "../intl/locale";
10
10
  import { markdownInfoBlocks } from "./markdown-info-blocks";
11
+ import { markdownLinkReference, renderMarkdownLink } from "./markdown-links";
11
12
  export const escapeHtml = value => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
12
13
  const safeUrl = value => {
13
14
  const normalized = value.trim().replace(/[\u0000-\u001f\u007f-\u009f\s]+/g, "");
@@ -58,9 +59,14 @@ export const createSafeRenderer = (options = {}) => {
58
59
  return body;
59
60
  }
60
61
  }
61
- const titleAttribute = title ? ` title="${escapeHtml(title)}"` : "";
62
- const target = options.linkTarget === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : "";
63
- return `<a href="${escapeHtml(url)}"${titleAttribute}${target}>${body}</a>`;
62
+ return renderMarkdownLink({
63
+ href: url,
64
+ html: body,
65
+ title: title ?? undefined,
66
+ target: options.linkTarget,
67
+ reference: markdownLinkReference(url),
68
+ locale: options.locale
69
+ });
64
70
  };
65
71
  renderer.image = ({
66
72
  href,
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/MarkdownView.tsx"],"sourcesContent":["import { Marked, Renderer, type Tokens } from \"marked\";\nimport { useLocale } from \"../intl/locale\";\nimport { markdownInfoBlocks } from \"./markdown-info-blocks\";\n\ntype CommonProps = MarkdownRenderOptions & {\n /** Optional additional CSS classes */\n class?: string;\n /**\n * Visual heading scale. Markdown heading levels and document structure stay\n * unchanged.\n */\n headingScale?: \"compact\" | \"normal\" | \"large\";\n};\n\nexport type MarkdownViewProps = CommonProps &\n (\n | {\n /** Untrusted Markdown. Raw HTML and unsafe link protocols are escaped. */\n markdown: string;\n /** Exact standalone text tokens to emphasize without changing Markdown parsing or trusted-HTML boundaries. */\n inlineTokens?: readonly string[];\n trustedHtml?: never;\n }\n | {\n /** Explicitly trusted, already-rendered HTML. The caller owns sanitization. */\n trustedHtml: string;\n markdown?: never;\n inlineTokens?: never;\n }\n );\n\nexport type MarkdownRenderOptions = {\n inlineTokens?: readonly string[];\n /** Whether to render images. False renders alt text without resource requests. Defaults to true. */\n allowImages?: boolean;\n /** Optional protocol allowlist, including trailing colons. Relative URLs resolve as HTTPS. */\n linkProtocols?: readonly string[];\n linkTarget?: \"_blank\";\n /** Locale for the screen-reader names of untitled info blocks. `MarkdownView` passes the inherited render locale. */\n locale?: string;\n};\n\nexport const escapeHtml = (value: string): string =>\n value.replaceAll(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#39;\");\n\nconst safeUrl = (value: string): string | null => {\n const normalized = value.trim().replace(/[\\u0000-\\u001f\\u007f-\\u009f\\s]+/g, \"\");\n if (/^(?:javascript|vbscript|data):/i.test(normalized)) return null;\n return value;\n};\n\nconst normalizeInlineTokens = (tokens: readonly string[] | undefined): string[] =>\n [...new Set(tokens?.filter(Boolean) ?? [])].sort((left, right) => right.length - left.length);\n\nconst isStandaloneInlineToken = (text: string, start: number, value: string): boolean => {\n const before = text[start - 1];\n if (before && /[a-zA-Z0-9_.%+\\-]/.test(before)) return false;\n const end = start + value.length;\n const after = text[end];\n if (after && /[a-zA-Z0-9_@]/.test(after)) return false;\n return !(after === \".\" && /[a-zA-Z0-9_]/.test(text[end + 1] ?? \"\"));\n};\n\nexport const createSafeRenderer = (options: MarkdownRenderOptions = {}): Renderer => {\n const renderer = new Renderer();\n const inlineTokens = normalizeInlineTokens(options.inlineTokens);\n const baseText = renderer.text.bind(renderer);\n // marked flags text that follows an inline <pre>, <code>, <kbd> or <script>\n // tag as already escaped, because it expects that tag to reach the output as\n // HTML. The tag is escaped here, so the text after it is escaped as well.\n const renderText: Renderer[\"text\"] = (token) => baseText(token.type === \"text\" && token.escaped ? { ...token, escaped: false } : token);\n const renderTable = renderer.table.bind(renderer);\n renderer.table = (token: Tokens.Table) => {\n const table = renderTable(token);\n const content = token.header.every((cell) => !cell.text.trim()) ? table.replace(/<thead>[\\s\\S]*?<\\/thead>\\n?/, \"\") : table;\n return `<div class=\"k2b-content-markdown__table\" tabindex=\"0\">${content}</div>`;\n };\n renderer.html = ({ text }: Tokens.HTML | Tokens.Tag) => escapeHtml(text);\n renderer.link = function ({ href, title, tokens }: Tokens.Link) {\n const body = this.parser.parseInline(tokens);\n const url = safeUrl(href);\n if (!url) return body;\n if (options.linkProtocols) {\n try {\n if (!options.linkProtocols.includes(new URL(url, \"https://markdown.invalid/\").protocol)) return body;\n } catch {\n return body;\n }\n }\n const titleAttribute = title ? ` title=\"${escapeHtml(title)}\"` : \"\";\n const target = options.linkTarget === \"_blank\" ? ' target=\"_blank\" rel=\"noopener noreferrer\"' : \"\";\n return `<a href=\"${escapeHtml(url)}\"${titleAttribute}${target}>${body}</a>`;\n };\n renderer.image = ({ href, title, text }: Tokens.Image) => {\n const url = safeUrl(href);\n if (!url || options.allowImages === false) return escapeHtml(text);\n const titleAttribute = title ? ` title=\"${escapeHtml(title)}\"` : \"\";\n return `<img src=\"${escapeHtml(url)}\" alt=\"${escapeHtml(text)}\"${titleAttribute}>`;\n };\n renderer.text = (token) => {\n if (inlineTokens.length === 0 || token.type === \"escape\" || (\"tokens\" in token && token.tokens)) return renderText(token);\n const parts: string[] = [];\n let plainStart = 0;\n let cursor = 0;\n while (cursor < token.text.length) {\n const inlineToken = inlineTokens.find(\n (candidate) => token.text.startsWith(candidate, cursor) && isStandaloneInlineToken(token.text, cursor, candidate),\n );\n if (!inlineToken) {\n cursor += 1;\n continue;\n }\n if (plainStart < cursor) {\n const text = token.text.slice(plainStart, cursor);\n parts.push(renderText({ ...token, raw: text, text }));\n }\n parts.push(\n `<span class=\"k2b-content-markdown__inline-token\">${renderText({ ...token, raw: inlineToken, text: inlineToken })}</span>`,\n );\n cursor += inlineToken.length;\n plainStart = cursor;\n }\n if (plainStart === 0) return renderText(token);\n if (plainStart < token.text.length) {\n const text = token.text.slice(plainStart);\n parts.push(renderText({ ...token, raw: text, text }));\n }\n return parts.join(\"\");\n };\n return renderer;\n};\n\nexport const renderSafeMarkdown = (markdown: string, options: MarkdownRenderOptions = {}): string =>\n createSafeMarked(options.locale).parse(markdown, { async: false, renderer: createSafeRenderer(options) }) as string;\n\n/**\n * Internal to the package: the Markdown grammar of `renderSafeMarkdown`, for a component that renders with an adjusted\n * safe renderer or reads the tokens, as `MessageRow` does.\n */\nexport const createSafeMarked = (locale?: string): Marked => new Marked(markdownInfoBlocks({ locale }));\n\n/**\n * Markdown View Component (SSR)\n *\n * Renders untrusted Markdown safely by default. Raw HTML and unsafe link\n * protocols are escaped. Info blocks (`:::note`, `:::info`, `:::success`,\n * `:::warning`, `:::danger`) render as the calm `NoticeCard`. Already-rendered\n * HTML requires the deliberately named `trustedHtml` boundary; the caller owns\n * sanitization in that mode.\n *\n * @example\n * ```tsx\n * import { MarkdownView } from \"@k2b/ui\";\n *\n * <MarkdownView markdown={source} />;\n * ```\n *\n * @example Compact headings, e.g. inside comments or file previews:\n * ```tsx\n * <MarkdownView markdown={source} headingScale=\"compact\" />;\n * ```\n */\nexport default function MarkdownView(props: MarkdownViewProps) {\n const locale = useLocale();\n const html = () =>\n props.markdown !== undefined\n ? renderSafeMarkdown(props.markdown, {\n inlineTokens: props.inlineTokens,\n allowImages: props.allowImages,\n linkProtocols: props.linkProtocols,\n linkTarget: props.linkTarget,\n locale: locale(),\n })\n : props.trustedHtml;\n return (\n <div\n class={`k2b-content-markdown ${props.class ?? \"\"}`}\n data-heading-scale={props.headingScale && props.headingScale !== \"normal\" ? props.headingScale : undefined}\n innerHTML={html()}\n />\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/MarkdownView.tsx"],"sourcesContent":["import { Marked, Renderer, type Tokens } from \"marked\";\nimport { useLocale } from \"../intl/locale\";\nimport { markdownInfoBlocks } from \"./markdown-info-blocks\";\nimport { markdownLinkReference, renderMarkdownLink } from \"./markdown-links\";\n\ntype CommonProps = MarkdownRenderOptions & {\n /** Optional additional CSS classes */\n class?: string;\n /**\n * Visual heading scale. Markdown heading levels and document structure stay\n * unchanged.\n */\n headingScale?: \"compact\" | \"normal\" | \"large\";\n};\n\nexport type MarkdownViewProps = CommonProps &\n (\n | {\n /** Untrusted Markdown. Raw HTML and unsafe link protocols are escaped. */\n markdown: string;\n /** Exact standalone text tokens to emphasize without changing Markdown parsing or trusted-HTML boundaries. */\n inlineTokens?: readonly string[];\n trustedHtml?: never;\n }\n | {\n /** Explicitly trusted, already-rendered HTML. The caller owns sanitization. */\n trustedHtml: string;\n markdown?: never;\n inlineTokens?: never;\n }\n );\n\nexport type MarkdownRenderOptions = {\n inlineTokens?: readonly string[];\n /** Whether to render images. False renders alt text without resource requests. Defaults to true. */\n allowImages?: boolean;\n /** Optional protocol allowlist, including trailing colons. Relative URLs resolve as HTTPS. */\n linkProtocols?: readonly string[];\n linkTarget?: \"_blank\";\n /** Locale for the screen-reader names of untitled info blocks. `MarkdownView` passes the inherited render locale. */\n locale?: string;\n};\n\nexport const escapeHtml = (value: string): string =>\n value.replaceAll(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#39;\");\n\nconst safeUrl = (value: string): string | null => {\n const normalized = value.trim().replace(/[\\u0000-\\u001f\\u007f-\\u009f\\s]+/g, \"\");\n if (/^(?:javascript|vbscript|data):/i.test(normalized)) return null;\n return value;\n};\n\nconst normalizeInlineTokens = (tokens: readonly string[] | undefined): string[] =>\n [...new Set(tokens?.filter(Boolean) ?? [])].sort((left, right) => right.length - left.length);\n\nconst isStandaloneInlineToken = (text: string, start: number, value: string): boolean => {\n const before = text[start - 1];\n if (before && /[a-zA-Z0-9_.%+\\-]/.test(before)) return false;\n const end = start + value.length;\n const after = text[end];\n if (after && /[a-zA-Z0-9_@]/.test(after)) return false;\n return !(after === \".\" && /[a-zA-Z0-9_]/.test(text[end + 1] ?? \"\"));\n};\n\nexport const createSafeRenderer = (options: MarkdownRenderOptions = {}): Renderer => {\n const renderer = new Renderer();\n const inlineTokens = normalizeInlineTokens(options.inlineTokens);\n const baseText = renderer.text.bind(renderer);\n // marked flags text that follows an inline <pre>, <code>, <kbd> or <script>\n // tag as already escaped, because it expects that tag to reach the output as\n // HTML. The tag is escaped here, so the text after it is escaped as well.\n const renderText: Renderer[\"text\"] = (token) => baseText(token.type === \"text\" && token.escaped ? { ...token, escaped: false } : token);\n const renderTable = renderer.table.bind(renderer);\n renderer.table = (token: Tokens.Table) => {\n const table = renderTable(token);\n const content = token.header.every((cell) => !cell.text.trim()) ? table.replace(/<thead>[\\s\\S]*?<\\/thead>\\n?/, \"\") : table;\n return `<div class=\"k2b-content-markdown__table\" tabindex=\"0\">${content}</div>`;\n };\n renderer.html = ({ text }: Tokens.HTML | Tokens.Tag) => escapeHtml(text);\n renderer.link = function ({ href, title, tokens }: Tokens.Link) {\n const body = this.parser.parseInline(tokens);\n const url = safeUrl(href);\n if (!url) return body;\n if (options.linkProtocols) {\n try {\n if (!options.linkProtocols.includes(new URL(url, \"https://markdown.invalid/\").protocol)) return body;\n } catch {\n return body;\n }\n }\n return renderMarkdownLink({\n href: url,\n html: body,\n title: title ?? undefined,\n target: options.linkTarget,\n reference: markdownLinkReference(url),\n locale: options.locale,\n });\n };\n renderer.image = ({ href, title, text }: Tokens.Image) => {\n const url = safeUrl(href);\n if (!url || options.allowImages === false) return escapeHtml(text);\n const titleAttribute = title ? ` title=\"${escapeHtml(title)}\"` : \"\";\n return `<img src=\"${escapeHtml(url)}\" alt=\"${escapeHtml(text)}\"${titleAttribute}>`;\n };\n renderer.text = (token) => {\n if (inlineTokens.length === 0 || token.type === \"escape\" || (\"tokens\" in token && token.tokens)) return renderText(token);\n const parts: string[] = [];\n let plainStart = 0;\n let cursor = 0;\n while (cursor < token.text.length) {\n const inlineToken = inlineTokens.find(\n (candidate) => token.text.startsWith(candidate, cursor) && isStandaloneInlineToken(token.text, cursor, candidate),\n );\n if (!inlineToken) {\n cursor += 1;\n continue;\n }\n if (plainStart < cursor) {\n const text = token.text.slice(plainStart, cursor);\n parts.push(renderText({ ...token, raw: text, text }));\n }\n parts.push(\n `<span class=\"k2b-content-markdown__inline-token\">${renderText({ ...token, raw: inlineToken, text: inlineToken })}</span>`,\n );\n cursor += inlineToken.length;\n plainStart = cursor;\n }\n if (plainStart === 0) return renderText(token);\n if (plainStart < token.text.length) {\n const text = token.text.slice(plainStart);\n parts.push(renderText({ ...token, raw: text, text }));\n }\n return parts.join(\"\");\n };\n return renderer;\n};\n\nexport const renderSafeMarkdown = (markdown: string, options: MarkdownRenderOptions = {}): string =>\n createSafeMarked(options.locale).parse(markdown, { async: false, renderer: createSafeRenderer(options) }) as string;\n\n/**\n * Internal to the package: the Markdown grammar of `renderSafeMarkdown`, for a component that renders with an adjusted\n * safe renderer or reads the tokens, as `MessageRow` does.\n */\nexport const createSafeMarked = (locale?: string): Marked => new Marked(markdownInfoBlocks({ locale }));\n\n/**\n * Markdown View Component (SSR)\n *\n * Renders untrusted Markdown safely by default. Raw HTML and unsafe link\n * protocols are escaped. Info blocks (`:::note`, `:::info`, `:::success`,\n * `:::warning`, `:::danger`) render as the calm `NoticeCard`. Already-rendered\n * HTML requires the deliberately named `trustedHtml` boundary; the caller owns\n * sanitization in that mode.\n *\n * @example\n * ```tsx\n * import { MarkdownView } from \"@k2b/ui\";\n *\n * <MarkdownView markdown={source} />;\n * ```\n *\n * @example Compact headings, e.g. inside comments or file previews:\n * ```tsx\n * <MarkdownView markdown={source} headingScale=\"compact\" />;\n * ```\n */\nexport default function MarkdownView(props: MarkdownViewProps) {\n const locale = useLocale();\n const html = () =>\n props.markdown !== undefined\n ? renderSafeMarkdown(props.markdown, {\n inlineTokens: props.inlineTokens,\n allowImages: props.allowImages,\n linkProtocols: props.linkProtocols,\n linkTarget: props.linkTarget,\n locale: locale(),\n })\n : props.trustedHtml;\n return (\n <div\n class={`k2b-content-markdown ${props.class ?? \"\"}`}\n data-heading-scale={props.headingScale && props.headingScale !== \"normal\" ? props.headingScale : undefined}\n innerHTML={html()}\n />\n );\n}\n"],"mappings":"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","ignoreList":[]}