@airalogy/aimd-renderer 2.4.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/src/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module "*.css" {}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * HTML rendering exports
3
+ */
4
+
5
+ export {
6
+ createCustomElementAimdRenderer,
7
+ createHtmlProcessor,
8
+ renderToHtml,
9
+ renderToHtmlSync,
10
+ parseAndExtract,
11
+ } from '../common/processor'
12
+
13
+ export {
14
+ createAimdRendererMessages,
15
+ DEFAULT_AIMD_RENDERER_LOCALE,
16
+ resolveAimdRendererLocale,
17
+ } from '../locales'
18
+
19
+ export type { ProcessorOptions } from '@airalogy/aimd-core/types'
20
+ export type {
21
+ AimdAssignerVisibility,
22
+ AimdHtmlNodeRenderer,
23
+ AimdHtmlRendererContext,
24
+ CustomElementAimdRendererOptions,
25
+ AimdRendererOptions,
26
+ RenderResult,
27
+ } from '../common/processor'
28
+ export type {
29
+ AimdRendererI18nOptions,
30
+ AimdRendererLocale,
31
+ AimdRendererMessages,
32
+ AimdRendererMessagesInput,
33
+ } from '../locales'
package/src/index.ts ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @airalogy/aimd-renderer
3
+ *
4
+ * AIMD rendering engines for HTML and Vue
5
+ */
6
+
7
+ // Common exports
8
+ export {
9
+ createCustomElementAimdRenderer,
10
+ createHtmlProcessor,
11
+ createRenderer,
12
+ defaultRenderer,
13
+ parseAndExtract,
14
+ renderToHtml,
15
+ renderToHtmlSync,
16
+ renderToVue,
17
+ } from './common/processor'
18
+
19
+ export {
20
+ createAimdRendererMessages,
21
+ DEFAULT_AIMD_RENDERER_LOCALE,
22
+ getAimdRendererQuizTypeLabel,
23
+ resolveAimdRendererLocale,
24
+ } from './locales'
25
+
26
+ export {
27
+ bubbleMenuEventKey,
28
+ draftEventKey,
29
+ fieldEventKey,
30
+ protocolKey,
31
+ reportEventKey,
32
+ } from './common/eventKeys'
33
+
34
+ export {
35
+ type AssetResponse,
36
+ createUnifiedTokenRenderer,
37
+ type TokenLike,
38
+ type TokenProps,
39
+ type UnifiedRendererContext,
40
+ type UnifiedTokenRendererOptions,
41
+ } from './common/unified-token-renderer'
42
+
43
+ // Vue renderer exports
44
+ export {
45
+ type AimdComponentRenderer,
46
+ type AimdRendererContext,
47
+ type AssetResolver,
48
+ createAssetRenderer,
49
+ createCodeBlockRenderer,
50
+ createComponentRenderer,
51
+ createEmbeddedRenderer,
52
+ createMermaidRenderer,
53
+ createStepCardRenderer,
54
+ type ElementRenderer,
55
+ hastToVue,
56
+ renderToVNodes,
57
+ type AimdStepCardRendererOptions,
58
+ type ShikiHighlighter,
59
+ type VueRendererOptions,
60
+ } from './vue/vue-renderer'
61
+
62
+ // Re-export types from aimd-core
63
+ export type {
64
+ ProcessorOptions,
65
+ RenderContext,
66
+ RenderMode,
67
+ RenderNode,
68
+ TokenRenderRule,
69
+ } from '@airalogy/aimd-core/types'
70
+
71
+ // Re-export RenderResult from processor
72
+ export type {
73
+ AimdAssignerVisibility,
74
+ AimdHtmlNodeRenderer,
75
+ AimdHtmlRendererContext,
76
+ CustomElementAimdRendererOptions,
77
+ AimdRendererOptions,
78
+ RenderResult,
79
+ } from './common/processor'
80
+ export type {
81
+ AimdRendererI18nOptions,
82
+ AimdRendererLocale,
83
+ AimdRendererMessages,
84
+ AimdRendererMessagesInput,
85
+ } from './locales'
86
+
87
+ // Helper function
88
+ export function getFinalIndent(item: { parent?: any, sequence: number, level: number }): string {
89
+ const { parent, sequence, level } = item
90
+ let indent = String(sequence + 1)
91
+
92
+ if (level === 1) {
93
+ return indent
94
+ }
95
+
96
+ let parentNode = parent
97
+
98
+ while (parentNode) {
99
+ indent = `${parentNode.sequence + 1}.${indent}`
100
+ parentNode = parentNode.parent
101
+ }
102
+
103
+ return indent
104
+ }
105
+
106
+ export function parseFieldTag(template: string): { type: string, name: string }[] {
107
+ const isTable = template.startsWith("var_table")
108
+ if (isTable) {
109
+ const [type, _, group, ...rest] = template.split(/(\\)?\|/g)
110
+ return [{ type, name: `${group}|${rest.filter(Boolean).join(",")}` }]
111
+ }
112
+ const [type, _, name] = template.split(/(\\)?\|/)
113
+ return [{ type, name }]
114
+ }
package/src/locales.ts ADDED
@@ -0,0 +1,256 @@
1
+ export type AimdRendererLocale = "en-US" | "zh-CN"
2
+
3
+ export const DEFAULT_AIMD_RENDERER_LOCALE: AimdRendererLocale = "en-US"
4
+
5
+ type DeepPartial<T> = {
6
+ [K in keyof T]?: T[K] extends (...args: any[]) => any
7
+ ? T[K]
8
+ : T[K] extends Array<infer U>
9
+ ? Array<DeepPartial<U>>
10
+ : T[K] extends object
11
+ ? DeepPartial<T[K]>
12
+ : T[K]
13
+ }
14
+
15
+ export interface AimdRendererMessages {
16
+ scope: {
17
+ var: string
18
+ quiz: string
19
+ step: string
20
+ check: string
21
+ table: string
22
+ figure: string
23
+ }
24
+ quiz: {
25
+ types: {
26
+ choice: string
27
+ singleChoice: string
28
+ multipleChoice: string
29
+ blank: string
30
+ open: string
31
+ scale: string
32
+ }
33
+ score: (score: string | number) => string
34
+ answer: (value: string) => string
35
+ rubric: (value: string) => string
36
+ }
37
+ step: {
38
+ sequence: (step: string | number) => string
39
+ reference: (step: string | number) => string
40
+ }
41
+ figure: {
42
+ reference: (value: string | number) => string
43
+ captionTitle: (sequence: number, title?: string) => string
44
+ }
45
+ assigner: {
46
+ clientSummary: string
47
+ serverSummary: string
48
+ }
49
+ }
50
+
51
+ export type AimdRendererMessagesInput = DeepPartial<AimdRendererMessages>
52
+
53
+ export interface AimdRendererI18nOptions {
54
+ locale?: AimdRendererLocale | string
55
+ messages?: AimdRendererMessagesInput
56
+ }
57
+
58
+ function detectRuntimeLocale(): string | undefined {
59
+ if (typeof document !== "undefined") {
60
+ const htmlLang = document.documentElement?.lang?.trim()
61
+ if (htmlLang)
62
+ return htmlLang
63
+ }
64
+
65
+ if (typeof navigator !== "undefined") {
66
+ if (navigator.language)
67
+ return navigator.language
68
+ if (Array.isArray(navigator.languages) && navigator.languages.length > 0)
69
+ return navigator.languages[0]
70
+ }
71
+
72
+ return undefined
73
+ }
74
+
75
+ const EN_US_MESSAGES: AimdRendererMessages = {
76
+ scope: {
77
+ var: "var",
78
+ quiz: "quiz",
79
+ step: "step",
80
+ check: "check",
81
+ table: "table",
82
+ figure: "figure",
83
+ },
84
+ quiz: {
85
+ types: {
86
+ choice: "choice",
87
+ singleChoice: "Single choice",
88
+ multipleChoice: "Multiple choice",
89
+ blank: "blank",
90
+ open: "open",
91
+ scale: "scale",
92
+ },
93
+ score: score => `${score} pt`,
94
+ answer: value => `Answer: ${value}`,
95
+ rubric: value => `Rubric: ${value}`,
96
+ },
97
+ step: {
98
+ sequence: step => `Step ${step} :`,
99
+ reference: step => `Step ${step}`,
100
+ },
101
+ figure: {
102
+ reference: value => `figure ${value}`,
103
+ captionTitle: (sequence, title) => title ? `figure ${sequence}: ${title}` : `figure ${sequence}`,
104
+ },
105
+ assigner: {
106
+ clientSummary: "Client assigner",
107
+ serverSummary: "Server assigner",
108
+ },
109
+ }
110
+
111
+ const ZH_CN_MESSAGES: AimdRendererMessages = {
112
+ scope: {
113
+ var: "变量",
114
+ quiz: "题目",
115
+ step: "步骤",
116
+ check: "检查点",
117
+ table: "表格",
118
+ figure: "图",
119
+ },
120
+ quiz: {
121
+ types: {
122
+ choice: "选择",
123
+ singleChoice: "单选",
124
+ multipleChoice: "多选",
125
+ blank: "填空",
126
+ open: "开放",
127
+ scale: "量表",
128
+ },
129
+ score: score => `${score} 分`,
130
+ answer: value => `答案:${value}`,
131
+ rubric: value => `评分标准:${value}`,
132
+ },
133
+ step: {
134
+ sequence: step => `步骤 ${step}:`,
135
+ reference: step => `步骤${step}`,
136
+ },
137
+ figure: {
138
+ reference: value => `图 ${value}`,
139
+ captionTitle: (sequence, title) => title ? `图 ${sequence}:${title}` : `图 ${sequence}`,
140
+ },
141
+ assigner: {
142
+ clientSummary: "前端 assigner",
143
+ serverSummary: "服务端 assigner",
144
+ },
145
+ }
146
+
147
+ const BASE_MESSAGES: Record<AimdRendererLocale, AimdRendererMessages> = {
148
+ "en-US": EN_US_MESSAGES,
149
+ "zh-CN": ZH_CN_MESSAGES,
150
+ }
151
+
152
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
153
+ return typeof value === "object" && value !== null && !Array.isArray(value)
154
+ }
155
+
156
+ function deepMerge<T>(base: T, override?: DeepPartial<T>): T {
157
+ if (!override)
158
+ return base
159
+
160
+ const result: Record<string, unknown> = { ...(base as Record<string, unknown>) }
161
+
162
+ for (const key of Object.keys(override) as Array<keyof T>) {
163
+ const overrideValue = override[key]
164
+ if (overrideValue === undefined)
165
+ continue
166
+
167
+ const baseValue = base[key]
168
+ if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {
169
+ result[key as string] = deepMerge(baseValue, overrideValue as any)
170
+ continue
171
+ }
172
+
173
+ result[key as string] = overrideValue as T[keyof T]
174
+ }
175
+
176
+ return result as T
177
+ }
178
+
179
+ export function resolveAimdRendererLocale(locale?: string): AimdRendererLocale {
180
+ const runtimeLocale = locale || detectRuntimeLocale()
181
+
182
+ if (runtimeLocale?.toLowerCase().startsWith("zh")) {
183
+ return "zh-CN"
184
+ }
185
+
186
+ return DEFAULT_AIMD_RENDERER_LOCALE
187
+ }
188
+
189
+ export function createAimdRendererMessages(
190
+ locale: string | undefined,
191
+ overrides?: AimdRendererMessagesInput,
192
+ ): AimdRendererMessages {
193
+ const resolvedLocale = resolveAimdRendererLocale(locale)
194
+ const merged = deepMerge(BASE_MESSAGES[resolvedLocale], overrides)
195
+ const choiceOverride = overrides?.quiz?.types?.choice
196
+ const hasSingleChoiceOverride = overrides?.quiz?.types?.singleChoice !== undefined
197
+ const hasMultipleChoiceOverride = overrides?.quiz?.types?.multipleChoice !== undefined
198
+
199
+ if (typeof choiceOverride === "string") {
200
+ if (!hasSingleChoiceOverride) {
201
+ merged.quiz.types.singleChoice = choiceOverride
202
+ }
203
+ if (!hasMultipleChoiceOverride) {
204
+ merged.quiz.types.multipleChoice = choiceOverride
205
+ }
206
+ }
207
+
208
+ return merged
209
+ }
210
+
211
+ export function getAimdRendererScopeLabel(
212
+ scope: string,
213
+ messages: Pick<AimdRendererMessages, "scope">,
214
+ ): string {
215
+ switch (scope) {
216
+ case "var":
217
+ return messages.scope.var
218
+ case "quiz":
219
+ return messages.scope.quiz
220
+ case "step":
221
+ return messages.scope.step
222
+ case "check":
223
+ return messages.scope.check
224
+ case "var_table":
225
+ return messages.scope.table
226
+ case "figure":
227
+ return messages.scope.figure
228
+ default:
229
+ return scope
230
+ }
231
+ }
232
+
233
+ export function getAimdRendererQuizTypeLabel(
234
+ quizType: string | undefined,
235
+ quizMode: string | undefined,
236
+ messages: Pick<AimdRendererMessages, "quiz">,
237
+ ): string {
238
+ switch (quizType) {
239
+ case "choice":
240
+ if (quizMode === "single") {
241
+ return messages.quiz.types.singleChoice
242
+ }
243
+ if (quizMode === "multiple") {
244
+ return messages.quiz.types.multipleChoice
245
+ }
246
+ return messages.quiz.types.choice
247
+ case "blank":
248
+ return messages.quiz.types.blank
249
+ case "open":
250
+ return messages.quiz.types.open
251
+ case "scale":
252
+ return messages.quiz.types.scale
253
+ default:
254
+ return quizType || messages.quiz.types.open
255
+ }
256
+ }
@@ -0,0 +1,2 @@
1
+ /* Re-export KaTeX base styles for AIMD renderer consumers. */
2
+ @import "katex/dist/katex.min.css";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Vue rendering exports
3
+ */
4
+
5
+ export {
6
+ type AimdComponentRenderer,
7
+ type AimdRendererContext,
8
+ type AssetResolver,
9
+ createAssetRenderer,
10
+ createCodeBlockRenderer,
11
+ createComponentRenderer,
12
+ createEmbeddedRenderer,
13
+ createMermaidRenderer,
14
+ createStepCardRenderer,
15
+ type ElementRenderer,
16
+ hastToVue,
17
+ renderToVNodes,
18
+ type AimdStepCardRendererOptions,
19
+ type ShikiHighlighter,
20
+ type VueRendererOptions,
21
+ } from './vue-renderer'
22
+
23
+ export {
24
+ renderToVue,
25
+ createRenderer,
26
+ defaultRenderer,
27
+ } from '../common/processor'
28
+
29
+ export {
30
+ createAimdRendererMessages,
31
+ DEFAULT_AIMD_RENDERER_LOCALE,
32
+ resolveAimdRendererLocale,
33
+ } from '../locales'
34
+
35
+ export type {
36
+ RenderContext,
37
+ RenderMode,
38
+ ProcessorOptions,
39
+ } from '@airalogy/aimd-core/types'
40
+
41
+ export type { AimdAssignerVisibility, AimdRendererOptions, RenderResult } from '../common/processor'
42
+ export type {
43
+ AimdRendererI18nOptions,
44
+ AimdRendererLocale,
45
+ AimdRendererMessages,
46
+ AimdRendererMessagesInput,
47
+ } from '../locales'