@aduptive/strapi-blockscene 1.0.0-alpha.3 → 1.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.
package/dist/admin.mjs CHANGED
@@ -1,39 +1,93 @@
1
1
  // admin/strapi4.tsx
2
- import * as React4 from "react";
3
- import { ModalLayout, ModalHeader, ModalBody, Typography as Typography3, Switch, Flex as Flex3, SingleSelect as SingleSelect2, SingleSelectOption as SingleSelectOption2 } from "@strapi/design-system";
4
- import { useCMEditViewDataManager, useFetchClient, useLibrary, useRBAC, auth } from "@strapi/helper-plugin";
2
+ import * as React5 from "react";
3
+ import {
4
+ ModalLayout,
5
+ ModalHeader,
6
+ ModalBody,
7
+ Typography as Typography3,
8
+ Switch,
9
+ Flex as Flex3,
10
+ SingleSelect as SingleSelect2,
11
+ SingleSelectOption as SingleSelectOption2
12
+ } from "@strapi/design-system";
13
+ import {
14
+ useCMEditViewDataManager,
15
+ useFetchClient,
16
+ useLibrary,
17
+ useRBAC,
18
+ auth
19
+ } from "@strapi/helper-plugin";
5
20
 
6
21
  // admin/Gallery.tsx
7
- import * as React from "react";
22
+ import * as React2 from "react";
8
23
  import { unstable_batchedUpdates } from "react-dom";
9
- import { Box, Button, Flex, Searchbar, SingleSelect, SingleSelectOption, Typography } from "@strapi/design-system";
24
+ import {
25
+ Box,
26
+ Button,
27
+ Flex,
28
+ Searchbar,
29
+ SingleSelect,
30
+ SingleSelectOption,
31
+ Typography
32
+ } from "@strapi/design-system";
10
33
  import styled from "styled-components";
11
34
 
12
35
  // admin/model.mjs
13
36
  import { generateNKeysBetween } from "fractional-indexing";
14
37
  function editableZones(schema, values, canEdit, disabled = false) {
15
38
  if (disabled) return [];
16
- return Object.entries(schema?.attributes || {}).filter(([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)).map(([name, attr]) => {
17
- const count = Array.isArray(values?.[name]) ? values[name].length : 0;
18
- return { name, components: attr.components || [], max: attr.max, count, full: count >= (attr.max ?? Infinity) };
39
+ return Object.entries(schema?.attributes || {}).filter(
40
+ ([name, attr]) => attr.type === "dynamiczone" && !attr.conditions && canEdit(name)
41
+ ).map(([name, attr]) => {
42
+ const rows = Array.isArray(values?.[name]) ? values[name] : [];
43
+ return {
44
+ name,
45
+ components: attr.components || [],
46
+ max: attr.max,
47
+ count: rows.length,
48
+ uids: rows.map(
49
+ (row) => row && typeof row.__component === "string" ? row.__component : ""
50
+ ),
51
+ full: rows.length >= (attr.max ?? Infinity)
52
+ };
19
53
  });
20
54
  }
21
55
  function canInsert(zone, uid, values, components, count = 1) {
22
- return Boolean(zone?.components.includes(uid) && components[uid] && (Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0) + count <= (zone.max ?? Infinity));
56
+ return Boolean(
57
+ zone?.components.includes(uid) && components[uid] && (Array.isArray(values?.[zone.name]) ? values[zone.name].length : 0) + count <= (zone.max ?? Infinity)
58
+ );
23
59
  }
24
60
  function candidatesFor(uid, meta = {}, config = {}) {
25
61
  const version = config.previewVersion ? `?v=${encodeURIComponent(config.previewVersion)}` : "";
26
62
  const base = (config.previewBaseUrl || "/block-previews").replace(/\/$/, "");
27
- return [meta.manualImage, meta.image, `${base}/${encodeURIComponent(uid)}.webp${version}`].filter(Boolean);
63
+ return [
64
+ meta.manualImage,
65
+ meta.image,
66
+ `${base}/${encodeURIComponent(uid)}.webp${version}`
67
+ ].filter(Boolean);
28
68
  }
29
- var SYSTEM_FIELDS = ["id", "documentId", "createdAt", "updatedAt", "publishedAt", "createdBy", "updatedBy", "locale", "localizations"];
69
+ var SYSTEM_FIELDS = [
70
+ "id",
71
+ "documentId",
72
+ "createdAt",
73
+ "updatedAt",
74
+ "publishedAt",
75
+ "createdBy",
76
+ "updatedBy",
77
+ "locale",
78
+ "localizations"
79
+ ];
30
80
  var fieldType = (attr) => attr?.type === "customField" ? String(attr.customField || "").includes("CKEditor") ? "richtext" : "customField" : attr?.type || "unknown";
31
81
  var fieldsOf = (schema) => Object.entries(schema?.attributes || {}).filter(([name]) => !SYSTEM_FIELDS.includes(name)).map(([name, attr]) => ({ name, type: fieldType(attr) }));
32
82
  var categoryOf = (uid, schema, meta = {}) => meta.category || schema?.category || uid.split(".")[0];
33
83
  var byCategory = (a, b) => (a === "admin" ? 1 : 0) - (b === "admin" ? 1 : 0) || a.localeCompare(b);
84
+ var pickable = (zone, config) => {
85
+ const closers = Object.values(config.groups || {});
86
+ return zone.components.filter((uid) => !closers.includes(uid));
87
+ };
34
88
  function entriesFor(zone, components, config, query, category = "all") {
35
89
  const needle = query.trim().toLocaleLowerCase();
36
- return zone.components.flatMap((uid) => {
90
+ return pickable(zone, config).flatMap((uid) => {
37
91
  const schema = components[uid];
38
92
  if (!schema) return [];
39
93
  const meta = config.components?.[uid] || {};
@@ -48,11 +102,13 @@ function entriesFor(zone, components, config, query, category = "all") {
48
102
  };
49
103
  if (category !== "all" && entry.category !== category) return [];
50
104
  return `${entry.label} ${entry.description} ${entry.category} ${uid} ${meta.keywords || ""}`.toLocaleLowerCase().includes(needle) ? [entry] : [];
51
- }).sort((a, b) => byCategory(a.category, b.category) || a.label.localeCompare(b.label));
105
+ }).sort(
106
+ (a, b) => byCategory(a.category, b.category) || a.label.localeCompare(b.label)
107
+ );
52
108
  }
53
109
  function categoriesFor(zone, components, config) {
54
110
  const counts = /* @__PURE__ */ new Map();
55
- for (const uid of zone.components) {
111
+ for (const uid of pickable(zone, config)) {
56
112
  const schema = components[uid];
57
113
  if (!schema) continue;
58
114
  const c = categoryOf(uid, schema, config.components?.[uid] || {});
@@ -62,8 +118,12 @@ function categoriesFor(zone, components, config) {
62
118
  }
63
119
  function groupEntries(entries) {
64
120
  const map = /* @__PURE__ */ new Map();
65
- for (const entry of entries) map.set(entry.category, [...map.get(entry.category) || [], entry]);
66
- return [...map.entries()].map(([category, items]) => ({ category, entries: items }));
121
+ for (const entry of entries)
122
+ map.set(entry.category, [...map.get(entry.category) || [], entry]);
123
+ return [...map.entries()].map(([category, items]) => ({
124
+ category,
125
+ entries: items
126
+ }));
67
127
  }
68
128
  function memoryKey({ base = "", userId, contentType, zone }) {
69
129
  if (!userId || !contentType || !zone) return null;
@@ -79,7 +139,8 @@ function readMemory(storage2, key) {
79
139
  }
80
140
  function writeMemory(storage2, key, value) {
81
141
  try {
82
- if (key && (value === "open" || value === "closed")) storage2?.setItem(key, value);
142
+ if (key && (value === "open" || value === "closed"))
143
+ storage2?.setItem(key, value);
83
144
  } catch {
84
145
  }
85
146
  }
@@ -165,6 +226,10 @@ function Wireframe({ template, palette, className, noPreview }) {
165
226
  );
166
227
  }
167
228
 
229
+ // admin/RowPreviews.tsx
230
+ import * as React from "react";
231
+ import { createPortal } from "react-dom";
232
+
168
233
  // admin/messages.ts
169
234
  import { useIntl } from "react-intl";
170
235
 
@@ -279,7 +344,8 @@ var en_default = {
279
344
  columns: "Columns",
280
345
  noPreview: "no preview",
281
346
  "templates.none": "No preview",
282
- noChanges: "No unsaved changes"
347
+ noChanges: "No unsaved changes",
348
+ rowPreview: "Open the {label} preview"
283
349
  };
284
350
 
285
351
  // admin/translations/pt-BR.json
@@ -393,7 +459,8 @@ var pt_BR_default = {
393
459
  columns: "Colunas",
394
460
  noPreview: "sem preview",
395
461
  "templates.none": "Sem preview",
396
- noChanges: "Sem altera\xE7\xF5es pendentes"
462
+ noChanges: "Sem altera\xE7\xF5es pendentes",
463
+ rowPreview: "Abrir a pr\xE9via de {label}"
397
464
  };
398
465
 
399
466
  // admin/translations/pt.json
@@ -507,7 +574,8 @@ var pt_default = {
507
574
  columns: "Colunas",
508
575
  noPreview: "sem pr\xE9-visualiza\xE7\xE3o",
509
576
  "templates.none": "Sem pr\xE9-visualiza\xE7\xE3o",
510
- noChanges: "Sem altera\xE7\xF5es pendentes"
577
+ noChanges: "Sem altera\xE7\xF5es pendentes",
578
+ rowPreview: "Abrir a pr\xE9-visualiza\xE7\xE3o de {label}"
511
579
  };
512
580
 
513
581
  // admin/translations/fr.json
@@ -621,7 +689,8 @@ var fr_default = {
621
689
  columns: "Colonnes",
622
690
  noPreview: "pas d\u2019aper\xE7u",
623
691
  "templates.none": "Pas d\u2019aper\xE7u",
624
- noChanges: "Aucune modification en attente"
692
+ noChanges: "Aucune modification en attente",
693
+ rowPreview: "Ouvrir l\u2019aper\xE7u de {label}"
625
694
  };
626
695
 
627
696
  // admin/translations/es.json
@@ -735,7 +804,8 @@ var es_default = {
735
804
  columns: "Columnas",
736
805
  noPreview: "sin vista previa",
737
806
  "templates.none": "Sin vista previa",
738
- noChanges: "Sin cambios pendientes"
807
+ noChanges: "Sin cambios pendientes",
808
+ rowPreview: "Abrir la vista previa de {label}"
739
809
  };
740
810
 
741
811
  // admin/translations/de.json
@@ -849,7 +919,8 @@ var de_default = {
849
919
  columns: "Spalten",
850
920
  noPreview: "keine Vorschau",
851
921
  "templates.none": "Keine Vorschau",
852
- noChanges: "Keine ungespeicherten \xC4nderungen"
922
+ noChanges: "Keine ungespeicherten \xC4nderungen",
923
+ rowPreview: "Vorschau von {label} \xF6ffnen"
853
924
  };
854
925
 
855
926
  // admin/translations/it.json
@@ -963,7 +1034,8 @@ var it_default = {
963
1034
  columns: "Colonne",
964
1035
  noPreview: "nessuna anteprima",
965
1036
  "templates.none": "Nessuna anteprima",
966
- noChanges: "Nessuna modifica in sospeso"
1037
+ noChanges: "Nessuna modifica in sospeso",
1038
+ rowPreview: "Apri l\u2019anteprima di {label}"
967
1039
  };
968
1040
 
969
1041
  // admin/translations/nl.json
@@ -1077,7 +1149,8 @@ var nl_default = {
1077
1149
  columns: "Kolommen",
1078
1150
  noPreview: "geen voorbeeld",
1079
1151
  "templates.none": "Geen voorbeeld",
1080
- noChanges: "Geen niet-opgeslagen wijzigingen"
1152
+ noChanges: "Geen niet-opgeslagen wijzigingen",
1153
+ rowPreview: "Voorbeeld van {label} openen"
1081
1154
  };
1082
1155
 
1083
1156
  // admin/messages.ts
@@ -1097,26 +1170,224 @@ function useMessages() {
1097
1170
  return out;
1098
1171
  }
1099
1172
 
1173
+ // admin/RowPreviews.tsx
1174
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
1175
+ var ATTR = "data-blockscene-row-thumb";
1176
+ function useAnchors(zones, onOpen) {
1177
+ const [anchors, setAnchors] = React.useState([]);
1178
+ const open = React.useRef(onOpen);
1179
+ open.current = onOpen;
1180
+ const latest = React.useRef(zones);
1181
+ latest.current = zones;
1182
+ const sync = React.useRef(() => {
1183
+ });
1184
+ sync.current = () => {
1185
+ const next = [];
1186
+ for (const zone of latest.current) {
1187
+ const list = zone.uids?.length ? findZoneList(zone.label) : null;
1188
+ if (!list) continue;
1189
+ toggles(list).forEach((button, index) => {
1190
+ const uid = zone.uids[index];
1191
+ if (!uid) return;
1192
+ let el = button.querySelector(
1193
+ `:scope > [${ATTR}]`
1194
+ );
1195
+ if (!el) {
1196
+ el = document.createElement("span");
1197
+ el.setAttribute(ATTR, "");
1198
+ el.style.cssText = "display:inline-flex;align-items:center";
1199
+ const activate = (event) => {
1200
+ if (!event.currentTarget.getAttribute("role"))
1201
+ return;
1202
+ event.preventDefault();
1203
+ event.stopPropagation();
1204
+ open.current(
1205
+ String(event.currentTarget.dataset.uid || "")
1206
+ );
1207
+ };
1208
+ el.addEventListener("click", activate, true);
1209
+ el.addEventListener(
1210
+ "keydown",
1211
+ (event) => {
1212
+ if (event.key === "Enter" || event.key === " ") activate(event);
1213
+ },
1214
+ true
1215
+ );
1216
+ button.insertBefore(el, button.firstChild);
1217
+ }
1218
+ el.dataset.uid = uid;
1219
+ next.push({ el, uid, zone: zone.name, index });
1220
+ });
1221
+ }
1222
+ for (const el of document.querySelectorAll(`[${ATTR}]`))
1223
+ if (!next.some((a) => a.el === el)) el.remove();
1224
+ setAnchors(
1225
+ (prev) => prev.length === next.length && prev.every((a, i) => a.el === next[i].el && a.uid === next[i].uid) ? prev : next
1226
+ );
1227
+ };
1228
+ React.useEffect(() => {
1229
+ sync.current();
1230
+ });
1231
+ React.useEffect(() => {
1232
+ let frame = 0;
1233
+ const observer = new MutationObserver(() => {
1234
+ if (frame) return;
1235
+ frame = requestAnimationFrame(() => {
1236
+ frame = 0;
1237
+ sync.current();
1238
+ });
1239
+ });
1240
+ observer.observe(document.body, { childList: true, subtree: true });
1241
+ return () => {
1242
+ observer.disconnect();
1243
+ cancelAnimationFrame(frame);
1244
+ for (const el of document.querySelectorAll(`[${ATTR}]`)) el.remove();
1245
+ };
1246
+ }, []);
1247
+ return anchors;
1248
+ }
1249
+ function RowPreviews({ zones, components, catalog, Modal: Modal2 }) {
1250
+ const t = useMessages();
1251
+ const [resolved, setResolved] = React.useState({});
1252
+ const [open, setOpen] = React.useState(null);
1253
+ const anchors = useAnchors(zones, (uid) => setOpen(uid));
1254
+ const candidates = (uid) => candidatesFor(uid, catalog?.components?.[uid] || {}, catalog || {});
1255
+ const label = (uid) => catalog?.components?.[uid]?.label || components?.[uid]?.info?.displayName || uid;
1256
+ const srcOf = (uid) => candidates(uid)[resolved[uid] ?? -1] || "";
1257
+ React.useEffect(() => {
1258
+ for (const { el, uid } of anchors) {
1259
+ if (srcOf(uid)) {
1260
+ el.setAttribute("role", "button");
1261
+ el.setAttribute("tabindex", "0");
1262
+ el.setAttribute("aria-label", t.f("rowPreview", { label: label(uid) }));
1263
+ el.style.cursor = "zoom-in";
1264
+ } else
1265
+ for (const name of ["role", "tabindex", "aria-label"])
1266
+ el.removeAttribute(name);
1267
+ }
1268
+ });
1269
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1270
+ anchors.map(
1271
+ (a) => createPortal(
1272
+ /* @__PURE__ */ jsx2(
1273
+ "span",
1274
+ {
1275
+ style: {
1276
+ display: "block",
1277
+ width: "6.4rem",
1278
+ marginRight: "8px",
1279
+ flex: "0 0 auto"
1280
+ },
1281
+ children: /* @__PURE__ */ jsx2(
1282
+ Thumb,
1283
+ {
1284
+ candidates: candidates(a.uid),
1285
+ blank: true,
1286
+ onResolved: (index) => setResolved(
1287
+ (prev) => prev[a.uid] === index ? prev : { ...prev, [a.uid]: index }
1288
+ )
1289
+ }
1290
+ )
1291
+ }
1292
+ ),
1293
+ a.el,
1294
+ `${a.zone}:${a.index}`
1295
+ )
1296
+ ),
1297
+ open && srcOf(open) && /* @__PURE__ */ jsx2(
1298
+ Modal2,
1299
+ {
1300
+ open: true,
1301
+ onOpenChange: (value) => {
1302
+ if (!value) setOpen(null);
1303
+ },
1304
+ trigger: null,
1305
+ title: label(open),
1306
+ children: /* @__PURE__ */ jsx2(
1307
+ "img",
1308
+ {
1309
+ src: srcOf(open),
1310
+ alt: "",
1311
+ style: {
1312
+ display: "block",
1313
+ margin: "0 auto",
1314
+ maxWidth: "100%",
1315
+ maxHeight: "75vh",
1316
+ objectFit: "contain"
1317
+ }
1318
+ }
1319
+ )
1320
+ }
1321
+ )
1322
+ ] });
1323
+ }
1324
+
1100
1325
  // admin/Gallery.tsx
1101
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1326
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1102
1327
  var Columns = styled.div`
1103
- column-count: ${({ $columns }) => $columns}; column-gap: 12px;
1104
- > * { break-inside: avoid; -webkit-column-break-inside: avoid; margin-bottom: 12px; }
1328
+ column-count: ${({ $columns }) => $columns};
1329
+ column-gap: 12px;
1330
+ > * {
1331
+ break-inside: avoid;
1332
+ -webkit-column-break-inside: avoid;
1333
+ margin-bottom: 12px;
1334
+ }
1105
1335
  `;
1106
1336
  var Tile = styled.button`
1107
- display: flex; flex-direction: column; gap: 8px; text-align: left; width: 100%;
1108
- padding: 8px; border: 1px solid ${({ theme }) => theme.colors.neutral200};
1109
- border-radius: 4px; background: ${({ theme }) => theme.colors.neutral0}; cursor: pointer;
1110
- &:hover { border-color: ${({ theme }) => theme.colors.primary600}; }
1111
- &:focus-visible { outline: 2px solid ${({ theme }) => theme.colors.primary600}; outline-offset: 2px; }
1337
+ display: flex;
1338
+ flex-direction: column;
1339
+ gap: 8px;
1340
+ text-align: left;
1341
+ width: 100%;
1342
+ padding: 8px;
1343
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
1344
+ border-radius: 4px;
1345
+ background: ${({ theme }) => theme.colors.neutral0};
1346
+ cursor: pointer;
1347
+ &:hover {
1348
+ border-color: ${({ theme }) => theme.colors.primary600};
1349
+ }
1350
+ &:focus-visible {
1351
+ outline: 2px solid ${({ theme }) => theme.colors.primary600};
1352
+ outline-offset: 2px;
1353
+ }
1354
+ `;
1355
+ var Clamp = styled(Typography)`
1356
+ display: -webkit-box;
1357
+ -webkit-line-clamp: 2;
1358
+ -webkit-box-orient: vertical;
1359
+ overflow: hidden;
1112
1360
  `;
1113
- var Clamp = styled(Typography)` display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; `;
1114
1361
  var Slider = styled.input`
1115
- appearance: none; -webkit-appearance: none; width: 8rem; height: 0.4rem; border-radius: 0.4rem; cursor: pointer;
1362
+ appearance: none;
1363
+ -webkit-appearance: none;
1364
+ width: 8rem;
1365
+ height: 0.4rem;
1366
+ border-radius: 0.4rem;
1367
+ cursor: pointer;
1116
1368
  background: ${({ theme, $percent }) => `linear-gradient(to right, ${theme.colors.primary600} ${$percent}%, ${theme.colors.neutral200} ${$percent}%)`};
1117
- &::-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; }
1118
- &::-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; }
1119
- &:focus-visible { outline: 0.2rem solid ${({ theme }) => theme.colors.primary600}; outline-offset: 0.2rem; }
1369
+ &::-webkit-slider-thumb {
1370
+ appearance: none;
1371
+ -webkit-appearance: none;
1372
+ width: 1.6rem;
1373
+ height: 1.6rem;
1374
+ border-radius: 50%;
1375
+ background: ${({ theme }) => theme.colors.neutral0};
1376
+ border: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1377
+ cursor: pointer;
1378
+ }
1379
+ &::-moz-range-thumb {
1380
+ width: 1.6rem;
1381
+ height: 1.6rem;
1382
+ border-radius: 50%;
1383
+ background: ${({ theme }) => theme.colors.neutral0};
1384
+ border: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1385
+ cursor: pointer;
1386
+ }
1387
+ &:focus-visible {
1388
+ outline: 0.2rem solid ${({ theme }) => theme.colors.primary600};
1389
+ outline-offset: 0.2rem;
1390
+ }
1120
1391
  `;
1121
1392
  var COLUMNS = { key: "blockscene:columns", min: 1, max: 5, initial: 3 };
1122
1393
  var SHOW_FIELDS_KEY = "blockscene:show-fields";
@@ -1142,16 +1413,31 @@ var remember = (key, value) => {
1142
1413
  }
1143
1414
  };
1144
1415
  var Frame = styled.div`
1145
- width: 100%; overflow: hidden; border-radius: 4px; line-height: 0;
1416
+ width: 100%;
1417
+ overflow: hidden;
1418
+ border-radius: 4px;
1419
+ line-height: 0;
1146
1420
  aspect-ratio: ${({ $fixed }) => $fixed ? "16 / 9" : "auto"};
1147
1421
  background: ${({ theme }) => theme.colors.neutral100};
1148
- img { width: 100%; height: auto; display: block; }
1422
+ img {
1423
+ width: 100%;
1424
+ height: auto;
1425
+ display: block;
1426
+ }
1149
1427
  `;
1150
- function Thumb({ candidates, template, palette, onResolved, noPreview, eager }) {
1428
+ function Thumb({
1429
+ candidates,
1430
+ template,
1431
+ palette,
1432
+ onResolved,
1433
+ noPreview,
1434
+ eager,
1435
+ blank
1436
+ }) {
1151
1437
  const key = candidates.join("|");
1152
- const [state, setState] = React.useState({ key, index: 0 });
1438
+ const [state, setState] = React2.useState({ key, index: 0 });
1153
1439
  const index = state.key === key ? state.index : 0;
1154
- const resolved = React.useRef("");
1440
+ const resolved = React2.useRef("");
1155
1441
  const report = (value) => {
1156
1442
  const tag = `${key}:${value}`;
1157
1443
  if (resolved.current !== tag) {
@@ -1159,11 +1445,19 @@ function Thumb({ candidates, template, palette, onResolved, noPreview, eager })
1159
1445
  onResolved?.(value);
1160
1446
  }
1161
1447
  };
1162
- React.useEffect(() => {
1448
+ React2.useEffect(() => {
1163
1449
  if (index >= candidates.length) report(-1);
1164
1450
  });
1165
- if (index >= candidates.length) return /* @__PURE__ */ jsx2(Frame, { $fixed: true, "data-thumb": "wireframe", children: /* @__PURE__ */ jsx2(Wireframe, { template, palette, noPreview }) });
1166
- return /* @__PURE__ */ jsx2(Frame, { "data-thumb": String(index), children: /* @__PURE__ */ jsx2(
1451
+ if (index >= candidates.length)
1452
+ return blank ? null : /* @__PURE__ */ jsx3(Frame, { $fixed: true, "data-thumb": "wireframe", children: /* @__PURE__ */ jsx3(
1453
+ Wireframe,
1454
+ {
1455
+ template,
1456
+ palette,
1457
+ noPreview
1458
+ }
1459
+ ) });
1460
+ return /* @__PURE__ */ jsx3(Frame, { "data-thumb": String(index), children: /* @__PURE__ */ jsx3(
1167
1461
  "img",
1168
1462
  {
1169
1463
  src: candidates[index],
@@ -1177,25 +1471,68 @@ function Thumb({ candidates, template, palette, onResolved, noPreview, eager })
1177
1471
  }
1178
1472
  function Card({ entry, palette, showFields, onSelect }) {
1179
1473
  const t = useMessages();
1180
- return /* @__PURE__ */ jsxs(Tile, { type: "button", onClick: onSelect, title: entry.uid, "data-testid": `blockscene-${entry.uid}`, children: [
1181
- /* @__PURE__ */ jsx2(Thumb, { candidates: entry.candidates, template: entry.template, palette, noPreview: t.noPreview }),
1182
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", ellipsis: true, children: entry.label }),
1183
- entry.description && /* @__PURE__ */ jsx2(Clamp, { variant: "pi", textColor: "neutral600", children: entry.description }),
1184
- showFields && entry.fields.length > 0 && /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral500", style: { fontStyle: "italic" }, "data-testid": "block-fields", children: entry.fields.map((f) => `${f.name} (${f.type})`).join(", ") })
1185
- ] });
1474
+ return /* @__PURE__ */ jsxs2(
1475
+ Tile,
1476
+ {
1477
+ type: "button",
1478
+ onClick: onSelect,
1479
+ title: entry.uid,
1480
+ "data-testid": `blockscene-${entry.uid}`,
1481
+ children: [
1482
+ /* @__PURE__ */ jsx3(
1483
+ Thumb,
1484
+ {
1485
+ candidates: entry.candidates,
1486
+ template: entry.template,
1487
+ palette,
1488
+ noPreview: t.noPreview
1489
+ }
1490
+ ),
1491
+ /* @__PURE__ */ jsx3(
1492
+ Typography,
1493
+ {
1494
+ variant: "pi",
1495
+ fontWeight: "bold",
1496
+ textColor: "neutral800",
1497
+ ellipsis: true,
1498
+ children: entry.label
1499
+ }
1500
+ ),
1501
+ entry.description && /* @__PURE__ */ jsx3(Clamp, { variant: "pi", textColor: "neutral600", children: entry.description }),
1502
+ showFields && entry.fields.length > 0 && /* @__PURE__ */ jsx3(
1503
+ Typography,
1504
+ {
1505
+ variant: "pi",
1506
+ textColor: "neutral500",
1507
+ style: { fontStyle: "italic" },
1508
+ "data-testid": "block-fields",
1509
+ children: entry.fields.map((f) => `${f.name} (${f.type})`).join(", ")
1510
+ }
1511
+ )
1512
+ ]
1513
+ }
1514
+ );
1186
1515
  }
1187
- function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, controlled }) {
1516
+ function ZoneGallery({
1517
+ zone,
1518
+ components,
1519
+ add,
1520
+ Modal: Modal2,
1521
+ Toggle,
1522
+ get,
1523
+ controlled
1524
+ }) {
1188
1525
  const t = useMessages();
1189
- const [ownOpen, setOwnOpen] = React.useState(false);
1526
+ const [ownOpen, setOwnOpen] = React2.useState(false);
1190
1527
  const open = controlled ? controlled.open : ownOpen;
1191
1528
  const setOpen = controlled ? controlled.onOpenChange : setOwnOpen;
1192
- const [query, setQuery] = React.useState("");
1193
- const [category, setCategory] = React.useState("all");
1194
- const [showFields, setShowFields] = React.useState(readShowFields);
1195
- const [columns, setColumns] = React.useState(readColumns);
1196
- const [config, setConfig] = React.useState(null);
1197
- const [error, setError] = React.useState("");
1198
- React.useEffect(() => {
1529
+ const [query, setQuery] = React2.useState("");
1530
+ const [category, setCategory] = React2.useState("all");
1531
+ const [showFields, setShowFields] = React2.useState(readShowFields);
1532
+ const [columns, setColumns] = React2.useState(readColumns);
1533
+ const [config, setConfig] = React2.useState(null);
1534
+ const [error, setError] = React2.useState("");
1535
+ React2.useEffect(() => {
1199
1536
  if (controlled) return;
1200
1537
  const onClick = (event) => {
1201
1538
  const button = event.target?.closest?.("button");
@@ -1207,7 +1544,7 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1207
1544
  document.addEventListener("click", onClick, true);
1208
1545
  return () => document.removeEventListener("click", onClick, true);
1209
1546
  }, [controlled, zone.name, zone.label]);
1210
- React.useEffect(() => {
1547
+ React2.useEffect(() => {
1211
1548
  if (!open) return;
1212
1549
  let active = true;
1213
1550
  setError("");
@@ -1223,7 +1560,10 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1223
1560
  }, [open, get, t.failed]);
1224
1561
  const entries = config ? entriesFor(zone, components, config, query, category) : [];
1225
1562
  const categories = config ? categoriesFor(zone, components, config) : [];
1226
- const total = categories.reduce((sum, [, count]) => sum + count, 0);
1563
+ const total = categories.reduce(
1564
+ (sum, [, count]) => sum + count,
1565
+ 0
1566
+ );
1227
1567
  const filtering = query.trim() !== "" || category !== "all";
1228
1568
  const reset = () => {
1229
1569
  setQuery("");
@@ -1247,21 +1587,41 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1247
1587
  setShowFields(value);
1248
1588
  remember(SHOW_FIELDS_KEY, String(value));
1249
1589
  };
1250
- return /* @__PURE__ */ jsx2(
1590
+ return /* @__PURE__ */ jsx3(
1251
1591
  Modal2,
1252
1592
  {
1253
1593
  open,
1254
1594
  onOpenChange: setOpen,
1255
1595
  title: t.gallery,
1256
- trigger: controlled ? null : /* @__PURE__ */ jsxs(Button, { variant: "secondary", fullWidth: true, onClick: () => setOpen(true), "data-testid": `open-gallery-${zone.name}`, children: [
1257
- t.add,
1258
- ": ",
1259
- zone.name
1260
- ] }),
1261
- children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1262
- /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1263
- /* @__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 }) }),
1264
- /* @__PURE__ */ jsx2(Box, { style: { flex: 1, minWidth: "12rem" }, children: /* @__PURE__ */ jsx2(
1596
+ trigger: controlled ? null : /* @__PURE__ */ jsxs2(
1597
+ Button,
1598
+ {
1599
+ variant: "secondary",
1600
+ fullWidth: true,
1601
+ onClick: () => setOpen(true),
1602
+ "data-testid": `open-gallery-${zone.name}`,
1603
+ children: [
1604
+ t.add,
1605
+ ": ",
1606
+ zone.name
1607
+ ]
1608
+ }
1609
+ ),
1610
+ children: /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1611
+ /* @__PURE__ */ jsxs2(Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1612
+ /* @__PURE__ */ jsx3(
1613
+ Typography,
1614
+ {
1615
+ variant: "omega",
1616
+ fontWeight: "bold",
1617
+ textColor: "neutral800",
1618
+ style: { whiteSpace: "nowrap" },
1619
+ "data-testid": `block-count-${zone.name}`,
1620
+ "aria-live": "polite",
1621
+ children: filtering ? t.f("countFiltered", { shown: entries.length, total }) : t.f("countAll", { count: total })
1622
+ }
1623
+ ),
1624
+ /* @__PURE__ */ jsx3(Box, { style: { flex: 1, minWidth: "12rem" }, children: /* @__PURE__ */ jsx3(
1265
1625
  Searchbar,
1266
1626
  {
1267
1627
  name: `block-search-${zone.name}`,
@@ -1273,14 +1633,54 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1273
1633
  children: t.search
1274
1634
  }
1275
1635
  ) }),
1276
- 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: [
1277
- /* @__PURE__ */ jsx2(SingleSelectOption, { value: "all", children: t.allCategories }),
1278
- categories.map(([name, count]) => /* @__PURE__ */ jsx2(SingleSelectOption, { value: name, children: `${name} (${count})` }, name))
1279
- ] }) }),
1280
- Toggle && /* @__PURE__ */ jsx2(Toggle, { name: `block-fields-${zone.name}`, label: t.showFields, hint: t.showFieldsHint, value: showFields, onChange: changeShowFields }),
1281
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
1282
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral600", id: `block-columns-${zone.name}`, children: t.columns }),
1283
- /* @__PURE__ */ jsx2(
1636
+ categories.length > 1 && /* @__PURE__ */ jsx3(
1637
+ Box,
1638
+ {
1639
+ style: { width: "14rem" },
1640
+ "data-testid": `block-category-${zone.name}`,
1641
+ children: /* @__PURE__ */ jsxs2(
1642
+ SingleSelect,
1643
+ {
1644
+ "aria-label": t.filterCategory,
1645
+ size: "S",
1646
+ value: category,
1647
+ onChange: (value) => setCategory(String(value)),
1648
+ children: [
1649
+ /* @__PURE__ */ jsx3(SingleSelectOption, { value: "all", children: t.allCategories }),
1650
+ categories.map(([name, count]) => /* @__PURE__ */ jsx3(
1651
+ SingleSelectOption,
1652
+ {
1653
+ value: name,
1654
+ children: `${name} (${count})`
1655
+ },
1656
+ name
1657
+ ))
1658
+ ]
1659
+ }
1660
+ )
1661
+ }
1662
+ ),
1663
+ Toggle && /* @__PURE__ */ jsx3(
1664
+ Toggle,
1665
+ {
1666
+ name: `block-fields-${zone.name}`,
1667
+ label: t.showFields,
1668
+ hint: t.showFieldsHint,
1669
+ value: showFields,
1670
+ onChange: changeShowFields
1671
+ }
1672
+ ),
1673
+ /* @__PURE__ */ jsxs2(Flex, { gap: 2, alignItems: "center", children: [
1674
+ /* @__PURE__ */ jsx3(
1675
+ Typography,
1676
+ {
1677
+ variant: "pi",
1678
+ textColor: "neutral600",
1679
+ id: `block-columns-${zone.name}`,
1680
+ children: t.columns
1681
+ }
1682
+ ),
1683
+ /* @__PURE__ */ jsx3(
1284
1684
  Slider,
1285
1685
  {
1286
1686
  type: "range",
@@ -1293,16 +1693,52 @@ function ZoneGallery({ zone, components, add, Modal: Modal2, Toggle, get, contro
1293
1693
  onChange: (e) => changeColumns(Number(e.target.value))
1294
1694
  }
1295
1695
  ),
1296
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", fontWeight: "bold", children: columns })
1696
+ /* @__PURE__ */ jsx3(Typography, { variant: "pi", fontWeight: "bold", children: columns })
1297
1697
  ] })
1298
1698
  ] }),
1299
- /* @__PURE__ */ jsx2(Typography, { variant: "pi", textColor: "neutral600", children: t.example }),
1300
- 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: [
1301
- /* @__PURE__ */ jsx2(Typography, { variant: "sigma", textColor: "neutral600", "data-testid": `block-group-${name}`, children: `${name} (${items.length})` }),
1302
- /* @__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)) })
1303
- ] }, name)) }) : /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", children: [
1304
- /* @__PURE__ */ jsx2(Typography, { children: t.empty }),
1305
- filtering && /* @__PURE__ */ jsx2(Button, { variant: "tertiary", size: "S", onClick: reset, children: t.clear })
1699
+ /* @__PURE__ */ jsx3(Typography, { variant: "pi", textColor: "neutral600", children: t.example }),
1700
+ error ? /* @__PURE__ */ jsx3(Typography, { role: "alert", textColor: "danger600", children: error }) : !config ? /* @__PURE__ */ jsx3(Typography, { children: t.loading }) : entries.length ? /* @__PURE__ */ jsx3(
1701
+ Flex,
1702
+ {
1703
+ direction: "column",
1704
+ alignItems: "stretch",
1705
+ gap: 4,
1706
+ style: { overflowY: "auto", maxHeight: "60vh", padding: "4px" },
1707
+ children: groupEntries(entries).map(
1708
+ ({ category: name, entries: items }) => /* @__PURE__ */ jsxs2(
1709
+ Flex,
1710
+ {
1711
+ direction: "column",
1712
+ alignItems: "stretch",
1713
+ gap: 2,
1714
+ children: [
1715
+ /* @__PURE__ */ jsx3(
1716
+ Typography,
1717
+ {
1718
+ variant: "sigma",
1719
+ textColor: "neutral600",
1720
+ "data-testid": `block-group-${name}`,
1721
+ children: `${name} (${items.length})`
1722
+ }
1723
+ ),
1724
+ /* @__PURE__ */ jsx3(Columns, { $columns: columns, children: items.map((entry) => /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
1725
+ Card,
1726
+ {
1727
+ entry,
1728
+ palette: config.palette,
1729
+ showFields,
1730
+ onSelect: () => select(entry.uid)
1731
+ }
1732
+ ) }, entry.uid)) })
1733
+ ]
1734
+ },
1735
+ name
1736
+ )
1737
+ )
1738
+ }
1739
+ ) : /* @__PURE__ */ jsxs2(Flex, { gap: 3, alignItems: "center", children: [
1740
+ /* @__PURE__ */ jsx3(Typography, { children: t.empty }),
1741
+ filtering && /* @__PURE__ */ jsx3(Button, { variant: "tertiary", size: "S", onClick: reset, children: t.clear })
1306
1742
  ] })
1307
1743
  ] })
1308
1744
  }
@@ -1316,13 +1752,19 @@ var storage = () => {
1316
1752
  }
1317
1753
  };
1318
1754
  var memoryBase = () => `${window.location.origin}${window.location.pathname.split("/content-manager")[0]}`;
1319
- function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1320
- const applied = React.useRef("");
1321
- const user = React.useRef(userId);
1755
+ function useInitialAccordions({
1756
+ zones,
1757
+ editor,
1758
+ docKey,
1759
+ contentType,
1760
+ userId
1761
+ }) {
1762
+ const applied = React2.useRef("");
1763
+ const user = React2.useRef(userId);
1322
1764
  user.current = userId;
1323
- const latest = React.useRef(zones);
1765
+ const latest = React2.useRef(zones);
1324
1766
  latest.current = zones;
1325
- React.useEffect(() => {
1767
+ React2.useEffect(() => {
1326
1768
  if (!editor?.enabled || !docKey || applied.current === docKey) return;
1327
1769
  if (applied.current.startsWith(`${contentType}:new`) && docKey.startsWith(`${contentType}:`)) {
1328
1770
  applied.current = docKey;
@@ -1331,7 +1773,10 @@ function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1331
1773
  applied.current = docKey;
1332
1774
  let tries = 0;
1333
1775
  const cancel = (event) => {
1334
- if (event.target?.closest?.('button[aria-expanded], [data-testid^="block-accordion-controls-"]')) stop();
1776
+ if (event.target?.closest?.(
1777
+ 'button[aria-expanded], [data-testid^="block-accordion-controls-"]'
1778
+ ))
1779
+ stop();
1335
1780
  };
1336
1781
  const stop = () => {
1337
1782
  clearInterval(timer);
@@ -1349,8 +1794,17 @@ function useInitialAccordions({ zones, editor, docKey, contentType, userId }) {
1349
1794
  for (const zone of pending) {
1350
1795
  const list = findZoneList(zone.label);
1351
1796
  if (!list) continue;
1352
- const key = memoryKey({ base: memoryBase(), userId: user.current, contentType, zone: zone.name });
1353
- setAll(list, initialState(editor, readMemory(storage(), key)) === "open", unstable_batchedUpdates);
1797
+ const key = memoryKey({
1798
+ base: memoryBase(),
1799
+ userId: user.current,
1800
+ contentType,
1801
+ zone: zone.name
1802
+ });
1803
+ setAll(
1804
+ list,
1805
+ initialState(editor, readMemory(storage(), key)) === "open",
1806
+ unstable_batchedUpdates
1807
+ );
1354
1808
  }
1355
1809
  }, 200);
1356
1810
  return stop;
@@ -1362,26 +1816,83 @@ function ZoneControls({ zone, editor, contentType, userId }) {
1362
1816
  const apply = (open) => {
1363
1817
  const list = findZoneList(zone.label);
1364
1818
  if (list) setAll(list, open, unstable_batchedUpdates);
1365
- writeMemory(storage(), memoryKey({ base: memoryBase(), userId, contentType, zone: zone.name }), open ? "open" : "closed");
1819
+ writeMemory(
1820
+ storage(),
1821
+ memoryKey({ base: memoryBase(), userId, contentType, zone: zone.name }),
1822
+ open ? "open" : "closed"
1823
+ );
1366
1824
  };
1367
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", "data-testid": `block-accordion-controls-${zone.name}`, children: [
1368
- editor.showOpenAll && /* @__PURE__ */ jsx2(Button, { type: "button", variant: "tertiary", size: "S", onClick: () => apply(true), children: t.openAll }),
1369
- editor.showCloseAll && /* @__PURE__ */ jsx2(Button, { type: "button", variant: "tertiary", size: "S", onClick: () => apply(false), children: t.closeAll })
1370
- ] });
1825
+ return /* @__PURE__ */ jsxs2(
1826
+ Flex,
1827
+ {
1828
+ gap: 2,
1829
+ wrap: "wrap",
1830
+ "data-testid": `block-accordion-controls-${zone.name}`,
1831
+ children: [
1832
+ editor.showOpenAll && /* @__PURE__ */ jsx3(
1833
+ Button,
1834
+ {
1835
+ type: "button",
1836
+ variant: "tertiary",
1837
+ size: "S",
1838
+ onClick: () => apply(true),
1839
+ children: t.openAll
1840
+ }
1841
+ ),
1842
+ editor.showCloseAll && /* @__PURE__ */ jsx3(
1843
+ Button,
1844
+ {
1845
+ type: "button",
1846
+ variant: "tertiary",
1847
+ size: "S",
1848
+ onClick: () => apply(false),
1849
+ children: t.closeAll
1850
+ }
1851
+ )
1852
+ ]
1853
+ }
1854
+ );
1371
1855
  }
1372
- function Gallery({ zones, editor, docKey, contentType, userId, ...props }) {
1856
+ function Gallery({
1857
+ zones,
1858
+ editor,
1859
+ docKey,
1860
+ contentType,
1861
+ userId,
1862
+ catalog,
1863
+ ...props
1864
+ }) {
1373
1865
  useInitialAccordions({ zones, editor, docKey, contentType, userId });
1374
- return /* @__PURE__ */ jsx2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: zones.map((zone) => /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1375
- !zone.full && /* @__PURE__ */ jsx2(ZoneGallery, { zone, ...props }),
1376
- /* @__PURE__ */ jsx2(ZoneControls, { zone, editor, contentType, userId })
1377
- ] }, zone.name)) });
1866
+ return /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1867
+ /* @__PURE__ */ jsx3(
1868
+ RowPreviews,
1869
+ {
1870
+ zones,
1871
+ components: props.components,
1872
+ catalog,
1873
+ Modal: props.Modal
1874
+ }
1875
+ ),
1876
+ zones.map((zone) => /* @__PURE__ */ jsxs2(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1877
+ !zone.full && /* @__PURE__ */ jsx3(ZoneGallery, { zone, ...props }),
1878
+ /* @__PURE__ */ jsx3(
1879
+ ZoneControls,
1880
+ {
1881
+ zone,
1882
+ editor,
1883
+ contentType,
1884
+ userId
1885
+ }
1886
+ )
1887
+ ] }, zone.name))
1888
+ ] });
1378
1889
  }
1379
1890
 
1380
1891
  // admin/Settings.tsx
1381
- import * as React2 from "react";
1892
+ import * as React3 from "react";
1382
1893
  import { Box as Box2, Button as Button2, Flex as Flex2, Searchbar as Searchbar2, Typography as Typography2 } from "@strapi/design-system";
1383
1894
  import styled2 from "styled-components";
1384
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1895
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1385
1896
  var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
1386
1897
  var Grid = styled2.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
1387
1898
  var Card2 = styled2.div`
@@ -1394,8 +1905,8 @@ var Bar = styled2.div`
1394
1905
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
1395
1906
  `;
1396
1907
  function useMainRect(anchor) {
1397
- const [rect, setRect] = React2.useState({ left: 0, width: 0 });
1398
- React2.useEffect(() => {
1908
+ const [rect, setRect] = React3.useState({ left: 0, width: 0 });
1909
+ React3.useEffect(() => {
1399
1910
  const read = () => {
1400
1911
  let host = document.getElementById("main-content");
1401
1912
  for (let el = anchor.current?.parentElement; !host && el; el = el.parentElement) if (["auto", "scroll"].includes(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) host = el;
@@ -1412,11 +1923,11 @@ var Swatches = styled2.div` display: grid; grid-template-columns: repeat(auto-fi
1412
1923
  var Label = styled2.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
1413
1924
  function ColorField({ name, label, value, onChange, disabled }) {
1414
1925
  const valid = COLOR2.test(value);
1415
- return /* @__PURE__ */ jsxs2(Label, { children: [
1416
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
1417
- /* @__PURE__ */ jsxs2(Flex2, { gap: 2, children: [
1418
- /* @__PURE__ */ jsx3("input", { type: "color", "aria-label": label, value: valid ? value : "#000000", disabled, onChange: (e) => onChange(e.target.value.toUpperCase()), style: { width: 40, height: 32, padding: 0, border: 0, background: "none" } }),
1419
- /* @__PURE__ */ jsx3(
1926
+ return /* @__PURE__ */ jsxs3(Label, { children: [
1927
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
1928
+ /* @__PURE__ */ jsxs3(Flex2, { gap: 2, children: [
1929
+ /* @__PURE__ */ jsx4("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" } }),
1930
+ /* @__PURE__ */ jsx4(
1420
1931
  "input",
1421
1932
  {
1422
1933
  name,
@@ -1436,18 +1947,18 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1436
1947
  const t = useMessages();
1437
1948
  const { get, put } = useClient();
1438
1949
  const { canRead, canUpdate, isLoading } = usePermissions2();
1439
- const [data, setData] = React2.useState(null);
1440
- const [catalog, setCatalog] = React2.useState(null);
1441
- const [settings, setSettings] = React2.useState(null);
1442
- const [media, setMedia] = React2.useState({});
1443
- const [sources, setSources] = React2.useState({});
1444
- const [dirty, setDirty] = React2.useState(false);
1445
- const [saving, setSaving] = React2.useState(false);
1446
- const [status, setStatus] = React2.useState("");
1447
- const [failed, setFailed] = React2.useState(false);
1448
- const [filter, setFilter] = React2.useState("");
1449
- const [picking, setPicking] = React2.useState(null);
1450
- React2.useEffect(() => {
1950
+ const [data, setData] = React3.useState(null);
1951
+ const [catalog, setCatalog] = React3.useState(null);
1952
+ const [settings, setSettings] = React3.useState(null);
1953
+ const [media, setMedia] = React3.useState({});
1954
+ const [sources, setSources] = React3.useState({});
1955
+ const [dirty, setDirty] = React3.useState(false);
1956
+ const [saving, setSaving] = React3.useState(false);
1957
+ const [status, setStatus] = React3.useState("");
1958
+ const [failed, setFailed] = React3.useState(false);
1959
+ const [filter, setFilter] = React3.useState("");
1960
+ const [picking, setPicking] = React3.useState(null);
1961
+ React3.useEffect(() => {
1451
1962
  if (!canRead || isLoading) return;
1452
1963
  let active = true;
1453
1964
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -1495,35 +2006,35 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1495
2006
  return index === 0 && manual ? t.manual : t.automatic;
1496
2007
  };
1497
2008
  const ready = !isLoading && canRead && !failed && settings && catalog;
1498
- const header = React2.useRef(null);
1499
- const [floating, setFloating] = React2.useState(false);
2009
+ const header = React3.useRef(null);
2010
+ const [floating, setFloating] = React3.useState(false);
1500
2011
  const main = useMainRect(header);
1501
- React2.useEffect(() => {
2012
+ React3.useEffect(() => {
1502
2013
  const el = header.current;
1503
2014
  if (!el || typeof IntersectionObserver === "undefined") return;
1504
2015
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
1505
2016
  observer.observe(el);
1506
2017
  return () => observer.disconnect();
1507
2018
  }, [ready]);
1508
- const controls = (suffix) => ready && /* @__PURE__ */ jsxs2(Flex2, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1509
- /* @__PURE__ */ jsx3(Typography2, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
1510
- /* @__PURE__ */ jsx3(Button2, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
2019
+ const controls = (suffix) => ready && /* @__PURE__ */ jsxs3(Flex2, { gap: 3, alignItems: "center", wrap: "wrap", children: [
2020
+ /* @__PURE__ */ jsx4(Typography2, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
2021
+ /* @__PURE__ */ jsx4(Button2, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
1511
2022
  ] });
1512
- return /* @__PURE__ */ jsx3(Box2, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 5, children: [
1513
- /* @__PURE__ */ jsx3("div", { ref: header, children: /* @__PURE__ */ jsxs2(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
1514
- /* @__PURE__ */ jsx3(Typography2, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
2023
+ return /* @__PURE__ */ jsx4(Box2, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 5, children: [
2024
+ /* @__PURE__ */ jsx4("div", { ref: header, children: /* @__PURE__ */ jsxs3(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2025
+ /* @__PURE__ */ jsx4(Typography2, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
1515
2026
  controls("")
1516
2027
  ] }) }),
1517
- floating && main.width > 0 && /* @__PURE__ */ jsx3(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs2(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
1518
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "p", children: t.settingsTitle }),
2028
+ floating && main.width > 0 && /* @__PURE__ */ jsx4(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs3(Flex2, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
2029
+ /* @__PURE__ */ jsx4(Typography2, { variant: "beta", tag: "p", children: t.settingsTitle }),
1519
2030
  controls("-floating")
1520
2031
  ] }) }),
1521
- /* @__PURE__ */ jsx3(Typography2, { textColor: "neutral600", children: t.settingsIntro }),
1522
- isLoading ? /* @__PURE__ */ jsx3(Typography2, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx3(Typography2, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx3(Typography2, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx3(Typography2, { children: t.loading }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1523
- data.disabled && /* @__PURE__ */ jsx3(Box2, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx3(Typography2, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
1524
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1525
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.palette }),
1526
- /* @__PURE__ */ jsx3(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx3(
2032
+ /* @__PURE__ */ jsx4(Typography2, { textColor: "neutral600", children: t.settingsIntro }),
2033
+ isLoading ? /* @__PURE__ */ jsx4(Typography2, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx4(Typography2, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx4(Typography2, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx4(Typography2, { children: t.loading }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2034
+ data.disabled && /* @__PURE__ */ jsx4(Box2, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx4(Typography2, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
2035
+ /* @__PURE__ */ jsx4(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
2036
+ /* @__PURE__ */ jsx4(Typography2, { variant: "beta", tag: "h2", children: t.palette }),
2037
+ /* @__PURE__ */ jsx4(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx4(
1527
2038
  ColorField,
1528
2039
  {
1529
2040
  name: `palette-${key}`,
@@ -1537,33 +2048,33 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1537
2048
  },
1538
2049
  key
1539
2050
  )) }),
1540
- invalid && /* @__PURE__ */ jsx3(Typography2, { role: "alert", textColor: "danger600", children: t.invalidColor }),
1541
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
1542
- /* @__PURE__ */ jsx3(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs2(Flex2, { direction: "column", gap: 1, children: [
1543
- /* @__PURE__ */ jsx3(Box2, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx3(Wireframe, { template, palette: settings.palette }) }),
1544
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
2051
+ invalid && /* @__PURE__ */ jsx4(Typography2, { role: "alert", textColor: "danger600", children: t.invalidColor }),
2052
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
2053
+ /* @__PURE__ */ jsx4(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs3(Flex2, { direction: "column", gap: 1, children: [
2054
+ /* @__PURE__ */ jsx4(Box2, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx4(Wireframe, { template, palette: settings.palette }) }),
2055
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
1545
2056
  ] }, template)) }),
1546
- /* @__PURE__ */ jsx3(Flex2, { children: /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
2057
+ /* @__PURE__ */ jsx4(Flex2, { children: /* @__PURE__ */ jsx4(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
1547
2058
  s.palette = { ...DEFAULT_PALETTE };
1548
2059
  return s;
1549
2060
  }), children: t.resetPalette }) })
1550
2061
  ] }) }),
1551
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1552
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.editor }),
1553
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2062
+ /* @__PURE__ */ jsx4(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
2063
+ /* @__PURE__ */ jsx4(Typography2, { variant: "beta", tag: "h2", children: t.editor }),
2064
+ /* @__PURE__ */ jsx4(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1554
2065
  s.editor.enabled = v;
1555
2066
  return s;
1556
2067
  }) }),
1557
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
1558
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2068
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
2069
+ /* @__PURE__ */ jsx4(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1559
2070
  s.editor.showOpenAll = v;
1560
2071
  return s;
1561
2072
  }) }),
1562
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2073
+ /* @__PURE__ */ jsx4(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1563
2074
  s.editor.showCloseAll = v;
1564
2075
  return s;
1565
2076
  }) }),
1566
- /* @__PURE__ */ jsx3(
2077
+ /* @__PURE__ */ jsx4(
1567
2078
  SelectField2,
1568
2079
  {
1569
2080
  name: "editor-initialState",
@@ -1577,9 +2088,9 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1577
2088
  })
1578
2089
  }
1579
2090
  ),
1580
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
1581
- previewSupported ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1582
- /* @__PURE__ */ jsx3(
2091
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
2092
+ previewSupported ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2093
+ /* @__PURE__ */ jsx4(
1583
2094
  TextField,
1584
2095
  {
1585
2096
  name: "editor-previewUrl",
@@ -1593,8 +2104,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1593
2104
  })
1594
2105
  }
1595
2106
  ),
1596
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
1597
- /* @__PURE__ */ jsx3(
2107
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
2108
+ /* @__PURE__ */ jsx4(
1598
2109
  SelectField2,
1599
2110
  {
1600
2111
  name: "editor-previewMode",
@@ -1608,26 +2119,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1608
2119
  })
1609
2120
  }
1610
2121
  ),
1611
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
1612
- data.blockPreviewAvailable && /* @__PURE__ */ jsxs2(Fragment, { children: [
1613
- /* @__PURE__ */ jsx3(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
2122
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
2123
+ data.blockPreviewAvailable && /* @__PURE__ */ jsxs3(Fragment2, { children: [
2124
+ /* @__PURE__ */ jsx4(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
1614
2125
  s.editor.blockPreviewInForm = v;
1615
2126
  return s;
1616
2127
  }) }),
1617
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
2128
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
1618
2129
  ] })
1619
- ] }) : /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
2130
+ ] }) : /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
1620
2131
  ] }) }),
1621
- /* @__PURE__ */ jsx3(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
1622
- /* @__PURE__ */ jsx3(Typography2, { variant: "beta", tag: "h2", children: t.components }),
1623
- /* @__PURE__ */ jsx3(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
1624
- /* @__PURE__ */ jsx3(Typography2, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
1625
- /* @__PURE__ */ jsx3(Grid, { "data-testid": "components-grid", children: components.map((component) => {
2132
+ /* @__PURE__ */ jsx4(Box2, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 4, children: [
2133
+ /* @__PURE__ */ jsx4(Typography2, { variant: "beta", tag: "h2", children: t.components }),
2134
+ /* @__PURE__ */ jsx4(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
2135
+ /* @__PURE__ */ jsx4(Typography2, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
2136
+ /* @__PURE__ */ jsx4(Grid, { "data-testid": "components-grid", children: components.map((component) => {
1626
2137
  const entry = settings.components[component.uid] || {};
1627
2138
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
1628
2139
  const candidates = candidatesFor(component.uid, meta, catalog);
1629
- return /* @__PURE__ */ jsxs2(Card2, { "data-testid": `settings-${component.uid}`, children: [
1630
- /* @__PURE__ */ jsx3(
2140
+ return /* @__PURE__ */ jsxs3(Card2, { "data-testid": `settings-${component.uid}`, children: [
2141
+ /* @__PURE__ */ jsx4(
1631
2142
  Thumb,
1632
2143
  {
1633
2144
  candidates,
@@ -1638,23 +2149,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1638
2149
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
1639
2150
  }
1640
2151
  ),
1641
- /* @__PURE__ */ jsxs2(Flex2, { direction: "column", alignItems: "flex-start", gap: 2, children: [
1642
- /* @__PURE__ */ jsx3(Typography2, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
1643
- /* @__PURE__ */ jsxs2(Typography2, { variant: "pi", textColor: "neutral600", children: [
2152
+ /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "flex-start", gap: 2, children: [
2153
+ /* @__PURE__ */ jsx4(Typography2, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
2154
+ /* @__PURE__ */ jsxs3(Typography2, { variant: "pi", textColor: "neutral600", children: [
1644
2155
  component.uid,
1645
2156
  " \xB7 ",
1646
2157
  component.category
1647
2158
  ] }),
1648
- /* @__PURE__ */ jsxs2(Typography2, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
2159
+ /* @__PURE__ */ jsxs3(Typography2, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
1649
2160
  t.source,
1650
2161
  ": ",
1651
2162
  sourceOf(component.uid, Boolean(meta.manualImage)),
1652
2163
  meta.manualName ? ` (${meta.manualName})` : ""
1653
2164
  ] }),
1654
- meta.manualMissing && /* @__PURE__ */ jsx3(Typography2, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
1655
- /* @__PURE__ */ jsxs2(Flex2, { gap: 2, wrap: "wrap", children: [
1656
- /* @__PURE__ */ jsx3(Button2, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
1657
- entry.mediaId && /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
2165
+ meta.manualMissing && /* @__PURE__ */ jsx4(Typography2, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
2166
+ /* @__PURE__ */ jsxs3(Flex2, { gap: 2, wrap: "wrap", children: [
2167
+ /* @__PURE__ */ jsx4(Button2, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
2168
+ entry.mediaId && /* @__PURE__ */ jsx4(Button2, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
1658
2169
  update((s) => {
1659
2170
  delete s.components[component.uid]?.mediaId;
1660
2171
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -1667,7 +2178,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1667
2178
  });
1668
2179
  }, children: t.useAutomatic })
1669
2180
  ] }),
1670
- /* @__PURE__ */ jsx3(
2181
+ /* @__PURE__ */ jsx4(
1671
2182
  SelectField2,
1672
2183
  {
1673
2184
  name: `template-${component.uid}`,
@@ -1685,7 +2196,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
1685
2196
  ] }, component.uid);
1686
2197
  }) })
1687
2198
  ] }) }),
1688
- picking && /* @__PURE__ */ jsx3(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
2199
+ picking && /* @__PURE__ */ jsx4(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
1689
2200
  const uid = picking;
1690
2201
  setPicking(null);
1691
2202
  if (!asset?.id) return;
@@ -1714,10 +2225,10 @@ function register(app, Component, to) {
1714
2225
  }
1715
2226
 
1716
2227
  // admin/catalog.ts
1717
- import * as React3 from "react";
2228
+ import * as React4 from "react";
1718
2229
  function useCatalog(get) {
1719
- const [catalog, setCatalog] = React3.useState(null);
1720
- React3.useEffect(() => {
2230
+ const [catalog, setCatalog] = React4.useState(null);
2231
+ React4.useEffect(() => {
1721
2232
  let active = true;
1722
2233
  get("/blockscene/catalog").then(({ data }) => {
1723
2234
  if (active) setCatalog(data);
@@ -1732,14 +2243,14 @@ function useCatalog(get) {
1732
2243
  }
1733
2244
 
1734
2245
  // admin/strapi4.tsx
1735
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2246
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1736
2247
  function Modal({ open, onOpenChange, trigger, title, children }) {
1737
- const id = React4.useId();
1738
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2248
+ const id = React5.useId();
2249
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
1739
2250
  trigger,
1740
- open && /* @__PURE__ */ jsxs3(ModalLayout, { onClose: () => onOpenChange(false), labelledBy: id, children: [
1741
- /* @__PURE__ */ jsx4(ModalHeader, { children: /* @__PURE__ */ jsx4(Typography3, { id, variant: "beta", children: title }) }),
1742
- /* @__PURE__ */ jsx4(ModalBody, { children })
2251
+ open && /* @__PURE__ */ jsxs4(ModalLayout, { onClose: () => onOpenChange(false), labelledBy: id, children: [
2252
+ /* @__PURE__ */ jsx5(ModalHeader, { children: /* @__PURE__ */ jsx5(Typography3, { id, variant: "beta", children: title }) }),
2253
+ /* @__PURE__ */ jsx5(ModalBody, { children })
1743
2254
  ] })
1744
2255
  ] });
1745
2256
  }
@@ -1750,17 +2261,37 @@ function Picker() {
1750
2261
  const components = c.allLayoutData?.components || {};
1751
2262
  const schema = c.layout || c.allLayoutData?.contentType;
1752
2263
  const allowed = (c.isCreatingEntry ? c.createActionAllowedFields : c.updateActionAllowedFields) || [];
1753
- const zones = editableZones(schema, c.modifiedData, (name) => allowed.includes(name) && schema?.metadatas?.[name]?.edit?.editable !== false, !c.addComponentToDynamicZone).map((zone) => ({ ...zone, label: schema?.metadatas?.[zone.name]?.edit?.label || zone.name }));
2264
+ const zones = editableZones(
2265
+ schema,
2266
+ c.modifiedData,
2267
+ (name) => allowed.includes(name) && schema?.metadatas?.[name]?.edit?.editable !== false,
2268
+ !c.addComponentToDynamicZone
2269
+ ).map((zone) => ({
2270
+ ...zone,
2271
+ label: schema?.metadatas?.[zone.name]?.edit?.label || zone.name
2272
+ }));
1754
2273
  const add = (zone, uid) => {
1755
2274
  const close = catalog?.groups?.[uid];
1756
- if (!canInsert(zone, uid, c.modifiedData, components, close ? 2 : 1) || close && !zone.components.includes(close)) return false;
1757
- c.addComponentToDynamicZone(zone.name, components[uid], components, Boolean(c.formErrors?.[zone.name]));
1758
- if (close) c.addComponentToDynamicZone(zone.name, components[close], components, Boolean(c.formErrors?.[zone.name]));
2275
+ if (!canInsert(zone, uid, c.modifiedData, components, close ? 2 : 1) || close && !zone.components.includes(close))
2276
+ return false;
2277
+ c.addComponentToDynamicZone(
2278
+ zone.name,
2279
+ components[uid],
2280
+ components,
2281
+ Boolean(c.formErrors?.[zone.name])
2282
+ );
2283
+ if (close)
2284
+ c.addComponentToDynamicZone(
2285
+ zone.name,
2286
+ components[close],
2287
+ components,
2288
+ Boolean(c.formErrors?.[zone.name])
2289
+ );
1759
2290
  return true;
1760
2291
  };
1761
2292
  if (!catalog?.editor?.enabled) return null;
1762
2293
  const docKey = `${c.slug}:${c.isCreatingEntry ? "new" : c.initialData?.id}:${c.initialData?.locale || ""}`;
1763
- return /* @__PURE__ */ jsx4(
2294
+ return /* @__PURE__ */ jsx5(
1764
2295
  Gallery,
1765
2296
  {
1766
2297
  zones,
@@ -1770,17 +2301,44 @@ function Picker() {
1770
2301
  Toggle: ToggleField,
1771
2302
  get,
1772
2303
  editor: catalog.editor,
2304
+ catalog,
1773
2305
  docKey,
1774
2306
  contentType: c.slug,
1775
2307
  userId: auth.getUserInfo?.()?.id
1776
2308
  }
1777
2309
  );
1778
2310
  }
1779
- var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs3(Flex3, { gap: 3, children: [
1780
- /* @__PURE__ */ jsx4(Switch, { name, "aria-label": label, selected: Boolean(value), disabled, onChange: () => onChange(!value) }),
1781
- /* @__PURE__ */ jsx4(Typography3, { children: label })
2311
+ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs4(Flex3, { gap: 3, children: [
2312
+ /* @__PURE__ */ jsx5(
2313
+ Switch,
2314
+ {
2315
+ name,
2316
+ "aria-label": label,
2317
+ selected: Boolean(value),
2318
+ disabled,
2319
+ onChange: () => onChange(!value)
2320
+ }
2321
+ ),
2322
+ /* @__PURE__ */ jsx5(Typography3, { children: label })
1782
2323
  ] });
1783
- var SelectField = ({ name, label, value, onChange, disabled, options }) => /* @__PURE__ */ jsx4(SingleSelect2, { name, label, value, disabled, onChange: (v) => onChange(String(v)), children: options.map((o) => /* @__PURE__ */ jsx4(SingleSelectOption2, { value: o.value, children: o.label }, o.value)) });
2324
+ var SelectField = ({
2325
+ name,
2326
+ label,
2327
+ value,
2328
+ onChange,
2329
+ disabled,
2330
+ options
2331
+ }) => /* @__PURE__ */ jsx5(
2332
+ SingleSelect2,
2333
+ {
2334
+ name,
2335
+ label,
2336
+ value,
2337
+ disabled,
2338
+ onChange: (v) => onChange(String(v)),
2339
+ children: options.map((o) => /* @__PURE__ */ jsx5(SingleSelectOption2, { value: o.value, children: o.label }, o.value))
2340
+ }
2341
+ );
1784
2342
  function MediaPicker({ onClose, onSelect }) {
1785
2343
  const { components } = useLibrary();
1786
2344
  const Dialog = components?.["media-library"];
@@ -1788,13 +2346,34 @@ function MediaPicker({ onClose, onSelect }) {
1788
2346
  onClose();
1789
2347
  return null;
1790
2348
  }
1791
- return /* @__PURE__ */ jsx4(Dialog, { onClose, allowedTypes: ["images"], onSelectAssets: (assets) => onSelect(assets?.[0]) });
2349
+ return /* @__PURE__ */ jsx5(
2350
+ Dialog,
2351
+ {
2352
+ onClose,
2353
+ allowedTypes: ["images"],
2354
+ onSelectAssets: (assets) => onSelect(assets?.[0])
2355
+ }
2356
+ );
1792
2357
  }
1793
2358
  function usePermissions() {
1794
2359
  const { allowedActions, isLoading } = useRBAC(permissions);
1795
- return { canRead: allowedActions.canRead, canUpdate: allowedActions.canUpdate, isLoading };
2360
+ return {
2361
+ canRead: allowedActions.canRead,
2362
+ canUpdate: allowedActions.canUpdate,
2363
+ isLoading
2364
+ };
1796
2365
  }
1797
- var SettingsPage = () => /* @__PURE__ */ jsx4(Settings, { useClient: useFetchClient, usePermissions, MediaPicker, ToggleField, SelectField, previewSupported: false });
2366
+ var SettingsPage = () => /* @__PURE__ */ jsx5(
2367
+ Settings,
2368
+ {
2369
+ useClient: useFetchClient,
2370
+ usePermissions,
2371
+ MediaPicker,
2372
+ ToggleField,
2373
+ SelectField,
2374
+ previewSupported: false
2375
+ }
2376
+ );
1798
2377
  var strapi4_default = {
1799
2378
  register(app) {
1800
2379
  register(app, SettingsPage, "/settings/blockscene");
@@ -1802,7 +2381,10 @@ var strapi4_default = {
1802
2381
  },
1803
2382
  registerTrads,
1804
2383
  bootstrap(app) {
1805
- app.injectContentManagerComponent("editView", "right-links", { name: "blockscene", Component: Picker });
2384
+ app.injectContentManagerComponent("editView", "right-links", {
2385
+ name: "blockscene",
2386
+ Component: Picker
2387
+ });
1806
2388
  }
1807
2389
  };
1808
2390
  export {