kempo-ui 0.4.5 → 0.4.7

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 (176) hide show
  1. package/dist/components/Chat.js +201 -0
  2. package/dist/components/CodeEditor.js +21 -2
  3. package/dist/components/Dropdown.js +1 -1
  4. package/dist/components/HtmlEditor.js +42 -3
  5. package/dist/components/Markdown.js +1 -0
  6. package/dist/components/MarkdownEditor.js +173 -0
  7. package/dist/components/Resize.js +12 -2
  8. package/dist/components/markdownEditorControls/Bold.js +11 -0
  9. package/dist/components/markdownEditorControls/BulletList.js +11 -0
  10. package/dist/components/markdownEditorControls/Code.js +11 -0
  11. package/dist/components/markdownEditorControls/FormatBlock.js +30 -0
  12. package/dist/components/markdownEditorControls/Heading.js +11 -0
  13. package/dist/components/markdownEditorControls/Image.js +93 -0
  14. package/dist/components/markdownEditorControls/Italic.js +11 -0
  15. package/dist/components/markdownEditorControls/Link.js +11 -0
  16. package/dist/components/markdownEditorControls/MarkdownEditorControl.js +39 -0
  17. package/dist/components/markdownEditorControls/Menu.js +30 -0
  18. package/dist/components/markdownEditorControls/NumberedList.js +11 -0
  19. package/dist/components/markdownEditorControls/Quote.js +11 -0
  20. package/dist/components/markdownEditorControls/SpeechToText.js +16 -0
  21. package/dist/components/markdownEditorControls/Strikethrough.js +11 -0
  22. package/dist/components/markdownEditorControls/Table.js +80 -0
  23. package/dist/utils/marked.esm.js +1 -0
  24. package/dist/utils/renderMarkdown.js +1 -0
  25. package/dist/utils/sanitizeHtml.js +1 -0
  26. package/docs/components/accordion.html +16 -0
  27. package/docs/components/aside.html +16 -0
  28. package/docs/components/calendar.html +16 -0
  29. package/docs/components/card.html +16 -0
  30. package/docs/components/chat.html +719 -0
  31. package/docs/components/code-editor.html +52 -0
  32. package/docs/components/color-picker.html +16 -0
  33. package/docs/components/combobox.html +16 -0
  34. package/docs/components/content-slider.html +16 -0
  35. package/docs/components/context.html +16 -0
  36. package/docs/components/dialog.html +16 -0
  37. package/docs/components/dropdown.html +16 -0
  38. package/docs/components/filter-list.html +16 -0
  39. package/docs/components/focus-capture.html +16 -0
  40. package/docs/components/html-editor.html +57 -0
  41. package/docs/components/hybrid-component.html +16 -0
  42. package/docs/components/icon.html +16 -0
  43. package/docs/components/import.html +16 -0
  44. package/docs/components/light-component.html +16 -0
  45. package/docs/components/markdown-editor.html +928 -0
  46. package/docs/components/markdown.html +650 -0
  47. package/docs/components/nav-spacer.html +16 -0
  48. package/docs/components/nav.html +16 -0
  49. package/docs/components/photo-viewer.html +16 -0
  50. package/docs/components/progress.html +16 -0
  51. package/docs/components/resize.html +19 -0
  52. package/docs/components/rich-textarea.html +650 -0
  53. package/docs/components/shadow-component.html +16 -0
  54. package/docs/components/show-more.html +16 -0
  55. package/docs/components/slider.html +16 -0
  56. package/docs/components/sortable.html +16 -0
  57. package/docs/components/speech-to-text.html +16 -0
  58. package/docs/components/spinner.html +16 -0
  59. package/docs/components/split.html +16 -0
  60. package/docs/components/table.html +16 -0
  61. package/docs/components/tableControls.html +16 -0
  62. package/docs/components/tableCustomFields.html +16 -0
  63. package/docs/components/tableFetchRecords.html +16 -0
  64. package/docs/components/tableFieldSortHide.html +16 -0
  65. package/docs/components/tablePagination.html +16 -0
  66. package/docs/components/tablePlaceholder.html +16 -0
  67. package/docs/components/tableRecordEditing.html +16 -0
  68. package/docs/components/tableRecordFiltering.html +16 -0
  69. package/docs/components/tableRecordHiding.html +16 -0
  70. package/docs/components/tableRecordSearching.html +16 -0
  71. package/docs/components/tableRecordSelection.html +16 -0
  72. package/docs/components/tableRowControls.html +16 -0
  73. package/docs/components/tableServerSync.html +16 -0
  74. package/docs/components/tableSorting.html +16 -0
  75. package/docs/components/tabs.html +16 -0
  76. package/docs/components/tags.html +16 -0
  77. package/docs/components/text-to-speech.html +16 -0
  78. package/docs/components/theme-select.html +16 -0
  79. package/docs/components/theme-switcher.html +16 -0
  80. package/docs/components/time.html +16 -0
  81. package/docs/components/timestamp.html +16 -0
  82. package/docs/components/toast.html +16 -0
  83. package/docs/components/toggle.html +16 -0
  84. package/docs/components/tree.html +16 -0
  85. package/docs/components/voice-selector.html +16 -0
  86. package/docs/icons/done_all.svg +1 -0
  87. package/docs/icons/format_quote.svg +1 -0
  88. package/docs/icons/format_strikethrough.svg +1 -0
  89. package/docs/icons/send.svg +1 -0
  90. package/docs/icons/text_fields.svg +1 -0
  91. package/docs/index.html +40 -0
  92. package/docs/src/components/Chat.js +201 -0
  93. package/docs/src/components/CodeEditor.js +21 -2
  94. package/docs/src/components/Dropdown.js +1 -1
  95. package/docs/src/components/HtmlEditor.js +42 -3
  96. package/docs/src/components/Markdown.js +1 -0
  97. package/docs/src/components/MarkdownEditor.js +173 -0
  98. package/docs/src/components/Resize.js +12 -2
  99. package/docs/src/components/markdownEditorControls/Bold.js +11 -0
  100. package/docs/src/components/markdownEditorControls/BulletList.js +11 -0
  101. package/docs/src/components/markdownEditorControls/Code.js +11 -0
  102. package/docs/src/components/markdownEditorControls/FormatBlock.js +30 -0
  103. package/docs/src/components/markdownEditorControls/Heading.js +11 -0
  104. package/docs/src/components/markdownEditorControls/Image.js +93 -0
  105. package/docs/src/components/markdownEditorControls/Italic.js +11 -0
  106. package/docs/src/components/markdownEditorControls/Link.js +11 -0
  107. package/docs/src/components/markdownEditorControls/MarkdownEditorControl.js +39 -0
  108. package/docs/src/components/markdownEditorControls/Menu.js +30 -0
  109. package/docs/src/components/markdownEditorControls/NumberedList.js +11 -0
  110. package/docs/src/components/markdownEditorControls/Quote.js +11 -0
  111. package/docs/src/components/markdownEditorControls/SpeechToText.js +16 -0
  112. package/docs/src/components/markdownEditorControls/Strikethrough.js +11 -0
  113. package/docs/src/components/markdownEditorControls/Table.js +80 -0
  114. package/docs/src/utils/marked.esm.js +1 -0
  115. package/docs/src/utils/renderMarkdown.js +1 -0
  116. package/docs/src/utils/sanitizeHtml.js +1 -0
  117. package/docs/utils/context.html +16 -0
  118. package/docs/utils/cookie.html +16 -0
  119. package/docs/utils/debounce.html +16 -0
  120. package/docs/utils/drag.html +16 -0
  121. package/docs/utils/elevation.html +20 -10
  122. package/docs/utils/formatTimestamp.html +16 -0
  123. package/docs/utils/object.html +16 -0
  124. package/docs/utils/propConverters.html +16 -0
  125. package/docs/utils/sanitizeHtml.html +559 -0
  126. package/docs/utils/string.html +16 -0
  127. package/docs/utils/theme.html +16 -0
  128. package/docs/utils/toTitleCase.html +16 -0
  129. package/docs/utils/type.html +16 -0
  130. package/docs/utils/voice.html +16 -0
  131. package/docs/utils/wait.html +16 -0
  132. package/docs-src/components/chat.page.html +230 -0
  133. package/docs-src/components/code-editor.page.html +36 -0
  134. package/docs-src/components/html-editor.page.html +41 -0
  135. package/docs-src/components/markdown-editor.page.html +439 -0
  136. package/docs-src/components/markdown.page.html +161 -0
  137. package/docs-src/components/resize.page.html +3 -0
  138. package/docs-src/index.page.html +24 -0
  139. package/docs-src/nav.fragment.html +16 -0
  140. package/docs-src/utils/elevation.page.html +4 -10
  141. package/docs-src/utils/sanitizeHtml.page.html +73 -0
  142. package/icons/done_all.svg +1 -0
  143. package/icons/format_quote.svg +1 -0
  144. package/icons/format_strikethrough.svg +1 -0
  145. package/icons/send.svg +1 -0
  146. package/icons/text_fields.svg +1 -0
  147. package/llms.txt +6 -2
  148. package/package.json +3 -2
  149. package/src/components/Chat.js +435 -0
  150. package/src/components/CodeEditor.js +58 -2
  151. package/src/components/Dropdown.js +1 -1
  152. package/src/components/HtmlEditor.js +84 -4
  153. package/src/components/Markdown.js +125 -0
  154. package/src/components/MarkdownEditor.js +654 -0
  155. package/src/components/Resize.js +17 -1
  156. package/src/components/markdownEditorControls/Bold.js +30 -0
  157. package/src/components/markdownEditorControls/BulletList.js +30 -0
  158. package/src/components/markdownEditorControls/Code.js +39 -0
  159. package/src/components/markdownEditorControls/FormatBlock.js +102 -0
  160. package/src/components/markdownEditorControls/Heading.js +45 -0
  161. package/src/components/markdownEditorControls/Image.js +250 -0
  162. package/src/components/markdownEditorControls/Italic.js +30 -0
  163. package/src/components/markdownEditorControls/Link.js +74 -0
  164. package/src/components/markdownEditorControls/MarkdownEditorControl.js +158 -0
  165. package/src/components/markdownEditorControls/Menu.js +86 -0
  166. package/src/components/markdownEditorControls/NumberedList.js +69 -0
  167. package/src/components/markdownEditorControls/Quote.js +30 -0
  168. package/src/components/markdownEditorControls/SpeechToText.js +65 -0
  169. package/src/components/markdownEditorControls/Strikethrough.js +35 -0
  170. package/src/components/markdownEditorControls/Table.js +164 -0
  171. package/src/utils/marked.esm.js +77 -0
  172. package/src/utils/renderMarkdown.js +29 -0
  173. package/src/utils/sanitizeHtml.js +137 -0
  174. package/tests/components/Chat.browser-test.js +540 -0
  175. package/tests/components/Markdown.browser-test.js +292 -0
  176. package/tests/utils/sanitizeHtml.browser-test.js +179 -0
@@ -16,6 +16,7 @@
16
16
  <a href="#customNodes">Custom Nodes Example</a><br />
17
17
  <a href="#preConfigured">Pre-Configured Editors</a><br />
18
18
  <a href="#height">Setting Height</a><br />
19
+ <a href="#disabled">Disabled / Read-only</a><br />
19
20
  <a href="#javascriptUsage">JavaScript API Example</a><br />
20
21
 
21
22
  <h6 class="mt"><a
@@ -255,6 +256,37 @@
255
256
  </div>
256
257
  </div>
257
258
 
259
+ <h3 id="disabled"><a
260
+ href="#disabled"
261
+ class="no-link"
262
+ >Disabled / Read-only</a></h3>
263
+ <p>The <code>disabled</code> attribute makes the editor non-interactive (Lexical is set to non-editable in visual mode, Monaco is set to <code>readOnly</code> in code mode), mutes the toolbar, fades the host, and excludes the field from form submission. The <code>readonly</code> attribute lets the user select and copy text and switch modes, but they can't edit; the value is still submitted with the form.</p>
264
+ <div class="row -mx">
265
+ <div class="col m-span-12 px">
266
+ <k-card label="HTML">
267
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-html-editor</span> <span class="hljs-attr">disabled</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;...&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-html-editor</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">k-html-editor</span> <span class="hljs-attr">readonly</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;...&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-html-editor</span>&gt;</span></code></pre>
268
+ </k-card>
269
+ </div>
270
+ <div class="col m-span-12 px">
271
+ <k-card label="Output">
272
+ <k-html-editor
273
+ disabled
274
+ controls="minimal"
275
+ class="b r mb"
276
+ style="height: 200px"
277
+ value="<p>This editor is <b>disabled</b> &mdash; the toolbar, content, and mode toggle are all inert.</p>"
278
+ ></k-html-editor>
279
+ <k-html-editor
280
+ readonly
281
+ controls="minimal"
282
+ class="b r"
283
+ style="height: 200px"
284
+ value="<p>This editor is <i>read-only</i> &mdash; you can select and copy, just not edit.</p>"
285
+ ></k-html-editor>
286
+ </k-card>
287
+ </div>
288
+ </div>
289
+
258
290
  <h3 id="javascriptUsage"><a
259
291
  href="#javascriptUsage"
260
292
  class="no-link"
@@ -392,6 +424,15 @@
392
424
  Configuration</a> for the full resolution order.
393
425
  </p>
394
426
 
427
+ <h5><code>disabled<i>: Boolean</i></code></h5>
428
+ <p>When <code>true</code>, the editor is non-interactive (Lexical's <code>setEditable(false)</code> in visual mode, Monaco's <code>readOnly</code> in code mode), the toolbar is muted, the host fades to <code>opacity: 0.6</code>, and the field is excluded from form submission. Syncs to <code>disabled</code> attribute.</p>
429
+
430
+ <h5><code>readonly<i>: Boolean</i></code></h5>
431
+ <p>When <code>true</code>, the user can select and copy text but not edit. The value is still submitted with the form. Syncs to <code>readonly</code> attribute.</p>
432
+
433
+ <h5><code>required<i>: Boolean</i></code></h5>
434
+ <p>When <code>true</code> and the editor is empty (no non-whitespace text), the element reports a <code>valueMissing</code> validity error to its parent <code>&lt;form&gt;</code>. Syncs to <code>required</code> attribute.</p>
435
+
395
436
  <h3 id="globalConfig"><a
396
437
  href="#globalConfig"
397
438
  class="no-link"
@@ -0,0 +1,439 @@
1
+ <page pageName="Markdown Editor" title="Markdown Editor - Components - Kempo Docs - A Web Components Solution">
2
+ <content>
3
+ <k-accordion persistent-id="toc" class="b r mb">
4
+ <k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
5
+ <k-accordion-panel name="toc-panel">
6
+ <div class="m pl">
7
+ <h6>Examples</h6>
8
+ <a href="#basicUsage">Basic Usage</a><br />
9
+ <a href="#withControls">With Controls</a><br />
10
+ <a href="#preConfigured">Pre-Configured Controls</a><br />
11
+ <a href="#bottomControls">Bottom Controls</a><br />
12
+ <a href="#defaultValue">Default Value</a><br />
13
+ <a href="#formUsage">Form Usage</a><br />
14
+ <a href="#disabled">Disabled / Read-only</a><br />
15
+ <a href="#tagFiltering">Tag Filtering</a><br />
16
+ <a href="#customControls">Custom Controls</a><br />
17
+
18
+ <h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
19
+ <a href="#constructor">Constructor</a><br />
20
+ <a href="#requirements">Requirements</a><br />
21
+ <a href="#properties">Properties</a><br />
22
+ <a href="#methods">Methods</a><br />
23
+ <a href="#cssVariables">CSS Variables</a><br />
24
+ <a href="#events">Events</a><br />
25
+ <a href="#controls">Built-in Controls</a><br />
26
+ </div>
27
+ </k-accordion-panel>
28
+ </k-accordion>
29
+
30
+ <p>A markdown editor with GitHub-style <strong>Write</strong> and <strong>Preview</strong> tabs. The user types markdown in the textarea and can flip to a live preview at any time. Markdown is parsed by <a href="https://marked.js.org" target="_blank">marked</a> &mdash; a full CommonMark + GFM parser &mdash; so tables, task lists, fenced code blocks, autolinks, and inline HTML all work out of the box. The rendered output runs through <a href="../utils/sanitizeHtml.html"><code>sanitizeHtml</code></a> using <code>allowed-tags</code> (an allowlist) <em>or</em> <code>disallowed-tags</code> (a denylist) &mdash; the two are mutually exclusive. <code>&lt;script&gt;</code> tags are <strong>always</strong> stripped unless you opt in via the <code>scripts-enabled</code> attribute, and <code>&lt;iframe&gt;</code> / <code>&lt;style&gt;</code> / similar dangerous tags are always stripped regardless of any setting.</p>
31
+ <p>Toolbar buttons are slotted via <code>controls-top</code> and <code>controls-bottom</code> &mdash; mix and match the built-in controls listed below or write your own by extending <a href="#customControls"><code>MarkdownEditorControl</code></a>.</p>
32
+
33
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
34
+ <p>Drop in the tag with a <code>placeholder</code> and you get a markdown textarea with a Write/Preview toggle. No toolbars, no controls &mdash; just the bare editor.</p>
35
+ <div class="row -mx">
36
+ <div class="col m-span-12 px">
37
+ <k-card label="HTML">
38
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">&quot;Type some **markdown**...&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
39
+ </k-card>
40
+ </div>
41
+ <div class="col m-span-12 px">
42
+ <k-card label="Output">
43
+ <k-markdown-editor placeholder="Type some **markdown**..."></k-markdown-editor>
44
+ </k-card>
45
+ </div>
46
+ </div>
47
+
48
+ <h3 id="withControls"><a href="#withControls" class="no-link">With Controls</a></h3>
49
+ <p>Wrap your toolbar buttons in a single <code>&lt;div slot="controls-top"&gt;</code> &mdash; that wrapper is the only element placed in the editor's <code>controls-top</code> slot, and you can lay it out however you like (the example below uses kempo-css's <code>d-f</code> for flex). Each control is its own custom element &mdash; import only the ones you need.</p>
50
+ <div class="row -mx">
51
+ <div class="col m-span-12 px">
52
+ <k-card label="HTML">
53
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">&quot;Write a comment...&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;controls-top&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;d-f&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-menu</span> <span class="hljs-attr">label</span>=<span class="hljs-string">&quot;Heading&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-icon</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;trigger&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;text_fields&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-icon</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h1&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h2&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h3&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h4&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h5&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-format-block</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;h6&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-format-block</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">k-md-menu</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-bold</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-bold</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-italic</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-italic</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-quote</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-quote</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-code</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-code</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-link</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-link</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-image</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-image</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-table</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-table</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-bullet-list</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-bullet-list</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-numbered-list</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-numbered-list</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
54
+ </k-card>
55
+ </div>
56
+ <div class="col m-span-12 px">
57
+ <k-card label="Output">
58
+ <k-markdown-editor placeholder="Write a comment...">
59
+ <div slot="controls-top" class="d-f">
60
+ <k-md-menu label="Heading">
61
+ <k-icon slot="trigger" name="text_fields"></k-icon>
62
+ <k-md-format-block tag="h1"></k-md-format-block>
63
+ <k-md-format-block tag="h2"></k-md-format-block>
64
+ <k-md-format-block tag="h3"></k-md-format-block>
65
+ <k-md-format-block tag="h4"></k-md-format-block>
66
+ <k-md-format-block tag="h5"></k-md-format-block>
67
+ <k-md-format-block tag="h6"></k-md-format-block>
68
+ </k-md-menu>
69
+ <k-md-bold></k-md-bold>
70
+ <k-md-italic></k-md-italic>
71
+ <k-md-quote></k-md-quote>
72
+ <k-md-code></k-md-code>
73
+ <k-md-link></k-md-link>
74
+ <k-md-image></k-md-image>
75
+ <k-md-table></k-md-table>
76
+ <k-md-bullet-list></k-md-bullet-list>
77
+ <k-md-numbered-list></k-md-numbered-list>
78
+ </div>
79
+ </k-markdown-editor>
80
+ </k-card>
81
+ </div>
82
+ </div>
83
+
84
+ <h3 id="preConfigured"><a href="#preConfigured" class="no-link">Pre-Configured Controls</a></h3>
85
+ <p>The <code>controls</code> attribute lets you opt into a built-in toolbar without writing any slot markup. Set it directly on <code>&lt;k-markdown-editor&gt;</code> to one of the three levels below; the matching control modules are imported automatically.</p>
86
+
87
+ <h4>controls="full"</h4>
88
+ <p>Every available control: heading menu (H1&ndash;H6), bold, italic, strikethrough, quote, code, link, image (with dialog/dropdown), table builder, bulleted and numbered lists, speech-to-text.</p>
89
+ <div class="row -mx">
90
+ <div class="col m-span-12 px">
91
+ <k-card label="HTML">
92
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">controls</span>=<span class="hljs-string">&quot;full&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
93
+ </k-card>
94
+ </div>
95
+ <div class="col m-span-12 px">
96
+ <k-card label="Output">
97
+ <k-markdown-editor controls="full" placeholder="Type some **markdown**..."></k-markdown-editor>
98
+ </k-card>
99
+ </div>
100
+ </div>
101
+
102
+ <h4>controls="normal"</h4>
103
+ <p>A balanced everyday toolbar: heading menu, bold, italic, quote, code, link, and lists.</p>
104
+ <div class="row -mx">
105
+ <div class="col m-span-12 px">
106
+ <k-card label="HTML">
107
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">controls</span>=<span class="hljs-string">&quot;normal&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
108
+ </k-card>
109
+ </div>
110
+ <div class="col m-span-12 px">
111
+ <k-card label="Output">
112
+ <k-markdown-editor controls="normal" placeholder="Type some **markdown**..."></k-markdown-editor>
113
+ </k-card>
114
+ </div>
115
+ </div>
116
+
117
+ <h4>controls="minimal"</h4>
118
+ <p>Essential controls only: heading menu (H1, H3, H5), bold, italic, and lists. Ideal for simple comment forms.</p>
119
+ <div class="row -mx">
120
+ <div class="col m-span-12 px">
121
+ <k-card label="HTML">
122
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">controls</span>=<span class="hljs-string">&quot;minimal&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
123
+ </k-card>
124
+ </div>
125
+ <div class="col m-span-12 px">
126
+ <k-card label="Output">
127
+ <k-markdown-editor controls="minimal" placeholder="Type some **markdown**..."></k-markdown-editor>
128
+ </k-card>
129
+ </div>
130
+ </div>
131
+
132
+ <p>If you provide your own children with <code>slot="controls-top"</code> (or <code>controls-bottom</code>), they replace the corresponding fallback content from the preset &mdash; so you can layer custom controls on top of any level.</p>
133
+
134
+ <h3 id="bottomControls"><a href="#bottomControls" class="no-link">Bottom Controls</a></h3>
135
+ <p>Same pattern for the bottom: wrap your action row in a single <code>&lt;div slot="controls-bottom"&gt;</code>. The footer is hidden until something is slotted in. Mix the built-in markdown controls with your own action buttons to build a Slack/GitHub-style composer.</p>
136
+ <div class="row -mx">
137
+ <div class="col m-span-12 px">
138
+ <k-card label="HTML">
139
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">&quot;Write a comment...&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;controls-top&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;d-f&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-bold</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-bold</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-italic</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-italic</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-link</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-link</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;controls-bottom&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;d-f&quot;</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;align-items:center; width:100%;&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;tc-muted&quot;</span>&gt;</span>Markdown supported<span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;margin-left:auto&quot;</span>&gt;</span>Comment<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
140
+ </k-card>
141
+ </div>
142
+ <div class="col m-span-12 px">
143
+ <k-card label="Output">
144
+ <k-markdown-editor placeholder="Write a comment...">
145
+ <div slot="controls-top" class="d-f">
146
+ <k-md-bold></k-md-bold>
147
+ <k-md-italic></k-md-italic>
148
+ <k-md-link></k-md-link>
149
+ </div>
150
+ <div slot="controls-bottom" class="d-f" style="align-items:center; width:100%;">
151
+ <small class="tc-muted">Markdown supported</small>
152
+ <button style="margin-left:auto">Comment</button>
153
+ </div>
154
+ </k-markdown-editor>
155
+ </k-card>
156
+ </div>
157
+ </div>
158
+
159
+ <h3 id="defaultValue"><a href="#defaultValue" class="no-link">Default Value</a></h3>
160
+ <p>Set initial markdown via the <code>value</code> attribute. Children are reserved for slotted controls (and their own content like icons or labels), so they are <strong>not</strong> read as the editor's value &mdash; otherwise icon labels, separators, etc. would leak into what the user sees.</p>
161
+ <div class="row -mx">
162
+ <div class="col m-span-12 px">
163
+ <k-card label="HTML">
164
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;# Hello&amp;#10;&amp;#10;Seeded from the value attribute. Try the Preview tab.&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
165
+ </k-card>
166
+ </div>
167
+ <div class="col m-span-12 px">
168
+ <k-card label="Output">
169
+ <k-markdown-editor value="# Hello&#10;&#10;Seeded from the value attribute. Try the Preview tab."></k-markdown-editor>
170
+ </k-card>
171
+ </div>
172
+ </div>
173
+
174
+ <h3 id="formUsage"><a href="#formUsage" class="no-link">Form Usage</a></h3>
175
+ <p>The component is form-associated. Set a <code>name</code> and the raw markdown is included in the form's <code>FormData</code>. <code>required</code> participates in the browser's standard validation when the editor is empty.</p>
176
+ <div class="row -mx">
177
+ <div class="col m-span-12 px">
178
+ <k-card label="HTML">
179
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;mdForm&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Comment:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;comment&quot;</span> <span class="hljs-attr">required</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">&quot;Markdown supported&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;controls-top&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;d-f&quot;</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-bold</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-bold</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">k-md-italic</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-md-italic</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span><br> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;submit&quot;</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></code></pre>
180
+ </k-card>
181
+ </div>
182
+ <div class="col m-span-12 px">
183
+ <k-card label="Output">
184
+ <form id="mdForm">
185
+ <label class="d-b mb">Comment:</label>
186
+ <k-markdown-editor name="comment" required placeholder="Markdown supported">
187
+ <div slot="controls-top" class="d-f">
188
+ <k-md-bold></k-md-bold>
189
+ <k-md-italic></k-md-italic>
190
+ </div>
191
+ </k-markdown-editor>
192
+ <div class="mt">
193
+ <button type="submit">Submit</button>
194
+ </div>
195
+ </form>
196
+ <p class="mt">Form data: <span id="mdOut">(submit to see)</span></p>
197
+ <script type="module">
198
+ document.getElementById('mdForm').addEventListener('submit', (e) => {
199
+ e.preventDefault();
200
+ const fd = new FormData(e.target);
201
+ document.getElementById('mdOut').textContent = JSON.stringify([...fd.entries()]);
202
+ });
203
+ </script>
204
+ </k-card>
205
+ </div>
206
+ </div>
207
+
208
+ <h3 id="disabled"><a href="#disabled" class="no-link">Disabled / Read-only</a></h3>
209
+ <p>The <code>disabled</code> attribute prevents user interaction (including switching tabs) and excludes the field from form submission. The <code>readonly</code> attribute keeps the editor visible and lets the user switch to Preview, but the textarea cannot be edited; the value is still submitted with the form.</p>
210
+ <div class="row -mx">
211
+ <div class="col m-span-12 px">
212
+ <k-card label="Output">
213
+ <k-markdown-editor disabled value="This is **disabled**." class="mb"></k-markdown-editor>
214
+ <k-markdown-editor readonly value="This is _read-only_ &mdash; check the Preview tab."></k-markdown-editor>
215
+ </k-card>
216
+ </div>
217
+ </div>
218
+
219
+ <h3 id="tagFiltering"><a href="#tagFiltering" class="no-link">Tag Filtering</a></h3>
220
+ <p>Inline HTML the user (or a custom control) types is filtered by either an allowlist or a denylist. The two attributes are mutually exclusive &mdash; if both are set, <code>allowed-tags</code> wins and a console warning fires.</p>
221
+
222
+ <h4>allowed-tags</h4>
223
+ <p>Permits only the tags you list. Anything else is unwrapped (its text survives, the wrapper is dropped). Type the markdown below and flip to Preview &mdash; only <code>strong</code>, <code>em</code>, <code>p</code>, and <code>br</code> get through; the <code>&lt;img&gt;</code>, <code>&lt;table&gt;</code>, and <code>&lt;script&gt;</code> are scrubbed out.</p>
224
+ <div class="row -mx">
225
+ <div class="col m-span-12 px">
226
+ <k-card label="HTML">
227
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">allowed-tags</span>=<span class="hljs-string">&quot;strong,em,p,br&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
228
+ </k-card>
229
+ </div>
230
+ <div class="col m-span-12 px">
231
+ <k-card label="Output">
232
+ <k-markdown-editor
233
+ allowed-tags="strong,em,p,br"
234
+ value="**Bold** and _italic_ pass through.&#10;&#10;An image is stripped: ![alt](https://example.com/x.png)&#10;&#10;A table is stripped:&#10;&#10;| a | b |&#10;| --- | --- |&#10;| 1 | 2 |&#10;&#10;A &lt;script&gt;alert('xss')&lt;/script&gt; is always nuked."
235
+ ></k-markdown-editor>
236
+ </k-card>
237
+ </div>
238
+ </div>
239
+
240
+ <h4>disallowed-tags</h4>
241
+ <p>Allows everything <em>except</em> the tags you list &mdash; useful when you only want to ban a couple of things rather than enumerate the whole set. Below, images are blocked but everything else (tables, code, etc.) renders normally.</p>
242
+ <div class="row -mx">
243
+ <div class="col m-span-12 px">
244
+ <k-card label="HTML">
245
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown-editor</span> <span class="hljs-attr">disallowed-tags</span>=<span class="hljs-string">&quot;img&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown-editor</span>&gt;</span></code></pre>
246
+ </k-card>
247
+ </div>
248
+ <div class="col m-span-12 px">
249
+ <k-card label="Output">
250
+ <k-markdown-editor
251
+ disallowed-tags="img"
252
+ value="Tables still work:&#10;&#10;| a | b |&#10;| --- | --- |&#10;| 1 | 2 |&#10;&#10;Images do not: ![alt](https://example.com/x.png)"
253
+ ></k-markdown-editor>
254
+ </k-card>
255
+ </div>
256
+ </div>
257
+
258
+ <h3 id="customControls"><a href="#customControls" class="no-link">Custom Controls</a></h3>
259
+ <p>To write your own toolbar button, extend <code>MarkdownEditorControl</code>. Override <code>command()</code> and use the helpers (<code>wrapSelection</code>, <code>insertLinePrefix</code>, <code>replaceSelection</code>, <code>insertAtCursor</code>) to manipulate the textarea. The base class hides the control automatically when the editor flips to Preview mode.</p>
260
+ <p>Below is a tiny control that drops a horizontal rule (<code>---</code> on its own line) at the cursor &mdash; not common enough to ship as a built-in, but a clean example of using <code>insertAtCursor</code>.</p>
261
+ <k-card label="JavaScript">
262
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">MarkdownEditorControl</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'./markdownEditorControls/MarkdownEditorControl.js'</span>;
263
+ <span class="hljs-keyword">import</span> { html } <span class="hljs-keyword">from</span> <span class="hljs-string">'./lit-all.min.js'</span>;
264
+
265
+ <span class="hljs-comment">// Inserts a horizontal rule (`---`) on its own line at the cursor.</span>
266
+ <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MarkdownHr</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">MarkdownEditorControl</span> {
267
+ <span class="hljs-title function_">constructor</span>(<span class="hljs-params"></span>) {
268
+ <span class="hljs-variable language_">super</span>();
269
+ <span class="hljs-variable language_">this</span>.<span class="hljs-property">label</span> = <span class="hljs-string">'Horizontal rule'</span>;
270
+ }
271
+ <span class="hljs-title function_">command</span>(<span class="hljs-params"></span>) {
272
+ <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">insertAtCursor</span>(<span class="hljs-string">'\n\n---\n\n'</span>);
273
+ }
274
+ <span class="hljs-title function_">render</span>(<span class="hljs-params"></span>) {
275
+ <span class="hljs-keyword">return</span> html<span class="hljs-string">`
276
+ &lt;button type=&quot;button&quot; class=<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.<span class="hljs-property">btnClass</span>}</span> @click=<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.<span class="hljs-property">handleClick</span>}</span>&gt;&mdash;&lt;/button&gt;
277
+ `</span>;
278
+ }
279
+ }
280
+ customElements.<span class="hljs-title function_">define</span>(<span class="hljs-string">'k-md-hr'</span>, <span class="hljs-title class_">MarkdownHr</span>);</code></pre>
281
+ </k-card>
282
+
283
+ <h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
284
+
285
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
286
+ <h6>Extends <a href="./shadow-component.html">ShadowComponent</a></h6>
287
+ <h5><code>new MarkdownEditor()</code></h5>
288
+
289
+ <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
290
+ <ul>
291
+ <li><a href="./shadow-component.html">ShadowComponent</a></li>
292
+ <li><code>src/utils/renderMarkdown.js</code> (thin wrapper around marked)</li>
293
+ <li><code>src/utils/marked.esm.js</code> (vendored marked, ~42KB)</li>
294
+ <li><a href="../utils/sanitizeHtml.html"><code>sanitizeHtml</code></a> utility</li>
295
+ </ul>
296
+
297
+ <h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
298
+ <h5><code>value<i>: String</i></code></h5>
299
+ <p>The current markdown source. Reading returns whatever the user has typed; writing replaces the editor content. Initial value comes from the <code>value</code> attribute. (Children are reserved for slotted controls and are <em>not</em> read as the editor value &mdash; otherwise control content like icons / labels would leak in.)</p>
300
+
301
+ <h5><code>name<i>: String</i></code></h5>
302
+ <p>The form field name. When set inside a <code>&lt;form&gt;</code>, the component participates in form submission. The submitted value is the raw markdown string. Syncs to <code>name</code> attribute.</p>
303
+
304
+ <h5><code>placeholder<i>: String</i></code></h5>
305
+ <p>Placeholder text shown in the textarea when the editor is empty. Syncs to <code>placeholder</code> attribute.</p>
306
+
307
+ <h5><code>disabled<i>: Boolean</i></code></h5>
308
+ <p>When <code>true</code>, the editor is non-interactive (textarea is disabled, mode tabs are disabled) and excluded from form submission. Syncs to <code>disabled</code> attribute.</p>
309
+
310
+ <h5><code>required<i>: Boolean</i></code></h5>
311
+ <p>When <code>true</code> and the editor is empty, the element reports a <code>valueMissing</code> validity error. Syncs to <code>required</code> attribute.</p>
312
+
313
+ <h5><code>readonly<i>: Boolean</i></code></h5>
314
+ <p>When <code>true</code>, the user can switch to Preview but cannot edit the textarea. Form submission still includes the value. Syncs to <code>readonly</code> attribute.</p>
315
+
316
+ <h5><code>mode<i>: String</i></code></h5>
317
+ <p>Either <code>'write'</code> (default) or <code>'preview'</code>. Syncs to <code>mode</code> attribute.</p>
318
+
319
+ <h5><code>allowedTags<i>: String</i></code></h5>
320
+ <p>Comma-separated <em>allowlist</em> for the rendered preview. Tags outside the list are unwrapped (their text survives, the wrapper is dropped). Pass <code>"*"</code> to allow every tag. Empty / unset uses sanitizeHtml's default (headings, paragraphs, lists, links, code, images, tables, etc.). Mutually exclusive with <code>disallowed-tags</code> &mdash; setting both warns and <code>allowed-tags</code> wins. Syncs to <code>allowed-tags</code> attribute.</p>
321
+
322
+ <h5><code>disallowedTags<i>: String</i></code></h5>
323
+ <p>Comma-separated <em>denylist</em>. Allows everything except the listed tags &mdash; useful when you want to block a couple of tags rather than enumerate the whole allowed set. Mutually exclusive with <code>allowed-tags</code>. Syncs to <code>disallowed-tags</code> attribute.</p>
324
+
325
+ <h5><code>scriptsEnabled<i>: Boolean</i></code></h5>
326
+ <p>When this attribute is present, <code>&lt;script&gt;</code> tags survive sanitization (provided they also pass the allow/deny check). When absent (the default), <code>&lt;script&gt;</code> &mdash; including its content &mdash; is always stripped to prevent XSS, regardless of any other configuration. Other dangerous tags (<code>&lt;iframe&gt;</code>, <code>&lt;style&gt;</code>, etc.) are always stripped. Only enable this for trusted input. Syncs to <code>scripts-enabled</code> attribute.</p>
327
+
328
+ <h5><code>controls<i>: String</i></code></h5>
329
+ <p>Activates a built-in toolbar. Accepted values:</p>
330
+ <ul>
331
+ <li><code>'none'</code> / <code>''</code> (default) &mdash; no built-in toolbar; use named slots to build your own.</li>
332
+ <li><code>'minimal'</code> &mdash; heading menu (H1, H3, H5), bold, italic, link, bulleted list, numbered list.</li>
333
+ <li><code>'normal'</code> &mdash; full heading menu (H1&ndash;H6), bold, italic, quote, code, link, lists.</li>
334
+ <li><code>'full'</code> &mdash; everything from <code>normal</code> plus strikethrough, image, table builder, and speech-to-text.</li>
335
+ </ul>
336
+ <p>The chosen set renders as fallback content inside the <code>controls-top</code> slot. Providing your own slot content replaces it. Setting a non-empty value also dynamically imports every built-in control module so consumers don't have to remember which imports each preset needs.</p>
337
+
338
+ <h5><code>renderedHtml<i>: String</i></code> (getter)</h5>
339
+ <p>The current value rendered to HTML by <code>renderMarkdown</code> (marked, with GFM enabled) and then run through <code>sanitizeHtml</code> using <code>allowedTags</code>.</p>
340
+
341
+ <h5><code>isEmpty<i>: Boolean</i></code> (getter)</h5>
342
+ <p><code>true</code> when <code>value.trim()</code> is empty.</p>
343
+
344
+ <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
345
+ <h5><code>focus()<i>: void</i></code></h5>
346
+ <p>Switches to write mode (if necessary) and focuses the textarea.</p>
347
+
348
+ <h5><code>blur()<i>: void</i></code></h5>
349
+ <p>Removes focus from the textarea.</p>
350
+
351
+ <h5><code>clear()<i>: void</i></code></h5>
352
+ <p>Empties the editor (equivalent to <code>el.value = ''</code>).</p>
353
+
354
+ <h5><code>setMode(mode)<i>: void</i></code></h5>
355
+ <p>Sets the mode to <code>'write'</code> or <code>'preview'</code>. Other values are ignored.</p>
356
+
357
+ <h5><code>togglePreview()<i>: void</i></code></h5>
358
+ <p>Flips between <code>'write'</code> and <code>'preview'</code>.</p>
359
+
360
+ <h5><code>getSelection()<i>: { start, end, text }</i></code></h5>
361
+ <p>Returns the current textarea selection. Used by control subclasses; in preview mode returns a zero-length selection at index 0.</p>
362
+
363
+ <h5><code>replaceSelection(text, options)<i>: void</i></code></h5>
364
+ <p>Replaces the current selection with <code>text</code>. <code>options.selectInserted</code> (default <code>true</code>) controls whether the inserted text ends up selected or whether the cursor moves to the end of it.</p>
365
+
366
+ <h5><code>wrapSelection(prefix, suffix, placeholder)<i>: void</i></code></h5>
367
+ <p>Wraps the current selection with <code>prefix</code>/<code>suffix</code>. If nothing is selected, inserts <code>placeholder</code> between them and selects it so the user can immediately overtype. <code>suffix</code> defaults to <code>prefix</code>.</p>
368
+
369
+ <h5><code>insertAtCursor(text)<i>: void</i></code></h5>
370
+ <p>Inserts <code>text</code> at the cursor position; selection is collapsed to the end of the inserted text.</p>
371
+
372
+ <h5><code>insertLinePrefix(prefix)<i>: void</i></code></h5>
373
+ <p>Prefixes every selected line (or the current line if no selection) with <code>prefix</code>. Used for headings, blockquotes, list items, etc.</p>
374
+
375
+ <h3 id="cssVariables"><a href="#cssVariables" class="no-link">CSS Variables</a></h3>
376
+ <table class="b">
377
+ <thead><tr><th>Variable</th><th>Default</th><th>Description</th></tr></thead>
378
+ <tbody>
379
+ <tr><td><code>--min_height</code></td><td><code>6rem</code></td><td>Minimum height of the textarea / preview area</td></tr>
380
+ <tr><td><code>--max_height</code></td><td><code>24rem</code></td><td>Maximum height before the editor scrolls internally</td></tr>
381
+ <tr><td><code>--padding</code></td><td><code>0.5rem 0.75rem</code></td><td>Padding inside the textarea / preview</td></tr>
382
+ <tr><td><code>--border</code></td><td><code>1px solid var(--c_border)</code></td><td>Border around the editor</td></tr>
383
+ </tbody>
384
+ </table>
385
+ <p class="mt">The component also uses kempo-css's global <code>--c_border</code>, <code>--c_bg</code>, <code>--tc</code>, <code>--tc_muted</code>, <code>--radius</code>, <code>--animation_ms</code>, and <code>--focus_shadow</code>; override those globally for site-wide tuning.</p>
386
+
387
+ <h3 id="events"><a href="#events" class="no-link">Events</a></h3>
388
+ <h5><code>input</code></h5>
389
+ <p>Fires on every keystroke (or programmatic change via the selection-manipulation API). <code>detail.value</code> is the current markdown.</p>
390
+
391
+ <h5><code>change</code></h5>
392
+ <p>Fires when the textarea blurs. <code>detail.value</code> is the markdown at the moment focus left.</p>
393
+
394
+ <h5><code>mode-change</code></h5>
395
+ <p>Fires when the user switches between Write and Preview tabs. <code>detail.mode</code> is the new mode.</p>
396
+
397
+ <h3 id="controls"><a href="#controls" class="no-link">Built-in Controls</a></h3>
398
+ <p>Each control is a separate custom element under <code>src/components/markdownEditorControls/</code>. Import the ones you want and slot them into <code>controls-top</code> or <code>controls-bottom</code>.</p>
399
+ <table class="b">
400
+ <thead><tr><th>Tag</th><th>File</th><th>Inserts</th></tr></thead>
401
+ <tbody>
402
+ <tr><td><code>&lt;k-md-bold&gt;</code></td><td><code>Bold.js</code></td><td><code>**bold**</code></td></tr>
403
+ <tr><td><code>&lt;k-md-italic&gt;</code></td><td><code>Italic.js</code></td><td><code>_italic_</code></td></tr>
404
+ <tr><td><code>&lt;k-md-strikethrough&gt;</code></td><td><code>Strikethrough.js</code></td><td><code>~~strike~~</code> &mdash; GFM strikethrough; renders to <code>&lt;del&gt;</code>.</td></tr>
405
+ <tr><td><code>&lt;k-md-heading&gt;</code></td><td><code>Heading.js</code></td><td><code>## </code> on the current line; <code>level</code> attribute (1&ndash;6) selects heading depth</td></tr>
406
+ <tr><td><code>&lt;k-md-code&gt;</code></td><td><code>Code.js</code></td><td>Inline backticks for a single line, fenced <code>```</code> blocks for multi-line selections</td></tr>
407
+ <tr><td><code>&lt;k-md-link&gt;</code></td><td><code>Link.js</code></td><td><code>[text](url)</code> with the URL portion pre-selected</td></tr>
408
+ <tr><td><code>&lt;k-md-image&gt;</code></td><td><code>Image.js</code></td><td>Opens a popover (URL + alt text) and inserts <code>![alt](url)</code> at the cursor. The popover type is configurable via <code>popup="dropdown"</code> (default, anchored next to the button) or <code>popup="dialog"</code> (modal Dialog).</td></tr>
409
+ <tr><td><code>&lt;k-md-table&gt;</code></td><td><code>Table.js</code></td><td>Opens a small dropdown with <em>cols × rows</em> inputs and a Create button. Inserts a GFM markdown table (with <code>Header 1</code>, <code>Header 2</code>&hellip; placeholders and empty body cells) at the cursor.</td></tr>
410
+ <tr><td><code>&lt;k-md-menu&gt;</code></td><td><code>Menu.js</code></td><td>Renders a single toolbar button that opens a Dropdown containing whatever child controls you slot into the default slot. The trigger button content is fully customizable via the <code>trigger</code> slot (defaults to a <code>menu</code> icon); <code>label</code> sets the accessible name.</td></tr>
411
+ <tr><td><code>&lt;k-md-format-block&gt;</code></td><td><code>FormatBlock.js</code></td><td>Heading-block picker designed to go inside a <code>&lt;k-md-menu&gt;</code>. <code>tag="h1"</code>&hellip;<code>"h6"</code> sets the heading level on the current line(s), swapping out any existing heading.</td></tr>
412
+ <tr><td><code>&lt;k-md-speech-to-text&gt;</code></td><td><code>SpeechToText.js</code></td><td>Reuses the standalone <a href="./speech-to-text.html"><code>&lt;k-speech-to-text&gt;</code></a> component &mdash; click the mic to start recording, click again to stop. The final transcript is inserted at the cursor when the user finishes speaking. Forwards <code>language</code> and <code>continuous</code> attributes through.</td></tr>
413
+ <tr><td><code>&lt;k-md-bullet-list&gt;</code></td><td><code>BulletList.js</code></td><td><code>- </code> prefix on each selected line</td></tr>
414
+ <tr><td><code>&lt;k-md-numbered-list&gt;</code></td><td><code>NumberedList.js</code></td><td><code>1. </code>, <code>2. </code>, &hellip; numbering each non-empty selected line</td></tr>
415
+ <tr><td><code>&lt;k-md-quote&gt;</code></td><td><code>Quote.js</code></td><td><code>&gt; </code> prefix on each selected line</td></tr>
416
+ </tbody>
417
+ </table>
418
+ <p class="mt">All controls extend <code>MarkdownEditorControl</code> &mdash; see <a href="#customControls">Custom Controls</a> to write your own.</p>
419
+ </content>
420
+ <content location="scripts">
421
+ <script type="module" src="{{pathToRoot}}src/components/MarkdownEditor.js"></script>
422
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Bold.js"></script>
423
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Italic.js"></script>
424
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Strikethrough.js"></script>
425
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/SpeechToText.js"></script>
426
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Heading.js"></script>
427
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Code.js"></script>
428
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Link.js"></script>
429
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Image.js"></script>
430
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Table.js"></script>
431
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Menu.js"></script>
432
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/FormatBlock.js"></script>
433
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/BulletList.js"></script>
434
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/NumberedList.js"></script>
435
+ <script type="module" src="{{pathToRoot}}src/components/markdownEditorControls/Quote.js"></script>
436
+ <script type="module" src="{{pathToRoot}}src/components/Accordion.js"></script>
437
+ <script type="module" src="{{pathToRoot}}src/components/Card.js"></script>
438
+ </content>
439
+ </page>