@airalogy/aimd-renderer 2.6.0 → 2.8.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 +38 -0
- package/README.zh-CN.md +38 -0
- 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/criticMarkup.d.ts +10 -0
- package/dist/common/criticMarkup.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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -73
- package/dist/locales.d.ts +3 -0
- package/dist/locales.d.ts.map +1 -1
- package/dist/{processor-CHbNEcN8.js → processor-C_zN3-hL.js} +3454 -2534
- package/dist/readonly-record-renderer-G9xOkOFe.js +711 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/readonly-record-renderer.d.ts +42 -0
- package/dist/vue/readonly-record-renderer.d.ts.map +1 -0
- package/dist/vue/vue-renderer.d.ts +7 -1
- package/dist/vue/vue-renderer.d.ts.map +1 -1
- package/dist/vue.js +20 -13
- package/package.json +2 -2
- package/src/__tests__/renderer.test.ts +536 -1
- package/src/common/assetUrls.ts +22 -0
- package/src/common/citationNumbering.ts +265 -0
- package/src/common/criticMarkup.ts +97 -0
- package/src/common/processor.ts +206 -25
- package/src/html/index.ts +4 -0
- package/src/index.ts +23 -0
- package/src/locales.ts +9 -0
- package/src/styles/katex.css +213 -0
- package/src/vue/index.ts +22 -0
- package/src/vue/readonly-record-renderer.ts +747 -0
- package/src/vue/vue-renderer.ts +183 -9
package/src/vue/vue-renderer.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Element, Root as HastRoot, Text as HastText, RootContent } from "hast"
|
|
2
2
|
import type { Component, VNode, VNodeChild } from "vue"
|
|
3
|
-
import type { AimdNode, AimdQuizNode, AimdStepNode, RenderContext } from "@airalogy/aimd-core/types"
|
|
3
|
+
import type { AimdNode, AimdQuizNode, AimdReferenceEntry, AimdStepNode, RenderContext } from "@airalogy/aimd-core/types"
|
|
4
|
+
import type { AimdCitationReferenceDisplay } from "../common/citationNumbering"
|
|
5
|
+
import type { AimdAssetUrlResolver } from "../common/assetUrls"
|
|
4
6
|
import {
|
|
5
7
|
formatAimdExampleValue,
|
|
6
8
|
getAimdFieldDescription,
|
|
@@ -10,6 +12,8 @@ import {
|
|
|
10
12
|
} from "@airalogy/aimd-core/utils"
|
|
11
13
|
import { Fragment, h } from "vue"
|
|
12
14
|
import type { AimdRendererI18nOptions, AimdRendererLocale, AimdRendererMessages } from "../locales"
|
|
15
|
+
import { resolveAimdAssetUrl } from "../common/assetUrls"
|
|
16
|
+
import { formatCitationReferenceSummary, moveReferenceSectionsToEnd } from "../common/citationNumbering"
|
|
13
17
|
import { resolveQuizPreviewOptions, type ResolvedQuizPreviewOptions } from "../common/quiz-preview"
|
|
14
18
|
import {
|
|
15
19
|
createAimdRendererMessages,
|
|
@@ -48,6 +52,7 @@ export type ElementRenderer = (
|
|
|
48
52
|
export interface AimdRendererContext extends RenderContext {
|
|
49
53
|
locale: AimdRendererLocale
|
|
50
54
|
messages: AimdRendererMessages
|
|
55
|
+
resolveAssetUrl?: AimdAssetUrlResolver
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export interface AimdStepCardRendererOptions {
|
|
@@ -159,6 +164,66 @@ function buildScaleBandChildren(quizNode: AimdQuizNode): VNodeChild[] {
|
|
|
159
164
|
]
|
|
160
165
|
}
|
|
161
166
|
|
|
167
|
+
function getReferenceContainer(entry: AimdReferenceEntry): string | undefined {
|
|
168
|
+
return entry.journal || entry.booktitle || entry.publisher
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildReferenceChildren(entry: AimdReferenceEntry): VNodeChild[] {
|
|
172
|
+
const children: VNodeChild[] = []
|
|
173
|
+
const pushText = (value: string | undefined, suffix = "") => {
|
|
174
|
+
const normalized = value?.trim()
|
|
175
|
+
if (!normalized) {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
if (children.length > 0) {
|
|
179
|
+
children.push(" ")
|
|
180
|
+
}
|
|
181
|
+
children.push(`${normalized}${suffix}`)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
pushText(entry.author)
|
|
185
|
+
pushText(entry.year ? `(${entry.year})` : undefined)
|
|
186
|
+
pushText(entry.title, entry.title && !/[.!?]$/.test(entry.title) ? "." : "")
|
|
187
|
+
pushText(getReferenceContainer(entry), ".")
|
|
188
|
+
|
|
189
|
+
if (entry.doi) {
|
|
190
|
+
if (children.length > 0) {
|
|
191
|
+
children.push(" ")
|
|
192
|
+
}
|
|
193
|
+
const doiHref = entry.doi.startsWith("http")
|
|
194
|
+
? entry.doi
|
|
195
|
+
: `https://doi.org/${entry.doi}`
|
|
196
|
+
children.push(h("a", { class: "aimd-refs__doi", href: doiHref }, `doi:${entry.doi}`))
|
|
197
|
+
}
|
|
198
|
+
else if (entry.url) {
|
|
199
|
+
if (children.length > 0) {
|
|
200
|
+
children.push(" ")
|
|
201
|
+
}
|
|
202
|
+
children.push(h("a", { class: "aimd-refs__url", href: entry.url }, entry.url))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return children.length > 0 ? children : [entry.id]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getCitationReferenceDisplays(node: AimdNode): AimdCitationReferenceDisplay[] {
|
|
209
|
+
const citationRefs = (node as any).citationRefs
|
|
210
|
+
if (Array.isArray(citationRefs) && citationRefs.length > 0) {
|
|
211
|
+
return citationRefs
|
|
212
|
+
.map(ref => ({
|
|
213
|
+
id: String(ref?.id || "").trim(),
|
|
214
|
+
label: String(ref?.label || ref?.id || "").trim(),
|
|
215
|
+
summary: String(ref?.summary || ref?.id || "").trim(),
|
|
216
|
+
}))
|
|
217
|
+
.filter(ref => ref.id && ref.label)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const refs: unknown[] = Array.isArray((node as any).refs) ? (node as any).refs : [node.id]
|
|
221
|
+
return refs
|
|
222
|
+
.map((ref: unknown) => String(ref).trim())
|
|
223
|
+
.filter(Boolean)
|
|
224
|
+
.map((ref: string) => ({ id: ref, label: ref, summary: formatCitationReferenceSummary(undefined, ref) }))
|
|
225
|
+
}
|
|
226
|
+
|
|
162
227
|
interface FieldMetadataHelp {
|
|
163
228
|
tooltip: string
|
|
164
229
|
description?: string
|
|
@@ -560,7 +625,10 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
560
625
|
"class": "aimd-ref aimd-ref--step",
|
|
561
626
|
"data-aimd-type": "ref_step",
|
|
562
627
|
"data-aimd-ref": refTarget,
|
|
628
|
+
"data-aimd-ref-target": refTarget,
|
|
629
|
+
"data-aimd-ref-kind": "step",
|
|
563
630
|
"data-aimd-step-sequence": stepSequence,
|
|
631
|
+
"tabindex": 0,
|
|
564
632
|
"title": refTarget,
|
|
565
633
|
}, [
|
|
566
634
|
h("span", { class: "aimd-ref__content" }, [
|
|
@@ -580,6 +648,9 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
580
648
|
"class": "aimd-ref aimd-ref--var",
|
|
581
649
|
"data-aimd-type": "ref_var",
|
|
582
650
|
"data-aimd-ref": refTarget,
|
|
651
|
+
"data-aimd-ref-target": refTarget,
|
|
652
|
+
"data-aimd-ref-kind": "var",
|
|
653
|
+
"tabindex": 0,
|
|
583
654
|
"title": refTarget,
|
|
584
655
|
}, [
|
|
585
656
|
h("span", { class: "aimd-ref__content" }, [
|
|
@@ -607,36 +678,64 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
607
678
|
// Display figure number if available, otherwise show ID
|
|
608
679
|
const displayText = ctx.messages.figure.reference(figureNumber !== undefined ? figureNumber : refTarget)
|
|
609
680
|
|
|
610
|
-
|
|
611
|
-
return h("a", {
|
|
681
|
+
return h("span", {
|
|
612
682
|
"class": "aimd-ref aimd-ref--fig",
|
|
613
683
|
"data-aimd-type": "ref_fig",
|
|
614
684
|
"data-aimd-ref": refTarget,
|
|
615
|
-
"
|
|
685
|
+
"data-aimd-ref-target": refTarget,
|
|
686
|
+
"data-aimd-ref-kind": "fig",
|
|
687
|
+
"tabindex": 0,
|
|
688
|
+
"title": refTarget,
|
|
616
689
|
}, [
|
|
617
690
|
h("span", { class: "aimd-ref__content" }, displayText),
|
|
618
691
|
])
|
|
619
692
|
},
|
|
620
693
|
|
|
621
694
|
cite: (node, ctx) => {
|
|
622
|
-
const
|
|
695
|
+
const citationRefs = getCitationReferenceDisplays(node)
|
|
623
696
|
|
|
624
697
|
return h("span", {
|
|
625
698
|
"class": "aimd-cite",
|
|
626
699
|
"data-aimd-type": "cite",
|
|
627
|
-
"data-aimd-refs":
|
|
700
|
+
"data-aimd-refs": citationRefs.map(ref => ref.id).join(","),
|
|
701
|
+
"data-aimd-citation-labels": citationRefs.map(ref => ref.label).join(","),
|
|
628
702
|
}, [
|
|
629
|
-
|
|
703
|
+
"[",
|
|
704
|
+
...citationRefs.flatMap((ref, index) => [
|
|
705
|
+
index > 0 ? ", " : "",
|
|
706
|
+
h("span", {
|
|
707
|
+
class: "aimd-cite__ref",
|
|
708
|
+
role: "doc-noteref",
|
|
709
|
+
tabindex: 0,
|
|
710
|
+
title: ref.summary,
|
|
711
|
+
"data-aimd-ref-id": ref.id,
|
|
712
|
+
"data-aimd-ref-summary": ref.summary,
|
|
713
|
+
"aria-label": ref.label === ref.id
|
|
714
|
+
? `Reference ${ref.summary}`
|
|
715
|
+
: `Reference ${ref.label}: ${ref.summary}`,
|
|
716
|
+
}, [
|
|
717
|
+
h("span", { class: "aimd-cite__label" }, ref.label),
|
|
718
|
+
h("span", {
|
|
719
|
+
class: "aimd-cite__popover",
|
|
720
|
+
role: "tooltip",
|
|
721
|
+
}, ref.summary),
|
|
722
|
+
]),
|
|
723
|
+
]),
|
|
724
|
+
"]",
|
|
630
725
|
])
|
|
631
726
|
},
|
|
632
727
|
|
|
633
728
|
fig: (node, ctx) => {
|
|
634
729
|
const figNode = node as any
|
|
635
730
|
const figId = figNode.id || node.id
|
|
636
|
-
const figSrc = figNode.src || ""
|
|
637
731
|
const figTitle = figNode.title
|
|
638
732
|
const figLegend = figNode.legend
|
|
639
733
|
const figSequence = figNode.sequence
|
|
734
|
+
const figSrc = resolveAimdAssetUrl(figNode.src || "", ctx.resolveAssetUrl, {
|
|
735
|
+
kind: "fig",
|
|
736
|
+
id: figId,
|
|
737
|
+
title: figTitle,
|
|
738
|
+
}) || ""
|
|
640
739
|
|
|
641
740
|
const children: VNodeChild[] = []
|
|
642
741
|
|
|
@@ -684,6 +783,29 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
684
783
|
"id": `fig-${figId}`,
|
|
685
784
|
}, children)
|
|
686
785
|
},
|
|
786
|
+
|
|
787
|
+
refs: (node, ctx) => {
|
|
788
|
+
const entries = Array.isArray((node as any).entries)
|
|
789
|
+
? (node as any).entries as AimdReferenceEntry[]
|
|
790
|
+
: []
|
|
791
|
+
|
|
792
|
+
return h("section", {
|
|
793
|
+
"class": "aimd-refs",
|
|
794
|
+
"data-aimd-type": "refs",
|
|
795
|
+
"data-aimd-id": node.id,
|
|
796
|
+
"id": "refs",
|
|
797
|
+
}, [
|
|
798
|
+
h("div", { class: "aimd-refs__title" }, ctx.messages.references.title),
|
|
799
|
+
h("ol", { class: "aimd-refs__list" }, entries.map(entry =>
|
|
800
|
+
h("li", {
|
|
801
|
+
id: `ref-${entry.id}`,
|
|
802
|
+
class: "aimd-refs__item",
|
|
803
|
+
"data-aimd-ref-id": entry.id,
|
|
804
|
+
"data-aimd-ref-type": entry.entry_type,
|
|
805
|
+
}, buildReferenceChildren(entry)),
|
|
806
|
+
)),
|
|
807
|
+
])
|
|
808
|
+
},
|
|
687
809
|
}
|
|
688
810
|
|
|
689
811
|
/**
|
|
@@ -709,7 +831,11 @@ export interface VueRendererOptions {
|
|
|
709
831
|
/**
|
|
710
832
|
* Render context
|
|
711
833
|
*/
|
|
712
|
-
context?: RenderContext & Partial<Pick<AimdRendererContext, "locale" | "messages">>
|
|
834
|
+
context?: RenderContext & Partial<Pick<AimdRendererContext, "locale" | "messages" | "resolveAssetUrl">>
|
|
835
|
+
/**
|
|
836
|
+
* Resolve protocol-local figure assets to displayable URLs.
|
|
837
|
+
*/
|
|
838
|
+
resolveAssetUrl?: AimdAssetUrlResolver
|
|
713
839
|
/**
|
|
714
840
|
* Custom AIMD component renderers
|
|
715
841
|
* Override default renderers or add new ones
|
|
@@ -751,6 +877,7 @@ function resolveRenderContext(options: VueRendererOptions): AimdRendererContext
|
|
|
751
877
|
quizPreview: context?.quizPreview ?? topLevelQuizPreview ?? undefined,
|
|
752
878
|
locale,
|
|
753
879
|
messages,
|
|
880
|
+
resolveAssetUrl: context?.resolveAssetUrl ?? options.resolveAssetUrl,
|
|
754
881
|
}
|
|
755
882
|
}
|
|
756
883
|
|
|
@@ -817,6 +944,29 @@ function preprocessFigures(node: HastRoot | RootContent, figCtx: FigureContext):
|
|
|
817
944
|
/**
|
|
818
945
|
* Parse AIMD node from HAST element properties
|
|
819
946
|
*/
|
|
947
|
+
function splitCsvProp(value: unknown): string[] {
|
|
948
|
+
return String(value || "")
|
|
949
|
+
.split(",")
|
|
950
|
+
.map(part => part.trim())
|
|
951
|
+
.filter(Boolean)
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function parseStringArrayProp(value: unknown): string[] {
|
|
955
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
956
|
+
return []
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
try {
|
|
960
|
+
const parsed = JSON.parse(value)
|
|
961
|
+
return Array.isArray(parsed)
|
|
962
|
+
? parsed.map(item => String(item || "").trim()).filter(Boolean)
|
|
963
|
+
: []
|
|
964
|
+
}
|
|
965
|
+
catch {
|
|
966
|
+
return []
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
820
970
|
function parseAimdFromProps(props: Record<string, unknown>): AimdNode | undefined {
|
|
821
971
|
let parsedFromJson: Record<string, unknown> | undefined
|
|
822
972
|
|
|
@@ -850,10 +1000,23 @@ function parseAimdFromProps(props: Record<string, unknown>): AimdNode | undefine
|
|
|
850
1000
|
}
|
|
851
1001
|
|
|
852
1002
|
const stepSequence = props["data-aimd-step-sequence"] || props.dataAimdStepSequence
|
|
1003
|
+
const citationRefIds = splitCsvProp(props["data-aimd-refs"] || props.dataAimdRefs)
|
|
1004
|
+
const citationLabels = splitCsvProp(props["data-aimd-citation-labels"] || props.dataAimdCitationLabels)
|
|
1005
|
+
const citationSummaries = parseStringArrayProp(props["data-aimd-citation-summaries"] || props.dataAimdCitationSummaries)
|
|
1006
|
+
const citationRefs = citationRefIds.map((refId, index) => ({
|
|
1007
|
+
id: refId,
|
|
1008
|
+
label: citationLabels[index] || refId,
|
|
1009
|
+
summary: citationSummaries[index] || refId,
|
|
1010
|
+
}))
|
|
1011
|
+
|
|
853
1012
|
if (parsedFromJson) {
|
|
854
1013
|
if (typeof stepSequence === "string" && stepSequence.trim()) {
|
|
855
1014
|
parsedFromJson.stepSequence = stepSequence
|
|
856
1015
|
}
|
|
1016
|
+
if (fieldType === "cite" && citationRefs.length > 0) {
|
|
1017
|
+
parsedFromJson.refs = citationRefs.map(ref => ref.id)
|
|
1018
|
+
parsedFromJson.citationRefs = citationRefs
|
|
1019
|
+
}
|
|
857
1020
|
return parsedFromJson as unknown as AimdNode
|
|
858
1021
|
}
|
|
859
1022
|
|
|
@@ -888,6 +1051,16 @@ function parseAimdFromProps(props: Record<string, unknown>): AimdNode | undefine
|
|
|
888
1051
|
} as AimdNode
|
|
889
1052
|
}
|
|
890
1053
|
|
|
1054
|
+
if (fieldType === "cite") {
|
|
1055
|
+
const refs = citationRefIds.length > 0 ? citationRefIds : [id]
|
|
1056
|
+
return {
|
|
1057
|
+
...baseNode,
|
|
1058
|
+
fieldType: "cite",
|
|
1059
|
+
refs,
|
|
1060
|
+
...(citationRefs.length > 0 ? { citationRefs } : {}),
|
|
1061
|
+
} as unknown as AimdNode
|
|
1062
|
+
}
|
|
1063
|
+
|
|
891
1064
|
// Add quiz-specific properties
|
|
892
1065
|
if (fieldType === "quiz") {
|
|
893
1066
|
const quizType = (props["data-aimd-quiz-type"] || props.dataAimdQuizType || "open") as string
|
|
@@ -1145,6 +1318,7 @@ export function renderToVNodes(
|
|
|
1145
1318
|
tree: HastRoot,
|
|
1146
1319
|
options: VueRendererOptions = {},
|
|
1147
1320
|
): VNode[] {
|
|
1321
|
+
moveReferenceSectionsToEnd(tree)
|
|
1148
1322
|
const result = hastToVue(tree, options)
|
|
1149
1323
|
|
|
1150
1324
|
if (result === null || result === undefined) {
|