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

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 +960 -156
  2. package/dist/admin.mjs +982 -155
  3. package/package.json +8 -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,712 @@ 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 moveFieldRow = useForm2("BlockscenePagePreview", (state) => state.moveFieldRow);
1851
+ const fields = c.layout?.edit?.layout?.flat(3) || [];
1852
+ const readable = (name) => c.isCreatingEntry || (rbac.canReadFields || []).includes(name);
1853
+ const zone = React3.useMemo(() => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name, [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]);
1854
+ const zoneLabel = fields.find((field) => field.name === zone)?.label || zone || "";
1855
+ 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");
1856
+ const [mode, setModeState] = React3.useState(() => {
1857
+ const v = read(MODE_KEY);
1858
+ return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
1859
+ });
1860
+ const setMode = (next) => {
1861
+ setModeState(next);
1862
+ write(MODE_KEY, next);
1863
+ };
1864
+ const [ratio, setRatio] = React3.useState(() => {
1865
+ const v = Number(read(RATIO_KEY));
1866
+ return v > 0 && v < 1 ? v : 0.5;
1867
+ });
1868
+ const main = useMainRect();
1869
+ const vw = main.width;
1870
+ const [dragging, setDragging] = React3.useState(false);
1871
+ const [ready, setReady] = React3.useState(false);
1872
+ const [failed, setFailed] = React3.useState(false);
1873
+ const [attempt, retry] = React3.useReducer((n) => n + 1, 0);
1874
+ const [picking, setPicking] = React3.useState(null);
1875
+ React3.useEffect(() => {
1876
+ setEditing(null);
1877
+ setPicking(null);
1878
+ setInserting(null);
1879
+ }, [c.id, c.form?.initialValues?.locale]);
1880
+ const [editing, setEditing] = React3.useState(null);
1881
+ const [inserting, setInserting] = React3.useState(null);
1882
+ const { get } = useFetchClient();
1883
+ const { toggleNotification } = useNotification();
1884
+ const zoneAttr = zone ? c.contentType?.attributes?.[zone] : null;
1885
+ const iframe = React3.useRef(null);
1886
+ const loaded = React3.useRef(false);
1887
+ const anchor = React3.useRef(null);
1888
+ const channel = React3.useMemo(() => crypto.randomUUID(), []);
1889
+ const { base, source } = usePreviewBase(editor?.previewUrl || "", c.model, c.isCreatingEntry ? void 0 : c.id, c.form?.initialValues?.locale);
1890
+ const url = base ? new URL(base) : null;
1891
+ url?.searchParams.set("channel", channel);
1892
+ const origin = url?.origin || "";
1893
+ const narrow = vw < NARROW;
1894
+ const paneWidth = clampWidth(ratio * vw, vw);
1895
+ const resizable = mode === "split" && !narrow;
1896
+ const active = mode !== "form" && Boolean(url) && Boolean(zone);
1897
+ const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
1898
+ const latest = React3.useRef(rows);
1899
+ latest.current = rows;
1900
+ const insertRows = (at, ...items) => items.forEach((item, i) => {
1901
+ const from = latest.current.length + i;
1902
+ addFieldRow(zone, item);
1903
+ if (at + i < from) moveFieldRow(zone, from, at + i);
1904
+ });
1905
+ const live = React3.useRef({ mode, canEdit, zoneLabel, components: c.components, setMode, onChange });
1906
+ live.current = { mode, canEdit, zoneLabel, components: c.components, setMode, onChange };
1907
+ const send = React3.useCallback(() => {
1908
+ 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);
1909
+ }, [channel, origin]);
1910
+ const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
1911
+ const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
1912
+ React3.useEffect(() => {
1913
+ if (resizable) markLayout(anchor.current);
1914
+ document.body.classList.toggle("bp-split", resizable);
1915
+ document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
1916
+ return () => document.body.classList.remove("bp-split");
1917
+ }, [resizable, paneWidth]);
1918
+ const applyWidth = (width, persist) => {
1919
+ const next = clampWidth(width, vw) / vw;
1920
+ setRatio(next);
1921
+ if (persist) write(RATIO_KEY, String(next));
1922
+ };
1923
+ const focusField = (index, field) => {
1924
+ const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
1925
+ if (!item) return;
1926
+ const header = toggles(item.parentElement)[index];
1927
+ if (header?.getAttribute("aria-expanded") === "false") header.click();
1928
+ const parts = field.split(".");
1929
+ const escaped = CSS.escape(`${zone}.${index}.${field}`);
1930
+ let tries = 0;
1931
+ const timer = setInterval(() => {
1932
+ if (++tries > 40) return clearInterval(timer);
1933
+ let scope = item;
1934
+ for (let i = 0; i + 1 < parts.length; i += 2) {
1935
+ const nested = [...scope.querySelectorAll("button[aria-expanded][data-radix-collection-item]")].filter((b) => b !== header);
1936
+ const row = nested[Number(parts[i + 1])];
1937
+ if (!row) return;
1938
+ if (row.getAttribute("aria-expanded") === "false") row.click();
1939
+ scope = row.closest("li, div") || scope;
1940
+ }
1941
+ const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
1942
+ if (!target) return;
1943
+ const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
1944
+ let container = target.matches(INPUT) ? target : target.parentElement;
1945
+ for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++) container = container.parentElement;
1946
+ const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
1947
+ if (!editable) return;
1948
+ if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
1949
+ editable.focus();
1950
+ if (document.activeElement !== editable) return;
1951
+ clearInterval(timer);
1952
+ editable.scrollIntoView({ block: "center", behavior: "smooth" });
1953
+ }, 150);
1954
+ };
1955
+ React3.useEffect(() => {
1956
+ if (!active) return;
1957
+ setReady(false);
1958
+ setFailed(false);
1959
+ const timeout = setTimeout(() => setFailed(true), 15e3);
1960
+ const receive = (event) => {
1961
+ const is = (type) => isPreviewMessage(event, origin, iframe.current?.contentWindow, channel, type);
1962
+ const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
1963
+ if (is("ready")) {
1964
+ clearTimeout(timeout);
1965
+ setReady(true);
1966
+ setFailed(false);
1967
+ send();
1968
+ return;
1969
+ }
1970
+ if (is("insert") && canEdit2) {
1971
+ const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
1972
+ if (after !== null && insertIndex(latest.current, after) < 0) return;
1973
+ if (latest.current.length >= (zoneAttr?.max ?? Infinity)) {
1974
+ toggleNotification({ type: "info", message: t.zoneFull });
1975
+ return;
1976
+ }
1977
+ setInserting({ after });
1978
+ return;
1979
+ }
1980
+ if (is("move-group") && canEdit2 && groups) {
1981
+ const next = (0, import_groups.moveGroup)(latest.current, String(event.data.key), event.data.direction === "up" ? "up" : "down", groups);
1982
+ if (next) onChange2(zone, next);
1983
+ return;
1984
+ }
1985
+ if (is("delete-group") && canEdit2 && groups) {
1986
+ const next = (0, import_groups.removeGroup)(latest.current, String(event.data.key), groups);
1987
+ if (next) onChange2(zone, next);
1988
+ return;
1989
+ }
1990
+ if (is("insert-group") && canEdit2 && groups) {
1991
+ const after = event.data.after === null || event.data.after === void 0 ? null : String(event.data.after);
1992
+ const index2 = insertIndex(latest.current, after);
1993
+ const uid2 = String(event.data.uid || "");
1994
+ const close = groups[uid2];
1995
+ const allowed = zoneAttr?.components || [];
1996
+ if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
1997
+ if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
1998
+ toggleNotification({ type: "info", message: t.zoneFull });
1999
+ return;
2000
+ }
2001
+ insertRows(index2, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, { __component: close });
2002
+ return;
2003
+ }
2004
+ const key = typeof event.data?.key === "string" ? event.data.key : "";
2005
+ const index = key ? indexOf(key) : -1;
2006
+ const uid = index >= 0 ? latest.current[index]?.__component : "";
2007
+ if (index < 0) return;
2008
+ if (is("select")) {
2009
+ const list = findZoneList(zoneLabel2);
2010
+ const item = list?.querySelectorAll(":scope > li")[index];
2011
+ const header = list ? toggles(list)[index] : null;
2012
+ if (header?.getAttribute("aria-expanded") === "false") header.click();
2013
+ item?.scrollIntoView({ block: "center", behavior: "smooth" });
2014
+ if (item) {
2015
+ item.style.outline = "2px solid #4945ff";
2016
+ setTimeout(() => {
2017
+ item.style.outline = "";
2018
+ }, 1500);
2019
+ }
2020
+ if (mode2 === "preview") setMode2("split");
2021
+ iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "highlight", key }, origin);
2022
+ } else if (is("focus")) {
2023
+ const attr = validateFocus(uid, event.data.field, components2);
2024
+ if (!attr) return;
2025
+ 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) });
2026
+ else focusField(index, event.data.field);
2027
+ } else if (is("media") && canEdit2) {
2028
+ const attr = mediaAttribute(uid, event.data.field, components2);
2029
+ if (attr) setPicking({ key, field: event.data.field, attr });
2030
+ } else if (is("media-remove") && canEdit2) {
2031
+ const attr = mediaAttribute(uid, event.data.field, components2);
2032
+ if (attr && !attr.required) onChange2(path(key, event.data.field), null);
2033
+ } else if (is("edit") && canEdit2) {
2034
+ const value = validateEdit(uid, event.data.field, event.data.value, components2);
2035
+ if (value !== null) onChange2(path(key, event.data.field), value);
2036
+ }
2037
+ };
2038
+ window.addEventListener("message", receive);
2039
+ if (loaded.current) iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "ping" }, origin);
2040
+ return () => {
2041
+ clearTimeout(timeout);
2042
+ window.removeEventListener("message", receive);
2043
+ };
2044
+ }, [active, attempt, channel, origin, groups]);
2045
+ React3.useEffect(() => {
2046
+ if (!ready || !active) return;
2047
+ const timer = setTimeout(send, 120);
2048
+ return () => clearTimeout(timer);
2049
+ }, [values, c.components, ready, active, send, mode]);
2050
+ if (!zone) return null;
2051
+ const modes = [["form", t.modeForm], ["split", t.modeSplit], ["preview", t.modePreview]];
2052
+ 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)) });
2053
+ const onKey = (event) => {
2054
+ const map = { ArrowLeft: paneWidth + KEY_STEP, ArrowRight: paneWidth - KEY_STEP, Home: MIN_PANE, End: vw - MIN_FORM };
2055
+ if (event.key in map) {
2056
+ event.preventDefault();
2057
+ applyWidth(map[event.key], true);
2058
+ }
2059
+ };
2060
+ const problems = groups ? (0, import_groups.validateGroups)(rows, groups) : [];
2061
+ const insertClose = (error) => {
2062
+ const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
2063
+ insertRows(end + 1, { __component: error.expected });
2064
+ };
2065
+ 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: [
2066
+ /* @__PURE__ */ jsx4("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ jsx4(Typography3, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
2067
+ /* @__PURE__ */ jsx4("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ jsxs3("li", { style: { marginBottom: 6 }, children: [
2068
+ /* @__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 || "" }) }),
2069
+ 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 }) }) }),
2070
+ error.code === "closeBeforeOpen" && /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
2071
+ ] }, `${error.code}-${error.index}`)) })
2072
+ ] }) });
2073
+ return /* @__PURE__ */ jsxs3(Flex3, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
2074
+ switcher,
2075
+ diagnostics,
2076
+ /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2077
+ active && createPortal(
2078
+ /* @__PURE__ */ jsxs3(
2079
+ Pane,
2080
+ {
2081
+ "data-testid": "page-preview-pane",
2082
+ "aria-label": t.previewPane,
2083
+ style: { left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth, width: mode === "preview" || narrow ? main.width : paneWidth, userSelect: dragging ? "none" : void 0 },
2084
+ children: [
2085
+ /* @__PURE__ */ jsx4("style", { children: SPLIT_STYLE }),
2086
+ resizable && /* @__PURE__ */ jsx4(
2087
+ Handle,
2088
+ {
2089
+ role: "separator",
2090
+ "aria-orientation": "vertical",
2091
+ "aria-label": t.resize,
2092
+ tabIndex: 0,
2093
+ "aria-valuemin": MIN_PANE,
2094
+ "aria-valuemax": Math.max(MIN_PANE, vw - MIN_FORM),
2095
+ "aria-valuenow": paneWidth,
2096
+ "data-testid": "page-preview-resizer",
2097
+ "data-dragging": dragging,
2098
+ onPointerDown: (e) => {
2099
+ e.preventDefault();
2100
+ e.currentTarget.setPointerCapture(e.pointerId);
2101
+ setDragging(true);
2102
+ },
2103
+ onPointerMove: (e) => {
2104
+ if (dragging) applyWidth(main.left + main.width - e.clientX, false);
2105
+ },
2106
+ onPointerUp: (e) => {
2107
+ if (!dragging) return;
2108
+ setDragging(false);
2109
+ e.currentTarget.releasePointerCapture(e.pointerId);
2110
+ applyWidth(main.left + main.width - e.clientX, true);
2111
+ },
2112
+ onPointerCancel: () => setDragging(false),
2113
+ onKeyDown: onKey
2114
+ }
2115
+ ),
2116
+ /* @__PURE__ */ jsxs3(Flex3, { padding: 2, gap: 2, justifyContent: "space-between", alignItems: "center", background: "neutral100", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 2 }, children: [
2117
+ switcher,
2118
+ /* @__PURE__ */ jsxs3(Flex3, { gap: 2, alignItems: "center", wrap: "wrap", style: { marginLeft: "auto" }, children: [
2119
+ /* @__PURE__ */ jsx4(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
2120
+ failed && /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
2121
+ /* @__PURE__ */ jsx4(ToolbarActions, { model: c.model, collectionType: c.collectionType, documentId: c.isCreatingEntry ? void 0 : c.id, locale: c.form?.initialValues?.locale })
2122
+ ] })
2123
+ ] }),
2124
+ /* @__PURE__ */ jsx4(
2125
+ Frame2,
2126
+ {
2127
+ ref: iframe,
2128
+ title: t.previewPane,
2129
+ src: url.href,
2130
+ sandbox: "allow-scripts allow-same-origin",
2131
+ referrerPolicy: "no-referrer",
2132
+ onLoad: () => {
2133
+ loaded.current = true;
2134
+ },
2135
+ style: dragging ? { pointerEvents: "none" } : void 0
2136
+ },
2137
+ attempt
2138
+ )
2139
+ ]
2140
+ }
2141
+ ),
2142
+ document.body
2143
+ ),
2144
+ inserting && zoneAttr && /* @__PURE__ */ jsx4(
2145
+ PickerModal,
2146
+ {
2147
+ zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
2148
+ components: c.components,
2149
+ Modal: PickerDialog,
2150
+ Toggle,
2151
+ get,
2152
+ open: true,
2153
+ onOpenChange: (open) => {
2154
+ if (!open) setInserting(null);
2155
+ },
2156
+ onSelect: (uid) => {
2157
+ const index = insertIndex(latest.current, inserting.after);
2158
+ setInserting(null);
2159
+ const close = groups?.[uid];
2160
+ if (index < 0 || !(zoneAttr.components || []).includes(uid) || close && !(zoneAttr.components || []).includes(close) || latest.current.length + (close ? 2 : 1) > (zoneAttr.max ?? Infinity)) {
2161
+ toggleNotification({ type: "warning", message: close ? t.zoneFull : t.insertMoved });
2162
+ return;
2163
+ }
2164
+ insertRows(index, { ...componentDefaults(c.components[uid], c.components), __component: uid }, ...close ? [{ __component: close }] : []);
2165
+ }
2166
+ }
2167
+ ),
2168
+ editing && /* @__PURE__ */ jsx4(
2169
+ FieldEditorModal,
2170
+ {
2171
+ label: editing.label,
2172
+ attribute: editing.attr,
2173
+ value: editing.value,
2174
+ onCancel: () => setEditing(null),
2175
+ onApply: (value) => {
2176
+ const index = indexOf(editing.key);
2177
+ setEditing(null);
2178
+ if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
2179
+ }
2180
+ }
2181
+ ),
2182
+ picking && MediaLibraryDialog && /* @__PURE__ */ jsx4(
2183
+ MediaLibraryDialog,
2184
+ {
2185
+ allowedTypes: picking.attr.allowedTypes || ["images"],
2186
+ multiple: picking.attr.multiple === true,
2187
+ onClose: () => setPicking(null),
2188
+ onSelectAssets: (assets) => {
2189
+ const index = indexOf(picking.key);
2190
+ setPicking(null);
2191
+ if (index < 0 || !assets?.length) return;
2192
+ const current = getIn(latest.current[index], picking.field);
2193
+ onChange(`${zone}.${index}.${picking.field}`, picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]);
2194
+ }
2195
+ }
2196
+ ),
2197
+ /* @__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 : "" })
2198
+ ] });
2199
+ }
2200
+
2201
+ // admin/Settings.tsx
2202
+ import * as React4 from "react";
2203
+ import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
2204
+ import styled3 from "styled-components";
2205
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1385
2206
  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`
2207
+ var Grid = styled3.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
2208
+ var Card2 = styled3.div`
1388
2209
  display: flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: 4px;
1389
2210
  border: 1px solid ${({ theme }) => theme.colors.neutral200}; background: ${({ theme }) => theme.colors.neutral0};
1390
2211
  `;
1391
- var Bar = styled2.div`
2212
+ var Bar = styled3.div`
1392
2213
  position: fixed; top: 0; z-index: 4; padding: 12px 32px;
1393
2214
  background: ${({ theme }) => theme.colors.neutral100}; border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
1394
2215
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
1395
2216
  `;
1396
- function useMainRect(anchor) {
1397
- const [rect, setRect] = React2.useState({ left: 0, width: 0 });
1398
- React2.useEffect(() => {
1399
- const read = () => {
2217
+ function useMainRect2(anchor) {
2218
+ const [rect, setRect] = React4.useState({ left: 0, width: 0 });
2219
+ React4.useEffect(() => {
2220
+ const read2 = () => {
1400
2221
  let host = document.getElementById("main-content");
1401
2222
  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
2223
  const r = (host || document.querySelector("main"))?.getBoundingClientRect();
1403
2224
  if (r) setRect({ left: Math.round(r.left), width: Math.round(r.width) });
1404
2225
  };
1405
- read();
1406
- window.addEventListener("resize", read);
1407
- return () => window.removeEventListener("resize", read);
2226
+ read2();
2227
+ window.addEventListener("resize", read2);
2228
+ return () => window.removeEventListener("resize", read2);
1408
2229
  });
1409
2230
  return rect;
1410
2231
  }
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; `;
2232
+ var Swatches = styled3.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; `;
2233
+ var Label = styled3.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
1413
2234
  function ColorField({ name, label, value, onChange, disabled }) {
1414
2235
  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(
2236
+ return /* @__PURE__ */ jsxs4(Label, { children: [
2237
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
2238
+ /* @__PURE__ */ jsxs4(Flex4, { gap: 2, children: [
2239
+ /* @__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" } }),
2240
+ /* @__PURE__ */ jsx5(
1420
2241
  "input",
1421
2242
  {
1422
2243
  name,
@@ -1432,22 +2253,22 @@ function ColorField({ name, label, value, onChange, disabled }) {
1432
2253
  ] });
1433
2254
  }
1434
2255
  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 }) {
2256
+ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: MediaPicker2, ToggleField: ToggleField2, SelectField: SelectField2, TextField: TextField2, previewSupported = false }) {
1436
2257
  const t = useMessages();
1437
2258
  const { get, put } = useClient();
1438
2259
  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(() => {
2260
+ const [data, setData] = React4.useState(null);
2261
+ const [catalog, setCatalog] = React4.useState(null);
2262
+ const [settings, setSettings] = React4.useState(null);
2263
+ const [media, setMedia] = React4.useState({});
2264
+ const [sources, setSources] = React4.useState({});
2265
+ const [dirty, setDirty] = React4.useState(false);
2266
+ const [saving, setSaving] = React4.useState(false);
2267
+ const [status, setStatus] = React4.useState("");
2268
+ const [failed, setFailed] = React4.useState(false);
2269
+ const [filter, setFilter] = React4.useState("");
2270
+ const [picking, setPicking] = React4.useState(null);
2271
+ React4.useEffect(() => {
1451
2272
  if (!canRead || isLoading) return;
1452
2273
  let active = true;
1453
2274
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -1495,35 +2316,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1495
2316
  return index === 0 && manual ? t.manual : t.automatic;
1496
2317
  };
1497
2318
  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(() => {
2319
+ const header = React4.useRef(null);
2320
+ const [floating, setFloating] = React4.useState(false);
2321
+ const main = useMainRect2(header);
2322
+ React4.useEffect(() => {
1502
2323
  const el = header.current;
1503
2324
  if (!el || typeof IntersectionObserver === "undefined") return;
1504
2325
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
1505
2326
  observer.observe(el);
1506
2327
  return () => observer.disconnect();
1507
2328
  }, [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 })
2329
+ const controls = (suffix) => ready && /* @__PURE__ */ jsxs4(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
2330
+ /* @__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 }),
2331
+ /* @__PURE__ */ jsx5(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
1511
2332
  ] });
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 }),
2333
+ return /* @__PURE__ */ jsx5(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
2334
+ /* @__PURE__ */ jsx5("div", { ref: header, children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2335
+ /* @__PURE__ */ jsx5(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
1515
2336
  controls("")
1516
2337
  ] }) }),
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 }),
2338
+ 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: [
2339
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
1519
2340
  controls("-floating")
1520
2341
  ] }) }),
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(
2342
+ /* @__PURE__ */ jsx5(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
2343
+ 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: [
2344
+ data.disabled && /* @__PURE__ */ jsx5(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
2345
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2346
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
2347
+ /* @__PURE__ */ jsx5(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx5(
1527
2348
  ColorField,
1528
2349
  {
1529
2350
  name: `palette-${key}`,
@@ -1537,33 +2358,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1537
2358
  },
1538
2359
  key
1539
2360
  )) }),
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] })
2361
+ invalid && /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
2362
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
2363
+ /* @__PURE__ */ jsx5(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs4(Flex4, { direction: "column", gap: 1, children: [
2364
+ /* @__PURE__ */ jsx5(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx5(Wireframe, { template, palette: settings.palette }) }),
2365
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
1545
2366
  ] }, template)) }),
1546
- /* @__PURE__ */ jsx3(Flex2, { children: /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
2367
+ /* @__PURE__ */ jsx5(Flex4, { children: /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
1547
2368
  s.palette = { ...DEFAULT_PALETTE };
1548
2369
  return s;
1549
2370
  }), children: t.resetPalette }) })
1550
2371
  ] }) }),
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) => {
2372
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2373
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
2374
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1554
2375
  s.editor.enabled = v;
1555
2376
  return s;
1556
2377
  }) }),
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) => {
2378
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
2379
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1559
2380
  s.editor.showOpenAll = v;
1560
2381
  return s;
1561
2382
  }) }),
1562
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2383
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1563
2384
  s.editor.showCloseAll = v;
1564
2385
  return s;
1565
2386
  }) }),
1566
- /* @__PURE__ */ jsx3(
2387
+ /* @__PURE__ */ jsx5(
1567
2388
  SelectField2,
1568
2389
  {
1569
2390
  name: "editor-initialState",
@@ -1577,10 +2398,10 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1577
2398
  })
1578
2399
  }
1579
2400
  ),
1580
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
1581
- previewSupported ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1582
- /* @__PURE__ */ jsx3(
1583
- TextField,
2401
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
2402
+ previewSupported ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2403
+ /* @__PURE__ */ jsx5(
2404
+ TextField2,
1584
2405
  {
1585
2406
  name: "editor-previewUrl",
1586
2407
  label: t.previewUrl,
@@ -1593,8 +2414,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1593
2414
  })
1594
2415
  }
1595
2416
  ),
1596
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
1597
- /* @__PURE__ */ jsx3(
2417
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
2418
+ /* @__PURE__ */ jsx5(
1598
2419
  SelectField2,
1599
2420
  {
1600
2421
  name: "editor-previewMode",
@@ -1608,26 +2429,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1608
2429
  })
1609
2430
  }
1610
2431
  ),
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) => {
2432
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
2433
+ data.blockPreviewAvailable && /* @__PURE__ */ jsxs4(Fragment, { children: [
2434
+ /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1614
2435
  s.editor.blockPreviewInForm = v;
1615
2436
  return s;
1616
2437
  }) }),
1617
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
2438
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
1618
2439
  ] })
1619
- ] }) : /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
2440
+ ] }) : /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
1620
2441
  ] }) }),
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) => {
2442
+ /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2443
+ /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.components }),
2444
+ /* @__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 }),
2445
+ /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
2446
+ /* @__PURE__ */ jsx5(Grid, { "data-testid": "components-grid", children: components.map((component) => {
1626
2447
  const entry = settings.components[component.uid] || {};
1627
2448
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
1628
2449
  const candidates = candidatesFor(component.uid, meta, catalog);
1629
- return /* @__PURE__ */ jsxs2(Card2, { "data-testid": `settings-${component.uid}`, children: [
1630
- /* @__PURE__ */ jsx3(
2450
+ return /* @__PURE__ */ jsxs4(Card2, { "data-testid": `settings-${component.uid}`, children: [
2451
+ /* @__PURE__ */ jsx5(
1631
2452
  Thumb,
1632
2453
  {
1633
2454
  candidates,
@@ -1638,23 +2459,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1638
2459
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
1639
2460
  }
1640
2461
  ),
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: [
2462
+ /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
2463
+ /* @__PURE__ */ jsx5(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
2464
+ /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", textColor: "neutral600", children: [
1644
2465
  component.uid,
1645
2466
  " \xB7 ",
1646
2467
  component.category
1647
2468
  ] }),
1648
- /* @__PURE__ */ jsxs2(Typography2, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
2469
+ /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
1649
2470
  t.source,
1650
2471
  ": ",
1651
2472
  sourceOf(component.uid, Boolean(meta.manualImage)),
1652
2473
  meta.manualName ? ` (${meta.manualName})` : ""
1653
2474
  ] }),
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: () => {
2475
+ meta.manualMissing && /* @__PURE__ */ jsx5(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
2476
+ /* @__PURE__ */ jsxs4(Flex4, { gap: 2, wrap: "wrap", children: [
2477
+ /* @__PURE__ */ jsx5(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
2478
+ entry.mediaId && /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
1658
2479
  update((s) => {
1659
2480
  delete s.components[component.uid]?.mediaId;
1660
2481
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -1667,7 +2488,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1667
2488
  });
1668
2489
  }, children: t.useAutomatic })
1669
2490
  ] }),
1670
- /* @__PURE__ */ jsx3(
2491
+ /* @__PURE__ */ jsx5(
1671
2492
  SelectField2,
1672
2493
  {
1673
2494
  name: `template-${component.uid}`,
@@ -1685,7 +2506,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1685
2506
  ] }, component.uid);
1686
2507
  }) })
1687
2508
  ] }) }),
1688
- picking && /* @__PURE__ */ jsx3(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
2509
+ picking && /* @__PURE__ */ jsx5(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
1689
2510
  const uid = picking;
1690
2511
  setPicking(null);
1691
2512
  if (!asset?.id) return;
@@ -1714,10 +2535,10 @@ function register(app, Component, to) {
1714
2535
  }
1715
2536
 
1716
2537
  // admin/catalog.ts
1717
- import * as React3 from "react";
2538
+ import * as React5 from "react";
1718
2539
  function useCatalog(get) {
1719
- const [catalog, setCatalog] = React3.useState(null);
1720
- React3.useEffect(() => {
2540
+ const [catalog, setCatalog] = React5.useState(null);
2541
+ React5.useEffect(() => {
1721
2542
  let active = true;
1722
2543
  get("/blockscene/catalog").then(({ data }) => {
1723
2544
  if (active) setCatalog(data);
@@ -1731,80 +2552,86 @@ function useCatalog(get) {
1731
2552
  return catalog;
1732
2553
  }
1733
2554
 
1734
- // admin/strapi4.tsx
1735
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1736
- function Modal({ open, onOpenChange, trigger, title, children }) {
1737
- const id = React4.useId();
1738
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1739
- trigger,
1740
- open && /* @__PURE__ */ jsxs3(ModalLayout, { onClose: () => onOpenChange(false), labelledBy: id, children: [
1741
- /* @__PURE__ */ jsx4(ModalHeader, { children: /* @__PURE__ */ jsx4(Typography3, { id, variant: "beta", children: title }) }),
1742
- /* @__PURE__ */ jsx4(ModalBody, { children })
2555
+ // admin/strapi5.tsx
2556
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2557
+ function Modal2({ open, onOpenChange, trigger, title, children }) {
2558
+ return /* @__PURE__ */ jsxs5(Dialog2.Root, { open, onOpenChange, children: [
2559
+ /* @__PURE__ */ jsx6(Dialog2.Trigger, { children: trigger }),
2560
+ /* @__PURE__ */ jsxs5(Dialog2.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
2561
+ /* @__PURE__ */ jsx6(Dialog2.Header, { children: /* @__PURE__ */ jsx6(Dialog2.Title, { children: title }) }),
2562
+ /* @__PURE__ */ jsx6(Dialog2.Body, { children })
1743
2563
  ] })
1744
2564
  ] });
1745
2565
  }
1746
- function Picker() {
1747
- const c = useCMEditViewDataManager();
1748
- const { get } = useFetchClient();
2566
+ function Panel() {
2567
+ const c = useContext2();
2568
+ const rbac = useDocumentRBAC2("Blockscene", (state) => state);
2569
+ const { get } = useFetchClient2();
2570
+ const t = useMessages();
2571
+ const user = useAuth("Blockscene", (state) => state.user);
2572
+ const formValues = useForm3("Blockscene", (state) => state.values);
1749
2573
  const catalog = useCatalog(get);
1750
- const components = c.allLayoutData?.components || {};
1751
- const schema = c.layout || c.allLayoutData?.contentType;
1752
- const allowed = (c.isCreatingEntry ? c.createActionAllowedFields : c.updateActionAllowedFields) || [];
1753
- 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 }));
2574
+ const creating = !c.id && !c.form?.initialValues?.id && !c.form?.initialValues?.documentId;
2575
+ const allowed = (creating ? rbac.canCreateFields : rbac.canUpdateFields) || [];
2576
+ const fields = c.layout?.edit?.layout?.flat(3) || [];
2577
+ const zones = editableZones(
2578
+ c.contentType,
2579
+ formValues ?? c.form?.values,
2580
+ (name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
2581
+ c.isLoading || c.form?.disabled || !c.form?.addFieldRow
2582
+ ).map((zone) => ({ ...zone, label: fields.find((field) => field.name === zone.name)?.label || zone.name }));
1754
2583
  const add = (zone, uid) => {
2584
+ const values = formValues ?? c.form.values;
1755
2585
  const close = catalog?.groups?.[uid];
1756
- if (!canInsert(zone, uid, c.modifiedData, components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
1757
- c.addComponentToDynamicZone(zone.name, components[uid], components, Boolean(c.formErrors?.[zone.name]));
1758
- if (close) c.addComponentToDynamicZone(zone.name, components[close], components, Boolean(c.formErrors?.[zone.name]));
2586
+ if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
2587
+ const at = Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0;
2588
+ c.form.addFieldRow(zone.name, { ...componentDefaults(c.components[uid], c.components), __component: uid }, at);
2589
+ if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
1759
2590
  return true;
1760
2591
  };
1761
- if (!catalog?.editor?.enabled) return null;
1762
- const docKey = `${c.slug}:${c.isCreatingEntry ? "new" : c.initialData?.id}:${c.initialData?.locale || ""}`;
1763
- return /* @__PURE__ */ jsx4(
1764
- Gallery,
1765
- {
1766
- zones,
1767
- components,
1768
- add,
1769
- Modal,
1770
- Toggle: ToggleField,
1771
- get,
1772
- editor: catalog.editor,
1773
- docKey,
1774
- contentType: c.slug,
1775
- userId: auth.getUserInfo?.()?.id
1776
- }
1777
- );
2592
+ if (!zones.length || !catalog?.editor?.enabled) return null;
2593
+ const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
2594
+ return { title: t.gallery, content: /* @__PURE__ */ jsxs5(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
2595
+ /* @__PURE__ */ jsx6(Gallery, { zones, components: c.components, add, Modal: Modal2, Toggle: ToggleField, get, editor: catalog.editor, docKey, contentType: c.model, userId: user?.id }),
2596
+ /* @__PURE__ */ jsx6(PagePreview, { editor: catalog.editor, groups: catalog.groups || null, Toggle: ToggleField })
2597
+ ] }) };
1778
2598
  }
1779
- var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs3(Flex3, { gap: 3, children: [
1780
- /* @__PURE__ */ jsx4(Switch, { name, "aria-label": label, selected: Boolean(value), disabled, onChange: () => onChange(!value) }),
1781
- /* @__PURE__ */ jsx4(Typography3, { children: label })
2599
+ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs5(Flex5, { gap: 3, children: [
2600
+ /* @__PURE__ */ jsx6(Switch, { name, "aria-label": label, checked: Boolean(value), disabled, onCheckedChange: onChange }),
2601
+ /* @__PURE__ */ jsx6(Typography5, { children: label })
2602
+ ] });
2603
+ var TextField = ({ name, label, value, onChange, disabled, placeholder }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2604
+ /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2605
+ /* @__PURE__ */ jsx6(TextInput, { name, value, disabled, placeholder, onChange: (e) => onChange(e.target.value) })
2606
+ ] });
2607
+ var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2608
+ /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2609
+ /* @__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)) })
1782
2610
  ] });
1783
- 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)) });
1784
2611
  function MediaPicker({ onClose, onSelect }) {
1785
- const { components } = useLibrary();
1786
- const Dialog = components?.["media-library"];
1787
- if (!Dialog) {
1788
- onClose();
1789
- return null;
1790
- }
1791
- return /* @__PURE__ */ jsx4(Dialog, { onClose, allowedTypes: ["images"], onSelectAssets: (assets) => onSelect(assets?.[0]) });
2612
+ const components = useStrapiApp3("Blockscene", (state) => state.components);
2613
+ const Dialog3 = components?.["media-library"];
2614
+ React6.useEffect(() => {
2615
+ if (!Dialog3) onClose();
2616
+ }, [Dialog3, onClose]);
2617
+ if (!Dialog3) return null;
2618
+ return /* @__PURE__ */ jsx6(Dialog3, { onClose, allowedTypes: ["images"], multiple: false, onSelectAssets: (assets) => onSelect(assets?.[0]) });
1792
2619
  }
1793
2620
  function usePermissions() {
1794
- const { allowedActions, isLoading } = useRBAC(permissions);
2621
+ const { allowedActions, isLoading } = useRBAC(Object.values(permissions).flat());
1795
2622
  return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
1796
2623
  }
1797
- var SettingsPage = () => /* @__PURE__ */ jsx4(Settings, { useClient: useFetchClient, usePermissions, MediaPicker, ToggleField, SelectField, previewSupported: false });
1798
- var strapi4_default = {
2624
+ var SettingsPage = () => /* @__PURE__ */ jsx6(Settings, { useClient: useFetchClient2, usePermissions, MediaPicker, ToggleField, SelectField, TextField, previewSupported: true });
2625
+ var strapi5_default = {
1799
2626
  register(app) {
1800
- register(app, SettingsPage, "/settings/blockscene");
2627
+ register(app, SettingsPage, "blockscene");
1801
2628
  app.registerPlugin({ id: "blockscene", name: "Blockscene" });
1802
2629
  },
1803
2630
  registerTrads,
1804
2631
  bootstrap(app) {
1805
- app.injectContentManagerComponent("editView", "right-links", { name: "blockscene", Component: Picker });
2632
+ app.getPlugin("content-manager").apis.addEditViewSidePanel([Panel]);
1806
2633
  }
1807
2634
  };
1808
2635
  export {
1809
- strapi4_default as default
2636
+ strapi5_default as default
1810
2637
  };