neogestify-ui-components 2.0.1 → 2.2.1

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.
Files changed (31) hide show
  1. package/README.md +153 -19
  2. package/dist/components/ElementLibraryBuilder/index.d.mts +5 -0
  3. package/dist/components/ElementLibraryBuilder/index.d.ts +5 -0
  4. package/dist/components/ElementLibraryBuilder/index.js +689 -0
  5. package/dist/components/ElementLibraryBuilder/index.js.map +1 -0
  6. package/dist/components/ElementLibraryBuilder/index.mjs +687 -0
  7. package/dist/components/ElementLibraryBuilder/index.mjs.map +1 -0
  8. package/dist/components/VenueMapEditor/index.d.mts +66 -5
  9. package/dist/components/VenueMapEditor/index.d.ts +66 -5
  10. package/dist/components/VenueMapEditor/index.js +199 -34
  11. package/dist/components/VenueMapEditor/index.js.map +1 -1
  12. package/dist/components/VenueMapEditor/index.mjs +199 -36
  13. package/dist/components/VenueMapEditor/index.mjs.map +1 -1
  14. package/dist/index.d.mts +2 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +592 -34
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +591 -36
  19. package/dist/index.mjs.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/ElementLibraryBuilder/builder.tsx +400 -0
  22. package/src/components/ElementLibraryBuilder/index.ts +1 -0
  23. package/src/components/VenueMapEditor/VenueMapEditor.tsx +79 -20
  24. package/src/components/VenueMapEditor/components/ElementNode.tsx +23 -0
  25. package/src/components/VenueMapEditor/components/PropertiesPanel.tsx +17 -4
  26. package/src/components/VenueMapEditor/components/Toolbar.tsx +73 -39
  27. package/src/components/VenueMapEditor/hooks/useLibraryStorage.ts +46 -0
  28. package/src/components/VenueMapEditor/index.ts +3 -0
  29. package/src/components/VenueMapEditor/types.ts +45 -3
  30. package/src/components/VenueMapEditor/utils/svgParser.ts +33 -0
  31. package/src/index.ts +1 -0
@@ -1,7 +1,34 @@
1
- import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
1
+ import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/components/VenueMapEditor/VenueMapEditor.tsx
5
+ function useLibraryStorage(storageKey) {
6
+ const [libs, setLibs] = useState(() => {
7
+ if (!storageKey) return {};
8
+ try {
9
+ const raw = localStorage.getItem(storageKey);
10
+ return raw ? JSON.parse(raw) : {};
11
+ } catch {
12
+ return {};
13
+ }
14
+ });
15
+ const setAndPersist = useCallback(
16
+ (newLibs) => {
17
+ setLibs(newLibs);
18
+ if (!storageKey) return;
19
+ try {
20
+ if (Object.keys(newLibs).length === 0) {
21
+ localStorage.removeItem(storageKey);
22
+ } else {
23
+ localStorage.setItem(storageKey, JSON.stringify(newLibs));
24
+ }
25
+ } catch {
26
+ }
27
+ },
28
+ [storageKey]
29
+ );
30
+ return [libs, setAndPersist];
31
+ }
5
32
  function IconCursor({ className }) {
6
33
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", className, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M2 1l12 5.5-5.5 1.5L7 13.5 2 1z" }) });
7
34
  }
@@ -64,6 +91,26 @@ function IconUpload({ className }) {
64
91
  function IconLayers({ className }) {
65
92
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", className, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M8.235 1.559a.5.5 0 0 0-.47 0l-7.5 4a.5.5 0 0 0 0 .882L3.188 8 .265 9.559a.5.5 0 0 0 0 .882l7.5 4a.5.5 0 0 0 .47 0l7.5-4a.5.5 0 0 0 0-.882L12.813 8l2.922-1.559a.5.5 0 0 0 0-.882l-7.5-4zm3.515 7.008L14.438 10 8 13.433 1.562 10 4.25 8.567l3.515 1.874a.5.5 0 0 0 .47 0l3.515-1.874zM8 9.433 1.562 6 8 2.567 14.438 6 8 9.433z" }) });
66
93
  }
94
+
95
+ // src/components/VenueMapEditor/utils/svgParser.ts
96
+ var DANGEROUS_TAGS = /\b(script|iframe|object|embed|link|style|meta)\b/gi;
97
+ var DANGEROUS_ATTRS = /\bon\w+\s*=/gi;
98
+ var DANGEROUS_HREF = /\bhref\s*=\s*["']?\s*javascript:/gi;
99
+ var DANGEROUS_XLINK = /\bxlink:href\s*=\s*["']?\s*javascript:/gi;
100
+ function sanitize(html) {
101
+ return html.replace(DANGEROUS_TAGS, "").replace(DANGEROUS_ATTRS, "").replace(DANGEROUS_HREF, "").replace(DANGEROUS_XLINK, "");
102
+ }
103
+ var VIEWBOX_RE = /viewBox\s*=\s*"([^"]+)"/i;
104
+ var SVG_OPEN_END_RE = /<svg[^>]*>/i;
105
+ function parseSvgMarkup(markup) {
106
+ const viewBoxMatch = markup.match(VIEWBOX_RE);
107
+ const viewBox = viewBoxMatch?.[1] ?? "0 0 100 100";
108
+ const svgOpenMatch = markup.match(SVG_OPEN_END_RE);
109
+ const afterOpen = svgOpenMatch ? markup.slice(svgOpenMatch.index + svgOpenMatch[0].length) : markup;
110
+ const closeIdx = afterOpen.lastIndexOf("</svg>");
111
+ const inner = closeIdx >= 0 ? afterOpen.slice(0, closeIdx) : afterOpen;
112
+ return { viewBox, innerHtml: sanitize(inner) };
113
+ }
67
114
  function ToolButton({ active, disabled, title, onClick, children }) {
68
115
  return /* @__PURE__ */ jsx(
69
116
  "button",
@@ -93,7 +140,15 @@ function TypeChip({ typeDef, active, onClick }) {
93
140
  active ? "border-blue-400 bg-blue-50 text-blue-700 font-medium" : "border-slate-200 bg-white text-slate-600 hover:border-slate-300 hover:bg-slate-50"
94
141
  ].join(" "),
95
142
  children: [
96
- /* @__PURE__ */ jsx(
143
+ typeDef.shape === "svg" && typeDef.svgMarkup ? /* @__PURE__ */ jsx(
144
+ "svg",
145
+ {
146
+ viewBox: parseSvgMarkup(typeDef.svgMarkup).viewBox,
147
+ className: "w-2.5 h-2.5 shrink-0",
148
+ style: { color: typeDef.strokeColor },
149
+ dangerouslySetInnerHTML: { __html: parseSvgMarkup(typeDef.svgMarkup).innerHtml }
150
+ }
151
+ ) : /* @__PURE__ */ jsx(
97
152
  "span",
98
153
  {
99
154
  className: "w-2.5 h-2.5 rounded-sm shrink-0",
@@ -128,6 +183,19 @@ function Toolbar({
128
183
  onLoadLibrary,
129
184
  onRemoveLibraryGroup
130
185
  }) {
186
+ const [activeGroupId, setActiveGroupId] = useState(
187
+ () => paletteGroups[0]?.id ?? null
188
+ );
189
+ useEffect(() => {
190
+ if (paletteGroups.length === 0) {
191
+ setActiveGroupId(null);
192
+ return;
193
+ }
194
+ if (!paletteGroups.find((g) => g.id === activeGroupId)) {
195
+ setActiveGroupId(paletteGroups[0].id);
196
+ }
197
+ }, [paletteGroups, activeGroupId]);
198
+ const activeGroup = paletteGroups.find((g) => g.id === activeGroupId) ?? null;
131
199
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col bg-white border-b border-slate-200 shadow-sm shrink-0", children: [
132
200
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 px-2 py-1.5", children: [
133
201
  /* @__PURE__ */ jsx(ToolButton, { title: "Seleccionar (V)", active: tool === "SELECT", onClick: () => onToolChange("SELECT"), children: /* @__PURE__ */ jsx(IconCursor, { className: "w-4 h-4" }) }),
@@ -165,21 +233,34 @@ function Toolbar({
165
233
  /* @__PURE__ */ jsx(ToolButton, { title: areaShape === "polygon" ? "Cambiar a rect\xE1ngulo" : "Cambiar a pol\xEDgono", onClick: () => onToggleAreaShape?.(), children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: areaShape === "polygon" ? "Poly" : "Rect" }) })
166
234
  ] })
167
235
  ] }),
168
- tool === "PLACE" && /* @__PURE__ */ jsx("div", { className: "flex items-stretch gap-0 border-t border-slate-100 bg-slate-50 overflow-x-auto", children: paletteGroups.map((group, gi) => /* @__PURE__ */ jsxs("div", { className: "flex items-center shrink-0", children: [
169
- gi > 0 && /* @__PURE__ */ jsx("div", { className: "w-px self-stretch bg-slate-200 mx-1" }),
170
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 px-1.5 shrink-0", children: [
171
- /* @__PURE__ */ jsx("span", { className: "text-[10px] text-slate-400 font-medium whitespace-nowrap select-none", children: group.name }),
172
- !group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsx(
173
- "button",
174
- {
175
- title: `Eliminar grupo "${group.name}"`,
176
- onClick: () => onRemoveLibraryGroup(group.id),
177
- className: "text-slate-300 hover:text-red-400 leading-none text-xs ml-0.5 transition-colors",
178
- children: "\xD7"
179
- }
180
- )
181
- ] }),
182
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-1 py-1.5", children: group.types.map((typeDef) => /* @__PURE__ */ jsx(
236
+ tool === "PLACE" && paletteGroups.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col border-t border-slate-100", children: [
237
+ /* @__PURE__ */ jsx("div", { className: "flex items-end gap-0 overflow-x-auto bg-slate-50 border-b border-slate-200 px-2 pt-1", children: paletteGroups.map((group) => /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: /* @__PURE__ */ jsxs(
238
+ "button",
239
+ {
240
+ onClick: () => setActiveGroupId(group.id),
241
+ className: [
242
+ "flex items-center gap-1 px-3 py-1 text-xs font-medium rounded-t border-x border-t transition-colors whitespace-nowrap",
243
+ group.id === activeGroupId ? "bg-white border-slate-200 text-slate-800 -mb-px pb-[5px]" : "bg-slate-50 border-transparent text-slate-400 hover:text-slate-600 hover:bg-slate-100"
244
+ ].join(" "),
245
+ children: [
246
+ group.name || "Sin nombre",
247
+ !group.isBase && onRemoveLibraryGroup && /* @__PURE__ */ jsx(
248
+ "span",
249
+ {
250
+ role: "button",
251
+ title: `Eliminar "${group.name}"`,
252
+ onClick: (e) => {
253
+ e.stopPropagation();
254
+ onRemoveLibraryGroup(group.id);
255
+ },
256
+ className: "ml-0.5 text-slate-300 hover:text-red-400 transition-colors leading-none",
257
+ children: "\xD7"
258
+ }
259
+ )
260
+ ]
261
+ }
262
+ ) }, group.id)) }),
263
+ activeGroup && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 flex-wrap px-2 py-1.5 bg-white min-h-[36px]", children: activeGroup.types.map((typeDef) => /* @__PURE__ */ jsx(
183
264
  TypeChip,
184
265
  {
185
266
  typeDef,
@@ -188,7 +269,7 @@ function Toolbar({
188
269
  },
189
270
  typeDef.id
190
271
  )) })
191
- ] }, group.id)) })
272
+ ] })
192
273
  ] });
193
274
  }
194
275
  var ZOOM_MIN = 0.1;
@@ -1128,6 +1209,7 @@ function ElementNode({
1128
1209
  {
1129
1210
  d: typeDef.svgPath,
1130
1211
  fill: fillColor,
1212
+ fillRule: typeDef.fillRule ?? "nonzero",
1131
1213
  stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
1132
1214
  strokeWidth: isSelected ? customPath.strokeWidth * 1.5 : customPath.strokeWidth,
1133
1215
  style: { cursor: bodyCursor },
@@ -1135,6 +1217,28 @@ function ElementNode({
1135
1217
  onClick: handleBodyClick
1136
1218
  }
1137
1219
  ) }),
1220
+ typeDef.shape === "svg" && typeDef.svgMarkup && (() => {
1221
+ const parsed = parseSvgMarkup(typeDef.svgMarkup);
1222
+ const parts = parsed.viewBox.split(/[\s,]+/).map(Number);
1223
+ const vw = parts[2] ?? 100;
1224
+ const vh = parts[3] ?? 100;
1225
+ const sx = vw > 0 ? w / vw : 1;
1226
+ const sy = vh > 0 ? h / vh : 1;
1227
+ const avgScale = Math.sqrt(Math.abs(sx * sy)) || 1;
1228
+ return /* @__PURE__ */ jsx(
1229
+ "g",
1230
+ {
1231
+ transform: `translate(${x}, ${y}) scale(${sx}, ${sy})`,
1232
+ fill: fillColor,
1233
+ stroke: isSelected ? "#3b82f6" : typeDef.strokeColor,
1234
+ strokeWidth: isSelected ? sw / avgScale * 1.5 : sw / avgScale,
1235
+ style: { cursor: bodyCursor },
1236
+ onMouseDown: tool === "SELECT" && !onViewerClick ? handleBodyDown : void 0,
1237
+ onClick: handleBodyClick,
1238
+ dangerouslySetInnerHTML: { __html: parsed.innerHtml }
1239
+ }
1240
+ );
1241
+ })(),
1138
1242
  (element.label ?? typeDef.label) && /* @__PURE__ */ jsx(
1139
1243
  "text",
1140
1244
  {
@@ -1633,14 +1737,35 @@ function PropertiesPanel({
1633
1737
  /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b border-slate-100 text-xs font-semibold text-slate-500 uppercase tracking-wide", children: "Propiedades" }),
1634
1738
  /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 p-3", children: [
1635
1739
  typeDef && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1636
- /* @__PURE__ */ jsx(
1740
+ typeDef.shape === "svg" ? /* @__PURE__ */ jsx(
1741
+ "svg",
1742
+ {
1743
+ viewBox: (() => {
1744
+ try {
1745
+ return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).viewBox : "0 0 100 100";
1746
+ } catch {
1747
+ return "0 0 100 100";
1748
+ }
1749
+ })(),
1750
+ className: "w-3.5 h-3.5 shrink-0 border border-slate-300 rounded-sm",
1751
+ style: { color: typeDef.strokeColor },
1752
+ dangerouslySetInnerHTML: { __html: (() => {
1753
+ try {
1754
+ return typeDef.svgMarkup ? parseSvgMarkup(typeDef.svgMarkup).innerHtml : "";
1755
+ } catch {
1756
+ return "";
1757
+ }
1758
+ })() }
1759
+ }
1760
+ ) : /* @__PURE__ */ jsx(
1637
1761
  "span",
1638
1762
  {
1639
1763
  className: "w-3.5 h-3.5 rounded-sm shrink-0 border",
1640
1764
  style: { background: typeDef.color, borderColor: typeDef.strokeColor }
1641
1765
  }
1642
1766
  ),
1643
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label })
1767
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-700 truncate", children: typeDef.label }),
1768
+ typeDef.shape === "svg" && /* @__PURE__ */ jsx("span", { className: "text-[9px] uppercase tracking-wide text-slate-400 font-medium ml-auto", children: "SVG" })
1644
1769
  ] }),
1645
1770
  /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-0.5", children: [
1646
1771
  /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-slate-400 uppercase tracking-wide", children: "Etiqueta" }),
@@ -1975,7 +2100,23 @@ function createDefaultMap() {
1975
2100
  ]
1976
2101
  };
1977
2102
  }
1978
- var EMPTY_DOMAIN_CONFIG = { id: "__empty__", name: "", elementTypes: [] };
2103
+ var DEFAULT_LIBRARY_KEY = "venueMapEditor:libraries";
2104
+ function mergeLibraries(existing, incoming) {
2105
+ const result = { ...existing };
2106
+ for (const [groupId, incomingGroup] of Object.entries(incoming)) {
2107
+ if (result[groupId]) {
2108
+ const existingIds = new Set(result[groupId].objects.map((o) => o.id));
2109
+ const newObjects = incomingGroup.objects.filter((o) => !existingIds.has(o.id));
2110
+ result[groupId] = {
2111
+ ...result[groupId],
2112
+ objects: [...result[groupId].objects, ...newObjects]
2113
+ };
2114
+ } else {
2115
+ result[groupId] = incomingGroup;
2116
+ }
2117
+ }
2118
+ return result;
2119
+ }
1979
2120
  function updateFloor(map, updatedFloor) {
1980
2121
  return {
1981
2122
  ...map,
@@ -2015,7 +2156,9 @@ function polygonToRect(area) {
2015
2156
  };
2016
2157
  }
2017
2158
  function VenueMapEditor({
2018
- domainConfig = EMPTY_DOMAIN_CONFIG,
2159
+ domainConfigs,
2160
+ domainConfig,
2161
+ libraryStorageKey = DEFAULT_LIBRARY_KEY,
2019
2162
  initialMap,
2020
2163
  onChange,
2021
2164
  width = "100%",
@@ -2029,7 +2172,13 @@ function VenueMapEditor({
2029
2172
  onElementClick,
2030
2173
  onElementTypeClick
2031
2174
  }) {
2175
+ const effectiveConfigs = useMemo(() => {
2176
+ if (domainConfigs && domainConfigs.length > 0) return domainConfigs;
2177
+ if (domainConfig) return [domainConfig];
2178
+ return [];
2179
+ }, [domainConfigs, domainConfig]);
2032
2180
  const initialMapRef = useRef(initialMap ?? createDefaultMap());
2181
+ const [persistedLibs, setPersistedLibs] = useLibraryStorage(libraryStorageKey);
2033
2182
  const { map, canUndo, canRedo, push, replace, undo, redo } = useHistory(
2034
2183
  initialMapRef.current
2035
2184
  );
@@ -2045,31 +2194,40 @@ function VenueMapEditor({
2045
2194
  const resetViewRef = useRef(() => void 0);
2046
2195
  const importInputRef = useRef(null);
2047
2196
  const libraryInputRef = useRef(null);
2197
+ const effectiveLibs = useMemo(() => ({
2198
+ ...map.libraries ?? {},
2199
+ ...persistedLibs
2200
+ }), [map.libraries, persistedLibs]);
2048
2201
  const buildTypeDefs = useCallback(() => {
2049
- const m = new Map(domainConfig.elementTypes.map((t) => [t.id, t]));
2050
- const libs = map.libraries ?? {};
2051
- for (const group of Object.values(libs)) {
2202
+ const m = /* @__PURE__ */ new Map();
2203
+ for (const cfg of effectiveConfigs) {
2204
+ for (const t of cfg.elementTypes) {
2205
+ if (!m.has(t.id)) m.set(t.id, t);
2206
+ }
2207
+ }
2208
+ for (const group of Object.values(effectiveLibs)) {
2052
2209
  for (const t of group.objects) {
2053
2210
  if (!m.has(t.id)) m.set(t.id, t);
2054
2211
  }
2055
2212
  }
2056
2213
  return m;
2057
- }, [domainConfig, map.libraries]);
2214
+ }, [effectiveConfigs, effectiveLibs]);
2058
2215
  const elementTypeDefs = useRef(buildTypeDefs());
2059
2216
  useEffect(() => {
2060
2217
  elementTypeDefs.current = buildTypeDefs();
2061
2218
  }, [buildTypeDefs]);
2062
2219
  const paletteGroups = useMemo(() => {
2063
2220
  const groups = [];
2064
- if (domainConfig.elementTypes.length > 0) {
2065
- groups.push({ id: domainConfig.id, name: domainConfig.name, types: domainConfig.elementTypes, isBase: true });
2221
+ for (const cfg of effectiveConfigs) {
2222
+ if (cfg.elementTypes.length > 0) {
2223
+ groups.push({ id: cfg.id, name: cfg.name, types: cfg.elementTypes, isBase: true });
2224
+ }
2066
2225
  }
2067
- const libs = map.libraries ?? {};
2068
- for (const [gid, group] of Object.entries(libs)) {
2226
+ for (const [gid, group] of Object.entries(effectiveLibs)) {
2069
2227
  groups.push({ id: gid, name: group.name, types: group.objects, isBase: false });
2070
2228
  }
2071
2229
  return groups;
2072
- }, [domainConfig, map.libraries]);
2230
+ }, [effectiveConfigs, effectiveLibs]);
2073
2231
  useEffect(() => {
2074
2232
  if (activePlaceTypeId) return;
2075
2233
  const firstType = paletteGroups[0]?.types[0];
@@ -2209,22 +2367,27 @@ function VenueMapEditor({
2209
2367
  reader.onload = (e) => {
2210
2368
  try {
2211
2369
  const parsed = JSON.parse(e.target?.result);
2212
- const merged = { ...map.libraries ?? {}, ...parsed };
2213
- push({ ...map, libraries: merged });
2370
+ const mergedPersisted = mergeLibraries(persistedLibs, parsed);
2371
+ setPersistedLibs(mergedPersisted);
2372
+ const mergedMap = mergeLibraries(map.libraries ?? {}, parsed);
2373
+ push({ ...map, libraries: mergedMap });
2214
2374
  } catch {
2215
2375
  }
2216
2376
  };
2217
2377
  reader.readAsText(file);
2218
2378
  },
2219
- [map, push]
2379
+ [map, push, persistedLibs, setPersistedLibs]
2220
2380
  );
2221
2381
  const handleRemoveLibraryGroup = useCallback(
2222
2382
  (groupId) => {
2383
+ const newPersistedLibs = { ...persistedLibs };
2384
+ delete newPersistedLibs[groupId];
2385
+ setPersistedLibs(newPersistedLibs);
2223
2386
  const libs = { ...map.libraries ?? {} };
2224
2387
  delete libs[groupId];
2225
2388
  push({ ...map, libraries: Object.keys(libs).length > 0 ? libs : void 0 });
2226
2389
  },
2227
- [map, push]
2390
+ [map, push, persistedLibs, setPersistedLibs]
2228
2391
  );
2229
2392
  const DEFAULT_WALL_THICKNESS = 8;
2230
2393
  const handleAddWall = useCallback(
@@ -2671,6 +2834,6 @@ function VenueMapViewer({ elementStatus, onElementClick, ...rest }) {
2671
2834
  );
2672
2835
  }
2673
2836
 
2674
- export { VenueMapEditor, VenueMapViewer, findNearestNode, genId, snapPoint, snapToGrid, usePanZoom };
2837
+ export { VenueMapEditor, VenueMapViewer, findNearestNode, genId, parseSvgMarkup, snapPoint, snapToGrid, useLibraryStorage, usePanZoom };
2675
2838
  //# sourceMappingURL=index.mjs.map
2676
2839
  //# sourceMappingURL=index.mjs.map