weifuwu 0.83.0 → 0.84.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 +56 -51
- package/dist/components/ChatInput/ChatInput.d.ts +2 -2
- package/dist/components/Confirm/Confirm.d.ts +2 -2
- package/dist/components/Notification/Notification.d.ts +0 -2
- package/dist/components/Toast/Toast.d.ts +0 -2
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +12 -12
- package/dist/components/style.css +17 -0
- package/dist/index.js +2285 -302
- package/dist/ui-dom/Confirm.d.ts +1 -1
- package/dist/ui-dom/Notification.d.ts +1 -1
- package/dist/ui-dom/Toast.d.ts +1 -1
- package/dist/ui-dom/hooks/index.d.ts +0 -1
- package/dist/ui-dom/hooks/popup.d.ts +2 -0
- package/dist/ui-dom/hooks/stable.d.ts +2 -6
- package/dist/ui-dom/index.d.ts +24 -19
- package/dist/ui-dom/index.js +39 -22
- package/dist/ui-dom/jsx-runtime.js +1 -1
- package/dist/ui-dom/popup-tracker.d.ts +1 -1
- package/dist/ui-dom/testing.d.ts +3 -9
- package/dist/ui-dom/testing.js +7 -1
- package/dist/ui-dom/types.d.ts +7 -37
- package/dist/ui-dom/vdom3/README.d.ts +6 -3
- package/dist/ui-dom/vdom3/app.d.ts +20 -0
- package/dist/ui-dom/vdom3/audit.d.ts +13 -0
- package/dist/ui-dom/vdom3/build.d.ts +7 -3
- package/dist/ui-dom/vdom3/commands.d.ts +13 -3
- package/dist/ui-dom/vdom3/comp-index.d.ts +18 -0
- package/dist/ui-dom/vdom3/delegate.d.ts +44 -0
- package/dist/ui-dom/vdom3/events.d.ts +8 -2
- package/dist/ui-dom/vdom3/index.d.ts +5 -3
- package/dist/ui-dom/vdom3/jsx.d.ts +26 -4
- package/dist/ui-dom/vdom3/record.d.ts +3 -0
- package/dist/ui-dom/vdom3/render.d.ts +19 -6
- package/dist/ui-dom/vdom3/replay.d.ts +11 -8
- package/dist/ui-dom/vdom3/root.d.ts +3 -7
- package/dist/ui-dom/vdom3/types.d.ts +102 -70
- package/dist/ui-dom/vdom3/ui.d.ts +3 -30
- package/dist/ui-dom/vdom3.js +11 -10
- package/dist/ui-dom/vnode.d.ts +9 -89
- package/docs/compat-three-library.md +1 -1
- package/docs/components-map.md +1 -1
- package/docs/frontend-middleware.md +59 -53
- package/docs/frontend-ui-dom.md +114 -132
- package/docs/frontend.md +28 -24
- package/docs/realtime.md +21 -21
- package/package.json +1 -1
- package/dist/test/client/setup.d.ts +0 -12
- package/dist/ui-dom/context.d.ts +0 -32
- package/dist/ui-dom/hooks/test/hooks.test.d.ts +0 -1
- package/dist/ui-dom/middleware/serve.d.ts +0 -27
- package/dist/ui-dom/router.d.ts +0 -73
- package/dist/ui-dom/vdom2/audit.d.ts +0 -28
- package/dist/ui-dom/vdom2/build.d.ts +0 -53
- package/dist/ui-dom/vdom2/ctx.d.ts +0 -32
- package/dist/ui-dom/vdom2/dom-trace.d.ts +0 -4
- package/dist/ui-dom/vdom2/events.d.ts +0 -65
- package/dist/ui-dom/vdom2/hydrate.d.ts +0 -21
- package/dist/ui-dom/vdom2/index.d.ts +0 -13
- package/dist/ui-dom/vdom2/kind.d.ts +0 -44
- package/dist/ui-dom/vdom2/lifecycle.d.ts +0 -54
- package/dist/ui-dom/vdom2/mount.d.ts +0 -77
- package/dist/ui-dom/vdom2/patch.d.ts +0 -116
- package/dist/ui-dom/vdom2/registry.d.ts +0 -27
- package/dist/ui-dom/vdom2/render.d.ts +0 -21
- package/dist/ui-dom/vdom2/route.d.ts +0 -29
- package/dist/ui-dom/vdom2/ssr.d.ts +0 -32
- package/dist/ui-dom/vdom2/trace.d.ts +0 -62
- package/dist/ui-dom/vdom2/transform.d.ts +0 -76
- package/dist/ui-dom/vdom2/transitions.d.ts +0 -24
- package/dist/ui-dom/vdom2/x2html.d.ts +0 -26
- package/dist/ui-dom/vdom3/compat.d.ts +0 -20
package/README.md
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
npm install weifuwu # 一个依赖,完整应用栈
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
**本地开发(apps/ 下的应用)**:dev 模式经 `node_modules/weifuwu/*` 软链解析到源码
|
|
10
|
+
(`import 'weifuwu/ui-dom'` 等——esbuild bundle 的模块解析;tsconfig paths 只管类型)。
|
|
11
|
+
新环境 `npm install` 后执行 `node scripts/dev-links.mjs` 创建软链(apps 运行前提)。
|
|
12
|
+
发布时才编译 dist(`node scripts/release.mjs`——dev 零构建)。
|
|
13
|
+
|
|
9
14
|
**定位**:面向需要完整应用栈、又不想缝合多个框架/服务、且重视代码与数据所有权的开发者——独立开发者、小团队、自托管/私有化部署。尤其当应用包含 **认证 + 实时消息 + AI 对话 + 后台管理** 时,weifuwu 把这些「每个应用都要的地基」全部内置为一行 `app.use(...)`。
|
|
10
15
|
|
|
11
16
|
### 五个关键卖点
|
|
@@ -23,7 +28,7 @@ npm install weifuwu # 一个依赖,完整应用栈
|
|
|
23
28
|
| 层 | 入口 | 能力 |
|
|
24
29
|
|----|------|------|
|
|
25
30
|
| 后端 | `weifuwu` | Trie 路由 / 中间件链 / serve / 自研 PG+Redis / SSR / GraphQL / WebSocket |
|
|
26
|
-
| 前端 | `weifuwu/ui-dom` | **
|
|
31
|
+
| 前端 | `weifuwu/ui-dom` | **vdom3 精准事件流引擎**——createRouter(路由)+ createRoot(挂载)+ 事件流(渲染本体:`entity:action` 统一命名、DOM = fold、可回放可断言)+ SSR(事件流序列化);组件=两阶段异步组件 / ctx.params 对齐后端;**weifuwu/components 直接复用**(VNode 契约唯一来源 ui-dom,见 `docs/frontend-ui-dom.md`) |
|
|
27
32
|
| 组件 | `weifuwu/components` | 115 个 HTML 原语组件(表单/表格/弹层/AiChat…),引用 `--wf-*` 主题变量 |
|
|
28
33
|
| 样式 | `weifuwu/layout` | 66 布局原语 + 156 工具类 + 177 主题 Token,零自定义 CSS 文件 |
|
|
29
34
|
| SaaS 地基 | 随包内置 | rateLimit / email / userSystem / messager / queue / ai → `ctx.*` 一行接入 |
|
|
@@ -99,7 +104,7 @@ npm install weifuwu # 一个依赖,完整应用栈
|
|
|
99
104
|
|
|
100
105
|
**async 工厂组件** — `async (initProps, ctx) => (props) => Promise<VNode>`(weifuwu **唯一组件形态**——同步组件已不支持):工厂层声明数据(`await ctx.data.get`)、mount 初始化状态(`let` + `render()`)、render 输出视图。异步在工厂边界与 renderFn,数据经闭包注入,写数据像写同步代码。三条纪律见[核心概念 · async 组件](#核心概念)。
|
|
101
106
|
|
|
102
|
-
**SPA/SSR/Hydration 统一透明** — 同一份路由定义(`
|
|
107
|
+
**SPA/SSR/Hydration 统一透明** — 同一份路由定义(`createRouter`)一个组件三场景自动适配:后端将 vnode 构建为**事件流**(`renderToEvents`)→ `eventsToHtml` 输出完整 HTML + `serializeEvents` 序列化进 `__DATA__`,客户端 `deserializeEvents` + `replay` 收养(DOM = fold(事件流)——零 DOM 猜测、不重建、无闪跳)。`ctx.data.get` 一个 API:SSR 预取 / hydration 命中(不重复请求)/ SPA 触发 fetch。服务端直接用 `.tsx`(`weifuwu/dev` Node loader),前后端同一 JSX 运行时。
|
|
103
108
|
|
|
104
109
|
**AI 是一等公民** — 自研 OpenAI 兼容协议(`docs/ai-contract.md`)+ 零依赖流式客户端 + agent 工具循环 + HITL 人工审批 + embedding 向量化。后端 `ctx.ai` 一个入口:`chat()` / `stream()` / `agent()`(`stream(messages, { emit })` emitter 抽象——事件可接任意通道,`runToResult()` 结构化结果)/ `approve()` / `embed()` / `embedMany()`;前端 `ctx.ui.useChat()`(会话语义)+ `AiChat` 组件(标准对话界面)——流式 token / 工具调用卡 / 审批卡开箱即用,协议对页面完全透明,不用 ai-sdk。
|
|
105
110
|
|
|
@@ -121,16 +126,14 @@ npm install weifuwu # 一个依赖,完整应用栈
|
|
|
121
126
|
|
|
122
127
|
| 模式 | 适用场景 | 后端 | 客户端入口 |
|
|
123
128
|
|------|---------|------|-----------|
|
|
124
|
-
| **SPA** | 应用页(Dashboard、工具、后台) | HTML 外壳 | `
|
|
125
|
-
| **SSR + Hydration** | 内容页(博客、营销,需要 SEO/首屏) | `
|
|
129
|
+
| **SPA** | 应用页(Dashboard、工具、后台) | HTML 外壳 | `createRouter(routes, root, { ctx })` |
|
|
130
|
+
| **SSR + Hydration** | 内容页(博客、营销,需要 SEO/首屏) | `renderToEvents` → `eventsToHtml` + `serializeEvents` | `deserializeEvents` + `replay`(事件流收养) |
|
|
126
131
|
|
|
127
132
|
### 先写共享部分(两种模式都一样)
|
|
128
133
|
|
|
129
134
|
```tsx
|
|
130
|
-
// routes.tsx ——
|
|
131
|
-
import {
|
|
132
|
-
|
|
133
|
-
const app = new UIRouter()
|
|
135
|
+
// routes.tsx —— 路由声明(createRouter——vdom3 事件流引擎)
|
|
136
|
+
import { createRouter, h } from 'weifuwu/ui-dom'
|
|
134
137
|
|
|
135
138
|
// async 组件(原生):await 数据 → 返回视图(外层初始化,内层渲染)
|
|
136
139
|
const Home = async (_init, ctx) => {
|
|
@@ -138,9 +141,7 @@ const Home = async (_init, ctx) => {
|
|
|
138
141
|
return async (props) => <h1>{msg.msg}</h1>
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
export { app }
|
|
144
|
+
export const routes = [{ path: '/', render: () => h(Home, {}) }]
|
|
144
145
|
```
|
|
145
146
|
|
|
146
147
|
### 模式 A:纯 SPA
|
|
@@ -168,47 +169,51 @@ serve(router, { port: 3000 })
|
|
|
168
169
|
```
|
|
169
170
|
|
|
170
171
|
```ts
|
|
171
|
-
// src/client.ts ——
|
|
172
|
-
import {
|
|
173
|
-
import {
|
|
172
|
+
// src/client.ts —— 纯客户端渲染(vdom3 事件流引擎)
|
|
173
|
+
import { createRouter } from 'weifuwu/ui-dom'
|
|
174
|
+
import { routes } from './routes.tsx'
|
|
174
175
|
|
|
175
|
-
|
|
176
|
+
createRouter(routes, document.querySelector('#root')!, { ctx }) // 监听 location → 匹配 → 事件流渲染
|
|
176
177
|
```
|
|
177
178
|
|
|
178
179
|
### 模式 B:SSR + Hydration(内容页/SEO)
|
|
179
180
|
|
|
180
|
-
同一份 `
|
|
181
|
+
同一份 `routes`、同一个组件,差异只在**后端构建事件流 → HTML + 序列化事件,客户端回放**:
|
|
181
182
|
|
|
182
183
|
```ts
|
|
183
|
-
// server.ts ——
|
|
184
|
+
// server.ts —— SSR:vnode → 事件流 → 完整 HTML + __DATA__(DOM = fold 不变量)
|
|
184
185
|
import { serve, Router, ui, cors } from 'weifuwu'
|
|
185
|
-
import {
|
|
186
|
-
import {
|
|
186
|
+
import { renderToEvents, eventsToHtml, serializeEvents } from 'weifuwu/ui-dom'
|
|
187
|
+
import { routes } from './routes.tsx'
|
|
187
188
|
|
|
188
189
|
const router = new Router()
|
|
189
190
|
router.use(cors())
|
|
190
191
|
router.use(ui())
|
|
191
192
|
|
|
192
|
-
// 路由级 SSR
|
|
193
|
-
// → 完整 HTML + __DATA__ + bundle/styles 引用(无需手写页面 handler)
|
|
193
|
+
// 路由级 SSR:匹配共享 routes → 构建 vnode → 事件流 → HTML + __DATA__ 序列化
|
|
194
194
|
router.get('*', async (req, ctx) => {
|
|
195
|
-
const
|
|
196
|
-
|
|
195
|
+
const def = routes.find((r) => r.path === (req.url ?? '/'))
|
|
196
|
+
const events = await renderToEvents(def?.render({}) ?? null) // 事件流(服务端真 fetch 数据)
|
|
197
|
+
const page = `<!doctype html><html><body><div id="root">${eventsToHtml(events)}</div>
|
|
198
|
+
<script>window.__DATA__=${serializeEvents(events)}</script>
|
|
199
|
+
<script src="/static/app.js"></script></body></html>`
|
|
200
|
+
return ctx.ui.html.unsafe(page)
|
|
197
201
|
})
|
|
198
202
|
|
|
199
203
|
router.get('/static/app.js', (req, ctx) => ctx.ui.js('./src/client.ts'))
|
|
200
|
-
router.get('/static/style.css', (req, ctx) => ctx.ui.css('./src/style.css'))
|
|
201
204
|
router.get('/api/hello', () => Response.json({ msg: 'world' }))
|
|
202
205
|
|
|
203
206
|
serve(router, { port: 3000 })
|
|
204
207
|
```
|
|
205
208
|
|
|
206
209
|
```ts
|
|
207
|
-
// src/client.ts ——
|
|
208
|
-
import {
|
|
209
|
-
import {
|
|
210
|
+
// src/client.ts —— 事件流回放收养(deserializeEvents + replay——零 DOM 猜测)
|
|
211
|
+
import { createRouter, deserializeEvents, replay } from 'weifuwu/ui-dom'
|
|
212
|
+
import { routes } from './routes.tsx'
|
|
210
213
|
|
|
211
|
-
|
|
214
|
+
const root = document.querySelector('#root')!
|
|
215
|
+
replay(deserializeEvents((window as any).__DATA__), root) // 收养服务端 HTML(无闪跳)
|
|
216
|
+
createRouter(routes, root) // 之后正常交互渲染
|
|
212
217
|
```
|
|
213
218
|
|
|
214
219
|
### 启动(两种模式都一样)
|
|
@@ -274,7 +279,7 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
274
279
|
</script>
|
|
275
280
|
|
|
276
281
|
<script type="module">
|
|
277
|
-
import {
|
|
282
|
+
import { createRouter, h } from 'weifuwu/ui-dom'
|
|
278
283
|
import { Card, Button, Badge } from 'weifuwu/components'
|
|
279
284
|
|
|
280
285
|
// 组件 = async (initProps, ctx) => (props) => Promise<VNode>(render-only:改状态后 ctx.ui.render())
|
|
@@ -299,10 +304,11 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
299
304
|
)
|
|
300
305
|
}
|
|
301
306
|
|
|
302
|
-
// 路由 +
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
307
|
+
// 路由 + 渲染(vdom3 事件流引擎:createRouter——监听 location → 匹配 → 事件流落地)
|
|
308
|
+
createRouter(
|
|
309
|
+
[{ path: '/', render: () => h(Counter, {}) }],
|
|
310
|
+
document.querySelector('#root')!,
|
|
311
|
+
)
|
|
306
312
|
</script>
|
|
307
313
|
</body>
|
|
308
314
|
</html>
|
|
@@ -314,7 +320,7 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
314
320
|
|
|
315
321
|
| 资源 | CDN 地址 | 说明 |
|
|
316
322
|
|------|---------|------|
|
|
317
|
-
| `weifuwu/ui-dom` | `https://unpkg.com/weifuwu@latest/dist/ui-dom/index.js` | 前端运行时(
|
|
323
|
+
| `weifuwu/ui-dom` | `https://unpkg.com/weifuwu@latest/dist/ui-dom/index.js` | 前端运行时(createRouter, createRoot, h, 事件流, 状态管理等) |
|
|
318
324
|
| `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/index.js` | 115 个 UI 组件(Button, Card, Table, Modal, Icon 等) |
|
|
319
325
|
| `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/style.css` | 组件 CSS + 177 个主题 Token + 66 个布局原语 + 156 个工具类 |
|
|
320
326
|
| 独立布局系统 | `https://unpkg.com/weifuwu@latest/dist/layout/weifuwu-layout.css` | 仅 CSS 布局,不依赖 JS |
|
|
@@ -334,8 +340,8 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
334
340
|
| `weifuwu` | **redis** | Redis 客户端(自研 RESP2 协议)→ `ctx.redis` | Router, REDIS_URL |
|
|
335
341
|
| `weifuwu/db` | **Memory 实现** | `createMemorySql()` / `MemoryRedis`——生产契约黑盒实现(开发/测试/单实例零数据库);`MemoryRedisServer`/`MemoryPostgresServer`——进程内线协议服务器(协议测试零 docker) | — |
|
|
336
342
|
| `weifuwu` | **ui** | SSR 渲染 + esbuild JS/CSS 动态编译 → `ctx.ui` | Router |
|
|
337
|
-
| `weifuwu/ui-dom` | **
|
|
338
|
-
| `weifuwu/ui-dom` | **
|
|
343
|
+
| `weifuwu/ui-dom` | **createRouter** | vdom3 路由:RouteDef[] + 中间件面 ctx 注入(对齐后端 `app.use`)——监听 location → 匹配 → 事件流渲染 | — |
|
|
344
|
+
| `weifuwu/ui-dom` | **SSR(事件流形态)** | `renderToEvents` → `eventsToHtml` + `serializeEvents`(DOM = fold——客户端 `replay` 收养) | Router, ui |
|
|
339
345
|
| `weifuwu` | **rateLimit** | 限流中间件(fixed/sliding,redis 多实例原子)→ `ctx.limit` | Router, redis |
|
|
340
346
|
| `weifuwu` | **email** | 邮件发送(Resend/SMTP 自研/自定义适配器)→ `ctx.email` | Router |
|
|
341
347
|
| `weifuwu` | **userSystem** | 用户系统(scrypt 密码哈希 + 混合会话 + 多租户感知)→ `ctx.user` / `ctx.auth` / `ctx.tenantId` + `/api/auth/*` | Router, postgres |
|
|
@@ -349,14 +355,13 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
349
355
|
| `weifuwu` | **ok / badRequest / …** | HTTP 响应辅助函数(ok/badRequest/... 等 12 个) | — |
|
|
350
356
|
| `weifuwu` | **parseBody** | JSON 请求体安全解析 | — |
|
|
351
357
|
| Router 方法 | **app.graphql()** | GraphQL 端点(支持 GraphiQL),Router 实例方法(无需单独 import) | Router |
|
|
352
|
-
| `weifuwu/ui-dom` | **
|
|
353
|
-
| `weifuwu/ui-dom` |
|
|
354
|
-
| `weifuwu/ui-dom/vdom3` | **vdom3 引擎**(转正中) | 下一代渲染(vnode + stream——事件流可回放/可逆);`createRouter` + `createRoot` | vdom3 |
|
|
358
|
+
| `weifuwu/ui-dom` | **createRouter / createRoot** | vdom3 事件流引擎:路由(RouteDef[] + ctx 注入)/ 挂载(组件树)——渲染全链路 `entity:action` 事件流(DOM = fold——可记录/回放/断言);**事件代理**(监听 O(1)——零重绑——统一注册表);**app 节点**(多应用加载——registerApp) | — |
|
|
359
|
+
| `weifuwu/ui-dom` | **事件流原语** | `stream` / `ev` / `evKey` / `replay` / `eventsOf` / `expectEventSequence`——渲染可观测/可断言 | — |
|
|
355
360
|
| `weifuwu/ui-dom` | **async 组件** | async 函数即组件(与同步同签名);数据走 ctx.data 三场景(三条纪律见[核心概念](#核心概念)) | — |
|
|
356
|
-
| `weifuwu/ui-dom` | **ctx.data** | 数据管道:SSR 预取 / hydration 命中 / SPA fetch(`ctx.data.get`) |
|
|
361
|
+
| `weifuwu/ui-dom` | **ctx.data** | 数据管道:SSR 预取 / hydration 命中 / SPA fetch(`ctx.data.get`) | — |
|
|
357
362
|
| `weifuwu/ui-dom` | **api / auth / ws** | HTTP 客户端 / 认证 / WebSocket 中间件 | — |
|
|
358
363
|
| `weifuwu/ui-dom` | **i18n** | 国际化中间件(运行时切换语言) | — |
|
|
359
|
-
| `weifuwu/ui-dom` | **
|
|
364
|
+
| `weifuwu/ui-dom` | **renderToEvents / eventsToHtml / serializeEvents** | 服务端渲染:vnode → 事件流 → HTML + `__DATA__` 序列化(客户端 `replay` 收养) | — |
|
|
360
365
|
| `weifuwu/ui-dom` | **useChat / AiChat 原语** | AI 会话(流式/工具调用/HITL) | — |
|
|
361
366
|
| `weifuwu/ui-dom` | **事件原语** | `usePopup`(统一弹窗能力层)/ `usePresence` / `useInView` / `useScrollPosition` / `useGlobalKey` / `useDrag` / `useDragDrop` / `useAnimationEnd` / `useTween` / `useReducedMotion`(浏览器事件/动画统一入口,见 [docs/mobile.md](docs/mobile.md)) | — |
|
|
362
367
|
| `weifuwu/components` | **113 个组件** | Button/Table/Modal/Confirm/Toast/... + `confirm()` / `toast()` 命令式中间件 | weifuwu/ui-dom |
|
|
@@ -371,7 +376,7 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
371
376
|
| 任务 | 用 | 位置 |
|
|
372
377
|
|------|-----|------|
|
|
373
378
|
| 起 HTTP 服务 + 路由 | `serve(app)` + `new Router()` + `app.get/post/...` | [docs/server.md](docs/server.md) |
|
|
374
|
-
| 渲染页面(SPA / SSR
|
|
379
|
+
| 渲染页面(SPA / SSR) | `createRouter(routes, root, { ctx })`;SSR = `renderToEvents` → `eventsToHtml` + `replay`(事件流形态) | [docs/frontend-ui-dom.md](docs/frontend-ui-dom.md) · [docs/frontend.md](docs/frontend.md) |
|
|
375
380
|
| 数据持久化 | `postgres()` → `` ctx.sql`SELECT *` `` · `redis()` → `ctx.redis` · **`sql.query`**(Query Language AST 双后端) | [docs/data.md](docs/data.md) |
|
|
376
381
|
| 零数据库开发/测试 | `createMemorySql()` / `MemoryRedis`——契约同真库、替换成本为零 | [docs/data.md](docs/data.md) |
|
|
377
382
|
| 数据管道(SSR 预取/hydration/SPA) | `ctx.data.get(key)` + async 组件 | [docs/frontend.md](docs/frontend.md) |
|
|
@@ -399,7 +404,7 @@ cd apps/agent-platform && npm run seed && npm run dev
|
|
|
399
404
|
| **UIHandler**(路由) | `async (location, ctx) => VNode` | ✅ 整体 | 每次路由变化执行 |
|
|
400
405
|
| **Component**(唯一形态) | `async (initProps, ctx) => (props) => Promise<VNode>` | ✅ 工厂 + renderFn | mount 一次 + render 每次;同步组件已不支持(类型强制 Promise) |
|
|
401
406
|
|
|
402
|
-
异步只在两个边界——路由 handler(整页)和组件工厂(数据声明)+ renderFn(强制异步)。渲染器按「返回值 instanceof Promise」统一判别:主路径 `buildVNode` async 预构建(await 全部工厂,兄弟并行)→ 原子落地(无**中间态**占位、无补全回调——注意:数组内 false/null 的静态诊断占位 `<!--wf-hole-->` 是另一回事,见「VDOM 输出透明」);运行时首次挂载的 async 组件同样在 buildVNode 阶段 await
|
|
407
|
+
异步只在两个边界——路由 handler(整页)和组件工厂(数据声明)+ renderFn(强制异步)。渲染器按「返回值 instanceof Promise」统一判别:主路径 `buildVNode` async 预构建(await 全部工厂,兄弟并行)→ 原子落地(无**中间态**占位、无补全回调——注意:数组内 false/null 的静态诊断占位 `<!--wf-hole-->` 是另一回事,见「VDOM 输出透明」);运行时首次挂载的 async 组件同样在 buildVNode 阶段 await;骨架屏:预置骨架屏 HTML → 首帧原子替换 + `handle.ready`。
|
|
403
408
|
### 两阶段组件(新手必读:为什么是两层)
|
|
404
409
|
|
|
405
410
|
组件 = `async (initProps, ctx) => (props) => Promise<VNode>`——**外层 = 初始化(只执行一次,可 await 数据),内层 = 渲染(每次状态/props 变化时执行,强制异步)**。类比:外层是对象的构造函数,内层是它的 render 方法。
|
|
@@ -425,15 +430,15 @@ const Counter = async (_init, ctx) => {
|
|
|
425
430
|
app.get('/users', (req, ctx) => { ctx.sql`SELECT *` })
|
|
426
431
|
// ctx 已注入 ctx.sql
|
|
427
432
|
|
|
428
|
-
前端: const router =
|
|
433
|
+
前端: const router = createRouter(routes, root, { ctx })
|
|
429
434
|
router.use(api({ baseURL: '/api' }))
|
|
430
435
|
router.use(auth())
|
|
431
436
|
router.get('/users', async (location, ctx) => h(UsersPage, {}))
|
|
432
|
-
|
|
437
|
+
// createRouter = 监听 location → 匹配 → 事件流渲染落地
|
|
433
438
|
// ctx 已注入 ctx.api, ctx.auth
|
|
434
439
|
```
|
|
435
440
|
|
|
436
|
-
前端 req = `window.location
|
|
441
|
+
前端 req = `window.location`,res = `VNode`,`createRouter` = 事件流渲染落地——与后端 `Request → Response`、`serve(router)` 完全同构。
|
|
437
442
|
|
|
438
443
|
### 状态管理
|
|
439
444
|
|
|
@@ -478,10 +483,10 @@ const UserProfile = async (_init, ctx) => {
|
|
|
478
483
|
| | SPA | SSR + Hydration |
|
|
479
484
|
|---|---|---|
|
|
480
485
|
| 适用 | 应用页(后台、工具、Dashboard) | 内容页(博客、营销,需要 SEO/首屏) |
|
|
481
|
-
| 后端 | HTML 外壳 | `
|
|
482
|
-
| 客户端 | `
|
|
486
|
+
| 后端 | HTML 外壳 | `renderToEvents` → `eventsToHtml` + `serializeEvents`(完整 HTML + `__DATA__`) |
|
|
487
|
+
| 客户端 | `createRouter(routes, root, { ctx })` | `deserializeEvents` + `replay`(事件流收养——DOM = fold) |
|
|
483
488
|
|
|
484
|
-
**怎么选**:默认 SPA;需要 SEO 或首屏即内容时用 SSR
|
|
489
|
+
**怎么选**:默认 SPA;需要 SEO 或首屏即内容时用 SSR。两种模式可混合——SSR 走共享 routes,未匹配走普通 handler。
|
|
485
490
|
|
|
486
491
|
### Closeable 接口
|
|
487
492
|
|
|
@@ -510,8 +515,8 @@ README 只保留入门内容(设计理念 / 快速开始 / 核心概念 / 模
|
|
|
510
515
|
|
|
511
516
|
| 文档 | 内容 |
|
|
512
517
|
|------|------|
|
|
513
|
-
| [docs/frontend.md](docs/frontend.md) | 前端核心:应用引导(
|
|
514
|
-
| [docs/frontend-ui-dom.md](docs/frontend-ui-dom.md) | **ui-dom**:
|
|
518
|
+
| [docs/frontend.md](docs/frontend.md) | 前端核心:应用引导(createRouter/createRoot——vdom3 事件流)/ 组件模型 / 异步组件 / 状态管理 / 条件与列表 / ref / 类型(weifuwu/client 已并入 ui-dom) |
|
|
519
|
+
| [docs/frontend-ui-dom.md](docs/frontend-ui-dom.md) | **ui-dom**:vdom3 精准事件流引擎(createRouter/createRoot + 事件流)——components 复用 + SSR(事件流形态)(前端唯一运行时) |
|
|
515
520
|
| [docs/frontend-middleware.md](docs/frontend-middleware.md) | 前端中间件:router / api / auth / ws / i18n / ErrorBoundary / confirm / toast / ScrollLock / extendCtx |
|
|
516
521
|
| [docs/components.md](docs/components.md) | 组件库(113 个组件 + 使用示例 + 组件列表) |
|
|
517
522
|
| [docs/layout.md](docs/layout.md) | 布局系统:66 个布局原语 + 156 个工具类 + 177 个主题 Token |
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 使用两阶段模型 + render-only。状态:内部 keyword(useControlledInput Map 缓存)。
|
|
16
16
|
*/
|
|
17
17
|
import type { Component, VNode } from '../../ui-dom/vnode.ts';
|
|
18
|
-
import type {
|
|
18
|
+
import type { V3Ui } from '../../ui-dom/vdom3/types.ts';
|
|
19
19
|
export interface ChatInputLabels {
|
|
20
20
|
send: string;
|
|
21
21
|
stop: string;
|
|
@@ -57,5 +57,5 @@ export interface ChatInputControl {
|
|
|
57
57
|
setValue: (v: string) => void;
|
|
58
58
|
}
|
|
59
59
|
export declare const ChatInput: Component<ChatInputProps, {
|
|
60
|
-
ui:
|
|
60
|
+
ui: V3Ui;
|
|
61
61
|
}>;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* 命令式:confirm() 中间件注入 ctx.confirm(),返回 Promise<boolean>。
|
|
6
6
|
*/
|
|
7
7
|
import type { Component } from '../../ui-dom/vnode.ts';
|
|
8
|
-
import type { AppMiddleware } from '../../ui-dom/types.ts';
|
|
9
8
|
export interface ConfirmProps {
|
|
10
9
|
open?: boolean;
|
|
11
10
|
title?: string;
|
|
@@ -20,6 +19,8 @@ export interface ConfirmProps {
|
|
|
20
19
|
maskClosable?: boolean;
|
|
21
20
|
onConfirm?: () => void;
|
|
22
21
|
onCancel?: () => void;
|
|
22
|
+
/** Modal 关闭回调(Escape/遮罩——onCancel 缺省时兜底——命令式兼容) */
|
|
23
|
+
onClose?: () => void;
|
|
23
24
|
}
|
|
24
25
|
/** 命令式 ctx.confirm 的选项(ConfirmProps 的子集) */
|
|
25
26
|
export interface ConfirmOptions {
|
|
@@ -40,5 +41,4 @@ export interface ConfirmInjected {
|
|
|
40
41
|
* 每次调用挂载 Confirm 组件到独立容器(mountVNode),
|
|
41
42
|
* resolve 后 callRefCleanup 清理(含 Modal 的 portal DOM)+ 移除容器。
|
|
42
43
|
*/
|
|
43
|
-
export declare function confirm(): AppMiddleware<{}, ConfirmInjected>;
|
|
44
44
|
export declare const Confirm: Component<ConfirmProps>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Component } from '../../ui-dom/vnode.ts';
|
|
2
|
-
import type { AppMiddleware } from '../../ui-dom/types.ts';
|
|
3
2
|
export type NotificationType = 'success' | 'error' | 'info' | 'warning';
|
|
4
3
|
export type NotificationPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
5
4
|
export interface NotificationItem {
|
|
@@ -67,4 +66,3 @@ export interface NotificationInjected {
|
|
|
67
66
|
}) => void;
|
|
68
67
|
};
|
|
69
68
|
}
|
|
70
|
-
export declare function notification(opts?: NotificationOptions): AppMiddleware<{}, NotificationInjected>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Component } from '../../ui-dom/vnode.ts';
|
|
2
|
-
import type { AppMiddleware } from '../../ui-dom/types.ts';
|
|
3
2
|
export type ToastType = 'success' | 'error' | 'info' | 'warning';
|
|
4
3
|
export type ToastPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center';
|
|
5
4
|
export interface ToastItem {
|
|
@@ -40,4 +39,3 @@ export interface ToastInjected {
|
|
|
40
39
|
onClick: () => void;
|
|
41
40
|
}) => void;
|
|
42
41
|
}
|
|
43
|
-
export declare function toast(opts?: ToastOptions): AppMiddleware<{}, ToastInjected>;
|
|
@@ -25,9 +25,9 @@ export { Table } from './Table/Table.ts';
|
|
|
25
25
|
export type { TableProps, TableColumn } from './Table/Table.ts';
|
|
26
26
|
export { Modal } from './Modal/Modal.ts';
|
|
27
27
|
export type { ModalProps } from './Modal/Modal.ts';
|
|
28
|
-
export { Confirm
|
|
28
|
+
export { Confirm } from './Confirm/Confirm.ts';
|
|
29
29
|
export type { ConfirmProps, ConfirmOptions } from './Confirm/Confirm.ts';
|
|
30
|
-
export { Toast
|
|
30
|
+
export { Toast } from './Toast/Toast.ts';
|
|
31
31
|
export type { ToastProps, ToastItem, ToastType, ToastPosition, ToastOptions, ToastInjected } from './Toast/Toast.ts';
|
|
32
32
|
export { Alert } from './Alert/Alert.ts';
|
|
33
33
|
export type { AlertProps, AlertVariant } from './Alert/Alert.ts';
|
|
@@ -157,7 +157,7 @@ export { Affix } from './Affix/Affix.ts';
|
|
|
157
157
|
export type { AffixProps } from './Affix/Affix.ts';
|
|
158
158
|
export { HoverCard } from './HoverCard/HoverCard.ts';
|
|
159
159
|
export type { HoverCardProps, HoverCardPosition } from './HoverCard/HoverCard.ts';
|
|
160
|
-
export { Notification
|
|
160
|
+
export { Notification } from './Notification/Notification.ts';
|
|
161
161
|
export type { NotificationProps, NotificationItem, NotificationType, NotificationPosition, NotificationOptions, NotificationInjected } from './Notification/Notification.ts';
|
|
162
162
|
export { ContextMenu } from './ContextMenu/ContextMenu.ts';
|
|
163
163
|
export type { ContextMenuProps, ContextMenuItem } from './ContextMenu/ContextMenu.ts';
|