weifuwu 0.75.0 → 0.76.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 (66) hide show
  1. package/README.md +13 -13
  2. package/dist/ai/types.d.ts +2 -0
  3. package/dist/components/AiChat/AiChat.d.ts +5 -1
  4. package/dist/components/ApprovalCard/ApprovalCard.d.ts +5 -2
  5. package/dist/components/CitationCard/CitationCard.d.ts +38 -0
  6. package/dist/components/Command/Command.d.ts +1 -1
  7. package/dist/components/JsonSchemaForm/JsonSchemaForm.d.ts +48 -0
  8. package/dist/components/ReasoningBlock/ReasoningBlock.d.ts +25 -0
  9. package/dist/components/Select/Select.d.ts +11 -1
  10. package/dist/components/SessionList/SessionList.d.ts +42 -0
  11. package/dist/components/TreeSelect/TreeSelect.d.ts +3 -2
  12. package/dist/components/index.d.ts +10 -2
  13. package/dist/components/index.js +14 -14
  14. package/dist/components/style.css +608 -20
  15. package/dist/index.js +140 -311
  16. package/dist/layout/weifuwu-layout.css +9 -0
  17. package/dist/test/client/setup.d.ts +12 -0
  18. package/dist/ui/index.d.ts +2 -2
  19. package/dist/ui-dom/Confirm.d.ts +5 -41
  20. package/dist/ui-dom/Notification.d.ts +8 -70
  21. package/dist/ui-dom/Toast.d.ts +8 -43
  22. package/dist/ui-dom/hooks/chat.d.ts +11 -0
  23. package/dist/ui-dom/hooks/events.d.ts +30 -0
  24. package/dist/ui-dom/hooks/external.d.ts +20 -0
  25. package/dist/ui-dom/hooks/index.d.ts +31 -0
  26. package/dist/ui-dom/hooks/input.d.ts +33 -0
  27. package/dist/ui-dom/hooks/media.d.ts +17 -0
  28. package/dist/ui-dom/hooks/popup.d.ts +37 -0
  29. package/dist/ui-dom/hooks/stable.d.ts +39 -0
  30. package/dist/ui-dom/hooks/test/hooks.test.d.ts +1 -0
  31. package/dist/ui-dom/hooks/types.d.ts +78 -0
  32. package/dist/ui-dom/index.d.ts +19 -17
  33. package/dist/ui-dom/index.js +11 -11
  34. package/dist/ui-dom/jsx-runtime.js +1 -1
  35. package/dist/ui-dom/store.d.ts +24 -0
  36. package/dist/ui-dom/testing.d.ts +74 -0
  37. package/dist/ui-dom/testing.js +1 -0
  38. package/dist/ui-dom/testing.test.d.ts +1 -0
  39. package/dist/ui-dom/types.d.ts +28 -11
  40. package/dist/ui-dom/use-chat.d.ts +19 -14
  41. package/dist/ui-dom/vdom/build.d.ts +34 -0
  42. package/dist/ui-dom/vdom/diff.d.ts +34 -0
  43. package/dist/ui-dom/vdom/hydration.d.ts +17 -0
  44. package/dist/ui-dom/vdom/index.d.ts +23 -0
  45. package/dist/ui-dom/vdom/mount.d.ts +49 -0
  46. package/dist/ui-dom/vdom/registry.d.ts +27 -0
  47. package/dist/ui-dom/vdom/render.d.ts +13 -0
  48. package/dist/ui-dom/vdom/scheduler.d.ts +13 -0
  49. package/dist/ui-dom/vdom/serve.d.ts +30 -0
  50. package/dist/ui-dom/vdom/ssr.d.ts +44 -0
  51. package/dist/ui-dom/vnode.d.ts +9 -26
  52. package/docs/components-map.md +18 -1
  53. package/docs/components.md +9 -4
  54. package/docs/custom-components.md +14 -8
  55. package/docs/frontend.md +2 -2
  56. package/docs/layout.md +2 -2
  57. package/package.json +5 -1
  58. package/dist/ui/ssr.d.ts +0 -35
  59. package/dist/ui-dom/diff.d.ts +0 -28
  60. package/dist/ui-dom/hydration.d.ts +0 -13
  61. package/dist/ui-dom/reactive.d.ts +0 -6
  62. package/dist/ui-dom/registry.d.ts +0 -27
  63. package/dist/ui-dom/render.d.ts +0 -46
  64. package/dist/ui-dom/serve.d.ts +0 -28
  65. package/dist/ui-dom/ssr.d.ts +0 -57
  66. package/dist/ui-dom/ui.d.ts +0 -78
package/dist/index.js CHANGED
@@ -3540,7 +3540,7 @@ function createAgent(client, config) {
3540
3540
  if (signal?.aborted) return;
3541
3541
  if (!finish.toolCalls?.length) {
3542
3542
  emit("wf:usage", usage);
3543
- emit("wf:done", { content: finish.content, usage });
3543
+ emit("wf:done", { content: finish.content, usage, reasoning: finish.reasoning_content });
3544
3544
  return;
3545
3545
  }
3546
3546
  all.push({
@@ -3641,311 +3641,24 @@ var HtmlSafe = class {
3641
3641
  var Fragment = /* @__PURE__ */ Symbol("Fragment");
3642
3642
  var Portal = /* @__PURE__ */ Symbol("Portal");
3643
3643
 
3644
- // src/ui-dom/debug.ts
3645
- var _enabled = null;
3646
- function uiDebugEnabled() {
3647
- if (_enabled !== null) return _enabled;
3648
- _enabled = typeof process !== "undefined" && process.env?.UI_DEBUG === "1" || globalThis.__UI_DEBUG === true || typeof localStorage !== "undefined" && localStorage.getItem("__UI_DEBUG") === "1";
3649
- return _enabled;
3650
- }
3651
- var _depth = 0;
3652
- var counters = /* @__PURE__ */ new Map();
3653
- function uiLog(label, msg, opts) {
3654
- if (!uiDebugEnabled()) return;
3655
- const n = (counters.get(label) ?? 0) + 1;
3656
- counters.set(label, n);
3657
- const first = 10;
3658
- const every = opts?.throttle ?? 100;
3659
- if (n > first && n % every !== 0) return;
3660
- const d = opts?.depth !== void 0 ? opts.depth : _depth;
3661
- const indent = " ".repeat(Math.min(d, 30));
3662
- console.log(`[ui-debug] ${indent}${label}#${n} ${msg}`);
3663
- }
3664
-
3665
- // src/ui-dom/reactive.ts
3666
- function createReactiveState(dirty) {
3667
- const _debugSets = /* @__PURE__ */ new Map();
3668
- const _dbg = (key, label) => {
3669
- const n = (_debugSets.get(key) ?? 0) + 1;
3670
- _debugSets.set(key, n);
3671
- if (n <= 5 || n % 100 === 0) uiLog(label, key + " count=" + n, { throttle: 100 });
3672
- };
3673
- const proxyCache = /* @__PURE__ */ new WeakMap();
3674
- const watchers = /* @__PURE__ */ new Set();
3675
- const reactive = (target) => {
3676
- if (target === null || typeof target !== "object") return target;
3677
- if (target instanceof Set || target instanceof Map) {
3678
- if (proxyCache.has(target)) return proxyCache.get(target);
3679
- const MUTATING = target instanceof Set ? /* @__PURE__ */ new Set(["add", "delete", "clear"]) : /* @__PURE__ */ new Set(["set", "delete", "clear"]);
3680
- const proxy2 = new Proxy(target, {
3681
- get(t, prop) {
3682
- const v = Reflect.get(t, prop);
3683
- if (typeof v === "function") {
3684
- if (!MUTATING.has(String(prop))) return v.bind(t);
3685
- return (...args) => {
3686
- const result = v.apply(t, args);
3687
- dirty();
3688
- for (const w of watchers) w();
3689
- return result;
3690
- };
3691
- }
3692
- return v;
3693
- },
3694
- set(t, key, value) {
3695
- Reflect.set(t, key, value);
3696
- _dbg(String(key), "set");
3697
- dirty();
3698
- for (const w of watchers) w();
3699
- return true;
3700
- }
3701
- });
3702
- proxyCache.set(target, proxy2);
3703
- return proxy2;
3704
- }
3705
- if (target instanceof Date || target instanceof RegExp) return target;
3706
- if (proxyCache.has(target)) return proxyCache.get(target);
3707
- const proxy = new Proxy(target, {
3708
- set(target2, key, value) {
3709
- const old = Reflect.get(target2, key);
3710
- if (old === value) return true;
3711
- Reflect.set(target2, key, value);
3712
- dirty();
3713
- for (const w of watchers) w();
3714
- return true;
3715
- },
3716
- get(target2, key) {
3717
- const value = Reflect.get(target2, key);
3718
- if (typeof value === "object" && value !== null) return reactive(value);
3719
- return value;
3720
- },
3721
- deleteProperty(target2, key) {
3722
- if (Reflect.has(target2, key)) {
3723
- Reflect.deleteProperty(target2, key);
3724
- dirty();
3725
- for (const w of watchers) w();
3726
- }
3727
- return true;
3728
- }
3729
- });
3730
- proxyCache.set(target, proxy);
3731
- return proxy;
3732
- };
3733
- const root = reactive({});
3734
- Object.defineProperty(root, "__watch", {
3735
- value: (cb) => {
3736
- watchers.add(cb);
3737
- return () => {
3738
- watchers.delete(cb);
3739
- };
3740
- },
3741
- writable: false,
3742
- enumerable: false
3743
- });
3744
- return root;
3745
- }
3746
-
3747
- // src/ui/ssr.ts
3644
+ // src/ui-dom/vdom/ssr.ts
3645
+ var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
3646
+ var SVG_TAGS = /* @__PURE__ */ new Set(["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g", "text", "defs", "use", "clipPath"]);
3748
3647
  function escape(s) {
3749
3648
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3750
3649
  }
3751
- var VOID_TAGS = /* @__PURE__ */ new Set([
3752
- "area",
3753
- "base",
3754
- "br",
3755
- "col",
3756
- "embed",
3757
- "hr",
3758
- "img",
3759
- "input",
3760
- "link",
3761
- "meta",
3762
- "param",
3763
- "source",
3764
- "track",
3765
- "wbr"
3766
- ]);
3767
3650
  function classToString(v) {
3768
3651
  if (typeof v === "string") return v;
3769
3652
  if (Array.isArray(v)) return v.filter(Boolean).join(" ");
3770
- if (v && typeof v === "object") {
3771
- return Object.entries(v).filter(([, b]) => b).map(([k]) => k).join(" ");
3772
- }
3653
+ if (v && typeof v === "object") return Object.entries(v).filter(([, on]) => on).map(([k]) => k).join(" ");
3773
3654
  return "";
3774
3655
  }
3775
3656
  function styleToString(v) {
3776
- if (typeof v === "string") return v;
3777
- if (v && typeof v === "object") {
3778
- return Object.entries(v).filter(([, x]) => x != null).map(([k, x]) => `${k}:${typeof x === "number" ? x + "px" : String(x)}`).join(";");
3779
- }
3780
- return "";
3781
- }
3782
- function createSsrContext(serverCtx, dataStore) {
3783
- const usedIds = /* @__PURE__ */ new Set();
3784
- const ui2 = {
3785
- $: () => createReactiveState(() => {
3786
- }),
3787
- dirty: () => {
3788
- },
3789
- render: () => {
3790
- },
3791
- selfId: (name) => {
3792
- if (usedIds.has(name)) throw new Error(`[weifuwu] Duplicate component ID: "${name}"`);
3793
- usedIds.add(name);
3794
- },
3795
- useMedia: () => {
3796
- },
3797
- useBreakpoint: () => {
3798
- },
3799
- usePopupPosition: () => ({ top: 0, left: 0, refresh: () => {
3800
- } }),
3801
- // 新原语族:SSR 确定性 no-op(不启动监听/会话/取数;组件挂载不崩即契约)
3802
- useHoverCapable: () => false,
3803
- useVisualViewport: () => ({ height: 0, offsetTop: 0, keyboardOpen: false }),
3804
- useLongPress: () => ({}),
3805
- useInView: () => ({ isIn: false, ready: false, observe: () => {
3806
- }, refresh: () => {
3807
- }, disconnect: () => {
3808
- } }),
3809
- useScrollPosition: () => ({ y: 0, refresh: () => {
3810
- } }),
3811
- useStableRef: (init) => (el) => {
3812
- if (el) init?.(el);
3813
- },
3814
- useGlobalKey: () => () => {
3815
- },
3816
- useReducedMotion: () => false,
3817
- useAnimationEnd: () => () => {
3818
- },
3819
- useTween: (target) => {
3820
- const handle = { value: target, reset: (to) => {
3821
- handle.value = to;
3822
- } };
3823
- return handle;
3824
- },
3825
- useDrag: () => ({ onPointerDown: () => {
3826
- } }),
3827
- useDragDrop: () => ({ dropProps: {}, dragProps: {} }),
3828
- useControlled: (options) => ({
3829
- value: options.value,
3830
- setValue: (v) => {
3831
- options.onChange?.(v);
3832
- },
3833
- controlled: options.value !== void 0
3834
- }),
3835
- useControlledInput: (options) => ({
3836
- value: options.value,
3837
- setValue: (v) => {
3838
- options.onChange?.(v);
3839
- },
3840
- controlled: options.value !== void 0,
3841
- keyword: "",
3842
- setKeyword: () => {
3843
- },
3844
- selectedLabel: "",
3845
- setSelectedLabel: () => {
3846
- }
3847
- }),
3848
- useOpen: (options) => ({
3849
- get open() {
3850
- return !!options.open;
3851
- },
3852
- setOpen: (v) => {
3853
- options.onOpenChange?.(v);
3854
- },
3855
- triggerProps: {}
3856
- }),
3857
- useAsync: () => ({ data: void 0, loading: true, error: void 0, reload: () => {
3858
- } }),
3859
- usePopup: () => ({
3860
- open: false,
3861
- setOpen: () => {
3862
- },
3863
- wrapProps: {},
3864
- portal: () => null,
3865
- refresh: () => {
3866
- }
3867
- }),
3868
- usePresence: () => ({
3869
- phase: "closed",
3870
- ref: () => {
3871
- },
3872
- sync: (open2) => open2 ? "open" : "closed"
3873
- }),
3874
- useDialog: () => ({
3875
- phase: "closed",
3876
- rootRef: () => {
3877
- },
3878
- panelRef: () => {
3879
- },
3880
- sync: (open2) => open2 ? "open" : "closed"
3881
- }),
3882
- // SSR 确定性空态:会话不启动(无事件/无网络),仅保证挂载不崩
3883
- useChat: () => ({
3884
- messages: [],
3885
- input: "",
3886
- streaming: false,
3887
- error: null,
3888
- usage: null,
3889
- step: null,
3890
- send: () => {
3891
- },
3892
- stop: () => {
3893
- },
3894
- retry: () => {
3895
- },
3896
- clear: () => {
3897
- },
3898
- approve: async () => {
3899
- },
3900
- dispose: () => {
3901
- }
3902
- })
3903
- };
3904
- const browser = {
3905
- activeElement: () => null,
3906
- byId: () => null,
3907
- query: () => null,
3908
- createElement: () => null,
3909
- bodyAppend: () => {
3910
- },
3911
- bodyRemove: () => {
3912
- },
3913
- copyText: async () => false,
3914
- execCommand: () => false,
3915
- selectionText: () => null,
3916
- getSelection: () => null,
3917
- queryCommandState: () => false,
3918
- queryCommandValue: () => "",
3919
- viewportHeight: () => 0,
3920
- scrollTop: () => 0,
3921
- hash: () => "",
3922
- setHash: () => {
3923
- },
3924
- timeout: () => 0,
3925
- rootElement: () => null,
3926
- storageGet: () => null,
3927
- storageSet: () => {
3928
- }
3929
- };
3930
- const ctx = Object.create(serverCtx ?? {});
3931
- ctx.ui = ui2;
3932
- ctx.browser = browser;
3933
- ctx.data = {
3934
- async get(key, fetcher) {
3935
- if (dataStore.has(key)) return dataStore.get(key);
3936
- if (!fetcher) return void 0;
3937
- const val = await fetcher();
3938
- dataStore.set(key, val);
3939
- return val;
3940
- },
3941
- set(key, val) {
3942
- dataStore.set(key, val);
3943
- },
3944
- has(key) {
3945
- return dataStore.has(key);
3946
- }
3947
- };
3948
- return ctx;
3657
+ return Object.entries(v).filter(([, val]) => val != null).map(([k, val]) => {
3658
+ const key = k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
3659
+ const value = typeof val === "number" && val !== 0 ? `${val}px` : String(val);
3660
+ return `${key}:${value}`;
3661
+ }).join(";");
3949
3662
  }
3950
3663
  async function renderSsr(input, ctx) {
3951
3664
  if (input == null || typeof input === "boolean") return "";
@@ -3956,17 +3669,10 @@ async function renderSsr(input, ctx) {
3956
3669
  return s;
3957
3670
  }
3958
3671
  const vnode = input;
3959
- if (vnode.type === Portal) return renderSsr(vnode.props?.children, ctx);
3960
- if (vnode.type === Fragment) return renderSsr(vnode.props?.children, ctx);
3672
+ if (vnode.type === Portal || vnode.type === Fragment) return renderSsr(vnode.props?.children, ctx);
3961
3673
  if (typeof vnode.type === "function") {
3962
3674
  const childCtx = Object.create(ctx);
3963
- let renderFn;
3964
- try {
3965
- renderFn = vnode.type(vnode.props ?? {}, childCtx);
3966
- } catch (e) {
3967
- throw e;
3968
- }
3969
- if (renderFn instanceof Promise) renderFn = await renderFn;
3675
+ const renderFn = await vnode.type(vnode.props ?? {}, childCtx);
3970
3676
  if (typeof renderFn !== "function") {
3971
3677
  throw new Error(
3972
3678
  `Component ${vnode.type.name || "anonymous"} must return a render function. Use (init_props, ctx) => (props) => VNode pattern.`
@@ -3995,6 +3701,10 @@ async function renderSsr(input, ctx) {
3995
3701
  attrs.push(` style="${escape(styleToString(value))}"`);
3996
3702
  continue;
3997
3703
  }
3704
+ if (key === "draggable" || key === "contenteditable" || key === "spellcheck") {
3705
+ attrs.push(` ${key}="${value ? "true" : "false"}"`);
3706
+ continue;
3707
+ }
3998
3708
  if (value === true) {
3999
3709
  attrs.push(` ${key}`);
4000
3710
  continue;
@@ -4003,21 +3713,140 @@ async function renderSsr(input, ctx) {
4003
3713
  attrs.push(` ${key}="${escape(String(value))}"`);
4004
3714
  }
4005
3715
  const attrStr = attrs.join("");
3716
+ const svgWrap = SVG_TAGS.has(tag);
4006
3717
  if (VOID_TAGS.has(tag)) return `<${tag}${attrStr}>`;
4007
3718
  if (innerHTML !== void 0) return `<${tag}${attrStr}>${innerHTML}</${tag}>`;
4008
3719
  const children = await renderSsr(props.children, ctx);
3720
+ if (svgWrap) return `<${tag}${attrStr}>${children}</${tag}>`;
4009
3721
  return `<${tag}${attrStr}>${children}</${tag}>`;
4010
3722
  }
4011
- async function ssrToString(Comp, props, serverCtx, opts = {}) {
4012
- const dataStore = opts.data ?? /* @__PURE__ */ new Map();
4013
- const ctx = createSsrContext(serverCtx, dataStore);
4014
- const html = await renderSsr({ type: Comp, props: props ?? {}, key: void 0 }, ctx);
4015
- return new HtmlSafe(html);
3723
+ function createSsrUi() {
3724
+ const noop = () => () => {
3725
+ };
3726
+ const noopReturn = () => ({});
3727
+ return {
3728
+ _selfId: "_wf_root",
3729
+ render: () => {
3730
+ },
3731
+ dirty: () => {
3732
+ },
3733
+ $: () => ({}),
3734
+ selfId: () => {
3735
+ },
3736
+ bumpCtxVersion: () => {
3737
+ },
3738
+ setMounting: () => {
3739
+ },
3740
+ endMounting: () => {
3741
+ },
3742
+ // hooks no-op(组件 SSR 安全——不注册监听/定时器)
3743
+ useChat: () => ({ messages: [], input: "", streaming: false, error: null, usage: null, step: null, send: () => {
3744
+ }, stop: () => {
3745
+ }, retry: () => {
3746
+ }, clear: () => {
3747
+ }, approve: () => {
3748
+ }, subscribe: () => () => {
3749
+ } }),
3750
+ useExternal: (store) => store,
3751
+ useMedia: noop,
3752
+ useBreakpoint: noop,
3753
+ usePopupPosition: () => ({ top: 0, left: 0, refresh: () => {
3754
+ } }),
3755
+ useHoverCapable: () => false,
3756
+ useStableRef: (init) => (el) => {
3757
+ if (el) init?.(el);
3758
+ },
3759
+ useVisualViewport: () => ({ height: 0, offsetTop: 0, keyboardOpen: false }),
3760
+ usePopup: () => ({ open: false, setOpen: () => {
3761
+ }, wrapProps: {}, portal: () => null, refresh: () => {
3762
+ } }),
3763
+ useLongPress: noopReturn,
3764
+ useInView: () => ({ isIn: false, ready: false, observe: () => {
3765
+ }, refresh: () => {
3766
+ }, disconnect: () => {
3767
+ } }),
3768
+ useScrollPosition: () => ({ y: 0, refresh: () => {
3769
+ } }),
3770
+ useAsync: () => ({ data: void 0, loading: true, error: void 0, reload: () => {
3771
+ } }),
3772
+ useControlled: () => ({ value: void 0, setValue: () => {
3773
+ }, controlled: false }),
3774
+ useControlledInput: () => ({ value: void 0, setValue: () => {
3775
+ }, keyword: "", setKeyword: () => {
3776
+ }, selectedLabel: "", setSelectedLabel: () => {
3777
+ }, controlled: false }),
3778
+ useOpen: () => ({ open: false, setOpen: () => {
3779
+ }, triggerProps: {} }),
3780
+ usePresence: () => ({ phase: "closed", ref: () => {
3781
+ }, sync: () => "closed" }),
3782
+ useDialog: () => ({ phase: "closed", rootRef: () => {
3783
+ }, panelRef: () => {
3784
+ }, sync: (open2) => open2 ? "open" : "closed" }),
3785
+ useGlobalKey: () => () => {
3786
+ },
3787
+ useDrag: noopReturn,
3788
+ useDragDrop: () => ({ dropProps: {}, dragProps: {} }),
3789
+ useReducedMotion: () => true,
3790
+ // SSR 无动画——组件直落终值
3791
+ useAnimationEnd: () => () => {
3792
+ },
3793
+ useTween: (t) => {
3794
+ let v = t;
3795
+ return {
3796
+ get value() {
3797
+ return v;
3798
+ },
3799
+ set value(n) {
3800
+ v = n;
3801
+ },
3802
+ reset: (n) => {
3803
+ if (n !== void 0) v = n;
3804
+ return v;
3805
+ }
3806
+ };
3807
+ }
3808
+ };
3809
+ }
3810
+ function createSsrContext(serverCtx, dataStore) {
3811
+ const ctx = {
3812
+ ...serverCtx,
3813
+ ui: createSsrUi(),
3814
+ browser: void 0
3815
+ };
3816
+ const dataCache = /* @__PURE__ */ new Map();
3817
+ ctx.data = {
3818
+ async get(key, fetcher) {
3819
+ const entry = dataCache.get(key);
3820
+ if (entry && "value" in entry) return entry.value;
3821
+ if (entry?.promise) return entry.promise;
3822
+ if (!fetcher) return void 0;
3823
+ const promise = Promise.resolve().then(() => fetcher()).then((val) => {
3824
+ dataCache.set(key, { value: val });
3825
+ dataStore.set(key, val);
3826
+ return val;
3827
+ });
3828
+ dataCache.set(key, { promise });
3829
+ return promise;
3830
+ },
3831
+ set(key, value) {
3832
+ dataCache.set(key, { value });
3833
+ dataStore.set(key, value);
3834
+ },
3835
+ has(key) {
3836
+ return dataCache.has(key);
3837
+ }
3838
+ };
3839
+ return ctx;
4016
3840
  }
4017
3841
  function serializeData(data) {
4018
3842
  const json = JSON.stringify(Object.fromEntries(data)).replace(/</g, "\\u003c");
4019
3843
  return `<script>window.__DATA__=${json};</script>`;
4020
3844
  }
3845
+ async function ssrToString(Comp, props, serverCtx, opts = {}) {
3846
+ const dataStore = opts.data ?? /* @__PURE__ */ new Map();
3847
+ const ctx = createSsrContext(serverCtx, dataStore);
3848
+ return renderSsr({ type: Comp, props: props ?? {}, key: void 0 }, ctx);
3849
+ }
4021
3850
 
4022
3851
  // src/ui/index.ts
4023
3852
  function escape2(s) {
@@ -852,6 +852,15 @@ body.wf-dragging {
852
852
  max-width: min(var(--wf-popup-max, 480px), calc(100vw - 32px));
853
853
  box-sizing: border-box;
854
854
  }
855
+
856
+ /* usePopup mask 模式遮罩:全屏 fixed,z-index 低于面板(overlay=80 < popover=120),
857
+ * 模态语义阻断页面交互;点击遮罩关闭(maskClosable 门控在组件层) */
858
+ .wf-popup-mask {
859
+ position: fixed;
860
+ inset: 0;
861
+ z-index: var(--wf-z-overlay);
862
+ background: var(--wf-overlay);
863
+ }
855
864
  }
856
865
 
857
866
  @layer layout {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * weifuwu/ui-dom 测试环境 setup
3
+ *
4
+ * 在每个测试文件开头调用:
5
+ * ```ts
6
+ * import { setupJsdom } from './setup.ts'
7
+ * setupJsdom()
8
+ * ```
9
+ *
10
+ * 提供 JSDOM 浏览器全局环境,供 jsx-runtime / signal 等模块使用。
11
+ */
12
+ export declare function setupJsdom(): void;
@@ -26,7 +26,7 @@
26
26
  * ```
27
27
  */
28
28
  import type { Middleware } from '../types.ts';
29
- import type { Component, AsyncComponent } from '../ui-dom/vnode.ts';
29
+ import type { Component } from '../ui-dom/vnode.ts';
30
30
  declare module '../types.ts' {
31
31
  interface Context {
32
32
  ui: {
@@ -41,7 +41,7 @@ declare module '../types.ts' {
41
41
  * 支持 async 工厂组件:await 工厂 → 数据进 HTML。
42
42
  * opts.data(Map)收集 ctx.data 预取结果,用 ctx.ui.ssrData(data) 序列化进 __DATA__。
43
43
  */
44
- ssr: (Comp: Component | AsyncComponent, props?: Record<string, any>, opts?: {
44
+ ssr: (Comp: Component, props?: Record<string, any>, opts?: {
45
45
  data?: Map<string, unknown>;
46
46
  }) => Promise<string>;
47
47
  /** 序列化 SSR 数据存储 → <script>window.__DATA__=...</script>(HtmlSafe) */
@@ -1,44 +1,8 @@
1
1
  /**
2
- * weifuwu/components — Confirm 确认对话框
2
+ * weifuwu/ui-dom — Confirm(命令式中间件)
3
3
  *
4
- * 声明式:基于 Modal 封装,footer 自带「取消 / 确定」按钮。
5
- * 命令式:confirm() 中间件注入 ctx.confirm(),返回 Promise<boolean>。
4
+ * v1 退役(design/render-only-plan.md):实现已收敛到 components/Confirm(vdom 引擎,
5
+ * mountCommand + render-only)。本文件保留为公开 API 兼容入口。
6
6
  */
7
- import type { Component } from './vnode.ts';
8
- import type { AppMiddleware } from './types.ts';
9
- export interface ConfirmProps {
10
- open?: boolean;
11
- title?: string;
12
- /** 提示内容(文本或任意 VNode) */
13
- message?: any;
14
- confirmText?: string;
15
- cancelText?: string;
16
- variant?: 'primary' | 'danger';
17
- /** 对话框宽度,如 '500px'、'80%',默认 Modal 的 400px */
18
- width?: string;
19
- /** 遮罩点击是否取消(默认 false:危险操作防误触;显式传 true 可恢复) */
20
- maskClosable?: boolean;
21
- onConfirm?: () => void;
22
- onCancel?: () => void;
23
- }
24
- /** 命令式 ctx.confirm 的选项(ConfirmProps 的子集) */
25
- export interface ConfirmOptions {
26
- title?: string;
27
- confirmText?: string;
28
- cancelText?: string;
29
- variant?: 'primary' | 'danger';
30
- width?: string;
31
- maskClosable?: boolean;
32
- }
33
- /** 命令式 ctx.confirm 的注入类型(AppMiddleware<{}, ConfirmInjected>) */
34
- export interface ConfirmInjected {
35
- confirm: (message: string, options?: ConfirmOptions) => Promise<boolean>;
36
- }
37
- /**
38
- * 命令式中间件:注入 ctx.confirm()
39
- *
40
- * 每次调用挂载 Confirm 组件到独立容器(mountVNode),
41
- * resolve 后 callRefCleanup 清理(含 Modal 的 portal DOM)+ 移除容器。
42
- */
43
- export declare function confirm(): AppMiddleware<{}, ConfirmInjected>;
44
- export declare const Confirm: Component<ConfirmProps>;
7
+ export { Confirm, confirm } from '../components/Confirm/Confirm.ts';
8
+ export type { ConfirmProps, ConfirmOptions, ConfirmInjected } from '../components/Confirm/Confirm.ts';
@@ -1,70 +1,8 @@
1
- import type { Component } from './vnode.ts';
2
- import type { AppMiddleware } from './types.ts';
3
- export type NotificationType = 'success' | 'error' | 'info' | 'warning';
4
- export type NotificationPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
5
- export interface NotificationItem {
6
- id: string;
7
- type: NotificationType;
8
- title: string;
9
- description?: string;
10
- /** 自动关闭时间(ms),0 = 不自动关闭,默认 4500 */
11
- duration?: number;
12
- action?: {
13
- label: string;
14
- onClick: () => void;
15
- };
16
- }
17
- export interface NotificationProps {
18
- items?: NotificationItem[];
19
- onRemove?: (id: string) => void;
20
- position?: NotificationPosition;
21
- /** 全局默认自动关闭时间(ms),默认 4500 */
22
- duration?: number;
23
- /** 最大显示条数,超出移除最早,默认 0 = 不限制 */
24
- max?: number;
25
- }
26
- export interface NotificationOptions {
27
- position?: NotificationPosition;
28
- duration?: number;
29
- max?: number;
30
- }
31
- /** 通知(对应 antd/EP Notification 队列式):title + description + icon + 操作,聚合角落 */
32
- export declare const Notification: Component<NotificationProps>;
33
- export interface NotificationInjected {
34
- notification: {
35
- (title: string, opts?: {
36
- type?: NotificationType;
37
- description?: string;
38
- duration?: number;
39
- action?: NotificationItem['action'];
40
- }): void;
41
- open: (opts: {
42
- type?: NotificationType;
43
- title: string;
44
- description?: string;
45
- duration?: number;
46
- action?: NotificationItem['action'];
47
- }) => void;
48
- success: (opts: {
49
- title: string;
50
- description?: string;
51
- duration?: number;
52
- }) => void;
53
- error: (opts: {
54
- title: string;
55
- description?: string;
56
- duration?: number;
57
- }) => void;
58
- info: (opts: {
59
- title: string;
60
- description?: string;
61
- duration?: number;
62
- }) => void;
63
- warning: (opts: {
64
- title: string;
65
- description?: string;
66
- duration?: number;
67
- }) => void;
68
- };
69
- }
70
- export declare function notification(opts?: NotificationOptions): AppMiddleware<{}, NotificationInjected>;
1
+ /**
2
+ * weifuwu/ui-dom — Notification(命令式中间件)
3
+ *
4
+ * v1 退役(design/render-only-plan.md):实现已收敛到 components/Notification(vdom 引擎,
5
+ * mountCommand + render-only)。本文件保留为公开 API 兼容入口。
6
+ */
7
+ export { Notification, notification } from '../components/Notification/Notification.ts';
8
+ export type { NotificationType, NotificationPosition, NotificationItem, NotificationProps, NotificationOptions, NotificationInjected, } from '../components/Notification/Notification.ts';