@standhigher/puck-page-builder 0.12.5 → 0.12.6

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
@@ -260,13 +260,7 @@ function EditorShell({ initialState, iframe = true }) {
260
260
  ] }),
261
261
  /* @__PURE__ */ jsxs2("main", { className: "pb-canvas-area", children: [
262
262
  /* @__PURE__ */ jsx2(CanvasToolbar, { device: state.device, zoom: state.zoom, onDevice: (device) => update({ device }), onZoom: (zoom) => update({ zoom }) }),
263
- /* @__PURE__ */ jsxs2("div", { className: "pb-canvas-stage", children: [
264
- /* @__PURE__ */ jsx2("div", { className: `pb-canvas-frame pb-canvas-frame--${state.device} pb-canvas-frame--zoom-${state.zoom}`, "data-device": state.device, "data-zoom": state.zoom, children: /* @__PURE__ */ jsx2(Puck.Preview, {}) }),
265
- selectedBlock ? /* @__PURE__ */ jsxs2("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${selectedBlock.label}`, children: [
266
- /* @__PURE__ */ jsx2("span", { children: selectedBlock.label }),
267
- /* @__PURE__ */ jsx2("span", { children: "Selected" })
268
- ] }) : null
269
- ] }),
263
+ /* @__PURE__ */ jsx2("div", { className: "pb-canvas-stage", children: /* @__PURE__ */ jsx2("div", { className: `pb-canvas-frame pb-canvas-frame--${state.device} pb-canvas-frame--zoom-${state.zoom}`, "data-device": state.device, "data-zoom": state.zoom, children: /* @__PURE__ */ jsx2(Puck.Preview, {}) }) }),
270
264
  !state.isLeftRailOpen || !state.isRightPanelOpen ? /* @__PURE__ */ jsxs2("div", { className: "pb-collapsed-actions", children: [
271
265
  !state.isLeftRailOpen ? /* @__PURE__ */ jsx2(Button, { onClick: () => update({ isLeftRailOpen: true }), children: "\u6253\u5F00\u533A\u5757\u9762\u677F" }) : null,
272
266
  !state.isRightPanelOpen ? /* @__PURE__ */ jsx2(Button, { onClick: () => update({ isRightPanelOpen: true }), children: "\u6253\u5F00\u5C5E\u6027\u9762\u677F" }) : null
@@ -353,22 +347,72 @@ function PropertyPanel({
353
347
  import { Puck as Puck2, usePuck } from "@puckeditor/core";
354
348
  import { Badge as Badge3, Banner, Button as Button2, ButtonGroup as ButtonGroup2, Frame, InlineStack as InlineStack3, Select as Select2, Text as Text3, TextField, Toast } from "@shopify/polaris";
355
349
  import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, ProductIcon, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2, XIcon as XIcon2 } from "@shopify/polaris-icons";
356
- import { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
350
+ import { useCallback as useCallback2, useEffect as useEffect4, useLayoutEffect, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
357
351
 
358
- // src/adapters/puck/canvas-extension-block.tsx
352
+ // src/adapters/puck/use-canvas-editing.ts
359
353
  import { registerOverlayPortal } from "@puckeditor/core";
360
354
  import { useEffect, useRef as useRef2 } from "react";
355
+ var editingControlSelector = "input, textarea, select, button, a[href], [contenteditable]:not([contenteditable=false]), [role=textbox]";
356
+ function useCanvasEditing(active) {
357
+ const ref = useRef2(null);
358
+ useEffect(() => {
359
+ const root = ref.current;
360
+ if (!active || !root) return;
361
+ const cleanupPortal = registerOverlayPortal(root, { disableDragOnFocus: false });
362
+ const protectEditingControl = (event) => {
363
+ const target = event.target;
364
+ const control = typeof target?.closest === "function" ? target.closest(editingControlSelector) : null;
365
+ if (control && root.contains(control)) event.stopPropagation();
366
+ };
367
+ root.addEventListener("pointerdown", protectEditingControl, { capture: true });
368
+ return () => {
369
+ root.removeEventListener("pointerdown", protectEditingControl, { capture: true });
370
+ cleanupPortal?.();
371
+ };
372
+ }, [active]);
373
+ return ref;
374
+ }
375
+
376
+ // src/adapters/puck/canvas-extension-block.tsx
361
377
  import { jsx as jsx3 } from "react/jsx-runtime";
362
- function CanvasExtensionBlock({ active, children, label, onSelect }) {
363
- const editorRef = useRef2(null);
364
- useEffect(() => active ? registerOverlayPortal(editorRef.current, { disableDrag: true }) : void 0, [active]);
365
- return /* @__PURE__ */ jsx3("div", { ref: editorRef, className: "pb-document-canvas__extension", "aria-label": "Select " + label + " in canvas", role: "group", tabIndex: 0, onClick: onSelect, onKeyDown: (event) => {
366
- if (event.key === "Enter" || event.key === " ") onSelect();
378
+ function CanvasExtensionBlock({ active, blockId, children, label, onSelect }) {
379
+ const editorRef = useCanvasEditing(active);
380
+ return /* @__PURE__ */ jsx3("div", { ref: editorRef, className: "pb-document-canvas__extension", "data-page-document-block-id": blockId, "aria-label": "Select " + label + " in canvas", role: "group", tabIndex: 0, onClick: onSelect, onKeyDown: (event) => {
381
+ if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
382
+ event.preventDefault();
383
+ onSelect();
384
+ }
367
385
  }, children });
368
386
  }
369
387
 
388
+ // src/adapters/puck/canvas-core-blocks.tsx
389
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
390
+ function CanvasTextBlock({ id, content, editable, onSelect, onPropsChange }) {
391
+ const ref = useCanvasEditing(editable);
392
+ return /* @__PURE__ */ jsx4("section", { ref, className: "pb-document-canvas__text", "data-page-document-block-id": id, "aria-label": "Select Text in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
393
+ if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
394
+ event.preventDefault();
395
+ onSelect(id);
396
+ }
397
+ }, children: /* @__PURE__ */ jsx4("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
398
+ if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" }, true);
399
+ }, children: content }) });
400
+ }
401
+ function CanvasImageBlock({ id, src, alt, editable, onSelect, onPropsChange }) {
402
+ const ref = useCanvasEditing(editable);
403
+ return /* @__PURE__ */ jsxs3("figure", { ref, className: "pb-document-canvas__image", "data-page-document-block-id": id, "aria-label": "Select Image in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
404
+ if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
405
+ event.preventDefault();
406
+ onSelect(id);
407
+ }
408
+ }, children: [
409
+ /* @__PURE__ */ jsx4("img", { src, alt }),
410
+ editable ? /* @__PURE__ */ jsx4("input", { "aria-label": "\u753B\u5E03\u56FE\u7247 URL", value: src, onChange: (event) => onPropsChange(id, { src: event.currentTarget.value }, true), onClick: (event) => event.stopPropagation() }) : null
411
+ ] });
412
+ }
413
+
370
414
  // src/adapters/puck/page-document-config.tsx
371
- import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
415
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
372
416
  function withBlockPermissions(component, type, resolvePermissions) {
373
417
  if (!resolvePermissions) return component;
374
418
  return {
@@ -386,11 +430,11 @@ function withBlockPermissions(component, type, resolvePermissions) {
386
430
  function canvasFieldFallback({ block, props, registry, onPropsChange }) {
387
431
  const fields = Object.entries(block.fields);
388
432
  if (!fields.length) return null;
389
- return /* @__PURE__ */ jsx4("div", { "aria-label": "Edit " + block.label + " values in canvas", onClick: (event) => event.stopPropagation(), style: { display: "grid", gap: 8, marginTop: 12, padding: 12, border: "1px dashed #94a3b8", borderRadius: 6, background: "#f8fafc" }, children: fields.map(([name, field]) => {
433
+ return /* @__PURE__ */ jsx5("div", { "aria-label": "Edit " + block.label + " values in canvas", onClick: (event) => event.stopPropagation(), style: { display: "grid", gap: 8, marginTop: 12, padding: 12, border: "1px dashed #94a3b8", borderRadius: 6, background: "#f8fafc" }, children: fields.map(([name, field]) => {
390
434
  const Field = registry?.getField(field.field)?.component;
391
- return Field ? /* @__PURE__ */ jsxs3("label", { style: { display: "grid", gap: 4, fontSize: 12, fontWeight: 600 }, children: [
435
+ return Field ? /* @__PURE__ */ jsxs4("label", { style: { display: "grid", gap: 4, fontSize: 12, fontWeight: 600 }, children: [
392
436
  field.label ?? name,
393
- /* @__PURE__ */ jsx4(Field, { value: props[name], onChange: (value) => onPropsChange({ [name]: value }) })
437
+ /* @__PURE__ */ jsx5(Field, { value: props[name], onChange: (value) => onPropsChange({ [name]: value }) })
394
438
  ] }, name) : null;
395
439
  }) });
396
440
  }
@@ -403,8 +447,8 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
403
447
  const id = typeof props.id === "string" ? props.id : `unknown-${block.type}`;
404
448
  const EditorRenderer = block.render.editor;
405
449
  const active = id === selectedBlockId;
406
- return /* @__PURE__ */ jsx4(CanvasExtensionBlock, { active, label: block.label, onSelect: () => onSelect(id), children: EditorRenderer ? /* @__PURE__ */ jsx4(EditorRenderer, { ...props, blockId: id, selected: active, onPropsChange: (next) => onPropsChange(id, next) }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
407
- /* @__PURE__ */ jsx4(BlockRenderer, { ...props }),
450
+ return /* @__PURE__ */ jsx5(CanvasExtensionBlock, { active, blockId: id, label: block.label, onSelect: () => onSelect(id), children: EditorRenderer ? /* @__PURE__ */ jsx5(EditorRenderer, { ...props, blockId: id, selected: active, onPropsChange: (next) => onPropsChange(id, next) }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
451
+ /* @__PURE__ */ jsx5(BlockRenderer, { ...props }),
408
452
  active ? canvasFieldFallback({ block, props, registry, onPropsChange: (next) => onPropsChange(id, next) }) : null
409
453
  ] }) });
410
454
  }
@@ -417,11 +461,7 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
417
461
  const id = typeof props.id === "string" ? props.id : "unknown-text";
418
462
  const content = typeof props.content === "string" ? props.content : "";
419
463
  const editable = id === selectedBlockId;
420
- return /* @__PURE__ */ jsx4("section", { className: "pb-document-canvas__text", "data-page-document-block-id": id, "aria-label": "Select Text in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
421
- if (event.key === "Enter" || event.key === " ") onSelect(id);
422
- }, children: /* @__PURE__ */ jsx4("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
423
- if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" }, true);
424
- }, children: content }) });
464
+ return /* @__PURE__ */ jsx5(CanvasTextBlock, { id, content, editable, onSelect, onPropsChange });
425
465
  }
426
466
  }, "core.text", resolveBlockPermissions),
427
467
  Image: withBlockPermissions({
@@ -430,12 +470,7 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
430
470
  const src = typeof props.src === "string" ? props.src : "";
431
471
  const alt = typeof props.alt === "string" ? props.alt : "";
432
472
  const editable = id === selectedBlockId;
433
- return /* @__PURE__ */ jsxs3("figure", { className: "pb-document-canvas__image", "data-page-document-block-id": id, "aria-label": "Select Image in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
434
- if (event.key === "Enter" || event.key === " ") onSelect(id);
435
- }, children: [
436
- /* @__PURE__ */ jsx4("img", { src, alt }),
437
- editable ? /* @__PURE__ */ jsx4("input", { "aria-label": "\u753B\u5E03\u56FE\u7247 URL", value: src, onChange: (event) => onPropsChange(id, { src: event.currentTarget.value }, true), onClick: (event) => event.stopPropagation() }) : null
438
- ] });
473
+ return /* @__PURE__ */ jsx5(CanvasImageBlock, { id, src, alt, editable, onSelect, onPropsChange });
439
474
  }
440
475
  }, "core.image", resolveBlockPermissions),
441
476
  ...extensionComponents
@@ -454,10 +489,10 @@ function toEngineBlock(block, registry) {
454
489
  if (!type) throw new Error(`V0.2 \u4E0D\u652F\u6301\u8F6C\u6362\u533A\u5757\u7C7B\u578B\uFF1A${block.type}`);
455
490
  return { type, props: { id: block.id, ...block.props } };
456
491
  }
457
- function toEngineData(document, registry) {
492
+ function toEngineData(document2, registry) {
458
493
  return {
459
- root: { props: { ...document.root } },
460
- content: document.blocks.map((block) => toEngineBlock(block, registry))
494
+ root: { props: { ...document2.root } },
495
+ content: document2.blocks.map((block) => toEngineBlock(block, registry))
461
496
  };
462
497
  }
463
498
  function fromEngineData(data, base, registry) {
@@ -544,7 +579,7 @@ function canApplyCanvasDocument(previous, next, getDefinition, policy) {
544
579
  }
545
580
 
546
581
  // src/editor/context/EditorContext.tsx
547
- import { jsx as jsx5 } from "react/jsx-runtime";
582
+ import { jsx as jsx6 } from "react/jsx-runtime";
548
583
  function historyReducer(state, action) {
549
584
  if (action.type === "select") return { ...state, selectedBlockId: action.id };
550
585
  if (action.type === "device") return { ...state, device: action.device };
@@ -577,7 +612,7 @@ function defaultBlock(type, blocks, registry) {
577
612
  return { id: uniqueBlockId(type, blocks), type, version: definition.version, props: definition.defaultProps, variant: definition.defaultVariant ?? "default", style: {} };
578
613
  }
579
614
  function EditorProvider({ initialDocument, registry, policy, loadState = "ready", sessionState = "active", session, sessionMessage, leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
580
- const [history, dispatch] = useReducer(historyReducer, initialDocument, (document) => ({ document, selectedBlockId: document.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document }));
615
+ const [history, dispatch] = useReducer(historyReducer, initialDocument, (document2) => ({ document: document2, selectedBlockId: document2.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document2 }));
581
616
  const historyRef = useRef3(history);
582
617
  useEffect2(() => {
583
618
  historyRef.current = history;
@@ -587,7 +622,7 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
587
622
  const editable = (loadState === "ready" || loadState === "success") && sessionState === "active";
588
623
  const selectedBlock = history.document.blocks.find((block) => block.id === history.selectedBlockId) ?? null;
589
624
  const pendingDeleteBlock = history.document.blocks.find((block) => block.id === pendingDeleteBlockId) ?? null;
590
- const replace = useCallback((document, selectedBlockId) => dispatch({ type: "replace", document, selectedBlockId }), []);
625
+ const replace = useCallback((document2, selectedBlockId) => dispatch({ type: "replace", document: document2, selectedBlockId }), []);
591
626
  const requestCanvasSelection = useCallback((id) => {
592
627
  if (editable) setCanvasSelectionRequest(id);
593
628
  }, [editable]);
@@ -595,11 +630,11 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
595
630
  setCanvasSelectionRequest(null);
596
631
  dispatch({ type: "select", id });
597
632
  }, []);
598
- const updateFromCanvas = useCallback((document) => {
633
+ const updateFromCanvas = useCallback((document2) => {
599
634
  const current = historyRef.current;
600
- if (!editable || JSON.stringify(document) === JSON.stringify(current.document)) return false;
601
- if (!canApplyCanvasDocument(current.document, document, (type) => registry?.getBlock(type), policy)) return false;
602
- replace(document, current.selectedBlockId);
635
+ if (!editable || JSON.stringify(document2) === JSON.stringify(current.document)) return false;
636
+ if (!canApplyCanvasDocument(current.document, document2, (type) => registry?.getBlock(type), policy)) return false;
637
+ replace(document2, current.selectedBlockId);
603
638
  return true;
604
639
  }, [editable, policy, registry, replace]);
605
640
  const updateBlockProps = useCallback((id, props) => {
@@ -755,10 +790,10 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
755
790
  redo: () => {
756
791
  if (editable) dispatch({ type: "redo" });
757
792
  },
758
- markSaved: (document = historyRef.current.document) => dispatch({ type: "saved", document })
793
+ markSaved: (document2 = historyRef.current.document) => dispatch({ type: "saved", document: document2 })
759
794
  };
760
795
  }, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, pendingDeleteBlock, pendingDeleteBlockId, policy, registry, replace, requestCanvasSelection, selectedBlock, session, sessionMessage, sessionState, updateBlockPresentation, updateBlockProps, updateFromCanvas]);
761
- return /* @__PURE__ */ jsx5(EditorContext.Provider, { value, children });
796
+ return /* @__PURE__ */ jsx6(EditorContext.Provider, { value, children });
762
797
  }
763
798
  function useEditorContext() {
764
799
  const context = useContext(EditorContext);
@@ -768,18 +803,18 @@ function useEditorContext() {
768
803
 
769
804
  // src/editor/components/PageStatusCard.tsx
770
805
  import { Badge as Badge2, InlineStack as InlineStack2, Text as Text2 } from "@shopify/polaris";
771
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
806
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
772
807
  function PageStatusCard({ status, sessionState }) {
773
808
  const publicationTone = status.publicationStatus === "published" ? "success" : "attention";
774
809
  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;
775
- return /* @__PURE__ */ jsxs4("div", { className: "pb-page-status-card", "data-testid": "page-status-card", "data-publication-status": status.publicationStatus, "data-session-state": sessionState, children: [
776
- /* @__PURE__ */ jsxs4(InlineStack2, { gap: "150", blockAlign: "center", wrap: true, children: [
777
- /* @__PURE__ */ jsx6(Badge2, { tone: publicationTone, children: status.publicationStatus === "published" ? "\u7EBF\u4E0A" : "\u672A\u53D1\u5E03" }),
778
- status.draftLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.draftLabel }) : null,
779
- status.publishedVersionLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.publishedVersionLabel }) : null,
780
- sessionLabel ? /* @__PURE__ */ jsx6(Badge2, { tone: sessionState === "lost" ? "critical" : "attention", children: sessionLabel }) : null
810
+ return /* @__PURE__ */ jsxs5("div", { className: "pb-page-status-card", "data-testid": "page-status-card", "data-publication-status": status.publicationStatus, "data-session-state": sessionState, children: [
811
+ /* @__PURE__ */ jsxs5(InlineStack2, { gap: "150", blockAlign: "center", wrap: true, children: [
812
+ /* @__PURE__ */ jsx7(Badge2, { tone: publicationTone, children: status.publicationStatus === "published" ? "\u7EBF\u4E0A" : "\u672A\u53D1\u5E03" }),
813
+ status.draftLabel ? /* @__PURE__ */ jsx7(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.draftLabel }) : null,
814
+ status.publishedVersionLabel ? /* @__PURE__ */ jsx7(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.publishedVersionLabel }) : null,
815
+ sessionLabel ? /* @__PURE__ */ jsx7(Badge2, { tone: sessionState === "lost" ? "critical" : "attention", children: sessionLabel }) : null
781
816
  ] }),
782
- status.lastSavedAt ? /* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
817
+ status.lastSavedAt ? /* @__PURE__ */ jsxs5(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
783
818
  "\u6700\u540E\u4FDD\u5B58\uFF1A",
784
819
  status.lastSavedAt
785
820
  ] }) : null
@@ -924,17 +959,197 @@ function createAdminI18n(locale) {
924
959
  return { locale: resolvedLocale, t: (key) => messages[resolvedLocale][key] };
925
960
  }
926
961
 
962
+ // src/editor/shell/CanvasLibraryDropTarget.tsx
963
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState3 } from "react";
964
+
927
965
  // src/editor/shell/drop-position.ts
928
966
  function nearestBlockIdAtY(blocks, y) {
929
967
  return blocks.find((block) => y < block.top + block.height / 2)?.id;
930
968
  }
931
- function blockIdAtRelativeY(ids, relativeY) {
932
- const index = Math.min(ids.length, Math.max(0, Math.round(relativeY * ids.length)));
933
- return ids[index];
969
+
970
+ // src/editor/shell/CanvasLibraryDropTarget.tsx
971
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
972
+ function uniqueBlocks(blocks) {
973
+ const seen = /* @__PURE__ */ new Set();
974
+ return blocks.filter((block) => !seen.has(block.id) && seen.add(block.id));
975
+ }
976
+ function scrollableAncestors(start) {
977
+ const result = [];
978
+ let node = start;
979
+ while (node) {
980
+ const style = window.getComputedStyle(node);
981
+ if (["auto", "scroll", "overlay"].includes(style.overflowY) && node.scrollHeight > node.clientHeight) result.push(node);
982
+ node = node.parentElement;
983
+ }
984
+ return result;
985
+ }
986
+ function scrollDelta(pointerY, rect, elapsedMs) {
987
+ const top = Math.max(0, rect.top);
988
+ const bottom = Math.min(window.innerHeight, rect.bottom);
989
+ const height = bottom - top;
990
+ if (height <= 0 || pointerY < top || pointerY > bottom) return 0;
991
+ const edge = Math.min(96, Math.max(36, height * 0.24));
992
+ const speed = 900;
993
+ if (pointerY < top + edge) return -speed * Math.min(1, (top + edge - pointerY) / edge) * elapsedMs / 1e3;
994
+ if (pointerY > bottom - edge) return speed * Math.min(1, (pointerY - (bottom - edge)) / edge) * elapsedMs / 1e3;
995
+ return 0;
996
+ }
997
+ function scrollElement(element, delta) {
998
+ const canScroll = delta < 0 ? element.scrollTop > 0 : element.scrollTop < element.scrollHeight - element.clientHeight;
999
+ if (!delta || !canScroll) return false;
1000
+ if (typeof element.scrollBy === "function") element.scrollBy({ top: delta, behavior: "auto" });
1001
+ else element.scrollTop += delta;
1002
+ return true;
1003
+ }
1004
+ function CanvasLibraryDropTarget({ frameRef, blocks, label, onDrop, onCancel, adminLocale }) {
1005
+ const targetRef = useRef4(null);
1006
+ const latestPreview = useRef4(null);
1007
+ const dragging = useRef4(false);
1008
+ const completed = useRef4(false);
1009
+ const pointerY = useRef4(0);
1010
+ const animationFrame = useRef4(null);
1011
+ const lastFrameTime = useRef4(null);
1012
+ const callbacks = useRef4({ onDrop, onCancel });
1013
+ useEffect3(() => {
1014
+ callbacks.current = { onDrop, onCancel };
1015
+ }, [onDrop, onCancel]);
1016
+ const orderedBlocks = uniqueBlocks(blocks);
1017
+ const [preview, setPreview] = useState3(null);
1018
+ const english = adminLocale?.toLowerCase().startsWith("en") ?? false;
1019
+ const stopAutoScroll = () => {
1020
+ if (animationFrame.current !== null) window.cancelAnimationFrame(animationFrame.current);
1021
+ animationFrame.current = null;
1022
+ lastFrameTime.current = null;
1023
+ };
1024
+ const clear = (cancel) => {
1025
+ stopAutoScroll();
1026
+ dragging.current = false;
1027
+ latestPreview.current = null;
1028
+ setPreview(null);
1029
+ if (cancel) {
1030
+ completed.current = true;
1031
+ callbacks.current.onCancel();
1032
+ }
1033
+ };
1034
+ const calculatePreview = (clientY) => {
1035
+ const target = targetRef.current;
1036
+ const frame = frameRef.current;
1037
+ if (!target || !frame) return null;
1038
+ const targetRect = target.getBoundingClientRect();
1039
+ const frameRect = frame.getBoundingClientRect();
1040
+ const iframe = frame.querySelector("iframe");
1041
+ const iframeDocument = iframe?.contentDocument;
1042
+ const root = iframeDocument ?? frame;
1043
+ const allowed = new Set(orderedBlocks.map((block) => block.id));
1044
+ const byId = /* @__PURE__ */ new Map();
1045
+ const iframeRect = iframeDocument ? iframe?.getBoundingClientRect() : void 0;
1046
+ const scaleY = iframe && iframeRect && iframeRect.height > 0 && iframe.clientHeight > 0 ? iframeRect.height / iframe.clientHeight : 1;
1047
+ const elements = Array.from(root.querySelectorAll("[data-page-document-block-id], [data-puck-dnd]"));
1048
+ for (const element of elements) {
1049
+ const id = element.dataset.pageDocumentBlockId ?? element.dataset.puckDnd;
1050
+ if (!id || !allowed.has(id) || byId.has(id) || element.closest('[aria-hidden="true"], [class*="DragOverlay"]')) continue;
1051
+ const rect = element.getBoundingClientRect();
1052
+ byId.set(id, { id, top: iframeRect ? iframeRect.top + rect.top * scaleY : rect.top, height: iframeRect ? rect.height * scaleY : rect.height });
1053
+ }
1054
+ const candidates = orderedBlocks.flatMap((block) => byId.get(block.id) ?? []);
1055
+ const beforeId = nearestBlockIdAtY(candidates, clientY);
1056
+ const markerCandidate = beforeId ? candidates.find((candidate) => candidate.id === beforeId) : candidates.at(-1);
1057
+ const markerViewportTop = markerCandidate ? beforeId ? markerCandidate.top : markerCandidate.top + markerCandidate.height : Math.min(frameRect.bottom, Math.max(frameRect.top, clientY));
1058
+ return { beforeId, markerTop: Math.max(0, markerViewportTop - targetRect.top), markerLeft: Math.max(0, frameRect.left - targetRect.left), markerWidth: frameRect.width };
1059
+ };
1060
+ const updatePreview = (clientY) => {
1061
+ pointerY.current = clientY;
1062
+ const next = calculatePreview(clientY);
1063
+ const previous = latestPreview.current;
1064
+ latestPreview.current = next;
1065
+ if (previous?.beforeId !== next?.beforeId || previous?.markerTop !== next?.markerTop || previous?.markerLeft !== next?.markerLeft || previous?.markerWidth !== next?.markerWidth) setPreview(next);
1066
+ };
1067
+ const autoScroll = (timestamp) => {
1068
+ if (!dragging.current) return;
1069
+ const elapsedMs = Math.min(64, Math.max(0, timestamp - (lastFrameTime.current ?? timestamp)));
1070
+ lastFrameTime.current = timestamp;
1071
+ const stage = targetRef.current?.parentElement;
1072
+ if (stage) {
1073
+ let scrolled = false;
1074
+ for (const ancestor of scrollableAncestors(stage)) {
1075
+ const delta = scrollDelta(pointerY.current, ancestor.getBoundingClientRect(), elapsedMs);
1076
+ if (scrollElement(ancestor, delta)) {
1077
+ scrolled = true;
1078
+ break;
1079
+ }
1080
+ }
1081
+ const documentScroller = document.scrollingElement;
1082
+ if (!scrolled && documentScroller) {
1083
+ const viewport = new DOMRect(0, 0, window.innerWidth, window.innerHeight);
1084
+ scrollElement(documentScroller, scrollDelta(pointerY.current, viewport, elapsedMs));
1085
+ }
1086
+ updatePreview(pointerY.current);
1087
+ }
1088
+ animationFrame.current = window.requestAnimationFrame(autoScroll);
1089
+ };
1090
+ const startAutoScroll = () => {
1091
+ if (animationFrame.current === null) animationFrame.current = window.requestAnimationFrame(autoScroll);
1092
+ };
1093
+ useEffect3(() => {
1094
+ const cancelOutside = (event) => {
1095
+ if (event.type === "drop" && targetRef.current?.contains(event.target)) return;
1096
+ if (!completed.current) clear(true);
1097
+ };
1098
+ const onKeyDown = (event) => {
1099
+ if (event.key === "Escape" && !completed.current) clear(true);
1100
+ };
1101
+ document.addEventListener("keydown", onKeyDown);
1102
+ document.addEventListener("dragend", cancelOutside, true);
1103
+ document.addEventListener("drop", cancelOutside, true);
1104
+ return () => {
1105
+ document.removeEventListener("keydown", onKeyDown);
1106
+ document.removeEventListener("dragend", cancelOutside, true);
1107
+ document.removeEventListener("drop", cancelOutside, true);
1108
+ stopAutoScroll();
1109
+ };
1110
+ }, []);
1111
+ const handleDragOver = (event) => {
1112
+ event.preventDefault();
1113
+ event.stopPropagation();
1114
+ event.dataTransfer.dropEffect = "copy";
1115
+ completed.current = false;
1116
+ dragging.current = true;
1117
+ updatePreview(event.clientY);
1118
+ startAutoScroll();
1119
+ };
1120
+ const handleDragLeave = (event) => {
1121
+ if (!event.relatedTarget || !event.currentTarget.contains(event.relatedTarget)) clear(false);
1122
+ };
1123
+ const handleDrop = (event) => {
1124
+ event.preventDefault();
1125
+ event.stopPropagation();
1126
+ const next = latestPreview.current ?? calculatePreview(event.clientY);
1127
+ completed.current = true;
1128
+ clear(false);
1129
+ callbacks.current.onDrop(next?.beforeId);
1130
+ };
1131
+ const position = preview?.beforeId ? orderedBlocks.findIndex((block) => block.id === preview.beforeId) + 1 : orderedBlocks.length + 1;
1132
+ const hint = english ? `Release to add ${label}${preview ? ` \xB7 position ${position}` : ""}` : `\u677E\u5F00\u4EE5\u6DFB\u52A0 ${label}${preview ? ` \xB7 \u7B2C ${position} \u4E2A\u4F4D\u7F6E` : ""}`;
1133
+ const style = { position: "absolute", inset: 0, zIndex: 2 };
1134
+ const markerLabel = english ? `Position ${position}` : `\u7B2C ${position} \u4E2A\u4F4D\u7F6E`;
1135
+ return /* @__PURE__ */ jsxs6("div", { ref: targetRef, className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": english ? "Block drop area" : "\u533A\u5757\u6295\u653E\u533A", style, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [
1136
+ /* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-target__hint", "aria-live": "polite", children: hint }),
1137
+ preview ? /* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-marker", "aria-hidden": "true", style: { top: preview.markerTop, left: preview.markerLeft, width: preview.markerWidth }, children: /* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-marker__label", children: markerLabel }) }) : null
1138
+ ] });
934
1139
  }
935
1140
 
936
1141
  // src/editor/shell/PageDocumentEditorShell.tsx
937
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1142
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1143
+ function scrollCanvasBlockIntoView(blockId, frame) {
1144
+ const selector = `[data-page-document-block-id="${CSS.escape(blockId)}"]`;
1145
+ const scroll = () => {
1146
+ const iframe = frame?.querySelector("iframe");
1147
+ const element = iframe?.contentDocument?.querySelector(selector) ?? document.querySelector(selector);
1148
+ element?.scrollIntoView({ block: "start", inline: "nearest" });
1149
+ };
1150
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(scroll);
1151
+ else scroll();
1152
+ }
938
1153
  var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
939
1154
  function blockLabel(block, registry) {
940
1155
  return blockTypeLabel(block.type, registry);
@@ -944,12 +1159,12 @@ function blockTypeLabel(type, registry) {
944
1159
  if (type === "core.image") return "\u56FE\u7247";
945
1160
  return registry?.getBlock(type)?.label ?? type;
946
1161
  }
947
- function validateDocumentBlocks(document, registry) {
948
- return registry ? validatePageDocumentWithRegistry(document, registry) : [];
1162
+ function validateDocumentBlocks(document2, registry) {
1163
+ return registry ? validatePageDocumentWithRegistry(document2, registry) : [];
949
1164
  }
950
1165
  function useEditorSession(adapter, pageId) {
951
- const [managed, setManaged] = useState3(() => adapter ? { state: "acquiring" } : { state: "active" });
952
- useEffect3(() => {
1166
+ const [managed, setManaged] = useState4(() => adapter ? { state: "acquiring" } : { state: "active" });
1167
+ useEffect4(() => {
953
1168
  let cancelled = false;
954
1169
  let session;
955
1170
  let heartbeatTimer;
@@ -991,31 +1206,31 @@ function useEditorSession(adapter, pageId) {
991
1206
  function PageDocumentEditorShell(props) {
992
1207
  const { sessionAdapter, initialDocument, onSessionStateChange } = props;
993
1208
  const managedSession = useEditorSession(sessionAdapter, initialDocument.pageId);
994
- useEffect3(() => {
1209
+ useEffect4(() => {
995
1210
  onSessionStateChange?.(managedSession.state);
996
1211
  }, [managedSession.state, onSessionStateChange]);
997
- 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 }) });
1212
+ return /* @__PURE__ */ jsx9(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__ */ jsx9(PageDocumentEditor, { ...props }) });
998
1213
  }
999
1214
  function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, inspectorSettings, productPicker, pageStatus, onBack, onHistory, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
1000
1215
  const editor = useEditorContext();
1001
- const editorRef = useRef4(editor);
1216
+ const editorRef = useRef5(editor);
1002
1217
  useLayoutEffect(() => {
1003
1218
  editorRef.current = editor;
1004
1219
  }, [editor]);
1005
- const [blockView, setBlockView] = useState3("blocks");
1006
- const [leftRailOpen, setLeftRailOpen] = useState3(true);
1007
- const [rightPanelOpen, setRightPanelOpen] = useState3(true);
1008
- const [draggingLibraryType, setDraggingLibraryType] = useState3(null);
1009
- const canvasFrameRef = useRef4(null);
1010
- const [canvasMutationVersion, setCanvasMutationVersion] = useState3(0);
1011
- const [canvasResetVersion, setCanvasResetVersion] = useState3(0);
1012
- const [request, setRequest] = useState3("idle");
1013
- const [notice, setNotice] = useState3(null);
1014
- const [draftRevision, setDraftRevision] = useState3(initialDraftRevision);
1015
- const [isOnline, setIsOnline] = useState3(() => typeof navigator === "undefined" || navigator.onLine);
1016
- const [saveState, setSaveState] = useState3("saved");
1017
- const [zoom, setZoom] = useState3("auto");
1018
- const [confirmBack, setConfirmBack] = useState3(false);
1220
+ const [blockView, setBlockView] = useState4("blocks");
1221
+ const [leftRailOpen, setLeftRailOpen] = useState4(true);
1222
+ const [rightPanelOpen, setRightPanelOpen] = useState4(true);
1223
+ const [draggingLibraryType, setDraggingLibraryType] = useState4(null);
1224
+ const canvasFrameRef = useRef5(null);
1225
+ const [canvasMutationVersion, setCanvasMutationVersion] = useState4(0);
1226
+ const [canvasResetVersion, setCanvasResetVersion] = useState4(0);
1227
+ const [request, setRequest] = useState4("idle");
1228
+ const [notice, setNotice] = useState4(null);
1229
+ const [draftRevision, setDraftRevision] = useState4(initialDraftRevision);
1230
+ const [isOnline, setIsOnline] = useState4(() => typeof navigator === "undefined" || navigator.onLine);
1231
+ const [saveState, setSaveState] = useState4("saved");
1232
+ const [zoom, setZoom] = useState4("auto");
1233
+ const [confirmBack, setConfirmBack] = useState4(false);
1019
1234
  const i18n = createAdminI18n(adminLocale);
1020
1235
  const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
1021
1236
  const canvasThemeStyle = useMemo2(() => {
@@ -1036,7 +1251,7 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1036
1251
  insert: editorRef.current.canAddBlock(type)
1037
1252
  }), []);
1038
1253
  const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry, resolveBlockPermissions), [confirmCanvasSelection, registry, resolveBlockPermissions, selectedBlockId, updateFromCanvasInput]);
1039
- useEffect3(() => {
1254
+ useEffect4(() => {
1040
1255
  const online = () => setIsOnline(true);
1041
1256
  const offline = () => setIsOnline(false);
1042
1257
  window.addEventListener("online", online);
@@ -1054,14 +1269,14 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1054
1269
  setSaveState("offline");
1055
1270
  return;
1056
1271
  }
1057
- const document = editor.document;
1272
+ const document2 = editor.document;
1058
1273
  setRequest("saving");
1059
1274
  setSaveState("saving");
1060
1275
  setNotice(null);
1061
1276
  try {
1062
- const result = draftPersistence ? await draftPersistence.saveDraft({ document, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document);
1277
+ const result = draftPersistence ? await draftPersistence.saveDraft({ document: document2, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document2);
1063
1278
  if (result?.revision !== void 0) setDraftRevision(result.revision);
1064
- editor.markSaved(document);
1279
+ editor.markSaved(document2);
1065
1280
  setSaveState("saved");
1066
1281
  } catch {
1067
1282
  setNotice("saveFailed");
@@ -1070,7 +1285,7 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1070
1285
  setRequest("idle");
1071
1286
  }
1072
1287
  }, [canPersistDraft, draftPersistence, draftRevision, editor, isOnline, onSave, request, validationIssues.length]);
1073
- useEffect3(() => {
1288
+ useEffect4(() => {
1074
1289
  if (!autoSave || !canPersistDraft || !editor.isDirty || resolvedSaveState !== "dirty" && resolvedSaveState !== "failed" || !isOnline || request !== "idle" || validationIssues.length > 0 || editor.sessionState !== "active") return;
1075
1290
  const timer = window.setTimeout(() => {
1076
1291
  void save();
@@ -1079,13 +1294,13 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1079
1294
  }, [autoSave, autoSaveDelayMs, canPersistDraft, editor.isDirty, editor.sessionState, isOnline, request, resolvedSaveState, save, validationIssues.length]);
1080
1295
  const publish = async () => {
1081
1296
  if (!publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline) return;
1082
- const document = editor.document;
1297
+ const document2 = editor.document;
1083
1298
  setRequest("publishing");
1084
1299
  setNotice(null);
1085
1300
  try {
1086
- if (publishAction) await publishAction.publish({ document, expectedRevision: draftRevision, session: editor.session, validationIssues });
1087
- else await onPublish?.(document);
1088
- editor.markSaved(document);
1301
+ if (publishAction) await publishAction.publish({ document: document2, expectedRevision: draftRevision, session: editor.session, validationIssues });
1302
+ else await onPublish?.(document2);
1303
+ editor.markSaved(document2);
1089
1304
  setSaveState("saved");
1090
1305
  setNotice("published");
1091
1306
  } catch {
@@ -1094,40 +1309,14 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1094
1309
  setRequest("idle");
1095
1310
  }
1096
1311
  };
1097
- if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx7(EditorStatus, { state: editor.loadState });
1098
- if (editor.sessionState !== "active") return /* @__PURE__ */ jsx7(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
1312
+ if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx9(EditorStatus, { state: editor.loadState });
1313
+ if (editor.sessionState !== "active") return /* @__PURE__ */ jsx9(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
1099
1314
  const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
1100
1315
  const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
1101
1316
  const addFromLibrary = (type, beforeId) => {
1102
1317
  const id = editor.addBlock(type, beforeId);
1103
1318
  if (id) editor.requestCanvasSelection(id);
1104
1319
  };
1105
- const getDropBeforeId = (clientY) => {
1106
- const frame = canvasFrameRef.current;
1107
- if (!frame) return void 0;
1108
- const iframe2 = frame.querySelector("iframe");
1109
- const root = iframe2?.contentDocument ?? frame;
1110
- const offsetTop = iframe2?.getBoundingClientRect().top ?? 0;
1111
- const elements = Array.from(root.querySelectorAll("[data-page-document-block-id]"));
1112
- const puckElements = elements.length > 0 ? elements : Array.from(root.querySelectorAll("[data-puck-dnd]"));
1113
- const blockElements = puckElements.length > 0 ? puckElements : Array.from(window.document.querySelectorAll("[data-page-document-block-id], [data-puck-dnd]"));
1114
- const candidates = blockElements.map((element) => ({
1115
- id: element.dataset.pageDocumentBlockId ?? element.dataset.puckDnd,
1116
- top: element.getBoundingClientRect().top,
1117
- height: element.getBoundingClientRect().height
1118
- })).filter((item) => typeof item.id === "string");
1119
- const nearestId = nearestBlockIdAtY(candidates, clientY - offsetTop);
1120
- if (nearestId || candidates.length > 0) return nearestId;
1121
- const rect = frame.getBoundingClientRect();
1122
- if (rect.height <= 0) return void 0;
1123
- return blockIdAtRelativeY(editor.document.blocks.map((block) => block.id), (clientY - rect.top) / rect.height);
1124
- };
1125
- const dropFromLibrary = (event) => {
1126
- event.preventDefault();
1127
- const type = event.dataTransfer.getData("application/x-page-document-block") || draggingLibraryType;
1128
- if (type && blockTypes.includes(type)) addFromLibrary(type, getDropBeforeId(event.clientY));
1129
- setDraggingLibraryType(null);
1130
- };
1131
1320
  const cancelLibraryDrop = (event) => {
1132
1321
  event.preventDefault();
1133
1322
  event.stopPropagation();
@@ -1141,58 +1330,63 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1141
1330
  else onBack();
1142
1331
  };
1143
1332
  const dismissPublishedNotice = () => setNotice((current) => current === "published" ? null : current);
1144
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1145
- /* @__PURE__ */ jsx7(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => {
1333
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
1334
+ /* @__PURE__ */ jsx9(Puck2, { config, data: engineData, iframe: { enabled: iframe }, dnd: { behavior: "static" }, overrides: { actionBar: CanvasSelectionActionBar }, onChange: (data) => {
1146
1335
  if (!editor.updateFromCanvas(fromEngineData(data, editor.document, registry))) setCanvasResetVersion((version) => version + 1);
1147
- }, children: /* @__PURE__ */ jsxs5(Puck2.Layout, { children: [
1148
- /* @__PURE__ */ jsx7(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion, permissionsVersion: `${editor.isEditable}:${editor.document.blocks.map((block) => `${block.id}:${block.type}`).join("|")}` }),
1149
- /* @__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, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
1150
- /* @__PURE__ */ jsx7("header", { className: "pb-header", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-content", children: [
1151
- /* @__PURE__ */ jsxs5("div", { className: "pb-header-title-group", children: [
1152
- onBack ? /* @__PURE__ */ jsx7(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
1153
- /* @__PURE__ */ jsxs5("div", { className: "pb-page-title", children: [
1154
- /* @__PURE__ */ jsx7(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
1155
- pageStatus ? /* @__PURE__ */ jsx7(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
1336
+ }, children: /* @__PURE__ */ jsxs7(Puck2.Layout, { children: [
1337
+ /* @__PURE__ */ jsx9(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion, permissionsVersion: `${editor.isEditable}:${editor.document.blocks.map((block) => `${block.id}:${block.type}`).join("|")}` }),
1338
+ /* @__PURE__ */ jsxs7("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, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
1339
+ /* @__PURE__ */ jsx9("header", { className: "pb-header", children: /* @__PURE__ */ jsxs7("div", { className: "pb-header-content", children: [
1340
+ /* @__PURE__ */ jsxs7("div", { className: "pb-header-title-group", children: [
1341
+ onBack ? /* @__PURE__ */ jsx9(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
1342
+ /* @__PURE__ */ jsxs7("div", { className: "pb-page-title", children: [
1343
+ /* @__PURE__ */ jsx9(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
1344
+ pageStatus ? /* @__PURE__ */ jsx9(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
1156
1345
  ] })
1157
1346
  ] }),
1158
- /* @__PURE__ */ jsx7("div", { className: "pb-header-controls", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
1159
- /* @__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)) }),
1160
- /* @__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) }) })
1347
+ /* @__PURE__ */ jsx9("div", { className: "pb-header-controls", children: /* @__PURE__ */ jsxs7("div", { className: "pb-header-device-toolbar", children: [
1348
+ /* @__PURE__ */ jsx9(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx9(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }),
1349
+ /* @__PURE__ */ jsx9("div", { className: "pb-zoom-control", children: /* @__PURE__ */ jsx9(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) }) })
1161
1350
  ] }) }),
1162
- /* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: true, children: [
1163
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
1164
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo }),
1165
- resolvedSaveState === "saved" ? null : /* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
1166
- onHistory ? /* @__PURE__ */ jsx7("span", { "data-editor-history": "", children: /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle", onClick: () => onHistory({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("history") }) }) : null,
1167
- onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
1168
- onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
1169
- /* @__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") })
1351
+ /* @__PURE__ */ jsx9("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs7(InlineStack3, { gap: "150", blockAlign: "center", wrap: true, children: [
1352
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
1353
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo }),
1354
+ resolvedSaveState === "saved" ? null : /* @__PURE__ */ jsx9(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
1355
+ onHistory ? /* @__PURE__ */ jsx9("span", { "data-editor-history": "", children: /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle", onClick: () => onHistory({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("history") }) }) : null,
1356
+ onPreview ? /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
1357
+ onAddToStore ? /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
1358
+ /* @__PURE__ */ jsx9(Button2, { variant: "primary", disabled: !publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") })
1170
1359
  ] }) })
1171
1360
  ] }) }),
1172
- editor.loadState === "success" ? /* @__PURE__ */ jsx7(Banner, { tone: "success", children: i18n.t("success") }) : null,
1173
- notice && notice !== "published" ? /* @__PURE__ */ jsx7("div", { className: `pb-editor-notice pb-editor-notice--${notice}`, "data-testid": "editor-notice", "data-notice": notice, children: /* @__PURE__ */ jsx7(Banner, { tone: "critical", children: i18n.t(notice) }) }) : null,
1174
- 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: [
1361
+ editor.loadState === "success" ? /* @__PURE__ */ jsx9(Banner, { tone: "success", children: i18n.t("success") }) : null,
1362
+ notice && notice !== "published" ? /* @__PURE__ */ jsx9("div", { className: `pb-editor-notice pb-editor-notice--${notice}`, "data-testid": "editor-notice", "data-notice": notice, children: /* @__PURE__ */ jsx9(Banner, { tone: "critical", children: i18n.t(notice) }) }) : null,
1363
+ validationIssues.length > 0 ? /* @__PURE__ */ jsx9(Banner, { tone: "critical", title: "\u533A\u5757\u5C5E\u6027\u672A\u901A\u8FC7\u6821\u9A8C", children: /* @__PURE__ */ jsx9("ul", { children: validationIssues.map((issue) => /* @__PURE__ */ jsxs7("li", { children: [
1175
1364
  issue.path,
1176
1365
  ": ",
1177
1366
  issue.message
1178
1367
  ] }, `${issue.path}-${issue.message}`)) }) }) : null,
1179
- /* @__PURE__ */ jsxs5("div", { className: `pb-workspace pb-workspace--document${leftRailOpen ? "" : " pb-workspace--left-closed"}${rightPanelOpen ? "" : " pb-workspace--right-closed"}`, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
1180
- /* @__PURE__ */ jsxs5("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
1181
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks" && leftRailOpen, variant: "tertiary", onClick: () => {
1368
+ /* @__PURE__ */ jsxs7("div", { className: `pb-workspace pb-workspace--document${leftRailOpen ? "" : " pb-workspace--left-closed"}${rightPanelOpen ? "" : " pb-workspace--right-closed"}`, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
1369
+ /* @__PURE__ */ jsxs7("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
1370
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks" && leftRailOpen, variant: "tertiary", onClick: () => {
1182
1371
  setBlockView("blocks");
1183
1372
  setLeftRailOpen(true);
1184
1373
  } }),
1185
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline" && leftRailOpen, variant: "tertiary", onClick: () => {
1374
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline" && leftRailOpen, variant: "tertiary", onClick: () => {
1186
1375
  setBlockView("outline");
1187
1376
  setLeftRailOpen(true);
1188
1377
  } })
1189
1378
  ] }),
1190
- /* @__PURE__ */ jsxs5("aside", { className: `pb-left-panel${leftRailOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u533A\u5757", children: [
1191
- /* @__PURE__ */ jsxs5(InlineStack3, { align: "space-between", blockAlign: "center", children: [
1192
- /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }),
1193
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseLeft"), icon: XIcon2, variant: "tertiary", onClick: () => setLeftRailOpen(false) })
1379
+ /* @__PURE__ */ jsxs7("aside", { className: `pb-left-panel${leftRailOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u533A\u5757", children: [
1380
+ /* @__PURE__ */ jsxs7(InlineStack3, { align: "space-between", blockAlign: "center", children: [
1381
+ /* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }),
1382
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("collapseLeft"), icon: XIcon2, variant: "tertiary", onClick: () => setLeftRailOpen(false) })
1194
1383
  ] }),
1195
- 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) => {
1384
+ blockView === "blocks" ? /* @__PURE__ */ jsx9("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__ */ jsxs7("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" : ""}`, onClick: () => {
1385
+ const block = editor.document.blocks.find((item) => item.type === type);
1386
+ if (!block) return;
1387
+ editor.requestCanvasSelection(block.id);
1388
+ scrollCanvasBlockIntoView(block.id, canvasFrameRef.current);
1389
+ }, onDragStart: (event) => {
1196
1390
  if (!editor.canAddBlock(type)) {
1197
1391
  event.preventDefault();
1198
1392
  return;
@@ -1201,63 +1395,68 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
1201
1395
  event.dataTransfer.effectAllowed = "copy";
1202
1396
  setDraggingLibraryType(type);
1203
1397
  }, onDragEnd: () => setDraggingLibraryType(null), children: [
1204
- /* @__PURE__ */ jsxs5("span", { className: "pb-library-block-title", children: [
1205
- /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
1206
- /* @__PURE__ */ jsx7("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(DragHandleIcon2, {}) })
1398
+ /* @__PURE__ */ jsxs7("span", { className: "pb-library-block-title", children: [
1399
+ /* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
1400
+ /* @__PURE__ */ jsx9("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx9(DragHandleIcon2, {}) })
1207
1401
  ] }),
1208
- /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
1209
- ] }, 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: [
1210
- /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
1211
- /* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
1402
+ /* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
1403
+ ] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx9(Text3, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs7("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: () => {
1404
+ editor.requestCanvasSelection(block.id);
1405
+ scrollCanvasBlockIntoView(block.id, canvasFrameRef.current);
1406
+ }, children: [
1407
+ /* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
1408
+ /* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
1212
1409
  ] }, block.id)) })
1213
1410
  ] }),
1214
- /* @__PURE__ */ jsxs5("main", { className: "pb-canvas-area", children: [
1215
- /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-stage", children: [
1216
- /* @__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, style: canvasThemeStyle, children: /* @__PURE__ */ jsx7(Puck2.Preview, {}) }),
1217
- 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: [
1218
- "\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
1219
- blockTypeLabel(draggingLibraryType, registry)
1220
- ] }) : null,
1221
- editor.selectedBlock ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
1222
- /* @__PURE__ */ jsx7("span", { children: blockLabel(editor.selectedBlock, registry) }),
1223
- /* @__PURE__ */ jsx7("span", { children: "Selected" })
1224
- ] }) : null
1411
+ /* @__PURE__ */ jsxs7("main", { className: "pb-canvas-area", children: [
1412
+ /* @__PURE__ */ jsxs7("div", { className: "pb-canvas-stage", children: [
1413
+ /* @__PURE__ */ jsx9("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, style: canvasThemeStyle, children: /* @__PURE__ */ jsx9(Puck2.Preview, {}) }),
1414
+ draggingLibraryType ? /* @__PURE__ */ jsx9(CanvasLibraryDropTarget, { frameRef: canvasFrameRef, blocks: editor.document.blocks.map((block) => ({ id: block.id, label: blockLabel(block, registry) })), label: blockTypeLabel(draggingLibraryType, registry), adminLocale, onDrop: (beforeId) => {
1415
+ addFromLibrary(draggingLibraryType, beforeId);
1416
+ setDraggingLibraryType(null);
1417
+ }, onCancel: () => setDraggingLibraryType(null) }) : null
1225
1418
  ] }),
1226
- !leftRailOpen || !rightPanelOpen ? /* @__PURE__ */ jsxs5("div", { className: "pb-collapsed-actions", children: [
1227
- !leftRailOpen ? /* @__PURE__ */ jsx7(Button2, { onClick: () => setLeftRailOpen(true), children: i18n.t("expandLeft") }) : null,
1228
- !rightPanelOpen ? /* @__PURE__ */ jsx7(Button2, { onClick: () => setRightPanelOpen(true), children: i18n.t("expandRight") }) : null
1419
+ !leftRailOpen || !rightPanelOpen ? /* @__PURE__ */ jsxs7("div", { className: "pb-collapsed-actions", children: [
1420
+ !leftRailOpen ? /* @__PURE__ */ jsx9(Button2, { onClick: () => setLeftRailOpen(true), children: i18n.t("expandLeft") }) : null,
1421
+ !rightPanelOpen ? /* @__PURE__ */ jsx9(Button2, { onClick: () => setRightPanelOpen(true), children: i18n.t("expandRight") }) : null
1229
1422
  ] }) : null
1230
1423
  ] }),
1231
- /* @__PURE__ */ jsxs5("aside", { className: `pb-right-panel${rightPanelOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u5C5E\u6027", children: [
1232
- /* @__PURE__ */ jsxs5(InlineStack3, { align: "space-between", blockAlign: "center", children: [
1233
- /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
1234
- /* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
1424
+ /* @__PURE__ */ jsxs7("aside", { className: `pb-right-panel${rightPanelOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u5C5E\u6027", children: [
1425
+ /* @__PURE__ */ jsxs7(InlineStack3, { align: "space-between", blockAlign: "center", children: [
1426
+ /* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
1427
+ /* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
1235
1428
  ] }),
1236
- editor.selectedBlock ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1237
- /* @__PURE__ */ jsx7(DocumentInspector, { settings: inspectorSettings?.[editor.selectedBlock.id] ?? inspectorSettings?.[editor.selectedBlock.type], pageId: editor.document.pageId, assetPicker, productPicker, i18n, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }, editor.selectedBlock.id),
1238
- /* @__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) })
1239
- ] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
1429
+ editor.selectedBlock ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1430
+ /* @__PURE__ */ jsx9(DocumentInspector, { settings: inspectorSettings?.[editor.selectedBlock.id] ?? inspectorSettings?.[editor.selectedBlock.type], pageId: editor.document.pageId, assetPicker, productPicker, i18n, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }, editor.selectedBlock.id),
1431
+ /* @__PURE__ */ jsx9(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) })
1432
+ ] }) : /* @__PURE__ */ jsx9(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
1240
1433
  ] })
1241
1434
  ] }),
1242
- editor.pendingDeleteBlock ? /* @__PURE__ */ jsx7(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
1243
- confirmBack ? /* @__PURE__ */ jsx7(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
1435
+ editor.pendingDeleteBlock ? /* @__PURE__ */ jsx9(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
1436
+ confirmBack ? /* @__PURE__ */ jsx9(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
1244
1437
  setConfirmBack(false);
1245
1438
  onBack?.();
1246
1439
  } }) : null
1247
1440
  ] })
1248
1441
  ] }) }),
1249
- /* @__PURE__ */ jsx7("div", { className: "pb-toast-host", ...notice === "published" ? { "data-testid": "editor-notice", "data-notice": "published" } : {}, children: /* @__PURE__ */ jsx7(Frame, { children: notice === "published" ? /* @__PURE__ */ jsx7(Toast, { content: i18n.t("publishSuccessTitle"), onDismiss: dismissPublishedNotice }) : null }) })
1442
+ /* @__PURE__ */ jsx9("div", { className: "pb-toast-host", ...notice === "published" ? { "data-testid": "editor-notice", "data-notice": "published" } : {}, children: /* @__PURE__ */ jsx9(Frame, { children: notice === "published" ? /* @__PURE__ */ jsx9(Toast, { content: i18n.t("publishSuccessTitle"), onDismiss: dismissPublishedNotice }) : null }) })
1250
1443
  ] });
1251
1444
  }
1445
+ function CanvasSelectionActionBar({ label, children, parentAction }) {
1446
+ void label;
1447
+ void children;
1448
+ void parentAction;
1449
+ return /* @__PURE__ */ jsx9(Fragment2, {});
1450
+ }
1252
1451
  function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion, canvasResetVersion, permissionsVersion }) {
1253
1452
  const puck = usePuck();
1254
- const lastSelectedId = useRef4(null);
1255
- const lastSyncedData = useRef4(null);
1256
- const lastCanvasMutationVersion = useRef4(0);
1257
- const lastCanvasResetVersion = useRef4(0);
1453
+ const lastSelectedId = useRef5(null);
1454
+ const lastSyncedData = useRef5(null);
1455
+ const lastCanvasMutationVersion = useRef5(0);
1456
+ const lastCanvasResetVersion = useRef5(0);
1258
1457
  const serializedData = JSON.stringify(data);
1259
1458
  const refreshPermissions = puck.refreshPermissions;
1260
- useEffect3(() => {
1459
+ useEffect4(() => {
1261
1460
  const forceReset = canvasResetVersion > lastCanvasResetVersion.current;
1262
1461
  if (lastSyncedData.current === serializedData && !forceReset) return;
1263
1462
  lastSyncedData.current = serializedData;
@@ -1268,16 +1467,17 @@ function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canva
1268
1467
  }
1269
1468
  puck.dispatch({ type: "setData", data });
1270
1469
  }, [canvasMutationVersion, canvasResetVersion, data, puck, serializedData]);
1271
- useEffect3(() => {
1470
+ useEffect4(() => {
1272
1471
  refreshPermissions?.({}, true);
1273
1472
  }, [permissionsVersion, refreshPermissions]);
1274
- useEffect3(() => {
1473
+ useEffect4(() => {
1275
1474
  if (!requestedBlockId) return;
1276
1475
  const selector = puck.getSelectorForId(requestedBlockId);
1277
1476
  if (selector) puck.dispatch({ type: "setUi", ui: { itemSelector: selector } });
1477
+ scrollCanvasBlockIntoView(requestedBlockId);
1278
1478
  }, [puck, requestedBlockId]);
1279
1479
  const selectedId = typeof puck.selectedItem?.props.id === "string" ? puck.selectedItem.props.id : null;
1280
- useEffect3(() => {
1480
+ useEffect4(() => {
1281
1481
  if (selectedId && lastSelectedId.current !== selectedId) {
1282
1482
  lastSelectedId.current = selectedId;
1283
1483
  onCanvasSelected(selectedId);
@@ -1289,32 +1489,32 @@ function EditorStatus({ state }) {
1289
1489
  const i18n = createAdminI18n();
1290
1490
  const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
1291
1491
  const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
1292
- 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 }) });
1492
+ return /* @__PURE__ */ jsx9("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx9(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
1293
1493
  }
1294
1494
  function EditorSessionStatus({ state, message }) {
1295
1495
  const i18n = createAdminI18n();
1296
1496
  const tone = state === "locked" ? "warning" : state === "readonly" ? "info" : "critical";
1297
1497
  const title = state === "acquiring" ? i18n.t("acquiringLock") : state === "locked" ? i18n.t("locked") : state === "readonly" ? i18n.t("readonly") : i18n.t("lockLost");
1298
- 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 }) });
1498
+ return /* @__PURE__ */ jsx9("div", { className: "pb-editor-status", "data-testid": "page-document-editor-session-state", "data-editor-session-state": state, children: /* @__PURE__ */ jsx9(Banner, { tone, title, children: message ?? title }) });
1299
1499
  }
1300
1500
  function InspectorSection({ title, children, defaultOpen = true }) {
1301
- return /* @__PURE__ */ jsxs5("details", { className: "pb-inspector-section", open: defaultOpen, children: [
1302
- /* @__PURE__ */ jsxs5("summary", { children: [
1303
- /* @__PURE__ */ jsx7("span", { children: title }),
1304
- /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2304" })
1501
+ return /* @__PURE__ */ jsxs7("details", { className: "pb-inspector-section", open: defaultOpen, children: [
1502
+ /* @__PURE__ */ jsxs7("summary", { children: [
1503
+ /* @__PURE__ */ jsx9("span", { children: title }),
1504
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2304" })
1305
1505
  ] }),
1306
- /* @__PURE__ */ jsx7("div", { className: "pb-inspector-section__body", children })
1506
+ /* @__PURE__ */ jsx9("div", { className: "pb-inspector-section__body", children })
1307
1507
  ] });
1308
1508
  }
1309
1509
  function InspectorTextControl({ label, value, control, maxLength, disabled, onChange }) {
1310
1510
  const stringValue = typeof value === "string" ? value : "";
1311
- 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) });
1511
+ if (control === "color") return /* @__PURE__ */ jsx9("input", { "aria-label": label, type: "color", value: /^#[\da-f]{6}$/i.test(stringValue) ? stringValue : "#000000", disabled, onChange: (event) => onChange(event.currentTarget.value) });
1312
1512
  const length = Array.from(stringValue).length;
1313
- const field = /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange: (next) => onChange(maxLength === void 0 ? next : Array.from(next).slice(0, maxLength).join("")), autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text", maxLength });
1513
+ const field = /* @__PURE__ */ jsx9(TextField, { label, labelHidden: true, value: stringValue, onChange: (next) => onChange(maxLength === void 0 ? next : Array.from(next).slice(0, maxLength).join("")), autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text", maxLength });
1314
1514
  if (maxLength === void 0) return field;
1315
- return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field__limited", children: [
1515
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field__limited", children: [
1316
1516
  field,
1317
- /* @__PURE__ */ jsxs5("span", { className: "pb-inspector-field__limit", "aria-label": `Limit ${maxLength}`, children: [
1517
+ /* @__PURE__ */ jsxs7("span", { className: "pb-inspector-field__limit", "aria-label": `Limit ${maxLength}`, children: [
1318
1518
  length,
1319
1519
  "/",
1320
1520
  maxLength
@@ -1322,20 +1522,20 @@ function InspectorTextControl({ label, value, control, maxLength, disabled, onCh
1322
1522
  ] });
1323
1523
  }
1324
1524
  function InspectorAssetControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
1325
- const [busy, setBusy] = useState3(false);
1326
- const [error, setError] = useState3(null);
1327
- const active = useRef4(true);
1328
- useEffect3(() => {
1525
+ const [busy, setBusy] = useState4(false);
1526
+ const [error, setError] = useState4(null);
1527
+ const active = useRef5(true);
1528
+ useEffect4(() => {
1329
1529
  active.current = !disabled;
1330
1530
  return () => {
1331
1531
  active.current = false;
1332
1532
  };
1333
1533
  }, [disabled]);
1334
1534
  const url = typeof value === "string" && validateFieldValue({ field: "", control: "asset" }, value).length === 0 ? value : "";
1335
- return /* @__PURE__ */ jsxs5("div", { className: "pb-asset-picker", "data-testid": "asset-picker", children: [
1336
- url ? /* @__PURE__ */ jsx7("img", { src: url, alt: "", className: "pb-asset-picker__preview" }) : /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("emptyAsset") }),
1337
- url ? /* @__PURE__ */ jsx7(Button2, { fullWidth: true, variant: "tertiary", disabled: disabled || busy, onClick: () => onChange(""), children: i18n.t("removeAsset") }) : null,
1338
- /* @__PURE__ */ jsx7(Button2, { fullWidth: true, disabled: disabled || busy || !picker, onClick: () => void (async () => {
1535
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-asset-picker", "data-testid": "asset-picker", children: [
1536
+ url ? /* @__PURE__ */ jsx9("img", { src: url, alt: "", className: "pb-asset-picker__preview" }) : /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("emptyAsset") }),
1537
+ url ? /* @__PURE__ */ jsx9(Button2, { fullWidth: true, variant: "tertiary", disabled: disabled || busy, onClick: () => onChange(""), children: i18n.t("removeAsset") }) : null,
1538
+ /* @__PURE__ */ jsx9(Button2, { fullWidth: true, disabled: disabled || busy || !picker, onClick: () => void (async () => {
1339
1539
  if (!picker) return;
1340
1540
  setBusy(true);
1341
1541
  setError(null);
@@ -1351,20 +1551,20 @@ function InspectorAssetControl({ value, disabled, picker, pageId, blockId, i18n,
1351
1551
  setBusy(false);
1352
1552
  }
1353
1553
  })(), children: busy ? i18n.t("selectingAsset") : i18n.t("selectAsset") }),
1354
- error ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
1554
+ error ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
1355
1555
  ] });
1356
1556
  }
1357
1557
  function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
1358
1558
  const products = parseProductReferences(value);
1359
- const [busy, setBusy] = useState3(false);
1360
- const [error, setError] = useState3(null);
1559
+ const [busy, setBusy] = useState4(false);
1560
+ const [error, setError] = useState4(null);
1361
1561
  const preview = products.slice(0, 4);
1362
- return /* @__PURE__ */ jsxs5("div", { className: "pb-product-picker", "data-testid": "product-picker", children: [
1363
- /* @__PURE__ */ jsxs5("div", { className: "pb-product-picker__summary", children: [
1364
- preview.length ? /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumbs", "aria-hidden": "true", children: preview.map((product, index) => /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumb", style: { zIndex: preview.length - index }, children: product.imageUrl ? /* @__PURE__ */ jsx7("img", { src: product.imageUrl, alt: "" }) : /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumb-fallback" }) }, product.id)) }) : null,
1365
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", children: products.length ? `${products.length} ${i18n.t("productCountSuffix")}` : i18n.t("emptyProducts") })
1562
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-product-picker", "data-testid": "product-picker", children: [
1563
+ /* @__PURE__ */ jsxs7("div", { className: "pb-product-picker__summary", children: [
1564
+ preview.length ? /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumbs", "aria-hidden": "true", children: preview.map((product, index) => /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumb", style: { zIndex: preview.length - index }, children: product.imageUrl ? /* @__PURE__ */ jsx9("img", { src: product.imageUrl, alt: "" }) : /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumb-fallback" }) }, product.id)) }) : null,
1565
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", children: products.length ? `${products.length} ${i18n.t("productCountSuffix")}` : i18n.t("emptyProducts") })
1366
1566
  ] }),
1367
- /* @__PURE__ */ jsx7(Button2, { fullWidth: true, icon: ProductIcon, variant: "secondary", disabled: disabled || busy || !picker, onClick: () => void (async () => {
1567
+ /* @__PURE__ */ jsx9(Button2, { fullWidth: true, icon: ProductIcon, variant: "secondary", disabled: disabled || busy || !picker, onClick: () => void (async () => {
1368
1568
  if (!picker) return;
1369
1569
  setBusy(true);
1370
1570
  setError(null);
@@ -1377,50 +1577,50 @@ function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i1
1377
1577
  setBusy(false);
1378
1578
  }
1379
1579
  })(), children: busy ? i18n.t("selectingProducts") : i18n.t("selectProducts") }),
1380
- error ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
1580
+ error ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
1381
1581
  ] });
1382
1582
  }
1383
1583
  function InspectorField({ name, field, value, registry, disabled, assetPicker, productPicker, pageId, blockId, i18n, onChange }) {
1384
1584
  const label = field.label ?? name;
1385
1585
  const issues = validateFieldValue(field, value);
1386
1586
  const Field = registry?.getField(field.field)?.component;
1387
- return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
1388
- /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field__heading", children: [
1389
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
1390
- field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
1587
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
1588
+ /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field__heading", children: [
1589
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
1590
+ field.description ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
1391
1591
  ] }),
1392
- field.control === "products" ? /* @__PURE__ */ jsx7(InspectorProductsControl, { value, disabled, picker: productPicker, pageId, blockId, i18n, onChange }) : field.control === "asset" ? /* @__PURE__ */ jsx7(InspectorAssetControl, { value, disabled, picker: assetPicker, pageId, blockId, i18n, onChange }) : field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, maxLength: field.validation?.maxLength, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
1393
- issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
1592
+ field.control === "products" ? /* @__PURE__ */ jsx9(InspectorProductsControl, { value, disabled, picker: productPicker, pageId, blockId, i18n, onChange }) : field.control === "asset" ? /* @__PURE__ */ jsx9(InspectorAssetControl, { value, disabled, picker: assetPicker, pageId, blockId, i18n, onChange }) : field.control ? /* @__PURE__ */ jsx9(InspectorTextControl, { label, value, control: field.control, maxLength: field.validation?.maxLength, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx9(Field, { value, onChange }) : null,
1593
+ issues.map((issue) => /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
1394
1594
  ] });
1395
1595
  }
1396
1596
  function InspectorActions({ block, canDuplicate, canDelete, canMove, canMoveUp, canMoveDown, i18n, onDuplicate, onDelete, onMove }) {
1397
- return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: [
1398
- /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1399
- /* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
1400
- /* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
1401
- /* @__PURE__ */ jsx7(Button2, { disabled: !canDuplicate, accessibilityLabel: canDuplicate ? i18n.t("duplicate") : i18n.t("duplicateUnavailable"), onClick: onDuplicate, children: i18n.t("duplicate") }),
1402
- /* @__PURE__ */ jsx7(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
1597
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: [
1598
+ /* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
1599
+ /* @__PURE__ */ jsx9(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
1600
+ /* @__PURE__ */ jsx9(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
1601
+ /* @__PURE__ */ jsx9(Button2, { disabled: !canDuplicate, accessibilityLabel: canDuplicate ? i18n.t("duplicate") : i18n.t("duplicateUnavailable"), onClick: onDuplicate, children: i18n.t("duplicate") }),
1602
+ /* @__PURE__ */ jsx9(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
1403
1603
  ] }),
1404
- !canDuplicate ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("duplicateUnavailable") }) : null
1604
+ !canDuplicate ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("duplicateUnavailable") }) : null
1405
1605
  ] });
1406
1606
  }
1407
1607
  function DeleteConfirmation({ block, config, i18n, onCancel, onConfirm }) {
1408
- 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: [
1409
- /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
1410
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
1411
- /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1412
- /* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
1413
- /* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
1608
+ return /* @__PURE__ */ jsx9("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs7("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-delete-confirmation-title", children: [
1609
+ /* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
1610
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
1611
+ /* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
1612
+ /* @__PURE__ */ jsx9(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
1613
+ /* @__PURE__ */ jsx9(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
1414
1614
  ] })
1415
1615
  ] }) });
1416
1616
  }
1417
1617
  function LeaveConfirmation({ i18n, onCancel, onConfirm }) {
1418
- 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: [
1419
- /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
1420
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
1421
- /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
1422
- /* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
1423
- /* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
1618
+ return /* @__PURE__ */ jsx9("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs7("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-leave-confirmation-title", children: [
1619
+ /* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
1620
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
1621
+ /* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
1622
+ /* @__PURE__ */ jsx9(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
1623
+ /* @__PURE__ */ jsx9(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
1424
1624
  ] })
1425
1625
  ] }) });
1426
1626
  }
@@ -1432,8 +1632,8 @@ function inspectorFieldConfig(name, field) {
1432
1632
  }
1433
1633
  var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
1434
1634
  function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
1435
- const [assetError, setAssetError] = useState3(null);
1436
- const [selectingAsset, setSelectingAsset] = useState3(false);
1635
+ const [assetError, setAssetError] = useState4(null);
1636
+ const [selectingAsset, setSelectingAsset] = useState4(false);
1437
1637
  const definition = registry?.getBlock(block.type);
1438
1638
  const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
1439
1639
  const [name, field] = entry;
@@ -1442,20 +1642,20 @@ function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n,
1442
1642
  (groups[group] ??= []).push([name, configuredField]);
1443
1643
  return groups;
1444
1644
  }, {}) : {};
1445
- return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
1446
- /* @__PURE__ */ jsxs5("header", { className: "pb-inspector__header", children: [
1447
- /* @__PURE__ */ jsx7(Badge3, { children: block.type }),
1448
- /* @__PURE__ */ jsxs5("div", { children: [
1449
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
1450
- /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
1645
+ return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
1646
+ /* @__PURE__ */ jsxs7("header", { className: "pb-inspector__header", children: [
1647
+ /* @__PURE__ */ jsx9(Badge3, { children: block.type }),
1648
+ /* @__PURE__ */ jsxs7("div", { children: [
1649
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
1650
+ /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
1451
1651
  ] })
1452
1652
  ] }),
1453
- 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, productPicker, pageId, blockId: block.id, i18n, onChange: (content) => onChange({ content }) }) }) : null,
1454
- block.type === "core.image" ? /* @__PURE__ */ jsxs5(InspectorSection, { title: "Image", children: [
1455
- /* @__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, productPicker, pageId, blockId: block.id, i18n, onChange: (src) => onChange({ src }) }),
1456
- /* @__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, productPicker, pageId, blockId: block.id, i18n, onChange: (alt) => onChange({ alt }) }),
1457
- assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1458
- /* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
1653
+ block.type === "core.text" ? /* @__PURE__ */ jsx9(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (content) => onChange({ content }) }) }) : null,
1654
+ block.type === "core.image" ? /* @__PURE__ */ jsxs7(InspectorSection, { title: "Image", children: [
1655
+ /* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (src) => onChange({ src }) }),
1656
+ /* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (alt) => onChange({ alt }) }),
1657
+ assetPicker ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1658
+ /* @__PURE__ */ jsx9(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
1459
1659
  setSelectingAsset(true);
1460
1660
  setAssetError(null);
1461
1661
  try {
@@ -1467,13 +1667,13 @@ function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n,
1467
1667
  setSelectingAsset(false);
1468
1668
  }
1469
1669
  })(), children: selectingAsset ? "\u6B63\u5728\u9009\u62E9\u7D20\u6750\u2026" : "\u9009\u62E9\u7D20\u6750" }),
1470
- assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
1670
+ assetError ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
1471
1671
  ] }) : null
1472
1672
  ] }) : null,
1473
- 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: field.persist === false ? settings?.values[name] : block.props[name], registry, disabled: disabled || field.persist === false && (!settings || Boolean(settings.disabled)), assetPicker, productPicker, pageId, blockId: block.id, i18n, onChange: (value) => field.persist === false ? settings?.onChange(name, value) : onChange({ [name]: value }) }, name)) }, group)),
1673
+ Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx9(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx9(InspectorField, { name, field, value: field.persist === false ? settings?.values[name] : block.props[name], registry, disabled: disabled || field.persist === false && (!settings || Boolean(settings.disabled)), assetPicker, productPicker, pageId, blockId: block.id, i18n, onChange: (value) => field.persist === false ? settings?.onChange(name, value) : onChange({ [name]: value }) }, name)) }, group)),
1474
1674
  Object.values(definition?.fields ?? {}).some((field) => field.persist === false) ? settings?.footer : null,
1475
- 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,
1476
- 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) => {
1675
+ appearanceControls && definition?.variants?.length ? /* @__PURE__ */ jsx9(InspectorSection, { title: "\u5916\u89C2", children: /* @__PURE__ */ jsx9(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,
1676
+ appearanceControls && definition ? /* @__PURE__ */ jsx9(InspectorSection, { title: "\u6837\u5F0F\u8986\u76D6", defaultOpen: false, children: appearanceTokens.map(([token, label]) => /* @__PURE__ */ jsx9(TextField, { label, value: block.style[token] ?? "", placeholder: "\u7EE7\u627F\u9875\u9762\u6216\u6A21\u677F\u8BBE\u7F6E", autoComplete: "off", disabled, onChange: (value) => {
1477
1677
  const style = { ...block.style };
1478
1678
  if (value.trim()) style[token] = value;
1479
1679
  else delete style[token];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standhigher/puck-page-builder",
3
- "version": "0.12.5",
3
+ "version": "0.12.6",
4
4
  "description": "React PageDocument editor, extension registry, and web renderer for BestTrack page experiences.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/styles.css CHANGED
@@ -90,16 +90,27 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
90
90
  .pb-canvas-area { display: flex; flex-direction: column; gap: var(--builder-space-3); min-width: 0; min-height: 0; padding: var(--builder-space-4); overflow: auto; background: var(--builder-canvas-bg); }
91
91
  .pb-canvas-toolbar { padding: var(--builder-space-2); border: 1px solid var(--builder-border); border-radius: var(--builder-panel-radius); background: var(--builder-surface); }
92
92
  .pb-canvas-stage { position: relative; display: block; padding: 0; height: auto; }
93
- .pb-canvas-frame { width: 100%; height: auto; margin: 0 auto; overflow: visible; border: 1px solid var(--builder-border); border-radius: var(--builder-canvas-radius); background: var(--builder-surface); box-shadow: var(--builder-canvas-shadow); transition: width 160ms ease, transform 160ms ease; }
93
+ .pb-canvas-frame { --puck-color-line-placeholder: var(--builder-accent); --puck-line-placeholder-width: 3px; width: 100%; height: auto; margin: 0 auto; overflow: visible; border: 1px solid var(--builder-border); border-radius: var(--builder-canvas-radius); background: var(--builder-surface); box-shadow: var(--builder-canvas-shadow); transition: width 160ms ease, transform 160ms ease; }
94
94
  .pb-canvas-frame iframe { display: block; width: 100%; height: auto !important; min-height: 0 !important; border: 0; }
95
95
  .pb-canvas-frame [class*="PuckPreview"] { position: relative; height: auto !important; }
96
- .pb-canvas-drop-target { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; min-height: 12rem; border: 2px dashed var(--builder-accent); border-radius: var(--builder-canvas-radius); color: var(--builder-accent); background: color-mix(in srgb, var(--builder-accent-subtle) 88%, transparent); font-weight: 600; pointer-events: auto; }
96
+ .pb-canvas-frame [data-puck-component] { cursor: grab; }
97
+ .pb-canvas-frame [data-puck-component]:active, .pb-canvas-frame [data-dnd-dragging] { cursor: grabbing; }
98
+ .pb-canvas-frame [data-puck-disabled] { cursor: pointer; }
99
+ .pb-canvas-frame [data-puck-component] [contenteditable="true"], .pb-canvas-frame [data-puck-component] input, .pb-canvas-frame [data-puck-component] textarea { cursor: text; }
100
+ .pb-canvas-drop-target { position: absolute; inset: 0; z-index: 2; display: grid; place-items: start center; min-height: 12rem; border: 0; border-radius: var(--builder-canvas-radius); color: var(--builder-accent); background: transparent; font-weight: 600; pointer-events: auto; }
101
+ .pb-canvas-drop-target__hint { position: sticky; top: var(--builder-space-3); z-index: 1; margin-top: var(--builder-space-3); padding: 6px 10px; border: 1px solid color-mix(in srgb, var(--builder-accent) 28%, transparent); border-radius: 999px; color: var(--builder-accent); background: color-mix(in srgb, var(--builder-surface) 92%, var(--builder-accent-subtle)); box-shadow: 0 4px 12px rgb(15 23 42 / 10%); font-size: 12px; line-height: 1.2; pointer-events: none; }
102
+ .pb-canvas-drop-target:has(.pb-canvas-drop-marker) > .pb-canvas-drop-target__hint { opacity: 0; }
103
+ .pb-canvas-drop-marker { position: absolute; z-index: 2; height: 3px; min-height: 3px; border-radius: 999px; background: var(--builder-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--builder-accent) 16%, transparent), 0 2px 8px color-mix(in srgb, var(--builder-accent) 38%, transparent); transform: translateY(-50%); pointer-events: none; }
104
+ .pb-canvas-drop-marker::before, .pb-canvas-drop-marker::after { position: absolute; top: 50%; width: 7px; height: 7px; border-radius: 50%; background: var(--builder-accent); box-shadow: 0 1px 4px color-mix(in srgb, var(--builder-accent) 38%, transparent); content: ""; transform: translateY(-50%); }
105
+ .pb-canvas-drop-marker::before { left: -2px; }
106
+ .pb-canvas-drop-marker::after { right: -2px; }
107
+ .pb-canvas-drop-marker__label { position: absolute; top: -8px; left: 50%; padding: 3px 8px; border-radius: 999px; color: var(--builder-accent); background: var(--builder-surface); box-shadow: 0 2px 8px rgb(15 23 42 / 12%); font-size: 11px; line-height: 1.2; white-space: nowrap; transform: translate(-50%, -100%); pointer-events: none; }
97
108
  .pb-canvas-frame--tablet { width: min(100%, 768px); }
98
109
  .pb-canvas-frame--mobile { width: min(100%, 390px); }
99
110
  .pb-canvas-frame--full { width: 100%; }
100
- .pb-canvas-frame--zoom-50 { transform: scale(.5); transform-origin: top center; }
101
- .pb-canvas-frame--zoom-70 { transform: scale(.7); transform-origin: top center; }
102
- .pb-canvas-frame--zoom-100 { transform: scale(1); }
111
+ .pb-canvas-frame--zoom-50 { zoom: 0.5; }
112
+ .pb-canvas-frame--zoom-70 { zoom: 0.7; }
113
+ .pb-canvas-frame--zoom-100 { zoom: 1; }
103
114
  .pb-canvas-overlay { position: absolute; top: var(--builder-space-2); left: 50%; display: flex; gap: var(--builder-space-2); padding: var(--builder-space-1) var(--builder-space-2); border: 2px solid var(--builder-accent); border-radius: var(--builder-control-radius); color: var(--builder-accent); background: var(--builder-surface); font-size: 12px; font-weight: 600; transform: translateX(-50%); box-shadow: var(--builder-canvas-shadow); pointer-events: none; }
104
115
  .pb-canvas-overlay span:last-child { color: var(--builder-text-muted); font-weight: 500; }
105
116
  .pb-collapsed-actions { display: flex; gap: var(--builder-space-2); }
@@ -115,6 +126,8 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
115
126
  .pb-document-block-row { display: flex; align-items: center; gap: var(--builder-space-2); padding: var(--builder-space-2); border: 1px solid transparent; border-radius: var(--builder-panel-radius); background: transparent; }
116
127
  .pb-document-block-row--library { width: 100%; flex-direction: column; align-items: flex-start; text-align: left; color: var(--builder-text); font: inherit; cursor: grab; }
117
128
  .pb-document-block-row--library:active { cursor: grabbing; }
129
+ .pb-document-block-row--library[aria-disabled="true"] { cursor: default; }
130
+ .pb-document-block-row--library[type="button"] { cursor: pointer; }
118
131
  .pb-library-block-title { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: var(--builder-space-2); }
119
132
  .pb-library-block-drag-hint { display: inline-flex; color: var(--builder-text-muted); pointer-events: none; }
120
133
  .pb-library-block-drag-hint svg { width: 16px; height: 16px; }
@@ -128,13 +141,14 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
128
141
  .pb-toast-host .Polaris-Frame { min-height: 0; width: 0; height: 0; overflow: visible; background: transparent; }
129
142
  .pb-toast-host .Polaris-Frame__Skip,
130
143
  .pb-toast-host .Polaris-Frame__Main { display: none; }
131
- .pb-document-canvas__text, .pb-document-canvas__image { max-width: 760px; margin: 0 auto; padding: 48px; color: #1d2939; background: #fff; cursor: pointer; }
144
+ .pb-document-canvas__text, .pb-document-canvas__image { max-width: 760px; margin: 0 auto; padding: 48px; color: #1d2939; background: #fff; cursor: grab; }
145
+ .pb-document-canvas__extension { cursor: grab; }
132
146
  .pb-document-canvas__text { font-size: clamp(32px, 4vw, 52px); font-weight: 650; line-height: 1.1; letter-spacing: -.03em; }
133
147
  .pb-document-canvas__text p { margin: 0; }
134
148
  .pb-document-canvas__text p[contenteditable="true"] { /* outline: 1px dashed var(--builder-accent); outline-offset: 8px; */ cursor: text; }
135
149
  .pb-document-canvas__image { padding-top: 0; }
136
150
  .pb-document-canvas__image img { display: block; width: 100%; border-radius: 12px; }
137
- .pb-document-canvas__image input { width: 100%; margin-top: var(--builder-space-3); padding: var(--builder-space-2); border: 1px solid var(--builder-accent); border-radius: var(--builder-control-radius); color: var(--builder-text); background: var(--builder-surface); font: inherit; }
151
+ .pb-document-canvas__image input { width: 100%; margin-top: var(--builder-space-3); padding: var(--builder-space-2); border: 1px solid var(--builder-accent); border-radius: var(--builder-control-radius); color: var(--builder-text); background: var(--builder-surface); font: inherit; cursor: text; }
138
152
  .pb-document-export { max-height: 420px; padding: var(--builder-space-3); overflow: auto; border-radius: var(--builder-control-radius); color: var(--builder-text); background: var(--builder-surface-subtle); font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; }
139
153
  .pb-web-renderer { min-height: 100vh; padding: 72px clamp(24px, 10vw, 180px); color: #1d2939; background: #fff; font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
140
154
  .pb-web-renderer__text { max-width: 760px; margin: 0 auto 40px; font-size: clamp(32px, 5vw, 64px); font-weight: 650; line-height: 1.08; letter-spacing: -.04em; }