@jimka/typescript-ui 0.6.0 → 0.7.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 (198) hide show
  1. package/dist/lib/{AbstractInput-sRNPzWel.js → AbstractInput-W2PLJV5_.js} +2 -2
  2. package/dist/lib/{AbstractInput-sRNPzWel.js.map → AbstractInput-W2PLJV5_.js.map} +1 -1
  3. package/dist/lib/{Anchor-CwRhdP2J.js → Anchor-iZhsvMlT.js} +2 -2
  4. package/dist/lib/{Anchor-CwRhdP2J.js.map → Anchor-iZhsvMlT.js.map} +1 -1
  5. package/dist/lib/AnimatedDropdown-D0RhVgv3.js +2 -0
  6. package/dist/lib/AnimatedDropdown-D0RhVgv3.js.map +1 -0
  7. package/dist/lib/Border-6GsTNV-2.js +2 -0
  8. package/dist/lib/Border-6GsTNV-2.js.map +1 -0
  9. package/dist/lib/Button-CnFAiQuV.js +2 -0
  10. package/dist/lib/Button-CnFAiQuV.js.map +1 -0
  11. package/dist/lib/{Card-QYo27r5b.js → Card-C20k2YHU.js} +2 -2
  12. package/dist/lib/{Card-QYo27r5b.js.map → Card-C20k2YHU.js.map} +1 -1
  13. package/dist/lib/Checkbox-CM1z8keM.js +2 -0
  14. package/dist/lib/Checkbox-CM1z8keM.js.map +1 -0
  15. package/dist/lib/{CheckboxMenuRow-CesLhSnp.js → CheckboxMenuRow-C1h4LPHR.js} +2 -2
  16. package/dist/lib/{CheckboxMenuRow-CesLhSnp.js.map → CheckboxMenuRow-C1h4LPHR.js.map} +1 -1
  17. package/dist/lib/{CodeEditor-N1FQodcF.js → CodeEditor-CTL2WjF8.js} +2 -2
  18. package/dist/lib/{CodeEditor-N1FQodcF.js.map → CodeEditor-CTL2WjF8.js.map} +1 -1
  19. package/dist/lib/ComboBox-PZ5YPL0X.js +2 -0
  20. package/dist/lib/ComboBox-PZ5YPL0X.js.map +1 -0
  21. package/dist/lib/Component-D99-15Zt.js +2 -0
  22. package/dist/lib/Component-D99-15Zt.js.map +1 -0
  23. package/dist/lib/Container-FWRjgF6T.js +2 -0
  24. package/dist/lib/{Container-abY5Ww3W.js.map → Container-FWRjgF6T.js.map} +1 -1
  25. package/dist/lib/DOM-Cjh7_7TF.js +2 -0
  26. package/dist/lib/DOM-Cjh7_7TF.js.map +1 -0
  27. package/dist/lib/Dialog-DN78RaVH.js +2 -0
  28. package/dist/lib/Dialog-DN78RaVH.js.map +1 -0
  29. package/dist/lib/{FieldDecorator-BlYMfZ9L.js → FieldDecorator-DyPX8CFh.js} +2 -2
  30. package/dist/lib/{FieldDecorator-BlYMfZ9L.js.map → FieldDecorator-DyPX8CFh.js.map} +1 -1
  31. package/dist/lib/{Fit-BhlB-HAo.js → Fit-CaPMj7Up.js} +2 -2
  32. package/dist/lib/{Fit-BhlB-HAo.js.map → Fit-CaPMj7Up.js.map} +1 -1
  33. package/dist/lib/{FloatingPanel-DkJXsZ2D.js → FloatingPanel-Dsy_vJxi.js} +2 -2
  34. package/dist/lib/{FloatingPanel-DkJXsZ2D.js.map → FloatingPanel-Dsy_vJxi.js.map} +1 -1
  35. package/dist/lib/Glyph-DiEpD3gy.js +2 -0
  36. package/dist/lib/Glyph-DiEpD3gy.js.map +1 -0
  37. package/dist/lib/{Grid-Daiz7F5d.js → Grid-ft_UUpj8.js} +2 -2
  38. package/dist/lib/{Grid-Daiz7F5d.js.map → Grid-ft_UUpj8.js.map} +1 -1
  39. package/dist/lib/{GridConstraints-C58LuTqM.js → GridConstraints-CvZ0Nzaw.js} +2 -2
  40. package/dist/lib/{GridConstraints-C58LuTqM.js.map → GridConstraints-CvZ0Nzaw.js.map} +1 -1
  41. package/dist/lib/{Header-Dql3Zxc6.js → Header-Osm3IjDw.js} +2 -2
  42. package/dist/lib/{Header-Dql3Zxc6.js.map → Header-Osm3IjDw.js.map} +1 -1
  43. package/dist/lib/{IconText-PUjhTLlp.js → IconText-BBaYJ2Fx.js} +2 -2
  44. package/dist/lib/{IconText-PUjhTLlp.js.map → IconText-BBaYJ2Fx.js.map} +1 -1
  45. package/dist/lib/{LayoutSerialization-CMxunLfV.js → LayoutSerialization-BC64__O7.js} +2 -2
  46. package/dist/lib/{LayoutSerialization-CMxunLfV.js.map → LayoutSerialization-BC64__O7.js.map} +1 -1
  47. package/dist/lib/List-CQbZK_pT.js +2 -0
  48. package/dist/lib/List-CQbZK_pT.js.map +1 -0
  49. package/dist/lib/{Menu-Q0bpXXze.js → Menu-CwHluLGT.js} +2 -2
  50. package/dist/lib/{Menu-Q0bpXXze.js.map → Menu-CwHluLGT.js.map} +1 -1
  51. package/dist/lib/{MenuButton-DCXi0tsP.js → MenuButton-BqCT-1yv.js} +2 -2
  52. package/dist/lib/{MenuButton-DCXi0tsP.js.map → MenuButton-BqCT-1yv.js.map} +1 -1
  53. package/dist/lib/{OverlayFade-3pof2AJH.js → OverlayFade-CuEXgimf.js} +2 -2
  54. package/dist/lib/{OverlayFade-3pof2AJH.js.map → OverlayFade-CuEXgimf.js.map} +1 -1
  55. package/dist/lib/{Panel-Cy1pdlWO.js → Panel-CtViePqY.js} +2 -2
  56. package/dist/lib/{Panel-Cy1pdlWO.js.map → Panel-CtViePqY.js.map} +1 -1
  57. package/dist/lib/{Position-BwtxouxK.js → Position-BIEU1Eze.js} +2 -2
  58. package/dist/lib/Position-BIEU1Eze.js.map +1 -0
  59. package/dist/lib/{ProgressSpinner-DK8xEQ9y.js → ProgressSpinner-BuBvC1FY.js} +2 -2
  60. package/dist/lib/{ProgressSpinner-DK8xEQ9y.js.map → ProgressSpinner-BuBvC1FY.js.map} +1 -1
  61. package/dist/lib/RadioButton-Df5J-H6m.js +2 -0
  62. package/dist/lib/RadioButton-Df5J-H6m.js.map +1 -0
  63. package/dist/lib/RadioMenuRow-Cfv0x6Qj.js +2 -0
  64. package/dist/lib/RadioMenuRow-Cfv0x6Qj.js.map +1 -0
  65. package/dist/lib/{RovingTabIndex-pHEv5oLD.js → RovingTabIndex--w28AnXc.js} +2 -2
  66. package/dist/lib/{RovingTabIndex-pHEv5oLD.js.map → RovingTabIndex--w28AnXc.js.map} +1 -1
  67. package/dist/lib/Scrollbar-DcnueNJd.js +2 -0
  68. package/dist/lib/Scrollbar-DcnueNJd.js.map +1 -0
  69. package/dist/lib/SelectableText-DZNxjKXR.js +2 -0
  70. package/dist/lib/SelectableText-DZNxjKXR.js.map +1 -0
  71. package/dist/lib/{Slider-CTbwR6HR.js → Slider-DNfd8Lxz.js} +2 -2
  72. package/dist/lib/{Slider-CTbwR6HR.js.map → Slider-DNfd8Lxz.js.map} +1 -1
  73. package/dist/lib/{Spacer-srdN2d2b.js → Spacer-BznfGbwG.js} +2 -2
  74. package/dist/lib/{Spacer-srdN2d2b.js.map → Spacer-BznfGbwG.js.map} +1 -1
  75. package/dist/lib/TabButton-CAACsMov.js +2 -0
  76. package/dist/lib/TabButton-CAACsMov.js.map +1 -0
  77. package/dist/lib/{TabCloseButton-CY7BGitY.js → TabCloseButton-CCiqsX3y.js} +2 -2
  78. package/dist/lib/{TabCloseButton-CY7BGitY.js.map → TabCloseButton-CCiqsX3y.js.map} +1 -1
  79. package/dist/lib/Text-O-FWa5Cy.js +2 -0
  80. package/dist/lib/Text-O-FWa5Cy.js.map +1 -0
  81. package/dist/lib/{Tooltip-CUOJOqQi.js → Tooltip-CyICVu15.js} +2 -2
  82. package/dist/lib/Tooltip-CyICVu15.js.map +1 -0
  83. package/dist/lib/{Tree-C64JNPWJ.js → Tree-CSEguhlk.js} +2 -2
  84. package/dist/lib/{Tree-C64JNPWJ.js.map → Tree-CSEguhlk.js.map} +1 -1
  85. package/dist/lib/{VBox-DfJPWXqc.js → VBox-DVe4UXwX.js} +2 -2
  86. package/dist/lib/{VBox-DfJPWXqc.js.map → VBox-DVe4UXwX.js.map} +1 -1
  87. package/dist/lib/{VirtualScroller-DTIn19o1.js → VirtualScroller-ggPTCQb0.js} +2 -2
  88. package/dist/lib/{VirtualScroller-DTIn19o1.js.map → VirtualScroller-ggPTCQb0.js.map} +1 -1
  89. package/dist/lib/component/button.es.js +1 -1
  90. package/dist/lib/component/chart.es.js +1 -1
  91. package/dist/lib/component/chart.es.js.map +1 -1
  92. package/dist/lib/component/container.es.js +1 -1
  93. package/dist/lib/component/diagram.es.js +1 -1
  94. package/dist/lib/component/display.es.js +1 -1
  95. package/dist/lib/component/display.es.js.map +1 -1
  96. package/dist/lib/component/editor.es.js +2 -2
  97. package/dist/lib/component/editor.es.js.map +1 -1
  98. package/dist/lib/component/input.es.js +1 -1
  99. package/dist/lib/component/input.es.js.map +1 -1
  100. package/dist/lib/component/list.es.js +1 -1
  101. package/dist/lib/component/menubar.es.js +1 -1
  102. package/dist/lib/component/table.es.js +6 -6
  103. package/dist/lib/component/table.es.js.map +1 -1
  104. package/dist/lib/component/tree.es.js +1 -1
  105. package/dist/lib/core.es.js +1 -1
  106. package/dist/lib/core.es.js.map +1 -1
  107. package/dist/lib/glyphs/solid/index.es.js +1 -1
  108. package/dist/lib/layout.es.js +1 -1
  109. package/dist/lib/overlay.es.js +1 -1
  110. package/dist/lib/overlay.es.js.map +1 -1
  111. package/dist/lib/primitive.es.js +1 -1
  112. package/dist/lib/router.es.js +1 -1
  113. package/dist/lib/{selectionsEqual-gMG7gCVI.js → selectionsEqual-C7MrKMQr.js} +2 -2
  114. package/dist/lib/{selectionsEqual-gMG7gCVI.js.map → selectionsEqual-C7MrKMQr.js.map} +1 -1
  115. package/dist/lib/types/component/button/Button.d.ts +9 -0
  116. package/dist/lib/types/component/button/TabButton.d.ts +5 -0
  117. package/dist/lib/types/component/button/ToggleButton.d.ts +3 -0
  118. package/dist/lib/types/component/chart/AbstractChart.d.ts +2 -0
  119. package/dist/lib/types/component/display/Markdown.d.ts +2 -1
  120. package/dist/lib/types/component/input/AbstractCalendarDropdown.d.ts +2 -0
  121. package/dist/lib/types/component/input/AbstractPickerField.d.ts +2 -0
  122. package/dist/lib/types/component/input/AutoCompleteDropdown.d.ts +2 -0
  123. package/dist/lib/types/component/input/ComboBox.d.ts +3 -1
  124. package/dist/lib/types/component/input/Link.d.ts +2 -0
  125. package/dist/lib/types/component/input/PasswordField.d.ts +2 -0
  126. package/dist/lib/types/component/input/PickerInput.d.ts +2 -0
  127. package/dist/lib/types/component/input/SelectableText.d.ts +11 -0
  128. package/dist/lib/types/component/input/SpinButton.d.ts +2 -0
  129. package/dist/lib/types/component/input/Text.d.ts +16 -2
  130. package/dist/lib/types/component/input/TextArea.d.ts +2 -0
  131. package/dist/lib/types/component/input/TextField.d.ts +2 -0
  132. package/dist/lib/types/component/input/TextInput.d.ts +2 -0
  133. package/dist/lib/types/component/input/TimePickerDropdown.d.ts +3 -1
  134. package/dist/lib/types/component/input/UsernameField.d.ts +2 -0
  135. package/dist/lib/types/component/input/index.d.ts +2 -0
  136. package/dist/lib/types/component/list/AbstractSelectableList.d.ts +4 -2
  137. package/dist/lib/types/component/table/Body.d.ts +40 -14
  138. package/dist/lib/types/component/table/Header.d.ts +1 -1
  139. package/dist/lib/types/component/table/Row.d.ts +20 -2
  140. package/dist/lib/types/component/table/Table.d.ts +2 -0
  141. package/dist/lib/types/component/table/TableExporter.d.ts +3 -0
  142. package/dist/lib/types/component/table/TablePanel.d.ts +1 -0
  143. package/dist/lib/types/component/table/TreeTablePanel.d.ts +1 -0
  144. package/dist/lib/types/component/table/cell/Cell.d.ts +7 -2
  145. package/dist/lib/types/component/table/cell/Default.d.ts +1 -1
  146. package/dist/lib/types/component/table/cell/Header.d.ts +3 -0
  147. package/dist/lib/types/component/table/cell/ResizeHandle.d.ts +1 -1
  148. package/dist/lib/types/component/table/cell/renderer/CellRenderer.d.ts +2 -2
  149. package/dist/lib/types/component/table/cell/renderer/String.d.ts +3 -1
  150. package/dist/lib/types/component/table/index.d.ts +1 -1
  151. package/dist/lib/types/core/AnimatedDropdown.d.ts +2 -0
  152. package/dist/lib/types/core/ClassStyleRules.d.ts +46 -3
  153. package/dist/lib/types/core/Component.d.ts +30 -2
  154. package/dist/lib/types/core/DOM.d.ts +5 -1
  155. package/dist/lib/types/core/LayerManager.d.ts +1 -0
  156. package/dist/lib/types/core/StyleTarget.d.ts +2 -0
  157. package/dist/lib/types/core/Theme.d.ts +1 -0
  158. package/dist/lib/types/core/Util.d.ts +4 -0
  159. package/dist/lib/types/core/index.d.ts +1 -1
  160. package/dist/lib/types/layout/Table.d.ts +2 -0
  161. package/dist/lib/types/overlay/AbstractWindow.d.ts +3 -0
  162. package/dist/lib/types/overlay/PopupPanel.d.ts +2 -0
  163. package/dist/lib/validation.es.js +1 -1
  164. package/package.json +1 -1
  165. package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js +0 -2
  166. package/dist/lib/AnimatedDropdown-H3Rh-YMZ.js.map +0 -1
  167. package/dist/lib/Border-0DJFDiij.js +0 -2
  168. package/dist/lib/Border-0DJFDiij.js.map +0 -1
  169. package/dist/lib/Button-MpMIYBYJ.js +0 -2
  170. package/dist/lib/Button-MpMIYBYJ.js.map +0 -1
  171. package/dist/lib/Checkbox-D0JYK-wc.js +0 -2
  172. package/dist/lib/Checkbox-D0JYK-wc.js.map +0 -1
  173. package/dist/lib/ComboBox-Dx8Tk9yg.js +0 -2
  174. package/dist/lib/ComboBox-Dx8Tk9yg.js.map +0 -1
  175. package/dist/lib/Component-B0ShfPVi.js +0 -2
  176. package/dist/lib/Component-B0ShfPVi.js.map +0 -1
  177. package/dist/lib/Container-abY5Ww3W.js +0 -2
  178. package/dist/lib/DOM-C-Edb3tA.js +0 -2
  179. package/dist/lib/DOM-C-Edb3tA.js.map +0 -1
  180. package/dist/lib/Dialog-cbA9V17e.js +0 -2
  181. package/dist/lib/Dialog-cbA9V17e.js.map +0 -1
  182. package/dist/lib/Glyph-_VeBacS2.js +0 -2
  183. package/dist/lib/Glyph-_VeBacS2.js.map +0 -1
  184. package/dist/lib/List-nx8220Fi.js +0 -2
  185. package/dist/lib/List-nx8220Fi.js.map +0 -1
  186. package/dist/lib/Position-BwtxouxK.js.map +0 -1
  187. package/dist/lib/RadioButton-BEwpqg1k.js +0 -2
  188. package/dist/lib/RadioButton-BEwpqg1k.js.map +0 -1
  189. package/dist/lib/RadioMenuRow-DP5qPyFR.js +0 -2
  190. package/dist/lib/RadioMenuRow-DP5qPyFR.js.map +0 -1
  191. package/dist/lib/Scrollbar-t8fFrUVQ.js +0 -2
  192. package/dist/lib/Scrollbar-t8fFrUVQ.js.map +0 -1
  193. package/dist/lib/TabButton-DLoMQzky.js +0 -2
  194. package/dist/lib/TabButton-DLoMQzky.js.map +0 -1
  195. package/dist/lib/Text-CMKnK9uB.js +0 -2
  196. package/dist/lib/Text-CMKnK9uB.js.map +0 -1
  197. package/dist/lib/Tooltip-CUOJOqQi.js.map +0 -1
  198. package/dist/lib/types/component/table/CellGeometry.d.ts +0 -6
@@ -1,4 +1,4 @@
1
- import{t as e}from"../DOM-C-Edb3tA.js";import{n as t}from"../Position-BwtxouxK.js";import{b as n,h as r,t as i}from"../Component-B0ShfPVi.js";import{t as a}from"../ListenerBag-WPdKuuds.js";import{t as o}from"../Card-QYo27r5b.js";import{n as s,r as c,t as l}from"../LanguageRegistry-CcVnbKuh.js";import"../languages-Dl8qdL8a.js";import{t as u}from"../CodeEditor-N1FQodcF.js";import{$createParagraphNode as ee,$getRoot as te,$getSelection as d,$isRangeSelection as f,FORMAT_TEXT_COMMAND as p,createEditor as ne}from"lexical";import{$convertFromMarkdownString as m,$convertToMarkdownString as h,BOLD_STAR as re,CODE as ie,HEADING as ae,INLINE_CODE as oe,ITALIC_STAR as g,LINK as se,ORDERED_LIST as ce,QUOTE as le,UNORDERED_LIST as ue,registerMarkdownShortcuts as de}from"@lexical/markdown";import{$createHeadingNode as fe,$createQuoteNode as pe,HeadingNode as me,QuoteNode as he,registerRichText as ge}from"@lexical/rich-text";import{INSERT_ORDERED_LIST_COMMAND as _e,INSERT_UNORDERED_LIST_COMMAND as ve,ListItemNode as ye,ListNode as _,registerList as v}from"@lexical/list";import{$toggleLink as y,LinkNode as b}from"@lexical/link";import{$createCodeNode as x,CodeHighlightNode as S,CodeNode as C}from"@lexical/code";import{createEmptyHistoryState as w,registerHistory as T}from"@lexical/history";import{$createTableCellNode as E,$createTableNode as D,$createTableRowNode as O,$deleteTableColumnAtSelection as k,$deleteTableRowAtSelection as A,$getTableCellNodeFromLexicalNode as j,$insertTableColumnAtSelection as M,$insertTableRowAtSelection as N,$isTableCellNode as P,$isTableNode as be,$isTableRowNode as xe,INSERT_TABLE_COMMAND as F,TableCellHeaderStates as I,TableCellNode as L,TableNode as R,TableRowNode as z,registerTableCellUnmergeTransform as Se,registerTablePlugin as Ce,registerTableSelectionObserver as we}from"@lexical/table";import{mergeRegister as Te}from"@lexical/utils";import{$setBlocksType as Ee}from"@lexical/selection";var De=/\|/;function splitTableRow(e){let t=e.trim();t.startsWith(`|`)&&(t=t.slice(1)),t.endsWith(`|`)&&!t.endsWith(`\\|`)&&(t=t.slice(0,-1));let n=[],r=``;for(let e=0;e<t.length;e+=1){let i=t[e];i===`\\`&&t[e+1]===`|`?(r+=`|`,e+=1):i===`|`?(n.push(r.trim()),r=``):r+=i}return n.push(r.trim()),n}function parseDelimiterRow(e){let t=splitTableRow(e),n=[];for(let e of t){if(!/^:?-+:?$/.test(e))return null;let t=e.startsWith(`:`),r=e.endsWith(`:`);t&&r?n.push(`center`):t?n.push(`left`):r?n.push(`right`):n.push(``)}return n}function formatDelimiterRow(e){return`| `+e.map(e=>{switch(e){case`left`:return`:---`;case`center`:return`:---:`;case`right`:return`---:`;default:return`---`}}).join(` | `)+` |`}function escapeCellText(e){return e.trim().replace(/\|/g,`\\|`).replace(/\n/g,`\\n`)}function createTableTransformer(e){return{dependencies:[R,z,L],regExpStart:De,type:`multiline-element`,handleImportAfterStartMatch:({lines:t,rootNode:n,startLineIndex:r})=>{let i=t[r+1];if(i===void 0)return null;let a=parseDelimiterRow(i);if(a===null)return null;let o=splitTableRow(t[r]),s=o.length;if(a.length!==s)return null;let c=[o],l=r+1;for(let e=r+2;e<t.length;e+=1){let n=t[e];if(n.trim()===``)break;c.push(splitTableRow(n)),l=e}let u=D();return c.forEach((t,n)=>{let r=O();for(let i=0;i<s;i+=1){let o=E(n===0?I.ROW:I.NO_STATUS);m((t[i]??``).replace(/\\n/g,`
1
+ import{t as e}from"../DOM-Cjh7_7TF.js";import{n as t}from"../Position-BIEU1Eze.js";import{b as n,h as r,t as i}from"../Component-D99-15Zt.js";import{t as a}from"../ListenerBag-WPdKuuds.js";import{t as o}from"../Card-C20k2YHU.js";import{n as s,r as c,t as l}from"../LanguageRegistry-CcVnbKuh.js";import"../languages-Dl8qdL8a.js";import{t as u}from"../CodeEditor-CTL2WjF8.js";import{$createParagraphNode as ee,$getRoot as te,$getSelection as d,$isRangeSelection as f,FORMAT_TEXT_COMMAND as p,createEditor as ne}from"lexical";import{$convertFromMarkdownString as m,$convertToMarkdownString as h,BOLD_STAR as re,CODE as ie,HEADING as ae,INLINE_CODE as oe,ITALIC_STAR as g,LINK as se,ORDERED_LIST as ce,QUOTE as le,UNORDERED_LIST as ue,registerMarkdownShortcuts as de}from"@lexical/markdown";import{$createHeadingNode as fe,$createQuoteNode as pe,HeadingNode as me,QuoteNode as he,registerRichText as ge}from"@lexical/rich-text";import{INSERT_ORDERED_LIST_COMMAND as _e,INSERT_UNORDERED_LIST_COMMAND as ve,ListItemNode as ye,ListNode as _,registerList as v}from"@lexical/list";import{$toggleLink as y,LinkNode as b}from"@lexical/link";import{$createCodeNode as x,CodeHighlightNode as S,CodeNode as C}from"@lexical/code";import{createEmptyHistoryState as w,registerHistory as T}from"@lexical/history";import{$createTableCellNode as E,$createTableNode as D,$createTableRowNode as O,$deleteTableColumnAtSelection as k,$deleteTableRowAtSelection as A,$getTableCellNodeFromLexicalNode as j,$insertTableColumnAtSelection as M,$insertTableRowAtSelection as N,$isTableCellNode as P,$isTableNode as be,$isTableRowNode as xe,INSERT_TABLE_COMMAND as F,TableCellHeaderStates as I,TableCellNode as L,TableNode as R,TableRowNode as z,registerTableCellUnmergeTransform as Se,registerTablePlugin as Ce,registerTableSelectionObserver as we}from"@lexical/table";import{mergeRegister as Te}from"@lexical/utils";import{$setBlocksType as Ee}from"@lexical/selection";var De=/\|/;function splitTableRow(e){let t=e.trim();t.startsWith(`|`)&&(t=t.slice(1)),t.endsWith(`|`)&&!t.endsWith(`\\|`)&&(t=t.slice(0,-1));let n=[],r=``;for(let e=0;e<t.length;e+=1){let i=t[e];i===`\\`&&t[e+1]===`|`?(r+=`|`,e+=1):i===`|`?(n.push(r.trim()),r=``):r+=i}return n.push(r.trim()),n}function parseDelimiterRow(e){let t=splitTableRow(e),n=[];for(let e of t){if(!/^:?-+:?$/.test(e))return null;let t=e.startsWith(`:`),r=e.endsWith(`:`);t&&r?n.push(`center`):t?n.push(`left`):r?n.push(`right`):n.push(``)}return n}function formatDelimiterRow(e){return`| `+e.map(e=>{switch(e){case`left`:return`:---`;case`center`:return`:---:`;case`right`:return`---:`;default:return`---`}}).join(` | `)+` |`}function escapeCellText(e){return e.trim().replace(/\|/g,`\\|`).replace(/\n/g,`\\n`)}function createTableTransformer(e){return{dependencies:[R,z,L],regExpStart:De,type:`multiline-element`,handleImportAfterStartMatch:({lines:t,rootNode:n,startLineIndex:r})=>{let i=t[r+1];if(i===void 0)return null;let a=parseDelimiterRow(i);if(a===null)return null;let o=splitTableRow(t[r]),s=o.length;if(a.length!==s)return null;let c=[o],l=r+1;for(let e=r+2;e<t.length;e+=1){let n=t[e];if(n.trim()===``)break;c.push(splitTableRow(n)),l=e}let u=D();return c.forEach((t,n)=>{let r=O();for(let i=0;i<s;i+=1){let o=E(n===0?I.ROW:I.NO_STATUS);m((t[i]??``).replace(/\\n/g,`
2
2
  `),e(),o),o.setFormat(a[i]??``),r.append(o)}u.append(r)}),n.append(u),[!0,l]},replace:()=>!1,export:t=>{if(!be(t))return null;let n=t.getChildren().filter(xe);if(n.length===0)return null;let renderRow=t=>`| `+t.getChildren().filter(P).map(t=>escapeCellText(h(e(),t))).join(` | `)+` |`,r=n[0],i=r.getChildren().filter(P).map(e=>e.getFormatType()),a=[renderRow(r),formatDelimiterRow(i)];for(let e=1;e<n.length;e+=1)a.push(renderRow(n[e]));return a.join(`
3
- `)}}}var B=[createTableTransformer(()=>B),ae,le,ie,ue,ce,re,g,oe,se],Oe=[me,he,_,ye,b,C,S,R,z,L],V=`ts-ui-mde-heading`,H=`ts-ui-mde-quote`,U=`ts-ui-mde-code`,W=`ts-ui-mde-inline-code`,G=`ts-ui-mde-list`,K=`ts-ui-mde-link`,q=`ts-ui-mde-bold`,J=`ts-ui-mde-italic`,Y=`ts-ui-mde-table`,ke=`ts-ui-mde-table-row`,X=`ts-ui-mde-table-cell`,Z=`ts-ui-mde-table-cell-header`,Q=`ts-ui-mde-table-cell-selected`,$=!1;function ensureMarkdownEditorClassRules(){$||($=!0,new n({scope:`class`,name:V,styles:{fontWeight:`600`}}),new n({scope:`class`,name:H,styles:{borderLeft:`3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,paddingLeft:`1em`,marginLeft:`0`}}),new n({scope:`class`,name:U,styles:{fontFamily:`var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)`,background:`rgba(127, 127, 127, 0.16)`,borderRadius:`var(--ts-ui-border-radius, 3px)`,padding:`0.6em 0.8em`,whiteSpace:`pre-wrap`,display:`block`,margin:`1em 0`}}),new n({scope:`class`,name:W,styles:{fontFamily:`var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)`,background:`rgba(127, 127, 127, 0.16)`,borderRadius:`var(--ts-ui-border-radius, 3px)`,padding:`0.1em 0.3em`}}),new n({scope:`class`,name:G,styles:{paddingLeft:`1.5em`}}),new n({scope:`class`,name:K,styles:{color:`var(--ts-ui-indicator-focus, #2563eb)`}}),new n({scope:`class`,name:q,styles:{fontWeight:`700`}}),new n({scope:`class`,name:J,styles:{fontStyle:`italic`}}),new n({scope:`class`,name:Y,styles:{borderCollapse:`collapse`}}),new n({scope:`class`,name:X,styles:{border:`1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,padding:`0.3em 0.6em`}}),new n({scope:`class`,name:Z,styles:{border:`1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,padding:`0.3em 0.6em`,fontWeight:`600`}}),new n({scope:`selector`,name:`.${X} > p`,styles:{margin:`0`}}),new n({scope:`class`,name:Q,styles:{backgroundColor:`var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))`}}))}var Ae={heading:{h1:V,h2:V,h3:V,h4:V,h5:V,h6:V},quote:H,code:U,list:{ul:G,ol:G},text:{bold:q,italic:J,code:W},link:K,table:Y,tableRow:ke,tableCell:X,tableCellHeader:Z,tableCellSelected:Q},je=300;function reportEditorError(e){throw e}function createBlockNode(e){switch(e){case`quote`:return pe();case`code`:return x();case`paragraph`:return ee();default:return fe(e)}}function $selectionIsInTableCell(){let e=d();return f(e)?j(e.anchor.getNode())!==null:!1}var WysiwygSurface=class extends i{_contentEditable=!1;_onReady;constructor(e){super(),this._onReady=e,this.setContentEditable(!0),this.setOverflow(`auto`),this.setPadding(new t(4,6,4,6)),this.setCursor(`text`),this.setUserSelect(`text`),this.setElementCSSRule(`lineHeight`,`var(--ts-ui-md-line-height, 1.8)`),this.onFirstLayout(()=>this._onReady())}init(t){super.init(t);let n=t??this.getElement();return n&&e.sink.apply(n,{setAttr:{contenteditable:this._contentEditable?`true`:`false`}}),this}setContentEditable(e){return this._contentEditable=e,this.setElementAttribute(`contenteditable`,e?`true`:`false`),this}getContentEditable(){return this._contentEditable}mount(t){if(t.getRootElement())return;let n=this.getElement();n&&(ensureMarkdownEditorClassRules(),e.sink.mountView(n,e=>(t.setRootElement(e),e)))}},MarkdownEditor=class extends i{_editor=null;_unregister=null;_unregisterTableView=null;_listeners=new a;_card;_wysiwyg;_codeEditor;constructor(e,t){super(t),e!==void 0&&this._options.value===void 0&&(this._options.value=e),this._card=new o,this.setLayoutManager(this._card),this._wysiwyg=new WysiwygSurface(()=>this.mountWysiwyg()),this._codeEditor=new u(this._options.value??``,{language:`markdown`,readOnly:this._options.readOnly??!1,listeners:{change:e=>this.handleCodeChange(e)}}),this.addComponent(this._wysiwyg),this.addComponent(this._codeEditor),this._card.setVisibleComponentId(this.getMode()===`source`?this._codeEditor.getId():this._wysiwyg.getId()),this.applyListeners(t?.listeners)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.readOnly!==void 0&&(this._options.readOnly=e.readOnly),e.mode!==void 0&&(this._options.mode=e.mode),this}getMode(){return this._options.mode??`wysiwyg`}setMode(e){if(this.getMode()===e)return this;let t=this.getValue();return this._options.mode=e,e===`source`?(this._codeEditor.setValue(t),this._card.setVisibleComponentId(this._codeEditor.getId())):(this.ensureEditor().update(()=>m(t,B),{discrete:!0}),this._card.setVisibleComponentId(this._wysiwyg.getId())),this}getValue(){if(this.getMode()===`source`)return this._codeEditor.getValue();let e=this._editor;return e?e.read(()=>h(B)):this._options.value??``}setValue(e){return this.getMode()===`source`?(this._codeEditor.setValue(e),this):(this.ensureEditor().update(()=>m(e,B),{discrete:!0}),this)}getReadOnly(){return this._options.readOnly??!1}setReadOnly(e){return this._options.readOnly=e,this._editor?.setEditable(!e),this._codeEditor.setReadOnly(e),this}focus(e=!1){return this.getMode()===`source`?this._codeEditor.focus(e):this.ensureEditor().focus(),this}toggleBold(){return this.ensureEditor().dispatchCommand(p,`bold`),this}toggleItalic(){return this.ensureEditor().dispatchCommand(p,`italic`),this}toggleInlineCode(){return this.ensureEditor().dispatchCommand(p,`code`),this}toggleUnorderedList(){return this.ensureEditor().dispatchCommand(ve,void 0),this}toggleOrderedList(){return this.ensureEditor().dispatchCommand(_e,void 0),this}toggleLink(e){return this.ensureEditor().update(()=>{let t=d();f(t)&&y(e)},{discrete:!0}),this}setBlockType(e){return this.ensureEditor().update(()=>{let t=d();f(t)&&Ee(t,()=>createBlockNode(e))},{discrete:!0}),this}insertTable(e,t){let n=this.ensureEditor();return n.update(()=>{f(d())||te().selectEnd()},{discrete:!0}),n.dispatchCommand(F,{columns:String(t),rows:String(e),includeHeaders:{rows:!0,columns:!1}}),this}insertTableRow(e=!0){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&N(e)},{discrete:!0}),this}deleteTableRow(){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&A()},{discrete:!0}),this}insertTableColumn(e=!0){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&M(e)},{discrete:!0}),this}deleteTableColumn(){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&k()},{discrete:!0}),this}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,t){this._listeners.fire(e,t)}setContentEditable(e){return this._wysiwyg.setContentEditable(e),this}getContentEditable(){return this._wysiwyg.getContentEditable()}destructor(){this._unregisterTableView?.(),this._unregister?.(),this._editor?.setRootElement(null),super.destructor()}ensureEditor(){if(!this._editor){let e=ne({nodes:Oe,theme:Ae,onError:reportEditorError});e.update(()=>m(this._options.value??``,B),{discrete:!0}),e.setEditable(!(this._options.readOnly??!1)),this._unregister=Te(ge(e),v(e),Ce(e),Se(e),T(e,w(),je),de(e,B),e.registerUpdateListener(()=>this.handleChange())),this._editor=e}return this._editor}mountWysiwyg(){let e=this.ensureEditor();this._wysiwyg.mount(e),e.getRootElement()&&!this._unregisterTableView&&(this._unregisterTableView=we(e,!0))}handleChange(){let e=this._editor;if(!e)return;let t=e.read(()=>h(B));t!==this._options.value&&(this._options.value=t,this.emit(`change`,{value:t}))}handleCodeChange(e){e.value!==this._options.value&&(this._options.value=e.value,this.emit(`change`,{value:e.value}))}},Me=r(MarkdownEditor);export{u as CodeEditor,Me as MarkdownEditor,l as getLanguage,s as listLanguages,c as registerLanguage};
3
+ `)}}}var B=[createTableTransformer(()=>B),ae,le,ie,ue,ce,re,g,oe,se],Oe=[me,he,_,ye,b,C,S,R,z,L],V=`ts-ui-mde-heading`,H=`ts-ui-mde-quote`,U=`ts-ui-mde-code`,W=`ts-ui-mde-inline-code`,G=`ts-ui-mde-list`,K=`ts-ui-mde-link`,q=`ts-ui-mde-bold`,J=`ts-ui-mde-italic`,Y=`ts-ui-mde-table`,ke=`ts-ui-mde-table-row`,X=`ts-ui-mde-table-cell`,Z=`ts-ui-mde-table-cell-header`,Q=`ts-ui-mde-table-cell-selected`,$=!1;function ensureMarkdownEditorClassRules(){$||($=!0,new n({scope:`class`,name:V,styles:{fontWeight:`600`}}),new n({scope:`class`,name:H,styles:{borderLeft:`3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,paddingLeft:`1em`,marginLeft:`0`}}),new n({scope:`class`,name:U,styles:{fontFamily:`var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)`,background:`rgba(127, 127, 127, 0.16)`,borderRadius:`var(--ts-ui-border-radius, 3px)`,padding:`0.6em 0.8em`,whiteSpace:`pre-wrap`,display:`block`,margin:`1em 0`}}),new n({scope:`class`,name:W,styles:{fontFamily:`var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)`,background:`rgba(127, 127, 127, 0.16)`,borderRadius:`var(--ts-ui-border-radius, 3px)`,padding:`0.1em 0.3em`}}),new n({scope:`class`,name:G,styles:{paddingLeft:`1.5em`}}),new n({scope:`class`,name:K,styles:{color:`var(--ts-ui-indicator-focus, #2563eb)`}}),new n({scope:`class`,name:q,styles:{fontWeight:`700`}}),new n({scope:`class`,name:J,styles:{fontStyle:`italic`}}),new n({scope:`class`,name:Y,styles:{borderCollapse:`collapse`}}),new n({scope:`class`,name:X,styles:{border:`1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,padding:`0.3em 0.6em`}}),new n({scope:`class`,name:Z,styles:{border:`1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))`,padding:`0.3em 0.6em`,fontWeight:`600`}}),new n({scope:`selector`,name:`.${X} > p`,styles:{margin:`0`}}),new n({scope:`class`,name:Q,styles:{backgroundColor:`var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))`}}))}var Ae={heading:{h1:V,h2:V,h3:V,h4:V,h5:V,h6:V},quote:H,code:U,list:{ul:G,ol:G},text:{bold:q,italic:J,code:W},link:K,table:Y,tableRow:ke,tableCell:X,tableCellHeader:Z,tableCellSelected:Q},je=300;function reportEditorError(e){throw e}function createBlockNode(e){switch(e){case`quote`:return pe();case`code`:return x();case`paragraph`:return ee();default:return fe(e)}}function $selectionIsInTableCell(){let e=d();return f(e)?j(e.anchor.getNode())!==null:!1}var Me={userSelect:`text`,cursor:`text`},WysiwygSurface=class extends i{_contentEditable=!1;_onReady;constructor(e,n){super(void 0,{...Me,...n??{}}),this._onReady=e,this.setContentEditable(!0),this.setOverflow(`auto`),this.setPadding(new t(4,6,4,6)),this.setElementCSSRule(`lineHeight`,`var(--ts-ui-md-line-height, 1.8)`),this.onFirstLayout(()=>this._onReady())}init(t){super.init(t);let n=t??this.getElement();return n&&e.sink.apply(n,{setAttr:{contenteditable:this._contentEditable?`true`:`false`}}),this}setContentEditable(e){return this._contentEditable=e,this.setElementAttribute(`contenteditable`,e?`true`:`false`),this}getContentEditable(){return this._contentEditable}mount(t){if(t.getRootElement())return;let n=this.getElement();n&&(ensureMarkdownEditorClassRules(),e.sink.mountView(n,e=>(t.setRootElement(e),e)))}},MarkdownEditor=class extends i{_editor=null;_unregister=null;_unregisterTableView=null;_listeners=new a;_card;_wysiwyg;_codeEditor;constructor(e,t){super(t),e!==void 0&&this._options.value===void 0&&(this._options.value=e),this._card=new o,this.setLayoutManager(this._card),this._wysiwyg=new WysiwygSurface(()=>this.mountWysiwyg()),this._codeEditor=new u(this._options.value??``,{language:`markdown`,readOnly:this._options.readOnly??!1,listeners:{change:e=>this.handleCodeChange(e)}}),this.addComponent(this._wysiwyg),this.addComponent(this._codeEditor),this._card.setVisibleComponentId(this.getMode()===`source`?this._codeEditor.getId():this._wysiwyg.getId()),this.applyListeners(t?.listeners)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.readOnly!==void 0&&(this._options.readOnly=e.readOnly),e.mode!==void 0&&(this._options.mode=e.mode),this}getMode(){return this._options.mode??`wysiwyg`}setMode(e){if(this.getMode()===e)return this;let t=this.getValue();return this._options.mode=e,e===`source`?(this._codeEditor.setValue(t),this._card.setVisibleComponentId(this._codeEditor.getId())):(this.ensureEditor().update(()=>m(t,B),{discrete:!0}),this._card.setVisibleComponentId(this._wysiwyg.getId())),this}getValue(){if(this.getMode()===`source`)return this._codeEditor.getValue();let e=this._editor;return e?e.read(()=>h(B)):this._options.value??``}setValue(e){return this.getMode()===`source`?(this._codeEditor.setValue(e),this):(this.ensureEditor().update(()=>m(e,B),{discrete:!0}),this)}getReadOnly(){return this._options.readOnly??!1}setReadOnly(e){return this._options.readOnly=e,this._editor?.setEditable(!e),this._codeEditor.setReadOnly(e),this}focus(e=!1){return this.getMode()===`source`?this._codeEditor.focus(e):this.ensureEditor().focus(),this}toggleBold(){return this.ensureEditor().dispatchCommand(p,`bold`),this}toggleItalic(){return this.ensureEditor().dispatchCommand(p,`italic`),this}toggleInlineCode(){return this.ensureEditor().dispatchCommand(p,`code`),this}toggleUnorderedList(){return this.ensureEditor().dispatchCommand(ve,void 0),this}toggleOrderedList(){return this.ensureEditor().dispatchCommand(_e,void 0),this}toggleLink(e){return this.ensureEditor().update(()=>{let t=d();f(t)&&y(e)},{discrete:!0}),this}setBlockType(e){return this.ensureEditor().update(()=>{let t=d();f(t)&&Ee(t,()=>createBlockNode(e))},{discrete:!0}),this}insertTable(e,t){let n=this.ensureEditor();return n.update(()=>{f(d())||te().selectEnd()},{discrete:!0}),n.dispatchCommand(F,{columns:String(t),rows:String(e),includeHeaders:{rows:!0,columns:!1}}),this}insertTableRow(e=!0){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&N(e)},{discrete:!0}),this}deleteTableRow(){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&A()},{discrete:!0}),this}insertTableColumn(e=!0){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&M(e)},{discrete:!0}),this}deleteTableColumn(){return this.ensureEditor().update(()=>{$selectionIsInTableCell()&&k()},{discrete:!0}),this}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,t){this._listeners.fire(e,t)}setContentEditable(e){return this._wysiwyg.setContentEditable(e),this}getContentEditable(){return this._wysiwyg.getContentEditable()}destructor(){this._unregisterTableView?.(),this._unregister?.(),this._editor?.setRootElement(null),super.destructor()}ensureEditor(){if(!this._editor){let e=ne({nodes:Oe,theme:Ae,onError:reportEditorError});e.update(()=>m(this._options.value??``,B),{discrete:!0}),e.setEditable(!(this._options.readOnly??!1)),this._unregister=Te(ge(e),v(e),Ce(e),Se(e),T(e,w(),je),de(e,B),e.registerUpdateListener(()=>this.handleChange())),this._editor=e}return this._editor}mountWysiwyg(){let e=this.ensureEditor();this._wysiwyg.mount(e),e.getRootElement()&&!this._unregisterTableView&&(this._unregisterTableView=we(e,!0))}handleChange(){let e=this._editor;if(!e)return;let t=e.read(()=>h(B));t!==this._options.value&&(this._options.value=t,this.emit(`change`,{value:t}))}handleCodeChange(e){e.value!==this._options.value&&(this._options.value=e.value,this.emit(`change`,{value:e.value}))}},Ne=r(MarkdownEditor);export{u as CodeEditor,Ne as MarkdownEditor,l as getLanguage,s as listLanguages,c as registerLanguage};
4
4
  //# sourceMappingURL=editor.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.es.js","names":[],"sources":["../../../src/typescript/lib/component/editor/markdownTableTransformer.ts","../../../src/typescript/lib/component/editor/markdownTransformers.ts","../../../src/typescript/lib/component/editor/editorNodes.ts","../../../src/typescript/lib/component/editor/editorTheme.ts","../../../src/typescript/lib/component/editor/MarkdownEditor.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ElementFormatType } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString } from \"@lexical/markdown\";\nimport type { MultilineElementTransformer, Transformer } from \"@lexical/markdown\";\nimport {\n $createTableNode, $createTableRowNode, $createTableCellNode,\n $isTableNode, $isTableRowNode, $isTableCellNode,\n TableNode, TableRowNode, TableCellNode, TableCellHeaderStates,\n} from \"@lexical/table\";\nimport type { TableRowNode as TableRowNodeType } from \"@lexical/table\";\n\n/**\n * A candidate table row: any line containing a pipe. Deliberately loose — a\n * line only becomes a table once the next line is a matching delimiter row,\n * which {@link createTableTransformer}'s `handleImportAfterStartMatch` checks.\n * Leading and trailing pipes are optional in GFM, so they cannot be part of\n * this test.\n */\nconst TABLE_ROW_REG_EXP = /\\|/;\n\n/**\n * Splits one row into its cell texts. Drops one optional leading and one\n * optional trailing pipe, splits on unescaped `|` only, turns each `\\|` back\n * into a literal `|`, and trims each cell. A manual scan rather than a regexp\n * with a lookbehind — the same choice `@lexical/markdown`'s own\n * `isTableRowDivider` makes, since a nested-quantifier lookbehind pattern can\n * run in super-linear time on a backtracking regexp engine.\n *\n * @param line - One raw table-row line (with or without surrounding pipes).\n * @returns The row's cell texts, trimmed and unescaped.\n *\n * @example\n * ```\n * splitTableRow(\"| a | b \\\\| c |\") // -> [\"a\", \"b | c\"]\n * splitTableRow(\"a | b\") // -> [\"a\", \"b\"]\n * ```\n */\nfunction splitTableRow(line: string): string[] {\n let trimmed = line.trim();\n\n if (trimmed.startsWith(\"|\")) {\n trimmed = trimmed.slice(1);\n }\n\n if (trimmed.endsWith(\"|\") && !trimmed.endsWith(\"\\\\|\")) {\n trimmed = trimmed.slice(0, -1);\n }\n\n const cells: string[] = [];\n let current = \"\";\n\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n\n if (char === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n } else if (char === \"|\") {\n cells.push(current.trim());\n current = \"\";\n } else {\n current += char;\n }\n }\n\n cells.push(current.trim());\n\n return cells;\n}\n\n/**\n * Reads a GFM delimiter row's per-column alignment, or `null` when the line\n * is not a delimiter row (any segment isn't `-`s optionally bounded by `:`).\n *\n * @param line - The candidate delimiter-row line.\n * @returns One {@link ElementFormatType} per column, or `null` when `line` is\n * not a valid delimiter row.\n *\n * @example\n * ```\n * parseDelimiterRow(\"| :--- | :---: | ---: | --- |\") // -> [\"left\", \"center\", \"right\", \"\"]\n * parseDelimiterRow(\"| a | b |\") // -> null\n * ```\n */\nfunction parseDelimiterRow(line: string): ElementFormatType[] | null {\n const segments = splitTableRow(line);\n const alignments: ElementFormatType[] = [];\n\n for (const segment of segments) {\n if (!/^:?-+:?$/.test(segment)) {\n return null;\n }\n\n const left = segment.startsWith(\":\");\n const right = segment.endsWith(\":\");\n\n if (left && right) {\n alignments.push(\"center\");\n } else if (left) {\n alignments.push(\"left\");\n } else if (right) {\n alignments.push(\"right\");\n } else {\n alignments.push(\"\");\n }\n }\n\n return alignments;\n}\n\n/**\n * Renders a delimiter row from the header cells' formats.\n *\n * @param alignments - One {@link ElementFormatType} per column.\n * @returns The rendered delimiter-row line.\n *\n * @example\n * ```\n * formatDelimiterRow([\"left\", \"center\", \"right\", \"\"]) // -> \"| :--- | :---: | ---: | --- |\"\n * ```\n */\nfunction formatDelimiterRow(alignments: ElementFormatType[]): string {\n const cells = alignments.map((alignment) => {\n switch (alignment) {\n case \"left\": return \":---\";\n case \"center\": return \":---:\";\n case \"right\": return \"---:\";\n default: return \"---\";\n }\n });\n\n return \"| \" + cells.join(\" | \") + \" |\";\n}\n\n/**\n * Prepares a cell's Markdown for embedding in a pipe row: trims it, escapes\n * every `|` to `\\|`, and replaces every newline with a literal `\\n`.\n *\n * @param markdown - The cell's exported Markdown.\n * @returns The pipe-row-safe cell text.\n */\nfunction escapeCellText(markdown: string): string {\n return markdown.trim().replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n}\n\n/**\n * Builds the GFM pipe-table transformer: a `MultilineElementTransformer` that\n * consumes a whole table block (header, delimiter, and body rows) in one pass\n * — the same `handleImportAfterStartMatch` hook the curated `CODE` transformer\n * uses for fenced blocks — so it can require a delimiter row before\n * committing, rather than the row-at-a-time shape Lexical's own playground\n * `TABLE` transformer uses.\n *\n * @param getTransformers - Returns the curated transformer array. Called at\n * import/export time, not at construction time, so the array may reference\n * the transformer this call returns (avoiding a module import cycle with\n * the file that builds that array).\n * @returns The table transformer, ready to include in a transformer array.\n */\nexport function createTableTransformer(getTransformers: () => Transformer[]): MultilineElementTransformer {\n return {\n dependencies: [TableNode, TableRowNode, TableCellNode],\n regExpStart: TABLE_ROW_REG_EXP,\n type: \"multiline-element\",\n\n handleImportAfterStartMatch: ({ lines, rootNode, startLineIndex }) => {\n const delimiterLine = lines[startLineIndex + 1];\n\n if (delimiterLine === undefined) {\n return null;\n }\n\n const alignments = parseDelimiterRow(delimiterLine);\n\n if (alignments === null) {\n return null;\n }\n\n const headerCells = splitTableRow(lines[startLineIndex]);\n const columnCount = headerCells.length;\n\n if (alignments.length !== columnCount) {\n return null;\n }\n\n const rows: string[][] = [headerCells];\n let lastConsumedLineIndex = startLineIndex + 1;\n\n // marked ends a table only at a blank line (or end of input), so a\n // following prose line with no pipe in it is absorbed as a\n // one-cell row rather than ending the table here.\n for (let lineIndex = startLineIndex + 2; lineIndex < lines.length; lineIndex += 1) {\n const line = lines[lineIndex];\n\n if (line.trim() === \"\") {\n break;\n }\n\n rows.push(splitTableRow(line));\n lastConsumedLineIndex = lineIndex;\n }\n\n const table = $createTableNode();\n\n rows.forEach((cells, rowIndex) => {\n const row = $createTableRowNode();\n\n for (let column = 0; column < columnCount; column += 1) {\n const cell = $createTableCellNode(\n rowIndex === 0 ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS);\n\n $convertFromMarkdownString(\n (cells[column] ?? \"\").replace(/\\\\n/g, \"\\n\"), getTransformers(), cell);\n\n // AFTER the conversion: it clears the cell's children, and\n // setting the format first risks the clear taking the\n // format with it.\n cell.setFormat(alignments[column] ?? \"\");\n row.append(cell);\n }\n\n table.append(row);\n });\n\n rootNode.append(table);\n\n return [true, lastConsumedLineIndex];\n },\n\n // Never reached: handleImportAfterStartMatch either imports the block or\n // declines it, so the default multiline import path never runs.\n replace: () => false,\n\n export: (node) => {\n if (!$isTableNode(node)) {\n return null;\n }\n\n const rows = node.getChildren().filter($isTableRowNode);\n\n if (rows.length === 0) {\n return null;\n }\n\n const renderRow = (row: TableRowNodeType): string => {\n const cells = row.getChildren().filter($isTableCellNode);\n const rendered = cells.map(\n (cell) => escapeCellText($convertToMarkdownString(getTransformers(), cell)));\n\n return \"| \" + rendered.join(\" | \") + \" |\";\n };\n\n const headerRow = rows[0];\n const headerCells = headerRow.getChildren().filter($isTableCellNode);\n const alignments = headerCells.map((cell) => cell.getFormatType());\n\n const lines = [renderRow(headerRow), formatDelimiterRow(alignments)];\n\n for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {\n lines.push(renderRow(rows[rowIndex]));\n }\n\n return lines.join(\"\\n\");\n },\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport {\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n} from \"@lexical/markdown\";\nimport type { Transformer } from \"@lexical/markdown\";\nimport { createTableTransformer } from \"~/component/editor/markdownTableTransformer.js\";\n\n// Lazy: called at import/export time rather than passed by value, so this\n// module can build TABLE from the very array it is about to join, with no\n// import cycle back into this file.\nconst TABLE = createTableTransformer(() => TRANSFORMERS);\n\n/**\n * The curated Markdown transformer array that defines `MarkdownEditor`'s\n * dialect — the exact subset of Markdown the read-only `Markdown` viewer\n * renders.\n *\n * @remarks\n * This is deliberately **not** Lexical's full `TRANSFORMERS` preset. The preset\n * also carries `STRIKETHROUGH`, `HIGHLIGHT`, `CHECK_LIST`, and an image\n * transformer — constructs the viewer drops to a plain-text fallback. Curating\n * the list down to these ten is the single source of truth that guarantees the\n * editor can never emit Markdown the viewer would fail to render: the same array\n * is passed to the import converter, the export converter, and the\n * markdown-shortcut typing registration, so what the user types, what the editor\n * stores, and what the viewer reads all agree.\n *\n * The mapping to the viewer's supported tokens is:\n *\n * - `HEADING` → `#`…`######` (heading)\n * - `QUOTE` → `> ` (blockquote)\n * - `CODE` → fenced ` ``` ` block (code)\n * - `UNORDERED_LIST` → `- ` (unordered list)\n * - `ORDERED_LIST` → `1. ` (ordered list)\n * - `BOLD_STAR` → `**b**` (strong)\n * - `ITALIC_STAR` → `*i*` (em)\n * - `INLINE_CODE` → `` `c` `` (codespan)\n * - `LINK` → `[t](url)` (link)\n * - `TABLE` → `| a | b |` (table)\n *\n * Star (not underscore) emphasis variants are chosen so bold/italic export is\n * deterministic and matches the viewer's demo output.\n */\nexport const TRANSFORMERS: Transformer[] = [\n TABLE,\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { HeadingNode, QuoteNode } from \"@lexical/rich-text\";\nimport { ListNode, ListItemNode } from \"@lexical/list\";\nimport { LinkNode } from \"@lexical/link\";\nimport { CodeNode, CodeHighlightNode } from \"@lexical/code\";\nimport { TableNode, TableRowNode, TableCellNode } from \"@lexical/table\";\nimport type { Klass, LexicalNode } from \"lexical\";\n\n/**\n * The Lexical node classes registered on every `MarkdownEditor`'s editor.\n *\n * @remarks\n * A Lexical editor can only build or parse a node type whose class it was\n * created with, so this set must cover every construct the curated transformer\n * list produces: headings (`HeadingNode`), blockquotes (`QuoteNode`),\n * ordered/unordered lists (`ListNode` + `ListItemNode`), links (`LinkNode`),\n * fenced code (`CodeNode` + its `CodeHighlightNode` children), and GFM tables\n * (`TableNode` + `TableRowNode` + `TableCellNode`). The remaining dialect\n * constructs — bold, italic, inline code, paragraphs — are plain text formats\n * on the always-present built-in text/paragraph nodes and need no registration.\n */\nexport const EDITOR_NODES: ReadonlyArray<Klass<LexicalNode>> = [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n LinkNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport type { EditorThemeClasses } from \"lexical\";\n\n// Class names Lexical stamps onto the rendered nodes. Prefixed `ts-ui-mde-`\n// (markdown editor) so they never collide with the read-only `Markdown`\n// viewer's `ts-ui-md-` rules; the two components replicate rather than share\n// their class rules — the shared contract is the theme-token names below, not\n// the selectors.\nconst HEADING_CLASS = \"ts-ui-mde-heading\";\nconst QUOTE_CLASS = \"ts-ui-mde-quote\";\nconst CODE_BLOCK_CLASS = \"ts-ui-mde-code\";\nconst INLINE_CODE_CLASS = \"ts-ui-mde-inline-code\";\nconst LIST_CLASS = \"ts-ui-mde-list\";\nconst LINK_CLASS = \"ts-ui-mde-link\";\nconst BOLD_CLASS = \"ts-ui-mde-bold\";\nconst ITALIC_CLASS = \"ts-ui-mde-italic\";\nconst TABLE_CLASS = \"ts-ui-mde-table\";\nconst TABLE_ROW_CLASS = \"ts-ui-mde-table-row\";\nconst TABLE_CELL_CLASS = \"ts-ui-mde-table-cell\";\nconst TABLE_CELL_HEADER_CLASS = \"ts-ui-mde-table-cell-header\";\nconst TABLE_CELL_SELECTED_CLASS = \"ts-ui-mde-table-cell-selected\";\n\n/** Guards the module-singleton class-rule registration in {@link ensureMarkdownEditorClassRules}. */\nlet _classRulesEnsured = false;\n\n/**\n * Registers the shared class rules that style the WYSIWYG editing surface,\n * once per document.\n *\n * @remarks\n * The rules mirror the read-only `Markdown` viewer's constructs and reference\n * the **same** CSS custom-property tokens — `--ts-ui-font-mono` for code,\n * `--ts-ui-border-color` for the quote bar, `--ts-ui-indicator-focus` for links\n * — so the edited rich text looks identical to what the viewer renders (the\n * point of WYSIWYG). Because the rules resolve live CSS variables, a theme\n * toggle recolours the surface with no rebuild and the component needs no\n * theme-change subscription. Each em-relative padding/indent is genuine\n * structural spacing (a code wash, a list-marker gutter, a quote bar), scaling\n * with the surrounding font — not a cosmetic nudge.\n */\nexport function ensureMarkdownEditorClassRules(): void {\n if (_classRulesEnsured) {\n return;\n }\n\n _classRulesEnsured = true;\n\n new StyleRule({\n scope: \"class\",\n name: HEADING_CLASS,\n // Semibold so headings read as headings independent of any UA reset.\n styles: { fontWeight: \"600\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: QUOTE_CLASS,\n styles: {\n // 3px quote bar — the framework's thin-border weight — plus an\n // em-relative gutter indenting the quoted prose off the bar.\n borderLeft: \"3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n paddingLeft: \"1em\",\n marginLeft: \"0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: CODE_BLOCK_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n // Translucent grey wash reads as \"code\" on light and dark alike.\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Structural inset giving the fenced code frame room.\n padding: \"0.6em 0.8em\",\n whiteSpace: \"pre-wrap\",\n // Lexical renders a fenced block as <code>, which is inline by\n // default and so sits on a text baseline — it rides up into a\n // preceding block instead of stacking below it. The viewer's\n // fenced code is a <pre>, a block with the browser's 1em block\n // margin; both are restated here so the two surfaces stack alike.\n display: \"block\",\n margin: \"1em 0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: INLINE_CODE_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Snug padding so the wash hugs the inline-code glyphs.\n padding: \"0.1em 0.3em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LIST_CLASS,\n // Room for the list marker; structural, em-relative indentation.\n styles: { paddingLeft: \"1.5em\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LINK_CLASS,\n // The framework's single accent hue (shared with focus/selection).\n styles: { color: \"var(--ts-ui-indicator-focus, #2563eb)\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: BOLD_CLASS,\n styles: { fontWeight: \"700\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: ITALIC_CLASS,\n styles: { fontStyle: \"italic\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CLASS,\n styles: { borderCollapse: \"collapse\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n // Structural cell padding, matching the read-only viewer's cells.\n padding: \"0.3em 0.6em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_HEADER_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n padding: \"0.3em 0.6em\",\n fontWeight: \"600\",\n },\n });\n\n new StyleRule({\n scope: \"selector\",\n name: `.${TABLE_CELL_CLASS} > p`,\n // Lexical wraps each cell's content in a paragraph, which would carry\n // the browser's default 1em block margin and make every editor cell\n // two line-heights taller than the same cell in the read-only viewer,\n // whose cells hold inline content directly. Only cell paragraphs are\n // reset: prose paragraphs keep their margin, which is what matches the\n // viewer outside a table.\n styles: { margin: \"0\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_SELECTED_CLASS,\n // @lexical/table stamps this class onto cells caught in a drag-selected\n // range; reuses the data-grid Table component's own row-selected token\n // so a selected cell reads the same way here as it does there.\n styles: { backgroundColor: \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\" },\n });\n}\n\n/**\n * The Lexical class-name theme map linking each node type to its shared class\n * rule from {@link ensureMarkdownEditorClassRules}.\n *\n * @remarks\n * Every heading level maps to the one heading rule (the tag itself carries the\n * size); ordered and unordered lists share the indentation rule. Bold, italic,\n * and inline code are text-format entries, and links carry the accent colour.\n */\nexport const EDITOR_THEME: EditorThemeClasses = {\n heading: {\n h1: HEADING_CLASS,\n h2: HEADING_CLASS,\n h3: HEADING_CLASS,\n h4: HEADING_CLASS,\n h5: HEADING_CLASS,\n h6: HEADING_CLASS,\n },\n quote: QUOTE_CLASS,\n code: CODE_BLOCK_CLASS,\n list: {\n ul: LIST_CLASS,\n ol: LIST_CLASS,\n },\n text: {\n bold: BOLD_CLASS,\n italic: ITALIC_CLASS,\n code: INLINE_CODE_CLASS,\n },\n link: LINK_CLASS,\n table: TABLE_CLASS,\n tableRow: TABLE_ROW_CLASS,\n tableCell: TABLE_CELL_CLASS,\n tableCellHeader: TABLE_CELL_HEADER_CLASS,\n tableCellSelected: TABLE_CELL_SELECTED_CLASS,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CodeEditor } from \"~/component/editor/CodeEditor.js\";\nimport type { CodeEditorChange } from \"~/component/editor/CodeEditor.js\";\nimport { createEditor, FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection, $getRoot, $createParagraphNode } from \"lexical\";\nimport type { LexicalEditor, ElementNode } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString, registerMarkdownShortcuts } from \"@lexical/markdown\";\nimport { registerRichText, $createHeadingNode, $createQuoteNode } from \"@lexical/rich-text\";\nimport type { HeadingTagType } from \"@lexical/rich-text\";\nimport { registerList, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND } from \"@lexical/list\";\nimport { $toggleLink } from \"@lexical/link\";\nimport { $createCodeNode } from \"@lexical/code\";\nimport { registerHistory, createEmptyHistoryState } from \"@lexical/history\";\nimport {\n registerTablePlugin, registerTableCellUnmergeTransform, registerTableSelectionObserver,\n $getTableCellNodeFromLexicalNode,\n $insertTableRowAtSelection, $deleteTableRowAtSelection,\n $insertTableColumnAtSelection, $deleteTableColumnAtSelection,\n INSERT_TABLE_COMMAND,\n} from \"@lexical/table\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { $setBlocksType } from \"@lexical/selection\";\nimport { TRANSFORMERS } from \"~/component/editor/markdownTransformers.js\";\nimport { EDITOR_NODES } from \"~/component/editor/editorNodes.js\";\nimport { EDITOR_THEME, ensureMarkdownEditorClassRules } from \"~/component/editor/editorTheme.js\";\n\n/**\n * The coalescing window (ms) for the undo/redo history: edits within this gap\n * merge into a single undo step, matching Lexical's own recommended default.\n */\nconst HISTORY_DELAY_MS = 300;\n\n/**\n * Payload of {@link MarkdownEditor}'s `\"change\"` event: the Markdown string\n * after the edit that triggered it.\n *\n * @category Components\n */\nexport interface MarkdownEditorChange {\n value: string;\n}\n\n/** The event {@link MarkdownEditor} exposes through its custom `on` / `off` surface. */\ntype MarkdownEditorEvent = \"change\";\n\n/**\n * The editing surface a {@link MarkdownEditor} currently shows: the WYSIWYG\n * rich-text surface, or the raw-Markdown source [`CodeEditor`](/api/component/editor/classes/CodeEditor).\n *\n * @category Components\n */\nexport type MarkdownEditorMode = \"wysiwyg\" | \"source\";\n\n/**\n * A block type accepted by {@link MarkdownEditor.setBlockType}: a paragraph, one\n * of the six heading levels, a blockquote, or a fenced code block.\n *\n * @category Components\n */\nexport type MarkdownBlockType = \"paragraph\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"quote\" | \"code\";\n\n/**\n * Construction-time options for {@link MarkdownEditor}.\n *\n * @category Components\n */\nexport interface MarkdownEditorOptions extends ComponentOptions {\n /** Initial Markdown source (also accepted as the positional first argument). */\n value?: string;\n /** Whether the editor is read-only. Default `false`. */\n readOnly?: boolean;\n /** Which surface is shown. Default `\"wysiwyg\"`. */\n mode?: MarkdownEditorMode;\n /** Construction-time listener bag; the only event is `\"change\"`. */\n listeners?: { change?: (payload: MarkdownEditorChange) => void };\n}\n\n/**\n * The `onError` handler for the Lexical editor: rethrows so a node/transform\n * fault surfaces loudly rather than being swallowed.\n *\n * @param error - The error Lexical reports.\n */\nfunction reportEditorError(error: Error): void {\n throw error;\n}\n\n/**\n * Builds the Lexical block node a {@link MarkdownEditor.setBlockType} call\n * converts the selected blocks into.\n *\n * @param type - The requested block type.\n * @returns A freshly-created element node of that type.\n */\nfunction createBlockNode(type: MarkdownBlockType): ElementNode {\n switch (type) {\n case \"quote\": return $createQuoteNode();\n case \"code\": return $createCodeNode();\n case \"paragraph\": return $createParagraphNode();\n default: return $createHeadingNode(type satisfies HeadingTagType);\n }\n}\n\n/**\n * Whether the caret currently sits inside a table cell — the precondition for\n * the row/column helpers, which throw rather than no-op when it does not.\n *\n * @returns Whether the current selection is a range selection anchored inside\n * a table cell.\n */\nfunction $selectionIsInTableCell(): boolean {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n return $getTableCellNodeFromLexicalNode(selection.anchor.getNode()) !== null;\n}\n\n/**\n * The private WYSIWYG editing surface: the element Lexical's `contenteditable`\n * view mounts into.\n *\n * @remarks\n * Split out from {@link MarkdownEditor} so the coordinator's own element can host\n * *both* this surface and the source {@link CodeEditor} as two [`Card`](/api/layout/classes/Card)\n * children — one DOM element per class, since Lexical's `contenteditable` and\n * CodeMirror's view are two foreign live widgets that each need their own host\n * element. `MarkdownEditor` keeps ownership of the Lexical editor object; only\n * the mount element (and its `contenteditable` / caret plumbing) lives here.\n */\nclass WysiwygSurface extends Component {\n\n /** Whether this surface's element hosts an editable region (`contenteditable`). */\n private _contentEditable: boolean = false;\n\n /** Invoked on the surface's first connected + sized layout — the moment to mount the live view. */\n private readonly _onReady: () => void;\n\n /**\n * Constructs the WYSIWYG surface.\n *\n * @param onReady - Called on the surface's first connected + sized layout.\n */\n constructor(onReady: () => void) {\n super();\n\n this._onReady = onReady;\n\n this.setContentEditable(true);\n // Fills its host box and scrolls internally when the document overflows.\n this.setOverflow(\"auto\");\n // A few pixels of inset off the padding box: without it the caret sits\n // flush against the surface's left edge (at column 0 of any line) and\n // gets clipped to invisibility. The padding is on the scroll container,\n // so it clears the caret at every edge while overflow scrolling still\n // reveals the full document. The values match source mode's CodeMirror\n // text inset so toggling modes doesn't shift the prose: 4px vertical\n // (CodeMirror's `.cm-content` padding is `4px 0`) and 6px horizontal\n // (its `.cm-line` padding-left is 6px).\n this.setPadding(new Insets(4, 6, 4, 6));\n // Text caret over the whole surface, signalling editability. `setCursor`\n // caches in `_options.cursor`, which `applyStyle` replays, so it survives\n // both detached construction and Lexical's mount.\n this.setCursor(\"text\");\n // The surface is a contenteditable editing host, which the browser\n // exempts from the framework's `user-select: none` on its own, and\n // Lexical additionally stamps `user-select: text` inline on the root\n // when it mounts. Stating the same intent here in the framework's own\n // rule means the select-and-copy behaviour does not depend on that\n // inline write surviving a later re-render.\n this.setUserSelect(\"text\");\n // Matches the read-only Markdown viewer's own root-level override\n // (Markdown.ts) so the edited prose reads at the same leading as the\n // preview instead of falling back to the tighter UI-control line-height\n // the theme applies to <html>.\n this.setElementCSSRule(\"lineHeight\", \"var(--ts-ui-md-line-height, 1.8)\");\n\n this.onFirstLayout(() => this._onReady());\n }\n\n /**\n * Replays the cached `contenteditable` state onto the freshly-created\n * element. `setContentEditable` (called during detached construction, before\n * an element exists) caches into `_contentEditable`, and its underlying\n * `setElementAttribute` now also caches into the base class's\n * `_elementAttributes` map and replays it from `Component.init()`, so this\n * replay is redundant — kept anyway for the reason below.\n *\n * The write targets the `element` handle directly — as the base class does\n * for its own cached attributes — because at init time the element is not yet\n * in the document, so a `getElement()`-based setter would miss it.\n *\n * @param element - Optional element to initialise; falls back to `getElement()`.\n * @returns This surface, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element ?? this.getElement();\n\n if (el) {\n DOM.sink.apply(el, { setAttr: { contenteditable: this._contentEditable ? \"true\" : \"false\" } });\n }\n\n return this;\n }\n\n /**\n * Sets whether this surface's element hosts an editable region. Caches the\n * state in `_contentEditable` and writes the `contenteditable` attribute\n * through to the element. The underlying `setElementAttribute` caches and\n * replays the value itself now; {@link WysiwygSurface.init} also replays\n * the cached `_contentEditable` state directly onto the element once it is\n * created, redundantly but for the `getElement()`-timing reason documented\n * there.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This surface, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._contentEditable = contentEditable;\n this.setElementAttribute(\"contenteditable\", contentEditable ? \"true\" : \"false\");\n\n return this;\n }\n\n /**\n * Returns whether this surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._contentEditable;\n }\n\n /**\n * Mounts the given Lexical editor's editable view into this surface's element\n * through the DOM seam's `mountView` escape. No-ops when the view is already\n * mounted, the element is not yet available, or offline (the seam returns\n * `null`, leaving the editor headless).\n *\n * @param editor - The Lexical editor whose view to mount here.\n */\n mount(editor: LexicalEditor): void {\n if (editor.getRootElement()) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n ensureMarkdownEditorClassRules();\n\n // The factory parameter is left unannotated: its `HTMLElement` type is\n // inferred from the seam signature, so no DOM type is named here and the\n // `no-raw-dom` *hold* clause stays green. `setRootElement` accepts the\n // element structurally.\n DOM.sink.mountView(element, (root) => { editor.setRootElement(root); return root; });\n }\n}\n\n/**\n * A WYSIWYG rich-text editor whose public value is a Markdown string, with an\n * optional raw-Markdown source mode.\n *\n * @remarks\n * `MarkdownEditor` edits a document as rendered rich text — no visible markup —\n * and reads/writes Markdown through Lexical's bidirectional converters, so it is\n * the editing counterpart to the read-only\n * [`Markdown`](/api/component/display/classes/Markdown) viewer. Its dialect is\n * deliberately the **exact subset** the viewer renders (headings, paragraphs,\n * bold, italic, inline code, ordered/unordered lists, blockquotes, fenced code,\n * links, and GFM pipe tables with per-column alignment); a curated transformer\n * list — not Lexical's full preset — guarantees the editor can never emit\n * Markdown the viewer would drop to plain text, so an edited document renders\n * identically in the viewer.\n *\n * Formatting is driven three ways, all without a built-in toolbar: Markdown\n * shortcut typing (`# ` → heading, `**b**` → bold, `- ` → list, `> ` → quote,\n * ` ``` ` → code), the default keyboard shortcuts (Ctrl/Cmd+B / +I, undo/redo),\n * and a thin imperative command API (`toggleBold`, `setBlockType`,\n * `toggleUnorderedList`, `toggleLink`, `insertTable`,\n * `insertTableRow`/`deleteTableRow`, `insertTableColumn`/`deleteTableColumn`,\n * …) a consumer can wire to their own `Button`s.\n *\n * A {@link MarkdownEditor.setMode | mode} (`\"wysiwyg\"` | `\"source\"`) swaps the\n * editing surface between that rich-text view and a raw-Markdown\n * [`CodeEditor`](/api/component/editor/classes/CodeEditor); both are bound to the\n * same Markdown value, so `getValue`/`setValue` and the `\"change\"` event behave\n * identically in either mode and switching modes preserves the document. Like the\n * command API, the mode toggle is **consumer-wired** — there is no built-in\n * chrome — so a consumer drives `setMode` from their own control.\n *\n * Internally the component's own element hosts two swappable child surfaces\n * through a [`Card`](/api/layout/classes/Card) layout: a private WYSIWYG surface owning Lexical's\n * `contenteditable` element (a *foreign live widget*, like the `EditorView`\n * behind `CodeEditor`) and the source `CodeEditor`. Lexical separates its editor\n * **state** (a pure, DOM-free immutable tree) from that view, so the Markdown\n * value get/set/round-trip runs headless: offline neither view attaches, yet\n * `getValue` / `setValue`, the command API, and mode switching still operate on\n * the state. The editor fills its host box and scrolls internally; give it a\n * sized host (a `Fit` panel or an explicit `preferredSize`), the same as\n * `CodeEditor`.\n *\n * @example\n * ```typescript\n * import { MarkdownEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new MarkdownEditor('# Title\\n\\nSome **bold** text.');\n * panel.addComponent(editor);\n * editor.on('change', ({ value }) => console.log(value));\n * editor.setMode('source'); // switch to raw-Markdown editing\n * ```\n *\n * @category Components\n */\nclass MarkdownEditor extends Component<MarkdownEditorOptions> {\n\n /**\n * The Lexical editor, built lazily (headless) on first use or first layout;\n * `null` until then. The editor is DOM-free until its root element mounts,\n * so this is non-null and fully usable offline once built.\n */\n private _editor: LexicalEditor | null = null;\n\n /** The `mergeRegister` teardown for the rich-text / list / history / shortcut / update registrations. */\n private _unregister: (() => void) | null = null;\n\n /**\n * The teardown for {@link registerTableSelectionObserver}, registered only\n * once the WYSIWYG view actually mounts (its mutation listener needs a real\n * table element in the document) and cleared on {@link dispose}.\n */\n private _unregisterTableView: (() => void) | null = null;\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<MarkdownEditorEvent> = new ListenerBag<MarkdownEditorEvent>();\n\n /** The Card layout swapping the visible editing surface between WYSIWYG and source. */\n private readonly _card: Card;\n\n /** The WYSIWYG surface hosting Lexical's `contenteditable` view. */\n private readonly _wysiwyg: WysiwygSurface;\n\n /** The raw-Markdown source surface, shown in `\"source\"` mode. */\n private readonly _codeEditor: CodeEditor;\n\n /**\n * Constructs a Markdown editor.\n *\n * @param value - Initial Markdown source (optional; defaults to `\"\"`).\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: MarkdownEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n // The component's own element hosts two swappable surfaces via a Card\n // (one DOM element per class): the WYSIWYG contenteditable and the source\n // CodeEditor. The Card shows exactly one at a time.\n this._card = new Card();\n this.setLayoutManager(this._card);\n\n this._wysiwyg = new WysiwygSurface(() => this.mountWysiwyg());\n this._codeEditor = new CodeEditor(this._options.value ?? \"\", {\n language: \"markdown\",\n readOnly: this._options.readOnly ?? false,\n listeners: { change: (payload) => this.handleCodeChange(payload) },\n });\n\n this.addComponent(this._wysiwyg);\n this.addComponent(this._codeEditor);\n\n // Pick the initial visible child from the mode.\n this._card.setVisibleComponentId(\n this.getMode() === \"source\" ? this._codeEditor.getId() : this._wysiwyg.getId());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Caches the `value` / `readOnly` / `mode` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. The editor\n * is built later (lazily), so these are pure caches applied to the freshly-\n * created editor state / surfaces in the constructor and `ensureEditor`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MarkdownEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n if (options.mode !== undefined) this._options.mode = options.mode;\n\n return this;\n }\n\n /**\n * Returns the active editing surface mode.\n *\n * @returns `\"source\"` when the raw-Markdown surface is shown, else `\"wysiwyg\"`.\n */\n getMode(): MarkdownEditorMode {\n return this._options.mode ?? \"wysiwyg\";\n }\n\n /**\n * Switches the active editing surface. Converts the current document across\n * the surfaces so no edits are lost — the outgoing surface's Markdown is read\n * (via {@link MarkdownEditor.getValue}) before the mode flips, then loaded\n * into the incoming surface — and swaps the visible [`Card`](/api/layout/classes/Card) child.\n * No-op (no conversion, no `\"change\"`) when already in `mode`.\n *\n * @param mode - The surface to show.\n * @returns This component, for method chaining.\n */\n setMode(mode: MarkdownEditorMode): this {\n if (this.getMode() === mode) {\n return this;\n }\n\n // Read the canonical Markdown from the OUTGOING surface before the flip.\n const markdown = this.getValue();\n this._options.mode = mode;\n\n if (mode === \"source\") {\n this._codeEditor.setValue(markdown);\n this._card.setVisibleComponentId(this._codeEditor.getId());\n } else {\n this.ensureEditor().update(\n () => $convertFromMarkdownString(markdown, TRANSFORMERS), { discrete: true });\n this._card.setVisibleComponentId(this._wysiwyg.getId());\n }\n\n return this;\n }\n\n /**\n * Returns the current document as a Markdown string, from whichever surface\n * is active: the raw source text in `\"source\"` mode, else converted live from\n * the Lexical editor state (or the cached pre-mount / offline value).\n *\n * @returns The Markdown source, or `\"\"` when unset.\n */\n getValue(): string {\n if (this.getMode() === \"source\") {\n return this._codeEditor.getValue();\n }\n\n const editor = this._editor;\n\n if (editor) {\n return editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document from a Markdown string, targeting the active\n * surface: the source `CodeEditor` in `\"source\"` mode, else the Lexical\n * editor (building it if needed). A resulting content change fires `\"change\"`.\n *\n * @param value - The new Markdown source.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.setValue(value);\n\n return this;\n }\n\n // Discrete forces a synchronous commit, so the resulting `\"change\"`\n // fires before this returns rather than on a later flush.\n this.ensureEditor().update(() => $convertFromMarkdownString(value, TRANSFORMERS), { discrete: true });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The read-only state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits, on **both** surfaces. Caches the\n * state; when the Lexical editor exists, toggles its editable flag (which\n * also drops the caret / editing affordances on the mounted view); and\n * forwards to the source `CodeEditor`.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n this._editor?.setEditable(!readOnly);\n this._codeEditor.setReadOnly(readOnly);\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the active editing surface, placing a caret\n * ready for typing: the Lexical contenteditable in WYSIWYG mode (building\n * and mounting the editor if needed), or the source `CodeEditor` in\n * `\"source\"` mode.\n *\n * Overrides {@link core!Component.focus}, whose default focuses this component's\n * own host element — the `Card` wrapper — which would leave the caret out of\n * the nested contenteditable the typing must land in. In WYSIWYG mode it\n * defers to Lexical's own `focus`, which both focuses the root element and\n * sets a selection so a caret appears without a click.\n *\n * @param preventScroll - Forwarded to the source `CodeEditor` in `\"source\"`\n * mode; ignored in WYSIWYG mode, where Lexical manages its own focus scroll.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.focus(preventScroll);\n } else {\n this.ensureEditor().focus();\n }\n\n return this;\n }\n\n /**\n * Toggles bold on the current selection. No-op (without throwing) when there\n * is no range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleBold(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"bold\");\n\n return this;\n }\n\n /**\n * Toggles italic on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleItalic(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"italic\");\n\n return this;\n }\n\n /**\n * Toggles inline code on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleInlineCode(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"code\");\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an unordered (bulleted) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleUnorderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an ordered (numbered) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleOrderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Wraps the current selection in a link to `url`, or unwraps it when `url`\n * is `null`. No-op without a range selection.\n *\n * @param url - The link target, or `null` to remove the link.\n * @returns This component, for method chaining.\n */\n toggleLink(url: string | null): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $toggleLink(url);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Converts the selected blocks to `type` (paragraph, `h1`–`h6`, blockquote,\n * or fenced code). No-op without a range selection.\n *\n * @param type - The target block type.\n * @returns This component, for method chaining.\n */\n setBlockType(type: MarkdownBlockType): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => createBlockNode(type));\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a table of `rows` rows by `columns` columns at the caret. The\n * first row is the header row, so `insertTable(2, 3)` gives a header row\n * plus one body row. The caret lands in the first header cell.\n *\n * @param rows - The number of rows, including the header row.\n * @param columns - The number of columns.\n * @returns This component, for method chaining.\n */\n insertTable(rows: number, columns: number): this {\n const editor = this.ensureEditor();\n\n // INSERT_TABLE_COMMAND inserts at the selection; a freshly built editor\n // may have none, so seed one first.\n editor.update(() => {\n if (!$isRangeSelection($getSelection())) {\n $getRoot().selectEnd();\n }\n }, { discrete: true });\n\n editor.dispatchCommand(INSERT_TABLE_COMMAND, {\n columns: String(columns),\n rows: String(rows),\n includeHeaders: { rows: true, columns: false },\n });\n\n return this;\n }\n\n /**\n * Inserts a row after (default) or before the row holding the caret.\n * No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current row.\n * @returns This component, for method chaining.\n */\n insertTableRow(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableRowAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the row holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableRow(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableRowAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a column after (default) or before the column holding the\n * caret. No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current column.\n * @returns This component, for method chaining.\n */\n insertTableColumn(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableColumnAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the column holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableColumn(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableColumnAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the document\n * content changes (typing, a command, or {@link MarkdownEditor.setValue}) in\n * whichever surface is active.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new Markdown value.\n * @returns This component, for method chaining.\n */\n on(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans the `\"change\"` event out to its registered listeners.\n *\n * @param event - Must be `\"change\"`.\n * @param payload - The event payload.\n */\n protected emit(event: MarkdownEditorEvent, payload: MarkdownEditorChange): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Sets whether the WYSIWYG surface's element hosts an editable region.\n * Forwards to the private surface, preserving the public setter.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This component, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._wysiwyg.setContentEditable(contentEditable);\n\n return this;\n }\n\n /**\n * Returns whether the WYSIWYG surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._wysiwyg.getContentEditable();\n }\n\n /**\n * Detaches the Lexical registrations and the WYSIWYG surface's live view,\n * then defers to the base class for the rest of teardown — including\n * disposing the registered `_codeEditor` child, which mirrors\n * `CodeEditor.destructor`. Call before discarding a dynamically-built\n * `MarkdownEditor`.\n */\n protected destructor(): void {\n this._unregisterTableView?.();\n this._unregister?.();\n this._editor?.setRootElement(null);\n\n // `_codeEditor` is registered via `addComponent`, so\n // `super.destructor()`'s child recursion below already disposes it —\n // an explicit call here would run `CodeEditor.destructor()` a second\n // time.\n super.destructor();\n }\n\n /**\n * Builds the headless Lexical editor on first use. Idempotent: the editor is\n * built once. Offline the editor stays headless — the state, and every value\n * / command operation, still work.\n *\n * @returns The live (headless-capable) Lexical editor.\n */\n private ensureEditor(): LexicalEditor {\n if (!this._editor) {\n const editor = createEditor({ nodes: EDITOR_NODES, theme: EDITOR_THEME, onError: reportEditorError });\n\n // Populate the initial state before registering the update listener,\n // so loading the cached value does not fire a spurious `\"change\"`.\n editor.update(() => $convertFromMarkdownString(this._options.value ?? \"\", TRANSFORMERS), { discrete: true });\n editor.setEditable(!(this._options.readOnly ?? false));\n\n this._unregister = mergeRegister(\n registerRichText(editor),\n registerList(editor),\n registerTablePlugin(editor),\n registerTableCellUnmergeTransform(editor),\n registerHistory(editor, createEmptyHistoryState(), HISTORY_DELAY_MS),\n registerMarkdownShortcuts(editor, TRANSFORMERS),\n editor.registerUpdateListener(() => this.handleChange()),\n );\n\n this._editor = editor;\n }\n\n return this._editor;\n }\n\n /**\n * Builds the headless editor (if needed) and mounts its live view into the\n * WYSIWYG surface. Driven by the surface's first connected layout, i.e. the\n * first time it is the visible `Card` child and gets sized.\n */\n private mountWysiwyg(): void {\n const editor = this.ensureEditor();\n\n this._wysiwyg.mount(editor);\n\n // Offline the seam's mountView returns null and the root stays unset;\n // the observer's mutation listener needs a real table element in the\n // document and throws without one, so it is view-time only.\n if (editor.getRootElement() && !this._unregisterTableView) {\n this._unregisterTableView = registerTableSelectionObserver(editor, true);\n }\n }\n\n /**\n * Recomputes the Markdown value from the committed editor state after a\n * Lexical update and, when it differs from the cached value (i.e. the content\n * — not merely the selection — changed), caches it and emits `\"change\"`.\n */\n private handleChange(): void {\n const editor = this._editor;\n\n if (!editor) {\n return;\n }\n\n const value = editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n\n if (value === this._options.value) {\n return;\n }\n\n this._options.value = value;\n this.emit(\"change\", { value });\n }\n\n /**\n * Recomputes the cached value from a source-surface (`CodeEditor`) edit and,\n * when it differs from the cached value, caches it and emits `\"change\"`. The\n * equality guard makes the programmatic `setValue` on a mode switch not\n * double-emit (it loads the value already equal to `_options.value`).\n *\n * @param payload - The `CodeEditor` change payload (the new source text).\n */\n private handleCodeChange(payload: CodeEditorChange): void {\n if (payload.value === this._options.value) {\n return;\n }\n\n this._options.value = payload.value;\n this.emit(\"change\", { value: payload.value });\n }\n}\n\nconst MarkdownEditorCallable = callable(MarkdownEditor);\ntype MarkdownEditorCallable = MarkdownEditor;\nexport {\n MarkdownEditor as _MarkdownEditor,\n MarkdownEditorCallable as MarkdownEditor,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"editor.es.js","names":[],"sources":["../../../src/typescript/lib/component/editor/markdownTableTransformer.ts","../../../src/typescript/lib/component/editor/markdownTransformers.ts","../../../src/typescript/lib/component/editor/editorNodes.ts","../../../src/typescript/lib/component/editor/editorTheme.ts","../../../src/typescript/lib/component/editor/MarkdownEditor.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ElementFormatType } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString } from \"@lexical/markdown\";\nimport type { MultilineElementTransformer, Transformer } from \"@lexical/markdown\";\nimport {\n $createTableNode, $createTableRowNode, $createTableCellNode,\n $isTableNode, $isTableRowNode, $isTableCellNode,\n TableNode, TableRowNode, TableCellNode, TableCellHeaderStates,\n} from \"@lexical/table\";\nimport type { TableRowNode as TableRowNodeType } from \"@lexical/table\";\n\n/**\n * A candidate table row: any line containing a pipe. Deliberately loose — a\n * line only becomes a table once the next line is a matching delimiter row,\n * which {@link createTableTransformer}'s `handleImportAfterStartMatch` checks.\n * Leading and trailing pipes are optional in GFM, so they cannot be part of\n * this test.\n */\nconst TABLE_ROW_REG_EXP = /\\|/;\n\n/**\n * Splits one row into its cell texts. Drops one optional leading and one\n * optional trailing pipe, splits on unescaped `|` only, turns each `\\|` back\n * into a literal `|`, and trims each cell. A manual scan rather than a regexp\n * with a lookbehind — the same choice `@lexical/markdown`'s own\n * `isTableRowDivider` makes, since a nested-quantifier lookbehind pattern can\n * run in super-linear time on a backtracking regexp engine.\n *\n * @param line - One raw table-row line (with or without surrounding pipes).\n * @returns The row's cell texts, trimmed and unescaped.\n *\n * @example\n * ```\n * splitTableRow(\"| a | b \\\\| c |\") // -> [\"a\", \"b | c\"]\n * splitTableRow(\"a | b\") // -> [\"a\", \"b\"]\n * ```\n */\nfunction splitTableRow(line: string): string[] {\n let trimmed = line.trim();\n\n if (trimmed.startsWith(\"|\")) {\n trimmed = trimmed.slice(1);\n }\n\n if (trimmed.endsWith(\"|\") && !trimmed.endsWith(\"\\\\|\")) {\n trimmed = trimmed.slice(0, -1);\n }\n\n const cells: string[] = [];\n let current = \"\";\n\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n\n if (char === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n } else if (char === \"|\") {\n cells.push(current.trim());\n current = \"\";\n } else {\n current += char;\n }\n }\n\n cells.push(current.trim());\n\n return cells;\n}\n\n/**\n * Reads a GFM delimiter row's per-column alignment, or `null` when the line\n * is not a delimiter row (any segment isn't `-`s optionally bounded by `:`).\n *\n * @param line - The candidate delimiter-row line.\n * @returns One {@link ElementFormatType} per column, or `null` when `line` is\n * not a valid delimiter row.\n *\n * @example\n * ```\n * parseDelimiterRow(\"| :--- | :---: | ---: | --- |\") // -> [\"left\", \"center\", \"right\", \"\"]\n * parseDelimiterRow(\"| a | b |\") // -> null\n * ```\n */\nfunction parseDelimiterRow(line: string): ElementFormatType[] | null {\n const segments = splitTableRow(line);\n const alignments: ElementFormatType[] = [];\n\n for (const segment of segments) {\n if (!/^:?-+:?$/.test(segment)) {\n return null;\n }\n\n const left = segment.startsWith(\":\");\n const right = segment.endsWith(\":\");\n\n if (left && right) {\n alignments.push(\"center\");\n } else if (left) {\n alignments.push(\"left\");\n } else if (right) {\n alignments.push(\"right\");\n } else {\n alignments.push(\"\");\n }\n }\n\n return alignments;\n}\n\n/**\n * Renders a delimiter row from the header cells' formats.\n *\n * @param alignments - One {@link ElementFormatType} per column.\n * @returns The rendered delimiter-row line.\n *\n * @example\n * ```\n * formatDelimiterRow([\"left\", \"center\", \"right\", \"\"]) // -> \"| :--- | :---: | ---: | --- |\"\n * ```\n */\nfunction formatDelimiterRow(alignments: ElementFormatType[]): string {\n const cells = alignments.map((alignment) => {\n switch (alignment) {\n case \"left\": return \":---\";\n case \"center\": return \":---:\";\n case \"right\": return \"---:\";\n default: return \"---\";\n }\n });\n\n return \"| \" + cells.join(\" | \") + \" |\";\n}\n\n/**\n * Prepares a cell's Markdown for embedding in a pipe row: trims it, escapes\n * every `|` to `\\|`, and replaces every newline with a literal `\\n`.\n *\n * @param markdown - The cell's exported Markdown.\n * @returns The pipe-row-safe cell text.\n */\nfunction escapeCellText(markdown: string): string {\n return markdown.trim().replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n}\n\n/**\n * Builds the GFM pipe-table transformer: a `MultilineElementTransformer` that\n * consumes a whole table block (header, delimiter, and body rows) in one pass\n * — the same `handleImportAfterStartMatch` hook the curated `CODE` transformer\n * uses for fenced blocks — so it can require a delimiter row before\n * committing, rather than the row-at-a-time shape Lexical's own playground\n * `TABLE` transformer uses.\n *\n * @param getTransformers - Returns the curated transformer array. Called at\n * import/export time, not at construction time, so the array may reference\n * the transformer this call returns (avoiding a module import cycle with\n * the file that builds that array).\n * @returns The table transformer, ready to include in a transformer array.\n */\nexport function createTableTransformer(getTransformers: () => Transformer[]): MultilineElementTransformer {\n return {\n dependencies: [TableNode, TableRowNode, TableCellNode],\n regExpStart: TABLE_ROW_REG_EXP,\n type: \"multiline-element\",\n\n handleImportAfterStartMatch: ({ lines, rootNode, startLineIndex }) => {\n const delimiterLine = lines[startLineIndex + 1];\n\n if (delimiterLine === undefined) {\n return null;\n }\n\n const alignments = parseDelimiterRow(delimiterLine);\n\n if (alignments === null) {\n return null;\n }\n\n const headerCells = splitTableRow(lines[startLineIndex]);\n const columnCount = headerCells.length;\n\n if (alignments.length !== columnCount) {\n return null;\n }\n\n const rows: string[][] = [headerCells];\n let lastConsumedLineIndex = startLineIndex + 1;\n\n // marked ends a table only at a blank line (or end of input), so a\n // following prose line with no pipe in it is absorbed as a\n // one-cell row rather than ending the table here.\n for (let lineIndex = startLineIndex + 2; lineIndex < lines.length; lineIndex += 1) {\n const line = lines[lineIndex];\n\n if (line.trim() === \"\") {\n break;\n }\n\n rows.push(splitTableRow(line));\n lastConsumedLineIndex = lineIndex;\n }\n\n const table = $createTableNode();\n\n rows.forEach((cells, rowIndex) => {\n const row = $createTableRowNode();\n\n for (let column = 0; column < columnCount; column += 1) {\n const cell = $createTableCellNode(\n rowIndex === 0 ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS);\n\n $convertFromMarkdownString(\n (cells[column] ?? \"\").replace(/\\\\n/g, \"\\n\"), getTransformers(), cell);\n\n // AFTER the conversion: it clears the cell's children, and\n // setting the format first risks the clear taking the\n // format with it.\n cell.setFormat(alignments[column] ?? \"\");\n row.append(cell);\n }\n\n table.append(row);\n });\n\n rootNode.append(table);\n\n return [true, lastConsumedLineIndex];\n },\n\n // Never reached: handleImportAfterStartMatch either imports the block or\n // declines it, so the default multiline import path never runs.\n replace: () => false,\n\n export: (node) => {\n if (!$isTableNode(node)) {\n return null;\n }\n\n const rows = node.getChildren().filter($isTableRowNode);\n\n if (rows.length === 0) {\n return null;\n }\n\n const renderRow = (row: TableRowNodeType): string => {\n const cells = row.getChildren().filter($isTableCellNode);\n const rendered = cells.map(\n (cell) => escapeCellText($convertToMarkdownString(getTransformers(), cell)));\n\n return \"| \" + rendered.join(\" | \") + \" |\";\n };\n\n const headerRow = rows[0];\n const headerCells = headerRow.getChildren().filter($isTableCellNode);\n const alignments = headerCells.map((cell) => cell.getFormatType());\n\n const lines = [renderRow(headerRow), formatDelimiterRow(alignments)];\n\n for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {\n lines.push(renderRow(rows[rowIndex]));\n }\n\n return lines.join(\"\\n\");\n },\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport {\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n} from \"@lexical/markdown\";\nimport type { Transformer } from \"@lexical/markdown\";\nimport { createTableTransformer } from \"~/component/editor/markdownTableTransformer.js\";\n\n// Lazy: called at import/export time rather than passed by value, so this\n// module can build TABLE from the very array it is about to join, with no\n// import cycle back into this file.\nconst TABLE = createTableTransformer(() => TRANSFORMERS);\n\n/**\n * The curated Markdown transformer array that defines `MarkdownEditor`'s\n * dialect — the exact subset of Markdown the read-only `Markdown` viewer\n * renders.\n *\n * @remarks\n * This is deliberately **not** Lexical's full `TRANSFORMERS` preset. The preset\n * also carries `STRIKETHROUGH`, `HIGHLIGHT`, `CHECK_LIST`, and an image\n * transformer — constructs the viewer drops to a plain-text fallback. Curating\n * the list down to these ten is the single source of truth that guarantees the\n * editor can never emit Markdown the viewer would fail to render: the same array\n * is passed to the import converter, the export converter, and the\n * markdown-shortcut typing registration, so what the user types, what the editor\n * stores, and what the viewer reads all agree.\n *\n * The mapping to the viewer's supported tokens is:\n *\n * - `HEADING` → `#`…`######` (heading)\n * - `QUOTE` → `> ` (blockquote)\n * - `CODE` → fenced ` ``` ` block (code)\n * - `UNORDERED_LIST` → `- ` (unordered list)\n * - `ORDERED_LIST` → `1. ` (ordered list)\n * - `BOLD_STAR` → `**b**` (strong)\n * - `ITALIC_STAR` → `*i*` (em)\n * - `INLINE_CODE` → `` `c` `` (codespan)\n * - `LINK` → `[t](url)` (link)\n * - `TABLE` → `| a | b |` (table)\n *\n * Star (not underscore) emphasis variants are chosen so bold/italic export is\n * deterministic and matches the viewer's demo output.\n */\nexport const TRANSFORMERS: Transformer[] = [\n TABLE,\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { HeadingNode, QuoteNode } from \"@lexical/rich-text\";\nimport { ListNode, ListItemNode } from \"@lexical/list\";\nimport { LinkNode } from \"@lexical/link\";\nimport { CodeNode, CodeHighlightNode } from \"@lexical/code\";\nimport { TableNode, TableRowNode, TableCellNode } from \"@lexical/table\";\nimport type { Klass, LexicalNode } from \"lexical\";\n\n/**\n * The Lexical node classes registered on every `MarkdownEditor`'s editor.\n *\n * @remarks\n * A Lexical editor can only build or parse a node type whose class it was\n * created with, so this set must cover every construct the curated transformer\n * list produces: headings (`HeadingNode`), blockquotes (`QuoteNode`),\n * ordered/unordered lists (`ListNode` + `ListItemNode`), links (`LinkNode`),\n * fenced code (`CodeNode` + its `CodeHighlightNode` children), and GFM tables\n * (`TableNode` + `TableRowNode` + `TableCellNode`). The remaining dialect\n * constructs — bold, italic, inline code, paragraphs — are plain text formats\n * on the always-present built-in text/paragraph nodes and need no registration.\n */\nexport const EDITOR_NODES: ReadonlyArray<Klass<LexicalNode>> = [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n LinkNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport type { EditorThemeClasses } from \"lexical\";\n\n// Class names Lexical stamps onto the rendered nodes. Prefixed `ts-ui-mde-`\n// (markdown editor) so they never collide with the read-only `Markdown`\n// viewer's `ts-ui-md-` rules; the two components replicate rather than share\n// their class rules — the shared contract is the theme-token names below, not\n// the selectors.\nconst HEADING_CLASS = \"ts-ui-mde-heading\";\nconst QUOTE_CLASS = \"ts-ui-mde-quote\";\nconst CODE_BLOCK_CLASS = \"ts-ui-mde-code\";\nconst INLINE_CODE_CLASS = \"ts-ui-mde-inline-code\";\nconst LIST_CLASS = \"ts-ui-mde-list\";\nconst LINK_CLASS = \"ts-ui-mde-link\";\nconst BOLD_CLASS = \"ts-ui-mde-bold\";\nconst ITALIC_CLASS = \"ts-ui-mde-italic\";\nconst TABLE_CLASS = \"ts-ui-mde-table\";\nconst TABLE_ROW_CLASS = \"ts-ui-mde-table-row\";\nconst TABLE_CELL_CLASS = \"ts-ui-mde-table-cell\";\nconst TABLE_CELL_HEADER_CLASS = \"ts-ui-mde-table-cell-header\";\nconst TABLE_CELL_SELECTED_CLASS = \"ts-ui-mde-table-cell-selected\";\n\n/** Guards the module-singleton class-rule registration in {@link ensureMarkdownEditorClassRules}. */\nlet _classRulesEnsured = false;\n\n/**\n * Registers the shared class rules that style the WYSIWYG editing surface,\n * once per document.\n *\n * @remarks\n * The rules mirror the read-only `Markdown` viewer's constructs and reference\n * the **same** CSS custom-property tokens — `--ts-ui-font-mono` for code,\n * `--ts-ui-border-color` for the quote bar, `--ts-ui-indicator-focus` for links\n * — so the edited rich text looks identical to what the viewer renders (the\n * point of WYSIWYG). Because the rules resolve live CSS variables, a theme\n * toggle recolours the surface with no rebuild and the component needs no\n * theme-change subscription. Each em-relative padding/indent is genuine\n * structural spacing (a code wash, a list-marker gutter, a quote bar), scaling\n * with the surrounding font — not a cosmetic nudge.\n */\nexport function ensureMarkdownEditorClassRules(): void {\n if (_classRulesEnsured) {\n return;\n }\n\n _classRulesEnsured = true;\n\n new StyleRule({\n scope: \"class\",\n name: HEADING_CLASS,\n // Semibold so headings read as headings independent of any UA reset.\n styles: { fontWeight: \"600\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: QUOTE_CLASS,\n styles: {\n // 3px quote bar — the framework's thin-border weight — plus an\n // em-relative gutter indenting the quoted prose off the bar.\n borderLeft: \"3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n paddingLeft: \"1em\",\n marginLeft: \"0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: CODE_BLOCK_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n // Translucent grey wash reads as \"code\" on light and dark alike.\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Structural inset giving the fenced code frame room.\n padding: \"0.6em 0.8em\",\n whiteSpace: \"pre-wrap\",\n // Lexical renders a fenced block as <code>, which is inline by\n // default and so sits on a text baseline — it rides up into a\n // preceding block instead of stacking below it. The viewer's\n // fenced code is a <pre>, a block with the browser's 1em block\n // margin; both are restated here so the two surfaces stack alike.\n display: \"block\",\n margin: \"1em 0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: INLINE_CODE_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Snug padding so the wash hugs the inline-code glyphs.\n padding: \"0.1em 0.3em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LIST_CLASS,\n // Room for the list marker; structural, em-relative indentation.\n styles: { paddingLeft: \"1.5em\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LINK_CLASS,\n // The framework's single accent hue (shared with focus/selection).\n styles: { color: \"var(--ts-ui-indicator-focus, #2563eb)\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: BOLD_CLASS,\n styles: { fontWeight: \"700\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: ITALIC_CLASS,\n styles: { fontStyle: \"italic\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CLASS,\n styles: { borderCollapse: \"collapse\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n // Structural cell padding, matching the read-only viewer's cells.\n padding: \"0.3em 0.6em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_HEADER_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n padding: \"0.3em 0.6em\",\n fontWeight: \"600\",\n },\n });\n\n new StyleRule({\n scope: \"selector\",\n name: `.${TABLE_CELL_CLASS} > p`,\n // Lexical wraps each cell's content in a paragraph, which would carry\n // the browser's default 1em block margin and make every editor cell\n // two line-heights taller than the same cell in the read-only viewer,\n // whose cells hold inline content directly. Only cell paragraphs are\n // reset: prose paragraphs keep their margin, which is what matches the\n // viewer outside a table.\n styles: { margin: \"0\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_SELECTED_CLASS,\n // @lexical/table stamps this class onto cells caught in a drag-selected\n // range; reuses the data-grid Table component's own row-selected token\n // so a selected cell reads the same way here as it does there.\n styles: { backgroundColor: \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\" },\n });\n}\n\n/**\n * The Lexical class-name theme map linking each node type to its shared class\n * rule from {@link ensureMarkdownEditorClassRules}.\n *\n * @remarks\n * Every heading level maps to the one heading rule (the tag itself carries the\n * size); ordered and unordered lists share the indentation rule. Bold, italic,\n * and inline code are text-format entries, and links carry the accent colour.\n */\nexport const EDITOR_THEME: EditorThemeClasses = {\n heading: {\n h1: HEADING_CLASS,\n h2: HEADING_CLASS,\n h3: HEADING_CLASS,\n h4: HEADING_CLASS,\n h5: HEADING_CLASS,\n h6: HEADING_CLASS,\n },\n quote: QUOTE_CLASS,\n code: CODE_BLOCK_CLASS,\n list: {\n ul: LIST_CLASS,\n ol: LIST_CLASS,\n },\n text: {\n bold: BOLD_CLASS,\n italic: ITALIC_CLASS,\n code: INLINE_CODE_CLASS,\n },\n link: LINK_CLASS,\n table: TABLE_CLASS,\n tableRow: TABLE_ROW_CLASS,\n tableCell: TABLE_CELL_CLASS,\n tableCellHeader: TABLE_CELL_HEADER_CLASS,\n tableCellSelected: TABLE_CELL_SELECTED_CLASS,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CodeEditor } from \"~/component/editor/CodeEditor.js\";\nimport type { CodeEditorChange } from \"~/component/editor/CodeEditor.js\";\nimport { createEditor, FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection, $getRoot, $createParagraphNode } from \"lexical\";\nimport type { LexicalEditor, ElementNode } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString, registerMarkdownShortcuts } from \"@lexical/markdown\";\nimport { registerRichText, $createHeadingNode, $createQuoteNode } from \"@lexical/rich-text\";\nimport type { HeadingTagType } from \"@lexical/rich-text\";\nimport { registerList, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND } from \"@lexical/list\";\nimport { $toggleLink } from \"@lexical/link\";\nimport { $createCodeNode } from \"@lexical/code\";\nimport { registerHistory, createEmptyHistoryState } from \"@lexical/history\";\nimport {\n registerTablePlugin, registerTableCellUnmergeTransform, registerTableSelectionObserver,\n $getTableCellNodeFromLexicalNode,\n $insertTableRowAtSelection, $deleteTableRowAtSelection,\n $insertTableColumnAtSelection, $deleteTableColumnAtSelection,\n INSERT_TABLE_COMMAND,\n} from \"@lexical/table\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { $setBlocksType } from \"@lexical/selection\";\nimport { TRANSFORMERS } from \"~/component/editor/markdownTransformers.js\";\nimport { EDITOR_NODES } from \"~/component/editor/editorNodes.js\";\nimport { EDITOR_THEME, ensureMarkdownEditorClassRules } from \"~/component/editor/editorTheme.js\";\n\n/**\n * The coalescing window (ms) for the undo/redo history: edits within this gap\n * merge into a single undo step, matching Lexical's own recommended default.\n */\nconst HISTORY_DELAY_MS = 300;\n\n/**\n * Payload of {@link MarkdownEditor}'s `\"change\"` event: the Markdown string\n * after the edit that triggered it.\n *\n * @category Components\n */\nexport interface MarkdownEditorChange {\n value: string;\n}\n\n/** The event {@link MarkdownEditor} exposes through its custom `on` / `off` surface. */\ntype MarkdownEditorEvent = \"change\";\n\n/**\n * The editing surface a {@link MarkdownEditor} currently shows: the WYSIWYG\n * rich-text surface, or the raw-Markdown source [`CodeEditor`](/api/component/editor/classes/CodeEditor).\n *\n * @category Components\n */\nexport type MarkdownEditorMode = \"wysiwyg\" | \"source\";\n\n/**\n * A block type accepted by {@link MarkdownEditor.setBlockType}: a paragraph, one\n * of the six heading levels, a blockquote, or a fenced code block.\n *\n * @category Components\n */\nexport type MarkdownBlockType = \"paragraph\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"quote\" | \"code\";\n\n/**\n * Construction-time options for {@link MarkdownEditor}.\n *\n * @category Components\n */\nexport interface MarkdownEditorOptions extends ComponentOptions {\n /** Initial Markdown source (also accepted as the positional first argument). */\n value?: string;\n /** Whether the editor is read-only. Default `false`. */\n readOnly?: boolean;\n /** Which surface is shown. Default `\"wysiwyg\"`. */\n mode?: MarkdownEditorMode;\n /** Construction-time listener bag; the only event is `\"change\"`. */\n listeners?: { change?: (payload: MarkdownEditorChange) => void };\n}\n\n/**\n * The `onError` handler for the Lexical editor: rethrows so a node/transform\n * fault surfaces loudly rather than being swallowed.\n *\n * @param error - The error Lexical reports.\n */\nfunction reportEditorError(error: Error): void {\n throw error;\n}\n\n/**\n * Builds the Lexical block node a {@link MarkdownEditor.setBlockType} call\n * converts the selected blocks into.\n *\n * @param type - The requested block type.\n * @returns A freshly-created element node of that type.\n */\nfunction createBlockNode(type: MarkdownBlockType): ElementNode {\n switch (type) {\n case \"quote\": return $createQuoteNode();\n case \"code\": return $createCodeNode();\n case \"paragraph\": return $createParagraphNode();\n default: return $createHeadingNode(type satisfies HeadingTagType);\n }\n}\n\n/**\n * Whether the caret currently sits inside a table cell — the precondition for\n * the row/column helpers, which throw rather than no-op when it does not.\n *\n * @returns Whether the current selection is a range selection anchored inside\n * a table cell.\n */\nfunction $selectionIsInTableCell(): boolean {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n return $getTableCellNodeFromLexicalNode(selection.anchor.getNode()) !== null;\n}\n\n// A text caret over the whole surface signals editability. The surface is also\n// a contenteditable editing host, which the browser exempts from the framework's\n// `user-select: none` on its own, and Lexical additionally stamps\n// `user-select: text` inline on the root when it mounts — stating the same\n// intent here in the framework's own rule means select-and-copy does not depend\n// on that inline write surviving a later re-render. As class defaults both land\n// on the shared `.WysiwygSurface` rule rather than each instance's `#id` rule.\nconst _defaultWysiwygSurfaceOptions: Partial<ComponentOptions> = { userSelect: \"text\", cursor: \"text\" };\n\n/**\n * The private WYSIWYG editing surface: the element Lexical's `contenteditable`\n * view mounts into.\n *\n * @remarks\n * Split out from {@link MarkdownEditor} so the coordinator's own element can host\n * *both* this surface and the source {@link CodeEditor} as two [`Card`](/api/layout/classes/Card)\n * children — one DOM element per class, since Lexical's `contenteditable` and\n * CodeMirror's view are two foreign live widgets that each need their own host\n * element. `MarkdownEditor` keeps ownership of the Lexical editor object; only\n * the mount element (and its `contenteditable` / caret plumbing) lives here.\n */\nclass WysiwygSurface extends Component {\n\n /** Whether this surface's element hosts an editable region (`contenteditable`). */\n private _contentEditable: boolean = false;\n\n /** Invoked on the surface's first connected + sized layout — the moment to mount the live view. */\n private readonly _onReady: () => void;\n\n /**\n * Constructs the WYSIWYG surface.\n *\n * @param onReady - Called on the surface's first connected + sized layout.\n * @param subclassDefaults - Optional subclass defaults bag.\n */\n constructor(onReady: () => void, subclassDefaults?: Partial<ComponentOptions>) {\n super(undefined, { ..._defaultWysiwygSurfaceOptions, ...(subclassDefaults ?? {}) });\n\n this._onReady = onReady;\n\n this.setContentEditable(true);\n // Fills its host box and scrolls internally when the document overflows.\n this.setOverflow(\"auto\");\n // A few pixels of inset off the padding box: without it the caret sits\n // flush against the surface's left edge (at column 0 of any line) and\n // gets clipped to invisibility. The padding is on the scroll container,\n // so it clears the caret at every edge while overflow scrolling still\n // reveals the full document. The values match source mode's CodeMirror\n // text inset so toggling modes doesn't shift the prose: 4px vertical\n // (CodeMirror's `.cm-content` padding is `4px 0`) and 6px horizontal\n // (its `.cm-line` padding-left is 6px).\n this.setPadding(new Insets(4, 6, 4, 6));\n // Matches the read-only Markdown viewer's own root-level override\n // (Markdown.ts) so the edited prose reads at the same leading as the\n // preview instead of falling back to the tighter UI-control line-height\n // the theme applies to <html>.\n this.setElementCSSRule(\"lineHeight\", \"var(--ts-ui-md-line-height, 1.8)\");\n\n this.onFirstLayout(() => this._onReady());\n }\n\n /**\n * Replays the cached `contenteditable` state onto the freshly-created\n * element. `setContentEditable` (called during detached construction, before\n * an element exists) caches into `_contentEditable`, and its underlying\n * `setElementAttribute` now also caches into the base class's\n * `_elementAttributes` map and replays it from `Component.init()`, so this\n * replay is redundant — kept anyway for the reason below.\n *\n * The write targets the `element` handle directly — as the base class does\n * for its own cached attributes — because at init time the element is not yet\n * in the document, so a `getElement()`-based setter would miss it.\n *\n * @param element - Optional element to initialise; falls back to `getElement()`.\n * @returns This surface, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element ?? this.getElement();\n\n if (el) {\n DOM.sink.apply(el, { setAttr: { contenteditable: this._contentEditable ? \"true\" : \"false\" } });\n }\n\n return this;\n }\n\n /**\n * Sets whether this surface's element hosts an editable region. Caches the\n * state in `_contentEditable` and writes the `contenteditable` attribute\n * through to the element. The underlying `setElementAttribute` caches and\n * replays the value itself now; {@link WysiwygSurface.init} also replays\n * the cached `_contentEditable` state directly onto the element once it is\n * created, redundantly but for the `getElement()`-timing reason documented\n * there.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This surface, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._contentEditable = contentEditable;\n this.setElementAttribute(\"contenteditable\", contentEditable ? \"true\" : \"false\");\n\n return this;\n }\n\n /**\n * Returns whether this surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._contentEditable;\n }\n\n /**\n * Mounts the given Lexical editor's editable view into this surface's element\n * through the DOM seam's `mountView` escape. No-ops when the view is already\n * mounted, the element is not yet available, or offline (the seam returns\n * `null`, leaving the editor headless).\n *\n * @param editor - The Lexical editor whose view to mount here.\n */\n mount(editor: LexicalEditor): void {\n if (editor.getRootElement()) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n ensureMarkdownEditorClassRules();\n\n // The factory parameter is left unannotated: its `HTMLElement` type is\n // inferred from the seam signature, so no DOM type is named here and the\n // `no-raw-dom` *hold* clause stays green. `setRootElement` accepts the\n // element structurally.\n DOM.sink.mountView(element, (root) => { editor.setRootElement(root); return root; });\n }\n}\n\n/**\n * A WYSIWYG rich-text editor whose public value is a Markdown string, with an\n * optional raw-Markdown source mode.\n *\n * @remarks\n * `MarkdownEditor` edits a document as rendered rich text — no visible markup —\n * and reads/writes Markdown through Lexical's bidirectional converters, so it is\n * the editing counterpart to the read-only\n * [`Markdown`](/api/component/display/classes/Markdown) viewer. Its dialect is\n * deliberately the **exact subset** the viewer renders (headings, paragraphs,\n * bold, italic, inline code, ordered/unordered lists, blockquotes, fenced code,\n * links, and GFM pipe tables with per-column alignment); a curated transformer\n * list — not Lexical's full preset — guarantees the editor can never emit\n * Markdown the viewer would drop to plain text, so an edited document renders\n * identically in the viewer.\n *\n * Formatting is driven three ways, all without a built-in toolbar: Markdown\n * shortcut typing (`# ` → heading, `**b**` → bold, `- ` → list, `> ` → quote,\n * ` ``` ` → code), the default keyboard shortcuts (Ctrl/Cmd+B / +I, undo/redo),\n * and a thin imperative command API (`toggleBold`, `setBlockType`,\n * `toggleUnorderedList`, `toggleLink`, `insertTable`,\n * `insertTableRow`/`deleteTableRow`, `insertTableColumn`/`deleteTableColumn`,\n * …) a consumer can wire to their own `Button`s.\n *\n * A {@link MarkdownEditor.setMode | mode} (`\"wysiwyg\"` | `\"source\"`) swaps the\n * editing surface between that rich-text view and a raw-Markdown\n * [`CodeEditor`](/api/component/editor/classes/CodeEditor); both are bound to the\n * same Markdown value, so `getValue`/`setValue` and the `\"change\"` event behave\n * identically in either mode and switching modes preserves the document. Like the\n * command API, the mode toggle is **consumer-wired** — there is no built-in\n * chrome — so a consumer drives `setMode` from their own control.\n *\n * Internally the component's own element hosts two swappable child surfaces\n * through a [`Card`](/api/layout/classes/Card) layout: a private WYSIWYG surface owning Lexical's\n * `contenteditable` element (a *foreign live widget*, like the `EditorView`\n * behind `CodeEditor`) and the source `CodeEditor`. Lexical separates its editor\n * **state** (a pure, DOM-free immutable tree) from that view, so the Markdown\n * value get/set/round-trip runs headless: offline neither view attaches, yet\n * `getValue` / `setValue`, the command API, and mode switching still operate on\n * the state. The editor fills its host box and scrolls internally; give it a\n * sized host (a `Fit` panel or an explicit `preferredSize`), the same as\n * `CodeEditor`.\n *\n * @example\n * ```typescript\n * import { MarkdownEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new MarkdownEditor('# Title\\n\\nSome **bold** text.');\n * panel.addComponent(editor);\n * editor.on('change', ({ value }) => console.log(value));\n * editor.setMode('source'); // switch to raw-Markdown editing\n * ```\n *\n * @category Components\n */\nclass MarkdownEditor extends Component<MarkdownEditorOptions> {\n\n /**\n * The Lexical editor, built lazily (headless) on first use or first layout;\n * `null` until then. The editor is DOM-free until its root element mounts,\n * so this is non-null and fully usable offline once built.\n */\n private _editor: LexicalEditor | null = null;\n\n /** The `mergeRegister` teardown for the rich-text / list / history / shortcut / update registrations. */\n private _unregister: (() => void) | null = null;\n\n /**\n * The teardown for {@link registerTableSelectionObserver}, registered only\n * once the WYSIWYG view actually mounts (its mutation listener needs a real\n * table element in the document) and cleared on {@link dispose}.\n */\n private _unregisterTableView: (() => void) | null = null;\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<MarkdownEditorEvent> = new ListenerBag<MarkdownEditorEvent>();\n\n /** The Card layout swapping the visible editing surface between WYSIWYG and source. */\n private readonly _card: Card;\n\n /** The WYSIWYG surface hosting Lexical's `contenteditable` view. */\n private readonly _wysiwyg: WysiwygSurface;\n\n /** The raw-Markdown source surface, shown in `\"source\"` mode. */\n private readonly _codeEditor: CodeEditor;\n\n /**\n * Constructs a Markdown editor.\n *\n * @param value - Initial Markdown source (optional; defaults to `\"\"`).\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: MarkdownEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n // The component's own element hosts two swappable surfaces via a Card\n // (one DOM element per class): the WYSIWYG contenteditable and the source\n // CodeEditor. The Card shows exactly one at a time.\n this._card = new Card();\n this.setLayoutManager(this._card);\n\n this._wysiwyg = new WysiwygSurface(() => this.mountWysiwyg());\n this._codeEditor = new CodeEditor(this._options.value ?? \"\", {\n language: \"markdown\",\n readOnly: this._options.readOnly ?? false,\n listeners: { change: (payload) => this.handleCodeChange(payload) },\n });\n\n this.addComponent(this._wysiwyg);\n this.addComponent(this._codeEditor);\n\n // Pick the initial visible child from the mode.\n this._card.setVisibleComponentId(\n this.getMode() === \"source\" ? this._codeEditor.getId() : this._wysiwyg.getId());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Caches the `value` / `readOnly` / `mode` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. The editor\n * is built later (lazily), so these are pure caches applied to the freshly-\n * created editor state / surfaces in the constructor and `ensureEditor`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MarkdownEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n if (options.mode !== undefined) this._options.mode = options.mode;\n\n return this;\n }\n\n /**\n * Returns the active editing surface mode.\n *\n * @returns `\"source\"` when the raw-Markdown surface is shown, else `\"wysiwyg\"`.\n */\n getMode(): MarkdownEditorMode {\n return this._options.mode ?? \"wysiwyg\";\n }\n\n /**\n * Switches the active editing surface. Converts the current document across\n * the surfaces so no edits are lost — the outgoing surface's Markdown is read\n * (via {@link MarkdownEditor.getValue}) before the mode flips, then loaded\n * into the incoming surface — and swaps the visible [`Card`](/api/layout/classes/Card) child.\n * No-op (no conversion, no `\"change\"`) when already in `mode`.\n *\n * @param mode - The surface to show.\n * @returns This component, for method chaining.\n */\n setMode(mode: MarkdownEditorMode): this {\n if (this.getMode() === mode) {\n return this;\n }\n\n // Read the canonical Markdown from the OUTGOING surface before the flip.\n const markdown = this.getValue();\n this._options.mode = mode;\n\n if (mode === \"source\") {\n this._codeEditor.setValue(markdown);\n this._card.setVisibleComponentId(this._codeEditor.getId());\n } else {\n this.ensureEditor().update(\n () => $convertFromMarkdownString(markdown, TRANSFORMERS), { discrete: true });\n this._card.setVisibleComponentId(this._wysiwyg.getId());\n }\n\n return this;\n }\n\n /**\n * Returns the current document as a Markdown string, from whichever surface\n * is active: the raw source text in `\"source\"` mode, else converted live from\n * the Lexical editor state (or the cached pre-mount / offline value).\n *\n * @returns The Markdown source, or `\"\"` when unset.\n */\n getValue(): string {\n if (this.getMode() === \"source\") {\n return this._codeEditor.getValue();\n }\n\n const editor = this._editor;\n\n if (editor) {\n return editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document from a Markdown string, targeting the active\n * surface: the source `CodeEditor` in `\"source\"` mode, else the Lexical\n * editor (building it if needed). A resulting content change fires `\"change\"`.\n *\n * @param value - The new Markdown source.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.setValue(value);\n\n return this;\n }\n\n // Discrete forces a synchronous commit, so the resulting `\"change\"`\n // fires before this returns rather than on a later flush.\n this.ensureEditor().update(() => $convertFromMarkdownString(value, TRANSFORMERS), { discrete: true });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The read-only state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits, on **both** surfaces. Caches the\n * state; when the Lexical editor exists, toggles its editable flag (which\n * also drops the caret / editing affordances on the mounted view); and\n * forwards to the source `CodeEditor`.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n this._editor?.setEditable(!readOnly);\n this._codeEditor.setReadOnly(readOnly);\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the active editing surface, placing a caret\n * ready for typing: the Lexical contenteditable in WYSIWYG mode (building\n * and mounting the editor if needed), or the source `CodeEditor` in\n * `\"source\"` mode.\n *\n * Overrides {@link core!Component.focus}, whose default focuses this component's\n * own host element — the `Card` wrapper — which would leave the caret out of\n * the nested contenteditable the typing must land in. In WYSIWYG mode it\n * defers to Lexical's own `focus`, which both focuses the root element and\n * sets a selection so a caret appears without a click.\n *\n * @param preventScroll - Forwarded to the source `CodeEditor` in `\"source\"`\n * mode; ignored in WYSIWYG mode, where Lexical manages its own focus scroll.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.focus(preventScroll);\n } else {\n this.ensureEditor().focus();\n }\n\n return this;\n }\n\n /**\n * Toggles bold on the current selection. No-op (without throwing) when there\n * is no range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleBold(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"bold\");\n\n return this;\n }\n\n /**\n * Toggles italic on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleItalic(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"italic\");\n\n return this;\n }\n\n /**\n * Toggles inline code on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleInlineCode(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"code\");\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an unordered (bulleted) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleUnorderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an ordered (numbered) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleOrderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Wraps the current selection in a link to `url`, or unwraps it when `url`\n * is `null`. No-op without a range selection.\n *\n * @param url - The link target, or `null` to remove the link.\n * @returns This component, for method chaining.\n */\n toggleLink(url: string | null): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $toggleLink(url);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Converts the selected blocks to `type` (paragraph, `h1`–`h6`, blockquote,\n * or fenced code). No-op without a range selection.\n *\n * @param type - The target block type.\n * @returns This component, for method chaining.\n */\n setBlockType(type: MarkdownBlockType): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => createBlockNode(type));\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a table of `rows` rows by `columns` columns at the caret. The\n * first row is the header row, so `insertTable(2, 3)` gives a header row\n * plus one body row. The caret lands in the first header cell.\n *\n * @param rows - The number of rows, including the header row.\n * @param columns - The number of columns.\n * @returns This component, for method chaining.\n */\n insertTable(rows: number, columns: number): this {\n const editor = this.ensureEditor();\n\n // INSERT_TABLE_COMMAND inserts at the selection; a freshly built editor\n // may have none, so seed one first.\n editor.update(() => {\n if (!$isRangeSelection($getSelection())) {\n $getRoot().selectEnd();\n }\n }, { discrete: true });\n\n editor.dispatchCommand(INSERT_TABLE_COMMAND, {\n columns: String(columns),\n rows: String(rows),\n includeHeaders: { rows: true, columns: false },\n });\n\n return this;\n }\n\n /**\n * Inserts a row after (default) or before the row holding the caret.\n * No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current row.\n * @returns This component, for method chaining.\n */\n insertTableRow(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableRowAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the row holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableRow(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableRowAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a column after (default) or before the column holding the\n * caret. No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current column.\n * @returns This component, for method chaining.\n */\n insertTableColumn(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableColumnAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the column holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableColumn(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableColumnAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the document\n * content changes (typing, a command, or {@link MarkdownEditor.setValue}) in\n * whichever surface is active.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new Markdown value.\n * @returns This component, for method chaining.\n */\n on(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans the `\"change\"` event out to its registered listeners.\n *\n * @param event - Must be `\"change\"`.\n * @param payload - The event payload.\n */\n protected emit(event: MarkdownEditorEvent, payload: MarkdownEditorChange): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Sets whether the WYSIWYG surface's element hosts an editable region.\n * Forwards to the private surface, preserving the public setter.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This component, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._wysiwyg.setContentEditable(contentEditable);\n\n return this;\n }\n\n /**\n * Returns whether the WYSIWYG surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._wysiwyg.getContentEditable();\n }\n\n /**\n * Detaches the Lexical registrations and the WYSIWYG surface's live view,\n * then defers to the base class for the rest of teardown — including\n * disposing the registered `_codeEditor` child, which mirrors\n * `CodeEditor.destructor`. Call before discarding a dynamically-built\n * `MarkdownEditor`.\n */\n protected destructor(): void {\n this._unregisterTableView?.();\n this._unregister?.();\n this._editor?.setRootElement(null);\n\n // `_codeEditor` is registered via `addComponent`, so\n // `super.destructor()`'s child recursion below already disposes it —\n // an explicit call here would run `CodeEditor.destructor()` a second\n // time.\n super.destructor();\n }\n\n /**\n * Builds the headless Lexical editor on first use. Idempotent: the editor is\n * built once. Offline the editor stays headless — the state, and every value\n * / command operation, still work.\n *\n * @returns The live (headless-capable) Lexical editor.\n */\n private ensureEditor(): LexicalEditor {\n if (!this._editor) {\n const editor = createEditor({ nodes: EDITOR_NODES, theme: EDITOR_THEME, onError: reportEditorError });\n\n // Populate the initial state before registering the update listener,\n // so loading the cached value does not fire a spurious `\"change\"`.\n editor.update(() => $convertFromMarkdownString(this._options.value ?? \"\", TRANSFORMERS), { discrete: true });\n editor.setEditable(!(this._options.readOnly ?? false));\n\n this._unregister = mergeRegister(\n registerRichText(editor),\n registerList(editor),\n registerTablePlugin(editor),\n registerTableCellUnmergeTransform(editor),\n registerHistory(editor, createEmptyHistoryState(), HISTORY_DELAY_MS),\n registerMarkdownShortcuts(editor, TRANSFORMERS),\n editor.registerUpdateListener(() => this.handleChange()),\n );\n\n this._editor = editor;\n }\n\n return this._editor;\n }\n\n /**\n * Builds the headless editor (if needed) and mounts its live view into the\n * WYSIWYG surface. Driven by the surface's first connected layout, i.e. the\n * first time it is the visible `Card` child and gets sized.\n */\n private mountWysiwyg(): void {\n const editor = this.ensureEditor();\n\n this._wysiwyg.mount(editor);\n\n // Offline the seam's mountView returns null and the root stays unset;\n // the observer's mutation listener needs a real table element in the\n // document and throws without one, so it is view-time only.\n if (editor.getRootElement() && !this._unregisterTableView) {\n this._unregisterTableView = registerTableSelectionObserver(editor, true);\n }\n }\n\n /**\n * Recomputes the Markdown value from the committed editor state after a\n * Lexical update and, when it differs from the cached value (i.e. the content\n * — not merely the selection — changed), caches it and emits `\"change\"`.\n */\n private handleChange(): void {\n const editor = this._editor;\n\n if (!editor) {\n return;\n }\n\n const value = editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n\n if (value === this._options.value) {\n return;\n }\n\n this._options.value = value;\n this.emit(\"change\", { value });\n }\n\n /**\n * Recomputes the cached value from a source-surface (`CodeEditor`) edit and,\n * when it differs from the cached value, caches it and emits `\"change\"`. The\n * equality guard makes the programmatic `setValue` on a mode switch not\n * double-emit (it loads the value already equal to `_options.value`).\n *\n * @param payload - The `CodeEditor` change payload (the new source text).\n */\n private handleCodeChange(payload: CodeEditorChange): void {\n if (payload.value === this._options.value) {\n return;\n }\n\n this._options.value = payload.value;\n this.emit(\"change\", { value: payload.value });\n }\n}\n\nconst MarkdownEditorCallable = callable(MarkdownEditor);\ntype MarkdownEditorCallable = MarkdownEditor;\nexport {\n MarkdownEditor as _MarkdownEditor,\n MarkdownEditorCallable as MarkdownEditor,\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"../DOM-C-Edb3tA.js";import{a as t,l as n,n as r,s as i}from"../Position-BwtxouxK.js";import{S as a,b as o,h as s,t as c,v as l}from"../Component-B0ShfPVi.js";import{t as u}from"../ListenerBag-WPdKuuds.js";import{c as d}from"../Scrollbar-t8fFrUVQ.js";import{t as f}from"../Glyph-_VeBacS2.js";import{t as p}from"../AnimatedDropdown-H3Rh-YMZ.js";import{t as m}from"../Fit-BhlB-HAo.js";import{t as h}from"../Text-CMKnK9uB.js";import{t as g}from"../AbstractInput-sRNPzWel.js";import{n as _,t as ee}from"../Checkbox-D0JYK-wc.js";import{n as v,r as y}from"../VBox-DfJPWXqc.js";import{t as b}from"../RadioButton-BEwpqg1k.js";import{t as x}from"../Button-MpMIYBYJ.js";import{n as S,t as C}from"../Slider-CTbwR6HR.js";import{a as w,c as te,d as T,f as E,h as D,i as O,l as k,m as A,n as j,o as M,p as N,r as P,s as F,t as I,u as L}from"../ComboBox-Dx8Tk9yg.js";import{calendar as R}from"../glyphs/solid/calendar.es.js";import{t as z}from"../List-nx8220Fi.js";import{chevron_down as B}from"../glyphs/solid/chevron_down.es.js";import{chevron_up as V}from"../glyphs/solid/chevron_up.es.js";f.register(R);var DateField=class extends L{constructor(e){super(e),this._button.setGlyph(`calendar`),this._options.value!==void 0&&this.setValue(this._options.value)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.minDate!==void 0&&(this._options.minDate=e.minDate),e.maxDate!==void 0&&(this._options.maxDate=e.maxDate),this}formatValue(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}parseRaw(e){let t=new Date(e+`T00:00:00`);return isNaN(t.getTime())?null:t}createDropdown(){return new w(e=>this.onDropdownSelected(e),{minDate:this._options.minDate??null,maxDate:this._options.maxDate??null})}onDropdownSelected(e){this.setValue(e),this.closeDropdown(),this._input.focus(),a.fireEvent(this._input,`input`)}getPreferredWidth(){return 160}},H=s(DateField);f.register(R);var DateTimeField=class extends L{constructor(e){super(e),this._button.setGlyph(`calendar`),this._options.showSeconds!==void 0&&(this._showSeconds=this._options.showSeconds),this._options.value!==void 0&&this.setValue(this._options.value)}applyOptions(e){return super.applyOptions(e),e.showSeconds!==void 0&&(this._options.showSeconds=e.showSeconds),e.value!==void 0&&(this._options.value=e.value),e.minDate!==void 0&&(this._options.minDate=e.minDate),e.maxDate!==void 0&&(this._options.maxDate=e.maxDate),this}formatValue(e){let t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,`0`),r=String(e.getDate()).padStart(2,`0`),i=String(e.getHours()).padStart(2,`0`),a=String(e.getMinutes()).padStart(2,`0`);return this._showSeconds?`${t}-${n}-${r} ${i}:${a}:${String(e.getSeconds()).padStart(2,`0`)}`:`${t}-${n}-${r} ${i}:${a}`}parseRaw(e){let[t,n]=e.trim().split(/\s+/);if(!t||!n)return null;let r=new Date(`${t}T${n}`);return isNaN(r.getTime())?null:r}createDropdown(){return new j(e=>this.onDropdownSelected(e),{showSeconds:this._showSeconds,minDate:this._options.minDate??null,maxDate:this._options.maxDate??null})}onDropdownSelected(e){this.setValue(e),a.fireEvent(this._input,`input`)}getPreferredWidth(){return 200}},U=s(DateTimeField),W={padding:new r(3,3,3,3),cursor:`text`,backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},PasswordField=class extends A{constructor(e,t){super(e,{...W,...t??{}}),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.setType(`password`),this._options.name===void 0&&this.setName(`password`),this._options.autoComplete===void 0&&this.setAutoComplete(e?.newPassword?`new-password`:`current-password`)}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this.getPadding(),this.getBorderSize()),t=this.getPreferredSizeConstraint()?.width??200;this.setPreferredSize({width:t,height:e});let r=this.getMaxSizeConstraint()?.width??2**53-1;this.setMaxSize({width:r,height:e});let i=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:i,height:e})}},ne=s(PasswordField),G={cursor:`text`,padding:new r(3,3,3,3),backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},UsernameField=class extends A{constructor(e,t){super(e,{...G,...t??{}}),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.setType(`text`),this._options.name===void 0&&this.setName(`username`),this._options.autoComplete===void 0&&this.setAutoComplete(e?.email?`email`:`username`)}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this.getPadding(),this.getBorderSize()),t=this.getPreferredSizeConstraint()?.width??200;this.setPreferredSize({width:t,height:e});let r=this.getMaxSizeConstraint()?.width??2**53-1;this.setMaxSize({width:r,height:e});let i=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:i,height:e})}},K=s(UsernameField),q={tag:`textarea`,cursor:`text`,overflow:`auto`,padding:new r(3,3,3,3),preferredSize:{width:200,height:200},minSize:{width:100,height:100},backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},TextArea=class extends A{constructor(e=``,t,n){super(e?{text:e,...t}:t,{...q,...n??{}}),this.setElementCSSRules({resize:`none`})}applyOptions(e){return super.applyOptions(e),e.rows!==void 0&&this.setRows(e.rows),e.cols!==void 0&&this.setCols(e.cols),e.wrap!==void 0&&this.setWrap(e.wrap),this}getRows(){return this._options.rows??null}setRows(e){return this._options.rows=e,this.setElementAttribute(`rows`,String(e)),this}clearRows(){return this._options.rows===void 0?this:(this._options.rows=void 0,this.removeElementAttribute(`rows`),this)}getCols(){return this._options.cols??null}setCols(e){return this._options.cols=e,this.setElementAttribute(`cols`,String(e)),this}clearCols(){return this._options.cols===void 0?this:(this._options.cols=void 0,this.removeElementAttribute(`cols`),this)}getWrap(){return this._options.wrap??null}setWrap(e){return this._options.wrap=e,this.setElementAttribute(`wrap`,e),this}clearWrap(){return this._options.wrap===void 0?this:(this._options.wrap=void 0,this.removeElementAttribute(`wrap`),this)}getBaseline(){return this.wrapInnerBaseline(n.measureTextBaseline())}init(t){super.init(t);let n=t||this.getElement();return this._options.rows!==void 0&&e.sink.apply(n,{setAttr:{rows:String(this._options.rows)}}),this._options.cols!==void 0&&e.sink.apply(n,{setAttr:{cols:String(this._options.cols)}}),this._options.wrap!==void 0&&e.sink.apply(n,{setAttr:{wrap:this._options.wrap}}),this}},J=s(TextArea),Y={outline:`none`},Toggle=class extends _{_track;_thumb;constructor(e,t){super({...e??{}},{...Y,...t??{}}),this.setLayoutManager(new y),this._track=new c,this._track.setBackgroundColor(`var(--ts-ui-toggle-track-bg-off, rgb(200, 200, 200))`),this._track.setBorderRadius(`999px`),this._track.setPreferredSize({width:36,height:20}),this._track.setMinSize({width:36,height:20}),this._track.setMaxSize({width:36,height:20}),this._track.setCursor(`pointer`),this._thumb=new c,this._thumb.setBackgroundColor(`var(--ts-ui-toggle-thumb-bg, rgb(255, 255, 255))`),this._thumb.setBorderRadius(`999px`),this._thumb.setPreferredSize({width:16,height:16}),this._thumb.setMaxSize({width:16,height:16}),this._thumb.setSize({width:16,height:16}),this._thumb.setShadow(`0 1px 2px rgba(0, 0, 0, 0.25)`),this._thumb.setX(2),this._thumb.setY(2),this._thumb.setPointerEvents(`none`),l.isReducedMotion()||(this._thumb.setTransition(`transform 120ms ease-out`),this._track.setTransition(`background-color 120ms ease-out`)),this._track.addComponent(this._thumb),super.addComponent(this._track),this.getAria().setRole(`switch`),this.getAria().setTabIndex(0),this.getAria().setChecked(!1),a.addListener(this._track,`click`,()=>this.activateFromPointer()),this.installKeyboard(),this._options.value!==void 0&&this.applyValue(this._options.value),this._options.label!==void 0&&this.applyLabel(this._options.label),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.label!==void 0&&(this._options.label=e.label),e.enabled!==void 0&&(this._options.enabled=e.enabled),e.readOnly!==void 0&&(this._options.readOnly=e.readOnly),this}activate(){this.setValue(!this.getValue())}getInteractiveSurface(){return this._track}activationKeys(){return[` `,`Enter`]}getValue(){return this._options.value??!1}setValue(e){let t=!!e;return t===this.getValue()?this:(this._options.value=t,this.applyValue(t),this.notifyChange(t),this)}clearValue(){return this.setValue(!1)}getBaseline(){let e=this._label;if(e===null)return null;let t=e.getBaseline();return t===null?null:this.wrapInnerBaseline(t+this.labelCenterOffset())}labelCenterOffset(){if(this._label===null)return 0;let e=this._track.getPreferredSize()?.height??0,t=this._label.getPreferredSize()?.height??e;return Math.max(0,Math.round((e-t)/2))}doLayout(){super.doLayout();let e=this._label,t=this.labelCenterOffset();return e!==null&&t>0&&e.setY(e.getY()+t),this}applyValue(e){this.getAria().setChecked(e),this._thumb.setTransform(e?`translateX(16px)`:`translateX(0px)`),this._track.setBackgroundColor(e?`var(--ts-ui-toggle-track-bg-on, rgb(30, 100, 200))`:`var(--ts-ui-toggle-track-bg-off, rgb(200, 200, 200))`)}},X=s(Toggle),Z=22,re={durationMs:100,backgroundColor:`var(--ts-ui-autocomplete-bg, rgb(255, 255, 255))`,border:`var(--ts-ui-input-border)`,borderRadius:`var(--ts-ui-border-radius, 4px)`,shadow:`var(--ts-ui-autocomplete-shadow, 2px 4px 8px rgba(0,0,0,0.15))`};new o({scope:`selector`,name:`.AutoCompleteDropdown .List:focus::after`,styles:{content:`none`}});var AutoCompleteDropdown=class extends p{_list;_onSelect;_onHide;constructor(e,t,n,r){super(n,{...re,...r??{}}),this._onSelect=e,this._onHide=t,this.setContain(`layout`),this.setLayoutManager(new m),this._list=new z,this._list.setBorder(`none`),this._list.setBorderRadius(`0`),this._list.setFocusOnRowClick(!1),this._list.setSelectFollowsFocus(!1),this.addComponent(this._list),this._list.on(`action`,()=>{let e=this._list.getValue();e&&this._onSelect(e)})}show(t,n){this.getElement(!0),this.pauseLayout(),this._list.setItemsArray(n.map(e=>({key:e,label:e}))),this.resumeLayout();let r=this.getPerimeterSize(),i=r.top+r.bottom,a=e.source.getElementRect(t);return this.setWidth(a.width),this.setHeight(n.length*Z+i),this.placeAnchored(a),this.setAnchorElement(t),this.showAnimated(),this.doLayout(),this}hide(){return this.hideAnimated(),this}handleKey(e){return this._list.handleKey(e)}getFocusedRowId(){return this._list.getFocusedRowId()}getList(){return this._list}onHideComplete(){this._onHide()}},ie=s(AutoCompleteDropdown);T(`.AutoCompleteField`);var AutoCompleteField=class extends g{_textField;_dropdown;_debounceTimer=null;_selectBag=new u;constructor(e){super(),this.setBorder(`var(--ts-ui-input-border)`),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this._textField=new k,this._textField.setBorder(`none`),this._textField.setBorderRadius(`0`),this._textField.setOutline(`none`),this.addComponent(this._textField),this.syncSizeFromTextField(),this.subscribeTheme(()=>this.syncSizeFromTextField()),this._dropdown=new ie(e=>this.onSuggestionSelected(e),()=>this.onDropdownHidden()),this._textField.getAria().setRole(`combobox`),this._textField.getAria().setAutoComplete(`list`),this._textField.getAria().setExpanded(!1),this._textField.getAria().setControls(this._dropdown.getId()),this._textField.getAria().setActiveDescendant(``),a.addListener(this._textField,`input`,()=>this.onInput()),a.addListener(this._textField,`keydown`,e=>this.onKeyDown(e)),a.addListener(this._textField,`blur`,()=>this.onBlur()),this._textField.on(`change`,e=>this.notifyChange(e)),e&&this.applyOptions(e),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.suggestions!==void 0&&this.setSuggestions(e.suggestions),e.minChars!==void 0&&this.setMinChars(e.minChars),e.debounceMs!==void 0&&this.setDebounceMs(e.debounceMs),e.maxSuggestions!==void 0&&this.setMaxSuggestions(e.maxSuggestions),e.matchMode!==void 0&&this.setMatchMode(e.matchMode),e.placeholder!==void 0&&this.setPlaceholder(e.placeholder),e.store!==void 0&&e.displayField!==void 0?this.setStore(e.store,e.displayField):(e.store!==void 0&&(this._options.store=e.store),e.displayField!==void 0&&(this._options.displayField=e.displayField)),this}syncSizeFromTextField(){let e=this.getPerimeterSize(),t=e.left+e.right,n=e.top+e.bottom,r=this._textField.getPreferredSize(),a=this._textField.getMaxSize(),o=this._textField.getMinSize();r&&this.setPreferredSize({width:r.width,height:r.height+n}),a&&this.setMaxSize({width:i(a.width+t),height:i(a.height+n)}),o&&this.setMinSize({width:o.width,height:o.height+n})}getBaseline(){return this.wrapInnerBaseline(this._textField.getBaseline())}doLayout(){super.doLayout();let e=this.getContentBounds();return e?(this._textField.setX(e.x),this._textField.setY(e.y),this._textField.setWidth(e.width),this._textField.setHeight(e.height),this):this}setValue(e){return this._textField.setValue(e),this}getValue(){return this._textField.getValue()}applyEnabled(e){this._textField.setEnabled(e)}applyReadOnly(e){this._textField.setReadOnly(e)}setSuggestions(e){return this._options.suggestions=e,this}setStore(e,t){return this._options.store=e,this._options.displayField=t,this}setMinChars(e){return this._options.minChars=e,this}setDebounceMs(e){return this._options.debounceMs=e,this}setMaxSuggestions(e){return this._options.maxSuggestions=e,this}setMatchMode(e){return this._options.matchMode=e,this}setPlaceholder(e){return this._options.placeholder=e,this._textField.setPlaceholder(e),this}on(e,t){return e===`select`?(this._selectBag.add(`select`,t),this):super.on(e,t)}off(e,t){return e===`select`?(this._selectBag.remove(`select`,t),this):super.off(e,t)}addSelectListener(e){this.on(`select`,e)}onInput(){this._debounceTimer!==null&&clearTimeout(this._debounceTimer);let e=this._options.minChars??1,t=this.getValue();if(t.length<e){this._dropdown.hide();return}this._debounceTimer=setTimeout(()=>this.querySuggestions(t),this._options.debounceMs??200)}onKeyDown(e){if(this._dropdown.isOpen()&&(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`)&&this._dropdown.handleKey(e))return this.updateActiveDescendant(),{prevent:!0};switch(e.key){case`ArrowDown`:return this.querySuggestions(this.getValue()),{prevent:!0};case`Escape`:this._dropdown.hide(),this._textField.focus();break;case`Tab`:this._dropdown.hide()}}onBlur(){setTimeout(()=>{let t=e.source.getActiveElement(),n=this._dropdown.getElement();n&&e.source.contains(n,t)||this._dropdown.hide()},150)}matches(e,t){let n=this._options.matchMode??`contains`,r=n===`containsCaseSensitive`||n===`startsWithCaseSensitive`,i=n===`startsWith`||n===`startsWithCaseSensitive`,a=r?e:e.toLowerCase(),o=r?t:t.toLowerCase();return i?a.startsWith(o):a.includes(o)}querySuggestions(e){let t=this._options.maxSuggestions??10,n=this._options.matchMode??`contains`,r=this._options.suggestions,i=this._options.store,a=this._options.displayField;if(r!==void 0){let n=r.filter(t=>this.matches(t,e)).slice(0,t);e===this.getValue()&&this.showSuggestions(n);return}if(i!==void 0&&a!==void 0){let r=n===`containsCaseSensitive`||n===`startsWithCaseSensitive`,o=n===`startsWith`||n===`startsWithCaseSensitive`?`startsWith`:`contains`;i.clearFilter(),i.filterBy({type:o,field:a,value:e,caseSensitive:r});let s=i.getRecords().map(e=>String(e.get(a))).slice(0,t);e===this.getValue()&&this.showSuggestions(s)}}showSuggestions(e){if(e.length===0){this._dropdown.hide();return}this._dropdown.show(this._textField.getElement(!0),e),this._textField.getAria().setExpanded(!0)}onDropdownHidden(){this._textField.getAria().setExpanded(!1),this._textField.getAria().setActiveDescendant(``)}updateActiveDescendant(){this._textField.getAria().setActiveDescendant(this._dropdown.getFocusedRowId()??``)}onSuggestionSelected(e){this.setValue(e),this._selectBag.fire(`select`,e),this._dropdown.hide(),this._textField.focus()}},ae=s(AutoCompleteField);f.register(V,B);var oe={border:`none`,borderRadius:`0`,insets:new r(0,0,0,0)},SpinButton=class extends x{_listeners=new u;_repeat;constructor(e,t,n){super(void 0,t,{...oe,glyph:e===`▲`?`chevron-up`:`chevron-down`,...n??{}}),this.updateSize(),this.subscribeTheme(()=>this.updateSize()),t?.shadow===void 0&&this.clearShadow(),t?.pressedShadow===void 0&&this.clearPressedShadow(),this.pinGlyphSize(8),this.getGlyph()?.setTranslate(0,-1),this._repeat=new d({initialDelay:400,decay:.75,floor:40,onTick:()=>this.emit(`tick`)}),a.addListener(this,`mousedown`,()=>this.onMouseDown()),a.addViewportListener(this,`mouseup`,()=>this.onMouseUp()),a.addViewportListener(this,`mouseleave`,()=>this.onMouseUp()),this.applyListeners(t?.listeners)}updateSize(){let e=n.lineHeightPx()+6+2,t=Math.floor((e-2)/2);this.setPreferredSize({width:18,height:t}),this.setMinSize({width:18,height:t}),this.setMaxSize({width:18,height:t})}on(e,t){return e===`action`?a.addListener(this,`click`,t):this._listeners.add(e,t),this}off(e,t){return e===`action`?a.removeListener(this,`click`,t):this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}onMouseDown(){this._repeat.start()}onMouseUp(){this._repeat.isRunning()&&this._repeat.stop()}},Q=s(SpinButton);T(`.NumberSpinner`),new o({scope:`selector`,name:`.NumberSpinner .TextField:focus`,styles:{boxShadow:`none`}});var se={insets:new r(0,0,0,0),border:`1px solid var(--ts-ui-button-border, rgb(200, 200, 200))`,borderRadius:`var(--ts-ui-border-radius, 4px)`,backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`},NumberSpinner=class extends g{_input;_upBtn;_downBtn;_btnBox;constructor(e,t){super(e,{...se,...t??{}}),this._input=new k,this._input.setTextAlign(`right`),this._input.setBorder(`none`),this._input.setBorderRadius(`0`),this._input.setOutline(`none`),this._input.setText(this.formatValue(0)),this._upBtn=new Q(`▲`),this._downBtn=new Q(`▼`),this._upBtn.setBorder({borderTop:`1px solid transparent`}),this._upBtn.setBorderRadius(`0`),this._downBtn.setBorder({borderTop:`1px solid var(--ts-ui-spinner-divider, rgb(180, 180, 180))`}),this._downBtn.setBorderRadius(`0`),this._btnBox=new c;let n=new v;n.setComponentSpacing(0),this._btnBox.setLayoutManager(n),this._btnBox.setInsets(new r(0,0,0,0)),this._btnBox.addComponent(this._upBtn),this._btnBox.addComponent(this._downBtn);let i=new y;i.setComponentSpacing(0),i.setStretching(!0),this.setLayoutManager(i),this.addComponent(this._input,{weight:1}),this.addComponent(this._btnBox),this._upBtn.on(`tick`,()=>this.applyValue(this.getValue()+this.getStep())),this._downBtn.on(`tick`,()=>this.applyValue(this.getValue()-this.getStep())),a.addListener(this._input,`blur`,()=>this.onBlur()),a.addListener(this._input,`keydown`,e=>this.onKeyDown(e)),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.getAria().setRole(`spinbutton`),this.getAria().setValueNow(0),this._options.min!==void 0&&this.setMin(this._options.min),this._options.max!==void 0&&this.setMax(this._options.max),this._options.step!==void 0&&this.setStep(this._options.step),this._options.precision!==void 0&&this.setPrecision(this._options.precision),this._options.value!==void 0&&this.setValue(this._options.value),this._options.enabled!==void 0&&this.setEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.setReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.min!==void 0&&(this._options.min=e.min),e.max!==void 0&&(this._options.max=e.max),e.step!==void 0&&(this._options.step=e.step),e.precision!==void 0&&(this._options.precision=e.precision),e.value!==void 0&&(this._options.value=e.value),e.enabled!==void 0&&(this._options.enabled=e.enabled),this}getBaseline(){return this.wrapInnerBaseline(this._input.getBaseline())}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this._input.getPadding(),this.getBorderSize()),r=this.getPreferredSizeConstraint()?.width??120;this.setPreferredSize({width:r,height:e});let i=this.getMaxSizeConstraint()?.width??t;this.setMaxSize({width:i,height:e});let a=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:a,height:e})}getValue(){return this._options.value??0}setValue(e){return this._setValueSilent(e),this}getMin(){return this._options.min??-1/0}setMin(e){return this._options.min=e,this.getAria().setValueMin(isFinite(e)?e:null),this}getMax(){return this._options.max??1/0}setMax(e){return this._options.max=e,this.getAria().setValueMax(isFinite(e)?e:null),this}getStep(){return this._options.step??1}setStep(e){return this._options.step=e,this}getPrecision(){return this._options.precision??null}setPrecision(e){return this._options.precision=e,this._input.setText(this.formatValue(this.getValue())),this}applyEnabled(e){e?(this._input.setDisabledAttribute(!1),this._upBtn.setPointerEvents(`auto`),this._downBtn.setPointerEvents(`auto`),this.clearOpacity()):(this._input.setDisabledAttribute(!0),this._upBtn.setPointerEvents(`none`),this._downBtn.setPointerEvents(`none`),this.setOpacity(.5))}applyReadOnly(e){this._input.setReadOnly(e)}applyValue(e){if(!this.isEnabled())return;let t=this.normalize(e);if(t===this.getValue()){this._input.setText(this.formatValue(t));return}this._options.value=t,this._input.setText(this.formatValue(t)),this.getAria().setValueNow(t),this.notifyChange(t)}_setValueSilent(e){let t=this.normalize(e);this._options.value=t,this._input.setText(this.formatValue(t)),this.getAria().setValueNow(t)}normalize(e){let t=Math.min(this.getMax(),Math.max(this.getMin(),e));return t=Math.round(t/this.getStep())*this.getStep(),t=parseFloat(t.toFixed(this.derivePrecision())),t}onBlur(){let e=parseFloat(this._input.getText().valueOf());if(isNaN(e)){this._input.setText(this.formatValue(this.getValue()));return}this.applyValue(e)}onKeyDown(e){if(e.key===`ArrowUp`)return this.applyValue(this.getValue()+this.getStep()),{prevent:!0};if(e.key===`ArrowDown`)return this.applyValue(this.getValue()-this.getStep()),{prevent:!0};e.key===`Enter`&&this.onBlur()}formatValue(e){return e.toFixed(this.derivePrecision())}derivePrecision(){let e=this._options.precision??null;if(e!==null)return e;let t=String(this.getStep()),n=t.indexOf(`.`);return n>=0?t.length-n-1:0}},ce=s(NumberSpinner),le=3,$=`no file selected`,HiddenFileInput=class extends c{_type=null;_multiple=!1;_accept=null;constructor(){super({tag:`input`}),this.setDisplayed(!1),this.setType(`file`)}getType(){return this._type}setType(e){this._type=e,this.setElementAttribute(`type`,e)}setMultipleAttribute(e){this._multiple=e,e?this.setElementAttribute(`multiple`,``):this.removeElementAttribute(`multiple`)}setAcceptAttribute(e){this._accept=e,this.setElementAttribute(`accept`,e)}setDisabled(e){this.setDisabledAttribute(e)}openPicker(){let t=this.getElement();t&&e.sink.click(t)}getFiles(){let t=this.getElement();return t?e.source.getFiles(t):null}clearFiles(){let t=this.getElement();t&&e.sink.setValue(t,``)}init(t){super.init(t);let n=t??this.getElement();return n?(this._type!==null&&e.sink.apply(n,{setAttr:{type:this._type}}),this._multiple&&e.sink.apply(n,{setAttr:{multiple:``}}),this._accept!==null&&e.sink.apply(n,{setAttr:{accept:this._accept}}),this):this}},FileField=class extends g{_button;_label;_input;_droppedFiles=[];constructor(e){super({...e??{}}),this.setLayoutManager(new y),this._button=new x(this.defaultButtonText()),this._button.on(`action`,()=>this.openPicker()),this._label=new h($,{truncate:!0}),this._label.setForegroundColor(`var(--ts-ui-form-text, inherit)`),this._input=new HiddenFileInput,a.addListener(this._input,`change`,()=>this.onNativeChange()),super.addComponent(this._button),super.addComponent(this._label,{weight:1}),super.addComponent(this._input),this._options.multiple!==void 0&&this.setMultiple(this._options.multiple),this._options.accept!==void 0&&this.setAccept(this._options.accept),this._options.buttonText!==void 0&&this._button.setText(this._options.buttonText),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.multiple!==void 0&&(this._options.multiple=e.multiple),e.accept!==void 0&&(this._options.accept=e.accept),e.buttonText!==void 0&&(this._options.buttonText=e.buttonText),this}defaultButtonText(){return this.isMultiple()?`Choose files…`:`Choose file…`}openPicker(){!this.isEnabled()||this.isReadOnly()||this._input.openPicker()}onNativeChange(){this._droppedFiles=[];let e=this.getValue();this.updateLabel(e),this.notifyChange(e)}acceptDroppedFiles(e){this._droppedFiles=Array.from(e);let t=this.getValue();this.updateLabel(t),this.notifyChange(t)}getValue(){if(this._droppedFiles.length>0)return this._droppedFiles;let e=this._input.getFiles();return e?Array.from(e):[]}setValue(e){return e&&e.length>0?(console.warn(`FileField '`+this.getId()+`' setValue ignored a non-empty File[]: the browser security model forbids programmatically populating a file input. Only setValue([]) (clearing) is supported.`),this):this.clearValue()}clearValue(){this._droppedFiles=[],this._input.clearFiles();let e=this.getValue();return this.updateLabel(e),this.notifyChange(e),this}isMultiple(){return this._options.multiple??!1}setMultiple(e){return this._options.multiple=!!e,this._input.setMultipleAttribute(this._options.multiple),this._options.buttonText===void 0&&this._button.setText(this.defaultButtonText()),this}getAccept(){return this._options.accept??null}setAccept(e){return this._options.accept=e,this._input.setAcceptAttribute(e),this}updateLabel(e){e.length===0?this._label.setText($):e.length===1?this._label.setText(e[0].name):e.length>le?this._label.setText(e.length+` files`):this._label.setText(e.map(e=>e.name).join(`, `))}applyEnabled(e){this._button.setEnabled(e),this._input.setDisabled(!e)}applyReadOnly(e){this._button.setEnabled(!e&&this.isEnabled()),this.getAria().setReadOnly(e)}},ue=s(FileField),de=`var(--ts-ui-filedropzone-border, 2px dashed rgba(80, 140, 240, 0.40))`,fe=`var(--ts-ui-filedropzone-bg, rgba(80, 140, 240, 0.06))`,pe=`var(--ts-ui-filedropzone-active-border, 2px dashed rgba(80, 140, 240, 0.80))`,me=`var(--ts-ui-filedropzone-active-bg, rgba(80, 140, 240, 0.18))`,he=`Drop files here or click to browse`,FileDropZone=class extends g{_prompt;_field;_dragDepth=0;constructor(e){super({...e??{}}),this.setLayoutManager(new v),this._prompt=new h(this._options.promptText??he),this._prompt.setForegroundColor(`var(--ts-ui-form-text, inherit)`),this._field=new FileField(this.fieldOptions()),this._field.on(`change`,e=>this.notifyChange(e)),super.addComponent(this._prompt),super.addComponent(this._field),this.setActive(!1),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),a.addSubtreeListener(this,`dragenter`,{prevent:!0,handler:this.onDragEnter}),a.addSubtreeListener(this,`dragover`,{prevent:!0,handler:this.onDragOver}),a.addSubtreeListener(this,`dragleave`,{prevent:!0,handler:this.onDragLeave}),a.addSubtreeListener(this,`drop`,{prevent:!0,handler:this.onDrop}),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.promptText!==void 0&&(this._options.promptText=e.promptText),this}fieldOptions(){return{multiple:this._options.multiple,accept:this._options.accept,buttonText:this._options.buttonText}}setActive(e){this.setBorder(e?pe:de),this.setBackgroundColor(e?me:fe)}onDragEnter(e){this._dragDepth++,this._dragDepth===1&&this.setActive(!0)}onDragOver(e){}onDragLeave(e){this._dragDepth=Math.max(0,this._dragDepth-1),this._dragDepth===0&&this.setActive(!1)}onDrop(e){if(this._dragDepth=0,this.setActive(!1),!this.isEnabled()||this.isReadOnly())return;let t=e.dataTransfer?.files;!t||t.length===0||this._field.acceptDroppedFiles(t)}getValue(){return this._field.getValue()}setValue(e){return this._field.setValue(e),this}clearValue(){return this._field.clearValue(),this}applyEnabled(e){this._field.setEnabled(e)}applyReadOnly(e){this._field.setReadOnly(e)}},ge=s(FileDropZone);export{_ as AbstractBooleanInput,g as AbstractInput,L as AbstractPickerField,ae as AutoCompleteField,ee as Checkbox,I as ComboBox,H as DateField,w as DatePickerDropdown,U as DateTimeField,j as DateTimePickerDropdown,ge as FileDropZone,ue as FileField,S as Label,D as Link,ce as NumberSpinner,ne as PasswordField,E as PickerButton,M as PickerCell,F as PickerCellList,te as PickerColumn,N as PickerInput,b as RadioButton,C as Slider,Q as SpinButton,h as Text,J as TextArea,k as TextField,A as TextInput,P as TimeField,O as TimePickerDropdown,X as Toggle,K as UsernameField};
1
+ import{t as e}from"../DOM-Cjh7_7TF.js";import{a as t,l as n,n as r,s as i}from"../Position-BIEU1Eze.js";import{S as a,b as o,h as s,t as c,v as l}from"../Component-D99-15Zt.js";import{t as u}from"../ListenerBag-WPdKuuds.js";import{c as d}from"../Scrollbar-DcnueNJd.js";import{t as f}from"../Glyph-DiEpD3gy.js";import{t as p}from"../AnimatedDropdown-D0RhVgv3.js";import{t as m}from"../Fit-CaPMj7Up.js";import{t as h}from"../Text-O-FWa5Cy.js";import{t as g}from"../AbstractInput-W2PLJV5_.js";import{n as _,t as ee}from"../Checkbox-CM1z8keM.js";import{n as v,r as y}from"../VBox-DVe4UXwX.js";import{t as b}from"../RadioButton-Df5J-H6m.js";import{t as x}from"../Button-CnFAiQuV.js";import{t as S}from"../SelectableText-DZNxjKXR.js";import{n as C,t as w}from"../Slider-DNfd8Lxz.js";import{a as T,c as te,d as E,f as ne,h as re,i as ie,l as D,m as O,n as k,o as A,p as j,r as M,s as N,t as P,u as F}from"../ComboBox-PZ5YPL0X.js";import{calendar as I}from"../glyphs/solid/calendar.es.js";import{t as L}from"../List-CQbZK_pT.js";import{chevron_down as R}from"../glyphs/solid/chevron_down.es.js";import{chevron_up as z}from"../glyphs/solid/chevron_up.es.js";f.register(I);var DateField=class extends F{constructor(e){super(e),this._button.setGlyph(`calendar`),this._options.value!==void 0&&this.setValue(this._options.value)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.minDate!==void 0&&(this._options.minDate=e.minDate),e.maxDate!==void 0&&(this._options.maxDate=e.maxDate),this}formatValue(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}parseRaw(e){let t=new Date(e+`T00:00:00`);return isNaN(t.getTime())?null:t}createDropdown(){return new T(e=>this.onDropdownSelected(e),{minDate:this._options.minDate??null,maxDate:this._options.maxDate??null})}onDropdownSelected(e){this.setValue(e),this.closeDropdown(),this._input.focus(),a.fireEvent(this._input,`input`)}getPreferredWidth(){return 160}},B=s(DateField);f.register(I);var DateTimeField=class extends F{constructor(e){super(e),this._button.setGlyph(`calendar`),this._options.showSeconds!==void 0&&(this._showSeconds=this._options.showSeconds),this._options.value!==void 0&&this.setValue(this._options.value)}applyOptions(e){return super.applyOptions(e),e.showSeconds!==void 0&&(this._options.showSeconds=e.showSeconds),e.value!==void 0&&(this._options.value=e.value),e.minDate!==void 0&&(this._options.minDate=e.minDate),e.maxDate!==void 0&&(this._options.maxDate=e.maxDate),this}formatValue(e){let t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,`0`),r=String(e.getDate()).padStart(2,`0`),i=String(e.getHours()).padStart(2,`0`),a=String(e.getMinutes()).padStart(2,`0`);return this._showSeconds?`${t}-${n}-${r} ${i}:${a}:${String(e.getSeconds()).padStart(2,`0`)}`:`${t}-${n}-${r} ${i}:${a}`}parseRaw(e){let[t,n]=e.trim().split(/\s+/);if(!t||!n)return null;let r=new Date(`${t}T${n}`);return isNaN(r.getTime())?null:r}createDropdown(){return new k(e=>this.onDropdownSelected(e),{showSeconds:this._showSeconds,minDate:this._options.minDate??null,maxDate:this._options.maxDate??null})}onDropdownSelected(e){this.setValue(e),a.fireEvent(this._input,`input`)}getPreferredWidth(){return 200}},V=s(DateTimeField),H={padding:new r(3,3,3,3),cursor:`text`,backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},PasswordField=class extends O{static ownClassStyleDefaults=H;constructor(e,t){super(e,{...H,...t??{}}),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.setType(`password`),this._options.name===void 0&&this.setName(`password`),this._options.autoComplete===void 0&&this.setAutoComplete(e?.newPassword?`new-password`:`current-password`)}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this.getPadding(),this.getBorderSize()),t=this.getPreferredSizeConstraint()?.width??200;this.setPreferredSize({width:t,height:e});let r=this.getMaxSizeConstraint()?.width??2**53-1;this.setMaxSize({width:r,height:e});let i=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:i,height:e})}},U=s(PasswordField),W={cursor:`text`,padding:new r(3,3,3,3),backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},UsernameField=class extends O{static ownClassStyleDefaults=W;constructor(e,t){super(e,{...W,...t??{}}),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.setType(`text`),this._options.name===void 0&&this.setName(`username`),this._options.autoComplete===void 0&&this.setAutoComplete(e?.email?`email`:`username`)}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this.getPadding(),this.getBorderSize()),t=this.getPreferredSizeConstraint()?.width??200;this.setPreferredSize({width:t,height:e});let r=this.getMaxSizeConstraint()?.width??2**53-1;this.setMaxSize({width:r,height:e});let i=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:i,height:e})}},G=s(UsernameField),K={tag:`textarea`,cursor:`text`,overflow:`auto`,padding:new r(3,3,3,3),preferredSize:{width:200,height:200},minSize:{width:100,height:100},backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`,foregroundColor:`var(--ts-ui-text-color, black)`},TextArea=class extends O{static ownClassStyleDefaults=K;constructor(e=``,t,n){super(e?{text:e,...t}:t,{...K,...n??{}}),this.setElementCSSRules({resize:`none`})}applyOptions(e){return super.applyOptions(e),e.rows!==void 0&&this.setRows(e.rows),e.cols!==void 0&&this.setCols(e.cols),e.wrap!==void 0&&this.setWrap(e.wrap),this}getRows(){return this._options.rows??null}setRows(e){return this._options.rows=e,this.setElementAttribute(`rows`,String(e)),this}clearRows(){return this._options.rows===void 0?this:(this._options.rows=void 0,this.removeElementAttribute(`rows`),this)}getCols(){return this._options.cols??null}setCols(e){return this._options.cols=e,this.setElementAttribute(`cols`,String(e)),this}clearCols(){return this._options.cols===void 0?this:(this._options.cols=void 0,this.removeElementAttribute(`cols`),this)}getWrap(){return this._options.wrap??null}setWrap(e){return this._options.wrap=e,this.setElementAttribute(`wrap`,e),this}clearWrap(){return this._options.wrap===void 0?this:(this._options.wrap=void 0,this.removeElementAttribute(`wrap`),this)}getBaseline(){return this.wrapInnerBaseline(n.measureTextBaseline())}init(t){super.init(t);let n=t||this.getElement();return this._options.rows!==void 0&&e.sink.apply(n,{setAttr:{rows:String(this._options.rows)}}),this._options.cols!==void 0&&e.sink.apply(n,{setAttr:{cols:String(this._options.cols)}}),this._options.wrap!==void 0&&e.sink.apply(n,{setAttr:{wrap:this._options.wrap}}),this}},ae=s(TextArea),q={outline:`none`},Toggle=class extends _{_track;_thumb;constructor(e,t){super({...e??{}},{...q,...t??{}}),this.setLayoutManager(new y),this._track=new c,this._track.setBackgroundColor(`var(--ts-ui-toggle-track-bg-off, rgb(200, 200, 200))`),this._track.setBorderRadius(`999px`),this._track.setPreferredSize({width:36,height:20}),this._track.setMinSize({width:36,height:20}),this._track.setMaxSize({width:36,height:20}),this._track.setCursor(`pointer`),this._thumb=new c,this._thumb.setBackgroundColor(`var(--ts-ui-toggle-thumb-bg, rgb(255, 255, 255))`),this._thumb.setBorderRadius(`999px`),this._thumb.setPreferredSize({width:16,height:16}),this._thumb.setMaxSize({width:16,height:16}),this._thumb.setSize({width:16,height:16}),this._thumb.setShadow(`0 1px 2px rgba(0, 0, 0, 0.25)`),this._thumb.setX(2),this._thumb.setY(2),this._thumb.setPointerEvents(`none`),l.isReducedMotion()||(this._thumb.setTransition(`transform 120ms ease-out`),this._track.setTransition(`background-color 120ms ease-out`)),this._track.addComponent(this._thumb),super.addComponent(this._track),this.getAria().setRole(`switch`),this.getAria().setTabIndex(0),this.getAria().setChecked(!1),a.addListener(this._track,`click`,()=>this.activateFromPointer()),this.installKeyboard(),this._options.value!==void 0&&this.applyValue(this._options.value),this._options.label!==void 0&&this.applyLabel(this._options.label),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.value!==void 0&&(this._options.value=e.value),e.label!==void 0&&(this._options.label=e.label),e.enabled!==void 0&&(this._options.enabled=e.enabled),e.readOnly!==void 0&&(this._options.readOnly=e.readOnly),this}activate(){this.setValue(!this.getValue())}getInteractiveSurface(){return this._track}activationKeys(){return[` `,`Enter`]}getValue(){return this._options.value??!1}setValue(e){let t=!!e;return t===this.getValue()?this:(this._options.value=t,this.applyValue(t),this.notifyChange(t),this)}clearValue(){return this.setValue(!1)}getBaseline(){let e=this._label;if(e===null)return null;let t=e.getBaseline();return t===null?null:this.wrapInnerBaseline(t+this.labelCenterOffset())}labelCenterOffset(){if(this._label===null)return 0;let e=this._track.getPreferredSize()?.height??0,t=this._label.getPreferredSize()?.height??e;return Math.max(0,Math.round((e-t)/2))}doLayout(){super.doLayout();let e=this._label,t=this.labelCenterOffset();return e!==null&&t>0&&(e.setY(e.getY()+e.getTranslateY()+t),e.setTranslate(e.getTranslateX(),0)),this}applyValue(e){this.getAria().setChecked(e),this._thumb.setTransform(e?`translateX(16px)`:`translateX(0px)`),this._track.setBackgroundColor(e?`var(--ts-ui-toggle-track-bg-on, rgb(30, 100, 200))`:`var(--ts-ui-toggle-track-bg-off, rgb(200, 200, 200))`)}},J=s(Toggle),oe=22,Y={durationMs:100,backgroundColor:`var(--ts-ui-autocomplete-bg, rgb(255, 255, 255))`,border:`var(--ts-ui-input-border)`,borderRadius:`var(--ts-ui-border-radius, 4px)`,shadow:`var(--ts-ui-autocomplete-shadow, 2px 4px 8px rgba(0,0,0,0.15))`};new o({scope:`selector`,name:`.AutoCompleteDropdown .List:focus::after`,styles:{content:`none`}});var AutoCompleteDropdown=class extends p{static ownClassStyleDefaults=Y;_list;_onSelect;_onHide;constructor(e,t,n,r){super(n,{...Y,...r??{}}),this._onSelect=e,this._onHide=t,this.setContain(`layout`),this.setLayoutManager(new m),this._list=new L,this._list.setBorder(`none`),this._list.setBorderRadius(`0`),this._list.setFocusOnRowClick(!1),this._list.setSelectFollowsFocus(!1),this.addComponent(this._list),this._list.on(`action`,()=>{let e=this._list.getValue();e&&this._onSelect(e)})}show(t,n){this.getElement(!0),this.pauseLayout(),this._list.setItemsArray(n.map(e=>({key:e,label:e}))),this.resumeLayout();let r=this.getPerimeterSize(),i=r.top+r.bottom,a=e.source.getElementRect(t);return this.setWidth(a.width),this.setHeight(n.length*oe+i),this.placeAnchored(a),this.setAnchorElement(t),this.showAnimated(),this.doLayout(),this}hide(){return this.hideAnimated(),this}handleKey(e){return this._list.handleKey(e)}getFocusedRowId(){return this._list.getFocusedRowId()}getList(){return this._list}onHideComplete(){this._onHide()}},se=s(AutoCompleteDropdown);E(`.AutoCompleteField`);var AutoCompleteField=class extends g{_textField;_dropdown;_debounceTimer=null;_selectBag=new u;constructor(e){super(),this.setBorder(`var(--ts-ui-input-border)`),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),this._textField=new D,this._textField.setBorder(`none`),this._textField.setBorderRadius(`0`),this._textField.setOutline(`none`),this.addComponent(this._textField),this.syncSizeFromTextField(),this.subscribeTheme(()=>this.syncSizeFromTextField()),this._dropdown=new se(e=>this.onSuggestionSelected(e),()=>this.onDropdownHidden()),this._textField.getAria().setRole(`combobox`),this._textField.getAria().setAutoComplete(`list`),this._textField.getAria().setExpanded(!1),this._textField.getAria().setControls(this._dropdown.getId()),this._textField.getAria().setActiveDescendant(``),a.addListener(this._textField,`input`,()=>this.onInput()),a.addListener(this._textField,`keydown`,e=>this.onKeyDown(e)),a.addListener(this._textField,`blur`,()=>this.onBlur()),this._textField.on(`change`,e=>this.notifyChange(e)),e&&this.applyOptions(e),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.suggestions!==void 0&&this.setSuggestions(e.suggestions),e.minChars!==void 0&&this.setMinChars(e.minChars),e.debounceMs!==void 0&&this.setDebounceMs(e.debounceMs),e.maxSuggestions!==void 0&&this.setMaxSuggestions(e.maxSuggestions),e.matchMode!==void 0&&this.setMatchMode(e.matchMode),e.placeholder!==void 0&&this.setPlaceholder(e.placeholder),e.store!==void 0&&e.displayField!==void 0?this.setStore(e.store,e.displayField):(e.store!==void 0&&(this._options.store=e.store),e.displayField!==void 0&&(this._options.displayField=e.displayField)),this}syncSizeFromTextField(){let e=this.getPerimeterSize(),t=e.left+e.right,n=e.top+e.bottom,r=this._textField.getPreferredSize(),a=this._textField.getMaxSize(),o=this._textField.getMinSize();r&&this.setPreferredSize({width:r.width,height:r.height+n}),a&&this.setMaxSize({width:i(a.width+t),height:i(a.height+n)}),o&&this.setMinSize({width:o.width,height:o.height+n})}getBaseline(){return this.wrapInnerBaseline(this._textField.getBaseline())}doLayout(){super.doLayout();let e=this.getContentBounds();return e?(this._textField.setX(e.x),this._textField.setY(e.y),this._textField.setWidth(e.width),this._textField.setHeight(e.height),this):this}setValue(e){return this._textField.setValue(e),this}getValue(){return this._textField.getValue()}applyEnabled(e){this._textField.setEnabled(e)}applyReadOnly(e){this._textField.setReadOnly(e)}setSuggestions(e){return this._options.suggestions=e,this}setStore(e,t){return this._options.store=e,this._options.displayField=t,this}setMinChars(e){return this._options.minChars=e,this}setDebounceMs(e){return this._options.debounceMs=e,this}setMaxSuggestions(e){return this._options.maxSuggestions=e,this}setMatchMode(e){return this._options.matchMode=e,this}setPlaceholder(e){return this._options.placeholder=e,this._textField.setPlaceholder(e),this}on(e,t){return e===`select`?(this._selectBag.add(`select`,t),this):super.on(e,t)}off(e,t){return e===`select`?(this._selectBag.remove(`select`,t),this):super.off(e,t)}addSelectListener(e){this.on(`select`,e)}onInput(){this._debounceTimer!==null&&clearTimeout(this._debounceTimer);let e=this._options.minChars??1,t=this.getValue();if(t.length<e){this._dropdown.hide();return}this._debounceTimer=setTimeout(()=>this.querySuggestions(t),this._options.debounceMs??200)}onKeyDown(e){if(this._dropdown.isOpen()&&(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`)&&this._dropdown.handleKey(e))return this.updateActiveDescendant(),{prevent:!0};switch(e.key){case`ArrowDown`:return this.querySuggestions(this.getValue()),{prevent:!0};case`Escape`:this._dropdown.hide(),this._textField.focus();break;case`Tab`:this._dropdown.hide()}}onBlur(){setTimeout(()=>{let t=e.source.getActiveElement(),n=this._dropdown.getElement();n&&e.source.contains(n,t)||this._dropdown.hide()},150)}matches(e,t){let n=this._options.matchMode??`contains`,r=n===`containsCaseSensitive`||n===`startsWithCaseSensitive`,i=n===`startsWith`||n===`startsWithCaseSensitive`,a=r?e:e.toLowerCase(),o=r?t:t.toLowerCase();return i?a.startsWith(o):a.includes(o)}querySuggestions(e){let t=this._options.maxSuggestions??10,n=this._options.matchMode??`contains`,r=this._options.suggestions,i=this._options.store,a=this._options.displayField;if(r!==void 0){let n=r.filter(t=>this.matches(t,e)).slice(0,t);e===this.getValue()&&this.showSuggestions(n);return}if(i!==void 0&&a!==void 0){let r=n===`containsCaseSensitive`||n===`startsWithCaseSensitive`,o=n===`startsWith`||n===`startsWithCaseSensitive`?`startsWith`:`contains`;i.clearFilter(),i.filterBy({type:o,field:a,value:e,caseSensitive:r});let s=i.getRecords().map(e=>String(e.get(a))).slice(0,t);e===this.getValue()&&this.showSuggestions(s)}}showSuggestions(e){if(e.length===0){this._dropdown.hide();return}this._dropdown.show(this._textField.getElement(!0),e),this._textField.getAria().setExpanded(!0)}onDropdownHidden(){this._textField.getAria().setExpanded(!1),this._textField.getAria().setActiveDescendant(``)}updateActiveDescendant(){this._textField.getAria().setActiveDescendant(this._dropdown.getFocusedRowId()??``)}onSuggestionSelected(e){this.setValue(e),this._selectBag.fire(`select`,e),this._dropdown.hide(),this._textField.focus()}},ce=s(AutoCompleteField);f.register(z,R);var X={border:`none`,borderRadius:`0`,insets:new r(0,0,0,0)},SpinButton=class extends x{static ownClassStyleDefaults=X;_listeners=new u;_repeat;constructor(e,t,n){super(void 0,t,{...X,glyph:e===`▲`?`chevron-up`:`chevron-down`,...n??{}}),this.updateSize(),this.subscribeTheme(()=>this.updateSize()),t?.shadow===void 0&&this.clearShadow(),t?.pressedShadow===void 0&&this.clearPressedShadow(),this.pinGlyphSize(8),this.getGlyph()?.setTranslate(0,-1),this._repeat=new d({initialDelay:400,decay:.75,floor:40,onTick:()=>this.emit(`tick`)}),a.addListener(this,`mousedown`,()=>this.onMouseDown()),a.addViewportListener(this,`mouseup`,()=>this.onMouseUp()),a.addViewportListener(this,`mouseleave`,()=>this.onMouseUp()),this.applyListeners(t?.listeners)}updateSize(){let e=n.lineHeightPx()+6+2,t=Math.floor((e-2)/2);this.setPreferredSize({width:18,height:t}),this.setMinSize({width:18,height:t}),this.setMaxSize({width:18,height:t})}on(e,t){return e===`action`?a.addListener(this,`click`,t):this._listeners.add(e,t),this}off(e,t){return e===`action`?a.removeListener(this,`click`,t):this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}onMouseDown(){this._repeat.start()}onMouseUp(){this._repeat.isRunning()&&this._repeat.stop()}},Z=s(SpinButton);E(`.NumberSpinner`),new o({scope:`selector`,name:`.NumberSpinner .TextField:focus`,styles:{boxShadow:`none`}});var le={insets:new r(0,0,0,0),border:`1px solid var(--ts-ui-button-border, rgb(200, 200, 200))`,borderRadius:`var(--ts-ui-border-radius, 4px)`,backgroundColor:`var(--ts-ui-input-bg, rgb(255, 255, 255))`},NumberSpinner=class extends g{_input;_upBtn;_downBtn;_btnBox;constructor(e,t){super(e,{...le,...t??{}}),this._input=new D,this._input.setTextAlign(`right`),this._input.setBorder(`none`),this._input.setBorderRadius(`0`),this._input.setOutline(`none`),this._input.setText(this.formatValue(0)),this._upBtn=new Z(`▲`),this._downBtn=new Z(`▼`),this._upBtn.setBorder({borderTop:`1px solid transparent`}),this._upBtn.setBorderRadius(`0`),this._downBtn.setBorder({borderTop:`1px solid var(--ts-ui-spinner-divider, rgb(180, 180, 180))`}),this._downBtn.setBorderRadius(`0`),this._btnBox=new c;let n=new v;n.setComponentSpacing(0),this._btnBox.setLayoutManager(n),this._btnBox.setInsets(new r(0,0,0,0)),this._btnBox.addComponent(this._upBtn),this._btnBox.addComponent(this._downBtn);let i=new y;i.setComponentSpacing(0),i.setStretching(!0),this.setLayoutManager(i),this.addComponent(this._input,{weight:1}),this.addComponent(this._btnBox),this._upBtn.on(`tick`,()=>this.applyValue(this.getValue()+this.getStep())),this._downBtn.on(`tick`,()=>this.applyValue(this.getValue()-this.getStep())),a.addListener(this._input,`blur`,()=>this.onBlur()),a.addListener(this._input,`keydown`,e=>this.onKeyDown(e)),this.updateHeight(),this.subscribeTheme(()=>this.updateHeight()),this.getAria().setRole(`spinbutton`),this.getAria().setValueNow(0),this._options.min!==void 0&&this.setMin(this._options.min),this._options.max!==void 0&&this.setMax(this._options.max),this._options.step!==void 0&&this.setStep(this._options.step),this._options.precision!==void 0&&this.setPrecision(this._options.precision),this._options.value!==void 0&&this.setValue(this._options.value),this._options.enabled!==void 0&&this.setEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.setReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.min!==void 0&&(this._options.min=e.min),e.max!==void 0&&(this._options.max=e.max),e.step!==void 0&&(this._options.step=e.step),e.precision!==void 0&&(this._options.precision=e.precision),e.value!==void 0&&(this._options.value=e.value),e.enabled!==void 0&&(this._options.enabled=e.enabled),this}getBaseline(){return this.wrapInnerBaseline(this._input.getBaseline())}updateHeight(){let e=n.singleLineBoxHeight(this.getInsets(),this._input.getPadding(),this.getBorderSize()),r=this.getPreferredSizeConstraint()?.width??120;this.setPreferredSize({width:r,height:e});let i=this.getMaxSizeConstraint()?.width??t;this.setMaxSize({width:i,height:e});let a=this.getMinSizeConstraint()?.width??0;this.setMinSize({width:a,height:e})}getValue(){return this._options.value??0}setValue(e){return this._setValueSilent(e),this}getMin(){return this._options.min??-1/0}setMin(e){return this._options.min=e,this.getAria().setValueMin(isFinite(e)?e:null),this}getMax(){return this._options.max??1/0}setMax(e){return this._options.max=e,this.getAria().setValueMax(isFinite(e)?e:null),this}getStep(){return this._options.step??1}setStep(e){return this._options.step=e,this}getPrecision(){return this._options.precision??null}setPrecision(e){return this._options.precision=e,this._input.setText(this.formatValue(this.getValue())),this}applyEnabled(e){e?(this._input.setDisabledAttribute(!1),this._upBtn.setPointerEvents(`auto`),this._downBtn.setPointerEvents(`auto`),this.clearOpacity()):(this._input.setDisabledAttribute(!0),this._upBtn.setPointerEvents(`none`),this._downBtn.setPointerEvents(`none`),this.setOpacity(.5))}applyReadOnly(e){this._input.setReadOnly(e)}applyValue(e){if(!this.isEnabled())return;let t=this.normalize(e);if(t===this.getValue()){this._input.setText(this.formatValue(t));return}this._options.value=t,this._input.setText(this.formatValue(t)),this.getAria().setValueNow(t),this.notifyChange(t)}_setValueSilent(e){let t=this.normalize(e);this._options.value=t,this._input.setText(this.formatValue(t)),this.getAria().setValueNow(t)}normalize(e){let t=Math.min(this.getMax(),Math.max(this.getMin(),e));return t=Math.round(t/this.getStep())*this.getStep(),t=parseFloat(t.toFixed(this.derivePrecision())),t}onBlur(){let e=parseFloat(this._input.getText().valueOf());if(isNaN(e)){this._input.setText(this.formatValue(this.getValue()));return}this.applyValue(e)}onKeyDown(e){if(e.key===`ArrowUp`)return this.applyValue(this.getValue()+this.getStep()),{prevent:!0};if(e.key===`ArrowDown`)return this.applyValue(this.getValue()-this.getStep()),{prevent:!0};e.key===`Enter`&&this.onBlur()}formatValue(e){return e.toFixed(this.derivePrecision())}derivePrecision(){let e=this._options.precision??null;if(e!==null)return e;let t=String(this.getStep()),n=t.indexOf(`.`);return n>=0?t.length-n-1:0}},ue=s(NumberSpinner),de=3,Q=`no file selected`,HiddenFileInput=class extends c{_type=null;_multiple=!1;_accept=null;constructor(){super({tag:`input`}),this.setDisplayed(!1),this.setType(`file`)}getType(){return this._type}setType(e){this._type=e,this.setElementAttribute(`type`,e)}setMultipleAttribute(e){this._multiple=e,e?this.setElementAttribute(`multiple`,``):this.removeElementAttribute(`multiple`)}setAcceptAttribute(e){this._accept=e,this.setElementAttribute(`accept`,e)}setDisabled(e){this.setDisabledAttribute(e)}openPicker(){let t=this.getElement();t&&e.sink.click(t)}getFiles(){let t=this.getElement();return t?e.source.getFiles(t):null}clearFiles(){let t=this.getElement();t&&e.sink.setValue(t,``)}init(t){super.init(t);let n=t??this.getElement();return n?(this._type!==null&&e.sink.apply(n,{setAttr:{type:this._type}}),this._multiple&&e.sink.apply(n,{setAttr:{multiple:``}}),this._accept!==null&&e.sink.apply(n,{setAttr:{accept:this._accept}}),this):this}},FileField=class extends g{_button;_label;_input;_droppedFiles=[];constructor(e){super({...e??{}}),this.setLayoutManager(new y),this._button=new x(this.defaultButtonText()),this._button.on(`action`,()=>this.openPicker()),this._label=new h(Q,{truncate:!0}),this._label.setForegroundColor(`var(--ts-ui-form-text, inherit)`),this._input=new HiddenFileInput,a.addListener(this._input,`change`,()=>this.onNativeChange()),super.addComponent(this._button),super.addComponent(this._label,{weight:1}),super.addComponent(this._input),this._options.multiple!==void 0&&this.setMultiple(this._options.multiple),this._options.accept!==void 0&&this.setAccept(this._options.accept),this._options.buttonText!==void 0&&this._button.setText(this._options.buttonText),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.multiple!==void 0&&(this._options.multiple=e.multiple),e.accept!==void 0&&(this._options.accept=e.accept),e.buttonText!==void 0&&(this._options.buttonText=e.buttonText),this}defaultButtonText(){return this.isMultiple()?`Choose files…`:`Choose file…`}openPicker(){!this.isEnabled()||this.isReadOnly()||this._input.openPicker()}onNativeChange(){this._droppedFiles=[];let e=this.getValue();this.updateLabel(e),this.notifyChange(e)}acceptDroppedFiles(e){this._droppedFiles=Array.from(e);let t=this.getValue();this.updateLabel(t),this.notifyChange(t)}getValue(){if(this._droppedFiles.length>0)return this._droppedFiles;let e=this._input.getFiles();return e?Array.from(e):[]}setValue(e){return e&&e.length>0?(console.warn(`FileField '`+this.getId()+`' setValue ignored a non-empty File[]: the browser security model forbids programmatically populating a file input. Only setValue([]) (clearing) is supported.`),this):this.clearValue()}clearValue(){this._droppedFiles=[],this._input.clearFiles();let e=this.getValue();return this.updateLabel(e),this.notifyChange(e),this}isMultiple(){return this._options.multiple??!1}setMultiple(e){return this._options.multiple=!!e,this._input.setMultipleAttribute(this._options.multiple),this._options.buttonText===void 0&&this._button.setText(this.defaultButtonText()),this}getAccept(){return this._options.accept??null}setAccept(e){return this._options.accept=e,this._input.setAcceptAttribute(e),this}updateLabel(e){e.length===0?this._label.setText(Q):e.length===1?this._label.setText(e[0].name):e.length>de?this._label.setText(e.length+` files`):this._label.setText(e.map(e=>e.name).join(`, `))}applyEnabled(e){this._button.setEnabled(e),this._input.setDisabled(!e)}applyReadOnly(e){this._button.setEnabled(!e&&this.isEnabled()),this.getAria().setReadOnly(e)}},fe=s(FileField),pe=`var(--ts-ui-filedropzone-border, 2px dashed rgba(80, 140, 240, 0.40))`,$=`var(--ts-ui-filedropzone-bg, rgba(80, 140, 240, 0.06))`,me=`var(--ts-ui-filedropzone-active-border, 2px dashed rgba(80, 140, 240, 0.80))`,he=`var(--ts-ui-filedropzone-active-bg, rgba(80, 140, 240, 0.18))`,ge=`Drop files here or click to browse`,FileDropZone=class extends g{_prompt;_field;_dragDepth=0;constructor(e){super({...e??{}}),this.setLayoutManager(new v),this._prompt=new h(this._options.promptText??ge),this._prompt.setForegroundColor(`var(--ts-ui-form-text, inherit)`),this._field=new FileField(this.fieldOptions()),this._field.on(`change`,e=>this.notifyChange(e)),super.addComponent(this._prompt),super.addComponent(this._field),this.setActive(!1),this.setBorderRadius(`var(--ts-ui-border-radius, 4px)`),a.addSubtreeListener(this,`dragenter`,{prevent:!0,handler:this.onDragEnter}),a.addSubtreeListener(this,`dragover`,{prevent:!0,handler:this.onDragOver}),a.addSubtreeListener(this,`dragleave`,{prevent:!0,handler:this.onDragLeave}),a.addSubtreeListener(this,`drop`,{prevent:!0,handler:this.onDrop}),this._options.enabled!==void 0&&this.applyEnabled(this._options.enabled),this._options.readOnly!==void 0&&this.applyReadOnly(this._options.readOnly)}applyOptions(e){return super.applyOptions(e),e.promptText!==void 0&&(this._options.promptText=e.promptText),this}fieldOptions(){return{multiple:this._options.multiple,accept:this._options.accept,buttonText:this._options.buttonText}}setActive(e){this.setBorder(e?me:pe),this.setBackgroundColor(e?he:$)}onDragEnter(e){this._dragDepth++,this._dragDepth===1&&this.setActive(!0)}onDragOver(e){}onDragLeave(e){this._dragDepth=Math.max(0,this._dragDepth-1),this._dragDepth===0&&this.setActive(!1)}onDrop(e){if(this._dragDepth=0,this.setActive(!1),!this.isEnabled()||this.isReadOnly())return;let t=e.dataTransfer?.files;!t||t.length===0||this._field.acceptDroppedFiles(t)}getValue(){return this._field.getValue()}setValue(e){return this._field.setValue(e),this}clearValue(){return this._field.clearValue(),this}applyEnabled(e){this._field.setEnabled(e)}applyReadOnly(e){this._field.setReadOnly(e)}},_e=s(FileDropZone);export{_ as AbstractBooleanInput,g as AbstractInput,F as AbstractPickerField,ce as AutoCompleteField,ee as Checkbox,P as ComboBox,B as DateField,T as DatePickerDropdown,V as DateTimeField,k as DateTimePickerDropdown,_e as FileDropZone,fe as FileField,C as Label,re as Link,ue as NumberSpinner,U as PasswordField,ne as PickerButton,A as PickerCell,N as PickerCellList,te as PickerColumn,j as PickerInput,b as RadioButton,S as SelectableText,w as Slider,Z as SpinButton,h as Text,ae as TextArea,D as TextField,O as TextInput,M as TimeField,ie as TimePickerDropdown,J as Toggle,G as UsernameField};
2
2
  //# sourceMappingURL=input.es.js.map