weifuwu 0.74.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.
- package/README.md +20 -20
- package/dist/ai/types.d.ts +3 -1
- package/dist/components/AiChat/AiChat.d.ts +5 -1
- package/dist/components/ApprovalCard/ApprovalCard.d.ts +5 -2
- package/dist/components/Button/Button.d.ts +2 -0
- package/dist/components/CitationCard/CitationCard.d.ts +38 -0
- package/dist/components/Command/Command.d.ts +1 -1
- package/dist/components/JsonSchemaForm/JsonSchemaForm.d.ts +48 -0
- package/dist/components/ReasoningBlock/ReasoningBlock.d.ts +25 -0
- package/dist/components/Select/Select.d.ts +11 -1
- package/dist/components/SessionList/SessionList.d.ts +42 -0
- package/dist/components/TreeSelect/TreeSelect.d.ts +3 -2
- package/dist/components/index.d.ts +10 -2
- package/dist/components/index.js +14 -14
- package/dist/components/style.css +608 -20
- package/dist/index.d.ts +0 -2
- package/dist/index.js +141 -433
- package/dist/layout/weifuwu-layout.css +9 -0
- package/dist/test/client/setup.d.ts +12 -0
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui-dom/Confirm.d.ts +5 -41
- package/dist/ui-dom/Notification.d.ts +8 -70
- package/dist/ui-dom/Toast.d.ts +8 -43
- package/dist/ui-dom/ai.d.ts +1 -1
- package/dist/ui-dom/browser.d.ts +1 -1
- package/dist/ui-dom/focus-trap.d.ts +1 -1
- package/dist/ui-dom/hooks/chat.d.ts +11 -0
- package/dist/ui-dom/hooks/events.d.ts +30 -0
- package/dist/ui-dom/hooks/external.d.ts +20 -0
- package/dist/ui-dom/hooks/index.d.ts +31 -0
- package/dist/ui-dom/hooks/input.d.ts +33 -0
- package/dist/ui-dom/hooks/media.d.ts +17 -0
- package/dist/ui-dom/hooks/popup.d.ts +37 -0
- package/dist/ui-dom/hooks/stable.d.ts +39 -0
- package/dist/ui-dom/hooks/test/hooks.test.d.ts +1 -0
- package/dist/ui-dom/hooks/types.d.ts +78 -0
- package/dist/ui-dom/index.d.ts +19 -17
- package/dist/ui-dom/index.js +12 -10
- package/dist/ui-dom/jsx-runtime.js +1 -1
- package/dist/ui-dom/middleware/api.d.ts +3 -3
- package/dist/ui-dom/middleware/auth.d.ts +1 -1
- package/dist/ui-dom/motion.d.ts +1 -1
- package/dist/ui-dom/popup.d.ts +1 -1
- package/dist/ui-dom/scroll-lock.d.ts +1 -1
- package/dist/ui-dom/store.d.ts +24 -0
- package/dist/ui-dom/testing.d.ts +74 -0
- package/dist/ui-dom/testing.js +1 -0
- package/dist/ui-dom/testing.test.d.ts +1 -0
- package/dist/ui-dom/types.d.ts +34 -17
- package/dist/ui-dom/use-chat.d.ts +19 -14
- package/dist/ui-dom/vdom/build.d.ts +34 -0
- package/dist/ui-dom/vdom/diff.d.ts +34 -0
- package/dist/ui-dom/vdom/hydration.d.ts +17 -0
- package/dist/ui-dom/vdom/index.d.ts +23 -0
- package/dist/ui-dom/vdom/mount.d.ts +49 -0
- package/dist/ui-dom/vdom/registry.d.ts +27 -0
- package/dist/ui-dom/vdom/render.d.ts +13 -0
- package/dist/ui-dom/vdom/scheduler.d.ts +13 -0
- package/dist/ui-dom/vdom/serve.d.ts +30 -0
- package/dist/ui-dom/vdom/ssr.d.ts +44 -0
- package/dist/ui-dom/vnode.d.ts +11 -44
- package/dist/user/token.d.ts +1 -1
- package/docs/compat-three-library.md +1 -1
- package/docs/components-map.md +19 -2
- package/docs/components.md +9 -4
- package/docs/custom-components.md +19 -14
- package/docs/data.md +16 -33
- package/docs/environment.md +1 -1
- package/docs/frontend-middleware.md +65 -79
- package/docs/frontend-ui-dom.md +4 -2
- package/docs/frontend.md +35 -38
- package/docs/layout.md +2 -2
- package/docs/realtime.md +23 -28
- package/docs/saas.md +8 -6
- package/package.json +5 -1
- package/dist/ui/ssr-page.d.ts +0 -41
- package/dist/ui/ssr.d.ts +0 -35
- package/dist/ui-dom/diff.d.ts +0 -28
- package/dist/ui-dom/hydration.d.ts +0 -13
- package/dist/ui-dom/reactive.d.ts +0 -6
- package/dist/ui-dom/registry.d.ts +0 -39
- package/dist/ui-dom/render.d.ts +0 -46
- package/dist/ui-dom/route-match.d.ts +0 -22
- package/dist/ui-dom/serve.d.ts +0 -28
- package/dist/ui-dom/ssr.d.ts +0 -56
- package/dist/ui-dom/ui.d.ts +0 -78
package/dist/ui-dom/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* weifuwu/
|
|
2
|
+
* weifuwu/ui-dom 类型定义
|
|
3
3
|
*/
|
|
4
4
|
import type { UseChatHandle, UseChatOptions } from './use-chat.ts';
|
|
5
5
|
import type { VNode } from './vnode.ts';
|
|
@@ -22,12 +22,6 @@ export interface PopupPositionOptions {
|
|
|
22
22
|
/** 视口边缘安全边距(px,默认 8) */
|
|
23
23
|
margin?: number;
|
|
24
24
|
}
|
|
25
|
-
/** 响应式状态容器(createReactiveState 返回值)——深度 Proxy:任意层级赋值触发 dirty */
|
|
26
|
-
export interface ReactiveState {
|
|
27
|
-
/** 订阅状态变更(任意层级赋值/删除触发);返回退订函数 */
|
|
28
|
-
__watch: (cb: () => void) => () => void;
|
|
29
|
-
[key: string]: any;
|
|
30
|
-
}
|
|
31
25
|
/** 异步取数工具返回值 — ctx.ui.useAsync()(data/loading/error 响应式,reload 重跑) */
|
|
32
26
|
export interface UseAsyncHandle<T = any> {
|
|
33
27
|
data?: T;
|
|
@@ -78,6 +72,16 @@ export interface UsePopupOptions {
|
|
|
78
72
|
closeOnOutside?: boolean;
|
|
79
73
|
/** Escape 关闭(默认 true) */
|
|
80
74
|
closeOnEscape?: boolean;
|
|
75
|
+
/** 遮罩(默认 false):渲染全屏 overlay(--wf-overlay,点击遮罩关闭,
|
|
76
|
+
* 模态语义阻断页面交互)。false = 无遮罩 document 外部点击(§5.4 默认)。
|
|
77
|
+
* 遮罩层 z-index = --wf-z-overlay(80) < 面板 --wf-z-popover(120)。
|
|
78
|
+
* 配合 maskClosable 控制遮罩点击是否关闭。 */
|
|
79
|
+
mask?: boolean;
|
|
80
|
+
/** 遮罩点击关闭(默认 true;mask:true 时生效——危险确认 maskClosable=false 防误触) */
|
|
81
|
+
maskClosable?: boolean;
|
|
82
|
+
/** 遮罩面板居中(默认 false;mask:true 时生效):面板覆盖全屏 flex 居中
|
|
83
|
+
* (预览图/全屏浮层场景——忽略 trigger 定位,替代 dropdown 的 bottom 定位) */
|
|
84
|
+
maskCentered?: boolean;
|
|
81
85
|
/** 长按触发时长(ms,仅 trigger='longpress',默认 500) */
|
|
82
86
|
longPressDuration?: number;
|
|
83
87
|
/** 打开触发回调(longpress 计时到/右键兼容时调用,携带光标坐标——右键菜单定位用) */
|
|
@@ -256,11 +260,7 @@ export interface WfuiContext {
|
|
|
256
260
|
/** UI 框架能力(由 createApp.mount 注入) */
|
|
257
261
|
ui: {
|
|
258
262
|
/** 触发组件重渲染(同步,无参 = 当前组件) */
|
|
259
|
-
render: (ids?: string[]) => void
|
|
260
|
-
/** 异步触发组件重渲染(微任务批处理,无参 = 当前组件) */
|
|
261
|
-
dirty: (ids?: string[]) => void;
|
|
262
|
-
/** 创建响应式状态容器:$.x = val 自动触发 dirty() */
|
|
263
|
-
$: () => Record<string, any>;
|
|
263
|
+
render: (ids?: string[]) => Promise<void>;
|
|
264
264
|
/**
|
|
265
265
|
* AI 对话会话:$ 超集(会话语义 + 工具调用内嵌 + HITL 审批)
|
|
266
266
|
*
|
|
@@ -359,6 +359,23 @@ export interface WfuiContext {
|
|
|
359
359
|
dropProps: Record<string, any>;
|
|
360
360
|
dragProps: Record<string, any>;
|
|
361
361
|
};
|
|
362
|
+
/**
|
|
363
|
+
* 订阅共享状态(render-only 共享原语——design/render-only-plan.md):
|
|
364
|
+
* mount 注册订阅(任何变化 → 自身重渲染),unmount 自动退订;返回 store 本身(活引用)。
|
|
365
|
+
* 参数契约:任何 { subscribe(cb): unsub } 的可订阅对象(createStore 产物 / useChat handle)。
|
|
366
|
+
*
|
|
367
|
+
* ```tsx
|
|
368
|
+
* const store = createStore({ count: 0 })
|
|
369
|
+
* const Comp = (_init, ctx) => {
|
|
370
|
+
* ctx.ui.useExternal(store)
|
|
371
|
+
* return () => h('div', {}, store.state.count)
|
|
372
|
+
* }
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
useExternal: (store: {
|
|
376
|
+
subscribe(cb: () => void): () => void;
|
|
377
|
+
[key: string]: any;
|
|
378
|
+
}) => any;
|
|
362
379
|
/**
|
|
363
380
|
* 可视视口跟踪(visualViewport):键盘弹起/缩放时自动更新 + dirty。
|
|
364
381
|
* 无 visualViewport(桌面)降级 innerHeight。fixed 底部栏防键盘遮挡用。
|
|
@@ -543,23 +560,23 @@ export interface WfuiContext {
|
|
|
543
560
|
};
|
|
544
561
|
/** API 客户端(由 api 中间件注入);options 为 ApiRequestOptions 形状(headers/signal) */
|
|
545
562
|
api?: {
|
|
546
|
-
get: <T =
|
|
563
|
+
get: <T = any>(url: string, options?: {
|
|
547
564
|
headers?: Record<string, string>;
|
|
548
565
|
signal?: AbortSignal;
|
|
549
566
|
}) => Promise<T>;
|
|
550
|
-
post: <T =
|
|
567
|
+
post: <T = any>(url: string, body?: unknown, options?: {
|
|
551
568
|
headers?: Record<string, string>;
|
|
552
569
|
signal?: AbortSignal;
|
|
553
570
|
}) => Promise<T>;
|
|
554
|
-
put: <T =
|
|
571
|
+
put: <T = any>(url: string, body?: unknown, options?: {
|
|
555
572
|
headers?: Record<string, string>;
|
|
556
573
|
signal?: AbortSignal;
|
|
557
574
|
}) => Promise<T>;
|
|
558
|
-
patch: <T =
|
|
575
|
+
patch: <T = any>(url: string, body?: unknown, options?: {
|
|
559
576
|
headers?: Record<string, string>;
|
|
560
577
|
signal?: AbortSignal;
|
|
561
578
|
}) => Promise<T>;
|
|
562
|
-
delete: <T =
|
|
579
|
+
delete: <T = any>(url: string, options?: {
|
|
563
580
|
headers?: Record<string, string>;
|
|
564
581
|
signal?: AbortSignal;
|
|
565
582
|
}) => Promise<T>;
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* 消息累积、工具调用内嵌、HITL 审批、错误恢复、stop/retry/clear。
|
|
6
6
|
*
|
|
7
7
|
* - 协议透明:消费 wf: 事件(design/ai-contract.md),页面不需要知道事件名
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
8
|
+
* - 状态即共享 store(render-only 方案——design/render-only-plan.md):
|
|
9
|
+
* state 是**普通对象**(非 Proxy),变化 → notify() → 订阅者(useExternal)重渲染;
|
|
10
|
+
* handle 带 subscribe——可被 useExternal 订阅(AiChat 等共享会话的子组件)
|
|
11
|
+
* - 就地累积:token 经 state.messages[idx] 就地 append(O(1)/token,
|
|
10
12
|
* 不重建数组;配合 key 稳定引用 → VDOM 只 patch 文本节点)
|
|
11
13
|
* - 工具内嵌:wf:tool_call/progress/result 按 toolCallId/id 聚合到
|
|
12
14
|
* 消息的 toolCalls[],ToolCallCard 直接消费(call/progress/result)
|
|
13
15
|
* - 审批:approval 挂消息,approve() POST approveUrl(协议 §4.5)
|
|
14
|
-
* - 生命周期:dispose()
|
|
16
|
+
* - 生命周期:dispose() 中止流(组件卸载时调用;卸载防泄漏)
|
|
15
17
|
*
|
|
16
18
|
* 核心与框架无关:transport 注入(默认 aiStream),node 直接测。
|
|
17
19
|
*/
|
|
@@ -29,13 +31,15 @@ export interface UiMessage {
|
|
|
29
31
|
id: string;
|
|
30
32
|
role: 'user' | 'assistant';
|
|
31
33
|
content: string;
|
|
34
|
+
/** thinking 模式推理过程(wf:done 下发后挂上,ReasoningBlock 展示) */
|
|
35
|
+
reasoning?: string;
|
|
32
36
|
status: 'streaming' | 'done' | 'error';
|
|
33
37
|
usage?: WfUsage;
|
|
34
38
|
toolCalls?: UiToolCall[];
|
|
35
39
|
approval?: WfApprovalRequest;
|
|
36
40
|
error?: WfError;
|
|
37
41
|
}
|
|
38
|
-
/** useChat
|
|
42
|
+
/** useChat 会话状态(普通对象 + 订阅——不再挂组件 $) */
|
|
39
43
|
export interface UseChatState {
|
|
40
44
|
messages: UiMessage[];
|
|
41
45
|
input: string;
|
|
@@ -44,10 +48,10 @@ export interface UseChatState {
|
|
|
44
48
|
usage: WfUsage | null;
|
|
45
49
|
/** 最近 wf:step(思考/工具指示),done/error 时清空 */
|
|
46
50
|
step: WfStep | null;
|
|
47
|
-
/** 页面自有状态与 chat 状态共处一个
|
|
51
|
+
/** 页面自有状态与 chat 状态共处一个 store(订阅者共享) */
|
|
48
52
|
[key: string]: any;
|
|
49
53
|
}
|
|
50
|
-
/** useChat 操作(挂到
|
|
54
|
+
/** useChat 操作(挂到 state 上的方法;调用不触发渲染,内部 notify 触发) */
|
|
51
55
|
export interface ChatApi {
|
|
52
56
|
/** 发送当前输入:追加 user + assistant 占位 → POST url */
|
|
53
57
|
send: () => void;
|
|
@@ -57,15 +61,16 @@ export interface ChatApi {
|
|
|
57
61
|
retry: () => void;
|
|
58
62
|
/** 清空会话并中止 */
|
|
59
63
|
clear: () => void;
|
|
60
|
-
/** 响应 HITL 审批(协议 §4.5):清卡片 + POST approveUrl */
|
|
61
|
-
approve: (decision: WfApprovalDecision, note?: string) => Promise<void>;
|
|
64
|
+
/** 响应 HITL 审批(协议 §4.5):清卡片 + POST approveUrl;modified 决策带修改后参数 */
|
|
65
|
+
approve: (decision: WfApprovalDecision, note?: string, modifiedArgs?: Record<string, unknown>) => Promise<void>;
|
|
62
66
|
/** 中止流并释放(组件卸载时调用) */
|
|
63
67
|
dispose: () => void;
|
|
64
|
-
/** 内部:订阅会话状态变更(AiChat 等共享 $ 的子组件用;返回退订)。
|
|
65
|
-
* 父组件 dirty 只驱动自身重渲染,共享 handle 的子组件需自行订阅。 */
|
|
66
|
-
__watch?: (cb: () => void) => () => void;
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
+
/** useChat 返回句柄:状态 + 方法 + subscribe(可被 useExternal 订阅——AiChat 等共享子组件) */
|
|
70
|
+
export type UseChatHandle = UseChatState & ChatApi & {
|
|
71
|
+
/** 订阅会话状态变更(任何变化 → cb);返回退订 */
|
|
72
|
+
subscribe: (cb: () => void) => () => void;
|
|
73
|
+
};
|
|
69
74
|
export interface UseChatOptions {
|
|
70
75
|
/** POST 端点(返回 wf: SSE 流) */
|
|
71
76
|
url: string;
|
|
@@ -85,6 +90,6 @@ export type ChatTransport = (url: string, body: unknown, callbacks: AiStreamCall
|
|
|
85
90
|
signal?: AbortSignal;
|
|
86
91
|
headers?: Record<string, string>;
|
|
87
92
|
}) => AiStreamHandle;
|
|
88
|
-
/** UiMessage[] → provider ChatMessage[](剥离 UI
|
|
93
|
+
/** UiMessage[] → provider ChatMessage[](剥离 UI 字段;reasoning 回传——thinking 模式闭环) */
|
|
89
94
|
export declare function toChatMessages(msgs: UiMessage[]): ChatMessage[];
|
|
90
|
-
export declare function createChatSession(state: UseChatState, transport: ChatTransport, options: UseChatOptions): ChatApi;
|
|
95
|
+
export declare function createChatSession(state: UseChatState, transport: ChatTransport, options: UseChatOptions, notify?: () => void): ChatApi;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/build — async 预构建(阶段 1)
|
|
3
|
+
*
|
|
4
|
+
* 核心不变量:
|
|
5
|
+
* - 组件 vnode 构建后 `_render` 已设(工厂只跑一次)
|
|
6
|
+
* - 旧树同位置同类型复用 `_render`(工厂不重跑——组件跨渲染保持内部状态)
|
|
7
|
+
* - 剪枝:已构建 + props 同 + 旧 _child 有值 → 复用旧 _child(renderFn 不重跑)
|
|
8
|
+
* - 兄弟组件并行(工厂同步执行到第一个 await 后并发等待)
|
|
9
|
+
* - **纯函数无 DOM**——构建产物只含 vnode 树
|
|
10
|
+
*/
|
|
11
|
+
import type { VNode, VNodeChild } from '../vnode.ts';
|
|
12
|
+
import type { WfuiContext } from '../types.ts';
|
|
13
|
+
import { type Registry } from './registry.ts';
|
|
14
|
+
/** 组件 props 浅比较(三态 skip 判定) */
|
|
15
|
+
export declare function componentPropsEqual(a: Record<string, any>, b: Record<string, any>): boolean;
|
|
16
|
+
/** 挂载 async 组件:await 工厂(两阶段:外层 mount 一次)→ 设 _render + 分配 id + childCtx */
|
|
17
|
+
export declare function mountAsyncComponent(vnode: VNode, ctx: WfuiContext, reg: Registry, opts?: {
|
|
18
|
+
reuse?: VNode;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
renderFn: (props: VNode['props']) => VNode | null;
|
|
21
|
+
childCtx: WfuiContext;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* 递归展开组件树(async):await 工厂 → renderFn → 递归子树。**零 DOM**。
|
|
25
|
+
*
|
|
26
|
+
* - 组件节点保留在树上(挂 `_render` + `_child`)——$ dirty 精准刷新锚点不丢
|
|
27
|
+
* - 兄弟组件 Promise.all 并行
|
|
28
|
+
* - 旧树对照(oldInput):同位置同类型组件复用旧 `_render`;同 props + 旧 _child 有值
|
|
29
|
+
* 复用旧 `_child`(renderFn 不重跑——三态 skip 语义前置)
|
|
30
|
+
* - 原地 mutate vnode(_render/_child)——引用保持
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildVNode(input: VNodeChild, ctx: WfuiContext, oldInput?: VNodeChild, reg?: Registry, opts?: {
|
|
33
|
+
force?: boolean;
|
|
34
|
+
}): Promise<VNodeChild>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/diff — 同步 patch(阶段 2)
|
|
3
|
+
*
|
|
4
|
+
* **不变量:diff 只处理已构建树**——组件 vnode 必已 `_render`/`_child`(buildVNode 预构建)。
|
|
5
|
+
* 遇未构建组件 → throw(开发期暴露;生产路径 renderByIds/导航都先 buildVNode await)。
|
|
6
|
+
* 这是第 1 代死循环的根治:diff 永不调用组件工厂、无 resolve 回调、无补全循环。
|
|
7
|
+
*
|
|
8
|
+
* 三态 skip:props 同 + 无 dirty + ctx 版本同 → 复用旧 _child(renderFn 不重跑)。
|
|
9
|
+
*/
|
|
10
|
+
import type { VNodeChild } from '../vnode.ts';
|
|
11
|
+
/** 递归文本/数组归一化(children 数组展开——嵌套数组扁平化,DOM 范围对齐) */
|
|
12
|
+
export declare function normalizeChildren(c: VNodeChild | undefined | null): VNodeChild[];
|
|
13
|
+
export interface PatchCtx {
|
|
14
|
+
browser: any;
|
|
15
|
+
registry: import('./registry.ts').Registry;
|
|
16
|
+
/** 三态 skip 的 dirty 集合(组件 id → true) */
|
|
17
|
+
/** 已渲染组件(skip 判定——记录到 ctx,供 scheduler 清 dirty) */
|
|
18
|
+
rendered?: Set<string>;
|
|
19
|
+
/** 当前 ctx 版本号 */
|
|
20
|
+
ctxVersion?: number;
|
|
21
|
+
getCtxVersion?: (id: string) => number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* patchValue — 同步 diff 单一节点。
|
|
25
|
+
* @returns newInput 的实际 DOM(null = 无 DOM)
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchValue(parent: Node, oldNode: Node | null, oldInput: VNodeChild, newInput: VNodeChild, ctx: PatchCtx): Node | null;
|
|
28
|
+
/** 属性 patch(只设不删语义保持简单;差异删除由 diff 上层处理) */
|
|
29
|
+
export declare function patchProps(el: Element, oldProps: Record<string, any>, newProps: Record<string, any>): void;
|
|
30
|
+
/**
|
|
31
|
+
* patchChildren — 数组 diff。
|
|
32
|
+
* @returns 每个新子项的 DOM 范围(Fragment 展开对齐)
|
|
33
|
+
*/
|
|
34
|
+
export declare function patchChildren(parent: Node, oldInput: VNodeChild | null | undefined, newInput: VNodeChild | null | undefined, ctx: PatchCtx, oldRange?: Node[]): (Node | null)[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/hydration — 游标收养(SSR HTML → 客户端,不重建 DOM)
|
|
3
|
+
*
|
|
4
|
+
* 与第 1 代 hydration 的区别:**先 buildVNode 预构建**(await 工厂——组件已 resolve、
|
|
5
|
+
* _child 已展开)→ 再游标收养(只接线属性/事件/ref——不重跑 renderFn/工厂)。
|
|
6
|
+
*
|
|
7
|
+
* 游标不变量:元素/文本 VNode 恰好消耗一个游标节点;组件/Fragment/数组透明;
|
|
8
|
+
* null 消耗零;创建时 insertBefore(游标) 且游标不动;收养/替换时游标前进。
|
|
9
|
+
*/
|
|
10
|
+
import type { VNode } from '../vnode.ts';
|
|
11
|
+
import type { WfuiContext } from '../types.ts';
|
|
12
|
+
import { mountAsyncComponent } from './build.ts';
|
|
13
|
+
/** Hydration 入口:buildVNode 预构建(await 工厂)→ 游标收养 */
|
|
14
|
+
export declare function hydrateVNode(container: Element, vnode: VNode, ctx: WfuiContext): Promise<void>;
|
|
15
|
+
/** 给组件 vnode 分配 id(hydration 需要——注册表定位) */
|
|
16
|
+
export declare function ensureHydrationId(vnode: VNode, ctx: WfuiContext): void;
|
|
17
|
+
export { mountAsyncComponent };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom — 全新虚拟 DOM 引擎(第 2 代)
|
|
3
|
+
*
|
|
4
|
+
* 设计初衷(修复第 1 代死循环/动态挂载问题):
|
|
5
|
+
* - 组件 vnode 进入 diff 前**必须已构建**(`_render` 已设)——diff 永不调用组件工厂
|
|
6
|
+
* - 构建(buildVNode)是 async 的,统一在渲染入口完成(首帧 / renderByIds / 导航)
|
|
7
|
+
* - 动态挂载组件在 buildVNode 阶段被 await(构建完成)→ diff 同步渲染——
|
|
8
|
+
* 无占位、无注释、无「resolve 回调触发补全」(第 1 代死循环根因)
|
|
9
|
+
* - 工厂只跑一次(vnode 级缓存 + 旧树同位置同类型复用)——无无限挂载
|
|
10
|
+
*
|
|
11
|
+
* 两阶段异步组件(唯一签名):
|
|
12
|
+
* async (initProps, ctx) => Promise<(props) => VNode | null>
|
|
13
|
+
*/
|
|
14
|
+
export { buildVNode } from './build.ts';
|
|
15
|
+
export { renderValue } from './render.ts';
|
|
16
|
+
export { patchValue } from './diff.ts';
|
|
17
|
+
export { createScheduler, type Scheduler } from './scheduler.ts';
|
|
18
|
+
export { createRegistry, type Registry } from './registry.ts';
|
|
19
|
+
export { createStore, type ExternalStore } from '../store.ts';
|
|
20
|
+
export { mountRoot, createVdomContext, mountCommand, unmountCommand, createCommandContainer } from './mount.ts';
|
|
21
|
+
export { hydrateVNode } from './hydration.ts';
|
|
22
|
+
export { renderSsr, ssrPage, ssrToString, serializeData, createSsrContext } from './ssr.ts';
|
|
23
|
+
export { uiServe, type UIServeOptions, type UIServeHandle } from './serve.ts';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/mount — 挂载入口(首帧 + ctx/ui 组装)
|
|
3
|
+
*
|
|
4
|
+
* 渲染管线:buildVNode(async 预构建)→ renderValue(同步落地)。
|
|
5
|
+
* ctx.ui:render/setMounting/endMounting——render-only(design/render-only-plan.md)。
|
|
6
|
+
*/
|
|
7
|
+
import type { VNode, VNodeChild, Component } from '../vnode.ts';
|
|
8
|
+
import type { WfuiContext } from '../types.ts';
|
|
9
|
+
import type { BrowserEnv } from '../types.ts';
|
|
10
|
+
import { type Scheduler } from './scheduler.ts';
|
|
11
|
+
import { type Registry } from './registry.ts';
|
|
12
|
+
export interface MountOptions {
|
|
13
|
+
browser: BrowserEnv;
|
|
14
|
+
root: HTMLElement;
|
|
15
|
+
registry?: Registry;
|
|
16
|
+
scheduler?: Scheduler;
|
|
17
|
+
onError?: (e: unknown) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface MountHandle {
|
|
20
|
+
ctx: WfuiContext;
|
|
21
|
+
registry: Registry;
|
|
22
|
+
scheduler: Scheduler;
|
|
23
|
+
/** 挂载根组件 */
|
|
24
|
+
mount(comp: Component | VNodeChild): Promise<void>;
|
|
25
|
+
/** 整树强制重渲染(force——测试辅助/手动刷新:renderFn 重跑 + patch) */
|
|
26
|
+
rerender(): Promise<void>;
|
|
27
|
+
/** 卸载(清理 DOM) */
|
|
28
|
+
unmount(): void;
|
|
29
|
+
}
|
|
30
|
+
export interface VdomContext {
|
|
31
|
+
ctx: WfuiContext;
|
|
32
|
+
registry: Registry;
|
|
33
|
+
scheduler: Scheduler;
|
|
34
|
+
rootUi: any;
|
|
35
|
+
destroyPopupListeners: () => void;
|
|
36
|
+
}
|
|
37
|
+
/** 组装 vdom 渲染上下文(ctx/registry/scheduler/rootUi——含完整 hooks 转发) */
|
|
38
|
+
export declare function createVdomContext(opts: MountOptions): VdomContext;
|
|
39
|
+
export declare function mountRoot(opts: MountOptions): MountHandle;
|
|
40
|
+
/** vdom 命令式挂载:buildVNode(await 工厂)→ renderValue → append + _parentNode */
|
|
41
|
+
export declare function mountCommand(container: HTMLElement, vnode: VNode, ctx: WfuiContext, opts?: {
|
|
42
|
+
onMounted?: () => void;
|
|
43
|
+
}): {
|
|
44
|
+
id: string;
|
|
45
|
+
};
|
|
46
|
+
/** vdom 命令式卸载:ref 清理 + 卸载钩子 + 容器移除 */
|
|
47
|
+
export declare function unmountCommand(container: HTMLElement, vnode: VNode | null, ctx: WfuiContext): void;
|
|
48
|
+
/** 创建命令式挂载容器(body 下独立 div) */
|
|
49
|
+
export declare function createCommandContainer(): HTMLDivElement | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/registry — 组件实例注册表(id → vnode)+ 卸载钩子
|
|
3
|
+
*
|
|
4
|
+
* 组件 id:`_wf_0` 自动分配(精准刷新锚点)+ `_customId`(ctx.ui.selfId 注册)。
|
|
5
|
+
* 卸载钩子:hooks(usePopup/useMedia 等)注册清理回调,组件卸载时执行。
|
|
6
|
+
*/
|
|
7
|
+
import type { VNode } from '../vnode.ts';
|
|
8
|
+
export interface Registry {
|
|
9
|
+
idRegistry: Map<string, VNode>;
|
|
10
|
+
/** 卸载钩子(按组件 id 匹配——组件卸载时执行对应清理) */
|
|
11
|
+
unmountHooks: Array<(id: string) => void>;
|
|
12
|
+
nextId(): string;
|
|
13
|
+
}
|
|
14
|
+
export declare function createRegistry(): Registry;
|
|
15
|
+
/** 给组件 vnode 分配 id(注册表写入) */
|
|
16
|
+
export declare function ensureId(reg: Registry, vnode: VNode): string;
|
|
17
|
+
/** 组件卸载回调注册(hooks 清理)——返回退订函数 */
|
|
18
|
+
export declare function onComponentUnmountFor(reg: Registry, hook: (id: string) => void): () => void;
|
|
19
|
+
/** 组件卸载:执行匹配的清理钩子 + 移除注册(vdom 渲染器卸载组件时调用) */
|
|
20
|
+
export declare function cleanupComponent(reg: Registry, id: string): void;
|
|
21
|
+
/** ref 调用错误隔离(用户 ref 抛错不中断渲染/清理管线) */
|
|
22
|
+
export declare function safeCallRef(ref: Function, arg: any, phase: 'mount' | 'cleanup', name?: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* 递归 ref 清理 + 组件卸载(组件 vnode 从树中移除时调用):
|
|
25
|
+
* idRegistry 注销 + 卸载钩子 + _child/props.children 递归 + ref(null) + Portal 容器移除
|
|
26
|
+
*/
|
|
27
|
+
export declare function callRefCleanupFor(input: any, reg: Registry): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/render — DOM 落地(阶段 2 同步)
|
|
3
|
+
*
|
|
4
|
+
* **只处理已构建的组件**(`_render` 已设——buildVNode 已 await 工厂)。
|
|
5
|
+
* 遇未构建组件 → 抛错(开发期暴露 bug;生产路径 renderByIds 先 build 后 patch,
|
|
6
|
+
* 不会出现未构建组件)。
|
|
7
|
+
*/
|
|
8
|
+
import type { VNodeChild } from '../vnode.ts';
|
|
9
|
+
import type { BrowserEnv } from '../types.ts';
|
|
10
|
+
export declare const SVG_TAGS: Set<string>;
|
|
11
|
+
export declare function setProp(el: Element, key: string, value: any): void;
|
|
12
|
+
/** 递归渲染(同步——组件必须已构建) */
|
|
13
|
+
export declare function renderValue(v: VNodeChild, ctx: any, browser?: BrowserEnv): Node | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WfuiContext } from '../types.ts';
|
|
2
|
+
import type { Registry } from './registry.ts';
|
|
3
|
+
export interface Scheduler {
|
|
4
|
+
render(ids?: string[]): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface SchedulerOptions {
|
|
7
|
+
registry: Registry;
|
|
8
|
+
ctx: WfuiContext;
|
|
9
|
+
rootEl?: HTMLElement;
|
|
10
|
+
getCtxVersion?: (id: string) => number;
|
|
11
|
+
onError?: (e: unknown) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createScheduler(opts: SchedulerOptions): Scheduler;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/serve — 第 2 代 uiServe(UIRouter + vdom 引擎)
|
|
3
|
+
*
|
|
4
|
+
* 与第 1 代 serve.ts 的区别(AGENTS.md §4.0 无自动渲染原则):
|
|
5
|
+
* - 渲染管线:buildVNode(async 预构建 await 全部)→ renderValue/patchValue(同步落地)
|
|
6
|
+
* - 调度:vdom scheduler(无 flush 批处理——$ 赋值直接 fire-and-forget 渲染)
|
|
7
|
+
* - 动态挂载:buildVNode 阶段 await(无占位/注释/补全回调)
|
|
8
|
+
*
|
|
9
|
+
* 保留公开 API(uiServe/UIServeOptions/UIServeHandle)——迁移无缝。
|
|
10
|
+
*/
|
|
11
|
+
import type { UIRouter } from '../router.ts';
|
|
12
|
+
import type { WfuiContext } from '../types.ts';
|
|
13
|
+
/** uiServe 选项 */
|
|
14
|
+
export interface UIServeOptions {
|
|
15
|
+
root: string | Element;
|
|
16
|
+
hydrate?: boolean;
|
|
17
|
+
/** loading 模式:不清空 root(信任调用方预置骨架屏 HTML)——首帧原子替换 */
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** serve 句柄 */
|
|
21
|
+
export interface UIServeHandle<C extends object = {}> {
|
|
22
|
+
/** 释放全部资源(监听/渲染状态/注册表) */
|
|
23
|
+
close(): void;
|
|
24
|
+
/** 首帧完成 Promise:await 全部工厂 + DOM 落地后 resolve */
|
|
25
|
+
ready: Promise<void>;
|
|
26
|
+
/** 当前 ctx(调试/测试用)——含 UIRouter ctx 注入的类型扩展 */
|
|
27
|
+
ctx: WfuiContext & C;
|
|
28
|
+
}
|
|
29
|
+
/** uiServe — 绑定唯一根节点 + URL 驱动渲染(vdom 引擎) */
|
|
30
|
+
export declare function uiServe<RC extends object = {}>(router: UIRouter<RC>, options: UIServeOptions): UIServeHandle<RC>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vdom/ssr — 服务端渲染(无 DOM,纯字符串)
|
|
3
|
+
*
|
|
4
|
+
* SSR 与引擎无关(vnode → HTML 字符串)——vdom 独立实现:
|
|
5
|
+
* - renderSsr:await 工厂 → renderFn → 递归(组件签名两阶段 async)
|
|
6
|
+
* - createSsrContext:SSR ctx shim(hooks no-op、$ 普通对象、ctx.data 预取)
|
|
7
|
+
* - ssrPage:router.execute → renderSsr → 完整 HTML(__DATA__ 种子)
|
|
8
|
+
*
|
|
9
|
+
* 渲染期非确定性(Date/Math.random/locale)导致 SSR/hydration mismatch——
|
|
10
|
+
* dev 检测,文档红线(AGENTS.md §3.5 诚实裁剪)。
|
|
11
|
+
*/
|
|
12
|
+
import type { VNodeChild, Component } from '../vnode.ts';
|
|
13
|
+
import type { WfuiContext } from '../types.ts';
|
|
14
|
+
import type { UIRouter } from '../router.ts';
|
|
15
|
+
/** HTML 转义(文本/属性值) */
|
|
16
|
+
export declare function escape(s: string): string;
|
|
17
|
+
/** 渲染 vnode → HTML 字符串(服务端) */
|
|
18
|
+
export declare function renderSsr(input: VNodeChild, ctx: WfuiContext): Promise<string>;
|
|
19
|
+
/** 创建 SSR ctx(serverCtx 注入 + dataStore 预取 + hooks shim) */
|
|
20
|
+
export declare function createSsrContext(serverCtx: any, dataStore: Map<string, unknown>): WfuiContext;
|
|
21
|
+
/** 序列化数据存储 → window.__DATA__ 脚本(防 XSS:转义 <) */
|
|
22
|
+
export declare function serializeData(data: Map<string, unknown>): string;
|
|
23
|
+
export interface SsrPageResult {
|
|
24
|
+
html: string;
|
|
25
|
+
/** __DATA__ 脚本(hydration 种子) */
|
|
26
|
+
dataScript: string;
|
|
27
|
+
/** 完整 HTML(含 dataScript) */
|
|
28
|
+
page: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* SSR 渲染路由页面 → 完整 HTML(Node 无 DOM)。
|
|
32
|
+
* 客户端 uiServe(router, { root, hydrate: true }) 收养。
|
|
33
|
+
*/
|
|
34
|
+
export declare function ssrPage(router: UIRouter, opts: {
|
|
35
|
+
url: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
lang?: string;
|
|
38
|
+
rootId?: string;
|
|
39
|
+
styles?: string[];
|
|
40
|
+
}): Promise<SsrPageResult>;
|
|
41
|
+
/** SSR 渲染组件 → HTML 片段 */
|
|
42
|
+
export declare function ssrToString(Comp: Component, props: Record<string, any>, serverCtx: any, opts?: {
|
|
43
|
+
data?: Map<string, unknown>;
|
|
44
|
+
}): Promise<string>;
|
package/dist/ui-dom/vnode.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* weifuwu/
|
|
2
|
+
* weifuwu/ui-dom VNode — 虚拟 DOM 节点
|
|
3
3
|
*
|
|
4
4
|
* VNode 是纯 JS 对象,不依赖 DOM。组件返回 VNode。
|
|
5
5
|
*
|
|
6
6
|
* h/jsx 由 esbuild JSX 编译调用:
|
|
7
|
-
* --jsxImportSource=weifuwu/
|
|
7
|
+
* --jsxImportSource=weifuwu/ui-dom
|
|
8
8
|
*/
|
|
9
9
|
import type { WfuiContext } from './types.ts';
|
|
10
|
-
export type VNodeType = string | Component<any, any> |
|
|
10
|
+
export type VNodeType = string | Component<any, any> | typeof Fragment | typeof Portal;
|
|
11
11
|
/**
|
|
12
12
|
* VNode 子节点合法值——组件可返回/渲染的多态内容。
|
|
13
13
|
* 递归联合:string/number/VNode/array/null/boolean 任意组合。
|
|
@@ -32,6 +32,8 @@ export interface VNode {
|
|
|
32
32
|
_customId?: string;
|
|
33
33
|
/** 组件输出的 DOM 父节点 */
|
|
34
34
|
_parentNode?: Node;
|
|
35
|
+
/** 父 vnode 引用(动态挂载补全向上找持有组件) */
|
|
36
|
+
_parentVNode?: VNode;
|
|
35
37
|
/** 组件输出的第一个 DOM 节点 */
|
|
36
38
|
_refNode?: Node | null;
|
|
37
39
|
/** 原生 async 组件缓存(vnode 级按实例):in-flight Promise → resolved renderFn(diff 传递继承) */
|
|
@@ -42,53 +44,18 @@ export interface VNode {
|
|
|
42
44
|
_ctxVersion?: number;
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
45
|
-
*
|
|
47
|
+
* 两阶段异步组件(统一签名——weifuwu 只支持这一种形态):
|
|
48
|
+
* async (initProps, ctx) => Promise<renderFn>
|
|
49
|
+
* 外层 = mount(一次,可 await 数据),内层 = render(每次 dirty/props 变化,同步)。
|
|
46
50
|
* P = props 类型(JSX 自动推断),C = 组件依赖的 ctx 注入(如 ApiInjected & RouteInjected)
|
|
47
|
-
*/
|
|
48
|
-
export type Component<P = {}, C extends object = {}> = (initProps: P, ctx: WfuiContext & C) => ((props: P) => VNode | null) | null;
|
|
49
|
-
/**
|
|
50
|
-
* 异步组件(统一签名——与 Component 同参,唯一差别是 async):
|
|
51
|
-
* async (initProps, ctx) => Promise<renderFn | null>
|
|
52
|
-
*
|
|
53
|
-
* 渲染器统一判别「返回值 instanceof Promise」:
|
|
54
|
-
* 客户端:占位 → resolve 后整树重渲染(vnode 级 _asyncDef 按实例缓存)
|
|
55
|
-
* SSR:直接 await(无占位)
|
|
56
|
-
*
|
|
57
|
-
* asyncComponent() 是兼容包装(工厂签名 (ctx),WeakMap 全局一次——代码分割场景)。
|
|
58
|
-
*/
|
|
59
|
-
export type AsyncComponent<C extends object = {}, P = {}> = (initProps: P, ctx: WfuiContext & C) => Promise<Component<P, C> | null>;
|
|
60
|
-
/**
|
|
61
|
-
* 兼容包装:async 工厂(旧签名 (ctx),WeakMap 全局一次——代码分割/昂贵一次性资源)。
|
|
62
|
-
* 统一为原生 async 组件签名 (initProps, ctx) => Promise<Component>,渲染器原生处理。
|
|
63
51
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* const { default: def } = await import('./view.tsx')
|
|
67
|
-
* return def
|
|
68
|
-
* })
|
|
69
|
-
* ```
|
|
52
|
+
* 模式 A(design/async-mode-a-plan.md):主路径 buildVNode await 全部工厂(无占位);
|
|
53
|
+
* 动态挂载兑底占位 + 局部补全。渲染器按「返回值是 Promise」原生判别。
|
|
70
54
|
*/
|
|
71
|
-
export
|
|
72
|
-
/** 判定一个组件类型是否为 async 工厂(asyncComponent 包装过) */
|
|
73
|
-
export declare function isAsyncComponent(type: any): type is AsyncComponent;
|
|
55
|
+
export type Component<P = {}, C extends object = {}> = (initProps: P, ctx: WfuiContext & C) => Promise<((props: P) => VNode | null) | null>;
|
|
74
56
|
export declare const Fragment: unique symbol;
|
|
75
57
|
/** Portal — 将子 VNode 渲染到 document.body 下的独立容器 */
|
|
76
58
|
export declare const Portal: unique symbol;
|
|
77
|
-
/**
|
|
78
|
-
* 占位显示策略组件(原生 async 组件未 resolve 时的占位 vnode)。
|
|
79
|
-
* 渲染时查 ctx 原型链上最近的 Suspense 边界:有 → fallback;无 → null(向后兼容)。
|
|
80
|
-
*/
|
|
81
|
-
export declare const Placeholder: Component;
|
|
82
|
-
/**
|
|
83
|
-
* Suspense 边界(可选):子树内 async 组件占位时显示 fallback。
|
|
84
|
-
* 渲染器对 type === Suspense 的组件在 childCtx 挂 _suspense(Object.create 继承 → 子树可见)。
|
|
85
|
-
* 语义:占位处局部显示 fallback(非 React 整树回滚);无边界时占位为 null。
|
|
86
|
-
*
|
|
87
|
-
* ```tsx
|
|
88
|
-
* h(Suspense, { fallback: h(Spinner) }, h(UserProfile, {}))
|
|
89
|
-
* ```
|
|
90
|
-
*/
|
|
91
|
-
export declare const Suspense: Component;
|
|
92
59
|
/** JSX 类型声明 — 使 TypeScript 理解自定义 JSX 运行时 */
|
|
93
60
|
export declare function jsx(type: VNodeType, props: Record<string, any> | null, key?: string | null): VNode;
|
|
94
61
|
export declare const jsxs: typeof jsx;
|
package/dist/user/token.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 会话令牌(零依赖,node:crypto)
|
|
3
3
|
*
|
|
4
4
|
* access token:HMAC-SHA256 签名的 JWT 形态(base64url),
|
|
5
|
-
* 与 weifuwu/
|
|
5
|
+
* 与 weifuwu/ui-dom 的 auth() 兼容(客户端解码 payload 检查 exp)。
|
|
6
6
|
* refresh token:不透明随机串(256-bit),DB 只存 SHA-256 哈希(可撤销)。
|
|
7
7
|
*/
|
|
8
8
|
/** 签发 JWT access token(HS256) */
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
### 框架级
|
|
106
106
|
| antd | weifuwu | 状态 | 备注 |
|
|
107
107
|
|------|---------|:----:|------|
|
|
108
|
-
| App | `
|
|
108
|
+
| App 引导 | `UIRouter + uiServe` | 🔧 | ui-dom 框架内置(weifuwu/client 已并入 ui-dom) |
|
|
109
109
|
| ConfigProvider | `--wf-*` token + ctx 注入 | 🔧 | client 框架内置 |
|
|
110
110
|
|
|
111
111
|
---
|
package/docs/components-map.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|------|---------|------|-------------|-----------|---------|
|
|
39
39
|
| 输入框 | `<Input label error hint variant>` | Input | Input | Input | weifuwu 自带 label/error/hint 包装(antd 需 Form.Item);`variant="borderless"` 内联编辑 |
|
|
40
40
|
| 文本域 | `<Textarea rows showCount maxLength>` | Input.TextArea | Input textarea | Textarea | weifuwu 独立组件 + 字数统计 |
|
|
41
|
-
| 选择器 | `<Select options searchable multiple>` | Select | Select | Select / Combobox | **searchable + 键盘 ↑↓ + multiple
|
|
41
|
+
| 选择器 | `<Select options searchable multiple>` | Select | Select | Select / Combobox | **searchable + 键盘 ↑↓ + multiple + optgroup 分组已实现**(近 Combobox) |
|
|
42
42
|
| 复选框 | `<Checkbox checked label>` | Checkbox | Checkbox | Checkbox | 等价 |
|
|
43
43
|
| 复选组 | `<CheckboxGroup options value>` | Checkbox.Group | Checkbox 组 | — | 数组受控 + 栅格列数 |
|
|
44
44
|
| 单选 | `<RadioGroup options value>` | Radio.Group | Radio | RadioGroup | 等价 |
|
|
@@ -115,6 +115,10 @@
|
|
|
115
115
|
| `<CodeBlock code lang>` | 代码块 + 语言标签 + 复制按钮 |
|
|
116
116
|
| `<MessageBubble role status>` | 聊天气泡(独立复用,AiChat 抽取) |
|
|
117
117
|
| `<ToolCallCard>` | AI 工具调用状态机(running/ok/error) |
|
|
118
|
+
| `<JsonSchemaForm schema value onSubmit>` | JSON Schema → 参数输入表单:类型映射 + 必填/范围校验 + 嵌套/数组(工具参数输入面;不支持项告警降级) |
|
|
119
|
+
| `<ReasoningBlock content>` | CoT 推理折叠展示(thinking 模式 reasoning_content;流式脉冲) |
|
|
120
|
+
| `<CitationCard items>` | RAG 引用来源列表:折叠 + 序号/标题/来源/片段/链接 + 溢出 +N |
|
|
121
|
+
| `<SessionList sessions>` | 会话管理:分组(今天/昨天/更早)+ 搜索 + 重命名/删除/新建 + 键盘导航 |
|
|
118
122
|
| `<ApprovalCard>` | HITL 人工审批卡片(pending/approved/rejected/timeout) |
|
|
119
123
|
| `<Editor>` | 富文本编辑器(contentEditable + 工具栏/表格/图片,零依赖) |
|
|
120
124
|
| `<ThemeSwitch>` | 明暗主题切换(CSS 变量驱动,配 `data-theme`) |
|
|
@@ -122,6 +126,19 @@
|
|
|
122
126
|
| `<AspectRatio>` | 宽高比容器(内容填满) |
|
|
123
127
|
| `<CopyButton>` | 复制(clipboard + execCommand 降级 + 成功状态机) |
|
|
124
128
|
|
|
129
|
+
## 第九批:AI 差异化(109 → 111 组件,2026-08)
|
|
130
|
+
|
|
131
|
+
> 三库业务组件覆盖 ~100% 后的差异化期——补 AI **输入层 + 推理展示层**。
|
|
132
|
+
> 完整设计/裁剪见 `design/ai-differentiation-plan.md`。
|
|
133
|
+
|
|
134
|
+
| 组件 | 类型 | 三库等价 | 要点 |
|
|
135
|
+
|------|------|---------|------|
|
|
136
|
+
| **JsonSchemaForm** | 新增 | @rjsf(依赖重,weifuwu 零依赖子集) | JSON Schema(对象子集)→ 参数输入表单:string/number/integer/boolean/enum/object/array 类型映射 + required/范围校验 + 嵌套/数组增删;不支持项($ref/组合 schema)告警降级 |
|
|
137
|
+
| **ReasoningBlock** | 新增 | 三库无 | CoT 推理折叠展示:aria-expanded + 键盘可达 + 流式脉冲 |
|
|
138
|
+
| **AiChat 集成** | 增强 | — | `WfDone.reasoning`(additive,收尾一次性下发)+ use-chat 聚合 + toChatMessages 回传 `reasoning_content`(thinking 模式闭环) |
|
|
139
|
+
|
|
140
|
+
> 协议变更:`wf:done` 新增可选 `reasoning` 字段(向后兼容),见 `design/ai-contract.md` §3.4。
|
|
141
|
+
|
|
125
142
|
## 快速迁移路径(三库 → weifuwu)
|
|
126
143
|
|
|
127
144
|
```tsx
|
|
@@ -193,4 +210,4 @@
|
|
|
193
210
|
| **StatCard ⬆️** | 增强 | antd Statistic.Countdown | countdown 模式:剩余 HH:MM:SS + 结束回调(1s tick + 清理纪律) |
|
|
194
211
|
|
|
195
212
|
> 三库 208 项(antd 84 / EP 74 / shadcn 50)→ 100% 覆盖(业务组件全对应;
|
|
196
|
-
> 框架级 App/ConfigProvider/Teleport/Overlay 由
|
|
213
|
+
> 框架级 App/ConfigProvider/Teleport/Overlay 由 UIRouter+uiServe/createPortal/--wf-* token 内置)。
|