@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
package/dist/vue.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { _ as e, a as d, b as s, c as r, u as o } from "./AimdEditorTopBar.vue_vue_type_script_setup_true_lang-gbfMDZSh.js";
|
|
2
|
+
import { _ as m } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.js";
|
|
3
|
+
import { A as E, a as l, _ as I, D as _, M as T, b as D, c as F, d as M, e as u, f as c, g as n, h as g, i as L, j as O, k as p, l as S, m as y, n as b, r as f } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-B8o1VbUH.js";
|
|
4
|
+
export {
|
|
5
|
+
E as AIMD_FIELD_TYPES,
|
|
6
|
+
l as AIMD_FIELD_TYPE_DEFINITIONS,
|
|
7
|
+
e as AimdEditor,
|
|
8
|
+
d as AimdEditorToolbar,
|
|
9
|
+
s as AimdEditorTopBar,
|
|
10
|
+
r as AimdFieldDialog,
|
|
11
|
+
m as AimdSourceEditor,
|
|
12
|
+
I as AimdWysiwygEditor,
|
|
13
|
+
_ as DEFAULT_AIMD_EDITOR_LOCALE,
|
|
14
|
+
T as MD_TOOLBAR_ITEMS,
|
|
15
|
+
D as MD_TOOLBAR_ITEM_DEFINITIONS,
|
|
16
|
+
F as aimdFieldInputRule,
|
|
17
|
+
M as aimdFieldNode,
|
|
18
|
+
u as aimdFieldView,
|
|
19
|
+
c as aimdMilkdownPlugins,
|
|
20
|
+
n as aimdRemarkPlugin,
|
|
21
|
+
g as buildAimdSyntax,
|
|
22
|
+
L as createAimdEditorMessages,
|
|
23
|
+
O as createAimdFieldTypes,
|
|
24
|
+
p as createAimdVarTypePresets,
|
|
25
|
+
S as createMdToolbarItems,
|
|
26
|
+
y as getDefaultAimdFields,
|
|
27
|
+
b as getQuickAimdSyntax,
|
|
28
|
+
f as resolveAimdEditorLocale,
|
|
29
|
+
o as useEditorContent
|
|
30
|
+
};
|
package/dist/wysiwyg.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { _ as a, D as r, i, j as d, k as t, r as A } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-B8o1VbUH.js";
|
|
2
|
+
export {
|
|
3
|
+
a as AimdWysiwygEditor,
|
|
4
|
+
r as DEFAULT_AIMD_EDITOR_LOCALE,
|
|
5
|
+
i as createAimdEditorMessages,
|
|
6
|
+
d as createAimdFieldTypes,
|
|
7
|
+
t as createAimdVarTypePresets,
|
|
8
|
+
A as resolveAimdEditorLocale
|
|
9
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@airalogy/aimd-editor",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.7.1",
|
|
5
|
+
"description": "AIMD editor with Monaco source mode and Milkdown WYSIWYG mode",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/airalogy/aimd.git",
|
|
10
|
+
"directory": "packages/aimd-editor"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/airalogy/aimd/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/airalogy/aimd/tree/main/packages/aimd-editor#readme",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"aimd",
|
|
18
|
+
"monaco",
|
|
19
|
+
"milkdown",
|
|
20
|
+
"editor",
|
|
21
|
+
"wysiwyg",
|
|
22
|
+
"markdown",
|
|
23
|
+
"vue"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./src/index.ts",
|
|
28
|
+
"import": "./src/index.ts"
|
|
29
|
+
},
|
|
30
|
+
"./monaco": {
|
|
31
|
+
"types": "./src/monaco.ts",
|
|
32
|
+
"import": "./src/monaco.ts"
|
|
33
|
+
},
|
|
34
|
+
"./vue": {
|
|
35
|
+
"types": "./src/vue/index.ts",
|
|
36
|
+
"import": "./src/vue/index.ts"
|
|
37
|
+
},
|
|
38
|
+
"./embedded": {
|
|
39
|
+
"types": "./src/embedded.ts",
|
|
40
|
+
"import": "./src/embedded.ts"
|
|
41
|
+
},
|
|
42
|
+
"./wysiwyg": {
|
|
43
|
+
"types": "./src/wysiwyg.ts",
|
|
44
|
+
"import": "./src/wysiwyg.ts"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"main": "./src/index.ts",
|
|
48
|
+
"types": "./src/index.ts",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist",
|
|
54
|
+
"src"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"type-check": "tsc --noEmit",
|
|
58
|
+
"build": "vite build",
|
|
59
|
+
"dev": "vite build --watch",
|
|
60
|
+
"test": "node --test ./tests/*.test.mjs"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@airalogy/aimd-core": "workspace:^",
|
|
64
|
+
"@airalogy/aimd-renderer": "workspace:^",
|
|
65
|
+
"@codingame/monaco-vscode-editor-api": "^20.2.1",
|
|
66
|
+
"@codingame/monaco-vscode-standalone-languages": "^20.2.1",
|
|
67
|
+
"@milkdown/kit": "^7.18.0",
|
|
68
|
+
"@milkdown/theme-nord": "^7.18.0",
|
|
69
|
+
"@milkdown/vue": "^7.18.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"monaco-editor": ">=0.50.0",
|
|
73
|
+
"vue": ">=3.3.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"monaco-editor": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/node": "^24.3.0",
|
|
82
|
+
"monaco-editor": "^0.52.2",
|
|
83
|
+
"shiki": "^2.3.2",
|
|
84
|
+
"typescript": "5.8.3",
|
|
85
|
+
"vite": "^7.1.3",
|
|
86
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
87
|
+
"vue": "^3.5.17",
|
|
88
|
+
"@vitejs/plugin-vue": "^6.0.1"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AimdToken,
|
|
5
|
+
AimdTokenDefinition,
|
|
6
|
+
AimdSuffix,
|
|
7
|
+
MarkupDefinition,
|
|
8
|
+
KeywordDefinition,
|
|
9
|
+
DelimiterDefinition,
|
|
10
|
+
scopeName,
|
|
11
|
+
} from '../tokens'
|
|
12
|
+
import { aimdTheme, aimdTokenColors } from '../theme'
|
|
13
|
+
import {
|
|
14
|
+
getDefaultAimdFields,
|
|
15
|
+
buildAimdSyntax,
|
|
16
|
+
createAimdFieldTypes,
|
|
17
|
+
createMdToolbarItems,
|
|
18
|
+
createAimdVarTypePresets,
|
|
19
|
+
AIMD_FIELD_TYPE_DEFINITIONS,
|
|
20
|
+
MD_TOOLBAR_ITEM_DEFINITIONS,
|
|
21
|
+
} from '../vue/types'
|
|
22
|
+
import { resolveAimdEditorLocale, createAimdEditorMessages } from '../vue/locales'
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Token definitions
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
describe('AimdToken definitions', () => {
|
|
29
|
+
it('has markup tokens', () => {
|
|
30
|
+
expect(AimdToken).toHaveProperty('MARKUP_AIMD_VARIABLE')
|
|
31
|
+
expect(AimdToken).toHaveProperty('MARKUP_AIMD_STEP')
|
|
32
|
+
expect(AimdToken).toHaveProperty('MARKUP_AIMD_CHECKPOINT')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('has keyword tokens', () => {
|
|
36
|
+
expect(AimdToken).toHaveProperty('KEYWORD_VARIABLE_AIMD')
|
|
37
|
+
expect(AimdToken).toHaveProperty('KEYWORD_STEP_AIMD')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('has delimiter tokens', () => {
|
|
41
|
+
expect(AimdToken).toHaveProperty('DELIMITER_PIPE_AIMD')
|
|
42
|
+
expect(AimdToken).toHaveProperty('DELIMITER_COLON_AIMD')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('token suffix is "aimd"', () => {
|
|
46
|
+
expect(AimdSuffix).toBe('aimd')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('scope name follows TextMate convention', () => {
|
|
50
|
+
expect(scopeName).toBe('text.html.markdown.aimd')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('MarkupDefinition has variable and step entries', () => {
|
|
54
|
+
expect(MarkupDefinition).toHaveProperty('MARKUP_AIMD_VARIABLE')
|
|
55
|
+
expect(MarkupDefinition).toHaveProperty('MARKUP_AIMD_STEP')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('KeywordDefinition has entries', () => {
|
|
59
|
+
expect(Object.keys(KeywordDefinition).length).toBeGreaterThan(0)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('DelimiterDefinition has entries', () => {
|
|
63
|
+
expect(Object.keys(DelimiterDefinition).length).toBeGreaterThan(0)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('AimdTokenDefinition has type tokens', () => {
|
|
67
|
+
expect(AimdTokenDefinition).toHaveProperty('KEYWORD_CONTROL_AIMD')
|
|
68
|
+
expect(AimdTokenDefinition).toHaveProperty('VARIABLE_PARAMETER_AIMD')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Theme (no Monaco dependency)
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
describe('AIMD theme', () => {
|
|
77
|
+
it('aimdTheme has proper structure', () => {
|
|
78
|
+
expect(aimdTheme).toHaveProperty('name')
|
|
79
|
+
expect(aimdTheme).toHaveProperty('settings')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('aimdTokenColors is an array', () => {
|
|
83
|
+
expect(Array.isArray(aimdTokenColors)).toBe(true)
|
|
84
|
+
expect(aimdTokenColors.length).toBeGreaterThan(0)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('token colors have scope and settings', () => {
|
|
88
|
+
for (const color of aimdTokenColors) {
|
|
89
|
+
expect(color).toHaveProperty('scope')
|
|
90
|
+
expect(color).toHaveProperty('settings')
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// getDefaultAimdFields
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
describe('getDefaultAimdFields', () => {
|
|
100
|
+
it('returns var defaults', () => {
|
|
101
|
+
const fields = getDefaultAimdFields('var')
|
|
102
|
+
expect(fields).toHaveProperty('name')
|
|
103
|
+
expect(fields).toHaveProperty('type')
|
|
104
|
+
expect(fields.type).toBe('str')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('returns step defaults with level 1', () => {
|
|
108
|
+
const fields = getDefaultAimdFields('step')
|
|
109
|
+
expect(fields).toHaveProperty('name')
|
|
110
|
+
expect(fields).toHaveProperty('level')
|
|
111
|
+
expect(fields.level).toBe('1')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('returns check defaults', () => {
|
|
115
|
+
const fields = getDefaultAimdFields('check')
|
|
116
|
+
expect(fields).toHaveProperty('name')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('returns quiz defaults', () => {
|
|
120
|
+
const fields = getDefaultAimdFields('quiz')
|
|
121
|
+
expect(fields).toHaveProperty('id')
|
|
122
|
+
expect(fields).toHaveProperty('quizType')
|
|
123
|
+
expect(fields.quizType).toBe('choice')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('returns var_table defaults', () => {
|
|
127
|
+
const fields = getDefaultAimdFields('var_table')
|
|
128
|
+
expect(fields).toHaveProperty('name')
|
|
129
|
+
expect(fields).toHaveProperty('subvars')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('returns generic defaults for unknown type', () => {
|
|
133
|
+
const fields = getDefaultAimdFields('unknown')
|
|
134
|
+
expect(fields).toHaveProperty('name')
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// buildAimdSyntax
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
describe('buildAimdSyntax', () => {
|
|
143
|
+
it('builds simple var syntax', () => {
|
|
144
|
+
const syntax = buildAimdSyntax('var', { name: 'temperature', type: 'float', default: '36.5', title: '' })
|
|
145
|
+
expect(syntax).toBe('{{var|temperature: float = 36.5}}')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('builds var with title', () => {
|
|
149
|
+
const syntax = buildAimdSyntax('var', { name: 'temp', type: 'float', default: '', title: 'Temperature' })
|
|
150
|
+
expect(syntax).toContain('title = "Temperature"')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('builds var without type or default', () => {
|
|
154
|
+
const syntax = buildAimdSyntax('var', { name: 'note', type: '', default: '', title: '' })
|
|
155
|
+
expect(syntax).toBe('{{var|note}}')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('uses fallback name when empty', () => {
|
|
159
|
+
const syntax = buildAimdSyntax('var', { name: '', type: '', default: '', title: '' })
|
|
160
|
+
expect(syntax).toContain('my_var')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('builds step syntax', () => {
|
|
164
|
+
const syntax = buildAimdSyntax('step', { name: 'wash_hands', level: '1' })
|
|
165
|
+
expect(syntax).toBe('{{step|wash_hands}}')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('builds step with level > 1', () => {
|
|
169
|
+
const syntax = buildAimdSyntax('step', { name: 'substep', level: '2' })
|
|
170
|
+
expect(syntax).toBe('{{step|substep, 2}}')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('builds var_table syntax', () => {
|
|
174
|
+
const syntax = buildAimdSyntax('var_table', { name: 'measurements', subvars: 'temp, pressure' })
|
|
175
|
+
expect(syntax).toContain('var_table|measurements')
|
|
176
|
+
expect(syntax).toContain('subvars=[temp, pressure]')
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('builds check syntax', () => {
|
|
180
|
+
const syntax = buildAimdSyntax('check', { name: 'verify_result' })
|
|
181
|
+
expect(syntax).toBe('{{check|verify_result}}')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('builds ref_step syntax', () => {
|
|
185
|
+
const syntax = buildAimdSyntax('ref_step', { name: 'step1' })
|
|
186
|
+
expect(syntax).toContain('ref_step')
|
|
187
|
+
expect(syntax).toContain('step1')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('builds quiz syntax', () => {
|
|
191
|
+
const syntax = buildAimdSyntax('quiz', {
|
|
192
|
+
id: 'q1',
|
|
193
|
+
quizType: 'choice',
|
|
194
|
+
mode: 'single',
|
|
195
|
+
stem: 'What is 1+1?',
|
|
196
|
+
options: 'A:One, B:Two',
|
|
197
|
+
answer: 'B',
|
|
198
|
+
blanks: '',
|
|
199
|
+
rubric: '',
|
|
200
|
+
score: '',
|
|
201
|
+
})
|
|
202
|
+
expect(syntax).toContain('```quiz')
|
|
203
|
+
expect(syntax).toContain('id: q1')
|
|
204
|
+
expect(syntax).toContain('type: choice')
|
|
205
|
+
expect(syntax).toContain('mode: single')
|
|
206
|
+
expect(syntax).toContain('What is 1+1?')
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Locale helpers
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
describe('resolveAimdEditorLocale', () => {
|
|
215
|
+
it('defaults to en-US', () => {
|
|
216
|
+
expect(resolveAimdEditorLocale()).toBe('en-US')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('resolves zh to zh-CN', () => {
|
|
220
|
+
expect(resolveAimdEditorLocale('zh')).toBe('zh-CN')
|
|
221
|
+
expect(resolveAimdEditorLocale('zh-CN')).toBe('zh-CN')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('resolves en to en-US', () => {
|
|
225
|
+
expect(resolveAimdEditorLocale('en')).toBe('en-US')
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('createAimdEditorMessages', () => {
|
|
230
|
+
it('creates messages with expected sections', () => {
|
|
231
|
+
const messages = createAimdEditorMessages('en-US')
|
|
232
|
+
expect(messages).toHaveProperty('defaults')
|
|
233
|
+
expect(messages).toHaveProperty('fieldTypes')
|
|
234
|
+
expect(messages).toHaveProperty('mdToolbar')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('merges custom overrides', () => {
|
|
238
|
+
const messages = createAimdEditorMessages('en-US', {
|
|
239
|
+
defaults: { questionStem: 'Custom stem' },
|
|
240
|
+
})
|
|
241
|
+
expect(messages.defaults.questionStem).toBe('Custom stem')
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Field type and toolbar definitions
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
describe('AIMD field type definitions', () => {
|
|
250
|
+
it('has expected field types', () => {
|
|
251
|
+
const types = AIMD_FIELD_TYPE_DEFINITIONS.map(d => d.type)
|
|
252
|
+
expect(types).toContain('var')
|
|
253
|
+
expect(types).toContain('step')
|
|
254
|
+
expect(types).toContain('check')
|
|
255
|
+
expect(types).toContain('quiz')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('createAimdFieldTypes localizes definitions', () => {
|
|
259
|
+
const messages = createAimdEditorMessages('en-US')
|
|
260
|
+
const types = createAimdFieldTypes(messages)
|
|
261
|
+
expect(types.length).toBe(AIMD_FIELD_TYPE_DEFINITIONS.length)
|
|
262
|
+
expect(types[0]).toHaveProperty('label')
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('MD toolbar item definitions', () => {
|
|
267
|
+
it('has expected toolbar actions', () => {
|
|
268
|
+
const actions = MD_TOOLBAR_ITEM_DEFINITIONS.map(d => d.action)
|
|
269
|
+
expect(actions).toContain('h1')
|
|
270
|
+
expect(actions).toContain('bold')
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('createMdToolbarItems localizes items', () => {
|
|
274
|
+
const messages = createAimdEditorMessages('en-US')
|
|
275
|
+
const items = createMdToolbarItems(messages)
|
|
276
|
+
expect(items.length).toBe(MD_TOOLBAR_ITEM_DEFINITIONS.length)
|
|
277
|
+
const nonSeparator = items.find(i => !i.action.startsWith('sep'))
|
|
278
|
+
expect(nonSeparator).toHaveProperty('title')
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
describe('AIMD var type presets', () => {
|
|
283
|
+
it('includes built-in recorder-aware code string presets', () => {
|
|
284
|
+
const messages = createAimdEditorMessages('en-US')
|
|
285
|
+
const presets = createAimdVarTypePresets(messages)
|
|
286
|
+
const presetValues = presets.map(preset => preset.value)
|
|
287
|
+
|
|
288
|
+
expect(presetValues).toContain('CodeStr')
|
|
289
|
+
expect(presetValues).toContain('PyStr')
|
|
290
|
+
expect(presetValues).toContain('JsStr')
|
|
291
|
+
expect(presetValues).toContain('TsStr')
|
|
292
|
+
expect(presetValues).toContain('JsonStr')
|
|
293
|
+
expect(presetValues).toContain('TomlStr')
|
|
294
|
+
expect(presetValues).toContain('YamlStr')
|
|
295
|
+
})
|
|
296
|
+
})
|
package/src/embedded.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { default as AimdSourceEditor } from './vue/AimdSourceEditor.vue'
|
|
2
|
+
export { default as AimdWysiwygEditor } from './vue/AimdWysiwygEditor.vue'
|
|
3
|
+
export {
|
|
4
|
+
createAimdEditorMessages,
|
|
5
|
+
DEFAULT_AIMD_EDITOR_LOCALE,
|
|
6
|
+
resolveAimdEditorLocale,
|
|
7
|
+
} from './vue/locales'
|
|
8
|
+
export {
|
|
9
|
+
createAimdFieldTypes,
|
|
10
|
+
createAimdVarTypePresets,
|
|
11
|
+
} from './vue/types'
|
|
12
|
+
export type {
|
|
13
|
+
AimdFieldType,
|
|
14
|
+
AimdVarTypePresetOption,
|
|
15
|
+
AimdEditorMessages,
|
|
16
|
+
AimdEditorMessagesInput,
|
|
17
|
+
AimdEditorLocale,
|
|
18
|
+
} from './vue/index'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { languages } from "@codingame/monaco-vscode-editor-api"
|
|
2
|
+
|
|
3
|
+
// @ts-ignore - No type definitions available
|
|
4
|
+
import { type languages as languagesNS, conf as markdownConf, language as markdownLanguage } from "@codingame/monaco-vscode-standalone-languages/markdown/markdown"
|
|
5
|
+
import { AimdToken } from "./tokens"
|
|
6
|
+
|
|
7
|
+
const autoClosingPairs: languages.IAutoClosingPairConditional[] = [
|
|
8
|
+
...(markdownConf.autoClosingPairs as any[] || []),
|
|
9
|
+
]
|
|
10
|
+
const surroundingPairs = autoClosingPairs
|
|
11
|
+
|
|
12
|
+
function getTokens(tokens: string, divider = "|"): string[] {
|
|
13
|
+
return tokens.split(divider)
|
|
14
|
+
}
|
|
15
|
+
const keywords: string[] = getTokens("var|var_table|check|step|ref_step|ref_var|ref_table|table_link")
|
|
16
|
+
|
|
17
|
+
export const language: languagesNS.IMonarchLanguage = {
|
|
18
|
+
...markdownLanguage,
|
|
19
|
+
tokenPostfix: ".aimd",
|
|
20
|
+
keywords,
|
|
21
|
+
tokenizer: {
|
|
22
|
+
...markdownLanguage.tokenizer!,
|
|
23
|
+
root: [
|
|
24
|
+
[/^\s*(```|~~~)\s*quiz(?:\s+.*)?\s*$/, {
|
|
25
|
+
token: "string",
|
|
26
|
+
next: "@quizCodeblock",
|
|
27
|
+
nextEmbedded: "yaml",
|
|
28
|
+
}],
|
|
29
|
+
[/^\s*(```|~~~)\s*assigner(?:\s+.*\bruntime\s*=\s*(?:"client"|'client'|client)\b.*)\s*$/, {
|
|
30
|
+
token: "string",
|
|
31
|
+
next: "@assignerCodeblock",
|
|
32
|
+
nextEmbedded: "javascript",
|
|
33
|
+
}],
|
|
34
|
+
[/^\s*(```|~~~)\s*assigner(?:\s+.*)?\s*$/, {
|
|
35
|
+
token: "string",
|
|
36
|
+
next: "@assignerCodeblock",
|
|
37
|
+
nextEmbedded: "python",
|
|
38
|
+
}],
|
|
39
|
+
...markdownLanguage.tokenizer.root,
|
|
40
|
+
{ include: "@aimd" },
|
|
41
|
+
],
|
|
42
|
+
table_body: [...markdownLanguage.tokenizer.table_body, { include: "@aimd" }],
|
|
43
|
+
assignerCodeblock: [
|
|
44
|
+
[/^\s*(```|~~~)\s*$/, { token: "string", next: "@pop", nextEmbedded: "@pop" }],
|
|
45
|
+
[/.*$/, ""],
|
|
46
|
+
],
|
|
47
|
+
quizCodeblock: [
|
|
48
|
+
[/^\s*(```|~~~)\s*$/, { token: "string", next: "@pop", nextEmbedded: "@pop" }],
|
|
49
|
+
[/.*$/, ""],
|
|
50
|
+
],
|
|
51
|
+
aimd: [
|
|
52
|
+
// 1. AIMD Protocol Fields: {{keyword|content}}
|
|
53
|
+
// This rule finds the opening '{{' and switches to the 'protocol' state
|
|
54
|
+
// to handle the special syntax. This has the highest priority.
|
|
55
|
+
[/\{\{/, {
|
|
56
|
+
token: AimdToken.PUNCTUATION_DEFINITION_BEGIN_AIMD,
|
|
57
|
+
bracket: "@open",
|
|
58
|
+
next: "@protocol",
|
|
59
|
+
}],
|
|
60
|
+
|
|
61
|
+
// Links: [text](url)
|
|
62
|
+
[/\[([^\]]+)\]\s*\(([^)]+)\)/, [
|
|
63
|
+
{ token: AimdToken.METATAG_LINK_AIMD }, // [
|
|
64
|
+
{ token: AimdToken.STRING_LINK_DESCRIPTION_AIMD }, // text
|
|
65
|
+
{ token: AimdToken.METATAG_LINK_AIMD }, // ](
|
|
66
|
+
{ token: AimdToken.STRING_LINK_URL_AIMD }, // url
|
|
67
|
+
{ token: AimdToken.METATAG_LINK_AIMD }, // )
|
|
68
|
+
]],
|
|
69
|
+
|
|
70
|
+
// Images: 
|
|
71
|
+
[/!\[([^\]]+)\]\s*\(([^)]+)\)/, [
|
|
72
|
+
{ token: AimdToken.METATAG_IMAGE_AIMD }, // 
|
|
77
|
+
]],
|
|
78
|
+
],
|
|
79
|
+
// This state is active inside {{ ... }}
|
|
80
|
+
protocol: [
|
|
81
|
+
// Match the keywords from your regexes
|
|
82
|
+
// [/(var_table|var|quiz|step|check|ref_step|ref_var|ref_fig|cite|fig)/, AimdToken.KEYWORD_CONTROL_AIMD],
|
|
83
|
+
[/var(\s*\|)/, AimdToken.KEYWORD_VARIABLE_AIMD],
|
|
84
|
+
[/var_table(\s*\|)/, AimdToken.KEYWORD_VARIABLE_TABLE_AIMD],
|
|
85
|
+
[/step(\s*\|)/, AimdToken.KEYWORD_STEP_AIMD],
|
|
86
|
+
[/check(\s*\|)/, AimdToken.KEYWORD_CHECKPOINT_AIMD],
|
|
87
|
+
[/ref_var(\s*\|)/, AimdToken.KEYWORD_REFERENCE_VARIABLE_AIMD],
|
|
88
|
+
[/ref_step(\s*\|)/, AimdToken.KEYWORD_REFERENCE_STEP_AIMD],
|
|
89
|
+
// Match the pipe delimiter
|
|
90
|
+
[/\|/, { token: AimdToken.DELIMITER_PIPE_AIMD, next: "@protocolContent" }],
|
|
91
|
+
// Match the closing '}}' and pop back to the root state
|
|
92
|
+
[/\}\}/, { token: AimdToken.PUNCTUATION_DEFINITION_END_AIMD, bracket: "@close", next: "@pop" }],
|
|
93
|
+
],
|
|
94
|
+
// Content inside protocol after the pipe delimiter - supports type syntax
|
|
95
|
+
protocolContent: [
|
|
96
|
+
// Match the closing '}}' and pop back to the root state
|
|
97
|
+
[/\}\}/, { token: AimdToken.PUNCTUATION_DEFINITION_END_AIMD, bracket: "@close", next: "@popall" }],
|
|
98
|
+
// Match subvars keyword
|
|
99
|
+
[/\bsubvars\b/, AimdToken.KEYWORD_OTHER_SUBVARS_AIMD],
|
|
100
|
+
// Match var keyword (for nested var() calls in subvars)
|
|
101
|
+
[/\bvar\b/, AimdToken.KEYWORD_VARIABLE_AIMD],
|
|
102
|
+
// Match type annotations after colon (e.g., : str, : int, : list[Student])
|
|
103
|
+
[/:/, { token: AimdToken.DELIMITER_COLON_AIMD, next: "@typeAnnotation" }],
|
|
104
|
+
// Match string literals (double or single quoted)
|
|
105
|
+
[/"([^"\\]|\\.)*"/, AimdToken.STRING_QUOTED_AIMD],
|
|
106
|
+
[/'([^'\\]|\\.)*'/, AimdToken.STRING_QUOTED_AIMD],
|
|
107
|
+
// Match numbers (integer and float)
|
|
108
|
+
[/-?\d+\.?\d*/, AimdToken.CONSTANT_NUMERIC_AIMD],
|
|
109
|
+
// Match boolean/null literals
|
|
110
|
+
[/\b(true|false|True|False|null|None)\b/, AimdToken.CONSTANT_LANGUAGE_AIMD],
|
|
111
|
+
// Match equals signs and commas used for parameters
|
|
112
|
+
[/=/, AimdToken.DELIMITER_PARAMETER_AIMD],
|
|
113
|
+
[/,/, AimdToken.DELIMITER_PARAMETER_AIMD],
|
|
114
|
+
// Match brackets for subvars and type params
|
|
115
|
+
[/[[\]()]/, AimdToken.DELIMITER_BRACKET_AIMD],
|
|
116
|
+
// Match variable name (identifier at start)
|
|
117
|
+
[/\b\w+\b/, AimdToken.VARIABLE_OTHER_AIMD],
|
|
118
|
+
// Skip whitespace
|
|
119
|
+
[/\s+/, ""],
|
|
120
|
+
],
|
|
121
|
+
// Type annotation state (after colon)
|
|
122
|
+
typeAnnotation: [
|
|
123
|
+
// Match type with generic params like list[Student]
|
|
124
|
+
[/\w+(?:\[[\w,\s]+\])?/, { token: AimdToken.SUPPORT_TYPE_AIMD, next: "@pop" }],
|
|
125
|
+
// Skip whitespace
|
|
126
|
+
[/\s+/, ""],
|
|
127
|
+
// Pop on other characters
|
|
128
|
+
[/./, { token: "@rematch", next: "@pop" }],
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const conf: languagesNS.LanguageConfiguration = {
|
|
134
|
+
...markdownConf,
|
|
135
|
+
autoClosingPairs,
|
|
136
|
+
surroundingPairs,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const completionItemProvider: languagesNS.CompletionItemProvider = {
|
|
140
|
+
provideCompletionItems: (doc: any, position: any) => {
|
|
141
|
+
const suggestions = keywords.map((value) => {
|
|
142
|
+
return {
|
|
143
|
+
label: value,
|
|
144
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
145
|
+
insertText: value,
|
|
146
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
147
|
+
} as languagesNS.CompletionItem
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
return { suggestions } as languagesNS.ProviderResult<languagesNS.CompletionList>
|
|
151
|
+
},
|
|
152
|
+
}
|
package/src/monaco.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @airalogy/aimd-editor/monaco
|
|
3
|
+
*
|
|
4
|
+
* AIMD Monaco editor integration
|
|
5
|
+
*
|
|
6
|
+
* This entry provides language config and themes for AIMD.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { language, conf, completionItemProvider } from './language-config'
|
|
10
|
+
export { aimdTokenColors, aimdTheme, createAimdExtendedTheme } from './theme'
|
|
11
|
+
export {
|
|
12
|
+
AimdToken,
|
|
13
|
+
AimdTokenDefinition,
|
|
14
|
+
AimdSuffix,
|
|
15
|
+
DelimiterDefinition,
|
|
16
|
+
KeywordDefinition,
|
|
17
|
+
MarkupDefinition,
|
|
18
|
+
scopeName,
|
|
19
|
+
} from './tokens'
|