@juspay/svelte-ui-components 4.3.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.
@@ -9,7 +9,8 @@
9
9
  classes,
10
10
  testId,
11
11
  titleSnippet,
12
- descriptionSnippet
12
+ descriptionSnippet,
13
+ density
13
14
  }: EmptyStateProperties = $props();
14
15
  </script>
15
16
 
@@ -17,6 +18,7 @@
17
18
  class="empty-state {classes ?? ''}"
18
19
  data-pw={typeof testId === 'string' ? testId : null}
19
20
  testID={typeof testId === 'string' ? testId : null}
21
+ data-density={typeof density === 'string' ? density : null}
20
22
  >
21
23
  {#if typeof icon === 'function'}
22
24
  <div class="empty-state-icon">
@@ -62,13 +64,28 @@
62
64
  .empty-state {
63
65
  display: flex;
64
66
  flex-direction: column;
65
- align-items: center;
67
+ align-items: var(--empty-state-align-items, center);
66
68
  padding: var(--empty-state-padding, 32px 16px);
67
69
  text-align: var(--empty-state-text-align, center);
68
70
  gap: var(--empty-state-gap, 0px);
69
71
  color: inherit;
70
72
  }
71
73
 
74
+ /* Density-scoped fallbacks. Higher specificity than `.empty-state` alone, so
75
+ these only take over when `data-density` is actually set — no attribute,
76
+ no match, today's defaults above stand untouched. `--empty-state-padding`
77
+ and `--empty-state-gap` are read first in both cases, so an instance-level
78
+ override always wins over either density's default. */
79
+ .empty-state[data-density='page'] {
80
+ padding: var(--empty-state-padding, 48px 24px);
81
+ gap: var(--empty-state-gap, 12px);
82
+ }
83
+
84
+ .empty-state[data-density='panel'] {
85
+ padding: var(--empty-state-padding, 16px 12px);
86
+ gap: var(--empty-state-gap, 4px);
87
+ }
88
+
72
89
  .empty-state-icon {
73
90
  width: var(--empty-state-icon-size, 48px);
74
91
  height: var(--empty-state-icon-size, 48px);
@@ -16,6 +16,16 @@ export type OptionalEmptyStateProperties = {
16
16
  children?: Snippet;
17
17
  classes?: string;
18
18
  testId?: string;
19
+ /**
20
+ * Sets sane padding/gap defaults for the placeholder's context, without
21
+ * requiring a wrapper class. Omit for today's default sizing (unchanged).
22
+ * `'page'` is roomier, for a full route/view standing in for its content.
23
+ * `'panel'` is tighter, for a constrained panel, popover, or rail.
24
+ * Exposed on the root element as `data-density` so a consumer's own CSS can
25
+ * target it too. Either default can still be overridden per-instance with
26
+ * `--empty-state-padding` / `--empty-state-gap`, which always take priority.
27
+ */
28
+ density?: 'page' | 'panel';
19
29
  /**
20
30
  * Optional snippet that replaces the `title` string at render time.
21
31
  * When provided, the mandatory `title` prop is still required for backward-compatibility
@@ -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
@@ -13717,12 +13717,12 @@ customElements.define("sui-card", create_custom_element(Card_wc, {
13717
13717
  //#region src/lib/EmptyState/EmptyState.svelte
13718
13718
  var root$44 = /* @__PURE__ */ from_html("<div class=\"empty-state-icon svelte-vmfces\"><!></div>"), root_1$37 = /* @__PURE__ */ from_html("<div class=\"empty-state-description svelte-vmfces\"><!></div>"), root_2$31 = /* @__PURE__ */ from_html("<div class=\"empty-state-description svelte-vmfces\"> </div>"), root_3$29 = /* @__PURE__ */ from_html("<div class=\"empty-state-actions svelte-vmfces\"><!></div>"), root_4$25 = /* @__PURE__ */ from_html("<div><!> <div class=\"empty-state-title svelte-vmfces\"><!></div> <!> <!></div>"), $$css$43 = {
13719
13719
  hash: "svelte-vmfces",
13720
- code: ".empty-state.svelte-vmfces {display:flex;flex-direction:column;align-items:center;padding:var(--empty-state-padding, 32px 16px);text-align:var(--empty-state-text-align, center);gap:var(--empty-state-gap, 0px);color:inherit;}.empty-state-icon.svelte-vmfces {width:var(--empty-state-icon-size, 48px);height:var(--empty-state-icon-size, 48px);color:var(--empty-state-icon-color, currentColor);opacity:var(--empty-state-icon-opacity, 0.4);margin-bottom:var(--empty-state-icon-margin-bottom, 16px);display:flex;align-items:center;justify-content:center;}.empty-state-icon.svelte-vmfces svg {width:100%;height:100%;}.empty-state-title.svelte-vmfces {font-size:var(--empty-state-title-font-size, 16px);font-weight:var(--empty-state-title-font-weight, 600);color:var(--empty-state-title-color, inherit);}.empty-state-description.svelte-vmfces {font-size:var(--empty-state-description-font-size, 14px);color:var(--empty-state-description-color, inherit);opacity:var(--empty-state-description-opacity, 0.6);max-width:var(--empty-state-description-max-width, 360px);margin-top:4px;}.empty-state-actions.svelte-vmfces {display:var(--empty-state-actions-display, block);flex-direction:var(--empty-state-actions-flex-direction, row);align-items:var(--empty-state-actions-align-items, stretch);justify-content:var(--empty-state-actions-justify-content, normal);gap:var(--empty-state-actions-gap, 0);margin-top:var(--empty-state-actions-margin-top, 16px);}"
13720
+ code: ".empty-state.svelte-vmfces {display:flex;flex-direction:column;align-items:var(--empty-state-align-items, center);padding:var(--empty-state-padding, 32px 16px);text-align:var(--empty-state-text-align, center);gap:var(--empty-state-gap, 0px);color:inherit;}\n\n /* Density-scoped fallbacks. Higher specificity than `.empty-state` alone, so\n these only take over when `data-density` is actually set — no attribute,\n no match, today's defaults above stand untouched. `--empty-state-padding`\n and `--empty-state-gap` are read first in both cases, so an instance-level\n override always wins over either density's default. */.empty-state[data-density='page'].svelte-vmfces {padding:var(--empty-state-padding, 48px 24px);gap:var(--empty-state-gap, 12px);}.empty-state[data-density='panel'].svelte-vmfces {padding:var(--empty-state-padding, 16px 12px);gap:var(--empty-state-gap, 4px);}.empty-state-icon.svelte-vmfces {width:var(--empty-state-icon-size, 48px);height:var(--empty-state-icon-size, 48px);color:var(--empty-state-icon-color, currentColor);opacity:var(--empty-state-icon-opacity, 0.4);margin-bottom:var(--empty-state-icon-margin-bottom, 16px);display:flex;align-items:center;justify-content:center;}.empty-state-icon.svelte-vmfces svg {width:100%;height:100%;}.empty-state-title.svelte-vmfces {font-size:var(--empty-state-title-font-size, 16px);font-weight:var(--empty-state-title-font-weight, 600);color:var(--empty-state-title-color, inherit);}.empty-state-description.svelte-vmfces {font-size:var(--empty-state-description-font-size, 14px);color:var(--empty-state-description-color, inherit);opacity:var(--empty-state-description-opacity, 0.6);max-width:var(--empty-state-description-max-width, 360px);margin-top:4px;}.empty-state-actions.svelte-vmfces {display:var(--empty-state-actions-display, block);flex-direction:var(--empty-state-actions-flex-direction, row);align-items:var(--empty-state-actions-align-items, stretch);justify-content:var(--empty-state-actions-justify-content, normal);gap:var(--empty-state-actions-gap, 0);margin-top:var(--empty-state-actions-margin-top, 16px);}"
13721
13721
  };
13722
13722
  function EmptyState(e, t) {
13723
13723
  push(t, !0), append_styles$1(e, $$css$43);
13724
- let n = prop(t, "title", 7), i = prop(t, "description", 7), a = prop(t, "icon", 7), o = prop(t, "children", 7), s = prop(t, "classes", 7), c = prop(t, "testId", 7), l = prop(t, "titleSnippet", 7), u = prop(t, "descriptionSnippet", 7);
13725
- var f = {
13724
+ let n = prop(t, "title", 7), i = prop(t, "description", 7), a = prop(t, "icon", 7), o = prop(t, "children", 7), s = prop(t, "classes", 7), c = prop(t, "testId", 7), l = prop(t, "titleSnippet", 7), u = prop(t, "descriptionSnippet", 7), f = prop(t, "density", 7);
13725
+ var p = {
13726
13726
  get title() {
13727
13727
  return n();
13728
13728
  },
@@ -13770,47 +13770,53 @@ function EmptyState(e, t) {
13770
13770
  },
13771
13771
  set descriptionSnippet(e) {
13772
13772
  u(e), flushSync();
13773
+ },
13774
+ get density() {
13775
+ return f();
13776
+ },
13777
+ set density(e) {
13778
+ f(e), flushSync();
13773
13779
  }
13774
- }, p = root_4$25(), h = child(p), S = (e) => {
13780
+ }, h = root_4$25(), S = child(h), C = (e) => {
13775
13781
  var t = root$44();
13776
13782
  snippet(child(t), a), reset(t), append(e, t);
13777
13783
  };
13778
- if_block(h, (e) => {
13779
- typeof a() == "function" && e(S);
13784
+ if_block(S, (e) => {
13785
+ typeof a() == "function" && e(C);
13780
13786
  });
13781
- var C = sibling(h, 2), k = child(C), R = (e) => {
13787
+ var k = sibling(S, 2), R = child(k), te = (e) => {
13782
13788
  var t = comment();
13783
13789
  snippet(first_child(t), l), append(e, t);
13784
- }, te = (e) => {
13790
+ }, ne = (e) => {
13785
13791
  var t = text();
13786
13792
  template_effect(() => set_text(t, n())), append(e, t);
13787
13793
  };
13788
- if_block(k, (e) => {
13789
- typeof l() == "function" ? e(R) : e(te, -1);
13790
- }), reset(C);
13791
- var ne = sibling(C, 2), re = (e) => {
13794
+ if_block(R, (e) => {
13795
+ typeof l() == "function" ? e(te) : e(ne, -1);
13796
+ }), reset(k);
13797
+ var re = sibling(k, 2), G = (e) => {
13792
13798
  var t = root_1$37();
13793
13799
  snippet(child(t), u), reset(t), template_effect(() => {
13794
13800
  set_attribute(t, "data-pw", typeof c() == "string" ? `${c()}-description` : null), set_attribute(t, "testid", typeof c() == "string" ? `${c()}-description` : null);
13795
13801
  }), append(e, t);
13796
- }, G = (e) => {
13802
+ }, be = (e) => {
13797
13803
  var t = root_2$31(), n = child(t, !0);
13798
13804
  reset(t), template_effect(() => {
13799
13805
  set_attribute(t, "data-pw", typeof c() == "string" ? `${c()}-description` : null), set_attribute(t, "testid", typeof c() == "string" ? `${c()}-description` : null), set_text(n, i());
13800
13806
  }), append(e, t);
13801
13807
  };
13802
- if_block(ne, (e) => {
13803
- typeof u() == "function" ? e(re) : typeof i() == "string" && i().length > 0 && e(G, 1);
13808
+ if_block(re, (e) => {
13809
+ typeof u() == "function" ? e(G) : typeof i() == "string" && i().length > 0 && e(be, 1);
13804
13810
  });
13805
- var be = sibling(ne, 2), Me = (e) => {
13811
+ var Me = sibling(re, 2), Re = (e) => {
13806
13812
  var t = root_3$29();
13807
13813
  snippet(child(t), o), reset(t), append(e, t);
13808
13814
  };
13809
- return if_block(be, (e) => {
13810
- typeof o() == "function" && e(Me);
13811
- }), reset(p), template_effect(() => {
13812
- set_class(p, 1, `empty-state ${s() ?? "" ?? ""}`, "svelte-vmfces"), set_attribute(p, "data-pw", typeof c() == "string" ? c() : null), set_attribute(p, "testid", typeof c() == "string" ? c() : null), set_attribute(C, "data-pw", typeof c() == "string" ? `${c()}-title` : null), set_attribute(C, "testid", typeof c() == "string" ? `${c()}-title` : null);
13813
- }), append(e, p), pop(f);
13815
+ return if_block(Me, (e) => {
13816
+ typeof o() == "function" && e(Re);
13817
+ }), reset(h), template_effect(() => {
13818
+ set_class(h, 1, `empty-state ${s() ?? "" ?? ""}`, "svelte-vmfces"), set_attribute(h, "data-pw", typeof c() == "string" ? c() : null), set_attribute(h, "testid", typeof c() == "string" ? c() : null), set_attribute(h, "data-density", typeof f() == "string" ? f() : null), set_attribute(k, "data-pw", typeof c() == "string" ? `${c()}-title` : null), set_attribute(k, "testid", typeof c() == "string" ? `${c()}-title` : null);
13819
+ }), append(e, h), pop(p);
13814
13820
  }
13815
13821
  create_custom_element(EmptyState, {
13816
13822
  title: {},
@@ -13820,7 +13826,8 @@ create_custom_element(EmptyState, {
13820
13826
  classes: {},
13821
13827
  testId: {},
13822
13828
  titleSnippet: {},
13823
- descriptionSnippet: {}
13829
+ descriptionSnippet: {},
13830
+ density: {}
13824
13831
  }, [], [], { mode: "open" });
13825
13832
  //#endregion
13826
13833
  //#region src/wc/components/EmptyState.wc.svelte
@@ -13884,6 +13891,7 @@ customElements.define("sui-empty-state", create_custom_element(EmptyState_wc, {
13884
13891
  attribute: "test-id",
13885
13892
  type: "String"
13886
13893
  },
13894
+ density: { type: "String" },
13887
13895
  icon: { type: "Object" },
13888
13896
  titleSnippet: { type: "Object" },
13889
13897
  descriptionSnippet: { type: "Object" }
@@ -33635,6 +33643,71 @@ function hasSafeProtocol(e, t) {
33635
33643
  function escapeHtml(e) {
33636
33644
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
33637
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
+ }
33638
33711
  function annotateExternalLinks(e) {
33639
33712
  return e.replace(EXTERNAL_ANCHOR_PATTERN, "<a href=\"$1\" target=\"_blank\" rel=\"noopener noreferrer\"");
33640
33713
  }
@@ -33643,12 +33716,18 @@ function wrapTables(e, t) {
33643
33716
  return e.replace(TABLE_OPEN, `${n}<table>`).replace(TABLE_CLOSE, "</table></div>");
33644
33717
  }
33645
33718
  function instanceFor(e) {
33646
- let t = e.breaks === !0, n = t ? "breaks" : "default", i = instances.get(n);
33647
- 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({
33648
33727
  gfm: !0,
33649
33728
  breaks: t,
33650
- renderer: sanitizingRenderer
33651
- }), instances.set(n, i)), i;
33729
+ renderer: createSanitizingRenderer(n, i, a)
33730
+ }), instances.set(o, s)), s;
33652
33731
  }
33653
33732
  function renderMarkdown(e, t = {}) {
33654
33733
  let n = instanceFor(t), i = t.inline === !0 ? n.parseInline(e) : n.parse(e);
@@ -33656,23 +33735,13 @@ function renderMarkdown(e, t = {}) {
33656
33735
  let a = annotateExternalLinks(i);
33657
33736
  return t.inline === !0 ? a : wrapTables(a, t.tableLabel);
33658
33737
  }
33659
- 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((() => {
33660
33739
  init_marked_esm(), SAFE_LINK_PROTOCOLS = new Set([
33661
33740
  "http:",
33662
33741
  "https:",
33663
33742
  "mailto:",
33664
33743
  "tel:"
33665
- ]), 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 = {
33666
- html(e) {
33667
- return escapeHtml(e.text);
33668
- },
33669
- link(e) {
33670
- return hasSafeProtocol(e.href, SAFE_LINK_PROTOCOLS) ? !1 : escapeHtml(e.text);
33671
- },
33672
- image(e) {
33673
- return hasSafeProtocol(e.href, SAFE_IMAGE_PROTOCOLS) ? !1 : escapeHtml(e.text);
33674
- }
33675
- }, 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;
33676
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 = {
33677
33746
  hash: "svelte-ivh8u",
33678
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;}"
@@ -36992,11 +37061,12 @@ var root$4 = /* @__PURE__ */ from_html("<div></div>"), $$css$5 = {
36992
37061
  };
36993
37062
  function MarkdownText(e, t) {
36994
37063
  push(t, !0), append_styles$1(e, $$css$5);
36995
- 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(), {
36996
37065
  breaks: i(),
36997
- tableLabel: s()
37066
+ tableLabel: s(),
37067
+ sanitize: c()
36998
37068
  }));
36999
- var l = {
37069
+ var u = {
37000
37070
  get markdown() {
37001
37071
  return n();
37002
37072
  },
@@ -37026,18 +37096,25 @@ function MarkdownText(e, t) {
37026
37096
  },
37027
37097
  set tableLabel(e) {
37028
37098
  s(e), flushSync();
37099
+ },
37100
+ get sanitize() {
37101
+ return c();
37102
+ },
37103
+ set sanitize(e) {
37104
+ c(e), flushSync();
37029
37105
  }
37030
- }, u = root$4();
37031
- return html(u, () => get(c), !0), reset(u), template_effect(() => {
37032
- 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);
37033
- }), 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);
37034
37110
  }
37035
37111
  create_custom_element(MarkdownText, {
37036
37112
  markdown: {},
37037
37113
  breaks: {},
37038
37114
  testId: {},
37039
37115
  classes: {},
37040
- tableLabel: {}
37116
+ tableLabel: {},
37117
+ sanitize: {}
37041
37118
  }, [], [], { mode: "open" });
37042
37119
  //#endregion
37043
37120
  //#region src/wc/components/MarkdownText.wc.svelte
@@ -37062,7 +37139,8 @@ customElements.define("sui-markdown-text", create_custom_element(MarkdownText_wc
37062
37139
  tableLabel: {
37063
37140
  attribute: "table-label",
37064
37141
  type: "String"
37065
- }
37142
+ },
37143
+ sanitize: { type: "Object" }
37066
37144
  }, [], [], { mode: "open" }));
37067
37145
  //#endregion
37068
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.3.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",