weifuwu 0.76.0 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +38 -41
  2. package/dist/components/DatePicker/DatePicker.d.ts +1 -1
  3. package/dist/components/List/List.d.ts +3 -0
  4. package/dist/components/Tour/Tour.d.ts +1 -1
  5. package/dist/components/index.js +12 -12
  6. package/dist/components/style.css +17 -0
  7. package/dist/index.js +1272 -1228
  8. package/dist/scheduler/index.d.ts +7 -2
  9. package/dist/ui-dom/hooks/index.d.ts +0 -1
  10. package/dist/ui-dom/hooks/popup.d.ts +1 -10
  11. package/dist/ui-dom/hooks/stable.d.ts +1 -1
  12. package/dist/ui-dom/hooks/types.d.ts +0 -1
  13. package/dist/ui-dom/index.d.ts +1 -1
  14. package/dist/ui-dom/index.js +10 -10
  15. package/dist/ui-dom/jsx-runtime.js +1 -1
  16. package/dist/ui-dom/testing.js +1 -1
  17. package/dist/ui-dom/types.d.ts +31 -41
  18. package/dist/ui-dom/vdom/audit.d.ts +20 -0
  19. package/dist/ui-dom/vdom/build.d.ts +10 -3
  20. package/dist/ui-dom/vdom/diff.d.ts +7 -8
  21. package/dist/ui-dom/vdom/index.d.ts +1 -1
  22. package/dist/ui-dom/vdom/mount.d.ts +14 -5
  23. package/dist/ui-dom/vdom/render.d.ts +4 -0
  24. package/dist/ui-dom/vdom/serve.d.ts +1 -1
  25. package/dist/ui-dom/vdom/transform.d.ts +32 -0
  26. package/dist/ui-dom/vnode.d.ts +18 -10
  27. package/docs/components.md +5 -5
  28. package/docs/custom-components.md +86 -54
  29. package/docs/examples.md +35 -41
  30. package/docs/frontend-middleware.md +3 -4
  31. package/docs/frontend-ui-dom.md +22 -18
  32. package/docs/frontend.md +243 -168
  33. package/docs/mobile.md +2 -2
  34. package/docs/realtime.md +8 -3
  35. package/package.json +1 -1
  36. package/dist/ui-dom/focus-trap.d.ts +0 -4
  37. package/dist/ui-dom/scroll-lock.d.ts +0 -5
  38. package/dist/ui-dom/vdom/scheduler.d.ts +0 -13
@@ -1 +1 @@
1
- var A=Symbol("Fragment"),S=Symbol("Portal");function W(e,n){if(e===n)return!0;let t=Object.keys(e),r=Object.keys(n);if(t.length!==r.length)return!1;for(let o of t)if(e[o]!==n[o])return!1;return!0}async function q(e,n,t,r){e._id||(r?.reuse?._id?e._id=r.reuse._id:e._id=t.nextId(),t.idRegistry.set(e._id,e)),r?.reuse&&(r.reuse._parentNode&&(e._parentNode=r.reuse._parentNode),r.reuse._refNode&&(e._refNode=r.reuse._refNode));let o=Object.create(n);o.ui=Object.create(n.ui);let i=o.ui;if(i._selfId=e._id,i._selfVNode=e,i.render=function(u){return u==null&&e._id?n.ui.render([e._id]):n.ui.render(u)},typeof e._render!="function"&&typeof r?.reuse?._render=="function"&&(e._render=r.reuse._render),typeof e._render!="function"){n.ui?.setMounting?.(!0);let u;try{u=await e.type(e.props??{},o)}finally{n.ui?.endMounting?.()}if(typeof u!="function")throw new Error(`Component ${e.type.name||"anonymous"} must return a render function. Use (init_props, ctx) => (props) => VNode pattern.`);e._render=u}return{renderFn:e._render,childCtx:o}}async function C(e,n,t,r,o){if(e==null||typeof e=="boolean"||typeof e=="string"||typeof e=="number")return e;if(Array.isArray(e)){let a=Array.isArray(t)?t:[];return await Promise.all(e.map((l,s)=>C(l,n,a[s],r,o))),e}let i=e,u=r??n.__registry,m=t!=null&&typeof t=="object"&&!Array.isArray(t)&&t.type===i.type?t:null;if(typeof i.type=="function"){let{childCtx:a}=await q(i,n,u,{reuse:m??void 0}),l=W(m?.props??{},i.props??{});if(o?.force||!l||m?._child==null){let s=await C(i._render(i.props),a,m?._child,u);i._child=s??null}else i._child=m._child;return i}if(i.type===A){let a=await C(i.props?.children??null,n,m?._child??m?.props?.children,u);return i._child=a??null,i}if(typeof i.type=="string"||typeof i.type=="symbol"){let a=await C(i.props?.children??null,n,m?.props?.children,u);return i._child=a??null,i}return i}var U=new Set(["svg","path","circle","rect","line","polyline","polygon","g","text","defs","use","clipPath"]),$=/^on[A-Z]/,K=new Set(["zIndex","opacity","lineHeight","fontWeight","fontSizeAdjust","flex","flexGrow","flexShrink","order","zoom","aspectRatio","gridRow","gridColumn","scale","rotate","animationIterationCount","columnCount","fillOpacity","strokeOpacity","stopOpacity","floodOpacity"]);function D(e,n,t){if(t==null||t===!1)return;let r=e.ownerDocument?.defaultView;if(n==="class"||n==="className"){if(typeof t=="object")for(let[o,i]of Object.entries(t))i&&e.classList.add(o);else e.setAttribute("class",String(t));return}if(n==="style"){if(typeof t=="string")e.setAttribute("style",t);else{let o=e.style;for(let[i,u]of Object.entries(t))u==null?o[i]="":i.startsWith("--")?o.setProperty(i,String(u)):typeof u=="number"&&!K.has(i)?o[i]=`${u}px`:o[i]=String(u)}return}if(n==="ref"){if(typeof t=="function")try{t(e)}catch(o){console.error("[weifuwu] ref error",o)}return}if($.test(n)){let o=n.slice(2).toLowerCase();e.addEventListener(o,t);return}if(n==="value"){e.value=t;return}if(n==="indeterminate"){e.indeterminate=!!t;return}if(n==="innerHTML"){e.innerHTML=String(t??"");return}if(n==="draggable"||n==="contenteditable"||n==="spellcheck"){e.setAttribute(n,t?"true":"false");return}if(n.startsWith("aria-")&&typeof t=="boolean"){e.setAttribute(n,t?"true":"false");return}if(t===!0){e.setAttribute(n,"");return}try{e[n]=t,e.getAttribute(n)!==String(t)&&e.setAttribute(n,String(t))}catch{e.setAttribute(n,String(t))}}function V(e,n,t){let r=n?.browser??t;if(!r)throw new Error("[vdom] renderValue requires browser env (ctx.browser)");if(e==null||typeof e=="boolean")return null;if(typeof e=="string"||typeof e=="number")return r.createTextNode(String(e));if(Array.isArray(e)){let a=r.createDocumentFragment();if(!a)return null;for(let l of e){let s=V(l,n,r);s!=null&&a.appendChild(s)}return a}let o=e;if(o.type===S){let a=r.bodyElement();if(!a)return null;let l=a.querySelector("#__wf_portal");l||(l=r.createElement("div"),l&&(l.id="__wf_portal",a.appendChild(l)));let s=r.createElement("div");if(s){s.setAttribute("data-portal",String(o.props?.portalKey??"wf"));let c=V(o.props?.children??null,n,r);c!=null&&s.appendChild(c),l&&l.appendChild(s),o._remoteEl=s}return null}if(o.type===A){let a=r.createDocumentFragment();if(!a)return null;let l=(Array.isArray(o.props?.children)?o.props.children:[o.props?.children]).flat(1/0);for(let s of l){let c=V(s,n,r);c!=null&&a.appendChild(c)}return o._childNodes=Array.from(a.childNodes),a}if(typeof o.type=="function"){if(typeof o._render!="function")throw new Error(`[vdom] component ${o.type.name||"anonymous"} not built (missing _render) \u2014 buildVNode must run before renderValue`);let a=o._child!==void 0?o._child:o._render(o.props);if(a==null)return o._child=null,null;let l=(Array.isArray(a)||typeof a=="object"&&typeof a.type=="function",a);o._child=l,typeof l=="object"&&!Array.isArray(l)&&(l._parentVNode=o);let s=V(l,n,r);return s&&(o._refNode=s),s}let i=o.type,u=U.has(i)?r.createElementNS("http://www.w3.org/2000/svg",i):r.createElement(i);if(!u)return null;o.el=u;let m;for(let[a,l]of Object.entries(o.props??{}))if(!(a==="children"||a==="key")){if(a==="value"&&u instanceof HTMLSelectElement){m=l;continue}D(u,a,l)}if(!("innerHTML"in(o.props??{}))){let a=(Array.isArray(o.props?.children)?o.props.children:[o.props?.children]).flat(1/0);for(let l of a){let s=V(l,n,r);if(s!=null&&(u.appendChild(s),l&&typeof l=="object"&&!Array.isArray(l)&&typeof l.type=="function")){let c=l;c._parentNode||(c._parentNode=u,c._refNode=s)}}}return m!==void 0&&(u.value=String(m)),u}function x(){return{activeElement:()=>typeof document<"u"?document.activeElement:null,byId:e=>typeof document<"u"?document.getElementById(e):null,query:e=>typeof document<"u"?document.querySelector(e):null,queryAll:e=>typeof document<"u"?document.querySelectorAll(e):null,createElement:e=>typeof document<"u"?document.createElement(e):null,createElementNS:(e,n)=>typeof document<"u"?document.createElementNS(e,n):null,createDocumentFragment:()=>typeof document<"u"?document.createDocumentFragment():null,createComment:e=>typeof document<"u"?document.createComment(e):null,createTextNode:e=>typeof document<"u"?document.createTextNode(e):null,addEventListener:(e,n,t)=>{typeof window<"u"&&window.addEventListener(e,n,t)},removeEventListener:(e,n,t)=>{typeof window<"u"&&window.removeEventListener(e,n,t)},scrollTo:e=>{typeof window<"u"&&window.scrollTo(0,e)},matchMedia:e=>typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia(e):null,visualViewport:()=>(typeof window<"u"?window:null)?.visualViewport??null,scrollingElement:()=>typeof document<"u"?document.scrollingElement:null,bodyElement:()=>typeof document<"u"?document.body:null,bodyAppend:e=>{typeof document<"u"&&document.body.appendChild(e)},bodyRemove:e=>{typeof document<"u"&&e.parentNode&&document.body.removeChild(e)},clearBody:()=>{typeof document<"u"&&(document.body.innerHTML="")},event:(e,n)=>{let t=typeof window<"u"?window:null,r=n&&(n.key||n.code)?"KeyboardEvent":n&&(n.clientX!==void 0||n.clientY!==void 0||n.pointerId!==void 0)?"PointerEvent":"Event";try{return new t[r](e,n)}catch{return new t.Event(e,n)}},dispatchEvent:(e,n)=>typeof e<"u"?e.dispatchEvent(n):!1,navigate:e=>{typeof window>"u"||(window.history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate",{state:null})))},copyText:async e=>{let n=typeof window<"u"?window:null;if(n?.navigator?.clipboard?.writeText)try{return await n.navigator.clipboard.writeText(e),!0}catch{}try{if(typeof document>"u")return!1;let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.select();let r=document.execCommand("copy");return document.body.removeChild(t),r}catch{return!1}},execCommand:(e,n)=>typeof document<"u"?document.execCommand(e,!1,n):!1,queryCommandState:e=>typeof document<"u"?document.queryCommandState(e):!1,queryCommandValue:e=>typeof document<"u"?document.queryCommandValue(e):"",selectionText:()=>typeof window<"u"?window.getSelection?.()?.toString()??null:null,getSelection:()=>typeof window<"u"?window.getSelection():null,viewportHeight:()=>typeof window<"u"?window.innerHeight:0,viewportWidth:()=>typeof window<"u"?window.innerWidth:0,pathname:()=>typeof window<"u"?window.location.pathname:"",createTreeWalker:(e,n)=>typeof document<"u"?document.createTreeWalker(e,n??NodeFilter.SHOW_ALL):null,scrollTop:()=>{let e=typeof document<"u"?document:null,n=typeof window<"u"?window:null;return e?.scrollingElement?.scrollTop??n?.scrollY??0},hash:()=>typeof window<"u"?window.location?.hash??"":"",setHash:e=>{typeof window<"u"&&(window.location.hash=e)},timeout:(e,n)=>typeof window<"u"?window.setTimeout(e,n):0,rootElement:()=>typeof document<"u"?document.documentElement:null,storageGet:e=>{try{return typeof window<"u"?window.localStorage?.getItem(e)??null:null}catch{return null}},storageSet:(e,n)=>{try{typeof window<"u"&&window.localStorage?.setItem(e,n)}catch{}}}}var G=0;function T(){return{idRegistry:new Map,unmountHooks:[],nextId:()=>`_wf_${G++}`}}function F(e,n){for(let t of[...e.unmountHooks])t(n);e.idRegistry.delete(n),e.idRegistry.delete(`custom:${n}`)}function z(e,n,t,r){try{e(n)}catch(o){console.error(`[weifuwu] ref ${t} error in <${r??"anonymous"}>`,o)}}function N(e,n){if(e==null||typeof e!="object")return;let t=e;if(t._id){if(t._customId&&n.idRegistry.delete(t._customId),n.idRegistry.delete(t._id),n.unmountHooks.length>0){let r=[...n.unmountHooks];for(let o of r)o(t._id);if(t._customId)for(let o of r)o(t._customId)}t._id=void 0,t._customId=void 0,t._render=void 0,t._parentNode=void 0,t._refNode=void 0}if(t._child!=null){if(Array.isArray(t._child))for(let r of t._child)r&&typeof r=="object"&&N(r,n);else N(t._child,n);t._child=void 0}if(t.props?.children&&typeof t.type=="string"){let r=Array.isArray(t.props.children)?t.props.children:[t.props.children];for(let o of r)o&&typeof o=="object"&&N(o,n)}if(typeof t.props?.ref=="function"&&z(t.props.ref,null,"cleanup"),t._remoteEl){let r=t._child;if(r!=null)if(Array.isArray(r))for(let o of r)o&&typeof o=="object"&&N(o,n);else typeof r=="object"&&N(r,n);t._remoteEl.remove(),t._remoteEl=void 0}}function M(e,n){if(n&&typeof e.type=="function"&&e._id){try{N(e,n)}catch(t){console.error("[weifuwu] ref cleanup error",t)}F(n,e._id)}}function B(e){if(e==null||typeof e=="boolean")return[];let n=[],t=Array.isArray(e)?[...e]:[e];for(;t.length>0;){let r=t.shift();Array.isArray(r)?t.unshift(...r):n.push(r)}return n}function v(e){if(e==null||typeof e!="object"||Array.isArray(e))return;let n=e;return n._placement==="remote"?n.props?.portalKey:n.key}function P(e,n,t,r,o){if(typeof r=="string"||typeof r=="number"){if(typeof t=="string"||typeof t=="number"){if(String(t)!==String(r)){let c=e.ownerDocument.createTextNode(String(r));return n?.parentNode?n.parentNode.replaceChild(c,n):e.appendChild(c),c}return n}let s=e.ownerDocument.createTextNode(String(r));return n?.parentNode?n.parentNode.replaceChild(s,n):e.appendChild(s),s}if(r==null||typeof r=="boolean"){if(t&&typeof t=="object"&&!Array.isArray(t)&&t.type===S){let s=t._remoteEl;try{N(t.props?.children,o.registry)}catch(c){console.error("[weifuwu] portal ref cleanup error",c)}return s?.parentNode?.removeChild(s),null}if(t&&typeof t=="object"&&!Array.isArray(t))if(typeof t.type=="function")M(t,o.registry);else try{N(t,o.registry)}catch(s){console.error("[weifuwu] ref cleanup error",s)}return n?.parentNode&&n.parentNode.removeChild(n),null}if(Array.isArray(r)){let s=e.ownerDocument.createDocumentFragment(),c=k(e,t,r,o,n?[n]:void 0);for(let w of c)w&&s.appendChild(w);return n?.parentNode?s.contains(n)?e.appendChild(s):n.parentNode.replaceChild(s,n):e.appendChild(s),s}let i=r;if(i.type===S){let s=t&&typeof t=="object"&&!Array.isArray(t)?t:null;if(s?.type===S){let c=s._remoteEl;if(c){let w=s.props?.children??null,E=i.props?.children??null;k(c,w,E,o)}return i._remoteEl=s._remoteEl,null}return V(i,o,o.browser??x()),null}if(i.type===A){let c=(t&&typeof t=="object"&&!Array.isArray(t)?t:null)?._childNodes,w=k(e,t,i.props?.children??null,o,c);return i._childNodes=w.filter(Boolean),n?.parentNode&&n.nodeType===8&&n.parentNode.removeChild(n),n&&n.nodeType===1?n:w[0]??null}if(typeof i.type=="function"){if(typeof i._render!="function")throw new Error(`[vdom] component ${i.type.name||"anonymous"} not built in diff \u2014 buildVNode must run before patchValue`);let s=t&&typeof t=="object"&&!Array.isArray(t)?t:null;s?.type===i.type&&s._id&&(i._id=s._id,o.registry.idRegistry.set(i._id,i)),i._parentNode=e,n&&(i._refNode=n);let c=s?.type===i.type,w=W(s?.props??{},i.props??{}),E=o.getCtxVersion?o.getCtxVersion(i._id??""):void 0,_=E===void 0||(o.ctxVersion??0)===E;if(s&&c&&w&&_&&s._child!==void 0)return i._child=s._child,n;let d;i._child!==void 0?d=i._child:(d=i._render(i.props),i._child=d);let f=P(e,n,s?._child,d,o);return f&&(i._refNode=f),f}let u=t&&typeof t=="object"&&!Array.isArray(t)?t:null,m=u?.type??null,a=i.type;if(n&&n.nodeType===1&&m===a){let s=n;return i.el=s,X(s,u?.props??{},i.props??{}),k(s,u?.props?.children??null,i.props?.children??null,o),s}let l=V(i,o,o.browser??x());if(l==null)return null;if(n?.parentNode)if(t!=null&&typeof t!="boolean"){if(typeof t=="object"&&!Array.isArray(t))try{N(t,o.registry)}catch(s){console.error("[weifuwu] ref cleanup error",s)}n.parentNode.replaceChild(l,n)}else n.parentNode.insertBefore(l,n);else e.appendChild(l);return l}function X(e,n,t){let r=new Set([...Object.keys(n),...Object.keys(t)]);for(let o of r){if(o==="children"||o==="key")continue;let i=n[o],u=t[o];if(i!==u){if(o.startsWith("on")){typeof i=="function"&&e.removeEventListener(o.slice(2).toLowerCase(),i),u!=null&&u!==!1&&e.addEventListener(o.slice(2).toLowerCase(),u);continue}if(u==null||u===!1){if(o==="class"||o==="className")e.removeAttribute("class");else if(o.startsWith("on"))e.removeEventListener(o.slice(2).toLowerCase(),i);else if(o==="ref"){if(typeof i=="function")try{i(null)}catch(m){console.error("[weifuwu] ref cleanup error",m)}}else if(o==="value")e.value="";else if(o==="indeterminate")e.indeterminate=!1;else{e.removeAttribute(o);try{delete e[o]}catch{}}continue}D(e,o,u)}}}function k(e,n,t,r,o){let i=B(n),u=B(t),m=o??Array.from(e.childNodes);if(u.some(d=>v(d)!==void 0)){for(let d=0;d<u.length;d++){let f=u[d];f&&typeof f=="object"&&!Array.isArray(f)&&v(f)===void 0&&(f.key=`pos:${d}`)}for(let d=0;d<i.length;d++){let f=i[d];f&&typeof f=="object"&&!Array.isArray(f)&&v(f)===void 0&&(f.key=`pos:${d}`)}}let l=i.map((d,f)=>{if(d==null||typeof d!="object"||Array.isArray(d))return m[f]??null;let y=d;return y._placement==="remote"?y._remoteEl??null:y.type===A?y._childNodes?.[0]??null:y._refNode??y.el??null});if(!u.some(d=>{if(d==null||typeof d!="object"||Array.isArray(d))return!1;let f=d;return f._placement!=="remote"&&f.key!==void 0})){let d=Math.max(i.length,u.length),f=[];for(let y=0;y<d;y++){let h=y<i.length?i[y]:null,p=y<u.length?u[y]:null;if(p==null||typeof p=="boolean"){if(h&&typeof h=="object"&&!Array.isArray(h))if(typeof h.type=="function")M(h,r.registry);else try{N(h,r.registry)}catch(R){console.error("[weifuwu] ref cleanup error",R)}let g=l[y];g?.parentNode&&g.parentNode.removeChild(g),f.push(null);continue}if(h==null||typeof h=="boolean"){let g=V(p,r,r.browser??x());if(g==null){f.push(null);continue}let R=null;for(let j=y+1;j<l.length;j++){let H=l[j];if(H&&H.parentNode===e){R=H;break}}R&&R.parentNode===e?e.insertBefore(g,R):e.appendChild(g),f.push(g);continue}let b=P(e,l[y],h,p,r);f.push(b)}return f}let c=new Map;i.forEach((d,f)=>{let y=v(d);y!==void 0&&d&&typeof d=="object"&&!Array.isArray(d)&&c.set(y,{vnode:d,nodes:[l[f]??null].filter(Boolean),index:f})});let w=[],E=new Set,_=null;return u.forEach((d,f)=>{let y=v(d),h=d;if(y!==void 0&&c.has(y)){let p=c.get(y),b=p.nodes[0]??null;E.add(y);let g=P(e,b,p.vnode,h,r);g&&g.parentNode===e&&_&&g.previousSibling!==_&&e.insertBefore(g,_.nextSibling),w.push(g),g&&(_=g)}else if(h?._placement==="remote"){let p=i[f]??null,b=P(e,l[f]??null,p,h,r);w.push(b),b&&(_=b)}else{let p=V(h,r,r.browser??x());w.push(p),p!=null&&(e.appendChild(p),_&&p.previousSibling!==_&&e.insertBefore(p,_.nextSibling),_=p)}}),i.forEach((d,f)=>{let y=v(d),h=d&&typeof d=="object"&&!Array.isArray(d)&&typeof d.type=="function";if(y!==void 0&&!E.has(y)){if(d&&typeof d=="object"&&!Array.isArray(d))if(h)M(d,r.registry);else try{N(d,r.registry)}catch(b){console.error("[weifuwu] ref cleanup error",b)}let p=l[f];p?.parentNode&&p.parentNode.removeChild(p)}else if(y===void 0){if(d&&typeof d=="object"&&!Array.isArray(d))if(h)M(d,r.registry);else try{N(d,r.registry)}catch(b){console.error("[weifuwu] ref cleanup error",b)}let p=l[f];p?.parentNode&&p.parentNode.removeChild(p)}}),w}function Y(e){return e.__registry??(e.__registry=T())}function O(e,n,t,r){let o=Y(t),i=t.browser??x();return C(n,t,void 0,o).then(()=>{let u=V(n,t,i);if(u!=null&&e.appendChild(u),n._id&&o){let m=o.idRegistry.get(n._id);m&&(m._parentNode=e)}r?.onMounted?.()}).catch(u=>console.error("[weifuwu] command mount error",u)),{id:n._id??""}}function Ve(e,n,t){return new Promise(r=>{O(e,n,t,{onMounted:r})})}async function _e(e,n,t,r,o){return await C(r,o,t,o.__registry),P(e,n,t,r,{browser:o.browser??o.__browser,registry:o.__registry})}function be(e,n){let t=n.__registry??(n.__registry=T());return C(e,n,void 0,t)}async function Ce(e,n,t){let r=await e(n,t);return typeof r=="function"?r(n):r}async function Ee(e,n,t){let r=await e(n,t);return()=>typeof r=="function"?r(n):r}function L(e,n){if(e==null||typeof e!="object")return;if(Array.isArray(e)){for(let r of e)L(r,n);return}n(e);let t=e?.props?.children;t!=null&&L(t,n)}function Ae(e,n){let t;return L(e,r=>{!t&&n(r)&&(t=r)}),t}function xe(e,n){let t=[];return L(e,r=>{typeof r?.props?.class=="string"&&r.props.class.split(" ").includes(n)&&t.push(r)}),t}function Re(e){let n={ui:{render:()=>{},ready:!0,useExternal:()=>{}}};return e?{ui:{...n.ui,...e.ui??{}},...e.browser?{browser:e.browser}:{}}:n}function Se(e=()=>!1,n){return{get open(){return e()},setOpen:n??(t=>{}),refresh:()=>{},portal:(t,r)=>e()?t:null,wrapProps:{}}}export{be as buildToDom,Se as createPopupMock,Re as createTestCtx,xe as findByClass,Ae as findVNode,Ee as mountComponent,Ve as mountToDom,_e as patchToDom,Ce as renderVNode,L as walkVNode};
1
+ var R=Symbol("Fragment"),j=Symbol("Portal");function W(e){if(e==null||typeof e=="boolean")return[];if(Array.isArray(e)){let n=!1;for(let o=0;o<e.length;o++)if(Array.isArray(e[o])){n=!0;break}if(!n)return e;let t=[],r=[...e].reverse();for(;r.length>0;){let o=r.pop();if(Array.isArray(o))for(let i=o.length-1;i>=0;i--)r.push(o[i]);else t.push(o)}return t}return[e]}function ne(e,n){if(e===n)return!0;let t=Object.keys(e),r=Object.keys(n);if(t.length!==r.length)return!1;for(let o of t)if(e[o]!==n[o])return!1;return!0}async function te(e,n,t,r){e._id||(r?.reuse?._id?e._id=r.reuse._id:e._id=t.nextId(),t.idRegistry.set(e._id,e)),r?.reuse&&(r.reuse._parentNode&&(e._parentNode=r.reuse._parentNode),r.reuse._refNode&&(e._refNode=r.reuse._refNode),r.reuse._ctxVersion!=null&&(e._ctxVersion=r.reuse._ctxVersion));let o=Object.create(n);o.ui=Object.create(n.ui);let i=o.ui;if(i._selfId=e._id,i._selfVNode=e,i.render=function(d){return d==null&&e._id?n.ui.render([e._id]):n.ui.render(d)},typeof e._render!="function"&&typeof r?.reuse?._render=="function"&&(e._render=r.reuse._render),typeof e._render!="function"){n.ui?.setMounting?.(!0);let d;try{d=await e.type(e.props??{},o)}finally{n.ui?.endMounting?.()}if(typeof d!="function")throw new Error(`Component ${e.type.name||"anonymous"} must return a render function. Use (init_props, ctx) => (props) => VNode pattern.`);e._render=d}return{renderFn:e._render,childCtx:o}}function J(e){return!!e&&typeof e.then=="function"}function P(e,n,t,r,o){if(e==null||typeof e=="boolean"||typeof e=="string"||typeof e=="number")return e;if(Array.isArray(e)){let m=Array.isArray(t)?t:[];for(let l=0;l<e.length;l++){let h=e[l];if(h!=null&&typeof h=="object"&&!Array.isArray(h)){let b=h;b.key===void 0?b.key=String(l):b.key=String(b.key)}}let f=e.map((l,h)=>P(l,n,m[h],r,o)),s=!1;for(let l=0;l<f.length;l++)if(J(f[l])){s=!0;break}return s?Promise.all(f).then(()=>e):e}let i=e,d=r??n.__registry,u=t!=null&&typeof t=="object"&&!Array.isArray(t)&&t.type===i.type?t:null;if(typeof i.type=="function"){i._id||(i._id=u?._id??d.nextId(),d.idRegistry.set(i._id,i)),u&&(u._parentNode&&(i._parentNode=u._parentNode),u._refNode&&(i._refNode=u._refNode),u._ctxVersion!=null&&(i._ctxVersion=u._ctxVersion),typeof u._render=="function"&&(i._render=u._render));let m=ne(u?.props??{},i.props??{}),f=n?.ui?._ctxVersion??0,s=(u?._ctxVersion??-1)===f;return!o?.force&&m&&s&&u?._child!=null?(i._child=u._child,i._ctxVersion=u._ctxVersion,i):(async()=>{let{childCtx:l}=await te(i,n,d,{reuse:u??void 0}),h=await P(await i._render(i.props),l,u?._child,d);return i._child=h??null,i._ctxVersion=f,i})()}if(i.type===R){let m=P(i.props?.children??null,n,u?._child??u?.props?.children,d);return J(m)?m.then(f=>(i._child=f??null,i)):(i._child=m??null,i)}if(typeof i.type=="string"||typeof i.type=="symbol"){let m=P(i.props?.children??null,n,u?.props?.children,d);return J(m)?m.then(f=>(i._child=f??null,i)):(i._child=m??null,i)}return i}function U(e){if(e===!1)return"false";if(e===null)return"null";if(e===void 0)return"undefined";if(e===!0)return"true";if(typeof e=="object")try{let n=JSON.stringify(e);return`object ${n!=null&&n.length>80?n.slice(0,80)+"\u2026":n??""}`}catch{return`object ${Object.prototype.toString.call(e)}`}return`bad-vnode type=${typeof e}`}var D=/^on[A-Z]/,re=/^on[A-Z].*Capture$/;function B(e){let n=re.test(e);return{type:(n?e.slice(2,-7):e.slice(2)).toLowerCase(),capture:n}}var X=new Set(["draggable","contenteditable","spellcheck","translate"]),I=new Set(["zIndex","opacity","lineHeight","fontWeight","fontSizeAdjust","flex","flexGrow","flexShrink","order","zoom","aspectRatio","gridRow","gridColumn","scale","rotate","animationIterationCount","columnCount","fillOpacity","strokeOpacity","stopOpacity","floodOpacity"]);var oe=new Set(["svg","path","circle","rect","line","polyline","polygon","g","text","defs","use","clipPath"]);function q(e,n,t){if(t==null)return;let r=e.ownerDocument?.defaultView;if(X.has(n)){e.setAttribute(n,t?"true":"false");return}if(t!==!1){if(n==="class"||n==="className"){if(typeof t=="object")for(let[o,i]of Object.entries(t))i&&e.classList.add(o);else e.setAttribute("class",String(t));return}if(n==="style"){if(typeof t=="string")e.setAttribute("style",t);else{let o=e.style;for(let[i,d]of Object.entries(t))d==null?o[i]="":i.startsWith("--")?o.setProperty(i,String(d)):typeof d=="number"&&!I.has(i)?o[i]=`${d}px`:o[i]=String(d)}return}if(n==="ref"){if(typeof t=="function")try{t(e)}catch(o){console.error("[weifuwu] ref error",o)}return}if(D.test(n)){let{type:o,capture:i}=B(n);if(typeof t!="function"){console.warn(`[weifuwu] event prop ${n} expects a function, got ${typeof t} \u2014 ignored`);return}e.addEventListener(o,t,i?{capture:!0}:void 0);return}if(n==="value"){e.value=t;return}if(n==="indeterminate"){e.indeterminate=!!t;return}if(n==="innerHTML"){e.innerHTML=String(t??"");return}if(n.startsWith("aria-")&&typeof t=="boolean"){e.setAttribute(n,t?"true":"false");return}if(t===!0){e.setAttribute(n,"");return}try{e[n]=t,e.getAttribute(n)!==String(t)&&e.setAttribute(n,String(t))}catch{e.setAttribute(n,String(t))}}}function H(e,n){return e.createComment(`wf-hole: ${U(n)}`)}function T(e,n,t){let r=n?.browser??t;if(!r)throw new Error("[vdom] renderValue requires browser env (ctx.browser)");if(e==null||typeof e=="boolean")return null;if(typeof e=="string"||typeof e=="number")return r.createTextNode(String(e));if(Array.isArray(e)){let f=r.createDocumentFragment();if(!f)return null;for(let s of e){let l=s==null||typeof s=="boolean"?H(r,s):T(s,n,r);l!=null&&f.appendChild(l)}return f}let o=e,i=o.type;if(typeof i!="string"&&typeof i!="function"&&i!==R&&i!==j)return console.warn(`[weifuwu] children \u9879\u975E\u6CD5\uFF1Atype=${String(i)}\uFF08${typeof i}\uFF09\uFF0C\u503C=${U(e)}\u2014\u2014\u5DF2\u5360\u4F4D\uFF08wf-hole\uFF09\uFF0C\u68C0\u67E5\u4F20\u5165\u7684 children`),H(r,e);if(o.type===j){let f=r.bodyElement();if(!f)return null;let s=f.querySelector("#__wf_portal");s||(s=r.createElement("div"),s&&(s.id="__wf_portal",f.appendChild(s)));let l=r.createElement("div");if(l){l.setAttribute("data-portal",String(o.props?.portalKey??"wf"));let h=T(o.props?.children??null,n,r);h!=null&&l.appendChild(h),s&&s.appendChild(l),o._remoteEl=l}return null}if(o.type===R){let f=r.createDocumentFragment();if(!f)return null;for(let s of W(o.props?.children)){let l=s==null||typeof s=="boolean"?H(r,s):T(s,n,r);l!=null&&f.appendChild(l)}return o._childNodes=Array.from(f.childNodes),f}if(typeof o.type=="function"){if(typeof o._render!="function")throw new Error(`[vdom] component ${o.type.name||"anonymous"} not built (missing _render) \u2014 buildVNode must run before renderValue`);if(o._child===void 0)throw new Error(`[vdom] component ${o.type.name||"anonymous"} not built (missing _child) \u2014 buildVNode must run before renderValue`);let f=o._child;if(f==null)return o._child=null,null;let s=(Array.isArray(f)||typeof f=="object"&&typeof f.type=="function",f);o._child=s,typeof s=="object"&&!Array.isArray(s)&&(s._parentVNode=o);let l=T(s,n,r);if(l){if(o._refNode=l,o._id)if(l.nodeType===11)for(let h of Array.from(l.childNodes))h.nodeType===1&&h.setAttribute("data-wf-id",o._id);else l.nodeType===1&&l.setAttribute("data-wf-id",o._id);if(o.key!=null)if(l.nodeType===11)for(let h of Array.from(l.childNodes))h.nodeType===1&&h.setAttribute("data-wf-key",o.key);else l.nodeType===1&&l.setAttribute("data-wf-key",o.key)}return l}let d=o.type,u=oe.has(d)?r.createElementNS("http://www.w3.org/2000/svg",d):r.createElement(d);if(!u)return null;o.el=u,o.key!=null&&u.setAttribute("data-wf-key",o.key);let m;for(let[f,s]of Object.entries(o.props??{}))if(!(f==="children"||f==="key")){if(f==="value"&&u instanceof HTMLSelectElement){m=s;continue}q(u,f,s)}if(!("innerHTML"in(o.props??{}))){let f=[];for(let s of W(o.props?.children)){let l=s==null||typeof s=="boolean"?H(r,s):T(s,n,r);if(l==null){f.push(null);continue}if(u.appendChild(l),f.push(l.nodeType===11?l.firstChild:l),s&&typeof s=="object"&&!Array.isArray(s)&&typeof s.type=="function"){let h=s;h._parentNode||(h._parentNode=u,h._refNode=l)}}o._childAnchors=f}return m!==void 0&&(u.value=String(m)),u}function k(){return{activeElement:()=>typeof document<"u"?document.activeElement:null,byId:e=>typeof document<"u"?document.getElementById(e):null,query:e=>typeof document<"u"?document.querySelector(e):null,queryAll:e=>typeof document<"u"?document.querySelectorAll(e):null,createElement:e=>typeof document<"u"?document.createElement(e):null,createElementNS:(e,n)=>typeof document<"u"?document.createElementNS(e,n):null,createDocumentFragment:()=>typeof document<"u"?document.createDocumentFragment():null,createComment:e=>typeof document<"u"?document.createComment(e):null,createTextNode:e=>typeof document<"u"?document.createTextNode(e):null,addEventListener:(e,n,t)=>{typeof window<"u"&&window.addEventListener(e,n,t)},removeEventListener:(e,n,t)=>{typeof window<"u"&&window.removeEventListener(e,n,t)},scrollTo:e=>{typeof window<"u"&&window.scrollTo(0,e)},matchMedia:e=>typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia(e):null,visualViewport:()=>(typeof window<"u"?window:null)?.visualViewport??null,scrollingElement:()=>typeof document<"u"?document.scrollingElement:null,bodyElement:()=>typeof document<"u"?document.body:null,bodyAppend:e=>{typeof document<"u"&&document.body.appendChild(e)},bodyRemove:e=>{typeof document<"u"&&e.parentNode&&document.body.removeChild(e)},clearBody:()=>{typeof document<"u"&&(document.body.innerHTML="")},event:(e,n)=>{let t=typeof window<"u"?window:null,r=n&&(n.key||n.code)?"KeyboardEvent":n&&(n.clientX!==void 0||n.clientY!==void 0||n.pointerId!==void 0)?"PointerEvent":"Event";try{return new t[r](e,n)}catch{return new t.Event(e,n)}},dispatchEvent:(e,n)=>typeof e<"u"?e.dispatchEvent(n):!1,navigate:e=>{typeof window>"u"||(window.history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate",{state:null})))},copyText:async e=>{let n=typeof window<"u"?window:null;if(n?.navigator?.clipboard?.writeText)try{return await n.navigator.clipboard.writeText(e),!0}catch{}try{if(typeof document>"u")return!1;let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.select();let r=document.execCommand("copy");return document.body.removeChild(t),r}catch{return!1}},execCommand:(e,n)=>typeof document<"u"?document.execCommand(e,!1,n):!1,queryCommandState:e=>typeof document<"u"?document.queryCommandState(e):!1,queryCommandValue:e=>typeof document<"u"?document.queryCommandValue(e):"",selectionText:()=>typeof window<"u"?window.getSelection?.()?.toString()??null:null,getSelection:()=>typeof window<"u"?window.getSelection():null,viewportHeight:()=>typeof window<"u"?window.innerHeight:0,viewportWidth:()=>typeof window<"u"?window.innerWidth:0,pathname:()=>typeof window<"u"?window.location.pathname:"",createTreeWalker:(e,n)=>typeof document<"u"?document.createTreeWalker(e,n??NodeFilter.SHOW_ALL):null,scrollTop:()=>{let e=typeof document<"u"?document:null,n=typeof window<"u"?window:null;return e?.scrollingElement?.scrollTop??n?.scrollY??0},hash:()=>typeof window<"u"?window.location?.hash??"":"",setHash:e=>{typeof window<"u"&&(window.location.hash=e)},timeout:(e,n)=>typeof window<"u"?window.setTimeout(e,n):0,rootElement:()=>typeof document<"u"?document.documentElement:null,storageGet:e=>{try{return typeof window<"u"?window.localStorage?.getItem(e)??null:null}catch{return null}},storageSet:(e,n)=>{try{typeof window<"u"&&window.localStorage?.setItem(e,n)}catch{}}}}var ie=0;function K(){return{idRegistry:new Map,unmountHooks:[],nextId:()=>`_wf_${ie++}`}}function Y(e,n){for(let t of[...e.unmountHooks])t(n);e.idRegistry.delete(n),e.idRegistry.delete(`custom:${n}`)}function se(e,n,t,r){try{e(n)}catch(o){console.error(`[weifuwu] ref ${t} error in <${r??"anonymous"}>`,o)}}function E(e,n){if(e==null||typeof e!="object")return;let t=e;if(t._id){if(t._customId&&n.idRegistry.delete(t._customId),n.idRegistry.delete(t._id),n.unmountHooks.length>0){let r=[...n.unmountHooks];for(let o of r)o(t._id);if(t._customId)for(let o of r)o(t._customId)}t._id=void 0,t._customId=void 0,t._render=void 0,t._parentNode=void 0,t._refNode=void 0}if(t._child!=null){if(Array.isArray(t._child))for(let r of t._child)r&&typeof r=="object"&&E(r,n);else E(t._child,n);t._child=void 0}if(t.props?.children&&typeof t.type=="string"){let r=Array.isArray(t.props.children)?t.props.children:[t.props.children];for(let o of r)o&&typeof o=="object"&&E(o,n)}if(typeof t.props?.ref=="function"&&se(t.props.ref,null,"cleanup"),t._remoteEl){let r=t._child;if(r!=null)if(Array.isArray(r))for(let o of r)o&&typeof o=="object"&&E(o,n);else typeof r=="object"&&E(r,n);t._remoteEl.remove(),t._remoteEl=void 0}}function O(e,n){if(n&&typeof e.type=="function"&&e._id){try{E(e,n)}catch(t){console.error("[weifuwu] ref cleanup error",t)}Y(n,e._id)}}function F(e){if(e==null||typeof e!="object"||Array.isArray(e))return;let n=e;return n._placement==="remote"?n.props?.portalKey:n.key}function Z(e,n){if(n&&e&&typeof e=="object"&&!Array.isArray(e)&&e.type===R){let t=e._childNodes;if(t&&t.length)return t}return[n]}function $(e,n,t,r,o){if(typeof r=="string"||typeof r=="number"){if(typeof t=="string"||typeof t=="number"){if(String(t)!==String(r)){if(n&&n.nodeType===3)return n.nodeValue=String(r),n;let l=e.ownerDocument.createTextNode(String(r));return n?.parentNode?n.parentNode.replaceChild(l,n):e.appendChild(l),l}return n}let s=e.ownerDocument.createTextNode(String(r));return n?.parentNode?n.parentNode.replaceChild(s,n):e.appendChild(s),s}if(r==null||typeof r=="boolean"){if(t&&typeof t=="object"&&!Array.isArray(t)&&t.type===j){let s=t._remoteEl;try{E(t.props?.children,o.registry)}catch(l){console.error("[weifuwu] portal ref cleanup error",l)}return s?.parentNode?.removeChild(s),null}if(t&&typeof t=="object"&&!Array.isArray(t))if(typeof t.type=="function")O(t,o.registry);else try{E(t,o.registry)}catch(s){console.error("[weifuwu] ref cleanup error",s)}return n?.parentNode&&n.parentNode.removeChild(n),null}if(Array.isArray(r)){if(t===r&&n?.parentNode)return n;let s=e.ownerDocument.createDocumentFragment(),l=z(e,t,r,o,n?[n]:void 0);for(let h of l)h&&s.appendChild(h);return n?.parentNode?s.contains(n)?e.appendChild(s):n.parentNode.replaceChild(s,n):e.appendChild(s),s}let i=r;if(i.type===j){let s=t&&typeof t=="object"&&!Array.isArray(t)?t:null;if(s?.type===j){let l=s._remoteEl;if(l){let h=s.props?.children??null,b=i.props?.children??null;z(l,h,b,o)}return i._remoteEl=s._remoteEl,null}return T(i,o,o.browser??k()),null}if(i.type===R){let s=t&&typeof t=="object"&&!Array.isArray(t)?t:null,l=s?._childNodes,h=z(e,s?.props?.children??t,i.props?.children??null,o,l);return i._childNodes=h.filter(Boolean),n?.parentNode&&n.nodeType===8&&n.parentNode.removeChild(n),n&&n.nodeType===1?n:h[0]??null}if(typeof i.type=="function"){if(typeof i._render!="function")throw new Error(`[vdom] component ${i.type.name||"anonymous"} not built in diff \u2014 buildVNode must run before patchValue`);let s=t&&typeof t=="object"&&!Array.isArray(t)?t:null,l=s?.type===i.type;if(!o.force&&s&&l&&s._child!==void 0&&i._child===s._child)return n;s?.type===i.type&&s._id&&(i._id=s._id,o.registry.idRegistry.set(i._id,i)),i._parentNode=e,n&&(i._refNode=n);let h;if(i._child===void 0)throw new Error(`[vdom] component ${i.type.name||"anonymous"} not built (missing _child) \u2014 buildVNode must run before patchValue`);h=i._child;let b=$(e,n,s?._child,h,o);return b&&(i._refNode=b),b}let d=t&&typeof t=="object"&&!Array.isArray(t)?t:null,u=d?.type??null,m=i.type;if(n&&n.nodeType===1&&u===m){let s=n;if(i.el=s,le(s,d?.props??{},i.props??{}),!("innerHTML"in(i.props??{}))){let l=[];z(s,d?.props?.children??null,i.props?.children??null,o,void 0,d?._childAnchors,l),i._childAnchors=l}return s}let f=T(i,o,o.browser??k());if(f==null)return null;if(n?.parentNode)if(t!=null&&typeof t!="boolean"){if(typeof t=="object"&&!Array.isArray(t))try{E(t,o.registry)}catch(s){console.error("[weifuwu] ref cleanup error",s)}n.parentNode.replaceChild(f,n)}else n.parentNode.insertBefore(f,n);else e.appendChild(f);return f}function le(e,n,t){let r=Object.keys(n),o=Object.keys(t);if(r.length===o.length){let d=!0;for(let u=0;u<r.length;u++)if(r[u]!==o[u]||n[r[u]]!==t[r[u]]){d=!1;break}if(d)return}let i=new Set([...r,...o]);for(let d of i){if(d==="children"||d==="key")continue;let u=n[d],m=t[d];if(u!==m){if(D.test(d)){let{type:f,capture:s}=B(d);typeof u=="function"&&e.removeEventListener(f,u,s?{capture:!0}:void 0),m!=null&&m!==!1&&(typeof m!="function"?console.warn(`[weifuwu] event prop ${d} expects a function, got ${typeof m} \u2014 ignored`):e.addEventListener(f,m,s?{capture:!0}:void 0));continue}if(d==="class"||d==="className"){m==null||m===!1?e.removeAttribute("class"):(e.className="",q(e,d,m));continue}if(m==null||m===!1){if(d==="class"||d==="className")e.removeAttribute("class");else if(D.test(d))e.removeEventListener(d.slice(2).toLowerCase(),u);else if(d==="ref"){if(typeof u=="function")try{u(null)}catch(f){console.error("[weifuwu] ref cleanup error",f)}}else if(d==="value")e.value="";else if(d==="indeterminate")e.indeterminate=!1;else{e.removeAttribute(d);try{delete e[d]}catch{}}continue}q(e,d,m)}}}function z(e,n,t,r,o,i,d){let u=W(n),m=W(t),f=i??o??Array.from(e.childNodes);if(m.some(a=>{if(a==null||typeof a!="object"||Array.isArray(a))return!1;let c=a;return c._placement!=="remote"&&c.key!==void 0})){for(let a=0;a<m.length;a++){let c=m[a];c&&typeof c=="object"&&!Array.isArray(c)&&F(c)===void 0&&(c.key=`pos:${a}`)}for(let a=0;a<u.length;a++){let c=u[a];c&&typeof c=="object"&&!Array.isArray(c)&&F(c)===void 0&&(c.key=`pos:${a}`)}}let l=i?i.map((a,c)=>a??(c<u.length?u[c]?._refNode??null:null)):u.map((a,c)=>{if(a==null||typeof a!="object"||Array.isArray(a))return f[c]??null;let N=a;return N._placement==="remote"?N._remoteEl??null:N.type===R?N._childNodes?.[0]??null:N._refNode??N.el??null});if(!m.some(a=>{if(a==null||typeof a!="object"||Array.isArray(a))return!1;let c=a;return c._placement!=="remote"&&c.key!==void 0})){let a=Math.max(u.length,m.length),c=[],N=V=>{d&&d.push(V)};for(let V=0;V<a;V++){let p=V<u.length?u[V]:null,y=V<m.length?m[V]:null;if(y==null||typeof y=="boolean"){let w=l[V],M=r.browser??k(),_=H(M,y);if(p==null||typeof p=="boolean"){w?.nodeType===8?(_&&w.nodeValue!==_.nodeValue&&(w.nodeValue=_.nodeValue),c.push(w),N(w)):(_&&w?.parentNode?w.parentNode.replaceChild(_,w):_&&e.appendChild(_),c.push(_),N(_));continue}if(p&&typeof p=="object"&&!Array.isArray(p))if(typeof p.type=="function")O(p,r.registry);else try{E(p,r.registry)}catch(v){console.error("[weifuwu] ref cleanup error",v)}_&&w?.parentNode?w.parentNode.replaceChild(_,w):_&&e.appendChild(_),c.push(_),N(_);continue}if(p!=null&&typeof p=="object"&&!Array.isArray(p)&&y!=null&&typeof y=="object"&&!Array.isArray(y)&&y===p){c.push(l[V]),N(l[V]);continue}if(p==null||typeof p=="boolean"){let w=T(y,r,r.browser??k());if(w==null){c.push(null),N(null);continue}let M=l[V];if(M&&M.nodeType===8&&M.nodeValue?.startsWith("wf-hole:")){M.parentNode?.replaceChild(w,M),c.push(w),N(w);continue}let _=null;for(let v=V+1;v<l.length;v++){let x=l[v];if(x&&x.parentNode===e){_=x;break}}if(!_){let v=null;for(let x=c.length-1;x>=0;x--)if(c[x]){v=c[x];break}if(v&&v.parentNode===e&&(_=v.nextSibling),!_){let x=l[l.length-1];x&&x.parentNode===e&&(_=x.nextSibling)}}_&&_.parentNode===e?e.insertBefore(w,_):e.appendChild(w),c.push(w),N(w);continue}let g=$(e,l[V],p,y,r),C=Z(y,g);c.push(...C),N(C[0]??g??null)}return c}let b=new Map;u.forEach((a,c)=>{let N=F(a);N!==void 0&&a&&typeof a=="object"&&!Array.isArray(a)&&b.set(N,{vnode:a,nodes:[l[c]??null].filter(Boolean),index:c})});let S=[],Q=new Set,L=a=>{d&&d.push(a)},A=null;return m.forEach((a,c)=>{let N=F(a),V=a;if(N!==void 0&&b.has(N)){let p=b.get(N),y=p.nodes[0]??null;Q.add(N);let g=$(e,y,p.vnode,V,r),C=Z(V,g),w=C[C.length-1]??g;w&&w.parentNode===e&&A&&w.previousSibling!==A&&e.insertBefore(w,A.nextSibling),S.push(...C),L(C[0]??g??null),w&&(A=w)}else{if(a==null||typeof a=="boolean"){let p=u[c]??null,y=l[c]??null,g=H(r.browser??k(),a);if(p==null||typeof p=="boolean")y?.nodeType===8?(g&&y.nodeValue!==g.nodeValue&&(y.nodeValue=g.nodeValue),S.push(y),L(y),y&&(A=y)):(g&&y?.parentNode?y.parentNode.replaceChild(g,y):g&&e.appendChild(g),S.push(g),L(g),g&&(A=g));else{if(typeof p=="object"&&!Array.isArray(p))if(typeof p.type=="function")O(p,r.registry);else try{E(p,r.registry)}catch(C){console.error("[weifuwu] ref cleanup error",C)}g&&y?.parentNode?y.parentNode.replaceChild(g,y):g&&e.appendChild(g),S.push(g),L(g),g&&(A=g)}return}if(V?._placement==="remote"){let p=u[c]??null,y=$(e,l[c]??null,p,V,r),g=Z(V,y);S.push(...g),L(g[0]??y??null);let C=g[g.length-1]??y;C&&(A=C)}else{let p=T(V,r,r.browser??k());if(S.push(p),L(p??null),p!=null){let y=l[c];y&&y.nodeType===8&&y.nodeValue?.startsWith("wf-hole:")?(y.parentNode?.replaceChild(p,y),A=p):(A?e.insertBefore(p,A.nextSibling):e.insertBefore(p,e.firstChild),A=p)}}}}),u.forEach((a,c)=>{let N=F(a),V=a&&typeof a=="object"&&!Array.isArray(a)&&typeof a.type=="function";if(N!==void 0&&!Q.has(N)){if(a&&typeof a=="object"&&!Array.isArray(a))if(V)O(a,r.registry);else try{E(a,r.registry)}catch(y){console.error("[weifuwu] ref cleanup error",y)}let p=l[c];p?.parentNode&&p.parentNode.removeChild(p)}else if(N===void 0){let p=c<m.length?m[c]:null,y=l[c];if(!(y?.nodeType===8&&y.nodeValue?.startsWith("wf-hole:"))||p==null){if(a&&typeof a=="object"&&!Array.isArray(a))if(V)O(a,r.registry);else try{E(a,r.registry)}catch(C){console.error("[weifuwu] ref cleanup error",C)}y?.parentNode&&y.parentNode.removeChild(y)}}}),S}function de(e){return e.__registry??(e.__registry=K())}function ee(e,n,t,r){let o=de(t),i=t.browser??k();return Promise.resolve(P(n,t,void 0,o)).then(()=>{let d=T(n,t,i);if(d!=null&&e.appendChild(d),n._id&&o){let u=o.idRegistry.get(n._id);u&&(u._parentNode=e)}r?.onMounted?.()}).catch(d=>console.error("[weifuwu] command mount error",d)),{id:n._id??""}}function Le(e,n,t){return new Promise(r=>{ee(e,n,t,{onMounted:r})})}async function We(e,n,t,r,o){return await P(r,o,t,o.__registry),$(e,n,t,r,{browser:o.browser??o.__browser,registry:o.__registry})}function De(e,n){let t=n.__registry??(n.__registry=K());return Promise.resolve(P(e,n,void 0,t))}async function $e(e,n,t){let r=await e(n,t);return typeof r=="function"?r(n):r}async function Be(e,n,t){let r=await e(n,t);return()=>typeof r=="function"?r(n):r}function G(e,n){if(e==null||typeof e!="object")return;if(Array.isArray(e)){for(let r of e)G(r,n);return}n(e);let t=e?.props?.children;t!=null&&G(t,n)}function Fe(e,n){let t;return G(e,r=>{!t&&n(r)&&(t=r)}),t}function Oe(e,n){let t=[];return G(e,r=>{typeof r?.props?.class=="string"&&r.props.class.split(" ").includes(n)&&t.push(r)}),t}function Ue(e){let n={ui:{render:()=>{},ready:!0,useExternal:()=>{}}};return e?{ui:{...n.ui,...e.ui??{}},...e.browser?{browser:e.browser}:{}}:n}function qe(e=()=>!1,n){return{get open(){return e()},setOpen:n??(t=>{}),refresh:()=>{},portal:(t,r)=>e()?t:null,wrapProps:{}}}export{De as buildToDom,qe as createPopupMock,Ue as createTestCtx,Oe as findByClass,Fe as findVNode,Be as mountComponent,Le as mountToDom,We as patchToDom,$e as renderVNode,G as walkVNode};
@@ -38,17 +38,20 @@ export interface PopupPosition {
38
38
  refresh: () => void;
39
39
  }
40
40
  /** 弹层触发方式 — usePopup 的 trigger */
41
- export type PopupTrigger = 'hover' | 'click' | 'longpress';
41
+ export type PopupTrigger = 'hover' | 'click' | 'longpress' | 'focus' | 'manual';
42
42
  /** 弹层组合器配置 — 供 ctx.ui.usePopup 使用 */
43
43
  export interface UsePopupOptions {
44
- /** 触发方式(支持 getter——动态读最新 props;hover 在触屏环境自动降级为 tap) */
45
- trigger: PopupTrigger | (() => PopupTrigger);
44
+ /** 触发方式(支持 getter——动态读最新 props;hover 在触屏环境自动降级为 tap)。
45
+ * 可选——缺省 'manual'(无触发 handler——Modal/Drawer 会话级模态场景) */
46
+ trigger?: PopupTrigger | (() => PopupTrigger);
46
47
  /** 弹出方向(支持 getter——动态读最新 props),默认 'bottom' */
47
48
  placement?: Placement | (() => Placement);
48
- /** 自由定位(支持 getter):提供则忽略 placement,直接用坐标(如右键菜单光标处) */
49
+ /** 自由定位(支持 getter):提供则忽略 placement,直接用坐标(如右键菜单光标处)。
50
+ * 可返回 width(可选)——portal 内联 style 精确宽度(DatePicker 跟随 trigger 宽) */
49
51
  position?: () => {
50
52
  x: number;
51
53
  y: number;
54
+ width?: number;
52
55
  };
53
56
  /** 水平对齐:center=居中于触发元素(默认),start=左对齐(Menubar 面板用) */
54
57
  center?: boolean;
@@ -56,8 +59,8 @@ export interface UsePopupOptions {
56
59
  gap?: number;
57
60
  /** 视口安全边距(px,默认 8) */
58
61
  margin?: number;
59
- /** 锚定元素 getter(ref 保存的触发元素) */
60
- el: () => HTMLElement | null;
62
+ /** 锚定元素 getter(ref 保存的触发元素);positioning 'none' 场景可省略 */
63
+ el?: () => HTMLElement | null;
61
64
  /** 是否打开(getter) */
62
65
  isOpen: () => boolean;
63
66
  /** 非受控:设置打开状态(调用方负责 render/dirty) */
@@ -66,8 +69,9 @@ export interface UsePopupOptions {
66
69
  open?: boolean | (() => boolean);
67
70
  /** 受控回调(可选) */
68
71
  onOpenChange?: (open: boolean) => void;
69
- /** 面板宽度(px,可选):自动 clamp 到视口(≤ 100vw - 32px) */
70
- width?: number;
72
+ /** 面板宽度(px 或 getter,可选):自动 clamp 到视口(≤ 100vw - 32px);getter 动态跟随
73
+ * (DatePicker date 模式跟随 trigger 宽,range 模式返回 undefined 自适应双面板) */
74
+ width?: number | (() => number | undefined);
71
75
  /** 点外部关闭(默认 true) */
72
76
  closeOnOutside?: boolean;
73
77
  /** Escape 关闭(默认 true) */
@@ -75,8 +79,9 @@ export interface UsePopupOptions {
75
79
  /** 遮罩(默认 false):渲染全屏 overlay(--wf-overlay,点击遮罩关闭,
76
80
  * 模态语义阻断页面交互)。false = 无遮罩 document 外部点击(§5.4 默认)。
77
81
  * 遮罩层 z-index = --wf-z-overlay(80) < 面板 --wf-z-popover(120)。
78
- * 配合 maskClosable 控制遮罩点击是否关闭。 */
79
- mask?: boolean;
82
+ * 配合 maskClosable 控制遮罩点击是否关闭。
83
+ * 传 VNode = 自定义遮罩内容(Tour 挖洞高亮遮罩——交互组件自控,不自动 onClick) */
84
+ mask?: boolean | VNode;
80
85
  /** 遮罩点击关闭(默认 true;mask:true 时生效——危险确认 maskClosable=false 防误触) */
81
86
  maskClosable?: boolean;
82
87
  /** 遮罩面板居中(默认 false;mask:true 时生效):面板覆盖全屏 flex 居中
@@ -95,12 +100,26 @@ export interface UsePopupOptions {
95
100
  closeDelay?: number | (() => number);
96
101
  /** 禁用(getter):禁用时所有触发不生效且 portal 不渲染 */
97
102
  disabled?: () => boolean;
103
+ /** 定位模式:'anchor'(默认——锚定 el 计算坐标)/ 'none'(不加坐标——组件自定义定位,
104
+ * 如 Modal 的 .wf-modal inset:0 居中) */
105
+ positioning?: 'anchor' | 'none';
106
+ /** 会话级模态能力(Modal/Drawer 用——锚定弹层默认全关,零成本) */
107
+ /** 退场状态机(open → exit → closed + animationend 卸载):组件 render 阶段调 sync(open) 驱动 */
108
+ presence?: boolean;
109
+ /** 焦点 trap(面板挂载时锁定焦点,卸载归还——会话级模态专用) */
110
+ trapFocus?: boolean;
111
+ /** 滚动锁(sync(true) 锁 body 滚动 / 面板卸载释放——会话级模态专用) */
112
+ lockScroll?: boolean;
98
113
  }
99
114
  /** 弹层组合器返回值 — usePopup */
100
115
  export interface UsePopupHandle {
101
116
  /** 当前打开状态(渲染期读取) */
102
117
  open: boolean;
103
118
  setOpen: (open: boolean) => void;
119
+ /** 当前阶段(presence 模式:open → exit → closed;非 presence:open/closed 二态) */
120
+ phase?: 'closed' | 'open' | 'exit';
121
+ /** 同步打开状态(render 阶段调用——presence 模式驱动退场状态机,返回当前 phase;非 presence 模式返回二态) */
122
+ sync?: (open: boolean) => 'closed' | 'open' | 'exit';
104
123
  /** spread 到触发/包装元素:触发(hover 门控/tap 降级/longpress)+ Escape + focus */
105
124
  wrapProps: Record<string, any>;
106
125
  /** 包装弹层内容:定位 + 视口/宽度 clamp + portal;关闭时返回 null */
@@ -261,6 +280,8 @@ export interface WfuiContext {
261
280
  ui: {
262
281
  /** 触发组件重渲染(同步,无参 = 当前组件) */
263
282
  render: (ids?: string[]) => Promise<void>;
283
+ /** 组件卸载钩子(mount 阶段注册——组件卸载时调用 fn;SSR no-op;返回退订) */
284
+ onUnmount?: (fn: () => void) => (() => void) | undefined;
264
285
  /**
265
286
  * AI 对话会话:$ 超集(会话语义 + 工具调用内嵌 + HITL 审批)
266
287
  *
@@ -467,26 +488,6 @@ export interface WfuiContext {
467
488
  onFocus: () => void;
468
489
  };
469
490
  };
470
- /**
471
- * 全屏对话框组合器(收敛 Modal/Drawer 的退场状态机 + 滚动锁 + 焦点 trap):
472
- * mount 创建,render 阶段 sync(open) 驱动状态机;组件只管布局。
473
- *
474
- * ```tsx
475
- * const dialog = ctx.ui.useDialog({ name: 'Modal' })
476
- * return (props) => {
477
- * const phase = dialog.sync(props.open)
478
- * if (phase === 'closed') return null
479
- * return createPortal(h('div', {
480
- * ref: dialog.rootRef,
481
- * class: `wf-modal ${phase === 'exit' ? 'wf-modal--exit' : 'wf-modal--enter'}`,
482
- * onKeyDown: (e) => { if (e.key === 'Escape') props.onClose?.() },
483
- * }, [overlay, h('div', { class: 'wf-modal-content', ref: dialog.panelRef }, children)]), 'modal')
484
- * }
485
- * ```
486
- * Escape 语义(危险操作差异)留在组件层——诚实裁剪。
487
- */
488
- /** 通用显隐状态机(非 dialog 浮层/面板):open → exit → closed(animationend 延迟卸载)。
489
- * useDialog 是其对话框特例(+ lockScroll/trapFocus)。mount 创建,render 阶段 sync(open)。 */
490
491
  usePresence: (options?: {
491
492
  name?: string;
492
493
  }) => {
@@ -496,17 +497,6 @@ export interface WfuiContext {
496
497
  /** render 阶段同步 open → 返回当前 phase */
497
498
  sync: (open: boolean) => 'closed' | 'open' | 'exit';
498
499
  };
499
- useDialog: (options?: {
500
- name?: string;
501
- }) => {
502
- phase: 'closed' | 'open' | 'exit';
503
- /** 挂到 portal 根(lockScroll + animationend 退场监听) */
504
- rootRef: (el: HTMLElement | null) => void;
505
- /** 挂到焦点 trap 目标(对话框面板) */
506
- panelRef: (el: HTMLElement | null) => void;
507
- /** render 阶段同步 open → 返回当前 phase */
508
- sync: (open: boolean) => 'closed' | 'open' | 'exit';
509
- };
510
500
  /** 响应式系统偏好(prefers-reduced-motion):JS 动画(rAF/tween)侧跳过用。
511
501
  * CSS 动画已有 _base.css 全局降级(0.01ms)——此原语覆盖 JS 动画路径。 */
512
502
  useReducedMotion: () => boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * vdom/audit — 结构一致性运行时校验(design/vdom-consistency-plan.md 阶段 C)
3
+ *
4
+ * 规则表(design/vdom-transform-rules.md)的运行时保证:把「用户的想法 = vnode = DOM」从
5
+ * 设计承诺变成每次 patch 的断言——错位即报错(dev),不静默传播(提交按钮消失事故的根治)。
6
+ *
7
+ * 开关:`__WF_VDOM_AUDIT`(dev/测试全开,生产默认关——O(n) 递归零生产开销)。
8
+ * 校验项:
9
+ * A1 数组数量:childNodes.length === children 数组长度(无 fragment/数组项展开时)
10
+ * A2 占位位置:数组占位项(false/null)⟷ childNodes 对应位置是注释(wf-hole)
11
+ * A3 元素类型:native vnode.type === DOM tagName
12
+ * A4 锚点:组件 _refNode 指向的节点仍在父 DOM 中
13
+ */
14
+ import type { VNodeChild } from '../vnode.ts';
15
+ /** audit 开关(dev/测试注入;生产默认关) */
16
+ export declare function auditEnabled(): boolean;
17
+ /** 数组级校验:childNodes 与 children 数组对齐(A1/A2——占位错位/数量错位,本次事故类别) */
18
+ export declare function auditChildren(parent: Node, children: VNodeChild[] | null | undefined, report: (msg: string) => void): void;
19
+ /** 树级校验:vnode 树 ↔ DOM 树递归对照(A3/A4)——入口调用一次 */
20
+ export declare function auditTree(parent: Node, child: VNodeChild, report: (msg: string) => void): void;
@@ -7,6 +7,11 @@
7
7
  * - 剪枝:已构建 + props 同 + 旧 _child 有值 → 复用旧 _child(renderFn 不重跑)
8
8
  * - 兄弟组件并行(工厂同步执行到第一个 await 后并发等待)
9
9
  * - **纯函数无 DOM**——构建产物只含 vnode 树
10
+ *
11
+ * V3-2(同步快路径):buildVNode 非 async——返回 `VNodeChild | Promise<VNodeChild>`。
12
+ * **红线(用户确认):组件两阶段异步定义不可改动**——组件 vnode 分支仍 await 工厂 +
13
+ * renderFn(renderFn 强制异步契约不变);仅「无需 await 的路径」(剪枝复用/文本/null/
14
+ * 已构建 native)同步返回——零微任务。调用方统一 await 吸收(同步值 await 仅 1 微任务)。
10
15
  */
11
16
  import type { VNode, VNodeChild } from '../vnode.ts';
12
17
  import type { WfuiContext } from '../types.ts';
@@ -17,18 +22,20 @@ export declare function componentPropsEqual(a: Record<string, any>, b: Record<st
17
22
  export declare function mountAsyncComponent(vnode: VNode, ctx: WfuiContext, reg: Registry, opts?: {
18
23
  reuse?: VNode;
19
24
  }): Promise<{
20
- renderFn: (props: VNode['props']) => VNode | null;
25
+ renderFn: (props: VNode['props']) => Promise<VNode | null>;
21
26
  childCtx: WfuiContext;
22
27
  }>;
23
28
  /**
24
- * 递归展开组件树(async):await 工厂 → renderFn → 递归子树。**零 DOM**。
29
+ * 递归展开组件树:await 工厂 → renderFn → 递归子树。**零 DOM**。
25
30
  *
26
31
  * - 组件节点保留在树上(挂 `_render` + `_child`)——$ dirty 精准刷新锚点不丢
27
32
  * - 兄弟组件 Promise.all 并行
28
33
  * - 旧树对照(oldInput):同位置同类型组件复用旧 `_render`;同 props + 旧 _child 有值
29
34
  * 复用旧 `_child`(renderFn 不重跑——三态 skip 语义前置)
30
35
  * - 原地 mutate vnode(_render/_child)——引用保持
36
+ * - V3-2:非 async——剪枝/文本/null/已构建 native 同步返回(零微任务);
37
+ * 组件路径(工厂 + renderFn await)返回 Promise(异步契约不变)
31
38
  */
32
39
  export declare function buildVNode(input: VNodeChild, ctx: WfuiContext, oldInput?: VNodeChild, reg?: Registry, opts?: {
33
40
  force?: boolean;
34
- }): Promise<VNodeChild>;
41
+ }): VNodeChild | Promise<VNodeChild>;
@@ -8,17 +8,16 @@
8
8
  * 三态 skip:props 同 + 无 dirty + ctx 版本同 → 复用旧 _child(renderFn 不重跑)。
9
9
  */
10
10
  import type { VNodeChild } from '../vnode.ts';
11
- /** 递归文本/数组归一化(children 数组展开——嵌套数组扁平化,DOM 范围对齐) */
12
- export declare function normalizeChildren(c: VNodeChild | undefined | null): VNodeChild[];
11
+ import { normalizeChildren } from '../vnode.ts';
12
+ export { normalizeChildren };
13
13
  export interface PatchCtx {
14
14
  browser: any;
15
15
  registry: import('./registry.ts').Registry;
16
- /** 三态 skip 的 dirty 集合(组件 id → true) */
17
- /** 已渲染组件(skip 判定——记录到 ctx,供 scheduler 清 dirty) */
18
- rendered?: Set<string>;
19
- /** 当前 ctx 版本号 */
16
+ /** 当前 ctx 版本号(三态 skip 版本比较:组件 _ctxVersion !== 当前版本 → 不 skip,
17
+ * 强制重渲染——bumpCtxVersion 递增后所有组件重跑 renderFn,如 i18n 切换语言) */
20
18
  ctxVersion?: number;
21
- getCtxVersion?: (id: string) => number;
19
+ /** force:跳过三态 skip(mountRoot.rerender 全量重跑用) */
20
+ force?: boolean;
22
21
  }
23
22
  /**
24
23
  * patchValue — 同步 diff 单一节点。
@@ -31,4 +30,4 @@ export declare function patchProps(el: Element, oldProps: Record<string, any>, n
31
30
  * patchChildren — 数组 diff。
32
31
  * @returns 每个新子项的 DOM 范围(Fragment 展开对齐)
33
32
  */
34
- export declare function patchChildren(parent: Node, oldInput: VNodeChild | null | undefined, newInput: VNodeChild | null | undefined, ctx: PatchCtx, oldRange?: Node[]): (Node | null)[];
33
+ export declare function patchChildren(parent: Node, oldInput: VNodeChild | null | undefined, newInput: VNodeChild | null | undefined, ctx: PatchCtx, oldRange?: Node[], oldAnchors?: (Node | null)[], anchorOut?: (Node | null)[]): (Node | null)[];
@@ -14,7 +14,7 @@
14
14
  export { buildVNode } from './build.ts';
15
15
  export { renderValue } from './render.ts';
16
16
  export { patchValue } from './diff.ts';
17
- export { createScheduler, type Scheduler } from './scheduler.ts';
17
+ export { createRenderer, type Renderer } from './mount.ts';
18
18
  export { createRegistry, type Registry } from './registry.ts';
19
19
  export { createStore, type ExternalStore } from '../store.ts';
20
20
  export { mountRoot, createVdomContext, mountCommand, unmountCommand, createCommandContainer } from './mount.ts';
@@ -7,19 +7,18 @@
7
7
  import type { VNode, VNodeChild, Component } from '../vnode.ts';
8
8
  import type { WfuiContext } from '../types.ts';
9
9
  import type { BrowserEnv } from '../types.ts';
10
- import { type Scheduler } from './scheduler.ts';
11
10
  import { type Registry } from './registry.ts';
12
11
  export interface MountOptions {
13
12
  browser: BrowserEnv;
14
13
  root: HTMLElement;
15
14
  registry?: Registry;
16
- scheduler?: Scheduler;
15
+ renderer?: Renderer;
17
16
  onError?: (e: unknown) => void;
18
17
  }
19
18
  export interface MountHandle {
20
19
  ctx: WfuiContext;
21
20
  registry: Registry;
22
- scheduler: Scheduler;
21
+ renderer: Renderer;
23
22
  /** 挂载根组件 */
24
23
  mount(comp: Component | VNodeChild): Promise<void>;
25
24
  /** 整树强制重渲染(force——测试辅助/手动刷新:renderFn 重跑 + patch) */
@@ -30,11 +29,21 @@ export interface MountHandle {
30
29
  export interface VdomContext {
31
30
  ctx: WfuiContext;
32
31
  registry: Registry;
33
- scheduler: Scheduler;
32
+ renderer: Renderer;
34
33
  rootUi: any;
35
34
  destroyPopupListeners: () => void;
36
35
  }
37
- /** 组装 vdom 渲染上下文(ctx/registry/scheduler/rootUi——含完整 hooks 转发) */
36
+ export interface Renderer {
37
+ render(ids?: string[]): Promise<void>;
38
+ }
39
+ export interface RendererOptions {
40
+ registry: Registry;
41
+ ctx: WfuiContext;
42
+ rootEl?: HTMLElement;
43
+ onError?: (e: unknown) => void;
44
+ }
45
+ export declare function createRenderer(opts: RendererOptions): Renderer;
46
+ /** 组装 vdom 渲染上下文(ctx/registry/renderer/rootUi——含完整 hooks 转发) */
38
47
  export declare function createVdomContext(opts: MountOptions): VdomContext;
39
48
  export declare function mountRoot(opts: MountOptions): MountHandle;
40
49
  /** vdom 命令式挂载:buildVNode(await 工厂)→ renderValue → append + _parentNode */
@@ -7,7 +7,11 @@
7
7
  */
8
8
  import type { VNodeChild } from '../vnode.ts';
9
9
  import type { BrowserEnv } from '../types.ts';
10
+ export { EVENT_RE, eventTarget, ENUMERATED_VALUE_BASED, holeDetail } from './transform.ts';
10
11
  export declare const SVG_TAGS: Set<string>;
11
12
  export declare function setProp(el: Element, key: string, value: any): void;
13
+ /** 占位内容(规则表 §1——wf-hole 内容可见可审计:false/null/undefined/true/对象摘要/bad-vnode) */
14
+ /** 创建占位节点(数组上下文的无渲染值 → 注释节点,childNodes 与数组同构——规则表 §1) */
15
+ export declare function createHole(browser: BrowserEnv, v: unknown): Node | null;
12
16
  /** 递归渲染(同步——组件必须已构建) */
13
17
  export declare function renderValue(v: VNodeChild, ctx: any, browser?: BrowserEnv): Node | null;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 与第 1 代 serve.ts 的区别(AGENTS.md §4.0 无自动渲染原则):
5
5
  * - 渲染管线:buildVNode(async 预构建 await 全部)→ renderValue/patchValue(同步落地)
6
- * - 调度:vdom scheduler(无 flush 批处理——$ 赋值直接 fire-and-forget 渲染)
6
+ * - 调度:vdom renderer(render() 直接执行——await = DOM 已同步)
7
7
  * - 动态挂载:buildVNode 阶段 await(无占位/注释/补全回调)
8
8
  *
9
9
  * 保留公开 API(uiServe/UIServeOptions/UIServeHandle)——迁移无缝。
@@ -0,0 +1,32 @@
1
+ /**
2
+ * transform — children/属性转化规则单一源(design/vdom-consistency-plan.md 阶段 0)
3
+ *
4
+ * 可推导性 by construction(design/vdom-transform-rules.md):所有 children 形态判定与
5
+ * 属性通道判定收敛到此单一模块——buildVNode / renderValue / patchChildren / renderSsr /
6
+ * hydrateVNode 全部调用,禁止各路径各自实现形态判定(同一语义多套实现 = 漂移 = 转化分叉)。
7
+ */
8
+ import type { VNodeChild } from '../vnode.ts';
9
+ /** 占位内容(规则表 §1——wf-hole 内容可见可审计:false/null/undefined/true/对象摘要/bad-vnode) */
10
+ export declare function holeDetail(v: unknown): string;
11
+ /** children 项分类(规则表 §1)——唯一判定,消费方只调函数不写判定 */
12
+ export type ChildKind = 'text' | 'hole' | 'native' | 'component' | 'fragment' | 'portal' | 'array' | 'invalid';
13
+ export declare function classifyChild(c: VNodeChild): ChildKind;
14
+ /** 非法 type 判定(组件/原生/Fragment/Portal 之外——数字/未知 Symbol/缺失 → 诊断占位) */
15
+ export declare function isInvalidVNodeType(t: unknown): boolean;
16
+ /** 数组上下文:无渲染值(false/null/true)→ 占位(规则表 §1——childNodes 与数组同构) */
17
+ export declare function isHoleChild(c: VNodeChild): boolean;
18
+ /** 数组项必有 key(规则表 §3):无显式 key 的元素/组件项赋默认下标 key(数组原始下标含占位位置,
19
+ * 统一字符串);文本/占位值豁免。原地写入 vnode.key(声明规则,非隐藏 magic) */
20
+ export declare function ensureArrayKeys(children: VNodeChild[]): void;
21
+ /** 事件 prop 判定:on + 大写字母(React 约定)——排除 once/only 等 on 开头非事件属性 */
22
+ export declare const EVENT_RE: RegExp;
23
+ /** 事件类型 + 捕获标志(onClick → click;onClickCapture → click + capture)——单一实现 */
24
+ export declare function eventTarget(key: string): {
25
+ type: string;
26
+ capture: boolean;
27
+ };
28
+ /** enumerated value-based 白名单(规则表 §2):空字符串解析为 false——必须显式 'true'/'false'。
29
+ * 对照 HTML 规范 enumerated 属性表:value-based 类(draggable/contenteditable/spellcheck/translate) */
30
+ export declare const ENUMERATED_VALUE_BASED: Set<string>;
31
+ /** CSS 无单位属性(数字不加 px)——其余数字样式属性必须加 px(规则表 §2 UNITLESS 白名单) */
32
+ export declare const UNITLESS_PROPS: Set<string>;
@@ -24,8 +24,8 @@ export interface VNode {
24
24
  _remoteEl?: HTMLElement | undefined;
25
25
  /** VNode 的 DOM 归属:'local' 在父 DOM 树下,'remote' 在别处 */
26
26
  _placement?: 'local' | 'remote';
27
- /** 两阶段组件的 render 函数(mount 返回的函数) */
28
- _render?: (props: Record<string, unknown>) => VNode | null;
27
+ /** 两阶段组件的 render 函数(mount 返回的函数——强制异步:props 变化时可 await 数据) */
28
+ _render?: (props: Record<string, unknown>) => Promise<VNode | null>;
29
29
  /** 组件实例 ID(如 '_wf_0') */
30
30
  _id?: string;
31
31
  /** 自定义组件 ID(ctx.ui.selfId() 注册,跨组件精准刷新) */
@@ -36,23 +36,28 @@ export interface VNode {
36
36
  _parentVNode?: VNode;
37
37
  /** 组件输出的第一个 DOM 节点 */
38
38
  _refNode?: Node | null;
39
- /** 原生 async 组件缓存(vnode 级按实例):in-flight Promise → resolved renderFn(diff 传递继承) */
40
- _asyncDef?: ((props: Record<string, unknown>) => VNode | null) | Promise<((props: Record<string, unknown>) => VNode | null)> | null;
39
+ /** 阶段 B:children 每位置的首 DOM 节点(规则表 §5 锚点优先——替代 source[i] 下标猜测,
40
+ * fragment/数组项多节点展开后相邻项不错位)。renderValue 记录,patchChildren 读取 + 回写 */
41
+ _childAnchors?: (Node | null)[];
41
42
  /** Fragment 展开后的多个直属 DOM 节点范围(diff 对齐用,见 diff.ts) */
42
43
  _childNodes?: Node[];
43
- /** 组件 mount/render 时的 ctx 版本号(供三态 skip 判定) */
44
+ /** 组件 renderFn 上次执行时的 ctx 版本号(buildVNode 剪枝 + diff 三态 skip 的版本比较——
45
+ * bumpCtxVersion 递增后版本不同 → 强制重跑 renderFn,如 i18n 切换语言) */
44
46
  _ctxVersion?: number;
45
47
  }
46
48
  /**
47
- * 两阶段异步组件(统一签名——weifuwu 只支持这一种形态):
49
+ * 两阶段异步组件(weifuwu 唯一组件形态):
48
50
  * async (initProps, ctx) => Promise<renderFn>
49
- * 外层 = mount(一次,可 await 数据),内层 = render(每次 dirty/props 变化,同步)。
51
+ * 外层 = mount(一次,可 await 数据),内层 = renderFn(每次 dirty/props 变化——**强制异步**,
52
+ * 可 await 数据;统一异步心智:两阶段都可 await,无「同步组件 vs 异步组件」二元形态)。
50
53
  * P = props 类型(JSX 自动推断),C = 组件依赖的 ctx 注入(如 ApiInjected & RouteInjected)
51
54
  *
52
- * 模式 A(design/async-mode-a-plan.md):主路径 buildVNode await 全部工厂(无占位);
53
- * 动态挂载兑底占位 + 局部补全。渲染器按「返回值是 Promise」原生判别。
55
+ * renderFn 签名:async (props) => Promise<VNode | null>——同步 renderFn 是类型错误
56
+ * (diff 永不执行 renderFn——渲染器在 buildVNode 阶段 await,同步上下文拿不到 vnode)。
57
+ * 渲染器按「返回值是 Promise」判别(主路径 buildVNode await 全部工厂 + renderFn)。
54
58
  */
55
- export type Component<P = {}, C extends object = {}> = (initProps: P, ctx: WfuiContext & C) => Promise<((props: P) => VNode | null) | null>;
59
+ export type RenderFn<P> = (props: P) => Promise<VNode | null>;
60
+ export type Component<P = {}, C extends object = {}> = (initProps: P, ctx: WfuiContext & C) => Promise<RenderFn<P> | null>;
56
61
  export declare const Fragment: unique symbol;
57
62
  /** Portal — 将子 VNode 渲染到 document.body 下的独立容器 */
58
63
  export declare const Portal: unique symbol;
@@ -63,6 +68,9 @@ export declare function jsxDEV(type: VNodeType, props: Record<string, any> | nul
63
68
  /** `h`(hyperscript)支持 variadic children: `h('div', {class:'x'}, child1, child2)` */
64
69
  export declare function h(type: VNodeType, props: Record<string, any> | null, ...children: VNodeChild[]): VNode;
65
70
  export declare function isNative(vnode: VNode): boolean;
71
+ /** 递归文本/数组归一化(children 数组展开——嵌套数组扁平化,DOM 范围对齐)。
72
+ * 栈展开(索引遍历替代 shift/unshift 头部操作——长数组 O(n) 而非 O(n²));逆序入栈 + pop 保持原顺序 */
73
+ export declare function normalizeChildren(c: VNodeChild | undefined | null): VNodeChild[];
66
74
  export declare function isComponent(vnode: VNode): boolean;
67
75
  export declare function isFragment(vnode: VNode): boolean;
68
76
  export declare function isPortal(vnode: VNode): boolean;
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
4
4
 
5
- 113 个 HTML 原语组件。每个是 `(_init, ctx) => (props) => VNode`(两阶段组件,与前端框架同一模型),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
5
+ 113 个 HTML 原语组件。每个是 `async (initProps, ctx) => (props) => Promise<VNode>`(两阶段组件,与前端框架同一模型——外层工厂 + 内层 renderFn 强制异步),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
6
6
 
7
7
  > **组件速查(weifuwu 组件 ↔ antd / Element Plus / shadcn-ui 对应 + 迁移示例)**:见 [`docs/components-map.md`](components-map.md)——从其他组件库迁来的开发者按功能直接找对应组件。
8
8
  > **自定义组件开发**:见 [docs/custom-components.md](custom-components.md)——usePopup/useControlled/对话框/AI 组件/类型纪律逐步指南。
@@ -133,9 +133,9 @@ import 'weifuwu/components/style.css' // 包含 Token + 58 布局原语 + 136
133
133
 
134
134
  ```
135
135
  mount ──────────────────────────────────────────
136
- const Counter = (_init, ctx) => { ← mount(只一次)
136
+ const Counter = async (_init, ctx) => { ← mount(只一次,可 await)
137
137
  let count = 0 ← 初始化状态
138
- return (props) => { ← render 函数
138
+ return async (props) => { ← renderFn(强制异步)
139
139
  // ... ← 每次 dirty/props 变化执行
140
140
  }
141
141
  }
@@ -149,7 +149,7 @@ ref ─────────────────────────
149
149
  })
150
150
 
151
151
  props 变化 ─────────────────────────────────────
152
- return (props) => {
152
+ return async (props) => {
153
153
  // 每次 render 都收到最新 props ← 相当于 onupdate
154
154
  if (props.value !== prevValue) { ... }
155
155
  }
@@ -558,7 +558,7 @@ props 变化 ──────────────────────
558
558
  | Markdown | `Markdown` | `content` | AI 回复渲染(安全子集 parser) |
559
559
  | CodeBlock | `CodeBlock` | `code`, `lang`, `title` | 代码块(语言标签 + 复制) |
560
560
  | Highlight | `Highlight` | `text`, `query: string \| string[]` | 搜索词高亮(mark) |
561
- | List | `List` | `items`, `renderItem`, `divided`, `header/footer/empty` | 通用列表 |
561
+ | List | `List` | `items`, `renderItem`, `keyBy?`, `divided`, `header/footer/empty` | 通用列表——`keyBy`(可选)自定义项 key:renderItem 渲染有内部状态的组件且列表动态增删/重排时传身份跟随内容的 key(默认数组下标 = 位置身份) |
562
562
  | Result | `Result` | `status`, `title`, `desc`, `extra` | 结果页(成功/失败/警告/信息) |
563
563
  | Icon | `Icon` | `name: IconName`, `size` | 图标(内置 25 个 stroke 图标,currentColor 随字号) |
564
564
  | StatCard | `StatCard` | `label`, `value`, `trend: 'up'\|'down'`, `trendLabel`, `icon`, `animate` | 统计卡片 |