@nextsparkjs/core 0.1.0-beta.158 → 0.1.0-beta.159

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 (33) hide show
  1. package/dist/components/dashboard/block-editor/batch-action-bar.d.ts +12 -0
  2. package/dist/components/dashboard/block-editor/batch-action-bar.d.ts.map +1 -0
  3. package/dist/components/dashboard/block-editor/batch-action-bar.js +99 -0
  4. package/dist/components/dashboard/block-editor/block-picker.d.ts +8 -3
  5. package/dist/components/dashboard/block-editor/block-picker.d.ts.map +1 -1
  6. package/dist/components/dashboard/block-editor/block-picker.js +5 -3
  7. package/dist/components/dashboard/block-editor/block-preview-canvas.d.ts +7 -3
  8. package/dist/components/dashboard/block-editor/block-preview-canvas.d.ts.map +1 -1
  9. package/dist/components/dashboard/block-editor/block-preview-canvas.js +17 -8
  10. package/dist/components/dashboard/block-editor/block-settings-panel.d.ts +2 -1
  11. package/dist/components/dashboard/block-editor/block-settings-panel.d.ts.map +1 -1
  12. package/dist/components/dashboard/block-editor/block-settings-panel.js +10 -1
  13. package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
  14. package/dist/components/dashboard/block-editor/builder-editor-view.js +190 -26
  15. package/dist/components/dashboard/block-editor/tree-view-node.d.ts +7 -2
  16. package/dist/components/dashboard/block-editor/tree-view-node.d.ts.map +1 -1
  17. package/dist/components/dashboard/block-editor/tree-view-node.js +16 -3
  18. package/dist/components/dashboard/block-editor/tree-view.d.ts +7 -2
  19. package/dist/components/dashboard/block-editor/tree-view.d.ts.map +1 -1
  20. package/dist/components/dashboard/block-editor/tree-view.js +7 -4
  21. package/dist/lib/blocks/clipboard.d.ts +8 -1
  22. package/dist/lib/blocks/clipboard.d.ts.map +1 -1
  23. package/dist/lib/blocks/clipboard.js +30 -8
  24. package/dist/messages/en/admin.json +20 -1
  25. package/dist/messages/en/index.d.ts +19 -0
  26. package/dist/messages/en/index.d.ts.map +1 -1
  27. package/dist/messages/es/admin.json +20 -1
  28. package/dist/messages/es/index.d.ts +19 -0
  29. package/dist/messages/es/index.d.ts.map +1 -1
  30. package/dist/styles/classes.json +3 -2
  31. package/dist/styles/ui.css +1 -1
  32. package/package.json +2 -2
  33. package/scripts/build/registry/post-build/page-generator.mjs +52 -4
@@ -5,7 +5,13 @@ import { useRouter } from "next/navigation";
5
5
  import { useTranslations } from "next-intl";
6
6
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
7
7
  import { v4 as uuidv4 } from "uuid";
8
- import { copyBlockToClipboard, getBlockFromClipboard, hasBlockInClipboard } from "../../../lib/blocks/clipboard.js";
8
+ import {
9
+ copyBlockToClipboard,
10
+ getBlockFromClipboard,
11
+ copyBlocksToClipboard,
12
+ getBlocksFromClipboard,
13
+ getClipboardBlockCount
14
+ } from "../../../lib/blocks/clipboard.js";
9
15
  import { Button } from "../../ui/button.js";
10
16
  import { Input } from "../../ui/input.js";
11
17
  import { Separator } from "../../ui/separator.js";
@@ -16,11 +22,13 @@ import { sel } from "../../../lib/test/index.js";
16
22
  import { BlockPicker } from "./block-picker.js";
17
23
  import { BlockPreviewCanvas } from "./block-preview-canvas.js";
18
24
  import { BlockSettingsPanel } from "./block-settings-panel.js";
25
+ import { BatchActionBar } from "./batch-action-bar.js";
19
26
  import { BlockService } from "../../../lib/services/block.service.js";
20
27
  import { ViewportToggle } from "./viewport-toggle.js";
21
28
  import { ConfigPanel } from "./config-panel.js";
22
29
  import { useSidebar } from "../../../contexts/sidebar-context.js";
23
30
  import { cn } from "../../../lib/utils/index.js";
31
+ import { isPatternReference } from "../../../types/pattern-reference.js";
24
32
  function getTeamId() {
25
33
  if (typeof window !== "undefined") {
26
34
  return localStorage.getItem("activeTeamId");
@@ -49,7 +57,8 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
49
57
  const queryClient = useQueryClient();
50
58
  const { isCollapsed } = useSidebar();
51
59
  const [blocks, setBlocks] = useState([]);
52
- const [selectedBlockId, setSelectedBlockId] = useState(null);
60
+ const [selectedBlockIds, setSelectedBlockIds] = useState(/* @__PURE__ */ new Set());
61
+ const lastSelectedIdRef = useRef(null);
53
62
  const [title, setTitle] = useState("");
54
63
  const [slug, setSlug] = useState("");
55
64
  const [slugManuallyEdited, setSlugManuallyEdited] = useState(false);
@@ -234,17 +243,68 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
234
243
  };
235
244
  saveMutation.mutate(data);
236
245
  }, [title, slug, blocks, pageSettings, entityFields, saveMutation, validateFields]);
246
+ const selectedBlockId = useMemo(() => {
247
+ if (selectedBlockIds.size === 1) return Array.from(selectedBlockIds)[0];
248
+ return null;
249
+ }, [selectedBlockIds]);
250
+ const setSelectedBlockId = useCallback((id2) => {
251
+ if (id2) {
252
+ setSelectedBlockIds(/* @__PURE__ */ new Set([id2]));
253
+ lastSelectedIdRef.current = id2;
254
+ } else {
255
+ setSelectedBlockIds(/* @__PURE__ */ new Set());
256
+ lastSelectedIdRef.current = null;
257
+ }
258
+ }, []);
259
+ const handleSelectBlock = useCallback((blockId, event) => {
260
+ if ((event == null ? void 0 : event.metaKey) || (event == null ? void 0 : event.ctrlKey)) {
261
+ setSelectedBlockIds((prev) => {
262
+ const next = new Set(prev);
263
+ if (next.has(blockId)) {
264
+ next.delete(blockId);
265
+ } else {
266
+ next.add(blockId);
267
+ }
268
+ lastSelectedIdRef.current = blockId;
269
+ return next;
270
+ });
271
+ } else if ((event == null ? void 0 : event.shiftKey) && lastSelectedIdRef.current) {
272
+ const anchorIdx = blocks.findIndex((b) => b.id === lastSelectedIdRef.current);
273
+ const targetIdx = blocks.findIndex((b) => b.id === blockId);
274
+ if (anchorIdx !== -1 && targetIdx !== -1) {
275
+ const start = Math.min(anchorIdx, targetIdx);
276
+ const end = Math.max(anchorIdx, targetIdx);
277
+ const rangeIds = blocks.slice(start, end + 1).map((b) => b.id);
278
+ setSelectedBlockIds((prev) => {
279
+ const next = new Set(prev);
280
+ rangeIds.forEach((id2) => next.add(id2));
281
+ return next;
282
+ });
283
+ }
284
+ } else {
285
+ setSelectedBlockIds(/* @__PURE__ */ new Set([blockId]));
286
+ lastSelectedIdRef.current = blockId;
287
+ }
288
+ }, [blocks]);
289
+ const handleSelectAll = useCallback(() => {
290
+ const nonPatternIds = blocks.filter((b) => !isPatternReference(b)).map((b) => b.id);
291
+ setSelectedBlockIds(new Set(nonPatternIds));
292
+ }, [blocks]);
293
+ const handleClearSelection = useCallback(() => {
294
+ setSelectedBlockIds(/* @__PURE__ */ new Set());
295
+ }, []);
296
+ const [clipboardCount, setClipboardCount] = useState(() => getClipboardBlockCount());
237
297
  const handleAddBlock = useCallback((blockSlug) => {
238
298
  const newBlock = {
239
299
  id: uuidv4(),
240
300
  blockSlug,
241
301
  props: {}
242
302
  };
243
- if (selectedBlockId) {
244
- const index = blocks.findIndex((b) => b.id === selectedBlockId);
245
- if (index !== -1) {
303
+ if (selectedBlockIds.size > 0) {
304
+ const lastIdx = Math.max(...Array.from(selectedBlockIds).map((id2) => blocks.findIndex((b) => b.id === id2)));
305
+ if (lastIdx !== -1) {
246
306
  const newBlocks = [...blocks];
247
- newBlocks.splice(index + 1, 0, newBlock);
307
+ newBlocks.splice(lastIdx + 1, 0, newBlock);
248
308
  setBlocks(newBlocks);
249
309
  setSelectedBlockId(newBlock.id);
250
310
  return;
@@ -252,29 +312,29 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
252
312
  }
253
313
  setBlocks((prev) => [...prev, newBlock]);
254
314
  setSelectedBlockId(newBlock.id);
255
- }, [blocks, selectedBlockId]);
315
+ }, [blocks, selectedBlockIds, setSelectedBlockId]);
256
316
  const handleAddPattern = useCallback((patternId) => {
257
317
  const patternRef = {
258
318
  type: "pattern",
259
319
  ref: patternId,
260
320
  id: uuidv4()
261
- // Unique instance ID
262
321
  };
263
322
  setBlocks((prev) => [...prev, patternRef]);
264
323
  setSelectedBlockId(patternRef.id);
265
- }, []);
324
+ }, [setSelectedBlockId]);
266
325
  const handleRemoveBlock = useCallback((blockId) => {
267
326
  setBlocks((prev) => prev.filter((b) => b.id !== blockId));
268
- if (selectedBlockId === blockId) {
269
- setSelectedBlockId(null);
270
- }
271
- }, [selectedBlockId]);
272
- const [clipboardHasBlock, setClipboardHasBlock] = useState(() => hasBlockInClipboard());
327
+ setSelectedBlockIds((prev) => {
328
+ const next = new Set(prev);
329
+ next.delete(blockId);
330
+ return next;
331
+ });
332
+ }, []);
273
333
  const handleCopyBlock = useCallback((blockId) => {
274
334
  const block = blocks.find((b) => b.id === blockId);
275
335
  if (block) {
276
336
  copyBlockToClipboard(block);
277
- setClipboardHasBlock(true);
337
+ setClipboardCount(1);
278
338
  toast.success(t("messages.blockCopied"));
279
339
  }
280
340
  }, [blocks, t]);
@@ -300,7 +360,68 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
300
360
  setBlocks((prev) => [...prev, newBlock]);
301
361
  setSelectedBlockId(newBlock.id);
302
362
  toast.success(t("messages.blockPasted"));
303
- }, [blocks, selectedBlockId, t]);
363
+ }, [blocks, selectedBlockId, t, setSelectedBlockId]);
364
+ const handleCopySelected = useCallback(() => {
365
+ const selected = blocks.filter((b) => selectedBlockIds.has(b.id) && !isPatternReference(b));
366
+ if (selected.length === 0) return;
367
+ copyBlocksToClipboard(selected, entitySlug);
368
+ setClipboardCount(selected.length);
369
+ toast.success(t("multiSelect.copied", { count: selected.length }));
370
+ }, [blocks, selectedBlockIds, entitySlug, t]);
371
+ const handleDeleteSelected = useCallback(() => {
372
+ const idsToDelete = selectedBlockIds;
373
+ setBlocks((prev) => prev.filter((b) => !idsToDelete.has(b.id)));
374
+ setSelectedBlockIds(/* @__PURE__ */ new Set());
375
+ toast.success(t("multiSelect.deleted", { count: idsToDelete.size }));
376
+ }, [selectedBlockIds, t]);
377
+ const handleDuplicateSelected = useCallback(() => {
378
+ const selectedOrdered = blocks.filter((b) => selectedBlockIds.has(b.id) && !isPatternReference(b));
379
+ if (selectedOrdered.length === 0) return;
380
+ const lastIdx = blocks.findIndex((b) => b.id === selectedOrdered[selectedOrdered.length - 1].id);
381
+ const duplicates = selectedOrdered.map((b) => ({
382
+ ...b,
383
+ id: uuidv4(),
384
+ props: { ...b.props }
385
+ }));
386
+ const newBlocks = [...blocks];
387
+ newBlocks.splice(lastIdx + 1, 0, ...duplicates);
388
+ setBlocks(newBlocks);
389
+ setSelectedBlockIds(new Set(duplicates.map((d) => d.id)));
390
+ toast.success(t("multiSelect.duplicated", { count: duplicates.length }));
391
+ }, [blocks, selectedBlockIds, t]);
392
+ const handlePasteBlocks = useCallback(() => {
393
+ const clipData = getBlocksFromClipboard();
394
+ if (!clipData || clipData.blocks.length === 0) {
395
+ handlePasteBlock();
396
+ return;
397
+ }
398
+ const allowedSlugs = new Set(BlockService.getForScope(entitySlug).map((b) => b.slug));
399
+ const compatible = clipData.blocks.filter((b) => allowedSlugs.has(b.blockSlug));
400
+ const skipped = clipData.blocks.length - compatible.length;
401
+ if (compatible.length === 0) {
402
+ toast.error(t("multiSelect.clipboardEmpty"));
403
+ return;
404
+ }
405
+ const newBlocks = compatible.map((b) => ({
406
+ id: uuidv4(),
407
+ blockSlug: b.blockSlug,
408
+ props: { ...b.props }
409
+ }));
410
+ const insertIdx = selectedBlockIds.size > 0 ? Math.max(...Array.from(selectedBlockIds).map((id2) => blocks.findIndex((b) => b.id === id2))) : blocks.length - 1;
411
+ const updatedBlocks = [...blocks];
412
+ updatedBlocks.splice(insertIdx + 1, 0, ...newBlocks);
413
+ setBlocks(updatedBlocks);
414
+ setSelectedBlockIds(new Set(newBlocks.map((b) => b.id)));
415
+ if (skipped > 0) {
416
+ toast.warning(t("multiSelect.pastedPartial", {
417
+ pasted: compatible.length,
418
+ total: clipData.blocks.length,
419
+ skipped
420
+ }));
421
+ } else {
422
+ toast.success(t("multiSelect.pasted", { count: newBlocks.length }));
423
+ }
424
+ }, [blocks, selectedBlockIds, entitySlug, t, handlePasteBlock]);
304
425
  const handleDuplicateBlock = useCallback((blockId) => {
305
426
  const block = blocks.find((b) => b.id === blockId);
306
427
  if (block) {
@@ -315,7 +436,7 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
315
436
  setBlocks(newBlocks);
316
437
  setSelectedBlockId(duplicated.id);
317
438
  }
318
- }, [blocks]);
439
+ }, [blocks, setSelectedBlockId]);
319
440
  const handleUpdateBlockProps = useCallback((blockId, props) => {
320
441
  setBlocks((prev) => prev.map(
321
442
  (block) => block.id === blockId ? { ...block, props } : block
@@ -342,6 +463,35 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
342
463
  return newBlocks;
343
464
  });
344
465
  }, []);
466
+ useEffect(() => {
467
+ if (viewMode !== "preview") return;
468
+ const handler = (e) => {
469
+ const target = e.target;
470
+ const isInputFocused = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
471
+ if (isInputFocused) return;
472
+ const isMod = e.metaKey || e.ctrlKey;
473
+ if (isMod && e.key === "a") {
474
+ e.preventDefault();
475
+ handleSelectAll();
476
+ } else if (e.key === "Escape") {
477
+ handleClearSelection();
478
+ } else if (isMod && e.key === "c" && selectedBlockIds.size > 0) {
479
+ e.preventDefault();
480
+ handleCopySelected();
481
+ } else if (isMod && e.key === "v") {
482
+ e.preventDefault();
483
+ handlePasteBlocks();
484
+ } else if (isMod && e.key === "d" && selectedBlockIds.size > 0) {
485
+ e.preventDefault();
486
+ handleDuplicateSelected();
487
+ } else if ((e.key === "Delete" || e.key === "Backspace") && selectedBlockIds.size > 0) {
488
+ e.preventDefault();
489
+ handleDeleteSelected();
490
+ }
491
+ };
492
+ document.addEventListener("keydown", handler);
493
+ return () => document.removeEventListener("keydown", handler);
494
+ }, [viewMode, selectedBlockIds, handleSelectAll, handleClearSelection, handleCopySelected, handlePasteBlocks, handleDuplicateSelected, handleDeleteSelected]);
345
495
  const handleEntityFieldChange = useCallback((field, value) => {
346
496
  setEntityFields((prev) => ({ ...prev, [field]: value }));
347
497
  onEntityFieldChangeProp == null ? void 0 : onEntityFieldChangeProp(field, value);
@@ -356,9 +506,9 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
356
506
  const handleViewModeChange = useCallback((mode2) => {
357
507
  setViewMode(mode2);
358
508
  if (mode2 === "settings") {
359
- setSelectedBlockId(null);
509
+ handleClearSelection();
360
510
  }
361
- }, []);
511
+ }, [handleClearSelection]);
362
512
  const selectedBlock = selectedBlockId ? blocks.find((b) => b.id === selectedBlockId) : void 0;
363
513
  const publicUrl = useMemo(() => {
364
514
  var _a2, _b, _c;
@@ -613,16 +763,17 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
613
763
  showPatternsTab,
614
764
  pageBlocks: blocks,
615
765
  selectedBlockId,
616
- onSelectBlock: setSelectedBlockId,
766
+ selectedBlockIds,
767
+ onSelectBlock: handleSelectBlock,
617
768
  onReorderBlocks: handleReorderBlocks,
618
769
  onCopyBlock: handleCopyBlock,
619
770
  onDuplicateBlock: handleDuplicateBlock,
620
771
  onRemoveBlock: handleRemoveBlock,
621
- onPasteBlock: handlePasteBlock,
622
- hasClipboardBlock: clipboardHasBlock
772
+ onPasteBlock: handlePasteBlocks,
773
+ clipboardCount
623
774
  }
624
775
  ) }),
625
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden", children: [
776
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden relative", children: [
626
777
  /* @__PURE__ */ jsx(
627
778
  "div",
628
779
  {
@@ -644,8 +795,8 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
644
795
  BlockPreviewCanvas,
645
796
  {
646
797
  blocks,
647
- selectedBlockId,
648
- onSelectBlock: setSelectedBlockId,
798
+ selectedBlockIds,
799
+ onSelectBlock: handleSelectBlock,
649
800
  onMoveUp: handleMoveBlockUp,
650
801
  onMoveDown: handleMoveBlockDown,
651
802
  onCopy: handleCopyBlock,
@@ -657,6 +808,18 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
657
808
  )
658
809
  }
659
810
  ),
811
+ viewMode === "preview" && selectedBlockIds.size > 1 && /* @__PURE__ */ jsx(
812
+ BatchActionBar,
813
+ {
814
+ selectedCount: selectedBlockIds.size,
815
+ onCopy: handleCopySelected,
816
+ onDuplicate: handleDuplicateSelected,
817
+ onDelete: handleDeleteSelected,
818
+ onClearSelection: handleClearSelection,
819
+ clipboardCount,
820
+ onPaste: handlePasteBlocks
821
+ }
822
+ ),
660
823
  /* @__PURE__ */ jsx("div", { className: cn("h-full", viewMode !== "settings" && "hidden"), children: /* @__PURE__ */ jsx(
661
824
  ConfigPanel,
662
825
  {
@@ -672,6 +835,7 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
672
835
  BlockSettingsPanel,
673
836
  {
674
837
  block: selectedBlock,
838
+ selectedCount: selectedBlockIds.size,
675
839
  onUpdateProps: (props) => {
676
840
  if (selectedBlockId) {
677
841
  handleUpdateBlockProps(selectedBlockId, props);
@@ -682,7 +846,7 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldCh
682
846
  handleRemoveBlock(selectedBlockId);
683
847
  }
684
848
  },
685
- onClose: () => setSelectedBlockId(null)
849
+ onClose: handleClearSelection
686
850
  }
687
851
  ) })
688
852
  ] })
@@ -3,12 +3,17 @@ import { type PatternReference } from '../../../types/pattern-reference';
3
3
  interface TreeViewNodeProps {
4
4
  block: BlockInstance | PatternReference;
5
5
  isSelected: boolean;
6
- onSelect: () => void;
6
+ isMultiSelect?: boolean;
7
+ onSelect: (event?: {
8
+ metaKey?: boolean;
9
+ shiftKey?: boolean;
10
+ ctrlKey?: boolean;
11
+ }) => void;
7
12
  onCopy?: () => void;
8
13
  onDuplicate?: () => void;
9
14
  onRemove?: () => void;
10
15
  isPartOfPattern?: boolean;
11
16
  }
12
- export declare function TreeViewNode({ block, isSelected, onSelect, onCopy, onDuplicate, onRemove, isPartOfPattern }: TreeViewNodeProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function TreeViewNode({ block, isSelected, isMultiSelect, onSelect, onCopy, onDuplicate, onRemove, isPartOfPattern }: TreeViewNodeProps): import("react/jsx-runtime").JSX.Element;
13
18
  export {};
14
19
  //# sourceMappingURL=tree-view-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree-view-node.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/tree-view-node.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAE5F,UAAU,iBAAiB;IACzB,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAAA;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,UAAU,EACV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,QAAQ,EACR,eAAuB,EACxB,EAAE,iBAAiB,2CA6HnB"}
1
+ {"version":3,"file":"tree-view-node.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/tree-view-node.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAE5F,UAAU,iBAAiB;IACzB,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAAA;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;IACxF,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,UAAU,EACV,aAAqB,EACrB,QAAQ,EACR,MAAM,EACN,WAAW,EACX,QAAQ,EACR,eAAuB,EACxB,EAAE,iBAAiB,2CA6InB"}
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useSortable } from "@dnd-kit/sortable";
4
4
  import { CSS } from "@dnd-kit/utilities";
5
- import { GripVertical, ClipboardCopy, Copy, Trash2 } from "lucide-react";
5
+ import { GripVertical, ClipboardCopy, Copy, Trash2, Check } from "lucide-react";
6
6
  import { cn } from "../../../lib/utils/index.js";
7
7
  import { sel } from "../../../lib/test/index.js";
8
8
  import { BlockService } from "../../../lib/services/block.service.js";
@@ -11,6 +11,7 @@ import { isPatternReference } from "../../../types/pattern-reference.js";
11
11
  function TreeViewNode({
12
12
  block,
13
13
  isSelected,
14
+ isMultiSelect = false,
14
15
  onSelect,
15
16
  onCopy,
16
17
  onDuplicate,
@@ -46,12 +47,24 @@ function TreeViewNode({
46
47
  className: cn(
47
48
  "group flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer transition-colors",
48
49
  "hover:bg-muted/80",
49
- isSelected && "bg-primary/10 ring-1 ring-primary",
50
+ isSelected && !isMultiSelect && "bg-primary/10 ring-1 ring-primary",
51
+ isSelected && isMultiSelect && "bg-primary/10 ring-1 ring-primary/70",
50
52
  isPartOfPattern && "ml-4 border-l-2 border-muted-foreground/20",
51
53
  isDragging && "z-50"
52
54
  ),
53
- onClick: onSelect,
55
+ onClick: (e) => onSelect({ metaKey: e.metaKey, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey }),
54
56
  children: [
57
+ isMultiSelect && !isPartOfPattern && /* @__PURE__ */ jsx(
58
+ "div",
59
+ {
60
+ className: cn(
61
+ "flex items-center justify-center w-4 h-4 rounded border shrink-0 transition-colors",
62
+ isSelected ? "bg-primary border-primary text-primary-foreground" : "border-muted-foreground/30 hover:border-primary"
63
+ ),
64
+ "data-cy": sel("blockEditor.treeView.nodeCheckbox", { id: block.id }),
65
+ children: isSelected && /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" })
66
+ }
67
+ ),
55
68
  !isPartOfPattern && /* @__PURE__ */ jsx(
56
69
  "div",
57
70
  {
@@ -3,7 +3,12 @@ import type { BlockInstance } from '../../../types/blocks';
3
3
  interface TreeViewProps {
4
4
  blocks: (BlockInstance | PatternReference)[];
5
5
  selectedBlockId: string | null;
6
- onSelectBlock: (id: string) => void;
6
+ selectedBlockIds?: Set<string>;
7
+ onSelectBlock: (id: string, event?: {
8
+ metaKey?: boolean;
9
+ shiftKey?: boolean;
10
+ ctrlKey?: boolean;
11
+ }) => void;
7
12
  onReorder: (blocks: (BlockInstance | PatternReference)[]) => void;
8
13
  onCopy?: (id: string) => void;
9
14
  onDuplicate?: (id: string) => void;
@@ -15,6 +20,6 @@ interface TreeViewProps {
15
20
  * Displays a tree structure of all blocks with drag & drop reordering.
16
21
  * Clicking a block selects it and scrolls to it in the preview.
17
22
  */
18
- export declare function TreeView({ blocks, selectedBlockId, onSelectBlock, onReorder, onCopy, onDuplicate, onRemove, emptyMessage }: TreeViewProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function TreeView({ blocks, selectedBlockId, selectedBlockIds, onSelectBlock, onReorder, onCopy, onDuplicate, onRemove, emptyMessage }: TreeViewProps): import("react/jsx-runtime").JSX.Element;
19
24
  export {};
20
25
  //# sourceMappingURL=tree-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree-view.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/tree-view.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D,UAAU,aAAa;IACrB,MAAM,EAAE,CAAC,aAAa,GAAG,gBAAgB,CAAC,EAAE,CAAA;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,aAAa,GAAG,gBAAgB,CAAC,EAAE,KAAK,IAAI,CAAA;IACjE,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,eAAe,EACf,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,YAAY,EACb,EAAE,aAAa,2CAkHf"}
1
+ {"version":3,"file":"tree-view.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/tree-view.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D,UAAU,aAAa;IACrB,MAAM,EAAE,CAAC,aAAa,GAAG,gBAAgB,CAAC,EAAE,CAAA;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC9B,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;IACzG,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,aAAa,GAAG,gBAAgB,CAAC,EAAE,KAAK,IAAI,CAAA;IACjE,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,YAAY,EACb,EAAE,aAAa,2CAqHf"}
@@ -24,6 +24,7 @@ import { isPatternReference } from "../../../types/pattern-reference.js";
24
24
  function TreeView({
25
25
  blocks,
26
26
  selectedBlockId,
27
+ selectedBlockIds,
27
28
  onSelectBlock,
28
29
  onReorder,
29
30
  onCopy,
@@ -74,8 +75,9 @@ function TreeView({
74
75
  }
75
76
  }
76
77
  }, [selectedBlockId]);
77
- const handleSelectBlock = useCallback((blockId) => {
78
- onSelectBlock(blockId);
78
+ const isMultiSelect = ((selectedBlockIds == null ? void 0 : selectedBlockIds.size) ?? 0) > 1;
79
+ const handleSelectBlock = useCallback((blockId, event) => {
80
+ onSelectBlock(blockId, event);
79
81
  }, [onSelectBlock]);
80
82
  if (blocks.length === 0) {
81
83
  return /* @__PURE__ */ jsxs(
@@ -110,8 +112,9 @@ function TreeView({
110
112
  TreeViewNode,
111
113
  {
112
114
  block: item.block,
113
- isSelected: selectedBlockId === item.id,
114
- onSelect: () => handleSelectBlock(item.id),
115
+ isSelected: selectedBlockIds ? selectedBlockIds.has(item.id) : selectedBlockId === item.id,
116
+ isMultiSelect,
117
+ onSelect: (event) => handleSelectBlock(item.id, event),
115
118
  onCopy: onCopy ? () => onCopy(item.id) : void 0,
116
119
  onDuplicate: onDuplicate ? () => onDuplicate(item.id) : void 0,
117
120
  onRemove: onRemove ? () => onRemove(item.id) : void 0,
@@ -2,10 +2,17 @@ import type { BlockInstance } from '../../types/blocks';
2
2
  export interface ClipboardBlock {
3
3
  blockSlug: string;
4
4
  props: Record<string, unknown>;
5
+ }
6
+ export interface ClipboardData {
7
+ blocks: ClipboardBlock[];
8
+ sourceEntitySlug?: string;
5
9
  copiedAt: number;
6
10
  }
11
+ export declare function copyBlocksToClipboard(blocks: BlockInstance[], sourceEntitySlug?: string): void;
7
12
  export declare function copyBlockToClipboard(block: BlockInstance): void;
13
+ export declare function getBlocksFromClipboard(): ClipboardData | null;
8
14
  export declare function getBlockFromClipboard(): ClipboardBlock | null;
9
- export declare function hasBlockInClipboard(): boolean;
15
+ export declare function getClipboardBlockCount(): number;
16
+ export declare function hasClipboardBlocks(): boolean;
10
17
  export declare function clearBlockClipboard(): void;
11
18
  //# sourceMappingURL=clipboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAO/D;AAED,wBAAgB,qBAAqB,IAAI,cAAc,GAAG,IAAI,CAQ7D;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAU9F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAE/D;AAED,wBAAgB,sBAAsB,IAAI,aAAa,GAAG,IAAI,CAY7D;AAED,wBAAgB,qBAAqB,IAAI,cAAc,GAAG,IAAI,CAI7D;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -1,23 +1,42 @@
1
1
  const CLIPBOARD_KEY = "nextspark:block-clipboard";
2
- function copyBlockToClipboard(block) {
2
+ function copyBlocksToClipboard(blocks, sourceEntitySlug) {
3
3
  const data = {
4
- blockSlug: block.blockSlug,
5
- props: { ...block.props },
4
+ blocks: blocks.map((b) => ({
5
+ blockSlug: b.blockSlug,
6
+ props: { ...b.props }
7
+ })),
8
+ sourceEntitySlug,
6
9
  copiedAt: Date.now()
7
10
  };
8
11
  localStorage.setItem(CLIPBOARD_KEY, JSON.stringify(data));
9
12
  }
10
- function getBlockFromClipboard() {
13
+ function copyBlockToClipboard(block) {
14
+ copyBlocksToClipboard([block]);
15
+ }
16
+ function getBlocksFromClipboard() {
11
17
  const raw = localStorage.getItem(CLIPBOARD_KEY);
12
18
  if (!raw) return null;
13
19
  try {
14
- return JSON.parse(raw);
20
+ const data = JSON.parse(raw);
21
+ if (Array.isArray(data == null ? void 0 : data.blocks)) {
22
+ return data;
23
+ }
24
+ return null;
15
25
  } catch {
16
26
  return null;
17
27
  }
18
28
  }
19
- function hasBlockInClipboard() {
20
- return localStorage.getItem(CLIPBOARD_KEY) !== null;
29
+ function getBlockFromClipboard() {
30
+ const data = getBlocksFromClipboard();
31
+ if (!data || data.blocks.length === 0) return null;
32
+ return data.blocks[data.blocks.length - 1];
33
+ }
34
+ function getClipboardBlockCount() {
35
+ var _a;
36
+ return ((_a = getBlocksFromClipboard()) == null ? void 0 : _a.blocks.length) ?? 0;
37
+ }
38
+ function hasClipboardBlocks() {
39
+ return getClipboardBlockCount() > 0;
21
40
  }
22
41
  function clearBlockClipboard() {
23
42
  localStorage.removeItem(CLIPBOARD_KEY);
@@ -25,6 +44,9 @@ function clearBlockClipboard() {
25
44
  export {
26
45
  clearBlockClipboard,
27
46
  copyBlockToClipboard,
47
+ copyBlocksToClipboard,
28
48
  getBlockFromClipboard,
29
- hasBlockInClipboard
49
+ getBlocksFromClipboard,
50
+ getClipboardBlockCount,
51
+ hasClipboardBlocks
30
52
  };
@@ -306,13 +306,32 @@
306
306
  },
307
307
  "layout": {
308
308
  "empty": "No blocks added yet",
309
- "pasteBlock": "Paste block"
309
+ "pasteBlock": "Paste block",
310
+ "pasteBlocks": "Paste {count} blocks"
310
311
  },
311
312
  "messages": {
312
313
  "saved": "Saved successfully",
313
314
  "created": "Created successfully",
314
315
  "blockCopied": "Block copied to clipboard",
315
316
  "blockPasted": "Block pasted"
317
+ },
318
+ "multiSelect": {
319
+ "selected": "{count, plural, one {# block selected} other {# blocks selected}}",
320
+ "selectAll": "Select all",
321
+ "deselectAll": "Deselect all",
322
+ "copy": "Copy",
323
+ "copied": "{count, plural, one {Block copied} other {# blocks copied}}",
324
+ "paste": "Paste",
325
+ "pasted": "{count, plural, one {Block pasted} other {# blocks pasted}}",
326
+ "pastedPartial": "{pasted} of {total} blocks pasted ({skipped} incompatible)",
327
+ "duplicate": "Duplicate",
328
+ "duplicated": "{count, plural, one {Block duplicated} other {# blocks duplicated}}",
329
+ "delete": "Delete",
330
+ "deleteConfirm": "{count, plural, one {Delete this block?} other {Delete # blocks?}}",
331
+ "deleted": "{count, plural, one {Block deleted} other {# blocks deleted}}",
332
+ "patternSkipped": "Pattern references excluded from selection",
333
+ "clipboardEmpty": "Clipboard is empty",
334
+ "settingsHint": "Use the action bar to manage selected blocks"
316
335
  }
317
336
  },
318
337
  "patterns": {
@@ -308,6 +308,7 @@ declare const _default: {
308
308
  layout: {
309
309
  empty: string;
310
310
  pasteBlock: string;
311
+ pasteBlocks: string;
311
312
  };
312
313
  messages: {
313
314
  saved: string;
@@ -315,6 +316,24 @@ declare const _default: {
315
316
  blockCopied: string;
316
317
  blockPasted: string;
317
318
  };
319
+ multiSelect: {
320
+ selected: string;
321
+ selectAll: string;
322
+ deselectAll: string;
323
+ copy: string;
324
+ copied: string;
325
+ paste: string;
326
+ pasted: string;
327
+ pastedPartial: string;
328
+ duplicate: string;
329
+ duplicated: string;
330
+ delete: string;
331
+ deleteConfirm: string;
332
+ deleted: string;
333
+ patternSkipped: string;
334
+ clipboardEmpty: string;
335
+ settingsHint: string;
336
+ };
318
337
  };
319
338
  patterns: {
320
339
  delete: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/en/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,wBAqBU"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/en/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,wBAqBU"}