@aduptive/strapi-blockscene 2.0.0-alpha.3 → 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 +12 -1
  2. package/dist/admin.cjs +1295 -366
  3. package/dist/admin.mjs +1334 -368
  4. package/package.json +1 -1
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,56 +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);
1850
- const moveFieldRow = useForm2("BlockscenePagePreview", (state) => state.moveFieldRow);
2483
+ const addFieldRow = useForm2(
2484
+ "BlockscenePagePreview",
2485
+ (state) => state.addFieldRow
2486
+ );
2487
+ const moveFieldRow = useForm2(
2488
+ "BlockscenePagePreview",
2489
+ (state) => state.moveFieldRow
2490
+ );
1851
2491
  const fields = c.layout?.edit?.layout?.flat(3) || [];
1852
2492
  const readable = (name) => c.isCreatingEntry || (rbac.canReadFields || []).includes(name);
1853
- const zone = React3.useMemo(() => editableZones(c.contentType, values, readable, c.isLoading)[0]?.name, [c.contentType, values, c.isLoading, rbac.canReadFields, c.isCreatingEntry]);
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
+ );
1854
2497
  const zoneLabel = fields.find((field) => field.name === zone)?.label || zone || "";
1855
- const canEdit = Boolean(zone && ((c.isCreatingEntry ? rbac.canCreateFields : rbac.canUpdateFields) || []).includes(zone) && fields.find((field) => field.name === zone)?.disabled !== true && !c.form?.disabled && typeof addFieldRow === "function");
1856
- const [mode, setModeState] = React3.useState(() => {
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(() => {
1857
2502
  const v = read(MODE_KEY);
1858
2503
  return v === "split" || v === "preview" ? v : editor?.previewMode || "form";
1859
2504
  });
@@ -1861,32 +2506,37 @@ function PagePreview({ editor, groups = null, Toggle }) {
1861
2506
  setModeState(next);
1862
2507
  write(MODE_KEY, next);
1863
2508
  };
1864
- const [ratio, setRatio] = React3.useState(() => {
2509
+ const [ratio, setRatio] = React4.useState(() => {
1865
2510
  const v = Number(read(RATIO_KEY));
1866
2511
  return v > 0 && v < 1 ? v : 0.5;
1867
2512
  });
1868
2513
  const main = useMainRect();
1869
2514
  const vw = main.width;
1870
- const [dragging, setDragging] = React3.useState(false);
1871
- const [ready, setReady] = React3.useState(false);
1872
- const [failed, setFailed] = React3.useState(false);
1873
- const [attempt, retry] = React3.useReducer((n) => n + 1, 0);
1874
- const [picking, setPicking] = React3.useState(null);
1875
- React3.useEffect(() => {
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(() => {
1876
2521
  setEditing(null);
1877
2522
  setPicking(null);
1878
2523
  setInserting(null);
1879
2524
  }, [c.id, c.form?.initialValues?.locale]);
1880
- const [editing, setEditing] = React3.useState(null);
1881
- const [inserting, setInserting] = React3.useState(null);
2525
+ const [editing, setEditing] = React4.useState(null);
2526
+ const [inserting, setInserting] = React4.useState(null);
1882
2527
  const { get } = useFetchClient();
1883
2528
  const { toggleNotification } = useNotification();
1884
2529
  const zoneAttr = zone ? c.contentType?.attributes?.[zone] : null;
1885
- const iframe = React3.useRef(null);
1886
- const loaded = React3.useRef(false);
1887
- const anchor = React3.useRef(null);
1888
- const channel = React3.useMemo(() => crypto.randomUUID(), []);
1889
- const { base, source } = usePreviewBase(editor?.previewUrl || "", c.model, c.isCreatingEntry ? void 0 : c.id, c.form?.initialValues?.locale);
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
+ );
1890
2540
  const url = base ? new URL(base) : null;
1891
2541
  url?.searchParams.set("channel", channel);
1892
2542
  const origin = url?.origin || "";
@@ -1895,21 +2545,50 @@ function PagePreview({ editor, groups = null, Toggle }) {
1895
2545
  const resizable = mode === "split" && !narrow;
1896
2546
  const active = mode !== "form" && Boolean(url) && Boolean(zone);
1897
2547
  const rows = zone && Array.isArray(values?.[zone]) ? values[zone] : [];
1898
- const latest = React3.useRef(rows);
2548
+ const latest = React4.useRef(rows);
1899
2549
  latest.current = rows;
1900
2550
  const insertRows = (at, ...items) => items.forEach((item, i) => {
1901
2551
  const from = latest.current.length + i;
1902
2552
  addFieldRow(zone, item);
1903
2553
  if (at + i < from) moveFieldRow(zone, from, at + i);
1904
2554
  });
1905
- const live = React3.useRef({ mode, canEdit, zoneLabel, components: c.components, setMode, onChange });
1906
- live.current = { mode, canEdit, zoneLabel, components: c.components, setMode, onChange };
1907
- const send = React3.useCallback(() => {
1908
- iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "update-page", blocks: projectPage(latest.current, live.current.components, window.location.origin), groups, locale: t.locale, mode: live.current.mode }, origin);
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
+ );
1909
2588
  }, [channel, origin]);
1910
2589
  const indexOf = (key) => latest.current.findIndex((row) => blockKey(row) === key);
1911
2590
  const path = (key, field) => `${zone}.${indexOf(key)}.${field}`;
1912
- React3.useEffect(() => {
2591
+ React4.useEffect(() => {
1913
2592
  if (resizable) markLayout(anchor.current);
1914
2593
  document.body.classList.toggle("bp-split", resizable);
1915
2594
  document.documentElement.style.setProperty("--bp-pane", `${paneWidth}px`);
@@ -1921,7 +2600,9 @@ function PagePreview({ editor, groups = null, Toggle }) {
1921
2600
  if (persist) write(RATIO_KEY, String(next));
1922
2601
  };
1923
2602
  const focusField = (index, field) => {
1924
- const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[index];
2603
+ const item = findZoneList(live.current.zoneLabel)?.querySelectorAll(
2604
+ ":scope > li"
2605
+ )[index];
1925
2606
  if (!item) return;
1926
2607
  const header = toggles(item.parentElement)[index];
1927
2608
  if (header?.getAttribute("aria-expanded") === "false") header.click();
@@ -1932,33 +2613,47 @@ function PagePreview({ editor, groups = null, Toggle }) {
1932
2613
  if (++tries > 40) return clearInterval(timer);
1933
2614
  let scope = item;
1934
2615
  for (let i = 0; i + 1 < parts.length; i += 2) {
1935
- const nested = [...scope.querySelectorAll("button[aria-expanded][data-radix-collection-item]")].filter((b) => b !== header);
2616
+ const nested = [
2617
+ ...scope.querySelectorAll(
2618
+ "button[aria-expanded][data-radix-collection-item]"
2619
+ )
2620
+ ].filter((b) => b !== header);
1936
2621
  const row = nested[Number(parts[i + 1])];
1937
2622
  if (!row) return;
1938
2623
  if (row.getAttribute("aria-expanded") === "false") row.click();
1939
2624
  scope = row.closest("li, div") || scope;
1940
2625
  }
1941
- const target = document.querySelector(`[name="${escaped}"], label[for="${escaped}"]`);
2626
+ const target = document.querySelector(
2627
+ `[name="${escaped}"], label[for="${escaped}"]`
2628
+ );
1942
2629
  if (!target) return;
1943
2630
  const INPUT = '.ck-editor__editable, textarea, input:not([type="hidden"]), select';
1944
2631
  let container = target.matches(INPUT) ? target : target.parentElement;
1945
- for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++) container = container.parentElement;
2632
+ for (let depth = 0; container && depth < 4 && !container.matches(INPUT) && !container.querySelector(INPUT); depth++)
2633
+ container = container.parentElement;
1946
2634
  const editable = container?.matches(INPUT) ? container : container?.querySelector(INPUT);
1947
2635
  if (!editable) return;
1948
- if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable) return;
2636
+ if (editable.classList.contains("ck-editor__editable") && !editable.isContentEditable)
2637
+ return;
1949
2638
  editable.focus();
1950
2639
  if (document.activeElement !== editable) return;
1951
2640
  clearInterval(timer);
1952
2641
  editable.scrollIntoView({ block: "center", behavior: "smooth" });
1953
2642
  }, 150);
1954
2643
  };
1955
- React3.useEffect(() => {
2644
+ React4.useEffect(() => {
1956
2645
  if (!active) return;
1957
2646
  setReady(false);
1958
2647
  setFailed(false);
1959
2648
  const timeout = setTimeout(() => setFailed(true), 15e3);
1960
2649
  const receive = (event) => {
1961
- 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
+ );
1962
2657
  const { mode: mode2, canEdit: canEdit2, zoneLabel: zoneLabel2, components: components2, setMode: setMode2, onChange: onChange2 } = live.current;
1963
2658
  if (is("ready")) {
1964
2659
  clearTimeout(timeout);
@@ -1978,12 +2673,21 @@ function PagePreview({ editor, groups = null, Toggle }) {
1978
2673
  return;
1979
2674
  }
1980
2675
  if (is("move-group") && canEdit2 && groups) {
1981
- const next = (0, import_groups.moveGroup)(latest.current, String(event.data.key), event.data.direction === "up" ? "up" : "down", groups);
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
+ );
1982
2682
  if (next) onChange2(zone, next);
1983
2683
  return;
1984
2684
  }
1985
2685
  if (is("delete-group") && canEdit2 && groups) {
1986
- 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
+ );
1987
2691
  if (next) onChange2(zone, next);
1988
2692
  return;
1989
2693
  }
@@ -1993,12 +2697,20 @@ function PagePreview({ editor, groups = null, Toggle }) {
1993
2697
  const uid2 = String(event.data.uid || "");
1994
2698
  const close = groups[uid2];
1995
2699
  const allowed = zoneAttr?.components || [];
1996
- if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close)) return;
2700
+ if (index2 < 0 || !close || !allowed.includes(uid2) || !allowed.includes(close))
2701
+ return;
1997
2702
  if (latest.current.length + 2 > (zoneAttr?.max ?? Infinity)) {
1998
2703
  toggleNotification({ type: "info", message: t.zoneFull });
1999
2704
  return;
2000
2705
  }
2001
- insertRows(index2, { ...componentDefaults(components2[uid2], components2), __component: uid2 }, { __component: close });
2706
+ insertRows(
2707
+ index2,
2708
+ {
2709
+ ...componentDefaults(components2[uid2], components2),
2710
+ __component: uid2
2711
+ },
2712
+ { __component: close }
2713
+ );
2002
2714
  return;
2003
2715
  }
2004
2716
  const key = typeof event.data?.key === "string" ? event.data.key : "";
@@ -2018,11 +2730,21 @@ function PagePreview({ editor, groups = null, Toggle }) {
2018
2730
  }, 1500);
2019
2731
  }
2020
2732
  if (mode2 === "preview") setMode2("split");
2021
- 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
+ );
2022
2737
  } else if (is("focus")) {
2023
2738
  const attr = validateFocus(uid, event.data.field, components2);
2024
2739
  if (!attr) return;
2025
- if (mode2 === "preview" && canEdit2 && (attr.customField || MODAL_TYPES.includes(attr.type))) setEditing({ key, field: event.data.field, attr, label: `${components2[uid]?.info?.displayName || uid} \xB7 ${event.data.field}`, value: getIn(latest.current[index], event.data.field) });
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
+ });
2026
2748
  else focusField(index, event.data.field);
2027
2749
  } else if (is("media") && canEdit2) {
2028
2750
  const attr = mediaAttribute(uid, event.data.field, components2);
@@ -2031,27 +2753,67 @@ function PagePreview({ editor, groups = null, Toggle }) {
2031
2753
  const attr = mediaAttribute(uid, event.data.field, components2);
2032
2754
  if (attr && !attr.required) onChange2(path(key, event.data.field), null);
2033
2755
  } else if (is("edit") && canEdit2) {
2034
- 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
+ );
2035
2762
  if (value !== null) onChange2(path(key, event.data.field), value);
2036
2763
  }
2037
2764
  };
2038
2765
  window.addEventListener("message", receive);
2039
- 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
+ );
2040
2771
  return () => {
2041
2772
  clearTimeout(timeout);
2042
2773
  window.removeEventListener("message", receive);
2043
2774
  };
2044
2775
  }, [active, attempt, channel, origin, groups]);
2045
- React3.useEffect(() => {
2776
+ React4.useEffect(() => {
2046
2777
  if (!ready || !active) return;
2047
2778
  const timer = setTimeout(send, 120);
2048
2779
  return () => clearTimeout(timer);
2049
2780
  }, [values, c.components, ready, active, send, mode]);
2050
2781
  if (!zone) return null;
2051
- const modes = [["form", t.modeForm], ["split", t.modeSplit], ["preview", t.modePreview]];
2052
- const switcher = /* @__PURE__ */ jsx4(Flex3, { gap: 1, wrap: "wrap", "data-testid": "page-preview-modes", role: "group", "aria-label": t.modeGroup, children: modes.map(([value, label]) => /* @__PURE__ */ jsx4(Button3, { type: "button", size: "S", variant: mode === value ? "default" : "tertiary", "aria-pressed": mode === value, onClick: () => setMode(value), disabled: !url && value !== "form", children: label }, value)) });
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
+ );
2053
2810
  const onKey = (event) => {
2054
- 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
+ };
2055
2817
  if (event.key in map) {
2056
2818
  event.preventDefault();
2057
2819
  applyWidth(map[event.key], true);
@@ -2062,28 +2824,69 @@ function PagePreview({ editor, groups = null, Toggle }) {
2062
2824
  const end = (0, import_groups.groupRange)(latest.current, error.index, groups)[1];
2063
2825
  insertRows(end + 1, { __component: error.expected });
2064
2826
  };
2065
- const diagnostics = problems.length > 0 && /* @__PURE__ */ jsx4(Box2, { role: "alert", "data-testid": "page-preview-diagnostics", padding: 2, background: "danger100", hasRadius: true, children: /* @__PURE__ */ jsxs3("details", { children: [
2066
- /* @__PURE__ */ jsx4("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ jsx4(Typography3, { variant: "pi", fontWeight: "bold", textColor: "danger700", children: t.f("diagSummary", { count: problems.length }) }) }),
2067
- /* @__PURE__ */ jsx4("ul", { style: { margin: "6px 0 0 16px", padding: 0 }, children: problems.map((error) => /* @__PURE__ */ jsxs3("li", { style: { marginBottom: 6 }, children: [
2068
- /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "danger700", children: t.f(error.code === "closeBeforeOpen" ? "diagCloseBeforeOpen" : error.code === "mismatch" ? "diagMismatch" : "diagUnclosed", { n: error.index + 1, uid: error.uid, open: error.open || "", expected: error.expected || "" }) }),
2069
- error.code === "unclosed" && canEdit && /* @__PURE__ */ jsx4(Box2, { paddingTop: 1, children: /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "danger-light", "data-testid": `diag-insert-close-${error.index}`, onClick: () => insertClose(error), children: t.f("diagInsertClose", { n: error.index + 1, expected: error.expected }) }) }),
2070
- error.code === "closeBeforeOpen" && /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: t.diagStrayHint })
2071
- ] }, `${error.code}-${error.index}`)) })
2072
- ] }) });
2073
- return /* @__PURE__ */ jsxs3(Flex3, { direction: "column", alignItems: "stretch", gap: 2, ref: anchor, children: [
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: [
2074
2873
  switcher,
2075
2874
  diagnostics,
2076
- /* @__PURE__ */ jsx4(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2077
- active && createPortal(
2078
- /* @__PURE__ */ jsxs3(
2875
+ /* @__PURE__ */ jsx5(Typography3, { variant: "pi", textColor: "neutral600", children: url ? t.previewHelp : t.previewNoUrl }),
2876
+ active && createPortal2(
2877
+ /* @__PURE__ */ jsxs4(
2079
2878
  Pane,
2080
2879
  {
2081
2880
  "data-testid": "page-preview-pane",
2082
2881
  "aria-label": t.previewPane,
2083
- style: { left: mode === "preview" || narrow ? main.left : main.left + main.width - paneWidth, width: mode === "preview" || narrow ? main.width : paneWidth, userSelect: dragging ? "none" : void 0 },
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
+ },
2084
2887
  children: [
2085
- /* @__PURE__ */ jsx4("style", { children: SPLIT_STYLE }),
2086
- resizable && /* @__PURE__ */ jsx4(
2888
+ /* @__PURE__ */ jsx5("style", { children: SPLIT_STYLE }),
2889
+ resizable && /* @__PURE__ */ jsx5(
2087
2890
  Handle,
2088
2891
  {
2089
2892
  role: "separator",
@@ -2101,7 +2904,8 @@ function PagePreview({ editor, groups = null, Toggle }) {
2101
2904
  setDragging(true);
2102
2905
  },
2103
2906
  onPointerMove: (e) => {
2104
- if (dragging) applyWidth(main.left + main.width - e.clientX, false);
2907
+ if (dragging)
2908
+ applyWidth(main.left + main.width - e.clientX, false);
2105
2909
  },
2106
2910
  onPointerUp: (e) => {
2107
2911
  if (!dragging) return;
@@ -2113,15 +2917,44 @@ function PagePreview({ editor, groups = null, Toggle }) {
2113
2917
  onKeyDown: onKey
2114
2918
  }
2115
2919
  ),
2116
- /* @__PURE__ */ jsxs3(Flex3, { padding: 2, gap: 2, justifyContent: "space-between", alignItems: "center", background: "neutral100", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 2 }, children: [
2117
- switcher,
2118
- /* @__PURE__ */ jsxs3(Flex3, { gap: 2, alignItems: "center", wrap: "wrap", style: { marginLeft: "auto" }, children: [
2119
- /* @__PURE__ */ jsx4(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
2120
- failed && /* @__PURE__ */ jsx4(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
2121
- /* @__PURE__ */ jsx4(ToolbarActions, { model: c.model, collectionType: c.collectionType, documentId: c.isCreatingEntry ? void 0 : c.id, locale: c.form?.initialValues?.locale })
2122
- ] })
2123
- ] }),
2124
- /* @__PURE__ */ jsx4(
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(
2125
2958
  Frame2,
2126
2959
  {
2127
2960
  ref: iframe,
@@ -2141,12 +2974,16 @@ function PagePreview({ editor, groups = null, Toggle }) {
2141
2974
  ),
2142
2975
  document.body
2143
2976
  ),
2144
- inserting && zoneAttr && /* @__PURE__ */ jsx4(
2977
+ inserting && zoneAttr && /* @__PURE__ */ jsx5(
2145
2978
  PickerModal,
2146
2979
  {
2147
- zone: { name: zone, components: zoneAttr.components || [], max: zoneAttr.max },
2980
+ zone: {
2981
+ name: zone,
2982
+ components: zoneAttr.components || [],
2983
+ max: zoneAttr.max
2984
+ },
2148
2985
  components: c.components,
2149
- Modal: PickerDialog,
2986
+ Modal: Modal3,
2150
2987
  Toggle,
2151
2988
  get,
2152
2989
  open: true,
@@ -2158,14 +2995,24 @@ function PagePreview({ editor, groups = null, Toggle }) {
2158
2995
  setInserting(null);
2159
2996
  const close = groups?.[uid];
2160
2997
  if (index < 0 || !(zoneAttr.components || []).includes(uid) || close && !(zoneAttr.components || []).includes(close) || latest.current.length + (close ? 2 : 1) > (zoneAttr.max ?? Infinity)) {
2161
- toggleNotification({ type: "warning", message: close ? t.zoneFull : t.insertMoved });
2998
+ toggleNotification({
2999
+ type: "warning",
3000
+ message: close ? t.zoneFull : t.insertMoved
3001
+ });
2162
3002
  return;
2163
3003
  }
2164
- insertRows(index, { ...componentDefaults(c.components[uid], c.components), __component: uid }, ...close ? [{ __component: close }] : []);
3004
+ insertRows(
3005
+ index,
3006
+ {
3007
+ ...componentDefaults(c.components[uid], c.components),
3008
+ __component: uid
3009
+ },
3010
+ ...close ? [{ __component: close }] : []
3011
+ );
2165
3012
  }
2166
3013
  }
2167
3014
  ),
2168
- editing && /* @__PURE__ */ jsx4(
3015
+ editing && /* @__PURE__ */ jsx5(
2169
3016
  FieldEditorModal,
2170
3017
  {
2171
3018
  label: editing.label,
@@ -2175,11 +3022,12 @@ function PagePreview({ editor, groups = null, Toggle }) {
2175
3022
  onApply: (value) => {
2176
3023
  const index = indexOf(editing.key);
2177
3024
  setEditing(null);
2178
- if (index >= 0) onChange(`${zone}.${index}.${editing.field}`, value);
3025
+ if (index >= 0)
3026
+ onChange(`${zone}.${index}.${editing.field}`, value);
2179
3027
  }
2180
3028
  }
2181
3029
  ),
2182
- picking && MediaLibraryDialog && /* @__PURE__ */ jsx4(
3030
+ picking && MediaLibraryDialog && /* @__PURE__ */ jsx5(
2183
3031
  MediaLibraryDialog,
2184
3032
  {
2185
3033
  allowedTypes: picking.attr.allowedTypes || ["images"],
@@ -2190,19 +3038,32 @@ function PagePreview({ editor, groups = null, Toggle }) {
2190
3038
  setPicking(null);
2191
3039
  if (index < 0 || !assets?.length) return;
2192
3040
  const current = getIn(latest.current[index], picking.field);
2193
- 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
+ );
2194
3045
  }
2195
3046
  }
2196
3047
  ),
2197
- /* @__PURE__ */ jsx4(Box2, { hidden: true, "data-testid": "page-preview-state", "data-mode": mode, "data-ready": ready, "data-failed": failed, "data-pane-width": paneWidth, "data-narrow": narrow, "data-preview-source": source, "data-editing": editing ? editing.field : "" })
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
+ )
2198
3059
  ] });
2199
3060
  }
2200
3061
 
2201
3062
  // admin/Settings.tsx
2202
- import * as React4 from "react";
3063
+ import * as React5 from "react";
2203
3064
  import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
2204
3065
  import styled3 from "styled-components";
2205
- 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";
2206
3067
  var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
2207
3068
  var Grid = styled3.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
2208
3069
  var Card2 = styled3.div`
@@ -2215,8 +3076,8 @@ var Bar = styled3.div`
2215
3076
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
2216
3077
  `;
2217
3078
  function useMainRect2(anchor) {
2218
- const [rect, setRect] = React4.useState({ left: 0, width: 0 });
2219
- React4.useEffect(() => {
3079
+ const [rect, setRect] = React5.useState({ left: 0, width: 0 });
3080
+ React5.useEffect(() => {
2220
3081
  const read2 = () => {
2221
3082
  let host = document.getElementById("main-content");
2222
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;
@@ -2233,11 +3094,11 @@ var Swatches = styled3.div` display: grid; grid-template-columns: repeat(auto-fi
2233
3094
  var Label = styled3.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
2234
3095
  function ColorField({ name, label, value, onChange, disabled }) {
2235
3096
  const valid = COLOR2.test(value);
2236
- return /* @__PURE__ */ jsxs4(Label, { children: [
2237
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
2238
- /* @__PURE__ */ jsxs4(Flex4, { gap: 2, children: [
2239
- /* @__PURE__ */ jsx5("input", { type: "color", "aria-label": label, value: valid ? value : "#000000", disabled, onChange: (e) => onChange(e.target.value.toUpperCase()), style: { width: 40, height: 32, padding: 0, border: 0, background: "none" } }),
2240
- /* @__PURE__ */ jsx5(
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(
2241
3102
  "input",
2242
3103
  {
2243
3104
  name,
@@ -2257,18 +3118,18 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2257
3118
  const t = useMessages();
2258
3119
  const { get, put } = useClient();
2259
3120
  const { canRead, canUpdate, isLoading } = usePermissions2();
2260
- const [data, setData] = React4.useState(null);
2261
- const [catalog, setCatalog] = React4.useState(null);
2262
- const [settings, setSettings] = React4.useState(null);
2263
- const [media, setMedia] = React4.useState({});
2264
- const [sources, setSources] = React4.useState({});
2265
- const [dirty, setDirty] = React4.useState(false);
2266
- const [saving, setSaving] = React4.useState(false);
2267
- const [status, setStatus] = React4.useState("");
2268
- const [failed, setFailed] = React4.useState(false);
2269
- const [filter, setFilter] = React4.useState("");
2270
- const [picking, setPicking] = React4.useState(null);
2271
- React4.useEffect(() => {
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(() => {
2272
3133
  if (!canRead || isLoading) return;
2273
3134
  let active = true;
2274
3135
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -2316,35 +3177,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2316
3177
  return index === 0 && manual ? t.manual : t.automatic;
2317
3178
  };
2318
3179
  const ready = !isLoading && canRead && !failed && settings && catalog;
2319
- const header = React4.useRef(null);
2320
- const [floating, setFloating] = React4.useState(false);
3180
+ const header = React5.useRef(null);
3181
+ const [floating, setFloating] = React5.useState(false);
2321
3182
  const main = useMainRect2(header);
2322
- React4.useEffect(() => {
3183
+ React5.useEffect(() => {
2323
3184
  const el = header.current;
2324
3185
  if (!el || typeof IntersectionObserver === "undefined") return;
2325
3186
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
2326
3187
  observer.observe(el);
2327
3188
  return () => observer.disconnect();
2328
3189
  }, [ready]);
2329
- const controls = (suffix) => ready && /* @__PURE__ */ jsxs4(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
2330
- /* @__PURE__ */ jsx5(Typography4, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
2331
- /* @__PURE__ */ jsx5(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
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 })
2332
3193
  ] });
2333
- return /* @__PURE__ */ jsx5(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
2334
- /* @__PURE__ */ jsx5("div", { ref: header, children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2335
- /* @__PURE__ */ jsx5(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
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 }),
2336
3197
  controls("")
2337
3198
  ] }) }),
2338
- floating && main.width > 0 && /* @__PURE__ */ jsx5(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs4(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2339
- /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
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 }),
2340
3201
  controls("-floating")
2341
3202
  ] }) }),
2342
- /* @__PURE__ */ jsx5(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
2343
- isLoading ? /* @__PURE__ */ jsx5(Typography4, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx5(Typography4, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx5(Typography4, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx5(Typography4, { children: t.loading }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
2344
- data.disabled && /* @__PURE__ */ jsx5(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
2345
- /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2346
- /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
2347
- /* @__PURE__ */ jsx5(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx5(
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(
2348
3209
  ColorField,
2349
3210
  {
2350
3211
  name: `palette-${key}`,
@@ -2358,33 +3219,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2358
3219
  },
2359
3220
  key
2360
3221
  )) }),
2361
- invalid && /* @__PURE__ */ jsx5(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
2362
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
2363
- /* @__PURE__ */ jsx5(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs4(Flex4, { direction: "column", gap: 1, children: [
2364
- /* @__PURE__ */ jsx5(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx5(Wireframe, { template, palette: settings.palette }) }),
2365
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
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] })
2366
3227
  ] }, template)) }),
2367
- /* @__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) => {
2368
3229
  s.palette = { ...DEFAULT_PALETTE };
2369
3230
  return s;
2370
3231
  }), children: t.resetPalette }) })
2371
3232
  ] }) }),
2372
- /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2373
- /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
2374
- /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
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) => {
2375
3236
  s.editor.enabled = v;
2376
3237
  return s;
2377
3238
  }) }),
2378
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
2379
- /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
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) => {
2380
3241
  s.editor.showOpenAll = v;
2381
3242
  return s;
2382
3243
  }) }),
2383
- /* @__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) => {
2384
3245
  s.editor.showCloseAll = v;
2385
3246
  return s;
2386
3247
  }) }),
2387
- /* @__PURE__ */ jsx5(
3248
+ /* @__PURE__ */ jsx6(
2388
3249
  SelectField2,
2389
3250
  {
2390
3251
  name: "editor-initialState",
@@ -2398,9 +3259,9 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2398
3259
  })
2399
3260
  }
2400
3261
  ),
2401
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
2402
- previewSupported ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2403
- /* @__PURE__ */ jsx5(
3262
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
3263
+ previewSupported ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
3264
+ /* @__PURE__ */ jsx6(
2404
3265
  TextField2,
2405
3266
  {
2406
3267
  name: "editor-previewUrl",
@@ -2414,8 +3275,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2414
3275
  })
2415
3276
  }
2416
3277
  ),
2417
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
2418
- /* @__PURE__ */ jsx5(
3278
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
3279
+ /* @__PURE__ */ jsx6(
2419
3280
  SelectField2,
2420
3281
  {
2421
3282
  name: "editor-previewMode",
@@ -2429,26 +3290,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2429
3290
  })
2430
3291
  }
2431
3292
  ),
2432
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
2433
- data.blockPreviewAvailable && /* @__PURE__ */ jsxs4(Fragment, { children: [
2434
- /* @__PURE__ */ jsx5(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
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) => {
2435
3296
  s.editor.blockPreviewInForm = v;
2436
3297
  return s;
2437
3298
  }) }),
2438
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
3299
+ /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
2439
3300
  ] })
2440
- ] }) : /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
3301
+ ] }) : /* @__PURE__ */ jsx6(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
2441
3302
  ] }) }),
2442
- /* @__PURE__ */ jsx5(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
2443
- /* @__PURE__ */ jsx5(Typography4, { variant: "beta", tag: "h2", children: t.components }),
2444
- /* @__PURE__ */ jsx5(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
2445
- /* @__PURE__ */ jsx5(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
2446
- /* @__PURE__ */ jsx5(Grid, { "data-testid": "components-grid", children: components.map((component) => {
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) => {
2447
3308
  const entry = settings.components[component.uid] || {};
2448
3309
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
2449
3310
  const candidates = candidatesFor(component.uid, meta, catalog);
2450
- return /* @__PURE__ */ jsxs4(Card2, { "data-testid": `settings-${component.uid}`, children: [
2451
- /* @__PURE__ */ jsx5(
3311
+ return /* @__PURE__ */ jsxs5(Card2, { "data-testid": `settings-${component.uid}`, children: [
3312
+ /* @__PURE__ */ jsx6(
2452
3313
  Thumb,
2453
3314
  {
2454
3315
  candidates,
@@ -2459,23 +3320,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2459
3320
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
2460
3321
  }
2461
3322
  ),
2462
- /* @__PURE__ */ jsxs4(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
2463
- /* @__PURE__ */ jsx5(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
2464
- /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", textColor: "neutral600", children: [
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: [
2465
3326
  component.uid,
2466
3327
  " \xB7 ",
2467
3328
  component.category
2468
3329
  ] }),
2469
- /* @__PURE__ */ jsxs4(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
3330
+ /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
2470
3331
  t.source,
2471
3332
  ": ",
2472
3333
  sourceOf(component.uid, Boolean(meta.manualImage)),
2473
3334
  meta.manualName ? ` (${meta.manualName})` : ""
2474
3335
  ] }),
2475
- meta.manualMissing && /* @__PURE__ */ jsx5(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
2476
- /* @__PURE__ */ jsxs4(Flex4, { gap: 2, wrap: "wrap", children: [
2477
- /* @__PURE__ */ jsx5(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
2478
- entry.mediaId && /* @__PURE__ */ jsx5(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
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: () => {
2479
3340
  update((s) => {
2480
3341
  delete s.components[component.uid]?.mediaId;
2481
3342
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -2488,7 +3349,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2488
3349
  });
2489
3350
  }, children: t.useAutomatic })
2490
3351
  ] }),
2491
- /* @__PURE__ */ jsx5(
3352
+ /* @__PURE__ */ jsx6(
2492
3353
  SelectField2,
2493
3354
  {
2494
3355
  name: `template-${component.uid}`,
@@ -2506,7 +3367,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
2506
3367
  ] }, component.uid);
2507
3368
  }) })
2508
3369
  ] }) }),
2509
- picking && /* @__PURE__ */ jsx5(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
3370
+ picking && /* @__PURE__ */ jsx6(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
2510
3371
  const uid = picking;
2511
3372
  setPicking(null);
2512
3373
  if (!asset?.id) return;
@@ -2535,10 +3396,10 @@ function register(app, Component, to) {
2535
3396
  }
2536
3397
 
2537
3398
  // admin/catalog.ts
2538
- import * as React5 from "react";
3399
+ import * as React6 from "react";
2539
3400
  function useCatalog(get) {
2540
- const [catalog, setCatalog] = React5.useState(null);
2541
- React5.useEffect(() => {
3401
+ const [catalog, setCatalog] = React6.useState(null);
3402
+ React6.useEffect(() => {
2542
3403
  let active = true;
2543
3404
  get("/blockscene/catalog").then(({ data }) => {
2544
3405
  if (active) setCatalog(data);
@@ -2553,13 +3414,13 @@ function useCatalog(get) {
2553
3414
  }
2554
3415
 
2555
3416
  // admin/strapi5.tsx
2556
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3417
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2557
3418
  function Modal2({ open, onOpenChange, trigger, title, children }) {
2558
- return /* @__PURE__ */ jsxs5(Dialog2.Root, { open, onOpenChange, children: [
2559
- /* @__PURE__ */ jsx6(Dialog2.Trigger, { children: trigger }),
2560
- /* @__PURE__ */ jsxs5(Dialog2.Content, { style: { width: "80vw", maxWidth: "80vw" }, children: [
2561
- /* @__PURE__ */ jsx6(Dialog2.Header, { children: /* @__PURE__ */ jsx6(Dialog2.Title, { children: title }) }),
2562
- /* @__PURE__ */ jsx6(Dialog2.Body, { children })
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 })
2563
3424
  ] })
2564
3425
  ] });
2565
3426
  }
@@ -2579,49 +3440,154 @@ function Panel() {
2579
3440
  formValues ?? c.form?.values,
2580
3441
  (name) => allowed.includes(name) && (creating || (rbac.canReadFields || []).includes(name)) && fields.find((field) => field.name === name)?.disabled !== true,
2581
3442
  c.isLoading || c.form?.disabled || !c.form?.addFieldRow
2582
- ).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
+ }));
2583
3447
  const add = (zone, uid) => {
2584
3448
  const values = formValues ?? c.form.values;
2585
3449
  const close = catalog?.groups?.[uid];
2586
- 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;
2587
3452
  const at = Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0;
2588
- c.form.addFieldRow(zone.name, { ...componentDefaults(c.components[uid], c.components), __component: uid }, at);
3453
+ c.form.addFieldRow(
3454
+ zone.name,
3455
+ {
3456
+ ...componentDefaults(c.components[uid], c.components),
3457
+ __component: uid
3458
+ },
3459
+ at
3460
+ );
2589
3461
  if (close) c.form.addFieldRow(zone.name, { __component: close }, at + 1);
2590
3462
  return true;
2591
3463
  };
2592
3464
  if (!zones.length || !catalog?.editor?.enabled) return null;
2593
3465
  const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
2594
- return { title: t.gallery, content: /* @__PURE__ */ jsxs5(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
2595
- /* @__PURE__ */ jsx6(Gallery, { zones, components: c.components, add, Modal: Modal2, Toggle: ToggleField, get, editor: catalog.editor, docKey, contentType: c.model, userId: user?.id }),
2596
- /* @__PURE__ */ jsx6(PagePreview, { editor: catalog.editor, groups: catalog.groups || null, Toggle: ToggleField })
2597
- ] }) };
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
+ };
2598
3496
  }
2599
- var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs5(Flex5, { gap: 3, children: [
2600
- /* @__PURE__ */ jsx6(Switch, { name, "aria-label": label, checked: Boolean(value), disabled, onCheckedChange: onChange }),
2601
- /* @__PURE__ */ jsx6(Typography5, { children: label })
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 })
2602
3509
  ] });
2603
- var TextField = ({ name, label, value, onChange, disabled, placeholder }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2604
- /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2605
- /* @__PURE__ */ jsx6(TextInput, { name, value, disabled, placeholder, onChange: (e) => onChange(e.target.value) })
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
+ )
2606
3529
  ] });
2607
- var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsxs5(Field.Root, { name, children: [
2608
- /* @__PURE__ */ jsx6(Field.Label, { children: label }),
2609
- /* @__PURE__ */ jsx6(SingleSelect2, { value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ jsx6(SingleSelectOption2, { value: o.value, children: o.label }, o.value)) })
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
+ )
2610
3548
  ] });
2611
3549
  function MediaPicker({ onClose, onSelect }) {
2612
- const components = useStrapiApp3("Blockscene", (state) => state.components);
2613
- const Dialog3 = components?.["media-library"];
2614
- React6.useEffect(() => {
2615
- if (!Dialog3) onClose();
2616
- }, [Dialog3, onClose]);
2617
- if (!Dialog3) return null;
2618
- return /* @__PURE__ */ jsx6(Dialog3, { onClose, allowedTypes: ["images"], multiple: false, onSelectAssets: (assets) => onSelect(assets?.[0]) });
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
+ );
2619
3568
  }
2620
3569
  function usePermissions() {
2621
- const { allowedActions, isLoading } = useRBAC(Object.values(permissions).flat());
2622
- 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
+ };
2623
3578
  }
2624
- 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
+ );
2625
3591
  var strapi5_default = {
2626
3592
  register(app) {
2627
3593
  register(app, SettingsPage, "blockscene");