@airalogy/aimd-renderer 2.5.0 → 2.6.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.
- package/README.md +6 -5
- package/README.zh-CN.md +6 -5
- package/dist/aimd-renderer.css +1 -1
- package/dist/common/processor.d.ts.map +1 -1
- package/dist/common/unified-token-renderer.d.ts.map +1 -1
- package/dist/html.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +262 -217
- package/dist/{processor-B2mByErv.js → processor-CHbNEcN8.js} +2687 -2094
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/vue-renderer.d.ts +22 -1
- package/dist/vue/vue-renderer.d.ts.map +1 -1
- package/dist/vue.js +10 -9
- package/package.json +6 -6
- package/src/__tests__/renderer.test.ts +191 -1
- package/src/common/processor.ts +173 -41
- package/src/common/unified-token-renderer.ts +104 -24
- package/src/index.ts +3 -0
- package/src/styles/katex.css +109 -0
- package/src/vue/index.ts +3 -0
- package/src/vue/vue-renderer.ts +318 -48
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vue rendering exports
|
|
3
3
|
*/
|
|
4
|
-
export { type AimdComponentRenderer, type AimdRendererContext, type AssetResolver, createAssetRenderer, createCodeBlockRenderer, createComponentRenderer, createEmbeddedRenderer, createMermaidRenderer, createStepCardRenderer, type ElementRenderer, hastToVue, renderToVNodes, type AimdStepCardRendererOptions, type ShikiHighlighter, type VueRendererOptions, } from './vue-renderer';
|
|
4
|
+
export { type AimdComponentRenderer, type AimdRendererContext, type AssetResolver, createAssetRenderer, createCodeBlockRenderer, createComponentRenderer, createEmbeddedRenderer, createMermaidRenderer, createStepCardRenderer, loadShikiHighlighter, type CodeBlockRendererOptions, type ElementRenderer, hastToVue, renderToVNodes, type LoadShikiHighlighterOptions, type AimdStepCardRendererOptions, type ShikiHighlighter, type VueRendererOptions, } from './vue-renderer';
|
|
5
5
|
export { renderToVue, createRenderer, defaultRenderer, } from '../common/processor';
|
|
6
6
|
export { createAimdRendererMessages, DEFAULT_AIMD_RENDERER_LOCALE, resolveAimdRendererLocale, } from '../locales';
|
|
7
7
|
export type { RenderContext, RenderMode, ProcessorOptions, } from '@airalogy/aimd-core/types';
|
package/dist/vue/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,eAAe,EACpB,SAAS,EACT,cAAc,EACd,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,2BAA2B,CAAA;AAElC,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACpG,YAAY,EACV,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,SAAS,EACT,cAAc,EACd,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,2BAA2B,CAAA;AAElC,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACpG,YAAY,EACV,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,YAAY,CAAA"}
|
|
@@ -97,6 +97,16 @@ export interface ShikiHighlighter {
|
|
|
97
97
|
lang: string;
|
|
98
98
|
theme: string;
|
|
99
99
|
}) => string;
|
|
100
|
+
codeToTokensBase?: (code: string, options: {
|
|
101
|
+
lang: string;
|
|
102
|
+
theme: string;
|
|
103
|
+
}) => Array<Array<{
|
|
104
|
+
content: string;
|
|
105
|
+
color?: string;
|
|
106
|
+
bgColor?: string;
|
|
107
|
+
fontStyle?: number;
|
|
108
|
+
htmlStyle?: string | Record<string, string>;
|
|
109
|
+
}>>;
|
|
100
110
|
codeToTokensWithThemes?: (code: string, options: {
|
|
101
111
|
lang: string;
|
|
102
112
|
themes: Record<string, string>;
|
|
@@ -107,12 +117,23 @@ export interface ShikiHighlighter {
|
|
|
107
117
|
}>;
|
|
108
118
|
}>>;
|
|
109
119
|
}
|
|
120
|
+
export interface CodeBlockRendererOptions {
|
|
121
|
+
theme?: string;
|
|
122
|
+
lineNumbers?: boolean;
|
|
123
|
+
wrap?: boolean;
|
|
124
|
+
className?: string;
|
|
125
|
+
}
|
|
126
|
+
export interface LoadShikiHighlighterOptions {
|
|
127
|
+
themes?: string[];
|
|
128
|
+
langs?: string[];
|
|
129
|
+
}
|
|
130
|
+
export declare function loadShikiHighlighter(options?: LoadShikiHighlighterOptions): Promise<ShikiHighlighter>;
|
|
110
131
|
/**
|
|
111
132
|
* Create code block element renderer with Shiki support
|
|
112
133
|
* @param highlighter - Shiki highlighter instance (can be reactive ref)
|
|
113
134
|
* @param defaultTheme - Default theme to use
|
|
114
135
|
*/
|
|
115
|
-
export declare function createCodeBlockRenderer(highlighter: ShikiHighlighter | null | (() => ShikiHighlighter | null),
|
|
136
|
+
export declare function createCodeBlockRenderer(highlighter: ShikiHighlighter | null | (() => ShikiHighlighter | null), optionsOrTheme?: string | CodeBlockRendererOptions): ElementRenderer;
|
|
116
137
|
/**
|
|
117
138
|
* Create Mermaid diagram element renderer
|
|
118
139
|
* @param MermaidComponent - Vue component to render Mermaid diagrams
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue-renderer.d.ts","sourceRoot":"","sources":["../../src/vue/vue-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAoB,WAAW,EAAE,MAAM,MAAM,CAAA;AACpF,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACvD,OAAO,KAAK,EAAE,QAAQ,EAA8B,aAAa,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"vue-renderer.d.ts","sourceRoot":"","sources":["../../src/vue/vue-renderer.ts"],"names":[],"mappings":"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"}
|
package/dist/vue.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { D as a, c as
|
|
1
|
+
import { D as a, c as s, f as d, g as n, h as t, i as o, j as R, k as c, l as i, m as l, n as m, o as A, q as E, s as h, e as g } from "./processor-CHbNEcN8.js";
|
|
2
2
|
export {
|
|
3
3
|
a as DEFAULT_AIMD_RENDERER_LOCALE,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
s as createAimdRendererMessages,
|
|
5
|
+
d as createAssetRenderer,
|
|
6
6
|
n as createCodeBlockRenderer,
|
|
7
7
|
t as createComponentRenderer,
|
|
8
8
|
o as createEmbeddedRenderer,
|
|
9
9
|
R as createMermaidRenderer,
|
|
10
10
|
c as createRenderer,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
i as createStepCardRenderer,
|
|
12
|
+
l as defaultRenderer,
|
|
13
|
+
m as hastToVue,
|
|
14
|
+
A as loadShikiHighlighter,
|
|
15
|
+
E as renderToVNodes,
|
|
16
|
+
h as renderToVue,
|
|
17
|
+
g as resolveAimdRendererLocale
|
|
17
18
|
};
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airalogy/aimd-renderer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.6.0",
|
|
5
5
|
"description": "AIMD (Airalogy Markdown) rendering engines for HTML and Vue",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/airalogy/
|
|
10
|
-
"directory": "packages/aimd-renderer"
|
|
9
|
+
"url": "https://github.com/airalogy/airalogy.git",
|
|
10
|
+
"directory": "packages/npm/aimd-renderer"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/airalogy/
|
|
13
|
+
"url": "https://github.com/airalogy/airalogy/issues"
|
|
14
14
|
},
|
|
15
|
-
"homepage": "https://github.com/airalogy/
|
|
15
|
+
"homepage": "https://github.com/airalogy/airalogy/tree/main/packages/npm/aimd-renderer#readme",
|
|
16
16
|
"keywords": [
|
|
17
17
|
"aimd",
|
|
18
18
|
"markdown",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"shiki": "^2.5.0",
|
|
65
65
|
"unified": "^11.0.5",
|
|
66
66
|
"vfile": "^6.0.3",
|
|
67
|
-
"@airalogy/aimd-core": "^2.
|
|
67
|
+
"@airalogy/aimd-core": "^2.8.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "^24.3.0",
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createRenderer,
|
|
10
10
|
} from '../common/processor'
|
|
11
11
|
import { getFinalIndent, parseFieldTag } from '../index'
|
|
12
|
-
import { createStepCardRenderer } from '../vue/vue-renderer'
|
|
12
|
+
import { createCodeBlockRenderer, createStepCardRenderer } from '../vue/vue-renderer'
|
|
13
13
|
|
|
14
14
|
function findVNodeByType(node: any, expectedType: string): any | null {
|
|
15
15
|
if (!node || typeof node !== 'object') {
|
|
@@ -149,6 +149,38 @@ describe('parseAndExtract', () => {
|
|
|
149
149
|
expect(fields.var).toContain('age')
|
|
150
150
|
expect(fields.step.length).toBeGreaterThan(0)
|
|
151
151
|
})
|
|
152
|
+
|
|
153
|
+
it('extracts multiline var tables with object-list defaults', () => {
|
|
154
|
+
const content = `{{var|monitoring_sites: list[MonitoringSite] = [
|
|
155
|
+
{"site_id": "S01", "latitude": 30.0, "longitude": 120.0, "elevation_m": 128.0},
|
|
156
|
+
{"site_id": "S02", "latitude": 30.1, "longitude": 120.1, "elevation_m": 82.0}
|
|
157
|
+
],
|
|
158
|
+
title = "Monitoring sites",
|
|
159
|
+
subvars = [
|
|
160
|
+
var(site_id: str, title = "Site ID"),
|
|
161
|
+
var(latitude: float, title = "Latitude"),
|
|
162
|
+
var(longitude: float, title = "Longitude"),
|
|
163
|
+
var(elevation_m: float, title = "Elevation")
|
|
164
|
+
]
|
|
165
|
+
}}`
|
|
166
|
+
const fields = parseAndExtract(content)
|
|
167
|
+
|
|
168
|
+
expect(fields.var_table[0]).toMatchObject({
|
|
169
|
+
id: 'monitoring_sites',
|
|
170
|
+
title: 'Monitoring sites',
|
|
171
|
+
type_annotation: 'list[MonitoringSite]',
|
|
172
|
+
default: [
|
|
173
|
+
{ site_id: 'S01', latitude: 30, longitude: 120, elevation_m: 128 },
|
|
174
|
+
{ site_id: 'S02', latitude: 30.1, longitude: 120.1, elevation_m: 82 },
|
|
175
|
+
],
|
|
176
|
+
})
|
|
177
|
+
expect(fields.var_table[0]?.subvars.map(subvar => subvar.id)).toEqual([
|
|
178
|
+
'site_id',
|
|
179
|
+
'latitude',
|
|
180
|
+
'longitude',
|
|
181
|
+
'elevation_m',
|
|
182
|
+
])
|
|
183
|
+
})
|
|
152
184
|
})
|
|
153
185
|
|
|
154
186
|
// ---------------------------------------------------------------------------
|
|
@@ -168,6 +200,51 @@ describe('renderToHtmlSync', () => {
|
|
|
168
200
|
expect(html).toContain('temperature')
|
|
169
201
|
})
|
|
170
202
|
|
|
203
|
+
it('renders AIMD var field display metadata', () => {
|
|
204
|
+
const { html, fields } = renderToHtmlSync('{{var|record_date: str, title="Record date", description="ISO date", examples=["2026-05-26", "2026-05-27"]}}')
|
|
205
|
+
expect(fields.var_definitions?.[0]?.title).toBe('Record date')
|
|
206
|
+
expect(fields.var_definitions?.[0]?.description).toBe('ISO date')
|
|
207
|
+
expect(fields.var_definitions?.[0]?.examples).toEqual(['2026-05-26', '2026-05-27'])
|
|
208
|
+
expect(html).toContain('data-aimd-title="Record date"')
|
|
209
|
+
expect(html).toContain('data-aimd-description="ISO date"')
|
|
210
|
+
expect(html).toContain('data-aimd-examples="2026-05-26, 2026-05-27"')
|
|
211
|
+
expect(html).toContain('Record date')
|
|
212
|
+
expect(html).toContain('record_date')
|
|
213
|
+
expect(html).toContain('ISO date')
|
|
214
|
+
expect(html).toContain('aria-label="ISO date')
|
|
215
|
+
expect(html).not.toContain('title="ISO date')
|
|
216
|
+
expect(html).not.toContain('aimd-field__description')
|
|
217
|
+
expect(html).toContain('aimd-field__metadata-popover')
|
|
218
|
+
expect(html).toContain('aimd-field__metadata-examples')
|
|
219
|
+
expect(html).toContain('aimd-field__metadata-example')
|
|
220
|
+
expect(html).toContain('tabindex="0"')
|
|
221
|
+
expect(html).toContain('2026-05-26')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('renders AIMD var_table and column display metadata', () => {
|
|
225
|
+
const { html, fields } = renderToHtmlSync('{{var_table|samples, title="Samples", description="Measured rows", examples=["S-001 row"], subvars=[var(sample_id: str, title="Sample ID", description="Tube identifier", examples=["S-001"])]}}')
|
|
226
|
+
expect(fields.var_table[0]?.title).toBe('Samples')
|
|
227
|
+
expect(fields.var_table[0]?.description).toBe('Measured rows')
|
|
228
|
+
expect(fields.var_table[0]?.examples).toEqual(['S-001 row'])
|
|
229
|
+
expect(fields.var_table[0]?.subvars[0]?.title).toBe('Sample ID')
|
|
230
|
+
expect(fields.var_table[0]?.subvars[0]?.examples).toEqual(['S-001'])
|
|
231
|
+
expect(html).toContain('Samples')
|
|
232
|
+
expect(html).toContain('samples')
|
|
233
|
+
expect(html).toContain('Measured rows')
|
|
234
|
+
expect(html).toContain('S-001 row')
|
|
235
|
+
expect(html).toContain('data-column-id="sample_id"')
|
|
236
|
+
expect(html).toContain('Sample ID')
|
|
237
|
+
expect(html).toContain('Tube identifier')
|
|
238
|
+
expect(html).toContain('aria-label="Measured rows')
|
|
239
|
+
expect(html).toContain('aria-label="Tube identifier')
|
|
240
|
+
expect(html).not.toContain('title="Measured rows')
|
|
241
|
+
expect(html).not.toContain('title="Tube identifier')
|
|
242
|
+
expect(html).not.toContain('aimd-field__description')
|
|
243
|
+
expect(html).toContain('aimd-field__metadata-popover')
|
|
244
|
+
expect(html).toContain('aimd-field__metadata-example')
|
|
245
|
+
expect(html).toContain('S-001')
|
|
246
|
+
})
|
|
247
|
+
|
|
171
248
|
it('returns extracted fields alongside HTML', () => {
|
|
172
249
|
const { fields } = renderToHtmlSync('{{step|wash}} and {{check|verify}}')
|
|
173
250
|
expect(fields.step.length).toBeGreaterThan(0)
|
|
@@ -201,6 +278,31 @@ describe('renderToHtmlSync', () => {
|
|
|
201
278
|
expect(html).toContain('Answer: true')
|
|
202
279
|
})
|
|
203
280
|
|
|
281
|
+
it('extracts true/false option followups', () => {
|
|
282
|
+
const { fields } = renderToHtmlSync(
|
|
283
|
+
[
|
|
284
|
+
'```quiz',
|
|
285
|
+
'id: q_true_false_followups',
|
|
286
|
+
'type: true_false',
|
|
287
|
+
'stem: "Was precipitate observed?"',
|
|
288
|
+
'options:',
|
|
289
|
+
' - key: true',
|
|
290
|
+
' text: "Yes"',
|
|
291
|
+
' followups:',
|
|
292
|
+
' - key: color',
|
|
293
|
+
' type: str',
|
|
294
|
+
' title: Color',
|
|
295
|
+
' - key: false',
|
|
296
|
+
' text: "No"',
|
|
297
|
+
'```',
|
|
298
|
+
].join('\n'),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
expect(fields.quiz[0].options?.[0]?.followups).toEqual([
|
|
302
|
+
{ key: 'color', type: 'str', required: true, title: 'Color' },
|
|
303
|
+
])
|
|
304
|
+
})
|
|
305
|
+
|
|
204
306
|
it('supports host custom element renderers for AIMD nodes', () => {
|
|
205
307
|
const { html } = renderToHtmlSync(
|
|
206
308
|
"{{step|verify, 2, title='Verify Output', subtitle='Cross-check', check=True, result=True}}\n\nStep body content.",
|
|
@@ -303,6 +405,94 @@ describe('renderToHtmlSync', () => {
|
|
|
303
405
|
})
|
|
304
406
|
|
|
305
407
|
describe('renderToVue', () => {
|
|
408
|
+
it('renders code blocks with line numbers and wrapping classes', async () => {
|
|
409
|
+
const { nodes } = await renderToVue(
|
|
410
|
+
'```json\n{\n "model":"qwen3.6-flash","enable_search":true\n}\n```',
|
|
411
|
+
{
|
|
412
|
+
elementRenderers: {
|
|
413
|
+
pre: createCodeBlockRenderer(null, {
|
|
414
|
+
lineNumbers: true,
|
|
415
|
+
wrap: true,
|
|
416
|
+
}),
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
const pre = findVNodeByType(nodes[0], 'pre') as any
|
|
422
|
+
expect(pre).toBeTruthy()
|
|
423
|
+
expect(pre.props.class).toContain('aimd-code-block')
|
|
424
|
+
expect(pre.props.class).toContain('aimd-code-block--line-numbers')
|
|
425
|
+
expect(pre.props.class).toContain('aimd-code-block--wrap')
|
|
426
|
+
expect(pre.props['data-lang']).toBe('json')
|
|
427
|
+
const code = findVNodeByType(pre, 'code') as any
|
|
428
|
+
expect(code.children).toHaveLength(3)
|
|
429
|
+
expect(code.children[1].children[1].props.style['--aimd-code-wrap-indent']).toBe('2ch')
|
|
430
|
+
expect(collectVNodeText(pre)).toContain('1')
|
|
431
|
+
expect(collectVNodeText(pre)).toContain('qwen3.6-flash')
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('keeps blank code lines visible without adding an extra line for the trailing markdown fence newline', async () => {
|
|
435
|
+
const { nodes } = await renderToVue(
|
|
436
|
+
'```json\n{\n\n "model": "qwen3.6-flash"\n}\n```',
|
|
437
|
+
{
|
|
438
|
+
elementRenderers: {
|
|
439
|
+
pre: createCodeBlockRenderer(null, {
|
|
440
|
+
lineNumbers: true,
|
|
441
|
+
wrap: true,
|
|
442
|
+
}),
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
const pre = findVNodeByType(nodes[0], 'pre') as any
|
|
448
|
+
const code = findVNodeByType(pre, 'code') as any
|
|
449
|
+
expect(code.children).toHaveLength(4)
|
|
450
|
+
expect(code.children[1].children[1].children).toBe('\u00a0')
|
|
451
|
+
expect(code.children[2].children[1].props.style['--aimd-code-wrap-indent']).toBe('2ch')
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
it('uses Shiki token output when a highlighter is available', async () => {
|
|
455
|
+
const highlighter = {
|
|
456
|
+
codeToHtml: (code: string) => code,
|
|
457
|
+
codeToTokensBase: () => [
|
|
458
|
+
[
|
|
459
|
+
{ content: ' ', color: '#24292e' },
|
|
460
|
+
{ content: '"model"', color: '#005cc5' },
|
|
461
|
+
],
|
|
462
|
+
],
|
|
463
|
+
}
|
|
464
|
+
const { nodes } = await renderToVue('```json\n "model"\n```', {
|
|
465
|
+
elementRenderers: {
|
|
466
|
+
pre: createCodeBlockRenderer(highlighter, {
|
|
467
|
+
lineNumbers: true,
|
|
468
|
+
wrap: true,
|
|
469
|
+
}),
|
|
470
|
+
},
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
const pre = findVNodeByType(nodes[0], 'pre') as any
|
|
474
|
+
const code = findVNodeByType(pre, 'code') as any
|
|
475
|
+
const tokenSpans = code.children[0].children[1].children
|
|
476
|
+
expect(tokenSpans[0].props.style).toEqual({ color: '#24292e' })
|
|
477
|
+
expect(tokenSpans[1].props.style).toEqual({ color: '#005cc5' })
|
|
478
|
+
expect(tokenSpans[1].children).toBe('"model"')
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('keeps the legacy plain pre fallback when called with a theme string and no highlighter', async () => {
|
|
482
|
+
const { nodes } = await renderToVue('```json\n{"model":"qwen"}\n```', {
|
|
483
|
+
elementRenderers: {
|
|
484
|
+
pre: createCodeBlockRenderer(null, 'github-light'),
|
|
485
|
+
},
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
const pre = findVNodeByType(nodes[0], 'pre') as any
|
|
489
|
+
expect(pre.props.class).toBe('language-json')
|
|
490
|
+
expect(pre.props.class).not.toContain('aimd-code-block')
|
|
491
|
+
const code = findVNodeByType(pre, 'code') as any
|
|
492
|
+
expect(code.props.class).toBe('language-json')
|
|
493
|
+
expect(code.children).toBe('{"model":"qwen"}')
|
|
494
|
+
})
|
|
495
|
+
|
|
306
496
|
it('renders host-ready step cards with grouped body content', async () => {
|
|
307
497
|
const { nodes } = await renderToVue(
|
|
308
498
|
"{{step|verify, 2, title='Verify Output', subtitle='Cross-check', check=True}}\n\nStep body content.",
|
package/src/common/processor.ts
CHANGED
|
@@ -512,6 +512,14 @@ import remarkRehype from "remark-rehype"
|
|
|
512
512
|
import { unified } from "unified"
|
|
513
513
|
|
|
514
514
|
import { protectAimdInlineTemplates, remarkAimd } from "@airalogy/aimd-core/parser"
|
|
515
|
+
import {
|
|
516
|
+
formatAimdExampleValue,
|
|
517
|
+
formatAimdExamples,
|
|
518
|
+
getAimdFieldDescription,
|
|
519
|
+
getAimdFieldDisplayLabel,
|
|
520
|
+
getAimdFieldExamples,
|
|
521
|
+
getAimdFieldTitle,
|
|
522
|
+
} from "@airalogy/aimd-core/utils"
|
|
515
523
|
import {
|
|
516
524
|
createAimdRendererMessages,
|
|
517
525
|
getAimdRendererQuizTypeLabel,
|
|
@@ -790,6 +798,121 @@ function buildScaleBandChildren(quizNode: AimdQuizNode): Array<Element | HastTex
|
|
|
790
798
|
} as Element]
|
|
791
799
|
}
|
|
792
800
|
|
|
801
|
+
function createTextNode(value: string): HastText {
|
|
802
|
+
return { type: "text", value }
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
interface FieldMetadataHelp {
|
|
806
|
+
tooltip: string
|
|
807
|
+
description?: string
|
|
808
|
+
examples: string[]
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function getFieldHelpText(definition: { kwargs?: Record<string, unknown> } | undefined): FieldMetadataHelp {
|
|
812
|
+
const description = getAimdFieldDescription(definition)
|
|
813
|
+
const examples = getAimdFieldExamples(definition)
|
|
814
|
+
.map(formatAimdExampleValue)
|
|
815
|
+
.map(example => example.trim())
|
|
816
|
+
.filter(Boolean)
|
|
817
|
+
const exampleText = examples.length > 0 ? `e.g. ${examples.join(", ")}` : undefined
|
|
818
|
+
const tooltipLines = [description, exampleText].filter((value): value is string => Boolean(value))
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
tooltip: tooltipLines.join("\n"),
|
|
822
|
+
description,
|
|
823
|
+
examples,
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function createFieldMetadataPopover(help: FieldMetadataHelp): Element | null {
|
|
828
|
+
if (!help.description && help.examples.length === 0) {
|
|
829
|
+
return null
|
|
830
|
+
}
|
|
831
|
+
const children: Array<Element | HastText> = []
|
|
832
|
+
if (help.description) {
|
|
833
|
+
children.push({
|
|
834
|
+
type: "element",
|
|
835
|
+
tagName: "span",
|
|
836
|
+
properties: { className: ["aimd-field__metadata-popover-line"] },
|
|
837
|
+
children: [createTextNode(help.description)],
|
|
838
|
+
} as Element)
|
|
839
|
+
}
|
|
840
|
+
if (help.examples.length > 0) {
|
|
841
|
+
children.push({
|
|
842
|
+
type: "element",
|
|
843
|
+
tagName: "span",
|
|
844
|
+
properties: { className: ["aimd-field__metadata-examples"] },
|
|
845
|
+
children: [
|
|
846
|
+
{
|
|
847
|
+
type: "element",
|
|
848
|
+
tagName: "span",
|
|
849
|
+
properties: { className: ["aimd-field__metadata-examples-label"] },
|
|
850
|
+
children: [createTextNode("e.g.")],
|
|
851
|
+
} as Element,
|
|
852
|
+
...help.examples.map((example) => ({
|
|
853
|
+
type: "element",
|
|
854
|
+
tagName: "span",
|
|
855
|
+
properties: { className: ["aimd-field__metadata-example"] },
|
|
856
|
+
children: [createTextNode(example)],
|
|
857
|
+
} as Element)),
|
|
858
|
+
],
|
|
859
|
+
} as Element)
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
type: "element",
|
|
863
|
+
tagName: "span",
|
|
864
|
+
properties: { className: ["aimd-field__metadata-popover"], role: "tooltip" },
|
|
865
|
+
children,
|
|
866
|
+
} as Element
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function createFieldNameElement(id: string, definition: { kwargs?: Record<string, unknown> } | undefined): Element {
|
|
870
|
+
const displayTitle = getAimdFieldDisplayLabel(id, definition)
|
|
871
|
+
const hasCustomTitle = getAimdFieldTitle(definition) !== undefined && displayTitle !== id
|
|
872
|
+
const help = getFieldHelpText(definition)
|
|
873
|
+
const hasHelp = Boolean(help.description) || help.examples.length > 0
|
|
874
|
+
const className = ["aimd-field__name"]
|
|
875
|
+
if (hasCustomTitle || hasHelp) {
|
|
876
|
+
className.push("aimd-field__name--with-metadata")
|
|
877
|
+
}
|
|
878
|
+
if (hasHelp) {
|
|
879
|
+
className.push("aimd-field__metadata-host")
|
|
880
|
+
}
|
|
881
|
+
const children: Array<Element | HastText> = [
|
|
882
|
+
{
|
|
883
|
+
type: "element",
|
|
884
|
+
tagName: "span",
|
|
885
|
+
properties: { className: ["aimd-field__title"] },
|
|
886
|
+
children: [createTextNode(displayTitle)],
|
|
887
|
+
} as Element,
|
|
888
|
+
]
|
|
889
|
+
|
|
890
|
+
if (hasCustomTitle) {
|
|
891
|
+
children.push({
|
|
892
|
+
type: "element",
|
|
893
|
+
tagName: "span",
|
|
894
|
+
properties: { className: ["aimd-field__key"] },
|
|
895
|
+
children: [createTextNode(id)],
|
|
896
|
+
} as Element)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const popover = createFieldMetadataPopover(help)
|
|
900
|
+
if (popover) {
|
|
901
|
+
children.push(popover)
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return {
|
|
905
|
+
type: "element",
|
|
906
|
+
tagName: "span",
|
|
907
|
+
properties: cleanProperties({
|
|
908
|
+
className,
|
|
909
|
+
tabIndex: hasHelp ? 0 : undefined,
|
|
910
|
+
"aria-label": help.tooltip || undefined,
|
|
911
|
+
}),
|
|
912
|
+
children,
|
|
913
|
+
} as Element
|
|
914
|
+
}
|
|
915
|
+
|
|
793
916
|
// ---------------------------------------------------------------------------
|
|
794
917
|
// AIMD handler (remark-rehype custom handler)
|
|
795
918
|
// ---------------------------------------------------------------------------
|
|
@@ -968,23 +1091,18 @@ function createAimdHandler(options: AimdRendererOptions = {}) {
|
|
|
968
1091
|
children: [{ type: "text", value: `[${refs.join(", ")}]` }],
|
|
969
1092
|
} as Element)
|
|
970
1093
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1094
|
+
else if (fieldType === "var") {
|
|
1095
|
+
// Variable: type label + id + optional type annotation
|
|
1096
|
+
const definition = "definition" in node ? node.definition : undefined
|
|
1097
|
+
children.push(
|
|
975
1098
|
{
|
|
976
1099
|
type: "element",
|
|
977
1100
|
tagName: "span",
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
tagName: "span",
|
|
984
|
-
properties: { className: ["aimd-field__name"] },
|
|
985
|
-
children: [{ type: "text", value: id }],
|
|
986
|
-
} as Element,
|
|
987
|
-
)
|
|
1101
|
+
properties: { className: ["aimd-field__scope"] },
|
|
1102
|
+
children: [{ type: "text", value: getAimdRendererScopeLabel("var", messages) }],
|
|
1103
|
+
} as Element,
|
|
1104
|
+
createFieldNameElement(id, definition),
|
|
1105
|
+
)
|
|
988
1106
|
if (definition?.type) {
|
|
989
1107
|
children.push({
|
|
990
1108
|
type: "element",
|
|
@@ -994,11 +1112,13 @@ function createAimdHandler(options: AimdRendererOptions = {}) {
|
|
|
994
1112
|
} as Element)
|
|
995
1113
|
}
|
|
996
1114
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1115
|
+
else if (fieldType === "var_table") {
|
|
1116
|
+
// var_table: render header + table preview
|
|
1117
|
+
const columns = "columns" in node ? (node as any).columns as string[] : []
|
|
1118
|
+
const definition = "definition" in node ? node.definition : undefined
|
|
1119
|
+
const subvarDefs = definition?.subvars
|
|
1120
|
+
children.push(
|
|
1121
|
+
{
|
|
1002
1122
|
type: "element",
|
|
1003
1123
|
tagName: "div",
|
|
1004
1124
|
properties: { className: ["aimd-field__header"] },
|
|
@@ -1006,17 +1126,12 @@ function createAimdHandler(options: AimdRendererOptions = {}) {
|
|
|
1006
1126
|
{
|
|
1007
1127
|
type: "element",
|
|
1008
1128
|
tagName: "span",
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
properties: { className: ["aimd-field__name"] },
|
|
1016
|
-
children: [{ type: "text", value: id }],
|
|
1017
|
-
} as Element,
|
|
1018
|
-
],
|
|
1019
|
-
} as Element,
|
|
1129
|
+
properties: { className: ["aimd-field__scope"] },
|
|
1130
|
+
children: [{ type: "text", value: getAimdRendererScopeLabel("var_table", messages) }],
|
|
1131
|
+
} as Element,
|
|
1132
|
+
createFieldNameElement(id, definition),
|
|
1133
|
+
],
|
|
1134
|
+
} as Element,
|
|
1020
1135
|
)
|
|
1021
1136
|
if (columns && columns.length > 0) {
|
|
1022
1137
|
children.push({
|
|
@@ -1033,12 +1148,14 @@ function createAimdHandler(options: AimdRendererOptions = {}) {
|
|
|
1033
1148
|
type: "element",
|
|
1034
1149
|
tagName: "tr",
|
|
1035
1150
|
properties: {},
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1151
|
+
children: columns.map(col => ({
|
|
1152
|
+
type: "element",
|
|
1153
|
+
tagName: "th",
|
|
1154
|
+
properties: cleanProperties({
|
|
1155
|
+
"data-column-id": col,
|
|
1156
|
+
}),
|
|
1157
|
+
children: [createFieldNameElement(col, subvarDefs?.[col])],
|
|
1158
|
+
} as Element)),
|
|
1042
1159
|
} as Element,
|
|
1043
1160
|
],
|
|
1044
1161
|
} as Element,
|
|
@@ -1242,14 +1359,29 @@ function createAimdHandler(options: AimdRendererOptions = {}) {
|
|
|
1242
1359
|
}
|
|
1243
1360
|
|
|
1244
1361
|
// Build properties
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1362
|
+
const properties: Properties = {
|
|
1363
|
+
"className": [baseClass, modifierClass],
|
|
1364
|
+
"data-aimd-type": node.fieldType,
|
|
1248
1365
|
"data-aimd-id": node.id,
|
|
1249
1366
|
"data-aimd-scope": node.scope,
|
|
1250
1367
|
"data-aimd-raw": node.raw,
|
|
1251
|
-
|
|
1252
|
-
|
|
1368
|
+
"data-aimd-json": aimdJson,
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
if ((node.fieldType === "var" || node.fieldType === "var_table") && "definition" in node) {
|
|
1372
|
+
const title = getAimdFieldTitle(node.definition)
|
|
1373
|
+
const description = getAimdFieldDescription(node.definition)
|
|
1374
|
+
const examples = formatAimdExamples(getAimdFieldExamples(node.definition))
|
|
1375
|
+
if (title) {
|
|
1376
|
+
properties["data-aimd-title"] = title
|
|
1377
|
+
}
|
|
1378
|
+
if (description) {
|
|
1379
|
+
properties["data-aimd-description"] = description
|
|
1380
|
+
}
|
|
1381
|
+
if (examples) {
|
|
1382
|
+
properties["data-aimd-examples"] = examples
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1253
1385
|
|
|
1254
1386
|
// Add reference href
|
|
1255
1387
|
if (isRef) {
|