@blokkli/editor 2.0.0-alpha.39 → 2.0.0-alpha.40

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 (160) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +15 -105
  3. package/dist/modules/agent/index.d.mts +1 -0
  4. package/dist/modules/agent/index.mjs +81 -8
  5. package/dist/modules/agent/runtime/app/composables/agentProvider.d.ts +8 -3
  6. package/dist/modules/agent/runtime/app/composables/agentProvider.js +108 -13
  7. package/dist/modules/agent/runtime/app/features/agent/Panel/DebugGallery/index.vue +7 -6
  8. package/dist/modules/agent/runtime/app/features/agent/Panel/index.d.vue.ts +2 -2
  9. package/dist/modules/agent/runtime/app/features/agent/Panel/index.vue +12 -10
  10. package/dist/modules/agent/runtime/app/features/agent/Panel/index.vue.d.ts +2 -2
  11. package/dist/modules/agent/runtime/app/features/agent/index.vue +29 -5
  12. package/dist/modules/agent/runtime/app/helpers/pageStructure.js +7 -17
  13. package/dist/modules/agent/runtime/app/prompts/fixReadability.d.ts +2 -0
  14. package/dist/modules/agent/runtime/app/prompts/fixReadability.js +49 -0
  15. package/dist/modules/agent/runtime/app/tools/add_paragraphs/index.js +1 -0
  16. package/dist/modules/agent/runtime/app/tools/check_readability/index.js +24 -15
  17. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.d.vue.ts +46 -0
  18. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue +739 -0
  19. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue.d.ts +46 -0
  20. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Details/index.d.vue.ts +6 -0
  21. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Details/index.vue +38 -0
  22. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Details/index.vue.d.ts +6 -0
  23. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/index.d.ts +38 -0
  24. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/index.js +115 -0
  25. package/dist/modules/agent/runtime/app/tools/delete_paragraphs/index.js +1 -0
  26. package/dist/modules/agent/runtime/app/tools/get_all_page_content/index.js +3 -5
  27. package/dist/modules/agent/runtime/app/tools/get_bundle_info/index.js +1 -0
  28. package/dist/modules/agent/runtime/app/tools/get_child_paragraphs/index.js +1 -0
  29. package/dist/modules/agent/runtime/app/tools/get_content_fields/index.js +4 -7
  30. package/dist/modules/agent/runtime/app/tools/get_page_structure/index.js +1 -0
  31. package/dist/modules/agent/runtime/app/tools/get_paragraph_context/index.js +10 -17
  32. package/dist/modules/agent/runtime/app/tools/get_paragraph_options/index.js +1 -0
  33. package/dist/modules/agent/runtime/app/tools/get_paragraphs_in_viewport/index.js +1 -0
  34. package/dist/modules/agent/runtime/app/tools/get_readability_issues/index.js +17 -70
  35. package/dist/modules/agent/runtime/app/tools/get_selected_paragraphs/index.js +1 -0
  36. package/dist/modules/agent/runtime/app/tools/helpers.d.ts +38 -10
  37. package/dist/modules/agent/runtime/app/tools/helpers.js +90 -27
  38. package/dist/modules/agent/runtime/app/tools/move_paragraphs/index.js +1 -0
  39. package/dist/modules/agent/runtime/app/tools/rearrange_paragraphs/index.js +1 -0
  40. package/dist/modules/agent/runtime/app/tools/schemas.d.ts +1 -1
  41. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.vue +66 -84
  42. package/dist/modules/agent/runtime/app/tools/update_text_fields/index.d.ts +8 -1
  43. package/dist/modules/agent/runtime/app/tools/update_text_fields/index.js +21 -14
  44. package/dist/modules/agent/runtime/app/types/index.d.ts +41 -0
  45. package/dist/modules/agent/runtime/server/Session.d.ts +8 -1
  46. package/dist/modules/agent/runtime/server/Session.js +154 -6
  47. package/dist/modules/agent/runtime/server/agent.js +5 -1
  48. package/dist/modules/agent/runtime/server/default-skills/fixReadability.js +31 -51
  49. package/dist/modules/agent/runtime/server/default-skills/rewriteAndTranslate.js +45 -4
  50. package/dist/modules/agent/runtime/server/providers/anthropic.js +2 -1
  51. package/dist/modules/agent/runtime/server/providers/openai.js +2 -1
  52. package/dist/modules/agent/runtime/server/providers/types.d.ts +2 -0
  53. package/dist/modules/agent/runtime/server/route.d.ts +2 -0
  54. package/dist/modules/agent/runtime/server/route.js +40 -0
  55. package/dist/modules/agent/runtime/server/routing.d.ts +18 -0
  56. package/dist/modules/agent/runtime/server/routing.js +111 -0
  57. package/dist/modules/agent/runtime/server/skills/types.d.ts +10 -0
  58. package/dist/modules/agent/runtime/server/stream.d.ts +2 -0
  59. package/dist/modules/agent/runtime/server/stream.js +190 -0
  60. package/dist/modules/agent/runtime/server/streamParser.d.ts +85 -0
  61. package/dist/modules/agent/runtime/server/streamParser.js +227 -0
  62. package/dist/modules/agent/runtime/server/templates/defineStreamTemplate.d.ts +21 -0
  63. package/dist/modules/agent/runtime/server/templates/defineStreamTemplate.js +3 -0
  64. package/dist/modules/agent/runtime/server/templates/definitions/fixReadability.d.ts +26 -0
  65. package/dist/modules/agent/runtime/server/templates/definitions/fixReadability.js +84 -0
  66. package/dist/modules/agent/runtime/server/templates/definitions/generateContent.d.ts +6 -0
  67. package/dist/modules/agent/runtime/server/templates/definitions/generateContent.js +29 -0
  68. package/dist/modules/agent/runtime/server/templates/definitions/rewrite.d.ts +5 -0
  69. package/dist/modules/agent/runtime/server/templates/definitions/rewrite.js +14 -0
  70. package/dist/modules/agent/runtime/server/templates/definitions/translate.d.ts +5 -0
  71. package/dist/modules/agent/runtime/server/templates/definitions/translate.js +23 -0
  72. package/dist/modules/agent/runtime/server/templates/index.d.ts +37 -0
  73. package/dist/modules/agent/runtime/server/templates/index.js +25 -0
  74. package/dist/modules/agent/runtime/server/templates/types.d.ts +17 -0
  75. package/dist/modules/agent/runtime/server/templates/types.js +0 -0
  76. package/dist/modules/agent/runtime/server/templates/utils.d.ts +5 -0
  77. package/dist/modules/agent/runtime/server/templates/utils.js +69 -0
  78. package/dist/modules/agent/runtime/shared/types.d.ts +18 -0
  79. package/dist/modules/agent/runtime/shared/types.js +16 -1
  80. package/dist/runtime/components/BlokkliItem.d.vue.ts +1 -1
  81. package/dist/runtime/components/BlokkliItem.vue +34 -3
  82. package/dist/runtime/components/BlokkliItem.vue.d.ts +1 -1
  83. package/dist/runtime/editor/components/Actions/index.vue +2 -2
  84. package/dist/runtime/editor/components/AnimationCanvas/index.vue +23 -2
  85. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.d.vue.ts +19 -0
  86. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue +106 -0
  87. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue.d.ts +19 -0
  88. package/dist/runtime/editor/components/DiffApproval/Highlight/index.d.vue.ts +22 -0
  89. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue +50 -0
  90. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue.d.ts +22 -0
  91. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.d.vue.ts +24 -0
  92. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue +113 -0
  93. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue.d.ts +24 -0
  94. package/dist/runtime/editor/components/DiffApproval/index.d.vue.ts +19 -0
  95. package/dist/runtime/editor/components/DiffApproval/index.vue +158 -0
  96. package/dist/runtime/editor/components/DiffApproval/index.vue.d.ts +19 -0
  97. package/dist/runtime/editor/components/DiffApproval/types.d.ts +7 -0
  98. package/dist/runtime/editor/components/DiffApproval/types.js +0 -0
  99. package/dist/runtime/editor/components/DiffViewer/DiffValue.vue +2 -11
  100. package/dist/runtime/editor/components/DraggableList.vue +1 -1
  101. package/dist/runtime/editor/components/EditProvider.vue +18 -1
  102. package/dist/runtime/editor/components/ShortcutIndicator/index.d.vue.ts +2 -2
  103. package/dist/runtime/editor/components/ShortcutIndicator/index.vue +27 -33
  104. package/dist/runtime/editor/components/ShortcutIndicator/index.vue.d.ts +2 -2
  105. package/dist/runtime/editor/components/index.d.ts +2 -1
  106. package/dist/runtime/editor/components/index.js +2 -0
  107. package/dist/runtime/editor/composables/useEditableFieldOverride.d.ts +2 -0
  108. package/dist/runtime/editor/composables/useEditableFieldOverride.js +21 -15
  109. package/dist/runtime/editor/css/output.css +1 -1
  110. package/dist/runtime/editor/events/index.d.ts +3 -0
  111. package/dist/runtime/editor/features/add-list/index.vue +1 -0
  112. package/dist/runtime/editor/features/analyze/Main.vue +1 -1
  113. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.d.ts +7 -2
  114. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.js +6 -1
  115. package/dist/runtime/editor/features/analyze/analyzers/readability.js +52 -222
  116. package/dist/runtime/editor/features/analyze/analyzers/types.d.ts +2 -2
  117. package/dist/runtime/editor/features/analyze/readability/adapterTypes.d.ts +9 -0
  118. package/dist/runtime/editor/features/analyze/readability/adapterTypes.js +0 -0
  119. package/dist/runtime/editor/features/analyze/readability/builtinAnalyzer.d.ts +6 -0
  120. package/dist/runtime/editor/features/analyze/readability/builtinAnalyzer.js +200 -0
  121. package/dist/runtime/editor/features/analyze/readability/chunkHtml.d.ts +15 -0
  122. package/dist/runtime/editor/features/analyze/readability/chunkHtml.js +97 -0
  123. package/dist/runtime/editor/features/analyze/readability/types.d.ts +72 -0
  124. package/dist/runtime/editor/features/analyze/readability/types.js +0 -0
  125. package/dist/runtime/editor/features/artboard/Renderer.vue +9 -5
  126. package/dist/runtime/editor/features/breadcrumbs/index.vue +1 -1
  127. package/dist/runtime/editor/features/dragging-overlay/index.vue +1 -1
  128. package/dist/runtime/editor/features/editable-field/index.vue +1 -1
  129. package/dist/runtime/editor/features/hover/index.vue +1 -1
  130. package/dist/runtime/editor/features/options/index.vue +1 -1
  131. package/dist/runtime/editor/features/selection/AddButtons/Renderer/index.vue +1 -1
  132. package/dist/runtime/editor/features/selection/index.vue +2 -2
  133. package/dist/runtime/editor/features/structure/List/Item/index.vue +1 -3
  134. package/dist/runtime/editor/helpers/diff/index.d.ts +11 -0
  135. package/dist/runtime/editor/helpers/diff/index.js +227 -0
  136. package/dist/runtime/editor/plugins/Sidebar/index.vue +1 -1
  137. package/dist/runtime/editor/providers/analyze.d.ts +2 -1
  138. package/dist/runtime/editor/providers/analyze.js +6 -3
  139. package/dist/runtime/editor/providers/directive.js +1 -0
  140. package/dist/runtime/editor/providers/fieldValue.d.ts +54 -0
  141. package/dist/runtime/editor/providers/fieldValue.js +126 -0
  142. package/dist/runtime/editor/providers/fieldValueAdapterTypes.d.ts +13 -0
  143. package/dist/runtime/editor/providers/fieldValueAdapterTypes.js +0 -0
  144. package/dist/runtime/editor/providers/readability.d.ts +23 -0
  145. package/dist/runtime/editor/providers/readability.js +130 -0
  146. package/dist/runtime/editor/providers/state.d.ts +16 -0
  147. package/dist/runtime/editor/providers/state.js +19 -1
  148. package/dist/runtime/editor/providers/ui.d.ts +7 -0
  149. package/dist/runtime/editor/providers/ui.js +7 -1
  150. package/dist/runtime/editor/translations/de.json +80 -8
  151. package/dist/runtime/editor/translations/fr.json +80 -8
  152. package/dist/runtime/editor/translations/gsw_CH.json +80 -8
  153. package/dist/runtime/editor/translations/it.json +80 -8
  154. package/dist/runtime/editor/types/app.d.ts +4 -0
  155. package/dist/shared/editor.9vf8ZnOp.mjs +288 -0
  156. package/package.json +2 -2
  157. package/dist/modules/agent/runtime/app/tools/update_text_fields/Item.d.vue.ts +0 -22
  158. package/dist/modules/agent/runtime/app/tools/update_text_fields/Item.vue +0 -95
  159. package/dist/modules/agent/runtime/app/tools/update_text_fields/Item.vue.d.ts +0 -22
  160. package/dist/shared/editor.BFIzNSQM.mjs +0 -146
@@ -0,0 +1,97 @@
1
+ const BLOCK_ELEMENTS = /* @__PURE__ */ new Set([
2
+ "ADDRESS",
3
+ "ARTICLE",
4
+ "ASIDE",
5
+ "BLOCKQUOTE",
6
+ "DD",
7
+ "DIV",
8
+ "DL",
9
+ "DT",
10
+ "FIELDSET",
11
+ "FIGCAPTION",
12
+ "FIGURE",
13
+ "FOOTER",
14
+ "FORM",
15
+ "H1",
16
+ "H2",
17
+ "H3",
18
+ "H4",
19
+ "H5",
20
+ "H6",
21
+ "HEADER",
22
+ "HR",
23
+ "LI",
24
+ "MAIN",
25
+ "NAV",
26
+ "OL",
27
+ "P",
28
+ "PRE",
29
+ "SECTION",
30
+ "TABLE",
31
+ "TD",
32
+ "TH",
33
+ "UL",
34
+ "DETAILS",
35
+ "SUMMARY",
36
+ "DIALOG"
37
+ ]);
38
+ function isBlockElement(el) {
39
+ return BLOCK_ELEMENTS.has(el.tagName);
40
+ }
41
+ function containsBlockElements(el) {
42
+ for (const child of el.children) {
43
+ if (isBlockElement(child)) {
44
+ return true;
45
+ }
46
+ if (containsBlockElements(child)) {
47
+ return true;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+ function traverse(el, results) {
53
+ if (el.tagName === "SCRIPT" || el.tagName === "STYLE") {
54
+ return;
55
+ }
56
+ if (isBlockElement(el)) {
57
+ if (!containsBlockElements(el)) {
58
+ const text = (el.textContent || "").trim();
59
+ if (text) {
60
+ results.push({
61
+ text,
62
+ html: el.innerHTML
63
+ });
64
+ }
65
+ } else {
66
+ for (const child of el.children) {
67
+ traverse(child, results);
68
+ }
69
+ }
70
+ } else {
71
+ for (const child of el.children) {
72
+ traverse(child, results);
73
+ }
74
+ }
75
+ }
76
+ export function chunkHtml(value, fieldType) {
77
+ const trimmed = value.trim();
78
+ if (!trimmed) {
79
+ return [];
80
+ }
81
+ if (fieldType === "plain") {
82
+ return [{ text: trimmed }];
83
+ }
84
+ const parser = new DOMParser();
85
+ const doc = parser.parseFromString(trimmed, "text/html");
86
+ const results = [];
87
+ for (const child of doc.body.children) {
88
+ traverse(child, results);
89
+ }
90
+ if (results.length === 0) {
91
+ const text = (doc.body.textContent || "").trim();
92
+ if (text) {
93
+ results.push({ text, html: doc.body.innerHTML });
94
+ }
95
+ }
96
+ return results;
97
+ }
@@ -0,0 +1,72 @@
1
+ import type { AnalyzeImpact } from '../analyzers/types.js';
2
+ /**
3
+ * Classification band for a chunk of text.
4
+ */
5
+ export type ReadabilityBand = 'easy' | 'ok' | 'hard';
6
+ /**
7
+ * Result for a single chunk of text within a field.
8
+ */
9
+ export type ReadabilityChunkResult = {
10
+ text: string;
11
+ html?: string;
12
+ score: number;
13
+ band: ReadabilityBand;
14
+ impact: AnalyzeImpact;
15
+ description?: string;
16
+ };
17
+ /**
18
+ * Result for a single field, containing all its scored chunks.
19
+ */
20
+ export type ReadabilityFieldResult = {
21
+ rawValue: string;
22
+ fieldType: 'plain' | 'markup';
23
+ chunks: ReadabilityChunkResult[];
24
+ };
25
+ /**
26
+ * Full analysis result keyed by "{uuid}/{fieldName}".
27
+ */
28
+ export type ReadabilityAnalysisResult = Record<string, ReadabilityFieldResult>;
29
+ /**
30
+ * Interface for a readability analyzer.
31
+ *
32
+ * One analyzer is active at a time. The built-in default uses
33
+ * LIX/CLI/ARI/Gulpease. Adapters can override with a custom implementation
34
+ * (e.g. CEFR via API).
35
+ */
36
+ export type ReadabilityAnalyzer = {
37
+ id: string;
38
+ label?: string | ((langcode: string) => string);
39
+ description?: string;
40
+ supportedLanguages?: string[];
41
+ minWordsForConfidence?: number;
42
+ /**
43
+ * Short label for the primary score metric (e.g. "LIX", "Gulpease", "CEFR").
44
+ * Used by the UI to display scores like "LIX: 45".
45
+ */
46
+ scoreLabel: string;
47
+ /**
48
+ * Optional initialization (e.g. lazy-load a library).
49
+ */
50
+ init?(langcode: string): void | Promise<void>;
51
+ /**
52
+ * Score an array of plain-text strings.
53
+ * Returns one primary score per input text (null if not scorable).
54
+ */
55
+ analyze(texts: string[], langcode: string): Promise<(number | null)[]>;
56
+ /**
57
+ * Classify a score into a readability band.
58
+ */
59
+ classifyBand(score: number, langcode: string): ReadabilityBand;
60
+ /**
61
+ * Determine the impact severity for a score.
62
+ */
63
+ impactForScore(score: number): AnalyzeImpact;
64
+ /**
65
+ * Return context text for the agent prompt (e.g. LIX reference table).
66
+ */
67
+ getAgentContext(): string;
68
+ /**
69
+ * Optional display formatting for a score value.
70
+ */
71
+ formatScore?(value: number): string;
72
+ };
@@ -272,11 +272,11 @@ onBlokkliEvent("keyPressed", (e) => {
272
272
  e.originalEvent.preventDefault();
273
273
  artboard.scrollPageDown();
274
274
  animation.requestDraw();
275
- } else if (e.code === "ArrowUp") {
275
+ } else if (e.code === "ArrowUp" && !ui.isApproving.value) {
276
276
  e.originalEvent.preventDefault();
277
277
  artboard.scrollUp();
278
278
  animation.requestDraw();
279
- } else if (e.code === "ArrowDown") {
279
+ } else if (e.code === "ArrowDown" && !ui.isApproving.value) {
280
280
  e.originalEvent.preventDefault();
281
281
  artboard.scrollDown();
282
282
  animation.requestDraw();
@@ -303,7 +303,8 @@ onBlokkliEvent("scrollIntoView", (e) => {
303
303
  artboard.scrollIntoView(rect, {
304
304
  scale: "none",
305
305
  axis: "y",
306
- behavior: e.immediate ? "instant" : "auto"
306
+ behavior: e.immediate ? "instant" : "auto",
307
+ area: "blocking"
307
308
  });
308
309
  } else {
309
310
  if (artboardElement.contains(e.element)) {
@@ -313,8 +314,11 @@ onBlokkliEvent("scrollIntoView", (e) => {
313
314
  }
314
315
  artboard.scrollElementIntoView(e.element, {
315
316
  scale: "none",
316
- axis: "y",
317
- behavior: e.immediate ? "instant" : "auto"
317
+ axis: "both",
318
+ behavior: e.immediate ? "instant" : "auto",
319
+ area: "blocking",
320
+ block: "auto",
321
+ inline: "auto"
318
322
  });
319
323
  } else {
320
324
  artboard.scrollToTop({
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Teleport v-if="ui.mainLayoutElement.value" :to="ui.mainLayoutElement.value">
3
- <div class="bk bk-breadcrumbs">
3
+ <div v-show="!ui.isApproving.value" class="bk bk-breadcrumbs">
4
4
  <ul>
5
5
  <li>
6
6
  <button
@@ -242,7 +242,7 @@ onBlokkliEvent("state:reloaded", async function() {
242
242
  if (!editableFieldElement) {
243
243
  return;
244
244
  }
245
- eventBus.emit("editable:focus", {
245
+ eventBus.emit("editable:open", {
246
246
  fieldName: editableField,
247
247
  uuid: newUuid
248
248
  });
@@ -97,7 +97,7 @@ const buildEditable = (fieldName, uuid) => {
97
97
  config
98
98
  };
99
99
  };
100
- onBlokkliEvent("editable:focus", (e) => {
100
+ onBlokkliEvent("editable:open", (e) => {
101
101
  if (!state.canEdit.value) {
102
102
  return;
103
103
  }
@@ -17,7 +17,7 @@ defineBlokkliFeature({
17
17
  const { selection, ui, dom, animation, $t } = useBlokkli();
18
18
  const isLocked = ref(false);
19
19
  const isVisible = computed(
20
- () => !isLocked.value && dom.isReady.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value && !selection.isDragging.value && !ui.hasTransformOverlayOpen.value && !ui.hasDialogOpen.value && !ui.hasNestedEditorOpen.value && !ui.isAnimating.value
20
+ () => !isLocked.value && dom.isReady.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value && !selection.isDragging.value && !ui.hasTransformOverlayOpen.value && !ui.hasDialogOpen.value && !ui.hasNestedEditorOpen.value && !ui.isAnimating.value && !ui.isApproving.value
21
21
  );
22
22
  </script>
23
23
 
@@ -2,7 +2,7 @@
2
2
  <Teleport to="#bk-blokkli-item-actions-controls">
3
3
  <OptionsForm
4
4
  v-if="
5
- definition && !selection.isDragging.value && !ui.isAnimating.value && uuids
5
+ definition && !selection.isDragging.value && !ui.isAnimating.value && !ui.isTransforming.value && uuids
6
6
  "
7
7
  :key="key + state.refreshKey.value + ui.isAnimating.value"
8
8
  :uuids
@@ -348,7 +348,7 @@ const { collector } = defineRenderer("add-buttons", {
348
348
  if (ui.openTooltip.value && ui.openTooltip.value !== "add-buttons") {
349
349
  return false;
350
350
  }
351
- if (ui.hasTransformOverlayOpen.value) {
351
+ if (ui.hasTransformOverlayOpen.value || ui.isApproving.value) {
352
352
  return false;
353
353
  }
354
354
  return true;
@@ -80,7 +80,7 @@ const stop = watch(
80
80
  }
81
81
  );
82
82
  const isVisible = computed(
83
- () => dom.isReady.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value && !selection.isDragging.value && !ui.isAnimating.value && hasSelectedOnce.value
83
+ () => dom.isReady.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value && !selection.isDragging.value && !ui.isAnimating.value && hasSelectedOnce.value && !ui.isApproving.value
84
84
  );
85
85
  const findMostVisibleBlock = () => {
86
86
  const viewport = ui.visibleViewportPadded.value;
@@ -240,7 +240,7 @@ onBlokkliEvent("keyPressed", (e) => {
240
240
  eventBus.emit("select:host:unselect");
241
241
  }
242
242
  } else if (e.code === "Tab") {
243
- if (tour.isTouring.value || ui.hasDialogOpen.value || ui.hasNestedEditorOpen.value) {
243
+ if (tour.isTouring.value || ui.hasDialogOpen.value || ui.hasNestedEditorOpen.value || ui.isApproving.value) {
244
244
  return;
245
245
  }
246
246
  e.originalEvent.preventDefault();
@@ -64,9 +64,7 @@ function onMouseUp(e) {
64
64
  } else {
65
65
  eventBus.emit("select", props.uuid);
66
66
  eventBus.emit("scrollIntoView", {
67
- uuid: props.uuid,
68
- immediate: true,
69
- center: true
67
+ uuid: props.uuid
70
68
  });
71
69
  }
72
70
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Compute an inline HTML diff between two strings.
3
+ *
4
+ * For content with block-level HTML elements (p, h1-h6, etc.), aligns blocks
5
+ * by tag name and diffs within each block individually, preventing diff markers
6
+ * from crossing element boundaries.
7
+ *
8
+ * For plain text or inline-only HTML, uses flat word-level diffing with
9
+ * post-processing to merge long runs of consecutive modifications.
10
+ */
11
+ export declare function computeDiff(before: string, after: string): string;
@@ -0,0 +1,227 @@
1
+ import diff from "html-diff-ts";
2
+ const PAIR_RE = /<del class="diffmod">((?:[^<]|<(?!\/del>))*?)<\/del><ins class="diffmod">((?:[^<]|<(?!\/ins>))*?)<\/ins>/g;
3
+ function tokenize(html) {
4
+ const tokens = [];
5
+ let lastIndex = 0;
6
+ PAIR_RE.lastIndex = 0;
7
+ let match = PAIR_RE.exec(html);
8
+ while (match) {
9
+ if (match.index > lastIndex) {
10
+ tokens.push({ type: "other", text: html.slice(lastIndex, match.index) });
11
+ }
12
+ tokens.push({
13
+ type: "pair",
14
+ del: match[1] || "",
15
+ ins: match[2] || "",
16
+ raw: match[0]
17
+ });
18
+ lastIndex = match.index + match[0].length;
19
+ match = PAIR_RE.exec(html);
20
+ }
21
+ if (lastIndex < html.length) {
22
+ tokens.push({ type: "other", text: html.slice(lastIndex) });
23
+ }
24
+ return tokens;
25
+ }
26
+ function isMergeableSeparator(text) {
27
+ if (/<[^>]+>/.test(text)) {
28
+ return false;
29
+ }
30
+ if (/^(?:\s|&nbsp;)*$/.test(text)) {
31
+ return true;
32
+ }
33
+ return text.length <= 20;
34
+ }
35
+ function cleanupDiffMods(html) {
36
+ const tokens = tokenize(html);
37
+ const runs = [];
38
+ let i = 0;
39
+ while (i < tokens.length) {
40
+ const token = tokens[i];
41
+ if (token?.type !== "pair") {
42
+ i++;
43
+ continue;
44
+ }
45
+ const run = { startIndex: i, endIndex: i + 1, pairCount: 1 };
46
+ let j = i + 1;
47
+ while (j < tokens.length) {
48
+ const next = tokens[j];
49
+ if (!next) break;
50
+ if (next.type === "pair") {
51
+ run.endIndex = j + 1;
52
+ run.pairCount++;
53
+ j++;
54
+ continue;
55
+ }
56
+ const afterNext = tokens[j + 1];
57
+ if (next.type === "other" && isMergeableSeparator(next.text) && afterNext?.type === "pair") {
58
+ run.endIndex = j + 2;
59
+ run.pairCount++;
60
+ j += 2;
61
+ continue;
62
+ }
63
+ break;
64
+ }
65
+ runs.push(run);
66
+ i = run.endIndex;
67
+ }
68
+ const mergeSet = /* @__PURE__ */ new Set();
69
+ const mergeRuns = runs.filter((r) => r.pairCount >= 3);
70
+ for (const run of mergeRuns) {
71
+ for (let k = run.startIndex; k < run.endIndex; k++) {
72
+ mergeSet.add(k);
73
+ }
74
+ }
75
+ if (mergeRuns.length === 0) {
76
+ return html;
77
+ }
78
+ let result = "";
79
+ let ri = 0;
80
+ for (let k = 0; k < tokens.length; k++) {
81
+ if (ri < mergeRuns.length && k === mergeRuns[ri]?.startIndex) {
82
+ const run = mergeRuns[ri];
83
+ const dels = [];
84
+ const inss = [];
85
+ for (let m = run.startIndex; m < run.endIndex; m++) {
86
+ const t2 = tokens[m];
87
+ if (t2.type === "pair") {
88
+ dels.push(t2.del);
89
+ inss.push(t2.ins);
90
+ } else {
91
+ dels.push(t2.text);
92
+ inss.push(t2.text);
93
+ }
94
+ }
95
+ result += `<del>${dels.join("")}</del><ins>${inss.join("")}</ins>`;
96
+ k = run.endIndex - 1;
97
+ ri++;
98
+ continue;
99
+ }
100
+ const t = tokens[k];
101
+ result += t.type === "pair" ? t.raw : t.text;
102
+ }
103
+ return result;
104
+ }
105
+ const BLOCK_TAGS = /* @__PURE__ */ new Set([
106
+ "p",
107
+ "h1",
108
+ "h2",
109
+ "h3",
110
+ "h4",
111
+ "h5",
112
+ "h6",
113
+ "ul",
114
+ "ol",
115
+ "blockquote",
116
+ "pre",
117
+ "div",
118
+ "table",
119
+ "figure",
120
+ "section",
121
+ "article"
122
+ ]);
123
+ function parseBlocks(html) {
124
+ const container = document.createElement("div");
125
+ container.innerHTML = html;
126
+ const blocks = [];
127
+ for (const child of container.childNodes) {
128
+ if (child.nodeType === Node.ELEMENT_NODE) {
129
+ const el = child;
130
+ const tag = el.tagName.toLowerCase();
131
+ if (!BLOCK_TAGS.has(tag)) {
132
+ return null;
133
+ }
134
+ let openTag = `<${tag}`;
135
+ for (const attr of el.attributes) {
136
+ openTag += ` ${attr.name}="${attr.value}"`;
137
+ }
138
+ openTag += ">";
139
+ blocks.push({ tag, innerHTML: el.innerHTML, openTag });
140
+ } else if (child.nodeType === Node.TEXT_NODE && child.textContent?.trim()) {
141
+ return null;
142
+ }
143
+ }
144
+ return blocks;
145
+ }
146
+ function alignBlocks(before, after) {
147
+ const m = before.length;
148
+ const n = after.length;
149
+ const dp = Array.from(
150
+ { length: m + 1 },
151
+ () => Array(n + 1).fill(0)
152
+ );
153
+ for (let i2 = 1; i2 <= m; i2++) {
154
+ for (let j2 = 1; j2 <= n; j2++) {
155
+ if (before[i2 - 1].tag === after[j2 - 1].tag) {
156
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
157
+ } else {
158
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
159
+ }
160
+ }
161
+ }
162
+ const entries = [];
163
+ let i = m;
164
+ let j = n;
165
+ while (i > 0 && j > 0) {
166
+ if (before[i - 1].tag === after[j - 1].tag) {
167
+ entries.push({
168
+ type: "match",
169
+ before: before[i - 1],
170
+ after: after[j - 1]
171
+ });
172
+ i--;
173
+ j--;
174
+ } else if (dp[i - 1][j] >= dp[i][j - 1]) {
175
+ entries.push({ type: "delete", before: before[i - 1] });
176
+ i--;
177
+ } else {
178
+ entries.push({ type: "insert", after: after[j - 1] });
179
+ j--;
180
+ }
181
+ }
182
+ while (i > 0) {
183
+ entries.push({ type: "delete", before: before[i - 1] });
184
+ i--;
185
+ }
186
+ while (j > 0) {
187
+ entries.push({ type: "insert", after: after[j - 1] });
188
+ j--;
189
+ }
190
+ return entries.reverse();
191
+ }
192
+ function computeBlockDiff(before, after) {
193
+ const alignment = alignBlocks(before, after);
194
+ const parts = [];
195
+ for (const entry of alignment) {
196
+ if (entry.type === "match") {
197
+ if (entry.before.innerHTML === entry.after.innerHTML) {
198
+ parts.push(
199
+ `${entry.after.openTag}${entry.after.innerHTML}</${entry.after.tag}>`
200
+ );
201
+ } else {
202
+ const innerDiff = diff(entry.before.innerHTML, entry.after.innerHTML);
203
+ parts.push(
204
+ `${entry.after.openTag}${cleanupDiffMods(innerDiff)}</${entry.after.tag}>`
205
+ );
206
+ }
207
+ } else if (entry.type === "delete") {
208
+ parts.push(
209
+ `${entry.before.openTag}<del>${entry.before.innerHTML}</del></${entry.before.tag}>`
210
+ );
211
+ } else {
212
+ parts.push(
213
+ `${entry.after.openTag}<ins>${entry.after.innerHTML}</ins></${entry.after.tag}>`
214
+ );
215
+ }
216
+ }
217
+ return parts.join("");
218
+ }
219
+ export function computeDiff(before, after) {
220
+ const beforeBlocks = parseBlocks(before);
221
+ const afterBlocks = parseBlocks(after);
222
+ if (beforeBlocks && afterBlocks && (beforeBlocks.length > 0 || afterBlocks.length > 0)) {
223
+ return computeBlockDiff(beforeBlocks, afterBlocks);
224
+ }
225
+ const raw = diff(before, after);
226
+ return cleanupDiffMods(raw);
227
+ }
@@ -253,7 +253,7 @@ onBlokkliEvent("item:dropped", () => {
253
253
  }
254
254
  });
255
255
  onBlokkliEvent("sidebar:open", (id) => {
256
- if (id === props.id) {
256
+ if (id === props.id && !isRenderedDetached.value) {
257
257
  activeSidebar.value = props.id;
258
258
  }
259
259
  });
@@ -2,6 +2,7 @@ import { type Ref, type ComputedRef } from '#imports';
2
2
  import type { AdaptersProvider } from './adapters.js';
3
3
  import type { StateProvider } from './state.js';
4
4
  import type { UiProvider } from './ui.js';
5
+ import type { ReadabilityProvider } from './readability.js';
5
6
  import type { AdapterContext } from '#blokkli/editor/adapter';
6
7
  import type { TextProvider } from './texts.js';
7
8
  import type { AnalyzeNode, AnalyzeResult, Analyzer, AnalyzerType } from '../features/analyze/analyzers/types.js';
@@ -40,4 +41,4 @@ export type AnalyzeProvider = {
40
41
  nodes: AnalyzeNode[];
41
42
  }>>;
42
43
  };
43
- export default function analyzeProvider(adapters: AdaptersProvider, state: StateProvider, ui: UiProvider, context: ComputedRef<AdapterContext>, $t: TextProvider): AnalyzeProvider;
44
+ export default function analyzeProvider(adapters: AdaptersProvider, state: StateProvider, ui: UiProvider, context: ComputedRef<AdapterContext>, $t: TextProvider, readability: ReadabilityProvider): AnalyzeProvider;
@@ -1,7 +1,7 @@
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
- export default function analyzeProvider(adapters, state, ui, context, $t) {
4
+ export default function analyzeProvider(adapters, state, ui, context, $t, readability) {
5
5
  const analyzers = ref([]);
6
6
  const isInitialized = ref(false);
7
7
  let initPromise = null;
@@ -12,7 +12,9 @@ export default function analyzeProvider(adapters, state, ui, context, $t) {
12
12
  ui.interfaceLanguage.value,
13
13
  ui.providerElement,
14
14
  state,
15
- $t
15
+ $t,
16
+ void 0,
17
+ readability
16
18
  );
17
19
  await Promise.all(
18
20
  fetched.map(async (analyzer) => {
@@ -40,7 +42,8 @@ export default function analyzeProvider(adapters, state, ui, context, $t) {
40
42
  element,
41
43
  state,
42
44
  $t,
43
- signal
45
+ signal,
46
+ readability
44
47
  );
45
48
  }
46
49
  async function runAnalyzer(analyzer, ctx) {
@@ -217,6 +217,7 @@ export default function(debug, ui) {
217
217
  });
218
218
  onBlokkliEvent("ui:resized", handleRefresh);
219
219
  onBlokkliEvent("option:finish-change", handleRefresh);
220
+ onBlokkliEvent("ui:update-rects", handleRefresh);
220
221
  onMounted(() => {
221
222
  doInitTimeout();
222
223
  });
@@ -0,0 +1,54 @@
1
+ import type { EntityContext } from '#blokkli/types';
2
+ import type { AdaptersProvider } from './adapters.js';
3
+ import type { DefinitionProvider } from './definition.js';
4
+ import type { DirectiveProvider } from './directive.js';
5
+ import type { StateProvider } from './state.js';
6
+ import type { BlockDefinitionProvider } from './types.js';
7
+ import './fieldValueAdapterTypes.js';
8
+ /**
9
+ * Simplified field type for editable fields.
10
+ */
11
+ export type FieldValueType = 'plain' | 'markup';
12
+ /**
13
+ * A text field value as provided by the adapter or directive system.
14
+ */
15
+ export type TextFieldValue = {
16
+ uuid: string;
17
+ fieldName: string;
18
+ value: string;
19
+ fieldType: FieldValueType;
20
+ };
21
+ /**
22
+ * The result of reading a field value.
23
+ */
24
+ export type ReadFieldValueResult = {
25
+ value: string;
26
+ fieldType: FieldValueType;
27
+ };
28
+ export type FieldValueProvider = {
29
+ /**
30
+ * Resolve an editable field config to its simplified type.
31
+ * Returns 'plain' for text fields, 'markup' for rich text/frame fields, null for unsupported.
32
+ */
33
+ resolveFieldType: (entityType: string, bundle: string, fieldName: string) => FieldValueType | null;
34
+ /**
35
+ * Read the current value of an editable field on a block or entity.
36
+ * Tries the registered getValue() callback first, falls back to DOM element reading.
37
+ */
38
+ readValue: (entityType: string, uuid: string, bundle: string, fieldName: string, fieldType: FieldValueType) => string;
39
+ /**
40
+ * Read the current value and field type of an editable field.
41
+ *
42
+ * Uses the correct strategy based on the field's configuration:
43
+ * 1. Component editables — use getValue() callback
44
+ * 2. Mutated props — read from mutatedItemProps or mutatedEntity
45
+ * 3. Direct DOM — read innerHTML (markup) or textContent (plain)
46
+ */
47
+ readFieldValue: (fieldName: string, host: EntityContext) => ReadFieldValueResult | null;
48
+ /**
49
+ * Get all text field values from the page.
50
+ * Tries the adapter method first, falls back to reading from directive system.
51
+ */
52
+ getTextFieldValues: () => Promise<TextFieldValue[]>;
53
+ };
54
+ export default function fieldValueProvider(adapters: AdaptersProvider, directive: DirectiveProvider, state: StateProvider, types: BlockDefinitionProvider, definitions: DefinitionProvider): FieldValueProvider;