@airalogy/aimd-renderer 2.7.0 → 2.8.1
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/dist/aimd-renderer.css +1 -1
- package/dist/common/assetUrls.d.ts +8 -0
- package/dist/common/assetUrls.d.ts.map +1 -0
- package/dist/common/citationNumbering.d.ts +11 -0
- package/dist/common/citationNumbering.d.ts.map +1 -0
- package/dist/common/processor.d.ts +2 -0
- package/dist/common/processor.d.ts.map +1 -1
- package/dist/html/index.d.ts +1 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/locales.d.ts +3 -0
- package/dist/locales.d.ts.map +1 -1
- package/dist/{processor-BOCQYqXE.js → processor-C_zN3-hL.js} +3128 -2534
- package/dist/{readonly-record-renderer-CkzY7UvT.js → readonly-record-renderer-G9xOkOFe.js} +1 -1
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/vue-renderer.d.ts +7 -1
- package/dist/vue/vue-renderer.d.ts.map +1 -1
- package/dist/vue.js +2 -2
- package/package.json +2 -2
- package/src/__tests__/renderer.test.ts +307 -0
- package/src/common/assetUrls.ts +22 -0
- package/src/common/citationNumbering.ts +265 -0
- package/src/common/processor.ts +193 -23
- package/src/html/index.ts +4 -0
- package/src/index.ts +5 -0
- package/src/locales.ts +9 -0
- package/src/styles/katex.css +607 -0
- package/src/vue/index.ts +4 -0
- package/src/vue/vue-renderer.ts +183 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as T, t as D } from "./processor-
|
|
1
|
+
import { s as T, t as D } from "./processor-C_zN3-hL.js";
|
|
2
2
|
import { defineComponent as B, shallowRef as G, ref as $, watch as K, h as d } from "vue";
|
|
3
3
|
const F = ["var", "var_table", "step", "check", "quiz"], Y = {
|
|
4
4
|
file: "FILE",
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export { renderToVue, createRenderer, defaultRenderer, } from '../common/process
|
|
|
7
7
|
export { createAimdRendererMessages, DEFAULT_AIMD_RENDERER_LOCALE, resolveAimdRendererLocale, } from '../locales';
|
|
8
8
|
export type { RenderContext, RenderMode, ProcessorOptions, } from '@airalogy/aimd-core/types';
|
|
9
9
|
export type { AimdAssignerVisibility, AimdRendererOptions, RenderResult } from '../common/processor';
|
|
10
|
+
export type { AimdAssetUrlResolver, AimdAssetUrlResolveContext, } from '../common/assetUrls';
|
|
10
11
|
export type { AimdRendererI18nOptions, AimdRendererLocale, AimdRendererMessages, AimdRendererMessagesInput, } from '../locales';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
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,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,yBAAyB,EACzB,iCAAiC,EACjC,oCAAoC,EACpC,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,2BAA2B,EAChC,KAAK,mCAAmC,EACxC,KAAK,gCAAgC,EACrC,KAAK,gCAAgC,GACtC,MAAM,4BAA4B,CAAA;AAEnC,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,yBAAyB,EACzB,iCAAiC,EACjC,oCAAoC,EACpC,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,2BAA2B,EAChC,KAAK,mCAAmC,EACxC,KAAK,gCAAgC,EACrC,KAAK,gCAAgC,GACtC,MAAM,4BAA4B,CAAA;AAEnC,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,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,YAAY,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Element, Root as HastRoot, RootContent } from "hast";
|
|
2
2
|
import type { Component, VNode, VNodeChild } from "vue";
|
|
3
3
|
import type { AimdNode, RenderContext } from "@airalogy/aimd-core/types";
|
|
4
|
+
import type { AimdAssetUrlResolver } from "../common/assetUrls";
|
|
4
5
|
import type { AimdRendererI18nOptions, AimdRendererLocale, AimdRendererMessages } from "../locales";
|
|
5
6
|
/**
|
|
6
7
|
* Extended Element data type
|
|
@@ -20,6 +21,7 @@ export type ElementRenderer = (node: Element, children: VNodeChild[], ctx: AimdR
|
|
|
20
21
|
export interface AimdRendererContext extends RenderContext {
|
|
21
22
|
locale: AimdRendererLocale;
|
|
22
23
|
messages: AimdRendererMessages;
|
|
24
|
+
resolveAssetUrl?: AimdAssetUrlResolver;
|
|
23
25
|
}
|
|
24
26
|
export interface AimdStepCardRendererOptions {
|
|
25
27
|
showScopeLabel?: boolean;
|
|
@@ -50,7 +52,11 @@ export interface VueRendererOptions {
|
|
|
50
52
|
/**
|
|
51
53
|
* Render context
|
|
52
54
|
*/
|
|
53
|
-
context?: RenderContext & Partial<Pick<AimdRendererContext, "locale" | "messages">>;
|
|
55
|
+
context?: RenderContext & Partial<Pick<AimdRendererContext, "locale" | "messages" | "resolveAssetUrl">>;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve protocol-local figure assets to displayable URLs.
|
|
58
|
+
*/
|
|
59
|
+
resolveAssetUrl?: AimdAssetUrlResolver;
|
|
54
60
|
/**
|
|
55
61
|
* Custom AIMD component renderers
|
|
56
62
|
* Override default renderers or add new ones
|
|
@@ -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,
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { D as a, c as d, f as s, g as R, h as n, i as o, j as t, k as c, l, m, n as E, o as i, q as A, s as C, e as D } from "./processor-
|
|
2
|
-
import { A as h, c as u, a as T, b as V, n as f, r as g } from "./readonly-record-renderer-
|
|
1
|
+
import { D as a, c as d, f as s, g as R, h as n, i as o, j as t, k as c, l, m, n as E, o as i, q as A, s as C, e as D } from "./processor-C_zN3-hL.js";
|
|
2
|
+
import { A as h, c as u, a as T, b as V, n as f, r as g } from "./readonly-record-renderer-G9xOkOFe.js";
|
|
3
3
|
export {
|
|
4
4
|
h as AIMD_RECORD_RENDER_SCOPES,
|
|
5
5
|
a as DEFAULT_AIMD_RENDERER_LOCALE,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airalogy/aimd-renderer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.8.1",
|
|
5
5
|
"description": "AIMD (Airalogy Markdown) rendering engines for HTML and Vue",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -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.10.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "^24.3.0",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
1
2
|
import { mount } from '@vue/test-utils'
|
|
2
3
|
import { h } from 'vue'
|
|
3
4
|
import { describe, expect, it, vi } from 'vitest'
|
|
@@ -18,6 +19,11 @@ import {
|
|
|
18
19
|
} from '../vue/readonly-record-renderer'
|
|
19
20
|
import { createCodeBlockRenderer, createStepCardRenderer } from '../vue/vue-renderer'
|
|
20
21
|
|
|
22
|
+
const rendererStylesPath = existsSync('src/styles/katex.css')
|
|
23
|
+
? 'src/styles/katex.css'
|
|
24
|
+
: 'packages/npm/aimd-renderer/src/styles/katex.css'
|
|
25
|
+
const rendererStyles = readFileSync(rendererStylesPath, 'utf8')
|
|
26
|
+
|
|
21
27
|
function findVNodeByType(node: any, expectedType: string): any | null {
|
|
22
28
|
if (!node || typeof node !== 'object') {
|
|
23
29
|
return null
|
|
@@ -43,6 +49,37 @@ function findVNodeByType(node: any, expectedType: string): any | null {
|
|
|
43
49
|
return null
|
|
44
50
|
}
|
|
45
51
|
|
|
52
|
+
function findVNodeByClass(node: any, expectedClass: string): any | null {
|
|
53
|
+
if (!node || typeof node !== 'object') {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const className = node.props?.class
|
|
58
|
+
const classes = Array.isArray(className)
|
|
59
|
+
? className
|
|
60
|
+
: typeof className === 'string'
|
|
61
|
+
? className.split(/\s+/)
|
|
62
|
+
: []
|
|
63
|
+
if (classes.includes(expectedClass)) {
|
|
64
|
+
return node
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const children = Array.isArray(node.children)
|
|
68
|
+
? node.children
|
|
69
|
+
: Array.isArray(node.component?.subTree?.children)
|
|
70
|
+
? node.component.subTree.children
|
|
71
|
+
: []
|
|
72
|
+
|
|
73
|
+
for (const child of children) {
|
|
74
|
+
const match = findVNodeByClass(child, expectedClass)
|
|
75
|
+
if (match) {
|
|
76
|
+
return match
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
46
83
|
function collectVNodeText(node: any): string {
|
|
47
84
|
if (node == null) {
|
|
48
85
|
return ''
|
|
@@ -237,6 +274,162 @@ describe('renderToHtmlSync', () => {
|
|
|
237
274
|
expect(html).toContain('temperature')
|
|
238
275
|
})
|
|
239
276
|
|
|
277
|
+
it('resolves figure asset URLs during HTML rendering', () => {
|
|
278
|
+
const seenContexts: Array<{ src: string, kind: string, id?: string, title?: string }> = []
|
|
279
|
+
const { html } = renderToHtmlSync([
|
|
280
|
+
'```fig',
|
|
281
|
+
'id: workflow_diagram',
|
|
282
|
+
'src: files/workflow-diagram.svg',
|
|
283
|
+
'title: Workflow Diagram',
|
|
284
|
+
'```',
|
|
285
|
+
].join('\n'), {
|
|
286
|
+
resolveAssetUrl: (src, context) => {
|
|
287
|
+
seenContexts.push({ src, ...context })
|
|
288
|
+
return `/assets/${src.split('/').pop()}`
|
|
289
|
+
},
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
expect(seenContexts).toEqual([{
|
|
293
|
+
src: 'files/workflow-diagram.svg',
|
|
294
|
+
kind: 'fig',
|
|
295
|
+
id: 'workflow_diagram',
|
|
296
|
+
title: 'Workflow Diagram',
|
|
297
|
+
}])
|
|
298
|
+
expect(html).toContain('src="/assets/workflow-diagram.svg"')
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('renders internal references without route-breaking bare hash hrefs', () => {
|
|
302
|
+
const { html } = renderToHtmlSync([
|
|
303
|
+
'As shown in {{ref_fig|workflow_diagram}}, follow {{ref_step|prepare_sample}}.',
|
|
304
|
+
'',
|
|
305
|
+
'```fig',
|
|
306
|
+
'id: workflow_diagram',
|
|
307
|
+
'src: files/workflow-diagram.svg',
|
|
308
|
+
'title: Workflow Diagram',
|
|
309
|
+
'```',
|
|
310
|
+
'',
|
|
311
|
+
'{{step|prepare_sample}}',
|
|
312
|
+
].join('\n'))
|
|
313
|
+
|
|
314
|
+
expect(html).not.toContain('href="#fig-workflow_diagram"')
|
|
315
|
+
expect(html).not.toContain('href="#step-prepare_sample"')
|
|
316
|
+
expect(html).toContain('<span class="aimd-ref aimd-ref--fig"')
|
|
317
|
+
expect(html).toContain('data-aimd-ref-target="workflow_diagram"')
|
|
318
|
+
expect(html).toContain('data-aimd-ref-kind="fig"')
|
|
319
|
+
expect(html).toContain('<span class="aimd-ref aimd-ref--step"')
|
|
320
|
+
expect(html).toContain('data-aimd-ref-target="prepare_sample"')
|
|
321
|
+
expect(html).toContain('data-aimd-ref-kind="step"')
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('styles rendered figures as attached image-caption blocks', () => {
|
|
325
|
+
expect(rendererStyles).toMatch(/\.aimd-figure \{[\s\S]*?width: fit-content;/)
|
|
326
|
+
expect(rendererStyles).toMatch(/\.aimd-figure \{[\s\S]*?overflow: hidden;/)
|
|
327
|
+
expect(rendererStyles).not.toContain('box-shadow: 0 10px 28px')
|
|
328
|
+
expect(rendererStyles).toMatch(/\.aimd-figure__caption \{[\s\S]*?border-top: 1px solid #d8e2ef;/)
|
|
329
|
+
expect(rendererStyles).toMatch(/\.aimd-figure__legend \{[\s\S]*?margin: 4px 0 0;/)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('styles AIMD fields without depending on recorder styles', () => {
|
|
333
|
+
expect(rendererStyles).toMatch(/\.aimd-field \{[\s\S]*?display: inline-flex;/)
|
|
334
|
+
expect(rendererStyles).toMatch(/\.aimd-field \{[\s\S]*?max-width: 100%;/)
|
|
335
|
+
expect(rendererStyles).toMatch(/\.aimd-field \{[\s\S]*?flex-wrap: wrap;/)
|
|
336
|
+
expect(rendererStyles).toMatch(/\.aimd-field--var \{[\s\S]*?background-color: var\(--aimd-var-bg\);/)
|
|
337
|
+
expect(rendererStyles).toMatch(/\.aimd-field__name--with-metadata \{[\s\S]*?flex-direction: column;/)
|
|
338
|
+
expect(rendererStyles).toMatch(/\.aimd-field__title \{[\s\S]*?overflow-wrap: anywhere;/)
|
|
339
|
+
expect(rendererStyles).toMatch(/\.aimd-field--var-table \{[\s\S]*?display: block;/)
|
|
340
|
+
expect(rendererStyles).toMatch(/\.aimd-field--var-table \{[\s\S]*?width: 100%;/)
|
|
341
|
+
expect(rendererStyles).toMatch(/\.aimd-field--var-table \.aimd-field__table-preview \{[\s\S]*?table-layout: fixed;/)
|
|
342
|
+
expect(rendererStyles).toMatch(/\.aimd-field--quiz \{[\s\S]*?max-width: 860px;/)
|
|
343
|
+
expect(rendererStyles).toMatch(/\.aimd-field--check \{[\s\S]*?display: inline-flex;/)
|
|
344
|
+
expect(rendererStyles).toMatch(/\.aimd-refs \{[\s\S]*?border-top: 1px solid #d8dee8;/)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('styles citation popovers as selectable hoverable content', () => {
|
|
348
|
+
expect(rendererStyles).toContain('.aimd-cite__popover')
|
|
349
|
+
expect(rendererStyles).not.toContain('.aimd-cite__ref::after')
|
|
350
|
+
expect(rendererStyles).toMatch(/\.aimd-cite__popover \{[\s\S]*?pointer-events: none;/)
|
|
351
|
+
expect(rendererStyles).toMatch(/\.aimd-cite__popover \{[\s\S]*?user-select: text;/)
|
|
352
|
+
expect(rendererStyles).toMatch(/\.aimd-cite__popover::before \{[\s\S]*?height: 10px;/)
|
|
353
|
+
expect(rendererStyles).toMatch(/\.aimd-cite__ref:hover \.aimd-cite__popover,[\s\S]*?pointer-events: auto;/)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('styles internal references as focusable route-safe targets', () => {
|
|
357
|
+
expect(rendererStyles).toMatch(/\.aimd-ref\[data-aimd-ref-target\] \{[\s\S]*?cursor: pointer;/)
|
|
358
|
+
expect(rendererStyles).toMatch(/\.aimd-ref\[data-aimd-ref-target\]:focus-visible \{[\s\S]*?outline: 2px solid rgba\(25, 118, 210, 0\.36\);/)
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
it('renders citation markers with reference tooltips and refs blocks', () => {
|
|
362
|
+
const content = [
|
|
363
|
+
'Cite the method {{cite|yang2025airalogyaiempowereduniversaldata, doe2024protocol}}.',
|
|
364
|
+
'',
|
|
365
|
+
'```refs',
|
|
366
|
+
'@misc{yang2025airalogyaiempowereduniversaldata,',
|
|
367
|
+
' title={Airalogy: AI-empowered universal data digitization for research automation},',
|
|
368
|
+
' author={Zijie Yang and Qiji Zhou and Fang Guo and Sijie Zhang and Yexun Xi and Jinglei Nie and Yudian Zhu and Liping Huang and Chou Wu and Yonghe Xia and Xiaoyu Ma and Yingming Pu and Panzhong Lu and Junshu Pan and Mingtao Chen and Tiannan Guo and Yanmei Dou and Hongyu Chen and Anping Zeng and Jiaxing Huang and Tian Xu and Yue Zhang},',
|
|
369
|
+
' year={2025},',
|
|
370
|
+
' eprint={2506.18586},',
|
|
371
|
+
' archivePrefix={arXiv},',
|
|
372
|
+
' primaryClass={cs.AI},',
|
|
373
|
+
' url={https://arxiv.org/abs/2506.18586},',
|
|
374
|
+
'}',
|
|
375
|
+
'',
|
|
376
|
+
'@misc{doe2024protocol,',
|
|
377
|
+
' title = "Protocol Notes",',
|
|
378
|
+
' author = "Doe, Jane",',
|
|
379
|
+
' year = "2024",',
|
|
380
|
+
' url = "https://example.com/protocol"',
|
|
381
|
+
'}',
|
|
382
|
+
'```',
|
|
383
|
+
].join('\n')
|
|
384
|
+
|
|
385
|
+
const { html, fields } = renderToHtmlSync(content)
|
|
386
|
+
|
|
387
|
+
expect(fields.cite).toEqual(['yang2025airalogyaiempowereduniversaldata', 'doe2024protocol'])
|
|
388
|
+
expect(fields.refs?.[0]).toMatchObject({
|
|
389
|
+
id: 'yang2025airalogyaiempowereduniversaldata',
|
|
390
|
+
entry_type: 'misc',
|
|
391
|
+
title: 'Airalogy: AI-empowered universal data digitization for research automation',
|
|
392
|
+
url: 'https://arxiv.org/abs/2506.18586',
|
|
393
|
+
})
|
|
394
|
+
expect(fields.refs?.[1]).toMatchObject({
|
|
395
|
+
id: 'doe2024protocol',
|
|
396
|
+
url: 'https://example.com/protocol',
|
|
397
|
+
})
|
|
398
|
+
expect(html).not.toContain('href="#ref-yang2025airalogyaiempowereduniversaldata"')
|
|
399
|
+
expect(html).not.toContain('href="#ref-doe2024protocol"')
|
|
400
|
+
expect(html).toContain('role="doc-noteref"')
|
|
401
|
+
expect(html).toContain('data-aimd-ref-id="yang2025airalogyaiempowereduniversaldata"')
|
|
402
|
+
expect(html).toContain('data-aimd-ref-summary="Zijie Yang')
|
|
403
|
+
expect(html).toContain('data-aimd-citation-labels="1,2"')
|
|
404
|
+
expect(html).toContain('<span class="aimd-cite__label">1</span>')
|
|
405
|
+
expect(html).toContain('<span class="aimd-cite__label">2</span>')
|
|
406
|
+
expect(html).toContain('<span class="aimd-cite__popover" role="tooltip">Zijie Yang')
|
|
407
|
+
expect(html).not.toContain('<span class="aimd-cite__label">yang2025airalogyaiempowereduniversaldata</span>')
|
|
408
|
+
expect(html).toContain('<section class="aimd-refs"')
|
|
409
|
+
expect(html).toContain('id="ref-yang2025airalogyaiempowereduniversaldata"')
|
|
410
|
+
expect(html).toContain('Airalogy: AI-empowered universal data digitization for research automation')
|
|
411
|
+
expect(html).toContain('href="https://arxiv.org/abs/2506.18586"')
|
|
412
|
+
expect(html).toContain('href="https://example.com/protocol"')
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('moves refs blocks to the end of the rendered document', () => {
|
|
416
|
+
const content = [
|
|
417
|
+
'```refs',
|
|
418
|
+
'@misc{doe2024protocol,',
|
|
419
|
+
' title = "Protocol Notes",',
|
|
420
|
+
' author = "Doe, Jane",',
|
|
421
|
+
' year = "2024"',
|
|
422
|
+
'}',
|
|
423
|
+
'```',
|
|
424
|
+
'',
|
|
425
|
+
'The body continues after the refs declaration.',
|
|
426
|
+
].join('\n')
|
|
427
|
+
|
|
428
|
+
const { html } = renderToHtmlSync(content)
|
|
429
|
+
|
|
430
|
+
expect(html.indexOf('<p>The body continues after the refs declaration.</p>')).toBeLessThan(html.indexOf('<section class="aimd-refs"'))
|
|
431
|
+
})
|
|
432
|
+
|
|
240
433
|
it('renders AIMD var field display metadata', () => {
|
|
241
434
|
const { html, fields } = renderToHtmlSync('{{var|record_date: str, title="Record date", description="ISO date", examples=["2026-05-26", "2026-05-27"]}}')
|
|
242
435
|
expect(fields.var_definitions?.[0]?.title).toBe('Record date')
|
|
@@ -442,6 +635,120 @@ describe('renderToHtmlSync', () => {
|
|
|
442
635
|
})
|
|
443
636
|
|
|
444
637
|
describe('renderToVue', () => {
|
|
638
|
+
it('renders references as Vue nodes', async () => {
|
|
639
|
+
const { nodes } = await renderToVue(
|
|
640
|
+
[
|
|
641
|
+
'Cite {{cite|yang2025airalogyaiempowereduniversaldata}}.',
|
|
642
|
+
'',
|
|
643
|
+
'```refs',
|
|
644
|
+
'@misc{yang2025airalogyaiempowereduniversaldata,',
|
|
645
|
+
' title={Airalogy: AI-empowered universal data digitization for research automation},',
|
|
646
|
+
' author={Zijie Yang and Qiji Zhou and Fang Guo and Sijie Zhang and Yexun Xi and Jinglei Nie and Yudian Zhu and Liping Huang and Chou Wu and Yonghe Xia and Xiaoyu Ma and Yingming Pu and Panzhong Lu and Junshu Pan and Mingtao Chen and Tiannan Guo and Yanmei Dou and Hongyu Chen and Anping Zeng and Jiaxing Huang and Tian Xu and Yue Zhang},',
|
|
647
|
+
' year={2025},',
|
|
648
|
+
' eprint={2506.18586},',
|
|
649
|
+
' archivePrefix={arXiv},',
|
|
650
|
+
' primaryClass={cs.AI},',
|
|
651
|
+
' url={https://arxiv.org/abs/2506.18586},',
|
|
652
|
+
'}',
|
|
653
|
+
'```',
|
|
654
|
+
].join('\n'),
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
const refsNode = nodes
|
|
658
|
+
.map(node => findVNodeByType(node, 'section'))
|
|
659
|
+
.find(Boolean) as any
|
|
660
|
+
const citeNode = nodes
|
|
661
|
+
.map(node => findVNodeByClass(node, 'aimd-cite'))
|
|
662
|
+
.find(Boolean) as any
|
|
663
|
+
|
|
664
|
+
expect(refsNode).toBeTruthy()
|
|
665
|
+
expect(refsNode.props.class).toBe('aimd-refs')
|
|
666
|
+
expect(refsNode.props.id).toBe('refs')
|
|
667
|
+
expect(citeNode).toBeTruthy()
|
|
668
|
+
const citeRefNode = findVNodeByClass(citeNode, 'aimd-cite__ref') as any
|
|
669
|
+
const citeLabelNode = findVNodeByClass(citeNode, 'aimd-cite__label') as any
|
|
670
|
+
const citePopoverNode = findVNodeByClass(citeNode, 'aimd-cite__popover') as any
|
|
671
|
+
expect(citeRefNode.type).toBe('span')
|
|
672
|
+
expect(citeRefNode.props.href).toBeUndefined()
|
|
673
|
+
expect(citeRefNode.props['data-aimd-ref-summary']).toContain('Airalogy: AI-empowered universal data digitization for research automation')
|
|
674
|
+
expect(collectVNodeText(citeLabelNode).trim()).toBe('1')
|
|
675
|
+
expect(citePopoverNode.type).toBe('span')
|
|
676
|
+
expect(citePopoverNode.props.role).toBe('tooltip')
|
|
677
|
+
expect(collectVNodeText(citePopoverNode)).toContain('Airalogy: AI-empowered universal data digitization for research automation')
|
|
678
|
+
expect(collectVNodeText(nodes)).toContain('References')
|
|
679
|
+
expect(collectVNodeText(nodes)).toContain('Airalogy: AI-empowered universal data digitization for research automation')
|
|
680
|
+
expect(collectVNodeText(citeNode)).not.toContain('yang2025airalogyaiempowereduniversaldata')
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
it('resolves figure asset URLs during Vue rendering', async () => {
|
|
684
|
+
const { nodes } = await renderToVue([
|
|
685
|
+
'```fig',
|
|
686
|
+
'id: workflow_diagram',
|
|
687
|
+
'src: files/workflow-diagram.svg',
|
|
688
|
+
'title: Workflow Diagram',
|
|
689
|
+
'```',
|
|
690
|
+
].join('\n'), {
|
|
691
|
+
resolveAssetUrl: (src, context) => {
|
|
692
|
+
expect(context).toMatchObject({
|
|
693
|
+
kind: 'fig',
|
|
694
|
+
id: 'workflow_diagram',
|
|
695
|
+
title: 'Workflow Diagram',
|
|
696
|
+
})
|
|
697
|
+
return `/assets/${src.split('/').pop()}`
|
|
698
|
+
},
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
const img = findVNodeByType({ children: nodes }, 'img') as any
|
|
702
|
+
expect(img).toBeTruthy()
|
|
703
|
+
expect(img.props.src).toBe('/assets/workflow-diagram.svg')
|
|
704
|
+
})
|
|
705
|
+
|
|
706
|
+
it('renders Vue figure references as route-safe internal markers', async () => {
|
|
707
|
+
const { nodes } = await renderToVue([
|
|
708
|
+
'As shown in {{ref_fig|workflow_diagram}}.',
|
|
709
|
+
'',
|
|
710
|
+
'```fig',
|
|
711
|
+
'id: workflow_diagram',
|
|
712
|
+
'src: files/workflow-diagram.svg',
|
|
713
|
+
'title: Workflow Diagram',
|
|
714
|
+
'```',
|
|
715
|
+
].join('\n'))
|
|
716
|
+
|
|
717
|
+
const figRefNode = findVNodeByClass({ children: nodes }, 'aimd-ref--fig') as any
|
|
718
|
+
expect(figRefNode).toBeTruthy()
|
|
719
|
+
expect(figRefNode.type).toBe('span')
|
|
720
|
+
expect(figRefNode.props.href).toBeUndefined()
|
|
721
|
+
expect(figRefNode.props['data-aimd-ref-target']).toBe('workflow_diagram')
|
|
722
|
+
expect(figRefNode.props['data-aimd-ref-kind']).toBe('fig')
|
|
723
|
+
expect(figRefNode.props.tabindex).toBe(0)
|
|
724
|
+
})
|
|
725
|
+
|
|
726
|
+
it('moves refs Vue nodes to the end of the rendered document', async () => {
|
|
727
|
+
const { nodes } = await renderToVue(
|
|
728
|
+
[
|
|
729
|
+
'```refs',
|
|
730
|
+
'@misc{doe2024protocol,',
|
|
731
|
+
' title = "Protocol Notes",',
|
|
732
|
+
' author = "Doe, Jane",',
|
|
733
|
+
' year = "2024"',
|
|
734
|
+
'}',
|
|
735
|
+
'```',
|
|
736
|
+
'',
|
|
737
|
+
'The body continues after the refs declaration.',
|
|
738
|
+
].join('\n'),
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
const rootChildren = Array.isArray((nodes[0] as any)?.children)
|
|
742
|
+
? (nodes[0] as any).children
|
|
743
|
+
: nodes
|
|
744
|
+
const refsIndex = rootChildren.findIndex((node: any) => Boolean(findVNodeByClass(node, 'aimd-refs')))
|
|
745
|
+
const bodyIndex = rootChildren.findIndex((node: any) => collectVNodeText(node).includes('The body continues after the refs declaration.'))
|
|
746
|
+
|
|
747
|
+
expect(bodyIndex).toBeGreaterThanOrEqual(0)
|
|
748
|
+
expect(refsIndex).toBeGreaterThanOrEqual(0)
|
|
749
|
+
expect(bodyIndex).toBeLessThan(refsIndex)
|
|
750
|
+
})
|
|
751
|
+
|
|
445
752
|
it('renders code blocks with line numbers and wrapping classes', async () => {
|
|
446
753
|
const { nodes } = await renderToVue(
|
|
447
754
|
'```json\n{\n "model":"qwen3.6-flash","enable_search":true\n}\n```',
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AimdAssetUrlResolveContext {
|
|
2
|
+
kind: "fig"
|
|
3
|
+
id?: string
|
|
4
|
+
title?: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type AimdAssetUrlResolver = (
|
|
8
|
+
src: string,
|
|
9
|
+
context: AimdAssetUrlResolveContext,
|
|
10
|
+
) => string | null | undefined
|
|
11
|
+
|
|
12
|
+
export function resolveAimdAssetUrl(
|
|
13
|
+
src: string | undefined,
|
|
14
|
+
resolver: AimdAssetUrlResolver | undefined,
|
|
15
|
+
context: AimdAssetUrlResolveContext,
|
|
16
|
+
): string | undefined {
|
|
17
|
+
if (!src || !resolver) {
|
|
18
|
+
return src
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return resolver(src, context) || src
|
|
22
|
+
}
|