@blokkli/editor 2.0.0-alpha.42 → 2.0.0-alpha.44

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 (78) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +4 -4
  3. package/dist/modules/agent/index.mjs +6 -2
  4. package/dist/modules/agent/runtime/app/composables/agentProvider.js +28 -19
  5. package/dist/modules/agent/runtime/app/tools/check_readability/index.js +1 -0
  6. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.d.vue.ts +2 -0
  7. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue +7 -3
  8. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue.d.ts +2 -0
  9. package/dist/modules/agent/runtime/app/tools/helpers.js +5 -3
  10. package/dist/modules/agent/runtime/server/Session.d.ts +2 -0
  11. package/dist/modules/agent/runtime/server/Session.js +17 -0
  12. package/dist/modules/agent/runtime/server/agent.js +2 -1
  13. package/dist/modules/agent/runtime/shared/types.d.ts +1 -0
  14. package/dist/modules/agent/runtime/shared/types.js +2 -1
  15. package/dist/runtime/components/BlokkliProvider.vue +14 -13
  16. package/dist/runtime/editor/components/Actions/Interactions/index.d.vue.ts +3 -0
  17. package/dist/runtime/editor/components/Actions/Interactions/index.vue +110 -0
  18. package/dist/runtime/editor/components/Actions/Interactions/index.vue.d.ts +3 -0
  19. package/dist/runtime/editor/components/Actions/index.vue +34 -3
  20. package/dist/runtime/editor/components/AnimationCanvas/index.vue +2 -10
  21. package/dist/runtime/editor/components/BundleSelector/index.vue +39 -11
  22. package/dist/runtime/editor/components/PreviewProvider.vue +1 -1
  23. package/dist/runtime/editor/components/Toolbar/index.vue +1 -2
  24. package/dist/runtime/editor/composables/useStickyToolbar.d.ts +1 -1
  25. package/dist/runtime/editor/composables/useStickyToolbar.js +25 -7
  26. package/dist/runtime/editor/css/output.css +1 -1
  27. package/dist/runtime/editor/features/analyze/Main.d.vue.ts +6 -0
  28. package/dist/runtime/editor/features/analyze/Main.vue +26 -1
  29. package/dist/runtime/editor/features/analyze/Main.vue.d.ts +6 -0
  30. package/dist/runtime/editor/features/analyze/Renderer/index.d.vue.ts +2 -0
  31. package/dist/runtime/editor/features/analyze/Renderer/index.vue +86 -15
  32. package/dist/runtime/editor/features/analyze/Renderer/index.vue.d.ts +2 -0
  33. package/dist/runtime/editor/features/analyze/analyzers/altText.d.ts +2 -0
  34. package/dist/runtime/editor/features/analyze/analyzers/altText.js +60 -0
  35. package/dist/runtime/editor/features/analyze/analyzers/headingStructure.d.ts +2 -0
  36. package/dist/runtime/editor/features/analyze/analyzers/headingStructure.js +141 -0
  37. package/dist/runtime/editor/features/analyze/analyzers/index.d.ts +5 -1
  38. package/dist/runtime/editor/features/analyze/analyzers/index.js +11 -1
  39. package/dist/runtime/editor/features/analyze/analyzers/readability.js +50 -16
  40. package/dist/runtime/editor/features/analyze/analyzers/types.d.ts +3 -2
  41. package/dist/runtime/editor/features/analyze/index.vue +12 -0
  42. package/dist/runtime/editor/features/analyze/readability/builtinAnalyzer.js +38 -22
  43. package/dist/runtime/editor/features/analyze/readability/types.d.ts +18 -3
  44. package/dist/runtime/editor/features/dragging-overlay/DragItems/index.d.vue.ts +1 -0
  45. package/dist/runtime/editor/features/dragging-overlay/DragItems/index.vue +110 -5
  46. package/dist/runtime/editor/features/dragging-overlay/DragItems/index.vue.d.ts +1 -0
  47. package/dist/runtime/editor/features/dragging-overlay/Renderer/index.d.vue.ts +1 -0
  48. package/dist/runtime/editor/features/dragging-overlay/Renderer/index.vue +16 -1
  49. package/dist/runtime/editor/features/dragging-overlay/Renderer/index.vue.d.ts +1 -0
  50. package/dist/runtime/editor/features/dragging-overlay/index.vue +2 -1
  51. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/ChunkOverlay.d.vue.ts +8 -0
  52. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/ChunkOverlay.vue +135 -0
  53. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/ChunkOverlay.vue.d.ts +8 -0
  54. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/index.d.vue.ts +7 -0
  55. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/index.vue +187 -0
  56. package/dist/runtime/editor/features/editable-field/Overlay/ReadabilityIndicator/index.vue.d.ts +7 -0
  57. package/dist/runtime/editor/features/editable-field/Overlay/index.vue +23 -0
  58. package/dist/runtime/editor/features/hover/Renderer/index.vue +1 -1
  59. package/dist/runtime/editor/features/options/Form/index.vue +7 -17
  60. package/dist/runtime/editor/features/options/index.vue +4 -3
  61. package/dist/runtime/editor/features/search/Overlay/Results/Content/index.vue +7 -11
  62. package/dist/runtime/editor/features/search/Overlay/Results/Page/index.vue +11 -13
  63. package/dist/runtime/editor/features/selection/Renderer/index.vue +2 -0
  64. package/dist/runtime/editor/features/translations/index.vue +1 -1
  65. package/dist/runtime/editor/helpers/draggable/index.d.ts +3 -0
  66. package/dist/runtime/editor/helpers/draggable/index.js +9 -0
  67. package/dist/runtime/editor/providers/analyze.js +1 -1
  68. package/dist/runtime/editor/providers/animation.js +1 -1
  69. package/dist/runtime/editor/providers/readability.js +16 -20
  70. package/dist/runtime/editor/providers/selection.d.ts +0 -4
  71. package/dist/runtime/editor/providers/selection.js +0 -2
  72. package/dist/runtime/editor/providers/ui.d.ts +16 -0
  73. package/dist/runtime/editor/providers/ui.js +5 -1
  74. package/dist/runtime/editor/translations/de.json +129 -1
  75. package/dist/runtime/editor/translations/fr.json +128 -0
  76. package/dist/runtime/editor/translations/gsw_CH.json +128 -0
  77. package/dist/runtime/editor/translations/it.json +128 -0
  78. package/package.json +1 -1
@@ -27,11 +27,21 @@
27
27
  v-else-if="!ui.isTransforming.value"
28
28
  :key="animation.renderKey.value"
29
29
  v-model="isRunning"
30
+ v-model:issue-count="issueCount"
31
+ v-model:has-violation="hasViolation"
30
32
  :langcode="context.language"
31
33
  :analyze
32
34
  :is-shown
33
35
  />
34
36
  </template>
37
+ <template v-if="issueCount" #badge>
38
+ <div
39
+ class="bk-sidebar-badge"
40
+ :class="hasViolation ? 'bk-is-red' : 'bk-is-yellow'"
41
+ >
42
+ {{ issueCount }}
43
+ </div>
44
+ </template>
35
45
  </PluginSidebar>
36
46
  </template>
37
47
 
@@ -51,6 +61,8 @@ defineBlokkliFeature({
51
61
  });
52
62
  const { $t, context, animation, ui, analyze } = useBlokkli();
53
63
  const isRunning = ref(false);
64
+ const issueCount = ref(0);
65
+ const hasViolation = ref(false);
54
66
  </script>
55
67
 
56
68
  <script>
@@ -1,38 +1,39 @@
1
1
  const SCORE_CONFIGS = {
2
2
  en: {
3
- label: "LIX",
4
- compute: (tr, text) => tr.lix(text),
5
- direction: "higher_harder",
6
- bands: { easy: 40, ok: 59 },
7
- impactThresholds: [50, 60, 70],
3
+ label: "FRE",
4
+ compute: (tr, text) => tr.fleschReadingEase(text),
5
+ direction: "higher_easier",
6
+ bands: { easy: 60, ok: 30 },
7
+ impactThresholds: [50, 30, 10],
8
+ minWords: 15,
8
9
  referenceTable: [
9
- { range: "Below 25", label: "Very easy (children's books)" },
10
- { range: "25\u201340", label: "Easy (simple articles)" },
11
- { range: "40\u201350", label: "Medium (newspapers)" },
12
- { range: "50\u201360", label: "Difficult (official documents)" },
10
+ { range: "Above 70", label: "Very easy (simple, conversational)" },
11
+ { range: "60\u201370", label: "Easy (standard web content)" },
12
+ { range: "50\u201360", label: "Fairly difficult (could be simpler)" },
13
+ { range: "30\u201350", label: "Difficult (academic, technical)" },
13
14
  {
14
- range: "Above 60",
15
+ range: "Below 30",
15
16
  label: "Very difficult \u2014 this is what gets flagged"
16
17
  },
17
- { range: "Above 70", label: "Critical \u2014 must be simplified" }
18
+ { range: "Below 10", label: "Critical \u2014 must be simplified" }
18
19
  ]
19
20
  },
20
21
  de: {
21
22
  label: "WSTF",
22
23
  compute: (tr, text) => tr.wienerSachtextformel(text, 1),
23
24
  direction: "higher_harder",
24
- bands: { easy: 7, ok: 14 },
25
- impactThresholds: [12, 16, 20],
25
+ bands: { easy: 15, ok: 18 },
26
+ impactThresholds: [16, 20, 24],
27
+ minWords: 5,
26
28
  referenceTable: [
27
- { range: "Below 4", label: "Very easy (children's books)" },
28
- { range: "4\u20137", label: "Easy (simple articles)" },
29
- { range: "7\u201311", label: "Medium (standard website content)" },
30
- { range: "11\u201314", label: "Moderately difficult (official documents)" },
29
+ { range: "Below 8", label: "Very easy (children's books)" },
30
+ { range: "8\u201315", label: "Easy (standard website content)" },
31
+ { range: "15\u201318", label: "Moderately difficult (could be simpler)" },
31
32
  {
32
- range: "Above 14",
33
+ range: "Above 18",
33
34
  label: "Difficult \u2014 this is what gets flagged"
34
35
  },
35
- { range: "Above 20", label: "Critical \u2014 must be simplified" }
36
+ { range: "Above 24", label: "Critical \u2014 must be simplified" }
36
37
  ]
37
38
  },
38
39
  fr: {
@@ -41,6 +42,7 @@ const SCORE_CONFIGS = {
41
42
  direction: "higher_harder",
42
43
  bands: { easy: 40, ok: 59 },
43
44
  impactThresholds: [50, 60, 70],
45
+ minWords: 5,
44
46
  referenceTable: [
45
47
  { range: "Below 25", label: "Very easy (children's books)" },
46
48
  { range: "25\u201340", label: "Easy (simple articles)" },
@@ -59,6 +61,7 @@ const SCORE_CONFIGS = {
59
61
  direction: "higher_easier",
60
62
  bands: { easy: 80, ok: 60 },
61
63
  impactThresholds: [60, 50, 40],
64
+ minWords: 5,
62
65
  referenceTable: [
63
66
  { range: "Above 80", label: "Very easy (children's books)" },
64
67
  { range: "60\u201380", label: "Easy (simple articles)" },
@@ -73,7 +76,6 @@ const SCORE_CONFIGS = {
73
76
  }
74
77
  };
75
78
  const SUPPORTED_LANGUAGES = ["en", "de", "fr", "it"];
76
- const DEFAULT_MIN_WORDS = 5;
77
79
  function getConfig(langcode) {
78
80
  if (isSupportedLangcode(langcode)) {
79
81
  return SCORE_CONFIGS[langcode];
@@ -153,7 +155,9 @@ export function createBuiltinReadabilityAnalyzer() {
153
155
  },
154
156
  description: "Analyzes text readability using language-specific algorithms (LIX, Wiener Sachtextformel, Gulpease).",
155
157
  supportedLanguages: SUPPORTED_LANGUAGES,
156
- minWordsForConfidence: DEFAULT_MIN_WORDS,
158
+ get minWordsForConfidence() {
159
+ return getConfig(currentLangcode).minWords;
160
+ },
157
161
  get scoreLabel() {
158
162
  return getConfig(currentLangcode).label;
159
163
  },
@@ -182,7 +186,7 @@ export function createBuiltinReadabilityAnalyzer() {
182
186
  const trimmed = text.trim();
183
187
  if (!trimmed) return null;
184
188
  const words = segmentWords(trimmed);
185
- if (words.length < DEFAULT_MIN_WORDS) return null;
189
+ if (words.length < config.minWords) return null;
186
190
  const score = safe(() => config.compute(tr, trimmed));
187
191
  return score != null ? round(score) : null;
188
192
  });
@@ -195,6 +199,18 @@ export function createBuiltinReadabilityAnalyzer() {
195
199
  },
196
200
  getAgentContext() {
197
201
  return buildAgentContext(getConfig(currentLangcode));
202
+ },
203
+ getScaleInfo(langcode) {
204
+ const config = getConfig(langcode);
205
+ const t1 = Math.min(config.bands.easy, config.bands.ok);
206
+ const t2 = Math.max(config.bands.easy, config.bands.ok);
207
+ const padding = Math.round((t2 - t1) * 0.5);
208
+ return {
209
+ thresholds: [t1, t2],
210
+ direction: config.direction,
211
+ scaleMin: Math.max(0, t1 - padding),
212
+ scaleMax: t2 + padding
213
+ };
198
214
  }
199
215
  };
200
216
  }
@@ -9,9 +9,9 @@ export type ReadabilityBand = 'easy' | 'ok' | 'hard';
9
9
  export type ReadabilityChunkResult = {
10
10
  text: string;
11
11
  html?: string;
12
- score: number;
13
- band: ReadabilityBand;
14
- impact: AnalyzeImpact;
12
+ score: number | null;
13
+ band: ReadabilityBand | null;
14
+ impact: AnalyzeImpact | null;
15
15
  description?: string;
16
16
  };
17
17
  /**
@@ -69,4 +69,19 @@ export type ReadabilityAnalyzer = {
69
69
  * Optional display formatting for a score value.
70
70
  */
71
71
  formatScore?(value: number): string;
72
+ /**
73
+ * Return scale information for visualizing score bands.
74
+ * Used by the UI to render a score bar with thresholds.
75
+ *
76
+ * - `thresholds`: The two boundary values between easy/ok and ok/hard.
77
+ * Listed in ascending order (lower value first).
78
+ * - `direction`: Whether higher scores mean easier or harder text.
79
+ * - `scaleMin`/`scaleMax`: The visual range of the score bar.
80
+ */
81
+ getScaleInfo?(langcode: string): {
82
+ thresholds: [number, number];
83
+ direction: 'higher_easier' | 'higher_harder';
84
+ scaleMin: number;
85
+ scaleMax: number;
86
+ };
72
87
  };
@@ -21,6 +21,7 @@ type __VLS_Props = {
21
21
  color?: string;
22
22
  backgroundColor?: string;
23
23
  activeLabel?: string;
24
+ activeRect?: Rectangle;
24
25
  };
25
26
  declare function getRect(): Rectangle;
26
27
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
@@ -14,6 +14,7 @@
14
14
  v-show="activeLabel"
15
15
  ref="labelEl"
16
16
  class="bk bk-dragging-overlay-label"
17
+ :class="labelPosition.className"
17
18
  :style="styleLabel"
18
19
  >
19
20
  <Icon name="bk_mdi_drag_pan" />
@@ -45,10 +46,12 @@ const props = defineProps({
45
46
  isTouch: { type: Boolean, required: true },
46
47
  color: { type: String, required: false },
47
48
  backgroundColor: { type: String, required: false },
48
- activeLabel: { type: String, required: false }
49
+ activeLabel: { type: String, required: false },
50
+ activeRect: { type: Object, required: false }
49
51
  });
50
52
  const MAX_WIDTH = 350;
51
53
  const MAX_HEIGHT = 200;
54
+ const LABEL_GAP = 20;
52
55
  const labelEl = useTemplateRef("labelEl");
53
56
  const labelWidth = ref(0);
54
57
  const labelHeight = ref(0);
@@ -107,12 +110,109 @@ const style = computed(() => {
107
110
  "--bk-active-color": props.color && props.activeLabel ? props.color : "rgba(255,255,255,0)"
108
111
  };
109
112
  });
110
- const styleLabel = computed(() => {
113
+ function rangesOverlap(aStart, aEnd, bStart, bEnd) {
114
+ return aStart < bEnd && aEnd > bStart;
115
+ }
116
+ function resolveVerticalPlacement(activeRect, dragItemsRect, lw, lh, vw, vh, gap) {
117
+ const x = Math.min(
118
+ Math.max(0, activeRect.x + activeRect.width / 2 - lw / 2),
119
+ vw - lw
120
+ );
121
+ const overlapsHorizontally = rangesOverlap(
122
+ x,
123
+ x + lw,
124
+ dragItemsRect.x,
125
+ dragItemsRect.x + dragItemsRect.width
126
+ );
127
+ let topEdge = activeRect.y;
128
+ if (overlapsHorizontally) {
129
+ topEdge = Math.min(topEdge, dragItemsRect.y);
130
+ }
131
+ let y = topEdge - lh - gap;
132
+ let placement = "top";
133
+ if (y < 0) {
134
+ let bottomEdge = activeRect.y + activeRect.height;
135
+ if (overlapsHorizontally) {
136
+ bottomEdge = Math.max(bottomEdge, dragItemsRect.y + dragItemsRect.height);
137
+ }
138
+ y = bottomEdge + gap;
139
+ placement = "bottom";
140
+ }
141
+ y = Math.min(Math.max(0, y), vh - lh);
142
+ return { x, y, placement, className: `bk-is-${placement}` };
143
+ }
144
+ function resolveHorizontalPlacement(activeRect, dragItemsRect, lw, lh, vw, vh, gap) {
145
+ const y = Math.min(
146
+ Math.max(0, activeRect.y + activeRect.height / 2 - lh / 2),
147
+ vh - lh
148
+ );
149
+ const overlapsVertically = rangesOverlap(
150
+ y,
151
+ y + lh,
152
+ dragItemsRect.y,
153
+ dragItemsRect.y + dragItemsRect.height
154
+ );
155
+ let leftEdge = activeRect.x;
156
+ if (overlapsVertically) {
157
+ leftEdge = Math.min(leftEdge, dragItemsRect.x);
158
+ }
159
+ let x = leftEdge - lw - gap;
160
+ let placement = "left";
161
+ if (x < 0) {
162
+ let rightEdge = activeRect.x + activeRect.width;
163
+ if (overlapsVertically) {
164
+ rightEdge = Math.max(rightEdge, dragItemsRect.x + dragItemsRect.width);
165
+ }
166
+ x = rightEdge + gap;
167
+ placement = "right";
168
+ }
169
+ x = Math.min(Math.max(0, x), vw - lw);
170
+ return { x, y, placement, className: `bk-is-${placement}` };
171
+ }
172
+ const labelPosition = computed(() => {
173
+ if (props.activeRect && !props.isTouch) {
174
+ const vw = ui.viewport.value.width;
175
+ const vh = ui.viewport.value.height;
176
+ const lw = labelWidth.value;
177
+ const lh = labelHeight.value;
178
+ const gap = LABEL_GAP;
179
+ const dragItemsRect = {
180
+ x: translateX.value,
181
+ y: translateY.value,
182
+ width: width.value,
183
+ height: height.value
184
+ };
185
+ const useHorizontal = props.activeRect.width >= props.activeRect.height && props.activeRect.width < vw / 2;
186
+ if (useHorizontal) {
187
+ return resolveHorizontalPlacement(
188
+ props.activeRect,
189
+ dragItemsRect,
190
+ lw,
191
+ lh,
192
+ vw,
193
+ vh,
194
+ gap
195
+ );
196
+ }
197
+ return resolveVerticalPlacement(
198
+ props.activeRect,
199
+ dragItemsRect,
200
+ lw,
201
+ lh,
202
+ vw,
203
+ vh,
204
+ gap
205
+ );
206
+ }
111
207
  const x = Math.min(
112
208
  Math.max(10, translateX.value - labelWidth.value / 2 + width.value / 2),
113
209
  ui.viewport.value.width - labelWidth.value
114
210
  );
115
211
  const y = Math.max(10, translateY.value - labelHeight.value - 20);
212
+ return { x, y, placement: "top", className: "bk-is-top" };
213
+ });
214
+ const styleLabel = computed(() => {
215
+ const { x, y } = labelPosition.value;
116
216
  return {
117
217
  transform: `translate(${x}px, ${y}px)`,
118
218
  "--bk-active-background-color": props.backgroundColor && props.activeLabel ? props.backgroundColor : "rgba(255,255,255,0)",
@@ -200,8 +300,13 @@ onMounted(() => {
200
300
  let boundsX = props.isTouch ? 0 : bounds.x;
201
301
  let boundsY = props.isTouch ? translateY.value : bounds.y;
202
302
  if (!mouseInsideBound && !props.isTouch) {
203
- boundsX = props.startCoords.x - bounds.width / 2;
204
- boundsY = props.startCoords.y - bounds.height / 2;
303
+ if (isExisting.value) {
304
+ boundsX = props.startCoords.x - 20;
305
+ boundsY = props.startCoords.y - 20;
306
+ } else {
307
+ boundsX = props.startCoords.x - bounds.width / 2;
308
+ boundsY = props.startCoords.y - bounds.height / 2;
309
+ }
205
310
  }
206
311
  offsetX.value = props.startCoords.x - boundsX;
207
312
  offsetY.value = props.startCoords.y - boundsY;
@@ -254,7 +359,7 @@ onMounted(() => {
254
359
  const borderRadius = isTop ? theme.getDraggableStyle(item.element).radiusMin : 4;
255
360
  return {
256
361
  isTop,
257
- from: ui.lowPerformanceMode.value || !mouseInsideBound ? to : from,
362
+ from: ui.lowPerformanceMode.value || !mouseInsideBound && !isExisting.value ? to : from,
258
363
  to,
259
364
  width: item.element.offsetWidth,
260
365
  height: item.element.offsetHeight,
@@ -21,6 +21,7 @@ type __VLS_Props = {
21
21
  color?: string;
22
22
  backgroundColor?: string;
23
23
  activeLabel?: string;
24
+ activeRect?: Rectangle;
24
25
  };
25
26
  declare function getRect(): Rectangle;
26
27
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
@@ -11,6 +11,7 @@ declare var __VLS_7: {
11
11
  backgroundColor: any;
12
12
  color: any;
13
13
  label: any;
14
+ activeRect: any;
14
15
  };
15
16
  type __VLS_Slots = {} & {
16
17
  default?: (props: typeof __VLS_7) => any;
@@ -4,6 +4,7 @@
4
4
  :background-color="activeBackgroundColorHex"
5
5
  :color="activeColor"
6
6
  :label="active?.label"
7
+ :active-rect="activeScreenRect"
7
8
  />
8
9
  </Teleport>
9
10
  </template>
@@ -611,7 +612,8 @@ const fieldRenderPalette = computed(() => {
611
612
  gradEnd: accent[500],
612
613
  borderOuter: accent[400],
613
614
  borderInner: accent[300],
614
- color: accent[950]
615
+ color: [255, 255, 255]
616
+ // color: accent[950],
615
617
  },
616
618
  "2": {
617
619
  gradStart: mono[700],
@@ -667,6 +669,19 @@ const activeBackgroundColorHex = computed(() => {
667
669
  }
668
670
  return "";
669
671
  });
672
+ const activeScreenRect = computed(() => {
673
+ if (!active.value) {
674
+ return;
675
+ }
676
+ const scale = ui.artboardScale.value;
677
+ const offset = ui.artboardOffset.value;
678
+ return {
679
+ x: active.value.x * scale + offset.x,
680
+ y: active.value.y * scale + offset.y,
681
+ width: active.value.width * scale,
682
+ height: active.value.height * scale
683
+ };
684
+ });
670
685
  const activeHoverField = ref(null);
671
686
  const activeHoverRect = computed(() => {
672
687
  if (!activeHoverField.value) {
@@ -11,6 +11,7 @@ declare var __VLS_7: {
11
11
  backgroundColor: any;
12
12
  color: any;
13
13
  label: any;
14
+ activeRect: any;
14
15
  };
15
16
  type __VLS_Slots = {} & {
16
17
  default?: (props: typeof __VLS_7) => any;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <Renderer
3
3
  v-if="dragItems.length && isVisible"
4
- v-slot="{ backgroundColor, color, label }"
4
+ v-slot="{ backgroundColor, color, label, activeRect }"
5
5
  :items="dragItems"
6
6
  :box="box"
7
7
  :mouse-x="mouseX"
@@ -18,6 +18,7 @@
18
18
  :background-color
19
19
  :color
20
20
  :active-label="label"
21
+ :active-rect="activeRect"
21
22
  />
22
23
  </Renderer>
23
24
  <Teleport to="#bk-canvas-overlay">
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ text: string;
3
+ fieldType: 'plain' | 'markup';
4
+ element: HTMLElement;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,135 @@
1
+ <template>
2
+ <Teleport to="#bk-canvas-overlay">
3
+ <div
4
+ class="bk bk-readability-chunk-overlay"
5
+ :class="{ 'bk-is-stale': stale }"
6
+ :style="containerStyle"
7
+ >
8
+ <div
9
+ v-for="chunk in visibleChunks"
10
+ :key="chunk.index"
11
+ class="bk-readability-chunk-rect"
12
+ :class="'bk-is-' + chunk.band"
13
+ :style="chunk.style"
14
+ >
15
+ <span class="bk-readability-chunk-badge">
16
+ {{ scoreLabel }} {{ formatScore(chunk.score) }}
17
+ </span>
18
+ </div>
19
+ </div>
20
+ </Teleport>
21
+ </template>
22
+
23
+ <script setup>
24
+ import {
25
+ ref,
26
+ computed,
27
+ watch,
28
+ onMounted,
29
+ onBeforeUnmount,
30
+ useBlokkli
31
+ } from "#imports";
32
+ import { onBlokkliEvent } from "#blokkli/editor/composables";
33
+ import { collectTextElements } from "../../../analyze/analyzers/helpers/collectTextElements";
34
+ const props = defineProps({
35
+ text: { type: String, required: true },
36
+ fieldType: { type: String, required: true },
37
+ element: { type: null, required: true }
38
+ });
39
+ const { readability, context, ui } = useBlokkli();
40
+ const chunks = ref([]);
41
+ const stale = ref(false);
42
+ let timeout = null;
43
+ let lastFullUpdate = 0;
44
+ const scoreLabel = computed(() => readability.analyzer.value.scoreLabel);
45
+ function formatScore(value) {
46
+ return readability.formatScore(value);
47
+ }
48
+ const containerStyle = computed(() => {
49
+ const offset = ui.artboardOffset.value;
50
+ return {
51
+ width: ui.artboardSize.value.width + "px",
52
+ height: ui.artboardSize.value.height + "px",
53
+ transform: `translate(${offset.x}px, ${offset.y}px) scale(${ui.artboardScale.value})`
54
+ };
55
+ });
56
+ const visibleChunks = computed(
57
+ () => chunks.value.filter((c) => c.style.width !== "0px")
58
+ );
59
+ function updatePositions() {
60
+ for (const chunk of chunks.value) {
61
+ const r = ui.getAbsoluteElementRect(chunk.element);
62
+ chunk.style = {
63
+ width: r.width + "px",
64
+ height: r.height + "px",
65
+ transform: `translate(${r.x}px, ${r.y}px)`
66
+ };
67
+ }
68
+ }
69
+ async function analyze(text) {
70
+ if (!text.trim() || props.fieldType !== "markup") {
71
+ chunks.value = [];
72
+ stale.value = false;
73
+ return;
74
+ }
75
+ const chunkResults = await readability.analyzeText(
76
+ text,
77
+ context.value.language,
78
+ props.fieldType
79
+ );
80
+ if (chunkResults.length === 0) {
81
+ chunks.value = [];
82
+ stale.value = false;
83
+ return;
84
+ }
85
+ await new Promise((resolve) => requestAnimationFrame(resolve));
86
+ const textElements = collectTextElements(props.element);
87
+ const newChunks = [];
88
+ const count = Math.min(chunkResults.length, textElements.length);
89
+ for (let i = 0; i < count; i++) {
90
+ const result = chunkResults[i];
91
+ if (result.score === null || result.band === null) continue;
92
+ const textEl = textElements[i];
93
+ const r = ui.getAbsoluteElementRect(textEl.element);
94
+ newChunks.push({
95
+ index: i,
96
+ score: result.score,
97
+ band: result.band,
98
+ element: textEl.element,
99
+ style: {
100
+ width: r.width + "px",
101
+ height: r.height + "px",
102
+ transform: `translate(${r.x}px, ${r.y}px)`
103
+ }
104
+ });
105
+ }
106
+ chunks.value = newChunks;
107
+ stale.value = false;
108
+ }
109
+ watch(
110
+ () => props.text,
111
+ (newText) => {
112
+ if (timeout) {
113
+ window.clearTimeout(timeout);
114
+ }
115
+ stale.value = true;
116
+ timeout = window.setTimeout(() => {
117
+ analyze(newText);
118
+ }, 500);
119
+ }
120
+ );
121
+ onBlokkliEvent("animationFrame", (ctx) => {
122
+ const forceRefresh = ctx.time - lastFullUpdate > 1e3;
123
+ if (!forceRefresh) return;
124
+ lastFullUpdate = ctx.time;
125
+ updatePositions();
126
+ });
127
+ onMounted(() => {
128
+ analyze(props.text);
129
+ });
130
+ onBeforeUnmount(() => {
131
+ if (timeout) {
132
+ window.clearTimeout(timeout);
133
+ }
134
+ });
135
+ </script>
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ text: string;
3
+ fieldType: 'plain' | 'markup';
4
+ element: HTMLElement;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ type __VLS_Props = {
2
+ text: string;
3
+ fieldType: 'plain' | 'markup';
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const _default: typeof __VLS_export;
7
+ export default _default;