@juspay/svelte-ui-components 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,16 @@
2
2
  import { renderMarkdown } from './markdown';
3
3
  import type { MarkdownTextProperties } from './properties';
4
4
 
5
- let { markdown, breaks = false, testId, classes, tableLabel }: MarkdownTextProperties = $props();
6
-
7
- let html = $derived(renderMarkdown(markdown, { breaks, tableLabel }));
5
+ let {
6
+ markdown,
7
+ breaks = false,
8
+ testId,
9
+ classes,
10
+ tableLabel,
11
+ sanitize
12
+ }: MarkdownTextProperties = $props();
13
+
14
+ let html = $derived(renderMarkdown(markdown, { breaks, tableLabel, sanitize }));
8
15
  </script>
9
16
 
10
17
  <div
@@ -55,26 +55,127 @@ function escapeHtml(value) {
55
55
  .replace(/"/g, '&quot;')
56
56
  .replace(/'/g, '&#39;');
57
57
  }
58
- const sanitizingRenderer = {
59
- html(token) {
60
- return escapeHtml(token.text);
61
- },
62
- link(token) {
63
- if (hasSafeProtocol(token.href, SAFE_LINK_PROTOCOLS)) {
64
- return false;
65
- }
66
- return escapeHtml(token.text);
67
- },
68
- image(token) {
69
- if (hasSafeProtocol(token.href, SAFE_IMAGE_PROTOCOLS)) {
70
- return false;
71
- }
72
- return escapeHtml(token.text);
58
+ /**
59
+ * `allowedProtocols` can only NARROW a surface's default allow-list, never
60
+ * widen it -- intersecting with the library default (rather than substituting
61
+ * it) is what makes listing an unsafe scheme like `javascript:` a no-op
62
+ * instead of a hole. Casing is normalised so `'HTTPS:'` narrows the same as
63
+ * `'https:'`, matching how `hasSafeProtocol` reads `URL.protocol` (always
64
+ * lower-case).
65
+ */
66
+ function narrow(defaults, allowedProtocols) {
67
+ if (!allowedProtocols) {
68
+ return defaults;
73
69
  }
74
- };
70
+ const requested = new Set(allowedProtocols.map((protocol) => protocol.toLowerCase()));
71
+ return new Set([...defaults].filter((protocol) => requested.has(protocol)));
72
+ }
73
+ function resolveProtocolSets(sanitize) {
74
+ return {
75
+ links: narrow(SAFE_LINK_PROTOCOLS, sanitize?.allowedProtocols),
76
+ images: narrow(SAFE_IMAGE_PROTOCOLS, sanitize?.allowedProtocols)
77
+ };
78
+ }
79
+ /**
80
+ * `null` means "no restriction" (today's default: every tag marked's GFM
81
+ * output can produce renders normally) -- unlike the protocol allow-lists,
82
+ * there is no built-in default set to fall back to once this exists, so its
83
+ * absence is what keeps old behaviour byte-for-byte.
84
+ */
85
+ function resolveTagAllowList(allowedTags) {
86
+ return allowedTags ? new Set(allowedTags) : null;
87
+ }
88
+ function tagAllowed(allowed, tag) {
89
+ return allowed === null || allowed.has(tag);
90
+ }
91
+ function createSanitizingRenderer(protocols, allowedTags, disableTaskLists) {
92
+ return {
93
+ html(token) {
94
+ return escapeHtml(token.text);
95
+ },
96
+ link(token) {
97
+ if (hasSafeProtocol(token.href, protocols.links) && tagAllowed(allowedTags, 'a')) {
98
+ return false;
99
+ }
100
+ return escapeHtml(token.text);
101
+ },
102
+ image(token) {
103
+ if (hasSafeProtocol(token.href, protocols.images) && tagAllowed(allowedTags, 'img')) {
104
+ return false;
105
+ }
106
+ return escapeHtml(token.text);
107
+ },
108
+ strong(token) {
109
+ if (tagAllowed(allowedTags, 'strong')) {
110
+ return false;
111
+ }
112
+ return this.parser.parseInline(token.tokens);
113
+ },
114
+ em(token) {
115
+ if (tagAllowed(allowedTags, 'em')) {
116
+ return false;
117
+ }
118
+ return this.parser.parseInline(token.tokens);
119
+ },
120
+ del(token) {
121
+ if (tagAllowed(allowedTags, 'del')) {
122
+ return false;
123
+ }
124
+ return this.parser.parseInline(token.tokens);
125
+ },
126
+ codespan(token) {
127
+ if (tagAllowed(allowedTags, 'code')) {
128
+ return false;
129
+ }
130
+ return escapeHtml(token.text);
131
+ },
132
+ code(token) {
133
+ if (tagAllowed(allowedTags, 'pre')) {
134
+ return false;
135
+ }
136
+ return `${escapeHtml(token.text)}\n`;
137
+ },
138
+ blockquote(token) {
139
+ if (tagAllowed(allowedTags, 'blockquote')) {
140
+ return false;
141
+ }
142
+ return this.parser.parse(token.tokens);
143
+ },
144
+ heading(token) {
145
+ if (tagAllowed(allowedTags, `h${token.depth}`)) {
146
+ return false;
147
+ }
148
+ return `${this.parser.parseInline(token.tokens)}\n`;
149
+ },
150
+ list(token) {
151
+ if (tagAllowed(allowedTags, token.ordered ? 'ol' : 'ul')) {
152
+ return false;
153
+ }
154
+ return token.items.map((item) => this.parser.parse(item.tokens)).join('');
155
+ },
156
+ table(token) {
157
+ if (tagAllowed(allowedTags, 'table')) {
158
+ return false;
159
+ }
160
+ const rowText = (cells) => cells.map((cell) => this.parser.parseInline(cell.tokens)).join(' | ');
161
+ const lines = [rowText(token.header), ...token.rows.map(rowText)];
162
+ return `<p>${lines.join('<br>')}</p>`;
163
+ },
164
+ hr() {
165
+ return tagAllowed(allowedTags, 'hr') ? false : '';
166
+ },
167
+ checkbox() {
168
+ return disableTaskLists ? '' : false;
169
+ }
170
+ };
171
+ }
75
172
  /* Safe to share across SSR requests: each instance's configuration (renderer,
76
173
  gfm, breaks) is fixed at construction and parse() takes no per-request state,
77
- so the cache only ever holds config-immutable parsers keyed by option shape. */
174
+ so the cache only ever holds config-immutable parsers keyed by option shape.
175
+ The key folds in the resolved protocol sets, tag allow-list and task-list
176
+ toggle (sorted for a stable string) so differently-configured `sanitize`
177
+ options get their own cached instance and never share a renderer with a
178
+ different allow-list. */
78
179
  const instances = new Map();
79
180
  /* External links open in a new tab with `rel="noopener noreferrer"` — the same
80
181
  default the library's Button/Card apply to `target="_blank"` anchors. The
@@ -108,10 +209,23 @@ function wrapTables(html, label) {
108
209
  }
109
210
  function instanceFor(options) {
110
211
  const breaks = options.breaks === true;
111
- const key = breaks ? 'breaks' : 'default';
212
+ const protocols = resolveProtocolSets(options.sanitize);
213
+ const allowedTags = resolveTagAllowList(options.sanitize?.allowedTags);
214
+ const disableTaskLists = options.sanitize?.disableTaskLists === true;
215
+ const key = [
216
+ breaks ? 'breaks' : 'default',
217
+ [...protocols.links].sort().join(','),
218
+ [...protocols.images].sort().join(','),
219
+ allowedTags ? [...allowedTags].sort().join(',') : '*',
220
+ disableTaskLists ? 'no-tasks' : 'tasks'
221
+ ].join('|');
112
222
  let instance = instances.get(key);
113
223
  if (!instance) {
114
- instance = new Marked({ gfm: true, breaks, renderer: sanitizingRenderer });
224
+ instance = new Marked({
225
+ gfm: true,
226
+ breaks,
227
+ renderer: createSanitizingRenderer(protocols, allowedTags, disableTaskLists)
228
+ });
115
229
  instances.set(key, instance);
116
230
  }
117
231
  return instance;
@@ -20,6 +20,57 @@ export type OptionalMarkdownTextProperties = {
20
20
  * worse than none.
21
21
  */
22
22
  tableLabel?: string;
23
+ /**
24
+ * Narrow which URL protocols survive on rendered links/images. See
25
+ * `MarkdownSanitizeOptions`.
26
+ */
27
+ sanitize?: MarkdownSanitizeOptions;
28
+ };
29
+ /**
30
+ * A consumer's own link policy can be stricter than the library default
31
+ * (`http:`/`https:`/`mailto:`/`tel:` for links, `http:`/`https:` for images) —
32
+ * for example, no live `mailto:`/`tel:` in a chat surface. `allowedProtocols`
33
+ * lets a caller restrict which of those defaults survive; it is intersected
34
+ * with the library's own allow-list for each surface, so it can only NARROW
35
+ * what already renders, never widen it (listing `javascript:` here can never
36
+ * resurrect it). Omitting `sanitize`, or `allowedProtocols`, keeps today's
37
+ * defaults untouched. This is independent of the "raw HTML is always escaped"
38
+ * guarantee, which has no opt-out.
39
+ */
40
+ export type MarkdownSanitizeOptions = {
41
+ /**
42
+ * Protocols (e.g. `'https:'`, `'mailto:'`) allowed to survive on rendered
43
+ * links and images, intersected with the library's own default allow-list
44
+ * for each surface. An empty array drops every link and image, keeping
45
+ * their text.
46
+ */
47
+ allowedProtocols?: string[];
48
+ /**
49
+ * Narrows which markdown-GENERATED tags are allowed to render as
50
+ * themselves. Unlike `allowedProtocols` there is no pre-set default list to
51
+ * intersect with — every tag `marked`'s own GFM output can produce renders
52
+ * normally until this is supplied, so passing it narrows from "everything"
53
+ * rather than from a built-in allow-list. A disallowed tag renders its
54
+ * parsed inner content as plain markup-free text instead of the element —
55
+ * the same "keep the text, drop the element" contract `allowedProtocols`
56
+ * uses for an unsafe link/image. Recognised names: `'a'`, `'img'`,
57
+ * `'strong'`, `'em'`, `'del'`, `'code'` (inline), `'pre'` (code block),
58
+ * `'blockquote'`, `'ul'`, `'ol'`, `'table'`, `'hr'`, and `'h1'`–`'h6'`. An
59
+ * unrecognised name is simply ignored — it narrows nothing, it does not
60
+ * error. Omitting `allowedTags` keeps today's defaults untouched: every tag
61
+ * renders as it does now. This is independent of the "raw HTML is always
62
+ * escaped" guarantee, which has no opt-out and is never affected by this
63
+ * list.
64
+ */
65
+ allowedTags?: string[];
66
+ /**
67
+ * Render GFM task-list items (`- [ ] done`) as plain list text instead of
68
+ * an `<input type="checkbox">`. The checkbox already renders `disabled` by
69
+ * default (it never submits or toggles), so this is a presentation choice
70
+ * for surfaces that would rather not show checkbox glyphs at all, not a
71
+ * safety one. Defaults to `false`, keeping today's checkbox rendering.
72
+ */
73
+ disableTaskLists?: boolean;
23
74
  };
24
75
  export type RenderMarkdownOptions = {
25
76
  /** Render single newlines as `<br>` (GFM "breaks" mode). */
@@ -33,4 +84,6 @@ export type RenderMarkdownOptions = {
33
84
  inline?: boolean;
34
85
  /** Accessible name for the scroll region wrapping each table. See `MarkdownTextProperties.tableLabel`. */
35
86
  tableLabel?: string;
87
+ /** Narrow the protocol allow-list. See `MarkdownSanitizeOptions`. */
88
+ sanitize?: MarkdownSanitizeOptions;
36
89
  };
package/dist-wc/index.js CHANGED
@@ -33643,6 +33643,71 @@ function hasSafeProtocol(e, t) {
33643
33643
  function escapeHtml(e) {
33644
33644
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
33645
33645
  }
33646
+ function narrow(e, t) {
33647
+ if (!t) return e;
33648
+ let n = new Set(t.map((e) => e.toLowerCase()));
33649
+ return new Set([...e].filter((e) => n.has(e)));
33650
+ }
33651
+ function resolveProtocolSets(e) {
33652
+ return {
33653
+ links: narrow(SAFE_LINK_PROTOCOLS, e?.allowedProtocols),
33654
+ images: narrow(SAFE_IMAGE_PROTOCOLS, e?.allowedProtocols)
33655
+ };
33656
+ }
33657
+ function resolveTagAllowList(e) {
33658
+ return e ? new Set(e) : null;
33659
+ }
33660
+ function tagAllowed(e, t) {
33661
+ return e === null || e.has(t);
33662
+ }
33663
+ function createSanitizingRenderer(e, t, n) {
33664
+ return {
33665
+ html(e) {
33666
+ return escapeHtml(e.text);
33667
+ },
33668
+ link(n) {
33669
+ return hasSafeProtocol(n.href, e.links) && tagAllowed(t, "a") ? !1 : escapeHtml(n.text);
33670
+ },
33671
+ image(n) {
33672
+ return hasSafeProtocol(n.href, e.images) && tagAllowed(t, "img") ? !1 : escapeHtml(n.text);
33673
+ },
33674
+ strong(e) {
33675
+ return tagAllowed(t, "strong") ? !1 : this.parser.parseInline(e.tokens);
33676
+ },
33677
+ em(e) {
33678
+ return tagAllowed(t, "em") ? !1 : this.parser.parseInline(e.tokens);
33679
+ },
33680
+ del(e) {
33681
+ return tagAllowed(t, "del") ? !1 : this.parser.parseInline(e.tokens);
33682
+ },
33683
+ codespan(e) {
33684
+ return tagAllowed(t, "code") ? !1 : escapeHtml(e.text);
33685
+ },
33686
+ code(e) {
33687
+ return tagAllowed(t, "pre") ? !1 : `${escapeHtml(e.text)}\n`;
33688
+ },
33689
+ blockquote(e) {
33690
+ return tagAllowed(t, "blockquote") ? !1 : this.parser.parse(e.tokens);
33691
+ },
33692
+ heading(e) {
33693
+ return tagAllowed(t, `h${e.depth}`) ? !1 : `${this.parser.parseInline(e.tokens)}\n`;
33694
+ },
33695
+ list(e) {
33696
+ return tagAllowed(t, e.ordered ? "ol" : "ul") ? !1 : e.items.map((e) => this.parser.parse(e.tokens)).join("");
33697
+ },
33698
+ table(e) {
33699
+ if (tagAllowed(t, "table")) return !1;
33700
+ let n = (e) => e.map((e) => this.parser.parseInline(e.tokens)).join(" | ");
33701
+ return `<p>${[n(e.header), ...e.rows.map(n)].join("<br>")}</p>`;
33702
+ },
33703
+ hr() {
33704
+ return tagAllowed(t, "hr") ? !1 : "";
33705
+ },
33706
+ checkbox() {
33707
+ return n ? "" : !1;
33708
+ }
33709
+ };
33710
+ }
33646
33711
  function annotateExternalLinks(e) {
33647
33712
  return e.replace(EXTERNAL_ANCHOR_PATTERN, "<a href=\"$1\" target=\"_blank\" rel=\"noopener noreferrer\"");
33648
33713
  }
@@ -33651,12 +33716,18 @@ function wrapTables(e, t) {
33651
33716
  return e.replace(TABLE_OPEN, `${n}<table>`).replace(TABLE_CLOSE, "</table></div>");
33652
33717
  }
33653
33718
  function instanceFor(e) {
33654
- let t = e.breaks === !0, n = t ? "breaks" : "default", i = instances.get(n);
33655
- return i || (i = new q({
33719
+ let t = e.breaks === !0, n = resolveProtocolSets(e.sanitize), i = resolveTagAllowList(e.sanitize?.allowedTags), a = e.sanitize?.disableTaskLists === !0, o = [
33720
+ t ? "breaks" : "default",
33721
+ [...n.links].sort().join(","),
33722
+ [...n.images].sort().join(","),
33723
+ i ? [...i].sort().join(",") : "*",
33724
+ a ? "no-tasks" : "tasks"
33725
+ ].join("|"), s = instances.get(o);
33726
+ return s || (s = new q({
33656
33727
  gfm: !0,
33657
33728
  breaks: t,
33658
- renderer: sanitizingRenderer
33659
- }), instances.set(n, i)), i;
33729
+ renderer: createSanitizingRenderer(n, i, a)
33730
+ }), instances.set(o, s)), s;
33660
33731
  }
33661
33732
  function renderMarkdown(e, t = {}) {
33662
33733
  let n = instanceFor(t), i = t.inline === !0 ? n.parseInline(e) : n.parse(e);
@@ -33664,23 +33735,13 @@ function renderMarkdown(e, t = {}) {
33664
33735
  let a = annotateExternalLinks(i);
33665
33736
  return t.inline === !0 ? a : wrapTables(a, t.tableLabel);
33666
33737
  }
33667
- var SAFE_LINK_PROTOCOLS, SAFE_IMAGE_PROTOCOLS, NUMERIC_REFERENCE, NAMED_WHITESPACE_REFERENCE, STRIPPED_BY_URL_PARSER, sanitizingRenderer, instances, EXTERNAL_ANCHOR_PATTERN, TABLE_OPEN, TABLE_CLOSE, init_markdown = __esmMin((() => {
33738
+ var SAFE_LINK_PROTOCOLS, SAFE_IMAGE_PROTOCOLS, NUMERIC_REFERENCE, NAMED_WHITESPACE_REFERENCE, STRIPPED_BY_URL_PARSER, instances, EXTERNAL_ANCHOR_PATTERN, TABLE_OPEN, TABLE_CLOSE, init_markdown = __esmMin((() => {
33668
33739
  init_marked_esm(), SAFE_LINK_PROTOCOLS = new Set([
33669
33740
  "http:",
33670
33741
  "https:",
33671
33742
  "mailto:",
33672
33743
  "tel:"
33673
- ]), SAFE_IMAGE_PROTOCOLS = new Set(["http:", "https:"]), NUMERIC_REFERENCE = /&#(x[0-9a-f]+|[0-9]+);?/gi, NAMED_WHITESPACE_REFERENCE = /&(tab|newline);/gi, STRIPPED_BY_URL_PARSER = /[\u0000-\u0020\u007f]/g, sanitizingRenderer = {
33674
- html(e) {
33675
- return escapeHtml(e.text);
33676
- },
33677
- link(e) {
33678
- return hasSafeProtocol(e.href, SAFE_LINK_PROTOCOLS) ? !1 : escapeHtml(e.text);
33679
- },
33680
- image(e) {
33681
- return hasSafeProtocol(e.href, SAFE_IMAGE_PROTOCOLS) ? !1 : escapeHtml(e.text);
33682
- }
33683
- }, instances = /* @__PURE__ */ new Map(), EXTERNAL_ANCHOR_PATTERN = /<a href="(https?:\/\/[^"]*)"/g, TABLE_OPEN = /<table>/g, TABLE_CLOSE = /<\/table>/g;
33744
+ ]), SAFE_IMAGE_PROTOCOLS = new Set(["http:", "https:"]), NUMERIC_REFERENCE = /&#(x[0-9a-f]+|[0-9]+);?/gi, NAMED_WHITESPACE_REFERENCE = /&(tab|newline);/gi, STRIPPED_BY_URL_PARSER = /[\u0000-\u0020\u007f]/g, instances = /* @__PURE__ */ new Map(), EXTERNAL_ANCHOR_PATTERN = /<a href="(https?:\/\/[^"]*)"/g, TABLE_OPEN = /<table>/g, TABLE_CLOSE = /<\/table>/g;
33684
33745
  })), root$11 = /* @__PURE__ */ from_html("<div class=\"avatar svelte-ivh8u\"><!></div>"), root_1$10 = /* @__PURE__ */ from_html("<div class=\"header svelte-ivh8u\"><!></div>"), root_2$7 = /* @__PURE__ */ from_html("<span class=\"marker svelte-ivh8u\" aria-hidden=\"true\"></span>"), root_3$7 = /* @__PURE__ */ from_html("<div class=\"body svelte-ivh8u\"><!></div>"), root_4$6 = /* @__PURE__ */ from_html("<div class=\"body svelte-ivh8u\"></div>"), root_5$6 = /* @__PURE__ */ from_html("<div class=\"body text svelte-ivh8u\"> </div>"), root_6$6 = /* @__PURE__ */ from_html("<div class=\"clamp-toggle svelte-ivh8u\"><!></div>"), root_7$5 = /* @__PURE__ */ from_html("<span class=\"typing svelte-ivh8u\"><!></span>"), root_8$5 = /* @__PURE__ */ from_html("<div class=\"message-attachments svelte-ivh8u\"><!></div>"), root_9$2 = /* @__PURE__ */ from_html("<div class=\"action svelte-ivh8u\"><!></div>"), root_10$1 = /* @__PURE__ */ from_html("<div class=\"action svelte-ivh8u\"><!></div> <div class=\"action svelte-ivh8u\"><!></div>", 1), root_11$1 = /* @__PURE__ */ from_html("<div class=\"actions svelte-ivh8u\"><!> <!> <!> <!></div>"), root_12$1 = /* @__PURE__ */ from_html("<article><div class=\"row svelte-ivh8u\"><!> <div class=\"content svelte-ivh8u\"><!> <div><!> <!> <!> <!></div> <!> <!></div></div></article>"), $$css$12 = {
33685
33746
  hash: "svelte-ivh8u",
33686
33747
  code: ".chat-message.svelte-ivh8u {box-sizing:border-box;display:flex;flex-direction:column;max-width:var(--chat-message-max-width, 82%);margin:var(--chat-message-margin, 0);}.chat-message.party-sender.svelte-ivh8u {align-self:flex-end;align-items:flex-end;}.chat-message.party-responder.svelte-ivh8u {align-self:flex-start;align-items:flex-start;}.row.svelte-ivh8u {display:flex;gap:var(--chat-message-gap, 10px);align-items:flex-end;width:100%;}.chat-message.party-sender.svelte-ivh8u .row:where(.svelte-ivh8u) {flex-direction:row-reverse;}.avatar.svelte-ivh8u {display:flex;flex-shrink:0;align-items:center;justify-content:center;}.content.svelte-ivh8u {display:flex;flex-direction:column;gap:var(--chat-message-content-gap, 6px);min-width:0;}.header.svelte-ivh8u {font-size:var(--chat-message-header-font-size, 0.75rem);color:var(--chat-message-header-color, #71717a);}.bubble.svelte-ivh8u {box-sizing:border-box;padding:var(--chat-message-bubble-padding, 9px 13px);border-radius:var(--chat-message-bubble-border-radius, 16px);font-size:var(--chat-message-font-size, 0.9375rem);line-height:var(--chat-message-line-height, 1.5);color:var(--chat-message-color, #27272a);background:var(--chat-message-background, #f4f4f5);border:var(--chat-message-border, none);box-shadow:var(--chat-message-box-shadow, none);word-wrap:break-word;overflow-wrap:anywhere;}\n\n /* Only the marker variant becomes a containing block: making every bubble relative\n would silently re-base any absolutely-positioned content a consumer renders in a\n body snippet. */.bubble.has-marker.svelte-ivh8u {position:relative;}.marker.svelte-ivh8u {\n /* Decorative and absolutely positioned, so it sits over the bubble's leading\n padding — where a link at the start of a responder message also sits. Without\n this it silently eats those clicks. */pointer-events:none;position:absolute;inset-block:var(--chat-message-marker-inset-block, 0);\n /* Logical, so the bar follows `role` alignment into RTL instead of pinning left. */inset-inline-start:var(--chat-message-marker-offset, 0px);width:var(--chat-message-marker-width, 2px);border-radius:var(--chat-message-marker-border-radius, 0);background:var(--chat-message-marker-color, #6d28d9);}.chat-message.party-sender.svelte-ivh8u .bubble:where(.svelte-ivh8u) {color:var(--chat-message-sender-color, #ffffff);background:var(--chat-message-sender-background, #18181b);border:var(--chat-message-sender-border, none);border-radius:var(\n --chat-message-sender-border-radius,\n var(--chat-message-bubble-border-radius, 16px)\n );}.chat-message.party-responder.svelte-ivh8u .bubble:where(.svelte-ivh8u) {color:var(--chat-message-responder-color, var(--chat-message-color, #27272a));background:var(--chat-message-responder-background, transparent);border:var(--chat-message-responder-border, none);padding:var(--chat-message-responder-padding, 2px 0);}.chat-message.error.svelte-ivh8u .bubble:where(.svelte-ivh8u) {color:var(--chat-message-error-color, #e0334b);}.text.svelte-ivh8u {white-space:pre-wrap;}.typing.svelte-ivh8u {display:inline-flex;align-items:center;}.message-attachments.svelte-ivh8u {display:flex;flex-direction:column;gap:var(--chat-message-attachments-gap, 8px);margin:var(--chat-message-attachments-margin, 4px 0 0 0);}.message-attachments.svelte-ivh8u:empty {display:none;}.actions.svelte-ivh8u {display:flex;align-items:center;gap:var(--chat-message-actions-gap, 2px);opacity:var(--chat-message-actions-opacity, 0);transition:var(--chat-message-actions-transition, opacity 0.15s ease);--button-width: var(--chat-message-action-size, 28px);--button-height: var(--chat-message-action-size, 28px);--button-padding: var(--chat-message-action-padding, 6px);--button-border-radius: var(--chat-message-action-border-radius, 6px);--button-color: var(--chat-message-action-background-color, transparent);--button-text-color: var(--chat-message-action-color, #71717a);--button-content-gap: 0px;--button-hover-color: var(--chat-message-action-hover-background-color, #f4f4f5);}.chat-message.svelte-ivh8u:hover .actions:where(.svelte-ivh8u),\n .chat-message.svelte-ivh8u:focus-within .actions:where(.svelte-ivh8u) {opacity:1;}.action.svelte-ivh8u {display:flex;}.action.svelte-ivh8u svg {height:100%;width:100%;}\n\n @media (hover: none) {.actions.svelte-ivh8u {opacity:1;}\n }.clamp-toggle.svelte-ivh8u {display:flex;margin-top:var(--chat-message-clamp-toggle-margin-top, 4px);}\n\n /* Clamp the rendered body, not the source: the message keeps its markup and its\n copy text, and expanding is a state change rather than a re-render. */.bubble[data-clamped='true'].svelte-ivh8u .body:where(.svelte-ivh8u) {display:-webkit-box;line-clamp:var(--chat-message-clamp-lines, var(--chat-message-clamp-lines-prop, 2));-webkit-line-clamp:var(--chat-message-clamp-lines, var(--chat-message-clamp-lines-prop, 2));-webkit-box-orient:vertical;overflow:hidden;}.body.svelte-ivh8u p {margin:var(--chat-message-paragraph-margin, 0 0 0.5em 0);}.body.svelte-ivh8u p:last-child {margin-bottom:0;}.body.svelte-ivh8u a {color:var(--chat-message-link-color, #6d28d9);text-decoration:underline;text-underline-offset:2px;}.body.svelte-ivh8u code {font-family:var(--chat-message-code-font-family, ui-monospace, monospace);font-size:0.88em;background:var(--chat-message-code-background, rgba(0, 0, 0, 0.05));padding:1px 5px;border-radius:4px;}.body.svelte-ivh8u pre {background:var(--chat-message-pre-background, rgba(0, 0, 0, 0.05));padding:10px 12px;border-radius:8px;overflow-x:auto;margin:0.5em 0;}.body.svelte-ivh8u pre code {background:transparent;padding:0;}.body.svelte-ivh8u ul,\n .body.svelte-ivh8u ol {margin:var(--chat-message-list-margin, 0.4em 0);padding-left:var(--chat-message-list-padding, 1.4em);}.body.svelte-ivh8u li {margin:0.2em 0;}.body.svelte-ivh8u h1,\n .body.svelte-ivh8u h2,\n .body.svelte-ivh8u h3,\n .body.svelte-ivh8u h4,\n .body.svelte-ivh8u h5,\n .body.svelte-ivh8u h6 {margin:var(--chat-message-heading-margin, 0.8em 0 0.4em 0);line-height:1.3;}.body.svelte-ivh8u h1 {font-size:1.35em;}.body.svelte-ivh8u h2 {font-size:1.2em;}.body.svelte-ivh8u h3 {font-size:1.1em;}.body.svelte-ivh8u h4,\n .body.svelte-ivh8u h5,\n .body.svelte-ivh8u h6 {font-size:1em;}.body.svelte-ivh8u blockquote {margin:0.5em 0;padding:2px 0 2px 12px;border-left:3px solid var(--chat-message-blockquote-border-color, rgba(0, 0, 0, 0.15));opacity:var(--chat-message-blockquote-opacity, 0.85);}.body.svelte-ivh8u table {display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;margin:0.5em 0;}.body.svelte-ivh8u th,\n .body.svelte-ivh8u td {padding:5px 10px;border:1px solid var(--chat-message-table-border-color, rgba(0, 0, 0, 0.12));text-align:left;}.body.svelte-ivh8u th {background:var(--chat-message-table-header-background, rgba(0, 0, 0, 0.04));}.body.svelte-ivh8u img {max-width:100%;border-radius:var(--chat-message-image-border-radius, 8px);}.body.svelte-ivh8u hr {border:none;border-top:1px solid var(--chat-message-hr-color, rgba(0, 0, 0, 0.12));margin:0.8em 0;}"
@@ -37000,11 +37061,12 @@ var root$4 = /* @__PURE__ */ from_html("<div></div>"), $$css$5 = {
37000
37061
  };
37001
37062
  function MarkdownText(e, t) {
37002
37063
  push(t, !0), append_styles$1(e, $$css$5);
37003
- let n = prop(t, "markdown", 7), i = prop(t, "breaks", 7, !1), a = prop(t, "testId", 7), o = prop(t, "classes", 7), s = prop(t, "tableLabel", 7), c = /* @__PURE__ */ user_derived(() => renderMarkdown(n(), {
37064
+ let n = prop(t, "markdown", 7), i = prop(t, "breaks", 7, !1), a = prop(t, "testId", 7), o = prop(t, "classes", 7), s = prop(t, "tableLabel", 7), c = prop(t, "sanitize", 7), l = /* @__PURE__ */ user_derived(() => renderMarkdown(n(), {
37004
37065
  breaks: i(),
37005
- tableLabel: s()
37066
+ tableLabel: s(),
37067
+ sanitize: c()
37006
37068
  }));
37007
- var l = {
37069
+ var u = {
37008
37070
  get markdown() {
37009
37071
  return n();
37010
37072
  },
@@ -37034,18 +37096,25 @@ function MarkdownText(e, t) {
37034
37096
  },
37035
37097
  set tableLabel(e) {
37036
37098
  s(e), flushSync();
37099
+ },
37100
+ get sanitize() {
37101
+ return c();
37102
+ },
37103
+ set sanitize(e) {
37104
+ c(e), flushSync();
37037
37105
  }
37038
- }, u = root$4();
37039
- return html(u, () => get(c), !0), reset(u), template_effect(() => {
37040
- set_class(u, 1, `markdown-text ${o() ?? "" ?? ""}`, "svelte-1d6q5h0"), set_attribute(u, "data-pw", typeof a() == "string" ? a() : null), set_attribute(u, "testid", typeof a() == "string" ? a() : null);
37041
- }), append(e, u), pop(l);
37106
+ }, f = root$4();
37107
+ return html(f, () => get(l), !0), reset(f), template_effect(() => {
37108
+ set_class(f, 1, `markdown-text ${o() ?? "" ?? ""}`, "svelte-1d6q5h0"), set_attribute(f, "data-pw", typeof a() == "string" ? a() : null), set_attribute(f, "testid", typeof a() == "string" ? a() : null);
37109
+ }), append(e, f), pop(u);
37042
37110
  }
37043
37111
  create_custom_element(MarkdownText, {
37044
37112
  markdown: {},
37045
37113
  breaks: {},
37046
37114
  testId: {},
37047
37115
  classes: {},
37048
- tableLabel: {}
37116
+ tableLabel: {},
37117
+ sanitize: {}
37049
37118
  }, [], [], { mode: "open" });
37050
37119
  //#endregion
37051
37120
  //#region src/wc/components/MarkdownText.wc.svelte
@@ -37070,7 +37139,8 @@ customElements.define("sui-markdown-text", create_custom_element(MarkdownText_wc
37070
37139
  tableLabel: {
37071
37140
  attribute: "table-label",
37072
37141
  type: "String"
37073
- }
37142
+ },
37143
+ sanitize: { type: "Object" }
37074
37144
  }, [], [], { mode: "open" }));
37075
37145
  //#endregion
37076
37146
  //#region src/wc/components/ChatComposer.wc.svelte
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "4.4.0",
3
+ "version": "4.5.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",