smart-code-editor 1.0.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.
Files changed (63) hide show
  1. package/README.md +155 -0
  2. package/lib/adapters/vanilla/index.d.ts +3 -0
  3. package/lib/adapters/vanilla/index.d.ts.map +1 -0
  4. package/lib/config/languages.d.ts +21 -0
  5. package/lib/config/languages.d.ts.map +1 -0
  6. package/lib/config/runnerStrategies.d.ts +12 -0
  7. package/lib/config/runnerStrategies.d.ts.map +1 -0
  8. package/lib/config/runnerStrategies_v2.d.ts +31 -0
  9. package/lib/config/runnerStrategies_v2.d.ts.map +1 -0
  10. package/lib/config/themes.d.ts +54 -0
  11. package/lib/config/themes.d.ts.map +1 -0
  12. package/lib/core/BackendRunner.d.ts +78 -0
  13. package/lib/core/BackendRunner.d.ts.map +1 -0
  14. package/lib/core/CodeRunner.d.ts +32 -0
  15. package/lib/core/CodeRunner.d.ts.map +1 -0
  16. package/lib/core/LanguageManager.d.ts +41 -0
  17. package/lib/core/LanguageManager.d.ts.map +1 -0
  18. package/lib/core/LayoutManager.d.ts +59 -0
  19. package/lib/core/LayoutManager.d.ts.map +1 -0
  20. package/lib/core/MonacoWrapper.d.ts +63 -0
  21. package/lib/core/MonacoWrapper.d.ts.map +1 -0
  22. package/lib/core/SmartCodeEditor.d.ts +140 -0
  23. package/lib/core/SmartCodeEditor.d.ts.map +1 -0
  24. package/lib/dev-main.d.ts +2 -0
  25. package/lib/dev-main.d.ts.map +1 -0
  26. package/lib/index.cjs +242 -0
  27. package/lib/index.d.ts +5 -0
  28. package/lib/index.d.ts.map +1 -0
  29. package/lib/index.js +1369 -0
  30. package/lib/index.umd.cjs +242 -0
  31. package/lib/shims-vue.d.ts +4 -0
  32. package/lib/types/index.d.ts +101 -0
  33. package/lib/types/index.d.ts.map +1 -0
  34. package/lib/types/language.d.ts +37 -0
  35. package/lib/types/language.d.ts.map +1 -0
  36. package/lib/types/question.d.ts +75 -0
  37. package/lib/types/question.d.ts.map +1 -0
  38. package/lib/utils/loader.d.ts +9 -0
  39. package/lib/utils/loader.d.ts.map +1 -0
  40. package/lib/utils/markdown.d.ts +2 -0
  41. package/lib/utils/markdown.d.ts.map +1 -0
  42. package/package.json +72 -0
  43. package/src/adapters/vanilla/index.ts +7 -0
  44. package/src/adapters/vue/SmartCodeEditor.vue +1190 -0
  45. package/src/config/languages.ts +273 -0
  46. package/src/config/runnerStrategies.ts +261 -0
  47. package/src/config/runnerStrategies_v2.ts +182 -0
  48. package/src/config/themes.ts +37 -0
  49. package/src/core/BackendRunner.ts +329 -0
  50. package/src/core/CodeRunner.ts +107 -0
  51. package/src/core/LanguageManager.ts +108 -0
  52. package/src/core/LayoutManager.ts +268 -0
  53. package/src/core/MonacoWrapper.ts +173 -0
  54. package/src/core/SmartCodeEditor.ts +1015 -0
  55. package/src/dev-app.vue +488 -0
  56. package/src/dev-main.ts +7 -0
  57. package/src/index.ts +19 -0
  58. package/src/shims-vue.d.ts +4 -0
  59. package/src/types/index.ts +129 -0
  60. package/src/types/language.ts +44 -0
  61. package/src/types/question.ts +98 -0
  62. package/src/utils/loader.ts +69 -0
  63. package/src/utils/markdown.ts +89 -0
@@ -0,0 +1,488 @@
1
+ <template>
2
+ <div class="dev-layout" :class="currentTheme">
3
+ <header class="app-header">
4
+ <div class="brand">
5
+ <h1>Code Editor</h1>
6
+ </div>
7
+
8
+ <div class="header-controls">
9
+ <button
10
+ class="icon-btn"
11
+ @click="toggleQuestionPanel"
12
+ :title="showQuestionPanel ? '隐藏试题' : '显示试题'"
13
+ :class="{ active: showQuestionPanel }"
14
+ >
15
+ 试题面板
16
+ </button>
17
+ <button class="icon-btn" @click="switchTheme" title="切换主题">
18
+ {{ currentTheme === "vs-dark" ? "🌙" : "☀️" }} 主题
19
+ </button>
20
+ <button class="icon-btn" @click="resetCode" title="重置代码">
21
+ 重置
22
+ </button>
23
+ </div>
24
+ </header>
25
+
26
+ <div class="workspace">
27
+ <div v-if="loading" class="loading-container">
28
+ <div class="loading-spinner"></div>
29
+ <p>正在加载题目...</p>
30
+ </div>
31
+
32
+ <div v-else-if="error" class="error-container">
33
+ <div class="error-icon">⚠️</div>
34
+ <p>{{ error }}</p>
35
+ <button @click="location.reload()" class="retry-btn">重试</button>
36
+ </div>
37
+ <!-- :enable-file-system="true" -->
38
+
39
+
40
+ <SmartCodeEditor
41
+ v-else-if="question"
42
+ v-model="code"
43
+ :language="currentLanguage"
44
+ :theme="currentTheme"
45
+ :question="question"
46
+ :config="editorConfig"
47
+ :enable-run="true"
48
+ :custom-runner="runCodeOnBackend"
49
+ :custom-submit-runner="runCodeOnBackend"
50
+ :enable-language-switch="true"
51
+ :show-question-panel="showQuestionPanel"
52
+ @submit="handleSubmit"
53
+ @language-change="handleLanguageChange"
54
+ />
55
+ </div>
56
+
57
+ <footer class="status-bar">
58
+ <div class="left-items">
59
+ <span class="item">{{ currentLanguage }}</span>
60
+ <span class="item">{{ code?.length || 0 }} chars</span>
61
+ </div>
62
+ <div class="right-items">
63
+ <span class="item">Theme: {{ currentTheme }}</span>
64
+ <span class="item">Version: 1.0.0</span>
65
+ </div>
66
+ </footer>
67
+ </div>
68
+ </template>
69
+
70
+ <script>
71
+ import { defineComponent, ref, computed, onMounted } from "vue";
72
+ import SmartCodeEditor from "./adapters/vue/SmartCodeEditor.vue";
73
+
74
+ export default defineComponent({
75
+ name: "DevApp",
76
+ components: {
77
+ SmartCodeEditor,
78
+ },
79
+ setup() {
80
+ const question = ref(null);
81
+ const loading = ref(true);
82
+ const error = ref("");
83
+ const questionId = ref(1);
84
+ onMounted(async () => {
85
+ try {
86
+ const urlParams = new URLSearchParams(window.location.search);
87
+ const qidParam = urlParams.get("question_id");
88
+ questionId.value = qidParam ? parseInt(qidParam, 10) : 1;
89
+
90
+ const res = await fetch(
91
+ `http://192.168.60.98:8080/api/v1/question?question_id=${questionId.value}`,
92
+ );
93
+ if (!res.ok) {
94
+ throw new Error("Failed to fetch question");
95
+ }
96
+ question.value = await res.json();
97
+
98
+ if (question.value && question.value.defaultLanguage) {
99
+ const lang = question.value.defaultLanguage;
100
+ const template = question.value.languageTemplates[lang];
101
+
102
+ if (template) {
103
+ code.value = template; // 加载题目对应的代码模板
104
+ currentLanguage.value = lang; // 同步语言选择
105
+ }
106
+ }
107
+ } catch (e) {
108
+ error.value = "无法加载题目,请确保后端服务已启动 (cargo run)";
109
+ console.error(e);
110
+ } finally {
111
+ loading.value = false;
112
+ }
113
+ });
114
+
115
+ const code = ref("");
116
+
117
+ const currentLanguage = ref("javascript");
118
+ const currentTheme = ref("vs");
119
+ const showQuestionPanel = ref(true);
120
+
121
+ const useQuestionMode = ref(false);
122
+
123
+ const editorConfig = computed(() => ({
124
+ questionMarkdown: {
125
+ editable: true,
126
+ placeholder: "用 Markdown 编写题目...",
127
+ },
128
+ }));
129
+
130
+ const runCodeOnBackend = async (
131
+ code,
132
+ language,
133
+ testCases,
134
+ isSubmit = false,
135
+ ) => {
136
+ try {
137
+ const payload = {
138
+ language,
139
+ code,
140
+ stdin: "",
141
+ testCases: isSubmit ? null : testCases,
142
+ questionId: questionId.value,
143
+ };
144
+
145
+ return await submitCodeAsync(payload);
146
+ } catch (error) {
147
+ console.error("Backend error:", error);
148
+ return {
149
+ output: "",
150
+ error: `后端连接失败: ${error.message}`,
151
+ executionTime: 0,
152
+ status: "error",
153
+ };
154
+ }
155
+ };
156
+
157
+ const submitCodeAsync = async (payload) => {
158
+ try {
159
+ const submitResponse = await fetch(
160
+ "http://192.168.60.98:8080/api/v1/submit",
161
+ {
162
+ method: "POST",
163
+ headers: { "Content-Type": "application/json" },
164
+ body: JSON.stringify(payload),
165
+ },
166
+ );
167
+
168
+ if (!submitResponse.ok) {
169
+ throw new Error(`提交失败: HTTP ${submitResponse.status}`);
170
+ }
171
+
172
+ const { submit_id, status } = await submitResponse.json();
173
+
174
+ // 第二步:轮询结果
175
+ return await pollResult(submit_id);
176
+ } catch (error) {
177
+ console.error("提交错误:", error);
178
+ return {
179
+ output: "",
180
+ error: `提交失败: ${error.message}`,
181
+ executionTime: 0,
182
+ status: "error",
183
+ };
184
+ }
185
+ };
186
+
187
+ // 轮询结果
188
+ const pollResult = async (submitId, maxAttempts = 60, interval = 1000) => {
189
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
190
+ try {
191
+ const response = await fetch(
192
+ `http://192.168.60.98:8080/api/v1/result/${submitId}`,
193
+ );
194
+
195
+ if (!response.ok) {
196
+ throw new Error(`查询失败: HTTP ${response.status}`);
197
+ }
198
+
199
+ const data = await response.json();
200
+
201
+ // 检查状态
202
+ if (data.status === "completed") {
203
+ const result = data.result;
204
+ return {
205
+ output: result.stdout || "",
206
+ error: result.error || result.stderr || "",
207
+ executionTime: result.execution_time_ms,
208
+ status: result.exit_code === 0 ? "success" : "error",
209
+ testResults: result.test_results || [],
210
+ };
211
+ } else if (data.status === "error") {
212
+ return {
213
+ output: "",
214
+ error: data.error || "代码执行出错",
215
+ executionTime: 0,
216
+ status: "error",
217
+ };
218
+ } else if (data.status === "pending" || data.status === "running") {
219
+ // 继续等待
220
+ await new Promise((resolve) => setTimeout(resolve, interval));
221
+ continue;
222
+ }
223
+ } catch (error) {
224
+ console.error("轮询错误:", error);
225
+ // 继续尝试
226
+ await new Promise((resolve) => setTimeout(resolve, interval));
227
+ }
228
+ }
229
+
230
+ // 超时
231
+ return {
232
+ output: "",
233
+ error: "执行超时,请稍后重试",
234
+ executionTime: 0,
235
+ status: "error",
236
+ };
237
+ };
238
+
239
+ const handleSubmit = (data) => {
240
+ console.log("Code submitted:", data);
241
+ };
242
+
243
+ const handleLanguageChange = (language) => {
244
+ currentLanguage.value = language;
245
+ };
246
+
247
+ const toggleQuestionPanel = () => {
248
+ showQuestionPanel.value = !showQuestionPanel.value;
249
+ };
250
+
251
+ const toggleMode = () => {
252
+ useQuestionMode.value = !useQuestionMode.value;
253
+ };
254
+
255
+ const switchTheme = () => {
256
+ currentTheme.value = currentTheme.value === "vs-dark" ? "vs" : "vs-dark";
257
+ };
258
+
259
+ const resetCode = () => {
260
+ code.value = `// 编写你的代码\nconsole.log("Hello, World!");`;
261
+ };
262
+
263
+ const testAPI = () => {
264
+ console.log("=== API 测试 ===");
265
+ console.log("Length:", code.value.length);
266
+ console.log("Lang:", currentLanguage.value);
267
+ };
268
+
269
+ return {
270
+ editorConfig,
271
+ code,
272
+ currentLanguage,
273
+ currentTheme,
274
+ showQuestionPanel,
275
+ useQuestionMode,
276
+ question,
277
+ runCodeOnBackend,
278
+ handleSubmit,
279
+ handleLanguageChange,
280
+ toggleQuestionPanel,
281
+ toggleMode,
282
+ switchTheme,
283
+ resetCode,
284
+ testAPI,
285
+ };
286
+ },
287
+ });
288
+ </script>
289
+
290
+ <style scoped>
291
+ /* 全局布局变量 (Scoped styles will apply to elements within this component) */
292
+ .dev-layout {
293
+ display: flex;
294
+ flex-direction: column;
295
+ height: 100vh;
296
+ background: var(--panel-bg);
297
+ color: var(--text-color);
298
+ font-family:
299
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
300
+ overflow: hidden;
301
+ transition:
302
+ background 0.3s,
303
+ color 0.3s;
304
+ }
305
+
306
+ /* Header 样式 */
307
+ .app-header {
308
+ height: 48px;
309
+ background: var(--header-bg);
310
+ border-bottom: 1px solid var(--border-color);
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: space-between;
314
+ padding: 0 16px;
315
+ user-select: none;
316
+ transition:
317
+ background 0.3s,
318
+ border-color 0.3s;
319
+ }
320
+
321
+ .brand {
322
+ display: flex;
323
+ align-items: center;
324
+ gap: 8px;
325
+ }
326
+
327
+ .brand h1 {
328
+ font-size: 14px;
329
+ font-weight: 600;
330
+ color: var(--text-color);
331
+ margin: 0;
332
+ }
333
+
334
+ .tag {
335
+ font-size: 10px;
336
+ background: #0e639c;
337
+ color: white;
338
+ padding: 2px 6px;
339
+ border-radius: 4px;
340
+ }
341
+
342
+ .header-controls {
343
+ display: flex;
344
+ gap: 8px;
345
+ }
346
+
347
+ .icon-btn {
348
+ background: transparent;
349
+ border: 1px solid transparent;
350
+ color: var(--text-color);
351
+ padding: 4px 10px;
352
+ font-size: 12px;
353
+ border-radius: 4px;
354
+ cursor: pointer;
355
+ transition: all 0.2s;
356
+ display: flex;
357
+ align-items: center;
358
+ gap: 6px;
359
+ }
360
+
361
+ .icon-btn:hover {
362
+ background: var(--hover-bg);
363
+ }
364
+
365
+ .icon-btn.active {
366
+ background: var(--active-bg);
367
+ border-color: var(--border-color);
368
+ }
369
+
370
+ /* 工作区布局 */
371
+ .workspace {
372
+ flex: 1;
373
+ overflow: hidden;
374
+ position: relative;
375
+ background: #F5F6F7;
376
+ }
377
+
378
+ /* 加载状态 */
379
+ .loading-container,
380
+ .error-container {
381
+ display: flex;
382
+ flex-direction: column;
383
+ align-items: center;
384
+ justify-content: center;
385
+ height: 100%;
386
+ color: var(--text-color);
387
+ }
388
+
389
+ .loading-spinner {
390
+ width: 50px;
391
+ height: 50px;
392
+ border: 4px solid rgba(255, 255, 255, 0.1);
393
+ border-top-color: var(--accent-color);
394
+ border-radius: 50%;
395
+ animation: spin 1s linear infinite;
396
+ margin-bottom: 20px;
397
+ }
398
+
399
+ @keyframes spin {
400
+ to {
401
+ transform: rotate(360deg);
402
+ }
403
+ }
404
+
405
+ .loading-container p,
406
+ .error-container p {
407
+ font-size: 16px;
408
+ opacity: 0.8;
409
+ margin: 10px 0;
410
+ }
411
+
412
+ .error-icon {
413
+ font-size: 48px;
414
+ margin-bottom: 10px;
415
+ }
416
+
417
+ .retry-btn {
418
+ margin-top: 20px;
419
+ padding: 10px 24px;
420
+ background: var(--accent-color);
421
+ color: white;
422
+ border: none;
423
+ border-radius: 6px;
424
+ cursor: pointer;
425
+ font-size: 14px;
426
+ transition: opacity 0.2s;
427
+ }
428
+
429
+ .retry-btn:hover {
430
+ opacity: 0.9;
431
+ }
432
+
433
+ /* 状态栏 */
434
+ .status-bar {
435
+ height: 24px;
436
+ background: #007acc;
437
+ color: white;
438
+ display: flex;
439
+ align-items: center;
440
+ justify-content: space-between;
441
+ padding: 0 12px;
442
+ font-size: 12px;
443
+ font-family: sans-serif;
444
+ }
445
+
446
+ .left-items,
447
+ .right-items {
448
+ display: flex;
449
+ gap: 16px;
450
+ }
451
+
452
+ .item {
453
+ display: flex;
454
+ align-items: center;
455
+ gap: 6px;
456
+ }
457
+ </style>
458
+
459
+ <style>
460
+ /* Global styles */
461
+ :root {
462
+ --header-height: 48px;
463
+ --status-bar-height: 24px;
464
+
465
+ /* Dark Theme Default */
466
+ --panel-bg: #1e1e1e;
467
+ --header-bg: #252526;
468
+ --text-color: #cccccc;
469
+ --border-color: #333;
470
+ --hover-bg: #3c3c3c;
471
+ --active-bg: #37373d;
472
+ --accent-color: #0e639c;
473
+ }
474
+
475
+ /* Light Theme Overrides */
476
+ .vs {
477
+ --panel-bg: #ffffff;
478
+ --header-bg: #f3f3f3;
479
+ --text-color: #333333;
480
+ --border-color: #e0e0e0;
481
+ --hover-bg: #e8e8e8;
482
+ --active-bg: #d4d4d4;
483
+ }
484
+
485
+ body {
486
+ margin: 0;
487
+ }
488
+ </style>
@@ -0,0 +1,7 @@
1
+ import { createApp } from "vue";
2
+ import DevApp from "./dev-app.vue";
3
+
4
+ const app = createApp(DevApp);
5
+ app.mount("#app");
6
+
7
+ console.log("Smart Code Editor 开发环境已启动");
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ // 导出核心类
2
+ export { SmartCodeEditor } from "./core/SmartCodeEditor";
3
+
4
+ // 导出类型
5
+ export type {
6
+ SmartCodeEditorOptions,
7
+ LanguageConfig,
8
+ RunResult,
9
+ RunnerStrategy,
10
+ MonacoEditorOptions,
11
+ } from "./types";
12
+
13
+ // 导出配置
14
+ export {
15
+ LANGUAGE_CONFIGS,
16
+ getAllLanguages,
17
+ getLanguageConfig,
18
+ } from "./config/languages";
19
+ export { THEME_CONFIGS, getAllThemes, getThemeConfig } from "./config/themes";
@@ -0,0 +1,4 @@
1
+ declare module "*.vue" {
2
+ const component: any;
3
+ export default component;
4
+ }
@@ -0,0 +1,129 @@
1
+ // Export new modular types
2
+ export * from "./language";
3
+ export * from "./question";
4
+
5
+ /**
6
+ * 代码运行结果
7
+ */
8
+ /**
9
+ * UI 绑定的测试用例(支持编辑)
10
+ */
11
+ export interface UITestCase {
12
+ id: string | number;
13
+ label?: string;
14
+ inputs: {
15
+ name: string;
16
+ value: string;
17
+ }[];
18
+ expected: string;
19
+ }
20
+
21
+ /**
22
+ * 运行时的测试用例
23
+ */
24
+ export interface TestCase {
25
+ id: string | number;
26
+ input: any[]; // 函数参数列表
27
+ expected?: any; // 预期输出
28
+ }
29
+
30
+ /**
31
+ * 单个用例的运行结果
32
+ */
33
+ export interface TestCaseResult {
34
+ id: string | number;
35
+ status: "passed" | "failed" | "error";
36
+ input: any[];
37
+ output: any; // 实际输出
38
+ expected?: any;
39
+ log?: string; // 该用例执行过程中的 console.log
40
+ error?: string; // 错误信息
41
+ executionTime?: number;
42
+ }
43
+
44
+ /**
45
+ * 代码运行结果
46
+ */
47
+ export interface RunResult {
48
+ output: string; // 输出内容
49
+ error?: string; // 错误信息
50
+ executionTime: number; // 执行时间(毫秒)
51
+ status: "success" | "error" | "timeout"; // 状态
52
+ testResults?: TestCaseResult[]; // 新增:结构化测试结果
53
+ }
54
+
55
+ /**
56
+ * 运行策略函数类型
57
+ */
58
+ export type RunnerStrategy = (
59
+ code: string,
60
+ testCases?: TestCase[],
61
+ ) => Promise<RunResult>;
62
+
63
+ /**
64
+ * 编辑器选项
65
+ */
66
+ export interface SmartCodeEditorOptions {
67
+ // 容器配置
68
+ container: HTMLElement | string;
69
+
70
+ // 编辑器配置
71
+ language?: string; // 默认语言
72
+ theme?: string; // 主题
73
+ value?: string; // 初始代码
74
+ readOnly?: boolean; // 只读模式
75
+
76
+ // 布局配置
77
+ showQuestionPanel?: boolean; // 显示试题面板
78
+ questionContent?: string; // 试题内容
79
+ questionMarkdown?: import("./question").QuestionMarkdownOptions; // Markdown 题目配置
80
+ defaultSplitRatio?: number; // 默认分割比例 0-1
81
+
82
+ // 语言配置
83
+ supportedLanguages?: string[]; // 支持的语言列表
84
+ enableLanguageSwitch?: boolean; // 允许切换语言
85
+
86
+ // 运行配置
87
+ enableRun?: boolean; // 启用代码运行
88
+ disableLocalRun?: boolean; // 禁用本地运行(用于完全托管给外部)
89
+ customRunner?: (
90
+ code: string,
91
+ language: string,
92
+ testCases?: TestCase[],
93
+ ) => Promise<RunResult>; // 自定义运行器
94
+ runTimeout?: number; // 运行超时时间(毫秒)
95
+ enableSubmit?: boolean; // 启用代码提交
96
+
97
+ // ===== 新增:题目配置 =====
98
+
99
+ /** 题目完整配置(可选) */
100
+ questionConfig?: import("./question").QuestionConfig;
101
+
102
+ /** 自定义语言模板映射(可选,会覆盖 questionConfig) */
103
+ languageTemplates?: import("./question").LanguageTemplates;
104
+
105
+ // 回调函数
106
+ onChange?: (value: string) => void;
107
+ onRun?: (result: RunResult) => void;
108
+ onSubmit?: (code: string, language: string) => void;
109
+ onLanguageChange?: (language: string) => void;
110
+ // 智能提示配置
111
+ suggestOnTriggerCharacters?: boolean;
112
+ quickSuggestions?: boolean | object;
113
+ }
114
+
115
+ /**
116
+ * Monaco Editor 配置选项
117
+ */
118
+ export interface MonacoEditorOptions {
119
+ language?: string;
120
+ theme?: string;
121
+ value?: string;
122
+ readOnly?: boolean;
123
+ minimap?: { enabled: boolean };
124
+ fontSize?: number;
125
+ lineNumbers?: "on" | "off";
126
+ automaticLayout?: boolean;
127
+ suggestOnTriggerCharacters?: boolean;
128
+ quickSuggestions?: boolean | object;
129
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 语言静态元数据
3
+ * 定义语言的基础属性(图标、Monaco ID、文件扩展名等)
4
+ */
5
+ export interface LanguageMetadata {
6
+ /** 语言唯一标识 */
7
+ id: string;
8
+
9
+ /** 显示名称 */
10
+ name: string;
11
+
12
+ /** Monaco Editor 语言 ID */
13
+ monacoId: string;
14
+
15
+ /** 图标 emoji(可选) */
16
+ icon?: string;
17
+
18
+ /** 文件扩展名列表 */
19
+ extensions: string[];
20
+
21
+ /**
22
+ * 默认代码模板(可选,用于向后兼容)
23
+ * 当外部未提供题目配置时使用
24
+ */
25
+ fallbackTemplate?: string;
26
+
27
+ /**
28
+ * 是否可运行
29
+ * 标记该语言是否支持代码执行
30
+ */
31
+ canRun?: boolean;
32
+ }
33
+
34
+ /**
35
+ * 语言配置(包含运行时信息)
36
+ * 合并了静态元数据和动态配置后的完整语言配置
37
+ */
38
+ export interface LanguageConfig extends LanguageMetadata {
39
+ /** 运行方式类型 */
40
+ runnerType?: "browser" | "wasm" | "remote";
41
+
42
+ /** 当前使用的代码模板(运行时确定) */
43
+ currentTemplate?: string;
44
+ }