@simmalugnt-se/payload-editor-assistant 0.11.3 → 0.12.1

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/form/diff.js CHANGED
@@ -41,7 +41,7 @@ const FIELD_PREVIEW = 300;
41
41
  * `labels` names related documents by id (see `labelRelations`), so a chosen image reads as its
42
42
  * filename or alt text instead of an id. Rich text is listed by `richTextLines` instead.
43
43
  */
44
- export function humanDiff(current, operations, language = "en", labels) {
44
+ export function humanDiff(current, operations, language = "en", labels, fieldLabel = label) {
45
45
  const copy = COPY[language];
46
46
  const nameOf = (value) => {
47
47
  const id = typeof value === "string" || typeof value === "number"
@@ -65,30 +65,30 @@ export function humanDiff(current, operations, language = "en", labels) {
65
65
  const before = getAtPath(current, parseFieldPath(operation.path));
66
66
  return {
67
67
  path: operation.path,
68
- text: `${label(operation.path)}: ${preview(named(before), copy)} → ${preview(named(operation.value), copy)}`,
68
+ text: `${fieldLabel(operation.path)}: ${preview(named(before), copy)} → ${preview(named(operation.value), copy)}`,
69
69
  };
70
70
  }
71
71
  case "unset":
72
- return { path: operation.path, text: copy.clear(label(operation.path)) };
72
+ return { path: operation.path, text: copy.clear(fieldLabel(operation.path)) };
73
73
  case "array.insert":
74
74
  return {
75
75
  path: operation.path,
76
- text: withFields(copy.addItem(operation.index + 1, label(operation.path)), operation.value, copy),
76
+ text: withFields(copy.addItem(operation.index + 1, fieldLabel(operation.path)), operation.value, copy),
77
77
  };
78
78
  case "array.remove":
79
79
  return {
80
80
  path: operation.path,
81
- text: copy.removeItem(operation.index + 1, label(operation.path)),
81
+ text: copy.removeItem(operation.index + 1, fieldLabel(operation.path)),
82
82
  };
83
83
  case "array.move":
84
84
  return {
85
85
  path: operation.path,
86
- text: copy.moveItem(operation.from + 1, operation.to + 1, label(operation.path)),
86
+ text: copy.moveItem(operation.from + 1, operation.to + 1, fieldLabel(operation.path)),
87
87
  };
88
88
  case "array.replace":
89
89
  return {
90
90
  path: operation.path,
91
- text: withFields(copy.replaceItem(operation.index + 1, label(operation.path)), operation.value, copy),
91
+ text: withFields(copy.replaceItem(operation.index + 1, fieldLabel(operation.path)), operation.value, copy),
92
92
  };
93
93
  case "blocks.insert":
94
94
  return {
@@ -98,7 +98,7 @@ export function humanDiff(current, operations, language = "en", labels) {
98
98
  case "blocks.remove":
99
99
  return {
100
100
  path: operation.path,
101
- text: copy.removeBlock(operation.index + 1, label(operation.path)),
101
+ text: copy.removeBlock(operation.index + 1, fieldLabel(operation.path)),
102
102
  };
103
103
  case "blocks.move":
104
104
  return { path: operation.path, text: copy.moveBlock(operation.from + 1, operation.to + 1) };
@@ -116,9 +116,9 @@ export function humanDiff(current, operations, language = "en", labels) {
116
116
  * The parts of a rich text field that changed: `before` as the model read it (Markdown blocks),
117
117
  * `after` as compiled, where `{ keep: i }` is block `i` unchanged.
118
118
  */
119
- export function richTextLines(path, before, after, language = "en") {
119
+ export function richTextLines(path, before, after, language = "en", fieldLabel = label) {
120
120
  const copy = COPY[language];
121
- const field = label(path);
121
+ const field = fieldLabel(path);
122
122
  const part = (block) => typeof block === "string"
123
123
  ? preview(block, copy, FIELD_PREVIEW)
124
124
  : `[${block.type ?? "?"}]`;
@@ -0,0 +1,22 @@
1
+ import type { FieldNode } from "../schema/fields.ts";
2
+ export type FieldPathGroup = {
3
+ path: string;
4
+ label: string;
5
+ kind: "container" | "block" | "row" | "group";
6
+ position?: number;
7
+ /** Where the group sits among its siblings: the schema position, or the row's own position. */
8
+ order: number;
9
+ };
10
+ /** Schema labels and one-based row positions for a concrete form field path. */
11
+ export declare function fieldPathLabels(fields: FieldNode[], path: string, data: unknown, language?: "en" | "sv"): {
12
+ label: string;
13
+ context?: string;
14
+ full: string;
15
+ };
16
+ /** Structured parents let the review group fields instead of repeating breadcrumbs. */
17
+ export declare function fieldPathInfo(fields: FieldNode[], path: string, data: unknown, language?: "en" | "sv"): {
18
+ label: string;
19
+ context?: string;
20
+ full: string;
21
+ groups: FieldPathGroup[];
22
+ };
@@ -0,0 +1,79 @@
1
+ /** Schema labels and one-based row positions for a concrete form field path. */
2
+ export function fieldPathLabels(fields, path, data, language = "en") {
3
+ const { groups: _groups, ...labels } = fieldPathInfo(fields, path, data, language);
4
+ return labels;
5
+ }
6
+ /** Structured parents let the review group fields instead of repeating breadcrumbs. */
7
+ export function fieldPathInfo(fields, path, data, language = "en") {
8
+ const parts = [];
9
+ const groups = [];
10
+ let scope = fields;
11
+ let value = data;
12
+ let field;
13
+ let schemaPath = "";
14
+ let concretePath = "";
15
+ for (const segment of path.split(".")) {
16
+ concretePath = concretePath ? `${concretePath}.${segment}` : segment;
17
+ value = value?.[segment];
18
+ if (/^\d+$/.test(segment)) {
19
+ const position = Number(segment) + 1;
20
+ if (field?.type === "blocks") {
21
+ const blockType = value?.blockType;
22
+ const block = field.blocks?.find((block) => block.slug === blockType);
23
+ parts.push(`${name(block?.label ?? block?.slug ?? "block")} (block ${position})`);
24
+ groups.push({
25
+ path: concretePath,
26
+ label: name(block?.label ?? block?.slug ?? "block"),
27
+ kind: "block",
28
+ position,
29
+ order: position,
30
+ });
31
+ scope = block?.fields ?? [];
32
+ }
33
+ else {
34
+ const row = language === "sv" ? "rad" : "row";
35
+ parts[parts.length - 1] = `${parts.at(-1) ?? ""} (${row} ${position})`;
36
+ groups.push({
37
+ path: concretePath,
38
+ label: `${name(row)} ${position}`,
39
+ kind: "row",
40
+ position,
41
+ order: position,
42
+ });
43
+ }
44
+ continue;
45
+ }
46
+ schemaPath = schemaPath ? `${schemaPath}.${segment}` : segment;
47
+ const at = scope.findIndex((entry) => entry.path === schemaPath);
48
+ field = at >= 0 ? scope[at] : undefined;
49
+ parts.push(name(field?.label ?? segment));
50
+ if (field?.type === "group" || field?.type === "blocks" || field?.type === "array") {
51
+ groups.push({
52
+ path: concretePath,
53
+ label: name(field.label ?? segment),
54
+ kind: field.type === "group" ? "group" : "container",
55
+ order: at,
56
+ });
57
+ }
58
+ else if (!field) {
59
+ const inside = scope.findIndex((entry) => entry.path.startsWith(`${schemaPath}.`));
60
+ if (inside >= 0) {
61
+ groups.push({ path: concretePath, label: name(segment), kind: "group", order: inside });
62
+ }
63
+ }
64
+ // Named tabs are flattened by walkFields, but their name remains in each child's path.
65
+ if (field)
66
+ scope = field.fields ?? [];
67
+ }
68
+ const label = parts.at(-1) ?? name(path);
69
+ const context = parts.length > 1 ? parts.slice(0, -1).join(" › ") : undefined;
70
+ return {
71
+ label,
72
+ ...(context ? { context } : {}),
73
+ full: parts.join(" › ") || label,
74
+ groups: groups.filter((group) => group.path !== path),
75
+ };
76
+ }
77
+ function name(value) {
78
+ return value.charAt(0).toUpperCase() + value.slice(1);
79
+ }
@@ -1,5 +1,8 @@
1
- import type { FieldNode } from "../schema/fields.ts";
1
+ import { type FieldNode } from "../schema/fields.ts";
2
+ import type { FormOperation } from "./operations.ts";
2
3
  import { type RichTextLink } from "./rich-text.ts";
4
+ /** Rich text fields the operations reach, whole or by block, as form paths. */
5
+ export declare function richTextPaths(operations: FormOperation[], fields: FieldNode[], current: Record<string, unknown>): string[];
3
6
  /**
4
7
  * A copy of `data` where each rich text field the converters can read is a list of `{ keep: i }`,
5
8
  * one per stored block, so operations can set, insert or remove single blocks (`layout.1.content.2`)
@@ -1,4 +1,22 @@
1
+ import { fieldAtPath } from "../schema/fields.js";
1
2
  import { fromBlocks, isLexicalState, isRichTextBlocks, toBlocks, } from "./rich-text.js";
3
+ /** Rich text fields the operations reach, whole or by block, as form paths. */
4
+ export function richTextPaths(operations, fields, current) {
5
+ const paths = new Set();
6
+ for (const operation of operations) {
7
+ if (!("path" in operation))
8
+ continue;
9
+ const segments = operation.path.split(".");
10
+ for (let end = 1; end <= segments.length; end++) {
11
+ const prefix = segments.slice(0, end).join(".");
12
+ if (fieldAtPath(fields, prefix, current)?.type === "richText") {
13
+ paths.add(prefix);
14
+ break;
15
+ }
16
+ }
17
+ }
18
+ return [...paths];
19
+ }
2
20
  /**
3
21
  * A copy of `data` where each rich text field the converters can read is a list of `{ keep: i }`,
4
22
  * one per stored block, so operations can set, insert or remove single blocks (`layout.1.content.2`)
@@ -13,7 +13,7 @@ export type AgentChatResult = {
13
13
  capability: "form.propose" | "draft.create" | "content.update";
14
14
  /** The form with the change applied, shown in Admin before the editor keeps it. */
15
15
  preview?: ProposalPreview;
16
- /** A batch's change list, with what the editor may rewrite. */
16
+ /** Structured fields and rich text previews for Admin's review. */
17
17
  lines?: ProposalLine[];
18
18
  };
19
19
  navigate?: {
@@ -228,7 +228,10 @@ function buildSdkTools(ai, ctx, captured, previews, emit) {
228
228
  : { ...executed, preview: undefined };
229
229
  if (preview && executed.ok && executed.pendingApproval) {
230
230
  previews.set(executed.pendingApproval.approvalId, preview);
231
- emit({ type: "proposal", pendingApproval: { ...executed.pendingApproval, preview } });
231
+ emit({
232
+ type: "proposal",
233
+ pendingApproval: { ...executed.pendingApproval, lines: executed.lines, preview },
234
+ });
232
235
  }
233
236
  emit({
234
237
  type: "step",
@@ -53,7 +53,7 @@ export type ToolSuccess = {
53
53
  nonce: string;
54
54
  capability: "form.propose" | "draft.create" | "content.update";
55
55
  };
56
- /** The change list; a batch shows it per document. */
56
+ /** Structured review data for Admin, including full rich text previews. */
57
57
  lines?: ProposalLine[];
58
58
  navigate?: {
59
59
  href: string;
@@ -94,7 +94,36 @@ export declare function executeTool(name: string, input: Record<string, unknown>
94
94
  */
95
95
  export declare function toolModelOutput(outcome: ToolOutcome): {
96
96
  type: "json";
97
- value: ToolOutcome;
97
+ value: ToolFailure;
98
+ } | {
99
+ type: "json";
100
+ value: {
101
+ ok: true;
102
+ capability: string;
103
+ data?: unknown;
104
+ pendingApproval?: {
105
+ title: string;
106
+ summary: string;
107
+ approvalId: string;
108
+ nonce: string;
109
+ capability: "form.propose" | "draft.create" | "content.update";
110
+ };
111
+ navigate?: {
112
+ href: string;
113
+ label: string;
114
+ } | {
115
+ candidates: Array<{
116
+ href: string;
117
+ label: string;
118
+ }>;
119
+ };
120
+ /** For the model only, as file parts (see `toolModelOutput`); never sent to Admin. */
121
+ images?: Array<{
122
+ id?: string | number;
123
+ mediaType: string;
124
+ data: string;
125
+ }>;
126
+ };
98
127
  } | {
99
128
  type: "content";
100
129
  value: ({
@@ -233,7 +233,7 @@ export async function executeTool(name, input, ctx) {
233
233
  nonce: proposed.nonce,
234
234
  capability,
235
235
  },
236
- ...(batch ? { lines: proposed.lines } : {}),
236
+ lines: proposed.lines,
237
237
  preview: proposed.preview,
238
238
  };
239
239
  }
@@ -246,10 +246,15 @@ export async function executeTool(name, input, ctx) {
246
246
  * has one, and the rest as text before them.
247
247
  */
248
248
  export function toolModelOutput(outcome) {
249
- if (!outcome.ok || !outcome.images?.length) {
249
+ if (!outcome.ok) {
250
250
  return { type: "json", value: outcome };
251
251
  }
252
- const { images, ...rest } = outcome;
252
+ // Full previews belong to Admin; the model already has its operations and the text summary.
253
+ const { lines: _lines, preview: _preview, ...modelOutcome } = outcome;
254
+ if (!modelOutcome.images?.length) {
255
+ return { type: "json", value: modelOutcome };
256
+ }
257
+ const { images, ...rest } = modelOutcome;
253
258
  return {
254
259
  type: "content",
255
260
  value: [
@@ -3,10 +3,15 @@ import { type FieldNode } from "./fields.ts";
3
3
  export declare function projectRecord(data: Record<string, unknown> | null | undefined, fields: FieldNode[], redact?: (input: {
4
4
  slug: string;
5
5
  data: Record<string, unknown>;
6
- }) => Record<string, unknown>, slug?: string): {
6
+ }) => Record<string, unknown>, slug?: string, mode?: "model" | "form"): {
7
7
  data: Record<string, unknown>;
8
8
  truncated: boolean;
9
9
  };
10
+ /** Allowlisted, redacted form data keeps stored rich text and is never replaced by a size excerpt. */
11
+ export declare function projectFormRecord(data: Record<string, unknown>, fields: FieldNode[], redact?: (input: {
12
+ slug: string;
13
+ data: Record<string, unknown>;
14
+ }) => Record<string, unknown>, slug?: string): Record<string, unknown>;
10
15
  export declare function projectIdentity(doc: Record<string, unknown>, locale?: string): Record<string, unknown>;
11
16
  export declare function capRecord(data: Record<string, unknown>): {
12
17
  data: Record<string, unknown>;
@@ -2,7 +2,7 @@ import { isDeniedFieldName } from "../config/denied-fields.js";
2
2
  import { toBlocks } from "../form/rich-text.js";
3
3
  import { isFieldAllowed } from "./fields.js";
4
4
  const MAX_CHARS = 16_000;
5
- export function projectRecord(data, fields, redact, slug = "") {
5
+ export function projectRecord(data, fields, redact, slug = "", mode = "model") {
6
6
  const projected = {};
7
7
  if (!data) {
8
8
  return { data: projected, truncated: false };
@@ -11,10 +11,14 @@ export function projectRecord(data, fields, redact, slug = "") {
11
11
  if (!(field.name in data)) {
12
12
  continue;
13
13
  }
14
- projected[field.name] = projectValue(data[field.name], field);
14
+ projected[field.name] = projectValue(data[field.name], field, mode);
15
15
  }
16
16
  const redacted = redact ? redact({ slug, data: projected }) : projected;
17
- return capRecord(redacted);
17
+ return mode === "form" ? { data: redacted, truncated: false } : capRecord(redacted);
18
+ }
19
+ /** Allowlisted, redacted form data keeps stored rich text and is never replaced by a size excerpt. */
20
+ export function projectFormRecord(data, fields, redact, slug = "") {
21
+ return projectRecord(data, fields, redact, slug, "form").data;
18
22
  }
19
23
  export function projectIdentity(doc, locale) {
20
24
  const identity = {
@@ -53,13 +57,14 @@ export function filterRootKeys(data, allowlist, denyFields) {
53
57
  }
54
58
  return next;
55
59
  }
56
- function projectValue(value, field) {
60
+ function projectValue(value, field, mode) {
57
61
  if (value === null || value === undefined) {
58
62
  return value;
59
63
  }
60
64
  if (field.type === "array" && Array.isArray(value)) {
61
65
  return value.map((item) => item && typeof item === "object" && !Array.isArray(item)
62
- ? projectRecord(item, field.fields ?? []).data
66
+ ? projectRecord(item, field.fields ?? [], undefined, "", mode)
67
+ .data
63
68
  : item);
64
69
  }
65
70
  if (field.type === "blocks" && Array.isArray(value)) {
@@ -70,7 +75,7 @@ function projectValue(value, field) {
70
75
  const record = item;
71
76
  const blockType = typeof record.blockType === "string" ? record.blockType : "";
72
77
  const block = field.blocks?.find((entry) => entry.slug === blockType);
73
- const projected = projectRecord(record, block?.fields ?? []).data;
78
+ const projected = projectRecord(record, block?.fields ?? [], undefined, "", mode).data;
74
79
  return {
75
80
  id: record.id,
76
81
  blockType,
@@ -79,13 +84,14 @@ function projectValue(value, field) {
79
84
  });
80
85
  }
81
86
  if (field.type === "group" && value && typeof value === "object" && !Array.isArray(value)) {
82
- return projectRecord(value, field.fields ?? []).data;
87
+ return projectRecord(value, field.fields ?? [], undefined, "", mode)
88
+ .data;
83
89
  }
84
90
  if (field.type === "relationship" || field.type === "upload") {
85
91
  return projectRelation(value);
86
92
  }
87
93
  if (field.type === "richText") {
88
- return toBlocks(value, field.editor) ?? value;
94
+ return mode === "form" ? value : (toBlocks(value, field.editor) ?? value);
89
95
  }
90
96
  return value;
91
97
  }
@@ -240,6 +240,12 @@ html[data-theme="dark"] .ea-aside {
240
240
  line-height: 1.5;
241
241
  }
242
242
 
243
+ .ea-approval__review {
244
+ max-height: 50vh;
245
+ overflow-y: auto;
246
+ margin-bottom: calc(var(--base) * 0.6);
247
+ }
248
+
243
249
  .ea-approval__hint {
244
250
  margin: 0 0 calc(var(--base) * 0.4);
245
251
  color: var(--theme-elevation-500);
@@ -479,7 +485,7 @@ html[data-theme="dark"] .ea-trigger--open .ea-trigger__icon {
479
485
  padding: calc(var(--base) * 0.5) calc(var(--base) * 0.6);
480
486
  border: 1px solid var(--theme-elevation-150);
481
487
  border-radius: var(--style-radius-m);
482
- background: var(--theme-elevation-50);
488
+ background: var(--theme-elevation-0);
483
489
  font-size: 13px;
484
490
  line-height: 1.45;
485
491
  }
@@ -504,10 +510,17 @@ html[data-theme="dark"] .ea-trigger--open .ea-trigger__icon {
504
510
  }
505
511
 
506
512
  .ea-batch__card-head {
513
+ position: sticky;
514
+ top: 0;
515
+ z-index: 1;
507
516
  display: flex;
508
517
  align-items: center;
509
518
  gap: calc(var(--base) * 0.4);
510
519
  min-width: 0;
520
+ margin: calc(var(--base) * -0.5) calc(var(--base) * -0.6) 0;
521
+ padding: calc(var(--base) * 0.5) calc(var(--base) * 0.6);
522
+ border-bottom: 1px solid var(--theme-elevation-150);
523
+ background: var(--theme-elevation-0);
511
524
  }
512
525
 
513
526
  .ea-batch__card-head input[type="checkbox"] {
@@ -549,29 +562,120 @@ html[data-theme="dark"] .ea-trigger--open .ea-trigger__icon {
549
562
  font-size: 12px;
550
563
  }
551
564
 
552
- .ea-batch__lines {
553
- margin: calc(var(--base) * 0.4) 0 0;
554
- padding-left: 1.1em;
565
+ .ea-review {
566
+ display: flex;
567
+ flex-direction: column;
568
+ gap: 16px;
569
+ margin-top: 16px;
570
+ font-size: 13px;
571
+ line-height: 1.5;
572
+ }
573
+
574
+ .ea-review__collection > h4 {
575
+ margin: 0 0 8px;
576
+ color: var(--theme-elevation-600);
577
+ font-size: 11px;
578
+ font-weight: 600;
579
+ letter-spacing: 0.06em;
580
+ text-transform: uppercase;
555
581
  }
556
582
 
557
- .ea-batch__edit {
583
+ .ea-review__children {
558
584
  display: flex;
559
585
  flex-direction: column;
560
- gap: 2px;
561
- margin-inline-start: -1.1em;
562
- list-style: none;
586
+ gap: 16px;
587
+ min-width: 0;
563
588
  }
564
589
 
565
- .ea-batch__label {
590
+ .ea-review__group {
591
+ padding-top: 12px;
592
+ border-top: 1px solid var(--theme-elevation-150);
593
+ }
594
+
595
+ .ea-review__group > summary {
596
+ display: flex;
597
+ align-items: center;
598
+ gap: 8px;
599
+ cursor: pointer;
600
+ color: var(--theme-text);
601
+ font-size: 14px;
566
602
  font-weight: 600;
603
+ list-style: none;
604
+ }
605
+
606
+ .ea-review__group > summary::-webkit-details-marker {
607
+ display: none;
567
608
  }
568
609
 
569
- .ea-batch__before {
610
+ .ea-review__group > summary::before {
611
+ content: "";
612
+ width: 5px;
613
+ height: 5px;
614
+ flex-shrink: 0;
615
+ border-right: 1.5px solid currentColor;
616
+ border-bottom: 1.5px solid currentColor;
617
+ transform: rotate(-45deg);
618
+ transition: transform 100ms;
619
+ }
620
+
621
+ .ea-review__group[open] > summary::before {
622
+ transform: rotate(45deg);
623
+ }
624
+
625
+ .ea-review__group > summary:focus-visible {
626
+ outline: 2px solid var(--ea-accent);
627
+ outline-offset: 3px;
628
+ }
629
+
630
+ .ea-review__group > .ea-review__children {
631
+ margin-top: 14px;
632
+ }
633
+
634
+ .ea-review__group--row,
635
+ .ea-review__group--group {
636
+ padding-top: 0;
637
+ padding-inline-start: 10px;
638
+ border-top: 0;
639
+ border-inline-start: 2px solid var(--theme-elevation-150);
640
+ }
641
+
642
+ .ea-review__group--row > summary,
643
+ .ea-review__group--group > summary {
644
+ font-size: 12px;
645
+ }
646
+
647
+ .ea-review__badge {
648
+ margin-inline-start: auto;
570
649
  color: var(--theme-elevation-600);
571
- overflow-wrap: anywhere;
650
+ font-size: 11px;
651
+ font-weight: 400;
652
+ }
653
+
654
+ .ea-review__field {
655
+ display: flex;
656
+ flex-direction: column;
657
+ gap: 6px;
658
+ min-width: 0;
659
+ }
660
+
661
+ .ea-review__field-head {
662
+ display: flex;
663
+ align-items: center;
664
+ justify-content: space-between;
665
+ gap: 8px;
666
+ }
667
+
668
+ .ea-review__field-head label {
669
+ font-size: 12px;
670
+ font-weight: 600;
671
+ }
672
+
673
+ .ea-review__new {
674
+ color: var(--theme-elevation-500);
675
+ font-size: 10px;
572
676
  }
573
677
 
574
- .ea-batch__edit textarea {
678
+ .ea-review__field textarea {
575
679
  box-sizing: border-box;
576
680
  width: 100%;
577
681
  padding: calc(var(--base) * 0.3) calc(var(--base) * 0.4);
@@ -586,12 +690,111 @@ html[data-theme="dark"] .ea-trigger--open .ea-trigger__icon {
586
690
  min-height: calc(2lh + calc(var(--base) * 0.6));
587
691
  }
588
692
 
589
- .ea-batch__count {
693
+ .ea-review__field textarea:focus-visible {
694
+ outline: 2px solid var(--ea-accent);
695
+ outline-offset: 1px;
696
+ }
697
+
698
+ .ea-review__count {
590
699
  align-self: flex-end;
591
700
  color: var(--theme-elevation-500);
592
701
  font-size: 11px;
593
702
  }
594
703
 
704
+ .ea-review__current {
705
+ color: var(--theme-elevation-600);
706
+ font-size: 12px;
707
+ }
708
+
709
+ .ea-review__current > summary {
710
+ cursor: pointer;
711
+ }
712
+
713
+ .ea-review__current > p,
714
+ .ea-review__value {
715
+ margin: 0;
716
+ white-space: pre-wrap;
717
+ overflow-wrap: anywhere;
718
+ }
719
+
720
+ .ea-review__current > p,
721
+ .ea-review__current > .ea-review__rich {
722
+ margin-top: 8px;
723
+ }
724
+
725
+ .ea-review__preview,
726
+ .ea-review__value {
727
+ padding: 12px;
728
+ border: 1px solid var(--theme-elevation-150);
729
+ border-radius: var(--style-radius-s);
730
+ background: var(--theme-elevation-50);
731
+ }
732
+
733
+ .ea-review__rich {
734
+ color: var(--theme-text);
735
+ line-height: 1.65;
736
+ overflow-wrap: anywhere;
737
+ }
738
+
739
+ .ea-review__rich > :first-child {
740
+ margin-top: 0;
741
+ }
742
+
743
+ .ea-review__rich > :last-child {
744
+ margin-bottom: 0;
745
+ }
746
+
747
+ .ea-review__rich p {
748
+ margin: 0 0 12px;
749
+ white-space: pre-wrap;
750
+ }
751
+
752
+ .ea-review__rich h1,
753
+ .ea-review__rich h2,
754
+ .ea-review__rich h3,
755
+ .ea-review__rich h4,
756
+ .ea-review__rich h5,
757
+ .ea-review__rich h6 {
758
+ margin: 16px 0 8px;
759
+ font-size: 14px;
760
+ font-weight: 600;
761
+ line-height: 1.4;
762
+ }
763
+
764
+ .ea-review__rich ul,
765
+ .ea-review__rich ol {
766
+ margin: 0 0 12px;
767
+ padding-inline-start: 20px;
768
+ }
769
+
770
+ .ea-review__rich li + li {
771
+ margin-top: 4px;
772
+ }
773
+
774
+ .ea-review__rich blockquote {
775
+ margin: 12px 0;
776
+ padding-inline-start: 12px;
777
+ border-inline-start: 2px solid var(--theme-elevation-250);
778
+ }
779
+
780
+ .ea-review__link {
781
+ text-decoration: underline;
782
+ text-underline-offset: 2px;
783
+ }
784
+
785
+ .ea-review__embedded {
786
+ display: block;
787
+ padding: 8px;
788
+ border: 1px dashed var(--theme-elevation-250);
789
+ color: var(--theme-elevation-600);
790
+ font-size: 12px;
791
+ }
792
+
793
+ .ea-review__change p {
794
+ margin: 0;
795
+ white-space: pre-wrap;
796
+ }
797
+
595
798
  .ea-batch__text,
596
799
  .ea-batch__hint {
597
800
  margin: calc(var(--base) * 0.3) 0 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simmalugnt-se/payload-editor-assistant",
3
- "version": "0.11.3",
3
+ "version": "0.12.1",
4
4
  "description": "Context-aware editor assistant plugin for Payload Admin",
5
5
  "keywords": [
6
6
  "payload",