@juspay/svelte-ui-components 4.4.0 → 4.6.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.
@@ -1,7 +1,14 @@
1
1
  <script lang="ts">
2
2
  import type { KeyboardInputProperties } from './properties';
3
3
 
4
- let { keys, separator = '+', testId, onclick, classes }: KeyboardInputProperties = $props();
4
+ let {
5
+ keys,
6
+ separator = '+',
7
+ testId,
8
+ onclick,
9
+ classes,
10
+ literal = false
11
+ }: KeyboardInputProperties = $props();
5
12
 
6
13
  const KEY_SYMBOLS: Record<string, string> = {
7
14
  cmd: '\u2318',
@@ -28,6 +35,9 @@
28
35
  let interactive = $derived(typeof onclick === 'function');
29
36
 
30
37
  function getSymbol(key: string): string {
38
+ if (literal) {
39
+ return key;
40
+ }
31
41
  return KEY_SYMBOLS[key.toLowerCase()] ?? key;
32
42
  }
33
43
 
@@ -6,6 +6,15 @@ export type OptionalKeyboardInputProperties = {
6
6
  separator?: string;
7
7
  testId?: string;
8
8
  classes?: string;
9
+ /**
10
+ * When true, every key renders exactly as given — the built-in KEY_SYMBOLS
11
+ * table (`space` → ␣, `tab` → ⇥, `cmd` → ⌘, …) is bypassed entirely.
12
+ * Defaults to false, so existing consumers keep today's glyph substitution
13
+ * unchanged. Use this when the word itself is the label that must appear
14
+ * (e.g. a "Hold SPACE" caption or a physical-key caption reading "Tab"),
15
+ * not the typographic symbol.
16
+ */
17
+ literal?: boolean;
9
18
  };
10
19
  export type KeyboardInputEventProperties = {
11
20
  onclick?: (event: MouseEvent) => void;
@@ -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
@@ -9834,7 +9834,7 @@ var root$64 = /* @__PURE__ */ from_html("<span class=\"separator svelte-14lwsom\
9834
9834
  };
9835
9835
  function KeyboardInput(e, t) {
9836
9836
  push(t, !0), append_styles$1(e, $$css$63);
9837
- let n = prop(t, "keys", 7), i = prop(t, "separator", 7, "+"), a = prop(t, "testId", 7), o = prop(t, "onclick", 7), s = prop(t, "classes", 7), c = {
9837
+ let n = prop(t, "keys", 7), i = prop(t, "separator", 7, "+"), a = prop(t, "testId", 7), o = prop(t, "onclick", 7), s = prop(t, "classes", 7), c = prop(t, "literal", 7, !1), l = {
9838
9838
  cmd: "⌘",
9839
9839
  command: "⌘",
9840
9840
  ctrl: "⌃",
@@ -9853,14 +9853,14 @@ function KeyboardInput(e, t) {
9853
9853
  left: "←",
9854
9854
  right: "→",
9855
9855
  space: "␣"
9856
- }, l = /* @__PURE__ */ user_derived(() => Array.isArray(n()) ? n() : n().split(i()).map((e) => e.trim())), u = /* @__PURE__ */ user_derived(() => typeof o() == "function");
9857
- function f(e) {
9858
- return c[e.toLowerCase()] ?? e;
9859
- }
9856
+ }, u = /* @__PURE__ */ user_derived(() => Array.isArray(n()) ? n() : n().split(i()).map((e) => e.trim())), f = /* @__PURE__ */ user_derived(() => typeof o() == "function");
9860
9857
  function p(e) {
9858
+ return c() ? e : l[e.toLowerCase()] ?? e;
9859
+ }
9860
+ function h(e) {
9861
9861
  (e.key === "Enter" || e.key === " ") && (e.preventDefault(), o()?.(new MouseEvent("click")));
9862
9862
  }
9863
- var h = {
9863
+ var S = {
9864
9864
  get keys() {
9865
9865
  return n();
9866
9866
  },
@@ -9890,9 +9890,15 @@ function KeyboardInput(e, t) {
9890
9890
  },
9891
9891
  set classes(e) {
9892
9892
  s(e), flushSync();
9893
+ },
9894
+ get literal() {
9895
+ return c();
9896
+ },
9897
+ set literal(e = !1) {
9898
+ c(e), flushSync();
9893
9899
  }
9894
- }, S = root_2$41();
9895
- return each(S, 21, () => get(l), index, (e, t, n) => {
9900
+ }, C = root_2$41();
9901
+ return each(C, 21, () => get(u), index, (e, t, n) => {
9896
9902
  var a = root_1$53(), o = first_child(a), s = (e) => {
9897
9903
  var t = root$64(), n = child(t, !0);
9898
9904
  reset(t), template_effect(() => set_text(n, i())), append(e, t);
@@ -9901,21 +9907,22 @@ function KeyboardInput(e, t) {
9901
9907
  n > 0 && e(s);
9902
9908
  });
9903
9909
  var c = sibling(o, 2), l = child(c, !0);
9904
- reset(c), template_effect((e) => set_text(l, e), [() => f(get(t))]), append(e, a);
9905
- }), reset(S), template_effect(() => {
9906
- set_class(S, 1, `keyboard-input ${s() ?? "" ?? ""}`, "svelte-14lwsom"), set_attribute(S, "data-pw", typeof a() == "string" ? a() : null), set_attribute(S, "testid", typeof a() == "string" ? a() : null), set_attribute(S, "role", get(u) ? "button" : null), set_attribute(S, "tabindex", get(u) ? 0 : null);
9907
- }), delegated("click", S, function(...e) {
9908
- (get(u) ? o() : null)?.apply(this, e);
9909
- }), delegated("keydown", S, function(...e) {
9910
- (get(u) ? p : null)?.apply(this, e);
9911
- }), append(e, S), pop(h);
9910
+ reset(c), template_effect((e) => set_text(l, e), [() => p(get(t))]), append(e, a);
9911
+ }), reset(C), template_effect(() => {
9912
+ set_class(C, 1, `keyboard-input ${s() ?? "" ?? ""}`, "svelte-14lwsom"), set_attribute(C, "data-pw", typeof a() == "string" ? a() : null), set_attribute(C, "testid", typeof a() == "string" ? a() : null), set_attribute(C, "role", get(f) ? "button" : null), set_attribute(C, "tabindex", get(f) ? 0 : null);
9913
+ }), delegated("click", C, function(...e) {
9914
+ (get(f) ? o() : null)?.apply(this, e);
9915
+ }), delegated("keydown", C, function(...e) {
9916
+ (get(f) ? h : null)?.apply(this, e);
9917
+ }), append(e, C), pop(S);
9912
9918
  }
9913
9919
  delegate(["click", "keydown"]), create_custom_element(KeyboardInput, {
9914
9920
  keys: {},
9915
9921
  separator: {},
9916
9922
  testId: {},
9917
9923
  onclick: {},
9918
- classes: {}
9924
+ classes: {},
9925
+ literal: {}
9919
9926
  }, [], [], { mode: "open" });
9920
9927
  //#endregion
9921
9928
  //#region src/wc/components/KeyboardInput.wc.svelte
@@ -9943,6 +9950,10 @@ customElements.define("sui-keyboard-input", create_custom_element(KeyboardInput_
9943
9950
  type: "String"
9944
9951
  },
9945
9952
  classes: { type: "String" },
9953
+ literal: {
9954
+ reflect: !0,
9955
+ type: "Boolean"
9956
+ },
9946
9957
  onclick: { type: "Object" }
9947
9958
  }, [], [], { mode: "open" }));
9948
9959
  //#endregion
@@ -33643,6 +33654,71 @@ function hasSafeProtocol(e, t) {
33643
33654
  function escapeHtml(e) {
33644
33655
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
33645
33656
  }
33657
+ function narrow(e, t) {
33658
+ if (!t) return e;
33659
+ let n = new Set(t.map((e) => e.toLowerCase()));
33660
+ return new Set([...e].filter((e) => n.has(e)));
33661
+ }
33662
+ function resolveProtocolSets(e) {
33663
+ return {
33664
+ links: narrow(SAFE_LINK_PROTOCOLS, e?.allowedProtocols),
33665
+ images: narrow(SAFE_IMAGE_PROTOCOLS, e?.allowedProtocols)
33666
+ };
33667
+ }
33668
+ function resolveTagAllowList(e) {
33669
+ return e ? new Set(e) : null;
33670
+ }
33671
+ function tagAllowed(e, t) {
33672
+ return e === null || e.has(t);
33673
+ }
33674
+ function createSanitizingRenderer(e, t, n) {
33675
+ return {
33676
+ html(e) {
33677
+ return escapeHtml(e.text);
33678
+ },
33679
+ link(n) {
33680
+ return hasSafeProtocol(n.href, e.links) && tagAllowed(t, "a") ? !1 : escapeHtml(n.text);
33681
+ },
33682
+ image(n) {
33683
+ return hasSafeProtocol(n.href, e.images) && tagAllowed(t, "img") ? !1 : escapeHtml(n.text);
33684
+ },
33685
+ strong(e) {
33686
+ return tagAllowed(t, "strong") ? !1 : this.parser.parseInline(e.tokens);
33687
+ },
33688
+ em(e) {
33689
+ return tagAllowed(t, "em") ? !1 : this.parser.parseInline(e.tokens);
33690
+ },
33691
+ del(e) {
33692
+ return tagAllowed(t, "del") ? !1 : this.parser.parseInline(e.tokens);
33693
+ },
33694
+ codespan(e) {
33695
+ return tagAllowed(t, "code") ? !1 : escapeHtml(e.text);
33696
+ },
33697
+ code(e) {
33698
+ return tagAllowed(t, "pre") ? !1 : `${escapeHtml(e.text)}\n`;
33699
+ },
33700
+ blockquote(e) {
33701
+ return tagAllowed(t, "blockquote") ? !1 : this.parser.parse(e.tokens);
33702
+ },
33703
+ heading(e) {
33704
+ return tagAllowed(t, `h${e.depth}`) ? !1 : `${this.parser.parseInline(e.tokens)}\n`;
33705
+ },
33706
+ list(e) {
33707
+ return tagAllowed(t, e.ordered ? "ol" : "ul") ? !1 : e.items.map((e) => this.parser.parse(e.tokens)).join("");
33708
+ },
33709
+ table(e) {
33710
+ if (tagAllowed(t, "table")) return !1;
33711
+ let n = (e) => e.map((e) => this.parser.parseInline(e.tokens)).join(" | ");
33712
+ return `<p>${[n(e.header), ...e.rows.map(n)].join("<br>")}</p>`;
33713
+ },
33714
+ hr() {
33715
+ return tagAllowed(t, "hr") ? !1 : "";
33716
+ },
33717
+ checkbox() {
33718
+ return n ? "" : !1;
33719
+ }
33720
+ };
33721
+ }
33646
33722
  function annotateExternalLinks(e) {
33647
33723
  return e.replace(EXTERNAL_ANCHOR_PATTERN, "<a href=\"$1\" target=\"_blank\" rel=\"noopener noreferrer\"");
33648
33724
  }
@@ -33651,12 +33727,18 @@ function wrapTables(e, t) {
33651
33727
  return e.replace(TABLE_OPEN, `${n}<table>`).replace(TABLE_CLOSE, "</table></div>");
33652
33728
  }
33653
33729
  function instanceFor(e) {
33654
- let t = e.breaks === !0, n = t ? "breaks" : "default", i = instances.get(n);
33655
- return i || (i = new q({
33730
+ let t = e.breaks === !0, n = resolveProtocolSets(e.sanitize), i = resolveTagAllowList(e.sanitize?.allowedTags), a = e.sanitize?.disableTaskLists === !0, o = [
33731
+ t ? "breaks" : "default",
33732
+ [...n.links].sort().join(","),
33733
+ [...n.images].sort().join(","),
33734
+ i ? [...i].sort().join(",") : "*",
33735
+ a ? "no-tasks" : "tasks"
33736
+ ].join("|"), s = instances.get(o);
33737
+ return s || (s = new q({
33656
33738
  gfm: !0,
33657
33739
  breaks: t,
33658
- renderer: sanitizingRenderer
33659
- }), instances.set(n, i)), i;
33740
+ renderer: createSanitizingRenderer(n, i, a)
33741
+ }), instances.set(o, s)), s;
33660
33742
  }
33661
33743
  function renderMarkdown(e, t = {}) {
33662
33744
  let n = instanceFor(t), i = t.inline === !0 ? n.parseInline(e) : n.parse(e);
@@ -33664,23 +33746,13 @@ function renderMarkdown(e, t = {}) {
33664
33746
  let a = annotateExternalLinks(i);
33665
33747
  return t.inline === !0 ? a : wrapTables(a, t.tableLabel);
33666
33748
  }
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((() => {
33749
+ 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
33750
  init_marked_esm(), SAFE_LINK_PROTOCOLS = new Set([
33669
33751
  "http:",
33670
33752
  "https:",
33671
33753
  "mailto:",
33672
33754
  "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;
33755
+ ]), 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
33756
  })), 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
33757
  hash: "svelte-ivh8u",
33686
33758
  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 +37072,12 @@ var root$4 = /* @__PURE__ */ from_html("<div></div>"), $$css$5 = {
37000
37072
  };
37001
37073
  function MarkdownText(e, t) {
37002
37074
  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(), {
37075
+ 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
37076
  breaks: i(),
37005
- tableLabel: s()
37077
+ tableLabel: s(),
37078
+ sanitize: c()
37006
37079
  }));
37007
- var l = {
37080
+ var u = {
37008
37081
  get markdown() {
37009
37082
  return n();
37010
37083
  },
@@ -37034,18 +37107,25 @@ function MarkdownText(e, t) {
37034
37107
  },
37035
37108
  set tableLabel(e) {
37036
37109
  s(e), flushSync();
37110
+ },
37111
+ get sanitize() {
37112
+ return c();
37113
+ },
37114
+ set sanitize(e) {
37115
+ c(e), flushSync();
37037
37116
  }
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);
37117
+ }, f = root$4();
37118
+ return html(f, () => get(l), !0), reset(f), template_effect(() => {
37119
+ 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);
37120
+ }), append(e, f), pop(u);
37042
37121
  }
37043
37122
  create_custom_element(MarkdownText, {
37044
37123
  markdown: {},
37045
37124
  breaks: {},
37046
37125
  testId: {},
37047
37126
  classes: {},
37048
- tableLabel: {}
37127
+ tableLabel: {},
37128
+ sanitize: {}
37049
37129
  }, [], [], { mode: "open" });
37050
37130
  //#endregion
37051
37131
  //#region src/wc/components/MarkdownText.wc.svelte
@@ -37070,7 +37150,8 @@ customElements.define("sui-markdown-text", create_custom_element(MarkdownText_wc
37070
37150
  tableLabel: {
37071
37151
  attribute: "table-label",
37072
37152
  type: "String"
37073
- }
37153
+ },
37154
+ sanitize: { type: "Object" }
37074
37155
  }, [], [], { mode: "open" }));
37075
37156
  //#endregion
37076
37157
  //#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.6.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",