@veltra/desktop 1.2.28 → 1.2.29

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 (39) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
  4. package/dist/components/cascade/cascade.vue.d.ts +5 -5
  5. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  6. package/dist/components/code-editor/code-editor.js +7 -2
  7. package/dist/components/code-editor/code-editor.js.map +1 -1
  8. package/dist/components/code-editor/lang-loaders.js +2 -1
  9. package/dist/components/code-editor/lang-loaders.js.map +1 -1
  10. package/dist/components/code-editor/langs/spel/completion.js +88 -0
  11. package/dist/components/code-editor/langs/spel/completion.js.map +1 -0
  12. package/dist/components/code-editor/langs/spel/index.js +12 -0
  13. package/dist/components/code-editor/langs/spel/index.js.map +1 -0
  14. package/dist/components/code-editor/langs/spel/language.js +177 -0
  15. package/dist/components/code-editor/langs/spel/language.js.map +1 -0
  16. package/dist/components/code-editor/style2.css +11 -0
  17. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  18. package/dist/components/input/input.vue.d.ts +2 -2
  19. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  20. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +5 -5
  21. package/dist/components/number-input/number-input.vue.d.ts +2 -2
  22. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  23. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  24. package/dist/components/select/select.vue.d.ts +2 -2
  25. package/dist/components/steps/steps.vue.d.ts +1 -1
  26. package/dist/components/textarea/textarea.vue.d.ts +2 -2
  27. package/dist/components/tip/tip.vue.d.ts +2 -2
  28. package/dist/components/tree-select/tree-select.vue.d.ts +5 -5
  29. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js +452 -2
  30. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js.map +1 -1
  31. package/dist/types/code-editor.d.ts +3 -2
  32. package/package.json +6 -6
  33. package/src/components/code-editor/code-editor.vue +6 -0
  34. package/src/components/code-editor/lang-loaders.ts +2 -1
  35. package/src/components/code-editor/langs/spel/completion.ts +30 -0
  36. package/src/components/code-editor/langs/spel/index.ts +12 -0
  37. package/src/components/code-editor/langs/spel/language.ts +198 -0
  38. package/src/components/code-editor/style.scss +12 -0
  39. package/src/types/code-editor.ts +3 -2
@@ -8,9 +8,9 @@ type __VLS_Slots = {
8
8
  declare const __VLS_base: import("vue").DefineComponent<ActionGroupProps, _ActionGroupExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ActionGroupProps> & Readonly<{}>, {
9
9
  type: import("@veltra/utils").ColorType;
10
10
  size: "small" | "default" | "large";
11
- circle: boolean;
12
- max: number;
13
11
  text: boolean;
12
+ max: number;
13
+ circle: boolean;
14
14
  loading: boolean;
15
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
16
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -14,8 +14,8 @@ declare const __VLS_base: import("vue").DefineComponent<ActionProps, {}, {}, {},
14
14
  }>, {
15
15
  type: ButtonType;
16
16
  size: import("@veltra/utils").ComponentSize;
17
- circle: boolean;
18
17
  text: boolean;
18
+ circle: boolean;
19
19
  loading: boolean;
20
20
  inDropdown: boolean;
21
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -6,20 +6,20 @@ declare const __VLS_base: import("vue").DefineComponent<BatchEditProps, {}, {},
6
6
  "update:selected": (value: Record<string, any> | undefined) => any;
7
7
  "update:checked": (value: Record<string, any>[]) => any;
8
8
  "update:current": (row?: TableRow | undefined) => any;
9
+ "update:data": (value: Record<string, any>[]) => any;
9
10
  "update:rows": (rows: TableRow[]) => any;
10
11
  "update:forest": (rows?: import("@cat-kit/core").Forest<Record<string, unknown>, any> | undefined) => any;
11
12
  "cell-click": (row: TableRow, column: TableColumn, ev: MouseEvent) => any;
12
13
  "row-click": (row: TableRow, ev: MouseEvent) => any;
13
- "update:data": (value: Record<string, any>[]) => any;
14
14
  }, string, import("vue").PublicProps, Readonly<BatchEditProps> & Readonly<{
15
15
  "onUpdate:selected"?: ((value: Record<string, any> | undefined) => any) | undefined;
16
16
  "onUpdate:checked"?: ((value: Record<string, any>[]) => any) | undefined;
17
17
  "onUpdate:current"?: ((row?: TableRow | undefined) => any) | undefined;
18
+ "onUpdate:data"?: ((value: Record<string, any>[]) => any) | undefined;
18
19
  "onUpdate:rows"?: ((rows: TableRow[]) => any) | undefined;
19
20
  "onUpdate:forest"?: ((rows?: import("@cat-kit/core").Forest<Record<string, unknown>, any> | undefined) => any) | undefined;
20
21
  "onCell-click"?: ((row: TableRow, column: TableColumn, ev: MouseEvent) => any) | undefined;
21
22
  "onRow-click"?: ((row: TableRow, ev: MouseEvent) => any) | undefined;
22
- "onUpdate:data"?: ((value: Record<string, any>[]) => any) | undefined;
23
23
  }>, {
24
24
  cols: string | [string, string];
25
25
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -2,26 +2,26 @@ import { CascadeProps } from "../../types/cascade.js";
2
2
  //#region src/components/cascade/cascade.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<CascadeProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
4
  "update:modelValue": (value?: string | string[] | undefined) => any;
5
- change: (...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any;
6
5
  clear: () => any;
6
+ change: (...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any;
7
7
  "update:label": (label?: string | string[] | undefined) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<CascadeProps> & Readonly<{
9
9
  "onUpdate:modelValue"?: ((value?: string | string[] | undefined) => any) | undefined;
10
- onChange?: ((...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any) | undefined;
11
10
  onClear?: (() => any) | undefined;
11
+ onChange?: ((...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any) | undefined;
12
12
  "onUpdate:label"?: ((label?: string | string[] | undefined) => any) | undefined;
13
13
  }>, {
14
+ disabled: boolean;
15
+ readonly: boolean;
14
16
  labelKey: string;
15
17
  valueKey: string;
16
18
  childrenKey: string;
17
19
  data: Record<string, any>[];
18
- disabled: boolean;
19
- readonly: boolean;
20
20
  placeholder: string;
21
21
  clearable: boolean;
22
22
  filterable: boolean;
23
- visibilityLimit: number;
24
23
  separator: string;
24
+ visibilityLimit: number;
25
25
  showFullPath: boolean;
26
26
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
27
  declare const _default: typeof __VLS_export;
@@ -12,10 +12,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
12
12
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
13
13
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
14
14
  }>, {
15
- labelKey: string;
16
- valueKey: string;
17
15
  disabled: boolean;
18
16
  readonly: boolean;
17
+ labelKey: string;
18
+ valueKey: string;
19
19
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
20
  declare const _default: typeof __VLS_export;
21
21
  //#endregion
@@ -6,7 +6,7 @@ import { oneDark } from "../../node_modules/.bun/@codemirror_theme-one-dark@6.1.
6
6
  import { basicSetup } from "./basic-setup.js";
7
7
  import { loadLanguage } from "./lang-loaders.js";
8
8
  import { buildFullDoc, extractBodyFromText, getShellRanges, mapSelectionToBody, shellExtension, textEqualsShellDoc } from "./shell-extension.js";
9
- import { computed, createBlock, createCommentVNode, createElementVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, shallowRef, unref, useModel, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
9
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, shallowRef, toDisplayString, unref, useModel, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
10
10
  import { useFormFallbackProps } from "@veltra/compositions";
11
11
  import { bem, injectFormContext, zIndex } from "@veltra/utils";
12
12
  //#region src/components/code-editor/code-editor.vue
@@ -75,6 +75,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
75
75
  })));
76
76
  /** 实际生效的语言:优先 lang model,否则回落 langs[0] */
77
77
  const activeLang = computed(() => lang.value ?? langs.value[0]);
78
+ /** 仅一种语言时,在选择器位置展示名称 */
79
+ const langLabel = computed(() => langs.value.length === 1 && activeLang.value ? activeLang.value.toUpperCase() : void 0);
78
80
  const shellConfig = computed(() => ({
79
81
  prefix: props.prefix ?? "",
80
82
  suffix: props.suffix ?? ""
@@ -265,7 +267,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
265
267
  "modelValue",
266
268
  "options",
267
269
  "disabled"
268
- ])) : createCommentVNode("v-if", true)]),
270
+ ])) : langLabel.value ? (openBlock(), createElementBlock("span", {
271
+ key: 1,
272
+ class: normalizeClass(unref(cls).e("lang-label"))
273
+ }, toDisplayString(langLabel.value), 3)) : createCommentVNode("v-if", true)]),
269
274
  _: 1
270
275
  }, 8, ["class", "style"]);
271
276
  };
@@ -1 +1 @@
1
- {"version":3,"file":"code-editor.js","names":[],"sources":["../../../src/components/code-editor/code-editor.vue"],"sourcesContent":["<template>\n <u-scroll :class=\"className\" :style=\"rootStyle\" @keyup.enter.stop>\n <div ref=\"container\"></div>\n\n <u-select\n v-if=\"showLangSelect\"\n :class=\"cls.e('lang-select')\"\n v-model=\"lang\"\n size=\"small\"\n :options=\"langOptions\"\n :clearable=\"false\"\n :disabled=\"disabled\"\n />\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Compartment, EditorState, type Extension, type Text } from '@codemirror/state'\nimport { oneDark } from '@codemirror/theme-one-dark'\nimport { EditorView, tooltips } from '@codemirror/view'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, injectFormContext, zIndex } from '@veltra/utils'\nimport {\n computed,\n onBeforeUnmount,\n shallowRef,\n useTemplateRef,\n watch,\n type CSSProperties\n} from 'vue'\n\nimport type { CodeEditorEmits, CodeEditorLang, CodeEditorProps } from '../../types'\nimport { UScroll } from '../scroll'\nimport { USelect } from '../select'\nimport { basicSetup } from './basic-setup'\nimport { loadLanguage } from './lang-loaders'\nimport {\n buildFullDoc,\n extractBodyFromText,\n getShellRanges,\n mapSelectionToBody,\n shellExtension,\n textEqualsShellDoc,\n type ShellConfig\n} from './shell-extension'\n\ndefineOptions({ name: 'UCodeEditor' })\n\nconst props = withDefaults(defineProps<CodeEditorProps>(), {\n disabled: undefined,\n readonly: undefined,\n dark: false,\n defaultLines: 8\n})\n\nconst emit = defineEmits<CodeEditorEmits>()\n\nconst model = defineModel<string>()\nconst lang = defineModel<CodeEditorLang>('lang')\n\nconst cls = bem('code-editor')\n\nconst { formProps } = injectFormContext()\n\nconst { disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n disabled: false,\n readonly: false\n})\n\nconst className = computed<string[]>(() => [\n cls.b,\n bem.is('disabled', disabled.value),\n bem.is('readonly', readonly.value),\n bem.is('dark', props.dark)\n])\n\nconst rootStyle = computed<CSSProperties>(() => ({\n '--u-code-editor-default-lines': Math.max(1, props.defaultLines)\n}))\n\nconst langs = computed(() => props.langs ?? [])\n\nconst showLangSelect = computed(() => langs.value.length > 1)\n\nconst langOptions = computed(() =>\n langs.value.map((value) => ({ label: value.toUpperCase(), value }))\n)\n\n/** 实际生效的语言:优先 lang model,否则回落 langs[0] */\nconst activeLang = computed<CodeEditorLang | undefined>(() => lang.value ?? langs.value[0])\n\nconst shellConfig = computed<ShellConfig>(() => ({\n prefix: props.prefix ?? '',\n suffix: props.suffix ?? ''\n}))\n\nconst containerRef = useTemplateRef('container')\nconst editor = shallowRef<EditorView | null>(null)\n\nconst themeCompartment = new Compartment()\nconst editableCompartment = new Compartment()\nconst readOnlyCompartment = new Compartment()\nconst langCompartment = new Compartment()\nconst shellCompartment = new Compartment()\n\n/** 聚焦时的正文快照,用于失焦时判断是否触发 change */\nlet focusSnapshot = ''\n\n/**\n * 编辑器最近一次写出的正文引用。\n * 用于短路 model → doc 同步,避免每次按键对整篇文档做 O(n) 相等比较。\n */\nlet lastEditorBody: string | undefined\n\n/** 上一帧是否处于 IME composition,用于在 composition 结束时补一次 model 同步 */\nlet wasComposing = false\n\n/** 将编辑器正文写回 v-model(跳过与上次相同的值,避免多余更新) */\nfunction writeModelFromDoc(doc: Text) {\n const nextBody = extractBodyFromText(doc, shellConfig.value)\n if (nextBody === lastEditorBody && nextBody === model.value) return\n lastEditorBody = nextBody\n model.value = nextBody\n}\n\nfunction buildExtensions(): Extension[] {\n return [\n basicSetup,\n tooltips({ parent: document.body }),\n EditorView.theme({ '.cm-tooltip': { zIndex: zIndex() } }),\n themeCompartment.of(props.dark ? oneDark : []),\n editableCompartment.of(EditorView.editable.of(!disabled.value)),\n readOnlyCompartment.of(EditorState.readOnly.of(readonly.value)),\n langCompartment.of([]),\n shellCompartment.of(shellExtension(shellConfig.value)),\n EditorView.updateListener.of((update) => {\n const composing = update.view.composing\n\n // IME 进行中不写回,避免中间态触发父级重渲染;结束后再统一同步\n if (update.docChanged && !composing) {\n writeModelFromDoc(update.state.doc)\n } else if (wasComposing && !composing) {\n writeModelFromDoc(update.state.doc)\n }\n wasComposing = composing\n\n if (!update.focusChanged) return\n if (update.view.hasFocus) {\n focusSnapshot = extractBodyFromText(update.state.doc, shellConfig.value)\n return\n }\n const current = extractBodyFromText(update.state.doc, shellConfig.value)\n if (current !== focusSnapshot) {\n emit('change', current)\n }\n })\n ]\n}\n\nlet editorToken = 0\nlet langToken = 0\n\nasync function applyLanguage() {\n const view = editor.value\n if (!view) return\n const myToken = ++langToken\n const language = activeLang.value\n\n if (!language) {\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure([]) })\n return\n }\n\n const ext = await loadLanguage(language)\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure(ext) })\n}\n\nfunction destroyEditor() {\n ++editorToken\n ++langToken\n editor.value?.destroy()\n editor.value = null\n lastEditorBody = undefined\n wasComposing = false\n}\n\nfunction createEditor() {\n destroyEditor()\n const myToken = ++editorToken\n const parent = containerRef.value\n if (!parent) return\n\n const config = shellConfig.value\n const body = model.value ?? ''\n const doc = buildFullDoc(body, config)\n const { bodyStart } = getShellRanges(doc.length, config)\n lastEditorBody = body\n\n const view = new EditorView({\n doc,\n selection: { anchor: bodyStart, head: bodyStart },\n extensions: buildExtensions(),\n parent\n })\n\n if (myToken !== editorToken) {\n view.destroy()\n return\n }\n\n editor.value = view\n applyLanguage()\n}\n\n/**\n * 将 model 正文同步为完整文档;可选同时 reconfigure shell,\n * 避免 prefix/suffix 变更时「先改扩展、后改文档」导致选区越界。\n */\nfunction syncDocFromModel(\n view: EditorView,\n body: string,\n config: ShellConfig,\n options?: { reconfigureShell?: boolean; prevShell?: ShellConfig }\n) {\n const current = view.state.doc\n const docChanged = !textEqualsShellDoc(current, body, config)\n const reconfigureShell = options?.reconfigureShell ?? false\n\n if (!docChanged && !reconfigureShell) return\n\n const nextDoc = buildFullDoc(body, config)\n const prevShell = options?.prevShell ?? config\n const prevRanges = getShellRanges(current.length, prevShell)\n const nextRanges = getShellRanges(nextDoc.length, config)\n const selection = mapSelectionToBody(view.state.selection, prevRanges, nextRanges, nextDoc.length)\n\n view.dispatch({\n ...(docChanged\n ? {\n changes: { from: 0, to: current.length, insert: nextDoc },\n selection,\n // 仅外壳变更时滚入视口;外部 model 回写不应打断用户滚动位置\n ...(reconfigureShell ? { scrollIntoView: true } : {})\n }\n : { selection }),\n ...(reconfigureShell ? { effects: shellCompartment.reconfigure(shellExtension(config)) } : {}),\n // 绕过 shell 的 change/transactionFilter,避免全量替换被拦截或选区被错误钳制\n filter: false\n })\n}\n\nwatch(\n containerRef,\n (el) => {\n if (el) createEditor()\n else destroyEditor()\n },\n { immediate: true, flush: 'post' }\n)\n\nwatch(activeLang, applyLanguage)\n\n/** langs 变化时,若当前 lang 未设或不在列表中则纠正为 langs[0] */\nwatch(\n langs,\n (list) => {\n if (!list.length) return\n if (!lang.value || !list.includes(lang.value)) {\n lang.value = list[0]\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => props.dark,\n (dark) => {\n editor.value?.dispatch({ effects: themeCompartment.reconfigure(dark ? oneDark : []) })\n }\n)\n\nwatch(disabled, (v) => {\n editor.value?.dispatch({ effects: editableCompartment.reconfigure(EditorView.editable.of(!v)) })\n})\n\nwatch(readonly, (v) => {\n editor.value?.dispatch({ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(v)) })\n})\n\nwatch(\n [model, editor, shellConfig],\n ([v, e], [, , prevShell]) => {\n if (!e) return\n // IME 组字中不接受外部回写,避免打断输入\n if (e.composing) return\n const config = shellConfig.value\n const shellChanged =\n !prevShell || prevShell.prefix !== config.prefix || prevShell.suffix !== config.suffix\n // 编辑器自身写出的 model 无需回写文档\n if (!shellChanged && v === lastEditorBody) return\n const body = v ?? ''\n syncDocFromModel(e, body, config, {\n reconfigureShell: shellChanged,\n prevShell: prevShell ?? config\n })\n lastEditorBody = body\n },\n { immediate: true }\n)\n\nonBeforeUnmount(destroyEditor)\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"code-editor.js","names":[],"sources":["../../../src/components/code-editor/code-editor.vue"],"sourcesContent":["<template>\n <u-scroll :class=\"className\" :style=\"rootStyle\" @keyup.enter.stop>\n <div ref=\"container\"></div>\n\n <u-select\n v-if=\"showLangSelect\"\n :class=\"cls.e('lang-select')\"\n v-model=\"lang\"\n size=\"small\"\n :options=\"langOptions\"\n :clearable=\"false\"\n :disabled=\"disabled\"\n />\n <span v-else-if=\"langLabel\" :class=\"cls.e('lang-label')\">{{ langLabel }}</span>\n </u-scroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Compartment, EditorState, type Extension, type Text } from '@codemirror/state'\nimport { oneDark } from '@codemirror/theme-one-dark'\nimport { EditorView, tooltips } from '@codemirror/view'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, injectFormContext, zIndex } from '@veltra/utils'\nimport {\n computed,\n onBeforeUnmount,\n shallowRef,\n useTemplateRef,\n watch,\n type CSSProperties\n} from 'vue'\n\nimport type { CodeEditorEmits, CodeEditorLang, CodeEditorProps } from '../../types'\nimport { UScroll } from '../scroll'\nimport { USelect } from '../select'\nimport { basicSetup } from './basic-setup'\nimport { loadLanguage } from './lang-loaders'\nimport {\n buildFullDoc,\n extractBodyFromText,\n getShellRanges,\n mapSelectionToBody,\n shellExtension,\n textEqualsShellDoc,\n type ShellConfig\n} from './shell-extension'\n\ndefineOptions({ name: 'UCodeEditor' })\n\nconst props = withDefaults(defineProps<CodeEditorProps>(), {\n disabled: undefined,\n readonly: undefined,\n dark: false,\n defaultLines: 8\n})\n\nconst emit = defineEmits<CodeEditorEmits>()\n\nconst model = defineModel<string>()\nconst lang = defineModel<CodeEditorLang>('lang')\n\nconst cls = bem('code-editor')\n\nconst { formProps } = injectFormContext()\n\nconst { disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n disabled: false,\n readonly: false\n})\n\nconst className = computed<string[]>(() => [\n cls.b,\n bem.is('disabled', disabled.value),\n bem.is('readonly', readonly.value),\n bem.is('dark', props.dark)\n])\n\nconst rootStyle = computed<CSSProperties>(() => ({\n '--u-code-editor-default-lines': Math.max(1, props.defaultLines)\n}))\n\nconst langs = computed(() => props.langs ?? [])\n\nconst showLangSelect = computed(() => langs.value.length > 1)\n\nconst langOptions = computed(() =>\n langs.value.map((value) => ({ label: value.toUpperCase(), value }))\n)\n\n/** 实际生效的语言:优先 lang model,否则回落 langs[0] */\nconst activeLang = computed<CodeEditorLang | undefined>(() => lang.value ?? langs.value[0])\n\n/** 仅一种语言时,在选择器位置展示名称 */\nconst langLabel = computed(() =>\n langs.value.length === 1 && activeLang.value ? activeLang.value.toUpperCase() : undefined\n)\n\nconst shellConfig = computed<ShellConfig>(() => ({\n prefix: props.prefix ?? '',\n suffix: props.suffix ?? ''\n}))\n\nconst containerRef = useTemplateRef('container')\nconst editor = shallowRef<EditorView | null>(null)\n\nconst themeCompartment = new Compartment()\nconst editableCompartment = new Compartment()\nconst readOnlyCompartment = new Compartment()\nconst langCompartment = new Compartment()\nconst shellCompartment = new Compartment()\n\n/** 聚焦时的正文快照,用于失焦时判断是否触发 change */\nlet focusSnapshot = ''\n\n/**\n * 编辑器最近一次写出的正文引用。\n * 用于短路 model → doc 同步,避免每次按键对整篇文档做 O(n) 相等比较。\n */\nlet lastEditorBody: string | undefined\n\n/** 上一帧是否处于 IME composition,用于在 composition 结束时补一次 model 同步 */\nlet wasComposing = false\n\n/** 将编辑器正文写回 v-model(跳过与上次相同的值,避免多余更新) */\nfunction writeModelFromDoc(doc: Text) {\n const nextBody = extractBodyFromText(doc, shellConfig.value)\n if (nextBody === lastEditorBody && nextBody === model.value) return\n lastEditorBody = nextBody\n model.value = nextBody\n}\n\nfunction buildExtensions(): Extension[] {\n return [\n basicSetup,\n tooltips({ parent: document.body }),\n EditorView.theme({ '.cm-tooltip': { zIndex: zIndex() } }),\n themeCompartment.of(props.dark ? oneDark : []),\n editableCompartment.of(EditorView.editable.of(!disabled.value)),\n readOnlyCompartment.of(EditorState.readOnly.of(readonly.value)),\n langCompartment.of([]),\n shellCompartment.of(shellExtension(shellConfig.value)),\n EditorView.updateListener.of((update) => {\n const composing = update.view.composing\n\n // IME 进行中不写回,避免中间态触发父级重渲染;结束后再统一同步\n if (update.docChanged && !composing) {\n writeModelFromDoc(update.state.doc)\n } else if (wasComposing && !composing) {\n writeModelFromDoc(update.state.doc)\n }\n wasComposing = composing\n\n if (!update.focusChanged) return\n if (update.view.hasFocus) {\n focusSnapshot = extractBodyFromText(update.state.doc, shellConfig.value)\n return\n }\n const current = extractBodyFromText(update.state.doc, shellConfig.value)\n if (current !== focusSnapshot) {\n emit('change', current)\n }\n })\n ]\n}\n\nlet editorToken = 0\nlet langToken = 0\n\nasync function applyLanguage() {\n const view = editor.value\n if (!view) return\n const myToken = ++langToken\n const language = activeLang.value\n\n if (!language) {\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure([]) })\n return\n }\n\n const ext = await loadLanguage(language)\n if (myToken !== langToken || editor.value !== view) return\n view.dispatch({ effects: langCompartment.reconfigure(ext) })\n}\n\nfunction destroyEditor() {\n ++editorToken\n ++langToken\n editor.value?.destroy()\n editor.value = null\n lastEditorBody = undefined\n wasComposing = false\n}\n\nfunction createEditor() {\n destroyEditor()\n const myToken = ++editorToken\n const parent = containerRef.value\n if (!parent) return\n\n const config = shellConfig.value\n const body = model.value ?? ''\n const doc = buildFullDoc(body, config)\n const { bodyStart } = getShellRanges(doc.length, config)\n lastEditorBody = body\n\n const view = new EditorView({\n doc,\n selection: { anchor: bodyStart, head: bodyStart },\n extensions: buildExtensions(),\n parent\n })\n\n if (myToken !== editorToken) {\n view.destroy()\n return\n }\n\n editor.value = view\n applyLanguage()\n}\n\n/**\n * 将 model 正文同步为完整文档;可选同时 reconfigure shell,\n * 避免 prefix/suffix 变更时「先改扩展、后改文档」导致选区越界。\n */\nfunction syncDocFromModel(\n view: EditorView,\n body: string,\n config: ShellConfig,\n options?: { reconfigureShell?: boolean; prevShell?: ShellConfig }\n) {\n const current = view.state.doc\n const docChanged = !textEqualsShellDoc(current, body, config)\n const reconfigureShell = options?.reconfigureShell ?? false\n\n if (!docChanged && !reconfigureShell) return\n\n const nextDoc = buildFullDoc(body, config)\n const prevShell = options?.prevShell ?? config\n const prevRanges = getShellRanges(current.length, prevShell)\n const nextRanges = getShellRanges(nextDoc.length, config)\n const selection = mapSelectionToBody(view.state.selection, prevRanges, nextRanges, nextDoc.length)\n\n view.dispatch({\n ...(docChanged\n ? {\n changes: { from: 0, to: current.length, insert: nextDoc },\n selection,\n // 仅外壳变更时滚入视口;外部 model 回写不应打断用户滚动位置\n ...(reconfigureShell ? { scrollIntoView: true } : {})\n }\n : { selection }),\n ...(reconfigureShell ? { effects: shellCompartment.reconfigure(shellExtension(config)) } : {}),\n // 绕过 shell 的 change/transactionFilter,避免全量替换被拦截或选区被错误钳制\n filter: false\n })\n}\n\nwatch(\n containerRef,\n (el) => {\n if (el) createEditor()\n else destroyEditor()\n },\n { immediate: true, flush: 'post' }\n)\n\nwatch(activeLang, applyLanguage)\n\n/** langs 变化时,若当前 lang 未设或不在列表中则纠正为 langs[0] */\nwatch(\n langs,\n (list) => {\n if (!list.length) return\n if (!lang.value || !list.includes(lang.value)) {\n lang.value = list[0]\n }\n },\n { immediate: true }\n)\n\nwatch(\n () => props.dark,\n (dark) => {\n editor.value?.dispatch({ effects: themeCompartment.reconfigure(dark ? oneDark : []) })\n }\n)\n\nwatch(disabled, (v) => {\n editor.value?.dispatch({ effects: editableCompartment.reconfigure(EditorView.editable.of(!v)) })\n})\n\nwatch(readonly, (v) => {\n editor.value?.dispatch({ effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(v)) })\n})\n\nwatch(\n [model, editor, shellConfig],\n ([v, e], [, , prevShell]) => {\n if (!e) return\n // IME 组字中不接受外部回写,避免打断输入\n if (e.composing) return\n const config = shellConfig.value\n const shellChanged =\n !prevShell || prevShell.prefix !== config.prefix || prevShell.suffix !== config.suffix\n // 编辑器自身写出的 model 无需回写文档\n if (!shellChanged && v === lastEditorBody) return\n const body = v ?? ''\n syncDocFromModel(e, body, config, {\n reconfigureShell: shellChanged,\n prevShell: prevShell ?? config\n })\n lastEditorBody = body\n },\n { immediate: true }\n)\n\nonBeforeUnmount(destroyEditor)\n</script>\n"],"mappings":"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"}
@@ -4,7 +4,8 @@ const LANG_LOADERS = {
4
4
  js: () => import("../../node_modules/.bun/@codemirror_lang-javascript@6.2.5/node_modules/@codemirror/lang-javascript/dist/index.js").then((m) => m.javascript({ typescript: true })),
5
5
  sql: () => import("../../node_modules/.bun/@codemirror_lang-sql@6.10.0/node_modules/@codemirror/lang-sql/dist/index.js").then((m) => m.sql()),
6
6
  java: () => import("../../node_modules/.bun/@codemirror_lang-java@6.0.2/node_modules/@codemirror/lang-java/dist/index.js").then((m) => m.java()),
7
- json: () => import("../../node_modules/.bun/@codemirror_lang-json@6.0.2/node_modules/@codemirror/lang-json/dist/index.js").then((m) => m.json())
7
+ json: () => import("../../node_modules/.bun/@codemirror_lang-json@6.0.2/node_modules/@codemirror/lang-json/dist/index.js").then((m) => m.json()),
8
+ spel: () => import("./langs/spel/index.js").then((m) => m.spel())
8
9
  };
9
10
  /** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */
10
11
  function loadLanguage(lang) {
@@ -1 +1 @@
1
- {"version":3,"file":"lang-loaders.js","names":[],"sources":["../../../src/components/code-editor/lang-loaders.ts"],"sourcesContent":["import type { Extension } from '@codemirror/state'\n\nimport type { CodeEditorLang } from '../../types'\n\n/** 语言包动态加载器,新增语言只需在此注册一条 */\nconst LANG_LOADERS: Record<CodeEditorLang, () => Promise<Extension>> = {\n js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),\n sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),\n java: () => import('@codemirror/lang-java').then((m) => m.java()),\n json: () => import('@codemirror/lang-json').then((m) => m.json())\n}\n\n/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */\nexport function loadLanguage(lang: CodeEditorLang): Promise<Extension> {\n return LANG_LOADERS[lang]()\n}\n"],"mappings":";;AAKA,MAAM,eAAiE;CACrE,UAAU,OAAO,mHAA8B,CAAC,MAAM,MAAM,EAAE,WAAW,EAAE,YAAY,KAAK,CAAC,CAAC;CAC9F,WAAW,OAAO,sGAAuB,CAAC,MAAM,MAAM,EAAE,IAAI,CAAC;CAC7D,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;AAClE;;AAGA,SAAgB,aAAa,MAA0C;CACrE,OAAO,aAAa,KAAK,CAAC;AAC5B"}
1
+ {"version":3,"file":"lang-loaders.js","names":[],"sources":["../../../src/components/code-editor/lang-loaders.ts"],"sourcesContent":["import type { Extension } from '@codemirror/state'\n\nimport type { CodeEditorLang } from '../../types'\n\n/** 语言包动态加载器,新增语言只需在此注册一条 */\nconst LANG_LOADERS: Record<CodeEditorLang, () => Promise<Extension>> = {\n js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),\n sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),\n java: () => import('@codemirror/lang-java').then((m) => m.java()),\n json: () => import('@codemirror/lang-json').then((m) => m.json()),\n spel: () => import('./langs/spel').then((m) => m.spel())\n}\n\n/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */\nexport function loadLanguage(lang: CodeEditorLang): Promise<Extension> {\n return LANG_LOADERS[lang]()\n}\n"],"mappings":";;AAKA,MAAM,eAAiE;CACrE,UAAU,OAAO,mHAA8B,CAAC,MAAM,MAAM,EAAE,WAAW,EAAE,YAAY,KAAK,CAAC,CAAC;CAC9F,WAAW,OAAO,sGAAuB,CAAC,MAAM,MAAM,EAAE,IAAI,CAAC;CAC7D,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,uGAAwB,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;CAChE,YAAY,OAAO,wBAAe,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC;AACzD;;AAGA,SAAgB,aAAa,MAA0C;CACrE,OAAO,aAAa,KAAK,CAAC;AAC5B"}
@@ -0,0 +1,88 @@
1
+ import { completeFromList } from "../../../../node_modules/.bun/@codemirror_autocomplete@6.20.3/node_modules/@codemirror/autocomplete/dist/index.js";
2
+ /** 基于关键字列表的补全源 */
3
+ const spelCompletion = completeFromList([
4
+ {
5
+ label: "true",
6
+ type: "constant"
7
+ },
8
+ {
9
+ label: "false",
10
+ type: "constant"
11
+ },
12
+ {
13
+ label: "null",
14
+ type: "constant"
15
+ },
16
+ {
17
+ label: "and",
18
+ type: "keyword"
19
+ },
20
+ {
21
+ label: "or",
22
+ type: "keyword"
23
+ },
24
+ {
25
+ label: "not",
26
+ type: "keyword"
27
+ },
28
+ {
29
+ label: "eq",
30
+ type: "keyword"
31
+ },
32
+ {
33
+ label: "ne",
34
+ type: "keyword"
35
+ },
36
+ {
37
+ label: "lt",
38
+ type: "keyword"
39
+ },
40
+ {
41
+ label: "le",
42
+ type: "keyword"
43
+ },
44
+ {
45
+ label: "gt",
46
+ type: "keyword"
47
+ },
48
+ {
49
+ label: "ge",
50
+ type: "keyword"
51
+ },
52
+ {
53
+ label: "matches",
54
+ type: "keyword"
55
+ },
56
+ {
57
+ label: "between",
58
+ type: "keyword"
59
+ },
60
+ {
61
+ label: "instanceof",
62
+ type: "keyword"
63
+ },
64
+ {
65
+ label: "new",
66
+ type: "keyword"
67
+ },
68
+ {
69
+ label: "T",
70
+ type: "type",
71
+ detail: "T(Type)",
72
+ info: "类型引用"
73
+ },
74
+ {
75
+ label: "#this",
76
+ type: "variable",
77
+ detail: "当前上下文"
78
+ },
79
+ {
80
+ label: "#root",
81
+ type: "variable",
82
+ detail: "根对象"
83
+ }
84
+ ]);
85
+ //#endregion
86
+ export { spelCompletion };
87
+
88
+ //# sourceMappingURL=completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/completion.ts"],"sourcesContent":["import { completeFromList, type Completion } from '@codemirror/autocomplete'\n\n/** SpEL 关键字与常用内置标识补全 */\nconst SPEL_COMPLETIONS: Completion[] = [\n // 字面量\n { label: 'true', type: 'constant' },\n { label: 'false', type: 'constant' },\n { label: 'null', type: 'constant' },\n // 关键字 / 运算符词\n { label: 'and', type: 'keyword' },\n { label: 'or', type: 'keyword' },\n { label: 'not', type: 'keyword' },\n { label: 'eq', type: 'keyword' },\n { label: 'ne', type: 'keyword' },\n { label: 'lt', type: 'keyword' },\n { label: 'le', type: 'keyword' },\n { label: 'gt', type: 'keyword' },\n { label: 'ge', type: 'keyword' },\n { label: 'matches', type: 'keyword' },\n { label: 'between', type: 'keyword' },\n { label: 'instanceof', type: 'keyword' },\n { label: 'new', type: 'keyword' },\n // 类型引用与上下文变量\n { label: 'T', type: 'type', detail: 'T(Type)', info: '类型引用' },\n { label: '#this', type: 'variable', detail: '当前上下文' },\n { label: '#root', type: 'variable', detail: '根对象' }\n]\n\n/** 基于关键字列表的补全源 */\nexport const spelCompletion = completeFromList(SPEL_COMPLETIONS)\n"],"mappings":";;AA6BA,MAAa,iBAAiB,iBAAiB;CAxB7C;EAAE,OAAO;EAAQ,MAAM;CAAW;CAClC;EAAE,OAAO;EAAS,MAAM;CAAW;CACnC;EAAE,OAAO;EAAQ,MAAM;CAAW;CAElC;EAAE,OAAO;EAAO,MAAM;CAAU;CAChC;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAO,MAAM;CAAU;CAChC;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAM,MAAM;CAAU;CAC/B;EAAE,OAAO;EAAW,MAAM;CAAU;CACpC;EAAE,OAAO;EAAW,MAAM;CAAU;CACpC;EAAE,OAAO;EAAc,MAAM;CAAU;CACvC;EAAE,OAAO;EAAO,MAAM;CAAU;CAEhC;EAAE,OAAO;EAAK,MAAM;EAAQ,QAAQ;EAAW,MAAM;CAAO;CAC5D;EAAE,OAAO;EAAS,MAAM;EAAY,QAAQ;CAAQ;CACpD;EAAE,OAAO;EAAS,MAAM;EAAY,QAAQ;CAAM;AAIL,CAAgB"}
@@ -0,0 +1,12 @@
1
+ import { LanguageSupport } from "../../../../node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js";
2
+ import { spelCompletion } from "./completion.js";
3
+ import { spelLanguage } from "./language.js";
4
+ //#region src/components/code-editor/langs/spel/index.ts
5
+ /** SpEL(Spring Expression Language)语言支持:语法高亮 + 基础补全 */
6
+ function spel() {
7
+ return new LanguageSupport(spelLanguage, [spelLanguage.data.of({ autocomplete: spelCompletion })]);
8
+ }
9
+ //#endregion
10
+ export { spel };
11
+
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/index.ts"],"sourcesContent":["import { LanguageSupport } from '@codemirror/language'\n\nimport { spelCompletion } from './completion'\nimport { spelLanguage } from './language'\n\n/** SpEL(Spring Expression Language)语言支持:语法高亮 + 基础补全 */\nexport function spel(): LanguageSupport {\n return new LanguageSupport(spelLanguage, [spelLanguage.data.of({ autocomplete: spelCompletion })])\n}\n\nexport { spelLanguage } from './language'\nexport { spelCompletion } from './completion'\n"],"mappings":";;;;;AAMA,SAAgB,OAAwB;CACtC,OAAO,IAAI,gBAAgB,cAAc,CAAC,aAAa,KAAK,GAAG,EAAE,cAAc,eAAe,CAAC,CAAC,CAAC;AACnG"}
@@ -0,0 +1,177 @@
1
+ import { StreamLanguage } from "../../../../node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js";
2
+ //#region src/components/code-editor/langs/spel/language.ts
3
+ const KEYWORDS = new Set([
4
+ "new",
5
+ "instanceof",
6
+ "matches",
7
+ "between",
8
+ "and",
9
+ "or",
10
+ "not",
11
+ "eq",
12
+ "ne",
13
+ "lt",
14
+ "le",
15
+ "gt",
16
+ "ge"
17
+ ]);
18
+ const BOOL_LITERALS = new Set(["true", "false"]);
19
+ /** 多字符运算符,按长度优先匹配 */
20
+ const OPERATORS = [
21
+ "?.",
22
+ "?:",
23
+ "?[",
24
+ "![",
25
+ "==",
26
+ "!=",
27
+ "<=",
28
+ ">=",
29
+ "&&",
30
+ "||",
31
+ "++",
32
+ "--"
33
+ ];
34
+ function isIdentStart(ch) {
35
+ return /[A-Za-z_]/.test(ch);
36
+ }
37
+ function isIdentChar(ch) {
38
+ return /[A-Za-z0-9_]/.test(ch);
39
+ }
40
+ function tokenString(stream, quote) {
41
+ let escaped = false;
42
+ while (!stream.eol()) {
43
+ const next = stream.next();
44
+ if (next === quote && !escaped) break;
45
+ escaped = !escaped && next === "\\";
46
+ }
47
+ return "string";
48
+ }
49
+ function tokenNumber(stream) {
50
+ stream.match(/^\d+(\.\d+)?([eE][+-]?\d+)?/);
51
+ return "number";
52
+ }
53
+ function tokenIdentifier(stream, state) {
54
+ stream.eatWhile(isIdentChar);
55
+ const word = stream.current();
56
+ state.afterT = false;
57
+ if (state.typeDepth > 0) {
58
+ state.afterDot = false;
59
+ return "typeName";
60
+ }
61
+ if (state.afterDot) {
62
+ state.afterDot = false;
63
+ return "propertyName";
64
+ }
65
+ if (BOOL_LITERALS.has(word)) return "bool";
66
+ if (word === "null") return "null";
67
+ if (KEYWORDS.has(word)) return "keyword";
68
+ if (word === "T" && stream.peek() === "(") {
69
+ state.afterT = true;
70
+ return "typeName";
71
+ }
72
+ return "name";
73
+ }
74
+ function tokenSpel(stream, state) {
75
+ if (stream.eatSpace()) return null;
76
+ const ch = stream.peek();
77
+ if (!ch) return null;
78
+ if (ch === "'" || ch === "\"") {
79
+ stream.next();
80
+ state.afterDot = false;
81
+ state.afterT = false;
82
+ return tokenString(stream, ch);
83
+ }
84
+ if (/\d/.test(ch)) {
85
+ state.afterDot = false;
86
+ state.afterT = false;
87
+ return tokenNumber(stream);
88
+ }
89
+ if (ch === "#") {
90
+ stream.next();
91
+ state.afterDot = false;
92
+ state.afterT = false;
93
+ const peek = stream.peek();
94
+ if (peek && isIdentStart(peek)) {
95
+ stream.eatWhile(isIdentChar);
96
+ return "variableName";
97
+ }
98
+ return "operator";
99
+ }
100
+ for (const op of OPERATORS) if (stream.match(op)) {
101
+ state.afterDot = false;
102
+ state.afterT = false;
103
+ return "operator";
104
+ }
105
+ if ("+-*/%^!=<>&|?:".includes(ch)) {
106
+ stream.next();
107
+ state.afterDot = false;
108
+ state.afterT = false;
109
+ return "operator";
110
+ }
111
+ if (ch === "(") {
112
+ stream.next();
113
+ state.afterDot = false;
114
+ if (state.afterT || state.typeDepth > 0) {
115
+ state.typeDepth++;
116
+ state.afterT = false;
117
+ }
118
+ return "paren";
119
+ }
120
+ if (ch === ")") {
121
+ stream.next();
122
+ state.afterDot = false;
123
+ state.afterT = false;
124
+ if (state.typeDepth > 0) state.typeDepth--;
125
+ return "paren";
126
+ }
127
+ if (ch === "[" || ch === "]") {
128
+ stream.next();
129
+ state.afterDot = false;
130
+ state.afterT = false;
131
+ return "paren";
132
+ }
133
+ if (ch === "{" || ch === "}") {
134
+ stream.next();
135
+ state.afterDot = false;
136
+ state.afterT = false;
137
+ return "brace";
138
+ }
139
+ if (ch === ",") {
140
+ stream.next();
141
+ state.afterDot = false;
142
+ state.afterT = false;
143
+ return "punctuation";
144
+ }
145
+ if (ch === ".") {
146
+ stream.next();
147
+ state.afterDot = true;
148
+ state.afterT = false;
149
+ return "punctuation";
150
+ }
151
+ if (isIdentStart(ch)) return tokenIdentifier(stream, state);
152
+ stream.next();
153
+ state.afterDot = false;
154
+ state.afterT = false;
155
+ return "punctuation";
156
+ }
157
+ /** SpEL StreamLanguage */
158
+ const spelLanguage = StreamLanguage.define({
159
+ name: "spel",
160
+ startState: () => ({
161
+ typeDepth: 0,
162
+ afterDot: false,
163
+ afterT: false
164
+ }),
165
+ token: tokenSpel,
166
+ languageData: { closeBrackets: { brackets: [
167
+ "(",
168
+ "[",
169
+ "{",
170
+ "'",
171
+ "\""
172
+ ] } }
173
+ });
174
+ //#endregion
175
+ export { spelLanguage };
176
+
177
+ //# sourceMappingURL=language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language.js","names":[],"sources":["../../../../../src/components/code-editor/langs/spel/language.ts"],"sourcesContent":["import { StreamLanguage, type StringStream } from '@codemirror/language'\n\n/** 词法状态:跟踪 T(...) 类型引用与属性访问 */\ninterface SpelState {\n /** T(...) 内括号深度,>0 表示处于类型名上下文 */\n typeDepth: number\n /** 刚消费完 `.`,下一标识符按属性名着色 */\n afterDot: boolean\n /** 刚消费完类型引用关键字 `T`,下一 `(` 进入类型上下文 */\n afterT: boolean\n}\n\nconst KEYWORDS = new Set([\n 'new',\n 'instanceof',\n 'matches',\n 'between',\n 'and',\n 'or',\n 'not',\n 'eq',\n 'ne',\n 'lt',\n 'le',\n 'gt',\n 'ge'\n])\n\nconst BOOL_LITERALS = new Set(['true', 'false'])\n\n/** 多字符运算符,按长度优先匹配 */\nconst OPERATORS = ['?.', '?:', '?[', '![', '==', '!=', '<=', '>=', '&&', '||', '++', '--']\n\nfunction isIdentStart(ch: string): boolean {\n return /[A-Za-z_]/.test(ch)\n}\n\nfunction isIdentChar(ch: string): boolean {\n return /[A-Za-z0-9_]/.test(ch)\n}\n\nfunction tokenString(stream: StringStream, quote: string): string {\n let escaped = false\n while (!stream.eol()) {\n const next = stream.next()\n if (next === quote && !escaped) break\n escaped = !escaped && next === '\\\\'\n }\n return 'string'\n}\n\nfunction tokenNumber(stream: StringStream): string {\n stream.match(/^\\d+(\\.\\d+)?([eE][+-]?\\d+)?/)\n return 'number'\n}\n\nfunction tokenIdentifier(stream: StringStream, state: SpelState): string {\n stream.eatWhile(isIdentChar)\n const word = stream.current()\n state.afterT = false\n\n if (state.typeDepth > 0) {\n state.afterDot = false\n return 'typeName'\n }\n\n if (state.afterDot) {\n state.afterDot = false\n return 'propertyName'\n }\n\n if (BOOL_LITERALS.has(word)) return 'bool'\n if (word === 'null') return 'null'\n if (KEYWORDS.has(word)) return 'keyword'\n\n // T(...) 类型引用\n if (word === 'T' && stream.peek() === '(') {\n state.afterT = true\n return 'typeName'\n }\n\n return 'name'\n}\n\nfunction tokenSpel(stream: StringStream, state: SpelState): string | null {\n if (stream.eatSpace()) return null\n\n const ch = stream.peek()\n if (!ch) return null\n\n // 字符串\n if (ch === \"'\" || ch === '\"') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return tokenString(stream, ch)\n }\n\n // 数字\n if (/\\d/.test(ch)) {\n state.afterDot = false\n state.afterT = false\n return tokenNumber(stream)\n }\n\n // #变量 / #this / #root\n if (ch === '#') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n const peek = stream.peek()\n if (peek && isIdentStart(peek)) {\n stream.eatWhile(isIdentChar)\n return 'variableName'\n }\n return 'operator'\n }\n\n // 多字符运算符\n for (const op of OPERATORS) {\n if (stream.match(op)) {\n state.afterDot = false\n state.afterT = false\n return 'operator'\n }\n }\n\n // 单字符运算符\n if ('+-*/%^!=<>&|?:'.includes(ch)) {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'operator'\n }\n\n if (ch === '(') {\n stream.next()\n state.afterDot = false\n if (state.afterT || state.typeDepth > 0) {\n state.typeDepth++\n state.afterT = false\n }\n return 'paren'\n }\n\n if (ch === ')') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n if (state.typeDepth > 0) state.typeDepth--\n return 'paren'\n }\n\n if (ch === '[' || ch === ']') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'paren'\n }\n\n if (ch === '{' || ch === '}') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'brace'\n }\n\n if (ch === ',') {\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'punctuation'\n }\n\n if (ch === '.') {\n stream.next()\n state.afterDot = true\n state.afterT = false\n return 'punctuation'\n }\n\n if (isIdentStart(ch)) {\n return tokenIdentifier(stream, state)\n }\n\n stream.next()\n state.afterDot = false\n state.afterT = false\n return 'punctuation'\n}\n\n/** SpEL StreamLanguage */\nexport const spelLanguage = StreamLanguage.define<SpelState>({\n name: 'spel',\n startState: () => ({ typeDepth: 0, afterDot: false, afterT: false }),\n token: tokenSpel,\n languageData: { closeBrackets: { brackets: ['(', '[', '{', \"'\", '\"'] } }\n})\n"],"mappings":"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"}
@@ -50,6 +50,17 @@
50
50
  .u-code-editor__lang-select input {
51
51
  text-align: right;
52
52
  }
53
+ .u-code-editor__lang-label {
54
+ position: absolute;
55
+ top: 2px;
56
+ right: 8px;
57
+ z-index: 2;
58
+ font-size: 12px;
59
+ line-height: 24px;
60
+ color: var(--u-code-editor-gutter-fg);
61
+ pointer-events: none;
62
+ user-select: none;
63
+ }
53
64
  .u-code-editor .cm-editor {
54
65
  background: transparent;
55
66
  color: var(--u-code-editor-fg);
@@ -11,8 +11,8 @@ declare const __VLS_base: import("vue").DefineComponent<UploaderProps, {}, {}, {
11
11
  }, string, import("vue").PublicProps, Readonly<UploaderProps> & Readonly<{
12
12
  onPick?: ((files: File[]) => any) | undefined;
13
13
  }>, {
14
- tag: string;
15
14
  multiple: boolean;
15
+ tag: string;
16
16
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
17
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
18
18
  declare const _default: typeof __VLS_export;
@@ -15,20 +15,20 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, _Inpu
15
15
  "update:modelValue": (value: string | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value: string) => any;
18
+ clear: () => any;
18
19
  change: (value: string) => any;
19
20
  "suffix:click": (value?: string | undefined) => any;
20
21
  "prefix:click": (value?: string | undefined) => any;
21
22
  focus: (value?: string | undefined) => any;
22
- clear: () => any;
23
23
  blur: (value?: string | undefined) => any;
24
24
  "native:input": (ev: Event) => any;
25
25
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
26
26
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
27
+ onClear?: (() => any) | undefined;
27
28
  onChange?: ((value: string) => any) | undefined;
28
29
  "onSuffix:click"?: ((value?: string | undefined) => any) | undefined;
29
30
  "onPrefix:click"?: ((value?: string | undefined) => any) | undefined;
30
31
  onFocus?: ((value?: string | undefined) => any) | undefined;
31
- onClear?: (() => any) | undefined;
32
32
  onBlur?: ((value?: string | undefined) => any) | undefined;
33
33
  "onNative:input"?: ((ev: Event) => any) | undefined;
34
34
  }>, {
@@ -26,13 +26,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
26
26
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
27
27
  onChange?: ((options: Record<string, any>[]) => any) | undefined;
28
28
  }>, {
29
- labelKey: string;
30
- valueKey: string;
31
29
  disabled: boolean;
32
30
  readonly: boolean;
31
+ minWidth: string;
32
+ labelKey: string;
33
+ valueKey: string;
33
34
  placeholder: string;
34
35
  clearable: boolean;
35
- minWidth: string;
36
36
  visibilityLimit: number;
37
37
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
38
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -14,22 +14,22 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
14
14
  "update:modelValue": (value: (string | number)[]) => any;
15
15
  } & {
16
16
  "update:modelValue": (value: any[]) => any;
17
- change: (checked: Record<string, any>[]) => any;
18
17
  clear: () => any;
18
+ change: (checked: Record<string, any>[]) => any;
19
19
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
20
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
21
- onChange?: ((checked: Record<string, any>[]) => any) | undefined;
22
21
  onClear?: (() => any) | undefined;
22
+ onChange?: ((checked: Record<string, any>[]) => any) | undefined;
23
23
  }>, {
24
+ disabled: boolean;
25
+ readonly: boolean;
26
+ minWidth: string;
24
27
  expandAll: boolean;
25
28
  labelKey: string;
26
29
  valueKey: string;
27
- disabled: boolean;
28
- readonly: boolean;
29
30
  placeholder: string;
30
31
  clearable: boolean;
31
32
  filterable: boolean;
32
- minWidth: string;
33
33
  visibilityLimit: number;
34
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
35
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -13,12 +13,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
13
13
  "update:modelValue": (value: number | undefined) => any;
14
14
  } & {
15
15
  "update:modelValue": (value?: number | undefined) => any;
16
- change: (value?: number | undefined) => any;
17
16
  clear: () => any;
17
+ change: (value?: number | undefined) => any;
18
18
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
19
19
  "onUpdate:modelValue"?: ((value?: number | undefined) => any) | undefined;
20
- onChange?: ((value?: number | undefined) => any) | undefined;
21
20
  onClear?: (() => any) | undefined;
21
+ onChange?: ((value?: number | undefined) => any) | undefined;
22
22
  }>, {
23
23
  disabled: boolean;
24
24
  readonly: boolean;