@standhigher/puck-page-builder 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode } from 'react';
2
- import { RenderTarget, PageDocument } from './schema.js';
2
+ import { RenderTarget, JsonValue, PageDocument } from './schema.js';
3
3
  import { ThemeTokens } from './theme.js';
4
4
 
5
5
  type ExtensionTarget = RenderTarget | "all";
@@ -12,12 +12,26 @@ type ValidationIssue = {
12
12
  type FieldConfig = {
13
13
  field: string;
14
14
  label?: string;
15
+ /** Product-facing editor metadata. Custom field components remain supported. */
16
+ control?: "text" | "textarea" | "url";
17
+ description?: string;
18
+ group?: string;
15
19
  required?: boolean;
16
20
  };
17
21
  type FieldProps<T = unknown> = {
18
22
  value: T;
19
23
  onChange(value: T): void;
20
24
  };
25
+ /**
26
+ * Props available only to an extension's editor-canvas renderer.
27
+ * They are separate from Web rendering so editor interaction cannot invoke a
28
+ * host Runtime or become persisted operational data.
29
+ */
30
+ type BlockEditorProps<P = Record<string, unknown>> = P & {
31
+ blockId: string;
32
+ selected: boolean;
33
+ onPropsChange(props: Record<string, JsonValue>): void;
34
+ };
21
35
  interface BlockDefinition<P = Record<string, unknown>> {
22
36
  type: string;
23
37
  version: number;
@@ -26,7 +40,10 @@ interface BlockDefinition<P = Record<string, unknown>> {
26
40
  targets: ExtensionTarget[];
27
41
  defaultProps: P;
28
42
  fields: Record<string, FieldConfig>;
29
- render: Partial<Record<RenderTarget, ComponentType<P>>>;
43
+ render: Partial<Record<RenderTarget, ComponentType<P>>> & {
44
+ /** Optional edit-mode renderer. WebRenderer never uses this surface. */
45
+ editor?: ComponentType<BlockEditorProps<P>>;
46
+ };
30
47
  defaultVariant?: string;
31
48
  variants?: BlockVariantDefinition[];
32
49
  dataSources?: string[];
@@ -168,4 +185,4 @@ declare class ExtensionRegistry {
168
185
  declare function createExtensionRegistry(extensions: PageBuilderExtension[], options?: ExtensionRegistryOptions): ExtensionRegistry;
169
186
  declare function createTemplateRegistry(templates: TemplateDefinition[], blocks?: BlockDefinition[]): TemplateRegistry;
170
187
 
171
- export { type BlockDefinition, type BlockVariantDefinition, type DataSourceDefinition, type EditorAction, type EditorActionPosition, type ExtensionActionContext, ExtensionRegistry, ExtensionRegistryError, type ExtensionRegistryErrorCode, type ExtensionRegistryOptions, type ExtensionTarget, type FieldConfig, type FieldDefinition, type FieldProps, type LifecycleHooks, type PageBuilderExtension, type RendererDefinition, type TemplateDefinition, TemplateRegistry, type TemplateSource, type UISlotContribution, type UISlotName, type ValidationIssue, createExtensionRegistry, createTemplateRegistry };
188
+ export { type BlockDefinition, type BlockEditorProps, type BlockVariantDefinition, type DataSourceDefinition, type EditorAction, type EditorActionPosition, type ExtensionActionContext, ExtensionRegistry, ExtensionRegistryError, type ExtensionRegistryErrorCode, type ExtensionRegistryOptions, type ExtensionTarget, type FieldConfig, type FieldDefinition, type FieldProps, type LifecycleHooks, type PageBuilderExtension, type RendererDefinition, type TemplateDefinition, TemplateRegistry, type TemplateSource, type UISlotContribution, type UISlotName, type ValidationIssue, createExtensionRegistry, createTemplateRegistry };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { ExtensionRegistry } from './extensions.js';
4
- export { BlockDefinition, BlockVariantDefinition, DataSourceDefinition, EditorAction, EditorActionPosition, ExtensionActionContext, ExtensionRegistryError, ExtensionRegistryErrorCode, ExtensionRegistryOptions, ExtensionTarget, FieldConfig, FieldDefinition, FieldProps, LifecycleHooks, PageBuilderExtension, RendererDefinition, TemplateDefinition, TemplateRegistry, TemplateSource, UISlotContribution, UISlotName, ValidationIssue, createExtensionRegistry, createTemplateRegistry } from './extensions.js';
4
+ export { BlockDefinition, BlockEditorProps, BlockVariantDefinition, DataSourceDefinition, EditorAction, EditorActionPosition, ExtensionActionContext, ExtensionRegistryError, ExtensionRegistryErrorCode, ExtensionRegistryOptions, ExtensionTarget, FieldConfig, FieldDefinition, FieldProps, LifecycleHooks, PageBuilderExtension, RendererDefinition, TemplateDefinition, TemplateRegistry, TemplateSource, UISlotContribution, UISlotName, ValidationIssue, createExtensionRegistry, createTemplateRegistry } from './extensions.js';
5
5
  import { PageDocument, BlockNode, JsonValue } from './schema.js';
6
6
  export { BlockStyleOverrides, DataBinding, PageDocumentIssue, PageDocumentMigration, PageDocumentSchemaVersion, PageSettings, RenderTarget, createPageDocument, migratePageDocument, validatePageDocument } from './schema.js';
7
7
  export { ThemeTokenName, ThemeTokens, mergeThemeTokens, normalizeThemeTokens, systemThemeTokens, toThemeStyle } from './theme.js';
package/dist/index.js CHANGED
@@ -342,12 +342,35 @@ function PropertyPanel({
342
342
 
343
343
  // src/editor/shell/PageDocumentEditorShell.tsx
344
344
  import { Puck as Puck2, usePuck } from "@puckeditor/core";
345
- import { Badge as Badge2, Banner, BlockStack as BlockStack2, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack2, Text as Text2, TextField } from "@shopify/polaris";
345
+ import { Badge as Badge2, Banner, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack2, Text as Text2, TextField } from "@shopify/polaris";
346
346
  import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2 } from "@shopify/polaris-icons";
347
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
347
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
348
+
349
+ // src/adapters/puck/canvas-extension-block.tsx
350
+ import { registerOverlayPortal } from "@puckeditor/core";
351
+ import { useEffect, useRef as useRef2 } from "react";
352
+ import { jsx as jsx3 } from "react/jsx-runtime";
353
+ function CanvasExtensionBlock({ active, children, label, onSelect }) {
354
+ const editorRef = useRef2(null);
355
+ useEffect(() => active ? registerOverlayPortal(editorRef.current, { disableDrag: true }) : void 0, [active]);
356
+ 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) => {
357
+ if (event.key === "Enter" || event.key === " ") onSelect();
358
+ }, children });
359
+ }
348
360
 
349
361
  // src/adapters/puck/page-document-config.tsx
350
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
362
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
363
+ function canvasFieldFallback({ block, props, registry, onPropsChange }) {
364
+ const fields = Object.entries(block.fields);
365
+ if (!fields.length) return null;
366
+ 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]) => {
367
+ const Field = registry?.getField(field.field)?.component;
368
+ return Field ? /* @__PURE__ */ jsxs3("label", { style: { display: "grid", gap: 4, fontSize: 12, fontWeight: 600 }, children: [
369
+ field.label ?? name,
370
+ /* @__PURE__ */ jsx4(Field, { value: props[name], onChange: (value) => onPropsChange({ [name]: value }) })
371
+ ] }, name) : null;
372
+ }) });
373
+ }
351
374
  function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId, registry) {
352
375
  const extensionComponents = Object.fromEntries((registry?.blocks ?? []).flatMap((block) => {
353
376
  const BlockRenderer = block.render.web;
@@ -355,9 +378,12 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
355
378
  return [[block.type, {
356
379
  render: (props) => {
357
380
  const id = typeof props.id === "string" ? props.id : `unknown-${block.type}`;
358
- return /* @__PURE__ */ jsx3("section", { className: "pb-document-canvas__extension", "aria-label": `Select ${block.label} in canvas`, role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
359
- if (event.key === "Enter" || event.key === " ") onSelect(id);
360
- }, children: /* @__PURE__ */ jsx3(BlockRenderer, { ...props }) });
381
+ const EditorRenderer = block.render.editor;
382
+ const active = id === selectedBlockId;
383
+ 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: [
384
+ /* @__PURE__ */ jsx4(BlockRenderer, { ...props }),
385
+ active ? canvasFieldFallback({ block, props, registry, onPropsChange: (next) => onPropsChange(id, next) }) : null
386
+ ] }) });
361
387
  }
362
388
  }]];
363
389
  }));
@@ -368,10 +394,10 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
368
394
  const id = typeof props.id === "string" ? props.id : "unknown-text";
369
395
  const content = typeof props.content === "string" ? props.content : "";
370
396
  const editable = id === selectedBlockId;
371
- return /* @__PURE__ */ jsx3("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) => {
397
+ 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) => {
372
398
  if (event.key === "Enter" || event.key === " ") onSelect(id);
373
- }, children: /* @__PURE__ */ jsx3("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
374
- if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" });
399
+ }, children: /* @__PURE__ */ jsx4("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
400
+ if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" }, true);
375
401
  }, children: content }) });
376
402
  }
377
403
  },
@@ -384,8 +410,8 @@ function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId,
384
410
  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) => {
385
411
  if (event.key === "Enter" || event.key === " ") onSelect(id);
386
412
  }, children: [
387
- /* @__PURE__ */ jsx3("img", { src, alt }),
388
- editable ? /* @__PURE__ */ jsx3("input", { "aria-label": "\u753B\u5E03\u56FE\u7247 URL", value: src, onChange: (event) => onPropsChange(id, { src: event.currentTarget.value }), onClick: (event) => event.stopPropagation() }) : null
413
+ /* @__PURE__ */ jsx4("img", { src, alt }),
414
+ 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
389
415
  ] });
390
416
  }
391
417
  },
@@ -435,8 +461,8 @@ function fromEngineData(data, base, registry) {
435
461
  }
436
462
 
437
463
  // src/editor/context/EditorContext.tsx
438
- import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef as useRef2, useState as useState2 } from "react";
439
- import { jsx as jsx4 } from "react/jsx-runtime";
464
+ import { createContext, useCallback, useContext, useEffect as useEffect2, useMemo, useReducer, useRef as useRef3, useState as useState2 } from "react";
465
+ import { jsx as jsx5 } from "react/jsx-runtime";
440
466
  function historyReducer(state, action) {
441
467
  if (action.type === "select") return { ...state, selectedBlockId: action.id };
442
468
  if (action.type === "device") return { ...state, device: action.device };
@@ -470,8 +496,8 @@ function defaultBlock(type, blocks, registry) {
470
496
  }
471
497
  function EditorProvider({ initialDocument, registry, loadState = "ready", leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
472
498
  const [history, dispatch] = useReducer(historyReducer, initialDocument, (document) => ({ document, selectedBlockId: document.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document }));
473
- const historyRef = useRef2(history);
474
- useEffect(() => {
499
+ const historyRef = useRef3(history);
500
+ useEffect2(() => {
475
501
  historyRef.current = history;
476
502
  }, [history]);
477
503
  const [canvasSelectionRequest, setCanvasSelectionRequest] = useState2(null);
@@ -496,11 +522,11 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
496
522
  const blocks = current.document.blocks.map((block) => block.id === id ? { ...block, props: { ...block.props, ...props } } : block);
497
523
  replace({ ...current.document, blocks }, id);
498
524
  }, [editable, replace]);
499
- useEffect(() => {
525
+ useEffect2(() => {
500
526
  onDocumentChange?.(history.document);
501
527
  }, [history.document, onDocumentChange]);
502
528
  const isDirty = JSON.stringify(history.document) !== JSON.stringify(history.savedDocument);
503
- useEffect(() => {
529
+ useEffect2(() => {
504
530
  if (!isDirty || typeof window === "undefined") return;
505
531
  const confirmLeave = (event) => {
506
532
  event.preventDefault();
@@ -509,7 +535,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
509
535
  window.addEventListener("beforeunload", confirmLeave);
510
536
  return () => window.removeEventListener("beforeunload", confirmLeave);
511
537
  }, [isDirty, leaveWarning]);
512
- useEffect(() => {
538
+ useEffect2(() => {
513
539
  if (typeof window === "undefined") return;
514
540
  const keydown = (event) => {
515
541
  const target = event.target;
@@ -604,7 +630,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
604
630
  markSaved: () => dispatch({ type: "saved" })
605
631
  };
606
632
  }, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, registry, replace, requestCanvasSelection, selectedBlock, updateBlockProps, updateFromCanvas]);
607
- return /* @__PURE__ */ jsx4(EditorContext.Provider, { value, children });
633
+ return /* @__PURE__ */ jsx5(EditorContext.Provider, { value, children });
608
634
  }
609
635
  function useEditorContext() {
610
636
  const context = useContext(EditorContext);
@@ -692,7 +718,7 @@ function blockIdAtRelativeY(ids, relativeY) {
692
718
  }
693
719
 
694
720
  // src/editor/shell/PageDocumentEditorShell.tsx
695
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
721
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
696
722
  var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
697
723
  function blockLabel(block, registry) {
698
724
  return blockTypeLabel(block.type, registry);
@@ -703,25 +729,25 @@ function blockTypeLabel(type, registry) {
703
729
  return registry?.getBlock(type)?.label ?? type;
704
730
  }
705
731
  function PageDocumentEditorShell(props) {
706
- return /* @__PURE__ */ jsx5(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, loadState: props.loadState, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx5(PageDocumentEditor, { ...props }) });
732
+ return /* @__PURE__ */ jsx6(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, loadState: props.loadState, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx6(PageDocumentEditor, { ...props }) });
707
733
  }
708
734
  function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPublish }) {
709
735
  const editor = useEditorContext();
710
736
  const [blockView, setBlockView] = useState3("blocks");
711
737
  const [draggingLibraryType, setDraggingLibraryType] = useState3(null);
712
- const canvasFrameRef = useRef3(null);
738
+ const canvasFrameRef = useRef4(null);
713
739
  const [canvasMutationVersion, setCanvasMutationVersion] = useState3(0);
714
740
  const [request, setRequest] = useState3("idle");
715
741
  const [notice, setNotice] = useState3(null);
716
742
  const i18n = createAdminI18n(adminLocale);
717
743
  const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
718
744
  const { confirmCanvasSelection, selectedBlockId, updateBlockProps } = editor;
719
- const updateFromCanvasInput = useCallback2((id, props) => {
720
- setCanvasMutationVersion((version) => version + 1);
745
+ const updateFromCanvasInput = useCallback2((id, props, preserveCanvasValue = false) => {
746
+ if (preserveCanvasValue) setCanvasMutationVersion((version) => version + 1);
721
747
  updateBlockProps(id, props);
722
748
  }, [updateBlockProps]);
723
749
  const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry), [confirmCanvasSelection, registry, selectedBlockId, updateFromCanvasInput]);
724
- if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx5(EditorStatus, { state: editor.loadState });
750
+ if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx6(EditorStatus, { state: editor.loadState });
725
751
  const blockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
726
752
  const addFromLibrary = (type, beforeId) => {
727
753
  const id = editor.addBlock(type, beforeId);
@@ -785,12 +811,12 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
785
811
  setRequest("idle");
786
812
  }
787
813
  };
788
- return /* @__PURE__ */ jsx5(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => editor.updateFromCanvas(fromEngineData(data, editor.document, registry)), children: /* @__PURE__ */ jsxs4(Puck2.Layout, { children: [
789
- /* @__PURE__ */ jsx5(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion }),
814
+ return /* @__PURE__ */ jsx6(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => editor.updateFromCanvas(fromEngineData(data, editor.document, registry)), children: /* @__PURE__ */ jsxs4(Puck2.Layout, { children: [
815
+ /* @__PURE__ */ jsx6(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion }),
790
816
  /* @__PURE__ */ jsxs4("div", { className: "pb-shell pb-shell--v04", "data-testid": "page-document-editor", "data-page-id": editor.document.pageId, "data-dirty": editor.isDirty, "data-editor-state": editor.loadState, children: [
791
- /* @__PURE__ */ jsx5("header", { className: "pb-header", children: /* @__PURE__ */ jsxs4(InlineStack2, { align: "space-between", blockAlign: "center", gap: "300", wrap: false, children: [
817
+ /* @__PURE__ */ jsx6("header", { className: "pb-header", children: /* @__PURE__ */ jsxs4(InlineStack2, { align: "space-between", blockAlign: "center", gap: "300", wrap: false, children: [
792
818
  /* @__PURE__ */ jsxs4("div", { className: "pb-page-title", children: [
793
- /* @__PURE__ */ jsx5(Text2, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
819
+ /* @__PURE__ */ jsx6(Text2, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
794
820
  /* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
795
821
  "PageDocument V",
796
822
  editor.document.schemaVersion,
@@ -799,54 +825,54 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
799
825
  ] })
800
826
  ] }),
801
827
  /* @__PURE__ */ jsxs4(InlineStack2, { gap: "150", blockAlign: "center", wrap: false, children: [
802
- /* @__PURE__ */ jsx5(Badge2, { tone: editor.isDirty ? "attention" : "success", children: editor.isDirty ? i18n.t("unsaved") : i18n.t("saved") }),
803
- /* @__PURE__ */ jsx5(Button2, { disabled: !onSave || !editor.isDirty || request !== "idle", onClick: () => void save(), children: request === "saving" ? i18n.t("saving") : i18n.t("save") }),
804
- /* @__PURE__ */ jsx5(Button2, { variant: "primary", disabled: !onPublish || request !== "idle", onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
805
- /* @__PURE__ */ jsx5(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
806
- /* @__PURE__ */ jsx5(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
828
+ /* @__PURE__ */ jsx6(Badge2, { tone: editor.isDirty ? "attention" : "success", children: editor.isDirty ? i18n.t("unsaved") : i18n.t("saved") }),
829
+ /* @__PURE__ */ jsx6(Button2, { disabled: !onSave || !editor.isDirty || request !== "idle", onClick: () => void save(), children: request === "saving" ? i18n.t("saving") : i18n.t("save") }),
830
+ /* @__PURE__ */ jsx6(Button2, { variant: "primary", disabled: !onPublish || request !== "idle", onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
831
+ /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
832
+ /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
807
833
  ] })
808
834
  ] }) }),
809
- editor.loadState === "success" ? /* @__PURE__ */ jsx5(Banner, { tone: "success", children: i18n.t("success") }) : null,
810
- notice ? /* @__PURE__ */ jsx5(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
835
+ editor.loadState === "success" ? /* @__PURE__ */ jsx6(Banner, { tone: "success", children: i18n.t("success") }) : null,
836
+ notice ? /* @__PURE__ */ jsx6(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
811
837
  /* @__PURE__ */ jsxs4("div", { className: "pb-workspace pb-workspace--document", children: [
812
838
  /* @__PURE__ */ jsxs4("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
813
- /* @__PURE__ */ jsx5(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks", variant: "tertiary", onClick: () => setBlockView("blocks") }),
814
- /* @__PURE__ */ jsx5(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline", variant: "tertiary", onClick: () => setBlockView("outline") })
839
+ /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks", variant: "tertiary", onClick: () => setBlockView("blocks") }),
840
+ /* @__PURE__ */ jsx6(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline", variant: "tertiary", onClick: () => setBlockView("outline") })
815
841
  ] }),
816
842
  /* @__PURE__ */ jsxs4("aside", { className: "pb-left-panel", "aria-label": "PageDocument \u533A\u5757", children: [
817
- /* @__PURE__ */ jsx5(InlineStack2, { align: "space-between", blockAlign: "center", children: /* @__PURE__ */ jsx5(Text2, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }) }),
818
- blockView === "blocks" ? /* @__PURE__ */ jsx5("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs4("div", { className: `pb-document-block-row pb-document-block-row--library ${editor.selectedBlock?.type === type ? "pb-document-block-row--selected" : ""}`, "data-block-type": type, "data-selected": editor.selectedBlock?.type === type, role: "listitem", draggable: editor.actionState.canAdd, "aria-label": `${blockTypeLabel(type, registry)}\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0${editor.selectedBlock?.type === type ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onDragStart: (event) => {
843
+ /* @__PURE__ */ jsx6(InlineStack2, { align: "space-between", blockAlign: "center", children: /* @__PURE__ */ jsx6(Text2, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }) }),
844
+ blockView === "blocks" ? /* @__PURE__ */ jsx6("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs4("div", { className: `pb-document-block-row pb-document-block-row--library ${editor.selectedBlock?.type === type ? "pb-document-block-row--selected" : ""}`, "data-block-type": type, "data-selected": editor.selectedBlock?.type === type, role: "listitem", draggable: editor.actionState.canAdd, "aria-label": `${blockTypeLabel(type, registry)}\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0${editor.selectedBlock?.type === type ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onDragStart: (event) => {
819
845
  event.dataTransfer.setData("application/x-page-document-block", type);
820
846
  event.dataTransfer.effectAllowed = "copy";
821
847
  setDraggingLibraryType(type);
822
848
  }, onDragEnd: () => setDraggingLibraryType(null), children: [
823
849
  /* @__PURE__ */ jsxs4("span", { className: "pb-library-block-title", children: [
824
- /* @__PURE__ */ jsx5(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
825
- /* @__PURE__ */ jsx5("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(DragHandleIcon2, {}) })
850
+ /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
851
+ /* @__PURE__ */ jsx6("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(DragHandleIcon2, {}) })
826
852
  ] }),
827
- /* @__PURE__ */ jsx5(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: type })
828
- ] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx5(Text2, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx5("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs4("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => editor.requestCanvasSelection(block.id), children: [
829
- /* @__PURE__ */ jsx5(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
830
- /* @__PURE__ */ jsx5(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
853
+ /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: type })
854
+ ] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx6(Text2, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx6("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs4("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => editor.requestCanvasSelection(block.id), children: [
855
+ /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
856
+ /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
831
857
  ] }, block.id)) })
832
858
  ] }),
833
859
  /* @__PURE__ */ jsxs4("main", { className: "pb-canvas-area", children: [
834
- /* @__PURE__ */ jsx5("div", { className: "pb-canvas-toolbar", children: /* @__PURE__ */ jsx5(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx5(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }) }),
860
+ /* @__PURE__ */ jsx6("div", { className: "pb-canvas-toolbar", children: /* @__PURE__ */ jsx6(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx6(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }) }),
835
861
  /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-stage", children: [
836
- /* @__PURE__ */ jsx5("div", { ref: canvasFrameRef, className: `pb-canvas-frame pb-canvas-frame--${editor.device}`, "data-device": editor.device, children: /* @__PURE__ */ jsx5(Puck2.Preview, {}) }),
862
+ /* @__PURE__ */ jsx6("div", { ref: canvasFrameRef, className: `pb-canvas-frame pb-canvas-frame--${editor.device}`, "data-device": editor.device, children: /* @__PURE__ */ jsx6(Puck2.Preview, {}) }),
837
863
  draggingLibraryType ? /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": "\u533A\u5757\u6295\u653E\u533A", onDragOver: (event) => event.preventDefault(), onDrop: dropFromLibrary, children: [
838
864
  "\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
839
865
  blockTypeLabel(draggingLibraryType, registry)
840
866
  ] }) : null,
841
867
  editor.selectedBlock ? /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
842
- /* @__PURE__ */ jsx5("span", { children: blockLabel(editor.selectedBlock, registry) }),
843
- /* @__PURE__ */ jsx5("span", { children: "Selected" })
868
+ /* @__PURE__ */ jsx6("span", { children: blockLabel(editor.selectedBlock, registry) }),
869
+ /* @__PURE__ */ jsx6("span", { children: "Selected" })
844
870
  ] }) : null
845
871
  ] })
846
872
  ] }),
847
873
  /* @__PURE__ */ jsxs4("aside", { className: "pb-right-panel", "aria-label": "PageDocument \u5C5E\u6027", children: [
848
- /* @__PURE__ */ jsx5(Text2, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
849
- editor.selectedBlock ? /* @__PURE__ */ jsx5(DocumentInspector, { block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props) }) : /* @__PURE__ */ jsx5(Text2, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
874
+ /* @__PURE__ */ jsx6(Text2, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
875
+ editor.selectedBlock ? /* @__PURE__ */ jsx6(DocumentInspector, { block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props) }) : /* @__PURE__ */ jsx6(Text2, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
850
876
  ] })
851
877
  ] })
852
878
  ] })
@@ -854,11 +880,11 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
854
880
  }
855
881
  function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion }) {
856
882
  const puck = usePuck();
857
- const lastSelectedId = useRef3(null);
858
- const lastSyncedData = useRef3(null);
859
- const lastCanvasMutationVersion = useRef3(0);
883
+ const lastSelectedId = useRef4(null);
884
+ const lastSyncedData = useRef4(null);
885
+ const lastCanvasMutationVersion = useRef4(0);
860
886
  const serializedData = JSON.stringify(data);
861
- useEffect2(() => {
887
+ useEffect3(() => {
862
888
  if (lastSyncedData.current === serializedData) return;
863
889
  lastSyncedData.current = serializedData;
864
890
  if (canvasMutationVersion > lastCanvasMutationVersion.current) {
@@ -867,13 +893,13 @@ function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canva
867
893
  }
868
894
  puck.dispatch({ type: "setData", data });
869
895
  }, [canvasMutationVersion, data, puck, serializedData]);
870
- useEffect2(() => {
896
+ useEffect3(() => {
871
897
  if (!requestedBlockId) return;
872
898
  const selector = puck.getSelectorForId(requestedBlockId);
873
899
  if (selector) puck.dispatch({ type: "setUi", ui: { itemSelector: selector } });
874
900
  }, [puck, requestedBlockId]);
875
901
  const selectedId = typeof puck.selectedItem?.props.id === "string" ? puck.selectedItem.props.id : null;
876
- useEffect2(() => {
902
+ useEffect3(() => {
877
903
  if (selectedId && lastSelectedId.current !== selectedId) {
878
904
  lastSelectedId.current = selectedId;
879
905
  onCanvasSelected(selectedId);
@@ -885,22 +911,62 @@ function EditorStatus({ state }) {
885
911
  const i18n = createAdminI18n();
886
912
  const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
887
913
  const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
888
- return /* @__PURE__ */ jsx5("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx5(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
914
+ return /* @__PURE__ */ jsx6("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx6(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
915
+ }
916
+ function InspectorSection({ title, children, defaultOpen = true }) {
917
+ return /* @__PURE__ */ jsxs4("details", { className: "pb-inspector-section", open: defaultOpen, children: [
918
+ /* @__PURE__ */ jsxs4("summary", { children: [
919
+ /* @__PURE__ */ jsx6("span", { children: title }),
920
+ /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\u2304" })
921
+ ] }),
922
+ /* @__PURE__ */ jsx6("div", { className: "pb-inspector-section__body", children })
923
+ ] });
924
+ }
925
+ function InspectorTextControl({ label, value, control, disabled, onChange }) {
926
+ const stringValue = typeof value === "string" ? value : "";
927
+ return /* @__PURE__ */ jsx6(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
928
+ }
929
+ function InspectorField({ name, field, value, registry, disabled, onChange }) {
930
+ const label = field.label ?? name;
931
+ const Field = registry?.getField(field.field)?.component;
932
+ return /* @__PURE__ */ jsxs4("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
933
+ /* @__PURE__ */ jsxs4("div", { className: "pb-inspector-field__heading", children: [
934
+ /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
935
+ field.description ? /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
936
+ ] }),
937
+ field.control ? /* @__PURE__ */ jsx6(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx6(Field, { value, onChange }) : null
938
+ ] });
939
+ }
940
+ function inspectorFieldConfig(name, field) {
941
+ const key = name.toLowerCase();
942
+ const group = /(?:href|url)/.test(key) ? "Links" : /(?:default|shipment|query|hide)/.test(key) ? "Tracking settings" : /(?:id|variant|theme)/.test(key) ? "Advanced" : "Content";
943
+ const description = field.description ?? (field.control === "textarea" ? "\u9002\u5408\u8F83\u957F\u6216\u591A\u884C\u7684\u5C55\u793A\u6587\u6848\u3002" : field.control === "url" ? "\u4F7F\u7528\u7AD9\u5185\u76F8\u5BF9\u8DEF\u5F84\u6216 HTTPS \u5730\u5740\u3002" : key.includes("shipment") ? "\u591A\u4E2A\u5305\u88F9\u6807\u7B7E\u4F7F\u7528 | \u5206\u9694\u3002" : key.includes("default") ? "\u4EC5\u7528\u4E8E\u7F16\u8F91\u5668\u548C\u7A7A\u72B6\u6001\u9884\u89C8\u3002" : void 0);
944
+ return { ...field, group: field.group ?? group, description };
889
945
  }
890
946
  function DocumentInspector({ block, registry, disabled, onChange }) {
891
947
  const definition = registry?.getBlock(block.type);
892
- return /* @__PURE__ */ jsxs4(BlockStack2, { gap: "300", "data-testid": "document-inspector", children: [
893
- /* @__PURE__ */ jsx5(Badge2, { children: block.type }),
894
- /* @__PURE__ */ jsx5(Text2, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
895
- block.type === "core.text" ? /* @__PURE__ */ jsx5(TextField, { label: "\u6587\u672C\u5185\u5BB9", value: typeof block.props.content === "string" ? block.props.content : "", onChange: (content) => onChange({ content }), autoComplete: "off", multiline: 4, disabled }) : null,
896
- block.type === "core.image" ? /* @__PURE__ */ jsxs4(Fragment, { children: [
897
- /* @__PURE__ */ jsx5(TextField, { label: "\u56FE\u7247 URL", value: typeof block.props.src === "string" ? block.props.src : "", onChange: (src) => onChange({ src }), autoComplete: "off", disabled }),
898
- /* @__PURE__ */ jsx5(TextField, { label: "\u66FF\u4EE3\u6587\u672C", value: typeof block.props.alt === "string" ? block.props.alt : "", onChange: (alt) => onChange({ alt }), autoComplete: "off", disabled })
948
+ const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
949
+ const [name, field] = entry;
950
+ const configuredField = inspectorFieldConfig(name, field);
951
+ const group = configuredField.group ?? "Content";
952
+ (groups[group] ??= []).push([name, configuredField]);
953
+ return groups;
954
+ }, {}) : {};
955
+ return /* @__PURE__ */ jsxs4("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
956
+ /* @__PURE__ */ jsxs4("header", { className: "pb-inspector__header", children: [
957
+ /* @__PURE__ */ jsx6(Badge2, { children: block.type }),
958
+ /* @__PURE__ */ jsxs4("div", { children: [
959
+ /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
960
+ /* @__PURE__ */ jsx6(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
961
+ ] })
962
+ ] }),
963
+ block.type === "core.text" ? /* @__PURE__ */ jsx6(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx6(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, onChange: (content) => onChange({ content }) }) }) : null,
964
+ block.type === "core.image" ? /* @__PURE__ */ jsxs4(InspectorSection, { title: "Image", children: [
965
+ /* @__PURE__ */ jsx6(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, onChange: (src) => onChange({ src }) }),
966
+ /* @__PURE__ */ jsx6(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, onChange: (alt) => onChange({ alt }) })
899
967
  ] }) : null,
900
- definition ? Object.entries(definition.fields).map(([name, field]) => {
901
- const Field = registry?.getField(field.field)?.component;
902
- return Field ? /* @__PURE__ */ jsx5(Field, { value: block.props[name], onChange: (value) => onChange({ ...block.props, [name]: value }) }, name) : null;
903
- }) : null
968
+ Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx6(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx6(InspectorField, { name, field, value: block.props[name], registry, disabled, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
969
+ definition ? /* @__PURE__ */ jsx6("p", { className: "pb-inspector__hint", children: "\u753B\u5E03\u4E2D\u5E26\u865A\u7EBF\u8FB9\u6846\u7684\u5185\u5BB9\u53EF\u76F4\u63A5\u7F16\u8F91\u3002" }) : null
904
970
  ] });
905
971
  }
906
972
  export {
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { WebRenderer, WebRendererProps } from './renderer.js';
2
- export { BlockDefinition, BlockVariantDefinition, DataSourceDefinition, EditorAction, EditorActionPosition, ExtensionActionContext, ExtensionRegistry, ExtensionRegistryError, ExtensionRegistryErrorCode, ExtensionRegistryOptions, ExtensionTarget, FieldConfig, FieldDefinition, FieldProps, LifecycleHooks, PageBuilderExtension, RendererDefinition, TemplateDefinition, TemplateRegistry, TemplateSource, UISlotContribution, UISlotName, ValidationIssue, createExtensionRegistry, createTemplateRegistry } from './extensions.js';
2
+ export { BlockDefinition, BlockEditorProps, BlockVariantDefinition, DataSourceDefinition, EditorAction, EditorActionPosition, ExtensionActionContext, ExtensionRegistry, ExtensionRegistryError, ExtensionRegistryErrorCode, ExtensionRegistryOptions, ExtensionTarget, FieldConfig, FieldDefinition, FieldProps, LifecycleHooks, PageBuilderExtension, RendererDefinition, TemplateDefinition, TemplateRegistry, TemplateSource, UISlotContribution, UISlotName, ValidationIssue, createExtensionRegistry, createTemplateRegistry } from './extensions.js';
3
3
  export { BlockNode, BlockStyleOverrides, DataBinding, JsonPrimitive, JsonValue, PageDocument, PageDocumentIssue, PageDocumentMigration, PageDocumentSchemaVersion, PageDocumentValidation, PageSettings, RenderTarget, createPageDocument, migratePageDocument, validatePageDocument } from './schema.js';
4
4
  export { ThemeTokenName, ThemeTokenValidation, ThemeTokens, mergeThemeTokens, normalizeThemeTokens, systemThemeTokens, toThemeStyle } from './theme.js';
5
5
  import 'react';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standhigher/puck-page-builder",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
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
@@ -13,7 +13,30 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
13
13
  .pb-tool-rail .Polaris-Button { min-width: 44px; min-height: 44px; }
14
14
  .pb-left-panel, .pb-right-panel { display: flex; flex-direction: column; gap: var(--builder-space-4); min-width: 0; padding: var(--builder-space-4); overflow: auto; background: var(--builder-surface); }
15
15
  .pb-left-panel { border-right: 1px solid var(--builder-border); }
16
- .pb-right-panel { border-left: 1px solid var(--builder-border); }
16
+ .pb-right-panel { border-left: 1px solid var(--builder-border); background: #f8fafc; }
17
+ .pb-right-panel > h2 { margin: 0; color: #475569; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; }
18
+ .pb-inspector { display: grid; gap: var(--builder-space-3); }
19
+ .pb-inspector__header { display: grid; gap: var(--builder-space-2); padding: var(--builder-space-3); border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; box-shadow: 0 1px 2px rgb(15 23 42 / 4%); }
20
+ .pb-inspector__header .Polaris-Badge { width: fit-content; max-width: 100%; overflow: hidden; color: #1d4ed8; background: #eff6ff; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
21
+ .pb-inspector__header p { margin: 0; }
22
+ .pb-inspector-section { overflow: hidden; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; box-shadow: 0 1px 2px rgb(15 23 42 / 3%); }
23
+ .pb-inspector-section > summary { display: flex; align-items: center; justify-content: space-between; min-height: 44px; padding: 0 var(--builder-space-3); color: #0f172a; font-size: 13px; font-weight: 650; cursor: pointer; list-style: none; }
24
+ .pb-inspector-section > summary::-webkit-details-marker { display: none; }
25
+ .pb-inspector-section > summary > span:last-child { color: #64748b; font-size: 16px; transition: transform 140ms ease; }
26
+ .pb-inspector-section:not([open]) > summary > span:last-child { transform: rotate(-90deg); }
27
+ .pb-inspector-section__body { display: grid; gap: var(--builder-space-3); padding: 0 var(--builder-space-3) var(--builder-space-3); border-top: 1px solid #f1f5f9; }
28
+ .pb-inspector-field { display: grid; gap: 6px; }
29
+ .pb-inspector-field__heading { display: grid; gap: 2px; }
30
+ .pb-inspector-field__heading p { margin: 0; }
31
+ .pb-inspector-field__heading p:first-child { color: #334155; font-size: 13px; }
32
+ .pb-inspector-field__heading p:last-child { color: #64748b; font-size: 12px; line-height: 1.45; }
33
+ .pb-inspector-field input, .pb-inspector-field textarea { width: 100%; min-height: 40px; padding: 9px 11px; border: 1px solid #cbd5e1; border-radius: 8px; outline: 0; color: #0f172a; background: #fff; font: inherit; font-size: 14px; line-height: 1.4; transition: border-color 140ms ease, box-shadow 140ms ease; }
34
+ .pb-inspector-field textarea { min-height: 96px; resize: vertical; }
35
+ .pb-inspector-field input[type="url"] { color: #334155; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
36
+ .pb-inspector-field input:hover, .pb-inspector-field textarea:hover { border-color: #94a3b8; }
37
+ .pb-inspector-field input:focus, .pb-inspector-field textarea:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgb(37 99 235 / 12%); }
38
+ .pb-inspector-field input:disabled, .pb-inspector-field textarea:disabled { color: #94a3b8; background: #f8fafc; cursor: not-allowed; }
39
+ .pb-inspector__hint { margin: 0; padding: 0 var(--builder-space-2); color: #64748b; font-size: 12px; line-height: 1.5; }
17
40
  .pb-panel--closed { display: none; }
18
41
  .pb-block-list { display: flex; flex: 1; flex-direction: column; gap: var(--builder-space-2); min-height: 0; }
19
42
  .pb-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; transition: background-color 120ms ease, border-color 120ms ease; }