@shwfed/config 2.9.10 → 2.9.12

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 (72) hide show
  1. package/dist/mcp.mjs +2 -2
  2. package/dist/module.json +1 -1
  3. package/dist/preview/assets/{FieldGroup.vue_vue_type_script_setup_true_lang-B_lv0Qb4.js → FieldGroup.vue_vue_type_script_setup_true_lang-CpDhqXJp.js} +1 -1
  4. package/dist/preview/assets/{badge-DkCzSREV.js → badge-Dsz2Rn8b.js} +1 -1
  5. package/dist/preview/assets/{config-BO6oZNCA.js → config-9-c5e5iP.js} +1 -1
  6. package/dist/preview/assets/{config-CLszlI7X.js → config-BCvaA0as.js} +1 -1
  7. package/dist/preview/assets/{config-DcOjw4Ib.js → config-BePS5kQb.js} +1 -1
  8. package/dist/preview/assets/{config-y0whCdxg.js → config-CQLR1Zao.js} +1 -1
  9. package/dist/preview/assets/{config-BzUZdTRZ.js → config-CRD1MpEn.js} +1 -1
  10. package/dist/preview/assets/{config-DG9kUyIX.js → config-DY7nQeRd.js} +1 -1
  11. package/dist/preview/assets/{config-DnxWfe-p.js → config-X6-9yQmS.js} +1 -1
  12. package/dist/preview/assets/{config-IAY_3DyY.js → config-Y2YuesjH.js} +1 -1
  13. package/dist/preview/assets/{config-CJYUWHSt.js → config-bFWnH6k3.js} +1 -1
  14. package/dist/preview/assets/{config-CfTdhIiz.js → config-hrBgOr32.js} +1 -1
  15. package/dist/preview/assets/{config-BQZZzdBi.js → config-if9TvUw2.js} +1 -1
  16. package/dist/preview/assets/config-vsMk-zRS.js +1 -0
  17. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-D6Dl1HG7.js → definition.vue_vue_type_script_setup_true_lang-CPrYbKjH.js} +1 -1
  18. package/dist/preview/assets/index-BoyzyftU.js +1 -0
  19. package/dist/preview/assets/{index-CEpio2Ai.js → index-Bvlt8fEt.js} +164 -164
  20. package/dist/preview/assets/{index-BxEz7F0S.js → index-DFRXx_kG.js} +1 -1
  21. package/dist/preview/assets/index-DJ5rzQJr.css +1 -0
  22. package/dist/preview/assets/{item-BgY4KzDy.js → item-auVJu6jy.js} +1 -1
  23. package/dist/preview/assets/{runtime-B0yAZrhv.js → runtime-1DsatQOR.js} +1 -1
  24. package/dist/preview/assets/runtime-28a_li4U.js +1 -0
  25. package/dist/preview/assets/{runtime-ObPbN32X.js → runtime-BDvuTpxY.js} +1 -1
  26. package/dist/preview/assets/{runtime-BweDVL2G.js → runtime-CBDARlsK.js} +1 -1
  27. package/dist/preview/assets/{runtime-C9L73GlB.js → runtime-CmgeUPz4.js} +1 -1
  28. package/dist/preview/assets/{runtime-BrfrrA2a.js → runtime-DaaQn8fX.js} +1 -1
  29. package/dist/preview/assets/{runtime-B1vIYtF_.js → runtime-Do_KQ5le.js} +1 -1
  30. package/dist/preview/assets/{runtime-74HxxB_6.js → runtime-DyeSWij2.js} +1 -1
  31. package/dist/preview/assets/{runtime-DIHeE12q.js → runtime-H7c112vi.js} +1 -1
  32. package/dist/preview/assets/{runtime-DNCNcjrA.js → runtime-_5B97gRO.js} +1 -1
  33. package/dist/preview/assets/{schema-meta-dmADcmVf.js → schema-meta-Dc89aD6v.js} +1 -1
  34. package/dist/preview/index.html +2 -2
  35. package/dist/runtime/components/actions/buttons/2026-05-11/com.shwfed.actions.button.modal.layout/config.vue +6 -1
  36. package/dist/runtime/components/actions/config.d.vue.ts +1 -0
  37. package/dist/runtime/components/actions/config.vue +6 -2
  38. package/dist/runtime/components/actions/config.vue.d.ts +1 -0
  39. package/dist/runtime/components/block-layout-editor/index.vue +25 -4
  40. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.tabs/config.vue +5 -2
  41. package/dist/runtime/components/config/blocks/2026-06-02/com.shwfed.block.card/config.vue +4 -1
  42. package/dist/runtime/components/config/config.d.vue.ts +1 -1
  43. package/dist/runtime/components/config/config.vue +2 -33
  44. package/dist/runtime/components/config/config.vue.d.ts +1 -1
  45. package/dist/runtime/components/config/use-editor.d.ts +19 -0
  46. package/dist/runtime/components/config/use-editor.js +36 -0
  47. package/dist/runtime/components/form/config.vue +38 -1
  48. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.markdown/schema.d.ts +1 -0
  49. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.markdown/schema.js +1 -1
  50. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/config.d.vue.ts +2 -0
  51. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/config.vue +3 -2
  52. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/config.vue.d.ts +2 -0
  53. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/runtime.vue +1 -1
  54. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/schema.d.ts +3 -0
  55. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.actions/schema.js +1 -1
  56. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.d.vue.ts +6 -6
  57. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue.d.ts +6 -6
  58. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +6 -6
  59. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +6 -6
  60. package/dist/runtime/components/form/index.vue +8 -1
  61. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.actions/config.vue +1 -0
  62. package/dist/runtime/components/table/config.vue +9 -3
  63. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +2 -2
  64. package/dist/runtime/components/ui/expression-editor/picker-entries.d.ts +1 -0
  65. package/dist/runtime/components/ui/expression-editor/picker-entries.js +12 -0
  66. package/dist/runtime/components/ui/expression-editor/scope-refs.js +1 -1
  67. package/dist/runtime/utils/cel-context.d.ts +21 -0
  68. package/package.json +1 -1
  69. package/dist/preview/assets/config-9Ny1em1E.js +0 -1
  70. package/dist/preview/assets/index-DEk1Sey_.css +0 -1
  71. package/dist/preview/assets/index-eXjRNljQ.js +0 -1
  72. package/dist/preview/assets/runtime--G6ZYJWm.js +0 -1
@@ -14,7 +14,7 @@ import { Markdown } from "../../../../ui/markdown";
14
14
  import { BREADCRUMB_EXTENSION_KEY } from "../../../../config/breadcrumb-extension";
15
15
  import { SIDEBAR_TAKEOVER_KEY } from "../../../../sidebar-takeover";
16
16
  import { lookupFindBlock } from "../../../../config/utils/block-ref-cache";
17
- import { useSlotEditor } from "../../../../config/use-editor";
17
+ import { useSlotEditor, useSlotClipboard } from "../../../../config/use-editor";
18
18
  import { getStructFieldDescription, getStructFieldTitle } from "../../../schema";
19
19
  import { schema } from "./schema";
20
20
  defineOptions({ name: "ShwfedModalLayoutActionConfig" });
@@ -35,6 +35,7 @@ const slot = computed({
35
35
  }
36
36
  });
37
37
  const editor = useSlotEditor(slot, { configure: configure.value });
38
+ const { copy: copyBlocks, paste: pasteBlocks } = useSlotClipboard(editor);
38
39
  function setModalTitle(next) {
39
40
  value.value = { ...value.value, modalTitle: next };
40
41
  }
@@ -311,6 +312,8 @@ onBeforeUnmount(() => {
311
312
  @drill-down="onDrillDown"
312
313
  @delete-item="deleteBlock"
313
314
  @delete-items="deleteBlocks"
315
+ @copy-items="copyBlocks"
316
+ @paste="pasteBlocks"
314
317
  />
315
318
  </div>
316
319
  </div>
@@ -352,6 +355,8 @@ onBeforeUnmount(() => {
352
355
  @drill-down="onDrillDown"
353
356
  @delete-item="deleteBlock"
354
357
  @delete-items="deleteBlocks"
358
+ @copy-items="copyBlocks"
359
+ @paste="pasteBlocks"
355
360
  />
356
361
  </div>
357
362
  </div>
@@ -2,6 +2,7 @@ import { type ActionsConfigValue, type RegistryItemValue, type RegistrySubItemVa
2
2
  import { Environment } from '../../vendor/cel-js/lib/index.js';
3
3
  type __VLS_Props = {
4
4
  configure?: (env: Environment) => void;
5
+ hideSize?: boolean;
5
6
  };
6
7
  type __VLS_ModelProps = {
7
8
  'modelValue': ActionsConfigValue;
@@ -49,7 +49,8 @@ import {
49
49
  } from "../../share/event-bus";
50
50
  defineOptions({ name: "ShwfedActionsConfig" });
51
51
  const props = defineProps({
52
- configure: { type: Function, required: false }
52
+ configure: { type: Function, required: false },
53
+ hideSize: { type: Boolean, required: false }
53
54
  });
54
55
  const config = defineModel("modelValue", { type: null, ...{ required: true } });
55
56
  const configure = props.configure ?? (() => {
@@ -1202,7 +1203,10 @@ const newGroupZoneId = (insertIndex) => `new-group-${insertIndex}`;
1202
1203
  <slot name="general-extra" />
1203
1204
 
1204
1205
  <div class="grid grid-cols-2 gap-4">
1205
- <Field orientation="vertical">
1206
+ <Field
1207
+ v-if="!hideSize"
1208
+ orientation="vertical"
1209
+ >
1206
1210
  <FieldLabel class="text-xs text-zinc-500">
1207
1211
  <template
1208
1212
  v-if="generalFieldDescription('size')"
@@ -2,6 +2,7 @@ import { type ActionsConfigValue, type RegistryItemValue, type RegistrySubItemVa
2
2
  import { Environment } from '../../vendor/cel-js/lib/index.js';
3
3
  type __VLS_Props = {
4
4
  configure?: (env: Environment) => void;
5
+ hideSize?: boolean;
5
6
  };
6
7
  type __VLS_ModelProps = {
7
8
  'modelValue': ActionsConfigValue;
@@ -413,6 +413,13 @@ function confirmDelete() {
413
413
  if (ids.length === 1) emit("delete-item", ids[0]);
414
414
  else emitDeleteMany(ids);
415
415
  }
416
+ const deleteConfirmRef = ref(null);
417
+ async function focusDeleteConfirm(event) {
418
+ event.preventDefault();
419
+ await nextTick();
420
+ await nextTick();
421
+ deleteConfirmRef.value?.$el?.focus({ preventScroll: true });
422
+ }
416
423
  function hideItems(itemIds) {
417
424
  const i = activeIndex.value;
418
425
  const ls = layouts.value[i];
@@ -470,6 +477,7 @@ function sharedAlignment(axis) {
470
477
  }
471
478
  function beginDrag(kind, itemId, e) {
472
479
  if (e.button !== 0) return;
480
+ if (isSpaceHeld.value) return;
473
481
  const entry = placementEntries.value.find((p) => p.itemId === itemId);
474
482
  if (!entry) return;
475
483
  e.preventDefault();
@@ -969,7 +977,11 @@ function onCanvasKeydown(e) {
969
977
  class="field-block rounded-md"
970
978
  :class="[
971
979
  drag && drag.itemIds.includes(entry.itemId) ? ['shadow-sm backdrop-blur-sm z-50', dragInvalid ? 'bg-red-200/50' : 'bg-white/50'] : ['shadow-xs bg-white'],
972
- isSelected(entry.itemId) ? 'is-selected' : ''
980
+ isSelected(entry.itemId) ? 'is-selected' : '',
981
+ // Pan owns the pointer while Space is held: blocks sit on top of
982
+ // the plane as siblings, so they must let events fall through for
983
+ // the plane\'s pan mousedown (and grab cursor) to engage.
984
+ isSpaceHeld ? 'pan-through' : ''
973
985
  ]"
974
986
  :style="blockStyle(entry)"
975
987
  @mousedown="beginDrag('move', entry.itemId, $event)"
@@ -1501,7 +1513,10 @@ function onCanvasKeydown(e) {
1501
1513
  deleteDialogOpen = open;
1502
1514
  }"
1503
1515
  >
1504
- <AlertDialogContent class="sm:px-12 sm:py-8 flex flex-col gap-12">
1516
+ <AlertDialogContent
1517
+ class="sm:px-12 sm:py-8 flex flex-col gap-12"
1518
+ @open-auto-focus="focusDeleteConfirm"
1519
+ >
1505
1520
  <AlertDialogHeader class="sr-only">
1506
1521
  <AlertDialogTitle>彻底删除{{ itemNoun }}</AlertDialogTitle>
1507
1522
  <AlertDialogDescription>Confirmation dialog</AlertDialogDescription>
@@ -1518,7 +1533,10 @@ function onCanvasKeydown(e) {
1518
1533
  <Icon icon="fluent:dismiss-20-regular" />
1519
1534
  取消
1520
1535
  </AlertDialogCancel>
1521
- <AlertDialogAction @click="confirmDelete">
1536
+ <AlertDialogAction
1537
+ ref="deleteConfirmRef"
1538
+ @click="confirmDelete"
1539
+ >
1522
1540
  <Icon icon="fluent:checkmark-20-regular" />
1523
1541
  确认
1524
1542
  </AlertDialogAction>
@@ -1529,5 +1547,8 @@ function onCanvasKeydown(e) {
1529
1547
  </template>
1530
1548
 
1531
1549
  <style scoped>
1532
- .field-block{border:1px solid #e4e4e7;cursor:move;position:absolute}.field-block.is-selected{border-color:var(--primary);box-shadow:0 0 0 1px var(--primary)}.frame-outline{border:1px dashed hsla(240,5%,84%,.7)}.resize-handle{background:transparent;position:absolute}.handle-n{top:-3px}.handle-n,.handle-s{cursor:ns-resize;height:6px;left:12px;right:12px}.handle-s{bottom:-3px}.handle-e{right:-3px}.handle-e,.handle-w{bottom:12px;cursor:ew-resize;top:12px;width:6px}.handle-w{left:-3px}.handle-nw{cursor:nwse-resize;left:-3px}.handle-ne,.handle-nw{height:10px;top:-3px;width:10px}.handle-ne{cursor:nesw-resize;right:-3px}.handle-sw{cursor:nesw-resize;left:-3px}.handle-se,.handle-sw{bottom:-3px;height:10px;width:10px}.handle-se{cursor:nwse-resize;right:-3px}.drill-button{align-items:center;background:transparent;border-radius:4px;color:#71717a;cursor:pointer;display:inline-flex;height:22px;justify-content:center;opacity:0;position:absolute;right:4px;top:4px;transition:opacity .12s ease,background-color .12s ease,color .12s ease;width:22px;z-index:5}.drill-button:focus-visible,.field-block:hover .drill-button{opacity:1}.drill-button:hover{background:#f4f4f5;color:var(--primary)}.gap-input :deep(input){text-align:center}.inline-config-pane{background:#fff;border-left:1px solid #e4e4e7;bottom:0;box-shadow:-4px 0 16px -8px rgba(0,0,0,.15);display:flex;flex-direction:column;position:absolute;right:0;top:0;-webkit-user-select:text;-moz-user-select:text;user-select:text;z-index:60}.inline-config-resizer{bottom:0;cursor:ew-resize;left:-3px;position:absolute;top:0;width:7px;z-index:1}.inline-config-resizer:after{background:transparent;bottom:0;content:"";left:3px;position:absolute;top:0;transition:background-color .12s ease;width:1px}.inline-config-resizer:hover:after{background:var(--primary)}.inline-config-header{align-items:center;border-bottom:1px solid #f4f4f5;display:flex;flex-shrink:0;gap:.5rem;padding:.5rem .5rem .5rem .75rem}.inline-config-close{align-items:center;border-radius:4px;color:#71717a;cursor:pointer;display:inline-flex;height:28px;justify-content:center;transition:background-color .12s ease,color .12s ease;width:28px}.inline-config-close:hover{background:#f4f4f5;color:#18181b}.inline-config-body{flex:1 1 0;min-height:0;overflow:auto;padding:1rem}.dist-indicator{align-items:center;color:#f87171;display:flex;justify-content:center;z-index:40}.dist-bottom,.dist-top{flex-direction:column}.dist-left,.dist-right{flex-direction:row}.dist-line{background:currentColor;flex:1 1 0}.dist-bottom .dist-line,.dist-top .dist-line{width:1px}.dist-left .dist-line,.dist-right .dist-line{height:1px}.dist-label{background:#fafafa;border-radius:2px;color:#f87171;font-size:10px;font-variant-numeric:tabular-nums;line-height:1;padding:1px 4px}
1550
+ .field-block{border:1px solid #e4e4e7;cursor:move;position:absolute}.field-block.is-selected{border-color:var(--primary);box-shadow:0 0 0 1px var(--primary)}
1551
+ /* !important because reka-ui's ContextMenuTrigger hardwires an inline
1552
+ `pointer-events: auto` on the trigger element — a plain utility class
1553
+ loses to it and the Space-held pan never sees the mousedown. */.field-block.pan-through{pointer-events:none!important}.frame-outline{border:1px dashed hsla(240,5%,84%,.7)}.resize-handle{background:transparent;position:absolute}.handle-n{top:-3px}.handle-n,.handle-s{cursor:ns-resize;height:6px;left:12px;right:12px}.handle-s{bottom:-3px}.handle-e{right:-3px}.handle-e,.handle-w{bottom:12px;cursor:ew-resize;top:12px;width:6px}.handle-w{left:-3px}.handle-nw{cursor:nwse-resize;left:-3px}.handle-ne,.handle-nw{height:10px;top:-3px;width:10px}.handle-ne{cursor:nesw-resize;right:-3px}.handle-sw{cursor:nesw-resize;left:-3px}.handle-se,.handle-sw{bottom:-3px;height:10px;width:10px}.handle-se{cursor:nwse-resize;right:-3px}.drill-button{align-items:center;background:transparent;border-radius:4px;color:#71717a;cursor:pointer;display:inline-flex;height:22px;justify-content:center;opacity:0;position:absolute;right:4px;top:4px;transition:opacity .12s ease,background-color .12s ease,color .12s ease;width:22px;z-index:5}.drill-button:focus-visible,.field-block:hover .drill-button{opacity:1}.drill-button:hover{background:#f4f4f5;color:var(--primary)}.gap-input :deep(input){text-align:center}.inline-config-pane{background:#fff;border-left:1px solid #e4e4e7;bottom:0;box-shadow:-4px 0 16px -8px rgba(0,0,0,.15);display:flex;flex-direction:column;position:absolute;right:0;top:0;-webkit-user-select:text;-moz-user-select:text;user-select:text;z-index:60}.inline-config-resizer{bottom:0;cursor:ew-resize;left:-3px;position:absolute;top:0;width:7px;z-index:1}.inline-config-resizer:after{background:transparent;bottom:0;content:"";left:3px;position:absolute;top:0;transition:background-color .12s ease;width:1px}.inline-config-resizer:hover:after{background:var(--primary)}.inline-config-header{align-items:center;border-bottom:1px solid #f4f4f5;display:flex;flex-shrink:0;gap:.5rem;padding:.5rem .5rem .5rem .75rem}.inline-config-close{align-items:center;border-radius:4px;color:#71717a;cursor:pointer;display:inline-flex;height:28px;justify-content:center;transition:background-color .12s ease,color .12s ease;width:28px}.inline-config-close:hover{background:#f4f4f5;color:#18181b}.inline-config-body{flex:1 1 0;min-height:0;overflow:auto;padding:1rem}.dist-indicator{align-items:center;color:#f87171;display:flex;justify-content:center;z-index:40}.dist-bottom,.dist-top{flex-direction:column}.dist-left,.dist-right{flex-direction:row}.dist-line{background:currentColor;flex:1 1 0}.dist-bottom .dist-line,.dist-top .dist-line{width:1px}.dist-left .dist-line,.dist-right .dist-line{height:1px}.dist-label{background:#fafafa;border-radius:2px;color:#f87171;font-size:10px;font-variant-numeric:tabular-nums;line-height:1;padding:1px 4px}
1533
1554
  </style>
@@ -20,7 +20,7 @@ import {
20
20
  } from "../../../../../composables/useTreeDnd";
21
21
  import { BREADCRUMB_EXTENSION_KEY } from "../../../breadcrumb-extension";
22
22
  import { findBlock } from "../../../utils/resolve";
23
- import { useSlotEditor } from "../../../use-editor";
23
+ import { useSlotClipboard, useSlotEditor } from "../../../use-editor";
24
24
  import { TabPaneMeta, getStructFieldDescription, getStructFieldTitle } from "./schema";
25
25
  defineOptions({ name: "ShwfedBlockTabsConfig" });
26
26
  const block = defineModel({ type: null, ...{ required: true } });
@@ -84,7 +84,8 @@ function buildShell(id) {
84
84
  set value(next) {
85
85
  editor.selectedBlockIds.value = next;
86
86
  }
87
- }
87
+ },
88
+ clipboard: useSlotClipboard(editor)
88
89
  };
89
90
  }
90
91
  function ensureShell(id) {
@@ -554,6 +555,8 @@ function deleteBlocksFromActive(ids) {
554
555
  @drill-down="onDrillDown"
555
556
  @delete-item="deleteBlockFromActive"
556
557
  @delete-items="deleteBlocksFromActive"
558
+ @copy-items="activeShell.clipboard.copy"
559
+ @paste="activeShell.clipboard.paste"
557
560
  />
558
561
  </div>
559
562
  </template>
@@ -23,7 +23,7 @@ import { Separator } from "../../../../ui/separator";
23
23
  import { Switch } from "../../../../ui/switch";
24
24
  import { BREADCRUMB_EXTENSION_KEY } from "../../../breadcrumb-extension";
25
25
  import { findBlock } from "../../../utils/resolve";
26
- import { useSlotEditor } from "../../../use-editor";
26
+ import { useSlotEditor, useSlotClipboard } from "../../../use-editor";
27
27
  import {
28
28
  BADGE_VARIANTS,
29
29
  badgeSchema,
@@ -60,6 +60,7 @@ const slotAccessor = computed({
60
60
  }
61
61
  });
62
62
  const editor = useSlotEditor(slotAccessor, { configure: configure.value });
63
+ const { copy: copyBlocks, paste: pasteBlocks } = useSlotClipboard(editor);
63
64
  const viewMode = ref("general");
64
65
  function selectContent() {
65
66
  viewMode.value = "content";
@@ -539,6 +540,8 @@ if (breadcrumbExt) {
539
540
  @drill-down="onDrillDown"
540
541
  @delete-item="(id) => editor.removeBlock(id)"
541
542
  @delete-items="(ids) => editor.removeBlocks(ids)"
543
+ @copy-items="copyBlocks"
544
+ @paste="pasteBlocks"
542
545
  />
543
546
  </div>
544
547
  </template>
@@ -1,4 +1,4 @@
1
- import type { ConfigEditorState } from './use-editor.js';
1
+ import { type ConfigEditorState } from './use-editor.js';
2
2
  type __VLS_Props = {
3
3
  state: ConfigEditorState;
4
4
  };
@@ -1,8 +1,6 @@
1
1
  <script setup>
2
2
  import { computed, provide, ref, toRef, useTemplateRef } from "vue";
3
3
  import { Icon } from "@iconify/vue";
4
- import { toast } from "vue-sonner";
5
- import { readClip, reidFragment, writeClip } from "../../share/clipboard";
6
4
  import BlockLayoutEditor from "../block-layout-editor/index.vue";
7
5
  import LayoutsSidebar from "../block-layout-editor/sidebar.vue";
8
6
  import LayoutMetaStrip from "../block-layout-editor/meta-strip.vue";
@@ -16,6 +14,7 @@ import { Separator } from "../ui/separator";
16
14
  import { provideEventAncestor } from "../../share/event-bus";
17
15
  import { BREADCRUMB_EXTENSION_KEY } from "./breadcrumb-extension";
18
16
  import { PAGE_TARGET_ID, PageConfig, getStructFieldDescription, getStructFieldTitle, metadata as pageMetadata } from "./schema";
17
+ import { useSlotClipboard } from "./use-editor";
19
18
  defineOptions({ name: "ShwfedConfigConfig" });
20
19
  const props = defineProps({
21
20
  state: { type: Object, required: true }
@@ -100,37 +99,7 @@ function addBlock(entry, options) {
100
99
  function onDrillDown(id) {
101
100
  props.state.onDrillDown(id);
102
101
  }
103
- async function copyBlocks(ids) {
104
- const want = new Set(ids);
105
- const items = props.state.blocks.value.filter((b) => want.has(b.id));
106
- if (items.length === 0) return;
107
- const ls = layouts.value[activeLayoutIndex.value];
108
- const placements = {};
109
- if (ls) {
110
- for (const id of ids) {
111
- const p = ls.layout.placements[id];
112
- if (p) placements[id] = p;
113
- }
114
- }
115
- if (!await writeClip("block", items, placements)) {
116
- toast.error("\u590D\u5236\u5931\u8D25");
117
- return;
118
- }
119
- toast.success(`\u5DF2\u590D\u5236 ${items.length} \u4E2A\u5757`);
120
- }
121
- async function pasteBlocks(target) {
122
- const payload = await readClip();
123
- if (!payload || payload.surface !== "block") return;
124
- const { items, placements } = reidFragment(payload.items, payload.placements);
125
- const pasted = items;
126
- if (pasted.length === 0) return;
127
- if (placements && Object.keys(placements).length > 0) {
128
- props.state.addBlocksWithPlacements(pasted, placements, target);
129
- } else {
130
- props.state.addBlocks(pasted);
131
- }
132
- toast.success(`\u5DF2\u7C98\u8D34 ${pasted.length} \u4E2A\u5757`);
133
- }
102
+ const { copy: copyBlocks, paste: pasteBlocks } = useSlotClipboard(props.state);
134
103
  function updateActiveBlock(next) {
135
104
  props.state.updateActiveBlock(next);
136
105
  }
@@ -1,4 +1,4 @@
1
- import type { ConfigEditorState } from './use-editor.js';
1
+ import { type ConfigEditorState } from './use-editor.js';
2
2
  type __VLS_Props = {
3
3
  state: ConfigEditorState;
4
4
  };
@@ -59,6 +59,25 @@ export interface SlotEditor {
59
59
  configure: (env: Environment) => void;
60
60
  }
61
61
  export declare function useSlotEditor(slot: WritableComputedRef<SlotValue> | Ref<SlotValue>, options?: SlotEditorOptions): SlotEditor;
62
+ export interface SlotClipboardTarget {
63
+ blocks: ComputedRef<ReadonlyArray<BlockValue>>;
64
+ layouts: {
65
+ value: LayoutSetValue[];
66
+ };
67
+ activeLayoutIndex: Ref<number>;
68
+ addBlocks: (blocks: ReadonlyArray<BlockValue>) => void;
69
+ addBlocksWithPlacements: (blocks: ReadonlyArray<BlockValue>, placements: Readonly<Record<string, PlacementValue>>, target: {
70
+ x: number;
71
+ y: number;
72
+ } | null) => void;
73
+ }
74
+ export declare function useSlotClipboard(target: SlotClipboardTarget): {
75
+ copy: (ids: string[]) => Promise<void>;
76
+ paste: (anchor: {
77
+ x: number;
78
+ y: number;
79
+ } | null) => Promise<void>;
80
+ };
62
81
  export interface ConfigEditorState {
63
82
  draft: Ref<PageConfigValue>;
64
83
  stack: Ref<StackEntry[]>;
@@ -1,6 +1,8 @@
1
1
  import { Schema } from "effect";
2
2
  import { computed, ref, shallowRef, watch } from "vue";
3
+ import { toast } from "vue-sonner";
3
4
  import { findFreePlacement, normalizeLayoutSet, placeGroupAt } from "../../share/layout.js";
5
+ import { readClip, reidFragment, writeClip } from "../../share/clipboard.js";
4
6
  import { PageConfig } from "./schema.js";
5
7
  import { BLOCKS, findBlock } from "./utils/resolve.js";
6
8
  import { formatValidationError } from "./utils/validation-error.js";
@@ -180,6 +182,40 @@ export function useSlotEditor(slot, options = {}) {
180
182
  configure
181
183
  };
182
184
  }
185
+ export function useSlotClipboard(target) {
186
+ async function copy(ids) {
187
+ const want = new Set(ids);
188
+ const items = target.blocks.value.filter((b) => want.has(b.id));
189
+ if (items.length === 0) return;
190
+ const ls = target.layouts.value[target.activeLayoutIndex.value];
191
+ const placements = {};
192
+ if (ls) {
193
+ for (const id of ids) {
194
+ const p = ls.layout.placements[id];
195
+ if (p) placements[id] = p;
196
+ }
197
+ }
198
+ if (!await writeClip("block", items, placements)) {
199
+ toast.error("\u590D\u5236\u5931\u8D25");
200
+ return;
201
+ }
202
+ toast.success(`\u5DF2\u590D\u5236 ${items.length} \u4E2A\u5757`);
203
+ }
204
+ async function paste(anchor) {
205
+ const payload = await readClip();
206
+ if (!payload || payload.surface !== "block") return;
207
+ const { items, placements } = reidFragment(payload.items, payload.placements);
208
+ const pasted = items;
209
+ if (pasted.length === 0) return;
210
+ if (placements && Object.keys(placements).length > 0) {
211
+ target.addBlocksWithPlacements(pasted, placements, anchor);
212
+ } else {
213
+ target.addBlocks(pasted);
214
+ }
215
+ toast.success(`\u5DF2\u7C98\u8D34 ${pasted.length} \u4E2A\u5757`);
216
+ }
217
+ return { copy, paste };
218
+ }
183
219
  export function useConfigEditor(model, options = {}) {
184
220
  const configure = options.configure ?? (() => {
185
221
  });
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
2
  import { computed, ref } from "vue";
3
- import { provideCELContext } from "../../utils/cel-context";
3
+ import { provideCELContext, provideScopeAncestor, SELECTIONS_VAR } from "../../utils/cel-context";
4
+ import { getLocalizedText } from "../../share/locale";
5
+ import { findField } from "./utils/resolve";
4
6
  import { ExpressionEditor } from "../ui/expression-editor";
5
7
  import { Field, FieldLabel } from "../ui/field";
6
8
  import { Input } from "../ui/input";
@@ -45,6 +47,41 @@ const formEventAncestor = computed(() => {
45
47
  };
46
48
  });
47
49
  provideEventAncestor(formEventAncestor);
50
+ function selectionFieldLabel(f) {
51
+ const fa = f;
52
+ if (typeof fa.displayName === "string" && fa.displayName.length > 0) return fa.displayName;
53
+ return getLocalizedText(fa.label, "zh") ?? "\u672A\u547D\u540D\u5B57\u6BB5";
54
+ }
55
+ const formScopeAncestor = computed(() => {
56
+ const id = config.value.id;
57
+ if (!id) return null;
58
+ const displayName = config.value.displayName;
59
+ const selectionMembers = config.value.fields.filter((f) => findField(f.type, f.compatibilityDate)?.metadata?.selection).map((f) => ({
60
+ key: `${SELECTIONS_VAR}[${JSON.stringify(f.id)}]`,
61
+ // Addressed key is the bracket lookup, but it mirrors the relative
62
+ // `selections` map — so the picker offers it only where a nearer row /
63
+ // subform shadows `selections`, not to a plain sibling field of this form.
64
+ shortcut: SELECTIONS_VAR,
65
+ label: selectionFieldLabel(f),
66
+ type: "dyn",
67
+ description: "\u8BE5\u9009\u62E9\u5B57\u6BB5\u5F53\u524D\u9009\u4E2D\u9879\u7684\u5B8C\u6574\u5BF9\u8C61\uFF08\u542B key \u4EE5\u5916\u7684\u5B57\u6BB5\uFF09"
68
+ }));
69
+ return {
70
+ id,
71
+ name: displayName && displayName.length > 0 ? displayName : void 0,
72
+ typeName: formMetadata.name,
73
+ typeIcon: formMetadata.icon,
74
+ members: [
75
+ { key: "form", label: "\u8868\u5355\u503C", type: "dyn", description: "\u8BE5\u8868\u5355\u7684\u5F53\u524D\u6574\u4F53\u72B6\u6001" },
76
+ ...selectionMembers
77
+ ],
78
+ // The form unconditionally rebinds both `form` and `selections` for its
79
+ // subtree (index.vue), so declare them explicitly rather than deriving from
80
+ // `members` — a form with no selection field still shadows `selections`.
81
+ shadows: ["form", SELECTIONS_VAR]
82
+ };
83
+ });
84
+ provideScopeAncestor(formScopeAncestor);
48
85
  const unitModel = computed({
49
86
  get: () => ({ fields: config.value.fields, layouts: config.value.layouts }),
50
87
  set: (next) => {
@@ -14,6 +14,7 @@ export declare const metadata: {
14
14
  readonly initial: 2;
15
15
  readonly min: 2;
16
16
  readonly max: number;
17
+ readonly grow: true;
17
18
  };
18
19
  };
19
20
  export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
@@ -7,7 +7,7 @@ export const metadata = {
7
7
  name: "Markdown",
8
8
  icon: "fluent:markdown-20-regular",
9
9
  w: { initial: 8, min: 2, max: Infinity },
10
- h: { initial: 2, min: 2, max: Infinity }
10
+ h: { initial: 2, min: 2, max: Infinity, grow: true }
11
11
  };
12
12
  export function schema(configure) {
13
13
  return Schema.Struct({
@@ -4,6 +4,7 @@ type __VLS_ModelProps = {
4
4
  };
5
5
  declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
6
  "update:modelValue": (value: {
7
+ readonly size: "default" | "sm" | "xs";
7
8
  readonly type: "com.shwfed.form.field.actions";
8
9
  readonly style?: string | undefined;
9
10
  readonly id: string;
@@ -88,6 +89,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
88
89
  }) => any;
89
90
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
90
91
  "onUpdate:modelValue"?: ((value: {
92
+ readonly size: "default" | "sm" | "xs";
91
93
  readonly type: "com.shwfed.form.field.actions";
92
94
  readonly style?: string | undefined;
93
95
  readonly id: string;
@@ -24,17 +24,18 @@ onBeforeUnmount(() => {
24
24
  const actionValue = computed({
25
25
  get: () => ({
26
26
  kind: "shwfed.component.action",
27
- size: "default",
27
+ size: value.value.size ?? "default",
28
28
  gap: value.value.gap ?? 4,
29
29
  style: value.value.style,
30
30
  groups: value.value.groups ?? [],
31
31
  items: value.value.items ?? []
32
32
  }),
33
33
  set: (next) => {
34
- const { gap, style, groups, items } = next;
34
+ const { size, gap, style, groups, items } = next;
35
35
  const { style: _drop, ...rest } = value.value;
36
36
  value.value = {
37
37
  ...rest,
38
+ size,
38
39
  gap,
39
40
  groups,
40
41
  items,
@@ -4,6 +4,7 @@ type __VLS_ModelProps = {
4
4
  };
5
5
  declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
6
  "update:modelValue": (value: {
7
+ readonly size: "default" | "sm" | "xs";
7
8
  readonly type: "com.shwfed.form.field.actions";
8
9
  readonly style?: string | undefined;
9
10
  readonly id: string;
@@ -88,6 +89,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
88
89
  }) => any;
89
90
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
90
91
  "onUpdate:modelValue"?: ((value: {
92
+ readonly size: "default" | "sm" | "xs";
91
93
  readonly type: "com.shwfed.form.field.actions";
92
94
  readonly style?: string | undefined;
93
95
  readonly id: string;
@@ -8,7 +8,7 @@ const props = defineProps({
8
8
  });
9
9
  const actionsConfig = computed(() => ({
10
10
  kind: "shwfed.component.action",
11
- size: "default",
11
+ size: props.config.size ?? "default",
12
12
  gap: props.config.gap,
13
13
  style: props.config.style,
14
14
  groups: props.config.groups.map((group) => ({
@@ -141,6 +141,9 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
141
141
  }, never>>, {
142
142
  default: () => never[];
143
143
  }>;
144
+ size: Schema.optionalWith<Schema.Literal<["default", "sm", "xs"]>, {
145
+ default: () => "default";
146
+ }>;
144
147
  gap: Schema.optionalWith<Schema.filter<Schema.filter<typeof Schema.Number>>, {
145
148
  default: () => number;
146
149
  }>;
@@ -14,7 +14,7 @@ export const metadata = {
14
14
  };
15
15
  export function schema(configure) {
16
16
  const { fields: actionFields } = ActionSchemaFields(configure);
17
- const { size: _size, groups, items, ...rest } = actionFields;
17
+ const { groups, items, ...rest } = actionFields;
18
18
  return Schema.Struct({
19
19
  type: Schema.Literal(type),
20
20
  compatibilityDate: Schema.Literal(compatibilityDate),
@@ -42,9 +42,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
42
42
  readonly mode: "formula" | "prefill";
43
43
  readonly expression: string;
44
44
  } | undefined;
45
- readonly precision?: number | undefined;
46
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
47
- readonly valueAsString?: boolean | undefined;
48
45
  readonly validations?: readonly {
49
46
  readonly message: readonly [{
50
47
  readonly locale: "zh";
@@ -56,6 +53,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
56
53
  readonly warning?: boolean | undefined;
57
54
  readonly when: string;
58
55
  }[] | undefined;
56
+ readonly precision?: number | undefined;
57
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
58
+ readonly valueAsString?: boolean | undefined;
59
59
  }) => any;
60
60
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
61
61
  "onUpdate:modelValue"?: ((value: {
@@ -97,9 +97,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
97
97
  readonly mode: "formula" | "prefill";
98
98
  readonly expression: string;
99
99
  } | undefined;
100
- readonly precision?: number | undefined;
101
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
102
- readonly valueAsString?: boolean | undefined;
103
100
  readonly validations?: readonly {
104
101
  readonly message: readonly [{
105
102
  readonly locale: "zh";
@@ -111,6 +108,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
111
108
  readonly warning?: boolean | undefined;
112
109
  readonly when: string;
113
110
  }[] | undefined;
111
+ readonly precision?: number | undefined;
112
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
113
+ readonly valueAsString?: boolean | undefined;
114
114
  }) => any) | undefined;
115
115
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
116
116
  declare const _default: typeof __VLS_export;
@@ -42,9 +42,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
42
42
  readonly mode: "formula" | "prefill";
43
43
  readonly expression: string;
44
44
  } | undefined;
45
- readonly precision?: number | undefined;
46
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
47
- readonly valueAsString?: boolean | undefined;
48
45
  readonly validations?: readonly {
49
46
  readonly message: readonly [{
50
47
  readonly locale: "zh";
@@ -56,6 +53,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
56
53
  readonly warning?: boolean | undefined;
57
54
  readonly when: string;
58
55
  }[] | undefined;
56
+ readonly precision?: number | undefined;
57
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
58
+ readonly valueAsString?: boolean | undefined;
59
59
  }) => any;
60
60
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
61
61
  "onUpdate:modelValue"?: ((value: {
@@ -97,9 +97,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
97
97
  readonly mode: "formula" | "prefill";
98
98
  readonly expression: string;
99
99
  } | undefined;
100
- readonly precision?: number | undefined;
101
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
102
- readonly valueAsString?: boolean | undefined;
103
100
  readonly validations?: readonly {
104
101
  readonly message: readonly [{
105
102
  readonly locale: "zh";
@@ -111,6 +108,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
111
108
  readonly warning?: boolean | undefined;
112
109
  readonly when: string;
113
110
  }[] | undefined;
111
+ readonly precision?: number | undefined;
112
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
113
+ readonly valueAsString?: boolean | undefined;
114
114
  }) => any) | undefined;
115
115
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
116
116
  declare const _default: typeof __VLS_export;
@@ -50,9 +50,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
50
50
  readonly locale: "en" | "ja" | "ko";
51
51
  readonly message: string;
52
52
  }[]] | undefined;
53
- readonly precision?: number | undefined;
54
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
55
- readonly valueAsString?: boolean | undefined;
56
53
  readonly validations?: readonly {
57
54
  readonly message: readonly [{
58
55
  readonly locale: "zh";
@@ -64,6 +61,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
64
61
  readonly warning?: boolean | undefined;
65
62
  readonly when: string;
66
63
  }[] | undefined;
64
+ readonly precision?: number | undefined;
65
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
66
+ readonly valueAsString?: boolean | undefined;
67
67
  }) => any;
68
68
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
69
69
  "onUpdate:modelValue"?: ((value: {
@@ -113,9 +113,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
113
113
  readonly locale: "en" | "ja" | "ko";
114
114
  readonly message: string;
115
115
  }[]] | undefined;
116
- readonly precision?: number | undefined;
117
- readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
118
- readonly valueAsString?: boolean | undefined;
119
116
  readonly validations?: readonly {
120
117
  readonly message: readonly [{
121
118
  readonly locale: "zh";
@@ -127,6 +124,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
127
124
  readonly warning?: boolean | undefined;
128
125
  readonly when: string;
129
126
  }[] | undefined;
127
+ readonly precision?: number | undefined;
128
+ readonly roundingMode?: "round" | "floor" | "ceil" | undefined;
129
+ readonly valueAsString?: boolean | undefined;
130
130
  }) => any) | undefined;
131
131
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
132
132
  declare const _default: typeof __VLS_export;