@shwfed/config 2.3.10 → 2.3.11

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 (50) hide show
  1. package/dist/mcp.mjs +704 -441
  2. package/dist/module.json +1 -1
  3. package/dist/preview/assets/{config-BxzDT_57.js → config-57-v4VXo.js} +1 -1
  4. package/dist/preview/assets/{config-2R4XkSGs.js → config-BZahzuEc.js} +1 -1
  5. package/dist/preview/assets/{config-KcLrpkc1.js → config-Be-2ZA2R.js} +1 -1
  6. package/dist/preview/assets/{config-ykJZssgx.js → config-BnZQO-Sp.js} +1 -1
  7. package/dist/preview/assets/{config-bxqg3yuv.js → config-Bp91DUdU.js} +1 -1
  8. package/dist/preview/assets/{config-CyScbxXy.js → config-DNokxY7M.js} +1 -1
  9. package/dist/preview/assets/{config-HdWYFZ09.js → config-_msO_f2R.js} +1 -1
  10. package/dist/preview/assets/{config-ja_GMXxV.js → config-_uPI8qV-.js} +1 -1
  11. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js → definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js} +1 -1
  12. package/dist/preview/assets/index-DDbl2Atj.js +1 -0
  13. package/dist/preview/assets/index-DGa3Oj3y.js +1075 -0
  14. package/dist/preview/assets/{index-BPKK3hGV.css → index-mbGtsgdv.css} +1 -1
  15. package/dist/preview/assets/{runtime-LBdh1D75.js → runtime-B9u14qqB.js} +1 -1
  16. package/dist/preview/assets/{runtime-B8aUJIpn.js → runtime-BBms4myv.js} +1 -1
  17. package/dist/preview/assets/{runtime-D2K1s33u.js → runtime-CfR7ZAND.js} +1 -1
  18. package/dist/preview/assets/{runtime-C9XnvD5A.js → runtime-D3EyeiyA.js} +1 -1
  19. package/dist/preview/assets/{runtime-DqZhDPHl.js → runtime-Dk9u-Ybw.js} +1 -1
  20. package/dist/preview/assets/{runtime-CpSiaWMP.js → runtime-XXqIAt53.js} +1 -1
  21. package/dist/preview/assets/{runtime-CxA8fvQP.js → runtime-cKWSGFod.js} +1 -1
  22. package/dist/preview/assets/{runtime-DAXQmtLg.js → runtime-w7V-p3t1.js} +1 -1
  23. package/dist/preview/index.html +2 -2
  24. package/dist/runtime/components/form/ai/fields-button.vue +3 -1
  25. package/dist/runtime/components/form/unit-config.vue +8 -4
  26. package/dist/runtime/components/form/utils/resolve.d.ts +48 -9
  27. package/dist/runtime/components/form/utils/resolve.js +59 -5
  28. package/dist/runtime/components/table/ai/columns-button.vue +1 -0
  29. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.d.vue.ts +10 -0
  30. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.vue +305 -0
  31. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.vue.d.ts +10 -0
  32. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.d.vue.ts +9 -0
  33. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.vue +81 -0
  34. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.vue.d.ts +9 -0
  35. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/schema.d.ts +55 -0
  36. package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/schema.js +82 -0
  37. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.d.vue.ts +10 -0
  38. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.vue +310 -0
  39. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.vue.d.ts +10 -0
  40. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.d.vue.ts +9 -0
  41. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.vue +81 -0
  42. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.vue.d.ts +9 -0
  43. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/schema.d.ts +66 -0
  44. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/schema.js +71 -0
  45. package/dist/runtime/components/table/config.vue +2 -1
  46. package/dist/runtime/components/table/utils/resolve.d.ts +53 -0
  47. package/dist/runtime/components/table/utils/resolve.js +66 -1
  48. package/package.json +1 -1
  49. package/dist/preview/assets/index-D3pf2RjG.js +0 -1
  50. package/dist/preview/assets/index-OUd02U3g.js +0 -1075
@@ -1 +1 @@
1
- import{d as u,ad as l,e as i,u as p,$ as d,o as m,k as f,af as _,ag as k,aj as g,ak as w,al as x}from"./index-OUd02U3g.js";const B=u({name:"ShwfedMarkdownItemRuntime",__name:"runtime",props:{config:{}},setup(n){const s=n,{locale:o}=l(),t=x(),c=(e,a)=>g(e,{...w(t),...a}),r=d(()=>{const e=_(s.config.content,o.value)??"";return k(e,c)});return(e,a)=>(m(),i(p(f),{"data-slot":"buttons-markdown",source:r.value,class:"prose prose-sm prose-zinc px-1"},null,8,["source"]))}});export{B as default};
1
+ import{d as u,ad as l,e as i,u as p,$ as d,o as m,k as f,af as _,ag as k,aj as g,ak as w,al as x}from"./index-DGa3Oj3y.js";const B=u({name:"ShwfedMarkdownItemRuntime",__name:"runtime",props:{config:{}},setup(n){const s=n,{locale:o}=l(),t=x(),c=(e,a)=>g(e,{...w(t),...a}),r=d(()=>{const e=_(s.config.content,o.value)??"";return k(e,c)});return(e,a)=>(m(),i(p(f),{"data-slot":"buttons-markdown",source:r.value,class:"prose prose-sm prose-zinc px-1"},null,8,["source"]))}});export{B as default};
@@ -1 +1 @@
1
- import{_ as o}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";import{d as n,e as a,u as c,o as f,ao as i}from"./index-OUd02U3g.js";const _=n({name:"ShwfedPrototypeActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const t=i;return(r,s)=>(f(),a(o,{"action-id":e.buttonId,effect:c(t)},null,8,["action-id","effect"]))}});export{_ as default};
1
+ import{_ as o}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";import{d as n,e as a,u as c,o as f,ao as i}from"./index-DGa3Oj3y.js";const _=n({name:"ShwfedPrototypeActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const t=i;return(r,s)=>(f(),a(o,{"action-id":e.buttonId,effect:c(t)},null,8,["action-id","effect"]))}});export{_ as default};
@@ -1 +1 @@
1
- import{_ as s}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";import{d as c,an as o,az as i,e as r,u as f,am as u,o as m}from"./index-OUd02U3g.js";const g=c({name:"ShwfedEventDispatchActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const n=e,t=u(),a=o(()=>i(t,n.config.triggers));return(p,d)=>(m(),r(s,{"action-id":e.buttonId,effect:f(a)},null,8,["action-id","effect"]))}});export{g as default};
1
+ import{_ as s}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";import{d as c,an as o,az as i,e as r,u as f,am as u,o as m}from"./index-DGa3Oj3y.js";const g=c({name:"ShwfedEventDispatchActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const n=e,t=u(),a=o(()=>i(t,n.config.triggers));return(p,d)=>(m(),r(s,{"action-id":e.buttonId,effect:f(a)},null,8,["action-id","effect"]))}});export{g as default};
@@ -1 +1 @@
1
- import{d as r,an as f,ai as u,e as p,u as g,aj as m,ak as l,al as d,am as x,o as E}from"./index-OUd02U3g.js";import{_}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";const k=r({name:"ShwfedHttpRequestActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(n){const e=n,t=d(),c=(s,o)=>m(s,{...l(t),...o}),i=x(),a=f(()=>u(e.config.expression,c,{messageExpression:e.config.messageExpression,resultExpression:e.config.resultExpression,channel:i,triggers:{success:e.config.onSuccess,warning:e.config.onWarning,error:e.config.onError,info:e.config.onInfo}}));return(s,o)=>(E(),p(_,{"action-id":n.buttonId,effect:g(a)},null,8,["action-id","effect"]))}});export{k as default};
1
+ import{d as r,an as f,ai as u,e as p,u as g,aj as m,ak as l,al as d,am as x,o as E}from"./index-DGa3Oj3y.js";import{_}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";const k=r({name:"ShwfedHttpRequestActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(n){const e=n,t=d(),c=(s,o)=>m(s,{...l(t),...o}),i=x(),a=f(()=>u(e.config.expression,c,{messageExpression:e.config.messageExpression,resultExpression:e.config.resultExpression,channel:i,triggers:{success:e.config.onSuccess,warning:e.config.onWarning,error:e.config.onError,info:e.config.onInfo}}));return(s,o)=>(E(),p(_,{"action-id":n.buttonId,effect:g(a)},null,8,["action-id","effect"]))}});export{k as default};
@@ -1 +1 @@
1
- import{d as i,an as f,ap as r,aq as d,e as u,u as m,aj as p,ak as l,al as _,o as g}from"./index-OUd02U3g.js";import{_ as h}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";const x=i({name:"ShwfedNavigationActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const t=e,o=_(),c=(n,a)=>p(n,{...l(o),...a}),s=f(()=>r(c(t.config.url),n=>d(()=>{window.open(n,t.config.mode)})));return(n,a)=>(g(),u(h,{"action-id":e.buttonId,effect:m(s)},null,8,["action-id","effect"]))}});export{x as default};
1
+ import{d as i,an as f,ap as r,aq as d,e as u,u as m,aj as p,ak as l,al as _,o as g}from"./index-DGa3Oj3y.js";import{_ as h}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";const x=i({name:"ShwfedNavigationActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(e){const t=e,o=_(),c=(n,a)=>p(n,{...l(o),...a}),s=f(()=>r(c(t.config.url),n=>d(()=>{window.open(n,t.config.mode)})));return(n,a)=>(g(),u(h,{"action-id":e.buttonId,effect:m(s)},null,8,["action-id","effect"]))}});export{x as default};
@@ -1 +1 @@
1
- import{d as s,ad as B,ar as E,ae as _,af as i,as as x,at as p,e as M,w as c,u as d,a0 as N,am as b,aq as k,o as w,f as L,au as T,g as u,av as I,aw as H,a7 as S,al as V,ax as $,ay as z}from"./index-OUd02U3g.js";import{_ as A}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";const F=s({name:"ShwfedModalLayoutActionRuntime",__name:"runtime",props:{buttonId:{},config:{},buttonTitle:{}},setup(a){const n=a,{locale:l}=B(),f=e=>{},r=V(),m=b(),o=N(null),v=E(n.buttonId,{close:()=>k(()=>{o.value?.()})},m),g=s({name:"ModalBoundaryBridge",setup(e,{slots:t}){return H(r),$(z,v),()=>t.default?.()}}),h=(e,t)=>S()?.(e,t),C=_(function*(){const e=i(n.config.modalTitle,l.value)??i(n.buttonTitle,l.value)??"",{modal:t,close:y}=yield*x({title:e,width:n.config.modalWidth});o.value=()=>p(y()),yield*t,o.value=null});return(e,t)=>(w(),M(A,{"action-id":a.buttonId,effect:d(C)},{default:c(()=>[L("div",{style:T(a.config.modalMinHeight?`min-height: ${a.config.modalMinHeight}`:void 0)},[u(d(g),null,{default:c(()=>[u(I,{"slot-value":a.config.slot,configure:f,"find-entry":h},null,8,["slot-value"])]),_:1})],4)]),_:1},8,["action-id","effect"]))}});export{F as default};
1
+ import{d as s,ad as B,ar as E,ae as _,af as i,as as x,at as p,e as M,w as c,u as d,a0 as N,am as b,aq as k,o as w,f as L,au as T,g as u,av as I,aw as H,a7 as S,al as V,ax as $,ay as z}from"./index-DGa3Oj3y.js";import{_ as A}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";const F=s({name:"ShwfedModalLayoutActionRuntime",__name:"runtime",props:{buttonId:{},config:{},buttonTitle:{}},setup(a){const n=a,{locale:l}=B(),f=e=>{},r=V(),m=b(),o=N(null),v=E(n.buttonId,{close:()=>k(()=>{o.value?.()})},m),g=s({name:"ModalBoundaryBridge",setup(e,{slots:t}){return H(r),$(z,v),()=>t.default?.()}}),h=(e,t)=>S()?.(e,t),C=_(function*(){const e=i(n.config.modalTitle,l.value)??i(n.buttonTitle,l.value)??"",{modal:t,close:y}=yield*x({title:e,width:n.config.modalWidth});o.value=()=>p(y()),yield*t,o.value=null});return(e,t)=>(w(),M(A,{"action-id":a.buttonId,effect:d(C)},{default:c(()=>[L("div",{style:T(a.config.modalMinHeight?`min-height: ${a.config.modalMinHeight}`:void 0)},[u(d(g),null,{default:c(()=>[u(I,{"slot-value":a.config.slot,configure:f,"find-entry":h},null,8,["slot-value"])]),_:1})],4)]),_:1},8,["action-id","effect"]))}});export{F as default};
@@ -1 +1 @@
1
- import{d as f,ad as u,ae as l,af as g,ag as d,ah as m,ai as p,e as x,u as E,aj as _,ak as h,al as k,am as w,o as C}from"./index-OUd02U3g.js";import{_ as I}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";const R=f({name:"ShwfedHttpRequestConfirmActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(s){const n=s,{locale:c}=u(),a=k(),t=(e,o)=>_(e,{...h(a),...o}),i=w(),r=l(function*(){const e=g(n.config.markdown,c.value)??"",o=d(e,t);(yield*m({content:o,icon:n.config.icon,color:n.config.color}))||(yield*p(n.config.expression,t,{messageExpression:n.config.messageExpression,resultExpression:n.config.resultExpression,channel:i,triggers:{success:n.config.onSuccess,warning:n.config.onWarning,error:n.config.onError,info:n.config.onInfo}}))});return(e,o)=>(C(),x(I,{"action-id":s.buttonId,effect:E(r)},null,8,["action-id","effect"]))}});export{R as default};
1
+ import{d as f,ad as u,ae as l,af as g,ag as d,ah as m,ai as p,e as x,u as E,aj as _,ak as h,al as k,am as w,o as C}from"./index-DGa3Oj3y.js";import{_ as I}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";const R=f({name:"ShwfedHttpRequestConfirmActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(s){const n=s,{locale:c}=u(),a=k(),t=(e,o)=>_(e,{...h(a),...o}),i=w(),r=l(function*(){const e=g(n.config.markdown,c.value)??"",o=d(e,t);(yield*m({content:o,icon:n.config.icon,color:n.config.color}))||(yield*p(n.config.expression,t,{messageExpression:n.config.messageExpression,resultExpression:n.config.resultExpression,channel:i,triggers:{success:n.config.onSuccess,warning:n.config.onWarning,error:n.config.onError,info:n.config.onInfo}}))});return(e,o)=>(C(),x(I,{"action-id":s.buttonId,effect:E(r)},null,8,["action-id","effect"]))}});export{R as default};
@@ -1 +1 @@
1
- import{d as _,an as j,ae as B,aq as r,az as m,aA as E,aB as S,e as k,u as v,aj as x,ak as L,am as b,o as C,al as R}from"./index-OUd02U3g.js";import{_ as T}from"./definition.vue_vue_type_script_setup_true_lang-CWyVRSOh.js";const I=_({name:"ShwfedHttpDownloadActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(d){const c=d,g=R(),t=(e,n)=>x(e,{...L(g),...n}),l=b(),p=new Set(["success","error","warning","info"]);function f(e){const n=URL.createObjectURL(e),o=document.createElement("a");o.href=n,o.download=e.name,document.body.appendChild(o),o.click(),o.remove(),URL.revokeObjectURL(n)}const y=j(()=>B(function*(){const{template:e,messageExpression:n,resultExpression:o}=c.config,u=yield*t(e.request);if(!e.download){const s=yield*u.file();return yield*r(()=>f(s)),yield*m(l,c.config.onSuccess)}const a={json:yield*u.json()},i=o===void 0?"success":yield*E(t(o,a),s=>p.has(s)?s:"success");if(n!==void 0){const s=yield*t(n,a);yield*r(()=>S[i](s))}if(i==="success"||i==="info"){const h=yield*(yield*t(e.download,a)).file();yield*r(()=>f(h))}const w={success:c.config.onSuccess,warning:c.config.onWarning,error:c.config.onError,info:c.config.onInfo};return yield*m(l,w[i])}));return(e,n)=>(C(),k(T,{"action-id":d.buttonId,effect:v(y)},null,8,["action-id","effect"]))}});export{I as default};
1
+ import{d as _,an as j,ae as B,aq as r,az as m,aA as E,aB as S,e as k,u as v,aj as x,ak as L,am as b,o as C,al as R}from"./index-DGa3Oj3y.js";import{_ as T}from"./definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js";const I=_({name:"ShwfedHttpDownloadActionRuntime",__name:"runtime",props:{buttonId:{},config:{}},setup(d){const c=d,g=R(),t=(e,n)=>x(e,{...L(g),...n}),l=b(),p=new Set(["success","error","warning","info"]);function f(e){const n=URL.createObjectURL(e),o=document.createElement("a");o.href=n,o.download=e.name,document.body.appendChild(o),o.click(),o.remove(),URL.revokeObjectURL(n)}const y=j(()=>B(function*(){const{template:e,messageExpression:n,resultExpression:o}=c.config,u=yield*t(e.request);if(!e.download){const s=yield*u.file();return yield*r(()=>f(s)),yield*m(l,c.config.onSuccess)}const a={json:yield*u.json()},i=o===void 0?"success":yield*E(t(o,a),s=>p.has(s)?s:"success");if(n!==void 0){const s=yield*t(n,a);yield*r(()=>S[i](s))}if(i==="success"||i==="info"){const h=yield*(yield*t(e.download,a)).file();yield*r(()=>f(h))}const w={success:c.config.onSuccess,warning:c.config.onWarning,error:c.config.onError,info:c.config.onInfo};return yield*m(l,w[i])}));return(e,n)=>(C(),k(T,{"action-id":d.buttonId,effect:v(y)},null,8,["action-id","effect"]))}});export{I as default};
@@ -10,8 +10,8 @@
10
10
  `--primary`, the primary button variant renders as transparent. */
11
11
  body { --primary: #009689; }
12
12
  </style>
13
- <script type="module" crossorigin src="./assets/index-OUd02U3g.js"></script>
14
- <link rel="stylesheet" crossorigin href="./assets/index-BPKK3hGV.css">
13
+ <script type="module" crossorigin src="./assets/index-DGa3Oj3y.js"></script>
14
+ <link rel="stylesheet" crossorigin href="./assets/index-mbGtsgdv.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="app"></div>
@@ -177,9 +177,11 @@ function collectErrors(fields) {
177
177
  return;
178
178
  }
179
179
  if (entry.deprecated) {
180
+ const sup = entry.supersededBy;
181
+ const replacement = sup ? sup.type === type ? `"${sup.compatibilityDate}"` : `"${sup.type}@${sup.compatibilityDate}"` : "the current alternative";
180
182
  out.push({
181
183
  path: `${prefix}.compatibilityDate`,
182
- message: `compatibilityDate "${field.compatibilityDate}" for "${type}" is deprecated. Use "${entry.supersededBy}" instead.`
184
+ message: `compatibilityDate "${field.compatibilityDate}" for "${type}" is deprecated. Use ${replacement} instead.`
183
185
  });
184
186
  return;
185
187
  }
@@ -90,6 +90,7 @@ function addLayout() {
90
90
  const FIELD_PICKER_ENTRIES = computed(() => {
91
91
  const byType = /* @__PURE__ */ new Map();
92
92
  for (const f of FIELDS) {
93
+ if (f.deprecated) continue;
93
94
  const prev = byType.get(f.type);
94
95
  if (!prev || f.compatibilityDate > prev.compatibilityDate) {
95
96
  byType.set(f.type, {
@@ -203,7 +204,7 @@ const migrationContext = { $cel: celForMigration };
203
204
  const latestEntry = computed(() => {
204
205
  const e = activeFieldEntry.value;
205
206
  if (!e?.deprecated || !e.supersededBy) return null;
206
- return findField(e.type, e.supersededBy) ?? null;
207
+ return findField(e.supersededBy.type, e.supersededBy.compatibilityDate) ?? null;
207
208
  });
208
209
  const canMigrate = computed(() => !!latestEntry.value?.migrate);
209
210
  async function migrateActiveField() {
@@ -213,7 +214,10 @@ async function migrateActiveField() {
213
214
  try {
214
215
  const next = await Effect.runPromise(target.migrate(sel, migrationContext));
215
216
  updateActiveField(next);
216
- toast.success(`\u5DF2\u8FC1\u79FB\u5230 ${target.compatibilityDate} \u7248\u672C`);
217
+ const isSameType = target.type === sel.type;
218
+ toast.success(
219
+ isSameType ? `\u5DF2\u8FC1\u79FB\u5230 ${target.compatibilityDate} \u7248\u672C` : `\u5DF2\u8FC1\u79FB\u5230 ${target.type}@${target.compatibilityDate}`
220
+ );
217
221
  } catch (err) {
218
222
  const message = err instanceof Error ? err.message : String(err);
219
223
  toast.error("\u8FC1\u79FB\u5931\u8D25", { description: message });
@@ -466,7 +470,7 @@ function isExtraActive(id) {
466
470
  v-if="activeFieldEntry"
467
471
  >
468
472
  <div
469
- v-if="activeFieldEntry.deprecated"
473
+ v-if="activeFieldEntry.deprecated && activeFieldEntry.supersededBy"
470
474
  class="mb-4 flex items-center gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800"
471
475
  role="alert"
472
476
  >
@@ -475,7 +479,7 @@ function isExtraActive(id) {
475
479
  class="size-4 shrink-0"
476
480
  />
477
481
  <span class="flex-1">
478
- 此字段类型已弃用,建议迁移至 <span class="tabular-nums font-medium">{{ activeFieldEntry.supersededBy }}</span> 版本。
482
+ 此字段类型已弃用,建议迁移至 <span class="tabular-nums font-medium">{{ activeFieldEntry.supersededBy.type === activeFieldEntry.type ? activeFieldEntry.supersededBy.compatibilityDate : `${activeFieldEntry.supersededBy.type}@${activeFieldEntry.supersededBy.compatibilityDate}` }}</span>。
479
483
  </span>
480
484
  <Button
481
485
  v-if="canMigrate"
@@ -13,12 +13,29 @@ export type MigrationContext = Readonly<{
13
13
  $cel: <T>(expression: string, context?: Record<string, unknown>) => Effect.Effect<T, unknown>;
14
14
  }>;
15
15
  /**
16
- * Migrates a value of the previous compatibilityDate's schema to this entry's
17
- * schema. Lives on the *target* (newer) entry. The Effect's success channel
18
- * is the migrated value; failures surface to the host as a toast — see
16
+ * Migrates a value of a source schema to this entry's schema. Lives on the
17
+ * target (newer) entry. The Effect's success channel is the migrated value;
18
+ * failures surface to the host as a toast — see
19
19
  * src/runtime/components/form/config.vue's migrate handler.
20
+ *
21
+ * The target is selected by one of two rules: (a) same `type` with a strictly
22
+ * newer `compatibilityDate`, or (b) an explicit `migrateFrom` declaration on
23
+ * the target naming this source `(type, compatibilityDate)`. Either way the
24
+ * migrate function only sees the previous value and the host capabilities.
20
25
  */
21
26
  export type MigrateFn = (prev: unknown, ctx: MigrationContext) => Effect.Effect<unknown, Error>;
27
+ /**
28
+ * Identifies an entry by `(type, compatibilityDate)`. Used for `migrateFrom`
29
+ * declarations on a target, and for the cross-type form of `supersededBy`
30
+ * exposed on a deprecated entry.
31
+ *
32
+ * Migration declarations only ever live on the *new* (target) code — old
33
+ * folders are never modified to announce that they have been superseded.
34
+ */
35
+ export type MigrateSource = Readonly<{
36
+ type: string;
37
+ compatibilityDate: string;
38
+ }>;
22
39
  export type FieldSizeRange = Readonly<{
23
40
  /** Span (in step units) used when the field is first placed. */
24
41
  initial: number;
@@ -54,16 +71,25 @@ export type FieldEntry = Readonly<{
54
71
  schema: SchemaFactory;
55
72
  config: Component;
56
73
  runtime: Component;
57
- /** True iff a newer entry with the same `type` exists in the registry. */
74
+ /**
75
+ * True iff this entry is shadowed by either (a) a newer-date entry sharing
76
+ * its `type`, or (b) another entry whose `migrateFrom` names this one.
77
+ */
58
78
  deprecated: boolean;
59
- /** `compatibilityDate` of the newest entry sharing this `type`, when deprecated. */
60
- supersededBy?: string;
61
79
  /**
62
- * Optional migration from the previous compatibilityDate's schema to this
63
- * entry's schema. When present, the host renders a "迁移" button on
64
- * deprecated fields whose superseding entry exposes this function.
80
+ * Identifies the entry that supersedes this one. Same `type` for the
81
+ * date-versioned rule; a different `type` for declared cross-type
82
+ * migrations.
83
+ */
84
+ supersededBy?: MigrateSource;
85
+ /**
86
+ * Optional migration from a recognized source schema to this entry's
87
+ * schema. When present, the host renders a "迁移" button on deprecated
88
+ * entries whose target exposes this function.
65
89
  */
66
90
  migrate?: MigrateFn;
91
+ /** Source schemas this entry accepts migration from. See module field. */
92
+ migrateFrom?: ReadonlyArray<MigrateSource>;
67
93
  /**
68
94
  * Optional creation-time defaults. The host merges the returned partial
69
95
  * onto the seed when the user adds a new field via the picker. Absent →
@@ -74,4 +100,17 @@ export type FieldEntry = Readonly<{
74
100
  export declare const FIELDS: ReadonlyArray<FieldEntry>;
75
101
  export declare function allFieldSchemas(configure: (env: Environment) => void): ReadonlyArray<AnySchema>;
76
102
  export declare function findField(type: string, compatibilityDate: string): FieldEntry | undefined;
103
+ /**
104
+ * Finds the registry entry that has declared `(type, compatibilityDate)` as a
105
+ * migration source on its `migrateFrom`. Use when a stored value's identity
106
+ * no longer matches any registry entry (the old folder was renamed away) —
107
+ * a present return means the value is still migratable via the target's
108
+ * `migrate`.
109
+ *
110
+ * Note: for the date-versioned rule (same `type`, newer `compatibilityDate`)
111
+ * the entry itself is in the registry and tagged `deprecated`/`supersededBy`;
112
+ * callers reach the target through `findField(supersededBy.type, …)` rather
113
+ * than this function.
114
+ */
115
+ export declare function findMigrationTarget(type: string, compatibilityDate: string): FieldEntry | undefined;
77
116
  export {};
@@ -11,28 +11,76 @@ const runtimeModules = import.meta.glob(
11
11
  "../fields/*/*/runtime.vue",
12
12
  { eager: true, import: "default", exhaustive: true }
13
13
  );
14
+ const sourceKey = (type, compatibilityDate) => `${type}@${compatibilityDate}`;
14
15
  const registry = defineRegistry({
15
16
  hostName: "shwfed-form",
16
17
  dirSegment: "fields",
17
18
  schemaModules,
18
19
  configModules,
19
20
  runtimeModules,
20
- // An entry is deprecated iff another entry shares its `type` with a strictly
21
- // greater `compatibilityDate`. The picker already shows only the latest per
22
- // type; this flag drives the deprecation alert in the right-pane inspector.
21
+ // An entry is deprecated by one of two rules:
22
+ // (a) another entry shares its `type` with a strictly greater
23
+ // `compatibilityDate` date-versioned supersession;
24
+ // (b) another entry declares this one in its `migrateFrom` — explicit
25
+ // cross-type supersession (rename, split, merge).
26
+ // Rule (a) wins on overlap, since the date-versioned rule is the implicit
27
+ // convention; an explicit `migrateFrom` against a still-current entry is
28
+ // almost always a mistake, but we let it slide as long as no two targets
29
+ // claim the same source (that collision throws below).
23
30
  deriveEntryExtras: (drafts) => {
24
31
  const latestByType = /* @__PURE__ */ new Map();
25
32
  for (const d of drafts) {
26
33
  const prev = latestByType.get(d.type);
27
34
  if (!prev || d.compatibilityDate > prev) latestByType.set(d.type, d.compatibilityDate);
28
35
  }
36
+ const migrationTargets = /* @__PURE__ */ new Map();
37
+ for (const d of drafts) {
38
+ const sources = d.module.migrateFrom;
39
+ if (!sources || sources.length === 0) continue;
40
+ for (const src of sources) {
41
+ const key = sourceKey(src.type, src.compatibilityDate);
42
+ const existing = migrationTargets.get(key);
43
+ if (existing) {
44
+ throw new Error(
45
+ `[shwfed-form] migrateFrom collision: source "${key}" is claimed by both "${sourceKey(existing.type, existing.compatibilityDate)}" and "${sourceKey(d.type, d.compatibilityDate)}"`
46
+ );
47
+ }
48
+ migrationTargets.set(key, { type: d.type, compatibilityDate: d.compatibilityDate });
49
+ }
50
+ }
29
51
  return (draft) => {
52
+ const declared = migrationTargets.get(sourceKey(draft.type, draft.compatibilityDate));
53
+ if (declared) {
54
+ return { deprecated: true, supersededBy: declared };
55
+ }
30
56
  const latest = latestByType.get(draft.type);
31
- const deprecated = draft.compatibilityDate < latest;
32
- return deprecated ? { deprecated: true, supersededBy: latest } : { deprecated: false };
57
+ if (draft.compatibilityDate < latest) {
58
+ return {
59
+ deprecated: true,
60
+ supersededBy: { type: draft.type, compatibilityDate: latest }
61
+ };
62
+ }
63
+ return { deprecated: false };
33
64
  };
34
65
  }
35
66
  });
67
+ let _migrationTargets = null;
68
+ function getMigrationTargets() {
69
+ if (_migrationTargets) return _migrationTargets;
70
+ const map = /* @__PURE__ */ new Map();
71
+ for (const entry of registry.ENTRIES) {
72
+ const sources = entry.module.migrateFrom;
73
+ if (!sources || sources.length === 0) continue;
74
+ for (const src of sources) {
75
+ map.set(sourceKey(src.type, src.compatibilityDate), {
76
+ type: entry.type,
77
+ compatibilityDate: entry.compatibilityDate
78
+ });
79
+ }
80
+ }
81
+ _migrationTargets = map;
82
+ return map;
83
+ }
36
84
  let _fields = null;
37
85
  function getFields() {
38
86
  if (_fields) return _fields;
@@ -46,6 +94,7 @@ function getFields() {
46
94
  deprecated: e.deprecated,
47
95
  ...e.supersededBy !== void 0 ? { supersededBy: e.supersededBy } : {},
48
96
  migrate: e.module.migrate,
97
+ ...e.module.migrateFrom !== void 0 ? { migrateFrom: e.module.migrateFrom } : {},
49
98
  defaults: e.module.defaults
50
99
  }));
51
100
  return _fields;
@@ -72,3 +121,8 @@ export function allFieldSchemas(configure) {
72
121
  export function findField(type, compatibilityDate) {
73
122
  return getFields().find((f) => f.type === type && f.compatibilityDate === compatibilityDate);
74
123
  }
124
+ export function findMigrationTarget(type, compatibilityDate) {
125
+ const target = getMigrationTargets().get(sourceKey(type, compatibilityDate));
126
+ if (!target) return void 0;
127
+ return findField(target.type, target.compatibilityDate);
128
+ }
@@ -80,6 +80,7 @@ function renderRegisterables() {
80
80
  lines.push("");
81
81
  for (const entry of COLUMNS) {
82
82
  if (entry.type === PROHIBITED_TYPE) continue;
83
+ if (entry.deprecated) continue;
83
84
  const fields = describeColumnFields(entry.type);
84
85
  lines.push(`## \`${entry.type}\` (compatibilityDate \`${entry.compatibilityDate}\`)`);
85
86
  lines.push("");
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,305 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { Icon } from "@iconify/vue";
4
+ import { ExpressionEditor } from "../../../../ui/expression-editor";
5
+ import { Switch } from "../../../../ui/switch";
6
+ import { Separator } from "../../../../ui/separator";
7
+ import { Field, FieldLabel } from "../../../../ui/field";
8
+ import { Locale } from "../../../../ui/locale";
9
+ import {
10
+ InputGroup,
11
+ InputGroupAddon,
12
+ InputGroupButton,
13
+ InputGroupNumberField
14
+ } from "../../../../ui/input-group";
15
+ import {
16
+ DropdownMenu,
17
+ DropdownMenuContent,
18
+ DropdownMenuItem,
19
+ DropdownMenuTrigger
20
+ } from "../../../../ui/dropdown-menu";
21
+ import { getStructFieldDescription, getStructFieldTitle } from "../../../utils/schema-meta";
22
+ import { Markdown } from "../../../../ui/markdown";
23
+ import TriggersField from "../../../../actions/components/triggers-field.vue";
24
+ import { schema } from "./schema";
25
+ defineOptions({ name: "ShwfedTableSwitchRendererConfig" });
26
+ const value = defineModel({ type: Object, ...{ required: true } });
27
+ const fieldSchema = schema(() => {
28
+ });
29
+ const fieldTitle = (field) => getStructFieldTitle(fieldSchema, field) ?? field;
30
+ const fieldDescription = (field) => getStructFieldDescription(fieldSchema, field);
31
+ const ALIGN_OPTIONS = [
32
+ { value: "left", label: "\u5DE6\u5BF9\u9F50", icon: "fluent:text-align-left-20-regular" },
33
+ { value: "center", label: "\u5C45\u4E2D", icon: "fluent:text-align-center-20-regular" },
34
+ { value: "right", label: "\u53F3\u5BF9\u9F50", icon: "fluent:text-align-right-20-regular" }
35
+ ];
36
+ const currentAlignIcon = computed(
37
+ () => (ALIGN_OPTIONS.find((o) => o.value === (value.value.align ?? "center")) ?? ALIGN_OPTIONS[1]).icon
38
+ );
39
+ const ROW_VARS = {
40
+ row: { type: "dyn", label: "\u5F53\u524D\u884C\u6570\u636E" },
41
+ index: { type: "number", label: "\u884C\u7D22\u5F15" }
42
+ };
43
+ const ROW_VALUE_VARS = {
44
+ ...ROW_VARS,
45
+ value: { type: "bool", label: "\u5207\u6362\u540E\u7684\u65B0\u503C" }
46
+ };
47
+ const JSON_VARS = {
48
+ json: { type: "dyn", label: "HTTP \u54CD\u5E94\u4F53" }
49
+ };
50
+ const disabledModel = computed({
51
+ get: () => value.value.disabled ?? "",
52
+ set: (v) => {
53
+ if (v === "") delete value.value.disabled;
54
+ else value.value.disabled = v;
55
+ }
56
+ });
57
+ const onChangeModel = computed({
58
+ get: () => value.value.onChange ?? "",
59
+ set: (v) => {
60
+ if (v === "") delete value.value.onChange;
61
+ else value.value.onChange = v;
62
+ }
63
+ });
64
+ const successMessageModel = computed({
65
+ get: () => value.value.successMessage ?? "",
66
+ set: (v) => {
67
+ if (v === "") delete value.value.successMessage;
68
+ else value.value.successMessage = v;
69
+ }
70
+ });
71
+ const triggers = computed(() => value.value.triggers ?? []);
72
+ function updateTriggers(next) {
73
+ if (next.length === 0) delete value.value.triggers;
74
+ else value.value.triggers = next;
75
+ }
76
+ </script>
77
+
78
+ <template>
79
+ <div class="space-y-5">
80
+ <div class="grid grid-cols-2 gap-x-6 gap-y-4">
81
+ <Field orientation="vertical">
82
+ <FieldLabel class="text-xs text-zinc-500">
83
+ <template
84
+ v-if="fieldDescription('title')"
85
+ #tooltip
86
+ >
87
+ <Markdown
88
+ :source="fieldDescription('title')"
89
+ block
90
+ class="prose prose-sm prose-zinc"
91
+ />
92
+ </template>
93
+ {{ fieldTitle("title") }}
94
+ </FieldLabel>
95
+ <Locale v-model="value.title" />
96
+ </Field>
97
+ <Field orientation="vertical">
98
+ <FieldLabel class="text-xs text-zinc-500">
99
+ <template
100
+ v-if="fieldDescription('tooltip')"
101
+ #tooltip
102
+ >
103
+ <Markdown
104
+ :source="fieldDescription('tooltip')"
105
+ block
106
+ class="prose prose-sm prose-zinc"
107
+ />
108
+ </template>
109
+ {{ fieldTitle("tooltip") }}
110
+ </FieldLabel>
111
+ <Locale
112
+ v-model="value.tooltip"
113
+ markdown
114
+ />
115
+ </Field>
116
+ </div>
117
+ <div class="grid grid-cols-2 gap-x-6 gap-y-4">
118
+ <Field orientation="vertical">
119
+ <FieldLabel class="text-xs text-zinc-500">
120
+ <template
121
+ v-if="fieldDescription('accessor')"
122
+ #tooltip
123
+ >
124
+ <Markdown
125
+ :source="fieldDescription('accessor')"
126
+ block
127
+ class="prose prose-sm prose-zinc"
128
+ />
129
+ </template>
130
+ {{ fieldTitle("accessor") }}
131
+ </FieldLabel>
132
+ <ExpressionEditor
133
+ v-model="value.accessor"
134
+ placeholder="如 row.enabled"
135
+ result-type="bool"
136
+ :extra-vars="ROW_VARS"
137
+ />
138
+ </Field>
139
+ <Field orientation="vertical">
140
+ <FieldLabel class="text-xs text-zinc-500">
141
+ <template
142
+ v-if="fieldDescription('size')"
143
+ #tooltip
144
+ >
145
+ <Markdown
146
+ :source="fieldDescription('size')"
147
+ block
148
+ class="prose prose-sm prose-zinc"
149
+ />
150
+ </template>
151
+ {{ fieldTitle("size") }}
152
+ </FieldLabel>
153
+ <InputGroup>
154
+ <InputGroupAddon align="inline-start">
155
+ <DropdownMenu>
156
+ <DropdownMenuTrigger as-child>
157
+ <InputGroupButton
158
+ variant="ghost"
159
+ size="xs"
160
+ :title="fieldTitle('align')"
161
+ >
162
+ <Icon :icon="currentAlignIcon" />
163
+ </InputGroupButton>
164
+ </DropdownMenuTrigger>
165
+ <DropdownMenuContent align="start">
166
+ <DropdownMenuItem
167
+ v-for="opt in ALIGN_OPTIONS"
168
+ :key="opt.value"
169
+ @select="value.align = opt.value"
170
+ >
171
+ <Icon :icon="opt.icon" />
172
+ {{ opt.label }}
173
+ </DropdownMenuItem>
174
+ </DropdownMenuContent>
175
+ </DropdownMenu>
176
+ </InputGroupAddon>
177
+ <InputGroupNumberField
178
+ :model-value="value.size"
179
+ :disabled="value.grow"
180
+ :min="0"
181
+ @update:model-value="(v) => value.size = v"
182
+ />
183
+ <InputGroupAddon align="inline-end">
184
+ <InputGroupButton
185
+ :variant="value.grow ? 'primary' : 'ghost'"
186
+ size="xs"
187
+ @click="value.grow = !value.grow"
188
+ >
189
+ <Icon :icon="value.grow ? 'fluent:lock-closed-20-regular' : 'fluent:arrow-autofit-width-20-regular'" />
190
+ {{ fieldTitle("grow") }}
191
+ </InputGroupButton>
192
+ </InputGroupAddon>
193
+ </InputGroup>
194
+ </Field>
195
+ <Field orientation="vertical">
196
+ <FieldLabel class="text-xs text-zinc-500">
197
+ <template
198
+ v-if="fieldDescription('disabled')"
199
+ #tooltip
200
+ >
201
+ <Markdown
202
+ :source="fieldDescription('disabled')"
203
+ block
204
+ class="prose prose-sm prose-zinc"
205
+ />
206
+ </template>
207
+ {{ fieldTitle("disabled") }}
208
+ </FieldLabel>
209
+ <ExpressionEditor
210
+ v-model="disabledModel"
211
+ placeholder="如 row.locked"
212
+ result-type="bool"
213
+ :extra-vars="ROW_VARS"
214
+ />
215
+ </Field>
216
+ <Field orientation="vertical">
217
+ <FieldLabel class="text-xs text-zinc-500">
218
+ <template
219
+ v-if="fieldDescription('onChange')"
220
+ #tooltip
221
+ >
222
+ <Markdown
223
+ :source="fieldDescription('onChange')"
224
+ block
225
+ class="prose prose-sm prose-zinc"
226
+ />
227
+ </template>
228
+ {{ fieldTitle("onChange") }}
229
+ </FieldLabel>
230
+ <ExpressionEditor
231
+ v-model="onChangeModel"
232
+ placeholder="如 http.post('/api/items/' + row.id).body({ enabled: value })"
233
+ result-type="HttpRequest"
234
+ :extra-vars="ROW_VALUE_VARS"
235
+ multiline
236
+ />
237
+ </Field>
238
+ <Field orientation="vertical">
239
+ <FieldLabel class="text-xs text-zinc-500">
240
+ <template
241
+ v-if="fieldDescription('successMessage')"
242
+ #tooltip
243
+ >
244
+ <Markdown
245
+ :source="fieldDescription('successMessage')"
246
+ block
247
+ class="prose prose-sm prose-zinc"
248
+ />
249
+ </template>
250
+ {{ fieldTitle("successMessage") }}
251
+ </FieldLabel>
252
+ <ExpressionEditor
253
+ v-model="successMessageModel"
254
+ placeholder="如 '已更新'"
255
+ result-type="string"
256
+ :extra-vars="JSON_VARS"
257
+ />
258
+ </Field>
259
+ <Field orientation="vertical">
260
+ <FieldLabel class="text-xs text-zinc-500">
261
+ <template
262
+ v-if="fieldDescription('triggers')"
263
+ #tooltip
264
+ >
265
+ <Markdown
266
+ :source="fieldDescription('triggers')"
267
+ block
268
+ class="prose prose-sm prose-zinc"
269
+ />
270
+ </template>
271
+ {{ fieldTitle("triggers") }}
272
+ </FieldLabel>
273
+ <TriggersField
274
+ :triggers="triggers"
275
+ @update:triggers="updateTriggers"
276
+ />
277
+ </Field>
278
+ </div>
279
+ <Separator />
280
+ <div class="flex flex-wrap gap-x-8 gap-y-3">
281
+ <Field
282
+ orientation="horizontal"
283
+ class="w-auto gap-2"
284
+ >
285
+ <Switch
286
+ :model-value="value.enableSorting ?? false"
287
+ @update:model-value="(v) => value.enableSorting = v"
288
+ />
289
+ <FieldLabel class="text-sm text-zinc-600">
290
+ <template
291
+ v-if="fieldDescription('enableSorting')"
292
+ #tooltip
293
+ >
294
+ <Markdown
295
+ :source="fieldDescription('enableSorting')"
296
+ block
297
+ class="prose prose-sm prose-zinc"
298
+ />
299
+ </template>
300
+ {{ fieldTitle("enableSorting") }}
301
+ </FieldLabel>
302
+ </Field>
303
+ </div>
304
+ </div>
305
+ </template>
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;