@aduptive/strapi-blockscene 2.0.0-alpha.5 → 2.0.0-alpha.6

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
@@ -132,13 +132,108 @@ var require_groups = __commonJS({
132
132
  }
133
133
  });
134
134
 
135
+ // server/diff.js
136
+ var require_diff = __commonJS({
137
+ "server/diff.js"(exports, module) {
138
+ "use strict";
139
+ var IGNORED = ["id", "documentId", "locale", "localizations", "createdAt", "updatedAt", "publishedAt", "firstPublishedAt", "createdBy", "updatedBy", "strapi_stage", "strapi_assignee", "__temp_key__", "__pivot"];
140
+ var morph = (attr) => String(attr?.relation || "").toLowerCase().includes("morph");
141
+ var kept2 = (name, attr) => attr && !IGNORED.includes(name) && attr.type !== "password" && !(attr.type === "relation" && (morph(attr) || attr.visible === false));
142
+ var empty = (value) => value === null || value === void 0 || value === "";
143
+ function snapshotPopulate(uid, schemaOf, stack = []) {
144
+ const schema = schemaOf(uid);
145
+ if (!schema || stack.includes(uid)) return {};
146
+ const scalars = (component) => Object.entries(schemaOf(component)?.attributes || {}).filter(([name, attr]) => kept2(name, attr) && !["relation", "media", "component", "dynamiczone"].includes(attr.type)).map(([name]) => name);
147
+ const nested = (component) => ({ select: scalars(component).length ? scalars(component) : ["id"], populate: snapshotPopulate(component, schemaOf, [...stack, uid]) });
148
+ const out = {};
149
+ for (const [name, attr] of Object.entries(schema.attributes || {})) {
150
+ if (!kept2(name, attr)) continue;
151
+ if (attr.type === "relation") out[name] = { select: ["documentId", "locale"] };
152
+ else if (attr.type === "media") out[name] = { select: ["id"] };
153
+ else if (attr.type === "component") out[name] = nested(attr.component);
154
+ else if (attr.type === "dynamiczone") out[name] = { on: Object.fromEntries((attr.components || []).map((component) => [component, nested(component)])) };
155
+ }
156
+ return out;
157
+ }
158
+ function canonical2(data, schema, schemaOf, { relations = true } = {}, depth = 0) {
159
+ const out = {};
160
+ if (!data || typeof data !== "object" || depth > 20) return out;
161
+ const idOf = (file) => file && typeof file === "object" ? file.id : file;
162
+ const ref = (item) => item && typeof item === "object" && item.documentId ? { documentId: item.documentId, ...item.locale && { locale: item.locale } } : null;
163
+ for (const [name, attr] of Object.entries(schema?.attributes || {})) {
164
+ const value = data[name];
165
+ if (!kept2(name, attr) || empty(value)) continue;
166
+ if (attr.type === "relation") {
167
+ if (!relations) continue;
168
+ const refs = (Array.isArray(value) ? value : [value]).map(ref).filter(Boolean);
169
+ if (refs.length) out[name] = Array.isArray(value) ? refs : refs[0];
170
+ } else if (attr.type === "media") {
171
+ const ids = (Array.isArray(value) ? value : [value]).map(idOf).filter((id) => !empty(id));
172
+ if (ids.length) out[name] = attr.multiple ? ids : ids[0];
173
+ } else if (attr.type === "component") {
174
+ const child = schemaOf(attr.component);
175
+ if (attr.repeatable) {
176
+ if (Array.isArray(value) && value.length) out[name] = value.map((item) => canonical2(item, child, schemaOf, { relations }, depth + 1));
177
+ } else out[name] = canonical2(value, child, schemaOf, { relations }, depth + 1);
178
+ } else if (attr.type === "dynamiczone") {
179
+ if (Array.isArray(value) && value.length) out[name] = value.filter((row) => row && typeof row.__component === "string").map((row) => ({ __component: row.__component, ...canonical2(row, schemaOf(row.__component), schemaOf, { relations }, depth + 1) }));
180
+ } else out[name] = value;
181
+ }
182
+ return out;
183
+ }
184
+ var stable = (value) => JSON.stringify(value, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.keys(v).sort().map((k) => [k, v[k]])) : v);
185
+ var same2 = (a, b) => stable(a ?? null) === stable(b ?? null);
186
+ function diffRows2(before = [], after = []) {
187
+ const a = before.map(stable), b = after.map(stable);
188
+ const n = a.length, m = b.length;
189
+ if (n * m > 25e4) return after.map((row, to) => ({ op: a[to] === b[to] ? "same" : "changed", from: to < n ? to : void 0, to, uid: row.__component }));
190
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
191
+ for (let i2 = n - 1; i2 >= 0; i2--) for (let j2 = m - 1; j2 >= 0; j2--) lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
192
+ const out = [];
193
+ const gap = (fromRows2, toRows2) => {
194
+ const removed = [...fromRows2];
195
+ for (const to of toRows2) {
196
+ const k = removed.findIndex((from) => before[from].__component === after[to].__component);
197
+ if (k === -1) out.push({ op: "added", to, uid: after[to].__component });
198
+ else out.push({ op: "changed", from: removed.splice(k, 1)[0], to, uid: after[to].__component });
199
+ }
200
+ for (const from of removed) out.push({ op: "removed", from, uid: before[from].__component });
201
+ };
202
+ let i = 0, j = 0, fromRows = [], toRows = [];
203
+ while (i < n || j < m) {
204
+ if (i < n && j < m && a[i] === b[j]) {
205
+ gap(fromRows, toRows);
206
+ fromRows = [];
207
+ toRows = [];
208
+ out.push({ op: "same", from: i, to: j, uid: after[j].__component });
209
+ i++;
210
+ j++;
211
+ } else if (j < m && (i >= n || lcs[i][j + 1] >= lcs[i + 1][j])) toRows.push(j++);
212
+ else fromRows.push(i++);
213
+ }
214
+ gap(fromRows, toRows);
215
+ return out;
216
+ }
217
+ function summarize(before, after, schema) {
218
+ const zones = Object.entries(schema?.attributes || {}).filter(([, attr]) => attr?.type === "dynamiczone").map(([name]) => name);
219
+ const blocks = { added: 0, removed: 0, changed: 0 };
220
+ if (!before) return { initial: true, fields: [], blocks };
221
+ const names = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after || {})])];
222
+ const fields = names.filter((name) => !zones.includes(name) && !same2(before[name], after?.[name])).sort();
223
+ for (const name of zones) for (const { op } of diffRows2(before[name], after?.[name])) if (op !== "same") blocks[op]++;
224
+ return { fields, blocks };
225
+ }
226
+ module.exports = { IGNORED, kept: kept2, snapshotPopulate, canonical: canonical2, stable, same: same2, diffRows: diffRows2, summarize };
227
+ }
228
+ });
229
+
135
230
  // admin/strapi5.tsx
136
- import * as React10 from "react";
231
+ import * as React11 from "react";
137
232
  import {
138
233
  Modal as Dialog,
139
234
  Switch,
140
- Flex as Flex6,
141
- Typography as Typography7,
235
+ Flex as Flex7,
236
+ Typography as Typography8,
142
237
  Field as Field2,
143
238
  SingleSelect,
144
239
  SingleSelectOption,
@@ -146,11 +241,12 @@ import {
146
241
  } from "@strapi/design-system";
147
242
  import {
148
243
  useFetchClient as useFetchClient2,
149
- useRBAC,
244
+ useRBAC as useRBAC2,
150
245
  useStrapiApp as useStrapiApp2,
151
246
  useAuth,
152
- useForm as useForm2,
153
- useNotification as useNotification2
247
+ useForm as useForm3,
248
+ useNotification as useNotification2,
249
+ getFetchClient
154
250
  } from "@strapi/strapi/admin";
155
251
  import {
156
252
  unstable_useContentManagerContext as useContext2,
@@ -332,6 +428,57 @@ function initialState(editor, remembered) {
332
428
  if (editor?.initialState === "remember") return remembered || "closed";
333
429
  return "closed";
334
430
  }
431
+ var withoutField = (rows, name) => rows.map((row) => row.filter((field) => field?.name !== name)).filter((row) => row.length);
432
+ function dropField(layout, name) {
433
+ if (!name || !layout?.components) return layout;
434
+ return { ...layout, components: Object.fromEntries(Object.entries(layout.components).map(([uid, component]) => [uid, Array.isArray(component?.layout) ? { ...component, layout: withoutField(component.layout, name) } : component])) };
435
+ }
436
+ function humanize(name) {
437
+ const text = String(name || "").replace(/[_\-\s]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").trim().split(" ").filter(Boolean).map((word) => /^[A-Z0-9]{2,}$/.test(word) && /[A-Z]/.test(word) ? word : word.toLowerCase()).join(" ");
438
+ return text.charAt(0).toUpperCase() + text.slice(1);
439
+ }
440
+ function localized(value, locale = "en") {
441
+ if (typeof value === "string") return value;
442
+ if (!value || typeof value !== "object") return void 0;
443
+ const keys = Object.keys(value);
444
+ const lang = (key2) => key2.toLowerCase().split("-")[0];
445
+ const lower = String(locale || "en").toLowerCase();
446
+ const key = keys.find((k) => k.toLowerCase() === lower) ?? keys.find((k) => k.toLowerCase() === lang(lower)) ?? keys.find((k) => lang(k) === lang(lower)) ?? keys.find((k) => lang(k) === "en") ?? keys[0];
447
+ return key === void 0 ? void 0 : value[key];
448
+ }
449
+ function fieldText(fields, owner, name, label, locale, on) {
450
+ const entry = owner && fields?.[owner]?.[name] || {};
451
+ const out = {};
452
+ const text = localized(entry.label, locale) ?? (on && label === name ? humanize(name) : void 0);
453
+ if (text !== void 0) out.label = text;
454
+ for (const key of ["description", "placeholder"]) {
455
+ const value = localized(entry[key], locale);
456
+ if (value !== void 0) out[key] = value;
457
+ }
458
+ return Object.keys(out).length ? out : null;
459
+ }
460
+ function layoutType(layout, types = {}) {
461
+ const names = (layout?.layout || []).flat(2).map((field) => field?.name);
462
+ const found = Object.entries(types || {}).filter(([, [displayName, ...attributes]]) => displayName === layout?.settings?.displayName && names.every((name) => attributes.includes(name)));
463
+ return found.length === 1 ? found[0][0] : found.length ? false : null;
464
+ }
465
+ var labelsOff = (catalog) => !catalog?.editor?.enabled || !catalog.editor.friendlyLabels && !Object.keys(catalog.fields || {}).length;
466
+ function labelEditLayout(layout, catalog, locale) {
467
+ if (labelsOff(catalog) || !Array.isArray(layout?.layout) || !layout.layout.length) return layout;
468
+ const uid = layoutType(layout, catalog.types);
469
+ if (uid === false || uid && catalog.contentTypes?.[uid]?.enabled === false) return layout;
470
+ const fix = (owner) => (field) => {
471
+ const text = field && fieldText(catalog.fields, owner, field.name, field.label, locale, catalog.editor.friendlyLabels);
472
+ if (!text) return field;
473
+ const { description, ...rest } = text;
474
+ return { ...field, ...rest, ...description !== void 0 && { hint: description } };
475
+ };
476
+ return {
477
+ ...layout,
478
+ layout: layout.layout.map((panel) => panel.map((row) => row.map(fix(uid)))),
479
+ components: Object.fromEntries(Object.entries(layout.components || {}).map(([cuid, component]) => [cuid, Array.isArray(component?.layout) ? { ...component, layout: component.layout.map((row) => row.map(fix(cuid))) } : component]))
480
+ };
481
+ }
335
482
 
336
483
  // admin/preview.mjs
337
484
  var import_groups = __toESM(require_groups(), 1);
@@ -483,15 +630,15 @@ var stageBackground = (theme) => {
483
630
 
484
631
  // admin/accordions.mjs
485
632
  function zoneLists(root = document) {
486
- return [...root.querySelectorAll("ol[aria-describedby]")].filter((list) => list.querySelector(":scope > li button[aria-expanded]"));
633
+ return [...root.querySelectorAll("ol[aria-describedby]")].filter((list2) => list2.querySelector(":scope > li button[aria-expanded]"));
487
634
  }
488
635
  function findZoneList(label, root = document) {
489
636
  const lists = zoneLists(root);
490
- const text = (list) => (list.parentElement?.textContent || "").trim().toLocaleLowerCase();
491
- return lists.find((list) => text(list).startsWith(String(label).toLocaleLowerCase())) || (lists.length === 1 ? lists[0] : null);
637
+ const text = (list2) => (list2.parentElement?.textContent || "").trim().toLocaleLowerCase();
638
+ return lists.find((list2) => text(list2).startsWith(String(label).toLocaleLowerCase())) || (lists.length === 1 ? lists[0] : null);
492
639
  }
493
- function toggles(list) {
494
- return [...list.querySelectorAll(":scope > li")].map((item) => item.querySelector("button[aria-expanded]")).filter(Boolean);
640
+ function toggles(list2) {
641
+ return [...list2.querySelectorAll(":scope > li")].map((item) => item.querySelector("button[aria-expanded]")).filter(Boolean);
495
642
  }
496
643
  function planRows(rects, view, margin = 0) {
497
644
  const now = [], later = [];
@@ -515,9 +662,9 @@ function stopAll() {
515
662
  var PER_FRAME = 3;
516
663
  var idle = (run) => typeof requestIdleCallback === "function" ? requestIdleCallback(run, { timeout: 300 }) : setTimeout(() => run({ timeRemaining: () => 8 }), 16);
517
664
  var frame = (run) => typeof requestAnimationFrame === "function" ? requestAnimationFrame(run) : setTimeout(run, 16);
518
- function setAll(list, open, batch = (run) => run()) {
519
- jobs.get(list)?.();
520
- jobs.delete(list);
665
+ function setAll(list2, open, batch = (run) => run()) {
666
+ jobs.get(list2)?.();
667
+ jobs.delete(list2);
521
668
  const header = (li) => li.querySelector("button[aria-expanded]");
522
669
  const differs = (li) => {
523
670
  const b = li.isConnected && header(li);
@@ -530,9 +677,9 @@ function setAll(list, open, batch = (run) => run()) {
530
677
  });
531
678
  return buttons.length;
532
679
  };
533
- const rows = [...list.querySelectorAll(":scope > li")].filter(differs);
680
+ const rows = [...list2.querySelectorAll(":scope > li")].filter(differs);
534
681
  if (!rows.length) return 0;
535
- const root = scroller(list);
682
+ const root = scroller(list2);
536
683
  const view = () => root ? root.getBoundingClientRect() : { top: 0, bottom: window.innerHeight };
537
684
  const height = view().bottom - view().top;
538
685
  const { now, later } = planRows(rows.map((li) => li.getBoundingClientRect()), view(), open ? height : 0);
@@ -563,7 +710,7 @@ function setAll(list, open, batch = (run) => run()) {
563
710
  if (queue.size && !handle && !stopped) handle = frame(flush);
564
711
  }, { root, rootMargin: `${Math.round(height)}px 0px` });
565
712
  later.forEach((i) => observer.observe(rows[i]));
566
- jobs.set(list, () => {
713
+ jobs.set(list2, () => {
567
714
  stopped = true;
568
715
  observer.disconnect();
569
716
  });
@@ -572,15 +719,15 @@ function setAll(list, open, batch = (run) => run()) {
572
719
  }
573
720
  const changed = click(now.map((i) => rows[i]));
574
721
  if (!later.length) return changed;
575
- const pending = later.map((i) => rows[i]);
722
+ const pending2 = later.map((i) => rows[i]);
576
723
  const slice = (deadline) => {
577
724
  if (stopped) return;
578
- click(pending.splice(0, deadline.timeRemaining() > 10 ? PER_FRAME : 1));
579
- if (pending.length) idle(slice);
580
- else jobs.delete(list);
725
+ click(pending2.splice(0, deadline.timeRemaining() > 10 ? PER_FRAME : 1));
726
+ if (pending2.length) idle(slice);
727
+ else jobs.delete(list2);
581
728
  };
582
729
  idle(slice);
583
- jobs.set(list, () => {
730
+ jobs.set(list2, () => {
584
731
  stopped = true;
585
732
  });
586
733
  return changed;
@@ -588,7 +735,9 @@ function setAll(list, open, batch = (run) => run()) {
588
735
  function isNativeAddButton(button, zone) {
589
736
  if (!button || button.getAttribute("aria-expanded") || button.closest('ol[aria-describedby] > li, [role="dialog"], [data-testid^="open-gallery-"], [data-testid^="block-"], [data-testid^="page-preview"], [data-testid^="bp-"], [data-testid^="zone-"], [data-blockscene-row-actions]')) return false;
590
737
  const text = (button.textContent || "").trim();
591
- return Boolean(text) && new RegExp(`(^|\\s)${String(zone.name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`).test(text);
738
+ const escape = (value) => String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
739
+ const names = [...new Set([zone.name, zone.label].filter(Boolean))].map(escape).join("|");
740
+ return Boolean(text) && new RegExp(`(^|\\s)(${names})$`).test(text);
592
741
  }
593
742
 
594
743
  // admin/wireframes.tsx
@@ -711,7 +860,6 @@ var en_default = {
711
860
  previewHelp: "Unsaved changes appear in the preview. Click a block to open its form, an image to open the Media Library, a text to edit it.",
712
861
  previewNoUrl: "No preview URL: set it in Settings, Blockscene, or configure Strapi's native Preview for this content type.",
713
862
  previewFailed: "The preview did not answer within 15 s. If the frontend is still loading it will appear by itself; the form stays usable.",
714
- previewReady: "Page preview \xB7 unsaved changes",
715
863
  previewLoading: "Loading preview\u2026",
716
864
  retry: "Try again",
717
865
  editText: "Edit text",
@@ -869,12 +1017,57 @@ var en_default = {
869
1017
  confirmDeleteOption: "Confirm before deleting a block",
870
1018
  duplicateOption: 'Show "Duplicate" on each block',
871
1019
  clipboardOption: 'Show "Copy" and "Paste" (across pages)',
1020
+ friendlyLabels: "Friendly field labels",
1021
+ friendlyLabelsHelp: `Fields whose label is still the attribute name read as words ("mobileColumnsCount" becomes "Mobile columns count"). Labels set in "Configure the view" and texts from the project's "fields" config are kept.`,
872
1022
  hiddenBlocks: "Blocks hidden on the site",
873
1023
  "hiddenModes.strip": "Removed from the content API",
874
1024
  "hiddenModes.flag": "Sent with the flag (the frontend decides)",
875
1025
  "hiddenModes.off": "Off (no hide button)",
876
1026
  hiddenBlocksHelp: "The eye on each block marks it hidden on the site. Removed: content API reads (REST, GraphQL) leave it out, the admin always shows it. Flag: it is sent with the attribute so the frontend can decide.",
877
- hiddenAttributeOff: "The plugin config sets hiddenAttribute to false: blocks cannot be hidden."
1027
+ hiddenAttributeOff: "The plugin config sets hiddenAttribute to false: blocks cannot be hidden.",
1028
+ historyTitle: "History",
1029
+ historyEmpty: "No versions yet. Saves, publishes and deletes are recorded from now on.",
1030
+ historyLoading: "Loading history\u2026",
1031
+ historyFailed: "The history could not be loaded.",
1032
+ historyRefresh: "Refresh history",
1033
+ historySystem: "System",
1034
+ "historyActions.create": "Created",
1035
+ "historyActions.update": "Saved",
1036
+ "historyActions.publish": "Published",
1037
+ "historyActions.unpublish": "Unpublished",
1038
+ "historyActions.discard": "Draft discarded",
1039
+ "historyActions.delete": "Deleted",
1040
+ "historyActions.restore": "Restored",
1041
+ historyInitial: "First version",
1042
+ historyNoChange: "No content change",
1043
+ historyMissing: "Not recorded (the capture failed)",
1044
+ historyBlocksSummary: "Blocks: {added} added, {removed} removed, {changed} changed",
1045
+ historyFieldsSummary: "Fields: {fields}",
1046
+ historyNotKept: "This version is no longer kept.",
1047
+ historyCompare: "Compared with the form",
1048
+ historySame: "Same content as the form.",
1049
+ "diffOps.added": "Added",
1050
+ "diffOps.removed": "Removed",
1051
+ "diffOps.changed": "Changed",
1052
+ historyDiffFields: "Fields that differ: {fields}",
1053
+ historyDiffNote: "Relations are not compared.",
1054
+ historyLoad: "Load this version",
1055
+ historyLoaded: "Version loaded into the form. Nothing is saved until you save.",
1056
+ historyLeft: "Left as they are: {fields}",
1057
+ historyRemovedFields: "No longer in the schema, not loaded: {fields}",
1058
+ historyMissingItems: "No longer exist, left out: {media} media files, {relations} related entries, {blocks} blocks",
1059
+ historyLoadFailed: "This version could not be loaded.",
1060
+ historyShowMore: "Show more",
1061
+ historySettings: "Version history",
1062
+ historySettingsHelp: "Records every create, save, publish, unpublish, discard and delete made through Strapi's document service (the admin, the REST and GraphQL APIs, other plugins), with a snapshot of the content. Editors see the versions in the edit view and can load one into the form. This is content activity, not a security audit log: direct database writes are not seen.",
1063
+ historyEnabled: "Record versions",
1064
+ historyAll: "All content types",
1065
+ historyTypes: "Content types",
1066
+ historyRetention: "Keep snapshots (days)",
1067
+ historyMax: "Snapshots per document (maximum)",
1068
+ historyEventDays: "Keep the activity (days)",
1069
+ historyRetentionHelp: "A nightly job empties snapshots older than this or beyond the per-document maximum (the newest are kept) and deletes activity older than its limit. It never deletes media files.",
1070
+ historyInvalid: "Whole numbers: 1 to 3650 days (the activity kept at least as long as the snapshots) and 1 to 1000 snapshots."
878
1071
  };
879
1072
 
880
1073
  // admin/translations/pt-BR.json
@@ -952,7 +1145,6 @@ var pt_BR_default = {
952
1145
  previewHelp: "Altera\xE7\xF5es n\xE3o salvas aparecem na preview. Clique em um bloco para abrir seu formul\xE1rio, em uma imagem para abrir a Media Library, em um texto para edit\xE1-lo.",
953
1146
  previewNoUrl: "Sem URL de preview: defina em Configura\xE7\xF5es, Blockscene, ou configure a Preview nativa do Strapi para este tipo de conte\xFAdo.",
954
1147
  previewFailed: "A preview n\xE3o respondeu em 15 s. Se o frontend ainda estiver carregando, ela aparece sozinha; o formul\xE1rio continua utiliz\xE1vel.",
955
- previewReady: "Preview da p\xE1gina \xB7 altera\xE7\xF5es n\xE3o salvas",
956
1148
  previewLoading: "Carregando preview\u2026",
957
1149
  retry: "Tentar novamente",
958
1150
  editText: "Editar texto",
@@ -1110,12 +1302,57 @@ var pt_BR_default = {
1110
1302
  confirmDeleteOption: "Confirmar antes de excluir um bloco",
1111
1303
  duplicateOption: 'Mostrar "Duplicar" em cada bloco',
1112
1304
  clipboardOption: 'Mostrar "Copiar" e "Colar" (entre p\xE1ginas)',
1305
+ friendlyLabels: "R\xF3tulos de campo amig\xE1veis",
1306
+ friendlyLabelsHelp: 'Campos cujo r\xF3tulo ainda \xE9 o nome do atributo aparecem como palavras ("mobileColumnsCount" vira "Mobile columns count"). R\xF3tulos definidos em "Configurar a visualiza\xE7\xE3o" e textos da configura\xE7\xE3o "fields" do projeto s\xE3o mantidos.',
1113
1307
  hiddenBlocks: "Blocos ocultos no site",
1114
1308
  "hiddenModes.strip": "Removidos da API de conte\xFAdo",
1115
1309
  "hiddenModes.flag": "Enviados com a marca\xE7\xE3o (o frontend decide)",
1116
1310
  "hiddenModes.off": "Desligado (sem bot\xE3o de ocultar)",
1117
1311
  hiddenBlocksHelp: "O olho em cada bloco o marca como oculto no site. Removidos: as leituras da API de conte\xFAdo (REST, GraphQL) o deixam de fora; o admin sempre o mostra. Marca\xE7\xE3o: ele \xE9 enviado com o atributo para o frontend decidir.",
1118
- hiddenAttributeOff: "A config do plugin define hiddenAttribute como false: n\xE3o \xE9 poss\xEDvel ocultar blocos."
1312
+ hiddenAttributeOff: "A config do plugin define hiddenAttribute como false: n\xE3o \xE9 poss\xEDvel ocultar blocos.",
1313
+ historyTitle: "Hist\xF3rico",
1314
+ historyEmpty: "Nenhuma vers\xE3o ainda. Salvamentos, publica\xE7\xF5es e exclus\xF5es passam a ser registrados a partir de agora.",
1315
+ historyLoading: "Carregando hist\xF3rico\u2026",
1316
+ historyFailed: "N\xE3o foi poss\xEDvel carregar o hist\xF3rico.",
1317
+ historyRefresh: "Atualizar hist\xF3rico",
1318
+ historySystem: "Sistema",
1319
+ "historyActions.create": "Criado",
1320
+ "historyActions.update": "Salvo",
1321
+ "historyActions.publish": "Publicado",
1322
+ "historyActions.unpublish": "Despublicado",
1323
+ "historyActions.discard": "Rascunho descartado",
1324
+ "historyActions.delete": "Exclu\xEDdo",
1325
+ "historyActions.restore": "Restaurado",
1326
+ historyInitial: "Primeira vers\xE3o",
1327
+ historyNoChange: "Sem mudan\xE7a de conte\xFAdo",
1328
+ historyMissing: "N\xE3o registrado (a captura falhou)",
1329
+ historyBlocksSummary: "Blocos: {added} adicionados, {removed} removidos, {changed} alterados",
1330
+ historyFieldsSummary: "Campos: {fields}",
1331
+ historyNotKept: "Esta vers\xE3o n\xE3o \xE9 mais mantida.",
1332
+ historyCompare: "Comparado com o formul\xE1rio",
1333
+ historySame: "Mesmo conte\xFAdo do formul\xE1rio.",
1334
+ "diffOps.added": "Adicionado",
1335
+ "diffOps.removed": "Removido",
1336
+ "diffOps.changed": "Alterado",
1337
+ historyDiffFields: "Campos diferentes: {fields}",
1338
+ historyDiffNote: "Rela\xE7\xF5es n\xE3o s\xE3o comparadas.",
1339
+ historyLoad: "Carregar esta vers\xE3o",
1340
+ historyLoaded: "Vers\xE3o carregada no formul\xE1rio. Nada \xE9 salvo at\xE9 voc\xEA salvar.",
1341
+ historyLeft: "Mantidos como est\xE3o: {fields}",
1342
+ historyRemovedFields: "N\xE3o existem mais no schema, n\xE3o carregados: {fields}",
1343
+ historyMissingItems: "N\xE3o existem mais, deixados de fora: {media} arquivos de m\xEDdia, {relations} entradas relacionadas, {blocks} blocos",
1344
+ historyLoadFailed: "N\xE3o foi poss\xEDvel carregar esta vers\xE3o.",
1345
+ historyShowMore: "Mostrar mais",
1346
+ historySettings: "Hist\xF3rico de vers\xF5es",
1347
+ historySettingsHelp: "Registra cada cria\xE7\xE3o, salvamento, publica\xE7\xE3o, despublica\xE7\xE3o, descarte e exclus\xE3o feitos pelo document service do Strapi (o admin, as APIs REST e GraphQL, outros plugins), com um snapshot do conte\xFAdo. Editores veem as vers\xF5es na tela de edi\xE7\xE3o e podem carregar uma no formul\xE1rio. \xC9 atividade de conte\xFAdo, n\xE3o um log de auditoria de seguran\xE7a: grava\xE7\xF5es diretas no banco n\xE3o s\xE3o vistas.",
1348
+ historyEnabled: "Registrar vers\xF5es",
1349
+ historyAll: "Todos os tipos de conte\xFAdo",
1350
+ historyTypes: "Tipos de conte\xFAdo",
1351
+ historyRetention: "Manter snapshots (dias)",
1352
+ historyMax: "Snapshots por documento (m\xE1ximo)",
1353
+ historyEventDays: "Manter a atividade (dias)",
1354
+ historyRetentionHelp: "Uma rotina noturna esvazia snapshots mais antigos que isso ou al\xE9m do m\xE1ximo por documento (os mais novos s\xE3o mantidos) e exclui a atividade mais antiga que o limite. Nunca exclui arquivos de m\xEDdia.",
1355
+ historyInvalid: "N\xFAmeros inteiros: 1 a 3650 dias (a atividade mantida pelo menos tanto quanto os snapshots) e 1 a 1000 snapshots."
1119
1356
  };
1120
1357
 
1121
1358
  // admin/translations/pt.json
@@ -1193,7 +1430,6 @@ var pt_default = {
1193
1430
  previewHelp: "As altera\xE7\xF5es por guardar aparecem na pr\xE9-visualiza\xE7\xE3o. Clique num bloco para abrir o seu formul\xE1rio, numa imagem para abrir a Media Library, num texto para o editar.",
1194
1431
  previewNoUrl: "Sem URL de pr\xE9-visualiza\xE7\xE3o: defina em Defini\xE7\xF5es, Blockscene, ou configure a Preview nativa do Strapi para este tipo de conte\xFAdo.",
1195
1432
  previewFailed: "A pr\xE9-visualiza\xE7\xE3o n\xE3o respondeu em 15 s. Se o frontend ainda estiver a carregar, ela aparece sozinha; o formul\xE1rio continua utiliz\xE1vel.",
1196
- previewReady: "Pr\xE9-visualiza\xE7\xE3o da p\xE1gina \xB7 altera\xE7\xF5es por guardar",
1197
1433
  previewLoading: "A carregar pr\xE9-visualiza\xE7\xE3o\u2026",
1198
1434
  retry: "Tentar novamente",
1199
1435
  editText: "Editar texto",
@@ -1351,12 +1587,57 @@ var pt_default = {
1351
1587
  confirmDeleteOption: "Confirmar antes de eliminar um bloco",
1352
1588
  duplicateOption: 'Mostrar "Duplicar" em cada bloco',
1353
1589
  clipboardOption: 'Mostrar "Copiar" e "Colar" (entre p\xE1ginas)',
1590
+ friendlyLabels: "Etiquetas de campo leg\xEDveis",
1591
+ friendlyLabelsHelp: 'Os campos cuja etiqueta ainda \xE9 o nome do atributo aparecem como palavras ("mobileColumnsCount" passa a "Mobile columns count"). As etiquetas definidas em "Configurar a vista" e os textos da configura\xE7\xE3o "fields" do projeto mant\xEAm-se.',
1354
1592
  hiddenBlocks: "Blocos ocultos no site",
1355
1593
  "hiddenModes.strip": "Removidos da API de conte\xFAdo",
1356
1594
  "hiddenModes.flag": "Enviados com a marca\xE7\xE3o (o frontend decide)",
1357
1595
  "hiddenModes.off": "Desligado (sem bot\xE3o de ocultar)",
1358
1596
  hiddenBlocksHelp: "O olho em cada bloco marca-o como oculto no site. Removidos: as leituras da API de conte\xFAdo (REST, GraphQL) deixam-no de fora; o admin mostra-o sempre. Marca\xE7\xE3o: \xE9 enviado com o atributo para o frontend decidir.",
1359
- hiddenAttributeOff: "A configura\xE7\xE3o do plugin define hiddenAttribute como false: n\xE3o \xE9 poss\xEDvel ocultar blocos."
1597
+ hiddenAttributeOff: "A configura\xE7\xE3o do plugin define hiddenAttribute como false: n\xE3o \xE9 poss\xEDvel ocultar blocos.",
1598
+ historyTitle: "Hist\xF3rico",
1599
+ historyEmpty: "Ainda n\xE3o h\xE1 vers\xF5es. Grava\xE7\xF5es, publica\xE7\xF5es e elimina\xE7\xF5es passam a ser registadas a partir de agora.",
1600
+ historyLoading: "A carregar o hist\xF3rico\u2026",
1601
+ historyFailed: "N\xE3o foi poss\xEDvel carregar o hist\xF3rico.",
1602
+ historyRefresh: "Atualizar hist\xF3rico",
1603
+ historySystem: "Sistema",
1604
+ "historyActions.create": "Criado",
1605
+ "historyActions.update": "Guardado",
1606
+ "historyActions.publish": "Publicado",
1607
+ "historyActions.unpublish": "Despublicado",
1608
+ "historyActions.discard": "Rascunho descartado",
1609
+ "historyActions.delete": "Eliminado",
1610
+ "historyActions.restore": "Restaurado",
1611
+ historyInitial: "Primeira vers\xE3o",
1612
+ historyNoChange: "Sem altera\xE7\xE3o de conte\xFAdo",
1613
+ historyMissing: "N\xE3o registado (a captura falhou)",
1614
+ historyBlocksSummary: "Blocos: {added} adicionados, {removed} removidos, {changed} alterados",
1615
+ historyFieldsSummary: "Campos: {fields}",
1616
+ historyNotKept: "Esta vers\xE3o j\xE1 n\xE3o \xE9 mantida.",
1617
+ historyCompare: "Comparado com o formul\xE1rio",
1618
+ historySame: "Mesmo conte\xFAdo do formul\xE1rio.",
1619
+ "diffOps.added": "Adicionado",
1620
+ "diffOps.removed": "Removido",
1621
+ "diffOps.changed": "Alterado",
1622
+ historyDiffFields: "Campos diferentes: {fields}",
1623
+ historyDiffNote: "As rela\xE7\xF5es n\xE3o s\xE3o comparadas.",
1624
+ historyLoad: "Carregar esta vers\xE3o",
1625
+ historyLoaded: "Vers\xE3o carregada no formul\xE1rio. Nada \xE9 guardado at\xE9 guardar.",
1626
+ historyLeft: "Mantidos como est\xE3o: {fields}",
1627
+ historyRemovedFields: "J\xE1 n\xE3o existem no esquema, n\xE3o carregados: {fields}",
1628
+ historyMissingItems: "J\xE1 n\xE3o existem, deixados de fora: {media} ficheiros multim\xE9dia, {relations} entradas relacionadas, {blocks} blocos",
1629
+ historyLoadFailed: "N\xE3o foi poss\xEDvel carregar esta vers\xE3o.",
1630
+ historyShowMore: "Mostrar mais",
1631
+ historySettings: "Hist\xF3rico de vers\xF5es",
1632
+ historySettingsHelp: "Regista cada cria\xE7\xE3o, grava\xE7\xE3o, publica\xE7\xE3o, despublica\xE7\xE3o, descarte e elimina\xE7\xE3o feitos pelo document service do Strapi (o admin, as APIs REST e GraphQL, outros plugins), com um snapshot do conte\xFAdo. Os editores veem as vers\xF5es no ecr\xE3 de edi\xE7\xE3o e podem carregar uma no formul\xE1rio. \xC9 atividade de conte\xFAdo, n\xE3o um registo de auditoria de seguran\xE7a: escritas diretas na base de dados n\xE3o s\xE3o vistas.",
1633
+ historyEnabled: "Registar vers\xF5es",
1634
+ historyAll: "Todos os tipos de conte\xFAdo",
1635
+ historyTypes: "Tipos de conte\xFAdo",
1636
+ historyRetention: "Manter snapshots (dias)",
1637
+ historyMax: "Snapshots por documento (m\xE1ximo)",
1638
+ historyEventDays: "Manter a atividade (dias)",
1639
+ historyRetentionHelp: "Uma tarefa noturna esvazia os snapshots mais antigos do que isto ou al\xE9m do m\xE1ximo por documento (os mais recentes s\xE3o mantidos) e elimina a atividade mais antiga do que o limite. Nunca elimina ficheiros multim\xE9dia.",
1640
+ historyInvalid: "N\xFAmeros inteiros: 1 a 3650 dias (a atividade mantida pelo menos tanto quanto os snapshots) e 1 a 1000 snapshots."
1360
1641
  };
1361
1642
 
1362
1643
  // admin/translations/fr.json
@@ -1434,7 +1715,6 @@ var fr_default = {
1434
1715
  previewHelp: "Les modifications non enregistr\xE9es apparaissent dans l'aper\xE7u. Cliquez sur un bloc pour ouvrir son formulaire, sur une image pour ouvrir la M\xE9diath\xE8que, sur un texte pour le modifier.",
1435
1716
  previewNoUrl: "Aucune URL d'aper\xE7u : d\xE9finissez-la dans Param\xE8tres, Blockscene, ou configurez l'aper\xE7u natif de Strapi pour ce type de contenu.",
1436
1717
  previewFailed: "L'aper\xE7u n'a pas r\xE9pondu en 15 s. Si le frontend est encore en train de charger, il appara\xEEtra de lui-m\xEAme ; le formulaire reste utilisable.",
1437
- previewReady: "Aper\xE7u de la page \xB7 modifications non enregistr\xE9es",
1438
1718
  previewLoading: "Chargement de l'aper\xE7u\u2026",
1439
1719
  retry: "R\xE9essayer",
1440
1720
  editText: "Modifier le texte",
@@ -1592,12 +1872,57 @@ var fr_default = {
1592
1872
  confirmDeleteOption: "Confirmer avant de supprimer un bloc",
1593
1873
  duplicateOption: "Afficher \xAB Dupliquer \xBB sur chaque bloc",
1594
1874
  clipboardOption: "Afficher \xAB Copier \xBB et \xAB Coller \xBB (entre pages)",
1875
+ friendlyLabels: "Libell\xE9s de champ lisibles",
1876
+ friendlyLabelsHelp: `Les champs dont le libell\xE9 est encore le nom de l'attribut s'affichent en mots ("mobileColumnsCount" devient "Mobile columns count"). Les libell\xE9s d\xE9finis dans \xAB Configurer la vue \xBB et les textes de la configuration \xAB fields \xBB du projet sont conserv\xE9s.`,
1595
1877
  hiddenBlocks: "Blocs masqu\xE9s sur le site",
1596
1878
  "hiddenModes.strip": "Retir\xE9s de l'API de contenu",
1597
1879
  "hiddenModes.flag": "Envoy\xE9s avec l'indicateur (le frontend d\xE9cide)",
1598
1880
  "hiddenModes.off": "D\xE9sactiv\xE9 (pas de bouton masquer)",
1599
1881
  hiddenBlocksHelp: "L'\u0153il de chaque bloc le marque comme masqu\xE9 sur le site. Retir\xE9s : les lectures de l'API de contenu (REST, GraphQL) l'omettent, l'admin l'affiche toujours. Indicateur : il est envoy\xE9 avec l'attribut et le frontend d\xE9cide.",
1600
- hiddenAttributeOff: "La configuration du plugin fixe hiddenAttribute \xE0 false : les blocs ne peuvent pas \xEAtre masqu\xE9s."
1882
+ hiddenAttributeOff: "La configuration du plugin fixe hiddenAttribute \xE0 false : les blocs ne peuvent pas \xEAtre masqu\xE9s.",
1883
+ historyTitle: "Historique",
1884
+ historyEmpty: "Aucune version pour l'instant. Les enregistrements, publications et suppressions sont d\xE9sormais consign\xE9s.",
1885
+ historyLoading: "Chargement de l'historique\u2026",
1886
+ historyFailed: "Impossible de charger l'historique.",
1887
+ historyRefresh: "Actualiser l'historique",
1888
+ historySystem: "Syst\xE8me",
1889
+ "historyActions.create": "Cr\xE9\xE9",
1890
+ "historyActions.update": "Enregistr\xE9",
1891
+ "historyActions.publish": "Publi\xE9",
1892
+ "historyActions.unpublish": "D\xE9publi\xE9",
1893
+ "historyActions.discard": "Brouillon abandonn\xE9",
1894
+ "historyActions.delete": "Supprim\xE9",
1895
+ "historyActions.restore": "Restaur\xE9",
1896
+ historyInitial: "Premi\xE8re version",
1897
+ historyNoChange: "Aucun changement de contenu",
1898
+ historyMissing: "Non consign\xE9 (la capture a \xE9chou\xE9)",
1899
+ historyBlocksSummary: "Blocs : {added} ajout\xE9s, {removed} supprim\xE9s, {changed} modifi\xE9s",
1900
+ historyFieldsSummary: "Champs : {fields}",
1901
+ historyNotKept: "Cette version n'est plus conserv\xE9e.",
1902
+ historyCompare: "Compar\xE9 au formulaire",
1903
+ historySame: "M\xEAme contenu que le formulaire.",
1904
+ "diffOps.added": "Ajout\xE9",
1905
+ "diffOps.removed": "Supprim\xE9",
1906
+ "diffOps.changed": "Modifi\xE9",
1907
+ historyDiffFields: "Champs diff\xE9rents : {fields}",
1908
+ historyDiffNote: "Les relations ne sont pas compar\xE9es.",
1909
+ historyLoad: "Charger cette version",
1910
+ historyLoaded: "Version charg\xE9e dans le formulaire. Rien n'est enregistr\xE9 tant que vous n'enregistrez pas.",
1911
+ historyLeft: "Laiss\xE9s tels quels : {fields}",
1912
+ historyRemovedFields: "N'existent plus dans le sch\xE9ma, non charg\xE9s : {fields}",
1913
+ historyMissingItems: "N'existent plus, laiss\xE9s de c\xF4t\xE9 : {media} fichiers m\xE9dia, {relations} entr\xE9es li\xE9es, {blocks} blocs",
1914
+ historyLoadFailed: "Impossible de charger cette version.",
1915
+ historyShowMore: "Afficher plus",
1916
+ historySettings: "Historique des versions",
1917
+ historySettingsHelp: "Consigne chaque cr\xE9ation, enregistrement, publication, d\xE9publication, abandon de brouillon et suppression passant par le document service de Strapi (l'admin, les API REST et GraphQL, d'autres plugins), avec un instantan\xE9 du contenu. Les \xE9diteurs voient les versions dans la vue d'\xE9dition et peuvent en charger une dans le formulaire. Il s'agit d'activit\xE9 de contenu, pas d'un journal d'audit de s\xE9curit\xE9 : les \xE9critures directes en base ne sont pas vues.",
1918
+ historyEnabled: "Consigner les versions",
1919
+ historyAll: "Tous les types de contenu",
1920
+ historyTypes: "Types de contenu",
1921
+ historyRetention: "Conserver les instantan\xE9s (jours)",
1922
+ historyMax: "Instantan\xE9s par document (maximum)",
1923
+ historyEventDays: "Conserver l'activit\xE9 (jours)",
1924
+ historyRetentionHelp: "Une t\xE2che nocturne vide les instantan\xE9s plus anciens que cela ou au-del\xE0 du maximum par document (les plus r\xE9cents sont gard\xE9s) et supprime l'activit\xE9 plus ancienne que sa limite. Elle ne supprime jamais de fichiers m\xE9dia.",
1925
+ historyInvalid: "Nombres entiers : 1 \xE0 3650 jours (l'activit\xE9 conserv\xE9e au moins aussi longtemps que les instantan\xE9s) et 1 \xE0 1000 instantan\xE9s."
1601
1926
  };
1602
1927
 
1603
1928
  // admin/translations/es.json
@@ -1675,7 +2000,6 @@ var es_default = {
1675
2000
  previewHelp: "Los cambios sin guardar aparecen en la vista previa. Haga clic en un bloque para abrir su formulario, en una imagen para abrir la Biblioteca multimedia, en un texto para editarlo.",
1676
2001
  previewNoUrl: "Sin URL de vista previa: def\xEDnala en Ajustes, Blockscene, o configure la Vista previa nativa de Strapi para este tipo de contenido.",
1677
2002
  previewFailed: "La vista previa no respondi\xF3 en 15 s. Si el frontend a\xFAn est\xE1 cargando, aparecer\xE1 sola; el formulario sigue utilizable.",
1678
- previewReady: "Vista previa de la p\xE1gina \xB7 cambios sin guardar",
1679
2003
  previewLoading: "Cargando vista previa\u2026",
1680
2004
  retry: "Reintentar",
1681
2005
  editText: "Editar texto",
@@ -1833,12 +2157,57 @@ var es_default = {
1833
2157
  confirmDeleteOption: "Confirmar antes de eliminar un bloque",
1834
2158
  duplicateOption: "Mostrar \xABDuplicar\xBB en cada bloque",
1835
2159
  clipboardOption: "Mostrar \xABCopiar\xBB y \xABPegar\xBB (entre p\xE1ginas)",
2160
+ friendlyLabels: "Etiquetas de campo legibles",
2161
+ friendlyLabelsHelp: 'Los campos cuya etiqueta sigue siendo el nombre del atributo se muestran como palabras ("mobileColumnsCount" pasa a "Mobile columns count"). Se conservan las etiquetas definidas en \xABConfigurar la vista\xBB y los textos de la configuraci\xF3n \xABfields\xBB del proyecto.',
1836
2162
  hiddenBlocks: "Bloques ocultos en el sitio",
1837
2163
  "hiddenModes.strip": "Eliminados de la API de contenido",
1838
2164
  "hiddenModes.flag": "Enviados con la marca (el frontend decide)",
1839
2165
  "hiddenModes.off": "Desactivado (sin bot\xF3n de ocultar)",
1840
2166
  hiddenBlocksHelp: "El ojo de cada bloque lo marca como oculto en el sitio. Eliminados: las lecturas de la API de contenido (REST, GraphQL) lo omiten; el admin siempre lo muestra. Marca: se env\xEDa con el atributo y el frontend decide.",
1841
- hiddenAttributeOff: "La configuraci\xF3n del plugin fija hiddenAttribute en false: no se pueden ocultar bloques."
2167
+ hiddenAttributeOff: "La configuraci\xF3n del plugin fija hiddenAttribute en false: no se pueden ocultar bloques.",
2168
+ historyTitle: "Historial",
2169
+ historyEmpty: "A\xFAn no hay versiones. Desde ahora se registran los guardados, las publicaciones y las eliminaciones.",
2170
+ historyLoading: "Cargando historial\u2026",
2171
+ historyFailed: "No se pudo cargar el historial.",
2172
+ historyRefresh: "Actualizar historial",
2173
+ historySystem: "Sistema",
2174
+ "historyActions.create": "Creado",
2175
+ "historyActions.update": "Guardado",
2176
+ "historyActions.publish": "Publicado",
2177
+ "historyActions.unpublish": "Despublicado",
2178
+ "historyActions.discard": "Borrador descartado",
2179
+ "historyActions.delete": "Eliminado",
2180
+ "historyActions.restore": "Restaurado",
2181
+ historyInitial: "Primera versi\xF3n",
2182
+ historyNoChange: "Sin cambios de contenido",
2183
+ historyMissing: "No registrado (la captura fall\xF3)",
2184
+ historyBlocksSummary: "Bloques: {added} a\xF1adidos, {removed} eliminados, {changed} modificados",
2185
+ historyFieldsSummary: "Campos: {fields}",
2186
+ historyNotKept: "Esta versi\xF3n ya no se conserva.",
2187
+ historyCompare: "Comparado con el formulario",
2188
+ historySame: "Mismo contenido que el formulario.",
2189
+ "diffOps.added": "A\xF1adido",
2190
+ "diffOps.removed": "Eliminado",
2191
+ "diffOps.changed": "Modificado",
2192
+ historyDiffFields: "Campos distintos: {fields}",
2193
+ historyDiffNote: "Las relaciones no se comparan.",
2194
+ historyLoad: "Cargar esta versi\xF3n",
2195
+ historyLoaded: "Versi\xF3n cargada en el formulario. No se guarda nada hasta que guardes.",
2196
+ historyLeft: "Se dejan como est\xE1n: {fields}",
2197
+ historyRemovedFields: "Ya no est\xE1n en el esquema, no se cargan: {fields}",
2198
+ historyMissingItems: "Ya no existen, se omiten: {media} archivos multimedia, {relations} entradas relacionadas, {blocks} bloques",
2199
+ historyLoadFailed: "No se pudo cargar esta versi\xF3n.",
2200
+ historyShowMore: "Mostrar m\xE1s",
2201
+ historySettings: "Historial de versiones",
2202
+ historySettingsHelp: "Registra cada creaci\xF3n, guardado, publicaci\xF3n, despublicaci\xF3n, descarte y eliminaci\xF3n hechos a trav\xE9s del document service de Strapi (el admin, las API REST y GraphQL, otros plugins), con una instant\xE1nea del contenido. Los editores ven las versiones en la vista de edici\xF3n y pueden cargar una en el formulario. Es actividad de contenido, no un registro de auditor\xEDa de seguridad: las escrituras directas en la base de datos no se ven.",
2203
+ historyEnabled: "Registrar versiones",
2204
+ historyAll: "Todos los tipos de contenido",
2205
+ historyTypes: "Tipos de contenido",
2206
+ historyRetention: "Conservar instant\xE1neas (d\xEDas)",
2207
+ historyMax: "Instant\xE1neas por documento (m\xE1ximo)",
2208
+ historyEventDays: "Conservar la actividad (d\xEDas)",
2209
+ historyRetentionHelp: "Una tarea nocturna vac\xEDa las instant\xE1neas m\xE1s antiguas que esto o por encima del m\xE1ximo por documento (se conservan las m\xE1s recientes) y elimina la actividad m\xE1s antigua que su l\xEDmite. Nunca elimina archivos multimedia.",
2210
+ historyInvalid: "N\xFAmeros enteros: de 1 a 3650 d\xEDas (la actividad se conserva al menos tanto como las instant\xE1neas) y de 1 a 1000 instant\xE1neas."
1842
2211
  };
1843
2212
 
1844
2213
  // admin/translations/de.json
@@ -1916,7 +2285,6 @@ var de_default = {
1916
2285
  previewHelp: "Ungespeicherte \xC4nderungen erscheinen in der Vorschau. Klicken Sie auf einen Block, um sein Formular zu \xF6ffnen, auf ein Bild f\xFCr die Medienbibliothek, auf einen Text, um ihn zu bearbeiten.",
1917
2286
  previewNoUrl: "Keine Vorschau-URL: in Einstellungen, Blockscene festlegen oder die native Strapi-Vorschau f\xFCr diesen Inhaltstyp konfigurieren.",
1918
2287
  previewFailed: "Die Vorschau hat innerhalb von 15 s nicht geantwortet. L\xE4dt das Frontend noch, erscheint sie von selbst; das Formular bleibt benutzbar.",
1919
- previewReady: "Seitenvorschau \xB7 ungespeicherte \xC4nderungen",
1920
2288
  previewLoading: "Vorschau wird geladen\u2026",
1921
2289
  retry: "Erneut versuchen",
1922
2290
  editText: "Text bearbeiten",
@@ -2074,12 +2442,57 @@ var de_default = {
2074
2442
  confirmDeleteOption: "Vor dem L\xF6schen eines Blocks nachfragen",
2075
2443
  duplicateOption: "\u201EDuplizieren\u201C an jedem Block anzeigen",
2076
2444
  clipboardOption: "\u201EKopieren\u201C und \u201EEinf\xFCgen\u201C anzeigen (seiten\xFCbergreifend)",
2445
+ friendlyLabels: "Lesbare Feldbezeichnungen",
2446
+ friendlyLabelsHelp: "Felder, deren Bezeichnung noch der Attributname ist, erscheinen als W\xF6rter (\u201EmobileColumnsCount\u201C wird zu \u201EMobile columns count\u201C). In \u201EAnsicht konfigurieren\u201C gesetzte Bezeichnungen und Texte aus der \u201Efields\u201C-Konfiguration des Projekts bleiben erhalten.",
2077
2447
  hiddenBlocks: "Auf der Website ausgeblendete Bl\xF6cke",
2078
2448
  "hiddenModes.strip": "Aus der Content-API entfernt",
2079
2449
  "hiddenModes.flag": "Mit Markierung gesendet (das Frontend entscheidet)",
2080
2450
  "hiddenModes.off": "Aus (kein Ausblenden-Knopf)",
2081
2451
  hiddenBlocksHelp: "Das Auge an jedem Block blendet ihn auf der Website aus. Entfernt: Lesezugriffe der Content-API (REST, GraphQL) lassen ihn weg, der Admin zeigt ihn immer. Markierung: Er wird mit dem Attribut gesendet und das Frontend entscheidet.",
2082
- hiddenAttributeOff: "Die Plugin-Konfiguration setzt hiddenAttribute auf false: Bl\xF6cke k\xF6nnen nicht ausgeblendet werden."
2452
+ hiddenAttributeOff: "Die Plugin-Konfiguration setzt hiddenAttribute auf false: Bl\xF6cke k\xF6nnen nicht ausgeblendet werden.",
2453
+ historyTitle: "Verlauf",
2454
+ historyEmpty: "Noch keine Versionen. Speichern, Ver\xF6ffentlichen und L\xF6schen werden ab jetzt aufgezeichnet.",
2455
+ historyLoading: "Verlauf wird geladen\u2026",
2456
+ historyFailed: "Der Verlauf konnte nicht geladen werden.",
2457
+ historyRefresh: "Verlauf aktualisieren",
2458
+ historySystem: "System",
2459
+ "historyActions.create": "Erstellt",
2460
+ "historyActions.update": "Gespeichert",
2461
+ "historyActions.publish": "Ver\xF6ffentlicht",
2462
+ "historyActions.unpublish": "Ver\xF6ffentlichung zur\xFCckgezogen",
2463
+ "historyActions.discard": "Entwurf verworfen",
2464
+ "historyActions.delete": "Gel\xF6scht",
2465
+ "historyActions.restore": "Wiederhergestellt",
2466
+ historyInitial: "Erste Version",
2467
+ historyNoChange: "Keine inhaltliche \xC4nderung",
2468
+ historyMissing: "Nicht aufgezeichnet (Erfassung fehlgeschlagen)",
2469
+ historyBlocksSummary: "Bl\xF6cke: {added} hinzugef\xFCgt, {removed} entfernt, {changed} ge\xE4ndert",
2470
+ historyFieldsSummary: "Felder: {fields}",
2471
+ historyNotKept: "Diese Version wird nicht mehr aufbewahrt.",
2472
+ historyCompare: "Verglichen mit dem Formular",
2473
+ historySame: "Gleicher Inhalt wie im Formular.",
2474
+ "diffOps.added": "Hinzugef\xFCgt",
2475
+ "diffOps.removed": "Entfernt",
2476
+ "diffOps.changed": "Ge\xE4ndert",
2477
+ historyDiffFields: "Abweichende Felder: {fields}",
2478
+ historyDiffNote: "Relationen werden nicht verglichen.",
2479
+ historyLoad: "Diese Version laden",
2480
+ historyLoaded: "Version ins Formular geladen. Nichts wird gespeichert, bis Sie speichern.",
2481
+ historyLeft: "Unver\xE4ndert gelassen: {fields}",
2482
+ historyRemovedFields: "Nicht mehr im Schema, nicht geladen: {fields}",
2483
+ historyMissingItems: "Existieren nicht mehr, ausgelassen: {media} Mediendateien, {relations} verkn\xFCpfte Eintr\xE4ge, {blocks} Bl\xF6cke",
2484
+ historyLoadFailed: "Diese Version konnte nicht geladen werden.",
2485
+ historyShowMore: "Mehr anzeigen",
2486
+ historySettings: "Versionsverlauf",
2487
+ historySettingsHelp: "Zeichnet jedes Erstellen, Speichern, Ver\xF6ffentlichen, Zur\xFCckziehen, Verwerfen und L\xF6schen \xFCber den Document Service von Strapi auf (Admin, REST- und GraphQL-API, andere Plugins), mit einem Snapshot des Inhalts. Redakteure sehen die Versionen in der Bearbeitungsansicht und k\xF6nnen eine ins Formular laden. Das ist Inhaltsaktivit\xE4t, kein Sicherheits-Audit-Log: direkte Datenbankschreibvorg\xE4nge werden nicht erfasst.",
2488
+ historyEnabled: "Versionen aufzeichnen",
2489
+ historyAll: "Alle Inhaltstypen",
2490
+ historyTypes: "Inhaltstypen",
2491
+ historyRetention: "Snapshots aufbewahren (Tage)",
2492
+ historyMax: "Snapshots pro Dokument (Maximum)",
2493
+ historyEventDays: "Aktivit\xE4t aufbewahren (Tage)",
2494
+ historyRetentionHelp: "Ein n\xE4chtlicher Job leert Snapshots, die \xE4lter sind oder das Maximum pro Dokument \xFCberschreiten (die neuesten bleiben), und l\xF6scht Aktivit\xE4t, die \xE4lter als ihr Limit ist. Mediendateien l\xF6scht er nie.",
2495
+ historyInvalid: "Ganze Zahlen: 1 bis 3650 Tage (die Aktivit\xE4t mindestens so lange wie die Snapshots) und 1 bis 1000 Snapshots."
2083
2496
  };
2084
2497
 
2085
2498
  // admin/translations/it.json
@@ -2157,7 +2570,6 @@ var it_default = {
2157
2570
  previewHelp: "Le modifiche non salvate compaiono nell'anteprima. Clicca un blocco per aprire il suo modulo, un'immagine per aprire la Libreria media, un testo per modificarlo.",
2158
2571
  previewNoUrl: "Nessun URL di anteprima: impostalo in Impostazioni, Blockscene, oppure configura l'Anteprima nativa di Strapi per questo tipo di contenuto.",
2159
2572
  previewFailed: "L'anteprima non ha risposto entro 15 s. Se il frontend sta ancora caricando, comparir\xE0 da sola; il modulo resta utilizzabile.",
2160
- previewReady: "Anteprima della pagina \xB7 modifiche non salvate",
2161
2573
  previewLoading: "Caricamento dell'anteprima\u2026",
2162
2574
  retry: "Riprova",
2163
2575
  editText: "Modifica testo",
@@ -2315,12 +2727,57 @@ var it_default = {
2315
2727
  confirmDeleteOption: "Chiedi conferma prima di eliminare un blocco",
2316
2728
  duplicateOption: 'Mostra "Duplica" su ogni blocco',
2317
2729
  clipboardOption: 'Mostra "Copia" e "Incolla" (tra pagine)',
2730
+ friendlyLabels: "Etichette dei campi leggibili",
2731
+ friendlyLabelsHelp: `I campi la cui etichetta \xE8 ancora il nome dell'attributo appaiono come parole ("mobileColumnsCount" diventa "Mobile columns count"). Le etichette impostate in "Configura la vista" e i testi della configurazione "fields" del progetto restano invariati.`,
2318
2732
  hiddenBlocks: "Blocchi nascosti sul sito",
2319
2733
  "hiddenModes.strip": "Rimossi dall'API dei contenuti",
2320
2734
  "hiddenModes.flag": "Inviati con il flag (decide il frontend)",
2321
2735
  "hiddenModes.off": "Disattivato (nessun pulsante nascondi)",
2322
2736
  hiddenBlocksHelp: "L'occhio su ogni blocco lo segna come nascosto sul sito. Rimossi: le letture dell'API dei contenuti (REST, GraphQL) lo omettono, l'admin lo mostra sempre. Flag: viene inviato con l'attributo e decide il frontend.",
2323
- hiddenAttributeOff: "La configurazione del plugin imposta hiddenAttribute a false: i blocchi non possono essere nascosti."
2737
+ hiddenAttributeOff: "La configurazione del plugin imposta hiddenAttribute a false: i blocchi non possono essere nascosti.",
2738
+ historyTitle: "Cronologia",
2739
+ historyEmpty: "Ancora nessuna versione. Da ora vengono registrati salvataggi, pubblicazioni ed eliminazioni.",
2740
+ historyLoading: "Caricamento della cronologia\u2026",
2741
+ historyFailed: "Impossibile caricare la cronologia.",
2742
+ historyRefresh: "Aggiorna cronologia",
2743
+ historySystem: "Sistema",
2744
+ "historyActions.create": "Creato",
2745
+ "historyActions.update": "Salvato",
2746
+ "historyActions.publish": "Pubblicato",
2747
+ "historyActions.unpublish": "Pubblicazione annullata",
2748
+ "historyActions.discard": "Bozza scartata",
2749
+ "historyActions.delete": "Eliminato",
2750
+ "historyActions.restore": "Ripristinato",
2751
+ historyInitial: "Prima versione",
2752
+ historyNoChange: "Nessuna modifica al contenuto",
2753
+ historyMissing: "Non registrato (acquisizione non riuscita)",
2754
+ historyBlocksSummary: "Blocchi: {added} aggiunti, {removed} rimossi, {changed} modificati",
2755
+ historyFieldsSummary: "Campi: {fields}",
2756
+ historyNotKept: "Questa versione non \xE8 pi\xF9 conservata.",
2757
+ historyCompare: "Confrontato con il modulo",
2758
+ historySame: "Stesso contenuto del modulo.",
2759
+ "diffOps.added": "Aggiunto",
2760
+ "diffOps.removed": "Rimosso",
2761
+ "diffOps.changed": "Modificato",
2762
+ historyDiffFields: "Campi diversi: {fields}",
2763
+ historyDiffNote: "Le relazioni non vengono confrontate.",
2764
+ historyLoad: "Carica questa versione",
2765
+ historyLoaded: "Versione caricata nel modulo. Nulla viene salvato finch\xE9 non salvi.",
2766
+ historyLeft: "Lasciati come sono: {fields}",
2767
+ historyRemovedFields: "Non pi\xF9 nello schema, non caricati: {fields}",
2768
+ historyMissingItems: "Non esistono pi\xF9, esclusi: {media} file multimediali, {relations} voci collegate, {blocks} blocchi",
2769
+ historyLoadFailed: "Impossibile caricare questa versione.",
2770
+ historyShowMore: "Mostra altro",
2771
+ historySettings: "Cronologia delle versioni",
2772
+ historySettingsHelp: "Registra ogni creazione, salvataggio, pubblicazione, annullamento della pubblicazione, scarto e eliminazione fatti tramite il document service di Strapi (l'admin, le API REST e GraphQL, altri plugin), con un'istantanea del contenuto. Gli editor vedono le versioni nella vista di modifica e possono caricarne una nel modulo. \xC8 attivit\xE0 sui contenuti, non un registro di audit di sicurezza: le scritture dirette nel database non vengono viste.",
2773
+ historyEnabled: "Registra le versioni",
2774
+ historyAll: "Tutti i tipi di contenuto",
2775
+ historyTypes: "Tipi di contenuto",
2776
+ historyRetention: "Conserva le istantanee (giorni)",
2777
+ historyMax: "Istantanee per documento (massimo)",
2778
+ historyEventDays: "Conserva l'attivit\xE0 (giorni)",
2779
+ historyRetentionHelp: "Un'attivit\xE0 notturna svuota le istantanee pi\xF9 vecchie di cos\xEC o oltre il massimo per documento (le pi\xF9 recenti restano) ed elimina l'attivit\xE0 pi\xF9 vecchia del suo limite. Non elimina mai file multimediali.",
2780
+ historyInvalid: "Numeri interi: da 1 a 3650 giorni (l'attivit\xE0 conservata almeno quanto le istantanee) e da 1 a 1000 istantanee."
2324
2781
  };
2325
2782
 
2326
2783
  // admin/translations/nl.json
@@ -2398,7 +2855,6 @@ var nl_default = {
2398
2855
  previewHelp: "Niet-opgeslagen wijzigingen verschijnen in het voorbeeld. Klik op een blok om het formulier te openen, op een afbeelding voor de Mediabibliotheek, op een tekst om die te bewerken.",
2399
2856
  previewNoUrl: "Geen voorbeeld-URL: stel die in bij Instellingen, Blockscene, of configureer Strapi's native Preview voor dit contenttype.",
2400
2857
  previewFailed: "Het voorbeeld antwoordde niet binnen 15 s. Als de frontend nog laadt, verschijnt het vanzelf; het formulier blijft bruikbaar.",
2401
- previewReady: "Paginavoorbeeld \xB7 niet-opgeslagen wijzigingen",
2402
2858
  previewLoading: "Voorbeeld laden\u2026",
2403
2859
  retry: "Opnieuw proberen",
2404
2860
  editText: "Tekst bewerken",
@@ -2556,12 +3012,57 @@ var nl_default = {
2556
3012
  confirmDeleteOption: "Bevestigen voordat een blok wordt verwijderd",
2557
3013
  duplicateOption: '"Dupliceren" op elk blok tonen',
2558
3014
  clipboardOption: `"Kopi\xEBren" en "Plakken" tonen (tussen pagina's)`,
3015
+ friendlyLabels: "Leesbare veldlabels",
3016
+ friendlyLabelsHelp: 'Velden waarvan het label nog de attribuutnaam is, verschijnen als woorden ("mobileColumnsCount" wordt "Mobile columns count"). Labels uit "Weergave configureren" en teksten uit de "fields"-configuratie van het project blijven behouden.',
2559
3017
  hiddenBlocks: "Op de site verborgen blokken",
2560
3018
  "hiddenModes.strip": "Verwijderd uit de content-API",
2561
3019
  "hiddenModes.flag": "Verzonden met de markering (de frontend beslist)",
2562
3020
  "hiddenModes.off": "Uit (geen verbergknop)",
2563
3021
  hiddenBlocksHelp: "Het oog op elk blok markeert het als verborgen op de site. Verwijderd: leesverzoeken van de content-API (REST, GraphQL) laten het weg, de admin toont het altijd. Markering: het wordt met het attribuut verzonden en de frontend beslist.",
2564
- hiddenAttributeOff: "De pluginconfiguratie zet hiddenAttribute op false: blokken kunnen niet verborgen worden."
3022
+ hiddenAttributeOff: "De pluginconfiguratie zet hiddenAttribute op false: blokken kunnen niet verborgen worden.",
3023
+ historyTitle: "Geschiedenis",
3024
+ historyEmpty: "Nog geen versies. Opslaan, publiceren en verwijderen worden vanaf nu vastgelegd.",
3025
+ historyLoading: "Geschiedenis laden\u2026",
3026
+ historyFailed: "De geschiedenis kon niet worden geladen.",
3027
+ historyRefresh: "Geschiedenis vernieuwen",
3028
+ historySystem: "Systeem",
3029
+ "historyActions.create": "Aangemaakt",
3030
+ "historyActions.update": "Opgeslagen",
3031
+ "historyActions.publish": "Gepubliceerd",
3032
+ "historyActions.unpublish": "Publicatie ingetrokken",
3033
+ "historyActions.discard": "Concept verworpen",
3034
+ "historyActions.delete": "Verwijderd",
3035
+ "historyActions.restore": "Hersteld",
3036
+ historyInitial: "Eerste versie",
3037
+ historyNoChange: "Geen inhoudelijke wijziging",
3038
+ historyMissing: "Niet vastgelegd (vastleggen mislukt)",
3039
+ historyBlocksSummary: "Blokken: {added} toegevoegd, {removed} verwijderd, {changed} gewijzigd",
3040
+ historyFieldsSummary: "Velden: {fields}",
3041
+ historyNotKept: "Deze versie wordt niet meer bewaard.",
3042
+ historyCompare: "Vergeleken met het formulier",
3043
+ historySame: "Zelfde inhoud als het formulier.",
3044
+ "diffOps.added": "Toegevoegd",
3045
+ "diffOps.removed": "Verwijderd",
3046
+ "diffOps.changed": "Gewijzigd",
3047
+ historyDiffFields: "Afwijkende velden: {fields}",
3048
+ historyDiffNote: "Relaties worden niet vergeleken.",
3049
+ historyLoad: "Deze versie laden",
3050
+ historyLoaded: "Versie in het formulier geladen. Er wordt niets opgeslagen totdat je opslaat.",
3051
+ historyLeft: "Gelaten zoals ze zijn: {fields}",
3052
+ historyRemovedFields: "Niet meer in het schema, niet geladen: {fields}",
3053
+ historyMissingItems: "Bestaan niet meer, weggelaten: {media} mediabestanden, {relations} gekoppelde items, {blocks} blokken",
3054
+ historyLoadFailed: "Deze versie kon niet worden geladen.",
3055
+ historyShowMore: "Meer tonen",
3056
+ historySettings: "Versiegeschiedenis",
3057
+ historySettingsHelp: "Legt elke aanmaak, opslag, publicatie, intrekking, verwerping en verwijdering via de document service van Strapi vast (de admin, de REST- en GraphQL-API's, andere plugins), met een momentopname van de inhoud. Redacteuren zien de versies in de bewerkweergave en kunnen er een in het formulier laden. Dit is inhoudsactiviteit, geen beveiligingsauditlog: rechtstreekse schrijfacties in de database worden niet gezien.",
3058
+ historyEnabled: "Versies vastleggen",
3059
+ historyAll: "Alle inhoudstypen",
3060
+ historyTypes: "Inhoudstypen",
3061
+ historyRetention: "Momentopnamen bewaren (dagen)",
3062
+ historyMax: "Momentopnamen per document (maximum)",
3063
+ historyEventDays: "Activiteit bewaren (dagen)",
3064
+ historyRetentionHelp: "Een nachtelijke taak leegt momentopnamen die ouder zijn of boven het maximum per document uitkomen (de nieuwste blijven) en verwijdert activiteit die ouder is dan de limiet. Mediabestanden worden nooit verwijderd.",
3065
+ historyInvalid: "Gehele getallen: 1 tot 3650 dagen (de activiteit minstens zo lang bewaard als de momentopnamen) en 1 tot 1000 momentopnamen."
2565
3066
  };
2566
3067
 
2567
3068
  // admin/messages.ts
@@ -2595,9 +3096,9 @@ function useAnchors(zones, onOpen) {
2595
3096
  sync.current = () => {
2596
3097
  const next = [];
2597
3098
  for (const zone of latest.current) {
2598
- const list = zone.uids?.length ? findZoneList(zone.label) : null;
2599
- if (!list) continue;
2600
- toggles(list).forEach((button, index) => {
3099
+ const list2 = zone.uids?.length ? findZoneList(zone.label) : null;
3100
+ if (!list2) continue;
3101
+ toggles(list2).forEach((button, index) => {
2601
3102
  const uid = zone.uids[index];
2602
3103
  if (!uid) return;
2603
3104
  let el = button.querySelector(
@@ -2808,7 +3309,7 @@ function toConnect(relations, target) {
2808
3309
  id: r.id,
2809
3310
  apiData: { id: r.id, documentId: r.documentId, locale: r.locale, isTemporary: true },
2810
3311
  status: r.status,
2811
- label: r.documentId,
3312
+ label: r.label ?? r.documentId,
2812
3313
  href: `../collection-types/${target}/${r.documentId}${r.locale ? `?plugins[i18n][locale]=${r.locale}` : ""}`,
2813
3314
  __temp_key__: keys[i]
2814
3315
  })),
@@ -2983,9 +3484,9 @@ function useAnchors2(zones, enabled) {
2983
3484
  const next = [];
2984
3485
  if (enabled)
2985
3486
  for (const zone of latest.current) {
2986
- const list = zone.count ? findZoneList(zone.label) : null;
2987
- if (!list) continue;
2988
- toggles(list).forEach((toggle, index) => {
3487
+ const list2 = zone.count ? findZoneList(zone.label) : null;
3488
+ if (!list2) continue;
3489
+ toggles(list2).forEach((toggle, index) => {
2989
3490
  const trash = trashOf(toggle);
2990
3491
  if (!trash || index >= zone.count) return;
2991
3492
  const holder = trash.parentElement !== actionsOf(toggle) && trash.parentElement?.children.length === 1 ? trash.parentElement : trash;
@@ -3103,9 +3604,9 @@ function RowActions({ zones, actions, Modal: Modal2 }) {
3103
3604
  const button = event.target?.closest?.("button");
3104
3605
  if (!button || bypass.current) return;
3105
3606
  for (const zone of latest.current.zones) {
3106
- const list = findZoneList(zone.label);
3107
- if (!list?.contains(button)) continue;
3108
- const headers = toggles(list);
3607
+ const list2 = findZoneList(zone.label);
3608
+ if (!list2?.contains(button)) continue;
3609
+ const headers = toggles(list2);
3109
3610
  const index = headers.findIndex((toggle) => trashOf(toggle) === button);
3110
3611
  if (index < 0) return;
3111
3612
  event.preventDefault();
@@ -3567,7 +4068,7 @@ var readCollapsed = () => {
3567
4068
  };
3568
4069
  var TYPE_ICONS = { hero: "hero", text: "text", media: "image", listing: "list", cards: "cards", cta: "link", form: "form", layout: "layout" };
3569
4070
  var FILTER_MENUS = ["tags", "media", "content"];
3570
- var toggled = (list, value) => list.includes(value) ? list.filter((item) => item !== value) : [...list, value];
4071
+ var toggled = (list2, value) => list2.includes(value) ? list2.filter((item) => item !== value) : [...list2, value];
3571
4072
  function FilterMenu({ name, label, options, picked, onChange, labelOf }) {
3572
4073
  const [open, setOpen] = React4.useState(false);
3573
4074
  const ref = React4.useRef(null);
@@ -4048,7 +4549,7 @@ function ZoneGallery({
4048
4549
  };
4049
4550
  const star = (uid) => savePrefs({ ...prefs, starred: toggled(prefs.starred, uid) });
4050
4551
  const options = config2 ? optionsFor(zone, components, config2) : null;
4051
- const inZone = (list) => options ? list.filter((uid) => options.uids.includes(uid)) : [];
4552
+ const inZone = (list2) => options ? list2.filter((uid) => options.uids.includes(uid)) : [];
4052
4553
  const filter = {
4053
4554
  ...picked,
4054
4555
  typology: TYPOLOGIES.includes(view) ? view : void 0,
@@ -4325,16 +4826,16 @@ function useInitialAccordions({
4325
4826
  };
4326
4827
  document.addEventListener("click", cancel, true);
4327
4828
  const timer = setInterval(() => {
4328
- const pending = latest.current.filter((zone) => zone.count > 0);
4329
- const ready = pending.length > 0 && pending.every((zone) => {
4330
- const list = findZoneList(zone.label);
4331
- return list && toggles(list).length >= zone.count;
4829
+ const pending2 = latest.current.filter((zone) => zone.count > 0);
4830
+ const ready = pending2.length > 0 && pending2.every((zone) => {
4831
+ const list2 = findZoneList(zone.label);
4832
+ return list2 && toggles(list2).length >= zone.count;
4332
4833
  }) && (editor.initialState !== "remember" || user.current);
4333
4834
  if (!ready && ++tries < 25) return;
4334
4835
  stop();
4335
- for (const zone of pending) {
4336
- const list = findZoneList(zone.label);
4337
- if (!list) continue;
4836
+ for (const zone of pending2) {
4837
+ const list2 = findZoneList(zone.label);
4838
+ if (!list2) continue;
4338
4839
  const key = memoryKey({
4339
4840
  base: memoryBase(),
4340
4841
  userId: user.current,
@@ -4342,7 +4843,7 @@ function useInitialAccordions({
4342
4843
  zone: zone.name
4343
4844
  });
4344
4845
  setAll(
4345
- list,
4846
+ list2,
4346
4847
  initialState(editor, readMemory(storage2(), key)) === "open",
4347
4848
  unstable_batchedUpdates
4348
4849
  );
@@ -4358,8 +4859,8 @@ function useLabelAnchor(zone, enabled) {
4358
4859
  if (!enabled) return;
4359
4860
  let frame2 = 0;
4360
4861
  const sync = () => {
4361
- const list = findZoneList(zone.label);
4362
- const row = list && [...list.parentElement?.children || []].find((child) => child !== list && child.textContent?.trim());
4862
+ const list2 = findZoneList(zone.label);
4863
+ const row = list2 && [...list2.parentElement?.children || []].find((child) => child !== list2 && child.textContent?.trim());
4363
4864
  let anchor = document.querySelector(`[${ZONE_ATTR}="${CSS.escape(zone.name)}"]`);
4364
4865
  if (anchor && anchor.parentElement !== row) {
4365
4866
  anchor.remove();
@@ -4396,8 +4897,8 @@ function ZoneControls({ zone, editor, contentType, userId, tools }) {
4396
4897
  const anchor = useLabelAnchor(zone, shown);
4397
4898
  if (!shown || !anchor) return null;
4398
4899
  const apply = (open) => {
4399
- const list = findZoneList(zone.label);
4400
- if (list) setAll(list, open, unstable_batchedUpdates);
4900
+ const list2 = findZoneList(zone.label);
4901
+ if (list2) setAll(list2, open, unstable_batchedUpdates);
4401
4902
  writeMemory(
4402
4903
  storage2(),
4403
4904
  memoryKey({ base: memoryBase(), userId, contentType, zone: zone.name }),
@@ -4458,7 +4959,8 @@ function Gallery({
4458
4959
  import * as React5 from "react";
4459
4960
  import { createPortal as createPortal4 } from "react-dom";
4460
4961
  import styled4, { useTheme } from "styled-components";
4461
- import { Box as Box2, Button as Button3, Flex as Flex3, Typography as Typography3 } from "@strapi/design-system";
4962
+ import { Box as Box2, Button as Button3, Flex as Flex3, MenuItem, SimpleMenu, Status, Typography as Typography3 } from "@strapi/design-system";
4963
+ import { useIntl as useIntl2 } from "react-intl";
4462
4964
  import {
4463
4965
  DescriptionComponentRenderer,
4464
4966
  useFetchClient,
@@ -4703,6 +5205,9 @@ function ToolbarActions({ model, collectionType, documentId, locale }) {
4703
5205
  );
4704
5206
  const { toggleNotification } = useNotification();
4705
5207
  const t = useMessages();
5208
+ const { formatMessage } = useIntl2();
5209
+ const modified = useForm("BlocksceneToolbar", (state) => state.modified);
5210
+ const status = documentId ? doc?.status : void 0;
4706
5211
  const descriptions = (plugins["content-manager"]?.apis?.getDocumentActions?.("panel") || []).filter((d) => ACTION_TYPES.includes(d.type));
4707
5212
  const props = {
4708
5213
  activeTab: query.status || "draft",
@@ -4731,23 +5236,36 @@ function ToolbarActions({ model, collectionType, documentId, locale }) {
4731
5236
  const typeOf = (action) => action.type || descriptions.find(
4732
5237
  (d) => typeof action.id === "string" && action.id.startsWith(`${d.name || d.displayName}-`)
4733
5238
  )?.type;
4734
- return /* @__PURE__ */ jsx6(DescriptionComponentRenderer, { props, descriptions, children: (actions) => /* @__PURE__ */ jsx6(Flex3, { gap: 1, "data-testid": "page-preview-actions", children: [...actions].sort(
4735
- (a, b) => ACTION_TYPES.indexOf(typeOf(a)) - ACTION_TYPES.indexOf(typeOf(b))
4736
- ).map((action) => {
4737
- const reason = action.disabled ? typeOf(action) === "publish" ? t.publishDisabledHint : t.saveDisabledHint : void 0;
4738
- return /* @__PURE__ */ jsx6("span", { title: reason, children: /* @__PURE__ */ jsx6(
4739
- Button3,
5239
+ return /* @__PURE__ */ jsx6(DescriptionComponentRenderer, { props, descriptions, children: (actions) => /* @__PURE__ */ jsxs4(Flex3, { gap: 2, alignItems: "center", "data-testid": "page-preview-actions", children: [
5240
+ status && /* @__PURE__ */ jsx6(
5241
+ Status,
4740
5242
  {
4741
5243
  size: "S",
4742
- variant: action.variant || "default",
4743
- disabled: action.disabled,
4744
- loading: action.loading,
4745
- "aria-description": reason,
4746
- onClick: (e) => run(action, e),
4747
- children: action.label
5244
+ role: "status",
5245
+ "data-testid": "page-preview-status",
5246
+ variant: status === "draft" ? "secondary" : status === "published" ? "success" : "alternative",
5247
+ children: /* @__PURE__ */ jsx6(Typography3, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({ id: `content-manager.containers.List.${status}`, defaultMessage: status.charAt(0).toUpperCase() + status.slice(1) }) })
4748
5248
  }
4749
- ) }, action.id);
4750
- }) }) });
5249
+ ),
5250
+ modified && /* @__PURE__ */ jsx6(Typography3, { variant: "pi", textColor: "neutral600", children: t.unsaved }),
5251
+ [...actions].sort(
5252
+ (a, b) => ACTION_TYPES.indexOf(typeOf(a)) - ACTION_TYPES.indexOf(typeOf(b))
5253
+ ).map((action) => {
5254
+ const reason = action.disabled ? typeOf(action) === "publish" ? t.publishDisabledHint : t.saveDisabledHint : void 0;
5255
+ return /* @__PURE__ */ jsx6("span", { title: reason, children: /* @__PURE__ */ jsx6(
5256
+ Button3,
5257
+ {
5258
+ size: "S",
5259
+ variant: typeOf(action) === "publish" ? "default" : "secondary",
5260
+ disabled: action.disabled,
5261
+ loading: action.loading,
5262
+ "aria-description": reason,
5263
+ onClick: (e) => run(action, e),
5264
+ children: action.label
5265
+ }
5266
+ ) }, action.id);
5267
+ })
5268
+ ] }) });
4751
5269
  }
4752
5270
  var COALESCE_MS = 400;
4753
5271
  var clone = (value) => {
@@ -5223,9 +5741,9 @@ function PagePreview({
5223
5741
  const uid = index >= 0 ? latest.current[index]?.__component : "";
5224
5742
  if (index < 0) return;
5225
5743
  if (is("select")) {
5226
- const list = findZoneList(zoneLabel2);
5227
- const item = list?.querySelectorAll(":scope > li")[index];
5228
- const header = list ? toggles(list)[index] : null;
5744
+ const list2 = findZoneList(zoneLabel2);
5745
+ const item = list2?.querySelectorAll(":scope > li")[index];
5746
+ const header = list2 ? toggles(list2)[index] : null;
5229
5747
  if (header?.getAttribute("aria-expanded") === "false") header.click();
5230
5748
  item?.scrollIntoView({ block: "center", behavior: "smooth" });
5231
5749
  if (item) {
@@ -5286,9 +5804,9 @@ function PagePreview({
5286
5804
  iframe.current?.contentWindow?.postMessage({ protocol: PROTOCOL, channel, type: "hover", key }, origin);
5287
5805
  };
5288
5806
  const onOver = (event) => {
5289
- const list = findZoneList(live.current.zoneLabel);
5807
+ const list2 = findZoneList(live.current.zoneLabel);
5290
5808
  const target = event.target;
5291
- const index = list?.contains(target) ? [...list.querySelectorAll(":scope > li")].findIndex((li) => li.contains(target)) : -1;
5809
+ const index = list2?.contains(target) ? [...list2.querySelectorAll(":scope > li")].findIndex((li) => li.contains(target)) : -1;
5292
5810
  post(index >= 0 ? blockKey(latest.current[index]) : null);
5293
5811
  };
5294
5812
  const onOut = (event) => {
@@ -5345,16 +5863,23 @@ function PagePreview({
5345
5863
  /* @__PURE__ */ jsx6(Tool, { icon: "undo", label: `${t.undo} (${MAC ? "\u2318Z" : "Ctrl+Z"})`, disabled: !history.canUndo, onClick: history.undo }),
5346
5864
  /* @__PURE__ */ jsx6(Tool, { icon: "redo", label: `${t.redo} (${MAC ? "\u21E7\u2318Z" : "Ctrl+Y"})`, disabled: !history.canRedo, onClick: history.redo })
5347
5865
  ] });
5348
- const devices = /* @__PURE__ */ jsx6(Flex3, { gap: 1, wrap: "wrap", "data-testid": "page-preview-devices", role: "group", "aria-label": t.deviceGroup, children: Object.keys(DEVICES).map((value) => /* @__PURE__ */ jsx6(
5349
- Tool,
5866
+ const deviceLabel = (value) => DEVICES[value] ? `${t.device[value]} \xB7 ${DEVICES[value]} px` : t.device[value];
5867
+ const devices = /* @__PURE__ */ jsx6(Box2, { "data-testid": "page-preview-devices", children: /* @__PURE__ */ jsx6(
5868
+ SimpleMenu,
5350
5869
  {
5351
- icon: value,
5352
- label: DEVICES[value] ? `${t.device[value]} \xB7 ${DEVICES[value]} px` : t.device[value],
5353
- active: device === value,
5354
- onClick: () => setDevice(value)
5355
- },
5356
- value
5357
- )) });
5870
+ variant: "tertiary",
5871
+ size: "S",
5872
+ "aria-label": `${t.deviceGroup}: ${deviceLabel(device)}`,
5873
+ label: /* @__PURE__ */ jsxs4(Flex3, { gap: 2, alignItems: "center", children: [
5874
+ /* @__PURE__ */ jsx6(Icon, { name: device, size: 16 }),
5875
+ t.device[device]
5876
+ ] }),
5877
+ children: Object.keys(DEVICES).map((value) => /* @__PURE__ */ jsx6(MenuItem, { onSelect: () => setDevice(value), "aria-checked": device === value, children: /* @__PURE__ */ jsxs4(Flex3, { gap: 2, alignItems: "center", children: [
5878
+ /* @__PURE__ */ jsx6(Icon, { name: value, size: 16 }),
5879
+ deviceLabel(value)
5880
+ ] }) }, value))
5881
+ }
5882
+ ) });
5358
5883
  const onKey = (event) => {
5359
5884
  const map = {
5360
5885
  ArrowLeft: paneWidth + KEY_STEP,
@@ -5514,7 +6039,7 @@ function PagePreview({
5514
6039
  wrap: "wrap",
5515
6040
  style: { marginLeft: "auto" },
5516
6041
  children: [
5517
- /* @__PURE__ */ jsx6(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : ready ? t.previewReady : t.previewLoading }),
6042
+ !ready && /* @__PURE__ */ jsx6(Typography3, { variant: "pi", role: "status", textColor: "neutral600", children: failed ? t.previewFailed : t.previewLoading }),
5518
6043
  failed && /* @__PURE__ */ jsx6(Button3, { size: "S", variant: "secondary", onClick: retry, children: t.retry }),
5519
6044
  /* @__PURE__ */ jsx6(
5520
6045
  ToolbarActions,
@@ -5756,11 +6281,254 @@ function PagePreview({
5756
6281
  ] });
5757
6282
  }
5758
6283
 
5759
- // admin/Settings.tsx
6284
+ // admin/History.tsx
5760
6285
  import * as React6 from "react";
5761
- import { Box as Box3, Button as Button4, Flex as Flex4, Searchbar as Searchbar2, Typography as Typography4 } from "@strapi/design-system";
5762
- import styled5 from "styled-components";
6286
+ import { Box as Box3, Button as Button4, Flex as Flex4, Typography as Typography4 } from "@strapi/design-system";
6287
+ import { useForm as useForm2, useQueryParams as useQueryParams2, useRBAC } from "@strapi/strapi/admin";
6288
+
6289
+ // admin/versions.mjs
6290
+ var import_diff = __toESM(require_diff(), 1);
6291
+ import { generateNKeysBetween as generateNKeysBetween3 } from "fractional-indexing";
6292
+ var targetOf = (attr) => attr.targetModel || attr.target;
6293
+ var list = (value) => Array.isArray(value) ? value : value == null ? [] : [value];
6294
+ var withKeys = (rows) => {
6295
+ const keys = rows.length ? generateNKeysBetween3(null, null, rows.length) : [];
6296
+ return rows.map((row, i) => ({ ...row, __temp_key__: keys[i] }));
6297
+ };
6298
+ function formDiff(snapshot, formValues, schema, components) {
6299
+ const schemaOf = (uid) => components?.[uid];
6300
+ const version = (0, import_diff.canonical)(snapshot, schema, schemaOf, { relations: false });
6301
+ const form = (0, import_diff.canonical)(formValues, schema, schemaOf, { relations: false });
6302
+ const zones = [];
6303
+ const fields = [];
6304
+ for (const [name, attr] of Object.entries(schema?.attributes || {})) {
6305
+ if (!(0, import_diff.kept)(name, attr) || attr.type === "relation") continue;
6306
+ if (attr.type === "dynamiczone") {
6307
+ const ops = (0, import_diff.diffRows)(form[name] || [], version[name] || []).filter((op) => op.op !== "same");
6308
+ if (ops.length) zones.push({ name, ops });
6309
+ } else if (!(0, import_diff.same)(form[name], version[name])) fields.push(name);
6310
+ }
6311
+ return { fields, zones };
6312
+ }
6313
+ function loadVersion(snapshot, { schema, components, current = {}, media = {}, relations = {}, server = {}, editable = () => true }) {
6314
+ const schemaOf = (uid) => components?.[uid];
6315
+ const report = { left: [], removed: [], blocks: 0, media: 0, relations: 0 };
6316
+ const file = (id) => media[id] || (report.media++, null);
6317
+ const resolve = (attr, refs) => {
6318
+ const targets = relations[targetOf(attr)] || [];
6319
+ const found = [];
6320
+ for (const ref of refs) {
6321
+ const hit = targets.find((r) => r.documentId === ref.documentId && (r.locale || null) === (ref.locale || null)) || targets.find((r) => r.documentId === ref.documentId);
6322
+ if (hit) found.push(hit);
6323
+ else report.relations++;
6324
+ }
6325
+ return found;
6326
+ };
6327
+ const build = (data, schema2) => {
6328
+ const out = {};
6329
+ for (const [name, attr] of Object.entries(schema2?.attributes || {})) {
6330
+ const value = data?.[name];
6331
+ if (!(0, import_diff.kept)(name, attr)) continue;
6332
+ if (attr.type === "relation") out[name] = toConnect(resolve(attr, list(value)), targetOf(attr));
6333
+ else if (value === void 0 || value === null) continue;
6334
+ else if (attr.type === "media") {
6335
+ if (attr.multiple) out[name] = list(value).map(file).filter(Boolean);
6336
+ else out[name] = file(value);
6337
+ } else if (attr.type === "component") {
6338
+ const child = schemaOf(attr.component);
6339
+ out[name] = attr.repeatable ? withKeys(list(value).map((item) => build(item, child))) : build(value, child);
6340
+ } else if (attr.type === "dynamiczone") out[name] = zone(attr, value);
6341
+ else out[name] = structuredClone(value);
6342
+ }
6343
+ return out;
6344
+ };
6345
+ const zone = (attr, rows) => withKeys(list(rows).filter((row) => {
6346
+ const ok = row && attr.components?.includes(row.__component) && schemaOf(row.__component);
6347
+ if (!ok) report.blocks++;
6348
+ return ok;
6349
+ }).map((row) => ({ ...build(row, schemaOf(row.__component)), __component: row.__component })));
6350
+ const values = { ...current };
6351
+ for (const name of Object.keys(snapshot || {})) if (!schema?.attributes?.[name]) report.removed.push(name);
6352
+ for (const [name, attr] of Object.entries(schema?.attributes || {})) {
6353
+ if (!(0, import_diff.kept)(name, attr)) continue;
6354
+ const value = snapshot?.[name];
6355
+ const empty = value === void 0 || value === null;
6356
+ if (!editable(name)) {
6357
+ if (!empty || current[name] != null) report.left.push(name);
6358
+ continue;
6359
+ }
6360
+ if (attr.type === "relation") {
6361
+ if (!Array.isArray(server[name])) {
6362
+ report.left.push(name);
6363
+ continue;
6364
+ }
6365
+ const wanted = resolve(attr, list(value));
6366
+ const has = new Set(server[name].map((r) => r.documentId));
6367
+ const keep = new Set(wanted.map((r) => r.documentId));
6368
+ values[name] = {
6369
+ connect: toConnect(wanted.filter((r) => !has.has(r.documentId)), targetOf(attr)).connect,
6370
+ disconnect: server[name].filter((r) => !keep.has(r.documentId)).map((r) => ({ id: r.id, status: r.status, apiData: { id: r.id, documentId: r.documentId, locale: r.locale } }))
6371
+ };
6372
+ } else if (attr.type === "component" || attr.type === "dynamiczone") {
6373
+ if (attr.type === "dynamiczone") values[name] = zone(attr, value);
6374
+ else if (attr.repeatable) values[name] = withKeys(list(value).map((item) => build(item, schemaOf(attr.component))));
6375
+ else values[name] = empty ? null : build(value, schemaOf(attr.component));
6376
+ } else if (attr.type === "media") {
6377
+ values[name] = attr.multiple ? list(value).map(file).filter(Boolean) : empty ? null : file(value);
6378
+ } else if (!empty) values[name] = structuredClone(value);
6379
+ else if (current[name] != null && current[name] !== "") values[name] = null;
6380
+ }
6381
+ return { values, report };
6382
+ }
6383
+
6384
+ // admin/History.tsx
5763
6385
  import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
6386
+ var READ = [{ action: "plugin::blockscene.history.read", subject: null }];
6387
+ var PAGE = 10;
6388
+ function History({ model, documentId, schema, components, editable, disabled, get, relationsOf: relationsOf2 }) {
6389
+ const t = useMessages();
6390
+ const { allowedActions, isLoading } = useRBAC(READ);
6391
+ const [{ query }] = useQueryParams2();
6392
+ const locale = typeof query?.plugins?.i18n?.locale === "string" ? query.plugins.i18n.locale : "";
6393
+ const values = useForm2("BlocksceneVersions", (state) => state.values);
6394
+ const initialValues = useForm2("BlocksceneVersions", (state) => state.initialValues);
6395
+ const setValues = useForm2("BlocksceneVersions", (state) => state.setValues);
6396
+ const [events, setEvents] = React6.useState(null);
6397
+ const [failed, setFailed] = React6.useState(false);
6398
+ const [shown, setShown] = React6.useState(PAGE);
6399
+ const [open, setOpen] = React6.useState(null);
6400
+ const [report, setReport] = React6.useState(null);
6401
+ const [loading, setLoading] = React6.useState(false);
6402
+ const [refresh, reload] = React6.useReducer((n) => n + 1, 0);
6403
+ const allowed = Boolean(allowedActions?.canRead);
6404
+ React6.useEffect(() => {
6405
+ if (!allowed || !documentId) return;
6406
+ let active = true;
6407
+ setFailed(false);
6408
+ get(`/blockscene/history/${encodeURIComponent(model)}/${encodeURIComponent(documentId)}${locale ? `?locale=${encodeURIComponent(locale)}` : ""}`).then(({ data }) => active && setEvents(Array.isArray(data?.results) ? data.results : [])).catch(() => active && setFailed(true));
6409
+ return () => {
6410
+ active = false;
6411
+ };
6412
+ }, [allowed, get, model, documentId, locale, initialValues, refresh]);
6413
+ React6.useEffect(() => {
6414
+ setOpen(null);
6415
+ setReport(null);
6416
+ setShown(PAGE);
6417
+ }, [model, documentId, locale]);
6418
+ if (isLoading || !allowed || !documentId) return null;
6419
+ const name = (uid) => components?.[uid]?.info?.displayName || uid;
6420
+ const when = (at) => {
6421
+ try {
6422
+ return new Date(at).toLocaleString(t.locale, { dateStyle: "medium", timeStyle: "short" });
6423
+ } catch {
6424
+ return at;
6425
+ }
6426
+ };
6427
+ const summary = (s) => {
6428
+ if (!s) return "";
6429
+ if (s.missing) return t.historyMissing;
6430
+ if (s.initial) return t.historyInitial;
6431
+ const parts = [];
6432
+ const b = s.blocks || {};
6433
+ if (b.added || b.removed || b.changed) parts.push(t.f("historyBlocksSummary", { added: b.added || 0, removed: b.removed || 0, changed: b.changed || 0 }));
6434
+ if (s.fields?.length) parts.push(t.f("historyFieldsSummary", { fields: s.fields.join(", ") }));
6435
+ return parts.join(" \xB7 ") || t.historyNoChange;
6436
+ };
6437
+ const select = async (event) => {
6438
+ setReport(null);
6439
+ if (open?.event?.id === event.id) return setOpen(null);
6440
+ setOpen({ event, loading: true });
6441
+ try {
6442
+ const { data } = await get(`/blockscene/history-events/${event.id}`);
6443
+ setOpen({ event, ...data });
6444
+ } catch {
6445
+ setOpen({ event, failed: true });
6446
+ }
6447
+ };
6448
+ const load2 = async () => {
6449
+ if (!open?.snapshot || typeof setValues !== "function" || disabled) return;
6450
+ setLoading(true);
6451
+ setReport(null);
6452
+ try {
6453
+ const server = {};
6454
+ for (const [field, attr] of Object.entries(schema?.attributes || {}))
6455
+ if (attr?.type === "relation" && editable(field)) server[field] = await relationsOf2(get, { uid: model, id: documentId, field }, locale).catch(() => void 0);
6456
+ const { values: next, report: report2 } = loadVersion(open.snapshot, { schema, components, current: values, media: open.media, relations: open.relations, server, editable });
6457
+ setValues(next);
6458
+ setReport(report2);
6459
+ } catch {
6460
+ setReport({ failed: true });
6461
+ } finally {
6462
+ setLoading(false);
6463
+ }
6464
+ };
6465
+ const diff = open?.snapshot ? formDiff(open.snapshot, values, schema, components) : null;
6466
+ const same2 = diff && !diff.fields.length && !diff.zones.length;
6467
+ return /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 2, "data-testid": "blockscene-versions", children: [
6468
+ /* @__PURE__ */ jsxs5(Flex4, { justifyContent: "space-between", alignItems: "center", children: [
6469
+ /* @__PURE__ */ jsx7(Typography4, { variant: "sigma", tag: "h3", textColor: "neutral600", children: t.historyTitle }),
6470
+ /* @__PURE__ */ jsx7(Button4, { variant: "tertiary", size: "S", onClick: reload, children: t.historyRefresh })
6471
+ ] }),
6472
+ failed ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.historyFailed }) : !events ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.historyLoading }) : !events.length ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.historyEmpty }) : /* @__PURE__ */ jsxs5(Flex4, { tag: "ol", direction: "column", alignItems: "stretch", gap: 1, style: { listStyle: "none", margin: 0, padding: 0 }, children: [
6473
+ events.slice(0, shown).map((event) => {
6474
+ const active = open?.event?.id === event.id;
6475
+ return /* @__PURE__ */ jsxs5("li", { "data-testid": `history-event-${event.id}`, children: [
6476
+ /* @__PURE__ */ jsx7(
6477
+ Box3,
6478
+ {
6479
+ tag: "button",
6480
+ type: "button",
6481
+ onClick: () => select(event),
6482
+ "aria-expanded": active,
6483
+ padding: 2,
6484
+ hasRadius: true,
6485
+ width: "100%",
6486
+ background: active ? "primary100" : "neutral0",
6487
+ borderColor: active ? "primary200" : "neutral150",
6488
+ style: { textAlign: "left", cursor: "pointer", borderStyle: "solid", borderWidth: 1 },
6489
+ children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "flex-start", gap: 1, children: [
6490
+ /* @__PURE__ */ jsx7(Typography4, { variant: "omega", fontWeight: "bold", children: t.historyActions[event.action] || event.action }),
6491
+ /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", textColor: "neutral600", children: [
6492
+ event.actorName || (event.actor === "system" ? t.historySystem : event.actor),
6493
+ " \xB7 ",
6494
+ when(event.at)
6495
+ ] }),
6496
+ /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: event.summary?.missing ? "warning700" : "neutral700", children: summary(event.summary) })
6497
+ ] })
6498
+ }
6499
+ ),
6500
+ active && /* @__PURE__ */ jsx7(Box3, { padding: 3, background: "neutral100", hasRadius: true, marginTop: 1, children: open.loading ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", children: t.historyLoading }) : open.failed ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.historyLoadFailed }) : !open.snapshot ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.historyNotKept }) : /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 2, children: [
6501
+ /* @__PURE__ */ jsx7(Typography4, { variant: "pi", fontWeight: "bold", children: t.historyCompare }),
6502
+ same2 ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.historySame }) : /* @__PURE__ */ jsxs5(Fragment5, { children: [
6503
+ diff.fields.length > 0 && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", children: t.f("historyDiffFields", { fields: diff.fields.join(", ") }) }),
6504
+ diff.zones.map((zone) => /* @__PURE__ */ jsx7(Flex4, { tag: "ul", direction: "column", alignItems: "stretch", gap: 1, style: { margin: 0, paddingLeft: 16 }, "data-testid": `history-diff-${zone.name}`, children: zone.ops.map((op, i) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", textColor: op.op === "added" ? "success700" : op.op === "removed" ? "danger700" : "warning700", children: [
6505
+ t.diffOps[op.op],
6506
+ ": ",
6507
+ name(op.uid),
6508
+ op.to !== void 0 ? ` (#${op.to + 1})` : op.from !== void 0 ? ` (#${op.from + 1})` : ""
6509
+ ] }) }, i)) }, zone.name))
6510
+ ] }),
6511
+ /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.historyDiffNote }),
6512
+ /* @__PURE__ */ jsx7(Flex4, { children: /* @__PURE__ */ jsx7(Button4, { size: "S", onClick: load2, loading, disabled: disabled || loading, "data-testid": "history-load", children: t.historyLoad }) }),
6513
+ report && /* @__PURE__ */ jsx7(Box3, { role: "status", padding: 2, background: report.failed ? "danger100" : "success100", hasRadius: true, children: report.failed ? /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "danger700", children: t.historyLoadFailed }) : /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "flex-start", gap: 1, children: [
6514
+ /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "success700", children: t.historyLoaded }),
6515
+ report.left.length > 0 && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "warning700", "data-testid": "history-left", children: t.f("historyLeft", { fields: report.left.join(", ") }) }),
6516
+ report.removed.length > 0 && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "warning700", children: t.f("historyRemovedFields", { fields: report.removed.join(", ") }) }),
6517
+ (report.media || report.relations || report.blocks) > 0 && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "warning700", children: t.f("historyMissingItems", { media: report.media, relations: report.relations, blocks: report.blocks }) })
6518
+ ] }) })
6519
+ ] }) })
6520
+ ] }, event.id);
6521
+ }),
6522
+ events.length > shown && /* @__PURE__ */ jsx7(Flex4, { children: /* @__PURE__ */ jsx7(Button4, { variant: "tertiary", size: "S", onClick: () => setShown(shown + PAGE), children: t.historyShowMore }) })
6523
+ ] })
6524
+ ] });
6525
+ }
6526
+
6527
+ // admin/Settings.tsx
6528
+ import * as React7 from "react";
6529
+ import { Box as Box4, Button as Button5, Flex as Flex5, Searchbar as Searchbar2, Typography as Typography5 } from "@strapi/design-system";
6530
+ import styled5 from "styled-components";
6531
+ import { Fragment as Fragment6, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
5764
6532
  var COLOR2 = /^#[0-9A-Fa-f]{6}$/;
5765
6533
  var Grid = styled5.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 16px; `;
5766
6534
  var Card2 = styled5.div`
@@ -5773,8 +6541,8 @@ var Bar = styled5.div`
5773
6541
  box-shadow: 0 1px 4px rgba(33, 33, 52, 0.1);
5774
6542
  `;
5775
6543
  function useMainRect2(anchor) {
5776
- const [rect, setRect] = React6.useState({ left: 0, width: 0 });
5777
- React6.useEffect(() => {
6544
+ const [rect, setRect] = React7.useState({ left: 0, width: 0 });
6545
+ React7.useEffect(() => {
5778
6546
  const read3 = () => {
5779
6547
  let host = document.getElementById("main-content");
5780
6548
  for (let el = anchor.current?.parentElement; !host && el; el = el.parentElement) if (["auto", "scroll"].includes(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) host = el;
@@ -5791,11 +6559,11 @@ var Swatches = styled5.div` display: grid; grid-template-columns: repeat(auto-fi
5791
6559
  var Label = styled5.label` display: flex; flex-direction: column; gap: 4px; font-size: 12px; `;
5792
6560
  function ColorField({ name, label, value, onChange, disabled }) {
5793
6561
  const valid = COLOR2.test(value);
5794
- return /* @__PURE__ */ jsxs5(Label, { children: [
5795
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
5796
- /* @__PURE__ */ jsxs5(Flex4, { gap: 2, children: [
5797
- /* @__PURE__ */ jsx7("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" } }),
5798
- /* @__PURE__ */ jsx7(
6562
+ return /* @__PURE__ */ jsxs6(Label, { children: [
6563
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
6564
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 2, children: [
6565
+ /* @__PURE__ */ jsx8("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" } }),
6566
+ /* @__PURE__ */ jsx8(
5799
6567
  "input",
5800
6568
  {
5801
6569
  name,
@@ -5811,14 +6579,14 @@ function ColorField({ name, label, value, onChange, disabled }) {
5811
6579
  ] });
5812
6580
  }
5813
6581
  function TagsField({ name, label, hint, value, onChange, disabled }) {
5814
- const [text, setText] = React6.useState(value.join(", "));
5815
- return /* @__PURE__ */ jsxs5(Label, { children: [
5816
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
5817
- /* @__PURE__ */ jsx7("input", { name, value: text, disabled, onChange: (e) => {
6582
+ const [text, setText] = React7.useState(value.join(", "));
6583
+ return /* @__PURE__ */ jsxs6(Label, { children: [
6584
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: label }),
6585
+ /* @__PURE__ */ jsx8("input", { name, value: text, disabled, onChange: (e) => {
5818
6586
  setText(e.target.value);
5819
6587
  onChange([...new Set(e.target.value.split(",").map((tag) => tag.replace(/[<>]/g, "").trim().slice(0, 24)).filter(Boolean))].slice(0, 10));
5820
6588
  }, style: { padding: "6px 8px", border: "1px solid #dcdce4", borderRadius: 4, fontSize: 13 } }),
5821
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: hint })
6589
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: hint })
5822
6590
  ] });
5823
6591
  }
5824
6592
  var FIELD_UID = /^(plugin|global)::[\w.-]+$/;
@@ -5826,11 +6594,11 @@ var LAZY_MAX = 20;
5826
6594
  var parseUids = (text) => [...new Set(text.split(/[\s,]+/).filter(Boolean))];
5827
6595
  var badUids = (uids = []) => uids.length > LAZY_MAX || uids.some((uid) => !FIELD_UID.test(uid));
5828
6596
  function UidListField({ TextField: TextField2, value, onChange, ...props }) {
5829
- const [text, setText] = React6.useState(value.join(", "));
5830
- React6.useEffect(() => {
6597
+ const [text, setText] = React7.useState(value.join(", "));
6598
+ React7.useEffect(() => {
5831
6599
  if (parseUids(text).join() !== value.join()) setText(value.join(", "));
5832
6600
  }, [value.join()]);
5833
- return /* @__PURE__ */ jsx7(TextField2, { ...props, value: text, onChange: (v) => {
6601
+ return /* @__PURE__ */ jsx8(TextField2, { ...props, value: text, onChange: (v) => {
5834
6602
  setText(v);
5835
6603
  onChange(parseUids(v));
5836
6604
  } });
@@ -5849,6 +6617,49 @@ var Checks = styled5.fieldset`
5849
6617
  legend { font-size: 12px; font-weight: 600; margin-bottom: 4px; }
5850
6618
  label { display: inline-flex; align-items: center; gap: 6px; }
5851
6619
  `;
6620
+ var DAYS = (n) => Number.isInteger(n) && n >= 1 && n <= 3650;
6621
+ var badRetention = (h) => !h || !DAYS(h.retentionDays) || !DAYS(h.eventDays) || h.eventDays < h.retentionDays || !(Number.isInteger(h.maxSnapshots) && h.maxSnapshots >= 1 && h.maxSnapshots <= 1e3);
6622
+ function HistorySettings({ types, history, set, t, disabled, ToggleField: ToggleField2, TextField: TextField2 }) {
6623
+ const all = history.contentTypes === "all";
6624
+ const list2 = all ? [] : history.contentTypes;
6625
+ const number = (key) => /* @__PURE__ */ jsx8(Box4, { style: { minWidth: 200 }, children: /* @__PURE__ */ jsx8(
6626
+ TextField2,
6627
+ {
6628
+ name: `history-${key}`,
6629
+ label: t[{ retentionDays: "historyRetention", maxSnapshots: "historyMax", eventDays: "historyEventDays" }[key]],
6630
+ value: Number.isFinite(history[key]) ? String(history[key]) : "",
6631
+ disabled,
6632
+ onChange: (v) => set(key, /^\d{1,5}$/.test(v.trim()) ? Number(v.trim()) : NaN)
6633
+ }
6634
+ ) });
6635
+ return /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "history-settings", children: [
6636
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.historySettings }),
6637
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.historySettingsHelp }),
6638
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "history-enabled", label: t.historyEnabled, value: history.enabled, disabled, onChange: (v) => set("enabled", v) }),
6639
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "history-all", label: t.historyAll, value: all, disabled: disabled || !history.enabled, onChange: (v) => set("contentTypes", v ? "all" : types.map((type) => type.uid)) }),
6640
+ !all && /* @__PURE__ */ jsxs6(Checks, { disabled: disabled || !history.enabled, children: [
6641
+ /* @__PURE__ */ jsx8("legend", { children: t.historyTypes }),
6642
+ types.map((type) => /* @__PURE__ */ jsxs6("label", { children: [
6643
+ /* @__PURE__ */ jsx8(
6644
+ "input",
6645
+ {
6646
+ type: "checkbox",
6647
+ name: `history-type-${type.uid}`,
6648
+ checked: list2.includes(type.uid),
6649
+ onChange: (e) => set("contentTypes", e.target.checked ? [...list2, type.uid] : list2.filter((uid) => uid !== type.uid))
6650
+ }
6651
+ ),
6652
+ type.displayName
6653
+ ] }, type.uid))
6654
+ ] }),
6655
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
6656
+ number("retentionDays"),
6657
+ number("maxSnapshots"),
6658
+ number("eventDays")
6659
+ ] }),
6660
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: badRetention(history) ? "danger600" : "neutral600", role: badRetention(history) ? "alert" : void 0, children: badRetention(history) ? t.historyInvalid : t.historyRetentionHelp })
6661
+ ] }) });
6662
+ }
5852
6663
  function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField2, TextField: TextField2 }) {
5853
6664
  const items = entry.sidebar || [];
5854
6665
  const save = (next) => set("sidebar", next.length ? next : void 0, void 0);
@@ -5860,10 +6671,10 @@ function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField
5860
6671
  };
5861
6672
  const fields = (type.attributes || []).filter((attr) => !SYSTEM_FIELDS2.includes(attr.name));
5862
6673
  const id = (name, index) => `sidebar-${name}-${type.uid}${index === void 0 ? "" : `-${index}`}`;
5863
- return /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 3, paddingLeft: 4, "data-testid": `sidebar-editor-${type.uid}`, children: [
5864
- /* @__PURE__ */ jsx7(Typography4, { variant: "delta", tag: "h3", children: t.f("sidebarTitle", { name: type.displayName }) }),
5865
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.sidebarHelp }),
5866
- /* @__PURE__ */ jsx7(Box3, { style: { maxWidth: 240 }, children: /* @__PURE__ */ jsx7(
6674
+ return /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 3, paddingLeft: 4, "data-testid": `sidebar-editor-${type.uid}`, children: [
6675
+ /* @__PURE__ */ jsx8(Typography5, { variant: "delta", tag: "h3", children: t.f("sidebarTitle", { name: type.displayName }) }),
6676
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.sidebarHelp }),
6677
+ /* @__PURE__ */ jsx8(Box4, { style: { maxWidth: 240 }, children: /* @__PURE__ */ jsx8(
5867
6678
  SelectField2,
5868
6679
  {
5869
6680
  name: id("position"),
@@ -5874,21 +6685,21 @@ function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField
5874
6685
  onChange: (v) => set("sidebarPosition", v, "left")
5875
6686
  }
5876
6687
  ) }),
5877
- items.map((item, index) => /* @__PURE__ */ jsxs5(Item, { "data-testid": id("item", index), children: [
5878
- /* @__PURE__ */ jsxs5(Flex4, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
5879
- /* @__PURE__ */ jsx7(Box3, { style: { flex: "1 1 200px" }, children: /* @__PURE__ */ jsx7(TextField2, { name: id("label", index), label: t.sidebarItemLabel, value: item.label, disabled, onChange: (v) => edit(index, { label: v }) }) }),
5880
- /* @__PURE__ */ jsx7(Box3, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx7(
6688
+ items.map((item, index) => /* @__PURE__ */ jsxs6(Item, { "data-testid": id("item", index), children: [
6689
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
6690
+ /* @__PURE__ */ jsx8(Box4, { style: { flex: "1 1 200px" }, children: /* @__PURE__ */ jsx8(TextField2, { name: id("label", index), label: t.sidebarItemLabel, value: item.label, disabled, onChange: (v) => edit(index, { label: v }) }) }),
6691
+ /* @__PURE__ */ jsx8(Box4, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx8(
5881
6692
  SelectField2,
5882
6693
  {
5883
6694
  name: id("icon", index),
5884
6695
  label: t.sidebarIcon,
5885
6696
  value: item.icon || "none",
5886
6697
  disabled,
5887
- options: [{ value: "none", label: t.noIcon }, ...SIDEBAR_ICONS.map((value) => ({ value, label: t.icons[value], icon: /* @__PURE__ */ jsx7(Icon, { name: value, size: 16 }) }))],
6698
+ options: [{ value: "none", label: t.noIcon }, ...SIDEBAR_ICONS.map((value) => ({ value, label: t.icons[value], icon: /* @__PURE__ */ jsx8(Icon, { name: value, size: 16 }) }))],
5888
6699
  onChange: (v) => edit(index, { icon: v === "none" ? void 0 : v })
5889
6700
  }
5890
6701
  ) }),
5891
- /* @__PURE__ */ jsx7(Box3, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx7(
6702
+ /* @__PURE__ */ jsx8(Box4, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx8(
5892
6703
  SelectField2,
5893
6704
  {
5894
6705
  name: id("open", index),
@@ -5899,16 +6710,16 @@ function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField
5899
6710
  onChange: (v) => edit(index, { open: v })
5900
6711
  }
5901
6712
  ) }),
5902
- /* @__PURE__ */ jsxs5(Flex4, { gap: 1, paddingBottom: 1, children: [
5903
- /* @__PURE__ */ jsx7(Tool, { icon: "up", label: t.moveUp, disabled: disabled || index === 0, onClick: () => move(index, -1) }),
5904
- /* @__PURE__ */ jsx7(Tool, { icon: "down", label: t.moveDown, disabled: disabled || index === items.length - 1, onClick: () => move(index, 1) }),
5905
- /* @__PURE__ */ jsx7(Tool, { icon: "close", label: t.sidebarRemove, disabled, onClick: () => save(items.filter((_, i) => i !== index)) })
6713
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 1, paddingBottom: 1, children: [
6714
+ /* @__PURE__ */ jsx8(Tool, { icon: "up", label: t.moveUp, disabled: disabled || index === 0, onClick: () => move(index, -1) }),
6715
+ /* @__PURE__ */ jsx8(Tool, { icon: "down", label: t.moveDown, disabled: disabled || index === items.length - 1, onClick: () => move(index, 1) }),
6716
+ /* @__PURE__ */ jsx8(Tool, { icon: "close", label: t.sidebarRemove, disabled, onClick: () => save(items.filter((_, i) => i !== index)) })
5906
6717
  ] })
5907
6718
  ] }),
5908
- /* @__PURE__ */ jsxs5(Checks, { disabled, children: [
5909
- /* @__PURE__ */ jsx7("legend", { children: t.sidebarFields }),
5910
- fields.map((attr) => /* @__PURE__ */ jsxs5("label", { children: [
5911
- /* @__PURE__ */ jsx7(
6719
+ /* @__PURE__ */ jsxs6(Checks, { disabled, children: [
6720
+ /* @__PURE__ */ jsx8("legend", { children: t.sidebarFields }),
6721
+ fields.map((attr) => /* @__PURE__ */ jsxs6("label", { children: [
6722
+ /* @__PURE__ */ jsx8(
5912
6723
  "input",
5913
6724
  {
5914
6725
  type: "checkbox",
@@ -5920,11 +6731,11 @@ function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField
5920
6731
  attr.name
5921
6732
  ] }, attr.name))
5922
6733
  ] }),
5923
- badItem(item) && /* @__PURE__ */ jsx7(Typography4, { role: "alert", variant: "pi", textColor: "danger600", children: t.sidebarInvalid })
6734
+ badItem(item) && /* @__PURE__ */ jsx8(Typography5, { role: "alert", variant: "pi", textColor: "danger600", children: t.sidebarInvalid })
5924
6735
  ] }, index)),
5925
- /* @__PURE__ */ jsxs5(Flex4, { gap: 2, alignItems: "center", children: [
5926
- /* @__PURE__ */ jsx7(
5927
- Button4,
6736
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 2, alignItems: "center", children: [
6737
+ /* @__PURE__ */ jsx8(
6738
+ Button5,
5928
6739
  {
5929
6740
  variant: "secondary",
5930
6741
  size: "S",
@@ -5934,7 +6745,7 @@ function SidebarEditor({ type, entry, set, t, disabled, SelectField: SelectField
5934
6745
  children: t.sidebarAdd
5935
6746
  }
5936
6747
  ),
5937
- items.length >= SIDEBAR_MAX && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.f("sidebarLimit", { max: SIDEBAR_MAX }) })
6748
+ items.length >= SIDEBAR_MAX && /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.f("sidebarLimit", { max: SIDEBAR_MAX }) })
5938
6749
  ] })
5939
6750
  ] });
5940
6751
  }
@@ -5943,18 +6754,18 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
5943
6754
  const t = useMessages();
5944
6755
  const { get, put, del } = useClient();
5945
6756
  const { canRead, canUpdate, isLoading } = usePermissions2();
5946
- const [data, setData] = React6.useState(null);
5947
- const [catalog, setCatalog] = React6.useState(null);
5948
- const [settings, setSettings] = React6.useState(null);
5949
- const [media, setMedia] = React6.useState({});
5950
- const [sources, setSources] = React6.useState({});
5951
- const [dirty, setDirty] = React6.useState(false);
5952
- const [saving, setSaving] = React6.useState(false);
5953
- const [status, setStatus] = React6.useState("");
5954
- const [failed, setFailed] = React6.useState(false);
5955
- const [filter, setFilter] = React6.useState("");
5956
- const [picking, setPicking] = React6.useState(null);
5957
- React6.useEffect(() => {
6757
+ const [data, setData] = React7.useState(null);
6758
+ const [catalog, setCatalog] = React7.useState(null);
6759
+ const [settings, setSettings] = React7.useState(null);
6760
+ const [media, setMedia] = React7.useState({});
6761
+ const [sources, setSources] = React7.useState({});
6762
+ const [dirty, setDirty] = React7.useState(false);
6763
+ const [saving, setSaving] = React7.useState(false);
6764
+ const [status, setStatus] = React7.useState("");
6765
+ const [failed, setFailed] = React7.useState(false);
6766
+ const [filter, setFilter] = React7.useState("");
6767
+ const [picking, setPicking] = React7.useState(null);
6768
+ React7.useEffect(() => {
5958
6769
  if (!canRead || isLoading) return;
5959
6770
  let active = true;
5960
6771
  Promise.all([get("/blockscene/settings"), get("/blockscene/catalog")]).then(([s, c]) => {
@@ -5978,7 +6789,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
5978
6789
  const badColor = settings && (Object.values(settings.palette).some((value) => !COLOR2.test(value)) || [settings.editor.previewUrl, settings.editor.blockPreviewUrl].some((url) => url && !/^https?:\/\/\S+$/.test(url)));
5979
6790
  const badSidebar = settings && Object.values(settings.contentTypes || {}).some((entry) => (entry.sidebar || []).some(badItem));
5980
6791
  const badLazy = settings && badUids(settings.editor.lazyFields);
5981
- const invalid = badColor || badSidebar || badLazy;
6792
+ const badHistory = settings && data?.historyTypes && badRetention(settings.history);
6793
+ const invalid = badColor || badSidebar || badLazy || badHistory;
5982
6794
  const save = async () => {
5983
6795
  if (!settings || invalid || !canUpdate) return;
5984
6796
  setSaving(true);
@@ -6030,36 +6842,36 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6030
6842
  return index === 0 && manual ? t.manual : t.automatic;
6031
6843
  };
6032
6844
  const ready = !isLoading && canRead && !failed && settings && catalog;
6033
- const header = React6.useRef(null);
6034
- const [floating, setFloating] = React6.useState(false);
6845
+ const header = React7.useRef(null);
6846
+ const [floating, setFloating] = React7.useState(false);
6035
6847
  const main = useMainRect2(header);
6036
- React6.useEffect(() => {
6848
+ React7.useEffect(() => {
6037
6849
  const el = header.current;
6038
6850
  if (!el || typeof IntersectionObserver === "undefined") return;
6039
6851
  const observer = new IntersectionObserver(([entry]) => setFloating(!entry.isIntersecting && entry.boundingClientRect.top < 0));
6040
6852
  observer.observe(el);
6041
6853
  return () => observer.disconnect();
6042
6854
  }, [ready]);
6043
- const controls = (suffix) => ready && /* @__PURE__ */ jsxs5(Flex4, { gap: 3, alignItems: "center", wrap: "wrap", children: [
6044
- /* @__PURE__ */ jsx7(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 }),
6045
- !suffix && /* @__PURE__ */ jsx7(Button4, { variant: "tertiary", onClick: restore, disabled: !canUpdate || saving, "data-testid": "restore-blockscene-settings", children: data.projectDefaults ? t.restoreProject : t.resetAll }),
6046
- /* @__PURE__ */ jsx7(Button4, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
6855
+ const controls = (suffix) => ready && /* @__PURE__ */ jsxs6(Flex5, { gap: 3, alignItems: "center", wrap: "wrap", children: [
6856
+ /* @__PURE__ */ jsx8(Typography5, { role: "status", "aria-live": "polite", textColor: dirty ? "warning700" : "neutral600", "data-testid": `${dirty ? "unsaved-indicator" : "save-status"}${suffix}`, children: dirty ? t.unsaved : status || t.noChanges }),
6857
+ !suffix && /* @__PURE__ */ jsx8(Button5, { variant: "tertiary", onClick: restore, disabled: !canUpdate || saving, "data-testid": "restore-blockscene-settings", children: data.projectDefaults ? t.restoreProject : t.resetAll }),
6858
+ /* @__PURE__ */ jsx8(Button5, { onClick: save, disabled: !canUpdate || invalid || !dirty, loading: saving, "data-testid": `save-blockscene-settings${suffix}`, children: t.save })
6047
6859
  ] });
6048
- return /* @__PURE__ */ jsx7(Box3, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 5, children: [
6049
- /* @__PURE__ */ jsx7("div", { ref: header, children: /* @__PURE__ */ jsxs5(Flex4, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
6050
- /* @__PURE__ */ jsx7(Typography4, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
6860
+ return /* @__PURE__ */ jsx8(Box4, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 5, children: [
6861
+ /* @__PURE__ */ jsx8("div", { ref: header, children: /* @__PURE__ */ jsxs6(Flex5, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
6862
+ /* @__PURE__ */ jsx8(Typography5, { variant: "alpha", tag: "h1", children: t.settingsTitle }),
6051
6863
  controls("")
6052
6864
  ] }) }),
6053
- floating && main.width > 0 && /* @__PURE__ */ jsx7(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: [
6054
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "p", children: t.settingsTitle }),
6865
+ floating && main.width > 0 && /* @__PURE__ */ jsx8(Bar, { style: { left: main.left, width: main.width }, "data-testid": "settings-floating-bar", children: /* @__PURE__ */ jsxs6(Flex5, { gap: 4, alignItems: "center", justifyContent: "space-between", wrap: "wrap", children: [
6866
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "p", children: t.settingsTitle }),
6055
6867
  controls("-floating")
6056
6868
  ] }) }),
6057
- /* @__PURE__ */ jsx7(Typography4, { textColor: "neutral600", children: t.settingsIntro }),
6058
- isLoading ? /* @__PURE__ */ jsx7(Typography4, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx7(Typography4, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx7(Typography4, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx7(Typography4, { children: t.loading }) : /* @__PURE__ */ jsxs5(Fragment5, { children: [
6059
- data.disabled && /* @__PURE__ */ jsx7(Box3, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx7(Typography4, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
6060
- /* @__PURE__ */ jsx7(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
6061
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "h2", children: t.palette }),
6062
- /* @__PURE__ */ jsx7(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx7(
6869
+ /* @__PURE__ */ jsx8(Typography5, { textColor: "neutral600", children: t.settingsIntro }),
6870
+ isLoading ? /* @__PURE__ */ jsx8(Typography5, { children: "\u2026" }) : !canRead ? /* @__PURE__ */ jsx8(Typography5, { role: "alert", children: t.denied }) : failed ? /* @__PURE__ */ jsx8(Typography5, { role: "alert", children: t.loadFailed }) : !settings || !catalog ? /* @__PURE__ */ jsx8(Typography5, { children: t.loading }) : /* @__PURE__ */ jsxs6(Fragment6, { children: [
6871
+ data.disabled && /* @__PURE__ */ jsx8(Box4, { padding: 4, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx8(Typography5, { role: "alert", textColor: "warning700", children: t.disabledByServer }) }),
6872
+ /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
6873
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.palette }),
6874
+ /* @__PURE__ */ jsx8(Swatches, { children: Object.keys(DEFAULT_PALETTE).map((key) => /* @__PURE__ */ jsx8(
6063
6875
  ColorField,
6064
6876
  {
6065
6877
  name: `palette-${key}`,
@@ -6073,37 +6885,42 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6073
6885
  },
6074
6886
  key
6075
6887
  )) }),
6076
- badColor && /* @__PURE__ */ jsx7(Typography4, { role: "alert", textColor: "danger600", children: t.invalidColor }),
6077
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
6078
- /* @__PURE__ */ jsx7(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs5(Flex4, { direction: "column", gap: 1, children: [
6079
- /* @__PURE__ */ jsx7(Box3, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx7(Wireframe, { template, palette: settings.palette }) }),
6080
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
6888
+ badColor && /* @__PURE__ */ jsx8(Typography5, { role: "alert", textColor: "danger600", children: t.invalidColor }),
6889
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.previewLabel }),
6890
+ /* @__PURE__ */ jsx8(Swatches, { "data-testid": "palette-preview", children: TEMPLATES.map((template) => /* @__PURE__ */ jsxs6(Flex5, { direction: "column", gap: 1, children: [
6891
+ /* @__PURE__ */ jsx8(Box4, { style: { aspectRatio: "16 / 9", overflow: "hidden" }, hasRadius: true, children: /* @__PURE__ */ jsx8(Wireframe, { template, palette: settings.palette }) }),
6892
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.templates[template] })
6081
6893
  ] }, template)) }),
6082
- /* @__PURE__ */ jsx7(Flex4, { children: /* @__PURE__ */ jsx7(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
6894
+ /* @__PURE__ */ jsx8(Flex5, { children: /* @__PURE__ */ jsx8(Button5, { variant: "tertiary", size: "S", disabled: !canUpdate, onClick: () => update((s) => {
6083
6895
  s.palette = { ...DEFAULT_PALETTE };
6084
6896
  return s;
6085
6897
  }), children: t.resetPalette }) })
6086
6898
  ] }) }),
6087
- /* @__PURE__ */ jsx7(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
6088
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "h2", children: t.editor }),
6089
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6899
+ /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
6900
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.editor }),
6901
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-enabled", label: t.enabled, value: editor.enabled, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6090
6902
  s.editor.enabled = v;
6091
6903
  return s;
6092
6904
  }) }),
6093
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
6094
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6905
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.enabledHelp }),
6906
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-showOpenAll", label: t.showOpenAll, value: editor.showOpenAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6095
6907
  s.editor.showOpenAll = v;
6096
6908
  return s;
6097
6909
  }) }),
6098
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6910
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-showCloseAll", label: t.showCloseAll, value: editor.showCloseAll, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6099
6911
  s.editor.showCloseAll = v;
6100
6912
  return s;
6101
6913
  }) }),
6102
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-showRowThumbnails", label: t.showRowThumbnails, value: editor.showRowThumbnails !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6914
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-showRowThumbnails", label: t.showRowThumbnails, value: editor.showRowThumbnails !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6103
6915
  s.editor.showRowThumbnails = v;
6104
6916
  return s;
6105
6917
  }) }),
6106
- /* @__PURE__ */ jsx7(
6918
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-friendlyLabels", label: t.friendlyLabels, value: editor.friendlyLabels !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6919
+ s.editor.friendlyLabels = v;
6920
+ return s;
6921
+ }) }),
6922
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.friendlyLabelsHelp }),
6923
+ /* @__PURE__ */ jsx8(
6107
6924
  SelectField2,
6108
6925
  {
6109
6926
  name: "editor-initialState",
@@ -6117,8 +6934,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6117
6934
  })
6118
6935
  }
6119
6936
  ),
6120
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
6121
- /* @__PURE__ */ jsx7(
6937
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.rememberHelp }),
6938
+ /* @__PURE__ */ jsx8(
6122
6939
  TextField2,
6123
6940
  {
6124
6941
  name: "editor-blockPreviewUrl",
@@ -6132,9 +6949,9 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6132
6949
  })
6133
6950
  }
6134
6951
  ),
6135
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewUrlHelp }),
6136
- previewSupported ? /* @__PURE__ */ jsxs5(Fragment5, { children: [
6137
- /* @__PURE__ */ jsx7(
6952
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.blockPreviewUrlHelp }),
6953
+ previewSupported ? /* @__PURE__ */ jsxs6(Fragment6, { children: [
6954
+ /* @__PURE__ */ jsx8(
6138
6955
  TextField2,
6139
6956
  {
6140
6957
  name: "editor-previewUrl",
@@ -6148,8 +6965,8 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6148
6965
  })
6149
6966
  }
6150
6967
  ),
6151
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
6152
- /* @__PURE__ */ jsx7(
6968
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.previewUrlHelp }),
6969
+ /* @__PURE__ */ jsx8(
6153
6970
  SelectField2,
6154
6971
  {
6155
6972
  name: "editor-previewMode",
@@ -6163,12 +6980,12 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6163
6980
  })
6164
6981
  }
6165
6982
  ),
6166
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
6167
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-lazyEditors", label: t.lazyEditors, value: editor.lazyEditors !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6983
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.previewModeHelp }),
6984
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-lazyEditors", label: t.lazyEditors, value: editor.lazyEditors !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6168
6985
  s.editor.lazyEditors = v;
6169
6986
  return s;
6170
6987
  }) }),
6171
- /* @__PURE__ */ jsx7(
6988
+ /* @__PURE__ */ jsx8(
6172
6989
  UidListField,
6173
6990
  {
6174
6991
  TextField: TextField2,
@@ -6183,31 +7000,31 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6183
7000
  })
6184
7001
  }
6185
7002
  ),
6186
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: badLazy ? "danger600" : "neutral600", role: badLazy ? "alert" : void 0, children: badLazy ? t.f("lazyFieldsInvalid", { max: LAZY_MAX }) : t.lazyEditorsHelp }),
6187
- data.blockPreviewAvailable && /* @__PURE__ */ jsxs5(Fragment5, { children: [
6188
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
7003
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: badLazy ? "danger600" : "neutral600", role: badLazy ? "alert" : void 0, children: badLazy ? t.f("lazyFieldsInvalid", { max: LAZY_MAX }) : t.lazyEditorsHelp }),
7004
+ data.blockPreviewAvailable && /* @__PURE__ */ jsxs6(Fragment6, { children: [
7005
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-blockPreviewInForm", label: t.blockPreviewInForm, value: editor.blockPreviewInForm, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6189
7006
  s.editor.blockPreviewInForm = v;
6190
7007
  return s;
6191
7008
  }) }),
6192
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
7009
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.blockPreviewInFormHelp })
6193
7010
  ] })
6194
- ] }) : /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
7011
+ ] }) : /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.previewV4 })
6195
7012
  ] }) }),
6196
- /* @__PURE__ */ jsx7(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "row-actions-settings", children: [
6197
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "h2", children: t.rowActionsTitle }),
6198
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-confirmDelete", label: t.confirmDeleteOption, value: editor.confirmDelete !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
7013
+ /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "row-actions-settings", children: [
7014
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.rowActionsTitle }),
7015
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-confirmDelete", label: t.confirmDeleteOption, value: editor.confirmDelete !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6199
7016
  s.editor.confirmDelete = v;
6200
7017
  return s;
6201
7018
  }) }),
6202
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-duplicate", label: t.duplicateOption, value: editor.duplicate !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
7019
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-duplicate", label: t.duplicateOption, value: editor.duplicate !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6203
7020
  s.editor.duplicate = v;
6204
7021
  return s;
6205
7022
  }) }),
6206
- /* @__PURE__ */ jsx7(ToggleField2, { name: "editor-clipboard", label: t.clipboardOption, value: editor.clipboard !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
7023
+ /* @__PURE__ */ jsx8(ToggleField2, { name: "editor-clipboard", label: t.clipboardOption, value: editor.clipboard !== false, disabled: !canUpdate || saving, onChange: (v) => update((s) => {
6207
7024
  s.editor.clipboard = v;
6208
7025
  return s;
6209
7026
  }) }),
6210
- /* @__PURE__ */ jsx7(
7027
+ /* @__PURE__ */ jsx8(
6211
7028
  SelectField2,
6212
7029
  {
6213
7030
  name: "editor-hiddenBlocks",
@@ -6221,11 +7038,26 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6221
7038
  })
6222
7039
  }
6223
7040
  ),
6224
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: data.hiddenAttribute ? t.hiddenBlocksHelp : t.hiddenAttributeOff })
7041
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: data.hiddenAttribute ? t.hiddenBlocksHelp : t.hiddenAttributeOff })
6225
7042
  ] }) }),
6226
- (data.contentTypes || []).length > 0 && /* @__PURE__ */ jsx7(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "content-types", children: [
6227
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "h2", children: t.contentTypes }),
6228
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", children: t.contentTypesHelp }),
7043
+ data.historyTypes && settings.history && /* @__PURE__ */ jsx8(
7044
+ HistorySettings,
7045
+ {
7046
+ types: data.historyTypes,
7047
+ history: settings.history,
7048
+ t,
7049
+ disabled: !canUpdate || saving,
7050
+ ToggleField: ToggleField2,
7051
+ TextField: TextField2,
7052
+ set: (key, value) => update((s) => {
7053
+ s.history = { ...s.history, [key]: value };
7054
+ return s;
7055
+ })
7056
+ }
7057
+ ),
7058
+ (data.contentTypes || []).length > 0 && /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, "data-testid": "content-types", children: [
7059
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.contentTypes }),
7060
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", children: t.contentTypesHelp }),
6229
7061
  data.contentTypes.map((type) => {
6230
7062
  const entry = settings.contentTypes?.[type.uid] || {};
6231
7063
  const set = (key, value, fallback) => update((s) => {
@@ -6237,9 +7069,9 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6237
7069
  else delete s.contentTypes[type.uid];
6238
7070
  return s;
6239
7071
  });
6240
- return /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
6241
- /* @__PURE__ */ jsxs5(Flex4, { gap: 6, alignItems: "flex-end", wrap: "wrap", "data-testid": `content-type-${type.uid}`, children: [
6242
- /* @__PURE__ */ jsx7(Box3, { style: { minWidth: 220 }, children: /* @__PURE__ */ jsx7(
7072
+ return /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
7073
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 6, alignItems: "flex-end", wrap: "wrap", "data-testid": `content-type-${type.uid}`, children: [
7074
+ /* @__PURE__ */ jsx8(Box4, { style: { minWidth: 220 }, children: /* @__PURE__ */ jsx8(
6243
7075
  ToggleField2,
6244
7076
  {
6245
7077
  name: `type-enabled-${type.uid}`,
@@ -6249,7 +7081,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6249
7081
  onChange: (v) => set("enabled", v, true)
6250
7082
  }
6251
7083
  ) }),
6252
- previewSupported && /* @__PURE__ */ jsx7(Box3, { style: { minWidth: 240 }, children: /* @__PURE__ */ jsx7(
7084
+ previewSupported && /* @__PURE__ */ jsx8(Box4, { style: { minWidth: 240 }, children: /* @__PURE__ */ jsx8(
6253
7085
  SelectField2,
6254
7086
  {
6255
7087
  name: `type-mode-${type.uid}`,
@@ -6261,21 +7093,21 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6261
7093
  }
6262
7094
  ) })
6263
7095
  ] }),
6264
- previewSupported && entry.enabled !== false && /* @__PURE__ */ jsx7(SidebarEditor, { type, entry, set, t, disabled: !canUpdate || saving, SelectField: SelectField2, TextField: TextField2 })
7096
+ previewSupported && entry.enabled !== false && /* @__PURE__ */ jsx8(SidebarEditor, { type, entry, set, t, disabled: !canUpdate || saving, SelectField: SelectField2, TextField: TextField2 })
6265
7097
  ] }, type.uid);
6266
7098
  }),
6267
- badSidebar && /* @__PURE__ */ jsx7(Typography4, { role: "alert", textColor: "danger600", children: t.sidebarInvalidSave })
7099
+ badSidebar && /* @__PURE__ */ jsx8(Typography5, { role: "alert", textColor: "danger600", children: t.sidebarInvalidSave })
6268
7100
  ] }) }),
6269
- /* @__PURE__ */ jsx7(Box3, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 4, children: [
6270
- /* @__PURE__ */ jsx7(Typography4, { variant: "beta", tag: "h2", children: t.components }),
6271
- /* @__PURE__ */ jsx7(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
6272
- /* @__PURE__ */ jsx7(Typography4, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
6273
- /* @__PURE__ */ jsx7(Grid, { "data-testid": "components-grid", children: components.map((component) => {
7101
+ /* @__PURE__ */ jsx8(Box4, { padding: 6, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 4, children: [
7102
+ /* @__PURE__ */ jsx8(Typography5, { variant: "beta", tag: "h2", children: t.components }),
7103
+ /* @__PURE__ */ jsx8(Searchbar2, { name: "component-filter", value: filter, placeholder: t.filter, clearLabel: t.clear, onClear: () => setFilter(""), onChange: (e) => setFilter(e.target.value), children: t.filter }),
7104
+ /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "neutral600", "data-testid": "components-count", children: t.f("countAll", { count: components.length }) }),
7105
+ /* @__PURE__ */ jsx8(Grid, { "data-testid": "components-grid", children: components.map((component) => {
6274
7106
  const entry = settings.components[component.uid] || {};
6275
7107
  const meta = { ...catalog.components?.[component.uid] || {}, ...media[component.uid] || {} };
6276
7108
  const candidates = candidatesFor(component.uid, meta, catalog);
6277
- return /* @__PURE__ */ jsxs5(Card2, { "data-testid": `settings-${component.uid}`, children: [
6278
- /* @__PURE__ */ jsx7(
7109
+ return /* @__PURE__ */ jsxs6(Card2, { "data-testid": `settings-${component.uid}`, children: [
7110
+ /* @__PURE__ */ jsx8(
6279
7111
  Thumb,
6280
7112
  {
6281
7113
  candidates,
@@ -6286,23 +7118,23 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6286
7118
  onResolved: (index) => setSources((current) => current[component.uid] === index ? current : { ...current, [component.uid]: index })
6287
7119
  }
6288
7120
  ),
6289
- /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "flex-start", gap: 2, children: [
6290
- /* @__PURE__ */ jsx7(Typography4, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
6291
- /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", textColor: "neutral600", children: [
7121
+ /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "flex-start", gap: 2, children: [
7122
+ /* @__PURE__ */ jsx8(Typography5, { fontWeight: "bold", textColor: "neutral800", children: component.displayName }),
7123
+ /* @__PURE__ */ jsxs6(Typography5, { variant: "pi", textColor: "neutral600", children: [
6292
7124
  component.uid,
6293
7125
  " \xB7 ",
6294
7126
  component.category
6295
7127
  ] }),
6296
- /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
7128
+ /* @__PURE__ */ jsxs6(Typography5, { variant: "pi", "data-testid": `source-${component.uid}`, children: [
6297
7129
  t.source,
6298
7130
  ": ",
6299
7131
  sourceOf(component.uid, Boolean(meta.manualImage)),
6300
7132
  meta.manualName ? ` (${meta.manualName})` : ""
6301
7133
  ] }),
6302
- meta.manualMissing && /* @__PURE__ */ jsx7(Typography4, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
6303
- /* @__PURE__ */ jsxs5(Flex4, { gap: 2, wrap: "wrap", children: [
6304
- /* @__PURE__ */ jsx7(Button4, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
6305
- entry.mediaId && /* @__PURE__ */ jsx7(Button4, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
7134
+ meta.manualMissing && /* @__PURE__ */ jsx8(Typography5, { variant: "pi", role: "alert", textColor: "danger600", children: t.brokenMedia }),
7135
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 2, wrap: "wrap", children: [
7136
+ /* @__PURE__ */ jsx8(Button5, { variant: "secondary", size: "S", disabled: !canUpdate || saving, onClick: () => setPicking(component.uid), children: entry.mediaId ? t.replace : t.choose }),
7137
+ entry.mediaId && /* @__PURE__ */ jsx8(Button5, { variant: "tertiary", size: "S", disabled: !canUpdate || saving, onClick: () => {
6306
7138
  update((s) => {
6307
7139
  delete s.components[component.uid]?.mediaId;
6308
7140
  if (!Object.keys(s.components[component.uid] || {}).length) delete s.components[component.uid];
@@ -6315,7 +7147,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6315
7147
  });
6316
7148
  }, children: t.useAutomatic })
6317
7149
  ] }),
6318
- /* @__PURE__ */ jsx7(
7150
+ /* @__PURE__ */ jsx8(
6319
7151
  SelectField2,
6320
7152
  {
6321
7153
  name: `template-${component.uid}`,
@@ -6329,7 +7161,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6329
7161
  })
6330
7162
  }
6331
7163
  ),
6332
- /* @__PURE__ */ jsx7(
7164
+ /* @__PURE__ */ jsx8(
6333
7165
  SelectField2,
6334
7166
  {
6335
7167
  name: `typology-${component.uid}`,
@@ -6340,7 +7172,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6340
7172
  onChange: (v) => setComponent(component.uid, "typology", v === "auto" ? void 0 : v)
6341
7173
  }
6342
7174
  ),
6343
- /* @__PURE__ */ jsx7(
7175
+ /* @__PURE__ */ jsx8(
6344
7176
  TagsField,
6345
7177
  {
6346
7178
  name: `tags-${component.uid}`,
@@ -6355,7 +7187,7 @@ function Settings({ useClient, usePermissions: usePermissions2, MediaPicker: Med
6355
7187
  ] }, component.uid);
6356
7188
  }) })
6357
7189
  ] }) }),
6358
- picking && /* @__PURE__ */ jsx7(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
7190
+ picking && /* @__PURE__ */ jsx8(MediaPicker2, { onClose: () => setPicking(null), onSelect: (asset) => {
6359
7191
  const uid = picking;
6360
7192
  setPicking(null);
6361
7193
  if (!asset?.id) return;
@@ -6384,14 +7216,14 @@ function register(app, Component2, to) {
6384
7216
  }
6385
7217
 
6386
7218
  // admin/Guard.tsx
6387
- import * as React7 from "react";
6388
- import { Box as Box4, Typography as Typography5 } from "@strapi/design-system";
6389
- import { jsx as jsx8 } from "react/jsx-runtime";
7219
+ import * as React8 from "react";
7220
+ import { Box as Box5, Typography as Typography6 } from "@strapi/design-system";
7221
+ import { jsx as jsx9 } from "react/jsx-runtime";
6390
7222
  var Notice = () => {
6391
7223
  const t = useMessages();
6392
- return /* @__PURE__ */ jsx8(Box4, { padding: 3, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx8(Typography5, { variant: "pi", textColor: "warning700", role: "alert", children: t.crashed }) });
7224
+ return /* @__PURE__ */ jsx9(Box5, { padding: 3, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsx9(Typography6, { variant: "pi", textColor: "warning700", role: "alert", children: t.crashed }) });
6393
7225
  };
6394
- var Guard = class extends React7.Component {
7226
+ var Guard = class extends React8.Component {
6395
7227
  constructor() {
6396
7228
  super(...arguments);
6397
7229
  __publicField(this, "state", { failed: false });
@@ -6403,17 +7235,23 @@ var Guard = class extends React7.Component {
6403
7235
  console.error("[blockscene] disabled for this view after an error:", error);
6404
7236
  }
6405
7237
  render() {
6406
- return this.state.failed ? /* @__PURE__ */ jsx8(Notice, {}) : this.props.children;
7238
+ return this.state.failed ? /* @__PURE__ */ jsx9(Notice, {}) : this.props.children;
6407
7239
  }
6408
7240
  };
6409
7241
 
6410
7242
  // admin/catalog.ts
6411
- import * as React8 from "react";
7243
+ import * as React9 from "react";
7244
+ var cached = null;
7245
+ var pending = null;
7246
+ var load = (get) => get("/blockscene/catalog").then(({ data }) => {
7247
+ cached = data;
7248
+ return data;
7249
+ });
6412
7250
  function useCatalog(get) {
6413
- const [catalog, setCatalog] = React8.useState(null);
6414
- React8.useEffect(() => {
7251
+ const [catalog, setCatalog] = React9.useState(null);
7252
+ React9.useEffect(() => {
6415
7253
  let active = true;
6416
- get("/blockscene/catalog").then(({ data }) => {
7254
+ load(get).then((data) => {
6417
7255
  if (active) setCatalog(data);
6418
7256
  }).catch(() => {
6419
7257
  if (active) setCatalog(null);
@@ -6424,10 +7262,31 @@ function useCatalog(get) {
6424
7262
  }, [get]);
6425
7263
  return catalog;
6426
7264
  }
7265
+ var adminLocale = () => {
7266
+ try {
7267
+ return localStorage.getItem("strapi-admin-language") || document.documentElement.lang || "en";
7268
+ } catch {
7269
+ return "en";
7270
+ }
7271
+ };
7272
+ var labelsHook = (get, relabel, drop) => (args) => {
7273
+ try {
7274
+ if (!cached) pending || (pending = load(get()).catch(() => {
7275
+ }).finally(() => {
7276
+ pending = null;
7277
+ }));
7278
+ const trimmed = drop(args.layout, cached ? cached.hiddenAttribute : "bsHidden");
7279
+ const layout = cached ? relabel(trimmed, cached, adminLocale()) : trimmed;
7280
+ return layout === args.layout ? args : { ...args, layout };
7281
+ } catch (error) {
7282
+ console.error("[blockscene] field labels skipped after an error:", error);
7283
+ return args;
7284
+ }
7285
+ };
6427
7286
 
6428
7287
  // admin/LazyInput.tsx
6429
- import * as React9 from "react";
6430
- import { Field, Flex as Flex5, Typography as Typography6 } from "@strapi/design-system";
7288
+ import * as React10 from "react";
7289
+ import { Field, Flex as Flex6, Typography as Typography7 } from "@strapi/design-system";
6431
7290
  import styled6 from "styled-components";
6432
7291
 
6433
7292
  // admin/sanitize.mjs
@@ -6467,8 +7326,8 @@ function cleanTree(root) {
6467
7326
  for (const { name, value } of [...el.attributes]) {
6468
7327
  const key = name.toLowerCase();
6469
7328
  if (key === "style") {
6470
- const kept = safeStyle(value);
6471
- if (kept) el.setAttribute(name, kept);
7329
+ const kept2 = safeStyle(value);
7330
+ if (kept2) el.setAttribute(name, kept2);
6472
7331
  else el.removeAttribute(name);
6473
7332
  } else if (key.startsWith("on") || key === "srcdoc" || URL_ATTRS.has(key) && unsafeUrl(value)) el.removeAttribute(name);
6474
7333
  }
@@ -6482,7 +7341,7 @@ function sanitizeHtml(html) {
6482
7341
  }
6483
7342
 
6484
7343
  // admin/LazyInput.tsx
6485
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
7344
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
6486
7345
  var config = { on: false, fields: [] };
6487
7346
  var listeners = /* @__PURE__ */ new Set();
6488
7347
  function setLazyConfig(next) {
@@ -6517,14 +7376,14 @@ var Preview = styled6.div`
6517
7376
  `;
6518
7377
  function Placeholder({ name, label, hint, error, required, labelAction, placeholder, value, disabled, onActivate }) {
6519
7378
  const t = useMessages();
6520
- const html = React9.useMemo(() => typeof value === "string" ? sanitizeHtml(value) : value == null ? "" : null, [value]);
7379
+ const html = React10.useMemo(() => typeof value === "string" ? sanitizeHtml(value) : value == null ? "" : null, [value]);
6521
7380
  const activate = disabled ? void 0 : (event) => {
6522
7381
  event.preventDefault();
6523
7382
  onActivate();
6524
7383
  };
6525
- return /* @__PURE__ */ jsx9(Field.Root, { id: name, name, error, hint, required, children: /* @__PURE__ */ jsxs6(Flex5, { direction: "column", alignItems: "stretch", gap: 1, children: [
6526
- /* @__PURE__ */ jsx9(Field.Label, { action: labelAction, children: label }),
6527
- /* @__PURE__ */ jsxs6(
7384
+ return /* @__PURE__ */ jsx10(Field.Root, { id: name, name, error, hint, required, children: /* @__PURE__ */ jsxs7(Flex6, { direction: "column", alignItems: "stretch", gap: 1, children: [
7385
+ /* @__PURE__ */ jsx10(Field.Label, { action: labelAction, children: label }),
7386
+ /* @__PURE__ */ jsxs7(
6528
7387
  Preview,
6529
7388
  {
6530
7389
  id: name,
@@ -6540,22 +7399,22 @@ function Placeholder({ name, label, hint, error, required, labelAction, placehol
6540
7399
  if (e.key === "Enter" || e.key === " ") activate?.(e);
6541
7400
  },
6542
7401
  children: [
6543
- /* @__PURE__ */ jsx9("div", { "data-bar": true, children: /* @__PURE__ */ jsx9(Typography6, { variant: "pi", textColor: "neutral500", children: disabled ? "" : t.lazyEdit }) }),
6544
- html === null ? /* @__PURE__ */ jsx9("div", { "data-body": true, children: JSON.stringify(value) }) : html ? /* @__PURE__ */ jsx9("div", { "data-body": true, dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx9("div", { "data-body": true, children: /* @__PURE__ */ jsx9(Typography6, { textColor: "neutral500", children: placeholder || "" }) })
7402
+ /* @__PURE__ */ jsx10("div", { "data-bar": true, children: /* @__PURE__ */ jsx10(Typography7, { variant: "pi", textColor: "neutral500", children: disabled ? "" : t.lazyEdit }) }),
7403
+ html === null ? /* @__PURE__ */ jsx10("div", { "data-body": true, children: JSON.stringify(value) }) : html ? /* @__PURE__ */ jsx10("div", { "data-body": true, dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx10("div", { "data-body": true, children: /* @__PURE__ */ jsx10(Typography7, { textColor: "neutral500", children: placeholder || "" }) })
6545
7404
  ]
6546
7405
  }
6547
7406
  ),
6548
- /* @__PURE__ */ jsx9(Field.Hint, {}),
6549
- /* @__PURE__ */ jsx9(Field.Error, {})
7407
+ /* @__PURE__ */ jsx10(Field.Hint, {}),
7408
+ /* @__PURE__ */ jsx10(Field.Error, {})
6550
7409
  ] }) });
6551
7410
  }
6552
7411
  function lazyInput(uid, Real) {
6553
- const Lazy = React9.forwardRef((props, ref) => {
6554
- React9.useSyncExternalStore(subscribe, read2);
6555
- const [active, setActive] = React9.useState(() => !lazyFor(uid));
6556
- const box = React9.useRef(null);
6557
- const focus = React9.useRef(false);
6558
- React9.useEffect(() => {
7412
+ const Lazy = React10.forwardRef((props, ref) => {
7413
+ React10.useSyncExternalStore(subscribe, read2);
7414
+ const [active, setActive] = React10.useState(() => !lazyFor(uid));
7415
+ const box = React10.useRef(null);
7416
+ const focus = React10.useRef(false);
7417
+ React10.useEffect(() => {
6559
7418
  if (!active || !focus.current) return;
6560
7419
  focus.current = false;
6561
7420
  let frame2 = 0;
@@ -6568,7 +7427,7 @@ function lazyInput(uid, Real) {
6568
7427
  poll();
6569
7428
  return () => cancelAnimationFrame(frame2);
6570
7429
  }, [active]);
6571
- return /* @__PURE__ */ jsx9("div", { ref: box, style: { display: "contents" }, children: active || !lazyFor(uid) ? /* @__PURE__ */ jsx9(Real, { ...props, ref }) : /* @__PURE__ */ jsx9(Placeholder, { ...props, onActivate: () => {
7430
+ return /* @__PURE__ */ jsx10("div", { ref: box, style: { display: "contents" }, children: active || !lazyFor(uid) ? /* @__PURE__ */ jsx10(Real, { ...props, ref }) : /* @__PURE__ */ jsx10(Placeholder, { ...props, onActivate: () => {
6572
7431
  focus.current = true;
6573
7432
  setActive(true);
6574
7433
  } }) });
@@ -6579,22 +7438,22 @@ function lazyInput(uid, Real) {
6579
7438
  function wrapCustomFields(app) {
6580
7439
  const all = app?.customFields?.getAll?.() || {};
6581
7440
  for (const [uid, field] of Object.entries(all)) {
6582
- const load = field?.components?.Input;
6583
- if (typeof load !== "function" || load.blockscene) continue;
6584
- const wrapped = () => Promise.resolve(load()).then((mod) => ({ ...mod, default: lazyInput(uid, mod?.default ?? mod) }));
7441
+ const load2 = field?.components?.Input;
7442
+ if (typeof load2 !== "function" || load2.blockscene) continue;
7443
+ const wrapped = () => Promise.resolve(load2()).then((mod) => ({ ...mod, default: lazyInput(uid, mod?.default ?? mod) }));
6585
7444
  wrapped.blockscene = true;
6586
7445
  field.components.Input = wrapped;
6587
7446
  }
6588
7447
  }
6589
7448
 
6590
7449
  // admin/strapi5.tsx
6591
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
7450
+ import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
6592
7451
  function Modal({ open, onOpenChange, trigger, title, children, width = "80vw" }) {
6593
- return /* @__PURE__ */ jsxs7(Dialog.Root, { open, onOpenChange, children: [
6594
- trigger && /* @__PURE__ */ jsx10(Dialog.Trigger, { children: trigger }),
6595
- /* @__PURE__ */ jsxs7(Dialog.Content, { style: { width, maxWidth: "92vw" }, children: [
6596
- /* @__PURE__ */ jsx10(Dialog.Header, { children: /* @__PURE__ */ jsx10(Dialog.Title, { children: title }) }),
6597
- /* @__PURE__ */ jsx10(Dialog.Body, { children })
7452
+ return /* @__PURE__ */ jsxs8(Dialog.Root, { open, onOpenChange, children: [
7453
+ trigger && /* @__PURE__ */ jsx11(Dialog.Trigger, { children: trigger }),
7454
+ /* @__PURE__ */ jsxs8(Dialog.Content, { style: { width, maxWidth: "92vw" }, children: [
7455
+ /* @__PURE__ */ jsx11(Dialog.Header, { children: /* @__PURE__ */ jsx11(Dialog.Title, { children: title }) }),
7456
+ /* @__PURE__ */ jsx11(Dialog.Body, { children })
6598
7457
  ] })
6599
7458
  ] });
6600
7459
  }
@@ -6609,9 +7468,9 @@ async function relationsOf(get, { uid, id, field }, locale) {
6609
7468
  return out.reverse();
6610
7469
  }
6611
7470
  function useRowForm(c, values, get) {
6612
- const onChange = useForm2("Blockscene", (state) => state.onChange);
7471
+ const onChange = useForm3("Blockscene", (state) => state.onChange);
6613
7472
  const { toggleNotification } = useNotification2();
6614
- const latest = React10.useRef(values);
7473
+ const latest = React11.useRef(values);
6615
7474
  latest.current = values;
6616
7475
  const locale = c.form?.initialValues?.locale || void 0;
6617
7476
  if (typeof onChange !== "function") return null;
@@ -6636,7 +7495,7 @@ function Panel2() {
6636
7495
  const rbac = useDocumentRBAC2("Blockscene", (state) => state);
6637
7496
  const { get, put } = useFetchClient2();
6638
7497
  const user = useAuth("Blockscene", (state) => state.user);
6639
- const formValues = useForm2("Blockscene", (state) => state.values);
7498
+ const formValues = useForm3("Blockscene", (state) => state.values);
6640
7499
  const catalog = useCatalog(get);
6641
7500
  const form = useRowForm(c, formValues ?? c.form?.values, get);
6642
7501
  const creating = !c.id && !c.form?.initialValues?.id && !c.form?.initialValues?.documentId;
@@ -6671,54 +7530,71 @@ function Panel2() {
6671
7530
  const typeSettings = catalog?.contentTypes?.[c.model] || {};
6672
7531
  const lazy = Boolean(catalog?.editor?.enabled && catalog.editor.lazyEditors !== false && typeSettings.enabled !== false);
6673
7532
  const lazyFields = (catalog?.editor?.lazyFields || []).join(",");
6674
- React10.useEffect(() => {
7533
+ React11.useEffect(() => {
6675
7534
  if (catalog) setLazyConfig({ on: lazy, fields: lazyFields ? lazyFields.split(",") : [] });
6676
7535
  }, [catalog, lazy, lazyFields]);
6677
- if (!zones.length || !catalog?.editor?.enabled || typeSettings.enabled === false) return null;
7536
+ const gallery = zones.length > 0 && catalog?.editor?.enabled && typeSettings.enabled !== false;
7537
+ const history = !creating && Boolean(c.id) && Boolean(catalog?.history?.contentTypes?.includes(c.model));
7538
+ if (!gallery && !history) return null;
6678
7539
  const docKey = `${c.model}:${creating ? "new" : c.id}:${c.form?.initialValues?.locale || ""}`;
6679
7540
  return {
6680
7541
  // The panel is the plugin (gallery, accordions, preview modes), so it carries the plugin's name; the dialog stays "Block gallery".
6681
7542
  title: "Blockscene",
6682
- content: /* @__PURE__ */ jsx10(Guard, { children: /* @__PURE__ */ jsxs7(Flex6, { direction: "column", alignItems: "stretch", gap: 4, children: [
6683
- /* @__PURE__ */ jsx10(
6684
- Gallery,
7543
+ content: /* @__PURE__ */ jsx11(Guard, { children: /* @__PURE__ */ jsxs8(Flex7, { direction: "column", alignItems: "stretch", gap: 4, children: [
7544
+ gallery && /* @__PURE__ */ jsxs8(Fragment7, { children: [
7545
+ /* @__PURE__ */ jsx11(
7546
+ Gallery,
7547
+ {
7548
+ zones,
7549
+ components: c.components,
7550
+ add,
7551
+ Modal,
7552
+ Toggle: ToggleField,
7553
+ get,
7554
+ put,
7555
+ editor: catalog.editor,
7556
+ catalog,
7557
+ docKey,
7558
+ contentType: c.model,
7559
+ userId: user?.id,
7560
+ form
7561
+ }
7562
+ ),
7563
+ /* @__PURE__ */ jsx11(
7564
+ PagePreview,
7565
+ {
7566
+ editor: {
7567
+ ...catalog.editor,
7568
+ previewMode: typeSettings.previewMode || catalog.editor.previewMode,
7569
+ sidebar: typeSettings.sidebar || [],
7570
+ sidebarPosition: typeSettings.sidebarPosition || "left"
7571
+ },
7572
+ groups: catalog.groups || null,
7573
+ hiddenAttribute: catalog.editor.hiddenBlocks !== "off" ? catalog.hiddenAttribute : null,
7574
+ form,
7575
+ Modal,
7576
+ Toggle: ToggleField
7577
+ }
7578
+ )
7579
+ ] }),
7580
+ history && /* @__PURE__ */ jsx11(
7581
+ History,
6685
7582
  {
6686
- zones,
7583
+ model: c.model,
7584
+ documentId: c.id,
7585
+ schema: c.contentType,
6687
7586
  components: c.components,
6688
- add,
6689
- Modal,
6690
- Toggle: ToggleField,
7587
+ editable: (name) => (rbac.canUpdateFields || []).some((field) => field === name || field.startsWith(`${name}.`)),
7588
+ disabled: Boolean(c.form?.disabled),
6691
7589
  get,
6692
- put,
6693
- editor: catalog.editor,
6694
- catalog,
6695
- docKey,
6696
- contentType: c.model,
6697
- userId: user?.id,
6698
- form
6699
- }
6700
- ),
6701
- /* @__PURE__ */ jsx10(
6702
- PagePreview,
6703
- {
6704
- editor: {
6705
- ...catalog.editor,
6706
- previewMode: typeSettings.previewMode || catalog.editor.previewMode,
6707
- sidebar: typeSettings.sidebar || [],
6708
- sidebarPosition: typeSettings.sidebarPosition || "left"
6709
- },
6710
- groups: catalog.groups || null,
6711
- hiddenAttribute: catalog.editor.hiddenBlocks !== "off" ? catalog.hiddenAttribute : null,
6712
- form,
6713
- Modal,
6714
- Toggle: ToggleField
7590
+ relationsOf
6715
7591
  }
6716
7592
  )
6717
7593
  ] }) })
6718
7594
  };
6719
7595
  }
6720
- var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs7(Flex6, { gap: 3, children: [
6721
- /* @__PURE__ */ jsx10(
7596
+ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ */ jsxs8(Flex7, { gap: 3, children: [
7597
+ /* @__PURE__ */ jsx11(
6722
7598
  Switch,
6723
7599
  {
6724
7600
  name,
@@ -6728,7 +7604,7 @@ var ToggleField = ({ name, label, value, onChange, disabled }) => /* @__PURE__ *
6728
7604
  onCheckedChange: onChange
6729
7605
  }
6730
7606
  ),
6731
- /* @__PURE__ */ jsx10(Typography7, { children: label })
7607
+ /* @__PURE__ */ jsx11(Typography8, { children: label })
6732
7608
  ] });
6733
7609
  var TextField = ({
6734
7610
  name,
@@ -6737,9 +7613,9 @@ var TextField = ({
6737
7613
  onChange,
6738
7614
  disabled,
6739
7615
  placeholder
6740
- }) => /* @__PURE__ */ jsxs7(Field2.Root, { name, children: [
6741
- /* @__PURE__ */ jsx10(Field2.Label, { children: label }),
6742
- /* @__PURE__ */ jsx10(
7616
+ }) => /* @__PURE__ */ jsxs8(Field2.Root, { name, children: [
7617
+ /* @__PURE__ */ jsx11(Field2.Label, { children: label }),
7618
+ /* @__PURE__ */ jsx11(
6743
7619
  TextInput,
6744
7620
  {
6745
7621
  name,
@@ -6757,16 +7633,16 @@ var SelectField = ({
6757
7633
  onChange,
6758
7634
  disabled,
6759
7635
  options
6760
- }) => /* @__PURE__ */ jsxs7(Field2.Root, { name, children: [
6761
- /* @__PURE__ */ jsx10(Field2.Label, { children: label }),
6762
- /* @__PURE__ */ jsx10(
7636
+ }) => /* @__PURE__ */ jsxs8(Field2.Root, { name, children: [
7637
+ /* @__PURE__ */ jsx11(Field2.Label, { children: label }),
7638
+ /* @__PURE__ */ jsx11(
6763
7639
  SingleSelect,
6764
7640
  {
6765
7641
  value,
6766
7642
  disabled,
6767
7643
  startIcon: options.find((o) => o.value === value)?.icon,
6768
7644
  onChange: (v) => onChange(String(v)),
6769
- children: options.map((o) => /* @__PURE__ */ jsx10(SingleSelectOption, { value: o.value, startIcon: o.icon, children: o.label }, o.value))
7645
+ children: options.map((o) => /* @__PURE__ */ jsx11(SingleSelectOption, { value: o.value, startIcon: o.icon, children: o.label }, o.value))
6770
7646
  }
6771
7647
  )
6772
7648
  ] });
@@ -6776,11 +7652,11 @@ function MediaPicker({ onClose, onSelect }) {
6776
7652
  (state) => state.components
6777
7653
  );
6778
7654
  const Dialog2 = components?.["media-library"];
6779
- React10.useEffect(() => {
7655
+ React11.useEffect(() => {
6780
7656
  if (!Dialog2) onClose();
6781
7657
  }, [Dialog2, onClose]);
6782
7658
  if (!Dialog2) return null;
6783
- return /* @__PURE__ */ jsx10(
7659
+ return /* @__PURE__ */ jsx11(
6784
7660
  Dialog2,
6785
7661
  {
6786
7662
  onClose,
@@ -6791,7 +7667,7 @@ function MediaPicker({ onClose, onSelect }) {
6791
7667
  );
6792
7668
  }
6793
7669
  function usePermissions() {
6794
- const { allowedActions, isLoading } = useRBAC(
7670
+ const { allowedActions, isLoading } = useRBAC2(
6795
7671
  Object.values(permissions).flat()
6796
7672
  );
6797
7673
  return {
@@ -6800,7 +7676,7 @@ function usePermissions() {
6800
7676
  isLoading
6801
7677
  };
6802
7678
  }
6803
- var SettingsPage = () => /* @__PURE__ */ jsx10(Guard, { children: /* @__PURE__ */ jsx10(
7679
+ var SettingsPage = () => /* @__PURE__ */ jsx11(Guard, { children: /* @__PURE__ */ jsx11(
6804
7680
  Settings,
6805
7681
  {
6806
7682
  useClient: useFetchClient2,
@@ -6822,6 +7698,7 @@ var strapi5_default = {
6822
7698
  registerTrads,
6823
7699
  bootstrap(app) {
6824
7700
  app.getPlugin("content-manager").apis.addEditViewSidePanel([Panel2]);
7701
+ app.registerHook("Admin/CM/pages/EditView/mutate-edit-view-layout", labelsHook(() => getFetchClient().get, labelEditLayout, dropField));
6825
7702
  wrapCustomFields(strapiApp);
6826
7703
  }
6827
7704
  };