@adminforth/agent 1.1.0 → 1.2.0

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.
@@ -18,7 +18,13 @@
18
18
 
19
19
  <div class="incremark-shiki-body">
20
20
  <div
21
- v-if="renderedHtml"
21
+ v-if="shouldRenderVega && !renderedHtml"
22
+ ref="vegaContainer"
23
+ class="incremark-vega"
24
+ />
25
+
26
+ <div
27
+ v-else-if="renderedHtml"
22
28
  class="incremark-shiki-html"
23
29
  v-html="renderedHtml"
24
30
  />
@@ -31,6 +37,7 @@
31
37
  <script setup lang="ts">
32
38
  import type { Code } from 'mdast';
33
39
  import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
40
+ import embed from 'vega-embed';
34
41
 
35
42
  import { highlightCodeSnippetHtml, type IncremarkCodeTheme } from './incremarkCodeHighlight';
36
43
 
@@ -53,15 +60,18 @@ const props = withDefaults(defineProps<{
53
60
  const renderedHtml = ref('');
54
61
  const copied = ref(false);
55
62
  const prefersDarkMode = ref(isDarkDocument());
63
+ const vegaContainer = ref<HTMLDivElement | null>(null);
56
64
 
57
65
  let copyResetTimeout: number | null = null;
58
66
  let renderRequestId = 0;
59
67
  let scheduledFrameId: number | null = null;
60
68
  let themeObserver: MutationObserver | null = null;
69
+ let vegaResult: { finalize: () => void } | null = null;
61
70
 
62
71
  const sourceCode = computed(() => props.node.value ?? '');
63
72
  const language = computed(() => props.node.lang?.trim().toLowerCase() || 'text');
64
- const languageLabel = computed(() => props.node.lang?.trim() || 'text');
73
+ const languageLabel = computed(() => language.value === 'vega-lite' ? '' : props.node.lang?.trim() || 'text');
74
+ const shouldRenderVega = computed(() => language.value === 'vega-lite' && props.blockStatus === 'completed');
65
75
  const codeTheme = computed<IncremarkCodeTheme>(() => {
66
76
  const requestedTheme = props.theme ?? (prefersDarkMode.value ? props.darkTheme : props.lightTheme);
67
77
 
@@ -77,7 +87,7 @@ const codeTheme = computed<IncremarkCodeTheme>(() => {
77
87
  });
78
88
 
79
89
  watch(
80
- [sourceCode, language, codeTheme, () => props.disableHighlight],
90
+ [sourceCode, language, codeTheme, () => props.disableHighlight, () => props.blockStatus],
81
91
  () => {
82
92
  scheduleHighlight();
83
93
  },
@@ -86,6 +96,7 @@ watch(
86
96
 
87
97
  onMounted(() => {
88
98
  if (typeof MutationObserver === 'undefined' || typeof document === 'undefined') {
99
+ scheduleHighlight();
89
100
  return;
90
101
  }
91
102
 
@@ -97,10 +108,13 @@ onMounted(() => {
97
108
  attributes: true,
98
109
  attributeFilter: ['class'],
99
110
  });
111
+
112
+ scheduleHighlight();
100
113
  });
101
114
 
102
115
  onBeforeUnmount(() => {
103
116
  renderRequestId += 1;
117
+ clearVega();
104
118
 
105
119
  if (copyResetTimeout !== null) {
106
120
  window.clearTimeout(copyResetTimeout);
@@ -154,6 +168,45 @@ function scheduleHighlight() {
154
168
  async function renderHighlight() {
155
169
  const requestId = ++renderRequestId;
156
170
 
171
+ if (shouldRenderVega.value) {
172
+ renderedHtml.value = '';
173
+
174
+ if (!sourceCode.value || !vegaContainer.value) {
175
+ return;
176
+ }
177
+
178
+ try {
179
+ clearVega();
180
+ const spec = JSON.parse(sourceCode.value);
181
+
182
+ if (spec.width == null) {
183
+ spec.width = 'container';
184
+ }
185
+
186
+ if (spec.autosize == null) {
187
+ spec.autosize = { type: 'fit-x', contains: 'padding' };
188
+ }
189
+
190
+ const result = await embed(vegaContainer.value, spec, {
191
+ actions: false,
192
+ renderer: 'svg',
193
+ });
194
+
195
+ if (requestId !== renderRequestId) {
196
+ result.finalize();
197
+ return;
198
+ }
199
+
200
+ vegaResult = result;
201
+ return;
202
+ } catch (error) {
203
+ clearVega();
204
+ console.error('Failed to render Vega-Lite block', error);
205
+ }
206
+ } else {
207
+ clearVega();
208
+ }
209
+
157
210
  if (!sourceCode.value || props.disableHighlight) {
158
211
  renderedHtml.value = '';
159
212
  return;
@@ -177,6 +230,15 @@ async function renderHighlight() {
177
230
  function isDarkDocument(): boolean {
178
231
  return typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
179
232
  }
233
+
234
+ function clearVega() {
235
+ vegaResult?.finalize();
236
+ vegaResult = null;
237
+
238
+ if (vegaContainer.value) {
239
+ vegaContainer.value.innerHTML = '';
240
+ }
241
+ }
180
242
  </script>
181
243
 
182
244
  <style scoped>
@@ -265,6 +327,11 @@ function isDarkDocument(): boolean {
265
327
  overflow-x: auto;
266
328
  }
267
329
 
330
+ .incremark-vega {
331
+ padding: 18px;
332
+ width: 100%;
333
+ }
334
+
268
335
  .incremark-shiki-fallback {
269
336
  margin: 0;
270
337
  padding: 18px;
@@ -298,4 +365,12 @@ function isDarkDocument(): boolean {
298
365
  :deep(.incremark-shiki-html .line) {
299
366
  min-height: 1.65em;
300
367
  }
368
+
369
+ :deep(.incremark-vega .vega-embed) {
370
+ width: 100%;
371
+ }
372
+
373
+ :deep(.incremark-vega){
374
+ padding: 0;
375
+ }
301
376
  </style>
@@ -21,6 +21,7 @@
21
21
  "ai": "^6.0.158",
22
22
  "dompurify": "^3.3.3",
23
23
  "katex": "^0.16.45",
24
- "marked": "^18.0.0"
24
+ "marked": "^18.0.0",
25
+ "vega-embed": "^7.1.0"
25
26
  }
26
27
  }