svelte-multiselect 11.2.1 → 11.2.2

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.
@@ -14,6 +14,6 @@ interface Props extends Omit<MultiSelectProps<Action>, `options`> {
14
14
  input?: HTMLInputElement | null;
15
15
  placeholder?: string;
16
16
  }
17
- declare const CmdPalette: import("svelte").Component<Props, {}, "open" | "dialog" | "input">;
17
+ declare const CmdPalette: import("svelte").Component<Props, {}, "dialog" | "input" | "open">;
18
18
  type CmdPalette = ReturnType<typeof CmdPalette>;
19
19
  export default CmdPalette;
@@ -6,21 +6,26 @@ let { content = ``, state = $bindable(`ready`), global_selector = null, global =
6
6
  error: { icon: `Alert`, text: `` },
7
7
  }, children, ...rest } = $props();
8
8
  $effect(() => {
9
- if (global || global_selector) {
10
- for (const node of document.querySelectorAll(global_selector ?? `pre > code`)) {
11
- // skip if <pre> already contains a button (presumably for copy)
12
- const pre = node.parentElement;
13
- if (!pre || (skip_selector && pre.querySelector(skip_selector)))
14
- continue;
15
- mount(CopyButton, {
16
- target: pre,
17
- props: {
18
- content: node.textContent ?? ``,
19
- style: `position: absolute; top: 9pt; right: 9pt;`,
20
- },
21
- });
9
+ if (!global && !global_selector)
10
+ return;
11
+ const apply_copy_buttons = () => {
12
+ const button_style = typeof global === `string`
13
+ ? global
14
+ : `position: absolute; top: 9pt; right: 9pt;`;
15
+ for (const code of document.querySelectorAll(global_selector ?? `pre > code`)) {
16
+ const pre = code.parentElement;
17
+ if (pre && !(skip_selector && pre.querySelector(skip_selector))) {
18
+ mount(CopyButton, {
19
+ target: pre,
20
+ props: { content: code.textContent ?? ``, style: button_style },
21
+ });
22
+ }
22
23
  }
23
- }
24
+ };
25
+ apply_copy_buttons();
26
+ const observer = new MutationObserver(apply_copy_buttons);
27
+ observer.observe(document.body, { childList: true, subtree: true });
28
+ return () => observer.disconnect();
24
29
  });
25
30
  async function copy() {
26
31
  try {
@@ -6,7 +6,7 @@ interface Props {
6
6
  content?: string;
7
7
  state?: State;
8
8
  global_selector?: string | null;
9
- global?: boolean;
9
+ global?: boolean | string;
10
10
  skip_selector?: string | null;
11
11
  as?: string;
12
12
  labels?: Record<State, {
@@ -18,7 +18,7 @@ export {};
18
18
 
19
19
  <svelte:element this={as} {style}>
20
20
  {#each files as file, idx (file.title)}
21
- {@const { title, content, _language = default_lang } = file ?? {}}
21
+ {@const { title, content, language = default_lang } = file ?? {}}
22
22
  <li>
23
23
  <!-- https://github.com/sveltejs/svelte/issues/12721#issuecomment-2269544690 -->
24
24
  <details bind:this={file.node}>
@@ -27,12 +27,12 @@ export {};
27
27
  {#if title_snippet}
28
28
  {@render title_snippet({ idx, ...file })}
29
29
  {:else}
30
- <code>{title.split(`/`).at(-1)}</code>
30
+ {@html title}
31
31
  {/if}
32
32
  </summary>
33
33
  {/if}
34
34
 
35
- <pre><code>{@html content}</code></pre>
35
+ <pre class="language-{language}"><code>{content}</code></pre>
36
36
  <!-- <pre><code>{@html hljs.highlight(content, { language }).value}</code></pre> -->
37
37
  </details>
38
38
  </li>
@@ -3,7 +3,7 @@ declare class __sveltets_Render<Option extends T> {
3
3
  props(): MultiSelectProps<T>;
4
4
  events(): {};
5
5
  slots(): {};
6
- bindings(): "open" | "input" | "value" | "selected" | "invalid" | "activeIndex" | "activeOption" | "form_input" | "matchingOptions" | "options" | "outerDiv" | "searchText";
6
+ bindings(): "input" | "invalid" | "open" | "value" | "selected" | "activeIndex" | "activeOption" | "form_input" | "matchingOptions" | "options" | "outerDiv" | "searchText";
7
7
  exports(): {};
8
8
  }
9
9
  interface $$IsomorphicComponent {
@@ -0,0 +1,46 @@
1
+ import { type Attachment } from 'svelte/attachments';
2
+ declare global {
3
+ interface CSS {
4
+ highlights: HighlightRegistry;
5
+ }
6
+ interface HighlightRegistry extends Map<string, Highlight> {
7
+ clear(): void;
8
+ delete(key: string): boolean;
9
+ set(key: string, value: Highlight): this;
10
+ }
11
+ }
12
+ export interface DraggableOptions {
13
+ handle_selector?: string;
14
+ on_drag_start?: (event: MouseEvent) => void;
15
+ on_drag?: (event: MouseEvent) => void;
16
+ on_drag_end?: (event: MouseEvent) => void;
17
+ }
18
+ export declare const draggable: (options?: DraggableOptions) => Attachment;
19
+ export declare function get_html_sort_value(element: HTMLElement): string;
20
+ export declare const sortable: ({ header_selector, asc_class, desc_class, sorted_style, }?: {
21
+ header_selector?: string | undefined;
22
+ asc_class?: string | undefined;
23
+ desc_class?: string | undefined;
24
+ sorted_style?: {
25
+ backgroundColor: string;
26
+ } | undefined;
27
+ }) => (node: HTMLElement) => () => void;
28
+ type HighlightOptions = {
29
+ query?: string;
30
+ disabled?: boolean;
31
+ node_filter?: (node: Node) => number;
32
+ css_class?: string;
33
+ };
34
+ export declare const highlight_matches: (ops: HighlightOptions) => (node: HTMLElement) => (() => void) | undefined;
35
+ export declare const tooltip: (options?: {
36
+ content?: string;
37
+ placement?: `top` | `bottom` | `left` | `right`;
38
+ delay?: number;
39
+ }) => Attachment;
40
+ type ClickOutsideConfig<T extends HTMLElement> = {
41
+ enabled?: boolean;
42
+ exclude?: string[];
43
+ callback?: (node: T, config: ClickOutsideConfig<T>) => void;
44
+ };
45
+ export declare const click_outside: <T extends HTMLElement>(config?: ClickOutsideConfig<T>) => (node: T) => () => void;
46
+ export {};
@@ -0,0 +1,345 @@
1
+ import {} from 'svelte/attachments';
2
+ // Svelte 5 attachment factory to make an element draggable
3
+ // @param options - Configuration options for dragging behavior
4
+ // @returns Attachment function that sets up dragging on an element
5
+ export const draggable = (options = {}) => (element) => {
6
+ const node = element;
7
+ // Use simple variables for maximum performance
8
+ let dragging = false;
9
+ let start = { x: 0, y: 0 };
10
+ const initial = { left: 0, top: 0, width: 0 };
11
+ const handle = options.handle_selector
12
+ ? node.querySelector(options.handle_selector)
13
+ : node;
14
+ if (!handle) {
15
+ console.warn(`Draggable: handle not found with selector "${options.handle_selector}"`);
16
+ return;
17
+ }
18
+ function handle_mousedown(event) {
19
+ // Only drag if mousedown is on the handle or its children
20
+ if (!handle?.contains?.(event.target))
21
+ return;
22
+ dragging = true;
23
+ // For position: fixed elements, use getBoundingClientRect for viewport-relative position
24
+ const computed_style = getComputedStyle(node);
25
+ if (computed_style.position === `fixed`) {
26
+ const rect = node.getBoundingClientRect();
27
+ initial.left = rect.left;
28
+ initial.top = rect.top;
29
+ initial.width = rect.width;
30
+ }
31
+ else {
32
+ // For other positioning, use offset values
33
+ initial.left = node.offsetLeft;
34
+ initial.top = node.offsetTop;
35
+ initial.width = node.offsetWidth;
36
+ }
37
+ node.style.left = `${initial.left}px`;
38
+ node.style.top = `${initial.top}px`;
39
+ node.style.width = `${initial.width}px`;
40
+ node.style.right = `auto`; // Prevent conflict with left
41
+ start = { x: event.clientX, y: event.clientY };
42
+ document.body.style.userSelect = `none`; // Prevent text selection during drag
43
+ if (handle)
44
+ handle.style.cursor = `grabbing`;
45
+ globalThis.addEventListener(`mousemove`, handle_mousemove);
46
+ globalThis.addEventListener(`mouseup`, handle_mouseup);
47
+ options.on_drag_start?.(event); // Call optional callback
48
+ }
49
+ function handle_mousemove(event) {
50
+ if (!dragging)
51
+ return;
52
+ // Use the exact same calculation as the fast old implementation
53
+ const dx = event.clientX - start.x;
54
+ const dy = event.clientY - start.y;
55
+ node.style.left = `${initial.left + dx}px`;
56
+ node.style.top = `${initial.top + dy}px`;
57
+ // Only call callback if it exists (minimize overhead)
58
+ if (options.on_drag)
59
+ options.on_drag(event);
60
+ }
61
+ function handle_mouseup(event) {
62
+ if (!dragging)
63
+ return;
64
+ dragging = false;
65
+ event.stopPropagation();
66
+ document.body.style.userSelect = ``;
67
+ if (handle)
68
+ handle.style.cursor = `grab`;
69
+ globalThis.removeEventListener(`mousemove`, handle_mousemove);
70
+ globalThis.removeEventListener(`mouseup`, handle_mouseup);
71
+ options.on_drag_end?.(event); // Call optional callback
72
+ }
73
+ if (handle) {
74
+ handle.addEventListener(`mousedown`, handle_mousedown);
75
+ handle.style.cursor = `grab`;
76
+ }
77
+ // Return cleanup function (this is the attachment pattern)
78
+ return () => {
79
+ globalThis.removeEventListener(`mousemove`, handle_mousemove);
80
+ globalThis.removeEventListener(`mouseup`, handle_mouseup);
81
+ if (handle) {
82
+ handle.removeEventListener(`mousedown`, handle_mousedown);
83
+ handle.style.cursor = ``; // Reset cursor
84
+ }
85
+ };
86
+ };
87
+ export function get_html_sort_value(element) {
88
+ if (element.dataset.sortValue !== undefined) {
89
+ return element.dataset.sortValue;
90
+ }
91
+ for (const child of Array.from(element.children)) {
92
+ const child_val = get_html_sort_value(child);
93
+ if (child_val !== ``)
94
+ return child_val;
95
+ }
96
+ return element.textContent ?? ``;
97
+ }
98
+ export const sortable = ({ header_selector = `thead th`, asc_class = `table-sort-asc`, desc_class = `table-sort-desc`, sorted_style = { backgroundColor: `rgba(255, 255, 255, 0.1)` }, } = {}) => (node) => {
99
+ // this action can be applied to bob-standard HTML tables to make them sortable by
100
+ // clicking on column headers (and clicking again to toggle sorting direction)
101
+ const headers = Array.from(node.querySelectorAll(header_selector));
102
+ let sort_col_idx;
103
+ let sort_dir = 1; // 1 = asc, -1 = desc
104
+ // Store event listeners for cleanup
105
+ const event_listeners = [];
106
+ for (const [idx, header] of headers.entries()) {
107
+ header.style.cursor = `pointer`; // add cursor pointer to headers
108
+ const init_styles = header.getAttribute(`style`) ?? ``;
109
+ const click_handler = () => {
110
+ // reset all headers to initial state
111
+ for (const header of headers) {
112
+ header.textContent = header.textContent?.replace(/ ↑| ↓/, ``) ?? ``;
113
+ header.classList.remove(asc_class, desc_class);
114
+ header.setAttribute(`style`, init_styles);
115
+ }
116
+ if (idx === sort_col_idx) {
117
+ sort_dir *= -1; // reverse sort direction
118
+ }
119
+ else {
120
+ sort_col_idx = idx; // set new sort column index
121
+ sort_dir = 1; // reset sort direction
122
+ }
123
+ header.classList.add(sort_dir > 0 ? asc_class : desc_class);
124
+ Object.assign(header.style, sorted_style);
125
+ header.textContent = `${header.textContent?.replace(/ ↑| ↓/, ``)} ${sort_dir > 0 ? `↑` : `↓`}`;
126
+ const table_body = node.querySelector(`tbody`);
127
+ if (!table_body)
128
+ return;
129
+ // re-sort table
130
+ const rows = Array.from(table_body.querySelectorAll(`tr`));
131
+ rows.sort((row_1, row_2) => {
132
+ const cell_1 = row_1.cells[sort_col_idx];
133
+ const cell_2 = row_2.cells[sort_col_idx];
134
+ const val_1 = get_html_sort_value(cell_1);
135
+ const val_2 = get_html_sort_value(cell_2);
136
+ if (val_1 === val_2)
137
+ return 0;
138
+ if (val_1 === ``)
139
+ return 1; // treat empty string as lower than any value
140
+ if (val_2 === ``)
141
+ return -1; // any value is considered higher than empty string
142
+ const num_1 = Number(val_1);
143
+ const num_2 = Number(val_2);
144
+ if (isNaN(num_1) && isNaN(num_2)) {
145
+ return sort_dir * val_1.localeCompare(val_2, undefined, { numeric: true });
146
+ }
147
+ return sort_dir * (num_1 - num_2);
148
+ });
149
+ for (const row of rows)
150
+ table_body.appendChild(row);
151
+ };
152
+ header.addEventListener(`click`, click_handler);
153
+ event_listeners.push({ header, handler: click_handler });
154
+ }
155
+ // Return cleanup function
156
+ return () => {
157
+ for (const { header, handler } of event_listeners) {
158
+ header.removeEventListener(`click`, handler);
159
+ header.style.cursor = ``; // Reset cursor
160
+ }
161
+ };
162
+ };
163
+ export const highlight_matches = (ops) => (node) => {
164
+ const { query = ``, disabled = false, node_filter = () => NodeFilter.FILTER_ACCEPT, css_class = `highlight-match`, } = ops;
165
+ // clear previous ranges from HighlightRegistry
166
+ CSS.highlights.clear();
167
+ if (!query || disabled || typeof CSS === `undefined` || !CSS.highlights)
168
+ return; // abort if CSS highlight API not supported
169
+ const tree_walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, {
170
+ acceptNode: node_filter,
171
+ });
172
+ const text_nodes = [];
173
+ let current_node = tree_walker.nextNode();
174
+ while (current_node) {
175
+ text_nodes.push(current_node);
176
+ current_node = tree_walker.nextNode();
177
+ }
178
+ // iterate over all text nodes and find matches
179
+ const ranges = text_nodes.map((el) => {
180
+ const text = el.textContent?.toLowerCase();
181
+ const indices = [];
182
+ let start_pos = 0;
183
+ while (text && start_pos < text.length) {
184
+ const index = text.indexOf(query, start_pos);
185
+ if (index === -1)
186
+ break;
187
+ indices.push(index);
188
+ start_pos = index + query.length;
189
+ }
190
+ // create range object for each str found in the text node
191
+ return indices.map((index) => {
192
+ const range = new Range();
193
+ range.setStart(el, index);
194
+ range.setEnd(el, index + query?.length);
195
+ return range;
196
+ });
197
+ });
198
+ // create Highlight object from ranges and add to registry
199
+ CSS.highlights.set(css_class, new Highlight(...ranges.flat()));
200
+ return () => {
201
+ CSS.highlights.delete(css_class);
202
+ };
203
+ };
204
+ // Global tooltip state to ensure only one tooltip is shown at a time
205
+ let current_tooltip = null;
206
+ let show_timeout;
207
+ let hide_timeout;
208
+ function clear_tooltip() {
209
+ if (show_timeout)
210
+ clearTimeout(show_timeout);
211
+ if (hide_timeout)
212
+ clearTimeout(hide_timeout);
213
+ if (current_tooltip) {
214
+ current_tooltip.remove();
215
+ current_tooltip = null;
216
+ }
217
+ }
218
+ export const tooltip = (options = {}) => (node) => {
219
+ // Handle null/undefined elements
220
+ if (!node || !(node instanceof HTMLElement))
221
+ return;
222
+ // Handle null/undefined options
223
+ const safe_options = options || {};
224
+ const cleanup_functions = [];
225
+ function setup_tooltip(element) {
226
+ if (!element)
227
+ return;
228
+ const content = safe_options.content || element.title ||
229
+ element.getAttribute(`aria-label`) || element.getAttribute(`data-title`);
230
+ if (!content)
231
+ return;
232
+ // Store original title and remove it to prevent default tooltip
233
+ // Only store title if we're not using custom content
234
+ if (element.title && !safe_options.content) {
235
+ element.setAttribute(`data-original-title`, element.title);
236
+ element.removeAttribute(`title`);
237
+ }
238
+ function show_tooltip() {
239
+ clear_tooltip();
240
+ show_timeout = setTimeout(() => {
241
+ const tooltip = document.createElement(`div`);
242
+ tooltip.className = `custom-tooltip`;
243
+ tooltip.style.cssText = `
244
+ position: absolute; z-index: 9999; opacity: 0;
245
+ background: var(--tooltip-bg); color: var(--text-color); border: var(--tooltip-border);
246
+ padding: 6px 10px; border-radius: 6px; font-size: 13px; line-height: 1.4;
247
+ max-width: 280px; word-wrap: break-word; pointer-events: none;
248
+ filter: drop-shadow(0 2px 8px rgba(0,0,0,0.25)); transition: opacity 0.15s ease-out;
249
+ `;
250
+ tooltip.innerHTML = content?.replace(/\r/g, `<br/>`) ?? ``;
251
+ document.body.appendChild(tooltip);
252
+ // Position tooltip
253
+ const rect = element.getBoundingClientRect();
254
+ const tooltip_rect = tooltip.getBoundingClientRect();
255
+ const placement = safe_options.placement || `bottom`;
256
+ const margin = 12;
257
+ let top = 0, left = 0;
258
+ if (placement === `top`) {
259
+ top = rect.top - tooltip_rect.height - margin;
260
+ left = rect.left + rect.width / 2 - tooltip_rect.width / 2;
261
+ }
262
+ else if (placement === `left`) {
263
+ top = rect.top + rect.height / 2 - tooltip_rect.height / 2;
264
+ left = rect.left - tooltip_rect.width - margin;
265
+ }
266
+ else if (placement === `right`) {
267
+ top = rect.top + rect.height / 2 - tooltip_rect.height / 2;
268
+ left = rect.right + margin;
269
+ }
270
+ else { // bottom
271
+ top = rect.bottom + margin;
272
+ left = rect.left + rect.width / 2 - tooltip_rect.width / 2;
273
+ }
274
+ // Keep in viewport
275
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - tooltip_rect.width - 8));
276
+ top = Math.max(8, Math.min(top, globalThis.innerHeight - tooltip_rect.height - 8));
277
+ tooltip.style.left = `${left + globalThis.scrollX}px`;
278
+ tooltip.style.top = `${top + globalThis.scrollY}px`;
279
+ tooltip.style.opacity = `1`;
280
+ current_tooltip = tooltip;
281
+ }, safe_options.delay || 100);
282
+ }
283
+ function hide_tooltip() {
284
+ clear_tooltip();
285
+ hide_timeout = setTimeout(() => {
286
+ if (current_tooltip) {
287
+ current_tooltip.style.opacity = `0`;
288
+ setTimeout(() => {
289
+ if (current_tooltip) {
290
+ current_tooltip.remove();
291
+ current_tooltip = null;
292
+ }
293
+ }, 150);
294
+ }
295
+ }, 50);
296
+ }
297
+ const events = [`mouseenter`, `mouseleave`, `focus`, `blur`];
298
+ const handlers = [show_tooltip, hide_tooltip, show_tooltip, hide_tooltip];
299
+ events.forEach((event, idx) => element.addEventListener(event, handlers[idx]));
300
+ return () => {
301
+ events.forEach((event, idx) => element.removeEventListener(event, handlers[idx]));
302
+ const original_title = element.getAttribute(`data-original-title`);
303
+ if (original_title) {
304
+ element.setAttribute(`title`, original_title);
305
+ element.removeAttribute(`data-original-title`);
306
+ }
307
+ };
308
+ }
309
+ // Setup tooltip for main node and children
310
+ const main_cleanup = setup_tooltip(node);
311
+ if (main_cleanup)
312
+ cleanup_functions.push(main_cleanup);
313
+ node.querySelectorAll(`[title], [aria-label], [data-title]`).forEach((element) => {
314
+ const child_cleanup = setup_tooltip(element);
315
+ if (child_cleanup)
316
+ cleanup_functions.push(child_cleanup);
317
+ });
318
+ if (cleanup_functions.length === 0)
319
+ return;
320
+ return () => {
321
+ cleanup_functions.forEach((cleanup) => cleanup());
322
+ clear_tooltip();
323
+ };
324
+ };
325
+ export const click_outside = (config = {}) => (node) => {
326
+ const { callback, enabled = true, exclude = [] } = config;
327
+ function handle_click(event) {
328
+ if (!enabled)
329
+ return;
330
+ const target = event.target;
331
+ const path = event.composedPath();
332
+ // Check if click target is the node or inside it
333
+ if (path.includes(node))
334
+ return;
335
+ // Check excluded selectors
336
+ if (exclude.some((selector) => target.closest(selector)))
337
+ return;
338
+ // Execute callback if provided, passing node and full config
339
+ callback?.(node, { callback, enabled, exclude });
340
+ // Dispatch custom event if click was outside
341
+ node.dispatchEvent(new CustomEvent(`outside-click`));
342
+ }
343
+ document.addEventListener(`click`, handle_click, true);
344
+ return () => document.removeEventListener(`click`, handle_click, true);
345
+ };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './attachments';
1
2
  export { default as CircleSpinner } from './CircleSpinner.svelte';
2
3
  export { default as CmdPalette } from './CmdPalette.svelte';
3
4
  export { default as CodeExample } from './CodeExample.svelte';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './attachments';
1
2
  export { default as CircleSpinner } from './CircleSpinner.svelte';
2
3
  export { default as CmdPalette } from './CmdPalette.svelte';
3
4
  export { default as CodeExample } from './CodeExample.svelte';
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "homepage": "https://janosh.github.io/svelte-multiselect",
6
6
  "repository": "https://github.com/janosh/svelte-multiselect",
7
7
  "license": "MIT",
8
- "version": "11.2.1",
8
+ "version": "11.2.2",
9
9
  "type": "module",
10
10
  "svelte": "./dist/index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
@@ -13,7 +13,7 @@
13
13
  "svelte": "^5.35.6"
14
14
  },
15
15
  "devDependencies": {
16
- "@playwright/test": "^1.54.0",
16
+ "@playwright/test": "^1.54.1",
17
17
  "@stylistic/eslint-plugin": "^5.1.0",
18
18
  "@sveltejs/adapter-static": "^3.0.8",
19
19
  "@sveltejs/kit": "^2.22.5",
@@ -21,7 +21,7 @@
21
21
  "@sveltejs/vite-plugin-svelte": "^6.0.0",
22
22
  "@types/node": "^24.0.13",
23
23
  "@vitest/coverage-v8": "^3.2.4",
24
- "eslint": "^9.30.1",
24
+ "eslint": "^9.31.0",
25
25
  "eslint-plugin-svelte": "^3.10.1",
26
26
  "hastscript": "^9.0.1",
27
27
  "jsdom": "^26.1.0",
@@ -32,7 +32,7 @@
32
32
  "svelte": "^5.35.6",
33
33
  "svelte-check": "^4.2.2",
34
34
  "svelte-preprocess": "^6.0.3",
35
- "svelte-toc": "^0.6.1",
35
+ "svelte-toc": "^0.6.2",
36
36
  "svelte2tsx": "^0.7.40",
37
37
  "typescript": "5.8.3",
38
38
  "typescript-eslint": "^8.36.0",
@@ -59,6 +59,10 @@
59
59
  "types": "./dist/index.d.ts",
60
60
  "svelte": "./dist/index.js",
61
61
  "default": "./dist/index.js"
62
+ },
63
+ "./attachments": {
64
+ "types": "./dist/attachments.d.ts",
65
+ "default": "./dist/attachments.js"
62
66
  }
63
67
  },
64
68
  "types": "./dist/index.d.ts",