@standhigher/puck-page-builder 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,8 +3,14 @@ import {
3
3
  ExtensionRegistryError,
4
4
  TemplateRegistry,
5
5
  createExtensionRegistry,
6
- createTemplateRegistry
7
- } from "./chunk-PUTYZI7B.js";
6
+ createTemplateRegistry,
7
+ maximumBlockInstances,
8
+ mergeBlockPolicies,
9
+ minimumBlockInstances,
10
+ validateBlockPolicy,
11
+ validateFieldValue,
12
+ validatePageDocumentWithRegistry
13
+ } from "./chunk-PFCWXLWN.js";
8
14
  import {
9
15
  WebRenderer
10
16
  } from "./chunk-KYXIXIWK.js";
@@ -342,7 +348,7 @@ function PropertyPanel({
342
348
 
343
349
  // src/editor/shell/PageDocumentEditorShell.tsx
344
350
  import { Puck as Puck2, usePuck } from "@puckeditor/core";
345
- import { Badge as Badge2, Banner, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack2, Text as Text2, TextField } from "@shopify/polaris";
351
+ import { Badge as Badge3, Banner, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack3, Select as Select2, Text as Text3, TextField } from "@shopify/polaris";
346
352
  import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2 } from "@shopify/polaris-icons";
347
353
  import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
348
354
 
@@ -462,6 +468,65 @@ function fromEngineData(data, base, registry) {
462
468
 
463
469
  // src/editor/context/EditorContext.tsx
464
470
  import { createContext, useCallback, useContext, useEffect as useEffect2, useMemo, useReducer, useRef as useRef3, useState as useState2 } from "react";
471
+
472
+ // src/editor/policy.ts
473
+ function operationEnabled(operation, policy, blockPolicy) {
474
+ const key = `allow${operation[0].toUpperCase()}${operation.slice(1)}`;
475
+ return policy?.operations?.[key] !== false && blockPolicy[key] !== false;
476
+ }
477
+ function resolvedBlockPolicy(type, definition, policy) {
478
+ return mergeBlockPolicies(definition?.policy, policy?.blocks?.[type]);
479
+ }
480
+ function blockCount(blocks, type) {
481
+ return blocks.filter((block) => block.type === type).length;
482
+ }
483
+ function canAddBlock(type, blocks, definition, policy) {
484
+ const blockPolicy = resolvedBlockPolicy(type, definition, policy);
485
+ return operationEnabled("add", policy, blockPolicy) && blockCount(blocks, type) < maximumBlockInstances(blockPolicy);
486
+ }
487
+ function canDeleteBlock(block, blocks, definition, policy) {
488
+ if (!block) return false;
489
+ const blockPolicy = resolvedBlockPolicy(block.type, definition, policy);
490
+ return operationEnabled("delete", policy, blockPolicy) && blockCount(blocks, block.type) > minimumBlockInstances(blockPolicy);
491
+ }
492
+ function canDuplicateBlock(block, blocks, definition, policy) {
493
+ if (!block) return false;
494
+ const blockPolicy = resolvedBlockPolicy(block.type, definition, policy);
495
+ return operationEnabled("duplicate", policy, blockPolicy) && canAddBlock(block.type, blocks, definition, policy);
496
+ }
497
+ function canDragBlock(block, definition, policy) {
498
+ if (!block) return false;
499
+ return operationEnabled("drag", policy, resolvedBlockPolicy(block.type, definition, policy));
500
+ }
501
+ function canApplyCanvasDocument(previous, next, getDefinition, policy) {
502
+ const previousById = new Map(previous.blocks.map((block) => [block.id, block]));
503
+ const nextById = new Map(next.blocks.map((block) => [block.id, block]));
504
+ for (const block of next.blocks) {
505
+ const previousBlock = previousById.get(block.id);
506
+ if (previousBlock && previousBlock.type !== block.type) return false;
507
+ }
508
+ for (const block of next.blocks) {
509
+ if (!previousById.has(block.id) && !canAddBlock(block.type, previous.blocks, getDefinition(block.type), policy)) return false;
510
+ }
511
+ for (const block of previous.blocks) {
512
+ if (!nextById.has(block.id) && !canDeleteBlock(block, previous.blocks, getDefinition(block.type), policy)) return false;
513
+ }
514
+ const types = new Set([...previous.blocks, ...next.blocks].map((block) => block.type));
515
+ for (const type of types) {
516
+ const definition = getDefinition(type);
517
+ const blockPolicy = resolvedBlockPolicy(type, definition, policy);
518
+ const nextCount = blockCount(next.blocks, type);
519
+ if (nextCount > maximumBlockInstances(blockPolicy) || nextCount < minimumBlockInstances(blockPolicy)) return false;
520
+ }
521
+ if (previous.blocks.length === next.blocks.length && previous.blocks.every((block) => nextById.has(block.id))) {
522
+ for (let index = 0; index < previous.blocks.length; index += 1) {
523
+ if (previous.blocks[index]?.id !== next.blocks[index]?.id && !canDragBlock(previous.blocks[index], getDefinition(previous.blocks[index].type), policy)) return false;
524
+ }
525
+ }
526
+ return true;
527
+ }
528
+
529
+ // src/editor/context/EditorContext.tsx
465
530
  import { jsx as jsx5 } from "react/jsx-runtime";
466
531
  function historyReducer(state, action) {
467
532
  if (action.type === "select") return { ...state, selectedBlockId: action.id };
@@ -476,7 +541,7 @@ function historyReducer(state, action) {
476
541
  if (!next) return state;
477
542
  return { ...state, ...next, past: [...state.past, { document: state.document, selectedBlockId: state.selectedBlockId }], future: state.future.slice(1) };
478
543
  }
479
- if (action.type === "saved") return { ...state, savedDocument: state.document };
544
+ if (action.type === "saved") return { ...state, savedDocument: action.document };
480
545
  const current = { document: state.document, selectedBlockId: state.selectedBlockId };
481
546
  return { ...state, document: action.document, selectedBlockId: action.selectedBlockId, past: [...state.past, current], future: [] };
482
547
  }
@@ -494,15 +559,17 @@ function defaultBlock(type, blocks, registry) {
494
559
  if (!definition) throw new Error(`Unknown PageDocument block type: ${type}`);
495
560
  return { id: uniqueBlockId(type, blocks), type, version: definition.version, props: definition.defaultProps, variant: definition.defaultVariant ?? "default", style: {} };
496
561
  }
497
- function EditorProvider({ initialDocument, registry, loadState = "ready", leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
562
+ function EditorProvider({ initialDocument, registry, policy, loadState = "ready", sessionState = "active", session, sessionMessage, leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
498
563
  const [history, dispatch] = useReducer(historyReducer, initialDocument, (document) => ({ document, selectedBlockId: document.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document }));
499
564
  const historyRef = useRef3(history);
500
565
  useEffect2(() => {
501
566
  historyRef.current = history;
502
567
  }, [history]);
503
568
  const [canvasSelectionRequest, setCanvasSelectionRequest] = useState2(null);
504
- const editable = loadState === "ready" || loadState === "success";
569
+ const [pendingDeleteBlockId, setPendingDeleteBlockId] = useState2(null);
570
+ const editable = (loadState === "ready" || loadState === "success") && sessionState === "active";
505
571
  const selectedBlock = history.document.blocks.find((block) => block.id === history.selectedBlockId) ?? null;
572
+ const pendingDeleteBlock = history.document.blocks.find((block) => block.id === pendingDeleteBlockId) ?? null;
506
573
  const replace = useCallback((document, selectedBlockId) => dispatch({ type: "replace", document, selectedBlockId }), []);
507
574
  const requestCanvasSelection = useCallback((id) => {
508
575
  if (editable) setCanvasSelectionRequest(id);
@@ -513,15 +580,23 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
513
580
  }, []);
514
581
  const updateFromCanvas = useCallback((document) => {
515
582
  const current = historyRef.current;
516
- if (!editable || JSON.stringify(document) === JSON.stringify(current.document)) return;
583
+ if (!editable || JSON.stringify(document) === JSON.stringify(current.document)) return false;
584
+ if (!canApplyCanvasDocument(current.document, document, (type) => registry?.getBlock(type), policy)) return false;
517
585
  replace(document, current.selectedBlockId);
518
- }, [editable, replace]);
586
+ return true;
587
+ }, [editable, policy, registry, replace]);
519
588
  const updateBlockProps = useCallback((id, props) => {
520
589
  if (!editable) return;
521
590
  const current = historyRef.current;
522
591
  const blocks = current.document.blocks.map((block) => block.id === id ? { ...block, props: { ...block.props, ...props } } : block);
523
592
  replace({ ...current.document, blocks }, id);
524
593
  }, [editable, replace]);
594
+ const updateBlockPresentation = useCallback((id, presentation) => {
595
+ if (!editable) return;
596
+ const current = historyRef.current;
597
+ const blocks = current.document.blocks.map((block) => block.id === id ? { ...block, ...presentation } : block);
598
+ replace({ ...current.document, blocks }, id);
599
+ }, [editable, replace]);
525
600
  useEffect2(() => {
526
601
  onDocumentChange?.(history.document);
527
602
  }, [history.document, onDocumentChange]);
@@ -555,27 +630,37 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
555
630
  const actionState = {
556
631
  canUndo: editable && history.past.length > 0,
557
632
  canRedo: editable && history.future.length > 0,
558
- canAdd: editable,
633
+ canAdd: editable && ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []].some((type) => canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy)),
559
634
  canEdit: editable && selectedBlock !== null,
560
- canDelete: editable && selectedBlock !== null,
561
- canDuplicate: editable && selectedBlock !== null,
562
- canReorder: editable && history.document.blocks.length > 1
635
+ canDelete: editable && canDeleteBlock(selectedBlock, history.document.blocks, registry?.getBlock(selectedBlock?.type ?? ""), policy),
636
+ canDuplicate: editable && canDuplicateBlock(selectedBlock, history.document.blocks, registry?.getBlock(selectedBlock?.type ?? ""), policy),
637
+ canReorder: editable && history.document.blocks.length > 1 && canDragBlock(selectedBlock, registry?.getBlock(selectedBlock?.type ?? ""), policy)
563
638
  };
564
639
  return {
565
640
  document: history.document,
566
641
  selectedBlockId: history.selectedBlockId,
567
642
  selectedBlock,
643
+ pendingDeleteBlock,
568
644
  canvasSelectionRequest,
569
645
  device: history.device,
570
646
  loadState,
647
+ sessionState,
648
+ session,
649
+ sessionMessage,
650
+ isEditable: editable,
571
651
  isDirty,
572
652
  actionState,
653
+ canAddBlock: (type) => editable && canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy),
654
+ canDragBlock: (id) => {
655
+ const block = history.document.blocks.find((item) => item.id === id);
656
+ return editable && canDragBlock(block, registry?.getBlock(block?.type ?? ""), policy);
657
+ },
573
658
  requestCanvasSelection,
574
659
  confirmCanvasSelection,
575
660
  updateFromCanvas,
576
661
  setDevice: (device) => dispatch({ type: "device", device }),
577
662
  addBlock: (type, beforeId) => {
578
- if (!editable) return null;
663
+ if (!editable || !canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy)) return null;
579
664
  const block = defaultBlock(type, history.document.blocks, registry);
580
665
  const blocks = [...history.document.blocks];
581
666
  const targetIndex = beforeId ? blocks.findIndex((item) => item.id === beforeId) : -1;
@@ -588,7 +673,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
588
673
  if (!editable) return;
589
674
  const index = history.document.blocks.findIndex((block2) => block2.id === id);
590
675
  const source = history.document.blocks[index];
591
- if (!source) return;
676
+ if (!canDuplicateBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
592
677
  const block = { ...source, id: uniqueBlockId(source.type, history.document.blocks), props: { ...source.props } };
593
678
  const blocks = [...history.document.blocks];
594
679
  blocks.splice(index + 1, 0, block);
@@ -597,7 +682,8 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
597
682
  deleteBlock: (id) => {
598
683
  if (!editable) return;
599
684
  const index = history.document.blocks.findIndex((block) => block.id === id);
600
- if (index < 0) return;
685
+ const source = history.document.blocks[index];
686
+ if (!canDeleteBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
601
687
  const blocks = history.document.blocks.filter((block) => block.id !== id);
602
688
  replace({ ...history.document, blocks }, blocks[index]?.id ?? blocks[index - 1]?.id ?? null);
603
689
  },
@@ -605,7 +691,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
605
691
  if (!editable) return;
606
692
  const from = history.document.blocks.findIndex((block) => block.id === id);
607
693
  const to = from + direction;
608
- if (from < 0 || to < 0 || to >= history.document.blocks.length) return;
694
+ if (from < 0 || to < 0 || to >= history.document.blocks.length || !canDragBlock(history.document.blocks[from], registry?.getBlock(history.document.blocks[from]?.type ?? ""), policy)) return;
609
695
  const blocks = [...history.document.blocks];
610
696
  [blocks[from], blocks[to]] = [blocks[to], blocks[from]];
611
697
  replace({ ...history.document, blocks }, id);
@@ -615,21 +701,38 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
615
701
  const source = history.document.blocks.find((block) => block.id === id);
616
702
  const withoutSource = history.document.blocks.filter((block) => block.id !== id);
617
703
  const targetIndex = withoutSource.findIndex((block) => block.id === beforeId);
618
- if (!source || targetIndex < 0) return;
704
+ if (!source || targetIndex < 0 || !canDragBlock(source, registry?.getBlock(source.type), policy)) return;
619
705
  const blocks = [...withoutSource];
620
706
  blocks.splice(targetIndex, 0, source);
621
707
  replace({ ...history.document, blocks }, id);
622
708
  },
623
709
  updateBlockProps,
710
+ updateBlockPresentation,
711
+ requestDeleteBlock: (id) => {
712
+ const block = history.document.blocks.find((item) => item.id === id);
713
+ if (editable && canDeleteBlock(block, history.document.blocks, registry?.getBlock(block?.type ?? ""), policy)) setPendingDeleteBlockId(id);
714
+ },
715
+ cancelDeleteBlock: () => setPendingDeleteBlockId(null),
716
+ confirmDeleteBlock: () => {
717
+ if (pendingDeleteBlockId) {
718
+ const id = pendingDeleteBlockId;
719
+ setPendingDeleteBlockId(null);
720
+ const index = history.document.blocks.findIndex((block) => block.id === id);
721
+ const source = history.document.blocks[index];
722
+ if (!canDeleteBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
723
+ const blocks = history.document.blocks.filter((block) => block.id !== id);
724
+ replace({ ...history.document, blocks }, blocks[index]?.id ?? blocks[index - 1]?.id ?? null);
725
+ }
726
+ },
624
727
  undo: () => {
625
728
  if (editable) dispatch({ type: "undo" });
626
729
  },
627
730
  redo: () => {
628
731
  if (editable) dispatch({ type: "redo" });
629
732
  },
630
- markSaved: () => dispatch({ type: "saved" })
733
+ markSaved: (document = historyRef.current.document) => dispatch({ type: "saved", document })
631
734
  };
632
- }, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, registry, replace, requestCanvasSelection, selectedBlock, updateBlockProps, updateFromCanvas]);
735
+ }, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, pendingDeleteBlock, pendingDeleteBlockId, policy, registry, replace, requestCanvasSelection, selectedBlock, session, sessionMessage, sessionState, updateBlockPresentation, updateBlockProps, updateFromCanvas]);
633
736
  return /* @__PURE__ */ jsx5(EditorContext.Provider, { value, children });
634
737
  }
635
738
  function useEditorContext() {
@@ -638,6 +741,26 @@ function useEditorContext() {
638
741
  return context;
639
742
  }
640
743
 
744
+ // src/editor/components/PageStatusCard.tsx
745
+ import { Badge as Badge2, InlineStack as InlineStack2, Text as Text2 } from "@shopify/polaris";
746
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
747
+ function PageStatusCard({ status, sessionState }) {
748
+ const publicationTone = status.publicationStatus === "published" ? "success" : "attention";
749
+ const sessionLabel = sessionState === "locked" ? `${status.editingBy ?? "\u5176\u4ED6\u4EBA"}\u6B63\u5728\u7F16\u8F91` : sessionState === "lost" ? "\u7F16\u8F91\u9501\u5DF2\u5931\u6548" : status.editingBy ? `${status.editingBy}\u6B63\u5728\u7F16\u8F91` : void 0;
750
+ return /* @__PURE__ */ jsxs4("div", { className: "pb-page-status-card", "data-testid": "page-status-card", "data-publication-status": status.publicationStatus, "data-session-state": sessionState, children: [
751
+ /* @__PURE__ */ jsxs4(InlineStack2, { gap: "150", blockAlign: "center", wrap: true, children: [
752
+ /* @__PURE__ */ jsx6(Badge2, { tone: publicationTone, children: status.publicationStatus === "published" ? "\u7EBF\u4E0A" : "\u672A\u53D1\u5E03" }),
753
+ status.draftLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.draftLabel }) : null,
754
+ status.publishedVersionLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.publishedVersionLabel }) : null,
755
+ sessionLabel ? /* @__PURE__ */ jsx6(Badge2, { tone: sessionState === "lost" ? "critical" : "attention", children: sessionLabel }) : null
756
+ ] }),
757
+ status.lastSavedAt ? /* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
758
+ "\u6700\u540E\u4FDD\u5B58\uFF1A",
759
+ status.lastSavedAt
760
+ ] }) : null
761
+ ] });
762
+ }
763
+
641
764
  // src/editor/i18n/admin.ts
642
765
  var messages = {
643
766
  "zh-CN": {
@@ -649,6 +772,10 @@ var messages = {
649
772
  redo: "\u6062\u590D",
650
773
  duplicate: "\u590D\u5236",
651
774
  remove: "\u5220\u9664",
775
+ confirmDeleteTitle: "\u786E\u8BA4\u5220\u9664\u533A\u5757\uFF1F",
776
+ confirmDeleteMessage: "\u5220\u9664\u540E\u53EF\u4F7F\u7528\u64A4\u9500\u6062\u590D\u3002",
777
+ cancel: "\u53D6\u6D88",
778
+ confirm: "\u786E\u8BA4\u5220\u9664",
652
779
  moveUp: "\u4E0A\u79FB",
653
780
  moveDown: "\u4E0B\u79FB",
654
781
  loading: "\u6B63\u5728\u52A0\u8F7D\u7F16\u8F91\u5668\u2026",
@@ -663,8 +790,21 @@ var messages = {
663
790
  publish: "\u53D1\u5E03",
664
791
  publishing: "\u6B63\u5728\u53D1\u5E03\u2026",
665
792
  saveFailed: "\u4FDD\u5B58\u8349\u7A3F\u5931\u8D25",
793
+ retrySave: "\u91CD\u8BD5\u4FDD\u5B58",
794
+ offline: "\u5F53\u524D\u79BB\u7EBF\uFF0C\u6062\u590D\u7F51\u7EDC\u540E\u5C06\u81EA\u52A8\u4FDD\u5B58",
666
795
  publishFailed: "\u53D1\u5E03\u5931\u8D25",
667
796
  published: "\u5DF2\u53D1\u5E03",
797
+ back: "\u8FD4\u56DE",
798
+ confirmLeaveTitle: "\u79BB\u5F00\u7F16\u8F91\u5668\uFF1F",
799
+ leave: "\u79BB\u5F00",
800
+ zoom: "\u7F29\u653E",
801
+ zoomAuto: "\u81EA\u52A8\u7F29\u653E",
802
+ preview: "\u6B63\u5F0F\u9884\u89C8",
803
+ addToStore: "\u6DFB\u52A0\u5230\u5E97\u94FA",
804
+ acquiringLock: "\u6B63\u5728\u83B7\u53D6\u7F16\u8F91\u9501\u2026",
805
+ locked: "\u8BE5\u9875\u9762\u6B63\u5728\u88AB\u7F16\u8F91",
806
+ lockLost: "\u7F16\u8F91\u9501\u5DF2\u5931\u6548",
807
+ readonly: "\u9875\u9762\u4E3A\u53EA\u8BFB\u72B6\u6001",
668
808
  leaveWarning: "\u4F60\u6709\u672A\u4FDD\u5B58\u7684\u66F4\u6539\u3002\u786E\u5B9A\u8981\u79BB\u5F00\u5417\uFF1F",
669
809
  selectBlock: "\u9009\u62E9\u4E00\u4E2A\u533A\u5757\u4EE5\u7F16\u8F91\u3002",
670
810
  desktop: "\u684C\u9762",
@@ -680,6 +820,10 @@ var messages = {
680
820
  redo: "Redo",
681
821
  duplicate: "Duplicate",
682
822
  remove: "Delete",
823
+ confirmDeleteTitle: "Delete this block?",
824
+ confirmDeleteMessage: "You can restore it with Undo after deleting.",
825
+ cancel: "Cancel",
826
+ confirm: "Delete block",
683
827
  moveUp: "Move up",
684
828
  moveDown: "Move down",
685
829
  loading: "Loading editor\u2026",
@@ -694,8 +838,21 @@ var messages = {
694
838
  publish: "Publish",
695
839
  publishing: "Publishing\u2026",
696
840
  saveFailed: "Could not save draft",
841
+ retrySave: "Retry save",
842
+ offline: "Offline \u2014 changes will save when connection returns",
697
843
  publishFailed: "Could not publish",
698
844
  published: "Published",
845
+ back: "Back",
846
+ confirmLeaveTitle: "Leave the editor?",
847
+ leave: "Leave",
848
+ zoom: "Zoom",
849
+ zoomAuto: "Auto zoom",
850
+ preview: "Preview",
851
+ addToStore: "Add to store",
852
+ acquiringLock: "Acquiring edit lock\u2026",
853
+ locked: "This page is being edited",
854
+ lockLost: "The edit lock has expired",
855
+ readonly: "This page is read-only",
699
856
  leaveWarning: "You have unsaved changes. Are you sure you want to leave?",
700
857
  selectBlock: "Select a block to edit it.",
701
858
  desktop: "Desktop",
@@ -718,7 +875,7 @@ function blockIdAtRelativeY(ids, relativeY) {
718
875
  }
719
876
 
720
877
  // src/editor/shell/PageDocumentEditorShell.tsx
721
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
878
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
722
879
  var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
723
880
  function blockLabel(block, registry) {
724
881
  return blockTypeLabel(block.type, registry);
@@ -728,27 +885,143 @@ function blockTypeLabel(type, registry) {
728
885
  if (type === "core.image") return "\u56FE\u7247";
729
886
  return registry?.getBlock(type)?.label ?? type;
730
887
  }
888
+ function validateDocumentBlocks(document, registry) {
889
+ return registry ? validatePageDocumentWithRegistry(document, registry) : [];
890
+ }
891
+ function useEditorSession(adapter, pageId) {
892
+ const [managed, setManaged] = useState3(() => adapter ? { state: "acquiring" } : { state: "active" });
893
+ useEffect3(() => {
894
+ let cancelled = false;
895
+ let session;
896
+ let heartbeatTimer;
897
+ const stopHeartbeat = () => {
898
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
899
+ heartbeatTimer = void 0;
900
+ };
901
+ if (!adapter) return;
902
+ void adapter.acquire({ pageId }).then((result) => {
903
+ if (cancelled) return;
904
+ if (result.state !== "active") {
905
+ setManaged({ state: result.state, message: result.message ?? (result.state === "locked" && result.editorName ? `${result.editorName} \u6B63\u5728\u7F16\u8F91` : void 0) });
906
+ return;
907
+ }
908
+ session = result.session;
909
+ setManaged({ state: "active", session });
910
+ if (!adapter.heartbeat) return;
911
+ const heartbeat = () => {
912
+ if (typeof navigator !== "undefined" && !navigator.onLine) return;
913
+ void adapter.heartbeat?.({ pageId, session: result.session }).catch(() => {
914
+ if (!cancelled) {
915
+ stopHeartbeat();
916
+ setManaged({ state: "lost", message: "\u7F16\u8F91\u9501\u5DF2\u5931\u6548\uFF0C\u8BF7\u6062\u590D\u7F51\u7EDC\u540E\u91CD\u65B0\u8FDB\u5165\u7F16\u8F91\u5668\u3002" });
917
+ }
918
+ });
919
+ };
920
+ heartbeatTimer = setInterval(heartbeat, adapter.heartbeatIntervalMs ?? 3e4);
921
+ }).catch(() => {
922
+ if (!cancelled) setManaged({ state: "lost", message: "\u65E0\u6CD5\u83B7\u53D6\u7F16\u8F91\u9501\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
923
+ });
924
+ return () => {
925
+ cancelled = true;
926
+ stopHeartbeat();
927
+ if (session) void adapter.release?.({ pageId, session });
928
+ };
929
+ }, [adapter, pageId]);
930
+ return managed;
931
+ }
731
932
  function PageDocumentEditorShell(props) {
732
- return /* @__PURE__ */ jsx6(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, loadState: props.loadState, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx6(PageDocumentEditor, { ...props }) });
933
+ const { sessionAdapter, initialDocument, onSessionStateChange } = props;
934
+ const managedSession = useEditorSession(sessionAdapter, initialDocument.pageId);
935
+ useEffect3(() => {
936
+ onSessionStateChange?.(managedSession.state);
937
+ }, [managedSession.state, onSessionStateChange]);
938
+ return /* @__PURE__ */ jsx7(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, policy: props.policy, loadState: props.loadState, sessionState: managedSession.state, session: managedSession.session, sessionMessage: managedSession.message, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx7(PageDocumentEditor, { ...props }) });
733
939
  }
734
- function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPublish }) {
940
+ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, pageStatus, onBack, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
735
941
  const editor = useEditorContext();
736
942
  const [blockView, setBlockView] = useState3("blocks");
737
943
  const [draggingLibraryType, setDraggingLibraryType] = useState3(null);
738
944
  const canvasFrameRef = useRef4(null);
739
945
  const [canvasMutationVersion, setCanvasMutationVersion] = useState3(0);
946
+ const [canvasResetVersion, setCanvasResetVersion] = useState3(0);
740
947
  const [request, setRequest] = useState3("idle");
741
948
  const [notice, setNotice] = useState3(null);
949
+ const [draftRevision, setDraftRevision] = useState3(initialDraftRevision);
950
+ const [isOnline, setIsOnline] = useState3(() => typeof navigator === "undefined" || navigator.onLine);
951
+ const [saveState, setSaveState] = useState3("saved");
952
+ const [zoom, setZoom] = useState3("auto");
953
+ const [confirmBack, setConfirmBack] = useState3(false);
742
954
  const i18n = createAdminI18n(adminLocale);
743
955
  const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
744
- const { confirmCanvasSelection, selectedBlockId, updateBlockProps } = editor;
956
+ const validationIssues = useMemo2(() => validateDocumentBlocks(editor.document, registry), [editor.document, registry]);
957
+ const { confirmCanvasSelection, selectedBlockId, updateBlockProps, updateBlockPresentation } = editor;
745
958
  const updateFromCanvasInput = useCallback2((id, props, preserveCanvasValue = false) => {
746
959
  if (preserveCanvasValue) setCanvasMutationVersion((version) => version + 1);
747
960
  updateBlockProps(id, props);
748
961
  }, [updateBlockProps]);
749
962
  const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry), [confirmCanvasSelection, registry, selectedBlockId, updateFromCanvasInput]);
750
- if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx6(EditorStatus, { state: editor.loadState });
751
- const blockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
963
+ useEffect3(() => {
964
+ const online = () => setIsOnline(true);
965
+ const offline = () => setIsOnline(false);
966
+ window.addEventListener("online", online);
967
+ window.addEventListener("offline", offline);
968
+ return () => {
969
+ window.removeEventListener("online", online);
970
+ window.removeEventListener("offline", offline);
971
+ };
972
+ }, []);
973
+ const resolvedSaveState = !isOnline && editor.isDirty ? "offline" : saveState === "offline" && editor.isDirty ? "dirty" : saveState === "saved" && editor.isDirty ? "dirty" : saveState;
974
+ const canPersistDraft = Boolean(draftPersistence || onSave);
975
+ const save = useCallback2(async () => {
976
+ if (!canPersistDraft || request !== "idle" || validationIssues.length > 0) return;
977
+ if (!isOnline) {
978
+ setSaveState("offline");
979
+ return;
980
+ }
981
+ const document = editor.document;
982
+ setRequest("saving");
983
+ setSaveState("saving");
984
+ setNotice(null);
985
+ try {
986
+ const result = draftPersistence ? await draftPersistence.saveDraft({ document, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document);
987
+ if (result?.revision !== void 0) setDraftRevision(result.revision);
988
+ editor.markSaved(document);
989
+ setSaveState("saved");
990
+ } catch {
991
+ setNotice("saveFailed");
992
+ setSaveState(typeof navigator !== "undefined" && !navigator.onLine ? "offline" : "failed");
993
+ } finally {
994
+ setRequest("idle");
995
+ }
996
+ }, [canPersistDraft, draftPersistence, draftRevision, editor, isOnline, onSave, request, validationIssues.length]);
997
+ useEffect3(() => {
998
+ if (!autoSave || !canPersistDraft || !editor.isDirty || resolvedSaveState !== "dirty" || !isOnline || request !== "idle" || validationIssues.length > 0 || editor.sessionState !== "active") return;
999
+ const timer = window.setTimeout(() => {
1000
+ void save();
1001
+ }, autoSaveDelayMs);
1002
+ return () => window.clearTimeout(timer);
1003
+ }, [autoSave, autoSaveDelayMs, canPersistDraft, editor.isDirty, editor.sessionState, isOnline, request, resolvedSaveState, save, validationIssues.length]);
1004
+ const publish = async () => {
1005
+ if (!publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline) return;
1006
+ const document = editor.document;
1007
+ setRequest("publishing");
1008
+ setNotice(null);
1009
+ try {
1010
+ if (publishAction) await publishAction.publish({ document, expectedRevision: draftRevision, session: editor.session, validationIssues });
1011
+ else await onPublish?.(document);
1012
+ editor.markSaved(document);
1013
+ setSaveState("saved");
1014
+ setNotice("published");
1015
+ } catch {
1016
+ setNotice("publishFailed");
1017
+ } finally {
1018
+ setRequest("idle");
1019
+ }
1020
+ };
1021
+ if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx7(EditorStatus, { state: editor.loadState });
1022
+ if (editor.sessionState !== "active") return /* @__PURE__ */ jsx7(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
1023
+ const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
1024
+ const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
752
1025
  const addFromLibrary = (type, beforeId) => {
753
1026
  const id = editor.addBlock(type, beforeId);
754
1027
  if (id) editor.requestCanvasSelection(id);
@@ -784,115 +1057,125 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
784
1057
  event.stopPropagation();
785
1058
  setDraggingLibraryType(null);
786
1059
  };
787
- const save = async () => {
788
- if (!onSave || request !== "idle") return;
789
- setRequest("saving");
790
- setNotice(null);
791
- try {
792
- await onSave(editor.document);
793
- editor.markSaved();
794
- } catch {
795
- setNotice("saveFailed");
796
- } finally {
797
- setRequest("idle");
798
- }
799
- };
800
- const publish = async () => {
801
- if (!onPublish || request !== "idle") return;
802
- setRequest("publishing");
803
- setNotice(null);
804
- try {
805
- await onPublish(editor.document);
806
- editor.markSaved();
807
- setNotice("published");
808
- } catch {
809
- setNotice("publishFailed");
810
- } finally {
811
- setRequest("idle");
812
- }
1060
+ const saveBadgeTone = resolvedSaveState === "failed" ? "critical" : resolvedSaveState === "offline" || resolvedSaveState === "dirty" ? "attention" : "success";
1061
+ const saveBadgeLabel = resolvedSaveState === "saving" ? i18n.t("saving") : resolvedSaveState === "failed" ? i18n.t("saveFailed") : resolvedSaveState === "offline" ? i18n.t("offline") : resolvedSaveState === "dirty" ? i18n.t("unsaved") : i18n.t("saved");
1062
+ const saveButtonLabel = request === "saving" ? i18n.t("saving") : resolvedSaveState === "failed" ? i18n.t("retrySave") : i18n.t("save");
1063
+ const requestBack = () => {
1064
+ if (!onBack) return;
1065
+ if (editor.isDirty || request === "saving") setConfirmBack(true);
1066
+ else onBack();
813
1067
  };
814
- return /* @__PURE__ */ jsx6(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => editor.updateFromCanvas(fromEngineData(data, editor.document, registry)), children: /* @__PURE__ */ jsxs4(Puck2.Layout, { children: [
815
- /* @__PURE__ */ jsx6(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion }),
816
- /* @__PURE__ */ jsxs4("div", { className: "pb-shell pb-shell--v04", "data-testid": "page-document-editor", "data-page-id": editor.document.pageId, "data-dirty": editor.isDirty, "data-editor-state": editor.loadState, children: [
817
- /* @__PURE__ */ jsx6("header", { className: "pb-header", children: /* @__PURE__ */ jsxs4(InlineStack2, { align: "space-between", blockAlign: "center", gap: "300", wrap: false, children: [
818
- /* @__PURE__ */ jsxs4("div", { className: "pb-page-title", children: [
819
- /* @__PURE__ */ jsx6(Text2, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
820
- /* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
821
- "PageDocument V",
822
- editor.document.schemaVersion,
823
- " \xB7 ",
824
- editor.document.target
1068
+ return /* @__PURE__ */ jsx7(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => {
1069
+ if (!editor.updateFromCanvas(fromEngineData(data, editor.document, registry))) setCanvasResetVersion((version) => version + 1);
1070
+ }, children: /* @__PURE__ */ jsxs5(Puck2.Layout, { children: [
1071
+ /* @__PURE__ */ jsx7(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion }),
1072
+ /* @__PURE__ */ jsxs5("div", { className: "pb-shell pb-shell--v04", "data-testid": "page-document-editor", "data-page-id": editor.document.pageId, "data-dirty": editor.isDirty, "data-editor-state": editor.loadState, "data-editor-session-state": editor.sessionState, "data-save-state": resolvedSaveState, children: [
1073
+ /* @__PURE__ */ jsx7("header", { className: "pb-header", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-content", children: [
1074
+ /* @__PURE__ */ jsxs5("div", { className: "pb-header-title-group", children: [
1075
+ onBack ? /* @__PURE__ */ jsx7(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
1076
+ /* @__PURE__ */ jsxs5("div", { className: "pb-page-title", children: [
1077
+ /* @__PURE__ */ jsx7(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
1078
+ /* @__PURE__ */ jsxs5(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: [
1079
+ "PageDocument V",
1080
+ editor.document.schemaVersion,
1081
+ " \xB7 ",
1082
+ editor.document.target
1083
+ ] }),
1084
+ pageStatus ? /* @__PURE__ */ jsx7(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
825
1085
  ] })
826
1086
  ] }),
827
- /* @__PURE__ */ jsxs4(InlineStack2, { gap: "150", blockAlign: "center", wrap: false, children: [
828
- /* @__PURE__ */ jsx6(Badge2, { tone: editor.isDirty ? "attention" : "success", children: editor.isDirty ? i18n.t("unsaved") : i18n.t("saved") }),
829
- /* @__PURE__ */ jsx6(Button2, { disabled: !onSave || !editor.isDirty || request !== "idle", onClick: () => void save(), children: request === "saving" ? i18n.t("saving") : i18n.t("save") }),
830
- /* @__PURE__ */ jsx6(Button2, { variant: "primary", disabled: !onPublish || request !== "idle", onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
831
- /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
832
- /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
833
- ] })
1087
+ /* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
1088
+ /* @__PURE__ */ jsx7(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx7(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }),
1089
+ /* @__PURE__ */ jsx7("div", { className: "pb-zoom-control", children: /* @__PURE__ */ jsx7(Select2, { label: i18n.t("zoom"), labelHidden: true, options: [{ label: i18n.t("zoomAuto"), value: "auto" }, { label: "50%", value: "50" }, { label: "70%", value: "70" }, { label: "100%", value: "100" }], value: zoom, onChange: (value) => setZoom(value) }) })
1090
+ ] }),
1091
+ /* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: false, children: [
1092
+ /* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
1093
+ /* @__PURE__ */ jsx7(Button2, { disabled: !canPersistDraft || !editor.isDirty || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void save(), children: saveButtonLabel }),
1094
+ onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
1095
+ onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
1096
+ /* @__PURE__ */ jsx7(Button2, { variant: "primary", disabled: !publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
1097
+ /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
1098
+ /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
1099
+ ] }) })
834
1100
  ] }) }),
835
- editor.loadState === "success" ? /* @__PURE__ */ jsx6(Banner, { tone: "success", children: i18n.t("success") }) : null,
836
- notice ? /* @__PURE__ */ jsx6(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
837
- /* @__PURE__ */ jsxs4("div", { className: "pb-workspace pb-workspace--document", children: [
838
- /* @__PURE__ */ jsxs4("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
839
- /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks", variant: "tertiary", onClick: () => setBlockView("blocks") }),
840
- /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline", variant: "tertiary", onClick: () => setBlockView("outline") })
1101
+ editor.loadState === "success" ? /* @__PURE__ */ jsx7(Banner, { tone: "success", children: i18n.t("success") }) : null,
1102
+ notice ? /* @__PURE__ */ jsx7(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
1103
+ validationIssues.length > 0 ? /* @__PURE__ */ jsx7(Banner, { tone: "critical", title: "\u533A\u5757\u5C5E\u6027\u672A\u901A\u8FC7\u6821\u9A8C", children: /* @__PURE__ */ jsx7("ul", { children: validationIssues.map((issue) => /* @__PURE__ */ jsxs5("li", { children: [
1104
+ issue.path,
1105
+ ": ",
1106
+ issue.message
1107
+ ] }, `${issue.path}-${issue.message}`)) }) }) : null,
1108
+ /* @__PURE__ */ jsxs5("div", { className: "pb-workspace pb-workspace--document", children: [
1109
+ /* @__PURE__ */ jsxs5("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
1110
+ /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks", variant: "tertiary", onClick: () => setBlockView("blocks") }),
1111
+ /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline", variant: "tertiary", onClick: () => setBlockView("outline") })
841
1112
  ] }),
842
- /* @__PURE__ */ jsxs4("aside", { className: "pb-left-panel", "aria-label": "PageDocument \u533A\u5757", children: [
843
- /* @__PURE__ */ jsx6(InlineStack2, { align: "space-between", blockAlign: "center", children: /* @__PURE__ */ jsx6(Text2, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }) }),
844
- blockView === "blocks" ? /* @__PURE__ */ jsx6("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs4("div", { className: `pb-document-block-row pb-document-block-row--library ${editor.selectedBlock?.type === type ? "pb-document-block-row--selected" : ""}`, "data-block-type": type, "data-selected": editor.selectedBlock?.type === type, role: "listitem", draggable: editor.actionState.canAdd, "aria-label": `${blockTypeLabel(type, registry)}\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0${editor.selectedBlock?.type === type ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onDragStart: (event) => {
1113
+ /* @__PURE__ */ jsxs5("aside", { className: "pb-left-panel", "aria-label": "PageDocument \u533A\u5757", children: [
1114
+ /* @__PURE__ */ jsx7(InlineStack3, { align: "space-between", blockAlign: "center", children: /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }) }),
1115
+ blockView === "blocks" ? /* @__PURE__ */ jsx7("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs5("div", { className: `pb-document-block-row pb-document-block-row--library ${editor.selectedBlock?.type === type ? "pb-document-block-row--selected" : ""}`, "data-block-type": type, "data-selected": editor.selectedBlock?.type === type, role: "listitem", draggable: editor.canAddBlock(type), "aria-disabled": !editor.canAddBlock(type), "aria-label": `${blockTypeLabel(type, registry)}\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0${editor.selectedBlock?.type === type ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onDragStart: (event) => {
1116
+ if (!editor.canAddBlock(type)) {
1117
+ event.preventDefault();
1118
+ return;
1119
+ }
845
1120
  event.dataTransfer.setData("application/x-page-document-block", type);
846
1121
  event.dataTransfer.effectAllowed = "copy";
847
1122
  setDraggingLibraryType(type);
848
1123
  }, onDragEnd: () => setDraggingLibraryType(null), children: [
849
- /* @__PURE__ */ jsxs4("span", { className: "pb-library-block-title", children: [
850
- /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
851
- /* @__PURE__ */ jsx6("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(DragHandleIcon2, {}) })
1124
+ /* @__PURE__ */ jsxs5("span", { className: "pb-library-block-title", children: [
1125
+ /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
1126
+ /* @__PURE__ */ jsx7("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(DragHandleIcon2, {}) })
852
1127
  ] }),
853
- /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: type })
854
- ] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx6(Text2, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx6("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs4("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => editor.requestCanvasSelection(block.id), children: [
855
- /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
856
- /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
1128
+ /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
1129
+ ] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx7("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs5("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => editor.requestCanvasSelection(block.id), children: [
1130
+ /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
1131
+ /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
857
1132
  ] }, block.id)) })
858
1133
  ] }),
859
- /* @__PURE__ */ jsxs4("main", { className: "pb-canvas-area", children: [
860
- /* @__PURE__ */ jsx6("div", { className: "pb-canvas-toolbar", children: /* @__PURE__ */ jsx6(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx6(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }) }),
861
- /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-stage", children: [
862
- /* @__PURE__ */ jsx6("div", { ref: canvasFrameRef, className: `pb-canvas-frame pb-canvas-frame--${editor.device}`, "data-device": editor.device, children: /* @__PURE__ */ jsx6(Puck2.Preview, {}) }),
863
- draggingLibraryType ? /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": "\u533A\u5757\u6295\u653E\u533A", onDragOver: (event) => event.preventDefault(), onDrop: dropFromLibrary, children: [
864
- "\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
865
- blockTypeLabel(draggingLibraryType, registry)
866
- ] }) : null,
867
- editor.selectedBlock ? /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
868
- /* @__PURE__ */ jsx6("span", { children: blockLabel(editor.selectedBlock, registry) }),
869
- /* @__PURE__ */ jsx6("span", { children: "Selected" })
870
- ] }) : null
871
- ] })
872
- ] }),
873
- /* @__PURE__ */ jsxs4("aside", { className: "pb-right-panel", "aria-label": "PageDocument \u5C5E\u6027", children: [
874
- /* @__PURE__ */ jsx6(Text2, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
875
- editor.selectedBlock ? /* @__PURE__ */ jsx6(DocumentInspector, { block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props) }) : /* @__PURE__ */ jsx6(Text2, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
1134
+ /* @__PURE__ */ jsx7("main", { className: "pb-canvas-area", children: /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-stage", children: [
1135
+ /* @__PURE__ */ jsx7("div", { ref: canvasFrameRef, className: `pb-canvas-frame pb-canvas-frame--${editor.device}${zoom === "auto" ? "" : ` pb-canvas-frame--zoom-${zoom}`}`, "data-device": editor.device, "data-zoom": zoom, children: /* @__PURE__ */ jsx7(Puck2.Preview, {}) }),
1136
+ draggingLibraryType ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": "\u533A\u5757\u6295\u653E\u533A", onDragOver: (event) => event.preventDefault(), onDrop: dropFromLibrary, children: [
1137
+ "\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
1138
+ blockTypeLabel(draggingLibraryType, registry)
1139
+ ] }) : null,
1140
+ editor.selectedBlock ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
1141
+ /* @__PURE__ */ jsx7("span", { children: blockLabel(editor.selectedBlock, registry) }),
1142
+ /* @__PURE__ */ jsx7("span", { children: "Selected" })
1143
+ ] }) : null
1144
+ ] }) }),
1145
+ /* @__PURE__ */ jsxs5("aside", { className: "pb-right-panel", "aria-label": "PageDocument \u5C5E\u6027", children: [
1146
+ /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
1147
+ editor.selectedBlock ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1148
+ /* @__PURE__ */ jsx7(DocumentInspector, { pageId: editor.document.pageId, assetPicker, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }),
1149
+ /* @__PURE__ */ jsx7(InspectorActions, { block: editor.selectedBlock, canDuplicate: editor.actionState.canDuplicate, canDelete: editor.actionState.canDelete, canMove: editor.actionState.canReorder, canMoveUp: editor.document.blocks[0]?.id !== editor.selectedBlock.id, canMoveDown: editor.document.blocks.at(-1)?.id !== editor.selectedBlock.id, i18n, onDuplicate: () => editor.duplicateBlock(editor.selectedBlock.id), onDelete: () => editor.requestDeleteBlock(editor.selectedBlock.id), onMove: (direction) => editor.moveBlock(editor.selectedBlock.id, direction) })
1150
+ ] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
876
1151
  ] })
877
- ] })
1152
+ ] }),
1153
+ editor.pendingDeleteBlock ? /* @__PURE__ */ jsx7(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
1154
+ confirmBack ? /* @__PURE__ */ jsx7(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
1155
+ setConfirmBack(false);
1156
+ onBack?.();
1157
+ } }) : null
878
1158
  ] })
879
1159
  ] }) });
880
1160
  }
881
- function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion }) {
1161
+ function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion, canvasResetVersion }) {
882
1162
  const puck = usePuck();
883
1163
  const lastSelectedId = useRef4(null);
884
1164
  const lastSyncedData = useRef4(null);
885
1165
  const lastCanvasMutationVersion = useRef4(0);
1166
+ const lastCanvasResetVersion = useRef4(0);
886
1167
  const serializedData = JSON.stringify(data);
887
1168
  useEffect3(() => {
888
- if (lastSyncedData.current === serializedData) return;
1169
+ const forceReset = canvasResetVersion > lastCanvasResetVersion.current;
1170
+ if (lastSyncedData.current === serializedData && !forceReset) return;
889
1171
  lastSyncedData.current = serializedData;
1172
+ if (forceReset) lastCanvasResetVersion.current = canvasResetVersion;
890
1173
  if (canvasMutationVersion > lastCanvasMutationVersion.current) {
891
1174
  lastCanvasMutationVersion.current = canvasMutationVersion;
892
1175
  return;
893
1176
  }
894
1177
  puck.dispatch({ type: "setData", data });
895
- }, [canvasMutationVersion, data, puck, serializedData]);
1178
+ }, [canvasMutationVersion, canvasResetVersion, data, puck, serializedData]);
896
1179
  useEffect3(() => {
897
1180
  if (!requestedBlockId) return;
898
1181
  const selector = puck.getSelectorForId(requestedBlockId);
@@ -911,39 +1194,79 @@ function EditorStatus({ state }) {
911
1194
  const i18n = createAdminI18n();
912
1195
  const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
913
1196
  const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
914
- return /* @__PURE__ */ jsx6("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx6(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
1197
+ return /* @__PURE__ */ jsx7("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx7(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
1198
+ }
1199
+ function EditorSessionStatus({ state, message }) {
1200
+ const i18n = createAdminI18n();
1201
+ const tone = state === "locked" ? "warning" : state === "readonly" ? "info" : "critical";
1202
+ const title = state === "acquiring" ? i18n.t("acquiringLock") : state === "locked" ? i18n.t("locked") : state === "readonly" ? i18n.t("readonly") : i18n.t("lockLost");
1203
+ return /* @__PURE__ */ jsx7("div", { className: "pb-editor-status", "data-testid": "page-document-editor-session-state", "data-editor-session-state": state, children: /* @__PURE__ */ jsx7(Banner, { tone, title, children: message ?? title }) });
915
1204
  }
916
1205
  function InspectorSection({ title, children, defaultOpen = true }) {
917
- return /* @__PURE__ */ jsxs4("details", { className: "pb-inspector-section", open: defaultOpen, children: [
918
- /* @__PURE__ */ jsxs4("summary", { children: [
919
- /* @__PURE__ */ jsx6("span", { children: title }),
920
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\u2304" })
1206
+ return /* @__PURE__ */ jsxs5("details", { className: "pb-inspector-section", open: defaultOpen, children: [
1207
+ /* @__PURE__ */ jsxs5("summary", { children: [
1208
+ /* @__PURE__ */ jsx7("span", { children: title }),
1209
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2304" })
921
1210
  ] }),
922
- /* @__PURE__ */ jsx6("div", { className: "pb-inspector-section__body", children })
1211
+ /* @__PURE__ */ jsx7("div", { className: "pb-inspector-section__body", children })
923
1212
  ] });
924
1213
  }
925
1214
  function InspectorTextControl({ label, value, control, disabled, onChange }) {
926
1215
  const stringValue = typeof value === "string" ? value : "";
927
- return /* @__PURE__ */ jsx6(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
1216
+ if (control === "color") return /* @__PURE__ */ jsx7("input", { "aria-label": label, type: "color", value: /^#[\da-f]{6}$/i.test(stringValue) ? stringValue : "#000000", disabled, onChange: (event) => onChange(event.currentTarget.value) });
1217
+ return /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
928
1218
  }
929
1219
  function InspectorField({ name, field, value, registry, disabled, onChange }) {
930
1220
  const label = field.label ?? name;
1221
+ const issues = validateFieldValue(field, value);
931
1222
  const Field = registry?.getField(field.field)?.component;
932
- return /* @__PURE__ */ jsxs4("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
933
- /* @__PURE__ */ jsxs4("div", { className: "pb-inspector-field__heading", children: [
934
- /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
935
- field.description ? /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
1223
+ return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
1224
+ /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field__heading", children: [
1225
+ /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
1226
+ field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
936
1227
  ] }),
937
- field.control ? /* @__PURE__ */ jsx6(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx6(Field, { value, onChange }) : null
1228
+ field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
1229
+ issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
938
1230
  ] });
939
1231
  }
1232
+ function InspectorActions({ block, canDuplicate, canDelete, canMove, canMoveUp, canMoveDown, i18n, onDuplicate, onDelete, onMove }) {
1233
+ return /* @__PURE__ */ jsx7("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1234
+ /* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
1235
+ /* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
1236
+ /* @__PURE__ */ jsx7(Button2, { disabled: !canDuplicate, onClick: onDuplicate, children: i18n.t("duplicate") }),
1237
+ /* @__PURE__ */ jsx7(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
1238
+ ] }) });
1239
+ }
1240
+ function DeleteConfirmation({ block, config, i18n, onCancel, onConfirm }) {
1241
+ return /* @__PURE__ */ jsx7("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs5("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-delete-confirmation-title", children: [
1242
+ /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
1243
+ /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
1244
+ /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1245
+ /* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
1246
+ /* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
1247
+ ] })
1248
+ ] }) });
1249
+ }
1250
+ function LeaveConfirmation({ i18n, onCancel, onConfirm }) {
1251
+ return /* @__PURE__ */ jsx7("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs5("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-leave-confirmation-title", children: [
1252
+ /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
1253
+ /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
1254
+ /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1255
+ /* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
1256
+ /* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
1257
+ ] })
1258
+ ] }) });
1259
+ }
940
1260
  function inspectorFieldConfig(name, field) {
941
1261
  const key = name.toLowerCase();
942
1262
  const group = /(?:href|url)/.test(key) ? "Links" : /(?:default|shipment|query|hide)/.test(key) ? "Tracking settings" : /(?:id|variant|theme)/.test(key) ? "Advanced" : "Content";
943
1263
  const description = field.description ?? (field.control === "textarea" ? "\u9002\u5408\u8F83\u957F\u6216\u591A\u884C\u7684\u5C55\u793A\u6587\u6848\u3002" : field.control === "url" ? "\u4F7F\u7528\u7AD9\u5185\u76F8\u5BF9\u8DEF\u5F84\u6216 HTTPS \u5730\u5740\u3002" : key.includes("shipment") ? "\u591A\u4E2A\u5305\u88F9\u6807\u7B7E\u4F7F\u7528 | \u5206\u9694\u3002" : key.includes("default") ? "\u4EC5\u7528\u4E8E\u7F16\u8F91\u5668\u548C\u7A7A\u72B6\u6001\u9884\u89C8\u3002" : void 0);
944
1264
  return { ...field, group: field.group ?? group, description };
945
1265
  }
946
- function DocumentInspector({ block, registry, disabled, onChange }) {
1266
+ var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
1267
+ function DocumentInspector({ pageId, assetPicker, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
1268
+ const [assetError, setAssetError] = useState3(null);
1269
+ const [selectingAsset, setSelectingAsset] = useState3(false);
947
1270
  const definition = registry?.getBlock(block.type);
948
1271
  const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
949
1272
  const [name, field] = entry;
@@ -952,21 +1275,43 @@ function DocumentInspector({ block, registry, disabled, onChange }) {
952
1275
  (groups[group] ??= []).push([name, configuredField]);
953
1276
  return groups;
954
1277
  }, {}) : {};
955
- return /* @__PURE__ */ jsxs4("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
956
- /* @__PURE__ */ jsxs4("header", { className: "pb-inspector__header", children: [
957
- /* @__PURE__ */ jsx6(Badge2, { children: block.type }),
958
- /* @__PURE__ */ jsxs4("div", { children: [
959
- /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
960
- /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
1278
+ return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
1279
+ /* @__PURE__ */ jsxs5("header", { className: "pb-inspector__header", children: [
1280
+ /* @__PURE__ */ jsx7(Badge3, { children: block.type }),
1281
+ /* @__PURE__ */ jsxs5("div", { children: [
1282
+ /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
1283
+ /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
961
1284
  ] })
962
1285
  ] }),
963
- block.type === "core.text" ? /* @__PURE__ */ jsx6(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx6(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, onChange: (content) => onChange({ content }) }) }) : null,
964
- block.type === "core.image" ? /* @__PURE__ */ jsxs4(InspectorSection, { title: "Image", children: [
965
- /* @__PURE__ */ jsx6(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, onChange: (src) => onChange({ src }) }),
966
- /* @__PURE__ */ jsx6(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, onChange: (alt) => onChange({ alt }) })
1286
+ block.type === "core.text" ? /* @__PURE__ */ jsx7(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx7(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, onChange: (content) => onChange({ content }) }) }) : null,
1287
+ block.type === "core.image" ? /* @__PURE__ */ jsxs5(InspectorSection, { title: "Image", children: [
1288
+ /* @__PURE__ */ jsx7(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, onChange: (src) => onChange({ src }) }),
1289
+ /* @__PURE__ */ jsx7(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, onChange: (alt) => onChange({ alt }) }),
1290
+ assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1291
+ /* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
1292
+ setSelectingAsset(true);
1293
+ setAssetError(null);
1294
+ try {
1295
+ const asset = await assetPicker.selectAsset({ pageId, blockId: block.id, current: { id: typeof block.props.assetId === "string" ? block.props.assetId : void 0, url: typeof block.props.src === "string" ? block.props.src : void 0, alt: typeof block.props.alt === "string" ? block.props.alt : void 0 } });
1296
+ if (asset) onChange({ ...block.props, assetId: asset.id, src: asset.url, alt: asset.alt ?? (typeof block.props.alt === "string" ? block.props.alt : "") });
1297
+ } catch {
1298
+ setAssetError("\u65E0\u6CD5\u9009\u62E9\u7D20\u6750\uFF0C\u8BF7\u91CD\u8BD5\u3002");
1299
+ } finally {
1300
+ setSelectingAsset(false);
1301
+ }
1302
+ })(), children: selectingAsset ? "\u6B63\u5728\u9009\u62E9\u7D20\u6750\u2026" : "\u9009\u62E9\u7D20\u6750" }),
1303
+ assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
1304
+ ] }) : null
967
1305
  ] }) : null,
968
- Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx6(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx6(InspectorField, { name, field, value: block.props[name], registry, disabled, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
969
- definition ? /* @__PURE__ */ jsx6("p", { className: "pb-inspector__hint", children: "\u753B\u5E03\u4E2D\u5E26\u865A\u7EBF\u8FB9\u6846\u7684\u5185\u5BB9\u53EF\u76F4\u63A5\u7F16\u8F91\u3002" }) : null
1306
+ Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx7(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx7(InspectorField, { name, field, value: block.props[name], registry, disabled, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
1307
+ appearanceControls && definition?.variants?.length ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u5916\u89C2", children: /* @__PURE__ */ jsx7(Select2, { label: "\u6837\u5F0F\u53D8\u4F53", options: definition.variants.map((variant) => ({ label: variant.label, value: variant.id })), value: block.variant, disabled, onChange: (variant) => onPresentationChange({ variant, style: block.style }) }) }) : null,
1308
+ appearanceControls && definition ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u6837\u5F0F\u8986\u76D6", defaultOpen: false, children: appearanceTokens.map(([token, label]) => /* @__PURE__ */ jsx7(TextField, { label, value: block.style[token] ?? "", placeholder: "\u7EE7\u627F\u9875\u9762\u6216\u6A21\u677F\u8BBE\u7F6E", autoComplete: "off", disabled, onChange: (value) => {
1309
+ const style = { ...block.style };
1310
+ if (value.trim()) style[token] = value;
1311
+ else delete style[token];
1312
+ onPresentationChange({ variant: block.variant, style });
1313
+ } }, token)) }) : null,
1314
+ definition ? /* @__PURE__ */ jsx7("p", { className: "pb-inspector__hint", children: "\u753B\u5E03\u4E2D\u5E26\u865A\u7EBF\u8FB9\u6846\u7684\u5185\u5BB9\u53EF\u76F4\u63A5\u7F16\u8F91\u3002" }) : null
970
1315
  ] });
971
1316
  }
972
1317
  export {
@@ -975,6 +1320,7 @@ export {
975
1320
  ExtensionRegistry,
976
1321
  ExtensionRegistryError,
977
1322
  PageDocumentEditorShell,
1323
+ PageStatusCard,
978
1324
  TemplateRegistry,
979
1325
  WebRenderer,
980
1326
  createAdminI18n,
@@ -982,12 +1328,18 @@ export {
982
1328
  createPageDocument,
983
1329
  createTemplateRegistry,
984
1330
  fromEngineData,
1331
+ maximumBlockInstances,
1332
+ mergeBlockPolicies,
985
1333
  mergeThemeTokens,
986
1334
  migratePageDocument,
1335
+ minimumBlockInstances,
987
1336
  normalizeThemeTokens,
988
1337
  systemThemeTokens,
989
1338
  toEngineData,
990
1339
  toThemeStyle,
991
1340
  useEditorContext,
992
- validatePageDocument
1341
+ validateBlockPolicy,
1342
+ validateFieldValue,
1343
+ validatePageDocument,
1344
+ validatePageDocumentWithRegistry
993
1345
  };