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
@@ -0,0 +1,161 @@
1
+ <page pageName="Markdown" title="Markdown - 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="#valueAttr">Value Attribute</a><br />
10
+ <a href="#breaks">Line Breaks</a><br />
11
+ <a href="#tagFiltering">Tag Filtering</a><br />
12
+ <a href="#dynamic">Updating At Runtime</a><br />
13
+
14
+ <h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
15
+ <a href="#constructor">Constructor</a><br />
16
+ <a href="#requirements">Requirements</a><br />
17
+ <a href="#properties">Properties</a><br />
18
+ <a href="#methods">Methods</a><br />
19
+ </div>
20
+ </k-accordion-panel>
21
+ </k-accordion>
22
+
23
+ <p>The <code>k-markdown</code> component takes its child text content (or a <code>value</code> attribute), parses it as markdown using <a href="https://marked.js.org" target="_blank">marked</a> (full CommonMark + GFM &mdash; tables, task lists, fenced code, autolinks, inline HTML), runs the result through <a href="{{pathToRoot}}utils/sanitizeHtml.html"><code>sanitizeHtml</code></a>, and renders the HTML in place. It renders to the light DOM so page-level typography styles cascade naturally into headings, paragraphs, tables, and so on.</p>
24
+ <p>Common leading whitespace is automatically stripped from the source so you can indent the markdown to match the surrounding HTML without breaking parsing.</p>
25
+
26
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
27
+ <p>Drop markdown text inside the tag and it will be replaced with the rendered HTML when the element connects.</p>
28
+ <div class="row -mx mb">
29
+ <div class="col d-span-6 m-span-12 px">
30
+ <k-card label="HTML">
31
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown</span>&gt;</span><br> # Hello World<br><br> This is **bold** and *italic* text.<br><br> - list item 1<br> - list item 2<br><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown</span>&gt;</span></code></pre>
32
+ </k-card>
33
+ </div>
34
+ <div class="col d-span-6 m-span-12 px">
35
+ <k-card label="Output">
36
+ <k-markdown>
37
+ # Hello World
38
+
39
+ This is **bold** and *italic* text.
40
+
41
+ - list item 1
42
+ - list item 2
43
+ </k-markdown>
44
+ </k-card>
45
+ </div>
46
+ </div>
47
+
48
+ <h3 id="valueAttr"><a href="#valueAttr" class="no-link">Value Attribute</a></h3>
49
+ <p>Skip the children and pass markdown directly via the <code>value</code> attribute when you only need a single line. The attribute also takes precedence over child content if both are provided.</p>
50
+ <div class="row -mx mb">
51
+ <div class="col d-span-6 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</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;Visit [Kempo UI](https://github.com/dustinpoissant/kempo-ui) for more.&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown</span>&gt;</span></code></pre>
54
+ </k-card>
55
+ </div>
56
+ <div class="col d-span-6 m-span-12 px">
57
+ <k-card label="Output">
58
+ <k-markdown value="Visit [Kempo UI](https://github.com/dustinpoissant/kempo-ui) for more."></k-markdown>
59
+ </k-card>
60
+ </div>
61
+ </div>
62
+
63
+ <h3 id="breaks"><a href="#breaks" class="no-link">Line Breaks</a></h3>
64
+ <p>By default, single newlines are joined into a paragraph (CommonMark behavior). Add the <code>breaks</code> attribute to convert single newlines to <code>&lt;br&gt;</code> &mdash; the Slack/iMessage convention &mdash; useful for chat-style content.</p>
65
+ <div class="row -mx mb">
66
+ <div class="col d-span-6 m-span-12 px">
67
+ <k-card label="HTML">
68
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown</span> <span class="hljs-attr">breaks</span>&gt;</span><br> Line one<br> Line two<br> Line three<br><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown</span>&gt;</span></code></pre>
69
+ </k-card>
70
+ </div>
71
+ <div class="col d-span-6 m-span-12 px">
72
+ <k-card label="Output">
73
+ <k-markdown breaks>
74
+ Line one
75
+ Line two
76
+ Line three
77
+ </k-markdown>
78
+ </k-card>
79
+ </div>
80
+ </div>
81
+
82
+ <h3 id="tagFiltering"><a href="#tagFiltering" class="no-link">Tag Filtering</a></h3>
83
+ <p>The rendered HTML is sanitized with the same <code>allowed-tags</code> / <code>disallowed-tags</code> controls used by <a href="{{pathToRoot}}components/markdown-editor.html"><code>k-markdown-editor</code></a>. Pass a comma-separated allowlist (or <code>*</code> for everything), or a comma-separated denylist &mdash; the two are mutually exclusive. <code>&lt;script&gt;</code>, <code>&lt;iframe&gt;</code>, <code>&lt;style&gt;</code>, and similar tags are always stripped unless you opt in with <code>scripts-enabled</code> (which only restores <code>&lt;script&gt;</code>).</p>
84
+ <div class="row -mx mb">
85
+ <div class="col d-span-6 m-span-12 px">
86
+ <k-card label="HTML">
87
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown</span> <span class="hljs-attr">allowed-tags</span>=<span class="hljs-string">&quot;p,strong,em,a&quot;</span>&gt;</span><br> Only **bold**, *italic*, [links](https://example.com), and paragraphs are kept.<br><br> # This heading will be unwrapped<br><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown</span>&gt;</span></code></pre>
88
+ </k-card>
89
+ </div>
90
+ <div class="col d-span-6 m-span-12 px">
91
+ <k-card label="Output">
92
+ <k-markdown allowed-tags="p,strong,em,a">
93
+ Only **bold**, *italic*, [links](https://example.com), and paragraphs are kept.
94
+
95
+ # This heading will be unwrapped
96
+ </k-markdown>
97
+ </k-card>
98
+ </div>
99
+ </div>
100
+
101
+ <h3 id="dynamic"><a href="#dynamic" class="no-link">Updating At Runtime</a></h3>
102
+ <p>Set the <code>value</code> property in JavaScript to re-render with new markdown.</p>
103
+ <div class="row -mx mb">
104
+ <div class="col d-span-6 m-span-12 px">
105
+ <k-card label="HTML">
106
+ <pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">k-markdown</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;dynamicMd&quot;</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;Click the button.&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-markdown</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;dynamicMdBtn&quot;</span>&gt;</span>Update<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span>&gt;</span><span class="language-javascript"><br> <span class="hljs-keyword">const</span> md = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;dynamicMd&#x27;</span>);<br> <span class="hljs-keyword">const</span> btn = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;dynamicMdBtn&#x27;</span>);<br> <span class="hljs-keyword">let</span> toggled = <span class="hljs-literal">false</span>;<br> btn.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-function">() =&gt;</span> {<br> toggled = !toggled;<br> md.<span class="hljs-property">value</span> = toggled<br> ? <span class="hljs-string">&#x27;## Updated!\n\nThis was set via JavaScript with **markdown** and a [link](https://example.com).&#x27;</span><br> : <span class="hljs-string">&#x27;Click the button.&#x27;</span>;<br> });<br></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
107
+ </k-card>
108
+ </div>
109
+ <div class="col d-span-6 m-span-12 px">
110
+ <k-card label="Output">
111
+ <k-markdown id="dynamicMd" value="Click the button."></k-markdown>
112
+ <button id="dynamicMdBtn" class="btn primary mt">Update</button>
113
+ </k-card>
114
+ </div>
115
+ </div>
116
+
117
+ <h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
118
+
119
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
120
+ <h6>Extends LightComponent</h6>
121
+
122
+ <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
123
+ <ul>
124
+ <li><a href="{{pathToRoot}}components/light-component.html">LightComponent</a></li>
125
+ <li><code>marked</code> (vendored at <code>src/utils/marked.esm.js</code>) via <a href="{{pathToRoot}}utils/renderMarkdown.js"><code>renderMarkdown</code></a></li>
126
+ <li><a href="{{pathToRoot}}utils/sanitizeHtml.html"><code>sanitizeHtml</code></a></li>
127
+ </ul>
128
+
129
+ <h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
130
+ <h5><code>value<i>: string</i></code></h5>
131
+ <p>The markdown source. If empty when the element connects, the component falls back to the dedented child text content. Setting this property at runtime re-renders the output.</p>
132
+ <h5><code>breaks<i>: boolean</i></code></h5>
133
+ <p>When <code>true</code>, single newlines convert to <code>&lt;br&gt;</code> instead of being joined into a paragraph. Defaults to <code>false</code>. Reflects to the <code>breaks</code> attribute.</p>
134
+ <h5><code>allowedTags<i>: string</i></code></h5>
135
+ <p>Comma-separated allowlist for the rendered HTML. Pass <code>*</code> to allow every tag. Mutually exclusive with <code>disallowedTags</code> &mdash; if both are set, <code>allowedTags</code> wins and a console warning is logged. Reflects to the <code>allowed-tags</code> attribute.</p>
136
+ <h5><code>disallowedTags<i>: string</i></code></h5>
137
+ <p>Comma-separated denylist &mdash; everything is allowed except the listed tags. Reflects to the <code>disallowed-tags</code> attribute.</p>
138
+ <h5><code>scriptsEnabled<i>: boolean</i></code></h5>
139
+ <p>When present, <code>&lt;script&gt;</code> tags survive sanitization (provided they also pass the allow/deny tag check). Defaults to off so script content can never reach the DOM. Reflects to the <code>scripts-enabled</code> attribute.</p>
140
+
141
+ <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
142
+ <h5><code>renderedHtml<i>: string</i></code> (getter)</h5>
143
+ <p>Returns the sanitized HTML string that was rendered &mdash; useful for copying the output or feeding it into another component.</p>
144
+ </content>
145
+ <content location="scripts">
146
+ <script type="module" src="{{pathToRoot}}src/components/Markdown.js"></script>
147
+ <script type="module" src="{{pathToRoot}}src/components/Accordion.js"></script>
148
+ <script type="module" src="{{pathToRoot}}src/components/Card.js"></script>
149
+ <script type="module">
150
+ const md = document.getElementById('dynamicMd');
151
+ const btn = document.getElementById('dynamicMdBtn');
152
+ let toggled = false;
153
+ btn.addEventListener('click', () => {
154
+ toggled = !toggled;
155
+ md.value = toggled
156
+ ? '## Updated!\n\nThis was set via JavaScript with **markdown** and a [link](https://example.com).'
157
+ : 'Click the button.';
158
+ });
159
+ </script>
160
+ </content>
161
+ </page>
@@ -121,6 +121,9 @@
121
121
  <h5><code>dimension<i>: string</i></code></h5>
122
122
  <p>Restricts resizing to a specific dimension. Possible values are <code>"width"</code> (horizontal only) or <code>"height"</code> (vertical only). If not specified, both dimensions can be resized. Syncs with the <code>dimension</code> attribute.</p>
123
123
 
124
+ <h5><code>disabled<i>: Boolean</i></code></h5>
125
+ <p>When <code>true</code>, the resize handles can't capture pointer events &mdash; a drag can't start, and any in-progress drag handlers no-op. Handles fade to <code>opacity: 0.4</code> as a visual cue. Syncs with the <code>disabled</code> attribute.</p>
126
+
124
127
  <h3 id="slots"><a href="#slots" class="no-link">Slots</a></h3>
125
128
  <h5><code>default</code></h5>
126
129
  <p>The main content area that will be resizable. Content automatically gets scroll behavior when it overflows the container.</p>
@@ -59,6 +59,12 @@
59
59
  <p class="tc-muted">Container component with optional labels and border styling.</p>
60
60
  </a>
61
61
  </div>
62
+ <div class="span-12 t-span-6 d-span-4 px">
63
+ <a href="{{pathToRoot}}components/chat.html" class="card mb no-link d-b">
64
+ <h3 class="tc-primary">Chat</h3>
65
+ <p class="tc-muted">Chat-room widget with rich-text input, send-on-enter option, and per-message state tracking.</p>
66
+ </a>
67
+ </div>
62
68
  <div class="span-12 t-span-6 d-span-4 px">
63
69
  <a href="{{pathToRoot}}components/code-editor.html" class="card mb no-link d-b">
64
70
  <h3 class="tc-primary">Code Editor</h3>
@@ -119,6 +125,18 @@
119
125
  <p class="tc-muted">WYSIWYG editor with formatting tools and dual editing modes.</p>
120
126
  </a>
121
127
  </div>
128
+ <div class="span-12 t-span-6 d-span-4 px">
129
+ <a href="{{pathToRoot}}components/markdown.html" class="card mb no-link d-b">
130
+ <h3 class="tc-primary">Markdown</h3>
131
+ <p class="tc-muted">Renders the markdown text inside the element as sanitized HTML in place.</p>
132
+ </a>
133
+ </div>
134
+ <div class="span-12 t-span-6 d-span-4 px">
135
+ <a href="{{pathToRoot}}components/markdown-editor.html" class="card mb no-link d-b">
136
+ <h3 class="tc-primary">Markdown Editor</h3>
137
+ <p class="tc-muted">Markdown editor with Write/Preview tabs, slot-based toolbars, and a tiny safe parser (no inline HTML).</p>
138
+ </a>
139
+ </div>
122
140
  <div class="span-12 t-span-6 d-span-4 px">
123
141
  <a href="{{pathToRoot}}components/icon.html" class="card mb no-link d-b">
124
142
  <h3 class="tc-primary">Icon</h3>
@@ -331,6 +349,12 @@
331
349
  <p class="tc-muted">Type converters for Lit component properties and attributes.</p>
332
350
  </a>
333
351
  </div>
352
+ <div class="span-12 t-span-6 d-span-4 px">
353
+ <a href="{{pathToRoot}}utils/sanitizeHtml.html" class="card mb no-link d-b">
354
+ <h3 class="tc-primary">sanitizeHtml</h3>
355
+ <p class="tc-muted">Whitelist HTML sanitizer for user-submitted rich text. Strips unsafe tags, attributes, and URL schemes.</p>
356
+ </a>
357
+ </div>
334
358
  <div class="span-12 t-span-6 d-span-4 px">
335
359
  <a href="{{pathToRoot}}utils/string.html" class="card mb no-link d-b">
336
360
  <h3 class="tc-primary">string</h3>
@@ -51,6 +51,9 @@
51
51
  >Calendar<br><small>Component</small></a></k-filter-item>
52
52
  <k-filter-item filter-keywords="card components"><a
53
53
  href="{{pathToRoot}}components/card.html">Card<br><small>Component</small></a></k-filter-item>
54
+ <k-filter-item filter-keywords="chat message conversation messaging components"><a
55
+ href="{{pathToRoot}}components/chat.html"
56
+ >Chat<br><small>Component</small></a></k-filter-item>
54
57
  <k-filter-item filter-keywords="code editor codeeditor monaco components"><a
55
58
  href="{{pathToRoot}}components/code-editor.html"
56
59
  >Code Editor<br><small>Component</small></a></k-filter-item>
@@ -79,6 +82,12 @@
79
82
  <k-filter-item filter-keywords="html editor components"><a
80
83
  href="{{pathToRoot}}components/html-editor.html">HTML
81
84
  Editor<br><small>Component</small></a></k-filter-item>
85
+ <k-filter-item filter-keywords="markdown render parser marked gfm components"><a
86
+ href="{{pathToRoot}}components/markdown.html"
87
+ >Markdown<br><small>Component</small></a></k-filter-item>
88
+ <k-filter-item filter-keywords="markdown editor markdowneditor write preview marked gfm tables components"><a
89
+ href="{{pathToRoot}}components/markdown-editor.html"
90
+ >Markdown Editor<br><small>Component</small></a></k-filter-item>
82
91
  <k-filter-item filter-keywords="icon components"><a
83
92
  href="{{pathToRoot}}components/icon.html">Icon<br><small>Component</small></a></k-filter-item>
84
93
  <k-filter-item filter-keywords="import components"><a
@@ -212,6 +221,9 @@
212
221
  <k-filter-item filter-keywords="propconverters prop converters utils utility"><a
213
222
  href="{{pathToRoot}}utils/propConverters.html"
214
223
  >propConverters<br><small>Utility</small></a></k-filter-item>
224
+ <k-filter-item filter-keywords="sanitizehtml sanitize html xss security utils utility"><a
225
+ href="{{pathToRoot}}utils/sanitizeHtml.html"
226
+ >sanitizeHtml<br><small>Utility</small></a></k-filter-item>
215
227
  <k-filter-item filter-keywords="string utils utility"><a
216
228
  href="{{pathToRoot}}utils/string.html">string<br><small>Utility</small></a></k-filter-item>
217
229
  <k-filter-item filter-keywords="theme utils utility"><a
@@ -272,6 +284,7 @@
272
284
  <a href="{{pathToRoot}}components/aside.html">Aside</a>
273
285
  <a href="{{pathToRoot}}components/calendar.html">Calendar</a>
274
286
  <a href="{{pathToRoot}}components/card.html">Card</a>
287
+ <a href="{{pathToRoot}}components/chat.html">Chat</a>
275
288
  <a href="{{pathToRoot}}components/code-editor.html">Code Editor</a>
276
289
  <a href="{{pathToRoot}}components/combobox.html">Combobox</a>
277
290
  <a href="{{pathToRoot}}components/color-picker.html">ColorPicker</a>
@@ -282,6 +295,8 @@
282
295
  <a href="{{pathToRoot}}components/filter-list.html">Filter List</a>
283
296
  <a href="{{pathToRoot}}components/focus-capture.html">FocusCapture</a>
284
297
  <a href="{{pathToRoot}}components/html-editor.html">HTML Editor</a>
298
+ <a href="{{pathToRoot}}components/markdown.html">Markdown</a>
299
+ <a href="{{pathToRoot}}components/markdown-editor.html">Markdown Editor</a>
285
300
  <a href="{{pathToRoot}}components/icon.html">Icon</a>
286
301
  <a href="{{pathToRoot}}components/import.html">Import</a>
287
302
  <a href="{{pathToRoot}}components/nav.html">Nav</a>
@@ -326,6 +341,7 @@
326
341
  <a href="{{pathToRoot}}utils/formatTimestamp.html">formatTimestamp</a>
327
342
  <a href="{{pathToRoot}}utils/object.html">object</a>
328
343
  <a href="{{pathToRoot}}utils/propConverters.html">propConverters</a>
344
+ <a href="{{pathToRoot}}utils/sanitizeHtml.html">sanitizeHtml</a>
329
345
  <a href="{{pathToRoot}}utils/string.html">string</a>
330
346
  <a href="{{pathToRoot}}utils/theme.html">theme</a>
331
347
  <a href="{{pathToRoot}}utils/type.html">type</a>
@@ -50,11 +50,11 @@
50
50
  <tr><td>0</td><td>0</td><td>Behind the page — sunken or background elements</td></tr>
51
51
  <tr><td>1</td><td>10</td><td><em>Buffer zone — reserved for customization</em></td></tr>
52
52
  <tr><td>2</td><td>20</td><td>Page default — normal document flow</td></tr>
53
- <tr><td>3</td><td>30</td><td>Fixed panels that sit below a navbar (push drawers)</td></tr>
53
+ <tr><td>3</td><td>30</td><td>Fixed panels that sit below a navbar (push drawers, dropdowns)</td></tr>
54
54
  <tr><td>4</td><td>40</td><td><em>Buffer zone — reserved for customization</em></td></tr>
55
55
  <tr><td>5</td><td>50</td><td>Fixed navbar (user-defined)</td></tr>
56
56
  <tr><td>6</td><td>60</td><td>Overlay drawers — above a fixed navbar</td></tr>
57
- <tr><td>7</td><td>70</td><td>Floating menus (dropdowns, popovers)</td></tr>
57
+ <tr><td>7</td><td>70</td><td><em>Buffer zone reserved for customization</em></td></tr>
58
58
  <tr><td>8</td><td>80</td><td>Full-screen modals and lightboxes</td></tr>
59
59
  <tr><td>9</td><td>90</td><td>Notification toasts</td></tr>
60
60
  <tr><td>10</td><td>100</td><td><em>Reserved — use only when absolutely necessary</em></td></tr>
@@ -79,7 +79,8 @@
79
79
  <td>30</td>
80
80
  <td>
81
81
  <a href="{{pathToRoot}}components/aside.html">Aside</a> (<code>main="push"</code>),
82
- <a href="{{pathToRoot}}components/side-panel.html">Side Panel</a>
82
+ <a href="{{pathToRoot}}components/side-panel.html">Side Panel</a>,
83
+ <a href="{{pathToRoot}}components/dropdown.html">Dropdown</a>
83
84
  </td>
84
85
  </tr>
85
86
  <tr>
@@ -89,13 +90,6 @@
89
90
  <a href="{{pathToRoot}}components/aside.html">Aside</a> (<code>main="overlay"</code>)
90
91
  </td>
91
92
  </tr>
92
- <tr>
93
- <td>7</td>
94
- <td>70</td>
95
- <td>
96
- <a href="{{pathToRoot}}components/dropdown.html">Dropdown</a>
97
- </td>
98
- </tr>
99
93
  <tr>
100
94
  <td>8</td>
101
95
  <td>80</td>
@@ -0,0 +1,73 @@
1
+ <page pageName="sanitizeHtml Utility" title="sanitizeHtml Utility - Kempo UI">
2
+ <content location="header"></content>
3
+ <content>
4
+ <h1>sanitizeHtml Utility</h1>
5
+
6
+ <k-accordion persistent-id="toc" class="b r mb">
7
+ <k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
8
+ <k-accordion-panel name="toc-panel">
9
+ <div class="m">
10
+ <h6>Usage</h6>
11
+ <a href="#description">Description</a><br />
12
+ <a href="#basicUsage">Basic Usage</a><br />
13
+ <a href="#api">API</a><br />
14
+ <a href="#defaults">Defaults</a><br />
15
+ <a href="#customizing">Customizing the Whitelist</a><br />
16
+ <a href="#serverNote">Server-Side Caveat</a><br />
17
+ </div>
18
+ </k-accordion-panel>
19
+ </k-accordion>
20
+
21
+ <h3 id="description"><a href="#description" class="no-link">Description</a></h3>
22
+ <p>A small whitelist-based HTML sanitizer for user-submitted rich text. Strips <code>&lt;script&gt;</code>, <code>&lt;style&gt;</code>, <code>&lt;iframe&gt;</code>, event-handler attributes, and unsafe URL schemes (<code>javascript:</code>, <code>data:</code>, etc.). Used by <a href="../components/chat.html"><code>&lt;k-chat&gt;</code></a> on every message it accepts, but works as a standalone util for any place you accept HTML from a contenteditable or rich-text editor.</p>
23
+
24
+ <div class="callout warning b r p mb">
25
+ <strong>This is a frontend defense, not a substitute for server sanitization.</strong>
26
+ Anything you persist or rebroadcast must be sanitized again on the server &mdash; an attacker can submit raw HTTP without ever loading your client code.
27
+ </div>
28
+
29
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
30
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> sanitizeHtml <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./utils/sanitizeHtml.js&#x27;</span>;<br><span class="hljs-keyword">const</span> dirty = <span class="hljs-string">&#x27;&lt;p&gt;Hello &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;&lt;b&gt;world&lt;/b&gt;&lt;/p&gt;&#x27;</span>;<br><span class="hljs-keyword">const</span> clean = <span class="hljs-title function_">sanitizeHtml</span>(dirty);<br><span class="hljs-comment">// → &#x27;&lt;p&gt;Hello &lt;b&gt;world&lt;/b&gt;&lt;/p&gt;&#x27;</span></code></pre>
31
+
32
+ <h3 id="api"><a href="#api" class="no-link">API</a></h3>
33
+
34
+ <h4><code>sanitizeHtml(html, options?)</code></h4>
35
+ <h5><code>html<i>: string</i></code></h5>
36
+ <p>Untrusted HTML to sanitize. <code>null</code>, <code>undefined</code>, and non-strings are coerced to <code>""</code>.</p>
37
+ <h5><code>options<i>: Object</i></code> (optional)</h5>
38
+ <ul>
39
+ <li><code>allowedTags</code> &mdash; <code>Set&lt;string&gt;</code> of UPPERCASE tag names (e.g. <code>"B"</code>, <code>"P"</code>). Overrides the default whitelist.</li>
40
+ <li><code>allowedAttrs</code> &mdash; <code>Set&lt;string&gt;</code> of attribute names allowed on every tag.</li>
41
+ <li><code>allowedAttrsPerTag</code> &mdash; <code>{ TAGNAME: Set&lt;string&gt; }</code> map of attributes allowed per specific tag.</li>
42
+ </ul>
43
+ <h5>Returns: <code>string</code></h5>
44
+ <p>The sanitized HTML. Disallowed elements are replaced by their (sanitized) children so user text is preserved; <code>&lt;script&gt;</code>, <code>&lt;style&gt;</code>, and similar are removed entirely (children too).</p>
45
+
46
+ <h3 id="defaults"><a href="#defaults" class="no-link">Defaults</a></h3>
47
+
48
+ <h5>Allowed tags (default)</h5>
49
+ <p><code>A</code>, <code>B</code>, <code>STRONG</code>, <code>I</code>, <code>EM</code>, <code>U</code>, <code>S</code>, <code>STRIKE</code>, <code>BR</code>, <code>P</code>, <code>DIV</code>, <code>SPAN</code>, <code>UL</code>, <code>OL</code>, <code>LI</code>, <code>BLOCKQUOTE</code>, <code>CODE</code>, <code>PRE</code>, <code>H1</code>&ndash;<code>H6</code></p>
50
+
51
+ <h5>Tags removed completely (children too)</h5>
52
+ <p><code>SCRIPT</code>, <code>STYLE</code>, <code>IFRAME</code>, <code>OBJECT</code>, <code>EMBED</code>, <code>LINK</code>, <code>META</code>, <code>BASE</code>, <code>FRAME</code>, <code>FRAMESET</code>, <code>NOSCRIPT</code>, <code>SVG</code>, <code>MATH</code></p>
53
+
54
+ <h5>Allowed attributes (default)</h5>
55
+ <ul>
56
+ <li>Globally: <code>class</code>, plus any <code>data-*</code></li>
57
+ <li>On <code>&lt;a&gt;</code>: <code>href</code>, <code>title</code>, <code>target</code>, <code>rel</code></li>
58
+ </ul>
59
+
60
+ <h5>URL handling</h5>
61
+ <p><code>href</code> and <code>src</code> values are checked against a safe-scheme regex; anything that doesn't match <code>http(s):</code>, <code>mailto:</code>, <code>tel:</code>, a relative path, or a fragment is removed. Links with <code>target="_blank"</code> automatically get <code>rel="noopener noreferrer"</code> to prevent reverse-tabnabbing.</p>
62
+
63
+ <h3 id="customizing"><a href="#customizing" class="no-link">Customizing the Whitelist</a></h3>
64
+ <p>Pass an options object to tighten or loosen the defaults:</p>
65
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> html = <span class="hljs-title function_">sanitizeHtml</span>(userInput, {<br> <span class="hljs-attr">allowedTags</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Set</span>([<span class="hljs-string">&#x27;B&#x27;</span>, <span class="hljs-string">&#x27;I&#x27;</span>, <span class="hljs-string">&#x27;EM&#x27;</span>, <span class="hljs-string">&#x27;STRONG&#x27;</span>, <span class="hljs-string">&#x27;A&#x27;</span>, <span class="hljs-string">&#x27;BR&#x27;</span>, <span class="hljs-string">&#x27;P&#x27;</span>]),<br> <span class="hljs-attr">allowedAttrsPerTag</span>: {<br> <span class="hljs-attr">A</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Set</span>([<span class="hljs-string">&#x27;href&#x27;</span>])<br> }<br>});</code></pre>
66
+
67
+ <h3 id="serverNote"><a href="#serverNote" class="no-link">Server-Side Caveat</a></h3>
68
+ <p>This util only protects content that flows through your client code. If your app accepts user-submitted HTML and stores or echoes it to other users, the server <strong>must</strong> independently sanitize before storing and again before sending it back. Battle-tested server libraries: <a href="https://github.com/cure53/DOMPurify" target="_blank" rel="noopener noreferrer">DOMPurify</a> (Node), <a href="https://github.com/apostrophecms/sanitize-html" target="_blank" rel="noopener noreferrer">sanitize-html</a> (Node), <a href="https://github.com/mozilla/bleach" target="_blank" rel="noopener noreferrer">Bleach</a> (Python), <a href="https://github.com/rgrove/sanitize" target="_blank" rel="noopener noreferrer">sanitize</a> (Ruby).</p>
69
+ </content>
70
+ <content location="scripts">
71
+ <script type="module" src="{{pathToRoot}}src/components/Accordion.js"></script>
72
+ </content>
73
+ </page>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M268-240 42-466l57-56 170 170 56 56-57 56Zm226 0L268-466l56-57 170 170 368-368 56 57-424 424Zm0-226-57-56 198-198 57 56-198 198Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m228-240 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T458-480L320-240h-92Zm360 0 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480L680-240h-92ZM362.5-517.5Q380-535 380-560t-17.5-42.5Q345-620 320-620t-42.5 17.5Q260-585 260-560t17.5 42.5Q295-500 320-500t42.5-17.5Zm360 0Q740-535 740-560t-17.5-42.5Q705-620 680-620t-42.5 17.5Q620-585 620-560t17.5 42.5Q655-500 680-500t42.5-17.5ZM680-560Zm-360 0Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M80-400v-80h800v80H80Zm340-160v-120H200v-120h560v120H540v120H420Zm0 400v-160h120v160H420Z"/></svg>
package/icons/send.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M280-160v-520H80v-120h520v120H400v520H280Zm360 0v-320H520v-120h360v120H760v320H640Z"/></svg>
package/llms.txt CHANGED
@@ -56,7 +56,8 @@ import { html, css, LitElement } from 'kempo-ui/src/lit-all.min.js';
56
56
  | `<k-aside>` `<k-aside-item>` `<k-aside-label>` `<k-aside-menu>` `<k-aside-spacer>` `<k-aside-toggle>` | `Aside.js` | Sidebar/drawer; `main="push"` or `main="overlay"`, `side="left|right"` | [aside.html](https://dustinpoissant.github.io/kempo-ui/components/aside.html) |
57
57
  | `<k-calendar>` | `Calendar.js` | Inline month-grid date picker widget; `mode="single"` (default) or `mode="range"`; `value` is ISO `YYYY-MM-DD` (single) or comma-separated pair (range); `min`/`max` bounds; form-associated; fires `change` | [calendar.html](https://dustinpoissant.github.io/kempo-ui/components/calendar.html) |
58
58
  | `<k-card>` | `Card.js` | Bordered container; optional `label` attribute | [card.html](https://dustinpoissant.github.io/kempo-ui/components/card.html) |
59
- | `<k-code-editor>` | `CodeEditor.js` | Monaco-powered code editor; `value`, `language`, `controls` attributes; form-associated; theme-aware | [code-editor.html](https://dustinpoissant.github.io/kempo-ui/components/code-editor.html) |
59
+ | `<k-chat>` | `Chat.js` | Chat-room widget; markdown input via embedded `<k-markdown-editor>` (rendered through `marked` with `breaks:true` so single newlines become `<br>`, then run through `sanitizeHtml` so any inline HTML is filtered against the allow-list); default Enter sends / Shift+Enter newline (Slack-style), add `enter-newline` to invert; `show-status` (or `show-status="text"` / `show-status="icons"`) renders iMessage-style `sending`/`delivered`/`read`/`failed` only on the most recent outgoing message (sending/failed always show); methods `addMessage(msg)`, `updateMessage(id, updates)`, `removeMessage(id)`, `clear()`, `send()`; fires `send` with `{id, html, markdown}` | [chat.html](https://dustinpoissant.github.io/kempo-ui/components/chat.html) |
60
+ | `<k-code-editor>` | `CodeEditor.js` | Monaco-powered code editor; `value`, `language`, `controls`, `disabled`/`readonly`/`required` attributes; form-associated (full validity + reset/restore/disabled callbacks); theme-aware | [code-editor.html](https://dustinpoissant.github.io/kempo-ui/components/code-editor.html) |
60
61
  | `<k-color-picker>` | `ColorPicker.js` | Full color picker (hex, rgb, hsl, hsv, alpha); `value` property | [color-picker.html](https://dustinpoissant.github.io/kempo-ui/components/color-picker.html) |
61
62
  | `<k-combobox>` | `Combobox.js` | Searchable dropdown with debounced `search` event, dynamic `setOptions()`, `searching` indicator, keyboard nav; options are strings or `{label, value}` objects | [combobox.html](https://dustinpoissant.github.io/kempo-ui/components/combobox.html) |
62
63
  | `<k-content-slider>` | `ContentSlider.js` | Swipeable carousel | [content-slider.html](https://dustinpoissant.github.io/kempo-ui/components/content-slider.html) |
@@ -65,7 +66,9 @@ import { html, css, LitElement } from 'kempo-ui/src/lit-all.min.js';
65
66
  | `<k-dropdown>` | `Dropdown.js` | Action menu with keyboard navigation | [dropdown.html](https://dustinpoissant.github.io/kempo-ui/components/dropdown.html) |
66
67
  | `<k-filter-list>` `<k-filter-item>` | `FilterList.js` `FilterItem.js` | Filter/search list; items have `filter` attribute with keywords | [filter-list.html](https://dustinpoissant.github.io/kempo-ui/components/filter-list.html) |
67
68
  | `<k-focus-capture>` | `FocusCapture.js` | Traps keyboard focus within children | [focus-capture.html](https://dustinpoissant.github.io/kempo-ui/components/focus-capture.html) |
68
- | `<k-html-editor>` | `HtmlEditor.js` | WYSIWYG rich text editor; `value` property; `controls` attribute (`full`/`normal`/`minimal`/`none`) for built-in toolbars | [html-editor.html](https://dustinpoissant.github.io/kempo-ui/components/html-editor.html) |
69
+ | `<k-html-editor>` | `HtmlEditor.js` | WYSIWYG rich text editor; `value` property; `controls` attribute (`full`/`normal`/`minimal`/`none`) for built-in toolbars; `disabled`/`readonly`/`required` (Lexical's `setEditable()` + Monaco's `readOnly`); form-associated | [html-editor.html](https://dustinpoissant.github.io/kempo-ui/components/html-editor.html) |
70
+ | `<k-markdown>` | `Markdown.js` | Renders child markdown text as sanitized HTML in place; `value` attribute (overrides children); `breaks` (single newline → `<br>`); `allowed-tags`/`disallowed-tags`/`scripts-enabled` for sanitization; light DOM render; `renderedHtml` getter | [markdown.html](https://dustinpoissant.github.io/kempo-ui/components/markdown.html) |
71
+ | `<k-markdown-editor>` | `MarkdownEditor.js` | Markdown editor with Write/Preview tabs; raw markdown is the form value; rendered with vendored `marked` (full CommonMark + GFM — tables, task lists, fenced code, autolinks, inline HTML) via `renderMarkdown.js`; then sanitized via `sanitizeHtml` using either `allowed-tags` (comma-separated allowlist; pass `*` for everything) OR `disallowed-tags` (denylist) — mutually exclusive, allowlist wins if both set; defaults cover standard markdown output incl. img/hr/table family; `<script>` is always stripped unless `scripts-enabled` attribute is set; iframe/style/etc. are always stripped; `value`, `name`, `placeholder`, `disabled`/`readonly`/`required`, `mode` (`write`/`preview`), `allowed-tags`, `controls` (`'minimal'`/`'normal'`/`'full'`/`'none'` — pre-built toolbar; auto-imports the relevant control modules; user-slotted children override the preset); methods `focus()`, `blur()`, `clear()`, `setMode()`, `togglePreview()`, plus selection helpers `getSelection()`, `replaceSelection()`, `wrapSelection()`, `insertAtCursor()`, `insertLinePrefix()`; `controls-top`/`controls-bottom` slots for toolbar buttons; built-in controls under `markdownEditorControls/` (`k-md-bold`, `k-md-italic`, `k-md-strikethrough` (`~~...~~`), `k-md-heading` w/ `level`, `k-md-code`, `k-md-link`, `k-md-image` w/ `popup` (opens a Dropdown by default — `popup="dialog"` to switch to modal Dialog — for URL + alt text), `k-md-table` (small Dropdown with cols × rows inputs + Create button, inserts a GFM markdown table), `k-md-menu` w/ `label` (toolbar button that opens a Dropdown of slotted child controls; `trigger` slot fully customizes the button content — defaults to a `menu` icon), `k-md-format-block` w/ `tag` (heading picker designed to live inside `k-md-menu` — `h1`-`h6`), `k-md-speech-to-text` w/ `language`/`continuous` (wraps `<k-speech-to-text>`; final transcript is inserted at the cursor when the user stops speaking), `k-md-bullet-list`, `k-md-numbered-list`, `k-md-quote`); extend `MarkdownEditorControl` for custom buttons (incl. ones that insert raw HTML — adjust `allowed-tags` accordingly); fires `input`, `change`, `mode-change` | [markdown-editor.html](https://dustinpoissant.github.io/kempo-ui/components/markdown-editor.html) |
69
72
  | `<k-icon>` | `Icon.js` | SVG icon; `name` (looked up in `pathsToIcons`) or `src` (direct URL); `rotation`, `direction`, `animation` attributes | [icon.html](https://dustinpoissant.github.io/kempo-ui/components/icon.html) |
70
73
  | `<k-import>` | `Import.js` | Includes external HTML fragments; `src` attribute | [import.html](https://dustinpoissant.github.io/kempo-ui/components/import.html) |
71
74
  | `<k-main>` | `Main.js` | Layout main; automatically adjusts margins when a `k-aside[main="push"]` opens | — |
@@ -248,6 +251,7 @@ Located in `src/utils/`. Import individually.
248
251
  | `theme.js` | `setTheme`, `getTheme`, `subscribeToTheme`, `initTheme`, `getCalculatedTheme` | Manage light/dark/auto theme; persists to localStorage | [theme.html](https://dustinpoissant.github.io/kempo-ui/utils/theme.html) |
249
252
  | `voice.js` | `getVoice`, `setVoice`, `subscribeToVoice`, `getAvailableVoices`, `waitForVoices`, `subscribeToAvailableVoices` | Persisted speech-synthesis voice preference (mirrors `theme`); used by `<k-voice-selector>` and consumed by `<k-text-to-speech>` when no explicit `voice` attr is set | [voice.html](https://dustinpoissant.github.io/kempo-ui/utils/voice.html) |
250
253
  | `propConverters.js` | `boolExists`, `boolTrueFalse` | Lit property converters for boolean attributes | [propConverters.html](https://dustinpoissant.github.io/kempo-ui/utils/propConverters.html) |
254
+ | `sanitizeHtml.js` | `default`, `sanitizeHtml`, `DEFAULT_TAGS`, `DEFAULT_ATTRS_GLOBAL`, `DEFAULT_ATTRS_PER_TAG` | Whitelist HTML sanitizer; strips `<script>`, `<style>`, `<iframe>`, event-handler attrs, unsafe URL schemes; defaults sized for rich-text input. **Frontend defense only — server must also sanitize.** | [sanitizeHtml.html](https://dustinpoissant.github.io/kempo-ui/utils/sanitizeHtml.html) |
251
255
  | `context.js` | `default` | `createContext(key, initialValue)` → `{ get, set, subscribe }` pub/sub state | [context.html](https://dustinpoissant.github.io/kempo-ui/utils/context.html) |
252
256
  | `cookie.js` | `saveCookie`, `getCookie`, `deleteCookie` | Get/set/delete browser cookies | [cookie.html](https://dustinpoissant.github.io/kempo-ui/utils/cookie.html) |
253
257
  | `drag.js` | `default` | `drag(options)` — attach mouse/touch drag listeners to an element; options: `element`, `callback`, `startCallback`, `moveCallback`, `endCallback`, `preventScroll` | [drag.html](https://dustinpoissant.github.io/kempo-ui/utils/drag.html) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo-ui",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "type": "module",
5
5
  "description": "A Lit based web-component library",
6
6
  "main": "index.js",
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@inquirer/prompts": "^8.3.0",
45
- "kempo-css": "^2.2.1"
45
+ "kempo-css": "^2.2.1",
46
+ "marked": "^18.0.2"
46
47
  }
47
48
  }