@warkypublic/svelix 0.1.27 → 0.1.28

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.
@@ -37,6 +37,10 @@
37
37
  value = $bindable<any>(undefined),
38
38
  }: BoxerProps = $props();
39
39
 
40
+ const instanceId = `boxer-${Math.random().toString(36).slice(2, 10)}`;
41
+ const inputId = `${instanceId}-input`;
42
+ const listboxId = `${instanceId}-listbox`;
43
+
40
44
  // Derive effective dataSource and onAPICall from adapter when not explicitly provided
41
45
  const dataSource = dataSourceProp ?? (adapter ? "server" : "local");
42
46
  const onAPICall =
@@ -88,6 +92,7 @@
88
92
  let popupTop = $state(0);
89
93
  let popupLeft = $state(0);
90
94
  let popupWidth = $state(0);
95
+ let activeOptionIndex = $state<number | null>(null);
91
96
  let backdropZ = $state(1090);
92
97
  let dropdownZ = $state(1100);
93
98
  let pointerInteractingWithDropdown = $state(false);
@@ -209,6 +214,8 @@
209
214
  const option = $store.boxerData?.[index];
210
215
  if (!option) return;
211
216
 
217
+ activeOptionIndex = index;
218
+
212
219
  if (multiSelect) {
213
220
  const currentValues = Array.isArray(value) ? value : [];
214
221
  const isSelected = currentValues.includes(option.value);
@@ -227,6 +234,7 @@
227
234
  store.setSearch("");
228
235
  store.setInput(option.label);
229
236
  store.setOpened(false);
237
+ activeOptionIndex = null;
230
238
  }
231
239
  }
232
240
 
@@ -238,6 +246,7 @@
238
246
  onChange?.(value);
239
247
  store.setSearch("");
240
248
  store.setInput("");
249
+ activeOptionIndex = null;
241
250
  targetRef?.focus();
242
251
  }
243
252
  if (openOnClear) {
@@ -247,6 +256,7 @@
247
256
 
248
257
  function focusDropdownItem(index: number) {
249
258
  const clamped = Math.max(0, Math.min(index, $store.boxerData.length - 1));
259
+ activeOptionIndex = clamped;
250
260
  requestAnimationFrame(() => {
251
261
  const el = parentEl?.querySelector<HTMLDivElement>(
252
262
  `[data-index="${clamped}"]`,
@@ -277,17 +287,20 @@
277
287
  );
278
288
  if (selectedIndex >= 0) {
279
289
  store.setOpened(false);
290
+ activeOptionIndex = null;
280
291
  } else {
281
292
  onOptionSubmit(0);
282
293
  }
283
294
  } else {
284
295
  store.setOpened(false);
296
+ activeOptionIndex = null;
285
297
  }
286
298
  break;
287
299
  case "Escape":
288
300
  if ($store.opened) {
289
301
  e.preventDefault();
290
302
  store.setOpened(false);
303
+ activeOptionIndex = null;
291
304
  }
292
305
  break;
293
306
  }
@@ -302,6 +315,7 @@
302
315
  case "ArrowUp":
303
316
  e.preventDefault();
304
317
  if (index === 0) {
318
+ activeOptionIndex = null;
305
319
  targetRef?.focus();
306
320
  } else {
307
321
  focusDropdownItem(index - 1);
@@ -316,6 +330,7 @@
316
330
  case "Escape":
317
331
  e.preventDefault();
318
332
  store.setOpened(false);
333
+ activeOptionIndex = null;
319
334
  targetRef?.focus();
320
335
  break;
321
336
  }
@@ -326,7 +341,8 @@
326
341
  const rect = anchorEl.getBoundingClientRect();
327
342
  const viewportWidth = typeof window === "undefined" ? rect.width : window.innerWidth;
328
343
  const horizontalPadding = 8;
329
- const nextWidth = Math.max(220, rect.width);
344
+ const maxWidth = Math.max(160, viewportWidth - horizontalPadding * 2);
345
+ const nextWidth = Math.min(Math.max(220, rect.width), maxWidth);
330
346
  const maxLeft = viewportWidth - nextWidth - horizontalPadding;
331
347
 
332
348
  popupTop = rect.bottom + 4;
@@ -368,6 +384,7 @@
368
384
  }
369
385
  export function close() {
370
386
  store.setOpened(false);
387
+ activeOptionIndex = null;
371
388
  }
372
389
  export function focus() {
373
390
  targetRef?.focus();
@@ -377,6 +394,7 @@
377
394
  }
378
395
  export function open() {
379
396
  store.setOpened(true);
397
+ activeOptionIndex = null;
380
398
  }
381
399
  export function setValue(val: unknown) {
382
400
  value = val;
@@ -462,6 +480,7 @@
462
480
  const relatedTarget = e.relatedTarget as Node | null;
463
481
  const insideDropdown = !!(relatedTarget && dropdownEl?.contains(relatedTarget));
464
482
  if (!currentTarget.contains(relatedTarget) && !insideDropdown) {
483
+ activeOptionIndex = null;
465
484
  if (!value && !multiSelect) {
466
485
  store.setSearch("");
467
486
  store.setInput("");
@@ -472,9 +491,13 @@
472
491
  >
473
492
  <BoxerTarget
474
493
  bind:this={targetRef}
494
+ activeDescendant={activeOptionIndex !== null ? `${instanceId}-option-${activeOptionIndex}` : undefined}
475
495
  {clearable}
496
+ controlsId={listboxId}
476
497
  {disabled}
477
498
  {error}
499
+ expanded={$store.opened}
500
+ {inputId}
478
501
  {label}
479
502
  {leftSection}
480
503
  {placeholder}
@@ -486,6 +509,7 @@
486
509
  onsearch={(v) => {
487
510
  store.setSearch(v);
488
511
  store.setInput(v);
512
+ activeOptionIndex = null;
489
513
  store.setOpened(true);
490
514
  }}
491
515
  />
@@ -495,6 +519,7 @@
495
519
  {#if !effectiveDisablePortal}
496
520
  <div
497
521
  class="fixed inset-0"
522
+ data-testid="boxer-backdrop"
498
523
  onpointerdown={onBackdropPointerDown}
499
524
  style:z-index={backdropZ}
500
525
  ></div>
@@ -505,6 +530,8 @@
505
530
  class={`${effectiveDisablePortal
506
531
  ? "absolute left-0 right-0 top-full mt-1"
507
532
  : "fixed"} card bg-surface-50-950 shadow-lg border border-surface-300-700 overflow-hidden`}
533
+ data-testid="boxer-dropdown"
534
+ id={listboxId}
508
535
  role="listbox"
509
536
  aria-label={label ?? "Options"}
510
537
  onpointerdown={markDropdownPointerInteraction}
@@ -539,6 +566,7 @@
539
566
  class:bg-primary-100={isSelected}
540
567
  class:dark:bg-primary-900={isSelected}
541
568
  data-index={vRow.index}
569
+ id={`${instanceId}-option-${vRow.index}`}
542
570
  role="option"
543
571
  aria-selected={isSelected}
544
572
  tabindex="-1"
@@ -561,7 +589,14 @@
561
589
  </div>
562
590
  </div>
563
591
  {:else}
564
- <div class="px-3 py-2 text-sm text-surface-500">Nothing found</div>
592
+ <div
593
+ aria-live="polite"
594
+ class="px-3 py-2 text-sm text-surface-500"
595
+ data-testid="boxer-empty"
596
+ role="status"
597
+ >
598
+ no data
599
+ </div>
565
600
  {/if}
566
601
  </div>
567
602
  {/if}
@@ -2,9 +2,13 @@
2
2
  import type { Snippet } from "svelte";
3
3
 
4
4
  export interface Props {
5
+ activeDescendant?: string;
6
+ controlsId?: string;
5
7
  clearable?: boolean;
6
8
  disabled?: boolean;
7
9
  error?: string;
10
+ expanded?: boolean;
11
+ inputId?: string;
8
12
  isFetching?: boolean;
9
13
  label?: string;
10
14
  leftSection?: Snippet;
@@ -17,9 +21,13 @@
17
21
  }
18
22
 
19
23
  let {
24
+ activeDescendant,
25
+ controlsId,
20
26
  clearable = true,
21
27
  disabled,
22
28
  error,
29
+ expanded = false,
30
+ inputId = 'boxer-input',
23
31
  isFetching,
24
32
  label,
25
33
  leftSection,
@@ -40,7 +48,7 @@
40
48
 
41
49
  <div class="flex flex-col gap-1">
42
50
  {#if label}
43
- <label class="label text-sm font-medium" for="boxer-input">{label}</label>
51
+ <label class="label text-sm font-medium" for={inputId}>{label}</label>
44
52
  {/if}
45
53
  <div class="flex items-center relative">
46
54
  {#if leftSection}
@@ -49,9 +57,17 @@
49
57
  </div>
50
58
  {/if}
51
59
  <input
52
- id="boxer-input"
60
+ id={inputId}
53
61
  bind:this={inputEl}
54
62
  bind:value={search}
63
+ aria-activedescendant={activeDescendant}
64
+ aria-autocomplete="list"
65
+ aria-controls={controlsId}
66
+ aria-describedby={error ? `${inputId}-error` : undefined}
67
+ aria-expanded={expanded}
68
+ aria-haspopup="listbox"
69
+ aria-invalid={!!error}
70
+ role="combobox"
55
71
  class="input pr-8"
56
72
  class:input-error={!!error}
57
73
  class:pl-8={!!leftSection}
@@ -82,6 +98,6 @@
82
98
  </div>
83
99
  </div>
84
100
  {#if error}
85
- <p class="text-error-500 text-xs">{error}</p>
101
+ <p class="text-error-500 text-xs" id={`${inputId}-error`}>{error}</p>
86
102
  {/if}
87
103
  </div>
@@ -1,8 +1,12 @@
1
1
  import type { Snippet } from "svelte";
2
2
  export interface Props {
3
+ activeDescendant?: string;
4
+ controlsId?: string;
3
5
  clearable?: boolean;
4
6
  disabled?: boolean;
5
7
  error?: string;
8
+ expanded?: boolean;
9
+ inputId?: string;
6
10
  isFetching?: boolean;
7
11
  label?: string;
8
12
  leftSection?: Snippet;
@@ -131,6 +131,7 @@
131
131
  let dragHeaderCurrentX = $state(0);
132
132
  let announcement = $state("");
133
133
  let hasFocus = $state(false);
134
+ const gridId = `gridler-${Math.random().toString(36).slice(2, 10)}`;
134
135
 
135
136
  let isDraggingVScrollbar = $state(false);
136
137
  let isDraggingHScrollbar = $state(false);
@@ -141,7 +142,13 @@
141
142
  let isHoveringVScrollbar = $state(false);
142
143
  let isHoveringHScrollbar = $state(false);
143
144
  let hoverMenuButton = $state(false);
145
+ let pendingGridMenuOpen = $state(false);
144
146
  let hoverSortCol = $state<number | null>(null);
147
+ let touchStartClientX = $state(0);
148
+ let touchStartClientY = $state(0);
149
+ let touchStartScrollX = $state(0);
150
+ let touchStartScrollY = $state(0);
151
+ let isTouchScrolling = $state(false);
145
152
 
146
153
  let containerRef: HTMLDivElement | undefined = $state();
147
154
  let canvasRef: HTMLCanvasElement | undefined = $state();
@@ -201,6 +208,35 @@
201
208
  const containerHeightStyle = $derived(
202
209
  typeof height === "number" ? `${height}px` : height,
203
210
  );
211
+ const activeCellDescription = $derived.by(() => {
212
+ if (!focusedCell) {
213
+ return rows > 0 && columns.length > 0
214
+ ? "Use arrow keys to move between cells. On touch devices, swipe to scroll and tap to select."
215
+ : "No rows available.";
216
+ }
217
+
218
+ const [col, row] = focusedCell;
219
+ const columnTitle = columns[col]?.title ?? `Column ${col + 1}`;
220
+ const cell = getCellContent(focusedCell);
221
+ const value = `${cell.displayData ?? ""}`.trim();
222
+ return value
223
+ ? `Row ${row + 1}, ${columnTitle}, value ${value}`
224
+ : `Row ${row + 1}, ${columnTitle}, empty cell`;
225
+ });
226
+ const selectionDescription = $derived.by(() => {
227
+ switch (currentSelection.type) {
228
+ case "cell":
229
+ return "One cell selected.";
230
+ case "range":
231
+ return `Range selected from row ${currentSelection.start[1] + 1} to row ${currentSelection.end[1] + 1}.`;
232
+ case "row":
233
+ return `${currentSelection.rows.length} row${currentSelection.rows.length === 1 ? "" : "s"} selected.`;
234
+ case "column":
235
+ return `${currentSelection.columns.length} column${currentSelection.columns.length === 1 ? "" : "s"} selected.`;
236
+ default:
237
+ return "No selection.";
238
+ }
239
+ });
204
240
 
205
241
  // ── Dark mode detection ───────────────────────────────────────────────────────
206
242
 
@@ -467,6 +503,7 @@
467
503
 
468
504
  function handleMouseDown(e: MouseEvent) {
469
505
  if (e.button !== 0) return;
506
+ pendingGridMenuOpen = false;
470
507
  containerRef?.focus();
471
508
 
472
509
  const sbGeo = getScrollbarGeometry(buildRenderContext());
@@ -504,7 +541,7 @@
504
541
  e.offsetX < rowMarkerWidth &&
505
542
  e.offsetY < headerHeight
506
543
  ) {
507
- onGridMenuOpen({ x: e.clientX, y: e.clientY });
544
+ pendingGridMenuOpen = true;
508
545
  return;
509
546
  }
510
547
 
@@ -792,6 +829,17 @@
792
829
  }
793
830
 
794
831
  function handleMouseUp(e: MouseEvent) {
832
+ if (
833
+ pendingGridMenuOpen &&
834
+ onGridMenuOpen &&
835
+ rowMarkerWidth > 0 &&
836
+ e.offsetX < rowMarkerWidth &&
837
+ e.offsetY < headerHeight
838
+ ) {
839
+ onGridMenuOpen({ x: e.clientX, y: e.clientY });
840
+ }
841
+ pendingGridMenuOpen = false;
842
+
795
843
  if (draggingHeaderCol !== null && onColumnMoved) {
796
844
  const fixedBoundaryX =
797
845
  fixedColumns > 0
@@ -883,6 +931,122 @@
883
931
  export function setAnnouncement(msg: string): void {
884
932
  announcement = msg;
885
933
  }
934
+
935
+ function getLocalPointFromClient(clientX: number, clientY: number) {
936
+ const rect = canvasRef?.getBoundingClientRect();
937
+ if (!rect) return null;
938
+
939
+ return {
940
+ offsetX: clientX - rect.left,
941
+ offsetY: clientY - rect.top,
942
+ };
943
+ }
944
+
945
+ function handleTouchTap(offsetX: number, offsetY: number) {
946
+ containerRef?.focus();
947
+
948
+ if (
949
+ onGridMenuOpen &&
950
+ rowMarkerWidth > 0 &&
951
+ offsetX < rowMarkerWidth &&
952
+ offsetY < headerHeight
953
+ ) {
954
+ const rect = canvasRef?.getBoundingClientRect();
955
+ onGridMenuOpen({
956
+ x: (rect?.left ?? 0) + offsetX,
957
+ y: (rect?.top ?? 0) + offsetY,
958
+ });
959
+ return;
960
+ }
961
+
962
+ const fixedBoundaryX =
963
+ fixedColumns > 0
964
+ ? getColumnX(effectiveColumns, Math.min(fixedColumns, columns.length))
965
+ : 0;
966
+ const localX = offsetX - rowMarkerWidth;
967
+ const effectiveScrollX =
968
+ fixedColumns > 0 && localX >= 0 && localX < fixedBoundaryX ? 0 : scrollX;
969
+
970
+ if (offsetY < headerHeight) {
971
+ const col = getColumnFromX(localX + effectiveScrollX, effectiveColumns);
972
+ if (col >= 0 && col < columns.length) {
973
+ toggleSort(col, false);
974
+ if (!readonly) {
975
+ currentSelection = { type: "column", columns: [col] };
976
+ onSelectionChange?.(currentSelection);
977
+ announcement = `Column ${columns[col].title} selected`;
978
+ }
979
+ }
980
+ return;
981
+ }
982
+
983
+ if (rowMarkers !== "none" && offsetX < rowMarkerWidth) {
984
+ const row = Math.floor((offsetY - headerHeight + scrollY) / rowHeight);
985
+ if (row >= 0 && row < rows) {
986
+ onRowToggle?.(row);
987
+ }
988
+ return;
989
+ }
990
+
991
+ const cell = getCellFromPoint(
992
+ localX,
993
+ offsetY,
994
+ effectiveScrollX,
995
+ scrollY,
996
+ effectiveColumns,
997
+ rowHeight,
998
+ headerHeight,
999
+ );
1000
+
1001
+ if (cell) {
1002
+ focusedCell = cell;
1003
+ currentSelection = { type: "cell", item: cell };
1004
+ onSelectionChange?.(currentSelection);
1005
+ announcement = `Row ${cell[1] + 1}, column ${columns[cell[0]]?.title ?? cell[0] + 1}`;
1006
+ }
1007
+ }
1008
+
1009
+ function handleTouchStart(e: TouchEvent) {
1010
+ if (e.touches.length !== 1) return;
1011
+
1012
+ const touch = e.touches[0];
1013
+ touchStartClientX = touch.clientX;
1014
+ touchStartClientY = touch.clientY;
1015
+ touchStartScrollX = scrollRef?.scrollLeft ?? 0;
1016
+ touchStartScrollY = scrollRef?.scrollTop ?? 0;
1017
+ isTouchScrolling = false;
1018
+ }
1019
+
1020
+ function handleTouchMove(e: TouchEvent) {
1021
+ if (e.touches.length !== 1 || !scrollRef) return;
1022
+
1023
+ const touch = e.touches[0];
1024
+ const deltaX = touch.clientX - touchStartClientX;
1025
+ const deltaY = touch.clientY - touchStartClientY;
1026
+
1027
+ if (!isTouchScrolling && (Math.abs(deltaX) > 8 || Math.abs(deltaY) > 8)) {
1028
+ isTouchScrolling = true;
1029
+ }
1030
+
1031
+ if (!isTouchScrolling) return;
1032
+
1033
+ e.preventDefault();
1034
+ scrollRef.scrollLeft = touchStartScrollX - deltaX;
1035
+ scrollRef.scrollTop = touchStartScrollY - deltaY;
1036
+ }
1037
+
1038
+ function handleTouchEnd(e: TouchEvent) {
1039
+ const touch = e.changedTouches[0];
1040
+ if (!touch) return;
1041
+
1042
+ const point = getLocalPointFromClient(touch.clientX, touch.clientY);
1043
+ const wasScrolling = isTouchScrolling;
1044
+ isTouchScrolling = false;
1045
+
1046
+ if (!point || wasScrolling) return;
1047
+
1048
+ handleTouchTap(point.offsetX, point.offsetY);
1049
+ }
886
1050
  </script>
887
1051
 
888
1052
  <div
@@ -893,6 +1057,10 @@
893
1057
  aria-rowcount={rows}
894
1058
  aria-colcount={columns.length}
895
1059
  aria-label="Data grid"
1060
+ aria-describedby={`${gridId}-instructions ${gridId}-selection ${gridId}-active`}
1061
+ aria-activedescendant={focusedCell ? `${gridId}-active` : undefined}
1062
+ aria-multiselectable="true"
1063
+ aria-readonly={readonly}
896
1064
  style:width={containerWidthStyle}
897
1065
  style:height={containerHeightStyle}
898
1066
  style:--gridler-border={mergedTheme.borderColor}
@@ -916,6 +1084,9 @@
916
1084
  onmousemove={handleMouseMove}
917
1085
  onmouseup={handleMouseUp}
918
1086
  onmouseleave={handleMouseLeave}
1087
+ ontouchstart={handleTouchStart}
1088
+ ontouchmove={handleTouchMove}
1089
+ ontouchend={handleTouchEnd}
919
1090
  ondblclick={handleDblClick}
920
1091
  onwheel={handleWheel}
921
1092
  oncontextmenu={handleContextMenu}
@@ -956,6 +1127,15 @@
956
1127
  />
957
1128
  {/if}
958
1129
 
1130
+ <div class="sr-only" id={`${gridId}-instructions`}>
1131
+ Swipe with one finger to scroll the grid on touch devices. Tap a cell to select it. Use arrow keys to move between cells when the grid is focused.
1132
+ </div>
1133
+ <div class="sr-only" id={`${gridId}-selection`}>
1134
+ {selectionDescription}
1135
+ </div>
1136
+ <div class="sr-only" id={`${gridId}-active`} role="gridcell">
1137
+ {activeCellDescription}
1138
+ </div>
959
1139
  <div role="status" aria-live="polite" class="sr-only">
960
1140
  {announcement}
961
1141
  </div>
@@ -986,6 +1166,8 @@
986
1166
  position: relative;
987
1167
  z-index: 1;
988
1168
  scrollbar-width: none;
1169
+ -webkit-overflow-scrolling: touch;
1170
+ overscroll-behavior: contain;
989
1171
  }
990
1172
 
991
1173
  .gridler-scroll::-webkit-scrollbar {
@@ -1003,6 +1185,7 @@
1003
1185
  left: 0;
1004
1186
  z-index: 2;
1005
1187
  cursor: default;
1188
+ touch-action: none;
1006
1189
  }
1007
1190
 
1008
1191
  .sr-only {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warkypublic/svelix",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Svelte 5 component library with Skeleton UI and Tailwind CSS",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
@@ -26,43 +26,43 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@changesets/cli": "^2.30.0",
29
- "@chromatic-com/storybook": "^5.0.1",
29
+ "@chromatic-com/storybook": "^5.0.2",
30
30
  "@eslint/compat": "^2.0.3",
31
31
  "@eslint/js": "^10.0.1",
32
32
  "@playwright/test": "^1.58.2",
33
- "@sentry/svelte": "^10.43.0",
34
- "@skeletonlabs/skeleton": "^4.12.1",
35
- "@skeletonlabs/skeleton-svelte": "^4.12.1",
36
- "@storybook/addon-a11y": "^10.2.17",
37
- "@storybook/addon-docs": "^10.2.17",
38
- "@storybook/addon-svelte-csf": "^5.0.11",
39
- "@storybook/addon-vitest": "^10.2.17",
40
- "@storybook/sveltekit": "^10.2.17",
33
+ "@sentry/svelte": "^10.45.0",
34
+ "@skeletonlabs/skeleton": "^4.13.0",
35
+ "@skeletonlabs/skeleton-svelte": "^4.13.0",
36
+ "@storybook/addon-a11y": "^10.3.1",
37
+ "@storybook/addon-docs": "^10.3.1",
38
+ "@storybook/addon-svelte-csf": "^5.0.12",
39
+ "@storybook/addon-vitest": "^10.3.1",
40
+ "@storybook/sveltekit": "^10.3.1",
41
41
  "@storybook/test": "^8.6.15",
42
42
  "@sveltejs/adapter-auto": "^7.0.1",
43
- "@sveltejs/kit": "^2.53.4",
43
+ "@sveltejs/kit": "^2.55.0",
44
44
  "@sveltejs/package": "^2.5.7",
45
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
46
- "@tailwindcss/vite": "^4.2.1",
47
- "@tanstack/svelte-virtual": "^3.13.21",
48
- "@types/node": "^25.4.0",
49
- "@vitest/browser-playwright": "^4.0.18",
50
- "@vitest/coverage-v8": "^4.0.18",
45
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
46
+ "@tailwindcss/vite": "^4.2.2",
47
+ "@tanstack/svelte-virtual": "^3.13.23",
48
+ "@types/node": "^25.5.0",
49
+ "@vitest/browser-playwright": "^4.1.0",
50
+ "@vitest/coverage-v8": "^4.1.0",
51
51
  "eslint": "^10.0.3",
52
52
  "eslint-plugin-svelte": "^3.15.2",
53
53
  "globals": "^17.4.0",
54
54
  "playwright": "^1.58.2",
55
55
  "publint": "^0.3.18",
56
- "storybook": "^10.2.17",
57
- "svelte": "^5.53.10",
56
+ "storybook": "^10.3.1",
57
+ "svelte": "^5.54.0",
58
58
  "svelte-check": "^4.4.5",
59
- "tailwindcss": "^4.2.1",
59
+ "tailwindcss": "^4.2.2",
60
60
  "tslib": "^2.8.1",
61
61
  "typescript": "^5.9.3",
62
- "typescript-eslint": "^8.57.0",
63
- "vite": "^7.3.1",
62
+ "typescript-eslint": "^8.57.1",
63
+ "vite": "^8.0.1",
64
64
  "vite-plugin-monaco-editor": "^1.1.0",
65
- "vitest": "^4.0.18"
65
+ "vitest": "^4.1.0"
66
66
  },
67
67
  "svelte": "./dist/index.js",
68
68
  "types": "./dist/index.d.ts",
@@ -76,13 +76,13 @@
76
76
  "@cartamd/plugin-math": "^4.3.1",
77
77
  "@friendofsvelte/tipex": "^0.1.1",
78
78
  "@js-temporal/polyfill": "^0.5.1",
79
- "@svar-ui/svelte-grid": "^2.5.1",
79
+ "@svar-ui/svelte-grid": "^2.6.0",
80
80
  "@warkypublic/resolvespec-js": "^1.0.1",
81
81
  "@warkypublic/artemis-kit": "^1.0.10",
82
82
  "carta-md": "^4.11.1",
83
83
  "github-markdown-css": "^5.9.0",
84
- "isomorphic-dompurify": "^3.1.0",
85
- "katex": "^0.16.38",
84
+ "isomorphic-dompurify": "^3.5.1",
85
+ "katex": "^0.16.39",
86
86
  "monaco-editor": "^0.55.1"
87
87
  },
88
88
  "scripts": {