@blokkli/editor 2.0.0-alpha.62 → 2.0.0-alpha.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/global/types/colorOptions.d.ts +16 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +168 -14
  4. package/dist/modules/agent/runtime/app/components/Conversation/Item/Assistant/index.vue +14 -5
  5. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/ConversationsTab/Item.vue +2 -2
  6. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/RatingsTab/Item.vue +2 -2
  7. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/SplitView/ConversationDetail.vue +2 -2
  8. package/dist/modules/agent/runtime/app/features/agent/Transcript/MessageContent.vue +14 -1
  9. package/dist/modules/agent/runtime/app/features/agent/Transcript/index.vue +30 -12
  10. package/dist/modules/agent/runtime/app/helpers/linkifyBlockUuids.d.ts +11 -0
  11. package/dist/modules/agent/runtime/app/helpers/linkifyBlockUuids.js +47 -0
  12. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.d.vue.ts +22 -0
  13. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.vue +28 -26
  14. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.vue.d.ts +22 -0
  15. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/index.d.ts +15 -0
  16. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.d.vue.ts +22 -0
  17. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue +59 -16
  18. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue.d.ts +22 -0
  19. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/index.d.ts +15 -0
  20. package/dist/modules/agent/runtime/app/tools/fieldDiffApproval.d.ts +85 -15
  21. package/dist/modules/agent/runtime/app/tools/fieldDiffApproval.js +138 -28
  22. package/dist/modules/agent/runtime/app/tools/schemas.d.ts +15 -5
  23. package/dist/modules/agent/runtime/app/tools/schemas.js +29 -12
  24. package/dist/modules/agent/runtime/app/tools/search_text/index.js +1 -1
  25. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.d.vue.ts +22 -0
  26. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.vue +54 -30
  27. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.vue.d.ts +22 -0
  28. package/dist/modules/agent/runtime/app/tools/update_text_fields/index.d.ts +15 -0
  29. package/dist/modules/agent/runtime/server/default-system-prompts/architecture.js +1 -1
  30. package/dist/modules/agent/runtime/server/default-system-prompts/important-rules.js +1 -0
  31. package/dist/modules/charts/runtime/blokkli/tools/chart_schemas.d.ts +1 -1
  32. package/dist/modules/charts/runtime/blokkli/tools/chart_schemas.js +12 -5
  33. package/dist/modules/charts/runtime/components/ChartRenderer/index.vue +7 -16
  34. package/dist/modules/charts/runtime/features/charts/Editor/ColorDropdown/index.vue +7 -77
  35. package/dist/modules/charts/runtime/features/charts/Editor/CsvImport/csvHelpers.d.ts +1 -1
  36. package/dist/modules/charts/runtime/features/charts/Editor/useChartEditorState.d.ts +1 -1
  37. package/dist/modules/charts/runtime/helpers/index.d.ts +2 -1
  38. package/dist/modules/charts/runtime/helpers/index.js +4 -2
  39. package/dist/modules/drupal/graphql/base/fragment.paragraphsBlokkliMutationResult.graphql +3 -0
  40. package/dist/modules/drupal/runtime/adapter/index.js +2 -1
  41. package/dist/runtime/composables/useBlokkliRuntimeConfig.d.ts +50 -0
  42. package/dist/runtime/composables/useBlokkliRuntimeConfig.js +34 -0
  43. package/dist/runtime/editor/adapter/index.d.ts +2 -1
  44. package/dist/runtime/editor/components/ColorDropdown/index.d.vue.ts +26 -0
  45. package/dist/runtime/editor/components/ColorDropdown/index.vue +116 -0
  46. package/dist/runtime/editor/components/ColorDropdown/index.vue.d.ts +26 -0
  47. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.d.vue.ts +14 -20
  48. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue +97 -52
  49. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue.d.ts +14 -20
  50. package/dist/runtime/editor/components/DiffApproval/Highlight/index.d.vue.ts +6 -6
  51. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue +30 -21
  52. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue.d.ts +6 -6
  53. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.d.vue.ts +19 -9
  54. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue +18 -9
  55. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue.d.ts +19 -9
  56. package/dist/runtime/editor/components/DiffApproval/index.d.vue.ts +14 -6
  57. package/dist/runtime/editor/components/DiffApproval/index.vue +62 -35
  58. package/dist/runtime/editor/components/DiffApproval/index.vue.d.ts +14 -6
  59. package/dist/runtime/editor/components/DiffApproval/types.d.ts +32 -0
  60. package/dist/runtime/editor/components/DiffApproval/types.js +22 -0
  61. package/dist/runtime/editor/components/Dropdown/index.d.vue.ts +5 -4
  62. package/dist/runtime/editor/components/Dropdown/index.vue +89 -29
  63. package/dist/runtime/editor/components/Dropdown/index.vue.d.ts +5 -4
  64. package/dist/runtime/editor/components/FlexTextarea/index.d.vue.ts +1 -1
  65. package/dist/runtime/editor/components/FlexTextarea/index.vue.d.ts +1 -1
  66. package/dist/runtime/editor/components/NestedEditorOverlay/index.d.vue.ts +2 -2
  67. package/dist/runtime/editor/components/NestedEditorOverlay/index.vue +3 -3
  68. package/dist/runtime/editor/components/NestedEditorOverlay/index.vue.d.ts +2 -2
  69. package/dist/runtime/editor/components/PopupHost/index.d.vue.ts +13 -0
  70. package/dist/runtime/editor/components/PopupHost/index.vue +12 -0
  71. package/dist/runtime/editor/components/PopupHost/index.vue.d.ts +13 -0
  72. package/dist/runtime/editor/components/index.d.ts +2 -0
  73. package/dist/runtime/editor/components/index.js +2 -0
  74. package/dist/runtime/editor/css/output.css +1 -1
  75. package/dist/runtime/editor/features/analyze/Main.vue +15 -2
  76. package/dist/runtime/editor/features/analyze/Results/ResultsItemNodes.vue +4 -1
  77. package/dist/runtime/editor/features/analyze/Results/ResultsItemNodesTarget.vue +25 -16
  78. package/dist/runtime/editor/features/analyze/analyzers/defaults/validations.d.ts +10 -0
  79. package/dist/runtime/editor/features/analyze/analyzers/defaults/validations.js +39 -0
  80. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.d.ts +5 -1
  81. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.js +5 -0
  82. package/dist/runtime/editor/features/analyze/index.vue +0 -1
  83. package/dist/runtime/editor/features/changelog/changelog.json +9 -1
  84. package/dist/runtime/editor/features/comments/Comment/Meta/index.vue +1 -1
  85. package/dist/runtime/editor/features/comments/Comment/index.vue +1 -1
  86. package/dist/runtime/editor/features/publish/Dialog/Violations.d.vue.ts +12 -0
  87. package/dist/runtime/editor/features/publish/Dialog/Violations.vue +117 -0
  88. package/dist/runtime/editor/features/publish/Dialog/Violations.vue.d.ts +12 -0
  89. package/dist/runtime/editor/features/publish/Dialog/index.vue +61 -24
  90. package/dist/runtime/editor/features/publish/index.vue +2 -4
  91. package/dist/runtime/editor/features/publish/types.d.ts +0 -4
  92. package/dist/runtime/editor/helpers/color/index.d.ts +7 -0
  93. package/dist/runtime/editor/helpers/color/index.js +14 -0
  94. package/dist/runtime/editor/helpers/diff/index.d.ts +87 -0
  95. package/dist/runtime/editor/helpers/diff/index.js +256 -0
  96. package/dist/runtime/editor/helpers/injections.d.ts +11 -0
  97. package/dist/runtime/editor/helpers/injections.js +1 -0
  98. package/dist/runtime/editor/plugins/Sidebar/Detached/index.d.vue.ts +1 -1
  99. package/dist/runtime/editor/plugins/Sidebar/Detached/index.vue.d.ts +1 -1
  100. package/dist/runtime/editor/plugins/Sidebar/index.d.vue.ts +2 -2
  101. package/dist/runtime/editor/plugins/Sidebar/index.vue.d.ts +2 -2
  102. package/dist/runtime/editor/providers/analyze.js +5 -2
  103. package/dist/runtime/editor/providers/config.d.ts +3 -1
  104. package/dist/runtime/editor/providers/config.js +46 -15
  105. package/dist/runtime/editor/providers/state.d.ts +13 -0
  106. package/dist/runtime/editor/providers/state.js +7 -0
  107. package/dist/runtime/editor/translations/de.json +7 -4
  108. package/dist/runtime/editor/translations/fr.json +2 -4
  109. package/dist/runtime/editor/translations/gsw_CH.json +7 -4
  110. package/dist/runtime/editor/translations/it.json +2 -4
  111. package/dist/runtime/helpers/colors.d.ts +39 -0
  112. package/dist/runtime/helpers/colors.js +28 -0
  113. package/dist/runtime/types/colors.d.ts +11 -0
  114. package/package.json +1 -1
  115. package/dist/runtime/editor/features/validations/Overlay/Item.d.vue.ts +0 -7
  116. package/dist/runtime/editor/features/validations/Overlay/Item.vue +0 -36
  117. package/dist/runtime/editor/features/validations/Overlay/Item.vue.d.ts +0 -7
  118. package/dist/runtime/editor/features/validations/Overlay/index.d.vue.ts +0 -7
  119. package/dist/runtime/editor/features/validations/Overlay/index.vue +0 -115
  120. package/dist/runtime/editor/features/validations/Overlay/index.vue.d.ts +0 -7
  121. package/dist/runtime/editor/features/validations/SidebarItem/index.d.vue.ts +0 -10
  122. package/dist/runtime/editor/features/validations/SidebarItem/index.vue +0 -41
  123. package/dist/runtime/editor/features/validations/SidebarItem/index.vue.d.ts +0 -10
  124. package/dist/runtime/editor/features/validations/index.d.vue.ts +0 -3
  125. package/dist/runtime/editor/features/validations/index.vue +0 -91
  126. package/dist/runtime/editor/features/validations/index.vue.d.ts +0 -3
  127. package/dist/runtime/editor/types/config.d.ts +0 -5
  128. /package/dist/runtime/{editor/types/config.js → types/colors.js} +0 -0
@@ -12,10 +12,13 @@
12
12
  <script setup>
13
13
  import { useBlokkli, ref, onMounted } from "#imports";
14
14
  import { DiffApproval } from "#blokkli/editor/components";
15
+ import { splitIntoSegments } from "#blokkli/editor/helpers/diff";
15
16
  import {
16
17
  skippedFieldsMessage,
17
18
  appendAgentNote,
18
- rejectedWithoutReasonMessage
19
+ rejectedWithoutReasonMessage,
20
+ partialRejectionGuidance,
21
+ decideFieldUpdates
19
22
  } from "../fieldDiffApproval";
20
23
  const props = defineProps({
21
24
  context: { type: null, required: true },
@@ -95,12 +98,18 @@ onMounted(async () => {
95
98
  const uuid = result.key.substring(0, separatorIndex);
96
99
  const fieldName = result.key.substring(separatorIndex + 1);
97
100
  const source = byKey.get(result.key);
101
+ const segments = source ? splitIntoSegments(
102
+ source.value,
103
+ result.translatedText,
104
+ source.fieldType
105
+ ) ?? void 0 : void 0;
98
106
  return {
99
107
  id: id++,
100
108
  uuid,
101
109
  fieldName,
102
110
  fieldLabel: source ? resolveFieldLabel(source.entityType, source.entityBundle, fieldName) : fieldName,
103
- value: result.translatedText
111
+ value: result.translatedText,
112
+ segments
104
113
  };
105
114
  });
106
115
  phase.value = "approving";
@@ -108,49 +117,42 @@ onMounted(async () => {
108
117
  async function onApply(data) {
109
118
  const targetLanguage = entityContext.value.language;
110
119
  const items = approvalItems.value;
111
- const rejectedByUser = {};
112
- const accepted = [];
113
- for (const item of items) {
114
- if (data.selected[item.id]) {
115
- accepted.push(item);
116
- continue;
117
- }
118
- const fields = rejectedByUser[item.uuid] ?? {};
119
- fields[item.fieldName] = {
120
- reasonForRejection: data.reasons[item.id] || ""
121
- };
122
- rejectedByUser[item.uuid] = fields;
123
- }
124
- if (accepted.length) {
120
+ const { updates, rejectedByUser, acceptedCount, totalCount } = decideFieldUpdates(items, data.selected, data.reasons);
121
+ if (updates.length) {
125
122
  await state.mutateWithLoadingState(
126
123
  () => adapter.importTranslationsBatched({
127
- items: accepted.map((item) => ({
124
+ items: updates.map((u) => ({
128
125
  langcode: targetLanguage,
129
- uuid: item.uuid,
130
- fieldName: item.fieldName,
131
- fieldValue: item.value
126
+ uuid: u.uuid,
127
+ fieldName: u.fieldName,
128
+ fieldValue: u.fieldValue
132
129
  }))
133
130
  })
134
131
  );
135
132
  }
136
- const acceptedCount = accepted.length;
137
- const label = acceptedCount === items.length ? $t(
133
+ const label = acceptedCount === totalCount ? $t(
138
134
  "aiAgentAutoTranslateAllApplied",
139
135
  "All @count translations applied"
140
136
  ).replace("@count", String(acceptedCount)) : $t(
141
137
  "aiAgentAutoTranslateSomeApplied",
142
138
  "@applied of @total translations applied"
143
- ).replace("@applied", String(acceptedCount)).replace("@total", String(items.length));
144
- const _details = accepted.map((item) => ({
139
+ ).replace("@applied", String(acceptedCount)).replace("@total", String(totalCount));
140
+ const updatesByItem = new Map(
141
+ updates.map((u) => [`${u.uuid}:${u.fieldName}`, u.fieldValue])
142
+ );
143
+ const _details = items.filter((item) => updatesByItem.has(`${item.uuid}:${item.fieldName}`)).map((item) => ({
145
144
  fieldLabel: item.fieldLabel,
146
145
  before: "",
147
- after: item.value
146
+ after: updatesByItem.get(`${item.uuid}:${item.fieldName}`)
148
147
  }));
149
148
  emitDone({
150
149
  acceptedCount,
151
150
  rejectedByUser,
152
151
  label,
153
- agentMessage: rejectedWithoutReasonMessage(rejectedByUser),
152
+ agentMessage: appendAgentNote(
153
+ partialRejectionGuidance(rejectedByUser),
154
+ rejectedWithoutReasonMessage(rejectedByUser)
155
+ ),
154
156
  historyIndex: state.currentMutationIndex.value,
155
157
  _details
156
158
  });
@@ -9,6 +9,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
9
9
  acceptedCount: number;
10
10
  rejectedByUser: Record<string, Record<string, {
11
11
  reasonForRejection: string;
12
+ partial?: {
13
+ accepted: number;
14
+ total: number;
15
+ rejectedSegments: {
16
+ tag: string;
17
+ beforeHtml: string;
18
+ afterHtml: string;
19
+ status: "deleted" | "matched" | "inserted";
20
+ reasonForRejection: string;
21
+ }[];
22
+ } | undefined;
12
23
  }>>;
13
24
  label: string;
14
25
  agentMessage?: string | undefined;
@@ -19,6 +30,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
19
30
  acceptedCount: number;
20
31
  rejectedByUser: Record<string, Record<string, {
21
32
  reasonForRejection: string;
33
+ partial?: {
34
+ accepted: number;
35
+ total: number;
36
+ rejectedSegments: {
37
+ tag: string;
38
+ beforeHtml: string;
39
+ afterHtml: string;
40
+ status: "deleted" | "matched" | "inserted";
41
+ reasonForRejection: string;
42
+ }[];
43
+ } | undefined;
22
44
  }>>;
23
45
  label: string;
24
46
  agentMessage?: string | undefined;
@@ -19,6 +19,21 @@ export declare const resultSchema: z.ZodObject<{
19
19
  acceptedCount: z.ZodNumber;
20
20
  rejectedByUser: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
21
21
  reasonForRejection: z.ZodString;
22
+ partial: z.ZodOptional<z.ZodObject<{
23
+ accepted: z.ZodNumber;
24
+ total: z.ZodNumber;
25
+ rejectedSegments: z.ZodArray<z.ZodObject<{
26
+ tag: z.ZodString;
27
+ beforeHtml: z.ZodString;
28
+ afterHtml: z.ZodString;
29
+ status: z.ZodEnum<{
30
+ deleted: "deleted";
31
+ matched: "matched";
32
+ inserted: "inserted";
33
+ }>;
34
+ reasonForRejection: z.ZodString;
35
+ }, z.core.$strip>>;
36
+ }, z.core.$strip>>;
22
37
  }, z.core.$strip>>>;
23
38
  label: z.ZodString;
24
39
  agentMessage: z.ZodOptional<z.ZodString>;
@@ -9,6 +9,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
9
9
  acceptedCount: number;
10
10
  rejectedByUser: Record<string, Record<string, {
11
11
  reasonForRejection: string;
12
+ partial?: {
13
+ accepted: number;
14
+ total: number;
15
+ rejectedSegments: {
16
+ tag: string;
17
+ beforeHtml: string;
18
+ afterHtml: string;
19
+ status: "deleted" | "matched" | "inserted";
20
+ reasonForRejection: string;
21
+ }[];
22
+ } | undefined;
12
23
  }>>;
13
24
  label: string;
14
25
  agentMessage?: string | undefined;
@@ -19,6 +30,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
19
30
  acceptedCount: number;
20
31
  rejectedByUser: Record<string, Record<string, {
21
32
  reasonForRejection: string;
33
+ partial?: {
34
+ accepted: number;
35
+ total: number;
36
+ rejectedSegments: {
37
+ tag: string;
38
+ beforeHtml: string;
39
+ afterHtml: string;
40
+ status: "deleted" | "matched" | "inserted";
41
+ reasonForRejection: string;
42
+ }[];
43
+ } | undefined;
22
44
  }>>;
23
45
  label: string;
24
46
  agentMessage?: string | undefined;
@@ -52,11 +52,13 @@
52
52
  <script setup>
53
53
  import { useBlokkli, ref, onMounted, onBeforeUnmount } from "#imports";
54
54
  import { Icon, DiffApproval } from "#blokkli/editor/components";
55
+ import { splitIntoSegments } from "#blokkli/editor/helpers/diff";
55
56
  import ToolCard from "../../features/agent/Panel/ToolCard/index.vue";
56
57
  import { applyOperations } from "../helpers";
57
58
  import {
58
59
  applyFieldDiffs,
59
60
  rejectedWithoutReasonMessage,
61
+ partialRejectionGuidance,
60
62
  skippedFieldsMessage,
61
63
  appendAgentNote
62
64
  } from "../fieldDiffApproval";
@@ -110,12 +112,21 @@ function transitionToApproval() {
110
112
  }
111
113
  if (override.originalValue === finalValue) continue;
112
114
  const itemId = idCounter++;
115
+ const field = props.params.fields.find(
116
+ (f) => f.uuid === fs.uuid && f.fieldName === fs.fieldName
117
+ );
118
+ const segments = field ? splitIntoSegments(
119
+ override.originalValue,
120
+ finalValue,
121
+ field.fieldType
122
+ ) ?? void 0 : void 0;
113
123
  items.push({
114
124
  id: itemId,
115
125
  uuid: fs.uuid,
116
126
  fieldName: fs.fieldName,
117
127
  fieldLabel: fs.fieldLabel,
118
- value: finalValue
128
+ value: finalValue,
129
+ segments
119
130
  });
120
131
  beforeValues.set(itemId, override.originalValue);
121
132
  }
@@ -172,7 +183,7 @@ function rejectAllFromApproval() {
172
183
  }
173
184
  async function applySelected(data) {
174
185
  const { selected, reasons } = data;
175
- const { acceptedCount, rejectedByUser, label } = await applyFieldDiffs(
186
+ const { acceptedCount, rejectedByUser, label, appliedByItemId } = await applyFieldDiffs(
176
187
  blokkli,
177
188
  props.context.adapter,
178
189
  completedItems.value,
@@ -180,40 +191,72 @@ async function applySelected(data) {
180
191
  reasons
181
192
  );
182
193
  const parts = [];
183
- const acceptedItems = completedItems.value.filter((item) => selected[item.id]);
184
- if (acceptedItems.length > 0) {
194
+ const fullyAccepted = completedItems.value.filter(
195
+ (item) => appliedByItemId[item.id] !== void 0 && !rejectedByUser[item.uuid]?.[item.fieldName]
196
+ );
197
+ if (fullyAccepted.length > 0) {
185
198
  parts.push("Accepted fields:");
186
- for (const item of acceptedItems) {
187
- const truncated = item.value.length > 200 ? item.value.slice(0, 200) + "..." : item.value;
199
+ for (const item of fullyAccepted) {
200
+ const written = appliedByItemId[item.id];
201
+ const truncated = written.length > 200 ? written.slice(0, 200) + "..." : written;
188
202
  parts.push(`- ${item.uuid} "${item.fieldName}": ${truncated}`);
189
203
  }
190
204
  }
191
- const rejectedItems = completedItems.value.filter(
192
- (item) => !selected[item.id]
205
+ const partials = completedItems.value.map((item) => {
206
+ const entry = rejectedByUser[item.uuid]?.[item.fieldName];
207
+ if (!entry?.partial || appliedByItemId[item.id] === void 0) return null;
208
+ return { item, entry };
209
+ }).filter(
210
+ (p) => p !== null
211
+ );
212
+ if (partials.length > 0) {
213
+ parts.push("Partially accepted fields:");
214
+ for (const { item, entry } of partials) {
215
+ const { accepted, total, rejectedSegments } = entry.partial;
216
+ parts.push(
217
+ `- ${item.uuid} "${item.fieldName}": ${accepted}/${total} chunks accepted`
218
+ );
219
+ for (const seg of rejectedSegments) {
220
+ const reasonText = seg.reasonForRejection ? ` (reason: ${seg.reasonForRejection})` : " (no reason given)";
221
+ parts.push(` \xB7 rejected <${seg.tag}> (${seg.status})${reasonText}`);
222
+ }
223
+ }
224
+ }
225
+ const fullyRejected = completedItems.value.map((item) => {
226
+ const entry = rejectedByUser[item.uuid]?.[item.fieldName];
227
+ if (!entry || entry.partial || appliedByItemId[item.id] !== void 0) {
228
+ return null;
229
+ }
230
+ return { item, entry };
231
+ }).filter(
232
+ (p) => p !== null
193
233
  );
194
- if (rejectedItems.length > 0) {
234
+ if (fullyRejected.length > 0) {
195
235
  parts.push("Rejected fields:");
196
- for (const item of rejectedItems) {
197
- const reason = reasons[item.id] || "";
198
- const reasonText = reason ? ` (reason: ${reason})` : " (no reason given)";
236
+ for (const { item, entry } of fullyRejected) {
237
+ const reasonText = entry.reasonForRejection ? ` (reason: ${entry.reasonForRejection})` : " (no reason given)";
199
238
  parts.push(`- ${item.uuid} "${item.fieldName}"${reasonText}`);
200
239
  }
201
240
  }
202
241
  let agentMessage = parts.join("\n");
242
+ const partialNote = partialRejectionGuidance(rejectedByUser);
243
+ if (partialNote) agentMessage += "\n\n" + partialNote;
203
244
  const followUp = rejectedWithoutReasonMessage(rejectedByUser);
204
245
  if (followUp) agentMessage += "\n" + followUp;
205
- const _details = completedItems.value.filter((item) => selected[item.id]).map((item) => {
246
+ const _details = completedItems.value.filter((item) => appliedByItemId[item.id] !== void 0).map((item) => {
206
247
  const fs = findFieldState(item.uuid, item.fieldName);
248
+ const isPartial = !!rejectedByUser[item.uuid]?.[item.fieldName]?.partial;
207
249
  const hadRetries = fs ? fs.allOperations.length > 0 : false;
208
- const operations = fs?.mode === "patch" && !hadRetries ? [...fs.operations] : [];
250
+ const operations = fs?.mode === "patch" && !hadRetries && !isPartial ? [...fs.operations] : [];
209
251
  return {
210
252
  fieldLabel: item.fieldLabel,
211
253
  before: fs?.originalBaseValue || beforeValues.get(item.id) || "",
212
- after: item.value,
254
+ after: appliedByItemId[item.id],
213
255
  mode: fs?.mode || "full",
214
256
  operations
215
257
  };
216
258
  });
259
+ const anyRejection = Object.keys(rejectedByUser).length > 0;
217
260
  emitDone({
218
261
  acceptedCount,
219
262
  rejectedByUser,
@@ -224,7 +267,7 @@ async function applySelected(data) {
224
267
  _usage: streamUsage.value,
225
268
  // Let the agent respond if anything was rejected or skipped, so it can
226
269
  // retry the skipped references.
227
- _skipLlmResponse: rejectedItems.length === 0 && !skippedNote
270
+ _skipLlmResponse: !anyRejection && !skippedNote
228
271
  });
229
272
  }
230
273
  onMounted(async () => {
@@ -9,6 +9,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
9
9
  acceptedCount: number;
10
10
  rejectedByUser: Record<string, Record<string, {
11
11
  reasonForRejection: string;
12
+ partial?: {
13
+ accepted: number;
14
+ total: number;
15
+ rejectedSegments: {
16
+ tag: string;
17
+ beforeHtml: string;
18
+ afterHtml: string;
19
+ status: "deleted" | "matched" | "inserted";
20
+ reasonForRejection: string;
21
+ }[];
22
+ } | undefined;
12
23
  }>>;
13
24
  label: string;
14
25
  agentMessage?: string | undefined;
@@ -19,6 +30,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
19
30
  acceptedCount: number;
20
31
  rejectedByUser: Record<string, Record<string, {
21
32
  reasonForRejection: string;
33
+ partial?: {
34
+ accepted: number;
35
+ total: number;
36
+ rejectedSegments: {
37
+ tag: string;
38
+ beforeHtml: string;
39
+ afterHtml: string;
40
+ status: "deleted" | "matched" | "inserted";
41
+ reasonForRejection: string;
42
+ }[];
43
+ } | undefined;
22
44
  }>>;
23
45
  label: string;
24
46
  agentMessage?: string | undefined;
@@ -41,6 +41,21 @@ export declare const resultSchema: z.ZodObject<{
41
41
  acceptedCount: z.ZodNumber;
42
42
  rejectedByUser: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
43
43
  reasonForRejection: z.ZodString;
44
+ partial: z.ZodOptional<z.ZodObject<{
45
+ accepted: z.ZodNumber;
46
+ total: z.ZodNumber;
47
+ rejectedSegments: z.ZodArray<z.ZodObject<{
48
+ tag: z.ZodString;
49
+ beforeHtml: z.ZodString;
50
+ afterHtml: z.ZodString;
51
+ status: z.ZodEnum<{
52
+ deleted: "deleted";
53
+ matched: "matched";
54
+ inserted: "inserted";
55
+ }>;
56
+ reasonForRejection: z.ZodString;
57
+ }, z.core.$strip>>;
58
+ }, z.core.$strip>>;
44
59
  }, z.core.$strip>>>;
45
60
  label: z.ZodString;
46
61
  agentMessage: z.ZodOptional<z.ZodString>;
@@ -1,10 +1,29 @@
1
1
  import type { BlokkliApp } from '#blokkli/editor/types/app';
2
2
  import type { FullBlokkliAdapter } from '#blokkli/editor/adapter';
3
3
  import type { ApprovalItem } from '#blokkli/editor/components/DiffApproval/types';
4
- /** Map of rejected paragraph UUID → field name → rejection details. */
5
- export type RejectedByUser = Record<string, Record<string, {
4
+ export type RejectedSegment = {
5
+ tag: string;
6
+ beforeHtml: string;
7
+ afterHtml: string;
8
+ status: 'matched' | 'inserted' | 'deleted';
6
9
  reasonForRejection: string;
7
- }>>;
10
+ };
11
+ export type FieldRejection = {
12
+ reasonForRejection: string;
13
+ /**
14
+ * Present when the user evaluated a segmented field chunk-by-chunk. When
15
+ * `accepted > 0` the field was updated with a hybrid value; when
16
+ * `accepted === 0` every chunk was rejected and the field stayed at its
17
+ * original value.
18
+ */
19
+ partial?: {
20
+ accepted: number;
21
+ total: number;
22
+ rejectedSegments: RejectedSegment[];
23
+ };
24
+ };
25
+ /** Map of paragraph UUID → field name → rejection details. */
26
+ export type RejectedByUser = Record<string, Record<string, FieldRejection>>;
8
27
  /**
9
28
  * A field reference (uuid + field name) that was dropped before applying,
10
29
  * because the paragraph or the field does not exist. Reported back to the agent
@@ -26,24 +45,75 @@ export declare function skippedFieldsMessage(skipped: SkippedField[]): string |
26
45
  * side. Used to add the skipped-fields note to every terminal `agentMessage`.
27
46
  */
28
47
  export declare function appendAgentNote(message: string | undefined, note: string | undefined): string | undefined;
29
- export type FieldDiffApplyResult = {
30
- acceptedCount: number;
48
+ export type DecidedUpdate = {
49
+ itemId: number;
50
+ uuid: string;
51
+ fieldName: string;
52
+ fieldValue: string;
53
+ };
54
+ export type FieldDecisionResult = {
55
+ /**
56
+ * One entry per accepted item, with the value that should be written. For
57
+ * segmented items this is the reassembled hybrid (accepted chunks + original
58
+ * chunks for rejected ones); for unsegmented items it's `item.value`.
59
+ */
60
+ updates: DecidedUpdate[];
31
61
  rejectedByUser: RejectedByUser;
62
+ /**
63
+ * Number of accepted toggle units across the whole batch. For unsegmented
64
+ * fields this is "field accepted" / "field rejected"; for segmented fields
65
+ * each changed chunk counts as one unit.
66
+ */
67
+ acceptedCount: number;
68
+ /** Total number of toggle units the user could have selected. */
69
+ totalCount: number;
70
+ };
71
+ /**
72
+ * Walk the approval items and decide, per field, what gets written and what
73
+ * gets reported as rejected. Pure: does no IO and does not depend on the
74
+ * adapter. The caller picks the mutation strategy (entity vs. batch update,
75
+ * translation import, …).
76
+ *
77
+ * `selected` and `reasons` are keyed by `ApprovalUnit.key` — the stringified
78
+ * item id for unsegmented items, `${itemId}:${segmentId}` for segmented.
79
+ */
80
+ export declare function decideFieldUpdates(items: ApprovalItem[], selected: Record<string, boolean>, reasons: Record<string, string>): FieldDecisionResult;
81
+ export type FieldDiffApplyResult = FieldDecisionResult & {
32
82
  label: string;
83
+ /**
84
+ * The value that was actually written for each accepted item, keyed by
85
+ * `ApprovalItem.id`. Used by the caller to build the `_details` panel
86
+ * (agent-facing audit log).
87
+ */
88
+ appliedByItemId: Record<number, string>;
33
89
  };
34
90
  /**
35
- * Apply the user's accept/reject decisions for a batch of field diffs: split
36
- * the accepted items into entity-level vs block-level updates, run the batched
37
- * mutation, and return the accepted count, rejection map, and a summary label.
91
+ * Apply the user's accept/reject decisions for a batch of field diffs through
92
+ * `updateFieldValueBatched`. Shared by `update_text_fields` and
93
+ * `delegate_text_rewrite`. The translation tool calls `decideFieldUpdates`
94
+ * directly because it imports through a different adapter method.
95
+ */
96
+ export declare function applyFieldDiffs(app: BlokkliApp, adapter: FullBlokkliAdapter<any>, items: ApprovalItem[], selected: Record<string, boolean>, reasons: Record<string, string>): Promise<FieldDiffApplyResult>;
97
+ /**
98
+ * Inline guidance the agent should follow when ANY field in this result came
99
+ * back with `partial` set AND at least one chunk was accepted — i.e. the
100
+ * field was updated with a hybrid value. Returns undefined when there were
101
+ * no such hybrids.
38
102
  *
39
- * Shared by `update_text_fields` and `delegate_text_rewrite`, which both render
40
- * the same diff-approval UI and report the same accepted/rejected outcome. Each
41
- * caller layers its own `agentMessage` / details / usage on top of the result.
103
+ * Scoping a follow-up to the rejected chunks (instead of rewriting the field
104
+ * whole) is non-obvious without this hint the model tends to re-propose
105
+ * the entire field and clobber the accepted chunks. The guidance only
106
+ * appears when a hybrid was actually written, so it costs nothing on the
107
+ * normal "all accepted" or "all rejected" paths.
42
108
  */
43
- export declare function applyFieldDiffs(app: BlokkliApp, adapter: FullBlokkliAdapter<any>, items: ApprovalItem[], selected: Record<number, boolean>, reasons: Record<number, string>): Promise<FieldDiffApplyResult>;
109
+ export declare function partialRejectionGuidance(rejected: RejectedByUser): string | undefined;
44
110
  /**
45
111
  * The follow-up instruction to give the agent when the user rejected fields
46
- * without a reason. Returns undefined when every rejection had a reason (or
47
- * there were none).
112
+ * or chunks without a reason. Returns undefined when every rejection had a
113
+ * reason (or there were none).
114
+ *
115
+ * Partial rejections are surfaced at chunk granularity so the agent knows to
116
+ * offer alternatives for the specific paragraph or list item, not the whole
117
+ * field.
48
118
  */
49
- export declare function rejectedWithoutReasonMessage(rejectedByUser: RejectedByUser): string | undefined;
119
+ export declare function rejectedWithoutReasonMessage(rejected: RejectedByUser): string | undefined;