@shwfed/config 2.5.3 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/mcp.mjs +1220 -1045
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +7 -1
  4. package/dist/preview/assets/config-B5FFtD0s.js +1 -0
  5. package/dist/preview/assets/config-BpWP2vu_.js +1 -0
  6. package/dist/preview/assets/{config-ekMG8PO0.js → config-C2OqUTNd.js} +1 -1
  7. package/dist/preview/assets/{config-BVjCjkAL.js → config-D7cjMBeK.js} +1 -1
  8. package/dist/preview/assets/config-DhORWTZC.js +1 -0
  9. package/dist/preview/assets/config-DuuYvFG_.js +1 -0
  10. package/dist/preview/assets/config-dpwN2-UY.js +1 -0
  11. package/dist/preview/assets/{config-DN6k6pjm.js → config-eP0EblYK.js} +1 -1
  12. package/dist/preview/assets/{config-CtGduGcR.js → config-hs_pZ5MM.js} +1 -1
  13. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-I7unZVnH.js → definition.vue_vue_type_script_setup_true_lang-B8-Uydoy.js} +1 -1
  14. package/dist/preview/assets/index-BGFrUxgg.js +680 -0
  15. package/dist/preview/assets/index-BoGW90Pq.css +1 -0
  16. package/dist/preview/assets/index-Bw16PZhL.js +1 -0
  17. package/dist/preview/assets/index-CG261V86.js +1 -0
  18. package/dist/preview/assets/item-aVe51Gy6.js +1 -0
  19. package/dist/preview/assets/runtime-3rNI0KDH.js +1 -0
  20. package/dist/preview/assets/runtime-BOn8EwHL.js +1 -0
  21. package/dist/preview/assets/runtime-Bwr-rb58.js +1 -0
  22. package/dist/preview/assets/runtime-CXQuhSAX.js +1 -0
  23. package/dist/preview/assets/runtime-CYGmRjmI.js +1 -0
  24. package/dist/preview/assets/runtime-Ca79Fs6I.js +1 -0
  25. package/dist/preview/assets/runtime-CfVt6IWe.js +1 -0
  26. package/dist/preview/assets/runtime-DYj-R8SZ.js +1 -0
  27. package/dist/preview/assets/runtime-Dd1GqYeP.js +1 -0
  28. package/dist/preview/index.html +2 -2
  29. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/config.d.vue.ts +59 -0
  30. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/config.vue +452 -0
  31. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/config.vue.d.ts +59 -0
  32. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/item.d.vue.ts +7 -0
  33. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/item.vue +112 -0
  34. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/item.vue.d.ts +7 -0
  35. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/runtime.d.vue.ts +59 -0
  36. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/runtime.vue +47 -0
  37. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/runtime.vue.d.ts +59 -0
  38. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/schema.d.ts +92 -0
  39. package/dist/runtime/components/config/blocks/2026-06-01/com.shwfed.block.animated.number/schema.js +117 -0
  40. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +4 -4
  41. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +4 -4
  42. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +6 -6
  43. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +6 -6
  44. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
  45. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
  46. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +4 -4
  47. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +4 -4
  48. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/schema.js +1 -1
  49. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/schema.js +1 -1
  50. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +2 -2
  51. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +2 -2
  52. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.d.vue.ts +2 -2
  53. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.vue.d.ts +2 -2
  54. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +2 -2
  55. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +2 -2
  56. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.d.vue.ts +2 -2
  57. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.vue.d.ts +2 -2
  58. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +2 -2
  59. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +2 -2
  60. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +2 -2
  61. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +2 -2
  62. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  63. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  64. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  65. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  66. package/dist/runtime/components/table/config.vue +17 -1
  67. package/dist/runtime/components/table/index.vue +1 -0
  68. package/dist/runtime/components/table/row-provider.d.vue.ts +1 -0
  69. package/dist/runtime/components/table/row-provider.vue +9 -2
  70. package/dist/runtime/components/table/row-provider.vue.d.ts +1 -0
  71. package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
  72. package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
  73. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
  74. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
  75. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
  76. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
  77. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
  78. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
  79. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.d.vue.ts +22 -0
  80. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue +134 -0
  81. package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue.d.ts +22 -0
  82. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +2 -2
  83. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +71 -50
  84. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +2 -2
  85. package/dist/runtime/components/ui/expression-editor/cel-language.d.ts +16 -0
  86. package/dist/runtime/components/ui/expression-editor/cel-language.js +114 -0
  87. package/dist/runtime/components/ui/expression-editor/chip-extension.d.ts +5 -0
  88. package/dist/runtime/components/ui/expression-editor/chip-extension.js +123 -0
  89. package/dist/runtime/components/ui/expression-editor/picker-entries.d.ts +17 -0
  90. package/dist/runtime/components/ui/expression-editor/picker-entries.js +34 -0
  91. package/dist/runtime/components/ui/expression-editor/scope-refs.d.ts +20 -0
  92. package/dist/runtime/components/ui/expression-editor/scope-refs.js +39 -0
  93. package/dist/runtime/share/expression.js +9 -0
  94. package/dist/runtime/utils/cel-context.d.ts +36 -0
  95. package/dist/runtime/utils/cel-context.js +39 -0
  96. package/package.json +7 -1
  97. package/dist/preview/assets/config-C5TWIUX-.js +0 -1
  98. package/dist/preview/assets/config-CH28q4_H.js +0 -1
  99. package/dist/preview/assets/config-D9tBv8LD.js +0 -1
  100. package/dist/preview/assets/config-DxB8qwAu.js +0 -1
  101. package/dist/preview/assets/config-U359oLg8.js +0 -1
  102. package/dist/preview/assets/index-CZ-XSjS_.js +0 -668
  103. package/dist/preview/assets/index-HfGseg4M.js +0 -1
  104. package/dist/preview/assets/index-nvAUAYGM.css +0 -1
  105. package/dist/preview/assets/runtime-27bDIkKv.js +0 -1
  106. package/dist/preview/assets/runtime-BAQ6ezPo.js +0 -1
  107. package/dist/preview/assets/runtime-BKQvzSax.js +0 -1
  108. package/dist/preview/assets/runtime-BafXFP0s.js +0 -1
  109. package/dist/preview/assets/runtime-CAzVJ-if.js +0 -1
  110. package/dist/preview/assets/runtime-CQly-3c3.js +0 -1
  111. package/dist/preview/assets/runtime-DJgDXKfE.js +0 -1
  112. package/dist/preview/assets/runtime-Dhd51Wyr.js +0 -1
  113. package/dist/preview/assets/runtime-x8IdC454.js +0 -1
@@ -0,0 +1,134 @@
1
+ <script setup>
2
+ import { Compartment, EditorState } from "@codemirror/state";
3
+ import { EditorView, keymap, placeholder as cmPlaceholder } from "@codemirror/view";
4
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
5
+ import { onBeforeUnmount, onMounted, useTemplateRef, watch } from "vue";
6
+ import { cn } from "../../../utils/cn";
7
+ import { celHighlighting } from "./cel-language";
8
+ import { scopeChips, scopeLookupFacet } from "./chip-extension";
9
+ defineOptions({ name: "UiCodeMirrorInput" });
10
+ const props = defineProps({
11
+ class: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
12
+ defaultValue: { type: String, required: false },
13
+ modelValue: { type: String, required: false },
14
+ multiline: { type: Boolean, required: false },
15
+ placeholder: { type: String, required: false },
16
+ scopeLookup: { type: Map, required: true }
17
+ });
18
+ const emits = defineEmits(["update:modelValue"]);
19
+ const host = useTemplateRef("host");
20
+ let view = null;
21
+ const lookupCompartment = new Compartment();
22
+ const modeCompartment = new Compartment();
23
+ let applyingExternal = false;
24
+ const editorTheme = EditorView.theme({
25
+ "&": {
26
+ fontSize: "0.875rem",
27
+ color: "var(--color-zinc-700)",
28
+ backgroundColor: "transparent"
29
+ },
30
+ "&.cm-focused": {
31
+ outline: "none"
32
+ },
33
+ ".cm-content": {
34
+ fontFamily: "var(--font-mono)",
35
+ padding: "7px 0",
36
+ lineHeight: "1.5",
37
+ caretColor: "var(--color-zinc-700)"
38
+ },
39
+ ".cm-placeholder": {
40
+ color: "var(--color-zinc-200)"
41
+ },
42
+ ".cm-scroller": {
43
+ fontFamily: "var(--font-mono)",
44
+ lineHeight: "1.5"
45
+ },
46
+ ".cm-line": {
47
+ padding: "0"
48
+ }
49
+ });
50
+ function modeExtension(multiline) {
51
+ if (multiline) return EditorView.lineWrapping;
52
+ return EditorState.transactionFilter.of((tr) => {
53
+ if (!tr.docChanged) return tr;
54
+ let hasNewline = false;
55
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
56
+ if (inserted.lines > 1) hasNewline = true;
57
+ });
58
+ return hasNewline ? [] : tr;
59
+ });
60
+ }
61
+ function makeState(doc) {
62
+ return EditorState.create({
63
+ doc,
64
+ extensions: [
65
+ history(),
66
+ keymap.of([...defaultKeymap, ...historyKeymap]),
67
+ cmPlaceholder(props.placeholder ?? ""),
68
+ lookupCompartment.of(scopeLookupFacet.of(props.scopeLookup)),
69
+ modeCompartment.of(modeExtension(!!props.multiline)),
70
+ editorTheme,
71
+ // CEL syntax colours sit *under* the chips: the chip plugin's
72
+ // `Decoration.replace` widgets hide the `__scopes__[...]` spans, so the
73
+ // marks here only show through on ordinary expression text.
74
+ celHighlighting(),
75
+ scopeChips(),
76
+ EditorView.contentAttributes.of({ "aria-label": props.placeholder ?? "Expression" }),
77
+ EditorView.updateListener.of((update) => {
78
+ if (!update.docChanged || applyingExternal) return;
79
+ emits("update:modelValue", update.state.doc.toString());
80
+ })
81
+ ]
82
+ });
83
+ }
84
+ onMounted(() => {
85
+ if (!host.value) return;
86
+ view = new EditorView({
87
+ state: makeState(props.modelValue ?? props.defaultValue ?? ""),
88
+ parent: host.value
89
+ });
90
+ });
91
+ onBeforeUnmount(() => {
92
+ view?.destroy();
93
+ view = null;
94
+ });
95
+ watch(() => props.modelValue, (next) => {
96
+ if (!view) return;
97
+ const incoming = next ?? "";
98
+ if (incoming === view.state.doc.toString()) return;
99
+ applyingExternal = true;
100
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: incoming } });
101
+ applyingExternal = false;
102
+ });
103
+ watch(() => props.scopeLookup, (lookup) => {
104
+ view?.dispatch({ effects: lookupCompartment.reconfigure(scopeLookupFacet.of(lookup)) });
105
+ });
106
+ watch(() => props.multiline, (multiline) => {
107
+ view?.dispatch({ effects: modeCompartment.reconfigure(modeExtension(!!multiline)) });
108
+ });
109
+ function insertAtSelection(text) {
110
+ if (!view) return;
111
+ const { from, to } = view.state.selection.main;
112
+ view.dispatch({
113
+ changes: { from, to, insert: text },
114
+ selection: { anchor: from + text.length }
115
+ });
116
+ view.focus();
117
+ }
118
+ function focus() {
119
+ view?.focus();
120
+ }
121
+ defineExpose({ insertAtSelection, focus });
122
+ </script>
123
+
124
+ <template>
125
+ <div
126
+ ref="host"
127
+ data-slot="input-group-control"
128
+ :class="cn(
129
+ 'flex-1 min-w-0 self-stretch overflow-x-auto px-3',
130
+ props.multiline ? '[&_.cm-content]:min-h-16' : '[&_.cm-scroller]:items-center',
131
+ props.class
132
+ )"
133
+ />
134
+ </template>
@@ -0,0 +1,22 @@
1
+ import type { HTMLAttributes } from 'vue';
2
+ import type { ScopeInfo } from './scope-refs.js';
3
+ type __VLS_Props = {
4
+ class?: HTMLAttributes['class'];
5
+ defaultValue?: string;
6
+ modelValue?: string;
7
+ multiline?: boolean;
8
+ placeholder?: string;
9
+ scopeLookup: Map<string, ScopeInfo>;
10
+ };
11
+ declare function insertAtSelection(text: string): void;
12
+ declare function focus(): void;
13
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
14
+ insertAtSelection: typeof insertAtSelection;
15
+ focus: typeof focus;
16
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
+ "update:modelValue": (payload: string) => any;
18
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ "onUpdate:modelValue"?: ((payload: string) => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -13,11 +13,11 @@ type __VLS_Props = {
13
13
  resultType?: string | string[];
14
14
  extraVars?: Record<string, VarSpec>;
15
15
  };
16
- declare var __VLS_14: {}, __VLS_108: {};
16
+ declare var __VLS_14: {}, __VLS_130: {};
17
17
  type __VLS_Slots = {} & {
18
18
  leading?: (props: typeof __VLS_14) => any;
19
19
  } & {
20
- trailing?: (props: typeof __VLS_108) => any;
20
+ trailing?: (props: typeof __VLS_130) => any;
21
21
  };
22
22
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
23
23
  "update:modelValue": (payload: string) => any;
@@ -1,11 +1,13 @@
1
1
  <script setup>
2
2
  import { Icon } from "@iconify/vue";
3
- import { computed, ref, useSlots, watch } from "vue";
4
- import { injectCELContext } from "../../../utils/cel-context";
3
+ import { computed, ref, useSlots, useTemplateRef, watch } from "vue";
4
+ import { injectCELContext, useScopeAncestry } from "../../../utils/cel-context";
5
5
  import { Markdown } from "../markdown";
6
- import { cn } from "../../../utils/cn";
6
+ import CodeMirrorInput from "./CodeMirrorInput.vue";
7
+ import { buildScopeLookup } from "./scope-refs";
8
+ import { buildScopeEntries, buildVarEntries } from "./picker-entries";
7
9
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../command";
8
- import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupTextarea } from "../input-group";
10
+ import { InputGroup, InputGroupAddon, InputGroupButton } from "../input-group";
9
11
  import { Popover, PopoverContent, PopoverTrigger } from "../popover";
10
12
  defineOptions({ name: "UiExpressionEditor" });
11
13
  const props = defineProps({
@@ -19,31 +21,31 @@ const props = defineProps({
19
21
  });
20
22
  const emits = defineEmits(["update:modelValue"]);
21
23
  const celContext = injectCELContext();
22
- const mergedEntries = computed(() => {
23
- const out = Object.entries(celContext).map(
24
- ([name, meta]) => ({ name, meta })
25
- );
26
- const seen = new Set(out.map((e) => e.name));
27
- for (const [name, meta] of Object.entries(props.extraVars ?? {})) {
28
- if (seen.has(name)) continue;
29
- out.push({ name, meta });
30
- }
31
- return out;
32
- });
33
- const hasVars = computed(() => mergedEntries.value.length > 0);
24
+ const scopeAncestry = useScopeAncestry();
25
+ const varEntries = computed(() => buildVarEntries(celContext, props.extraVars));
26
+ const scopeEntries = computed(() => buildScopeEntries(scopeAncestry.value.slice(1)));
27
+ const scopeLookup = computed(() => buildScopeLookup(scopeAncestry.value));
28
+ const hasVars = computed(() => varEntries.value.length > 0 || scopeEntries.value.length > 0);
34
29
  const open = ref(false);
35
30
  const entries = ref([]);
31
+ const shownVars = computed(() => entries.value.filter((e) => e.group === "var"));
32
+ const shownScopes = computed(() => entries.value.filter((e) => e.group === "scope"));
36
33
  const hoveredName = ref(null);
34
+ const editorRef = useTemplateRef("editor");
35
+ function insertVariable(text) {
36
+ editorRef.value?.insertAtSelection(text);
37
+ open.value = false;
38
+ }
37
39
  watch(open, (v) => {
38
40
  if (v) {
39
- entries.value = mergedEntries.value;
40
- hoveredName.value = entries.value[0]?.name ?? null;
41
+ entries.value = [...varEntries.value, ...scopeEntries.value];
42
+ hoveredName.value = entries.value[0]?.id ?? null;
41
43
  }
42
44
  });
43
- const hoveredMeta = computed(
44
- () => entries.value.find((e) => e.name === hoveredName.value)?.meta ?? null
45
+ const hoveredEntry = computed(
46
+ () => entries.value.find((e) => e.id === hoveredName.value) ?? null
45
47
  );
46
- const hoveredDescription = computed(() => hoveredMeta.value?.description ?? "");
48
+ const hoveredDescription = computed(() => hoveredEntry.value?.description ?? "");
47
49
  const resultTypeLabel = computed(() => {
48
50
  const rt = props.resultType;
49
51
  if (!rt) return null;
@@ -65,21 +67,15 @@ const addonAlign = computed(
65
67
  >
66
68
  <slot name="leading" />
67
69
  </InputGroupAddon>
68
- <InputGroupTextarea
69
- v-if="props.multiline"
70
+ <CodeMirrorInput
71
+ ref="editor"
70
72
  :model-value="props.modelValue"
71
73
  :default-value="props.defaultValue"
72
74
  :placeholder="props.placeholder"
73
- :class="cn('font-mono text-xs', props.class)"
74
- @update:model-value="(v) => emits('update:modelValue', String(v))"
75
- />
76
- <InputGroupInput
77
- v-else
78
- :model-value="props.modelValue"
79
- :default-value="props.defaultValue"
80
- :placeholder="props.placeholder"
81
- :class="cn('font-mono text-xs', props.class)"
82
- @update:model-value="(v) => emits('update:modelValue', String(v))"
75
+ :multiline="props.multiline"
76
+ :scope-lookup="scopeLookup"
77
+ :class="props.class"
78
+ @update:model-value="(v) => emits('update:modelValue', v)"
83
79
  />
84
80
  <InputGroupAddon
85
81
  v-if="showAddon"
@@ -109,34 +105,59 @@ const addonAlign = computed(
109
105
  <CommandEmpty>No variables.</CommandEmpty>
110
106
  <CommandGroup>
111
107
  <CommandItem
112
- v-for="entry in entries"
113
- :key="entry.name"
114
- :value="entry.name"
115
- class="cursor-default gap-2"
116
- @mouseenter="hoveredName = entry.name"
117
- @focus="hoveredName = entry.name"
108
+ v-for="entry in shownVars"
109
+ :key="entry.id"
110
+ :value="entry.display"
111
+ class="cursor-pointer gap-2"
112
+ @select="insertVariable(entry.insert)"
113
+ @mouseenter="hoveredName = entry.id"
114
+ @focus="hoveredName = entry.id"
118
115
  >
119
- <span class="font-mono text-xs text-zinc-800">{{ entry.name }}</span>
120
- <span class="flex-1 truncate text-xs text-zinc-500">{{ entry.meta.label }}</span>
116
+ <Icon
117
+ icon="fluent:braces-variable-20-regular"
118
+ class="size-3.5 shrink-0 text-zinc-400"
119
+ />
120
+ <span class="font-mono text-xs text-zinc-800">{{ entry.display }}</span>
121
+ <span class="flex-1 truncate text-xs text-zinc-500">{{ entry.label }}</span>
121
122
  <span class="rounded bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] leading-none text-purple-700 select-none">
122
- {{ entry.meta.type }}
123
+ {{ entry.type }}
124
+ </span>
125
+ </CommandItem>
126
+ </CommandGroup>
127
+ <CommandGroup
128
+ v-if="shownScopes.length > 0"
129
+ heading="跨层引用"
130
+ >
131
+ <CommandItem
132
+ v-for="entry in shownScopes"
133
+ :key="entry.id"
134
+ :value="`${entry.display} ${entry.id}`"
135
+ class="cursor-pointer gap-2"
136
+ @select="insertVariable(entry.insert)"
137
+ @mouseenter="hoveredName = entry.id"
138
+ @focus="hoveredName = entry.id"
139
+ >
140
+ <Icon
141
+ icon="fluent:link-20-regular"
142
+ class="size-3.5 shrink-0 text-zinc-400"
143
+ />
144
+ <span class="flex-1 truncate text-xs text-zinc-700">{{ entry.display }}</span>
145
+ <span class="rounded bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] leading-none text-purple-700 select-none">
146
+ {{ entry.type }}
123
147
  </span>
124
148
  </CommandItem>
125
149
  </CommandGroup>
126
150
  </CommandList>
127
151
  <div
128
- v-if="hoveredMeta"
152
+ v-if="hoveredEntry"
129
153
  class="border-t border-zinc-200 px-3 py-2 group-data-[side=top]/popover:border-t-0 group-data-[side=top]/popover:border-b"
130
154
  >
131
- <div class="mb-1 flex items-center gap-2">
132
- <span class="font-mono text-xs text-zinc-800">{{ hoveredName }}</span>
133
- <span class="rounded bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] leading-none text-purple-700 select-none">
134
- {{ hoveredMeta.type }}
155
+ <div class="flex items-center gap-2">
156
+ <span class="text-xs font-medium text-zinc-700">{{ hoveredEntry.label }}</span>
157
+ <span class="ml-auto shrink-0 rounded bg-purple-100 px-1.5 py-0.5 font-mono text-[10px] leading-none text-purple-700 select-none">
158
+ {{ hoveredEntry.type }}
135
159
  </span>
136
160
  </div>
137
- <div class="text-xs font-medium text-zinc-700">
138
- {{ hoveredMeta.label }}
139
- </div>
140
161
  <Markdown
141
162
  v-if="hoveredDescription"
142
163
  :source="hoveredDescription"
@@ -13,11 +13,11 @@ type __VLS_Props = {
13
13
  resultType?: string | string[];
14
14
  extraVars?: Record<string, VarSpec>;
15
15
  };
16
- declare var __VLS_14: {}, __VLS_108: {};
16
+ declare var __VLS_14: {}, __VLS_130: {};
17
17
  type __VLS_Slots = {} & {
18
18
  leading?: (props: typeof __VLS_14) => any;
19
19
  } & {
20
- trailing?: (props: typeof __VLS_108) => any;
20
+ trailing?: (props: typeof __VLS_130) => any;
21
21
  };
22
22
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
23
23
  "update:modelValue": (payload: string) => any;
@@ -0,0 +1,16 @@
1
+ import type { Extension } from '@codemirror/state';
2
+ import { StreamLanguage } from '@codemirror/language';
3
+ type State = {
4
+ afterDot: boolean;
5
+ };
6
+ /** The CEL stream language (tokeniser only — no indentation/folding services). */
7
+ export declare const celLanguage: StreamLanguage<State>;
8
+ /** One tokenised slice of a line: the source `text` and its stream-token name. */
9
+ export type CELToken = {
10
+ text: string;
11
+ token: string | null;
12
+ };
13
+ export declare function tokenizeCEL(line: string): CELToken[];
14
+ /** Full lexical-highlighting extension: the CEL language + its colour scheme. */
15
+ export declare function celHighlighting(): Extension;
16
+ export {};
@@ -0,0 +1,114 @@
1
+ import {
2
+ HighlightStyle,
3
+ StreamLanguage,
4
+ StringStream,
5
+ syntaxHighlighting
6
+ } from "@codemirror/language";
7
+ import { tags as t } from "@lezer/highlight";
8
+ const ATOMS = /* @__PURE__ */ new Set(["true", "false", "null"]);
9
+ const OPERATOR_RE = /^(?:==|!=|<=|>=|&&|\|\||[+\-*/%<>!?])/;
10
+ const IDENT_RE = /^[A-Z_$][\w$]*/i;
11
+ const NUMBER_RE = /^(?:0x[0-9a-f]+|\d+\.\d*(?:e[+-]?\d+)?|\d+(?:e[+-]?\d+)?)u?/i;
12
+ function eatString(stream) {
13
+ const s = stream.string;
14
+ const start = stream.pos;
15
+ let i = start;
16
+ let prefixLen = 0;
17
+ while (prefixLen < 2 && /[br]/i.test(s[i + prefixLen] ?? "")) prefixLen++;
18
+ const quote = s[i + prefixLen];
19
+ if (quote !== '"' && quote !== "'") return false;
20
+ const raw = /r/i.test(s.slice(i, i + prefixLen));
21
+ i += prefixLen;
22
+ const triple = s.slice(i, i + 3) === quote + quote + quote;
23
+ const closer = triple ? quote + quote + quote : quote;
24
+ i += closer.length;
25
+ while (i < s.length) {
26
+ const c = s[i];
27
+ if (!raw && c === "\\") {
28
+ i += 2;
29
+ continue;
30
+ }
31
+ if (s.slice(i, i + closer.length) === closer) {
32
+ i += closer.length;
33
+ break;
34
+ }
35
+ if (!triple && (c === "\n" || c === "\r")) break;
36
+ i++;
37
+ }
38
+ stream.pos = Math.min(i, s.length);
39
+ return true;
40
+ }
41
+ const celStreamParser = {
42
+ startState: () => ({ afterDot: false }),
43
+ copyState: (state) => ({ afterDot: state.afterDot }),
44
+ token(stream, state) {
45
+ if (stream.eatSpace()) return null;
46
+ if (stream.match(/^\.\.\./)) {
47
+ state.afterDot = false;
48
+ return null;
49
+ }
50
+ if (eatString(stream)) {
51
+ state.afterDot = false;
52
+ return "string";
53
+ }
54
+ if (stream.match(NUMBER_RE)) {
55
+ state.afterDot = false;
56
+ return "number";
57
+ }
58
+ const ident = stream.match(IDENT_RE);
59
+ if (ident) {
60
+ const word = ident[0];
61
+ const afterDot = state.afterDot;
62
+ state.afterDot = false;
63
+ if (afterDot) return "propertyName";
64
+ if (word === "in") return "keyword";
65
+ if (ATOMS.has(word)) return "atom";
66
+ return stream.peek() === "(" ? "variableName.function" : "variableName";
67
+ }
68
+ if (stream.match(OPERATOR_RE)) {
69
+ state.afterDot = false;
70
+ return "operator";
71
+ }
72
+ if (stream.eat(".")) {
73
+ state.afterDot = true;
74
+ return null;
75
+ }
76
+ state.afterDot = false;
77
+ stream.next();
78
+ return null;
79
+ }
80
+ };
81
+ export const celLanguage = StreamLanguage.define(celStreamParser);
82
+ export function tokenizeCEL(line) {
83
+ const stream = new StringStream(line, 4, 2);
84
+ const state = celStreamParser.startState();
85
+ const out = [];
86
+ while (!stream.eol()) {
87
+ stream.start = stream.pos;
88
+ const token = celStreamParser.token(stream, state);
89
+ if (stream.pos === stream.start) stream.next();
90
+ out.push({ text: line.slice(stream.start, stream.pos), token });
91
+ }
92
+ return out;
93
+ }
94
+ const celHighlightStyle = HighlightStyle.define([
95
+ { tag: t.keyword, color: "#5a3e8e" },
96
+ // magenta — control keyword `in`
97
+ { tag: t.operator, color: "#5a3e8e" },
98
+ // magenta — && || == + - …
99
+ { tag: t.string, color: "#385f0d" },
100
+ // green — strings / bytes
101
+ { tag: t.number, color: "#965027" },
102
+ // orange — numbers
103
+ { tag: t.atom, color: "#965027" },
104
+ // orange — true / false / null
105
+ { tag: t.variableName, color: "#343b58" },
106
+ // navy — variables
107
+ { tag: t.propertyName, color: "#0f4b6e" },
108
+ // cyan — object properties / member access
109
+ { tag: t.function(t.variableName), color: "#2959aa" }
110
+ // blue — function calls
111
+ ]);
112
+ export function celHighlighting() {
113
+ return [celLanguage, syntaxHighlighting(celHighlightStyle)];
114
+ }
@@ -0,0 +1,5 @@
1
+ import { type Extension, Facet } from '@codemirror/state';
2
+ import { type ScopeInfo } from './scope-refs.js';
3
+ export declare const scopeLookupFacet: Facet<Map<string, ScopeInfo>, Map<string, ScopeInfo>>;
4
+ /** The full inline-chip extension: decoration plugin + theme. */
5
+ export declare function scopeChips(): Extension;
@@ -0,0 +1,123 @@
1
+ import { Facet, RangeSetBuilder } from "@codemirror/state";
2
+ import {
3
+ Decoration,
4
+ EditorView,
5
+ ViewPlugin,
6
+ WidgetType
7
+ } from "@codemirror/view";
8
+ import { scanScopeRefs } from "./scope-refs.js";
9
+ export const scopeLookupFacet = Facet.define({
10
+ combine: (values) => values.length > 0 ? values[values.length - 1] : /* @__PURE__ */ new Map()
11
+ });
12
+ class ScopeChipWidget extends WidgetType {
13
+ constructor(id, member, resolved) {
14
+ super();
15
+ this.id = id;
16
+ this.member = member;
17
+ this.resolved = resolved;
18
+ }
19
+ eq(other) {
20
+ return other.id === this.id && other.member === this.member && other.resolved?.origin === this.resolved?.origin && other.resolved?.label === this.resolved?.label && other.resolved?.type === this.resolved?.type;
21
+ }
22
+ toDOM() {
23
+ const chip = document.createElement("span");
24
+ chip.className = "cm-scope-chip";
25
+ chip.setAttribute("data-scope-id", this.id);
26
+ chip.setAttribute("data-scope-member", this.member);
27
+ const text = document.createElement("span");
28
+ text.className = "cm-scope-chip-label";
29
+ if (this.resolved) {
30
+ text.textContent = `${this.resolved.origin} \u203A ${this.resolved.label}`;
31
+ chip.title = this.resolved.origin;
32
+ } else {
33
+ chip.classList.add("cm-scope-chip-dangling");
34
+ const short = this.id.length > 8 ? `${this.id.slice(0, 8)}\u2026` : this.id;
35
+ text.textContent = `${short} \u203A ${this.member}`;
36
+ chip.title = `\u672A\u77E5\u4F5C\u7528\u57DF ${this.id}`;
37
+ }
38
+ chip.appendChild(text);
39
+ const type = this.resolved?.type;
40
+ if (type) {
41
+ const badge = document.createElement("span");
42
+ badge.className = "cm-scope-chip-type";
43
+ badge.textContent = type;
44
+ chip.appendChild(badge);
45
+ }
46
+ return chip;
47
+ }
48
+ ignoreEvent() {
49
+ return false;
50
+ }
51
+ }
52
+ function buildChips(view) {
53
+ const lookup = view.state.facet(scopeLookupFacet);
54
+ const builder = new RangeSetBuilder();
55
+ for (const ref of scanScopeRefs(view.state.doc.toString())) {
56
+ const info = lookup.get(ref.id);
57
+ const member = info?.members.get(ref.member);
58
+ const resolved = info && member ? { origin: info.origin, label: member.label, type: member.type } : null;
59
+ builder.add(
60
+ ref.from,
61
+ ref.to,
62
+ Decoration.replace({ widget: new ScopeChipWidget(ref.id, ref.member, resolved) })
63
+ );
64
+ }
65
+ return builder.finish();
66
+ }
67
+ const chipPlugin = ViewPlugin.fromClass(
68
+ class {
69
+ decorations;
70
+ constructor(view) {
71
+ this.decorations = buildChips(view);
72
+ }
73
+ update(update) {
74
+ if (update.docChanged || update.viewportChanged || update.startState.facet(scopeLookupFacet) !== update.state.facet(scopeLookupFacet)) {
75
+ this.decorations = buildChips(update.view);
76
+ }
77
+ }
78
+ },
79
+ {
80
+ decorations: (plugin) => plugin.decorations,
81
+ // Atomic ranges so the caret steps over a chip and Backspace removes the
82
+ // whole `__scopes__[...].member` at once — the user's delete-as-unit choice.
83
+ provide: (plugin) => EditorView.atomicRanges.of((view) => view.plugin(plugin)?.decorations ?? Decoration.none)
84
+ }
85
+ );
86
+ const chipTheme = EditorView.theme({
87
+ ".cm-scope-chip": {
88
+ display: "inline-flex",
89
+ alignItems: "center",
90
+ gap: "0.25rem",
91
+ margin: "0 1px",
92
+ padding: "0.125rem 0.25rem",
93
+ borderRadius: "0.25rem",
94
+ background: "#f4f4f5",
95
+ border: "1px solid #e4e4e7",
96
+ color: "#3f3f46",
97
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
98
+ verticalAlign: "baseline",
99
+ cursor: "default",
100
+ whiteSpace: "nowrap"
101
+ },
102
+ ".cm-scope-chip-dangling": {
103
+ background: "#fef2f2",
104
+ borderColor: "#fecaca",
105
+ color: "#b91c1c"
106
+ },
107
+ ".cm-scope-chip-label": {
108
+ fontSize: "0.75rem",
109
+ lineHeight: "1.1"
110
+ },
111
+ ".cm-scope-chip-type": {
112
+ borderRadius: "0.25rem",
113
+ background: "#f3e8ff",
114
+ padding: "0.05rem 0.25rem",
115
+ fontFamily: "var(--font-mono)",
116
+ fontSize: "10px",
117
+ lineHeight: "1",
118
+ color: "#7e22ce"
119
+ }
120
+ });
121
+ export function scopeChips() {
122
+ return [chipPlugin, chipTheme];
123
+ }
@@ -0,0 +1,17 @@
1
+ import { type ScopeAncestor } from '../../../utils/cel-context.js';
2
+ export type PickEntry = {
3
+ id: string;
4
+ group: 'var' | 'scope';
5
+ insert: string;
6
+ display: string;
7
+ label: string;
8
+ type: string;
9
+ description?: string;
10
+ };
11
+ export type PickVarMeta = {
12
+ type: string;
13
+ label: string;
14
+ description?: string;
15
+ };
16
+ export declare function buildVarEntries(celContext: Record<string, PickVarMeta>, extraVars?: Record<string, PickVarMeta>): PickEntry[];
17
+ export declare function buildScopeEntries(ancestry: ReadonlyArray<ScopeAncestor>): PickEntry[];
@@ -0,0 +1,34 @@
1
+ import { SCOPE_ADDRESS_VAR } from "../../../utils/cel-context.js";
2
+ import { scopeOrigin } from "./scope-refs.js";
3
+ export function buildVarEntries(celContext, extraVars) {
4
+ const out = [];
5
+ const seen = /* @__PURE__ */ new Set();
6
+ for (const [name, meta] of Object.entries(celContext)) {
7
+ if (name === SCOPE_ADDRESS_VAR) continue;
8
+ seen.add(name);
9
+ out.push({ id: name, group: "var", insert: name, display: name, label: meta.label, type: meta.type, description: meta.description });
10
+ }
11
+ for (const [name, meta] of Object.entries(extraVars ?? {})) {
12
+ if (seen.has(name) || name === SCOPE_ADDRESS_VAR) continue;
13
+ out.push({ id: name, group: "var", insert: name, display: name, label: meta.label, type: meta.type, description: meta.description });
14
+ }
15
+ return out;
16
+ }
17
+ export function buildScopeEntries(ancestry) {
18
+ const out = [];
19
+ for (const ancestor of ancestry) {
20
+ const origin = scopeOrigin(ancestor);
21
+ for (const member of ancestor.members) {
22
+ out.push({
23
+ id: `scope:${ancestor.id}:${member.key}`,
24
+ group: "scope",
25
+ insert: `${SCOPE_ADDRESS_VAR}[${JSON.stringify(ancestor.id)}].${member.key}`,
26
+ display: `${origin} \u203A ${member.label}`,
27
+ label: origin,
28
+ type: member.type,
29
+ description: member.description
30
+ });
31
+ }
32
+ }
33
+ return out;
34
+ }