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

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 (4) hide show
  1. package/README.md +17 -5
  2. package/dist/admin.cjs +1307 -372
  3. package/dist/admin.mjs +1346 -374
  4. package/package.json +3 -5
package/dist/admin.mjs CHANGED
@@ -131,30 +131,61 @@ var require_groups = __commonJS({
131
131
  });
132
132
 
133
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";
134
+ import * as React7 from "react";
135
+ import {
136
+ Modal as Dialog,
137
+ Switch,
138
+ Flex as Flex5,
139
+ Typography as Typography5,
140
+ Field,
141
+ SingleSelect as SingleSelect2,
142
+ SingleSelectOption as SingleSelectOption2,
143
+ TextInput
144
+ } from "@strapi/design-system";
145
+ import {
146
+ useFetchClient as useFetchClient2,
147
+ useRBAC,
148
+ useStrapiApp as useStrapiApp3,
149
+ useAuth,
150
+ useForm as useForm3
151
+ } from "@strapi/strapi/admin";
152
+ import {
153
+ unstable_useContentManagerContext as useContext2,
154
+ useDocumentRBAC as useDocumentRBAC2
155
+ } from "@strapi/content-manager/strapi-admin";
138
156
 
139
157
  // admin/Gallery.tsx
140
- import * as React from "react";
158
+ import * as React2 from "react";
141
159
  import { unstable_batchedUpdates } from "react-dom";
142
- import { Box, Button, Flex, Searchbar, SingleSelect, SingleSelectOption, Typography } from "@strapi/design-system";
160
+ import {
161
+ Box,
162
+ Button,
163
+ Flex,
164
+ Searchbar,
165
+ SingleSelect,
166
+ SingleSelectOption,
167
+ Typography
168
+ } from "@strapi/design-system";
143
169
  import styled from "styled-components";
144
170
 
145
171
  // admin/model.mjs
146
172
  import { generateNKeysBetween } from "fractional-indexing";
147
173
  function componentDefaults(schema, components, stack = []) {
148
- if (!schema || stack.includes(schema)) throw new Error("Invalid recursive component defaults");
174
+ if (!schema || stack.includes(schema))
175
+ throw new Error("Invalid recursive component defaults");
149
176
  const row = {};
150
177
  for (const [name, attr] of Object.entries(schema.attributes || {})) {
151
178
  if (Object.hasOwn(attr, "default")) {
152
179
  const value = structuredClone(attr.default);
153
180
  if (attr.type === "password") row[name] = "";
154
- else if (attr.type === "relation") row[name] = { connect: [], disconnect: [] };
181
+ else if (attr.type === "relation")
182
+ row[name] = { connect: [], disconnect: [] };
155
183
  else if (value !== null || attr.type === "boolean") row[name] = value;
156
184
  } else if (attr.type === "component" && attr.required) {
157
- const create = () => componentDefaults(components[attr.component], components, [...stack, schema]);
185
+ const create = () => componentDefaults(components[attr.component], components, [
186
+ ...stack,
187
+ schema
188
+ ]);
158
189
  if (attr.repeatable) {
159
190
  const keys = generateNKeysBetween(null, null, attr.min || 0);
160
191
  row[name] = keys.map((key) => ({ ...create(), __temp_key__: key }));
@@ -165,27 +196,58 @@ function componentDefaults(schema, components, stack = []) {
165
196
  }
166
197
  function editableZones(schema, values, canEdit, disabled = false) {
167
198
  if (disabled) return [];
168
- return Object.entries(schema?.attributes || {}).filter(([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)).map(([name, attr]) => {
169
- const count = Array.isArray(values?.[name]) ? values[name].length : 0;
170
- return { name, components: attr.components || [], max: attr.max, count, full: count >= (attr.max ?? Infinity) };
199
+ return Object.entries(schema?.attributes || {}).filter(
200
+ ([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)
201
+ ).map(([name, attr]) => {
202
+ const rows = Array.isArray(values?.[name]) ? values[name] : [];
203
+ return {
204
+ name,
205
+ components: attr.components || [],
206
+ max: attr.max,
207
+ count: rows.length,
208
+ uids: rows.map(
209
+ (row) => row && typeof row.__component === "string" ? row.__component : ""
210
+ ),
211
+ full: rows.length >= (attr.max ?? Infinity)
212
+ };
171
213
  });
172
214
  }
173
215
  function canInsert(zone, uid, values, components, count = 1) {
174
- return Boolean(zone?.components.includes(uid) && components[uid] && (Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0) + count <= (zone.max ?? Infinity));
216
+ return Boolean(
217
+ zone?.components.includes(uid) && components[uid] && (Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0) + count <= (zone.max ?? Infinity)
218
+ );
175
219
  }
176
220
  function candidatesFor(uid, meta = {}, config = {}) {
177
221
  const version = config.previewVersion ? `?v=${encodeURIComponent(config.previewVersion)}` : "";
178
222
  const base = (config.previewBaseUrl || "/block-previews").replace(/\/$/, "");
179
- return [meta.manualImage, meta.image, `${base}/${encodeURIComponent(uid)}.webp${version}`].filter(Boolean);
223
+ return [
224
+ meta.manualImage,
225
+ meta.image,
226
+ `${base}/${encodeURIComponent(uid)}.webp${version}`
227
+ ].filter(Boolean);
180
228
  }
181
- var SYSTEM_FIELDS = ["id", "documentId", "createdAt", "updatedAt", "publishedAt", "createdBy", "updatedBy", "locale", "localizations"];
229
+ var SYSTEM_FIELDS = [
230
+ "id",
231
+ "documentId",
232
+ "createdAt",
233
+ "updatedAt",
234
+ "publishedAt",
235
+ "createdBy",
236
+ "updatedBy",
237
+ "locale",
238
+ "localizations"
239
+ ];
182
240
  var fieldType = (attr) => attr?.type === "customField" ? String(attr.customField || "").includes("CKEditor") ? "richtext" : "customField" : attr?.type || "unknown";
183
241
  var fieldsOf = (schema) => Object.entries(schema?.attributes || {}).filter(([name]) => !SYSTEM_FIELDS.includes(name)).map(([name, attr]) => ({ name, type: fieldType(attr) }));
184
242
  var categoryOf = (uid, schema, meta = {}) => meta.category || schema?.category || uid.split(".")[0];
185
243
  var byCategory = (a, b) => (a === "admin" ? 1 : 0) - (b === "admin" ? 1 : 0) || a.localeCompare(b);
244
+ var pickable = (zone, config) => {
245
+ const closers = Object.values(config.groups || {});
246
+ return zone.components.filter((uid) => !closers.includes(uid));
247
+ };
186
248
  function entriesFor(zone, components, config, query, category = "all") {
187
249
  const needle = query.trim().toLocaleLowerCase();
188
- return zone.components.flatMap((uid) => {
250
+ return pickable(zone, config).flatMap((uid) => {
189
251
  const schema = components[uid];
190
252
  if (!schema) return [];
191
253
  const meta = config.components?.[uid] || {};
@@ -200,11 +262,13 @@ function entriesFor(zone, components, config, query, category = "all") {
200
262
  };
201
263
  if (category !== "all" && entry.category !== category) return [];
202
264
  return `${entry.label} ${entry.description} ${entry.category} ${uid} ${meta.keywords || ""}`.toLocaleLowerCase().includes(needle) ? [entry] : [];
203
- }).sort((a, b) => byCategory(a.category, b.category) || a.label.localeCompare(b.label));
265
+ }).sort(
266
+ (a, b) => byCategory(a.category, b.category) || a.label.localeCompare(b.label)
267
+ );
204
268
  }
205
269
  function categoriesFor(zone, components, config) {
206
270
  const counts = /* @__PURE__ */ new Map();
207
- for (const uid of zone.components) {
271
+ for (const uid of pickable(zone, config)) {
208
272
  const schema = components[uid];
209
273
  if (!schema) continue;
210
274
  const c = categoryOf(uid, schema, config.components?.[uid] || {});
@@ -214,8 +278,12 @@ function categoriesFor(zone, components, config) {
214
278
  }
215
279
  function groupEntries(entries) {
216
280
  const map = /* @__PURE__ */ new Map();
217
- for (const entry of entries) map.set(entry.category, [...map.get(entry.category) || [], entry]);
218
- return [...map.entries()].map(([category, items]) => ({ category, entries: items }));
281
+ for (const entry of entries)
282
+ map.set(entry.category, [...map.get(entry.category) || [], entry]);
283
+ return [...map.entries()].map(([category, items]) => ({
284
+ category,
285
+ entries: items
286
+ }));
219
287
  }
220
288
  function memoryKey({ base = "", userId, contentType, zone }) {
221
289
  if (!userId || !contentType || !zone) return null;
@@ -231,7 +299,8 @@ function readMemory(storage2, key) {
231
299
  }
232
300
  function writeMemory(storage2, key, value) {
233
301
  try {
234
- if (key && (value === "open" || value === "closed")) storage2?.setItem(key, value);
302
+ if (key && (value === "open" || value === "closed"))
303
+ storage2?.setItem(key, value);
235
304
  } catch {
236
305
  }
237
306
  }
@@ -317,6 +386,10 @@ function Wireframe({ template, palette, className, noPreview }) {
317
386
  );
318
387
  }
319
388
 
389
+ // admin/RowPreviews.tsx
390
+ import * as React from "react";
391
+ import { createPortal } from "react-dom";
392
+
320
393
  // admin/messages.ts
321
394
  import { useIntl } from "react-intl";
322
395
 
@@ -431,7 +504,8 @@ var en_default = {
431
504
  columns: "Columns",
432
505
  noPreview: "no preview",
433
506
  "templates.none": "No preview",
434
- noChanges: "No unsaved changes"
507
+ noChanges: "No unsaved changes",
508
+ rowPreview: "Open the {label} preview"
435
509
  };
436
510
 
437
511
  // admin/translations/pt-BR.json
@@ -545,7 +619,8 @@ var pt_BR_default = {
545
619
  columns: "Colunas",
546
620
  noPreview: "sem preview",
547
621
  "templates.none": "Sem preview",
548
- noChanges: "Sem altera\xE7\xF5es pendentes"
622
+ noChanges: "Sem altera\xE7\xF5es pendentes",
623
+ rowPreview: "Abrir a pr\xE9via de {label}"
549
624
  };
550
625
 
551
626
  // admin/translations/pt.json
@@ -659,7 +734,8 @@ var pt_default = {
659
734
  columns: "Colunas",
660
735
  noPreview: "sem pr\xE9-visualiza\xE7\xE3o",
661
736
  "templates.none": "Sem pr\xE9-visualiza\xE7\xE3o",
662
- noChanges: "Sem altera\xE7\xF5es pendentes"
737
+ noChanges: "Sem altera\xE7\xF5es pendentes",
738
+ rowPreview: "Abrir a pr\xE9-visualiza\xE7\xE3o de {label}"
663
739
  };
664
740
 
665
741
  // admin/translations/fr.json
@@ -773,7 +849,8 @@ var fr_default = {
773
849
  columns: "Colonnes",
774
850
  noPreview: "pas d\u2019aper\xE7u",
775
851
  "templates.none": "Pas d\u2019aper\xE7u",
776
- noChanges: "Aucune modification en attente"
852
+ noChanges: "Aucune modification en attente",
853
+ rowPreview: "Ouvrir l\u2019aper\xE7u de {label}"
777
854
  };
778
855
 
779
856
  // admin/translations/es.json
@@ -887,7 +964,8 @@ var es_default = {
887
964
  columns: "Columnas",
888
965
  noPreview: "sin vista previa",
889
966
  "templates.none": "Sin vista previa",
890
- noChanges: "Sin cambios pendientes"
967
+ noChanges: "Sin cambios pendientes",
968
+ rowPreview: "Abrir la vista previa de {label}"
891
969
  };
892
970
 
893
971
  // admin/translations/de.json
@@ -1001,7 +1079,8 @@ var de_default = {
1001
1079
  columns: "Spalten",
1002
1080
  noPreview: "keine Vorschau",
1003
1081
  "templates.none": "Keine Vorschau",
1004
- noChanges: "Keine ungespeicherten \xC4nderungen"
1082
+ noChanges: "Keine ungespeicherten \xC4nderungen",
1083
+ rowPreview: "Vorschau von {label} \xF6ffnen"
1005
1084
  };
1006
1085
 
1007
1086
  // admin/translations/it.json
@@ -1115,7 +1194,8 @@ var it_default = {
1115
1194
  columns: "Colonne",
1116
1195
  noPreview: "nessuna anteprima",
1117
1196
  "templates.none": "Nessuna anteprima",
1118
- noChanges: "Nessuna modifica in sospeso"
1197
+ noChanges: "Nessuna modifica in sospeso",
1198
+ rowPreview: "Apri l\u2019anteprima di {label}"
1119
1199
  };
1120
1200
 
1121
1201
  // admin/translations/nl.json
@@ -1229,7 +1309,8 @@ var nl_default = {
1229
1309
  columns: "Kolommen",
1230
1310
  noPreview: "geen voorbeeld",
1231
1311
  "templates.none": "Geen voorbeeld",
1232
- noChanges: "Geen niet-opgeslagen wijzigingen"
1312
+ noChanges: "Geen niet-opgeslagen wijzigingen",
1313
+ rowPreview: "Voorbeeld van {label} openen"
1233
1314
  };
1234
1315
 
1235
1316
  // admin/messages.ts
@@ -1249,26 +1330,224 @@ function useMessages() {
1249
1330
  return out;
1250
1331
  }
1251
1332
 
1333
+ // admin/RowPreviews.tsx
1334
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
1335
+ var ATTR = "data-blockscene-row-thumb";
1336
+ function useAnchors(zones, onOpen) {
1337
+ const [anchors, setAnchors] = React.useState([]);
1338
+ const open = React.useRef(onOpen);
1339
+ open.current = onOpen;
1340
+ const latest = React.useRef(zones);
1341
+ latest.current = zones;
1342
+ const sync = React.useRef(() => {
1343
+ });
1344
+ sync.current = () => {
1345
+ const next = [];
1346
+ for (const zone of latest.current) {
1347
+ const list = zone.uids?.length ? findZoneList(zone.label) : null;
1348
+ if (!list) continue;
1349
+ toggles(list).forEach((button, index) => {
1350
+ const uid = zone.uids[index];
1351
+ if (!uid) return;
1352
+ let el = button.querySelector(
1353
+ `:scope > [${ATTR}]`
1354
+ );
1355
+ if (!el) {
1356
+ el = document.createElement("span");
1357
+ el.setAttribute(ATTR, "");
1358
+ el.style.cssText = "display:inline-flex;align-items:center";
1359
+ const activate = (event) => {
1360
+ if (!event.currentTarget.getAttribute("role"))
1361
+ return;
1362
+ event.preventDefault();
1363
+ event.stopPropagation();
1364
+ open.current(
1365
+ String(event.currentTarget.dataset.uid || "")
1366
+ );
1367
+ };
1368
+ el.addEventListener("click", activate, true);
1369
+ el.addEventListener(
1370
+ "keydown",
1371
+ (event) => {
1372
+ if (event.key === "Enter" || event.key === " ") activate(event);
1373
+ },
1374
+ true
1375
+ );
1376
+ button.insertBefore(el, button.firstChild);
1377
+ }
1378
+ el.dataset.uid = uid;
1379
+ next.push({ el, uid, zone: zone.name, index });
1380
+ });
1381
+ }
1382
+ for (const el of document.querySelectorAll(`[${ATTR}]`))
1383
+ if (!next.some((a) => a.el === el)) el.remove();
1384
+ setAnchors(
1385
+ (prev) => prev.length === next.length && prev.every((a, i) => a.el === next[i].el && a.uid === next[i].uid) ? prev : next
1386
+ );
1387
+ };
1388
+ React.useEffect(() => {
1389
+ sync.current();
1390
+ });
1391
+ React.useEffect(() => {
1392
+ let frame = 0;
1393
+ const observer = new MutationObserver(() => {
1394
+ if (frame) return;
1395
+ frame = requestAnimationFrame(() => {
1396
+ frame = 0;
1397
+ sync.current();
1398
+ });
1399
+ });
1400
+ observer.observe(document.body, { childList: true, subtree: true });
1401
+ return () => {
1402
+ observer.disconnect();
1403
+ cancelAnimationFrame(frame);
1404
+ for (const el of document.querySelectorAll(`[${ATTR}]`)) el.remove();
1405
+ };
1406
+ }, []);
1407
+ return anchors;
1408
+ }
1409
+ function RowPreviews({ zones, components, catalog, Modal: Modal3 }) {
1410
+ const t = useMessages();
1411
+ const [resolved, setResolved] = React.useState({});
1412
+ const [open, setOpen] = React.useState(null);
1413
+ const anchors = useAnchors(zones, (uid) => setOpen(uid));
1414
+ const candidates = (uid) => candidatesFor(uid, catalog?.components?.[uid] || {}, catalog || {});
1415
+ const label = (uid) => catalog?.components?.[uid]?.label || components?.[uid]?.info?.displayName || uid;
1416
+ const srcOf = (uid) => candidates(uid)[resolved[uid] ?? -1] || "";
1417
+ React.useEffect(() => {
1418
+ for (const { el, uid } of anchors) {
1419
+ if (srcOf(uid)) {
1420
+ el.setAttribute("role", "button");
1421
+ el.setAttribute("tabindex", "0");
1422
+ el.setAttribute("aria-label", t.f("rowPreview", { label: label(uid) }));
1423
+ el.style.cursor = "zoom-in";
1424
+ } else
1425
+ for (const name of ["role", "tabindex", "aria-label"])
1426
+ el.removeAttribute(name);
1427
+ }
1428
+ });
1429
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1430
+ anchors.map(
1431
+ (a) => createPortal(
1432
+ /* @__PURE__ */ jsx2(
1433
+ "span",
1434
+ {
1435
+ style: {
1436
+ display: "block",
1437
+ width: "6.4rem",
1438
+ marginRight: "8px",
1439
+ flex: "0 0 auto"
1440
+ },
1441
+ children: /* @__PURE__ */ jsx2(
1442
+ Thumb,
1443
+ {
1444
+ candidates: candidates(a.uid),
1445
+ blank: true,
1446
+ onResolved: (index) => setResolved(
1447
+ (prev) => prev[a.uid] === index ? prev : { ...prev, [a.uid]: index }
1448
+ )
1449
+ }
1450
+ )
1451
+ }
1452
+ ),
1453
+ a.el,
1454
+ `${a.zone}:${a.index}`
1455
+ )
1456
+ ),
1457
+ open && srcOf(open) && /* @__PURE__ */ jsx2(
1458
+ Modal3,
1459
+ {
1460
+ open: true,
1461
+ onOpenChange: (value) => {
1462
+ if (!value) setOpen(null);
1463
+ },
1464
+ trigger: null,
1465
+ title: label(open),
1466
+ children: /* @__PURE__ */ jsx2(
1467
+ "img",
1468
+ {
1469
+ src: srcOf(open),
1470
+ alt: "",
1471
+ style: {
1472
+ display: "block",
1473
+ margin: "0 auto",
1474
+ maxWidth: "100%",
1475
+ maxHeight: "75vh",
1476
+ objectFit: "contain"
1477
+ }
1478
+ }
1479
+ )
1480
+ }
1481
+ )
1482
+ ] });
1483
+ }
1484
+
1252
1485
  // admin/Gallery.tsx
1253
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1486
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1254
1487
  var Columns = styled.div`
1255
- column-count: ${({ $columns }) => $columns}; column-gap: 12px;
1256
- > * { break-inside: avoid; -webkit-column-break-inside: avoid; margin-bottom: 12px; }
1488
+ column-count: ${({ $columns }) => $columns};
1489
+ column-gap: 12px;
1490
+ > * {
1491
+ break-inside: avoid;
1492
+ -webkit-column-break-inside: avoid;
1493
+ margin-bottom: 12px;
1494
+ }
1257
1495
  `;
1258
1496
  var Tile = styled.button`
1259
- display: flex; flex-direction: column; gap: 8px; text-align: left; width: 100%;
1260
- padding: 8px; border: 1px solid ${({ theme }) => theme.colors.neutral200};
1261
- border-radius: 4px; background: ${({ theme }) => theme.colors.neutral0}; cursor: pointer;
1262
- &:hover { border-color: ${({ theme }) => theme.colors.primary600}; }
1263
- &:focus-visible { outline: 2px solid ${({ theme }) => theme.colors.primary600}; outline-offset: 2px; }
1497
+ display: flex;
1498
+ flex-direction: column;
1499
+ gap: 8px;
1500
+ text-align: left;
1501
+ width: 100%;
1502
+ padding: 8px;
1503
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
1504
+ border-radius: 4px;
1505
+ background: ${({ theme }) => theme.colors.neutral0};
1506
+ cursor: pointer;
1507
+ &:hover {
1508
+ border-color: ${({ theme }) => theme.colors.primary600};
1509
+ }
1510
+ &:focus-visible {
1511
+ outline: 2px solid ${({ theme }) => theme.colors.primary600};
1512
+ outline-offset: 2px;
1513
+ }
1514
+ `;
1515
+ var Clamp = styled(Typography)`
1516
+ display: -webkit-box;
1517
+ -webkit-line-clamp: 2;
1518
+ -webkit-box-orient: vertical;
1519
+ overflow: hidden;
1264
1520
  `;
1265
- var Clamp = styled(Typography)` display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; `;
1266
1521
  var Slider = styled.input`
1267
- appearance: none; -webkit-appearance: none; width: 8rem; height: 0.4rem; border-radius: 0.4rem; cursor: pointer;
1522
+ appearance: none;
1523
+ -webkit-appearance: none;
1524
+ width: 8rem;
1525
+ height: 0.4rem;
1526
+ border-radius: 0.4rem;
1527
+ cursor: pointer;
1268
1528
  background: ${({ theme, $percent }) => `linear-gradient(to right, ${theme.colors.primary600} ${$percent}%, ${theme.colors.neutral200} ${$percent}%)`};
1269
- &::-webkit-slider-thumb { appearance: none; -webkit-appearance: none; width: 1.6rem; height: 1.6rem; border-radius: 50%; background: ${({ theme }) => theme.colors.neutral0}; border: 0.2rem solid ${({ theme }) => theme.colors.primary600}; cursor: pointer; }
1270
- &::-moz-range-thumb { width: 1.6rem; height: 1.6rem; border-radius: 50%; background: ${({ theme }) => theme.colors.neutral0}; border: 0.2rem solid ${({ theme }) => theme.colors.primary600}; cursor: pointer; }
1271
- &:focus-visible { outline: 0.2rem solid ${({ theme }) => theme.colors.primary600}; outline-offset: 0.2rem; }
1529
+ &::-webkit-slider-thumb {
1530
+ appearance: none;
1531
+ -webkit-appearance: none;
1532
+ width: 1.6rem;
1533
+ height: 1.6rem;
1534
+ border-radius: 50%;
1535
+ background: ${({ theme }) => theme.colors.neutral0};
1536
+ border: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1537
+ cursor: pointer;
1538
+ }
1539
+ &::-moz-range-thumb {
1540
+ width: 1.6rem;
1541
+ height: 1.6rem;
1542
+ border-radius: 50%;
1543
+ background: ${({ theme }) => theme.colors.neutral0};
1544
+ border: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1545
+ cursor: pointer;
1546
+ }
1547
+ &:focus-visible {
1548
+ outline: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1549
+ outline-offset: 0.2rem;
1550
+ }
1272
1551
  `;
1273
1552
  var COLUMNS = { key: "blockscene:columns", min: 1, max: 5, initial: 3 };
1274
1553
  var SHOW_FIELDS_KEY = "blockscene:show-fields";
@@ -1294,16 +1573,31 @@ var remember = (key, value) => {
1294
1573
  }
1295
1574
  };
1296
1575
  var Frame = styled.div`
1297
- width: 100%; overflow: hidden; border-radius: 4px; line-height: 0;
1576
+ width: 100%;
1577
+ overflow: hidden;
1578
+ border-radius: 4px;
1579
+ line-height: 0;
1298
1580
  aspect-ratio: ${({ $fixed }) => $fixed ? "16 / 9" : "auto"};
1299
1581
  background: ${({ theme }) => theme.colors.neutral100};
1300
- img { width: 100%; height: auto; display: block; }
1582
+ img {
1583
+ width: 100%;
1584
+ height: auto;
1585
+ display: block;
1586
+ }
1301
1587
  `;
1302
- function Thumb({ candidates, template, palette, onResolved, noPreview, eager }) {
1588
+ function Thumb({
1589
+ candidates,
1590
+ template,
1591
+ palette,
1592
+ onResolved,
1593
+ noPreview,
1594
+ eager,
1595
+ blank
1596
+ }) {
1303
1597
  const key = candidates.join("|");
1304
- const [state, setState] = React.useState({ key, index: 0 });
1598
+ const [state, setState] = React2.useState({ key, index: 0 });
1305
1599
  const index = state.key === key ? state.index : 0;
1306
- const resolved = React.useRef("");
1600
+ const resolved = React2.useRef("");
1307
1601
  const report = (value) => {
1308
1602
  const tag = `${key}:${value}`;
1309
1603
  if (resolved.current !== tag) {
@@ -1311,11 +1605,19 @@ function Thumb({ candidates, template, palette, onResolved, noPreview, eager })
1311
1605
  onResolved?.(value);
1312
1606
  }
1313
1607
  };
1314
- React.useEffect(() => {
1608
+ React2.useEffect(() => {
1315
1609
  if (index >= candidates.length) report(-1);
1316
1610
  });
1317
- if (index >= candidates.length) return /* @__PURE__ */ jsx2(Frame, { $fixed: true, "data-thumb": "wireframe", children: /* @__PURE__ */ jsx2(Wireframe, { template, palette, noPreview }) });
1318
- return /* @__PURE__ */ jsx2(Frame, { "data-thumb": String(index), children: /* @__PURE__ */ jsx2(
1611
+ if (index >= candidates.length)
1612
+ return blank ? null : /* @__PURE__ */ jsx3(Frame, { $fixed: true, "data-thumb": "wireframe", children: /* @__PURE__ */ jsx3(
1613
+ Wireframe,
1614
+ {
1615
+ template,
1616
+ palette,
1617
+ noPreview
1618
+ }
1619
+ ) });
1620
+ return /* @__PURE__ */ jsx3(Frame, { "data-thumb": String(index), children: /* @__PURE__ */ jsx3(
1319
1621
  "img",
1320
1622
  {
1321
1623
  src: candidates[index],
@@ -1329,28 +1631,90 @@ function Thumb({ candidates, template, palette, onResolved, noPreview, eager })
1329
1631
  }
1330
1632
  function Card({ entry, palette, showFields, onSelect }) {
1331
1633
  const t = useMessages();
1332
- return /* @__PURE__ */ jsxs(Tile, { type: "button", onClick: onSelect, title: entry.uid, "data-testid": `blockscene-${entry.uid}`, children: [
1333
- /* @__PURE__ */ jsx2(Thumb, { candidates: entry.candidates, template: entry.template, palette, noPreview: t.noPreview }),
1334
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", ellipsis: true, children: entry.label }),
1335
- entry.description && /* @__PURE__ */ jsx2(Clamp, { variant: "pi", textColor: "neutral600", children: entry.description }),
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(", ") })
1337
- ] });
1634
+ return /* @__PURE__ */ jsxs2(
1635
+ Tile,
1636
+ {
1637
+ type: "button",
1638
+ onClick: onSelect,
1639
+ title: entry.uid,
1640
+ "data-testid": `blockscene-${entry.uid}`,
1641
+ children: [
1642
+ /* @__PURE__ */ jsx3(
1643
+ Thumb,
1644
+ {
1645
+ candidates: entry.candidates,
1646
+ template: entry.template,
1647
+ palette,
1648
+ noPreview: t.noPreview
1649
+ }
1650
+ ),
1651
+ /* @__PURE__ */ jsx3(
1652
+ Typography,
1653
+ {
1654
+ variant: "pi",
1655
+ fontWeight: "bold",
1656
+ textColor: "neutral800",
1657
+ ellipsis: true,
1658
+ children: entry.label
1659
+ }
1660
+ ),
1661
+ entry.description && /* @__PURE__ */ jsx3(Clamp, { variant: "pi", textColor: "neutral600", children: entry.description }),
1662
+ showFields && entry.fields.length > 0 && /* @__PURE__ */ jsx3(
1663
+ Typography,
1664
+ {
1665
+ variant: "pi",
1666
+ textColor: "neutral500",
1667
+ style: { fontStyle: "italic" },
1668
+ "data-testid": "block-fields",
1669
+ children: entry.fields.map((f) => `${f.name} (${f.type})`).join(", ")
1670
+ }
1671
+ )
1672
+ ]
1673
+ }
1674
+ );
1338
1675
  }
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 } });
1676
+ function PickerModal({
1677
+ zone,
1678
+ components,
1679
+ open,
1680
+ onOpenChange,
1681
+ onSelect,
1682
+ Modal: Modal3,
1683
+ Toggle,
1684
+ get
1685
+ }) {
1686
+ return /* @__PURE__ */ jsx3(
1687
+ ZoneGallery,
1688
+ {
1689
+ zone,
1690
+ components,
1691
+ Modal: Modal3,
1692
+ Toggle,
1693
+ get,
1694
+ controlled: { open, onOpenChange, onSelect }
1695
+ }
1696
+ );
1341
1697
  }
1342
- function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, controlled }) {
1698
+ function ZoneGallery({
1699
+ zone,
1700
+ components,
1701
+ add,
1702
+ Modal: Modal3,
1703
+ Toggle,
1704
+ get,
1705
+ controlled
1706
+ }) {
1343
1707
  const t = useMessages();
1344
- const [ownOpen, setOwnOpen] = React.useState(false);
1708
+ const [ownOpen, setOwnOpen] = React2.useState(false);
1345
1709
  const open = controlled ? controlled.open : ownOpen;
1346
1710
  const setOpen = controlled ? controlled.onOpenChange : setOwnOpen;
1347
- const [query, setQuery] = React.useState("");
1348
- const [category, setCategory] = React.useState("all");
1349
- const [showFields, setShowFields] = React.useState(readShowFields);
1350
- const [columns, setColumns] = React.useState(readColumns);
1351
- const [config, setConfig] = React.useState(null);
1352
- const [error, setError] = React.useState("");
1353
- React.useEffect(() => {
1711
+ const [query, setQuery] = React2.useState("");
1712
+ const [category, setCategory] = React2.useState("all");
1713
+ const [showFields, setShowFields] = React2.useState(readShowFields);
1714
+ const [columns, setColumns] = React2.useState(readColumns);
1715
+ const [config, setConfig] = React2.useState(null);
1716
+ const [error, setError] = React2.useState("");
1717
+ React2.useEffect(() => {
1354
1718
  if (controlled) return;
1355
1719
  const onClick = (event) => {
1356
1720
  const button = event.target?.closest?.("button");
@@ -1362,7 +1726,7 @@ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, contro
1362
1726
  document.addEventListener("click", onClick, true);
1363
1727
  return () => document.removeEventListener("click", onClick, true);
1364
1728
  }, [controlled, zone.name, zone.label]);
1365
- React.useEffect(() => {
1729
+ React2.useEffect(() => {
1366
1730
  if (!open) return;
1367
1731
  let active = true;
1368
1732
  setError("");
@@ -1378,7 +1742,10 @@ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, contro
1378
1742
  }, [open, get, t.failed]);
1379
1743
  const entries = config ? entriesFor(zone, components, config, query, category) : [];
1380
1744
  const categories = config ? categoriesFor(zone, components, config) : [];
1381
- const total = categories.reduce((sum, [, count]) => sum + count, 0);
1745
+ const total = categories.reduce(
1746
+ (sum, [, count]) => sum + count,
1747
+ 0
1748
+ );
1382
1749
  const filtering = query.trim() !== "" || category !== "all";
1383
1750
  const reset = () => {
1384
1751
  setQuery("");
@@ -1402,21 +1769,41 @@ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, contro
1402
1769
  setShowFields(value);
1403
1770
  remember(SHOW_FIELDS_KEY, String(value));
1404
1771
  };
1405
- return /* @__PURE__ */ jsx2(
1772
+ return /* @__PURE__ */ jsx3(
1406
1773
  Modal3,
1407
1774
  {
1408
1775
  open,
1409
1776
  onOpenChange: setOpen,
1410
1777
  title: t.gallery,
1411
- trigger: controlled ? null : /* @__PURE__ */ jsxs(Button, { variant: "secondary", fullWidth: true, onClick: () => setOpen(true), "data-testid": `open-gallery-${zone.name}`, children: [
1412
- t.add,
1413
- ": ",
1414
- zone.name
1415
- ] }),
1416
- children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1417
- /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1418
- /* @__PURE__ */ jsx2(Typography, { variant: "omega", fontWeight: "bold", textColor: "neutral800", style: { whiteSpace: "nowrap" }, "data-testid": `block-count-${zone.name}`, "aria-live": "polite", children: filtering ? t.f("countFiltered", { shown: entries.length, total }) : t.f("countAll", { count: total }) }),
1419
- /* @__PURE__ */ jsx2(Box, { style: { flex: 1, minWidth: "12rem" }, children: /* @__PURE__ */ jsx2(
1778
+ trigger: controlled ? null : /* @__PURE__ */ jsxs2(
1779
+ Button,
1780
+ {
1781
+ variant: "secondary",
1782
+ fullWidth: true,
1783
+ onClick: () => setOpen(true),
1784
+ "data-testid": `open-gallery-${zone.name}`,
1785
+ children: [
1786
+ t.add,
1787
+ ": ",
1788
+ zone.name
1789
+ ]
1790
+ }
1791
+ ),
1792
+ children: /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1793
+ /* @__PURE__ */ jsxs2(Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1794
+ /* @__PURE__ */ jsx3(
1795
+ Typography,
1796
+ {
1797
+ variant: "omega",
1798
+ fontWeight: "bold",
1799
+ textColor: "neutral800",
1800
+ style: { whiteSpace: "nowrap" },
1801
+ "data-testid": `block-count-${zone.name}`,
1802
+ "aria-live": "polite",
1803
+ children: filtering ? t.f("countFiltered", { shown: entries.length, total }) : t.f("countAll", { count: total })
1804
+ }
1805
+ ),
1806
+ /* @__PURE__ */ jsx3(Box, { style: { flex: 1, minWidth: "12rem" }, children: /* @__PURE__ */ jsx3(
1420
1807
  Searchbar,
1421
1808
  {
1422
1809
  name: `block-search-${zone.name}`,
@@ -1428,14 +1815,54 @@ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, contro
1428
1815
  children: t.search
1429
1816
  }
1430
1817
  ) }),
1431
- categories.length > 1 && /* @__PURE__ */ jsx2(Box, { style: { width: "14rem" }, "data-testid": `block-category-${zone.name}`, children: /* @__PURE__ */ jsxs(SingleSelect, { "aria-label": t.filterCategory, size: "S", value: category, onChange: (value) => setCategory(String(value)), children: [
1432
- /* @__PURE__ */ jsx2(SingleSelectOption, { value: "all", children: t.allCategories }),
1433
- categories.map(([name, count]) => /* @__PURE__ */ jsx2(SingleSelectOption, { value: name, children: `${name} (${count})` }, name))
1434
- ] }) }),
1435
- Toggle && /* @__PURE__ */ jsx2(Toggle, { name: `block-fields-${zone.name}`, label: t.showFields, hint: t.showFieldsHint, value: showFields, onChange: changeShowFields }),
1436
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
1437
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral600", id: `block-columns-${zone.name}`, children: t.columns }),
1438
- /* @__PURE__ */ jsx2(
1818
+ categories.length > 1 && /* @__PURE__ */ jsx3(
1819
+ Box,
1820
+ {
1821
+ style: { width: "14rem" },
1822
+ "data-testid": `block-category-${zone.name}`,
1823
+ children: /* @__PURE__ */ jsxs2(
1824
+ SingleSelect,
1825
+ {
1826
+ "aria-label": t.filterCategory,
1827
+ size: "S",
1828
+ value: category,
1829
+ onChange: (value) => setCategory(String(value)),
1830
+ children: [
1831
+ /* @__PURE__ */ jsx3(SingleSelectOption, { value: "all", children: t.allCategories }),
1832
+ categories.map(([name, count]) => /* @__PURE__ */ jsx3(
1833
+ SingleSelectOption,
1834
+ {
1835
+ value: name,
1836
+ children: `${name} (${count})`
1837
+ },
1838
+ name
1839
+ ))
1840
+ ]
1841
+ }
1842
+ )
1843
+ }
1844
+ ),
1845
+ Toggle && /* @__PURE__ */ jsx3(
1846
+ Toggle,
1847
+ {
1848
+ name: `block-fields-${zone.name}`,
1849
+ label: t.showFields,
1850
+ hint: t.showFieldsHint,
1851
+ value: showFields,
1852
+ onChange: changeShowFields
1853
+ }
1854
+ ),
1855
+ /* @__PURE__ */ jsxs2(Flex, { gap: 2, alignItems: "center", children: [
1856
+ /* @__PURE__ */ jsx3(
1857
+ Typography,
1858
+ {
1859
+ variant: "pi",
1860
+ textColor: "neutral600",
1861
+ id: `block-columns-${zone.name}`,
1862
+ children: t.columns
1863
+ }
1864
+ ),
1865
+ /* @__PURE__ */ jsx3(
1439
1866
  Slider,
1440
1867
  {
1441
1868
  type: "range",
@@ -1448,16 +1875,52 @@ function ZoneGallery({ zone, components, add, Modal: Modal3, Toggle, get, contro
1448
1875
  onChange: (e) => changeColumns(Number(e.target.value))
1449
1876
  }
1450
1877
  ),
1451
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", fontWeight: "bold", children: columns })
1878
+ /* @__PURE__ */ jsx3(Typography, { variant: "pi", fontWeight: "bold", children: columns })
1452
1879
  ] })
1453
1880
  ] }),
1454
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral600", children: t.example }),
1455
- error ? /* @__PURE__ */ jsx2(Typography, { role: "alert", textColor: "danger600", children: error }) : !config ? /* @__PURE__ */ jsx2(Typography, { children: t.loading }) : entries.length ? /* @__PURE__ */ jsx2(Flex, { direction: "column", alignItems: "stretch", gap: 4, style: { overflowY: "auto", maxHeight: "60vh", padding: "4px" }, children: groupEntries(entries).map(({ category: name, entries: items }) => /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1456
- /* @__PURE__ */ jsx2(Typography, { variant: "sigma", textColor: "neutral600", "data-testid": `block-group-${name}`, children: `${name} (${items.length})` }),
1457
- /* @__PURE__ */ jsx2(Columns, { $columns: columns, children: items.map((entry) => /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(Card, { entry, palette: config.palette, showFields, onSelect: () => select(entry.uid) }) }, entry.uid)) })
1458
- ] }, name)) }) : /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", children: [
1459
- /* @__PURE__ */ jsx2(Typography, { children: t.empty }),
1460
- filtering && /* @__PURE__ */ jsx2(Button, { variant: "tertiary", size: "S", onClick: reset, children: t.clear })
1881
+ /* @__PURE__ */ jsx3(Typography, { variant: "pi", textColor: "neutral600", children: t.example }),
1882
+ error ? /* @__PURE__ */ jsx3(Typography, { role: "alert", textColor: "danger600", children: error }) : !config ? /* @__PURE__ */ jsx3(Typography, { children: t.loading }) : entries.length ? /* @__PURE__ */ jsx3(
1883
+ Flex,
1884
+ {
1885
+ direction: "column",
1886
+ alignItems: "stretch",
1887
+ gap: 4,
1888
+ style: { overflowY: "auto", maxHeight: "60vh", padding: "4px" },
1889
+ children: groupEntries(entries).map(
1890
+ ({ category: name, entries: items }) => /* @__PURE__ */ jsxs2(
1891
+ Flex,
1892
+ {
1893
+ direction: "column",
1894
+ alignItems: "stretch",
1895
+ gap: 2,
1896
+ children: [
1897
+ /* @__PURE__ */ jsx3(
1898
+ Typography,
1899
+ {
1900
+ variant: "sigma",
1901
+ textColor: "neutral600",
1902
+ "data-testid": `block-group-${name}`,
1903
+ children: `${name} (${items.length})`
1904
+ }
1905
+ ),
1906
+ /* @__PURE__ */ jsx3(Columns, { $columns: columns, children: items.map((entry) => /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
1907
+ Card,
1908
+ {
1909
+ entry,
1910
+ palette: config.palette,
1911
+ showFields,
1912
+ onSelect: () => select(entry.uid)
1913
+ }
1914
+ ) }, entry.uid)) })
1915
+ ]
1916
+ },
1917
+ name
1918
+ )
1919
+ )
1920
+ }
1921
+ ) : /* @__PURE__ */ jsxs2(Flex, { gap: 3, alignItems: "center", children: [
1922
+ /* @__PURE__ */ jsx3(Typography, { children: t.empty }),
1923
+ filtering && /* @__PURE__ */ jsx3(Button, { variant: "tertiary", size: "S", onClick: reset, children: t.clear })
1461
1924
  ] })
1462
1925
  ] })
1463
1926
  }
@@ -1471,13 +1934,19 @@ var storage = () => {
1471
1934
  }
1472
1935
  };
1473
1936
  var memoryBase = () => `${window.location.origin}${window.location.pathname.split("/content-manager")[0]}`;
1474
- function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1475
- const applied = React.useRef("");
1476
- const user = React.useRef(userId);
1937
+ function useInitialAccordions({
1938
+ zones,
1939
+ editor,
1940
+ docKey,
1941
+ contentType,
1942
+ userId
1943
+ }) {
1944
+ const applied = React2.useRef("");
1945
+ const user = React2.useRef(userId);
1477
1946
  user.current = userId;
1478
- const latest = React.useRef(zones);
1947
+ const latest = React2.useRef(zones);
1479
1948
  latest.current = zones;
1480
- React.useEffect(() => {
1949
+ React2.useEffect(() => {
1481
1950
  if (!editor?.enabled || !docKey || applied.current === docKey) return;
1482
1951
  if (applied.current.startsWith(`${contentType}:new`) && docKey.startsWith(`${contentType}:`)) {
1483
1952
  applied.current = docKey;
@@ -1486,7 +1955,10 @@ function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1486
1955
  applied.current = docKey;
1487
1956
  let tries = 0;
1488
1957
  const cancel = (event) => {
1489
- if (event.target?.closest?.('button[aria-expanded], [data-testid^="block-accordion-controls-"]')) stop();
1958
+ if (event.target?.closest?.(
1959
+ 'button[aria-expanded], [data-testid^="block-accordion-controls-"]'
1960
+ ))
1961
+ stop();
1490
1962
  };
1491
1963
  const stop = () => {
1492
1964
  clearInterval(timer);
@@ -1504,8 +1976,17 @@ function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1504
1976
  for (const zone of pending) {
1505
1977
  const list = findZoneList(zone.label);
1506
1978
  if (!list) continue;
1507
- const key = memoryKey({ base: memoryBase(), userId: user.current, contentType, zone: zone.name });
1508
- setAll(list, initialState(editor, readMemory(storage(), key)) === "open", unstable_batchedUpdates);
1979
+ const key = memoryKey({
1980
+ base: memoryBase(),
1981
+ userId: user.current,
1982
+ contentType,
1983
+ zone: zone.name
1984
+ });
1985
+ setAll(
1986
+ list,
1987
+ initialState(editor, readMemory(storage(), key)) === "open",
1988
+ unstable_batchedUpdates
1989
+ );
1509
1990
  }
1510
1991
  }, 200);
1511
1992
  return stop;
@@ -1517,50 +1998,117 @@ function ZoneControls({ zone, editor, contentType, userId }) {
1517
1998
  const apply = (open) => {
1518
1999
  const list = findZoneList(zone.label);
1519
2000
  if (list) setAll(list, open, unstable_batchedUpdates);
1520
- writeMemory(storage(), memoryKey({ base: memoryBase(), userId, contentType, zone: zone.name }), open ? "open" : "closed");
2001
+ writeMemory(
2002
+ storage(),
2003
+ memoryKey({ base: memoryBase(), userId, contentType, zone: zone.name }),
2004
+ open ? "open" : "closed"
2005
+ );
1521
2006
  };
1522
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", "data-testid": `block-accordion-controls-${zone.name}`, children: [
1523
- editor.showOpenAll && /* @__PURE__ */ jsx2(Button, { type: "button", variant: "tertiary", size: "S", onClick: () => apply(true), children: t.openAll }),
1524
- editor.showCloseAll && /* @__PURE__ */ jsx2(Button, { type: "button", variant: "tertiary", size: "S", onClick: () => apply(false), children: t.closeAll })
1525
- ] });
2007
+ return /* @__PURE__ */ jsxs2(
2008
+ Flex,
2009
+ {
2010
+ gap: 2,
2011
+ wrap: "wrap",
2012
+ "data-testid": `block-accordion-controls-${zone.name}`,
2013
+ children: [
2014
+ editor.showOpenAll && /* @__PURE__ */ jsx3(
2015
+ Button,
2016
+ {
2017
+ type: "button",
2018
+ variant: "tertiary",
2019
+ size: "S",
2020
+ onClick: () => apply(true),
2021
+ children: t.openAll
2022
+ }
2023
+ ),
2024
+ editor.showCloseAll && /* @__PURE__ */ jsx3(
2025
+ Button,
2026
+ {
2027
+ type: "button",
2028
+ variant: "tertiary",
2029
+ size: "S",
2030
+ onClick: () => apply(false),
2031
+ children: t.closeAll
2032
+ }
2033
+ )
2034
+ ]
2035
+ }
2036
+ );
1526
2037
  }
1527
- function Gallery({ zones, editor, docKey, contentType, userId, ...props }) {
2038
+ function Gallery({
2039
+ zones,
2040
+ editor,
2041
+ docKey,
2042
+ contentType,
2043
+ userId,
2044
+ catalog,
2045
+ ...props
2046
+ }) {
1528
2047
  useInitialAccordions({ zones, editor, docKey, contentType, userId });
1529
- return /* @__PURE__ */ jsx2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: zones.map((zone) => /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1530
- !zone.full && /* @__PURE__ */ jsx2(ZoneGallery, { zone, ...props }),
1531
- /* @__PURE__ */ jsx2(ZoneControls, { zone, editor, contentType, userId })
1532
- ] }, zone.name)) });
2048
+ return /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
2049
+ /* @__PURE__ */ jsx3(
2050
+ RowPreviews,
2051
+ {
2052
+ zones,
2053
+ components: props.components,
2054
+ catalog,
2055
+ Modal: props.Modal
2056
+ }
2057
+ ),
2058
+ zones.map((zone) => /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
2059
+ !zone.full && /* @__PURE__ */ jsx3(ZoneGallery, { zone, ...props }),
2060
+ /* @__PURE__ */ jsx3(
2061
+ ZoneControls,
2062
+ {
2063
+ zone,
2064
+ editor,
2065
+ contentType,
2066
+ userId
2067
+ }
2068
+ )
2069
+ ] }, zone.name))
2070
+ ] });
1533
2071
  }
1534
2072
 
1535
2073
  // admin/PagePreview.tsx
1536
- import * as React3 from "react";
1537
- import { createPortal } from "react-dom";
2074
+ import * as React4 from "react";
2075
+ import { createPortal as createPortal2 } from "react-dom";
1538
2076
  import styled2 from "styled-components";
1539
2077
  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";
2078
+ import {
2079
+ DescriptionComponentRenderer,
2080
+ useFetchClient,
2081
+ useForm as useForm2,
2082
+ useNotification,
2083
+ useQueryParams,
2084
+ useStrapiApp as useStrapiApp2
2085
+ } from "@strapi/strapi/admin";
2086
+ import {
2087
+ unstable_useContentManagerContext as useContext,
2088
+ unstable_useDocument as useDocument,
2089
+ useDocumentRBAC
2090
+ } from "@strapi/content-manager/strapi-admin";
1543
2091
 
1544
2092
  // admin/FieldEditorModal.tsx
1545
- import * as React2 from "react";
2093
+ import * as React3 from "react";
1546
2094
  import { Form, InputRenderer, useForm, useStrapiApp } from "@strapi/strapi/admin";
1547
2095
  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";
2096
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1549
2097
  var Actions = ({ onApply, onCancel }) => {
1550
2098
  const t = useMessages();
1551
2099
  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 })
2100
+ return /* @__PURE__ */ jsxs3(Flex2, { gap: 2, justifyContent: "flex-end", children: [
2101
+ /* @__PURE__ */ jsx4(Button2, { variant: "tertiary", onClick: onCancel, children: t.cancel }),
2102
+ /* @__PURE__ */ jsx4(Button2, { onClick: () => onApply(value ?? null), "data-testid": "field-editor-apply", children: t.apply })
1555
2103
  ] });
1556
2104
  };
1557
2105
  function FieldEditorModal({ label, attribute, value, onApply, onCancel }) {
1558
2106
  const t = useMessages();
1559
2107
  const customFields = useStrapiApp("BlocksceneFieldEditor", (state) => state.customFields);
1560
- const [Input, setInput] = React2.useState(void 0);
2108
+ const [Input, setInput] = React3.useState(void 0);
1561
2109
  const isCustom = Boolean(attribute?.customField);
1562
2110
  const isText = attribute?.type === "string" || attribute?.type === "text";
1563
- React2.useEffect(() => {
2111
+ React3.useEffect(() => {
1564
2112
  let active = true;
1565
2113
  if (!isCustom) {
1566
2114
  setInput(null);
@@ -1581,24 +2129,24 @@ function FieldEditorModal({ label, attribute, value, onApply, onCancel }) {
1581
2129
  };
1582
2130
  }, [attribute, customFields, isCustom]);
1583
2131
  const supported = isText || isCustom && Input;
1584
- return /* @__PURE__ */ jsx3(Modal.Root, { open: true, onOpenChange: (open) => {
2132
+ return /* @__PURE__ */ jsx4(Modal.Root, { open: true, onOpenChange: (open) => {
1585
2133
  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: [
2134
+ }, children: /* @__PURE__ */ jsxs3(Modal.Content, { "aria-label": `${t.editText} ${label}`, children: [
2135
+ /* @__PURE__ */ jsx4(Modal.Header, { children: /* @__PURE__ */ jsxs3(Modal.Title, { children: [
1588
2136
  t.editText,
1589
2137
  " \xB7 ",
1590
2138
  label
1591
2139
  ] }) }),
1592
- /* @__PURE__ */ jsxs2(Form, { initialValues: { value: value ?? (isText ? "" : null) }, method: "PUT", onSubmit: () => {
2140
+ /* @__PURE__ */ jsxs3(Form, { initialValues: { value: value ?? (isText ? "" : null) }, method: "PUT", onSubmit: () => {
1593
2141
  }, 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 })
2142
+ /* @__PURE__ */ jsx4(Modal.Body, { children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "field-editor-modal", children: [
2143
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.editHelp }),
2144
+ isCustom && Input === void 0 && /* @__PURE__ */ jsx4(Typography2, { children: t.loading }),
2145
+ isCustom && Input && /* @__PURE__ */ jsx4(Input, { name: "value", attribute, label, type: attribute.type }),
2146
+ isText && /* @__PURE__ */ jsx4(InputRenderer, { type: attribute.type === "text" ? "text" : "string", name: "value", label }),
2147
+ (isCustom && Input === null || !isCustom && !isText) && /* @__PURE__ */ jsx4(Typography2, { role: "alert", textColor: "danger600", children: t.editUnsupported })
1600
2148
  ] }) }),
1601
- /* @__PURE__ */ jsx3(Modal.Footer, { children: supported ? /* @__PURE__ */ jsx3(Actions, { onApply, onCancel }) : /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", onClick: onCancel, children: t.close }) })
2149
+ /* @__PURE__ */ jsx4(Modal.Footer, { children: supported ? /* @__PURE__ */ jsx4(Actions, { onApply, onCancel }) : /* @__PURE__ */ jsx4(Button2, { variant: "tertiary", onClick: onCancel, children: t.close }) })
1602
2150
  ] })
1603
2151
  ] }) });
1604
2152
  }
@@ -1709,6 +2257,7 @@ var mediaAttribute = (uid, field, components) => {
1709
2257
  const attr = resolveField(uid, field, components);
1710
2258
  return attr?.type === "media" ? attr : null;
1711
2259
  };
2260
+ var getIn = (row, path) => String(path).split(".").reduce((value, key) => value == null ? void 0 : value[key], row);
1712
2261
  function isPreviewMessage(event, origin, source, channel, type) {
1713
2262
  return event.origin === origin && event.source === source && event.data?.protocol === PROTOCOL && event.data?.channel === channel && event.data?.type === type;
1714
2263
  }
@@ -1719,7 +2268,7 @@ function insertIndex(rows, after) {
1719
2268
  }
1720
2269
 
1721
2270
  // admin/PagePreview.tsx
1722
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2271
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1723
2272
  var MODE_KEY = "blockscene:page-mode";
1724
2273
  var RATIO_KEY = "blockscene:page-split-ratio";
1725
2274
  var MIN_PANE = 360;
@@ -1740,20 +2289,32 @@ var write = (key, value) => {
1740
2289
  } catch {
1741
2290
  }
1742
2291
  };
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);
2292
+ var clampWidth = (width, vw) => Math.round(
2293
+ Math.min(Math.max(width, MIN_PANE), Math.max(MIN_PANE, vw - MIN_FORM))
2294
+ );
1745
2295
  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};
2296
+ position: fixed;
2297
+ top: 0;
2298
+ bottom: 0;
2299
+ z-index: 2;
2300
+ display: flex;
2301
+ flex-direction: column;
2302
+ background: ${({ theme }) => theme.colors.neutral0};
2303
+ border-left: 1px solid ${({ theme }) => theme.colors.neutral200};
1748
2304
  box-shadow: ${({ theme }) => theme.shadows.popupShadow};
1749
2305
  `;
1750
2306
  function useMainRect() {
1751
- const [rect, setRect] = React3.useState(() => ({ left: 0, width: window.innerWidth }));
1752
- React3.useEffect(() => {
2307
+ const [rect, setRect] = React4.useState(() => ({
2308
+ left: 0,
2309
+ width: window.innerWidth
2310
+ }));
2311
+ React4.useEffect(() => {
1753
2312
  const main = document.getElementById("main-content");
1754
2313
  const measure = () => {
1755
2314
  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 });
2315
+ setRect(
2316
+ r && r.width > 0 ? { left: Math.round(r.left), width: Math.round(r.width) } : { left: 0, width: window.innerWidth }
2317
+ );
1757
2318
  };
1758
2319
  measure();
1759
2320
  const observer = main && "ResizeObserver" in window ? new ResizeObserver(measure) : null;
@@ -1767,12 +2328,37 @@ function useMainRect() {
1767
2328
  return rect;
1768
2329
  }
1769
2330
  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; }
2331
+ position: absolute;
2332
+ top: 0;
2333
+ bottom: 0;
2334
+ left: -5px;
2335
+ width: 10px;
2336
+ cursor: col-resize;
2337
+ z-index: 3;
2338
+ touch-action: none;
2339
+ &::after {
2340
+ content: "";
2341
+ position: absolute;
2342
+ top: 0;
2343
+ bottom: 0;
2344
+ left: 4px;
2345
+ width: 2px;
2346
+ }
2347
+ &:hover::after,
2348
+ &[data-dragging="true"]::after,
2349
+ &:focus-visible::after {
2350
+ background: ${({ theme }) => theme.colors.primary600};
2351
+ }
2352
+ &:focus-visible {
2353
+ outline: none;
2354
+ }
2355
+ `;
2356
+ var Frame2 = styled2.iframe`
2357
+ flex: 1;
2358
+ width: 100%;
2359
+ border: 0;
2360
+ background: white;
1774
2361
  `;
1775
- var Frame2 = styled2.iframe` flex: 1; width: 100%; border: 0; background: white; `;
1776
2362
  var SPLIT_STYLE = `
1777
2363
  body.bp-split #main-content { padding-right: calc(var(--bp-pane, 50vw) + 1.6rem) !important; }
1778
2364
  body.bp-split [data-bp-grid] { display: flex !important; flex-direction: column-reverse; gap: 1.6rem; }
@@ -1781,20 +2367,26 @@ body.bp-split [data-bp-panels] > div { flex-direction: row; flex-wrap: wrap; ali
1781
2367
  body.bp-split [data-bp-panels] > div > * { flex: 1 1 24rem; }`;
1782
2368
  var markLayout = (anchor) => {
1783
2369
  let item = anchor;
1784
- while (item?.parentElement && getComputedStyle(item.parentElement).display !== "grid") item = item.parentElement;
2370
+ while (item?.parentElement && getComputedStyle(item.parentElement).display !== "grid")
2371
+ item = item.parentElement;
1785
2372
  if (!item?.parentElement) return;
1786
2373
  item.dataset.bpPanels = "";
1787
2374
  item.parentElement.dataset.bpGrid = "";
1788
2375
  };
1789
2376
  function usePreviewBase(override, model, documentId, locale) {
1790
2377
  const { get } = useFetchClient();
1791
- const [native, setNative] = React3.useState(null);
1792
- React3.useEffect(() => {
2378
+ const [native, setNative] = React4.useState(null);
2379
+ React4.useEffect(() => {
1793
2380
  if (override || !model) return;
1794
2381
  let active = true;
1795
- const query = new URLSearchParams({ status: "draft", ...documentId ? { documentId } : {}, ...locale ? { locale } : {} });
2382
+ const query = new URLSearchParams({
2383
+ status: "draft",
2384
+ ...documentId ? { documentId } : {},
2385
+ ...locale ? { locale } : {}
2386
+ });
1796
2387
  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`);
2388
+ if (active && typeof data?.data?.url === "string")
2389
+ setNative(`${new URL(data.data.url).origin}/block-preview/page`);
1798
2390
  }).catch(() => {
1799
2391
  if (active) setNative(null);
1800
2392
  });
@@ -1804,55 +2396,109 @@ function usePreviewBase(override, model, documentId, locale) {
1804
2396
  }, [get, model, documentId, locale, override]);
1805
2397
  return override ? { base: override, source: "custom" } : native ? { base: native, source: "native" } : { base: null, source: "none" };
1806
2398
  }
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
2399
  var ACTION_TYPES = ["update", "publish"];
1817
- var nativeButtons = () => document.querySelectorAll('#main-content button:not([data-testid="page-preview-pane"] button)');
2400
+ var nativeButtons = () => document.querySelectorAll(
2401
+ '#main-content button:not([data-testid="page-preview-pane"] button)'
2402
+ );
1818
2403
  function ToolbarActions({ model, collectionType, documentId, locale }) {
1819
- const plugins = useStrapiApp2("BlocksceneToolbar", (state) => state.plugins);
2404
+ const plugins = useStrapiApp2(
2405
+ "BlocksceneToolbar",
2406
+ (state) => state.plugins
2407
+ );
1820
2408
  const [{ query }] = useQueryParams();
1821
- const { document: doc, meta } = useDocument({ model, collectionType, documentId, params: { locale } }, { skip: !documentId });
2409
+ const { document: doc, meta } = useDocument(
2410
+ { model, collectionType, documentId, params: { locale } },
2411
+ { skip: !documentId }
2412
+ );
1822
2413
  const { toggleNotification } = useNotification();
1823
2414
  const t = useMessages();
1824
2415
  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 };
2416
+ const props = {
2417
+ activeTab: query.status || "draft",
2418
+ model,
2419
+ documentId,
2420
+ document: doc,
2421
+ meta,
2422
+ collectionType
2423
+ };
1826
2424
  const run = async (action, event) => {
1827
- const native = [...nativeButtons()].find((b) => b.textContent?.trim() === action.label);
2425
+ const native = [...nativeButtons()].find(
2426
+ (b) => b.textContent?.trim() === action.label
2427
+ );
1828
2428
  if (native) {
1829
2429
  native.click();
1830
2430
  return;
1831
2431
  }
1832
2432
  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 });
2433
+ if (action.dialog && !mute && action.dialog.type === "notification")
2434
+ toggleNotification({
2435
+ title: action.dialog.title,
2436
+ message: action.dialog.content,
2437
+ type: action.dialog.status
2438
+ });
1834
2439
  };
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) => {
2440
+ const typeOf = (action) => action.type || descriptions.find(
2441
+ (d) => typeof action.id === "string" && action.id.startsWith(`${d.name || d.displayName}-`)
2442
+ )?.type;
2443
+ return /* @__PURE__ */ jsx5(DescriptionComponentRenderer, { props, descriptions, children: (actions) => /* @__PURE__ */ jsx5(Flex3, { gap: 1, "data-testid": "page-preview-actions", children: [...actions].sort(
2444
+ (a, b) => ACTION_TYPES.indexOf(typeOf(a)) - ACTION_TYPES.indexOf(typeOf(b))
2445
+ ).map((action) => {
1837
2446
  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);
2447
+ return /* @__PURE__ */ jsx5("span", { title: reason, children: /* @__PURE__ */ jsx5(
2448
+ Button3,
2449
+ {
2450
+ size: "S",
2451
+ variant: action.variant || "default",
2452
+ disabled: action.disabled,
2453
+ loading: action.loading,
2454
+ "aria-description": reason,
2455
+ onClick: (e) => run(action, e),
2456
+ children: action.label
2457
+ }
2458
+ ) }, action.id);
1839
2459
  }) }) });
1840
2460
  }
1841
- function PagePreview({ editor, groups = null, Toggle }) {
2461
+ function PagePreview({
2462
+ editor,
2463
+ groups = null,
2464
+ Modal: Modal3,
2465
+ Toggle
2466
+ }) {
1842
2467
  const t = useMessages();
1843
2468
  const c = useContext();
1844
- const rbac = useDocumentRBAC("BlockscenePagePreview", (state) => state);
2469
+ const rbac = useDocumentRBAC(
2470
+ "BlockscenePagePreview",
2471
+ (state) => state
2472
+ );
1845
2473
  const values = useForm2("BlockscenePagePreview", (state) => state.values);
1846
- const onChange = useForm2("BlockscenePagePreview", (state) => state.onChange);
1847
- const components = useStrapiApp2("BlockscenePagePreview", (state) => state.components);
2474
+ const onChange = useForm2(
2475
+ "BlockscenePagePreview",
2476
+ (state) => state.onChange
2477
+ );
2478
+ const components = useStrapiApp2(
2479
+ "BlockscenePagePreview",
2480
+ (state) => state.components
2481
+ );
1848
2482
  const MediaLibraryDialog = components?.["media-library"];
1849
- const addFieldRow = useForm2("BlockscenePagePreview", (state) => state.addFieldRow);
2483
+ const addFieldRow = useForm2(
2484
+ "BlockscenePagePreview",
2485
+ (state) => state.addFieldRow
2486
+ );
2487
+ const moveFieldRow = useForm2(
2488
+ "BlockscenePagePreview",
2489
+ (state) => state.moveFieldRow
2490
+ );
1850
2491
  const fields = c.layout?.edit?.layout?.flat(3) || [];
1851
2492
  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]);
2493
+ const zone = React4.useMemo(
2494
+ () => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name,
2495
+ [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]
2496
+ );
1853
2497
  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(() => {
2498
+ const canEdit = Boolean(
2499
+ zone && ((c.isCreatingEntry ? rbac.canCreateFields : rbac.canUpdateFields) || []).includes(zone) && fields.find((field) => field.name === zone)?.disabled !== true && !c.form?.disabled && typeof addFieldRow === "function"
2500
+ );
2501
+ const [mode, setModeState] = React4.useState(() => {
1856
2502
  const v = read(MODE_KEY);
1857
2503
  return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
1858
2504
  });
@@ -1860,32 +2506,37 @@ function PagePreview({ editor, groups = null, Toggle }) {
1860
2506
  setModeState(next);
1861
2507
  write(MODE_KEY, next);
1862
2508
  };
1863
- const [ratio, setRatio] = React3.useState(() => {
2509
+ const [ratio, setRatio] = React4.useState(() => {
1864
2510
  const v = Number(read(RATIO_KEY));
1865
2511
  return v > 0 && v < 1 ? v : 0.5;
1866
2512
  });
1867
2513
  const main = useMainRect();
1868
2514
  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(() => {
2515
+ const [dragging, setDragging] = React4.useState(false);
2516
+ const [ready, setReady] = React4.useState(false);
2517
+ const [failed, setFailed] = React4.useState(false);
2518
+ const [attempt, retry] = React4.useReducer((n) => n + 1, 0);
2519
+ const [picking, setPicking] = React4.useState(null);
2520
+ React4.useEffect(() => {
1875
2521
  setEditing(null);
1876
2522
  setPicking(null);
1877
2523
  setInserting(null);
1878
2524
  }, [c.id, c.form?.initialValues?.locale]);
1879
- const [editing, setEditing] = React3.useState(null);
1880
- const [inserting, setInserting] = React3.useState(null);
2525
+ const [editing, setEditing] = React4.useState(null);
2526
+ const [inserting, setInserting] = React4.useState(null);
1881
2527
  const { get } = useFetchClient();
1882
2528
  const { toggleNotification } = useNotification();
1883
2529
  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);
2530
+ const iframe = React4.useRef(null);
2531
+ const loaded = React4.useRef(false);
2532
+ const anchor = React4.useRef(null);
2533
+ const channel = React4.useMemo(() => crypto.randomUUID(), []);
2534
+ const { base, source } = usePreviewBase(
2535
+ editor?.previewUrl || "",
2536
+ c.model,
2537
+ c.isCreatingEntry ? void 0 : c.id,
2538
+ c.form?.initialValues?.locale
2539
+ );
1889
2540
  const url = base ? new URL(base) : null;
1890
2541
  url?.searchParams.set("channel", channel);
1891
2542
  const origin = url?.origin || "";
@@ -1894,16 +2545,50 @@ function PagePreview({ editor, groups = null, Toggle }) {
1894
2545
  const resizable = mode === "split" && !narrow;
1895
2546
  const active = mode !== "form" && Boolean(url) && Boolean(zone);
1896
2547
  const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
1897
- const latest = React3.useRef(rows);
2548
+ const latest = React4.useRef(rows);
1898
2549
  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);
2550
+ const insertRows = (at, ...items) => items.forEach((item, i) => {
2551
+ const from = latest.current.length + i;
2552
+ addFieldRow(zone, item);
2553
+ if (at + i < from) moveFieldRow(zone, from, at + i);
2554
+ });
2555
+ const live = React4.useRef({
2556
+ mode,
2557
+ canEdit,
2558
+ zoneLabel,
2559
+ components: c.components,
2560
+ setMode,
2561
+ onChange
2562
+ });
2563
+ live.current = {
2564
+ mode,
2565
+ canEdit,
2566
+ zoneLabel,
2567
+ components: c.components,
2568
+ setMode,
2569
+ onChange
2570
+ };
2571
+ const send = React4.useCallback(() => {
2572
+ iframe.current?.contentWindow?.postMessage(
2573
+ {
2574
+ protocol: PROTOCOL,
2575
+ channel,
2576
+ type: "update-page",
2577
+ blocks: projectPage(
2578
+ latest.current,
2579
+ live.current.components,
2580
+ window.location.origin
2581
+ ),
2582
+ groups,
2583
+ locale: t.locale,
2584
+ mode: live.current.mode
2585
+ },
2586
+ origin
2587
+ );
1903
2588
  }, [channel, origin]);
1904
2589
  const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
1905
2590
  const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
1906
- React3.useEffect(() => {
2591
+ React4.useEffect(() => {
1907
2592
  if (resizable) markLayout(anchor.current);
1908
2593
  document.body.classList.toggle("bp-split", resizable);
1909
2594
  document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
@@ -1915,7 +2600,9 @@ function PagePreview({ editor, groups = null, Toggle }) {
1915
2600
  if (persist) write(RATIO_KEY, String(next));
1916
2601
  };
1917
2602
  const focusField = (index, field) => {
1918
- const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
2603
+ const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(
2604
+ ":scope > li"
2605
+ )[index];
1919
2606
  if (!item) return;
1920
2607
  const header = toggles(item.parentElement)[index];
1921
2608
  if (header?.getAttribute("aria-expanded") === "false") header.click();
@@ -1926,33 +2613,47 @@ function PagePreview({ editor, groups = null, Toggle }) {
1926
2613
  if (++tries > 40) return clearInterval(timer);
1927
2614
  let scope = item;
1928
2615
  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);
2616
+ const nested = [
2617
+ ...scope.querySelectorAll(
2618
+ "button[aria-expanded][data-radix-collection-item]"
2619
+ )
2620
+ ].filter((b) => b !== header);
1930
2621
  const row = nested[Number(parts[i + 1])];
1931
2622
  if (!row) return;
1932
2623
  if (row.getAttribute("aria-expanded") === "false") row.click();
1933
2624
  scope = row.closest("li, div") || scope;
1934
2625
  }
1935
- const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
2626
+ const target = document.querySelector(
2627
+ `[name="${escaped}"], label[for="${escaped}"]`
2628
+ );
1936
2629
  if (!target) return;
1937
2630
  const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
1938
2631
  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;
2632
+ for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++)
2633
+ container = container.parentElement;
1940
2634
  const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
1941
2635
  if (!editable) return;
1942
- if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
2636
+ if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable)
2637
+ return;
1943
2638
  editable.focus();
1944
2639
  if (document.activeElement !== editable) return;
1945
2640
  clearInterval(timer);
1946
2641
  editable.scrollIntoView({ block: "center", behavior: "smooth" });
1947
2642
  }, 150);
1948
2643
  };
1949
- React3.useEffect(() => {
2644
+ React4.useEffect(() => {
1950
2645
  if (!active) return;
1951
2646
  setReady(false);
1952
2647
  setFailed(false);
1953
2648
  const timeout = setTimeout(() => setFailed(true), 15e3);
1954
2649
  const receive = (event) => {
1955
- const is = (type) => isPreviewMessage(event, origin, iframe.current?.contentWindow, channel, type);
2650
+ const is = (type) => isPreviewMessage(
2651
+ event,
2652
+ origin,
2653
+ iframe.current?.contentWindow,
2654
+ channel,
2655
+ type
2656
+ );
1956
2657
  const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
1957
2658
  if (is("ready")) {
1958
2659
  clearTimeout(timeout);
@@ -1972,12 +2673,21 @@ function PagePreview({ editor, groups = null, Toggle }) {
1972
2673
  return;
1973
2674
  }
1974
2675
  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);
2676
+ const next = (0, import_groups.moveGroup)(
2677
+ latest.current,
2678
+ String(event.data.key),
2679
+ event.data.direction === "up" ? "up" : "down",
2680
+ groups
2681
+ );
1976
2682
  if (next) onChange2(zone, next);
1977
2683
  return;
1978
2684
  }
1979
2685
  if (is("delete-group") && canEdit2 && groups) {
1980
- const next = (0, import_groups.removeGroup)(latest.current, String(event.data.key), groups);
2686
+ const next = (0, import_groups.removeGroup)(
2687
+ latest.current,
2688
+ String(event.data.key),
2689
+ groups
2690
+ );
1981
2691
  if (next) onChange2(zone, next);
1982
2692
  return;
1983
2693
  }
@@ -1987,13 +2697,20 @@ function PagePreview({ editor, groups = null, Toggle }) {
1987
2697
  const uid2 = String(event.data.uid || "");
1988
2698
  const close = groups[uid2];
1989
2699
  const allowed = zoneAttr?.components || [];
1990
- if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
2700
+ if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close))
2701
+ return;
1991
2702
  if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
1992
2703
  toggleNotification({ type: "info", message: t.zoneFull });
1993
2704
  return;
1994
2705
  }
1995
- addFieldRow(zone, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, index2);
1996
- addFieldRow(zone, { __component: close }, index2 + 1);
2706
+ insertRows(
2707
+ index2,
2708
+ {
2709
+ ...componentDefaults(components2[uid2], components2),
2710
+ __component: uid2
2711
+ },
2712
+ { __component: close }
2713
+ );
1997
2714
  return;
1998
2715
  }
1999
2716
  const key = typeof event.data?.key === "string" ? event.data.key : "";
@@ -2013,11 +2730,21 @@ function PagePreview({ editor, groups = null, Toggle }) {
2013
2730
  }, 1500);
2014
2731
  }
2015
2732
  if (mode2 === "preview") setMode2("split");
2016
- iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "highlight", key }, origin);
2733
+ iframe.current?.contentWindow?.postMessage(
2734
+ { protocol: PROTOCOL, channel, type: "highlight", key },
2735
+ origin
2736
+ );
2017
2737
  } else if (is("focus")) {
2018
2738
  const attr = validateFocus(uid, event.data.field, components2);
2019
2739
  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) });
2740
+ if (mode2 === "preview" && canEdit2 && (attr.customField || MODAL_TYPES.includes(attr.type)))
2741
+ setEditing({
2742
+ key,
2743
+ field: event.data.field,
2744
+ attr,
2745
+ label: `${components2[uid]?.info?.displayName || uid} \xB7 ${event.data.field}`,
2746
+ value: getIn(latest.current[index], event.data.field)
2747
+ });
2021
2748
  else focusField(index, event.data.field);
2022
2749
  } else if (is("media") && canEdit2) {
2023
2750
  const attr = mediaAttribute(uid, event.data.field, components2);
@@ -2026,27 +2753,67 @@ function PagePreview({ editor, groups = null, Toggle }) {
2026
2753
  const attr = mediaAttribute(uid, event.data.field, components2);
2027
2754
  if (attr && !attr.required) onChange2(path(key, event.data.field), null);
2028
2755
  } else if (is("edit") && canEdit2) {
2029
- const value = validateEdit(uid, event.data.field, event.data.value, components2);
2756
+ const value = validateEdit(
2757
+ uid,
2758
+ event.data.field,
2759
+ event.data.value,
2760
+ components2
2761
+ );
2030
2762
  if (value !== null) onChange2(path(key, event.data.field), value);
2031
2763
  }
2032
2764
  };
2033
2765
  window.addEventListener("message", receive);
2034
- if (loaded.current) iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "ping" }, origin);
2766
+ if (loaded.current)
2767
+ iframe.current?.contentWindow?.postMessage(
2768
+ { protocol: PROTOCOL, channel, type: "ping" },
2769
+ origin
2770
+ );
2035
2771
  return () => {
2036
2772
  clearTimeout(timeout);
2037
2773
  window.removeEventListener("message", receive);
2038
2774
  };
2039
2775
  }, [active, attempt, channel, origin, groups]);
2040
- React3.useEffect(() => {
2776
+ React4.useEffect(() => {
2041
2777
  if (!ready || !active) return;
2042
2778
  const timer = setTimeout(send, 120);
2043
2779
  return () => clearTimeout(timer);
2044
2780
  }, [values, c.components, ready, active, send, mode]);
2045
2781
  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)) });
2782
+ const modes = [
2783
+ ["form", t.modeForm],
2784
+ ["split", t.modeSplit],
2785
+ ["preview", t.modePreview]
2786
+ ];
2787
+ const switcher = /* @__PURE__ */ jsx5(
2788
+ Flex3,
2789
+ {
2790
+ gap: 1,
2791
+ wrap: "wrap",
2792
+ "data-testid": "page-preview-modes",
2793
+ role: "group",
2794
+ "aria-label": t.modeGroup,
2795
+ children: modes.map(([value, label]) => /* @__PURE__ */ jsx5(
2796
+ Button3,
2797
+ {
2798
+ type: "button",
2799
+ size: "S",
2800
+ variant: mode === value ? "default" : "tertiary",
2801
+ "aria-pressed": mode === value,
2802
+ onClick: () => setMode(value),
2803
+ disabled: !url && value !== "form",
2804
+ children: label
2805
+ },
2806
+ value
2807
+ ))
2808
+ }
2809
+ );
2048
2810
  const onKey = (event) => {
2049
- const map = { ArrowLeft: paneWidth + KEY_STEP, ArrowRight: paneWidth - KEY_STEP, Home: MIN_PANE, End: vw - MIN_FORM };
2811
+ const map = {
2812
+ ArrowLeft: paneWidth + KEY_STEP,
2813
+ ArrowRight: paneWidth - KEY_STEP,
2814
+ Home: MIN_PANE,
2815
+ End: vw - MIN_FORM
2816
+ };
2050
2817
  if (event.key in map) {
2051
2818
  event.preventDefault();
2052
2819
  applyWidth(map[event.key], true);
@@ -2055,30 +2822,71 @@ function PagePreview({ editor, groups = null, Toggle }) {
2055
2822
  const problems = groups ? (0, import_groups.validateGroups)(rows, groups) : [];
2056
2823
  const insertClose = (error) => {
2057
2824
  const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
2058
- addFieldRow(zone, { __component: error.expected }, end + 1);
2825
+ insertRows(end + 1, { __component: error.expected });
2059
2826
  };
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: [
2827
+ const diagnostics = problems.length > 0 && /* @__PURE__ */ jsx5(
2828
+ Box2,
2829
+ {
2830
+ role: "alert",
2831
+ "data-testid": "page-preview-diagnostics",
2832
+ padding: 2,
2833
+ background: "danger100",
2834
+ hasRadius: true,
2835
+ children: /* @__PURE__ */ jsxs4("details", { children: [
2836
+ /* @__PURE__ */ jsx5("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ jsx5(Typography3, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
2837
+ /* @__PURE__ */ jsx5("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ jsxs4(
2838
+ "li",
2839
+ {
2840
+ style: { marginBottom: 6 },
2841
+ children: [
2842
+ /* @__PURE__ */ jsx5(Typography3, { variant: "pi", textColor: "danger700", children: t.f(
2843
+ error.code === "closeBeforeOpen" ? "diagCloseBeforeOpen" : error.code === "mismatch" ? "diagMismatch" : "diagUnclosed",
2844
+ {
2845
+ n: error.index + 1,
2846
+ uid: error.uid,
2847
+ open: error.open || "",
2848
+ expected: error.expected || ""
2849
+ }
2850
+ ) }),
2851
+ error.code === "unclosed" && canEdit && /* @__PURE__ */ jsx5(Box2, { paddingTop: 1, children: /* @__PURE__ */ jsx5(
2852
+ Button3,
2853
+ {
2854
+ size: "S",
2855
+ variant: "danger-light",
2856
+ "data-testid": `diag-insert-close-${error.index}`,
2857
+ onClick: () => insertClose(error),
2858
+ children: t.f("diagInsertClose", {
2859
+ n: error.index + 1,
2860
+ expected: error.expected
2861
+ })
2862
+ }
2863
+ ) }),
2864
+ error.code === "closeBeforeOpen" && /* @__PURE__ */ jsx5(Typography3, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
2865
+ ]
2866
+ },
2867
+ `${error.code}-${error.index}`
2868
+ )) })
2869
+ ] })
2870
+ }
2871
+ );
2872
+ return /* @__PURE__ */ jsxs4(Flex3, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
2069
2873
  switcher,
2070
2874
  diagnostics,
2071
- /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2072
- active && createPortal(
2073
- /* @__PURE__ */ jsxs3(
2875
+ /* @__PURE__ */ jsx5(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2876
+ active && createPortal2(
2877
+ /* @__PURE__ */ jsxs4(
2074
2878
  Pane,
2075
2879
  {
2076
2880
  "data-testid": "page-preview-pane",
2077
2881
  "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 },
2882
+ style: {
2883
+ left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth,
2884
+ width: mode === "preview" || narrow ? main.width : paneWidth,
2885
+ userSelect: dragging ? "none" : void 0
2886
+ },
2079
2887
  children: [
2080
- /* @__PURE__ */ jsx4("style", { children: SPLIT_STYLE }),
2081
- resizable && /* @__PURE__ */ jsx4(
2888
+ /* @__PURE__ */ jsx5("style", { children: SPLIT_STYLE }),
2889
+ resizable && /* @__PURE__ */ jsx5(
2082
2890
  Handle,
2083
2891
  {
2084
2892
  role: "separator",
@@ -2096,7 +2904,8 @@ function PagePreview({ editor, groups = null, Toggle }) {
2096
2904
  setDragging(true);
2097
2905
  },
2098
2906
  onPointerMove: (e) => {
2099
- if (dragging) applyWidth(main.left + main.width - e.clientX, false);
2907
+ if (dragging)
2908
+ applyWidth(main.left + main.width - e.clientX, false);
2100
2909
  },
2101
2910
  onPointerUp: (e) => {
2102
2911
  if (!dragging) return;
@@ -2108,15 +2917,44 @@ function PagePreview({ editor, groups = null, Toggle }) {
2108
2917
  onKeyDown: onKey
2109
2918
  }
2110
2919
  ),
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(
2920
+ /* @__PURE__ */ jsxs4(
2921
+ Flex3,
2922
+ {
2923
+ padding: 2,
2924
+ gap: 2,
2925
+ justifyContent: "space-between",
2926
+ alignItems: "center",
2927
+ background: "neutral100",
2928
+ wrap: "wrap",
2929
+ style: { position: "sticky", top: 0, zIndex: 2 },
2930
+ children: [
2931
+ switcher,
2932
+ /* @__PURE__ */ jsxs4(
2933
+ Flex3,
2934
+ {
2935
+ gap: 2,
2936
+ alignItems: "center",
2937
+ wrap: "wrap",
2938
+ style: { marginLeft: "auto" },
2939
+ children: [
2940
+ /* @__PURE__ */ jsx5(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
2941
+ failed && /* @__PURE__ */ jsx5(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
2942
+ /* @__PURE__ */ jsx5(
2943
+ ToolbarActions,
2944
+ {
2945
+ model: c.model,
2946
+ collectionType: c.collectionType,
2947
+ documentId: c.isCreatingEntry ? void 0 : c.id,
2948
+ locale: c.form?.initialValues?.locale
2949
+ }
2950
+ )
2951
+ ]
2952
+ }
2953
+ )
2954
+ ]
2955
+ }
2956
+ ),
2957
+ /* @__PURE__ */ jsx5(
2120
2958
  Frame2,
2121
2959
  {
2122
2960
  ref: iframe,
@@ -2136,12 +2974,16 @@ function PagePreview({ editor, groups = null, Toggle }) {
2136
2974
  ),
2137
2975
  document.body
2138
2976
  ),
2139
- inserting && zoneAttr && /* @__PURE__ */ jsx4(
2977
+ inserting && zoneAttr && /* @__PURE__ */ jsx5(
2140
2978
  PickerModal,
2141
2979
  {
2142
- zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
2980
+ zone: {
2981
+ name: zone,
2982
+ components: zoneAttr.components || [],
2983
+ max: zoneAttr.max
2984
+ },
2143
2985
  components: c.components,
2144
- Modal: PickerDialog,
2986
+ Modal: Modal3,
2145
2987
  Toggle,
2146
2988
  get,
2147
2989
  open: true,
@@ -2153,15 +2995,24 @@ function PagePreview({ editor, groups = null, Toggle }) {
2153
2995
  setInserting(null);
2154
2996
  const close = groups?.[uid];
2155
2997
  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 });
2998
+ toggleNotification({
2999
+ type: "warning",
3000
+ message: close ? t.zoneFull : t.insertMoved
3001
+ });
2157
3002
  return;
2158
3003
  }
2159
- addFieldRow(zone, { ...componentDefaults(c.components[uid], c.components), __component: uid }, index);
2160
- if (close) addFieldRow(zone, { __component: close }, index + 1);
3004
+ insertRows(
3005
+ index,
3006
+ {
3007
+ ...componentDefaults(c.components[uid], c.components),
3008
+ __component: uid
3009
+ },
3010
+ ...close ? [{ __component: close }] : []
3011
+ );
2161
3012
  }
2162
3013
  }
2163
3014
  ),
2164
- editing && /* @__PURE__ */ jsx4(
3015
+ editing && /* @__PURE__ */ jsx5(
2165
3016
  FieldEditorModal,
2166
3017
  {
2167
3018
  label: editing.label,
@@ -2171,11 +3022,12 @@ function PagePreview({ editor, groups = null, Toggle }) {
2171
3022
  onApply: (value) => {
2172
3023
  const index = indexOf(editing.key);
2173
3024
  setEditing(null);
2174
- if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
3025
+ if (index >= 0)
3026
+ onChange(`${zone}.${index}.${editing.field}`, value);
2175
3027
  }
2176
3028
  }
2177
3029
  ),
2178
- picking && MediaLibraryDialog && /* @__PURE__ */ jsx4(
3030
+ picking && MediaLibraryDialog && /* @__PURE__ */ jsx5(
2179
3031
  MediaLibraryDialog,
2180
3032
  {
2181
3033
  allowedTypes: picking.attr.allowedTypes || ["images"],
@@ -2186,19 +3038,32 @@ function PagePreview({ editor, groups = null, Toggle }) {
2186
3038
  setPicking(null);
2187
3039
  if (index < 0 || !assets?.length) return;
2188
3040
  const current = getIn(latest.current[index], picking.field);
2189
- onChange(`${zone}.${index}.${picking.field}`, picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]);
3041
+ onChange(
3042
+ `${zone}.${index}.${picking.field}`,
3043
+ picking.attr.multiple ? [...Array.isArray(current) ? current : [], ...assets] : assets[0]
3044
+ );
2190
3045
  }
2191
3046
  }
2192
3047
  ),
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 : "" })
3048
+ /* @__PURE__ */ jsx5(
3049
+ Box2,
3050
+ {
3051
+ hidden: true,
3052
+ "data-testid": "page-preview-state",
3053
+ "data-mode": mode,
3054
+ "data-ready": ready,
3055
+ "data-failed": failed,
3056
+ "data-preview-source": source
3057
+ }
3058
+ )
2194
3059
  ] });
2195
3060
  }
2196
3061
 
2197
3062
  // admin/Settings.tsx
2198
- import * as React4 from "react";
3063
+ import * as React5 from "react";
2199
3064
  import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
2200
3065
  import styled3 from "styled-components";
2201
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
3066
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2202
3067
  var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
2203
3068
  var Grid = styled3.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
2204
3069
  var Card2 = styled3.div`
@@ -2211,8 +3076,8 @@ var Bar = styled3.div`
2211
3076
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
2212
3077
  `;
2213
3078
  function useMainRect2(anchor) {
2214
- const [rect, setRect] = React4.useState({ left: 0, width: 0 });
2215
- React4.useEffect(() => {
3079
+ const [rect, setRect] = React5.useState({ left: 0, width: 0 });
3080
+ React5.useEffect(() => {
2216
3081
  const read2 = () => {
2217
3082
  let host = document.getElementById("main-content");
2218
3083
  for (let el = anchor.current?.parentElement; !host && el; el = el.parentElement) if (["auto", "scroll"].includes(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) host = el;
@@ -2229,11 +3094,11 @@ var Swatches = styled3.div` display: grid; grid-template-columns: repeat(auto-fi
2229
3094
  var Label = styled3.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
2230
3095
  function ColorField({ name, label, value, onChange, disabled }) {
2231
3096
  const valid = COLOR2.test(value);
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(
3097
+ return /* @__PURE__ */ jsxs5(Label, { children: [
3098
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
3099
+ /* @__PURE__ */ jsxs5(Flex4, { gap: 2, children: [
3100
+ /* @__PURE__ */ jsx6("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" } }),
3101
+ /* @__PURE__ */ jsx6(
2237
3102
  "input",
2238
3103
  {
2239
3104
  name,
@@ -2253,18 +3118,18 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2253
3118
  const t = useMessages();
2254
3119
  const { get, put } = useClient();
2255
3120
  const { canRead, canUpdate, isLoading } = usePermissions2();
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(() => {
3121
+ const [data, setData] = React5.useState(null);
3122
+ const [catalog, setCatalog] = React5.useState(null);
3123
+ const [settings, setSettings] = React5.useState(null);
3124
+ const [media, setMedia] = React5.useState({});
3125
+ const [sources, setSources] = React5.useState({});
3126
+ const [dirty, setDirty] = React5.useState(false);
3127
+ const [saving, setSaving] = React5.useState(false);
3128
+ const [status, setStatus] = React5.useState("");
3129
+ const [failed, setFailed] = React5.useState(false);
3130
+ const [filter, setFilter] = React5.useState("");
3131
+ const [picking, setPicking] = React5.useState(null);
3132
+ React5.useEffect(() => {
2268
3133
  if (!canRead || isLoading) return;
2269
3134
  let active = true;
2270
3135
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -2312,35 +3177,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2312
3177
  return index === 0 && manual ? t.manual : t.automatic;
2313
3178
  };
2314
3179
  const ready = !isLoading && canRead && !failed && settings && catalog;
2315
- const header = React4.useRef(null);
2316
- const [floating, setFloating] = React4.useState(false);
3180
+ const header = React5.useRef(null);
3181
+ const [floating, setFloating] = React5.useState(false);
2317
3182
  const main = useMainRect2(header);
2318
- React4.useEffect(() => {
3183
+ React5.useEffect(() => {
2319
3184
  const el = header.current;
2320
3185
  if (!el || typeof IntersectionObserver === "undefined") return;
2321
3186
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
2322
3187
  observer.observe(el);
2323
3188
  return () => observer.disconnect();
2324
3189
  }, [ready]);
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 })
3190
+ const controls = (suffix) => ready && /* @__PURE__ */ jsxs5(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
3191
+ /* @__PURE__ */ jsx6(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 }),
3192
+ /* @__PURE__ */ jsx6(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
2328
3193
  ] });
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 }),
3194
+ return /* @__PURE__ */ jsx6(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
3195
+ /* @__PURE__ */ jsx6("div", { ref: header, children: /* @__PURE__ */ jsxs5(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
3196
+ /* @__PURE__ */ jsx6(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
2332
3197
  controls("")
2333
3198
  ] }) }),
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 }),
3199
+ floating && main.width > 0 && /* @__PURE__ */ jsx6(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs5(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
3200
+ /* @__PURE__ */ jsx6(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
2336
3201
  controls("-floating")
2337
3202
  ] }) }),
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(
3203
+ /* @__PURE__ */ jsx6(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
3204
+ isLoading ? /* @__PURE__ */ jsx6(Typography4, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx6(Typography4, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx6(Typography4, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx6(Typography4, { children: t.loading }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
3205
+ data.disabled && /* @__PURE__ */ jsx6(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx6(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
3206
+ /* @__PURE__ */ jsx6(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
3207
+ /* @__PURE__ */ jsx6(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
3208
+ /* @__PURE__ */ jsx6(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx6(
2344
3209
  ColorField,
2345
3210
  {
2346
3211
  name: `palette-${key}`,
@@ -2354,33 +3219,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2354
3219
  },
2355
3220
  key
2356
3221
  )) }),
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] })
3222
+ invalid && /* @__PURE__ */ jsx6(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
3223
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
3224
+ /* @__PURE__ */ jsx6(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs5(Flex4, { direction: "column", gap: 1, children: [
3225
+ /* @__PURE__ */ jsx6(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx6(Wireframe, { template, palette: settings.palette }) }),
3226
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
2362
3227
  ] }, template)) }),
2363
- /* @__PURE__ */ jsx5(Flex4, { children: /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
3228
+ /* @__PURE__ */ jsx6(Flex4, { children: /* @__PURE__ */ jsx6(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
2364
3229
  s.palette = { ...DEFAULT_PALETTE };
2365
3230
  return s;
2366
3231
  }), children: t.resetPalette }) })
2367
3232
  ] }) }),
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) => {
3233
+ /* @__PURE__ */ jsx6(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
3234
+ /* @__PURE__ */ jsx6(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
3235
+ /* @__PURE__ */ jsx6(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2371
3236
  s.editor.enabled = v;
2372
3237
  return s;
2373
3238
  }) }),
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) => {
3239
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
3240
+ /* @__PURE__ */ jsx6(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2376
3241
  s.editor.showOpenAll = v;
2377
3242
  return s;
2378
3243
  }) }),
2379
- /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
3244
+ /* @__PURE__ */ jsx6(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2380
3245
  s.editor.showCloseAll = v;
2381
3246
  return s;
2382
3247
  }) }),
2383
- /* @__PURE__ */ jsx5(
3248
+ /* @__PURE__ */ jsx6(
2384
3249
  SelectField2,
2385
3250
  {
2386
3251
  name: "editor-initialState",
@@ -2394,9 +3259,9 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2394
3259
  })
2395
3260
  }
2396
3261
  ),
2397
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
2398
- previewSupported ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2399
- /* @__PURE__ */ jsx5(
3262
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
3263
+ previewSupported ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
3264
+ /* @__PURE__ */ jsx6(
2400
3265
  TextField2,
2401
3266
  {
2402
3267
  name: "editor-previewUrl",
@@ -2410,8 +3275,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2410
3275
  })
2411
3276
  }
2412
3277
  ),
2413
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
2414
- /* @__PURE__ */ jsx5(
3278
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
3279
+ /* @__PURE__ */ jsx6(
2415
3280
  SelectField2,
2416
3281
  {
2417
3282
  name: "editor-previewMode",
@@ -2425,26 +3290,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2425
3290
  })
2426
3291
  }
2427
3292
  ),
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) => {
3293
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
3294
+ data.blockPreviewAvailable && /* @__PURE__ */ jsxs5(Fragment2, { children: [
3295
+ /* @__PURE__ */ jsx6(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2431
3296
  s.editor.blockPreviewInForm = v;
2432
3297
  return s;
2433
3298
  }) }),
2434
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
3299
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
2435
3300
  ] })
2436
- ] }) : /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
3301
+ ] }) : /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
2437
3302
  ] }) }),
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) => {
3303
+ /* @__PURE__ */ jsx6(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
3304
+ /* @__PURE__ */ jsx6(Typography4, { variant: "beta", tag: "h2", children: t.components }),
3305
+ /* @__PURE__ */ jsx6(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
3306
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
3307
+ /* @__PURE__ */ jsx6(Grid, { "data-testid": "components-grid", children: components.map((component) => {
2443
3308
  const entry = settings.components[component.uid] || {};
2444
3309
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
2445
3310
  const candidates = candidatesFor(component.uid, meta, catalog);
2446
- return /* @__PURE__ */ jsxs4(Card2, { "data-testid": `settings-${component.uid}`, children: [
2447
- /* @__PURE__ */ jsx5(
3311
+ return /* @__PURE__ */ jsxs5(Card2, { "data-testid": `settings-${component.uid}`, children: [
3312
+ /* @__PURE__ */ jsx6(
2448
3313
  Thumb,
2449
3314
  {
2450
3315
  candidates,
@@ -2455,23 +3320,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2455
3320
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
2456
3321
  }
2457
3322
  ),
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: [
3323
+ /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
3324
+ /* @__PURE__ */ jsx6(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
3325
+ /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", textColor: "neutral600", children: [
2461
3326
  component.uid,
2462
3327
  " \xB7 ",
2463
3328
  component.category
2464
3329
  ] }),
2465
- /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
3330
+ /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
2466
3331
  t.source,
2467
3332
  ": ",
2468
3333
  sourceOf(component.uid, Boolean(meta.manualImage)),
2469
3334
  meta.manualName ? ` (${meta.manualName})` : ""
2470
3335
  ] }),
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: () => {
3336
+ meta.manualMissing && /* @__PURE__ */ jsx6(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
3337
+ /* @__PURE__ */ jsxs5(Flex4, { gap: 2, wrap: "wrap", children: [
3338
+ /* @__PURE__ */ jsx6(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
3339
+ entry.mediaId && /* @__PURE__ */ jsx6(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
2475
3340
  update((s) => {
2476
3341
  delete s.components[component.uid]?.mediaId;
2477
3342
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -2484,7 +3349,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2484
3349
  });
2485
3350
  }, children: t.useAutomatic })
2486
3351
  ] }),
2487
- /* @__PURE__ */ jsx5(
3352
+ /* @__PURE__ */ jsx6(
2488
3353
  SelectField2,
2489
3354
  {
2490
3355
  name: `template-${component.uid}`,
@@ -2502,7 +3367,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2502
3367
  ] }, component.uid);
2503
3368
  }) })
2504
3369
  ] }) }),
2505
- picking && /* @__PURE__ */ jsx5(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
3370
+ picking && /* @__PURE__ */ jsx6(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
2506
3371
  const uid = picking;
2507
3372
  setPicking(null);
2508
3373
  if (!asset?.id) return;
@@ -2515,24 +3380,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2515
3380
  ] })
2516
3381
  ] }) });
2517
3382
  }
2518
- function register(app, Component) {
3383
+ function register(app, Component, to) {
2519
3384
  app.createSettingSection(
2520
3385
  { id: "blockscene", intlLabel: { id: "blockscene.title", defaultMessage: "Blockscene" } },
2521
3386
  [{
2522
3387
  id: "blockscene-settings",
2523
- to: "/settings/blockscene",
3388
+ to,
2524
3389
  intlLabel: { id: "blockscene.settings", defaultMessage: "Gallery" },
2525
- Component: async () => Component,
3390
+ // Module shape: older Strapi 4 (e.g. 4.11) only reads `.default` from the loader result.
3391
+ // Not an `async` function: Strapi 5 warns on AsyncFunction loaders.
3392
+ Component: () => Promise.resolve({ default: Component }),
2526
3393
  permissions: permissions.read
2527
3394
  }]
2528
3395
  );
2529
3396
  }
2530
3397
 
2531
3398
  // admin/catalog.ts
2532
- import * as React5 from "react";
3399
+ import * as React6 from "react";
2533
3400
  function useCatalog(get) {
2534
- const [catalog, setCatalog] = React5.useState(null);
2535
- React5.useEffect(() => {
3401
+ const [catalog, setCatalog] = React6.useState(null);
3402
+ React6.useEffect(() => {
2536
3403
  let active = true;
2537
3404
  get("/blockscene/catalog").then(({ data }) => {
2538
3405
  if (active) setCatalog(data);
@@ -2547,13 +3414,13 @@ function useCatalog(get) {
2547
3414
  }
2548
3415
 
2549
3416
  // admin/strapi5.tsx
2550
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3417
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2551
3418
  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 })
3419
+ return /* @__PURE__ */ jsxs6(Dialog.Root, { open, onOpenChange, children: [
3420
+ trigger && /* @__PURE__ */ jsx7(Dialog.Trigger, { children: trigger }),
3421
+ /* @__PURE__ */ jsxs6(Dialog.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
3422
+ /* @__PURE__ */ jsx7(Dialog.Header, { children: /* @__PURE__ */ jsx7(Dialog.Title, { children: title }) }),
3423
+ /* @__PURE__ */ jsx7(Dialog.Body, { children })
2557
3424
  ] })
2558
3425
  ] });
2559
3426
  }
@@ -2573,52 +3440,157 @@ function Panel() {
2573
3440
  formValues ?? c.form?.values,
2574
3441
  (name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
2575
3442
  c.isLoading || c.form?.disabled || !c.form?.addFieldRow
2576
- ).map((zone) => ({ ...zone, label: fields.find((field) => field.name === zone.name)?.label || zone.name }));
3443
+ ).map((zone) => ({
3444
+ ...zone,
3445
+ label: fields.find((field) => field.name === zone.name)?.label || zone.name
3446
+ }));
2577
3447
  const add = (zone, uid) => {
2578
3448
  const values = formValues ?? c.form.values;
2579
3449
  const close = catalog?.groups?.[uid];
2580
- if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
3450
+ if (c.form.disabled || !canInsert(zone, uid, values, c.components, close ? 2 : 1) || close && !zone.components.includes(close))
3451
+ return false;
2581
3452
  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);
3453
+ c.form.addFieldRow(
3454
+ zone.name,
3455
+ {
3456
+ ...componentDefaults(c.components[uid], c.components),
3457
+ __component: uid
3458
+ },
3459
+ at
3460
+ );
2583
3461
  if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
2584
3462
  return true;
2585
3463
  };
2586
3464
  if (!zones.length || !catalog?.editor?.enabled) return null;
2587
3465
  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
- ] }) };
3466
+ return {
3467
+ title: t.gallery,
3468
+ content: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
3469
+ /* @__PURE__ */ jsx7(
3470
+ Gallery,
3471
+ {
3472
+ zones,
3473
+ components: c.components,
3474
+ add,
3475
+ Modal: Modal2,
3476
+ Toggle: ToggleField,
3477
+ get,
3478
+ editor: catalog.editor,
3479
+ catalog,
3480
+ docKey,
3481
+ contentType: c.model,
3482
+ userId: user?.id
3483
+ }
3484
+ ),
3485
+ /* @__PURE__ */ jsx7(
3486
+ PagePreview,
3487
+ {
3488
+ editor: catalog.editor,
3489
+ groups: catalog.groups || null,
3490
+ Modal: Modal2,
3491
+ Toggle: ToggleField
3492
+ }
3493
+ )
3494
+ ] })
3495
+ };
2592
3496
  }
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 })
3497
+ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs6(Flex5, { gap: 3, children: [
3498
+ /* @__PURE__ */ jsx7(
3499
+ Switch,
3500
+ {
3501
+ name,
3502
+ "aria-label": label,
3503
+ checked: Boolean(value),
3504
+ disabled,
3505
+ onCheckedChange: onChange
3506
+ }
3507
+ ),
3508
+ /* @__PURE__ */ jsx7(Typography5, { children: label })
2596
3509
  ] });
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) })
3510
+ var TextField = ({
3511
+ name,
3512
+ label,
3513
+ value,
3514
+ onChange,
3515
+ disabled,
3516
+ placeholder
3517
+ }) => /* @__PURE__ */ jsxs6(Field.Root, { name, children: [
3518
+ /* @__PURE__ */ jsx7(Field.Label, { children: label }),
3519
+ /* @__PURE__ */ jsx7(
3520
+ TextInput,
3521
+ {
3522
+ name,
3523
+ value,
3524
+ disabled,
3525
+ placeholder,
3526
+ onChange: (e) => onChange(e.target.value)
3527
+ }
3528
+ )
2600
3529
  ] });
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)) })
3530
+ var SelectField = ({
3531
+ name,
3532
+ label,
3533
+ value,
3534
+ onChange,
3535
+ disabled,
3536
+ options
3537
+ }) => /* @__PURE__ */ jsxs6(Field.Root, { name, children: [
3538
+ /* @__PURE__ */ jsx7(Field.Label, { children: label }),
3539
+ /* @__PURE__ */ jsx7(
3540
+ SingleSelect2,
3541
+ {
3542
+ value,
3543
+ disabled,
3544
+ onChange: (v) => onChange(String(v)),
3545
+ children: options.map((o) => /* @__PURE__ */ jsx7(SingleSelectOption2, { value: o.value, children: o.label }, o.value))
3546
+ }
3547
+ )
2604
3548
  ] });
2605
3549
  function MediaPicker({ onClose, onSelect }) {
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]) });
3550
+ const components = useStrapiApp3(
3551
+ "Blockscene",
3552
+ (state) => state.components
3553
+ );
3554
+ const Dialog2 = components?.["media-library"];
3555
+ React7.useEffect(() => {
3556
+ if (!Dialog2) onClose();
3557
+ }, [Dialog2, onClose]);
3558
+ if (!Dialog2) return null;
3559
+ return /* @__PURE__ */ jsx7(
3560
+ Dialog2,
3561
+ {
3562
+ onClose,
3563
+ allowedTypes: ["images"],
3564
+ multiple: false,
3565
+ onSelectAssets: (assets) => onSelect(assets?.[0])
3566
+ }
3567
+ );
2613
3568
  }
2614
3569
  function usePermissions() {
2615
- const { allowedActions, isLoading } = useRBAC(permissions);
2616
- return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
3570
+ const { allowedActions, isLoading } = useRBAC(
3571
+ Object.values(permissions).flat()
3572
+ );
3573
+ return {
3574
+ canRead: allowedActions.canRead,
3575
+ canUpdate: allowedActions.canUpdate,
3576
+ isLoading
3577
+ };
2617
3578
  }
2618
- var SettingsPage = () => /* @__PURE__ */ jsx6(Settings, { useClient: useFetchClient2, usePermissions, MediaPicker, ToggleField, SelectField, TextField, previewSupported: true });
3579
+ var SettingsPage = () => /* @__PURE__ */ jsx7(
3580
+ Settings,
3581
+ {
3582
+ useClient: useFetchClient2,
3583
+ usePermissions,
3584
+ MediaPicker,
3585
+ ToggleField,
3586
+ SelectField,
3587
+ TextField,
3588
+ previewSupported: true
3589
+ }
3590
+ );
2619
3591
  var strapi5_default = {
2620
3592
  register(app) {
2621
- register(app, SettingsPage);
3593
+ register(app, SettingsPage, "blockscene");
2622
3594
  app.registerPlugin({ id: "blockscene", name: "Blockscene" });
2623
3595
  },
2624
3596
  registerTrads,