@puzzle-ui/ui 0.3.0 → 0.5.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/README.md +70 -70
  2. package/dist/components/actions/button/button.css +5 -2
  3. package/dist/components/actions/button-group/button-group.css +16 -17
  4. package/dist/components/data-display/badge/badge.css +15 -2
  5. package/dist/components/data-display/badge/index.cjs +13 -2
  6. package/dist/components/data-display/badge/index.cjs.map +1 -1
  7. package/dist/components/data-display/badge/index.d.cts +7 -2
  8. package/dist/components/data-display/badge/index.d.ts +7 -2
  9. package/dist/components/data-display/badge/index.js +13 -2
  10. package/dist/components/data-display/badge/index.js.map +1 -1
  11. package/dist/components/data-display/empty-state/empty-state.css +128 -0
  12. package/dist/components/data-display/empty-state/empty-state.utils.cjs +6 -0
  13. package/dist/components/data-display/empty-state/empty-state.utils.cjs.map +1 -0
  14. package/dist/components/data-display/empty-state/empty-state.utils.js +6 -0
  15. package/dist/components/data-display/empty-state/empty-state.utils.js.map +1 -0
  16. package/dist/components/data-display/empty-state/index.cjs +49 -0
  17. package/dist/components/data-display/empty-state/index.cjs.map +1 -0
  18. package/dist/components/data-display/empty-state/index.d.cts +19 -0
  19. package/dist/components/data-display/empty-state/index.d.ts +19 -0
  20. package/dist/components/data-display/empty-state/index.js +44 -0
  21. package/dist/components/data-display/empty-state/index.js.map +1 -0
  22. package/dist/components/data-display/rating/index.cjs +84 -0
  23. package/dist/components/data-display/rating/index.cjs.map +1 -0
  24. package/dist/components/data-display/rating/index.d.cts +3 -0
  25. package/dist/components/data-display/rating/index.d.ts +3 -0
  26. package/dist/components/data-display/rating/index.js +84 -0
  27. package/dist/components/data-display/rating/index.js.map +1 -0
  28. package/dist/components/data-display/rating/rating.css +72 -0
  29. package/dist/components/data-display/rating/rating.types.d.cts +15 -0
  30. package/dist/components/data-display/rating/rating.types.d.ts +15 -0
  31. package/dist/components/data-display/rating/rating.utils.cjs +27 -0
  32. package/dist/components/data-display/rating/rating.utils.cjs.map +1 -0
  33. package/dist/components/data-display/rating/rating.utils.js +25 -0
  34. package/dist/components/data-display/rating/rating.utils.js.map +1 -0
  35. package/dist/components/editors/text-editor/editor.cjs.map +1 -1
  36. package/dist/components/editors/text-editor/editor.js.map +1 -1
  37. package/dist/components/editors/text-editor/text-document.cjs.map +1 -1
  38. package/dist/components/editors/text-editor/text-document.js.map +1 -1
  39. package/dist/components/editors/text-editor/text-parser.cjs.map +1 -1
  40. package/dist/components/editors/text-editor/text-parser.js.map +1 -1
  41. package/dist/components/editors/text-editor/text-renderer.cjs.map +1 -1
  42. package/dist/components/editors/text-editor/text-renderer.js.map +1 -1
  43. package/dist/components/forms/autocomplete/index.cjs.map +1 -1
  44. package/dist/components/forms/autocomplete/index.js.map +1 -1
  45. package/dist/components/forms/checkbox/checkbox.css +2 -10
  46. package/dist/components/forms/color-picker/color-picker.css +351 -0
  47. package/dist/components/forms/color-picker/color-picker.types.d.cts +25 -0
  48. package/dist/components/forms/color-picker/color-picker.types.d.ts +25 -0
  49. package/dist/components/forms/color-picker/color-picker.utils.cjs +116 -0
  50. package/dist/components/forms/color-picker/color-picker.utils.cjs.map +1 -0
  51. package/dist/components/forms/color-picker/color-picker.utils.js +109 -0
  52. package/dist/components/forms/color-picker/color-picker.utils.js.map +1 -0
  53. package/dist/components/forms/color-picker/index.cjs +340 -0
  54. package/dist/components/forms/color-picker/index.cjs.map +1 -0
  55. package/dist/components/forms/color-picker/index.d.cts +3 -0
  56. package/dist/components/forms/color-picker/index.d.ts +3 -0
  57. package/dist/components/forms/color-picker/index.js +340 -0
  58. package/dist/components/forms/color-picker/index.js.map +1 -0
  59. package/dist/components/forms/file-upload/file-upload.utils.cjs.map +1 -1
  60. package/dist/components/forms/file-upload/file-upload.utils.js.map +1 -1
  61. package/dist/components/forms/input/number-input.js +5 -5
  62. package/dist/components/forms/input/number-input.js.map +1 -1
  63. package/dist/components/forms/input-group/index.d.cts +3 -0
  64. package/dist/components/forms/input-group/index.d.ts +3 -0
  65. package/dist/components/forms/input-group/input-group.cjs +18 -0
  66. package/dist/components/forms/input-group/input-group.cjs.map +1 -0
  67. package/dist/components/forms/input-group/input-group.css +62 -0
  68. package/dist/components/forms/input-group/input-group.d.cts +7 -0
  69. package/dist/components/forms/input-group/input-group.d.ts +7 -0
  70. package/dist/components/forms/input-group/input-group.js +18 -0
  71. package/dist/components/forms/input-group/input-group.js.map +1 -0
  72. package/dist/components/forms/input-group/input-group.types.d.cts +7 -0
  73. package/dist/components/forms/input-group/input-group.types.d.ts +7 -0
  74. package/dist/components/forms/input-group/input-group.utils.cjs +5 -0
  75. package/dist/components/forms/input-group/input-group.utils.cjs.map +1 -0
  76. package/dist/components/forms/input-group/input-group.utils.js +5 -0
  77. package/dist/components/forms/input-group/input-group.utils.js.map +1 -0
  78. package/dist/components/forms/radio-group/index.d.cts +2 -1
  79. package/dist/components/forms/radio-group/index.d.ts +2 -1
  80. package/dist/components/forms/select/index.cjs.map +1 -1
  81. package/dist/components/forms/select/index.js.map +1 -1
  82. package/dist/components/forms/slider/index.cjs +1 -1
  83. package/dist/components/forms/slider/index.cjs.map +1 -1
  84. package/dist/components/forms/slider/index.js +1 -1
  85. package/dist/components/forms/slider/index.js.map +1 -1
  86. package/dist/components/forms/stepper/index.d.cts +4 -2
  87. package/dist/components/forms/stepper/index.d.ts +4 -2
  88. package/dist/components/forms/time-picker/index.cjs +108 -0
  89. package/dist/components/forms/time-picker/index.cjs.map +1 -0
  90. package/dist/components/forms/time-picker/index.d.cts +3 -0
  91. package/dist/components/forms/time-picker/index.d.ts +3 -0
  92. package/dist/components/forms/time-picker/index.js +108 -0
  93. package/dist/components/forms/time-picker/index.js.map +1 -0
  94. package/dist/components/forms/time-picker/time-picker.css +166 -0
  95. package/dist/components/forms/time-picker/time-picker.types.d.cts +34 -0
  96. package/dist/components/forms/time-picker/time-picker.types.d.ts +34 -0
  97. package/dist/components/forms/time-picker/time-picker.utils.cjs +25 -0
  98. package/dist/components/forms/time-picker/time-picker.utils.cjs.map +1 -0
  99. package/dist/components/forms/time-picker/time-picker.utils.js +24 -0
  100. package/dist/components/forms/time-picker/time-picker.utils.js.map +1 -0
  101. package/dist/components/forms/token-input/index.cjs +189 -0
  102. package/dist/components/forms/token-input/index.cjs.map +1 -0
  103. package/dist/components/forms/token-input/index.d.cts +3 -0
  104. package/dist/components/forms/token-input/index.d.ts +3 -0
  105. package/dist/components/forms/token-input/index.js +189 -0
  106. package/dist/components/forms/token-input/index.js.map +1 -0
  107. package/dist/components/forms/token-input/token-input.css +145 -0
  108. package/dist/components/forms/token-input/token-input.types.d.cts +48 -0
  109. package/dist/components/forms/token-input/token-input.types.d.ts +48 -0
  110. package/dist/components/forms/token-input/token-input.utils.cjs +30 -0
  111. package/dist/components/forms/token-input/token-input.utils.cjs.map +1 -0
  112. package/dist/components/forms/token-input/token-input.utils.js +24 -0
  113. package/dist/components/forms/token-input/token-input.utils.js.map +1 -0
  114. package/dist/components/layout/aspect-ratio/aspect-ratio.css +14 -0
  115. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs +11 -0
  116. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs.map +1 -0
  117. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js +11 -0
  118. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js.map +1 -0
  119. package/dist/components/layout/aspect-ratio/index.cjs +16 -0
  120. package/dist/components/layout/aspect-ratio/index.cjs.map +1 -0
  121. package/dist/components/layout/aspect-ratio/index.d.cts +11 -0
  122. package/dist/components/layout/aspect-ratio/index.d.ts +11 -0
  123. package/dist/components/layout/aspect-ratio/index.js +16 -0
  124. package/dist/components/layout/aspect-ratio/index.js.map +1 -0
  125. package/dist/components/layout/container/container.css +1 -0
  126. package/dist/components/layout/page-layout/page-layout.css +1 -0
  127. package/dist/components/layout/resizable-panels/index.cjs +1 -1
  128. package/dist/components/layout/resizable-panels/index.cjs.map +1 -1
  129. package/dist/components/layout/resizable-panels/index.js +1 -1
  130. package/dist/components/layout/resizable-panels/index.js.map +1 -1
  131. package/dist/components/layout/resizable-panels/resizable-panels.utils.cjs.map +1 -1
  132. package/dist/components/layout/resizable-panels/resizable-panels.utils.js.map +1 -1
  133. package/dist/components/layout/scroll-area/index.cjs +27 -0
  134. package/dist/components/layout/scroll-area/index.cjs.map +1 -0
  135. package/dist/components/layout/scroll-area/index.d.cts +15 -0
  136. package/dist/components/layout/scroll-area/index.d.ts +15 -0
  137. package/dist/components/layout/scroll-area/index.js +27 -0
  138. package/dist/components/layout/scroll-area/index.js.map +1 -0
  139. package/dist/components/layout/scroll-area/scroll-area.css +25 -0
  140. package/dist/components/layout/scroll-area/scroll-area.utils.cjs +8 -0
  141. package/dist/components/layout/scroll-area/scroll-area.utils.cjs.map +1 -0
  142. package/dist/components/layout/scroll-area/scroll-area.utils.d.cts +2 -0
  143. package/dist/components/layout/scroll-area/scroll-area.utils.d.ts +2 -0
  144. package/dist/components/layout/scroll-area/scroll-area.utils.js +8 -0
  145. package/dist/components/layout/scroll-area/scroll-area.utils.js.map +1 -0
  146. package/dist/components/layout/section/index.cjs +10 -4
  147. package/dist/components/layout/section/index.cjs.map +1 -1
  148. package/dist/components/layout/section/index.d.cts +1 -1
  149. package/dist/components/layout/section/index.d.ts +1 -1
  150. package/dist/components/layout/section/index.js +11 -5
  151. package/dist/components/layout/section/index.js.map +1 -1
  152. package/dist/components/layout/section/section.css +9 -3
  153. package/dist/components/layout/section/types.d.cts +5 -5
  154. package/dist/components/layout/section/types.d.ts +5 -5
  155. package/dist/components/layout/separator/index.cjs +18 -0
  156. package/dist/components/layout/separator/index.cjs.map +1 -0
  157. package/dist/components/layout/separator/index.d.cts +17 -0
  158. package/dist/components/layout/separator/index.d.ts +17 -0
  159. package/dist/components/layout/separator/index.js +18 -0
  160. package/dist/components/layout/separator/index.js.map +1 -0
  161. package/dist/components/layout/separator/separator.css +15 -0
  162. package/dist/components/layout/separator/separator.utils.cjs +11 -0
  163. package/dist/components/layout/separator/separator.utils.cjs.map +1 -0
  164. package/dist/components/layout/separator/separator.utils.d.cts +2 -0
  165. package/dist/components/layout/separator/separator.utils.d.ts +2 -0
  166. package/dist/components/layout/separator/separator.utils.js +11 -0
  167. package/dist/components/layout/separator/separator.utils.js.map +1 -0
  168. package/dist/components/layout/stack/index.cjs +18 -2
  169. package/dist/components/layout/stack/index.cjs.map +1 -1
  170. package/dist/components/layout/stack/index.d.cts +8 -6
  171. package/dist/components/layout/stack/index.d.ts +8 -6
  172. package/dist/components/layout/stack/index.js +18 -2
  173. package/dist/components/layout/stack/index.js.map +1 -1
  174. package/dist/components/layout/stack/stack.css +118 -138
  175. package/dist/components/navigation/accordion/index.cjs.map +1 -1
  176. package/dist/components/navigation/accordion/index.js.map +1 -1
  177. package/dist/components/navigation/carousel/index.cjs +5 -2
  178. package/dist/components/navigation/carousel/index.cjs.map +1 -1
  179. package/dist/components/navigation/carousel/index.js +5 -2
  180. package/dist/components/navigation/carousel/index.js.map +1 -1
  181. package/dist/components/navigation/pagination/index.cjs +146 -0
  182. package/dist/components/navigation/pagination/index.cjs.map +1 -0
  183. package/dist/components/navigation/pagination/index.d.cts +3 -0
  184. package/dist/components/navigation/pagination/index.d.ts +3 -0
  185. package/dist/components/navigation/pagination/index.js +146 -0
  186. package/dist/components/navigation/pagination/index.js.map +1 -0
  187. package/dist/components/navigation/pagination/pagination.css +134 -0
  188. package/dist/components/navigation/pagination/pagination.types.d.cts +38 -0
  189. package/dist/components/navigation/pagination/pagination.types.d.ts +38 -0
  190. package/dist/components/navigation/pagination/pagination.utils.cjs +54 -0
  191. package/dist/components/navigation/pagination/pagination.utils.cjs.map +1 -0
  192. package/dist/components/navigation/pagination/pagination.utils.js +53 -0
  193. package/dist/components/navigation/pagination/pagination.utils.js.map +1 -0
  194. package/dist/components/navigation/tabs/index.d.cts +3 -2
  195. package/dist/components/navigation/tabs/index.d.ts +3 -2
  196. package/dist/components/navigation/tree-view/index.cjs.map +1 -1
  197. package/dist/components/navigation/tree-view/index.js.map +1 -1
  198. package/dist/components/overlays/context-menu/context-menu.css +151 -0
  199. package/dist/components/overlays/context-menu/context-menu.types.d.cts +69 -0
  200. package/dist/components/overlays/context-menu/context-menu.types.d.ts +69 -0
  201. package/dist/components/overlays/context-menu/context-menu.utils.cjs +46 -0
  202. package/dist/components/overlays/context-menu/context-menu.utils.cjs.map +1 -0
  203. package/dist/components/overlays/context-menu/context-menu.utils.js +40 -0
  204. package/dist/components/overlays/context-menu/context-menu.utils.js.map +1 -0
  205. package/dist/components/overlays/context-menu/index.cjs +830 -0
  206. package/dist/components/overlays/context-menu/index.cjs.map +1 -0
  207. package/dist/components/overlays/context-menu/index.d.cts +42 -0
  208. package/dist/components/overlays/context-menu/index.d.ts +42 -0
  209. package/dist/components/overlays/context-menu/index.js +818 -0
  210. package/dist/components/overlays/context-menu/index.js.map +1 -0
  211. package/dist/components/overlays/dropdown-menu/dropdown-menu-content.cjs.map +1 -1
  212. package/dist/components/overlays/dropdown-menu/dropdown-menu-content.js.map +1 -1
  213. package/dist/components/overlays/dropdown-menu/dropdown-menu.utils.cjs.map +1 -1
  214. package/dist/components/overlays/dropdown-menu/dropdown-menu.utils.js.map +1 -1
  215. package/dist/components/typography/heading.cjs +6 -2
  216. package/dist/components/typography/heading.cjs.map +1 -1
  217. package/dist/components/typography/heading.d.cts +3 -2
  218. package/dist/components/typography/heading.d.ts +3 -2
  219. package/dist/components/typography/heading.js +6 -2
  220. package/dist/components/typography/heading.js.map +1 -1
  221. package/dist/components/typography/text.cjs +6 -2
  222. package/dist/components/typography/text.cjs.map +1 -1
  223. package/dist/components/typography/text.d.cts +3 -2
  224. package/dist/components/typography/text.d.ts +3 -2
  225. package/dist/components/typography/text.js +6 -2
  226. package/dist/components/typography/text.js.map +1 -1
  227. package/dist/components/utilities/preview/index.cjs +89 -21
  228. package/dist/components/utilities/preview/index.cjs.map +1 -1
  229. package/dist/components/utilities/preview/index.d.cts +6 -2
  230. package/dist/components/utilities/preview/index.d.ts +6 -2
  231. package/dist/components/utilities/preview/index.js +89 -21
  232. package/dist/components/utilities/preview/index.js.map +1 -1
  233. package/dist/components/utilities/preview/preview.css +26 -11
  234. package/dist/components/utilities/transition/index.d.cts +1 -1
  235. package/dist/components/utilities/transition/index.d.ts +1 -1
  236. package/dist/index.cjs +136 -97
  237. package/dist/index.d.cts +23 -2
  238. package/dist/index.d.ts +23 -2
  239. package/dist/index.js +12 -1
  240. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"file":"text-document.cjs","names":[],"sources":["../../../../src/components/editors/text-editor/text-document.ts"],"sourcesContent":["import type {\n TextBlock,\n TextDocument,\n TextInline,\n TextMark,\n TextPoint,\n TextSelection,\n TextBlockVariant,\n TextAlign,\n TextListType,\n} from \"./text-editor.types\";\n\nconst HEADING_VARIANTS = new Set<TextBlockVariant>([\n \"display1\",\n \"display2\",\n \"headline1\",\n \"headline2\",\n \"headline3\",\n \"headline4\",\n]);\n\nconst MARK_ORDER: TextMark[] = [\"bold\", \"italic\", \"underline\", \"strike\"];\n\nexport const isHeadingVariant = (variant: TextBlockVariant): boolean =>\n HEADING_VARIANTS.has(variant);\n\nexport const createTextDocument = (text = \"\"): TextDocument => ({\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n variant: \"body1\",\n children: text ? [{ text }] : [],\n },\n ],\n});\n\nexport const createTextSelection = (block = 0, offset = 0): TextSelection => ({\n anchor: { block, offset },\n focus: { block, offset },\n});\n\nexport const getBlockText = (block: TextBlock): string =>\n block.children.map((child) => child.text).join(\"\");\n\nexport const getBlockLength = (block: TextBlock): number =>\n block.children.reduce((length, child) => length + child.text.length, 0);\n\nexport const isCollapsedSelection = (selection: TextSelection): boolean =>\n selection.anchor.block === selection.focus.block &&\n selection.anchor.offset === selection.focus.offset;\n\nexport const getOrderedSelection = (\n selection: TextSelection,\n): { start: TextPoint; end: TextPoint } => {\n const { anchor, focus } = selection;\n if (\n anchor.block < focus.block ||\n (anchor.block === focus.block && anchor.offset <= focus.offset)\n ) {\n return { start: anchor, end: focus };\n }\n\n return { start: focus, end: anchor };\n};\n\nconst normalizeMarks = (marks: TextMark[] = []): TextMark[] =>\n MARK_ORDER.filter((mark) => marks.includes(mark));\n\nconst sameMarks = (left: TextMark[] = [], right: TextMark[] = []): boolean => {\n const a = normalizeMarks(left);\n const b = normalizeMarks(right);\n return a.length === b.length && a.every((mark, index) => mark === b[index]);\n};\n\nexport const normalizeInlines = (inlines: TextInline[]): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const inline of inlines) {\n if (!inline.text) continue;\n\n const marks = normalizeMarks(inline.marks);\n const previous = result[result.length - 1];\n\n if (previous && sameMarks(previous.marks, marks)) {\n previous.text += inline.text;\n continue;\n }\n\n result.push({\n text: inline.text,\n ...(marks.length ? { marks } : {}),\n });\n }\n\n return result;\n};\n\nconst isTextMark = (value: unknown): value is TextMark =>\n value === \"bold\" ||\n value === \"italic\" ||\n value === \"underline\" ||\n value === \"strike\";\n\nconst isTextVariant = (value: unknown): value is TextBlockVariant =>\n value === \"display1\" ||\n value === \"display2\" ||\n value === \"headline1\" ||\n value === \"headline2\" ||\n value === \"headline3\" ||\n value === \"headline4\" ||\n value === \"subtitle1\" ||\n value === \"subtitle2\" ||\n value === \"body1\" ||\n value === \"body2\" ||\n value === \"button\" ||\n value === \"caption\" ||\n value === \"overline\";\n\nexport const normalizeTextDocument = (input: TextDocument): TextDocument => {\n const content = Array.isArray(input?.content)\n ? input.content.flatMap((candidate) => {\n if (!candidate || !Array.isArray(candidate.children)) return [];\n\n const variant = isTextVariant(candidate.variant)\n ? candidate.variant\n : \"body1\";\n\n const children = normalizeInlines(\n candidate.children.flatMap((inline) => {\n if (!inline || typeof inline.text !== \"string\") return [];\n const marks = Array.isArray(inline.marks)\n ? inline.marks.filter(isTextMark)\n : [];\n return [{ text: inline.text, marks }];\n }),\n );\n\n const listType =\n candidate.listType === \"bullet\" || candidate.listType === \"ordered\"\n ? candidate.listType\n : undefined;\n const align =\n candidate.align === \"left\" ||\n candidate.align === \"center\" ||\n candidate.align === \"right\" ||\n candidate.align === \"justify\"\n ? (candidate.align as TextAlign)\n : undefined;\n\n const type = listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\";\n const headingLevel =\n type === \"heading\" &&\n Number.isInteger(candidate.headingLevel) &&\n candidate.headingLevel! >= 1 &&\n candidate.headingLevel! <= 6\n ? candidate.headingLevel\n : undefined;\n\n return [\n {\n type: listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n children,\n ...(listType ? { listType } : {}),\n ...(align ? { align } : {}),\n ...(headingLevel ? { headingLevel } : {}),\n } satisfies TextBlock,\n ];\n })\n : [];\n\n return {\n type: \"doc\",\n content: content.length\n ? content\n : [{ type: \"paragraph\", variant: \"body1\", children: [] }],\n };\n};\n\nconst sliceInlines = (\n inlines: TextInline[],\n from: number,\n to: number,\n): TextInline[] => {\n if (to <= from) return [];\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = position + inline.text.length;\n const overlapStart = Math.max(start, from);\n const overlapEnd = Math.min(end, to);\n\n if (overlapStart < overlapEnd) {\n result.push({\n text: inline.text.slice(overlapStart - start, overlapEnd - start),\n ...(inline.marks?.length ? { marks: [...inline.marks] } : {}),\n });\n }\n\n position = end;\n if (position >= to) break;\n }\n\n return normalizeInlines(result);\n};\n\nconst transformInlineRange = (\n inlines: TextInline[],\n from: number,\n to: number,\n transform: (marks: TextMark[]) => TextMark[],\n): TextInline[] => {\n if (to <= from) return inlines.map((inline) => ({ ...inline }));\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = start + inline.text.length;\n\n if (end <= from || start >= to) {\n result.push({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n });\n } else {\n const leftLength = Math.max(0, from - start);\n const rightStart = Math.max(0, to - start);\n const marks = inline.marks ?? [];\n\n if (leftLength > 0) {\n result.push({\n text: inline.text.slice(0, leftLength),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n\n const middleEnd = Math.min(inline.text.length, to - start);\n if (middleEnd > leftLength) {\n result.push({\n text: inline.text.slice(leftLength, middleEnd),\n marks: transform([...marks]),\n });\n }\n\n if (rightStart < inline.text.length) {\n result.push({\n text: inline.text.slice(Math.max(0, rightStart)),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n }\n\n position = end;\n }\n\n return normalizeInlines(result);\n};\n\nexport const applyMark = (\n document: TextDocument,\n selection: TextSelection,\n mark: TextMark,\n force?: boolean,\n): TextDocument => {\n if (isCollapsedSelection(selection)) return document;\n\n const { start, end } = getOrderedSelection(selection);\n const allMarked = document.content.every((block, index) => {\n if (index < start.block || index > end.block) return true;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return true;\n\n let position = 0;\n for (const inline of block.children) {\n const inlineStart = position;\n const inlineEnd = position + inline.text.length;\n position = inlineEnd;\n if (inlineEnd <= from || inlineStart >= to) continue;\n if (!inline.marks?.includes(mark)) return false;\n }\n return true;\n });\n\n const addMark = force ?? !allMarked;\n const content = document.content.map((block, index) => {\n if (index < start.block || index > end.block) return block;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return block;\n\n return {\n ...block,\n children: transformInlineRange(block.children, from, to, (marks) => {\n const next = new Set(marks);\n if (addMark) next.add(mark);\n else next.delete(mark);\n return MARK_ORDER.filter((item) => next.has(item));\n }),\n };\n });\n\n return { type: \"doc\", content };\n};\n\nexport const setTextVariant = (\n document: TextDocument,\n selection: TextSelection,\n variant: TextBlockVariant,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return {\n ...block,\n type: block.listType\n ? \"listItem\"\n : isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n };\n }),\n };\n};\n\nconst inlineWithOffset = (\n inlines: TextInline[],\n offset: number,\n): { before: TextInline[]; after: TextInline[] } => ({\n before: sliceInlines(inlines, 0, offset),\n after: sliceInlines(\n inlines,\n offset,\n inlines.reduce((sum, item) => sum + item.text.length, 0),\n ),\n});\n\nexport const insertDocumentAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n insertedDocument: TextDocument,\n): { document: TextDocument; selection: TextSelection } => {\n const source = normalizeTextDocument(document);\n const inserted = normalizeTextDocument(insertedDocument);\n const { start, end } = getOrderedSelection(selection);\n const startIndex = Math.max(\n 0,\n Math.min(start.block, source.content.length - 1),\n );\n const endIndex = Math.max(\n startIndex,\n Math.min(end.block, source.content.length - 1),\n );\n const startBlock = source.content[startIndex];\n const endBlock = source.content[endIndex];\n const startOffset = Math.max(\n 0,\n Math.min(start.offset, getBlockLength(startBlock)),\n );\n const endOffset = Math.max(0, Math.min(end.offset, getBlockLength(endBlock)));\n const prefix = inlineWithOffset(startBlock.children, startOffset).before;\n const suffix = inlineWithOffset(endBlock.children, endOffset).after;\n const insertedBlocks = inserted.content.map((block) => ({\n ...block,\n children: block.children.map((inline) => ({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n })),\n }));\n\n const leftSurvives = prefix.length > 0 || startOffset > 0;\n const rightSurvives =\n suffix.length > 0 || endOffset < getBlockLength(endBlock);\n const first = insertedBlocks[0];\n const last = insertedBlocks[insertedBlocks.length - 1];\n const separatePrefix = leftSurvives && first.type === \"heading\";\n const separateSuffix = rightSurvives && last.type === \"heading\";\n const replacement: TextBlock[] = [];\n\n if (separatePrefix) {\n replacement.push({\n ...startBlock,\n children: prefix,\n });\n }\n\n if (insertedBlocks.length === 1) {\n if (separatePrefix || separateSuffix) {\n replacement.push({\n ...first,\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n if (separateSuffix) {\n replacement.push({ ...endBlock, children: suffix });\n } else {\n const finalIndex = replacement.length - 1;\n replacement[finalIndex] = {\n ...replacement[finalIndex],\n children: normalizeInlines([\n ...replacement[finalIndex].children,\n ...suffix,\n ]),\n };\n }\n } else {\n const useInsertedStyle = !leftSurvives && !rightSurvives;\n replacement.push({\n ...(useInsertedStyle ? first : startBlock),\n type: useInsertedStyle ? first.type : startBlock.type,\n variant: useInsertedStyle ? first.variant : startBlock.variant,\n children: normalizeInlines([...prefix, ...first.children, ...suffix]),\n });\n }\n } else {\n replacement.push({\n ...(separatePrefix || !leftSurvives ? first : startBlock),\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n\n for (let index = 1; index < insertedBlocks.length - 1; index += 1) {\n replacement.push(insertedBlocks[index]);\n }\n\n if (separateSuffix) {\n replacement.push(last);\n replacement.push({ ...endBlock, children: suffix });\n } else {\n replacement.push({\n ...(rightSurvives ? endBlock : last),\n type: rightSurvives ? endBlock.type : last.type,\n variant: rightSurvives ? endBlock.variant : last.variant,\n children: normalizeInlines([...last.children, ...suffix]),\n });\n }\n }\n\n const content = [\n ...source.content.slice(0, startIndex),\n ...replacement,\n ...source.content.slice(endIndex + 1),\n ];\n const caretReplacementIndex = separateSuffix\n ? replacement.length - 2\n : replacement.length - 1;\n const caretBlock = replacement[Math.max(0, caretReplacementIndex)];\n const suffixLength = suffix.reduce(\n (total, inline) => total + inline.text.length,\n 0,\n );\n const caretOffset = separateSuffix\n ? getBlockLength(caretBlock)\n : Math.max(0, getBlockLength(caretBlock) - suffixLength);\n const caret = {\n block: startIndex + Math.max(0, caretReplacementIndex),\n offset: caretOffset,\n };\n\n return {\n document: {\n type: \"doc\",\n content: content.length ? content : createTextDocument().content,\n },\n selection: { anchor: caret, focus: caret },\n };\n};\n\nexport const insertTextAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n text: string,\n marks: TextMark[] = [],\n): { document: TextDocument; selection: TextSelection } => {\n const inserted: TextDocument = {\n type: \"doc\",\n content: text.split(\"\\n\").map((line) => ({\n type: \"paragraph\",\n variant: \"body1\",\n children: line\n ? [{ text: line, ...(marks.length ? { marks: [...marks] } : {}) }]\n : [],\n })),\n };\n return insertDocumentAtSelection(document, selection, inserted);\n};\n\n/** Apply text alignment to every block touched by the current selection. */\nexport const setTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n align: TextAlign,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return { ...block, align };\n }),\n };\n};\n\nexport const getTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n): TextAlign | null => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n if (selected.length === 0) return null;\n const align = selected[0].align ?? null;\n return selected.every((block) => (block.align ?? null) === align)\n ? align\n : null;\n};\n\n/** Toggle a list type for the selected blocks; null removes list formatting. */\nexport const setListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType | null,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n const remove =\n listType != null &&\n selected.length > 0 &&\n selected.every((block) => block.listType === listType);\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n if (remove || listType == null) {\n const { listType: _listType, ...rest } = block;\n return {\n ...rest,\n type: block.type === \"listItem\" ? \"paragraph\" : block.type,\n };\n }\n return { ...block, type: \"listItem\", listType };\n }),\n };\n};\n\nexport const getListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType,\n): boolean => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n return (\n selected.length > 0 &&\n selected.every((block) => block.listType === listType)\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-document.cjs","names":[],"sources":["../../../../src/components/editors/text-editor/text-document.ts"],"sourcesContent":["import type {\n TextBlock,\n TextDocument,\n TextInline,\n TextMark,\n TextPoint,\n TextSelection,\n TextBlockVariant,\n TextAlign,\n TextListType,\n} from \"./text-editor.types\";\n\nconst HEADING_VARIANTS = new Set<TextBlockVariant>([\n \"display1\",\n \"display2\",\n \"headline1\",\n \"headline2\",\n \"headline3\",\n \"headline4\",\n]);\n\nconst MARK_ORDER: TextMark[] = [\"bold\", \"italic\", \"underline\", \"strike\"];\n\nexport const isHeadingVariant = (variant: TextBlockVariant): boolean =>\n HEADING_VARIANTS.has(variant);\n\nexport const createTextDocument = (text = \"\"): TextDocument => ({\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n variant: \"body1\",\n children: text ? [{ text }] : [],\n },\n ],\n});\n\nexport const createTextSelection = (block = 0, offset = 0): TextSelection => ({\n anchor: { block, offset },\n focus: { block, offset },\n});\n\nexport const getBlockText = (block: TextBlock): string =>\n block.children.map((child) => child.text).join(\"\");\n\nexport const getBlockLength = (block: TextBlock): number =>\n block.children.reduce((length, child) => length + child.text.length, 0);\n\nexport const isCollapsedSelection = (selection: TextSelection): boolean =>\n selection.anchor.block === selection.focus.block &&\n selection.anchor.offset === selection.focus.offset;\n\nexport const getOrderedSelection = (\n selection: TextSelection,\n): { start: TextPoint; end: TextPoint } => {\n const { anchor, focus } = selection;\n if (\n anchor.block < focus.block ||\n (anchor.block === focus.block && anchor.offset <= focus.offset)\n ) {\n return { start: anchor, end: focus };\n }\n\n return { start: focus, end: anchor };\n};\n\nconst normalizeMarks = (marks: TextMark[] = []): TextMark[] =>\n MARK_ORDER.filter((mark) => marks.includes(mark));\n\nconst sameMarks = (left: TextMark[] = [], right: TextMark[] = []): boolean => {\n const a = normalizeMarks(left);\n const b = normalizeMarks(right);\n return a.length === b.length && a.every((mark, index) => mark === b[index]);\n};\n\nexport const normalizeInlines = (inlines: TextInline[]): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const inline of inlines) {\n if (!inline.text) continue;\n\n const marks = normalizeMarks(inline.marks);\n const previous = result[result.length - 1];\n\n if (previous && sameMarks(previous.marks, marks)) {\n previous.text += inline.text;\n continue;\n }\n\n result.push({\n text: inline.text,\n ...(marks.length ? { marks } : {}),\n });\n }\n\n return result;\n};\n\nconst isTextMark = (value: unknown): value is TextMark =>\n value === \"bold\" ||\n value === \"italic\" ||\n value === \"underline\" ||\n value === \"strike\";\n\nconst isTextVariant = (value: unknown): value is TextBlockVariant =>\n value === \"display1\" ||\n value === \"display2\" ||\n value === \"headline1\" ||\n value === \"headline2\" ||\n value === \"headline3\" ||\n value === \"headline4\" ||\n value === \"subtitle1\" ||\n value === \"subtitle2\" ||\n value === \"body1\" ||\n value === \"body2\" ||\n value === \"button\" ||\n value === \"caption\" ||\n value === \"overline\";\n\nexport const normalizeTextDocument = (input: TextDocument): TextDocument => {\n const content = Array.isArray(input?.content)\n ? input.content.flatMap((candidate) => {\n if (!candidate || !Array.isArray(candidate.children)) return [];\n\n const variant = isTextVariant(candidate.variant)\n ? candidate.variant\n : \"body1\";\n\n const children = normalizeInlines(\n candidate.children.flatMap((inline) => {\n if (!inline || typeof inline.text !== \"string\") return [];\n const marks = Array.isArray(inline.marks)\n ? inline.marks.filter(isTextMark)\n : [];\n return [{ text: inline.text, marks }];\n }),\n );\n\n const listType =\n candidate.listType === \"bullet\" || candidate.listType === \"ordered\"\n ? candidate.listType\n : undefined;\n const align =\n candidate.align === \"left\" ||\n candidate.align === \"center\" ||\n candidate.align === \"right\" ||\n candidate.align === \"justify\"\n ? (candidate.align as TextAlign)\n : undefined;\n\n const type = listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\";\n const headingLevel =\n type === \"heading\" &&\n Number.isInteger(candidate.headingLevel) &&\n candidate.headingLevel! >= 1 &&\n candidate.headingLevel! <= 6\n ? candidate.headingLevel\n : undefined;\n\n return [\n {\n type: listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n children,\n ...(listType ? { listType } : {}),\n ...(align ? { align } : {}),\n ...(headingLevel ? { headingLevel } : {}),\n } satisfies TextBlock,\n ];\n })\n : [];\n\n return {\n type: \"doc\",\n content: content.length\n ? content\n : [{ type: \"paragraph\", variant: \"body1\", children: [] }],\n };\n};\n\nconst sliceInlines = (\n inlines: TextInline[],\n from: number,\n to: number,\n): TextInline[] => {\n if (to <= from) return [];\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = position + inline.text.length;\n const overlapStart = Math.max(start, from);\n const overlapEnd = Math.min(end, to);\n\n if (overlapStart < overlapEnd) {\n result.push({\n text: inline.text.slice(overlapStart - start, overlapEnd - start),\n ...(inline.marks?.length ? { marks: [...inline.marks] } : {}),\n });\n }\n\n position = end;\n if (position >= to) break;\n }\n\n return normalizeInlines(result);\n};\n\nconst transformInlineRange = (\n inlines: TextInline[],\n from: number,\n to: number,\n transform: (marks: TextMark[]) => TextMark[],\n): TextInline[] => {\n if (to <= from) return inlines.map((inline) => ({ ...inline }));\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = start + inline.text.length;\n\n if (end <= from || start >= to) {\n result.push({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n });\n } else {\n const leftLength = Math.max(0, from - start);\n const rightStart = Math.max(0, to - start);\n const marks = inline.marks ?? [];\n\n if (leftLength > 0) {\n result.push({\n text: inline.text.slice(0, leftLength),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n\n const middleEnd = Math.min(inline.text.length, to - start);\n if (middleEnd > leftLength) {\n result.push({\n text: inline.text.slice(leftLength, middleEnd),\n marks: transform([...marks]),\n });\n }\n\n if (rightStart < inline.text.length) {\n result.push({\n text: inline.text.slice(Math.max(0, rightStart)),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n }\n\n position = end;\n }\n\n return normalizeInlines(result);\n};\n\nexport const applyMark = (\n document: TextDocument,\n selection: TextSelection,\n mark: TextMark,\n force?: boolean,\n): TextDocument => {\n if (isCollapsedSelection(selection)) return document;\n\n const { start, end } = getOrderedSelection(selection);\n const allMarked = document.content.every((block, index) => {\n if (index < start.block || index > end.block) return true;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return true;\n\n let position = 0;\n for (const inline of block.children) {\n const inlineStart = position;\n const inlineEnd = position + inline.text.length;\n position = inlineEnd;\n if (inlineEnd <= from || inlineStart >= to) continue;\n if (!inline.marks?.includes(mark)) return false;\n }\n return true;\n });\n\n const addMark = force ?? !allMarked;\n const content = document.content.map((block, index) => {\n if (index < start.block || index > end.block) return block;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return block;\n\n return {\n ...block,\n children: transformInlineRange(block.children, from, to, (marks) => {\n const next = new Set(marks);\n if (addMark) next.add(mark);\n else next.delete(mark);\n return MARK_ORDER.filter((item) => next.has(item));\n }),\n };\n });\n\n return { type: \"doc\", content };\n};\n\nexport const setTextVariant = (\n document: TextDocument,\n selection: TextSelection,\n variant: TextBlockVariant,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return {\n ...block,\n type: block.listType\n ? \"listItem\"\n : isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n };\n }),\n };\n};\n\nconst inlineWithOffset = (\n inlines: TextInline[],\n offset: number,\n): { before: TextInline[]; after: TextInline[] } => ({\n before: sliceInlines(inlines, 0, offset),\n after: sliceInlines(\n inlines,\n offset,\n inlines.reduce((sum, item) => sum + item.text.length, 0),\n ),\n});\n\nexport const insertDocumentAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n insertedDocument: TextDocument,\n): { document: TextDocument; selection: TextSelection } => {\n const source = normalizeTextDocument(document);\n const inserted = normalizeTextDocument(insertedDocument);\n const { start, end } = getOrderedSelection(selection);\n const startIndex = Math.max(\n 0,\n Math.min(start.block, source.content.length - 1),\n );\n const endIndex = Math.max(\n startIndex,\n Math.min(end.block, source.content.length - 1),\n );\n const startBlock = source.content[startIndex]!;\n const endBlock = source.content[endIndex]!;\n const startOffset = Math.max(\n 0,\n Math.min(start.offset, getBlockLength(startBlock)),\n );\n const endOffset = Math.max(0, Math.min(end.offset, getBlockLength(endBlock)));\n const prefix = inlineWithOffset(startBlock.children, startOffset).before;\n const suffix = inlineWithOffset(endBlock.children, endOffset).after;\n const insertedBlocks = inserted.content.map((block) => ({\n ...block,\n children: block.children.map((inline) => ({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n })),\n }));\n\n const leftSurvives = prefix.length > 0 || startOffset > 0;\n const rightSurvives =\n suffix.length > 0 || endOffset < getBlockLength(endBlock);\n const first = insertedBlocks[0]!;\n const last = insertedBlocks[insertedBlocks.length - 1]!;\n const separatePrefix = leftSurvives && first.type === \"heading\";\n const separateSuffix = rightSurvives && last.type === \"heading\";\n const replacement: TextBlock[] = [];\n\n if (separatePrefix) {\n replacement.push({\n ...startBlock,\n children: prefix,\n });\n }\n\n if (insertedBlocks.length === 1) {\n if (separatePrefix || separateSuffix) {\n replacement.push({\n ...first,\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n if (separateSuffix) {\n replacement.push({ ...endBlock, children: suffix });\n } else {\n const finalIndex = replacement.length - 1;\n replacement[finalIndex] = {\n ...replacement[finalIndex]!,\n children: normalizeInlines([\n ...replacement[finalIndex]!.children,\n ...suffix,\n ]),\n };\n }\n } else {\n const useInsertedStyle = !leftSurvives && !rightSurvives;\n replacement.push({\n ...(useInsertedStyle ? first : startBlock),\n type: useInsertedStyle ? first.type : startBlock.type,\n variant: useInsertedStyle ? first.variant : startBlock.variant,\n children: normalizeInlines([...prefix, ...first.children, ...suffix]),\n });\n }\n } else {\n replacement.push({\n ...(separatePrefix || !leftSurvives ? first : startBlock),\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n\n for (let index = 1; index < insertedBlocks.length - 1; index += 1) {\n replacement.push(insertedBlocks[index]!);\n }\n\n if (separateSuffix) {\n replacement.push(last);\n replacement.push({ ...endBlock, children: suffix });\n } else {\n replacement.push({\n ...(rightSurvives ? endBlock : last),\n type: rightSurvives ? endBlock.type : last.type,\n variant: rightSurvives ? endBlock.variant : last.variant,\n children: normalizeInlines([...last.children, ...suffix]),\n });\n }\n }\n\n const content = [\n ...source.content.slice(0, startIndex),\n ...replacement,\n ...source.content.slice(endIndex + 1),\n ];\n const caretReplacementIndex = separateSuffix\n ? replacement.length - 2\n : replacement.length - 1;\n const caretBlock = replacement[Math.max(0, caretReplacementIndex)]!;\n const suffixLength = suffix.reduce(\n (total, inline) => total + inline.text.length,\n 0,\n );\n const caretOffset = separateSuffix\n ? getBlockLength(caretBlock)\n : Math.max(0, getBlockLength(caretBlock) - suffixLength);\n const caret = {\n block: startIndex + Math.max(0, caretReplacementIndex),\n offset: caretOffset,\n };\n\n return {\n document: {\n type: \"doc\",\n content: content.length ? content : createTextDocument().content,\n },\n selection: { anchor: caret, focus: caret },\n };\n};\n\nexport const insertTextAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n text: string,\n marks: TextMark[] = [],\n): { document: TextDocument; selection: TextSelection } => {\n const inserted: TextDocument = {\n type: \"doc\",\n content: text.split(\"\\n\").map((line) => ({\n type: \"paragraph\",\n variant: \"body1\",\n children: line\n ? [{ text: line, ...(marks.length ? { marks: [...marks] } : {}) }]\n : [],\n })),\n };\n return insertDocumentAtSelection(document, selection, inserted);\n};\n\n/** Apply text alignment to every block touched by the current selection. */\nexport const setTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n align: TextAlign,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return { ...block, align };\n }),\n };\n};\n\nexport const getTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n): TextAlign | null => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n if (selected.length === 0) return null;\n const align = selected[0]!.align ?? null;\n return selected.every((block) => (block.align ?? null) === align)\n ? align\n : null;\n};\n\n/** Toggle a list type for the selected blocks; null removes list formatting. */\nexport const setListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType | null,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n const remove =\n listType != null &&\n selected.length > 0 &&\n selected.every((block) => block.listType === listType);\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n if (remove || listType == null) {\n const { listType: _listType, ...rest } = block;\n return {\n ...rest,\n type: block.type === \"listItem\" ? \"paragraph\" : block.type,\n };\n }\n return { ...block, type: \"listItem\", listType };\n }),\n };\n};\n\nexport const getListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType,\n): boolean => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n return (\n selected.length > 0 &&\n selected.every((block) => block.listType === listType)\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-document.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-document.ts"],"sourcesContent":["import type {\n TextBlock,\n TextDocument,\n TextInline,\n TextMark,\n TextPoint,\n TextSelection,\n TextBlockVariant,\n TextAlign,\n TextListType,\n} from \"./text-editor.types\";\n\nconst HEADING_VARIANTS = new Set<TextBlockVariant>([\n \"display1\",\n \"display2\",\n \"headline1\",\n \"headline2\",\n \"headline3\",\n \"headline4\",\n]);\n\nconst MARK_ORDER: TextMark[] = [\"bold\", \"italic\", \"underline\", \"strike\"];\n\nexport const isHeadingVariant = (variant: TextBlockVariant): boolean =>\n HEADING_VARIANTS.has(variant);\n\nexport const createTextDocument = (text = \"\"): TextDocument => ({\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n variant: \"body1\",\n children: text ? [{ text }] : [],\n },\n ],\n});\n\nexport const createTextSelection = (block = 0, offset = 0): TextSelection => ({\n anchor: { block, offset },\n focus: { block, offset },\n});\n\nexport const getBlockText = (block: TextBlock): string =>\n block.children.map((child) => child.text).join(\"\");\n\nexport const getBlockLength = (block: TextBlock): number =>\n block.children.reduce((length, child) => length + child.text.length, 0);\n\nexport const isCollapsedSelection = (selection: TextSelection): boolean =>\n selection.anchor.block === selection.focus.block &&\n selection.anchor.offset === selection.focus.offset;\n\nexport const getOrderedSelection = (\n selection: TextSelection,\n): { start: TextPoint; end: TextPoint } => {\n const { anchor, focus } = selection;\n if (\n anchor.block < focus.block ||\n (anchor.block === focus.block && anchor.offset <= focus.offset)\n ) {\n return { start: anchor, end: focus };\n }\n\n return { start: focus, end: anchor };\n};\n\nconst normalizeMarks = (marks: TextMark[] = []): TextMark[] =>\n MARK_ORDER.filter((mark) => marks.includes(mark));\n\nconst sameMarks = (left: TextMark[] = [], right: TextMark[] = []): boolean => {\n const a = normalizeMarks(left);\n const b = normalizeMarks(right);\n return a.length === b.length && a.every((mark, index) => mark === b[index]);\n};\n\nexport const normalizeInlines = (inlines: TextInline[]): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const inline of inlines) {\n if (!inline.text) continue;\n\n const marks = normalizeMarks(inline.marks);\n const previous = result[result.length - 1];\n\n if (previous && sameMarks(previous.marks, marks)) {\n previous.text += inline.text;\n continue;\n }\n\n result.push({\n text: inline.text,\n ...(marks.length ? { marks } : {}),\n });\n }\n\n return result;\n};\n\nconst isTextMark = (value: unknown): value is TextMark =>\n value === \"bold\" ||\n value === \"italic\" ||\n value === \"underline\" ||\n value === \"strike\";\n\nconst isTextVariant = (value: unknown): value is TextBlockVariant =>\n value === \"display1\" ||\n value === \"display2\" ||\n value === \"headline1\" ||\n value === \"headline2\" ||\n value === \"headline3\" ||\n value === \"headline4\" ||\n value === \"subtitle1\" ||\n value === \"subtitle2\" ||\n value === \"body1\" ||\n value === \"body2\" ||\n value === \"button\" ||\n value === \"caption\" ||\n value === \"overline\";\n\nexport const normalizeTextDocument = (input: TextDocument): TextDocument => {\n const content = Array.isArray(input?.content)\n ? input.content.flatMap((candidate) => {\n if (!candidate || !Array.isArray(candidate.children)) return [];\n\n const variant = isTextVariant(candidate.variant)\n ? candidate.variant\n : \"body1\";\n\n const children = normalizeInlines(\n candidate.children.flatMap((inline) => {\n if (!inline || typeof inline.text !== \"string\") return [];\n const marks = Array.isArray(inline.marks)\n ? inline.marks.filter(isTextMark)\n : [];\n return [{ text: inline.text, marks }];\n }),\n );\n\n const listType =\n candidate.listType === \"bullet\" || candidate.listType === \"ordered\"\n ? candidate.listType\n : undefined;\n const align =\n candidate.align === \"left\" ||\n candidate.align === \"center\" ||\n candidate.align === \"right\" ||\n candidate.align === \"justify\"\n ? (candidate.align as TextAlign)\n : undefined;\n\n const type = listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\";\n const headingLevel =\n type === \"heading\" &&\n Number.isInteger(candidate.headingLevel) &&\n candidate.headingLevel! >= 1 &&\n candidate.headingLevel! <= 6\n ? candidate.headingLevel\n : undefined;\n\n return [\n {\n type: listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n children,\n ...(listType ? { listType } : {}),\n ...(align ? { align } : {}),\n ...(headingLevel ? { headingLevel } : {}),\n } satisfies TextBlock,\n ];\n })\n : [];\n\n return {\n type: \"doc\",\n content: content.length\n ? content\n : [{ type: \"paragraph\", variant: \"body1\", children: [] }],\n };\n};\n\nconst sliceInlines = (\n inlines: TextInline[],\n from: number,\n to: number,\n): TextInline[] => {\n if (to <= from) return [];\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = position + inline.text.length;\n const overlapStart = Math.max(start, from);\n const overlapEnd = Math.min(end, to);\n\n if (overlapStart < overlapEnd) {\n result.push({\n text: inline.text.slice(overlapStart - start, overlapEnd - start),\n ...(inline.marks?.length ? { marks: [...inline.marks] } : {}),\n });\n }\n\n position = end;\n if (position >= to) break;\n }\n\n return normalizeInlines(result);\n};\n\nconst transformInlineRange = (\n inlines: TextInline[],\n from: number,\n to: number,\n transform: (marks: TextMark[]) => TextMark[],\n): TextInline[] => {\n if (to <= from) return inlines.map((inline) => ({ ...inline }));\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = start + inline.text.length;\n\n if (end <= from || start >= to) {\n result.push({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n });\n } else {\n const leftLength = Math.max(0, from - start);\n const rightStart = Math.max(0, to - start);\n const marks = inline.marks ?? [];\n\n if (leftLength > 0) {\n result.push({\n text: inline.text.slice(0, leftLength),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n\n const middleEnd = Math.min(inline.text.length, to - start);\n if (middleEnd > leftLength) {\n result.push({\n text: inline.text.slice(leftLength, middleEnd),\n marks: transform([...marks]),\n });\n }\n\n if (rightStart < inline.text.length) {\n result.push({\n text: inline.text.slice(Math.max(0, rightStart)),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n }\n\n position = end;\n }\n\n return normalizeInlines(result);\n};\n\nexport const applyMark = (\n document: TextDocument,\n selection: TextSelection,\n mark: TextMark,\n force?: boolean,\n): TextDocument => {\n if (isCollapsedSelection(selection)) return document;\n\n const { start, end } = getOrderedSelection(selection);\n const allMarked = document.content.every((block, index) => {\n if (index < start.block || index > end.block) return true;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return true;\n\n let position = 0;\n for (const inline of block.children) {\n const inlineStart = position;\n const inlineEnd = position + inline.text.length;\n position = inlineEnd;\n if (inlineEnd <= from || inlineStart >= to) continue;\n if (!inline.marks?.includes(mark)) return false;\n }\n return true;\n });\n\n const addMark = force ?? !allMarked;\n const content = document.content.map((block, index) => {\n if (index < start.block || index > end.block) return block;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return block;\n\n return {\n ...block,\n children: transformInlineRange(block.children, from, to, (marks) => {\n const next = new Set(marks);\n if (addMark) next.add(mark);\n else next.delete(mark);\n return MARK_ORDER.filter((item) => next.has(item));\n }),\n };\n });\n\n return { type: \"doc\", content };\n};\n\nexport const setTextVariant = (\n document: TextDocument,\n selection: TextSelection,\n variant: TextBlockVariant,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return {\n ...block,\n type: block.listType\n ? \"listItem\"\n : isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n };\n }),\n };\n};\n\nconst inlineWithOffset = (\n inlines: TextInline[],\n offset: number,\n): { before: TextInline[]; after: TextInline[] } => ({\n before: sliceInlines(inlines, 0, offset),\n after: sliceInlines(\n inlines,\n offset,\n inlines.reduce((sum, item) => sum + item.text.length, 0),\n ),\n});\n\nexport const insertDocumentAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n insertedDocument: TextDocument,\n): { document: TextDocument; selection: TextSelection } => {\n const source = normalizeTextDocument(document);\n const inserted = normalizeTextDocument(insertedDocument);\n const { start, end } = getOrderedSelection(selection);\n const startIndex = Math.max(\n 0,\n Math.min(start.block, source.content.length - 1),\n );\n const endIndex = Math.max(\n startIndex,\n Math.min(end.block, source.content.length - 1),\n );\n const startBlock = source.content[startIndex];\n const endBlock = source.content[endIndex];\n const startOffset = Math.max(\n 0,\n Math.min(start.offset, getBlockLength(startBlock)),\n );\n const endOffset = Math.max(0, Math.min(end.offset, getBlockLength(endBlock)));\n const prefix = inlineWithOffset(startBlock.children, startOffset).before;\n const suffix = inlineWithOffset(endBlock.children, endOffset).after;\n const insertedBlocks = inserted.content.map((block) => ({\n ...block,\n children: block.children.map((inline) => ({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n })),\n }));\n\n const leftSurvives = prefix.length > 0 || startOffset > 0;\n const rightSurvives =\n suffix.length > 0 || endOffset < getBlockLength(endBlock);\n const first = insertedBlocks[0];\n const last = insertedBlocks[insertedBlocks.length - 1];\n const separatePrefix = leftSurvives && first.type === \"heading\";\n const separateSuffix = rightSurvives && last.type === \"heading\";\n const replacement: TextBlock[] = [];\n\n if (separatePrefix) {\n replacement.push({\n ...startBlock,\n children: prefix,\n });\n }\n\n if (insertedBlocks.length === 1) {\n if (separatePrefix || separateSuffix) {\n replacement.push({\n ...first,\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n if (separateSuffix) {\n replacement.push({ ...endBlock, children: suffix });\n } else {\n const finalIndex = replacement.length - 1;\n replacement[finalIndex] = {\n ...replacement[finalIndex],\n children: normalizeInlines([\n ...replacement[finalIndex].children,\n ...suffix,\n ]),\n };\n }\n } else {\n const useInsertedStyle = !leftSurvives && !rightSurvives;\n replacement.push({\n ...(useInsertedStyle ? first : startBlock),\n type: useInsertedStyle ? first.type : startBlock.type,\n variant: useInsertedStyle ? first.variant : startBlock.variant,\n children: normalizeInlines([...prefix, ...first.children, ...suffix]),\n });\n }\n } else {\n replacement.push({\n ...(separatePrefix || !leftSurvives ? first : startBlock),\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n\n for (let index = 1; index < insertedBlocks.length - 1; index += 1) {\n replacement.push(insertedBlocks[index]);\n }\n\n if (separateSuffix) {\n replacement.push(last);\n replacement.push({ ...endBlock, children: suffix });\n } else {\n replacement.push({\n ...(rightSurvives ? endBlock : last),\n type: rightSurvives ? endBlock.type : last.type,\n variant: rightSurvives ? endBlock.variant : last.variant,\n children: normalizeInlines([...last.children, ...suffix]),\n });\n }\n }\n\n const content = [\n ...source.content.slice(0, startIndex),\n ...replacement,\n ...source.content.slice(endIndex + 1),\n ];\n const caretReplacementIndex = separateSuffix\n ? replacement.length - 2\n : replacement.length - 1;\n const caretBlock = replacement[Math.max(0, caretReplacementIndex)];\n const suffixLength = suffix.reduce(\n (total, inline) => total + inline.text.length,\n 0,\n );\n const caretOffset = separateSuffix\n ? getBlockLength(caretBlock)\n : Math.max(0, getBlockLength(caretBlock) - suffixLength);\n const caret = {\n block: startIndex + Math.max(0, caretReplacementIndex),\n offset: caretOffset,\n };\n\n return {\n document: {\n type: \"doc\",\n content: content.length ? content : createTextDocument().content,\n },\n selection: { anchor: caret, focus: caret },\n };\n};\n\nexport const insertTextAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n text: string,\n marks: TextMark[] = [],\n): { document: TextDocument; selection: TextSelection } => {\n const inserted: TextDocument = {\n type: \"doc\",\n content: text.split(\"\\n\").map((line) => ({\n type: \"paragraph\",\n variant: \"body1\",\n children: line\n ? [{ text: line, ...(marks.length ? { marks: [...marks] } : {}) }]\n : [],\n })),\n };\n return insertDocumentAtSelection(document, selection, inserted);\n};\n\n/** Apply text alignment to every block touched by the current selection. */\nexport const setTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n align: TextAlign,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return { ...block, align };\n }),\n };\n};\n\nexport const getTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n): TextAlign | null => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n if (selected.length === 0) return null;\n const align = selected[0].align ?? null;\n return selected.every((block) => (block.align ?? null) === align)\n ? align\n : null;\n};\n\n/** Toggle a list type for the selected blocks; null removes list formatting. */\nexport const setListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType | null,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n const remove =\n listType != null &&\n selected.length > 0 &&\n selected.every((block) => block.listType === listType);\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n if (remove || listType == null) {\n const { listType: _listType, ...rest } = block;\n return {\n ...rest,\n type: block.type === \"listItem\" ? \"paragraph\" : block.type,\n };\n }\n return { ...block, type: \"listItem\", listType };\n }),\n };\n};\n\nexport const getListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType,\n): boolean => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n return (\n selected.length > 0 &&\n selected.every((block) => block.listType === listType)\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-document.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-document.ts"],"sourcesContent":["import type {\n TextBlock,\n TextDocument,\n TextInline,\n TextMark,\n TextPoint,\n TextSelection,\n TextBlockVariant,\n TextAlign,\n TextListType,\n} from \"./text-editor.types\";\n\nconst HEADING_VARIANTS = new Set<TextBlockVariant>([\n \"display1\",\n \"display2\",\n \"headline1\",\n \"headline2\",\n \"headline3\",\n \"headline4\",\n]);\n\nconst MARK_ORDER: TextMark[] = [\"bold\", \"italic\", \"underline\", \"strike\"];\n\nexport const isHeadingVariant = (variant: TextBlockVariant): boolean =>\n HEADING_VARIANTS.has(variant);\n\nexport const createTextDocument = (text = \"\"): TextDocument => ({\n type: \"doc\",\n content: [\n {\n type: \"paragraph\",\n variant: \"body1\",\n children: text ? [{ text }] : [],\n },\n ],\n});\n\nexport const createTextSelection = (block = 0, offset = 0): TextSelection => ({\n anchor: { block, offset },\n focus: { block, offset },\n});\n\nexport const getBlockText = (block: TextBlock): string =>\n block.children.map((child) => child.text).join(\"\");\n\nexport const getBlockLength = (block: TextBlock): number =>\n block.children.reduce((length, child) => length + child.text.length, 0);\n\nexport const isCollapsedSelection = (selection: TextSelection): boolean =>\n selection.anchor.block === selection.focus.block &&\n selection.anchor.offset === selection.focus.offset;\n\nexport const getOrderedSelection = (\n selection: TextSelection,\n): { start: TextPoint; end: TextPoint } => {\n const { anchor, focus } = selection;\n if (\n anchor.block < focus.block ||\n (anchor.block === focus.block && anchor.offset <= focus.offset)\n ) {\n return { start: anchor, end: focus };\n }\n\n return { start: focus, end: anchor };\n};\n\nconst normalizeMarks = (marks: TextMark[] = []): TextMark[] =>\n MARK_ORDER.filter((mark) => marks.includes(mark));\n\nconst sameMarks = (left: TextMark[] = [], right: TextMark[] = []): boolean => {\n const a = normalizeMarks(left);\n const b = normalizeMarks(right);\n return a.length === b.length && a.every((mark, index) => mark === b[index]);\n};\n\nexport const normalizeInlines = (inlines: TextInline[]): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const inline of inlines) {\n if (!inline.text) continue;\n\n const marks = normalizeMarks(inline.marks);\n const previous = result[result.length - 1];\n\n if (previous && sameMarks(previous.marks, marks)) {\n previous.text += inline.text;\n continue;\n }\n\n result.push({\n text: inline.text,\n ...(marks.length ? { marks } : {}),\n });\n }\n\n return result;\n};\n\nconst isTextMark = (value: unknown): value is TextMark =>\n value === \"bold\" ||\n value === \"italic\" ||\n value === \"underline\" ||\n value === \"strike\";\n\nconst isTextVariant = (value: unknown): value is TextBlockVariant =>\n value === \"display1\" ||\n value === \"display2\" ||\n value === \"headline1\" ||\n value === \"headline2\" ||\n value === \"headline3\" ||\n value === \"headline4\" ||\n value === \"subtitle1\" ||\n value === \"subtitle2\" ||\n value === \"body1\" ||\n value === \"body2\" ||\n value === \"button\" ||\n value === \"caption\" ||\n value === \"overline\";\n\nexport const normalizeTextDocument = (input: TextDocument): TextDocument => {\n const content = Array.isArray(input?.content)\n ? input.content.flatMap((candidate) => {\n if (!candidate || !Array.isArray(candidate.children)) return [];\n\n const variant = isTextVariant(candidate.variant)\n ? candidate.variant\n : \"body1\";\n\n const children = normalizeInlines(\n candidate.children.flatMap((inline) => {\n if (!inline || typeof inline.text !== \"string\") return [];\n const marks = Array.isArray(inline.marks)\n ? inline.marks.filter(isTextMark)\n : [];\n return [{ text: inline.text, marks }];\n }),\n );\n\n const listType =\n candidate.listType === \"bullet\" || candidate.listType === \"ordered\"\n ? candidate.listType\n : undefined;\n const align =\n candidate.align === \"left\" ||\n candidate.align === \"center\" ||\n candidate.align === \"right\" ||\n candidate.align === \"justify\"\n ? (candidate.align as TextAlign)\n : undefined;\n\n const type = listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\";\n const headingLevel =\n type === \"heading\" &&\n Number.isInteger(candidate.headingLevel) &&\n candidate.headingLevel! >= 1 &&\n candidate.headingLevel! <= 6\n ? candidate.headingLevel\n : undefined;\n\n return [\n {\n type: listType\n ? \"listItem\"\n : candidate.type === \"heading\" && isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n children,\n ...(listType ? { listType } : {}),\n ...(align ? { align } : {}),\n ...(headingLevel ? { headingLevel } : {}),\n } satisfies TextBlock,\n ];\n })\n : [];\n\n return {\n type: \"doc\",\n content: content.length\n ? content\n : [{ type: \"paragraph\", variant: \"body1\", children: [] }],\n };\n};\n\nconst sliceInlines = (\n inlines: TextInline[],\n from: number,\n to: number,\n): TextInline[] => {\n if (to <= from) return [];\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = position + inline.text.length;\n const overlapStart = Math.max(start, from);\n const overlapEnd = Math.min(end, to);\n\n if (overlapStart < overlapEnd) {\n result.push({\n text: inline.text.slice(overlapStart - start, overlapEnd - start),\n ...(inline.marks?.length ? { marks: [...inline.marks] } : {}),\n });\n }\n\n position = end;\n if (position >= to) break;\n }\n\n return normalizeInlines(result);\n};\n\nconst transformInlineRange = (\n inlines: TextInline[],\n from: number,\n to: number,\n transform: (marks: TextMark[]) => TextMark[],\n): TextInline[] => {\n if (to <= from) return inlines.map((inline) => ({ ...inline }));\n\n const result: TextInline[] = [];\n let position = 0;\n\n for (const inline of inlines) {\n const start = position;\n const end = start + inline.text.length;\n\n if (end <= from || start >= to) {\n result.push({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n });\n } else {\n const leftLength = Math.max(0, from - start);\n const rightStart = Math.max(0, to - start);\n const marks = inline.marks ?? [];\n\n if (leftLength > 0) {\n result.push({\n text: inline.text.slice(0, leftLength),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n\n const middleEnd = Math.min(inline.text.length, to - start);\n if (middleEnd > leftLength) {\n result.push({\n text: inline.text.slice(leftLength, middleEnd),\n marks: transform([...marks]),\n });\n }\n\n if (rightStart < inline.text.length) {\n result.push({\n text: inline.text.slice(Math.max(0, rightStart)),\n ...(marks.length ? { marks: [...marks] } : {}),\n });\n }\n }\n\n position = end;\n }\n\n return normalizeInlines(result);\n};\n\nexport const applyMark = (\n document: TextDocument,\n selection: TextSelection,\n mark: TextMark,\n force?: boolean,\n): TextDocument => {\n if (isCollapsedSelection(selection)) return document;\n\n const { start, end } = getOrderedSelection(selection);\n const allMarked = document.content.every((block, index) => {\n if (index < start.block || index > end.block) return true;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return true;\n\n let position = 0;\n for (const inline of block.children) {\n const inlineStart = position;\n const inlineEnd = position + inline.text.length;\n position = inlineEnd;\n if (inlineEnd <= from || inlineStart >= to) continue;\n if (!inline.marks?.includes(mark)) return false;\n }\n return true;\n });\n\n const addMark = force ?? !allMarked;\n const content = document.content.map((block, index) => {\n if (index < start.block || index > end.block) return block;\n const from = index === start.block ? start.offset : 0;\n const to = index === end.block ? end.offset : getBlockLength(block);\n if (to <= from) return block;\n\n return {\n ...block,\n children: transformInlineRange(block.children, from, to, (marks) => {\n const next = new Set(marks);\n if (addMark) next.add(mark);\n else next.delete(mark);\n return MARK_ORDER.filter((item) => next.has(item));\n }),\n };\n });\n\n return { type: \"doc\", content };\n};\n\nexport const setTextVariant = (\n document: TextDocument,\n selection: TextSelection,\n variant: TextBlockVariant,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return {\n ...block,\n type: block.listType\n ? \"listItem\"\n : isHeadingVariant(variant)\n ? \"heading\"\n : \"paragraph\",\n variant,\n };\n }),\n };\n};\n\nconst inlineWithOffset = (\n inlines: TextInline[],\n offset: number,\n): { before: TextInline[]; after: TextInline[] } => ({\n before: sliceInlines(inlines, 0, offset),\n after: sliceInlines(\n inlines,\n offset,\n inlines.reduce((sum, item) => sum + item.text.length, 0),\n ),\n});\n\nexport const insertDocumentAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n insertedDocument: TextDocument,\n): { document: TextDocument; selection: TextSelection } => {\n const source = normalizeTextDocument(document);\n const inserted = normalizeTextDocument(insertedDocument);\n const { start, end } = getOrderedSelection(selection);\n const startIndex = Math.max(\n 0,\n Math.min(start.block, source.content.length - 1),\n );\n const endIndex = Math.max(\n startIndex,\n Math.min(end.block, source.content.length - 1),\n );\n const startBlock = source.content[startIndex]!;\n const endBlock = source.content[endIndex]!;\n const startOffset = Math.max(\n 0,\n Math.min(start.offset, getBlockLength(startBlock)),\n );\n const endOffset = Math.max(0, Math.min(end.offset, getBlockLength(endBlock)));\n const prefix = inlineWithOffset(startBlock.children, startOffset).before;\n const suffix = inlineWithOffset(endBlock.children, endOffset).after;\n const insertedBlocks = inserted.content.map((block) => ({\n ...block,\n children: block.children.map((inline) => ({\n ...inline,\n ...(inline.marks ? { marks: [...inline.marks] } : {}),\n })),\n }));\n\n const leftSurvives = prefix.length > 0 || startOffset > 0;\n const rightSurvives =\n suffix.length > 0 || endOffset < getBlockLength(endBlock);\n const first = insertedBlocks[0]!;\n const last = insertedBlocks[insertedBlocks.length - 1]!;\n const separatePrefix = leftSurvives && first.type === \"heading\";\n const separateSuffix = rightSurvives && last.type === \"heading\";\n const replacement: TextBlock[] = [];\n\n if (separatePrefix) {\n replacement.push({\n ...startBlock,\n children: prefix,\n });\n }\n\n if (insertedBlocks.length === 1) {\n if (separatePrefix || separateSuffix) {\n replacement.push({\n ...first,\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n if (separateSuffix) {\n replacement.push({ ...endBlock, children: suffix });\n } else {\n const finalIndex = replacement.length - 1;\n replacement[finalIndex] = {\n ...replacement[finalIndex]!,\n children: normalizeInlines([\n ...replacement[finalIndex]!.children,\n ...suffix,\n ]),\n };\n }\n } else {\n const useInsertedStyle = !leftSurvives && !rightSurvives;\n replacement.push({\n ...(useInsertedStyle ? first : startBlock),\n type: useInsertedStyle ? first.type : startBlock.type,\n variant: useInsertedStyle ? first.variant : startBlock.variant,\n children: normalizeInlines([...prefix, ...first.children, ...suffix]),\n });\n }\n } else {\n replacement.push({\n ...(separatePrefix || !leftSurvives ? first : startBlock),\n type: separatePrefix || !leftSurvives ? first.type : startBlock.type,\n variant:\n separatePrefix || !leftSurvives ? first.variant : startBlock.variant,\n children: separatePrefix\n ? first.children\n : normalizeInlines([...prefix, ...first.children]),\n });\n\n for (let index = 1; index < insertedBlocks.length - 1; index += 1) {\n replacement.push(insertedBlocks[index]!);\n }\n\n if (separateSuffix) {\n replacement.push(last);\n replacement.push({ ...endBlock, children: suffix });\n } else {\n replacement.push({\n ...(rightSurvives ? endBlock : last),\n type: rightSurvives ? endBlock.type : last.type,\n variant: rightSurvives ? endBlock.variant : last.variant,\n children: normalizeInlines([...last.children, ...suffix]),\n });\n }\n }\n\n const content = [\n ...source.content.slice(0, startIndex),\n ...replacement,\n ...source.content.slice(endIndex + 1),\n ];\n const caretReplacementIndex = separateSuffix\n ? replacement.length - 2\n : replacement.length - 1;\n const caretBlock = replacement[Math.max(0, caretReplacementIndex)]!;\n const suffixLength = suffix.reduce(\n (total, inline) => total + inline.text.length,\n 0,\n );\n const caretOffset = separateSuffix\n ? getBlockLength(caretBlock)\n : Math.max(0, getBlockLength(caretBlock) - suffixLength);\n const caret = {\n block: startIndex + Math.max(0, caretReplacementIndex),\n offset: caretOffset,\n };\n\n return {\n document: {\n type: \"doc\",\n content: content.length ? content : createTextDocument().content,\n },\n selection: { anchor: caret, focus: caret },\n };\n};\n\nexport const insertTextAtSelection = (\n document: TextDocument,\n selection: TextSelection,\n text: string,\n marks: TextMark[] = [],\n): { document: TextDocument; selection: TextSelection } => {\n const inserted: TextDocument = {\n type: \"doc\",\n content: text.split(\"\\n\").map((line) => ({\n type: \"paragraph\",\n variant: \"body1\",\n children: line\n ? [{ text: line, ...(marks.length ? { marks: [...marks] } : {}) }]\n : [],\n })),\n };\n return insertDocumentAtSelection(document, selection, inserted);\n};\n\n/** Apply text alignment to every block touched by the current selection. */\nexport const setTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n align: TextAlign,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n return { ...block, align };\n }),\n };\n};\n\nexport const getTextAlign = (\n document: TextDocument,\n selection: TextSelection,\n): TextAlign | null => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n if (selected.length === 0) return null;\n const align = selected[0]!.align ?? null;\n return selected.every((block) => (block.align ?? null) === align)\n ? align\n : null;\n};\n\n/** Toggle a list type for the selected blocks; null removes list formatting. */\nexport const setListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType | null,\n): TextDocument => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n const remove =\n listType != null &&\n selected.length > 0 &&\n selected.every((block) => block.listType === listType);\n return {\n type: \"doc\",\n content: document.content.map((block, index) => {\n if (index < start.block || index > lastBlock) return block;\n if (remove || listType == null) {\n const { listType: _listType, ...rest } = block;\n return {\n ...rest,\n type: block.type === \"listItem\" ? \"paragraph\" : block.type,\n };\n }\n return { ...block, type: \"listItem\", listType };\n }),\n };\n};\n\nexport const getListType = (\n document: TextDocument,\n selection: TextSelection,\n listType: TextListType,\n): boolean => {\n const { start, end } = getOrderedSelection(selection);\n const lastBlock = isCollapsedSelection(selection)\n ? start.block\n : end.offset === 0 && end.block > start.block\n ? end.block - 1\n : end.block;\n const selected = document.content.slice(start.block, lastBlock + 1);\n return (\n selected.length > 0 &&\n selected.every((block) => block.listType === listType)\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-parser.cjs","names":["normalizeInlines","isHeadingVariant","createTextDocument","normalizeTextDocument"],"sources":["../../../../src/components/editors/text-editor/text-parser.ts"],"sourcesContent":["import { createTextDocument, isHeadingVariant, normalizeInlines, normalizeTextDocument } from \"./text-document\";\nimport type { TextBlock, TextDocument, TextInline, TextMark, TextBlockVariant, TextAlign, TextListType } from \"./text-editor.types\";\n\nconst VARIANTS = new Set<TextBlockVariant>([\n \"display1\", \"display2\", \"headline1\", \"headline2\", \"headline3\", \"headline4\",\n \"subtitle1\", \"subtitle2\", \"body1\", \"body2\", \"button\", \"caption\", \"overline\",\n]);\n\nconst MARK_TAGS: Record<string, TextMark> = {\n b: \"bold\",\n strong: \"bold\",\n i: \"italic\",\n em: \"italic\",\n u: \"underline\",\n s: \"strike\",\n strike: \"strike\",\n del: \"strike\",\n};\n\nconst HEADING_VARIANTS: Record<string, TextBlockVariant> = {\n h1: \"headline1\",\n h2: \"headline2\",\n h3: \"headline3\",\n h4: \"headline4\",\n};\n\nconst appendText = (target: TextInline[], text: string, marks: TextMark[]) => {\n if (!text) return;\n target.push({ text, ...(marks.length ? { marks: [...marks] } : {}) });\n};\n\nconst readInlineNodes = (\n nodes: NodeListOf<ChildNode> | ChildNode[],\n inheritedMarks: TextMark[] = [],\n): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const node of Array.from(nodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n appendText(result, node.textContent ?? \"\", inheritedMarks);\n continue;\n }\n\n if (!(node instanceof Element)) continue;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) continue;\n\n if (tag === \"br\") {\n if (!node.hasAttribute(\"data-puzzle-placeholder\")) {\n appendText(result, \"\\n\", inheritedMarks);\n }\n continue;\n }\n\n if (\n node.children.length === 1 &&\n node.firstElementChild?.tagName.toLowerCase() === \"br\" &&\n node.textContent === \"\"\n ) {\n // Browsers often represent an empty contenteditable block as <p><br></p>.\n continue;\n }\n\n const mark = MARK_TAGS[tag];\n const nextMarks = mark\n ? Array.from(new Set([...inheritedMarks, mark]))\n : inheritedMarks;\n\n // Unsupported elements are transparent: keep their readable children but\n // deliberately discard their styling, attributes and behavior.\n result.push(...readInlineNodes(Array.from(node.childNodes), nextMarks));\n }\n\n return normalizeInlines(result);\n};\n\nconst createBlock = (\n variant: TextBlockVariant,\n children: TextInline[],\n forceHeading?: boolean,\n align?: TextAlign,\n listType?: TextListType,\n): TextBlock => ({\n type: listType ? \"listItem\" : forceHeading || isHeadingVariant(variant) ? \"heading\" : \"paragraph\",\n variant,\n children: normalizeInlines(children),\n ...(align ? { align } : {}),\n ...(listType ? { listType } : {}),\n});\n\nconst getBlockAlign = (element: Element): TextAlign | undefined => {\n const align =\n element.getAttribute(\"data-puzzle-align\") ??\n (element as HTMLElement).style.textAlign;\n return align === \"left\" || align === \"center\" || align === \"right\" || align === \"justify\"\n ? align\n : undefined;\n};\n\nconst getBlockVariant = (element: Element): TextBlockVariant | undefined => {\n const provided = element.getAttribute(\"data-puzzle-variant\");\n if (provided && VARIANTS.has(provided as TextBlockVariant)) {\n return provided as TextBlockVariant;\n }\n\n return HEADING_VARIANTS[element.tagName.toLowerCase()];\n};\n\nconst containsBlockChild = (element: Element): boolean =>\n Array.from(element.children).some((child) =>\n [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\", \"ul\", \"ol\", \"li\"].includes(\n child.tagName.toLowerCase(),\n ),\n );\n\nconst readHtmlBlocks = (nodes: NodeListOf<ChildNode> | ChildNode[]): TextBlock[] => {\n const blocks: TextBlock[] = [];\n let looseInlines: TextInline[] = [];\n\n const flushLoose = () => {\n if (!looseInlines.length) return;\n blocks.push(createBlock(\"body1\", looseInlines));\n looseInlines = [];\n };\n\n const visit = (node: ChildNode) => {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (looseInlines.length === 0 && !text.trim()) return;\n appendText(looseInlines, text, []);\n return;\n }\n\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) return;\n const isKnownBlock = [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\"].includes(tag);\n\n if (isKnownBlock && containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (isKnownBlock) {\n flushLoose();\n const variant = getBlockVariant(node) ?? \"body1\";\n const forceHeading = Boolean(HEADING_VARIANTS[tag]);\n const dataListType = node.getAttribute(\"data-puzzle-list-type\");\n const listType: TextListType | undefined = dataListType === \"bullet\" || dataListType === \"ordered\" ? dataListType : undefined;\n blocks.push(createBlock(variant, readInlineNodes(Array.from(node.childNodes)), forceHeading, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"li\") {\n flushLoose();\n const parentTag = node.parentElement?.tagName.toLowerCase();\n const listType: TextListType | undefined = parentTag === \"ul\" ? \"bullet\" : parentTag === \"ol\" ? \"ordered\" : undefined;\n blocks.push(createBlock(\"body1\", readInlineNodes(Array.from(node.childNodes)), false, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"ul\" || tag === \"ol\") {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (tag === \"br\") {\n appendText(looseInlines, \"\\n\", []);\n return;\n }\n\n if (containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n // Text directly inside an unknown wrapper is retained; the wrapper itself\n // and all unsupported attributes are discarded.\n looseInlines.push(...readInlineNodes([node]));\n };\n\n for (const node of Array.from(nodes)) visit(node);\n flushLoose();\n return blocks;\n};\n\n/** Parse HTML into the supported document model. Runs in a browser environment. */\nexport const parseHtmlToDocument = (html: string): TextDocument => {\n if (typeof DOMParser === \"undefined\") {\n const withoutUnsafeContent = html.replace(\n /<(script|style|iframe|object|embed|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi,\n \"\",\n );\n return createTextDocument(withoutUnsafeContent.replace(/<[^>]*>/g, \"\"));\n }\n\n const parsed = new DOMParser().parseFromString(html, \"text/html\");\n const blocks = readHtmlBlocks(Array.from(parsed.body.childNodes));\n\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n\nconst parseInlineMarkdown = (input: string): TextInline[] => {\n const pattern = /!\\[([^\\]]*)\\]\\([^)]+\\)|\\[([^\\]]+)\\]\\([^)]+\\)|\\*\\*(.+?)\\*\\*|__(.+?)__|~~(.+?)~~|\\*(.+?)\\*|_(.+?)_|`([^`]+)`/g;\n const result: TextInline[] = [];\n let previousIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(input)) !== null) {\n appendText(result, input.slice(previousIndex, match.index), []);\n\n if (match[1] !== undefined) {\n // Images are not supported nodes; preserve their useful alt text.\n appendText(result, match[1], []);\n } else if (match[2] !== undefined) {\n // Links are not supported nodes; preserve the link label only.\n appendText(result, match[2], []);\n } else if (match[3] !== undefined || match[4] !== undefined) {\n appendText(result, match[3] ?? match[4] ?? \"\", [\"bold\"]);\n } else if (match[5] !== undefined) {\n appendText(result, match[5], [\"strike\"]);\n } else if (match[6] !== undefined || match[7] !== undefined) {\n appendText(result, match[6] ?? match[7] ?? \"\", [\"italic\"]);\n } else if (match[8] !== undefined) {\n // Inline code is not supported; preserve its contents as regular text.\n appendText(result, match[8], []);\n }\n\n previousIndex = pattern.lastIndex;\n }\n\n appendText(result, input.slice(previousIndex), []);\n return normalizeInlines(result);\n};\n\n/**\n * Parse a small Markdown subset without a dependency. Unsupported constructs\n * are flattened to readable text; supported inline marks, headings and lists remain semantic.\n */\nexport const parseMarkdownToDocument = (markdown: string): TextDocument => {\n const normalized = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalized.split(\"\\n\");\n const blocks: TextBlock[] = [];\n let paragraphLines: string[] = [];\n let insideCodeFence = false;\n\n const flushParagraph = () => {\n if (!paragraphLines.length) return;\n const text = paragraphLines.join(\"\\n\");\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(text)));\n paragraphLines = [];\n };\n\n for (const line of lines) {\n if (/^\\s*```/.test(line) || /^\\s*~~~/.test(line)) {\n flushParagraph();\n insideCodeFence = !insideCodeFence;\n continue;\n }\n\n if (!insideCodeFence && !line.trim()) {\n flushParagraph();\n continue;\n }\n\n if (!insideCodeFence) {\n const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.+?)\\s*#*\\s*$/);\n if (heading) {\n flushParagraph();\n const level = heading[1].length;\n const variant = (`headline${level}`) as TextBlockVariant;\n blocks.push(createBlock(variant, parseInlineMarkdown(heading[2]), true));\n continue;\n }\n\n if (/^\\s{0,3}(?:[-*_]\\s*){3,}$/.test(line)) {\n flushParagraph();\n continue;\n }\n\n const listItem = line.match(/^\\s{0,3}([-+*]|\\d+[.)])\\s+(.+)$/);\n if (listItem) {\n flushParagraph();\n const listType: TextListType = /^\\d/.test(listItem[1]) ? \"ordered\" : \"bullet\";\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(listItem[2]), false, undefined, listType));\n continue;\n }\n\n const quote = line.match(/^\\s{0,3}>\\s?(.*)$/);\n if (quote) {\n paragraphLines.push(quote[1]);\n continue;\n }\n }\n\n paragraphLines.push(line);\n }\n\n flushParagraph();\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-parser.cjs","names":["normalizeInlines","isHeadingVariant","createTextDocument","normalizeTextDocument"],"sources":["../../../../src/components/editors/text-editor/text-parser.ts"],"sourcesContent":["import { createTextDocument, isHeadingVariant, normalizeInlines, normalizeTextDocument } from \"./text-document\";\nimport type { TextBlock, TextDocument, TextInline, TextMark, TextBlockVariant, TextAlign, TextListType } from \"./text-editor.types\";\n\nconst VARIANTS = new Set<TextBlockVariant>([\n \"display1\", \"display2\", \"headline1\", \"headline2\", \"headline3\", \"headline4\",\n \"subtitle1\", \"subtitle2\", \"body1\", \"body2\", \"button\", \"caption\", \"overline\",\n]);\n\nconst MARK_TAGS: Record<string, TextMark> = {\n b: \"bold\",\n strong: \"bold\",\n i: \"italic\",\n em: \"italic\",\n u: \"underline\",\n s: \"strike\",\n strike: \"strike\",\n del: \"strike\",\n};\n\nconst HEADING_VARIANTS: Record<string, TextBlockVariant> = {\n h1: \"headline1\",\n h2: \"headline2\",\n h3: \"headline3\",\n h4: \"headline4\",\n};\n\nconst appendText = (target: TextInline[], text: string, marks: TextMark[]) => {\n if (!text) return;\n target.push({ text, ...(marks.length ? { marks: [...marks] } : {}) });\n};\n\nconst readInlineNodes = (\n nodes: NodeListOf<ChildNode> | ChildNode[],\n inheritedMarks: TextMark[] = [],\n): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const node of Array.from(nodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n appendText(result, node.textContent ?? \"\", inheritedMarks);\n continue;\n }\n\n if (!(node instanceof Element)) continue;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) continue;\n\n if (tag === \"br\") {\n if (!node.hasAttribute(\"data-puzzle-placeholder\")) {\n appendText(result, \"\\n\", inheritedMarks);\n }\n continue;\n }\n\n if (\n node.children.length === 1 &&\n node.firstElementChild?.tagName.toLowerCase() === \"br\" &&\n node.textContent === \"\"\n ) {\n // Browsers often represent an empty contenteditable block as <p><br></p>.\n continue;\n }\n\n const mark = MARK_TAGS[tag];\n const nextMarks = mark\n ? Array.from(new Set([...inheritedMarks, mark]))\n : inheritedMarks;\n\n // Unsupported elements are transparent: keep their readable children but\n // deliberately discard their styling, attributes and behavior.\n result.push(...readInlineNodes(Array.from(node.childNodes), nextMarks));\n }\n\n return normalizeInlines(result);\n};\n\nconst createBlock = (\n variant: TextBlockVariant,\n children: TextInline[],\n forceHeading?: boolean,\n align?: TextAlign,\n listType?: TextListType,\n): TextBlock => ({\n type: listType ? \"listItem\" : forceHeading || isHeadingVariant(variant) ? \"heading\" : \"paragraph\",\n variant,\n children: normalizeInlines(children),\n ...(align ? { align } : {}),\n ...(listType ? { listType } : {}),\n});\n\nconst getBlockAlign = (element: Element): TextAlign | undefined => {\n const align =\n element.getAttribute(\"data-puzzle-align\") ??\n (element as HTMLElement).style.textAlign;\n return align === \"left\" || align === \"center\" || align === \"right\" || align === \"justify\"\n ? align\n : undefined;\n};\n\nconst getBlockVariant = (element: Element): TextBlockVariant | undefined => {\n const provided = element.getAttribute(\"data-puzzle-variant\");\n if (provided && VARIANTS.has(provided as TextBlockVariant)) {\n return provided as TextBlockVariant;\n }\n\n return HEADING_VARIANTS[element.tagName.toLowerCase()];\n};\n\nconst containsBlockChild = (element: Element): boolean =>\n Array.from(element.children).some((child) =>\n [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\", \"ul\", \"ol\", \"li\"].includes(\n child.tagName.toLowerCase(),\n ),\n );\n\nconst readHtmlBlocks = (nodes: NodeListOf<ChildNode> | ChildNode[]): TextBlock[] => {\n const blocks: TextBlock[] = [];\n let looseInlines: TextInline[] = [];\n\n const flushLoose = () => {\n if (!looseInlines.length) return;\n blocks.push(createBlock(\"body1\", looseInlines));\n looseInlines = [];\n };\n\n const visit = (node: ChildNode) => {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (looseInlines.length === 0 && !text.trim()) return;\n appendText(looseInlines, text, []);\n return;\n }\n\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) return;\n const isKnownBlock = [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\"].includes(tag);\n\n if (isKnownBlock && containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (isKnownBlock) {\n flushLoose();\n const variant = getBlockVariant(node) ?? \"body1\";\n const forceHeading = Boolean(HEADING_VARIANTS[tag]);\n const dataListType = node.getAttribute(\"data-puzzle-list-type\");\n const listType: TextListType | undefined = dataListType === \"bullet\" || dataListType === \"ordered\" ? dataListType : undefined;\n blocks.push(createBlock(variant, readInlineNodes(Array.from(node.childNodes)), forceHeading, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"li\") {\n flushLoose();\n const parentTag = node.parentElement?.tagName.toLowerCase();\n const listType: TextListType | undefined = parentTag === \"ul\" ? \"bullet\" : parentTag === \"ol\" ? \"ordered\" : undefined;\n blocks.push(createBlock(\"body1\", readInlineNodes(Array.from(node.childNodes)), false, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"ul\" || tag === \"ol\") {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (tag === \"br\") {\n appendText(looseInlines, \"\\n\", []);\n return;\n }\n\n if (containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n // Text directly inside an unknown wrapper is retained; the wrapper itself\n // and all unsupported attributes are discarded.\n looseInlines.push(...readInlineNodes([node]));\n };\n\n for (const node of Array.from(nodes)) visit(node);\n flushLoose();\n return blocks;\n};\n\n/** Parse HTML into the supported document model. Runs in a browser environment. */\nexport const parseHtmlToDocument = (html: string): TextDocument => {\n if (typeof DOMParser === \"undefined\") {\n const withoutUnsafeContent = html.replace(\n /<(script|style|iframe|object|embed|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi,\n \"\",\n );\n return createTextDocument(withoutUnsafeContent.replace(/<[^>]*>/g, \"\"));\n }\n\n const parsed = new DOMParser().parseFromString(html, \"text/html\");\n const blocks = readHtmlBlocks(Array.from(parsed.body.childNodes));\n\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n\nconst parseInlineMarkdown = (input: string): TextInline[] => {\n const pattern = /!\\[([^\\]]*)\\]\\([^)]+\\)|\\[([^\\]]+)\\]\\([^)]+\\)|\\*\\*(.+?)\\*\\*|__(.+?)__|~~(.+?)~~|\\*(.+?)\\*|_(.+?)_|`([^`]+)`/g;\n const result: TextInline[] = [];\n let previousIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(input)) !== null) {\n appendText(result, input.slice(previousIndex, match.index), []);\n\n if (match[1] !== undefined) {\n // Images are not supported nodes; preserve their useful alt text.\n appendText(result, match[1], []);\n } else if (match[2] !== undefined) {\n // Links are not supported nodes; preserve the link label only.\n appendText(result, match[2], []);\n } else if (match[3] !== undefined || match[4] !== undefined) {\n appendText(result, match[3] ?? match[4] ?? \"\", [\"bold\"]);\n } else if (match[5] !== undefined) {\n appendText(result, match[5], [\"strike\"]);\n } else if (match[6] !== undefined || match[7] !== undefined) {\n appendText(result, match[6] ?? match[7] ?? \"\", [\"italic\"]);\n } else if (match[8] !== undefined) {\n // Inline code is not supported; preserve its contents as regular text.\n appendText(result, match[8], []);\n }\n\n previousIndex = pattern.lastIndex;\n }\n\n appendText(result, input.slice(previousIndex), []);\n return normalizeInlines(result);\n};\n\n/**\n * Parse a small Markdown subset without a dependency. Unsupported constructs\n * are flattened to readable text; supported inline marks, headings and lists remain semantic.\n */\nexport const parseMarkdownToDocument = (markdown: string): TextDocument => {\n const normalized = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalized.split(\"\\n\");\n const blocks: TextBlock[] = [];\n let paragraphLines: string[] = [];\n let insideCodeFence = false;\n\n const flushParagraph = () => {\n if (!paragraphLines.length) return;\n const text = paragraphLines.join(\"\\n\");\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(text)));\n paragraphLines = [];\n };\n\n for (const line of lines) {\n if (/^\\s*```/.test(line) || /^\\s*~~~/.test(line)) {\n flushParagraph();\n insideCodeFence = !insideCodeFence;\n continue;\n }\n\n if (!insideCodeFence && !line.trim()) {\n flushParagraph();\n continue;\n }\n\n if (!insideCodeFence) {\n const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.+?)\\s*#*\\s*$/);\n if (heading) {\n flushParagraph();\n const level = heading[1]!.length;\n const variant = (`headline${level}`) as TextBlockVariant;\n blocks.push(createBlock(variant, parseInlineMarkdown(heading[2]!), true));\n continue;\n }\n\n if (/^\\s{0,3}(?:[-*_]\\s*){3,}$/.test(line)) {\n flushParagraph();\n continue;\n }\n\n const listItem = line.match(/^\\s{0,3}([-+*]|\\d+[.)])\\s+(.+)$/);\n if (listItem) {\n flushParagraph();\n const listType: TextListType = /^\\d/.test(listItem[1]!) ? \"ordered\" : \"bullet\";\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(listItem[2]!), false, undefined, listType));\n continue;\n }\n\n const quote = line.match(/^\\s{0,3}>\\s?(.*)$/);\n if (quote) {\n paragraphLines.push(quote[1]!);\n continue;\n }\n }\n\n paragraphLines.push(line);\n }\n\n flushParagraph();\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-parser.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-parser.ts"],"sourcesContent":["import { createTextDocument, isHeadingVariant, normalizeInlines, normalizeTextDocument } from \"./text-document\";\nimport type { TextBlock, TextDocument, TextInline, TextMark, TextBlockVariant, TextAlign, TextListType } from \"./text-editor.types\";\n\nconst VARIANTS = new Set<TextBlockVariant>([\n \"display1\", \"display2\", \"headline1\", \"headline2\", \"headline3\", \"headline4\",\n \"subtitle1\", \"subtitle2\", \"body1\", \"body2\", \"button\", \"caption\", \"overline\",\n]);\n\nconst MARK_TAGS: Record<string, TextMark> = {\n b: \"bold\",\n strong: \"bold\",\n i: \"italic\",\n em: \"italic\",\n u: \"underline\",\n s: \"strike\",\n strike: \"strike\",\n del: \"strike\",\n};\n\nconst HEADING_VARIANTS: Record<string, TextBlockVariant> = {\n h1: \"headline1\",\n h2: \"headline2\",\n h3: \"headline3\",\n h4: \"headline4\",\n};\n\nconst appendText = (target: TextInline[], text: string, marks: TextMark[]) => {\n if (!text) return;\n target.push({ text, ...(marks.length ? { marks: [...marks] } : {}) });\n};\n\nconst readInlineNodes = (\n nodes: NodeListOf<ChildNode> | ChildNode[],\n inheritedMarks: TextMark[] = [],\n): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const node of Array.from(nodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n appendText(result, node.textContent ?? \"\", inheritedMarks);\n continue;\n }\n\n if (!(node instanceof Element)) continue;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) continue;\n\n if (tag === \"br\") {\n if (!node.hasAttribute(\"data-puzzle-placeholder\")) {\n appendText(result, \"\\n\", inheritedMarks);\n }\n continue;\n }\n\n if (\n node.children.length === 1 &&\n node.firstElementChild?.tagName.toLowerCase() === \"br\" &&\n node.textContent === \"\"\n ) {\n // Browsers often represent an empty contenteditable block as <p><br></p>.\n continue;\n }\n\n const mark = MARK_TAGS[tag];\n const nextMarks = mark\n ? Array.from(new Set([...inheritedMarks, mark]))\n : inheritedMarks;\n\n // Unsupported elements are transparent: keep their readable children but\n // deliberately discard their styling, attributes and behavior.\n result.push(...readInlineNodes(Array.from(node.childNodes), nextMarks));\n }\n\n return normalizeInlines(result);\n};\n\nconst createBlock = (\n variant: TextBlockVariant,\n children: TextInline[],\n forceHeading?: boolean,\n align?: TextAlign,\n listType?: TextListType,\n): TextBlock => ({\n type: listType ? \"listItem\" : forceHeading || isHeadingVariant(variant) ? \"heading\" : \"paragraph\",\n variant,\n children: normalizeInlines(children),\n ...(align ? { align } : {}),\n ...(listType ? { listType } : {}),\n});\n\nconst getBlockAlign = (element: Element): TextAlign | undefined => {\n const align =\n element.getAttribute(\"data-puzzle-align\") ??\n (element as HTMLElement).style.textAlign;\n return align === \"left\" || align === \"center\" || align === \"right\" || align === \"justify\"\n ? align\n : undefined;\n};\n\nconst getBlockVariant = (element: Element): TextBlockVariant | undefined => {\n const provided = element.getAttribute(\"data-puzzle-variant\");\n if (provided && VARIANTS.has(provided as TextBlockVariant)) {\n return provided as TextBlockVariant;\n }\n\n return HEADING_VARIANTS[element.tagName.toLowerCase()];\n};\n\nconst containsBlockChild = (element: Element): boolean =>\n Array.from(element.children).some((child) =>\n [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\", \"ul\", \"ol\", \"li\"].includes(\n child.tagName.toLowerCase(),\n ),\n );\n\nconst readHtmlBlocks = (nodes: NodeListOf<ChildNode> | ChildNode[]): TextBlock[] => {\n const blocks: TextBlock[] = [];\n let looseInlines: TextInline[] = [];\n\n const flushLoose = () => {\n if (!looseInlines.length) return;\n blocks.push(createBlock(\"body1\", looseInlines));\n looseInlines = [];\n };\n\n const visit = (node: ChildNode) => {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (looseInlines.length === 0 && !text.trim()) return;\n appendText(looseInlines, text, []);\n return;\n }\n\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) return;\n const isKnownBlock = [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\"].includes(tag);\n\n if (isKnownBlock && containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (isKnownBlock) {\n flushLoose();\n const variant = getBlockVariant(node) ?? \"body1\";\n const forceHeading = Boolean(HEADING_VARIANTS[tag]);\n const dataListType = node.getAttribute(\"data-puzzle-list-type\");\n const listType: TextListType | undefined = dataListType === \"bullet\" || dataListType === \"ordered\" ? dataListType : undefined;\n blocks.push(createBlock(variant, readInlineNodes(Array.from(node.childNodes)), forceHeading, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"li\") {\n flushLoose();\n const parentTag = node.parentElement?.tagName.toLowerCase();\n const listType: TextListType | undefined = parentTag === \"ul\" ? \"bullet\" : parentTag === \"ol\" ? \"ordered\" : undefined;\n blocks.push(createBlock(\"body1\", readInlineNodes(Array.from(node.childNodes)), false, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"ul\" || tag === \"ol\") {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (tag === \"br\") {\n appendText(looseInlines, \"\\n\", []);\n return;\n }\n\n if (containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n // Text directly inside an unknown wrapper is retained; the wrapper itself\n // and all unsupported attributes are discarded.\n looseInlines.push(...readInlineNodes([node]));\n };\n\n for (const node of Array.from(nodes)) visit(node);\n flushLoose();\n return blocks;\n};\n\n/** Parse HTML into the supported document model. Runs in a browser environment. */\nexport const parseHtmlToDocument = (html: string): TextDocument => {\n if (typeof DOMParser === \"undefined\") {\n const withoutUnsafeContent = html.replace(\n /<(script|style|iframe|object|embed|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi,\n \"\",\n );\n return createTextDocument(withoutUnsafeContent.replace(/<[^>]*>/g, \"\"));\n }\n\n const parsed = new DOMParser().parseFromString(html, \"text/html\");\n const blocks = readHtmlBlocks(Array.from(parsed.body.childNodes));\n\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n\nconst parseInlineMarkdown = (input: string): TextInline[] => {\n const pattern = /!\\[([^\\]]*)\\]\\([^)]+\\)|\\[([^\\]]+)\\]\\([^)]+\\)|\\*\\*(.+?)\\*\\*|__(.+?)__|~~(.+?)~~|\\*(.+?)\\*|_(.+?)_|`([^`]+)`/g;\n const result: TextInline[] = [];\n let previousIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(input)) !== null) {\n appendText(result, input.slice(previousIndex, match.index), []);\n\n if (match[1] !== undefined) {\n // Images are not supported nodes; preserve their useful alt text.\n appendText(result, match[1], []);\n } else if (match[2] !== undefined) {\n // Links are not supported nodes; preserve the link label only.\n appendText(result, match[2], []);\n } else if (match[3] !== undefined || match[4] !== undefined) {\n appendText(result, match[3] ?? match[4] ?? \"\", [\"bold\"]);\n } else if (match[5] !== undefined) {\n appendText(result, match[5], [\"strike\"]);\n } else if (match[6] !== undefined || match[7] !== undefined) {\n appendText(result, match[6] ?? match[7] ?? \"\", [\"italic\"]);\n } else if (match[8] !== undefined) {\n // Inline code is not supported; preserve its contents as regular text.\n appendText(result, match[8], []);\n }\n\n previousIndex = pattern.lastIndex;\n }\n\n appendText(result, input.slice(previousIndex), []);\n return normalizeInlines(result);\n};\n\n/**\n * Parse a small Markdown subset without a dependency. Unsupported constructs\n * are flattened to readable text; supported inline marks, headings and lists remain semantic.\n */\nexport const parseMarkdownToDocument = (markdown: string): TextDocument => {\n const normalized = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalized.split(\"\\n\");\n const blocks: TextBlock[] = [];\n let paragraphLines: string[] = [];\n let insideCodeFence = false;\n\n const flushParagraph = () => {\n if (!paragraphLines.length) return;\n const text = paragraphLines.join(\"\\n\");\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(text)));\n paragraphLines = [];\n };\n\n for (const line of lines) {\n if (/^\\s*```/.test(line) || /^\\s*~~~/.test(line)) {\n flushParagraph();\n insideCodeFence = !insideCodeFence;\n continue;\n }\n\n if (!insideCodeFence && !line.trim()) {\n flushParagraph();\n continue;\n }\n\n if (!insideCodeFence) {\n const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.+?)\\s*#*\\s*$/);\n if (heading) {\n flushParagraph();\n const level = heading[1].length;\n const variant = (`headline${level}`) as TextBlockVariant;\n blocks.push(createBlock(variant, parseInlineMarkdown(heading[2]), true));\n continue;\n }\n\n if (/^\\s{0,3}(?:[-*_]\\s*){3,}$/.test(line)) {\n flushParagraph();\n continue;\n }\n\n const listItem = line.match(/^\\s{0,3}([-+*]|\\d+[.)])\\s+(.+)$/);\n if (listItem) {\n flushParagraph();\n const listType: TextListType = /^\\d/.test(listItem[1]) ? \"ordered\" : \"bullet\";\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(listItem[2]), false, undefined, listType));\n continue;\n }\n\n const quote = line.match(/^\\s{0,3}>\\s?(.*)$/);\n if (quote) {\n paragraphLines.push(quote[1]);\n continue;\n }\n }\n\n paragraphLines.push(line);\n }\n\n flushParagraph();\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-parser.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-parser.ts"],"sourcesContent":["import { createTextDocument, isHeadingVariant, normalizeInlines, normalizeTextDocument } from \"./text-document\";\nimport type { TextBlock, TextDocument, TextInline, TextMark, TextBlockVariant, TextAlign, TextListType } from \"./text-editor.types\";\n\nconst VARIANTS = new Set<TextBlockVariant>([\n \"display1\", \"display2\", \"headline1\", \"headline2\", \"headline3\", \"headline4\",\n \"subtitle1\", \"subtitle2\", \"body1\", \"body2\", \"button\", \"caption\", \"overline\",\n]);\n\nconst MARK_TAGS: Record<string, TextMark> = {\n b: \"bold\",\n strong: \"bold\",\n i: \"italic\",\n em: \"italic\",\n u: \"underline\",\n s: \"strike\",\n strike: \"strike\",\n del: \"strike\",\n};\n\nconst HEADING_VARIANTS: Record<string, TextBlockVariant> = {\n h1: \"headline1\",\n h2: \"headline2\",\n h3: \"headline3\",\n h4: \"headline4\",\n};\n\nconst appendText = (target: TextInline[], text: string, marks: TextMark[]) => {\n if (!text) return;\n target.push({ text, ...(marks.length ? { marks: [...marks] } : {}) });\n};\n\nconst readInlineNodes = (\n nodes: NodeListOf<ChildNode> | ChildNode[],\n inheritedMarks: TextMark[] = [],\n): TextInline[] => {\n const result: TextInline[] = [];\n\n for (const node of Array.from(nodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n appendText(result, node.textContent ?? \"\", inheritedMarks);\n continue;\n }\n\n if (!(node instanceof Element)) continue;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) continue;\n\n if (tag === \"br\") {\n if (!node.hasAttribute(\"data-puzzle-placeholder\")) {\n appendText(result, \"\\n\", inheritedMarks);\n }\n continue;\n }\n\n if (\n node.children.length === 1 &&\n node.firstElementChild?.tagName.toLowerCase() === \"br\" &&\n node.textContent === \"\"\n ) {\n // Browsers often represent an empty contenteditable block as <p><br></p>.\n continue;\n }\n\n const mark = MARK_TAGS[tag];\n const nextMarks = mark\n ? Array.from(new Set([...inheritedMarks, mark]))\n : inheritedMarks;\n\n // Unsupported elements are transparent: keep their readable children but\n // deliberately discard their styling, attributes and behavior.\n result.push(...readInlineNodes(Array.from(node.childNodes), nextMarks));\n }\n\n return normalizeInlines(result);\n};\n\nconst createBlock = (\n variant: TextBlockVariant,\n children: TextInline[],\n forceHeading?: boolean,\n align?: TextAlign,\n listType?: TextListType,\n): TextBlock => ({\n type: listType ? \"listItem\" : forceHeading || isHeadingVariant(variant) ? \"heading\" : \"paragraph\",\n variant,\n children: normalizeInlines(children),\n ...(align ? { align } : {}),\n ...(listType ? { listType } : {}),\n});\n\nconst getBlockAlign = (element: Element): TextAlign | undefined => {\n const align =\n element.getAttribute(\"data-puzzle-align\") ??\n (element as HTMLElement).style.textAlign;\n return align === \"left\" || align === \"center\" || align === \"right\" || align === \"justify\"\n ? align\n : undefined;\n};\n\nconst getBlockVariant = (element: Element): TextBlockVariant | undefined => {\n const provided = element.getAttribute(\"data-puzzle-variant\");\n if (provided && VARIANTS.has(provided as TextBlockVariant)) {\n return provided as TextBlockVariant;\n }\n\n return HEADING_VARIANTS[element.tagName.toLowerCase()];\n};\n\nconst containsBlockChild = (element: Element): boolean =>\n Array.from(element.children).some((child) =>\n [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\", \"ul\", \"ol\", \"li\"].includes(\n child.tagName.toLowerCase(),\n ),\n );\n\nconst readHtmlBlocks = (nodes: NodeListOf<ChildNode> | ChildNode[]): TextBlock[] => {\n const blocks: TextBlock[] = [];\n let looseInlines: TextInline[] = [];\n\n const flushLoose = () => {\n if (!looseInlines.length) return;\n blocks.push(createBlock(\"body1\", looseInlines));\n looseInlines = [];\n };\n\n const visit = (node: ChildNode) => {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (looseInlines.length === 0 && !text.trim()) return;\n appendText(looseInlines, text, []);\n return;\n }\n\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toLowerCase();\n if ([\"script\", \"style\", \"iframe\", \"object\", \"embed\", \"noscript\"].includes(tag)) return;\n const isKnownBlock = [\"p\", \"div\", \"h1\", \"h2\", \"h3\", \"h4\"].includes(tag);\n\n if (isKnownBlock && containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (isKnownBlock) {\n flushLoose();\n const variant = getBlockVariant(node) ?? \"body1\";\n const forceHeading = Boolean(HEADING_VARIANTS[tag]);\n const dataListType = node.getAttribute(\"data-puzzle-list-type\");\n const listType: TextListType | undefined = dataListType === \"bullet\" || dataListType === \"ordered\" ? dataListType : undefined;\n blocks.push(createBlock(variant, readInlineNodes(Array.from(node.childNodes)), forceHeading, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"li\") {\n flushLoose();\n const parentTag = node.parentElement?.tagName.toLowerCase();\n const listType: TextListType | undefined = parentTag === \"ul\" ? \"bullet\" : parentTag === \"ol\" ? \"ordered\" : undefined;\n blocks.push(createBlock(\"body1\", readInlineNodes(Array.from(node.childNodes)), false, getBlockAlign(node), listType));\n return;\n }\n\n if (tag === \"ul\" || tag === \"ol\") {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n if (tag === \"br\") {\n appendText(looseInlines, \"\\n\", []);\n return;\n }\n\n if (containsBlockChild(node)) {\n flushLoose();\n for (const child of Array.from(node.childNodes)) visit(child);\n return;\n }\n\n // Text directly inside an unknown wrapper is retained; the wrapper itself\n // and all unsupported attributes are discarded.\n looseInlines.push(...readInlineNodes([node]));\n };\n\n for (const node of Array.from(nodes)) visit(node);\n flushLoose();\n return blocks;\n};\n\n/** Parse HTML into the supported document model. Runs in a browser environment. */\nexport const parseHtmlToDocument = (html: string): TextDocument => {\n if (typeof DOMParser === \"undefined\") {\n const withoutUnsafeContent = html.replace(\n /<(script|style|iframe|object|embed|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi,\n \"\",\n );\n return createTextDocument(withoutUnsafeContent.replace(/<[^>]*>/g, \"\"));\n }\n\n const parsed = new DOMParser().parseFromString(html, \"text/html\");\n const blocks = readHtmlBlocks(Array.from(parsed.body.childNodes));\n\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n\nconst parseInlineMarkdown = (input: string): TextInline[] => {\n const pattern = /!\\[([^\\]]*)\\]\\([^)]+\\)|\\[([^\\]]+)\\]\\([^)]+\\)|\\*\\*(.+?)\\*\\*|__(.+?)__|~~(.+?)~~|\\*(.+?)\\*|_(.+?)_|`([^`]+)`/g;\n const result: TextInline[] = [];\n let previousIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(input)) !== null) {\n appendText(result, input.slice(previousIndex, match.index), []);\n\n if (match[1] !== undefined) {\n // Images are not supported nodes; preserve their useful alt text.\n appendText(result, match[1], []);\n } else if (match[2] !== undefined) {\n // Links are not supported nodes; preserve the link label only.\n appendText(result, match[2], []);\n } else if (match[3] !== undefined || match[4] !== undefined) {\n appendText(result, match[3] ?? match[4] ?? \"\", [\"bold\"]);\n } else if (match[5] !== undefined) {\n appendText(result, match[5], [\"strike\"]);\n } else if (match[6] !== undefined || match[7] !== undefined) {\n appendText(result, match[6] ?? match[7] ?? \"\", [\"italic\"]);\n } else if (match[8] !== undefined) {\n // Inline code is not supported; preserve its contents as regular text.\n appendText(result, match[8], []);\n }\n\n previousIndex = pattern.lastIndex;\n }\n\n appendText(result, input.slice(previousIndex), []);\n return normalizeInlines(result);\n};\n\n/**\n * Parse a small Markdown subset without a dependency. Unsupported constructs\n * are flattened to readable text; supported inline marks, headings and lists remain semantic.\n */\nexport const parseMarkdownToDocument = (markdown: string): TextDocument => {\n const normalized = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalized.split(\"\\n\");\n const blocks: TextBlock[] = [];\n let paragraphLines: string[] = [];\n let insideCodeFence = false;\n\n const flushParagraph = () => {\n if (!paragraphLines.length) return;\n const text = paragraphLines.join(\"\\n\");\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(text)));\n paragraphLines = [];\n };\n\n for (const line of lines) {\n if (/^\\s*```/.test(line) || /^\\s*~~~/.test(line)) {\n flushParagraph();\n insideCodeFence = !insideCodeFence;\n continue;\n }\n\n if (!insideCodeFence && !line.trim()) {\n flushParagraph();\n continue;\n }\n\n if (!insideCodeFence) {\n const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.+?)\\s*#*\\s*$/);\n if (heading) {\n flushParagraph();\n const level = heading[1]!.length;\n const variant = (`headline${level}`) as TextBlockVariant;\n blocks.push(createBlock(variant, parseInlineMarkdown(heading[2]!), true));\n continue;\n }\n\n if (/^\\s{0,3}(?:[-*_]\\s*){3,}$/.test(line)) {\n flushParagraph();\n continue;\n }\n\n const listItem = line.match(/^\\s{0,3}([-+*]|\\d+[.)])\\s+(.+)$/);\n if (listItem) {\n flushParagraph();\n const listType: TextListType = /^\\d/.test(listItem[1]!) ? \"ordered\" : \"bullet\";\n blocks.push(createBlock(\"body1\", parseInlineMarkdown(listItem[2]!), false, undefined, listType));\n continue;\n }\n\n const quote = line.match(/^\\s{0,3}>\\s?(.*)$/);\n if (quote) {\n paragraphLines.push(quote[1]!);\n continue;\n }\n }\n\n paragraphLines.push(line);\n }\n\n flushParagraph();\n return normalizeTextDocument({\n type: \"doc\",\n content: blocks.length ? blocks : createTextDocument().content,\n });\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-renderer.cjs","names":["cn"],"sources":["../../../../src/components/editors/text-editor/text-renderer.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@puzzle-ui/core\";\nimport \"./text-editor.css\";\nimport type { TextBlock, TextDocument, TextMark } from \"./text-editor.types\";\n\nexport type TextRendererProps = { document: TextDocument; className?: string };\n\nconst MARK_COMPONENTS: Record<TextMark, (children: ReactNode) => ReactNode> = {\n bold: (children) => <strong>{children}</strong>,\n italic: (children) => <em>{children}</em>,\n underline: (children) => <u>{children}</u>,\n strike: (children) => <s>{children}</s>,\n};\n\nconst renderInline = (block: TextBlock, blockIndex: number): ReactNode[] =>\n block.children.map((inline, inlineIndex) => {\n const parts = inline.text.split(\"\\n\");\n let node: ReactNode = parts.flatMap((part, partIndex) => partIndex === 0\n ? [part]\n : [<br key={`break-${blockIndex}-${inlineIndex}-${partIndex}`} />, part]);\n for (const mark of [...(inline.marks ?? [])].reverse()) node = MARK_COMPONENTS[mark](node);\n return <span key={`${blockIndex}-${inlineIndex}`}>{node}</span>;\n });\n\nconst HEADING_TAGS = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n// Without an explicit level, fall back to the level implied by the variant.\nconst getHeadingTag = (block: TextBlock): (typeof HEADING_TAGS)[number] => {\n if (block.headingLevel) return HEADING_TAGS[block.headingLevel - 1];\n\n switch (block.variant) {\n case \"display1\": case \"headline1\": return \"h1\";\n case \"display2\": case \"headline2\": return \"h2\";\n case \"headline3\": return \"h3\";\n default: return \"h4\";\n }\n};\n\nconst TextRenderer = ({ document, className }: TextRendererProps) => {\n const blocks = document.content;\n const output: ReactNode[] = [];\n let index = 0;\n while (index < blocks.length) {\n const block = blocks[index];\n if (block.listType) {\n const listType = block.listType;\n const items: ReactNode[] = [];\n while (index < blocks.length && blocks[index].listType === listType) {\n const item = blocks[index];\n const itemChildren = renderInline(item, index);\n items.push(<li key={index} className={cn(\"puzzle-text-renderer__block\", `puzzle-typography-${item.variant}`)} style={{ textAlign: item.align }}>\n {itemChildren.length ? itemChildren : <br />}\n </li>);\n index++;\n }\n const List = listType === \"ordered\" ? \"ol\" : \"ul\";\n output.push(<List key={`list-${index}`} className=\"puzzle-text-renderer__list\">{items}</List>);\n continue;\n }\n const blockClass = cn(\"puzzle-text-renderer__block\", `puzzle-typography-${block.variant}`);\n const children = renderInline(block, index);\n const style = { textAlign: block.align };\n if (block.type === \"heading\") {\n const HeadingTag = getHeadingTag(block);\n output.push(<HeadingTag key={index} className={blockClass} style={style}>{children.length ? children : <br />}</HeadingTag>);\n } else {\n output.push(<p key={index} className={blockClass} style={style}>{children.length ? children : <br />}</p>);\n }\n index++;\n }\n return <div className={cn(\"puzzle-text-renderer\", className)}>{output}</div>;\n};\n\nexport { TextRenderer };\n"],"mappings":";;;;AAOA,MAAM,kBAAwE;CAC5E,OAAO,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD,EAAS,SAAiB,CAAA;CAC9C,SAAS,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAK,SAAa,CAAA;CACxC,YAAY,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAI,SAAY,CAAA;CACzC,SAAS,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAI,SAAY,CAAA;AACxC;AAEA,MAAM,gBAAgB,OAAkB,eACtC,MAAM,SAAS,KAAK,QAAQ,gBAAgB;CAE1C,IAAI,OADU,OAAO,KAAK,MAAM,IACN,CAAC,CAAC,SAAS,MAAM,cAAc,cAAc,IACnE,CAAC,IAAI,IACL,CAAC,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,CAA6D,GAApD,SAAS,WAAW,GAAG,YAAY,GAAG,WAAc,GAAG,IAAI,CAAC;CAC1E,KAAK,MAAM,QAAQ,CAAC,GAAI,OAAO,SAAS,CAAC,CAAE,CAAC,CAAC,QAAQ,GAAG,OAAO,gBAAgB,KAAK,CAAC,IAAI;CACzF,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAA4C,KAAW,GAA5C,GAAG,WAAW,GAAG,aAA2B;AAChE,CAAC;AAEH,MAAM,eAAe;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AAGxD,MAAM,iBAAiB,UAAoD;CACzE,IAAI,MAAM,cAAc,OAAO,aAAa,MAAM,eAAe;CAEjE,QAAQ,MAAM,SAAd;EACE,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK,aAAa,OAAO;EACzB,SAAS,OAAO;CAClB;AACF;AAEA,MAAM,gBAAgB,EAAE,UAAU,gBAAmC;CACnE,MAAM,SAAS,SAAS;CACxB,MAAM,SAAsB,CAAC;CAC7B,IAAI,QAAQ;CACZ,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,QAAQ,OAAO;EACrB,IAAI,MAAM,UAAU;GAClB,MAAM,WAAW,MAAM;GACvB,MAAM,QAAqB,CAAC;GAC5B,OAAO,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC,aAAa,UAAU;IACnE,MAAM,OAAO,OAAO;IACpB,MAAM,eAAe,aAAa,MAAM,KAAK;IAC7C,MAAM,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;KAAgB,YAAA,GAAWA,gBAAAA,GAAAA,CAAG,+BAA+B,qBAAqB,KAAK,SAAS;KAAG,OAAO,EAAE,WAAW,KAAK,MAAM;KAC1I,UAAA,aAAa,SAAS,eAAe,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,CAAK,CAAA;IACzC,GAFgB,KAEhB,CAAC;IACL;GACF;GACA,MAAM,OAAO,aAAa,YAAY,OAAO;GAC7C,OAAO,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;IAA4B,WAAU;IAA8B,UAAA;GAAY,GAArE,QAAQ,OAA6D,CAAC;GAC7F;EACF;EACA,MAAM,cAAA,GAAaA,gBAAAA,GAAAA,CAAG,+BAA+B,qBAAqB,MAAM,SAAS;EACzF,MAAM,WAAW,aAAa,OAAO,KAAK;EAC1C,MAAM,QAAQ,EAAE,WAAW,MAAM,MAAM;EACvC,IAAI,MAAM,SAAS,WAAW;GAC5B,MAAM,aAAa,cAAc,KAAK;GACtC,OAAO,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;IAAwB,WAAW;IAAmB;IAAQ,UAAA,SAAS,SAAS,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,CAAK,CAAA;GAAc,GAA7F,KAA6F,CAAC;EAC7H,OACE,OAAO,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;GAAe,WAAW;GAAmB;GAAQ,UAAA,SAAS,SAAS,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,CAAK,CAAA;EAAK,GAApF,KAAoF,CAAC;EAE3G;CACF;CACA,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,YAAA,GAAWA,gBAAAA,GAAAA,CAAG,wBAAwB,SAAS;EAAI,UAAA;CAAY,CAAA;AAC7E"}
1
+ {"version":3,"file":"text-renderer.cjs","names":["cn"],"sources":["../../../../src/components/editors/text-editor/text-renderer.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@puzzle-ui/core\";\nimport \"./text-editor.css\";\nimport type { TextBlock, TextDocument, TextMark } from \"./text-editor.types\";\n\nexport type TextRendererProps = { document: TextDocument; className?: string };\n\nconst MARK_COMPONENTS: Record<TextMark, (children: ReactNode) => ReactNode> = {\n bold: (children) => <strong>{children}</strong>,\n italic: (children) => <em>{children}</em>,\n underline: (children) => <u>{children}</u>,\n strike: (children) => <s>{children}</s>,\n};\n\nconst renderInline = (block: TextBlock, blockIndex: number): ReactNode[] =>\n block.children.map((inline, inlineIndex) => {\n const parts = inline.text.split(\"\\n\");\n let node: ReactNode = parts.flatMap((part, partIndex) => partIndex === 0\n ? [part]\n : [<br key={`break-${blockIndex}-${inlineIndex}-${partIndex}`} />, part]);\n for (const mark of [...(inline.marks ?? [])].reverse()) node = MARK_COMPONENTS[mark](node);\n return <span key={`${blockIndex}-${inlineIndex}`}>{node}</span>;\n });\n\nconst HEADING_TAGS = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n// Without an explicit level, fall back to the level implied by the variant.\nconst getHeadingTag = (block: TextBlock): (typeof HEADING_TAGS)[number] => {\n if (block.headingLevel) return HEADING_TAGS[block.headingLevel - 1]!;\n\n switch (block.variant) {\n case \"display1\": case \"headline1\": return \"h1\";\n case \"display2\": case \"headline2\": return \"h2\";\n case \"headline3\": return \"h3\";\n default: return \"h4\";\n }\n};\n\nconst TextRenderer = ({ document, className }: TextRendererProps) => {\n const blocks = document.content;\n const output: ReactNode[] = [];\n let index = 0;\n while (index < blocks.length) {\n const block = blocks[index]!;\n if (block.listType) {\n const listType = block.listType;\n const items: ReactNode[] = [];\n while (index < blocks.length && blocks[index]!.listType === listType) {\n const item = blocks[index]!;\n const itemChildren = renderInline(item, index);\n items.push(<li key={index} className={cn(\"puzzle-text-renderer__block\", `puzzle-typography-${item.variant}`)} style={{ textAlign: item.align }}>\n {itemChildren.length ? itemChildren : <br />}\n </li>);\n index++;\n }\n const List = listType === \"ordered\" ? \"ol\" : \"ul\";\n output.push(<List key={`list-${index}`} className=\"puzzle-text-renderer__list\">{items}</List>);\n continue;\n }\n const blockClass = cn(\"puzzle-text-renderer__block\", `puzzle-typography-${block.variant}`);\n const children = renderInline(block, index);\n const style = { textAlign: block.align };\n if (block.type === \"heading\") {\n const HeadingTag = getHeadingTag(block);\n output.push(<HeadingTag key={index} className={blockClass} style={style}>{children.length ? children : <br />}</HeadingTag>);\n } else {\n output.push(<p key={index} className={blockClass} style={style}>{children.length ? children : <br />}</p>);\n }\n index++;\n }\n return <div className={cn(\"puzzle-text-renderer\", className)}>{output}</div>;\n};\n\nexport { TextRenderer };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"text-renderer.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-renderer.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@puzzle-ui/core\";\nimport \"./text-editor.css\";\nimport type { TextBlock, TextDocument, TextMark } from \"./text-editor.types\";\n\nexport type TextRendererProps = { document: TextDocument; className?: string };\n\nconst MARK_COMPONENTS: Record<TextMark, (children: ReactNode) => ReactNode> = {\n bold: (children) => <strong>{children}</strong>,\n italic: (children) => <em>{children}</em>,\n underline: (children) => <u>{children}</u>,\n strike: (children) => <s>{children}</s>,\n};\n\nconst renderInline = (block: TextBlock, blockIndex: number): ReactNode[] =>\n block.children.map((inline, inlineIndex) => {\n const parts = inline.text.split(\"\\n\");\n let node: ReactNode = parts.flatMap((part, partIndex) => partIndex === 0\n ? [part]\n : [<br key={`break-${blockIndex}-${inlineIndex}-${partIndex}`} />, part]);\n for (const mark of [...(inline.marks ?? [])].reverse()) node = MARK_COMPONENTS[mark](node);\n return <span key={`${blockIndex}-${inlineIndex}`}>{node}</span>;\n });\n\nconst HEADING_TAGS = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n// Without an explicit level, fall back to the level implied by the variant.\nconst getHeadingTag = (block: TextBlock): (typeof HEADING_TAGS)[number] => {\n if (block.headingLevel) return HEADING_TAGS[block.headingLevel - 1];\n\n switch (block.variant) {\n case \"display1\": case \"headline1\": return \"h1\";\n case \"display2\": case \"headline2\": return \"h2\";\n case \"headline3\": return \"h3\";\n default: return \"h4\";\n }\n};\n\nconst TextRenderer = ({ document, className }: TextRendererProps) => {\n const blocks = document.content;\n const output: ReactNode[] = [];\n let index = 0;\n while (index < blocks.length) {\n const block = blocks[index];\n if (block.listType) {\n const listType = block.listType;\n const items: ReactNode[] = [];\n while (index < blocks.length && blocks[index].listType === listType) {\n const item = blocks[index];\n const itemChildren = renderInline(item, index);\n items.push(<li key={index} className={cn(\"puzzle-text-renderer__block\", `puzzle-typography-${item.variant}`)} style={{ textAlign: item.align }}>\n {itemChildren.length ? itemChildren : <br />}\n </li>);\n index++;\n }\n const List = listType === \"ordered\" ? \"ol\" : \"ul\";\n output.push(<List key={`list-${index}`} className=\"puzzle-text-renderer__list\">{items}</List>);\n continue;\n }\n const blockClass = cn(\"puzzle-text-renderer__block\", `puzzle-typography-${block.variant}`);\n const children = renderInline(block, index);\n const style = { textAlign: block.align };\n if (block.type === \"heading\") {\n const HeadingTag = getHeadingTag(block);\n output.push(<HeadingTag key={index} className={blockClass} style={style}>{children.length ? children : <br />}</HeadingTag>);\n } else {\n output.push(<p key={index} className={blockClass} style={style}>{children.length ? children : <br />}</p>);\n }\n index++;\n }\n return <div className={cn(\"puzzle-text-renderer\", className)}>{output}</div>;\n};\n\nexport { TextRenderer };\n"],"mappings":";;;;AAOA,MAAM,kBAAwE;CAC5E,OAAO,aAAa,oBAAC,UAAD,EAAS,SAAiB,CAAA;CAC9C,SAAS,aAAa,oBAAC,MAAD,EAAK,SAAa,CAAA;CACxC,YAAY,aAAa,oBAAC,KAAD,EAAI,SAAY,CAAA;CACzC,SAAS,aAAa,oBAAC,KAAD,EAAI,SAAY,CAAA;AACxC;AAEA,MAAM,gBAAgB,OAAkB,eACtC,MAAM,SAAS,KAAK,QAAQ,gBAAgB;CAE1C,IAAI,OADU,OAAO,KAAK,MAAM,IACN,CAAC,CAAC,SAAS,MAAM,cAAc,cAAc,IACnE,CAAC,IAAI,IACL,CAAC,oBAAC,MAAD,CAA6D,GAApD,SAAS,WAAW,GAAG,YAAY,GAAG,WAAc,GAAG,IAAI,CAAC;CAC1E,KAAK,MAAM,QAAQ,CAAC,GAAI,OAAO,SAAS,CAAC,CAAE,CAAC,CAAC,QAAQ,GAAG,OAAO,gBAAgB,KAAK,CAAC,IAAI;CACzF,OAAO,oBAAC,QAAD,EAAA,UAA4C,KAAW,GAA5C,GAAG,WAAW,GAAG,aAA2B;AAChE,CAAC;AAEH,MAAM,eAAe;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AAGxD,MAAM,iBAAiB,UAAoD;CACzE,IAAI,MAAM,cAAc,OAAO,aAAa,MAAM,eAAe;CAEjE,QAAQ,MAAM,SAAd;EACE,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK,aAAa,OAAO;EACzB,SAAS,OAAO;CAClB;AACF;AAEA,MAAM,gBAAgB,EAAE,UAAU,gBAAmC;CACnE,MAAM,SAAS,SAAS;CACxB,MAAM,SAAsB,CAAC;CAC7B,IAAI,QAAQ;CACZ,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,QAAQ,OAAO;EACrB,IAAI,MAAM,UAAU;GAClB,MAAM,WAAW,MAAM;GACvB,MAAM,QAAqB,CAAC;GAC5B,OAAO,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC,aAAa,UAAU;IACnE,MAAM,OAAO,OAAO;IACpB,MAAM,eAAe,aAAa,MAAM,KAAK;IAC7C,MAAM,KAAK,oBAAC,MAAD;KAAgB,WAAW,GAAG,+BAA+B,qBAAqB,KAAK,SAAS;KAAG,OAAO,EAAE,WAAW,KAAK,MAAM;KAC1I,UAAA,aAAa,SAAS,eAAe,oBAAC,MAAD,CAAK,CAAA;IACzC,GAFgB,KAEhB,CAAC;IACL;GACF;GACA,MAAM,OAAO,aAAa,YAAY,OAAO;GAC7C,OAAO,KAAK,oBAAC,MAAD;IAA4B,WAAU;IAA8B,UAAA;GAAY,GAArE,QAAQ,OAA6D,CAAC;GAC7F;EACF;EACA,MAAM,aAAa,GAAG,+BAA+B,qBAAqB,MAAM,SAAS;EACzF,MAAM,WAAW,aAAa,OAAO,KAAK;EAC1C,MAAM,QAAQ,EAAE,WAAW,MAAM,MAAM;EACvC,IAAI,MAAM,SAAS,WAAW;GAC5B,MAAM,aAAa,cAAc,KAAK;GACtC,OAAO,KAAK,oBAAC,YAAD;IAAwB,WAAW;IAAmB;IAAQ,UAAA,SAAS,SAAS,WAAW,oBAAC,MAAD,CAAK,CAAA;GAAc,GAA7F,KAA6F,CAAC;EAC7H,OACE,OAAO,KAAK,oBAAC,KAAD;GAAe,WAAW;GAAmB;GAAQ,UAAA,SAAS,SAAS,WAAW,oBAAC,MAAD,CAAK,CAAA;EAAK,GAApF,KAAoF,CAAC;EAE3G;CACF;CACA,OAAO,oBAAC,OAAD;EAAK,WAAW,GAAG,wBAAwB,SAAS;EAAI,UAAA;CAAY,CAAA;AAC7E"}
1
+ {"version":3,"file":"text-renderer.js","names":[],"sources":["../../../../src/components/editors/text-editor/text-renderer.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@puzzle-ui/core\";\nimport \"./text-editor.css\";\nimport type { TextBlock, TextDocument, TextMark } from \"./text-editor.types\";\n\nexport type TextRendererProps = { document: TextDocument; className?: string };\n\nconst MARK_COMPONENTS: Record<TextMark, (children: ReactNode) => ReactNode> = {\n bold: (children) => <strong>{children}</strong>,\n italic: (children) => <em>{children}</em>,\n underline: (children) => <u>{children}</u>,\n strike: (children) => <s>{children}</s>,\n};\n\nconst renderInline = (block: TextBlock, blockIndex: number): ReactNode[] =>\n block.children.map((inline, inlineIndex) => {\n const parts = inline.text.split(\"\\n\");\n let node: ReactNode = parts.flatMap((part, partIndex) => partIndex === 0\n ? [part]\n : [<br key={`break-${blockIndex}-${inlineIndex}-${partIndex}`} />, part]);\n for (const mark of [...(inline.marks ?? [])].reverse()) node = MARK_COMPONENTS[mark](node);\n return <span key={`${blockIndex}-${inlineIndex}`}>{node}</span>;\n });\n\nconst HEADING_TAGS = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n// Without an explicit level, fall back to the level implied by the variant.\nconst getHeadingTag = (block: TextBlock): (typeof HEADING_TAGS)[number] => {\n if (block.headingLevel) return HEADING_TAGS[block.headingLevel - 1]!;\n\n switch (block.variant) {\n case \"display1\": case \"headline1\": return \"h1\";\n case \"display2\": case \"headline2\": return \"h2\";\n case \"headline3\": return \"h3\";\n default: return \"h4\";\n }\n};\n\nconst TextRenderer = ({ document, className }: TextRendererProps) => {\n const blocks = document.content;\n const output: ReactNode[] = [];\n let index = 0;\n while (index < blocks.length) {\n const block = blocks[index]!;\n if (block.listType) {\n const listType = block.listType;\n const items: ReactNode[] = [];\n while (index < blocks.length && blocks[index]!.listType === listType) {\n const item = blocks[index]!;\n const itemChildren = renderInline(item, index);\n items.push(<li key={index} className={cn(\"puzzle-text-renderer__block\", `puzzle-typography-${item.variant}`)} style={{ textAlign: item.align }}>\n {itemChildren.length ? itemChildren : <br />}\n </li>);\n index++;\n }\n const List = listType === \"ordered\" ? \"ol\" : \"ul\";\n output.push(<List key={`list-${index}`} className=\"puzzle-text-renderer__list\">{items}</List>);\n continue;\n }\n const blockClass = cn(\"puzzle-text-renderer__block\", `puzzle-typography-${block.variant}`);\n const children = renderInline(block, index);\n const style = { textAlign: block.align };\n if (block.type === \"heading\") {\n const HeadingTag = getHeadingTag(block);\n output.push(<HeadingTag key={index} className={blockClass} style={style}>{children.length ? children : <br />}</HeadingTag>);\n } else {\n output.push(<p key={index} className={blockClass} style={style}>{children.length ? children : <br />}</p>);\n }\n index++;\n }\n return <div className={cn(\"puzzle-text-renderer\", className)}>{output}</div>;\n};\n\nexport { TextRenderer };\n"],"mappings":";;;;AAOA,MAAM,kBAAwE;CAC5E,OAAO,aAAa,oBAAC,UAAD,EAAS,SAAiB,CAAA;CAC9C,SAAS,aAAa,oBAAC,MAAD,EAAK,SAAa,CAAA;CACxC,YAAY,aAAa,oBAAC,KAAD,EAAI,SAAY,CAAA;CACzC,SAAS,aAAa,oBAAC,KAAD,EAAI,SAAY,CAAA;AACxC;AAEA,MAAM,gBAAgB,OAAkB,eACtC,MAAM,SAAS,KAAK,QAAQ,gBAAgB;CAE1C,IAAI,OADU,OAAO,KAAK,MAAM,IACN,CAAC,CAAC,SAAS,MAAM,cAAc,cAAc,IACnE,CAAC,IAAI,IACL,CAAC,oBAAC,MAAD,CAA6D,GAApD,SAAS,WAAW,GAAG,YAAY,GAAG,WAAc,GAAG,IAAI,CAAC;CAC1E,KAAK,MAAM,QAAQ,CAAC,GAAI,OAAO,SAAS,CAAC,CAAE,CAAC,CAAC,QAAQ,GAAG,OAAO,gBAAgB,KAAK,CAAC,IAAI;CACzF,OAAO,oBAAC,QAAD,EAAA,UAA4C,KAAW,GAA5C,GAAG,WAAW,GAAG,aAA2B;AAChE,CAAC;AAEH,MAAM,eAAe;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AAGxD,MAAM,iBAAiB,UAAoD;CACzE,IAAI,MAAM,cAAc,OAAO,aAAa,MAAM,eAAe;CAEjE,QAAQ,MAAM,SAAd;EACE,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK;EAAY,KAAK,aAAa,OAAO;EAC1C,KAAK,aAAa,OAAO;EACzB,SAAS,OAAO;CAClB;AACF;AAEA,MAAM,gBAAgB,EAAE,UAAU,gBAAmC;CACnE,MAAM,SAAS,SAAS;CACxB,MAAM,SAAsB,CAAC;CAC7B,IAAI,QAAQ;CACZ,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,QAAQ,OAAO;EACrB,IAAI,MAAM,UAAU;GAClB,MAAM,WAAW,MAAM;GACvB,MAAM,QAAqB,CAAC;GAC5B,OAAO,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAE,aAAa,UAAU;IACpE,MAAM,OAAO,OAAO;IACpB,MAAM,eAAe,aAAa,MAAM,KAAK;IAC7C,MAAM,KAAK,oBAAC,MAAD;KAAgB,WAAW,GAAG,+BAA+B,qBAAqB,KAAK,SAAS;KAAG,OAAO,EAAE,WAAW,KAAK,MAAM;KAC1I,UAAA,aAAa,SAAS,eAAe,oBAAC,MAAD,CAAK,CAAA;IACzC,GAFgB,KAEhB,CAAC;IACL;GACF;GACA,MAAM,OAAO,aAAa,YAAY,OAAO;GAC7C,OAAO,KAAK,oBAAC,MAAD;IAA4B,WAAU;IAA8B,UAAA;GAAY,GAArE,QAAQ,OAA6D,CAAC;GAC7F;EACF;EACA,MAAM,aAAa,GAAG,+BAA+B,qBAAqB,MAAM,SAAS;EACzF,MAAM,WAAW,aAAa,OAAO,KAAK;EAC1C,MAAM,QAAQ,EAAE,WAAW,MAAM,MAAM;EACvC,IAAI,MAAM,SAAS,WAAW;GAC5B,MAAM,aAAa,cAAc,KAAK;GACtC,OAAO,KAAK,oBAAC,YAAD;IAAwB,WAAW;IAAmB;IAAQ,UAAA,SAAS,SAAS,WAAW,oBAAC,MAAD,CAAK,CAAA;GAAc,GAA7F,KAA6F,CAAC;EAC7H,OACE,OAAO,KAAK,oBAAC,KAAD;GAAe,WAAW;GAAmB;GAAQ,UAAA,SAAS,SAAS,WAAW,oBAAC,MAAD,CAAK,CAAA;EAAK,GAApF,KAAoF,CAAC;EAE3G;CACF;CACA,OAAO,oBAAC,OAAD;EAAK,WAAW,GAAG,wBAAwB,SAAS;EAAI,UAAA;CAAY,CAAA;AAC7E"}