@shwfed/config 2.12.8 → 2.12.9

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 (29) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +18 -18
  3. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +18 -18
  4. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +18 -18
  5. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +18 -18
  6. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +18 -18
  7. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +18 -18
  8. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +18 -18
  9. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +18 -18
  10. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +18 -18
  11. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +18 -18
  12. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +18 -18
  13. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +18 -18
  14. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +18 -18
  15. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +18 -18
  16. package/dist/runtime/components/operations/2026-07-06/com.shwfed.operation.table.add-row/config.d.vue.ts +15 -0
  17. package/dist/runtime/components/operations/2026-07-06/com.shwfed.operation.table.add-row/config.vue +47 -0
  18. package/dist/runtime/components/operations/2026-07-06/com.shwfed.operation.table.add-row/config.vue.d.ts +15 -0
  19. package/dist/runtime/components/operations/2026-07-06/com.shwfed.operation.table.add-row/schema.d.ts +12 -0
  20. package/dist/runtime/components/operations/2026-07-06/com.shwfed.operation.table.add-row/schema.js +28 -0
  21. package/dist/runtime/components/operations/utils/resolve.d.ts +7 -1
  22. package/dist/runtime/components/operations/utils/resolve.js +14 -6
  23. package/dist/runtime/components/table/config.vue +93 -4
  24. package/dist/runtime/components/table/index.vue +27 -0
  25. package/dist/runtime/components/table/schema.d.ts +2 -1
  26. package/dist/runtime/components/table/schema.js +13 -1
  27. package/dist/runtime/share/clipboard.d.ts +1 -1
  28. package/dist/runtime/share/clipboard.js +1 -1
  29. package/package.json +1 -1
@@ -56,6 +56,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
56
56
  readonly message: string;
57
57
  }[]];
58
58
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
59
+ readonly nodeTooltip?: readonly [{
60
+ readonly locale: "zh";
61
+ readonly message: string;
62
+ }, ...{
63
+ readonly locale: "en" | "ja" | "ko";
64
+ readonly message: string;
65
+ }[]] | undefined;
66
+ readonly nodeSelectable?: string | undefined;
67
+ readonly expandAll: boolean;
59
68
  readonly validations?: readonly {
60
69
  readonly message: readonly [{
61
70
  readonly locale: "zh";
@@ -67,15 +76,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
67
76
  readonly warning?: boolean | undefined;
68
77
  readonly when: string;
69
78
  }[] | undefined;
70
- readonly nodeTooltip?: readonly [{
71
- readonly locale: "zh";
72
- readonly message: string;
73
- }, ...{
74
- readonly locale: "en" | "ja" | "ko";
75
- readonly message: string;
76
- }[]] | undefined;
77
- readonly nodeSelectable?: string | undefined;
78
- readonly expandAll: boolean;
79
79
  }) => any;
80
80
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
81
81
  "onUpdate:modelValue"?: ((value: {
@@ -131,6 +131,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
131
131
  readonly message: string;
132
132
  }[]];
133
133
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
134
+ readonly nodeTooltip?: readonly [{
135
+ readonly locale: "zh";
136
+ readonly message: string;
137
+ }, ...{
138
+ readonly locale: "en" | "ja" | "ko";
139
+ readonly message: string;
140
+ }[]] | undefined;
141
+ readonly nodeSelectable?: string | undefined;
142
+ readonly expandAll: boolean;
134
143
  readonly validations?: readonly {
135
144
  readonly message: readonly [{
136
145
  readonly locale: "zh";
@@ -142,15 +151,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
142
151
  readonly warning?: boolean | undefined;
143
152
  readonly when: string;
144
153
  }[] | undefined;
145
- readonly nodeTooltip?: readonly [{
146
- readonly locale: "zh";
147
- readonly message: string;
148
- }, ...{
149
- readonly locale: "en" | "ja" | "ko";
150
- readonly message: string;
151
- }[]] | undefined;
152
- readonly nodeSelectable?: string | undefined;
153
- readonly expandAll: boolean;
154
154
  }) => any) | undefined;
155
155
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
156
156
  declare const _default: typeof __VLS_export;
@@ -56,6 +56,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
56
56
  readonly message: string;
57
57
  }[]];
58
58
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
59
+ readonly nodeTooltip?: readonly [{
60
+ readonly locale: "zh";
61
+ readonly message: string;
62
+ }, ...{
63
+ readonly locale: "en" | "ja" | "ko";
64
+ readonly message: string;
65
+ }[]] | undefined;
66
+ readonly nodeSelectable?: string | undefined;
67
+ readonly expandAll: boolean;
59
68
  readonly validations?: readonly {
60
69
  readonly message: readonly [{
61
70
  readonly locale: "zh";
@@ -67,15 +76,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
67
76
  readonly warning?: boolean | undefined;
68
77
  readonly when: string;
69
78
  }[] | undefined;
70
- readonly nodeTooltip?: readonly [{
71
- readonly locale: "zh";
72
- readonly message: string;
73
- }, ...{
74
- readonly locale: "en" | "ja" | "ko";
75
- readonly message: string;
76
- }[]] | undefined;
77
- readonly nodeSelectable?: string | undefined;
78
- readonly expandAll: boolean;
79
79
  }) => any;
80
80
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
81
81
  "onUpdate:modelValue"?: ((value: {
@@ -131,6 +131,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
131
131
  readonly message: string;
132
132
  }[]];
133
133
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
134
+ readonly nodeTooltip?: readonly [{
135
+ readonly locale: "zh";
136
+ readonly message: string;
137
+ }, ...{
138
+ readonly locale: "en" | "ja" | "ko";
139
+ readonly message: string;
140
+ }[]] | undefined;
141
+ readonly nodeSelectable?: string | undefined;
142
+ readonly expandAll: boolean;
134
143
  readonly validations?: readonly {
135
144
  readonly message: readonly [{
136
145
  readonly locale: "zh";
@@ -142,15 +151,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
142
151
  readonly warning?: boolean | undefined;
143
152
  readonly when: string;
144
153
  }[] | undefined;
145
- readonly nodeTooltip?: readonly [{
146
- readonly locale: "zh";
147
- readonly message: string;
148
- }, ...{
149
- readonly locale: "en" | "ja" | "ko";
150
- readonly message: string;
151
- }[]] | undefined;
152
- readonly nodeSelectable?: string | undefined;
153
- readonly expandAll: boolean;
154
154
  }) => any) | undefined;
155
155
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
156
156
  declare const _default: typeof __VLS_export;
@@ -55,6 +55,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
55
55
  readonly locale: "en" | "ja" | "ko";
56
56
  readonly message: string;
57
57
  }[]];
58
+ readonly nodeTooltip?: readonly [{
59
+ readonly locale: "zh";
60
+ readonly message: string;
61
+ }, ...{
62
+ readonly locale: "en" | "ja" | "ko";
63
+ readonly message: string;
64
+ }[]] | undefined;
65
+ readonly nodeSelectable?: string | undefined;
66
+ readonly expandAll: boolean;
58
67
  readonly validations?: readonly {
59
68
  readonly message: readonly [{
60
69
  readonly locale: "zh";
@@ -66,15 +75,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
66
75
  readonly warning?: boolean | undefined;
67
76
  readonly when: string;
68
77
  }[] | undefined;
69
- readonly nodeTooltip?: readonly [{
70
- readonly locale: "zh";
71
- readonly message: string;
72
- }, ...{
73
- readonly locale: "en" | "ja" | "ko";
74
- readonly message: string;
75
- }[]] | undefined;
76
- readonly nodeSelectable?: string | undefined;
77
- readonly expandAll: boolean;
78
78
  }) => any;
79
79
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
80
80
  "onUpdate:modelValue"?: ((value: {
@@ -129,6 +129,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
129
129
  readonly locale: "en" | "ja" | "ko";
130
130
  readonly message: string;
131
131
  }[]];
132
+ readonly nodeTooltip?: readonly [{
133
+ readonly locale: "zh";
134
+ readonly message: string;
135
+ }, ...{
136
+ readonly locale: "en" | "ja" | "ko";
137
+ readonly message: string;
138
+ }[]] | undefined;
139
+ readonly nodeSelectable?: string | undefined;
140
+ readonly expandAll: boolean;
132
141
  readonly validations?: readonly {
133
142
  readonly message: readonly [{
134
143
  readonly locale: "zh";
@@ -140,15 +149,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
140
149
  readonly warning?: boolean | undefined;
141
150
  readonly when: string;
142
151
  }[] | undefined;
143
- readonly nodeTooltip?: readonly [{
144
- readonly locale: "zh";
145
- readonly message: string;
146
- }, ...{
147
- readonly locale: "en" | "ja" | "ko";
148
- readonly message: string;
149
- }[]] | undefined;
150
- readonly nodeSelectable?: string | undefined;
151
- readonly expandAll: boolean;
152
152
  }) => any) | undefined;
153
153
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
154
154
  declare const _default: typeof __VLS_export;
@@ -55,6 +55,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
55
55
  readonly locale: "en" | "ja" | "ko";
56
56
  readonly message: string;
57
57
  }[]];
58
+ readonly nodeTooltip?: readonly [{
59
+ readonly locale: "zh";
60
+ readonly message: string;
61
+ }, ...{
62
+ readonly locale: "en" | "ja" | "ko";
63
+ readonly message: string;
64
+ }[]] | undefined;
65
+ readonly nodeSelectable?: string | undefined;
66
+ readonly expandAll: boolean;
58
67
  readonly validations?: readonly {
59
68
  readonly message: readonly [{
60
69
  readonly locale: "zh";
@@ -66,15 +75,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
66
75
  readonly warning?: boolean | undefined;
67
76
  readonly when: string;
68
77
  }[] | undefined;
69
- readonly nodeTooltip?: readonly [{
70
- readonly locale: "zh";
71
- readonly message: string;
72
- }, ...{
73
- readonly locale: "en" | "ja" | "ko";
74
- readonly message: string;
75
- }[]] | undefined;
76
- readonly nodeSelectable?: string | undefined;
77
- readonly expandAll: boolean;
78
78
  }) => any;
79
79
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
80
80
  "onUpdate:modelValue"?: ((value: {
@@ -129,6 +129,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
129
129
  readonly locale: "en" | "ja" | "ko";
130
130
  readonly message: string;
131
131
  }[]];
132
+ readonly nodeTooltip?: readonly [{
133
+ readonly locale: "zh";
134
+ readonly message: string;
135
+ }, ...{
136
+ readonly locale: "en" | "ja" | "ko";
137
+ readonly message: string;
138
+ }[]] | undefined;
139
+ readonly nodeSelectable?: string | undefined;
140
+ readonly expandAll: boolean;
132
141
  readonly validations?: readonly {
133
142
  readonly message: readonly [{
134
143
  readonly locale: "zh";
@@ -140,15 +149,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
140
149
  readonly warning?: boolean | undefined;
141
150
  readonly when: string;
142
151
  }[] | undefined;
143
- readonly nodeTooltip?: readonly [{
144
- readonly locale: "zh";
145
- readonly message: string;
146
- }, ...{
147
- readonly locale: "en" | "ja" | "ko";
148
- readonly message: string;
149
- }[]] | undefined;
150
- readonly nodeSelectable?: string | undefined;
151
- readonly expandAll: boolean;
152
152
  }) => any) | undefined;
153
153
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
154
154
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,15 @@
1
+ import { type Value } from './schema.js';
2
+ type __VLS_ModelProps = {
3
+ modelValue: Value;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:modelValue": (value: {
7
+ readonly newRow?: string | undefined;
8
+ }) => any;
9
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
10
+ "onUpdate:modelValue"?: ((value: {
11
+ readonly newRow?: string | undefined;
12
+ }) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -0,0 +1,47 @@
1
+ <script setup>
2
+ import { ExpressionEditor } from "../../../ui/expression-editor";
3
+ import { Field, FieldGroup, FieldLabel } from "../../../ui/field";
4
+ import { Markdown } from "../../../ui/markdown";
5
+ import { getStructFieldDescription, getStructFieldTitle } from "../../../../share/schema-meta";
6
+ import { schema } from "./schema";
7
+ defineOptions({ name: "ShwfedTableAddRowOperationConfig" });
8
+ const value = defineModel({ type: null, ...{ required: true } });
9
+ const opSchema = schema(() => {
10
+ });
11
+ const fieldTitle = (f) => getStructFieldTitle(opSchema, f) ?? f;
12
+ const fieldDescription = (f) => getStructFieldDescription(opSchema, f);
13
+ function patchNewRow(v) {
14
+ const next = { ...value.value };
15
+ if (v.length === 0) Reflect.deleteProperty(next, "newRow");
16
+ else next.newRow = v;
17
+ value.value = next;
18
+ }
19
+ </script>
20
+
21
+ <template>
22
+ <FieldGroup>
23
+ <Field orientation="vertical">
24
+ <FieldLabel class="text-xs text-zinc-500">
25
+ <template
26
+ v-if="fieldDescription('newRow')"
27
+ #tooltip
28
+ >
29
+ <Markdown
30
+ :source="fieldDescription('newRow')"
31
+ block
32
+ class="prose prose-sm prose-zinc"
33
+ />
34
+ </template>
35
+ {{ fieldTitle("newRow") }}
36
+ </FieldLabel>
37
+ <ExpressionEditor
38
+ :model-value="value.newRow ?? ''"
39
+ multiline
40
+ placeholder="例:{'status': 'active', 'qty': 1}"
41
+ result-type="map"
42
+ class="min-h-16"
43
+ @update:model-value="(v) => patchNewRow(v)"
44
+ />
45
+ </Field>
46
+ </FieldGroup>
47
+ </template>
@@ -0,0 +1,15 @@
1
+ import { type Value } from './schema.js';
2
+ type __VLS_ModelProps = {
3
+ modelValue: Value;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "update:modelValue": (value: {
7
+ readonly newRow?: string | undefined;
8
+ }) => any;
9
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
10
+ "onUpdate:modelValue"?: ((value: {
11
+ readonly newRow?: string | undefined;
12
+ }) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { Schema } from 'effect';
2
+ import type { Environment } from '../../../../vendor/cel-js/lib/index.js';
3
+ export declare const type: "com.shwfed.operation.table.add-row";
4
+ export declare const compatibilityDate: "2026-07-06";
5
+ export declare const metadata: {
6
+ readonly name: "新增一行";
7
+ readonly icon: "fluent:add-20-regular";
8
+ };
9
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
10
+ newRow: Schema.optional<Schema.Schema<string, string, never>>;
11
+ }>;
12
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
@@ -0,0 +1,28 @@
1
+ import { Schema } from "effect";
2
+ import { Expression } from "../../../../share/expression.js";
3
+ import { md } from "../../../../share/markdown.js";
4
+ export const type = "com.shwfed.operation.table.add-row";
5
+ export const compatibilityDate = "2026-07-06";
6
+ export const metadata = {
7
+ name: "\u65B0\u589E\u4E00\u884C",
8
+ icon: "fluent:add-20-regular"
9
+ };
10
+ export function schema(configure) {
11
+ const CelRow = Expression({ configure, resultType: "map" });
12
+ return Schema.Struct({
13
+ newRow: Schema.optional(CelRow.annotations({
14
+ title: "\u9ED8\u8BA4\u503C",
15
+ description: md`
16
+ 点击时求值,结果作为新行的初始字段值(须为对象/映射,如
17
+ \`{'status': 'active', 'qty': 1}\`)。可引用点击处作用域的变量
18
+ (例如行内操作按钮的 \`row\`)。留空则新增一个空行。
19
+ `
20
+ }))
21
+ }).annotations({
22
+ title: "TableAddRowOperation",
23
+ description: md`
24
+ 向目标表格追加一行。可选「默认值」表达式为新行提供初始字段值;
25
+ 留空则追加一个空行(与无参数时行为一致)。
26
+ `
27
+ });
28
+ }
@@ -44,7 +44,13 @@ export type OperationEntry = Readonly<{
44
44
  schema: SchemaFactory;
45
45
  defaults?: () => Record<string, unknown>;
46
46
  config: Component;
47
- handler: OperationHandler;
47
+ /**
48
+ * The generic behaviour, when the op ships a `runtime.ts`. Absent for
49
+ * host-coupled ops (no `runtime.ts`): those act on a specific host's private
50
+ * state, so the host hand-wires the handler into its own `provideEventTarget`
51
+ * rather than exposing it through `operationHandlers`.
52
+ */
53
+ handler?: OperationHandler;
48
54
  }>;
49
55
  export declare const OPERATIONS: ReadonlyArray<OperationEntry>;
50
56
  export declare function findOperation(type: string, compatibilityDate: string): OperationEntry | undefined;
@@ -18,7 +18,7 @@ const configModules = Object.fromEntries(
18
18
  ])
19
19
  );
20
20
  const PATH_RE = /\/([^/]+)\/([^/]+)\/schema\.(?:ts|js)$/;
21
- function findSibling(map, date, type, file) {
21
+ function findSiblingOptional(map, date, type, file) {
22
22
  const candidates = file.endsWith(".ts") ? [file, `${file.slice(0, -3)}.js`] : [file];
23
23
  for (const candidate of candidates) {
24
24
  const suffix = `/${date}/${type}/${candidate}`;
@@ -26,7 +26,14 @@ function findSibling(map, date, type, file) {
26
26
  if (key.endsWith(suffix)) return value;
27
27
  }
28
28
  }
29
- throw new Error(`[shwfed-operations] missing ${file} for ${type}@${date}`);
29
+ return void 0;
30
+ }
31
+ function findSibling(map, date, type, file) {
32
+ const found = findSiblingOptional(map, date, type, file);
33
+ if (found === void 0) {
34
+ throw new Error(`[shwfed-operations] missing ${file} for ${type}@${date}`);
35
+ }
36
+ return found;
30
37
  }
31
38
  let cached = null;
32
39
  function compute() {
@@ -43,7 +50,7 @@ function compute() {
43
50
  if (mod.compatibilityDate !== date) {
44
51
  throw new Error(`[shwfed-operations] compatibilityDate "${mod.compatibilityDate}" does not match folder "${date}"`);
45
52
  }
46
- const runtime = findSibling(runtimeModules, date, type, "runtime.ts");
53
+ const runtime = findSiblingOptional(runtimeModules, date, type, "runtime.ts");
47
54
  entries.push({
48
55
  type: mod.type,
49
56
  compatibilityDate: mod.compatibilityDate,
@@ -51,7 +58,7 @@ function compute() {
51
58
  schema: mod.schema,
52
59
  defaults: mod.defaults,
53
60
  config: findSibling(configModules, date, type, "config.vue"),
54
- handler: runtime.handler
61
+ handler: runtime?.handler
55
62
  });
56
63
  }
57
64
  cached = entries;
@@ -69,8 +76,9 @@ export function operationHandlers(exposed, ctx) {
69
76
  const handlers = {};
70
77
  for (const { type, compatibilityDate } of exposed) {
71
78
  const entry = findOperation(type, compatibilityDate);
72
- if (!entry) continue;
73
- handlers[type] = (params, cel, step) => entry.handler(params, { ...ctx, ...cel && { cel }, ...step && { step } });
79
+ if (!entry?.handler) continue;
80
+ const handler = entry.handler;
81
+ handlers[type] = (params, cel, step) => handler(params, { ...ctx, ...cel && { cel }, ...step && { step } });
74
82
  }
75
83
  return handlers;
76
84
  }
@@ -13,6 +13,7 @@ import {
13
13
  } from "../../composables/useTreeDnd";
14
14
  import { getStructFieldDescription, getStructFieldTitle } from "./utils/schema-meta";
15
15
  import { provideEventAncestor } from "../../share/event-bus";
16
+ import { readClip, reidFragment, writeClip } from "../../share/clipboard";
16
17
  import { Markdown } from "../ui/markdown";
17
18
  import { Badge } from "../ui/badge";
18
19
  import { ScrollArea } from "../ui/scroll-area";
@@ -251,6 +252,9 @@ function writeGeneralPatch(patch) {
251
252
  const editingGeneralConfig = computed(() => readGeneral());
252
253
  const selected = ref(props.hideDataSource ? { kind: "general" } : { kind: "ai" });
253
254
  const fieldErrors = ref({});
255
+ const pickedColumnIds = ref([]);
256
+ const pickedColumnIdSet = computed(() => new Set(pickedColumnIds.value));
257
+ const pickAnchor = ref(null);
254
258
  watch(
255
259
  config,
256
260
  (value) => {
@@ -524,6 +528,10 @@ function deleteColumn(columnId) {
524
528
  resolveSelection();
525
529
  }
526
530
  function resolveSelection() {
531
+ const live = new Set(editingColumns.value.map((c) => c.id));
532
+ if (pickedColumnIds.value.some((id) => !live.has(id))) {
533
+ pickedColumnIds.value = pickedColumnIds.value.filter((id) => live.has(id));
534
+ }
527
535
  const s = selected.value;
528
536
  if (s.kind === "ai") return;
529
537
  if (s.kind === "general") return;
@@ -552,9 +560,88 @@ function selectQuery() {
552
560
  function selectGroup(id) {
553
561
  selected.value = { kind: "group", id };
554
562
  }
555
- function selectColumn(id) {
563
+ const orderedColumnIds = computed(
564
+ () => renderEntries.value.filter((e) => e.kind === "column").map((e) => e.columnId)
565
+ );
566
+ function columnRange(a, b) {
567
+ const ord = orderedColumnIds.value;
568
+ const ia = ord.indexOf(a);
569
+ const ib = ord.indexOf(b);
570
+ if (ia === -1 || ib === -1) return [b];
571
+ const [lo, hi] = ia <= ib ? [ia, ib] : [ib, ia];
572
+ return ord.slice(lo, hi + 1);
573
+ }
574
+ function selectColumn(id, e) {
575
+ const meta = !!e && (e.metaKey || e.ctrlKey);
576
+ if (meta && e.shiftKey) {
577
+ const range = pickAnchor.value ? columnRange(pickAnchor.value, id) : [id];
578
+ const set = new Set(pickedColumnIds.value);
579
+ for (const cid of range) set.add(cid);
580
+ pickedColumnIds.value = orderedColumnIds.value.filter((cid) => set.has(cid));
581
+ pickAnchor.value = id;
582
+ return;
583
+ }
584
+ if (meta) {
585
+ const set = new Set(pickedColumnIds.value);
586
+ if (set.has(id)) set.delete(id);
587
+ else set.add(id);
588
+ pickedColumnIds.value = orderedColumnIds.value.filter((cid) => set.has(cid));
589
+ pickAnchor.value = id;
590
+ return;
591
+ }
592
+ pickedColumnIds.value = [];
593
+ pickAnchor.value = null;
556
594
  selected.value = { kind: "column", id };
557
595
  }
596
+ watch(selected, (s) => {
597
+ if (s.kind !== "column") {
598
+ pickedColumnIds.value = [];
599
+ pickAnchor.value = null;
600
+ }
601
+ });
602
+ async function copyColumns() {
603
+ if (pickedColumnIds.value.length === 0) return;
604
+ const set = pickedColumnIdSet.value;
605
+ const cols = orderedColumnIds.value.filter((id) => set.has(id)).map((id) => editingColumns.value.find((c) => c.id === id)).filter((c) => !!c).map(({ groupId: _g, ...rest }) => rest);
606
+ if (cols.length === 0) return;
607
+ const ok = await writeClip("table", cols);
608
+ if (ok) toast.success(`\u5DF2\u590D\u5236 ${cols.length} \u5217`);
609
+ else toast.error("\u590D\u5236\u5931\u8D25");
610
+ }
611
+ async function pasteColumns() {
612
+ const clip = await readClip();
613
+ if (!clip || clip.surface !== "table") return;
614
+ const { items } = reidFragment(clip.items);
615
+ const pasted = items.map(({ groupId: _g, ...rest }) => rest);
616
+ if (pasted.length === 0) return;
617
+ editingColumns.value = [...editingColumns.value, ...pasted];
618
+ const ids = pasted.map((c) => c.id);
619
+ pickedColumnIds.value = ids;
620
+ pickAnchor.value = ids[ids.length - 1] ?? null;
621
+ if (ids.length > 0) selected.value = { kind: "column", id: ids[ids.length - 1] };
622
+ toast.success(`\u5DF2\u7C98\u8D34 ${pasted.length} \u5217`);
623
+ }
624
+ function isTextInput(el) {
625
+ if (!(el instanceof HTMLElement)) return false;
626
+ const tag = el.tagName;
627
+ return tag === "INPUT" || tag === "TEXTAREA" || el.isContentEditable;
628
+ }
629
+ function onColumnListKeydown(e) {
630
+ if (isTextInput(e.target)) return;
631
+ const mod = e.metaKey || e.ctrlKey;
632
+ if (!mod || e.shiftKey || e.altKey) return;
633
+ const key = e.key.toLowerCase();
634
+ if (key === "c") {
635
+ if (pickedColumnIds.value.length === 0) return;
636
+ const sel = window.getSelection();
637
+ if (sel && !sel.isCollapsed) return;
638
+ e.preventDefault();
639
+ void copyColumns();
640
+ } else if (key === "v") {
641
+ e.preventDefault();
642
+ void pasteColumns();
643
+ }
644
+ }
558
645
  const breadcrumbExt = inject(BREADCRUMB_EXTENSION_KEY, null);
559
646
  if (breadcrumbExt) {
560
647
  const crumb = breadcrumbExt.add();
@@ -1074,7 +1161,9 @@ const tableQueryValue = computed({
1074
1161
  <div class="flex min-h-128 gap-6">
1075
1162
  <div
1076
1163
  v-if="!isDrilled"
1077
- class="flex w-64 shrink-0 flex-col"
1164
+ class="flex w-64 shrink-0 flex-col outline-none"
1165
+ tabindex="-1"
1166
+ @keydown="onColumnListKeydown"
1078
1167
  >
1079
1168
  <ScrollArea class="flex-1">
1080
1169
  <div class="flex flex-col gap-1">
@@ -1224,10 +1313,10 @@ const tableQueryValue = computed({
1224
1313
  class="row group column-row"
1225
1314
  :class="[
1226
1315
  entry.groupId ? 'pl-6' : 'pl-2',
1227
- selected.kind === 'column' && selected.id === entry.columnId ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : 'text-zinc-600 hover:bg-zinc-50'
1316
+ selected.kind === 'column' && selected.id === entry.columnId ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : pickedColumnIdSet.has(entry.columnId) ? 'bg-zinc-100 text-zinc-700' : 'text-zinc-600 hover:bg-zinc-50'
1228
1317
  ]"
1229
1318
  :data-instruction="dnd.instructionFor(entry.columnId) ?? void 0"
1230
- @click.stop="selectColumn(entry.columnId)"
1319
+ @click.stop="selectColumn(entry.columnId, $event)"
1231
1320
  >
1232
1321
  <Icon
1233
1322
  icon="fluent:re-order-dots-vertical-20-filled"
@@ -18,6 +18,7 @@ import { fetchJsonOption } from "../../share/request";
18
18
  import { useI18n } from "vue-i18n";
19
19
  import { celBindings, provideCELContext, injectCELContext } from "../../utils/cel-context";
20
20
  import TableRowProvider from "./row-provider.vue";
21
+ import * as addRowOp from "../operations/2026-07-06/com.shwfed.operation.table.add-row/schema";
21
22
  import { getLocalizedText } from "../../share/locale";
22
23
  import { Button } from "../ui/button";
23
24
  import { Markdown } from "../ui/markdown";
@@ -492,6 +493,32 @@ provideEventTarget(tableInstanceId, {
492
493
  // `table` form field) sees the live list. Intended for client-side data:
493
494
  // with server pagination (`dataSource.total`) `rowData` is only the
494
495
  // current page, so a fetch will overwrite these edits.
496
+ // `add-row` is a host-coupled operation: it is declared in the operations
497
+ // registry for its param editor, but ships no generic `runtime.ts` (the
498
+ // append touches THIS instance's private `rowData`), so the handler is
499
+ // hand-wired here. Its optional `newRow` param is a CEL expression evaluated
500
+ // at click time against the *emit-site* scope (the dispatching button's own
501
+ // `cel`, where a row action's `row` is live). An object result seeds the new
502
+ // row; absent / non-object / evaluation error → a bare `{}` (the param-less
503
+ // default). Falls back to the table's own evaluator when no emit-site `cel`.
504
+ [addRowOp.type]: (params, cel) => {
505
+ const expr = params?.newRow;
506
+ if (!expr) {
507
+ return Effect.sync(() => {
508
+ rowData.value = [...rowData.value, {}];
509
+ });
510
+ }
511
+ return (cel ? cel(expr) : $cel(expr, mergeCelContext({}))).pipe(
512
+ Effect.map((result) => result !== null && typeof result === "object" && !Array.isArray(result) ? { ...result } : {}),
513
+ Effect.catchAll(() => Effect.succeed({})),
514
+ Effect.map((seed) => {
515
+ rowData.value = [...rowData.value, seed];
516
+ })
517
+ );
518
+ },
519
+ // Back-compat: triggers authored before `add-row` moved into the operations
520
+ // registry carry the bare `operation: 'add-row'` (no params). Keep appending
521
+ // an empty row for them.
495
522
  "add-row": () => Effect.sync(() => {
496
523
  rowData.value = [...rowData.value, {}];
497
524
  }),
@@ -24,7 +24,8 @@ export declare const metadata: {
24
24
  readonly name: "清空所有选中行";
25
25
  readonly icon: "fluent:select-all-off-20-regular";
26
26
  }, {
27
- readonly id: "add-row";
27
+ readonly id: "com.shwfed.operation.table.add-row";
28
+ readonly compatibilityDate: "2026-07-06";
28
29
  readonly name: "新增一行";
29
30
  readonly icon: "fluent:add-20-regular";
30
31
  }, {