@aduptive/strapi-blockscene 1.0.0-alpha.1 → 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/admin.cjs +955 -155
  2. package/dist/admin.mjs +976 -153
  3. package/package.json +9 -9
package/dist/admin.mjs CHANGED
@@ -1,7 +1,140 @@
1
- // admin/strapi4.tsx
2
- import * as React4 from "react";
3
- import { ModalLayout, ModalHeader, ModalBody, Typography as Typography3, Switch, Flex as Flex3, SingleSelect as SingleSelect2, SingleSelectOption as SingleSelectOption2 } from "@strapi/design-system";
4
- import { useCMEditViewDataManager, useFetchClient, useLibrary, useRBAC, auth } from "@strapi/helper-plugin";
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // server/groups.js
28
+ var require_groups = __commonJS({
29
+ "server/groups.js"(exports, module) {
30
+ "use strict";
31
+ var UID = /^[a-z0-9-]+\.[a-z0-9-]+$/;
32
+ function safeGroups2(value, knownUids) {
33
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
34
+ const out = {};
35
+ for (const [open, close] of Object.entries(value)) {
36
+ if (!UID.test(open) || typeof close !== "string" || !UID.test(close) || open === close) continue;
37
+ if (knownUids && (!knownUids.includes(open) || !knownUids.includes(close))) continue;
38
+ out[open] = close;
39
+ }
40
+ for (const close of Object.values(out)) if (out[close]) return null;
41
+ return Object.keys(out).length ? out : null;
42
+ }
43
+ var uidOf = (row) => typeof row?.__component === "string" ? row.__component : "";
44
+ var isOpener = (row, g) => Boolean(g) && Object.prototype.hasOwnProperty.call(g, uidOf(row));
45
+ var isClose = (row, g) => Boolean(g) && Object.values(g).includes(uidOf(row));
46
+ var blockKey2 = (row) => row && row.id != null ? `${row.__component}#${row.id}` : row && row.__temp_key__ != null ? String(row.__temp_key__) : null;
47
+ function validateGroups2(rows, g) {
48
+ const errors = [];
49
+ if (!g || !Array.isArray(rows)) return errors;
50
+ const stack = [];
51
+ rows.forEach((row, index) => {
52
+ const uid = uidOf(row);
53
+ if (isOpener(row, g)) {
54
+ stack.push({ index, uid, close: g[uid] });
55
+ return;
56
+ }
57
+ if (!isClose(row, g)) return;
58
+ const top = stack[stack.length - 1];
59
+ if (!top) errors.push({ code: "closeBeforeOpen", index, uid });
60
+ else if (top.close !== uid) errors.push({ code: "mismatch", index, uid, expected: top.close, open: top.uid, openIndex: top.index });
61
+ else stack.pop();
62
+ });
63
+ for (const open of stack) errors.push({ code: "unclosed", index: open.index, uid: open.uid, expected: open.close });
64
+ return errors;
65
+ }
66
+ function groupRange2(rows, start, g) {
67
+ const stack = [g[uidOf(rows[start])]];
68
+ for (let i = start + 1; i < rows.length; i++) {
69
+ const row = rows[i];
70
+ if (isOpener(row, g)) stack.push(g[uidOf(row)]);
71
+ else if (isClose(row, g) && uidOf(row) === stack[stack.length - 1]) {
72
+ stack.pop();
73
+ if (!stack.length) return [start, i];
74
+ }
75
+ }
76
+ return [start, rows.length - 1];
77
+ }
78
+ function groupRows2(rows, g, depth = 0, offset = 0) {
79
+ const out = [];
80
+ let i = 0;
81
+ while (i < rows.length) {
82
+ const row = rows[i];
83
+ if (isOpener(row, g)) {
84
+ const [, end] = groupRange2(rows, i, g);
85
+ const closed = uidOf(rows[end]) === g[uidOf(row)] && end > i;
86
+ out.push({
87
+ type: "group",
88
+ key: blockKey2(row),
89
+ closeKey: closed ? blockKey2(rows[end]) : null,
90
+ row,
91
+ index: offset + i,
92
+ depth,
93
+ children: groupRows2(rows.slice(i + 1, closed ? end : end + 1), g, depth + 1, offset + i + 1)
94
+ });
95
+ i = end + 1;
96
+ continue;
97
+ }
98
+ out.push({ type: "block", key: blockKey2(row), row, index: offset + i, depth, stray: isClose(row, g) });
99
+ i++;
100
+ }
101
+ return out;
102
+ }
103
+ function topLevelRanges2(rows, g) {
104
+ const ranges = [];
105
+ let i = 0;
106
+ while (i < rows.length) {
107
+ const end = isOpener(rows[i], g) ? groupRange2(rows, i, g)[1] : i;
108
+ ranges.push([i, end]);
109
+ i = end + 1;
110
+ }
111
+ return ranges;
112
+ }
113
+ function moveGroup2(rows, key, direction, g) {
114
+ if (!g) return null;
115
+ const ranges = topLevelRanges2(rows, g);
116
+ const at = ranges.findIndex(([s]) => blockKey2(rows[s]) === String(key));
117
+ const to = at + (direction === "up" ? -1 : 1);
118
+ if (at < 0 || to < 0 || to >= ranges.length) return null;
119
+ const [a, b] = direction === "up" ? [ranges[to], ranges[at]] : [ranges[at], ranges[to]];
120
+ return [...rows.slice(0, a[0]), ...rows.slice(b[0], b[1] + 1), ...rows.slice(a[0], a[1] + 1), ...rows.slice(b[1] + 1)];
121
+ }
122
+ function removeGroup2(rows, key, g) {
123
+ if (!g) return null;
124
+ const start = rows.findIndex((row) => blockKey2(row) === String(key));
125
+ if (start < 0 || !isOpener(rows[start], g)) return null;
126
+ const [, end] = groupRange2(rows, start, g);
127
+ return [...rows.slice(0, start), ...rows.slice(end + 1)];
128
+ }
129
+ module.exports = { safeGroups: safeGroups2, validateGroups: validateGroups2, groupRange: groupRange2, groupRows: groupRows2, topLevelRanges: topLevelRanges2, moveGroup: moveGroup2, removeGroup: removeGroup2, blockKey: blockKey2, isOpener, isClose };
130
+ }
131
+ });
132
+
133
+ // admin/strapi5.tsx
134
+ import * as React6 from "react";
135
+ import { Modal as Dialog2, Switch, Flex as Flex5, Typography as Typography5, Field, SingleSelect as SingleSelect2, SingleSelectOption as SingleSelectOption2, TextInput } from "@strapi/design-system";
136
+ import { useFetchClient as useFetchClient2, useRBAC, useStrapiApp as useStrapiApp3, useAuth, useForm as useForm3 } from "@strapi/strapi/admin";
137
+ import { unstable_useContentManagerContext as useContext2, useDocumentRBAC as useDocumentRBAC2 } from "@strapi/content-manager/strapi-admin";
5
138
 
6
139
  // admin/Gallery.tsx
7
140
  import * as React from "react";
@@ -11,6 +144,25 @@ import styled from "styled-components";
11
144
 
12
145
  // admin/model.mjs
13
146
  import { generateNKeysBetween } from "fractional-indexing";
147
+ function componentDefaults(schema, components, stack = []) {
148
+ if (!schema || stack.includes(schema)) throw new Error("Invalid recursive component defaults");
149
+ const row = {};
150
+ for (const [name, attr] of Object.entries(schema.attributes || {})) {
151
+ if (Object.hasOwn(attr, "default")) {
152
+ const value = structuredClone(attr.default);
153
+ if (attr.type === "password") row[name] = "";
154
+ else if (attr.type === "relation") row[name] = { connect: [], disconnect: [] };
155
+ else if (value !== null || attr.type === "boolean") row[name] = value;
156
+ } else if (attr.type === "component" && attr.required) {
157
+ const create = () => componentDefaults(components[attr.component], components, [...stack, schema]);
158
+ if (attr.repeatable) {
159
+ const keys = generateNKeysBetween(null, null, attr.min || 0);
160
+ row[name] = keys.map((key) => ({ ...create(), __temp_key__: key }));
161
+ } else row[name] = create();
162
+ } else if (attr.type === "dynamiczone" && attr.required) row[name] = [];
163
+ }
164
+ return row;
165
+ }
14
166
  function editableZones(schema, values, canEdit, disabled = false) {
15
167
  if (disabled) return [];
16
168
  return Object.entries(schema?.attributes || {}).filter(([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)).map(([name, attr]) => {
@@ -1184,7 +1336,10 @@ function Card({ entry, palette, showFields, onSelect }) {
1184
1336
  showFields && entry.fields.length > 0 && /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral500", style: { fontStyle: "italic" }, "data-testid": "block-fields", children: entry.fields.map((f) => `${f.name} (${f.type})`).join(", ") })
1185
1337
  ] });
1186
1338
  }
1187
- function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, controlled }) {
1339
+ function PickerModal({ zone, components, open, onOpenChange, onSelect, Modal: Modal3, Toggle, get }) {
1340
+ return /* @__PURE__ */ jsx2(ZoneGallery, { zone, components, Modal: Modal3, Toggle, get, controlled: { open, onOpenChange, onSelect } });
1341
+ }
1342
+ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, controlled }) {
1188
1343
  const t = useMessages();
1189
1344
  const [ownOpen, setOwnOpen] = React.useState(false);
1190
1345
  const open = controlled ? controlled.open : ownOpen;
@@ -1248,7 +1403,7 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1248
1403
  remember(SHOW_FIELDS_KEY, String(value));
1249
1404
  };
1250
1405
  return /* @__PURE__ */ jsx2(
1251
- Modal2,
1406
+ Modal3,
1252
1407
  {
1253
1408
  open,
1254
1409
  onOpenChange: setOpen,
@@ -1377,46 +1532,708 @@ function Gallery({ zones, editor, docKey, contentType, userId, ...props }) {
1377
1532
  ] }, zone.name)) });
1378
1533
  }
1379
1534
 
1380
- // admin/Settings.tsx
1381
- import * as React2 from "react";
1382
- import { Box as Box2, Button as Button2, Flex as Flex2, Searchbar as Searchbar2, Typography as Typography2 } from "@strapi/design-system";
1535
+ // admin/PagePreview.tsx
1536
+ import * as React3 from "react";
1537
+ import { createPortal } from "react-dom";
1383
1538
  import styled2 from "styled-components";
1384
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1539
+ import { Box as Box2, Button as Button3, Flex as Flex3, Typography as Typography3 } from "@strapi/design-system";
1540
+ import { DescriptionComponentRenderer, useFetchClient, useForm as useForm2, useNotification, useQueryParams, useStrapiApp as useStrapiApp2 } from "@strapi/strapi/admin";
1541
+ import { Modal as Dialog } from "@strapi/design-system";
1542
+ import { unstable_useContentManagerContext as useContext, unstable_useDocument as useDocument, useDocumentRBAC } from "@strapi/content-manager/strapi-admin";
1543
+
1544
+ // admin/FieldEditorModal.tsx
1545
+ import * as React2 from "react";
1546
+ import { Form, InputRenderer, useForm, useStrapiApp } from "@strapi/strapi/admin";
1547
+ import { Button as Button2, Flex as Flex2, Modal, Typography as Typography2 } from "@strapi/design-system";
1548
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1549
+ var Actions = ({ onApply, onCancel }) => {
1550
+ const t = useMessages();
1551
+ const value = useForm("BlocksceneFieldEditor", (state) => state.values.value);
1552
+ return /* @__PURE__ */ jsxs2(Flex2, { gap: 2, justifyContent: "flex-end", children: [
1553
+ /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", onClick: onCancel, children: t.cancel }),
1554
+ /* @__PURE__ */ jsx3(Button2, { onClick: () => onApply(value ?? null), "data-testid": "field-editor-apply", children: t.apply })
1555
+ ] });
1556
+ };
1557
+ function FieldEditorModal({ label, attribute, value, onApply, onCancel }) {
1558
+ const t = useMessages();
1559
+ const customFields = useStrapiApp("BlocksceneFieldEditor", (state) => state.customFields);
1560
+ const [Input, setInput] = React2.useState(void 0);
1561
+ const isCustom = Boolean(attribute?.customField);
1562
+ const isText = attribute?.type === "string" || attribute?.type === "text";
1563
+ React2.useEffect(() => {
1564
+ let active = true;
1565
+ if (!isCustom) {
1566
+ setInput(null);
1567
+ return;
1568
+ }
1569
+ const field = customFields?.get?.(attribute.customField);
1570
+ if (!field?.components?.Input) {
1571
+ setInput(null);
1572
+ return;
1573
+ }
1574
+ Promise.resolve(field.components.Input()).then((mod) => {
1575
+ if (active) setInput(() => mod.default || mod);
1576
+ }).catch(() => {
1577
+ if (active) setInput(null);
1578
+ });
1579
+ return () => {
1580
+ active = false;
1581
+ };
1582
+ }, [attribute, customFields, isCustom]);
1583
+ const supported = isText || isCustom && Input;
1584
+ return /* @__PURE__ */ jsx3(Modal.Root, { open: true, onOpenChange: (open) => {
1585
+ if (!open) onCancel();
1586
+ }, children: /* @__PURE__ */ jsxs2(Modal.Content, { "aria-label": `${t.editText} ${label}`, children: [
1587
+ /* @__PURE__ */ jsx3(Modal.Header, { children: /* @__PURE__ */ jsxs2(Modal.Title, { children: [
1588
+ t.editText,
1589
+ " \xB7 ",
1590
+ label
1591
+ ] }) }),
1592
+ /* @__PURE__ */ jsxs2(Form, { initialValues: { value: value ?? (isText ? "" : null) }, method: "PUT", onSubmit: () => {
1593
+ }, children: [
1594
+ /* @__PURE__ */ jsx3(Modal.Body, { children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "field-editor-modal", children: [
1595
+ /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.editHelp }),
1596
+ isCustom && Input === void 0 && /* @__PURE__ */ jsx3(Typography2, { children: t.loading }),
1597
+ isCustom && Input && /* @__PURE__ */ jsx3(Input, { name: "value", attribute, label, type: attribute.type }),
1598
+ isText && /* @__PURE__ */ jsx3(InputRenderer, { type: attribute.type === "text" ? "text" : "string", name: "value", label }),
1599
+ (isCustom && Input === null || !isCustom && !isText) && /* @__PURE__ */ jsx3(Typography2, { role: "alert", textColor: "danger600", children: t.editUnsupported })
1600
+ ] }) }),
1601
+ /* @__PURE__ */ jsx3(Modal.Footer, { children: supported ? /* @__PURE__ */ jsx3(Actions, { onApply, onCancel }) : /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", onClick: onCancel, children: t.close }) })
1602
+ ] })
1603
+ ] }) });
1604
+ }
1605
+
1606
+ // admin/preview.mjs
1607
+ var import_groups = __toESM(require_groups(), 1);
1608
+ var PROTOCOL = "blockscene:page-preview:v1";
1609
+ var TEXT_TYPES = ["string", "text"];
1610
+ var RICH_TYPES = ["richtext", "blocks", "customField"];
1611
+ var EDITABLE_TYPES = [...TEXT_TYPES, ...RICH_TYPES];
1612
+ var SCALAR = ["string", "text", "richtext", "email", "enumeration", "integer", "biginteger", "float", "decimal", "boolean", "date", "datetime", "time", "json", "blocks", "uid", "customField"];
1613
+ var blockKey = (row) => row && row.id != null ? `${row.__component}#${row.id}` : row && row.__temp_key__ != null ? String(row.__temp_key__) : null;
1614
+ function projectRow(row, schema, components, cmsOrigin, depth = 0) {
1615
+ if (!row || !schema || depth > 3) return null;
1616
+ const out = {};
1617
+ for (const [name, attr] of Object.entries(schema.attributes || {})) {
1618
+ const value = row[name];
1619
+ if (attr.type === "password" || attr.type === "relation" || attr.private) continue;
1620
+ if (SCALAR.includes(attr.type)) {
1621
+ if (value !== void 0) out[name] = typeof value === "string" ? value.slice(0, 2e5) : value;
1622
+ } else if (attr.type === "media") {
1623
+ const files = (attr.multiple ? Array.isArray(value) ? value : [] : value ? [value] : []).map((file) => projectMedia(file, cmsOrigin)).filter(Boolean);
1624
+ out[name] = attr.multiple ? files : files[0] || null;
1625
+ } else if (attr.type === "component") {
1626
+ const child = components?.[attr.component];
1627
+ out[name] = attr.repeatable ? (Array.isArray(value) ? value : []).slice(0, 100).map((item, i) => ({ ...projectRow(item, child, components, cmsOrigin, depth + 1), __key: blockKey(item) ?? String(i) })) : projectRow(value, child, components, cmsOrigin, depth + 1);
1628
+ }
1629
+ }
1630
+ return out;
1631
+ }
1632
+ function projectMedia(file, cmsOrigin) {
1633
+ if (!file || typeof file.url !== "string") return null;
1634
+ try {
1635
+ const url = new URL(file.url, cmsOrigin);
1636
+ if (!["http:", "https:"].includes(url.protocol)) return null;
1637
+ return { url: url.href, alternativeText: String(file.alternativeText || ""), caption: String(file.caption || ""), width: Number(file.width) || 0, height: Number(file.height) || 0, mime: String(file.mime || "") };
1638
+ } catch {
1639
+ return null;
1640
+ }
1641
+ }
1642
+ function resolveField(uid, field, components) {
1643
+ if (typeof field !== "string" || field.length > 200) return null;
1644
+ let schema = components?.[uid];
1645
+ const parts = field.split(".");
1646
+ for (let i = 0; i < parts.length; i++) {
1647
+ const attr = schema?.attributes?.[parts[i]];
1648
+ if (!attr || attr.private === true) return null;
1649
+ if (i === parts.length - 1) return attr;
1650
+ if (attr.type !== "component") return null;
1651
+ if (attr.repeatable) {
1652
+ if (!/^\d{1,3}$/.test(parts[i + 1] || "")) return null;
1653
+ i++;
1654
+ }
1655
+ schema = components?.[attr.component];
1656
+ }
1657
+ return null;
1658
+ }
1659
+ function editableFields(uid, row, components) {
1660
+ const schema = components?.[uid];
1661
+ const out = [];
1662
+ const SYSTEM = ["id", "documentId", "locale", "createdAt", "updatedAt", "publishedAt", "createdBy", "updatedBy", "localizations", "__component", "__temp_key__"];
1663
+ const push = (name, attr, label) => {
1664
+ if (!SYSTEM.includes(name.split(".").pop()) && attr?.private !== true && EDITABLE_TYPES.includes(attr?.type)) out.push({ name, type: attr.type, label });
1665
+ };
1666
+ for (const [name, attr] of Object.entries(schema?.attributes || {})) {
1667
+ if (attr.type === "component" && attr.repeatable) {
1668
+ (Array.isArray(row?.[name]) ? row[name] : []).slice(0, 50).forEach((_, i) => {
1669
+ for (const [child, childAttr] of Object.entries(components?.[attr.component]?.attributes || {})) push(`${name}.${i}.${child}`, childAttr, `${name} ${i + 1} \xB7 ${child}`);
1670
+ });
1671
+ } else push(name, attr, name);
1672
+ }
1673
+ return out;
1674
+ }
1675
+ function mediaFields(uid, row, components) {
1676
+ const media = {};
1677
+ for (const [name, attr] of Object.entries(components?.[uid]?.attributes || {})) {
1678
+ if (attr.type === "media" && attr.private !== true) media[name] = { required: attr.required === true, multiple: attr.multiple === true, present: attr.multiple ? Array.isArray(row?.[name]) && row[name].length > 0 : Boolean(row?.[name]) };
1679
+ }
1680
+ return media;
1681
+ }
1682
+ function projectPage(rows, components, cmsOrigin) {
1683
+ return (Array.isArray(rows) ? rows : []).slice(0, 200).flatMap((row, index) => {
1684
+ const uid = row?.__component;
1685
+ const key = blockKey(row);
1686
+ const schema = components?.[uid];
1687
+ if (!uid || key == null || !schema) return [];
1688
+ return [{
1689
+ key,
1690
+ index,
1691
+ uid,
1692
+ label: schema.info?.displayName || uid,
1693
+ data: projectRow(row, schema, components, cmsOrigin),
1694
+ fields: editableFields(uid, row, components),
1695
+ media: mediaFields(uid, row, components)
1696
+ }];
1697
+ });
1698
+ }
1699
+ var validateFocus = (uid, field, components) => {
1700
+ const attr = resolveField(uid, field, components);
1701
+ return attr && EDITABLE_TYPES.includes(attr.type) ? attr : null;
1702
+ };
1703
+ function validateEdit(uid, field, value, components) {
1704
+ if (typeof value !== "string" || value.length > 5e3) return null;
1705
+ const attr = resolveField(uid, field, components);
1706
+ return attr && TEXT_TYPES.includes(attr.type) ? value.replace(/[\r\n]+/g, " ") : null;
1707
+ }
1708
+ var mediaAttribute = (uid, field, components) => {
1709
+ const attr = resolveField(uid, field, components);
1710
+ return attr?.type === "media" ? attr : null;
1711
+ };
1712
+ function isPreviewMessage(event, origin, source, channel, type) {
1713
+ return event.origin === origin && event.source === source && event.data?.protocol === PROTOCOL && event.data?.channel === channel && event.data?.type === type;
1714
+ }
1715
+ function insertIndex(rows, after) {
1716
+ if (after === null || after === void 0) return 0;
1717
+ const index = (Array.isArray(rows) ? rows : []).findIndex((row) => blockKey(row) === String(after));
1718
+ return index < 0 ? -1 : index + 1;
1719
+ }
1720
+
1721
+ // admin/PagePreview.tsx
1722
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1723
+ var MODE_KEY = "blockscene:page-mode";
1724
+ var RATIO_KEY = "blockscene:page-split-ratio";
1725
+ var MIN_PANE = 360;
1726
+ var MIN_FORM = 520;
1727
+ var NARROW = 960;
1728
+ var KEY_STEP = 32;
1729
+ var MODAL_TYPES = ["customField", "string", "text"];
1730
+ var read = (key) => {
1731
+ try {
1732
+ return localStorage.getItem(key);
1733
+ } catch {
1734
+ return null;
1735
+ }
1736
+ };
1737
+ var write = (key, value) => {
1738
+ try {
1739
+ localStorage.setItem(key, value);
1740
+ } catch {
1741
+ }
1742
+ };
1743
+ var clampWidth = (width, vw) => Math.round(Math.min(Math.max(width, MIN_PANE), Math.max(MIN_PANE, vw - MIN_FORM)));
1744
+ var getIn = (object, path) => path.split(".").reduce((acc, part) => acc == null ? acc : acc[part], object);
1745
+ var Pane = styled2.aside`
1746
+ position: fixed; top: 0; bottom: 0; z-index: 2; display: flex; flex-direction: column;
1747
+ background: ${({ theme }) => theme.colors.neutral0}; border-left: 1px solid ${({ theme }) => theme.colors.neutral200};
1748
+ box-shadow: ${({ theme }) => theme.shadows.popupShadow};
1749
+ `;
1750
+ function useMainRect() {
1751
+ const [rect, setRect] = React3.useState(() => ({ left: 0, width: window.innerWidth }));
1752
+ React3.useEffect(() => {
1753
+ const main = document.getElementById("main-content");
1754
+ const measure = () => {
1755
+ const r = main?.getBoundingClientRect();
1756
+ setRect(r && r.width > 0 ? { left: Math.round(r.left), width: Math.round(r.width) } : { left: 0, width: window.innerWidth });
1757
+ };
1758
+ measure();
1759
+ const observer = main && "ResizeObserver" in window ? new ResizeObserver(measure) : null;
1760
+ if (main) observer?.observe(main);
1761
+ window.addEventListener("resize", measure);
1762
+ return () => {
1763
+ observer?.disconnect();
1764
+ window.removeEventListener("resize", measure);
1765
+ };
1766
+ }, []);
1767
+ return rect;
1768
+ }
1769
+ var Handle = styled2.div`
1770
+ position: absolute; top: 0; bottom: 0; left: -5px; width: 10px; cursor: col-resize; z-index: 3; touch-action: none;
1771
+ &::after { content: ''; position: absolute; top: 0; bottom: 0; left: 4px; width: 2px; }
1772
+ &:hover::after, &[data-dragging='true']::after, &:focus-visible::after { background: ${({ theme }) => theme.colors.primary600}; }
1773
+ &:focus-visible { outline: none; }
1774
+ `;
1775
+ var Frame2 = styled2.iframe` flex: 1; width: 100%; border: 0; background: white; `;
1776
+ var SPLIT_STYLE = `
1777
+ body.bp-split #main-content { padding-right: calc(var(--bp-pane, 50vw) + 1.6rem) !important; }
1778
+ body.bp-split [data-bp-grid] { display: flex !important; flex-direction: column-reverse; gap: 1.6rem; }
1779
+ body.bp-split [data-bp-panels] { width: 100%; }
1780
+ body.bp-split [data-bp-panels] > div { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: 1.2rem; }
1781
+ body.bp-split [data-bp-panels] > div > * { flex: 1 1 24rem; }`;
1782
+ var markLayout = (anchor) => {
1783
+ let item = anchor;
1784
+ while (item?.parentElement && getComputedStyle(item.parentElement).display !== "grid") item = item.parentElement;
1785
+ if (!item?.parentElement) return;
1786
+ item.dataset.bpPanels = "";
1787
+ item.parentElement.dataset.bpGrid = "";
1788
+ };
1789
+ function usePreviewBase(override, model, documentId, locale) {
1790
+ const { get } = useFetchClient();
1791
+ const [native, setNative] = React3.useState(null);
1792
+ React3.useEffect(() => {
1793
+ if (override || !model) return;
1794
+ let active = true;
1795
+ const query = new URLSearchParams({ status: "draft", ...documentId ? { documentId } : {}, ...locale ? { locale } : {} });
1796
+ get(`/content-manager/preview/url/${model}?${query}`).then(({ data }) => {
1797
+ if (active && typeof data?.data?.url === "string") setNative(`${new URL(data.data.url).origin}/block-preview/page`);
1798
+ }).catch(() => {
1799
+ if (active) setNative(null);
1800
+ });
1801
+ return () => {
1802
+ active = false;
1803
+ };
1804
+ }, [get, model, documentId, locale, override]);
1805
+ return override ? { base: override, source: "custom" } : native ? { base: native, source: "native" } : { base: null, source: "none" };
1806
+ }
1807
+ function PickerDialog({ open, onOpenChange, trigger, title, children }) {
1808
+ return /* @__PURE__ */ jsxs3(Dialog.Root, { open, onOpenChange, children: [
1809
+ trigger && /* @__PURE__ */ jsx4(Dialog.Trigger, { children: trigger }),
1810
+ /* @__PURE__ */ jsxs3(Dialog.Content, { children: [
1811
+ /* @__PURE__ */ jsx4(Dialog.Header, { children: /* @__PURE__ */ jsx4(Dialog.Title, { children: title }) }),
1812
+ /* @__PURE__ */ jsx4(Dialog.Body, { children })
1813
+ ] })
1814
+ ] });
1815
+ }
1816
+ var ACTION_TYPES = ["update", "publish"];
1817
+ var nativeButtons = () => document.querySelectorAll('#main-content button:not([data-testid="page-preview-pane"] button)');
1818
+ function ToolbarActions({ model, collectionType, documentId, locale }) {
1819
+ const plugins = useStrapiApp2("BlocksceneToolbar", (state) => state.plugins);
1820
+ const [{ query }] = useQueryParams();
1821
+ const { document: doc, meta } = useDocument({ model, collectionType, documentId, params: { locale } }, { skip: !documentId });
1822
+ const { toggleNotification } = useNotification();
1823
+ const t = useMessages();
1824
+ const descriptions = (plugins["content-manager"]?.apis?.getDocumentActions?.("panel") || []).filter((d) => ACTION_TYPES.includes(d.type));
1825
+ const props = { activeTab: query.status || "draft", model, documentId, document: doc, meta, collectionType };
1826
+ const run = async (action, event) => {
1827
+ const native = [...nativeButtons()].find((b) => b.textContent?.trim() === action.label);
1828
+ if (native) {
1829
+ native.click();
1830
+ return;
1831
+ }
1832
+ const mute = await action.onClick?.(event);
1833
+ if (action.dialog && !mute && action.dialog.type === "notification") toggleNotification({ title: action.dialog.title, message: action.dialog.content, type: action.dialog.status });
1834
+ };
1835
+ const typeOf = (action) => action.type || descriptions.find((d) => typeof action.id === "string" && action.id.startsWith(`${d.name || d.displayName}-`))?.type;
1836
+ return /* @__PURE__ */ jsx4(DescriptionComponentRenderer, { props, descriptions, children: (actions) => /* @__PURE__ */ jsx4(Flex3, { gap: 1, "data-testid": "page-preview-actions", children: [...actions].sort((a, b) => ACTION_TYPES.indexOf(typeOf(a)) - ACTION_TYPES.indexOf(typeOf(b))).map((action) => {
1837
+ const reason = action.disabled ? typeOf(action) === "publish" ? t.publishDisabledHint : t.saveDisabledHint : void 0;
1838
+ return /* @__PURE__ */ jsx4("span", { title: reason, children: /* @__PURE__ */ jsx4(Button3, { size: "S", variant: action.variant || "default", disabled: action.disabled, loading: action.loading, "aria-description": reason, onClick: (e) => run(action, e), children: action.label }) }, action.id);
1839
+ }) }) });
1840
+ }
1841
+ function PagePreview({ editor, groups = null, Toggle }) {
1842
+ const t = useMessages();
1843
+ const c = useContext();
1844
+ const rbac = useDocumentRBAC("BlockscenePagePreview", (state) => state);
1845
+ const values = useForm2("BlockscenePagePreview", (state) => state.values);
1846
+ const onChange = useForm2("BlockscenePagePreview", (state) => state.onChange);
1847
+ const components = useStrapiApp2("BlockscenePagePreview", (state) => state.components);
1848
+ const MediaLibraryDialog = components?.["media-library"];
1849
+ const addFieldRow = useForm2("BlockscenePagePreview", (state) => state.addFieldRow);
1850
+ const fields = c.layout?.edit?.layout?.flat(3) || [];
1851
+ const readable = (name) => c.isCreatingEntry || (rbac.canReadFields || []).includes(name);
1852
+ const zone = React3.useMemo(() => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name, [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]);
1853
+ const zoneLabel = fields.find((field) => field.name === zone)?.label || zone || "";
1854
+ const canEdit = Boolean(zone && ((c.isCreatingEntry ? rbac.canCreateFields : rbac.canUpdateFields) || []).includes(zone) && fields.find((field) => field.name === zone)?.disabled !== true && !c.form?.disabled && typeof addFieldRow === "function");
1855
+ const [mode, setModeState] = React3.useState(() => {
1856
+ const v = read(MODE_KEY);
1857
+ return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
1858
+ });
1859
+ const setMode = (next) => {
1860
+ setModeState(next);
1861
+ write(MODE_KEY, next);
1862
+ };
1863
+ const [ratio, setRatio] = React3.useState(() => {
1864
+ const v = Number(read(RATIO_KEY));
1865
+ return v > 0 && v < 1 ? v : 0.5;
1866
+ });
1867
+ const main = useMainRect();
1868
+ const vw = main.width;
1869
+ const [dragging, setDragging] = React3.useState(false);
1870
+ const [ready, setReady] = React3.useState(false);
1871
+ const [failed, setFailed] = React3.useState(false);
1872
+ const [attempt, retry] = React3.useReducer((n) => n + 1, 0);
1873
+ const [picking, setPicking] = React3.useState(null);
1874
+ React3.useEffect(() => {
1875
+ setEditing(null);
1876
+ setPicking(null);
1877
+ setInserting(null);
1878
+ }, [c.id, c.form?.initialValues?.locale]);
1879
+ const [editing, setEditing] = React3.useState(null);
1880
+ const [inserting, setInserting] = React3.useState(null);
1881
+ const { get } = useFetchClient();
1882
+ const { toggleNotification } = useNotification();
1883
+ const zoneAttr = zone ? c.contentType?.attributes?.[zone] : null;
1884
+ const iframe = React3.useRef(null);
1885
+ const loaded = React3.useRef(false);
1886
+ const anchor = React3.useRef(null);
1887
+ const channel = React3.useMemo(() => crypto.randomUUID(), []);
1888
+ const { base, source } = usePreviewBase(editor?.previewUrl || "", c.model, c.isCreatingEntry ? void 0 : c.id, c.form?.initialValues?.locale);
1889
+ const url = base ? new URL(base) : null;
1890
+ url?.searchParams.set("channel", channel);
1891
+ const origin = url?.origin || "";
1892
+ const narrow = vw < NARROW;
1893
+ const paneWidth = clampWidth(ratio * vw, vw);
1894
+ const resizable = mode === "split" && !narrow;
1895
+ const active = mode !== "form" && Boolean(url) && Boolean(zone);
1896
+ const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
1897
+ const latest = React3.useRef(rows);
1898
+ latest.current = rows;
1899
+ const live = React3.useRef({ mode, canEdit, zoneLabel, components: c.components, setMode, onChange });
1900
+ live.current = { mode, canEdit, zoneLabel, components: c.components, setMode, onChange };
1901
+ const send = React3.useCallback(() => {
1902
+ iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "update-page", blocks: projectPage(latest.current, live.current.components, window.location.origin), groups, locale: t.locale, mode: live.current.mode }, origin);
1903
+ }, [channel, origin]);
1904
+ const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
1905
+ const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
1906
+ React3.useEffect(() => {
1907
+ if (resizable) markLayout(anchor.current);
1908
+ document.body.classList.toggle("bp-split", resizable);
1909
+ document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
1910
+ return () => document.body.classList.remove("bp-split");
1911
+ }, [resizable, paneWidth]);
1912
+ const applyWidth = (width, persist) => {
1913
+ const next = clampWidth(width, vw) / vw;
1914
+ setRatio(next);
1915
+ if (persist) write(RATIO_KEY, String(next));
1916
+ };
1917
+ const focusField = (index, field) => {
1918
+ const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
1919
+ if (!item) return;
1920
+ const header = toggles(item.parentElement)[index];
1921
+ if (header?.getAttribute("aria-expanded") === "false") header.click();
1922
+ const parts = field.split(".");
1923
+ const escaped = CSS.escape(`${zone}.${index}.${field}`);
1924
+ let tries = 0;
1925
+ const timer = setInterval(() => {
1926
+ if (++tries > 40) return clearInterval(timer);
1927
+ let scope = item;
1928
+ for (let i = 0; i + 1 < parts.length; i += 2) {
1929
+ const nested = [...scope.querySelectorAll("button[aria-expanded][data-radix-collection-item]")].filter((b) => b !== header);
1930
+ const row = nested[Number(parts[i + 1])];
1931
+ if (!row) return;
1932
+ if (row.getAttribute("aria-expanded") === "false") row.click();
1933
+ scope = row.closest("li, div") || scope;
1934
+ }
1935
+ const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
1936
+ if (!target) return;
1937
+ const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
1938
+ let container = target.matches(INPUT) ? target : target.parentElement;
1939
+ for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++) container = container.parentElement;
1940
+ const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
1941
+ if (!editable) return;
1942
+ if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
1943
+ editable.focus();
1944
+ if (document.activeElement !== editable) return;
1945
+ clearInterval(timer);
1946
+ editable.scrollIntoView({ block: "center", behavior: "smooth" });
1947
+ }, 150);
1948
+ };
1949
+ React3.useEffect(() => {
1950
+ if (!active) return;
1951
+ setReady(false);
1952
+ setFailed(false);
1953
+ const timeout = setTimeout(() => setFailed(true), 15e3);
1954
+ const receive = (event) => {
1955
+ const is = (type) => isPreviewMessage(event, origin, iframe.current?.contentWindow, channel, type);
1956
+ const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
1957
+ if (is("ready")) {
1958
+ clearTimeout(timeout);
1959
+ setReady(true);
1960
+ setFailed(false);
1961
+ send();
1962
+ return;
1963
+ }
1964
+ if (is("insert") && canEdit2) {
1965
+ const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
1966
+ if (after !== null && insertIndex(latest.current, after) < 0) return;
1967
+ if (latest.current.length >= (zoneAttr?.max ?? Infinity)) {
1968
+ toggleNotification({ type: "info", message: t.zoneFull });
1969
+ return;
1970
+ }
1971
+ setInserting({ after });
1972
+ return;
1973
+ }
1974
+ if (is("move-group") && canEdit2 && groups) {
1975
+ const next = (0, import_groups.moveGroup)(latest.current, String(event.data.key), event.data.direction === "up" ? "up" : "down", groups);
1976
+ if (next) onChange2(zone, next);
1977
+ return;
1978
+ }
1979
+ if (is("delete-group") && canEdit2 && groups) {
1980
+ const next = (0, import_groups.removeGroup)(latest.current, String(event.data.key), groups);
1981
+ if (next) onChange2(zone, next);
1982
+ return;
1983
+ }
1984
+ if (is("insert-group") && canEdit2 && groups) {
1985
+ const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
1986
+ const index2 = insertIndex(latest.current, after);
1987
+ const uid2 = String(event.data.uid || "");
1988
+ const close = groups[uid2];
1989
+ const allowed = zoneAttr?.components || [];
1990
+ if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
1991
+ if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
1992
+ toggleNotification({ type: "info", message: t.zoneFull });
1993
+ return;
1994
+ }
1995
+ addFieldRow(zone, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, index2);
1996
+ addFieldRow(zone, { __component: close }, index2 + 1);
1997
+ return;
1998
+ }
1999
+ const key = typeof event.data?.key === "string" ? event.data.key : "";
2000
+ const index = key ? indexOf(key) : -1;
2001
+ const uid = index >= 0 ? latest.current[index]?.__component : "";
2002
+ if (index < 0) return;
2003
+ if (is("select")) {
2004
+ const list = findZoneList(zoneLabel2);
2005
+ const item = list?.querySelectorAll(":scope > li")[index];
2006
+ const header = list ? toggles(list)[index] : null;
2007
+ if (header?.getAttribute("aria-expanded") === "false") header.click();
2008
+ item?.scrollIntoView({ block: "center", behavior: "smooth" });
2009
+ if (item) {
2010
+ item.style.outline = "2px solid #4945ff";
2011
+ setTimeout(() => {
2012
+ item.style.outline = "";
2013
+ }, 1500);
2014
+ }
2015
+ if (mode2 === "preview") setMode2("split");
2016
+ iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "highlight", key }, origin);
2017
+ } else if (is("focus")) {
2018
+ const attr = validateFocus(uid, event.data.field, components2);
2019
+ if (!attr) return;
2020
+ if (mode2 === "preview" && canEdit2 && (attr.customField || MODAL_TYPES.includes(attr.type))) setEditing({ key, field: event.data.field, attr, label: `${components2[uid]?.info?.displayName || uid} \xB7 ${event.data.field}`, value: getIn(latest.current[index], event.data.field) });
2021
+ else focusField(index, event.data.field);
2022
+ } else if (is("media") && canEdit2) {
2023
+ const attr = mediaAttribute(uid, event.data.field, components2);
2024
+ if (attr) setPicking({ key, field: event.data.field, attr });
2025
+ } else if (is("media-remove") && canEdit2) {
2026
+ const attr = mediaAttribute(uid, event.data.field, components2);
2027
+ if (attr && !attr.required) onChange2(path(key, event.data.field), null);
2028
+ } else if (is("edit") && canEdit2) {
2029
+ const value = validateEdit(uid, event.data.field, event.data.value, components2);
2030
+ if (value !== null) onChange2(path(key, event.data.field), value);
2031
+ }
2032
+ };
2033
+ window.addEventListener("message", receive);
2034
+ if (loaded.current) iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "ping" }, origin);
2035
+ return () => {
2036
+ clearTimeout(timeout);
2037
+ window.removeEventListener("message", receive);
2038
+ };
2039
+ }, [active, attempt, channel, origin, groups]);
2040
+ React3.useEffect(() => {
2041
+ if (!ready || !active) return;
2042
+ const timer = setTimeout(send, 120);
2043
+ return () => clearTimeout(timer);
2044
+ }, [values, c.components, ready, active, send, mode]);
2045
+ if (!zone) return null;
2046
+ const modes = [["form", t.modeForm], ["split", t.modeSplit], ["preview", t.modePreview]];
2047
+ const switcher = /* @__PURE__ */ jsx4(Flex3, { gap: 1, wrap: "wrap", "data-testid": "page-preview-modes", role: "group", "aria-label": t.modeGroup, children: modes.map(([value, label]) => /* @__PURE__ */ jsx4(Button3, { type: "button", size: "S", variant: mode === value ? "default" : "tertiary", "aria-pressed": mode === value, onClick: () => setMode(value), disabled: !url && value !== "form", children: label }, value)) });
2048
+ const onKey = (event) => {
2049
+ const map = { ArrowLeft: paneWidth + KEY_STEP, ArrowRight: paneWidth - KEY_STEP, Home: MIN_PANE, End: vw - MIN_FORM };
2050
+ if (event.key in map) {
2051
+ event.preventDefault();
2052
+ applyWidth(map[event.key], true);
2053
+ }
2054
+ };
2055
+ const problems = groups ? (0, import_groups.validateGroups)(rows, groups) : [];
2056
+ const insertClose = (error) => {
2057
+ const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
2058
+ addFieldRow(zone, { __component: error.expected }, end + 1);
2059
+ };
2060
+ const diagnostics = problems.length > 0 && /* @__PURE__ */ jsx4(Box2, { role: "alert", "data-testid": "page-preview-diagnostics", padding: 2, background: "danger100", hasRadius: true, children: /* @__PURE__ */ jsxs3("details", { children: [
2061
+ /* @__PURE__ */ jsx4("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ jsx4(Typography3, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
2062
+ /* @__PURE__ */ jsx4("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ jsxs3("li", { style: { marginBottom: 6 }, children: [
2063
+ /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "danger700", children: t.f(error.code === "closeBeforeOpen" ? "diagCloseBeforeOpen" : error.code === "mismatch" ? "diagMismatch" : "diagUnclosed", { n: error.index + 1, uid: error.uid, open: error.open || "", expected: error.expected || "" }) }),
2064
+ error.code === "unclosed" && canEdit && /* @__PURE__ */ jsx4(Box2, { paddingTop: 1, children: /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "danger-light", "data-testid": `diag-insert-close-${error.index}`, onClick: () => insertClose(error), children: t.f("diagInsertClose", { n: error.index + 1, expected: error.expected }) }) }),
2065
+ error.code === "closeBeforeOpen" && /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
2066
+ ] }, `${error.code}-${error.index}`)) })
2067
+ ] }) });
2068
+ return /* @__PURE__ */ jsxs3(Flex3, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
2069
+ switcher,
2070
+ diagnostics,
2071
+ /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2072
+ active && createPortal(
2073
+ /* @__PURE__ */ jsxs3(
2074
+ Pane,
2075
+ {
2076
+ "data-testid": "page-preview-pane",
2077
+ "aria-label": t.previewPane,
2078
+ style: { left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth, width: mode === "preview" || narrow ? main.width : paneWidth, userSelect: dragging ? "none" : void 0 },
2079
+ children: [
2080
+ /* @__PURE__ */ jsx4("style", { children: SPLIT_STYLE }),
2081
+ resizable && /* @__PURE__ */ jsx4(
2082
+ Handle,
2083
+ {
2084
+ role: "separator",
2085
+ "aria-orientation": "vertical",
2086
+ "aria-label": t.resize,
2087
+ tabIndex: 0,
2088
+ "aria-valuemin": MIN_PANE,
2089
+ "aria-valuemax": Math.max(MIN_PANE, vw - MIN_FORM),
2090
+ "aria-valuenow": paneWidth,
2091
+ "data-testid": "page-preview-resizer",
2092
+ "data-dragging": dragging,
2093
+ onPointerDown: (e) => {
2094
+ e.preventDefault();
2095
+ e.currentTarget.setPointerCapture(e.pointerId);
2096
+ setDragging(true);
2097
+ },
2098
+ onPointerMove: (e) => {
2099
+ if (dragging) applyWidth(main.left + main.width - e.clientX, false);
2100
+ },
2101
+ onPointerUp: (e) => {
2102
+ if (!dragging) return;
2103
+ setDragging(false);
2104
+ e.currentTarget.releasePointerCapture(e.pointerId);
2105
+ applyWidth(main.left + main.width - e.clientX, true);
2106
+ },
2107
+ onPointerCancel: () => setDragging(false),
2108
+ onKeyDown: onKey
2109
+ }
2110
+ ),
2111
+ /* @__PURE__ */ jsxs3(Flex3, { padding: 2, gap: 2, justifyContent: "space-between", alignItems: "center", background: "neutral100", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 2 }, children: [
2112
+ switcher,
2113
+ /* @__PURE__ */ jsxs3(Flex3, { gap: 2, alignItems: "center", wrap: "wrap", style: { marginLeft: "auto" }, children: [
2114
+ /* @__PURE__ */ jsx4(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
2115
+ failed && /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
2116
+ /* @__PURE__ */ jsx4(ToolbarActions, { model: c.model, collectionType: c.collectionType, documentId: c.isCreatingEntry ? void 0 : c.id, locale: c.form?.initialValues?.locale })
2117
+ ] })
2118
+ ] }),
2119
+ /* @__PURE__ */ jsx4(
2120
+ Frame2,
2121
+ {
2122
+ ref: iframe,
2123
+ title: t.previewPane,
2124
+ src: url.href,
2125
+ sandbox: "allow-scripts allow-same-origin",
2126
+ referrerPolicy: "no-referrer",
2127
+ onLoad: () => {
2128
+ loaded.current = true;
2129
+ },
2130
+ style: dragging ? { pointerEvents: "none" } : void 0
2131
+ },
2132
+ attempt
2133
+ )
2134
+ ]
2135
+ }
2136
+ ),
2137
+ document.body
2138
+ ),
2139
+ inserting && zoneAttr && /* @__PURE__ */ jsx4(
2140
+ PickerModal,
2141
+ {
2142
+ zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
2143
+ components: c.components,
2144
+ Modal: PickerDialog,
2145
+ Toggle,
2146
+ get,
2147
+ open: true,
2148
+ onOpenChange: (open) => {
2149
+ if (!open) setInserting(null);
2150
+ },
2151
+ onSelect: (uid) => {
2152
+ const index = insertIndex(latest.current, inserting.after);
2153
+ setInserting(null);
2154
+ const close = groups?.[uid];
2155
+ if (index < 0 || !(zoneAttr.components || []).includes(uid) || close && !(zoneAttr.components || []).includes(close) || latest.current.length + (close ? 2 : 1) > (zoneAttr.max ?? Infinity)) {
2156
+ toggleNotification({ type: "warning", message: close ? t.zoneFull : t.insertMoved });
2157
+ return;
2158
+ }
2159
+ addFieldRow(zone, { ...componentDefaults(c.components[uid], c.components), __component: uid }, index);
2160
+ if (close) addFieldRow(zone, { __component: close }, index + 1);
2161
+ }
2162
+ }
2163
+ ),
2164
+ editing && /* @__PURE__ */ jsx4(
2165
+ FieldEditorModal,
2166
+ {
2167
+ label: editing.label,
2168
+ attribute: editing.attr,
2169
+ value: editing.value,
2170
+ onCancel: () => setEditing(null),
2171
+ onApply: (value) => {
2172
+ const index = indexOf(editing.key);
2173
+ setEditing(null);
2174
+ if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
2175
+ }
2176
+ }
2177
+ ),
2178
+ picking && MediaLibraryDialog && /* @__PURE__ */ jsx4(
2179
+ MediaLibraryDialog,
2180
+ {
2181
+ allowedTypes: picking.attr.allowedTypes || ["images"],
2182
+ multiple: picking.attr.multiple === true,
2183
+ onClose: () => setPicking(null),
2184
+ onSelectAssets: (assets) => {
2185
+ const index = indexOf(picking.key);
2186
+ setPicking(null);
2187
+ if (index < 0 || !assets?.length) return;
2188
+ const current = getIn(latest.current[index], picking.field);
2189
+ onChange(`${zone}.${index}.${picking.field}`, picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]);
2190
+ }
2191
+ }
2192
+ ),
2193
+ /* @__PURE__ */ jsx4(Box2, { hidden: true, "data-testid": "page-preview-state", "data-mode": mode, "data-ready": ready, "data-failed": failed, "data-pane-width": paneWidth, "data-narrow": narrow, "data-preview-source": source, "data-editing": editing ? editing.field : "" })
2194
+ ] });
2195
+ }
2196
+
2197
+ // admin/Settings.tsx
2198
+ import * as React4 from "react";
2199
+ import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
2200
+ import styled3 from "styled-components";
2201
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1385
2202
  var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
1386
- var Grid = styled2.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
1387
- var Card2 = styled2.div`
2203
+ var Grid = styled3.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
2204
+ var Card2 = styled3.div`
1388
2205
  display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 4px;
1389
2206
  border: 1px solid ${({ theme }) => theme.colors.neutral200}; background: ${({ theme }) => theme.colors.neutral0};
1390
2207
  `;
1391
- var Bar = styled2.div`
2208
+ var Bar = styled3.div`
1392
2209
  position: fixed; top: 0; z-index: 4; padding: 12px 32px;
1393
2210
  background: ${({ theme }) => theme.colors.neutral100}; border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
1394
2211
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
1395
2212
  `;
1396
- function useMainRect(anchor) {
1397
- const [rect, setRect] = React2.useState({ left: 0, width: 0 });
1398
- React2.useEffect(() => {
1399
- const read = () => {
2213
+ function useMainRect2(anchor) {
2214
+ const [rect, setRect] = React4.useState({ left: 0, width: 0 });
2215
+ React4.useEffect(() => {
2216
+ const read2 = () => {
1400
2217
  let host = document.getElementById("main-content");
1401
2218
  for (let el = anchor.current?.parentElement; !host && el; el = el.parentElement) if (["auto", "scroll"].includes(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) host = el;
1402
2219
  const r = (host || document.querySelector("main"))?.getBoundingClientRect();
1403
2220
  if (r) setRect({ left: Math.round(r.left), width: Math.round(r.width) });
1404
2221
  };
1405
- read();
1406
- window.addEventListener("resize", read);
1407
- return () => window.removeEventListener("resize", read);
2222
+ read2();
2223
+ window.addEventListener("resize", read2);
2224
+ return () => window.removeEventListener("resize", read2);
1408
2225
  });
1409
2226
  return rect;
1410
2227
  }
1411
- var Swatches = styled2.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; `;
1412
- var Label = styled2.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
2228
+ var Swatches = styled3.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; `;
2229
+ var Label = styled3.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
1413
2230
  function ColorField({ name, label, value, onChange, disabled }) {
1414
2231
  const valid = COLOR2.test(value);
1415
- return /* @__PURE__ */ jsxs2(Label, { children: [
1416
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
1417
- /* @__PURE__ */ jsxs2(Flex2, { gap: 2, children: [
1418
- /* @__PURE__ */ jsx3("input", { type: "color", "aria-label": label, value: valid ? value : "#000000", disabled, onChange: (e) => onChange(e.target.value.toUpperCase()), style: { width: 40, height: 32, padding: 0, border: 0, background: "none" } }),
1419
- /* @__PURE__ */ jsx3(
2232
+ return /* @__PURE__ */ jsxs4(Label, { children: [
2233
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
2234
+ /* @__PURE__ */ jsxs4(Flex4, { gap: 2, children: [
2235
+ /* @__PURE__ */ jsx5("input", { type: "color", "aria-label": label, value: valid ? value : "#000000", disabled, onChange: (e) => onChange(e.target.value.toUpperCase()), style: { width: 40, height: 32, padding: 0, border: 0, background: "none" } }),
2236
+ /* @__PURE__ */ jsx5(
1420
2237
  "input",
1421
2238
  {
1422
2239
  name,
@@ -1432,22 +2249,22 @@ function ColorField({ name, label, value, onChange, disabled }) {
1432
2249
  ] });
1433
2250
  }
1434
2251
  var permissions = { read: [{ action: "plugin::blockscene.settings.read", subject: null }], update: [{ action: "plugin::blockscene.settings.update", subject: null }] };
1435
- function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField, previewSupported = false }) {
2252
+ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField: TextField2, previewSupported = false }) {
1436
2253
  const t = useMessages();
1437
2254
  const { get, put } = useClient();
1438
2255
  const { canRead, canUpdate, isLoading } = usePermissions2();
1439
- const [data, setData] = React2.useState(null);
1440
- const [catalog, setCatalog] = React2.useState(null);
1441
- const [settings, setSettings] = React2.useState(null);
1442
- const [media, setMedia] = React2.useState({});
1443
- const [sources, setSources] = React2.useState({});
1444
- const [dirty, setDirty] = React2.useState(false);
1445
- const [saving, setSaving] = React2.useState(false);
1446
- const [status, setStatus] = React2.useState("");
1447
- const [failed, setFailed] = React2.useState(false);
1448
- const [filter, setFilter] = React2.useState("");
1449
- const [picking, setPicking] = React2.useState(null);
1450
- React2.useEffect(() => {
2256
+ const [data, setData] = React4.useState(null);
2257
+ const [catalog, setCatalog] = React4.useState(null);
2258
+ const [settings, setSettings] = React4.useState(null);
2259
+ const [media, setMedia] = React4.useState({});
2260
+ const [sources, setSources] = React4.useState({});
2261
+ const [dirty, setDirty] = React4.useState(false);
2262
+ const [saving, setSaving] = React4.useState(false);
2263
+ const [status, setStatus] = React4.useState("");
2264
+ const [failed, setFailed] = React4.useState(false);
2265
+ const [filter, setFilter] = React4.useState("");
2266
+ const [picking, setPicking] = React4.useState(null);
2267
+ React4.useEffect(() => {
1451
2268
  if (!canRead || isLoading) return;
1452
2269
  let active = true;
1453
2270
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -1495,35 +2312,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1495
2312
  return index === 0 && manual ? t.manual : t.automatic;
1496
2313
  };
1497
2314
  const ready = !isLoading && canRead && !failed && settings && catalog;
1498
- const header = React2.useRef(null);
1499
- const [floating, setFloating] = React2.useState(false);
1500
- const main = useMainRect(header);
1501
- React2.useEffect(() => {
2315
+ const header = React4.useRef(null);
2316
+ const [floating, setFloating] = React4.useState(false);
2317
+ const main = useMainRect2(header);
2318
+ React4.useEffect(() => {
1502
2319
  const el = header.current;
1503
2320
  if (!el || typeof IntersectionObserver === "undefined") return;
1504
2321
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
1505
2322
  observer.observe(el);
1506
2323
  return () => observer.disconnect();
1507
2324
  }, [ready]);
1508
- const controls = (suffix) => ready && /* @__PURE__ */ jsxs2(Flex2, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1509
- /* @__PURE__ */ jsx3(Typography2, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
1510
- /* @__PURE__ */ jsx3(Button2, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
2325
+ const controls = (suffix) => ready && /* @__PURE__ */ jsxs4(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
2326
+ /* @__PURE__ */ jsx5(Typography4, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
2327
+ /* @__PURE__ */ jsx5(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
1511
2328
  ] });
1512
- return /* @__PURE__ */ jsx3(Box2, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 5, children: [
1513
- /* @__PURE__ */ jsx3("div", { ref: header, children: /* @__PURE__ */ jsxs2(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
1514
- /* @__PURE__ */ jsx3(Typography2, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
2329
+ return /* @__PURE__ */ jsx5(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
2330
+ /* @__PURE__ */ jsx5("div", { ref: header, children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2331
+ /* @__PURE__ */ jsx5(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
1515
2332
  controls("")
1516
2333
  ] }) }),
1517
- floating && main.width > 0 && /* @__PURE__ */ jsx3(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs2(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
1518
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "p", children: t.settingsTitle }),
2334
+ floating && main.width > 0 && /* @__PURE__ */ jsx5(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2335
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
1519
2336
  controls("-floating")
1520
2337
  ] }) }),
1521
- /* @__PURE__ */ jsx3(Typography2, { textColor: "neutral600", children: t.settingsIntro }),
1522
- isLoading ? /* @__PURE__ */ jsx3(Typography2, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx3(Typography2, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx3(Typography2, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx3(Typography2, { children: t.loading }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1523
- data.disabled && /* @__PURE__ */ jsx3(Box2, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx3(Typography2, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
1524
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1525
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.palette }),
1526
- /* @__PURE__ */ jsx3(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx3(
2338
+ /* @__PURE__ */ jsx5(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
2339
+ isLoading ? /* @__PURE__ */ jsx5(Typography4, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx5(Typography4, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx5(Typography4, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx5(Typography4, { children: t.loading }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
2340
+ data.disabled && /* @__PURE__ */ jsx5(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
2341
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2342
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
2343
+ /* @__PURE__ */ jsx5(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx5(
1527
2344
  ColorField,
1528
2345
  {
1529
2346
  name: `palette-${key}`,
@@ -1537,33 +2354,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1537
2354
  },
1538
2355
  key
1539
2356
  )) }),
1540
- invalid && /* @__PURE__ */ jsx3(Typography2, { role: "alert", textColor: "danger600", children: t.invalidColor }),
1541
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
1542
- /* @__PURE__ */ jsx3(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs2(Flex2, { direction: "column", gap: 1, children: [
1543
- /* @__PURE__ */ jsx3(Box2, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx3(Wireframe, { template, palette: settings.palette }) }),
1544
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
2357
+ invalid && /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
2358
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
2359
+ /* @__PURE__ */ jsx5(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs4(Flex4, { direction: "column", gap: 1, children: [
2360
+ /* @__PURE__ */ jsx5(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx5(Wireframe, { template, palette: settings.palette }) }),
2361
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
1545
2362
  ] }, template)) }),
1546
- /* @__PURE__ */ jsx3(Flex2, { children: /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
2363
+ /* @__PURE__ */ jsx5(Flex4, { children: /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
1547
2364
  s.palette = { ...DEFAULT_PALETTE };
1548
2365
  return s;
1549
2366
  }), children: t.resetPalette }) })
1550
2367
  ] }) }),
1551
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1552
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.editor }),
1553
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2368
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2369
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
2370
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1554
2371
  s.editor.enabled = v;
1555
2372
  return s;
1556
2373
  }) }),
1557
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
1558
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2374
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
2375
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1559
2376
  s.editor.showOpenAll = v;
1560
2377
  return s;
1561
2378
  }) }),
1562
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2379
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1563
2380
  s.editor.showCloseAll = v;
1564
2381
  return s;
1565
2382
  }) }),
1566
- /* @__PURE__ */ jsx3(
2383
+ /* @__PURE__ */ jsx5(
1567
2384
  SelectField2,
1568
2385
  {
1569
2386
  name: "editor-initialState",
@@ -1577,10 +2394,10 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1577
2394
  })
1578
2395
  }
1579
2396
  ),
1580
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
1581
- previewSupported ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1582
- /* @__PURE__ */ jsx3(
1583
- TextField,
2397
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
2398
+ previewSupported ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2399
+ /* @__PURE__ */ jsx5(
2400
+ TextField2,
1584
2401
  {
1585
2402
  name: "editor-previewUrl",
1586
2403
  label: t.previewUrl,
@@ -1593,8 +2410,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1593
2410
  })
1594
2411
  }
1595
2412
  ),
1596
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
1597
- /* @__PURE__ */ jsx3(
2413
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
2414
+ /* @__PURE__ */ jsx5(
1598
2415
  SelectField2,
1599
2416
  {
1600
2417
  name: "editor-previewMode",
@@ -1608,26 +2425,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1608
2425
  })
1609
2426
  }
1610
2427
  ),
1611
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
1612
- data.blockPreviewAvailable && /* @__PURE__ */ jsxs2(Fragment, { children: [
1613
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2428
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
2429
+ data.blockPreviewAvailable && /* @__PURE__ */ jsxs4(Fragment, { children: [
2430
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1614
2431
  s.editor.blockPreviewInForm = v;
1615
2432
  return s;
1616
2433
  }) }),
1617
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
2434
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
1618
2435
  ] })
1619
- ] }) : /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
2436
+ ] }) : /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
1620
2437
  ] }) }),
1621
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1622
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.components }),
1623
- /* @__PURE__ */ jsx3(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
1624
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
1625
- /* @__PURE__ */ jsx3(Grid, { "data-testid": "components-grid", children: components.map((component) => {
2438
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2439
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.components }),
2440
+ /* @__PURE__ */ jsx5(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
2441
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
2442
+ /* @__PURE__ */ jsx5(Grid, { "data-testid": "components-grid", children: components.map((component) => {
1626
2443
  const entry = settings.components[component.uid] || {};
1627
2444
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
1628
2445
  const candidates = candidatesFor(component.uid, meta, catalog);
1629
- return /* @__PURE__ */ jsxs2(Card2, { "data-testid": `settings-${component.uid}`, children: [
1630
- /* @__PURE__ */ jsx3(
2446
+ return /* @__PURE__ */ jsxs4(Card2, { "data-testid": `settings-${component.uid}`, children: [
2447
+ /* @__PURE__ */ jsx5(
1631
2448
  Thumb,
1632
2449
  {
1633
2450
  candidates,
@@ -1638,23 +2455,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1638
2455
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
1639
2456
  }
1640
2457
  ),
1641
- /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "flex-start", gap: 2, children: [
1642
- /* @__PURE__ */ jsx3(Typography2, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
1643
- /* @__PURE__ */ jsxs2(Typography2, { variant: "pi", textColor: "neutral600", children: [
2458
+ /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
2459
+ /* @__PURE__ */ jsx5(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
2460
+ /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", textColor: "neutral600", children: [
1644
2461
  component.uid,
1645
2462
  " \xB7 ",
1646
2463
  component.category
1647
2464
  ] }),
1648
- /* @__PURE__ */ jsxs2(Typography2, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
2465
+ /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
1649
2466
  t.source,
1650
2467
  ": ",
1651
2468
  sourceOf(component.uid, Boolean(meta.manualImage)),
1652
2469
  meta.manualName ? ` (${meta.manualName})` : ""
1653
2470
  ] }),
1654
- meta.manualMissing && /* @__PURE__ */ jsx3(Typography2, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
1655
- /* @__PURE__ */ jsxs2(Flex2, { gap: 2, wrap: "wrap", children: [
1656
- /* @__PURE__ */ jsx3(Button2, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
1657
- entry.mediaId && /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
2471
+ meta.manualMissing && /* @__PURE__ */ jsx5(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
2472
+ /* @__PURE__ */ jsxs4(Flex4, { gap: 2, wrap: "wrap", children: [
2473
+ /* @__PURE__ */ jsx5(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
2474
+ entry.mediaId && /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
1658
2475
  update((s) => {
1659
2476
  delete s.components[component.uid]?.mediaId;
1660
2477
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -1667,7 +2484,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1667
2484
  });
1668
2485
  }, children: t.useAutomatic })
1669
2486
  ] }),
1670
- /* @__PURE__ */ jsx3(
2487
+ /* @__PURE__ */ jsx5(
1671
2488
  SelectField2,
1672
2489
  {
1673
2490
  name: `template-${component.uid}`,
@@ -1685,7 +2502,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1685
2502
  ] }, component.uid);
1686
2503
  }) })
1687
2504
  ] }) }),
1688
- picking && /* @__PURE__ */ jsx3(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
2505
+ picking && /* @__PURE__ */ jsx5(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
1689
2506
  const uid = picking;
1690
2507
  setPicking(null);
1691
2508
  if (!asset?.id) return;
@@ -1712,10 +2529,10 @@ function register(app, Component) {
1712
2529
  }
1713
2530
 
1714
2531
  // admin/catalog.ts
1715
- import * as React3 from "react";
2532
+ import * as React5 from "react";
1716
2533
  function useCatalog(get) {
1717
- const [catalog, setCatalog] = React3.useState(null);
1718
- React3.useEffect(() => {
2534
+ const [catalog, setCatalog] = React5.useState(null);
2535
+ React5.useEffect(() => {
1719
2536
  let active = true;
1720
2537
  get("/blockscene/catalog").then(({ data }) => {
1721
2538
  if (active) setCatalog(data);
@@ -1729,80 +2546,86 @@ function useCatalog(get) {
1729
2546
  return catalog;
1730
2547
  }
1731
2548
 
1732
- // admin/strapi4.tsx
1733
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1734
- function Modal({ open, onOpenChange, trigger, title, children }) {
1735
- const id = React4.useId();
1736
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1737
- trigger,
1738
- open && /* @__PURE__ */ jsxs3(ModalLayout, { onClose: () => onOpenChange(false), labelledBy: id, children: [
1739
- /* @__PURE__ */ jsx4(ModalHeader, { children: /* @__PURE__ */ jsx4(Typography3, { id, variant: "beta", children: title }) }),
1740
- /* @__PURE__ */ jsx4(ModalBody, { children })
2549
+ // admin/strapi5.tsx
2550
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2551
+ function Modal2({ open, onOpenChange, trigger, title, children }) {
2552
+ return /* @__PURE__ */ jsxs5(Dialog2.Root, { open, onOpenChange, children: [
2553
+ /* @__PURE__ */ jsx6(Dialog2.Trigger, { children: trigger }),
2554
+ /* @__PURE__ */ jsxs5(Dialog2.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
2555
+ /* @__PURE__ */ jsx6(Dialog2.Header, { children: /* @__PURE__ */ jsx6(Dialog2.Title, { children: title }) }),
2556
+ /* @__PURE__ */ jsx6(Dialog2.Body, { children })
1741
2557
  ] })
1742
2558
  ] });
1743
2559
  }
1744
- function Picker() {
1745
- const c = useCMEditViewDataManager();
1746
- const { get } = useFetchClient();
2560
+ function Panel() {
2561
+ const c = useContext2();
2562
+ const rbac = useDocumentRBAC2("Blockscene", (state) => state);
2563
+ const { get } = useFetchClient2();
2564
+ const t = useMessages();
2565
+ const user = useAuth("Blockscene", (state) => state.user);
2566
+ const formValues = useForm3("Blockscene", (state) => state.values);
1747
2567
  const catalog = useCatalog(get);
1748
- const components = c.allLayoutData?.components || {};
1749
- const schema = c.layout || c.allLayoutData?.contentType;
1750
- const allowed = (c.isCreatingEntry ? c.createActionAllowedFields : c.updateActionAllowedFields) || [];
1751
- const zones = editableZones(schema, c.modifiedData, (name) => allowed.includes(name) && schema?.metadatas?.[name]?.edit?.editable !== false, !c.addComponentToDynamicZone).map((zone) => ({ ...zone, label: schema?.metadatas?.[zone.name]?.edit?.label || zone.name }));
2568
+ const creating = !c.id && !c.form?.initialValues?.id && !c.form?.initialValues?.documentId;
2569
+ const allowed = (creating ? rbac.canCreateFields : rbac.canUpdateFields) || [];
2570
+ const fields = c.layout?.edit?.layout?.flat(3) || [];
2571
+ const zones = editableZones(
2572
+ c.contentType,
2573
+ formValues ?? c.form?.values,
2574
+ (name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
2575
+ c.isLoading || c.form?.disabled || !c.form?.addFieldRow
2576
+ ).map((zone) => ({ ...zone, label: fields.find((field) => field.name === zone.name)?.label || zone.name }));
1752
2577
  const add = (zone, uid) => {
2578
+ const values = formValues ?? c.form.values;
1753
2579
  const close = catalog?.groups?.[uid];
1754
- if (!canInsert(zone, uid, c.modifiedData, components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
1755
- c.addComponentToDynamicZone(zone.name, components[uid], components, Boolean(c.formErrors?.[zone.name]));
1756
- if (close) c.addComponentToDynamicZone(zone.name, components[close], components, Boolean(c.formErrors?.[zone.name]));
2580
+ if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
2581
+ const at = Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0;
2582
+ c.form.addFieldRow(zone.name, { ...componentDefaults(c.components[uid], c.components), __component: uid }, at);
2583
+ if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
1757
2584
  return true;
1758
2585
  };
1759
- if (!catalog?.editor?.enabled) return null;
1760
- const docKey = `${c.slug}:${c.isCreatingEntry ? "new" : c.initialData?.id}:${c.initialData?.locale || ""}`;
1761
- return /* @__PURE__ */ jsx4(
1762
- Gallery,
1763
- {
1764
- zones,
1765
- components,
1766
- add,
1767
- Modal,
1768
- Toggle: ToggleField,
1769
- get,
1770
- editor: catalog.editor,
1771
- docKey,
1772
- contentType: c.slug,
1773
- userId: auth.getUserInfo?.()?.id
1774
- }
1775
- );
2586
+ if (!zones.length || !catalog?.editor?.enabled) return null;
2587
+ const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
2588
+ return { title: t.gallery, content: /* @__PURE__ */ jsxs5(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
2589
+ /* @__PURE__ */ jsx6(Gallery, { zones, components: c.components, add, Modal: Modal2, Toggle: ToggleField, get, editor: catalog.editor, docKey, contentType: c.model, userId: user?.id }),
2590
+ /* @__PURE__ */ jsx6(PagePreview, { editor: catalog.editor, groups: catalog.groups || null, Toggle: ToggleField })
2591
+ ] }) };
1776
2592
  }
1777
- var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs3(Flex3, { gap: 3, children: [
1778
- /* @__PURE__ */ jsx4(Switch, { name, "aria-label": label, selected: Boolean(value), disabled, onChange: () => onChange(!value) }),
1779
- /* @__PURE__ */ jsx4(Typography3, { children: label })
2593
+ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs5(Flex5, { gap: 3, children: [
2594
+ /* @__PURE__ */ jsx6(Switch, { name, "aria-label": label, checked: Boolean(value), disabled, onCheckedChange: onChange }),
2595
+ /* @__PURE__ */ jsx6(Typography5, { children: label })
2596
+ ] });
2597
+ var TextField = ({ name, label, value, onChange, disabled, placeholder }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2598
+ /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2599
+ /* @__PURE__ */ jsx6(TextInput, { name, value, disabled, placeholder, onChange: (e) => onChange(e.target.value) })
2600
+ ] });
2601
+ var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2602
+ /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2603
+ /* @__PURE__ */ jsx6(SingleSelect2, { value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ jsx6(SingleSelectOption2, { value: o.value, children: o.label }, o.value)) })
1780
2604
  ] });
1781
- var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsx4(SingleSelect2, { name, label, value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ jsx4(SingleSelectOption2, { value: o.value, children: o.label }, o.value)) });
1782
2605
  function MediaPicker({ onClose, onSelect }) {
1783
- const { components } = useLibrary();
1784
- const Dialog = components?.["media-library"];
1785
- if (!Dialog) {
1786
- onClose();
1787
- return null;
1788
- }
1789
- return /* @__PURE__ */ jsx4(Dialog, { onClose, allowedTypes: ["images"], onSelectAssets: (assets) => onSelect(assets?.[0]) });
2606
+ const components = useStrapiApp3("Blockscene", (state) => state.components);
2607
+ const Dialog3 = components?.["media-library"];
2608
+ React6.useEffect(() => {
2609
+ if (!Dialog3) onClose();
2610
+ }, [Dialog3, onClose]);
2611
+ if (!Dialog3) return null;
2612
+ return /* @__PURE__ */ jsx6(Dialog3, { onClose, allowedTypes: ["images"], multiple: false, onSelectAssets: (assets) => onSelect(assets?.[0]) });
1790
2613
  }
1791
2614
  function usePermissions() {
1792
2615
  const { allowedActions, isLoading } = useRBAC(permissions);
1793
2616
  return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
1794
2617
  }
1795
- var SettingsPage = () => /* @__PURE__ */ jsx4(Settings, { useClient: useFetchClient, usePermissions, MediaPicker, ToggleField, SelectField, previewSupported: false });
1796
- var strapi4_default = {
2618
+ var SettingsPage = () => /* @__PURE__ */ jsx6(Settings, { useClient: useFetchClient2, usePermissions, MediaPicker, ToggleField, SelectField, TextField, previewSupported: true });
2619
+ var strapi5_default = {
1797
2620
  register(app) {
1798
2621
  register(app, SettingsPage);
1799
2622
  app.registerPlugin({ id: "blockscene", name: "Blockscene" });
1800
2623
  },
1801
2624
  registerTrads,
1802
2625
  bootstrap(app) {
1803
- app.injectContentManagerComponent("editView", "right-links", { name: "blockscene", Component: Picker });
2626
+ app.getPlugin("content-manager").apis.addEditViewSidePanel([Panel]);
1804
2627
  }
1805
2628
  };
1806
2629
  export {
1807
- strapi4_default as default
2630
+ strapi5_default as default
1808
2631
  };