@blokkli/editor 2.0.0-alpha.61 → 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 +38 -27
  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
@@ -21,3 +21,90 @@ export declare function computeDiff(before: string, after: string): string;
21
21
  * <ins> rather than wrapping the block tags themselves.
22
22
  */
23
23
  export declare function computeInsertion(after: string): string;
24
+ /**
25
+ * One chunk of a field's diff that can be individually accepted or rejected
26
+ * by the user. A markup field becomes an ordered list of segments; lists
27
+ * (<ul>/<ol>) become a `list` segment whose children are the individual
28
+ * <li> atoms.
29
+ *
30
+ * Acceptance semantics (see `reassembleValue`):
31
+ * - matched: accepted → afterHtml, rejected → beforeHtml
32
+ * - inserted: accepted → keep, rejected → drop
33
+ * - deleted: accepted → drop (matches after), rejected → restore (matches before)
34
+ */
35
+ export type AtomicSegment = {
36
+ kind: 'atomic';
37
+ id: string;
38
+ tag: string;
39
+ openTag: string;
40
+ closeTag: string;
41
+ beforeHtml: string;
42
+ afterHtml: string;
43
+ changed: boolean;
44
+ status: 'matched' | 'inserted' | 'deleted';
45
+ };
46
+ export type ListSegment = {
47
+ kind: 'list';
48
+ id: string;
49
+ tag: 'ul' | 'ol';
50
+ openTag: string;
51
+ closeTag: string;
52
+ children: AtomicSegment[];
53
+ };
54
+ export type Segment = AtomicSegment | ListSegment;
55
+ /**
56
+ * Split a before/after value pair into chunks the user can individually
57
+ * accept or reject. Returns null when chunk-level approval can't apply:
58
+ *
59
+ * - non-markup fields
60
+ * - either side contains inline-only HTML or stray top-level text
61
+ * (`parseBlocks` returns null)
62
+ * - both sides empty
63
+ *
64
+ * Two-level structure: top-level block alignment, then a one-level recurse
65
+ * into matched <ul>/<ol> blocks whose innerHTML differs. <li> is the leaf.
66
+ *
67
+ * Used for both rewrites (word-diff preview) and translations
68
+ * (insertion-only preview). The difference is at the renderer.
69
+ */
70
+ export declare function splitIntoSegments(before: string, after: string, fieldType: 'plain' | 'markup'): Segment[] | null;
71
+ /**
72
+ * True when at least one segment (or list child) actually changes. Use to
73
+ * skip the approval row for fields where the proposed value already matches
74
+ * the current value chunk-for-chunk.
75
+ */
76
+ export declare function segmentsHaveChanges(segments: Segment[]): boolean;
77
+ /**
78
+ * Render segments as preview HTML with `data-chunk-index` attributes on each
79
+ * atomic block, so the approval UI can locate each chunk's DOM bounds. The
80
+ * resulting markup replaces the editable's innerHTML during preview, just
81
+ * like `computeDiff` / `computeInsertion`.
82
+ *
83
+ * `insertionsOnly` matches the existing prop on DiffApproval — translations
84
+ * render new content as a single <ins> per leaf, rewrites render word-diffs.
85
+ *
86
+ * `acceptedById` (optional) makes the preview track per-segment acceptance:
87
+ * rejected matched segments render their original content with no markers,
88
+ * rejected insertions render as deletions, rejected deletions render plain.
89
+ * Defaults to "all accepted" when omitted (initial render).
90
+ */
91
+ export declare function renderSegmentDiff(segments: Segment[], opts?: {
92
+ insertionsOnly?: boolean;
93
+ acceptedById?: Record<string, boolean>;
94
+ }): string;
95
+ /**
96
+ * Reassemble a field value from segments using per-segment acceptance.
97
+ * Acceptance defaults to `true` for any unmapped segment id, mirroring the
98
+ * DiffApproval initial state.
99
+ *
100
+ * For `list` segments the wrapper is emitted only when at least one child
101
+ * survives — accepting a full deletion of every item drops the list itself.
102
+ */
103
+ export declare function reassembleValue(segments: Segment[], acceptedById: Record<string, boolean>): string;
104
+ /**
105
+ * Walk a segment list and return every atomic segment in reading order,
106
+ * including those inside list wrappers. Used by the approval UI for
107
+ * keyboard nav, selection initialisation, and the rejected-segments
108
+ * feedback to the agent.
109
+ */
110
+ export declare function flattenSegments(segments: Segment[]): AtomicSegment[];
@@ -234,3 +234,259 @@ export function computeInsertion(after) {
234
234
  }
235
235
  return `<ins>${after}</ins>`;
236
236
  }
237
+ function parseListItems(html) {
238
+ const container = document.createElement("div");
239
+ container.innerHTML = html;
240
+ const items = [];
241
+ for (const child of container.childNodes) {
242
+ if (child.nodeType === Node.ELEMENT_NODE) {
243
+ const el = child;
244
+ const tag = el.tagName.toLowerCase();
245
+ if (tag !== "li") return null;
246
+ let openTag = `<${tag}`;
247
+ for (const attr of el.attributes) {
248
+ openTag += ` ${attr.name}="${attr.value}"`;
249
+ }
250
+ openTag += ">";
251
+ items.push({ tag, innerHTML: el.innerHTML, openTag });
252
+ } else if (child.nodeType === Node.TEXT_NODE && child.textContent?.trim()) {
253
+ return null;
254
+ }
255
+ }
256
+ return items;
257
+ }
258
+ function alignListItems(before, after) {
259
+ const m = before.length;
260
+ const n = after.length;
261
+ const dp = Array.from(
262
+ { length: m + 1 },
263
+ () => Array(n + 1).fill(0)
264
+ );
265
+ for (let i2 = 1; i2 <= m; i2++) {
266
+ for (let j2 = 1; j2 <= n; j2++) {
267
+ if (before[i2 - 1].innerHTML === after[j2 - 1].innerHTML) {
268
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
269
+ } else {
270
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
271
+ }
272
+ }
273
+ }
274
+ const ops = [];
275
+ let i = m;
276
+ let j = n;
277
+ while (i > 0 && j > 0) {
278
+ if (before[i - 1].innerHTML === after[j - 1].innerHTML) {
279
+ ops.push({
280
+ type: "unchanged",
281
+ before: before[i - 1],
282
+ after: after[j - 1]
283
+ });
284
+ i--;
285
+ j--;
286
+ } else if (dp[i - 1][j] >= dp[i][j - 1]) {
287
+ ops.push({ type: "before", block: before[i - 1] });
288
+ i--;
289
+ } else {
290
+ ops.push({ type: "after", block: after[j - 1] });
291
+ j--;
292
+ }
293
+ }
294
+ while (i > 0) {
295
+ ops.push({ type: "before", block: before[i - 1] });
296
+ i--;
297
+ }
298
+ while (j > 0) {
299
+ ops.push({ type: "after", block: after[j - 1] });
300
+ j--;
301
+ }
302
+ ops.reverse();
303
+ const entries = [];
304
+ let k = 0;
305
+ while (k < ops.length) {
306
+ const op = ops[k];
307
+ if (op.type === "unchanged") {
308
+ entries.push({ type: "match", before: op.before, after: op.after });
309
+ k++;
310
+ continue;
311
+ }
312
+ const befores = [];
313
+ const afters = [];
314
+ while (k < ops.length && ops[k].type !== "unchanged") {
315
+ const r = ops[k];
316
+ if (r.type === "before") befores.push(r.block);
317
+ else if (r.type === "after") afters.push(r.block);
318
+ k++;
319
+ }
320
+ const pairCount = Math.min(befores.length, afters.length);
321
+ for (let p = 0; p < pairCount; p++) {
322
+ entries.push({ type: "match", before: befores[p], after: afters[p] });
323
+ }
324
+ for (let p = pairCount; p < befores.length; p++) {
325
+ entries.push({ type: "delete", before: befores[p] });
326
+ }
327
+ for (let p = pairCount; p < afters.length; p++) {
328
+ entries.push({ type: "insert", after: afters[p] });
329
+ }
330
+ }
331
+ return entries;
332
+ }
333
+ function atomicFromAlignment(entry, id) {
334
+ if (entry.type === "match") {
335
+ return {
336
+ kind: "atomic",
337
+ id,
338
+ tag: entry.after.tag,
339
+ openTag: entry.after.openTag,
340
+ closeTag: `</${entry.after.tag}>`,
341
+ beforeHtml: entry.before.innerHTML,
342
+ afterHtml: entry.after.innerHTML,
343
+ changed: entry.before.innerHTML !== entry.after.innerHTML,
344
+ status: "matched"
345
+ };
346
+ }
347
+ if (entry.type === "delete") {
348
+ return {
349
+ kind: "atomic",
350
+ id,
351
+ tag: entry.before.tag,
352
+ openTag: entry.before.openTag,
353
+ closeTag: `</${entry.before.tag}>`,
354
+ beforeHtml: entry.before.innerHTML,
355
+ afterHtml: "",
356
+ changed: true,
357
+ status: "deleted"
358
+ };
359
+ }
360
+ return {
361
+ kind: "atomic",
362
+ id,
363
+ tag: entry.after.tag,
364
+ openTag: entry.after.openTag,
365
+ closeTag: `</${entry.after.tag}>`,
366
+ beforeHtml: "",
367
+ afterHtml: entry.after.innerHTML,
368
+ changed: true,
369
+ status: "inserted"
370
+ };
371
+ }
372
+ export function splitIntoSegments(before, after, fieldType) {
373
+ if (fieldType !== "markup") return null;
374
+ const beforeBlocks = parseBlocks(before);
375
+ const afterBlocks = parseBlocks(after);
376
+ if (!beforeBlocks || !afterBlocks) return null;
377
+ if (beforeBlocks.length === 0 && afterBlocks.length === 0) return null;
378
+ const alignment = alignBlocks(beforeBlocks, afterBlocks);
379
+ const segments = [];
380
+ alignment.forEach((entry, index) => {
381
+ const id = String(index);
382
+ if (entry.type === "match" && (entry.after.tag === "ul" || entry.after.tag === "ol") && entry.before.innerHTML !== entry.after.innerHTML) {
383
+ const beforeItems = parseListItems(entry.before.innerHTML);
384
+ const afterItems = parseListItems(entry.after.innerHTML);
385
+ if (beforeItems && afterItems) {
386
+ const childAlignment = alignListItems(beforeItems, afterItems);
387
+ const children = childAlignment.map(
388
+ (childEntry, childIndex) => atomicFromAlignment(childEntry, `${id}/${childIndex}`)
389
+ );
390
+ segments.push({
391
+ kind: "list",
392
+ id,
393
+ tag: entry.after.tag,
394
+ openTag: entry.after.openTag,
395
+ closeTag: `</${entry.after.tag}>`,
396
+ children
397
+ });
398
+ return;
399
+ }
400
+ }
401
+ segments.push(atomicFromAlignment(entry, id));
402
+ });
403
+ return segments;
404
+ }
405
+ export function segmentsHaveChanges(segments) {
406
+ return segments.some(
407
+ (seg) => seg.kind === "list" ? seg.children.some(
408
+ (c) => c.status !== "matched" || c.beforeHtml !== c.afterHtml
409
+ ) : seg.status !== "matched" || seg.beforeHtml !== seg.afterHtml
410
+ );
411
+ }
412
+ function injectAttribute(openTag, name, value) {
413
+ return openTag.slice(0, -1) + ` ${name}="${value}">`;
414
+ }
415
+ function renderAtomicDiffMarkup(seg, opts) {
416
+ const open = injectAttribute(seg.openTag, "data-chunk-index", seg.id);
417
+ const accepted = opts.acceptedById?.[seg.id] !== false;
418
+ if (seg.status === "inserted") {
419
+ if (!accepted) {
420
+ return `${open}<del>${seg.afterHtml}</del>${seg.closeTag}`;
421
+ }
422
+ return `${open}<ins>${seg.afterHtml}</ins>${seg.closeTag}`;
423
+ }
424
+ if (seg.status === "deleted") {
425
+ if (!accepted) {
426
+ return `${open}${seg.beforeHtml}${seg.closeTag}`;
427
+ }
428
+ return `${open}<del>${seg.beforeHtml}</del>${seg.closeTag}`;
429
+ }
430
+ if (!accepted) {
431
+ return `${open}${seg.beforeHtml}${seg.closeTag}`;
432
+ }
433
+ if (opts.insertionsOnly) {
434
+ return `${open}<ins>${seg.afterHtml}</ins>${seg.closeTag}`;
435
+ }
436
+ if (seg.beforeHtml === seg.afterHtml) {
437
+ return `${open}${seg.afterHtml}${seg.closeTag}`;
438
+ }
439
+ const innerDiff = diff(seg.beforeHtml, seg.afterHtml);
440
+ return `${open}${cleanupDiffMods(innerDiff)}${seg.closeTag}`;
441
+ }
442
+ export function renderSegmentDiff(segments, opts = {}) {
443
+ const parts = [];
444
+ for (const seg of segments) {
445
+ if (seg.kind === "list") {
446
+ const childParts = seg.children.map(
447
+ (c) => renderAtomicDiffMarkup(c, opts)
448
+ );
449
+ parts.push(`${seg.openTag}${childParts.join("")}${seg.closeTag}`);
450
+ } else {
451
+ parts.push(renderAtomicDiffMarkup(seg, opts));
452
+ }
453
+ }
454
+ return parts.join("");
455
+ }
456
+ export function reassembleValue(segments, acceptedById) {
457
+ const isAccepted = (id) => acceptedById[id] !== false;
458
+ const renderAtomic = (seg) => {
459
+ const accepted = isAccepted(seg.id);
460
+ if (seg.status === "matched") {
461
+ return seg.openTag + (accepted ? seg.afterHtml : seg.beforeHtml) + seg.closeTag;
462
+ }
463
+ if (seg.status === "inserted") {
464
+ return accepted ? seg.openTag + seg.afterHtml + seg.closeTag : "";
465
+ }
466
+ return accepted ? "" : seg.openTag + seg.beforeHtml + seg.closeTag;
467
+ };
468
+ const parts = [];
469
+ for (const seg of segments) {
470
+ if (seg.kind === "list") {
471
+ const childParts = seg.children.map(renderAtomic).filter((p) => p);
472
+ if (childParts.length > 0) {
473
+ parts.push(`${seg.openTag}${childParts.join("")}${seg.closeTag}`);
474
+ }
475
+ } else {
476
+ const out = renderAtomic(seg);
477
+ if (out) parts.push(out);
478
+ }
479
+ }
480
+ return parts.join("");
481
+ }
482
+ export function flattenSegments(segments) {
483
+ const out = [];
484
+ for (const seg of segments) {
485
+ if (seg.kind === "list") {
486
+ out.push(...seg.children);
487
+ } else {
488
+ out.push(seg);
489
+ }
490
+ }
491
+ return out;
492
+ }
@@ -0,0 +1,11 @@
1
+ import type { InjectionKey, Ref } from 'vue';
2
+ /**
3
+ * A ref to the nearest popup host element (provided by `<PopupHost>`).
4
+ *
5
+ * Descendants that render floating menus (e.g. the chart editor's color
6
+ * picker) inject this and teleport their content into the host. This lets
7
+ * the menu escape any `overflow: hidden`/`overflow: auto` clipping ancestor
8
+ * while staying inside the popup host's stacking context. When no host is
9
+ * provided, descendants fall back to inline rendering.
10
+ */
11
+ export declare const INJECT_POPUP_HOST: InjectionKey<Ref<HTMLElement | null>>;
@@ -0,0 +1 @@
1
+ export const INJECT_POPUP_HOST = Symbol("blokkli_popup_host");
@@ -34,8 +34,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
34
34
  width: number;
35
35
  height: number;
36
36
  };
37
- minHeight: number;
38
37
  minWidth: number;
38
+ minHeight: number;
39
39
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
41
  declare const _default: typeof __VLS_export;
@@ -34,8 +34,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
34
34
  width: number;
35
35
  height: number;
36
36
  };
37
- minHeight: number;
38
37
  minWidth: number;
38
+ minHeight: number;
39
39
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
41
  declare const _default: typeof __VLS_export;
@@ -200,9 +200,9 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
200
200
  width: number;
201
201
  height: number;
202
202
  };
203
- minHeight: number;
204
- minWidth: number;
205
203
  tourText: string;
204
+ minWidth: number;
205
+ minHeight: number;
206
206
  region: SidebarRegion;
207
207
  tooltipTitle: string;
208
208
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
@@ -200,9 +200,9 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
200
200
  width: number;
201
201
  height: number;
202
202
  };
203
- minHeight: number;
204
- minWidth: number;
205
203
  tourText: string;
204
+ minWidth: number;
205
+ minHeight: number;
206
206
  region: SidebarRegion;
207
207
  tooltipTitle: string;
208
208
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
@@ -1,12 +1,15 @@
1
1
  import { ref } from "#imports";
2
2
  import { AnalyzerContext } from "../features/analyze/analyzers/helpers/Context.js";
3
3
  import { normalizeToArray } from "../features/analyze/analyzers/helpers/normalizeArray.js";
4
+ import { validationsAnalyzer } from "../features/analyze/analyzers/defaults/validations.js";
5
+ const DEFAULT_ANALYZERS = [validationsAnalyzer];
4
6
  export default function analyzeProvider(adapters, state, ui, context, $t, readability) {
5
7
  const analyzers = ref([]);
6
8
  const isInitialized = ref(false);
7
9
  let initPromise = null;
8
10
  async function doInit() {
9
11
  const fetched = await adapters.getAggregated("getAnalyzers");
12
+ const all = [...DEFAULT_ANALYZERS, ...fetched];
10
13
  const initCtx = new AnalyzerContext(
11
14
  context.value.language,
12
15
  ui.interfaceLanguage.value,
@@ -17,13 +20,13 @@ export default function analyzeProvider(adapters, state, ui, context, $t, readab
17
20
  readability
18
21
  );
19
22
  await Promise.all(
20
- fetched.map(async (analyzer) => {
23
+ all.map(async (analyzer) => {
21
24
  if (analyzer.init) {
22
25
  await analyzer.init(initCtx);
23
26
  }
24
27
  })
25
28
  );
26
- analyzers.value = fetched;
29
+ analyzers.value = all;
27
30
  isInitialized.value = true;
28
31
  }
29
32
  function ensureInitialized() {
@@ -1,8 +1,10 @@
1
1
  import { type ComputedRef } from '#imports';
2
- import type { ColorOption } from '../types/config.js';
2
+ import type { ColorOption } from '#blokkli/types/colors';
3
3
  export type ConfigProvider = {
4
4
  colorOptions: ComputedRef<ColorOption[]>;
5
5
  getColorOption: (id: string) => ColorOption | undefined;
6
6
  getColorHex: (id: string) => string;
7
+ isColorEnabled: (id: string) => boolean;
8
+ canonicalColorId: (option: ColorOption) => string;
7
9
  };
8
10
  export default function (): ConfigProvider;
@@ -1,18 +1,49 @@
1
- import { computed } from "#imports";
1
+ import { computed, useAppConfig } from "#imports";
2
2
  import { colorOptions as moduleColorOptions } from "#blokkli-build/editor-config";
3
+ import {
4
+ canonicalColorId,
5
+ findColorOption,
6
+ isColorIdValid
7
+ } from "#blokkli/helpers/colors";
8
+ import { useBlokkliRuntimeConfig } from "../../composables/useBlokkliRuntimeConfig.js";
3
9
  export default function() {
4
- const colorOptions = computed(
5
- () => Object.entries(moduleColorOptions).map(([id, entry]) => ({
6
- id,
7
- hex: entry.hex,
8
- label: entry.label
9
- }))
10
- );
11
- function getColorOption(id) {
12
- return colorOptions.value.find((c) => c.id === id);
13
- }
14
- function getColorHex(id) {
15
- return getColorOption(id)?.hex || "#888888";
16
- }
17
- return { colorOptions, getColorOption, getColorHex };
10
+ const appConfig = useAppConfig();
11
+ const { resolveColorHex } = useBlokkliRuntimeConfig();
12
+ const colorOptions = computed(() => {
13
+ const overrides = appConfig.blokkli?.colorOptions ?? {};
14
+ const result = [];
15
+ for (const [id, entry] of Object.entries(moduleColorOptions)) {
16
+ if (overrides[id] === null) continue;
17
+ if ("shades" in entry) {
18
+ const shades = [];
19
+ for (const [shadeId, declaredHex] of Object.entries(entry.shades)) {
20
+ const value = overrides[`${id}.${shadeId}`];
21
+ if (value === null) continue;
22
+ shades.push({
23
+ id: shadeId,
24
+ hex: typeof value === "string" ? value : declaredHex,
25
+ isMain: shadeId === entry.mainShade
26
+ });
27
+ }
28
+ if (shades.length === 0) continue;
29
+ const main = shades.find((s) => s.isMain) ?? shades[0];
30
+ result.push({ id, hex: main.hex, label: entry.label, shades });
31
+ } else {
32
+ const value = overrides[id];
33
+ result.push({
34
+ id,
35
+ hex: typeof value === "string" ? value : entry.hex,
36
+ label: entry.label
37
+ });
38
+ }
39
+ }
40
+ return result;
41
+ });
42
+ return {
43
+ colorOptions,
44
+ getColorOption: (id) => findColorOption(id, colorOptions.value),
45
+ getColorHex: resolveColorHex,
46
+ isColorEnabled: (id) => isColorIdValid(id, colorOptions.value),
47
+ canonicalColorId
48
+ };
18
49
  }
@@ -95,6 +95,19 @@ export type StateProvider = {
95
95
  * @returns True if successful, false if failed
96
96
  */
97
97
  mutateWithLoadingState: MutateWithLoadingStateFunction;
98
+ /**
99
+ * Apply the raw state returned by a mutation as the current editor state.
100
+ *
101
+ * Use this when a caller has invoked an adapter mutation directly (bypassing
102
+ * {@link mutateWithLoadingState}) and wants to push the response's `state`
103
+ * payload into the editor without an extra round-trip. The caller is
104
+ * responsible for only invoking this when the response belongs to the
105
+ * current edit state.
106
+ *
107
+ * @param rawState - The `state` field from a {@link MutationResponseLike}
108
+ * response, or `null`/`undefined` to no-op.
109
+ */
110
+ applyMutationState: (rawState: unknown) => void;
98
111
  /**
99
112
  * Current edit mode.
100
113
  *
@@ -370,6 +370,12 @@ export default async function(eventBus, adapter, context, $t, providerKey, permi
370
370
  function setOverrideState(state) {
371
371
  setContext(state, true);
372
372
  }
373
+ function applyMutationState(rawState) {
374
+ if (!rawState) {
375
+ return;
376
+ }
377
+ setContext(adapter.mapState(rawState));
378
+ }
373
379
  function clearOverrideState() {
374
380
  if (!_mappedState) {
375
381
  throw new Error("Missing previous state.");
@@ -391,6 +397,7 @@ export default async function(eventBus, adapter, context, $t, providerKey, permi
391
397
  violations,
392
398
  currentMutationIndex,
393
399
  mutateWithLoadingState,
400
+ applyMutationState,
394
401
  editMode,
395
402
  canEdit,
396
403
  isLoading: readonly(isLoading),
@@ -291,6 +291,9 @@
291
291
  "analyzerReadabiliyOkTitle": "Text könnte einfacher sein",
292
292
  "analyzerReadabiliyShorterSentences": "Verwenden Sie kürzere Sätze und einfachere Formulierungen.",
293
293
  "analyzerReadabiliyTitle": "Probleme bei Lesbarkeit",
294
+ "analyzerValidationsDescription": "Validierungsfehler für den aktuellen Stand der Seite.",
295
+ "analyzerValidationsLabel": "Validierungen",
296
+ "analyzerValidationsPassDescription": "Keine Validierungsfehler für den aktuellen Stand der Seite.",
294
297
  "arrowLeft": "Pfeil links",
295
298
  "arrowRight": "Pfeil rechts",
296
299
  "artboard": "Vorschau",
@@ -534,6 +537,7 @@
534
537
  "deleteConversation": "Konversation löschen",
535
538
  "deleteError": "Das Element konnte nicht entfernt werden.",
536
539
  "deleteNoPermission": "Löschen aufgrund fehlender Berechtigung nicht möglich.",
540
+ "deleted": "gelöscht",
537
541
  "description": "Beschreibung",
538
542
  "diffBefore": "Vorher",
539
543
  "diffModeInline": "Hervorgehoben",
@@ -672,6 +676,7 @@
672
676
  "libraryPlaceDialogTitle": "Block aus Bibliothek hinzufügen",
673
677
  "link": "Link",
674
678
  "loading": "Wird geladen",
679
+ "mainColor": "Hauptfarbe",
675
680
  "manageSchedule": "Planung verwalten",
676
681
  "manageScheduling": "Planung verwalten",
677
682
  "maskTourText": "Wechseln Sie zwischen Anzeigen und Verbergen nicht bearbeitbarer Teile der Seite.",
@@ -764,6 +769,8 @@
764
769
  "publishSuccess": "Änderungen erfolgreich publiziert.",
765
770
  "publishSuccessfullyPublished": "Erfolgreich veröffentlicht",
766
771
  "publishSuccessfullySaved": "Erfolgreich gespeichert",
772
+ "publishValidationErrorsDescription": "Beheben Sie die folgenden Probleme und publizieren Sie erneut.",
773
+ "publishValidationErrorsTitle": "Publikation nicht möglich: Validierungsfehler",
767
774
  "publishWillBePublished": "Wird publiziert",
768
775
  "publishWillBeScheduled": "Wird geplant",
769
776
  "qrCode": "QR-Code",
@@ -929,10 +936,6 @@
929
936
  "translationsTranslateNoTexts": "Keine übersetzbaren Texte auf dieser Seite gefunden.",
930
937
  "undo": "Rückgängig",
931
938
  "unexpectedMutationError": "Ein unerwarteter Fehler ist aufgetreten",
932
- "userDeleted": "[gelöscht]",
933
- "validationsNoneFound": "Keine Validierungsfehler gefunden.",
934
- "validationsToolbarLabel": "Validierungen",
935
- "validationsTourText": "Zeigt Validierungsfehler für Inhalte auf der aktuellen Seite an.",
936
939
  "video": "Video",
937
940
  "viewBannerReviewText": "Sie können Kommentare ansehen und hinzufügen, aber keine Inhalte bearbeiten.",
938
941
  "viewBannerReviewTitle": "Sie befinden sich im Review-Modus.",
@@ -94,6 +94,7 @@
94
94
  "commentsMarkAsUnresolved": "Marquer comme non résolu",
95
95
  "commentsShowResolved": "Afficher les résolus",
96
96
  "deleteError": "L'élément n'a pas pu être supprimé.",
97
+ "deleted": "supprimé",
97
98
  "duplicate": "Dupliquer",
98
99
  "duplicateError": "Les éléments n'ont pas pu être dupliqués.",
99
100
  "edit": "Modifier",
@@ -163,8 +164,5 @@
163
164
  "settingsDialogTitle": "Paramètres",
164
165
  "settingsMenuDescription": "Paramètres personnels pour l’éditeur",
165
166
  "structureToolbarLabel": "Structure",
166
- "translationsBatchTranslateMenuDescription": "Traduire tous les éléments",
167
- "userDeleted": "[supprimé]",
168
- "validationsNoneFound": "Aucune erreur de validation trouvée.",
169
- "validationsToolbarLabel": "Validations"
167
+ "translationsBatchTranslateMenuDescription": "Traduire tous les éléments"
170
168
  }
@@ -291,6 +291,9 @@
291
291
  "analyzerReadabiliyOkTitle": "Teggscht chönnt eifacher si",
292
292
  "analyzerReadabiliyShorterSentences": "Bruch chürzeri Sätz und eifacheri Formulierige.",
293
293
  "analyzerReadabiliyTitle": "Läsbarkeitsproblem im Teggscht",
294
+ "analyzerValidationsDescription": "Validierungsfehler für den aktuellen Stand der Seite.",
295
+ "analyzerValidationsLabel": "Validierungen",
296
+ "analyzerValidationsPassDescription": "Keine Validierungsfehler für den aktuellen Stand der Seite.",
294
297
  "arrowLeft": "Pfeil links",
295
298
  "arrowRight": "Pfeil rechts",
296
299
  "artboard": "Zeichenflächi",
@@ -534,6 +537,7 @@
534
537
  "deleteConversation": "Konversation löschen",
535
538
  "deleteError": "Dr Block het nid chönne glöscht wärde.",
536
539
  "deleteNoPermission": "Lösche ufgrund fählender Berächtigung nid möglich.",
540
+ "deleted": "glöscht",
537
541
  "description": "Beschreibung",
538
542
  "diffBefore": "Vorhär",
539
543
  "diffModeInline": "Inline",
@@ -672,6 +676,7 @@
672
676
  "libraryPlaceDialogTitle": "Block us dr Bibliothek drzuefüege",
673
677
  "link": "Link",
674
678
  "loading": "Wird geladen",
679
+ "mainColor": "Hauptfarbe",
675
680
  "manageSchedule": "Planung verwalte...",
676
681
  "manageScheduling": "Planung verwalten",
677
682
  "maskTourText": "Wächsle zwüsche Aazeige oder Verstecke vo nid-bearbeitbare Teile vo dr Sitte.",
@@ -764,6 +769,8 @@
764
769
  "publishSuccess": "Änderige erfolgriich publiziert.",
765
770
  "publishSuccessfullyPublished": "Erfolgriich veröffentlicht",
766
771
  "publishSuccessfullySaved": "Erfolgriich gspeicheret",
772
+ "publishValidationErrorsDescription": "Beheben Sie die folgenden Probleme und publizieren Sie erneut.",
773
+ "publishValidationErrorsTitle": "Publikation nicht möglich: Validierungsfehler",
767
774
  "publishWillBePublished": "Würd publiziert",
768
775
  "publishWillBeScheduled": "Würd plant",
769
776
  "qrCode": "QR-Code",
@@ -929,10 +936,6 @@
929
936
  "translationsTranslateNoTexts": "Keine übersetzbaren Texte auf dieser Seite gefunden.",
930
937
  "undo": "Rückgängig",
931
938
  "unexpectedMutationError": "Es isch e unerwartete Fähler ufträte.",
932
- "userDeleted": "[glöscht]",
933
- "validationsNoneFound": "Keini Validierigsfähler gfunde.",
934
- "validationsToolbarLabel": "Validierige",
935
- "validationsTourText": "Lueg Validierigsfähler für Inhalt oder Struktur uf dr aktuälle Sitte aa.",
936
939
  "video": "Video",
937
940
  "viewBannerReviewText": "Du chasch Kommentär aaluege und drzuefüege, aber kän Inhalt bearbeite.",
938
941
  "viewBannerReviewTitle": "Du bisch im Review-Modus.",