@airalogy/aimd-editor 1.7.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/README.md +59 -0
- package/README.zh-CN.md +43 -0
- package/dist/AimdEditorTopBar.vue_vue_type_script_setup_true_lang-gbfMDZSh.js +1131 -0
- package/dist/AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.js +274 -0
- package/dist/AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-B8o1VbUH.js +25012 -0
- package/dist/aimd-editor.css +1 -0
- package/dist/embedded.js +11 -0
- package/dist/index.js +44 -0
- package/dist/monaco.js +16 -0
- package/dist/theme-B8dCnOx-.js +583 -0
- package/dist/vue.js +30 -0
- package/dist/wysiwyg.js +9 -0
- package/package.json +90 -0
- package/src/__tests__/editor.test.ts +296 -0
- package/src/embedded.ts +18 -0
- package/src/index.ts +10 -0
- package/src/language-config.ts +152 -0
- package/src/monaco.ts +19 -0
- package/src/theme.ts +166 -0
- package/src/tokens.ts +120 -0
- package/src/vue/AimdEditor.vue +715 -0
- package/src/vue/AimdEditorToolbar.vue +83 -0
- package/src/vue/AimdEditorTopBar.vue +39 -0
- package/src/vue/AimdFieldDialog.vue +1102 -0
- package/src/vue/AimdSourceEditor.vue +330 -0
- package/src/vue/AimdWysiwygEditor.vue +569 -0
- package/src/vue/aimdInlineMarkdownNormalization.ts +10 -0
- package/src/vue/comparableAimdMarkdown.ts +6 -0
- package/src/vue/env.d.ts +7 -0
- package/src/vue/index.ts +45 -0
- package/src/vue/locales.ts +667 -0
- package/src/vue/milkdown-aimd-plugin.ts +378 -0
- package/src/vue/programmaticMarkdownSyncGuard.ts +66 -0
- package/src/vue/types.ts +449 -0
- package/src/vue/useEditorContent.ts +252 -0
- package/src/wysiwyg.ts +17 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
export type AimdEditorLocale = 'en-US' | 'zh-CN'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_AIMD_EDITOR_LOCALE: AimdEditorLocale = '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 AimdEditorMessages {
|
|
16
|
+
mode: {
|
|
17
|
+
source: string
|
|
18
|
+
sourceTitle: string
|
|
19
|
+
wysiwyg: string
|
|
20
|
+
wysiwygTitle: string
|
|
21
|
+
}
|
|
22
|
+
common: {
|
|
23
|
+
loadingEditor: string
|
|
24
|
+
preview: string
|
|
25
|
+
cancel: string
|
|
26
|
+
insert: string
|
|
27
|
+
remove: string
|
|
28
|
+
none: string
|
|
29
|
+
available: string
|
|
30
|
+
}
|
|
31
|
+
fieldTypes: {
|
|
32
|
+
var: { label: string, desc: string }
|
|
33
|
+
var_table: { label: string, desc: string }
|
|
34
|
+
quiz: { label: string, desc: string }
|
|
35
|
+
step: { label: string, desc: string }
|
|
36
|
+
check: { label: string, desc: string }
|
|
37
|
+
ref_step: { label: string, desc: string }
|
|
38
|
+
ref_var: { label: string, desc: string }
|
|
39
|
+
ref_fig: { label: string, desc: string }
|
|
40
|
+
cite: { label: string, desc: string }
|
|
41
|
+
}
|
|
42
|
+
varTypePresets: {
|
|
43
|
+
str: { label: string, desc: string }
|
|
44
|
+
int: { label: string, desc: string }
|
|
45
|
+
float: { label: string, desc: string }
|
|
46
|
+
bool: { label: string, desc: string }
|
|
47
|
+
date: { label: string, desc: string }
|
|
48
|
+
datetime: { label: string, desc: string }
|
|
49
|
+
time: { label: string, desc: string }
|
|
50
|
+
codeStr: { label: string, desc: string }
|
|
51
|
+
pyStr: { label: string, desc: string }
|
|
52
|
+
jsStr: { label: string, desc: string }
|
|
53
|
+
tsStr: { label: string, desc: string }
|
|
54
|
+
jsonStr: { label: string, desc: string }
|
|
55
|
+
tomlStr: { label: string, desc: string }
|
|
56
|
+
yamlStr: { label: string, desc: string }
|
|
57
|
+
dnaSequence: { label: string, desc: string }
|
|
58
|
+
currentTime: { label: string, desc: string }
|
|
59
|
+
userName: { label: string, desc: string }
|
|
60
|
+
airalogyMarkdown: { label: string, desc: string }
|
|
61
|
+
}
|
|
62
|
+
mdToolbar: {
|
|
63
|
+
h1: string
|
|
64
|
+
h2: string
|
|
65
|
+
h3: string
|
|
66
|
+
bold: string
|
|
67
|
+
italic: string
|
|
68
|
+
strikethrough: string
|
|
69
|
+
ul: string
|
|
70
|
+
ol: string
|
|
71
|
+
blockquote: string
|
|
72
|
+
code: string
|
|
73
|
+
codeblock: string
|
|
74
|
+
link: string
|
|
75
|
+
image: string
|
|
76
|
+
table: string
|
|
77
|
+
hr: string
|
|
78
|
+
math: string
|
|
79
|
+
}
|
|
80
|
+
dialog: {
|
|
81
|
+
title: (label: string) => string
|
|
82
|
+
variableId: string
|
|
83
|
+
typePresetLabel: string
|
|
84
|
+
typeHint: string
|
|
85
|
+
customType: string
|
|
86
|
+
type: string
|
|
87
|
+
defaultValue: string
|
|
88
|
+
titleLabel: string
|
|
89
|
+
tableId: string
|
|
90
|
+
subVariableColumns: string
|
|
91
|
+
subVariableColumnsHint: string
|
|
92
|
+
stepId: string
|
|
93
|
+
level: string
|
|
94
|
+
level1: string
|
|
95
|
+
level2: string
|
|
96
|
+
level3: string
|
|
97
|
+
quizId: string
|
|
98
|
+
questionType: string
|
|
99
|
+
score: string
|
|
100
|
+
stem: string
|
|
101
|
+
blankStemHint: string
|
|
102
|
+
mode: string
|
|
103
|
+
options: string
|
|
104
|
+
dragToReorder: string
|
|
105
|
+
answer: string
|
|
106
|
+
correct: string
|
|
107
|
+
optionsHint: string
|
|
108
|
+
blanks: string
|
|
109
|
+
blanksHint: string
|
|
110
|
+
rubric: string
|
|
111
|
+
checkpointId: string
|
|
112
|
+
referencedStepId: string
|
|
113
|
+
referencedVariableId: string
|
|
114
|
+
referencedFigureId: string
|
|
115
|
+
citationId: string
|
|
116
|
+
citationHint: string
|
|
117
|
+
customTypeHint: string
|
|
118
|
+
}
|
|
119
|
+
placeholders: {
|
|
120
|
+
variableId: string
|
|
121
|
+
type: string
|
|
122
|
+
defaultValue: string
|
|
123
|
+
title: string
|
|
124
|
+
tableId: string
|
|
125
|
+
subVariableColumns: string
|
|
126
|
+
stepId: string
|
|
127
|
+
quizId: string
|
|
128
|
+
score: string
|
|
129
|
+
stem: string
|
|
130
|
+
optionKey: string
|
|
131
|
+
optionText: string
|
|
132
|
+
blankKey: string
|
|
133
|
+
blankAnswer: string
|
|
134
|
+
rubric: string
|
|
135
|
+
checkpointId: string
|
|
136
|
+
citationIds: string
|
|
137
|
+
}
|
|
138
|
+
quiz: {
|
|
139
|
+
types: {
|
|
140
|
+
choice: string
|
|
141
|
+
blank: string
|
|
142
|
+
open: string
|
|
143
|
+
}
|
|
144
|
+
modes: {
|
|
145
|
+
single: string
|
|
146
|
+
multiple: string
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
actions: {
|
|
150
|
+
addOption: string
|
|
151
|
+
addBlank: string
|
|
152
|
+
}
|
|
153
|
+
errors: {
|
|
154
|
+
blankQuizRequiresBlankKey: string
|
|
155
|
+
blankKeysMustBeUnique: (keys: string[]) => string
|
|
156
|
+
blankStemRequiresPlaceholders: string
|
|
157
|
+
duplicatePlaceholders: (keys: string[]) => string
|
|
158
|
+
undefinedPlaceholders: (keys: string[]) => string
|
|
159
|
+
missingPlaceholders: (keys: string[]) => string
|
|
160
|
+
}
|
|
161
|
+
defaults: {
|
|
162
|
+
questionStem: string
|
|
163
|
+
fillQuestionStem: string
|
|
164
|
+
optionText: (key: string) => string
|
|
165
|
+
}
|
|
166
|
+
snippets: {
|
|
167
|
+
heading: string
|
|
168
|
+
boldText: string
|
|
169
|
+
italicText: string
|
|
170
|
+
strikethrough: string
|
|
171
|
+
listItem: string
|
|
172
|
+
quote: string
|
|
173
|
+
code: string
|
|
174
|
+
codeBlock: string
|
|
175
|
+
linkText: string
|
|
176
|
+
altText: string
|
|
177
|
+
mathFormula: string
|
|
178
|
+
tableColumnA: string
|
|
179
|
+
tableColumnB: string
|
|
180
|
+
tableColumnC: string
|
|
181
|
+
}
|
|
182
|
+
blockMenu: {
|
|
183
|
+
addBlockTitle: string
|
|
184
|
+
placeholder: string
|
|
185
|
+
groups: {
|
|
186
|
+
text: string
|
|
187
|
+
list: string
|
|
188
|
+
advanced: string
|
|
189
|
+
aimd: string
|
|
190
|
+
}
|
|
191
|
+
items: {
|
|
192
|
+
text: string
|
|
193
|
+
heading1: string
|
|
194
|
+
heading2: string
|
|
195
|
+
heading3: string
|
|
196
|
+
quote: string
|
|
197
|
+
divider: string
|
|
198
|
+
bulletList: string
|
|
199
|
+
orderedList: string
|
|
200
|
+
codeBlock: string
|
|
201
|
+
table: string
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
completions: {
|
|
205
|
+
insertAimdField: (keyword: string) => string
|
|
206
|
+
quizBlockLabel: string
|
|
207
|
+
quizBlock: string
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type AimdEditorMessagesInput = DeepPartial<AimdEditorMessages>
|
|
212
|
+
|
|
213
|
+
function detectRuntimeLocale(): string | undefined {
|
|
214
|
+
if (typeof document !== 'undefined') {
|
|
215
|
+
const htmlLang = document.documentElement?.lang?.trim()
|
|
216
|
+
if (htmlLang) return htmlLang
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (typeof navigator !== 'undefined') {
|
|
220
|
+
if (navigator.language) return navigator.language
|
|
221
|
+
if (Array.isArray(navigator.languages) && navigator.languages.length > 0) {
|
|
222
|
+
return navigator.languages[0]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return undefined
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const EN_US_MESSAGES: AimdEditorMessages = {
|
|
230
|
+
mode: {
|
|
231
|
+
source: 'Source',
|
|
232
|
+
sourceTitle: 'Source Mode',
|
|
233
|
+
wysiwyg: 'WYSIWYG',
|
|
234
|
+
wysiwygTitle: 'WYSIWYG Mode',
|
|
235
|
+
},
|
|
236
|
+
common: {
|
|
237
|
+
loadingEditor: 'Loading Editor...',
|
|
238
|
+
preview: 'Preview',
|
|
239
|
+
cancel: 'Cancel',
|
|
240
|
+
insert: 'Insert',
|
|
241
|
+
remove: 'Remove',
|
|
242
|
+
none: 'None',
|
|
243
|
+
available: 'Available',
|
|
244
|
+
},
|
|
245
|
+
fieldTypes: {
|
|
246
|
+
var: { label: 'Variable', desc: 'Define a variable' },
|
|
247
|
+
var_table: { label: 'Var Table', desc: 'Define a variable table' },
|
|
248
|
+
quiz: { label: 'Quiz', desc: 'Define a quiz item' },
|
|
249
|
+
step: { label: 'Step', desc: 'Define a step' },
|
|
250
|
+
check: { label: 'Checkpoint', desc: 'Define a checkpoint' },
|
|
251
|
+
ref_step: { label: 'Ref Step', desc: 'Reference a defined step' },
|
|
252
|
+
ref_var: { label: 'Ref Var', desc: 'Reference a defined variable' },
|
|
253
|
+
ref_fig: { label: 'Ref Fig', desc: 'Reference a defined figure' },
|
|
254
|
+
cite: { label: 'Citation', desc: 'Insert a citation' },
|
|
255
|
+
},
|
|
256
|
+
varTypePresets: {
|
|
257
|
+
str: { label: 'str', desc: 'Single-line text' },
|
|
258
|
+
int: { label: 'int', desc: 'Whole number' },
|
|
259
|
+
float: { label: 'float', desc: 'Decimal number' },
|
|
260
|
+
bool: { label: 'bool', desc: 'Yes / no checkbox' },
|
|
261
|
+
date: { label: 'date', desc: 'Calendar date picker' },
|
|
262
|
+
datetime: { label: 'datetime', desc: 'Date and time picker' },
|
|
263
|
+
time: { label: 'time', desc: 'Time-only picker' },
|
|
264
|
+
codeStr: { label: 'CodeStr', desc: 'Plain code editor without language-specific highlighting' },
|
|
265
|
+
pyStr: { label: 'PyStr', desc: 'Python code editor with syntax highlighting' },
|
|
266
|
+
jsStr: { label: 'JsStr', desc: 'JavaScript code editor with syntax highlighting' },
|
|
267
|
+
tsStr: { label: 'TsStr', desc: 'TypeScript code editor with syntax highlighting' },
|
|
268
|
+
jsonStr: { label: 'JsonStr', desc: 'JSON editor with syntax highlighting' },
|
|
269
|
+
tomlStr: { label: 'TomlStr', desc: 'TOML-like code editor with syntax highlighting' },
|
|
270
|
+
yamlStr: { label: 'YamlStr', desc: 'YAML editor with syntax highlighting' },
|
|
271
|
+
dnaSequence: { label: 'DNASequence', desc: 'Editable DNA sequence with annotations' },
|
|
272
|
+
currentTime: { label: 'CurrentTime', desc: 'Auto-fill the current time' },
|
|
273
|
+
userName: { label: 'UserName', desc: 'Auto-fill the current user name' },
|
|
274
|
+
airalogyMarkdown: { label: 'AiralogyMarkdown', desc: 'Multi-line markdown notes' },
|
|
275
|
+
},
|
|
276
|
+
mdToolbar: {
|
|
277
|
+
h1: 'Heading 1',
|
|
278
|
+
h2: 'Heading 2',
|
|
279
|
+
h3: 'Heading 3',
|
|
280
|
+
bold: 'Bold',
|
|
281
|
+
italic: 'Italic',
|
|
282
|
+
strikethrough: 'Strikethrough',
|
|
283
|
+
ul: 'Unordered List',
|
|
284
|
+
ol: 'Ordered List',
|
|
285
|
+
blockquote: 'Blockquote',
|
|
286
|
+
code: 'Inline Code',
|
|
287
|
+
codeblock: 'Code Block',
|
|
288
|
+
link: 'Link',
|
|
289
|
+
image: 'Image',
|
|
290
|
+
table: 'Table',
|
|
291
|
+
hr: 'Horizontal Rule',
|
|
292
|
+
math: 'Math Formula',
|
|
293
|
+
},
|
|
294
|
+
dialog: {
|
|
295
|
+
title: label => `Insert AIMD ${label}`,
|
|
296
|
+
variableId: 'Variable ID',
|
|
297
|
+
typePresetLabel: 'Common Types',
|
|
298
|
+
typeHint: 'Pick the closest match for the field you want to collect.',
|
|
299
|
+
customType: 'Custom / Advanced Type',
|
|
300
|
+
type: 'Type',
|
|
301
|
+
defaultValue: 'Default Value',
|
|
302
|
+
titleLabel: 'Title',
|
|
303
|
+
tableId: 'Table ID',
|
|
304
|
+
subVariableColumns: 'Sub-variable Columns',
|
|
305
|
+
subVariableColumnsHint: 'Comma-separated column names',
|
|
306
|
+
stepId: 'Step ID',
|
|
307
|
+
level: 'Level',
|
|
308
|
+
level1: '1 (Top level)',
|
|
309
|
+
level2: '2 (Sub-step)',
|
|
310
|
+
level3: '3 (Sub-sub-step)',
|
|
311
|
+
quizId: 'Quiz ID',
|
|
312
|
+
questionType: 'Question Type',
|
|
313
|
+
score: 'Score',
|
|
314
|
+
stem: 'Stem',
|
|
315
|
+
blankStemHint: 'Use placeholders in stem like [[b1]], [[b2]] and keep keys consistent with the blanks list.',
|
|
316
|
+
mode: 'Mode',
|
|
317
|
+
options: 'Options',
|
|
318
|
+
dragToReorder: 'Drag to reorder',
|
|
319
|
+
answer: 'Answer',
|
|
320
|
+
correct: 'Correct',
|
|
321
|
+
optionsHint: 'Use unique keys (A/B/C), then mark answer directly in each row.',
|
|
322
|
+
blanks: 'Blanks',
|
|
323
|
+
blanksHint: 'Use keys like b1, b2 and refer to them in stem as [[b1]], [[b2]].',
|
|
324
|
+
rubric: 'Rubric',
|
|
325
|
+
checkpointId: 'Checkpoint ID',
|
|
326
|
+
referencedStepId: 'Referenced Step ID',
|
|
327
|
+
referencedVariableId: 'Referenced Variable ID',
|
|
328
|
+
referencedFigureId: 'Referenced Figure ID',
|
|
329
|
+
citationId: 'Citation ID',
|
|
330
|
+
citationHint: 'Comma-separated citation IDs',
|
|
331
|
+
customTypeHint: 'Optional. Enter another AIMD type if you need something more specific, such as list or dict.',
|
|
332
|
+
},
|
|
333
|
+
placeholders: {
|
|
334
|
+
variableId: 'sample_id',
|
|
335
|
+
type: 'list / dict / CustomType',
|
|
336
|
+
defaultValue: 'Optional',
|
|
337
|
+
title: 'Display title (optional)',
|
|
338
|
+
tableId: 'table_id',
|
|
339
|
+
subVariableColumns: 'col1, col2, col3',
|
|
340
|
+
stepId: 'step_id',
|
|
341
|
+
quizId: 'quiz_choice_1',
|
|
342
|
+
score: 'Optional, e.g. 5',
|
|
343
|
+
stem: 'Question stem',
|
|
344
|
+
optionKey: 'A',
|
|
345
|
+
optionText: 'Option text',
|
|
346
|
+
blankKey: 'b1',
|
|
347
|
+
blankAnswer: 'Expected answer',
|
|
348
|
+
rubric: 'Optional rubric',
|
|
349
|
+
checkpointId: 'check_id',
|
|
350
|
+
citationIds: 'ref1, ref2',
|
|
351
|
+
},
|
|
352
|
+
quiz: {
|
|
353
|
+
types: {
|
|
354
|
+
choice: 'choice',
|
|
355
|
+
blank: 'blank',
|
|
356
|
+
open: 'open',
|
|
357
|
+
},
|
|
358
|
+
modes: {
|
|
359
|
+
single: 'single',
|
|
360
|
+
multiple: 'multiple',
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
actions: {
|
|
364
|
+
addOption: '+ Add option',
|
|
365
|
+
addBlank: '+ Add blank',
|
|
366
|
+
},
|
|
367
|
+
errors: {
|
|
368
|
+
blankQuizRequiresBlankKey: 'Blank quiz requires at least one non-empty blank key.',
|
|
369
|
+
blankKeysMustBeUnique: keys => `Blank keys must be unique: ${keys.join(', ')}`,
|
|
370
|
+
blankStemRequiresPlaceholders: 'Blank quiz stem must contain placeholders like [[b1]].',
|
|
371
|
+
duplicatePlaceholders: keys => `Stem contains duplicate placeholders: ${keys.join(', ')}`,
|
|
372
|
+
undefinedPlaceholders: keys => `Stem contains undefined placeholders: ${keys.join(', ')}`,
|
|
373
|
+
missingPlaceholders: keys => `Stem is missing placeholders for blank keys: ${keys.join(', ')}`,
|
|
374
|
+
},
|
|
375
|
+
defaults: {
|
|
376
|
+
questionStem: 'Which option is correct?',
|
|
377
|
+
fillQuestionStem: 'Please fill this question stem.',
|
|
378
|
+
optionText: key => `Option ${key}`,
|
|
379
|
+
},
|
|
380
|
+
snippets: {
|
|
381
|
+
heading: 'Heading',
|
|
382
|
+
boldText: 'bold text',
|
|
383
|
+
italicText: 'italic text',
|
|
384
|
+
strikethrough: 'strikethrough',
|
|
385
|
+
listItem: 'list item',
|
|
386
|
+
quote: 'quote',
|
|
387
|
+
code: 'code',
|
|
388
|
+
codeBlock: 'code block',
|
|
389
|
+
linkText: 'link text',
|
|
390
|
+
altText: 'alt text',
|
|
391
|
+
mathFormula: 'E = mc^2',
|
|
392
|
+
tableColumnA: 'Col A',
|
|
393
|
+
tableColumnB: 'Col B',
|
|
394
|
+
tableColumnC: 'Col C',
|
|
395
|
+
},
|
|
396
|
+
blockMenu: {
|
|
397
|
+
addBlockTitle: 'Click to add block',
|
|
398
|
+
placeholder: 'Please enter...',
|
|
399
|
+
groups: {
|
|
400
|
+
text: 'Text',
|
|
401
|
+
list: 'List',
|
|
402
|
+
advanced: 'Advanced',
|
|
403
|
+
aimd: 'AIMD',
|
|
404
|
+
},
|
|
405
|
+
items: {
|
|
406
|
+
text: 'Text',
|
|
407
|
+
heading1: 'Heading 1',
|
|
408
|
+
heading2: 'Heading 2',
|
|
409
|
+
heading3: 'Heading 3',
|
|
410
|
+
quote: 'Quote',
|
|
411
|
+
divider: 'Divider',
|
|
412
|
+
bulletList: 'Bullet List',
|
|
413
|
+
orderedList: 'Ordered List',
|
|
414
|
+
codeBlock: 'Code Block',
|
|
415
|
+
table: 'Table',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
completions: {
|
|
419
|
+
insertAimdField: keyword => `Insert AIMD ${keyword} field`,
|
|
420
|
+
quizBlockLabel: 'quiz block',
|
|
421
|
+
quizBlock: 'Insert AIMD quiz code block',
|
|
422
|
+
},
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const ZH_CN_MESSAGES: AimdEditorMessages = {
|
|
426
|
+
mode: {
|
|
427
|
+
source: '源码',
|
|
428
|
+
sourceTitle: '源码模式',
|
|
429
|
+
wysiwyg: '所见即所得',
|
|
430
|
+
wysiwygTitle: '所见即所得模式',
|
|
431
|
+
},
|
|
432
|
+
common: {
|
|
433
|
+
loadingEditor: '正在加载编辑器...',
|
|
434
|
+
preview: '预览',
|
|
435
|
+
cancel: '取消',
|
|
436
|
+
insert: '插入',
|
|
437
|
+
remove: '删除',
|
|
438
|
+
none: '无',
|
|
439
|
+
available: '可用项',
|
|
440
|
+
},
|
|
441
|
+
fieldTypes: {
|
|
442
|
+
var: { label: '变量', desc: '定义一个变量' },
|
|
443
|
+
var_table: { label: '变量表', desc: '定义一个变量表' },
|
|
444
|
+
quiz: { label: '题目', desc: '定义一个题目块' },
|
|
445
|
+
step: { label: '步骤', desc: '定义一个步骤' },
|
|
446
|
+
check: { label: '检查点', desc: '定义一个检查点' },
|
|
447
|
+
ref_step: { label: '引用步骤', desc: '引用已定义的步骤' },
|
|
448
|
+
ref_var: { label: '引用变量', desc: '引用已定义的变量' },
|
|
449
|
+
ref_fig: { label: '引用图片', desc: '引用已定义的图片' },
|
|
450
|
+
cite: { label: '引用文献', desc: '插入文献引用' },
|
|
451
|
+
},
|
|
452
|
+
varTypePresets: {
|
|
453
|
+
str: { label: 'str', desc: '单行文本' },
|
|
454
|
+
int: { label: 'int', desc: '整数' },
|
|
455
|
+
float: { label: 'float', desc: '小数' },
|
|
456
|
+
bool: { label: 'bool', desc: '是 / 否复选框' },
|
|
457
|
+
date: { label: 'date', desc: '日期选择器' },
|
|
458
|
+
datetime: { label: 'datetime', desc: '日期时间选择器' },
|
|
459
|
+
time: { label: 'time', desc: '时间选择器' },
|
|
460
|
+
codeStr: { label: 'CodeStr', desc: '通用代码编辑器,不指定语言高亮' },
|
|
461
|
+
pyStr: { label: 'PyStr', desc: '带 Python 语法高亮的代码编辑器' },
|
|
462
|
+
jsStr: { label: 'JsStr', desc: '带 JavaScript 语法高亮的代码编辑器' },
|
|
463
|
+
tsStr: { label: 'TsStr', desc: '带 TypeScript 语法高亮的代码编辑器' },
|
|
464
|
+
jsonStr: { label: 'JsonStr', desc: '带 JSON 语法高亮的编辑器' },
|
|
465
|
+
tomlStr: { label: 'TomlStr', desc: '带 TOML 风格语法高亮的编辑器' },
|
|
466
|
+
yamlStr: { label: 'YamlStr', desc: '带 YAML 语法高亮的编辑器' },
|
|
467
|
+
dnaSequence: { label: 'DNASequence', desc: '可编辑的 DNA 序列与注释' },
|
|
468
|
+
currentTime: { label: 'CurrentTime', desc: '自动填入当前时间' },
|
|
469
|
+
userName: { label: 'UserName', desc: '自动填入当前用户名' },
|
|
470
|
+
airalogyMarkdown: { label: 'AiralogyMarkdown', desc: '多行 Markdown 备注' },
|
|
471
|
+
},
|
|
472
|
+
mdToolbar: {
|
|
473
|
+
h1: '一级标题',
|
|
474
|
+
h2: '二级标题',
|
|
475
|
+
h3: '三级标题',
|
|
476
|
+
bold: '加粗',
|
|
477
|
+
italic: '斜体',
|
|
478
|
+
strikethrough: '删除线',
|
|
479
|
+
ul: '无序列表',
|
|
480
|
+
ol: '有序列表',
|
|
481
|
+
blockquote: '引用块',
|
|
482
|
+
code: '行内代码',
|
|
483
|
+
codeblock: '代码块',
|
|
484
|
+
link: '链接',
|
|
485
|
+
image: '图片',
|
|
486
|
+
table: '表格',
|
|
487
|
+
hr: '分割线',
|
|
488
|
+
math: '数学公式',
|
|
489
|
+
},
|
|
490
|
+
dialog: {
|
|
491
|
+
title: label => `插入 AIMD ${label}`,
|
|
492
|
+
variableId: '变量 ID',
|
|
493
|
+
typePresetLabel: '常用类型',
|
|
494
|
+
typeHint: '先选一个最接近的输入方式,后面也可以再手动改。',
|
|
495
|
+
customType: '自定义 / 高级类型',
|
|
496
|
+
type: '类型',
|
|
497
|
+
defaultValue: '默认值',
|
|
498
|
+
titleLabel: '标题',
|
|
499
|
+
tableId: '表格 ID',
|
|
500
|
+
subVariableColumns: '子变量列',
|
|
501
|
+
subVariableColumnsHint: '多个列名用逗号分隔',
|
|
502
|
+
stepId: '步骤 ID',
|
|
503
|
+
level: '层级',
|
|
504
|
+
level1: '1(顶层)',
|
|
505
|
+
level2: '2(子步骤)',
|
|
506
|
+
level3: '3(子子步骤)',
|
|
507
|
+
quizId: '题目 ID',
|
|
508
|
+
questionType: '题目类型',
|
|
509
|
+
score: '分值',
|
|
510
|
+
stem: '题干',
|
|
511
|
+
blankStemHint: '在题干中使用 [[b1]]、[[b2]] 这样的占位符,并与下方填空键保持一致。',
|
|
512
|
+
mode: '模式',
|
|
513
|
+
options: '选项',
|
|
514
|
+
dragToReorder: '拖拽排序',
|
|
515
|
+
answer: '答案',
|
|
516
|
+
correct: '正确',
|
|
517
|
+
optionsHint: '请使用唯一键(A/B/C),并直接在每一行标记答案。',
|
|
518
|
+
blanks: '填空项',
|
|
519
|
+
blanksHint: '请使用 b1、b2 这样的键,并在题干中写成 [[b1]]、[[b2]]。',
|
|
520
|
+
rubric: '评分说明',
|
|
521
|
+
checkpointId: '检查点 ID',
|
|
522
|
+
referencedStepId: '引用步骤 ID',
|
|
523
|
+
referencedVariableId: '引用变量 ID',
|
|
524
|
+
referencedFigureId: '引用图片 ID',
|
|
525
|
+
citationId: '文献 ID',
|
|
526
|
+
citationHint: '多个文献 ID 用逗号分隔',
|
|
527
|
+
customTypeHint: '可选。如果需要更具体的 AIMD 类型,可以在这里输入,例如 list 或 dict。',
|
|
528
|
+
},
|
|
529
|
+
placeholders: {
|
|
530
|
+
variableId: 'sample_id',
|
|
531
|
+
type: 'list / dict / CustomType',
|
|
532
|
+
defaultValue: '可选',
|
|
533
|
+
title: '显示标题(可选)',
|
|
534
|
+
tableId: 'table_id',
|
|
535
|
+
subVariableColumns: 'col1, col2, col3',
|
|
536
|
+
stepId: 'step_id',
|
|
537
|
+
quizId: 'quiz_choice_1',
|
|
538
|
+
score: '可选,例如 5',
|
|
539
|
+
stem: '请输入题干',
|
|
540
|
+
optionKey: 'A',
|
|
541
|
+
optionText: '选项内容',
|
|
542
|
+
blankKey: 'b1',
|
|
543
|
+
blankAnswer: '预期答案',
|
|
544
|
+
rubric: '可选的评分说明',
|
|
545
|
+
checkpointId: 'check_id',
|
|
546
|
+
citationIds: 'ref1, ref2',
|
|
547
|
+
},
|
|
548
|
+
quiz: {
|
|
549
|
+
types: {
|
|
550
|
+
choice: '选择题',
|
|
551
|
+
blank: '填空题',
|
|
552
|
+
open: '开放题',
|
|
553
|
+
},
|
|
554
|
+
modes: {
|
|
555
|
+
single: '单选',
|
|
556
|
+
multiple: '多选',
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
actions: {
|
|
560
|
+
addOption: '+ 添加选项',
|
|
561
|
+
addBlank: '+ 添加填空项',
|
|
562
|
+
},
|
|
563
|
+
errors: {
|
|
564
|
+
blankQuizRequiresBlankKey: '填空题至少需要一个非空的填空键。',
|
|
565
|
+
blankKeysMustBeUnique: keys => `填空键必须唯一:${keys.join(', ')}`,
|
|
566
|
+
blankStemRequiresPlaceholders: '填空题题干必须包含类似 [[b1]] 的占位符。',
|
|
567
|
+
duplicatePlaceholders: keys => `题干中存在重复占位符:${keys.join(', ')}`,
|
|
568
|
+
undefinedPlaceholders: keys => `题干中存在未定义的占位符:${keys.join(', ')}`,
|
|
569
|
+
missingPlaceholders: keys => `这些填空键未在题干中使用:${keys.join(', ')}`,
|
|
570
|
+
},
|
|
571
|
+
defaults: {
|
|
572
|
+
questionStem: '哪个选项是正确的?',
|
|
573
|
+
fillQuestionStem: '请填写题干内容。',
|
|
574
|
+
optionText: key => `选项 ${key}`,
|
|
575
|
+
},
|
|
576
|
+
snippets: {
|
|
577
|
+
heading: '标题',
|
|
578
|
+
boldText: '加粗文本',
|
|
579
|
+
italicText: '斜体文本',
|
|
580
|
+
strikethrough: '删除线文本',
|
|
581
|
+
listItem: '列表项',
|
|
582
|
+
quote: '引用内容',
|
|
583
|
+
code: '代码',
|
|
584
|
+
codeBlock: '代码块',
|
|
585
|
+
linkText: '链接文字',
|
|
586
|
+
altText: '图片说明',
|
|
587
|
+
mathFormula: 'E = mc^2',
|
|
588
|
+
tableColumnA: '列 A',
|
|
589
|
+
tableColumnB: '列 B',
|
|
590
|
+
tableColumnC: '列 C',
|
|
591
|
+
},
|
|
592
|
+
blockMenu: {
|
|
593
|
+
addBlockTitle: '点击添加块',
|
|
594
|
+
placeholder: '请输入内容...',
|
|
595
|
+
groups: {
|
|
596
|
+
text: '文本',
|
|
597
|
+
list: '列表',
|
|
598
|
+
advanced: '高级',
|
|
599
|
+
aimd: 'AIMD',
|
|
600
|
+
},
|
|
601
|
+
items: {
|
|
602
|
+
text: '正文',
|
|
603
|
+
heading1: '一级标题',
|
|
604
|
+
heading2: '二级标题',
|
|
605
|
+
heading3: '三级标题',
|
|
606
|
+
quote: '引用块',
|
|
607
|
+
divider: '分割线',
|
|
608
|
+
bulletList: '无序列表',
|
|
609
|
+
orderedList: '有序列表',
|
|
610
|
+
codeBlock: '代码块',
|
|
611
|
+
table: '表格',
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
completions: {
|
|
615
|
+
insertAimdField: keyword => `插入 AIMD ${keyword} 字段`,
|
|
616
|
+
quizBlockLabel: '题目代码块',
|
|
617
|
+
quizBlock: '插入 AIMD 题目代码块',
|
|
618
|
+
},
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const BASE_MESSAGES: Record<AimdEditorLocale, AimdEditorMessages> = {
|
|
622
|
+
'en-US': EN_US_MESSAGES,
|
|
623
|
+
'zh-CN': ZH_CN_MESSAGES,
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
627
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function deepMerge<T>(base: T, override?: DeepPartial<T>): T {
|
|
631
|
+
if (!override) return base
|
|
632
|
+
|
|
633
|
+
const result: Record<string, unknown> = { ...(base as Record<string, unknown>) }
|
|
634
|
+
|
|
635
|
+
for (const key of Object.keys(override) as Array<keyof T>) {
|
|
636
|
+
const overrideValue = override[key]
|
|
637
|
+
if (overrideValue === undefined) continue
|
|
638
|
+
|
|
639
|
+
const baseValue = base[key]
|
|
640
|
+
if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {
|
|
641
|
+
result[key as string] = deepMerge(baseValue, overrideValue as any)
|
|
642
|
+
continue
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
result[key as string] = overrideValue as T[keyof T]
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return result as T
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
export function resolveAimdEditorLocale(locale?: string): AimdEditorLocale {
|
|
652
|
+
const runtimeLocale = locale || detectRuntimeLocale()
|
|
653
|
+
|
|
654
|
+
if (runtimeLocale?.toLowerCase().startsWith('zh')) {
|
|
655
|
+
return 'zh-CN'
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return DEFAULT_AIMD_EDITOR_LOCALE
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function createAimdEditorMessages(
|
|
662
|
+
locale: string | undefined,
|
|
663
|
+
overrides?: AimdEditorMessagesInput,
|
|
664
|
+
): AimdEditorMessages {
|
|
665
|
+
const resolvedLocale = resolveAimdEditorLocale(locale)
|
|
666
|
+
return deepMerge(BASE_MESSAGES[resolvedLocale], overrides)
|
|
667
|
+
}
|