@veltra/desktop 1.2.28 → 1.2.30

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 (48) 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/auto-complete/auto-complete.vue.d.ts +2 -2
  4. package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
  5. package/dist/components/cascade/cascade.vue.d.ts +5 -5
  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/date-picker/date-picker.vue.d.ts +2 -2
  18. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
  19. package/dist/components/empty/empty.vue.d.ts +1 -1
  20. package/dist/components/grid-input/grid-input.js +19 -2
  21. package/dist/components/grid-input/grid-input.js.map +1 -1
  22. package/dist/components/input/input.vue.d.ts +8 -8
  23. package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
  24. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
  25. package/dist/components/number/number.vue.d.ts +1 -1
  26. package/dist/components/number-input/number-input.vue.d.ts +4 -4
  27. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  28. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  29. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  30. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  31. package/dist/components/select/select.vue.d.ts +2 -2
  32. package/dist/components/steps/steps.vue.d.ts +3 -3
  33. package/dist/components/textarea/textarea.vue.d.ts +4 -4
  34. package/dist/components/tip/tip.vue.d.ts +2 -2
  35. package/dist/components/tree-select/tree-select.vue.d.ts +4 -4
  36. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  37. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js +452 -2
  38. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js.map +1 -1
  39. package/dist/types/code-editor.d.ts +3 -2
  40. package/package.json +6 -6
  41. package/src/components/code-editor/code-editor.vue +6 -0
  42. package/src/components/code-editor/lang-loaders.ts +2 -1
  43. package/src/components/code-editor/langs/spel/completion.ts +30 -0
  44. package/src/components/code-editor/langs/spel/index.ts +12 -0
  45. package/src/components/code-editor/langs/spel/language.ts +198 -0
  46. package/src/components/code-editor/style.scss +12 -0
  47. package/src/components/grid-input/grid-input.vue +24 -2
  48. package/src/types/code-editor.ts +3 -2
@@ -7,10 +7,10 @@ type __VLS_Slots = {
7
7
  };
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
+ text: boolean;
10
11
  size: "small" | "default" | "large";
11
- circle: boolean;
12
12
  max: number;
13
- text: boolean;
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>;
@@ -13,9 +13,9 @@ declare const __VLS_base: import("vue").DefineComponent<ActionProps, {}, {}, {},
13
13
  onRun?: (() => any) | undefined;
14
14
  }>, {
15
15
  type: ButtonType;
16
+ text: boolean;
16
17
  size: import("@veltra/utils").ComponentSize;
17
18
  circle: boolean;
18
- text: boolean;
19
19
  loading: boolean;
20
20
  inDropdown: boolean;
21
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -22,10 +22,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, _Auto
22
22
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
23
23
  onSelect?: ((value: string) => any) | undefined;
24
24
  }>, {
25
- disabled: boolean;
26
- readonly: boolean;
27
25
  placeholder: string;
28
26
  clearable: boolean;
27
+ disabled: boolean;
28
+ readonly: boolean;
29
29
  allowCustom: boolean;
30
30
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
31
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -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,27 +2,27 @@ 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
14
  labelKey: string;
15
15
  valueKey: string;
16
16
  childrenKey: string;
17
17
  data: Record<string, any>[];
18
- disabled: boolean;
19
- readonly: boolean;
20
18
  placeholder: string;
21
19
  clearable: boolean;
22
20
  filterable: boolean;
23
- visibilityLimit: number;
21
+ disabled: boolean;
22
+ readonly: boolean;
24
23
  separator: string;
25
24
  showFullPath: boolean;
25
+ visibilityLimit: number;
26
26
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
27
  declare const _default: typeof __VLS_export;
28
28
  //#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);
@@ -6,10 +6,10 @@ declare const __VLS_export: import("vue").DefineComponent<DatePickerProps, {}, {
6
6
  "onUpdate:modelValue"?: ((value?: string | number | Date | undefined) => any) | undefined;
7
7
  }>, {
8
8
  type: "date" | "month" | "year";
9
- disabled: boolean;
10
- readonly: boolean;
11
9
  placeholder: string;
12
10
  clearable: boolean;
11
+ disabled: boolean;
12
+ readonly: boolean;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -6,10 +6,10 @@ declare const __VLS_export: import("vue").DefineComponent<DateRangePickerProps,
6
6
  "onUpdate:modelValue"?: ((value?: [string, string] | undefined) => any) | undefined;
7
7
  }>, {
8
8
  type: "date" | "month" | "year";
9
- disabled: boolean;
10
- readonly: boolean;
11
9
  placeholder: [string, string];
12
10
  clearable: boolean;
11
+ disabled: boolean;
12
+ readonly: boolean;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -1,8 +1,8 @@
1
1
  import { EmptyProps } from "../../types/empty.js";
2
2
  //#region src/components/empty/empty.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<EmptyProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<EmptyProps> & Readonly<{}>, {
4
- size: number;
5
4
  text: string;
5
+ size: number;
6
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
7
  declare const _default: typeof __VLS_export;
8
8
  //#endregion
@@ -1,4 +1,4 @@
1
- import { Fragment, computed, createCommentVNode, createElementBlock, defineComponent, getCurrentInstance, normalizeClass, openBlock, ref, renderList, toDisplayString, unref } from "vue";
1
+ import { Fragment, computed, createCommentVNode, createElementBlock, defineComponent, getCurrentInstance, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, watch } from "vue";
2
2
  import { useFallbackProps } from "@veltra/compositions";
3
3
  import { bem } from "@veltra/utils";
4
4
  //#region src/components/grid-input/grid-input.vue
@@ -30,8 +30,25 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
30
30
  const position = ref(-1);
31
31
  const blur = ref(false);
32
32
  const instance = getCurrentInstance();
33
+ /** 将格子数组拼成对外 modelValue */
34
+ const joinValue = (cells) => cells.join(props.separator);
35
+ /** 按分隔符解析 modelValue 为格子内容 */
36
+ const parseValue = (value) => {
37
+ if (!value) return [];
38
+ const sep = props.separator;
39
+ return (sep === "" ? Array.from(value) : value.split(sep)).filter((part) => part !== "").slice(0, props.length);
40
+ };
41
+ watch(() => [
42
+ props.modelValue,
43
+ props.separator,
44
+ props.length
45
+ ], () => {
46
+ const next = parseValue(props.modelValue);
47
+ if (joinValue(next) === joinValue(valueArray.value)) return;
48
+ valueArray.value = next;
49
+ }, { immediate: true });
33
50
  const emitValue = () => {
34
- const value = valueArray.value.join(props.separator);
51
+ const value = joinValue(valueArray.value);
35
52
  emit("input", value);
36
53
  emit("update:modelValue", value);
37
54
  };