@marianmeres/stuic 3.143.0 → 3.145.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.
@@ -0,0 +1,337 @@
1
+ /* ============================================================================
2
+ SLIDER COMPONENT TOKENS
3
+ Override globally: :root { --stuic-slider-fill: var(--color-green-500); }
4
+ Override locally: <Slider style="--stuic-slider-fill: var(--color-green-500);">
5
+
6
+ Sizing (structural, override at any scope — resolved at the element):
7
+ --stuic-slider-thickness cross-axis size (also set by size="sm|md|lg")
8
+ --stuic-slider-length main-axis size (or use a width/height class)
9
+ --stuic-slider-thumb-inset gap between thumb and track edge
10
+ ============================================================================ */
11
+
12
+ :root {
13
+ /* Track (pill background) */
14
+ --stuic-slider-track: var(--stuic-color-muted);
15
+
16
+ /* Fill (value indicator) */
17
+ --stuic-slider-fill: var(--stuic-color-primary);
18
+
19
+ /* Thumb */
20
+ --stuic-slider-thumb: var(--color-white);
21
+ --stuic-slider-thumb-foreground: var(--stuic-color-foreground);
22
+
23
+ /* Tick marks (a second, fill-clipped layer keeps them readable over the fill) */
24
+ --stuic-slider-tick: color-mix(in srgb, var(--stuic-color-foreground) 25%, transparent);
25
+ --stuic-slider-tick-on-fill: color-mix(
26
+ in srgb,
27
+ var(--stuic-color-background) 55%,
28
+ transparent
29
+ );
30
+
31
+ /* Focus ring */
32
+ --stuic-slider-ring-width: 4px;
33
+ --stuic-slider-ring-color: var(--stuic-color-ring);
34
+ }
35
+
36
+ @layer components {
37
+ /* ============================================================================
38
+ BASE / ROOT
39
+ ============================================================================ */
40
+
41
+ .stuic-slider {
42
+ /* Internal vars (intent may override the fill) */
43
+ --_track: var(--stuic-slider-track);
44
+ --_fill: var(--stuic-slider-fill);
45
+ --_thumb-bg: var(--stuic-slider-thumb);
46
+ --_thumb-fg: var(--stuic-slider-thumb-foreground);
47
+ --_tick: var(--stuic-slider-tick);
48
+ --_tick-on-fill: var(--stuic-slider-tick-on-fill);
49
+ --_tick-size: var(--stuic-slider-tick-size, 4px);
50
+
51
+ /* Structural (fallbacks resolved here so scoped overrides work) */
52
+ --_thickness: var(--stuic-slider-thickness, 2rem);
53
+ --_length: var(--stuic-slider-length, 10rem);
54
+ --_thumb-inset: var(--stuic-slider-thumb-inset, 3px);
55
+ /* Main-axis extent of the fill (percentages resolve against the track,
56
+ which is the containing block of both the fill and the tick layers) */
57
+ --_fill-len: calc(var(--_ratio) * 100%);
58
+
59
+ display: inline-block;
60
+ vertical-align: middle;
61
+ flex-shrink: 0;
62
+ cursor: pointer;
63
+ }
64
+
65
+ /* FUNCTIONAL, not cosmetic — keyed on an attribute the component always emits
66
+ so it survives `unstyled`: `touch-action: none` keeps the browser from
67
+ stealing touch drags for scrolling, `position: relative` anchors the
68
+ absolutely-positioned track/thumb/hidden input. Attribute specificity (0,1,0)
69
+ leaves it overridable by a consumer utility class (Tailwind's utilities layer
70
+ beats this components layer). */
71
+ [data-stuic-slider] {
72
+ position: relative;
73
+ touch-action: none;
74
+ user-select: none;
75
+ -webkit-user-select: none;
76
+ -webkit-tap-highlight-color: transparent;
77
+ }
78
+
79
+ .stuic-slider[data-size="sm"] {
80
+ --_thickness: var(--stuic-slider-thickness, 1.25rem);
81
+ }
82
+ /* md is the base default (2rem) */
83
+ .stuic-slider[data-size="lg"] {
84
+ --_thickness: var(--stuic-slider-thickness, 3rem);
85
+ }
86
+
87
+ .stuic-slider[data-orientation="horizontal"] {
88
+ width: var(--_length);
89
+ height: var(--_thickness);
90
+ }
91
+ .stuic-slider[data-orientation="vertical"] {
92
+ width: var(--_thickness);
93
+ height: var(--_length);
94
+ }
95
+
96
+ /* Disabled */
97
+ .stuic-slider[data-disabled="true"] {
98
+ cursor: not-allowed;
99
+ opacity: 0.6;
100
+ }
101
+
102
+ /* ============================================================================
103
+ TRACK + FILL
104
+ ============================================================================ */
105
+
106
+ .stuic-slider > .track {
107
+ position: absolute;
108
+ inset: 0;
109
+ overflow: hidden;
110
+ border-radius: var(--stuic-slider-radius, 9999px);
111
+ background: var(--_track);
112
+ transition: box-shadow var(--stuic-slider-transition, var(--stuic-transition));
113
+ }
114
+
115
+ .stuic-slider .fill {
116
+ position: absolute;
117
+ background: var(--_fill);
118
+ }
119
+ /* With a value-tracking thumb, the fill reaches the far edge of the thumb */
120
+ .stuic-slider[data-thumb-travels="true"] {
121
+ --_fill-len: calc(var(--_ratio) * (100% - var(--_thickness)) + var(--_thickness));
122
+ }
123
+ .stuic-slider[data-orientation="horizontal"] .fill {
124
+ inset-block: 0;
125
+ inset-inline-start: 0;
126
+ width: var(--_fill-len);
127
+ }
128
+ .stuic-slider[data-orientation="vertical"] .fill {
129
+ inset-inline: 0;
130
+ bottom: 0;
131
+ height: var(--_fill-len);
132
+ }
133
+
134
+ /* Rounded leading edge ("pill inside a pill") */
135
+ .stuic-slider[data-fill-rounded="true"] .fill {
136
+ border-radius: var(--stuic-slider-fill-radius, var(--stuic-slider-radius, 9999px));
137
+ }
138
+
139
+ /* ============================================================================
140
+ THUMB
141
+ ============================================================================ */
142
+
143
+ .stuic-slider > .thumb {
144
+ position: absolute;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: calc(var(--_thickness) - 2 * var(--_thumb-inset));
149
+ height: calc(var(--_thickness) - 2 * var(--_thumb-inset));
150
+ border-radius: var(--stuic-slider-thumb-radius, 9999px);
151
+ background: var(--_thumb-bg);
152
+ color: var(--_thumb-fg);
153
+ box-shadow: var(--stuic-slider-thumb-shadow, var(--stuic-shadow));
154
+ /* No pointer-events rules needed: the full-size hidden input is the last
155
+ child and always the topmost hit target, so the thumb never receives
156
+ pointer events. Grab detection is geometric, in _onpointerdown. */
157
+ }
158
+ .stuic-slider[data-orientation="horizontal"] > .thumb {
159
+ inset-block-start: var(--_thumb-inset);
160
+ inset-inline-start: var(--_thumb-inset);
161
+ }
162
+ .stuic-slider[data-orientation="vertical"] > .thumb {
163
+ inset-inline-start: var(--_thumb-inset);
164
+ bottom: var(--_thumb-inset);
165
+ }
166
+ /* thumbPosition="value": ride the fill edge (default). thumbPosition="start"
167
+ keeps the offsets above, so only the bar moves (iOS volume look). */
168
+ .stuic-slider[data-thumb-travels="true"][data-orientation="horizontal"] > .thumb {
169
+ inset-inline-start: calc(
170
+ var(--_thumb-inset) + (100% - var(--_thickness)) * var(--_ratio)
171
+ );
172
+ }
173
+ .stuic-slider[data-thumb-travels="true"][data-orientation="vertical"] > .thumb {
174
+ bottom: calc(var(--_thumb-inset) + (100% - var(--_thickness)) * var(--_ratio));
175
+ }
176
+ /* Smooth value movement (keyboard steps); instant while dragging */
177
+ .stuic-slider .fill,
178
+ .stuic-slider > .thumb,
179
+ .stuic-slider > .value {
180
+ transition:
181
+ width var(--stuic-slider-transition, var(--stuic-transition)),
182
+ height var(--stuic-slider-transition, var(--stuic-transition)),
183
+ inset-inline-start var(--stuic-slider-transition, var(--stuic-transition)),
184
+ bottom var(--stuic-slider-transition, var(--stuic-transition));
185
+ }
186
+ .stuic-slider[data-dragging="true"] .fill,
187
+ .stuic-slider[data-dragging="true"] > .thumb,
188
+ .stuic-slider[data-dragging="true"] > .value {
189
+ transition: none;
190
+ }
191
+
192
+ /* ============================================================================
193
+ TICKS
194
+ ============================================================================ */
195
+
196
+ /* Two identical tick layers clipped to complementary halves of the track: the
197
+ base one covers the unfilled part, the `on-fill` one the filled part, each
198
+ with its own color. A single layer would vanish into whichever it overlaps,
199
+ and overlapping layers would composite (both tick colors are translucent). */
200
+ .stuic-slider .ticks {
201
+ position: absolute;
202
+ inset: 0;
203
+ transition: clip-path var(--stuic-slider-transition, var(--stuic-transition));
204
+ }
205
+ .stuic-slider[data-dragging="true"] .ticks {
206
+ transition: none;
207
+ }
208
+ .stuic-slider .ticks[data-on-fill="true"] .tick {
209
+ background: var(--_tick-on-fill);
210
+ }
211
+ .stuic-slider[data-orientation="horizontal"] .ticks {
212
+ clip-path: inset(0 0 0 var(--_fill-len));
213
+ }
214
+ .stuic-slider[data-orientation="horizontal"] .ticks[data-on-fill="true"] {
215
+ clip-path: inset(0 calc(100% - var(--_fill-len)) 0 0);
216
+ }
217
+ .stuic-slider[data-orientation="horizontal"]:dir(rtl) .ticks {
218
+ clip-path: inset(0 var(--_fill-len) 0 0);
219
+ }
220
+ .stuic-slider[data-orientation="horizontal"]:dir(rtl) .ticks[data-on-fill="true"] {
221
+ clip-path: inset(0 0 0 calc(100% - var(--_fill-len)));
222
+ }
223
+ .stuic-slider[data-orientation="vertical"] .ticks {
224
+ clip-path: inset(0 0 var(--_fill-len) 0);
225
+ }
226
+ .stuic-slider[data-orientation="vertical"] .ticks[data-on-fill="true"] {
227
+ clip-path: inset(calc(100% - var(--_fill-len)) 0 0 0);
228
+ }
229
+
230
+ .stuic-slider .tick {
231
+ position: absolute;
232
+ width: var(--_tick-size);
233
+ height: var(--_tick-size);
234
+ border-radius: 9999px;
235
+ background: var(--_tick);
236
+ }
237
+ /* Without a traveling thumb there is no thickness inset to hide behind, so the
238
+ travel is inset by half a tick — otherwise the ticks at ratio 0 and 1 sit
239
+ centered on the track edges and the overflow:hidden track halves them. */
240
+ .stuic-slider[data-orientation="horizontal"] .tick {
241
+ top: 50%;
242
+ inset-inline-start: calc(
243
+ var(--_tick-size) / 2 + (100% - var(--_tick-size)) * var(--_tick-ratio)
244
+ );
245
+ transform: translate(-50%, -50%);
246
+ }
247
+ .stuic-slider[data-orientation="vertical"] .tick {
248
+ left: 50%;
249
+ bottom: calc(var(--_tick-size) / 2 + (100% - var(--_tick-size)) * var(--_tick-ratio));
250
+ transform: translate(-50%, 50%);
251
+ }
252
+ /* With a value-tracking thumb, ticks align to the thumb-center travel */
253
+ .stuic-slider[data-thumb-travels="true"][data-orientation="horizontal"] .tick {
254
+ inset-inline-start: calc(
255
+ var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_tick-ratio)
256
+ );
257
+ }
258
+ .stuic-slider[data-thumb-travels="true"][data-orientation="vertical"] .tick {
259
+ bottom: calc(var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_tick-ratio));
260
+ }
261
+ /* Logical-property positioning + physical translate: flip centering in RTL */
262
+ .stuic-slider[data-orientation="horizontal"]:dir(rtl) .tick {
263
+ transform: translate(50%, -50%);
264
+ }
265
+
266
+ /* ============================================================================
267
+ VALUE LABEL
268
+ ============================================================================ */
269
+
270
+ .stuic-slider > .value {
271
+ position: absolute;
272
+ pointer-events: none;
273
+ white-space: nowrap;
274
+ font-size: var(--text-xs);
275
+ color: var(--stuic-color-muted-foreground);
276
+ }
277
+ .stuic-slider[data-orientation="horizontal"] > .value {
278
+ bottom: calc(100% + var(--stuic-slider-value-gap, 0.375rem));
279
+ inset-inline-start: calc(var(--_ratio) * 100%);
280
+ transform: translateX(-50%);
281
+ }
282
+ .stuic-slider[data-orientation="vertical"] > .value {
283
+ inset-inline-start: calc(100% + var(--stuic-slider-value-gap, 0.375rem));
284
+ bottom: calc(var(--_ratio) * 100%);
285
+ transform: translateY(50%);
286
+ }
287
+ .stuic-slider[data-thumb-travels="true"][data-orientation="horizontal"] > .value {
288
+ inset-inline-start: calc(
289
+ var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_ratio)
290
+ );
291
+ }
292
+ .stuic-slider[data-thumb-travels="true"][data-orientation="vertical"] > .value {
293
+ bottom: calc(var(--_thickness) / 2 + (100% - var(--_thickness)) * var(--_ratio));
294
+ }
295
+ .stuic-slider[data-orientation="horizontal"]:dir(rtl) > .value {
296
+ transform: translateX(50%);
297
+ }
298
+
299
+ /* The full-size hidden input is the topmost hit target */
300
+ .stuic-slider > input {
301
+ cursor: inherit;
302
+ }
303
+
304
+ /* ============================================================================
305
+ FOCUS RING (keyboard-initiated focus; data-ring is set by the component —
306
+ :focus-visible is unreliable for pointer-initiated programmatic focus)
307
+ ============================================================================ */
308
+
309
+ .stuic-slider[data-ring="true"] > .track {
310
+ box-shadow: 0 0 0 var(--stuic-slider-ring-width)
311
+ color-mix(
312
+ in srgb,
313
+ var(--stuic-slider-ring-color) 30%,
314
+ var(--stuic-color-background)
315
+ );
316
+ }
317
+
318
+ /* ============================================================================
319
+ INTENT COLOR MAPPING
320
+ ============================================================================ */
321
+
322
+ .stuic-slider[data-intent="primary"] {
323
+ --_fill: var(--stuic-color-primary);
324
+ }
325
+ .stuic-slider[data-intent="accent"] {
326
+ --_fill: var(--stuic-color-accent);
327
+ }
328
+ .stuic-slider[data-intent="success"] {
329
+ --_fill: var(--stuic-color-success);
330
+ }
331
+ .stuic-slider[data-intent="warning"] {
332
+ --_fill: var(--stuic-color-warning);
333
+ }
334
+ .stuic-slider[data-intent="destructive"] {
335
+ --_fill: var(--stuic-color-destructive);
336
+ }
337
+ }
@@ -0,0 +1 @@
1
+ export { default as Slider, type Props as SliderProps, type SliderIntent, type SliderOrientation, type SliderThumbPosition, type SliderRenderCtx, } from "./Slider.svelte";
@@ -0,0 +1 @@
1
+ export { default as Slider, } from "./Slider.svelte";
package/dist/index.css CHANGED
@@ -71,7 +71,9 @@ In practice:
71
71
  @import "./components/LoginOrRegisterForm/index.css";
72
72
  @import "./components/Checkout/index.css";
73
73
  @import "./components/CommandMenu/index.css";
74
- @import "./components/CommentInput/index.css";
74
+ /* NOTE: CommentInput/index.css is deliberately NOT imported here — same reason as
75
+ MarkdownEditor's. It is a subpath-export component and imports its own CSS
76
+ locally (see components/CommentInput/index.css). Do not re-add. */
75
77
  @import "./components/ContactUsForm/index.css";
76
78
  @import "./components/CronInput/index.css";
77
79
  @import "./components/DataTable/index.css";
@@ -96,6 +98,7 @@ In practice:
96
98
  @import "./components/Progress/index.css";
97
99
  @import "./components/Separator/index.css";
98
100
  @import "./components/Skeleton/index.css";
101
+ @import "./components/Slider/index.css";
99
102
  @import "./components/Spinner/index.css";
100
103
  @import "./components/Switch/index.css";
101
104
  @import "./components/TabbedMenu/index.css";
package/dist/index.d.ts CHANGED
@@ -38,7 +38,6 @@ export * from "./components/Checkout/index.js";
38
38
  export * from "./components/Collapsible/index.js";
39
39
  export * from "./components/ColorScheme/index.js";
40
40
  export * from "./components/CommandMenu/index.js";
41
- export * from "./components/CommentInput/index.js";
42
41
  export * from "./components/ContactUsForm/index.js";
43
42
  export * from "./components/CronInput/index.js";
44
43
  export * from "./components/DataTable/index.js";
@@ -68,6 +67,7 @@ export * from "./components/PricingTable/index.js";
68
67
  export * from "./components/Progress/index.js";
69
68
  export * from "./components/Separator/index.js";
70
69
  export * from "./components/Skeleton/index.js";
70
+ export * from "./components/Slider/index.js";
71
71
  export * from "./components/SlidingPanels/index.js";
72
72
  export * from "./components/Spinner/index.js";
73
73
  export * from "./components/Switch/index.js";
package/dist/index.js CHANGED
@@ -39,7 +39,11 @@ export * from "./components/Checkout/index.js";
39
39
  export * from "./components/Collapsible/index.js";
40
40
  export * from "./components/ColorScheme/index.js";
41
41
  export * from "./components/CommandMenu/index.js";
42
- export * from "./components/CommentInput/index.js";
42
+ // NOTE: CommentInput is deliberately NOT exported here. It embeds MarkdownEditor,
43
+ // whose optional @milkdown/* + @codemirror/* peers would then be pulled into the
44
+ // root entry graph of every consumer. It ships as the `@marianmeres/stuic/comment-input`
45
+ // subpath export instead, mirroring `@marianmeres/stuic/markdown-editor`.
46
+ // Guarded by src/lib/barrel-optional-peers.test.ts — do not re-add.
43
47
  export * from "./components/ContactUsForm/index.js";
44
48
  export * from "./components/CronInput/index.js";
45
49
  export * from "./components/DataTable/index.js";
@@ -69,6 +73,7 @@ export * from "./components/PricingTable/index.js";
69
73
  export * from "./components/Progress/index.js";
70
74
  export * from "./components/Separator/index.js";
71
75
  export * from "./components/Skeleton/index.js";
76
+ export * from "./components/Slider/index.js";
72
77
  export * from "./components/SlidingPanels/index.js";
73
78
  export * from "./components/Spinner/index.js";
74
79
  export * from "./components/Switch/index.js";
@@ -80,6 +80,17 @@ src/lib/
80
80
 
81
81
  **DO NOT** use `import './index.css'` inside component `.svelte` files.
82
82
 
83
+ **Exception — subpath-export components.** A component whose peer dependencies are
84
+ declared _optional_ (`MarkdownEditor`, `CommentInput`) is kept off the root barrel and
85
+ shipped behind its own subpath export, so those peers never enter the entry graph of
86
+ consumers who don't use it. Such a component imports its own `index.css` locally and is
87
+ **not** `@import`-ed into `src/lib/index.css` — centralizing it would ship the styles to
88
+ every consumer, defeating the isolation. `sideEffects: ["**/*.css"]` in `package.json`
89
+ keeps the local import alive for real users.
90
+
91
+ Both halves are enforced by `src/lib/barrel-optional-peers.test.ts` — adding such a
92
+ component to the barrel (or its CSS to the central stylesheet) fails the suite.
93
+
83
94
  ---
84
95
 
85
96
  ## Data Flow
@@ -151,9 +162,20 @@ Theme CSS files are provided by the `@marianmeres/design-tokens` package (a regu
151
162
  ### Package Exports
152
163
 
153
164
  ```json
154
- ".": Main entry (components, utils, actions, icons, theme types)
165
+ ".": Main entry (components, utils, actions, icons, theme types)
166
+ "./utils": Utilities only
167
+ "./phone-validation": Phone validation helpers
168
+ "./markdown-editor": MarkdownEditor — optional peer deps, local CSS
169
+ "./comment-input": CommentInput — optional peer deps, local CSS
155
170
  ```
156
171
 
172
+ The last two are deliberately **off** the main entry: they reach `@milkdown/*` and
173
+ `@codemirror/*`, which are optional peers. A consumer that has not installed those
174
+ peers gets a hard bundler error (rolldown reports every named import from the
175
+ unresolved stub as `MISSING_EXPORT`) if the root barrel can reach them — even though
176
+ the editor backends sit behind `await import()`, because the specifiers are static
177
+ literals that a bundler must still resolve at build time.
178
+
157
179
  ---
158
180
 
159
181
  ## Security Boundaries
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 57 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
5
+ 63 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
@@ -29,6 +29,7 @@
29
29
  | Button | Actions with intent/variant/size |
30
30
  | ButtonGroupRadio | Toggle group (single selection) |
31
31
  | Switch | Boolean toggle |
32
+ | Slider | Fancy range input (fill + optional thumb) |
32
33
  | TwCheck | Styled checkbox/radio |
33
34
  | DropdownMenu | Popover menu |
34
35
  | CommandMenu | Command palette (keyboard-driven) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.143.0",
3
+ "version": "3.145.0",
4
4
  "packageManager": "pnpm@11.5.0",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -19,8 +19,9 @@
19
19
  "test:ui": "vitest --ui",
20
20
  "svelte-check": "svelte-check",
21
21
  "svelte-package": "svelte-package",
22
- "rp": "pnpm run build && ./release.sh patch",
23
- "rpm": "pnpm run build && ./release.sh minor"
22
+ "rp": "pnpm run build && deno run -A jsr:@marianmeres/release patch --no-push && npm publish --access public && git push --follow-tags",
23
+ "rpm": "pnpm run build && deno run -A jsr:@marianmeres/release minor --no-push && npm publish --access public && git push --follow-tags",
24
+ "release": "deno run -A jsr:@marianmeres/release"
24
25
  },
25
26
  "files": [
26
27
  "dist",
@@ -54,6 +55,11 @@
54
55
  "types": "./dist/components/MarkdownEditor/index.d.ts",
55
56
  "svelte": "./dist/components/MarkdownEditor/index.js",
56
57
  "default": "./dist/components/MarkdownEditor/index.js"
58
+ },
59
+ "./comment-input": {
60
+ "types": "./dist/components/CommentInput/index.d.ts",
61
+ "svelte": "./dist/components/CommentInput/index.js",
62
+ "default": "./dist/components/CommentInput/index.js"
57
63
  }
58
64
  },
59
65
  "peerDependencies": {
@@ -72,8 +78,6 @@
72
78
  "@milkdown/prose": "^7.0.0",
73
79
  "@milkdown/transformer": "^7.0.0",
74
80
  "@milkdown/utils": "^7.0.0",
75
- "dompurify": "^3.0.0",
76
- "marked": "^18.0.0",
77
81
  "svelte": "^5.0.0"
78
82
  },
79
83
  "peerDependenciesMeta": {
@@ -121,12 +125,6 @@
121
125
  },
122
126
  "@milkdown/utils": {
123
127
  "optional": true
124
- },
125
- "dompurify": {
126
- "optional": true
127
- },
128
- "marked": {
129
- "optional": true
130
128
  }
131
129
  },
132
130
  "devDependencies": {
@@ -157,11 +155,9 @@
157
155
  "@tailwindcss/vite": "^4.3.1",
158
156
  "@types/node": "^25.9.4",
159
157
  "@vitest/browser-playwright": "^4.1.9",
160
- "dompurify": "^3.4.11",
161
158
  "dotenv": "^16.6.1",
162
159
  "eslint": "^9.39.4",
163
160
  "globals": "^16.5.0",
164
- "marked": "^18.0.5",
165
161
  "playwright": "^1.61.1",
166
162
  "prettier": "^3.9.0",
167
163
  "prettier-plugin-svelte": "^3.5.2",