weifuwu 0.88.0 → 0.89.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 (389) hide show
  1. package/README.md +111 -135
  2. package/dist/client/components/ActionSheet/ActionSheet.d.ts +1 -1
  3. package/dist/client/components/AiChat/AiChat.d.ts +3 -4
  4. package/dist/client/components/ChatInput/ChatInput.d.ts +5 -0
  5. package/dist/client/components/CitationCard/CitationCard.d.ts +1 -1
  6. package/dist/client/components/Command/Command.d.ts +1 -1
  7. package/dist/client/components/ContextMenu/ContextMenu.d.ts +1 -1
  8. package/dist/client/components/DatePicker/DatePicker.d.ts +1 -1
  9. package/dist/client/components/Dropdown/Dropdown.d.ts +1 -1
  10. package/dist/client/components/Editor/model/html.d.ts +10 -0
  11. package/dist/client/components/ImageCropper/ImageCropper.d.ts +6 -2
  12. package/dist/client/components/Menu/Menu.d.ts +1 -1
  13. package/dist/client/components/NavMenu/NavMenu.d.ts +1 -1
  14. package/dist/client/components/Popconfirm/Popconfirm.d.ts +1 -1
  15. package/dist/client/components/SlideCanvas/SlideCanvas.d.ts +1 -1
  16. package/dist/client/components/Tour/Tour.d.ts +1 -1
  17. package/dist/client/components/TreeSelect/TreeSelect.d.ts +1 -1
  18. package/dist/client/components/index.js +41 -41
  19. package/dist/client/components/style.css +298 -644
  20. package/dist/client/layout/weifuwu-layout.css +286 -640
  21. package/dist/client/vdom/commands.d.ts +4 -1
  22. package/dist/client/vdom/core/diff/attrs.d.ts +10 -2
  23. package/dist/client/vdom/core/field/attributes.d.ts +12 -0
  24. package/dist/client/vdom/core/field/events.d.ts +8 -0
  25. package/dist/client/vdom/core/field/input-sync.d.ts +14 -0
  26. package/dist/client/vdom/core/field/props.d.ts +13 -1
  27. package/dist/client/vdom/core/field/ref.d.ts +6 -0
  28. package/dist/client/vdom/core/field/style.d.ts +7 -1
  29. package/dist/client/vdom/core/node/component.d.ts +3 -4
  30. package/dist/client/vdom/core/node/native.d.ts +6 -1
  31. package/dist/client/vdom/core/patch/index.d.ts +24 -1
  32. package/dist/client/vdom/core/patch/processors.d.ts +8 -1
  33. package/dist/client/vdom/core/protocol.d.ts +45 -0
  34. package/dist/client/vdom/core/router.d.ts +7 -0
  35. package/dist/client/vdom/core/ssr/absorb.d.ts +31 -4
  36. package/dist/client/vdom/core/transform/index.d.ts +6 -6
  37. package/dist/client/vdom/core/v2/cycle.d.ts +54 -0
  38. package/dist/client/vdom/core/v2/diff.d.ts +80 -0
  39. package/dist/client/vdom/core/v2/integrate.d.ts +24 -0
  40. package/dist/client/vdom/core/v2/render.d.ts +28 -0
  41. package/dist/client/vdom/core/v2/schedule.d.ts +33 -0
  42. package/dist/client/vdom/core/v2/serve.d.ts +4 -0
  43. package/dist/client/vdom/core/v2/spy.d.ts +21 -0
  44. package/dist/client/vdom/core/v2/ssr.d.ts +14 -0
  45. package/dist/client/vdom/core/vnode.d.ts +9 -6
  46. package/dist/client/vdom/dev/error-counter.d.ts +27 -0
  47. package/dist/client/vdom/dev/render-health.d.ts +60 -0
  48. package/dist/client/vdom/hooks/chat.d.ts +32 -5
  49. package/dist/client/vdom/hooks/drag-media.d.ts +3 -1
  50. package/dist/client/vdom/hooks/env.d.ts +32 -0
  51. package/dist/client/vdom/hooks/popup-manager.d.ts +21 -0
  52. package/dist/client/vdom/hooks/popup.d.ts +4 -1
  53. package/dist/client/vdom/hooks/stable.d.ts +4 -0
  54. package/dist/client/vdom/hooks/use-observable.d.ts +13 -0
  55. package/dist/client/vdom/index.d.ts +14 -6
  56. package/dist/client/vdom/index.js +4 -7
  57. package/dist/client/vdom/jsx-runtime.js +1 -1
  58. package/dist/client/vdom/middlewares/api.d.ts +8 -0
  59. package/dist/client/vdom/middlewares/auth-i18n.d.ts +7 -0
  60. package/dist/client/vdom/middlewares/ws.d.ts +24 -0
  61. package/dist/client/vdom/observable/index.d.ts +13 -0
  62. package/dist/client/vdom/observable/observable.d.ts +26 -0
  63. package/dist/client/vdom/observable/operators.d.ts +76 -0
  64. package/dist/client/vdom/observable/sources.d.ts +54 -0
  65. package/dist/client/vdom/observable/types.d.ts +45 -0
  66. package/dist/client/vdom/store.d.ts +17 -2
  67. package/dist/client/vdom/testing.js +2 -2
  68. package/dist/server/ai/agent.d.ts +18 -0
  69. package/dist/server/ai/client.d.ts +14 -2
  70. package/dist/server/ai/contracts.d.ts +2 -2
  71. package/dist/server/ai/sse.d.ts +6 -0
  72. package/dist/server/core/collect.d.ts +31 -0
  73. package/dist/server/core/error-counter.d.ts +11 -0
  74. package/dist/server/core/hub.d.ts +2 -0
  75. package/dist/server/core/router.d.ts +12 -9
  76. package/dist/server/db/contracts.d.ts +24 -0
  77. package/dist/server/db/memory-redis.d.ts +3 -4
  78. package/dist/server/db/memory-sql.d.ts +20 -4
  79. package/dist/server/db/postgres-server.d.ts +2 -0
  80. package/dist/server/db/query.d.ts +21 -0
  81. package/dist/server/db/redis-server.d.ts +4 -2
  82. package/dist/server/db/sql-parser.d.ts +2 -1
  83. package/dist/server/email/index.d.ts +2 -0
  84. package/dist/server/index.d.ts +4 -1
  85. package/dist/server/index.js +3927 -1984
  86. package/dist/server/messager/index.d.ts +21 -2
  87. package/dist/server/middleware/compress.d.ts +25 -0
  88. package/dist/server/ui/index.d.ts +8 -1
  89. package/dist/server/user/index.d.ts +4 -0
  90. package/dist/shared/router/chain.d.ts +9 -0
  91. package/dist/shared/router/context.d.ts +35 -0
  92. package/dist/shared/router/ctx-fields.d.ts +20 -0
  93. package/dist/shared/router/pipeline.d.ts +70 -0
  94. package/dist/shared/router/trie.d.ts +11 -3
  95. package/dist/shared/router/types.d.ts +23 -0
  96. package/docs/style-guide.md +44 -41
  97. package/package.json +12 -10
  98. package/content/apps/admin.md +0 -55
  99. package/content/apps/agent-platform.md +0 -35
  100. package/content/apps/auth.md +0 -49
  101. package/content/apps/multi.md +0 -44
  102. package/content/apps/todo.md +0 -51
  103. package/content/backend/ai.md +0 -28
  104. package/content/backend/auth.md +0 -20
  105. package/content/backend/email.md +0 -23
  106. package/content/backend/graphql.md +0 -23
  107. package/content/backend/http.md +0 -23
  108. package/content/backend/limit.md +0 -23
  109. package/content/backend/middleware.md +0 -17
  110. package/content/backend/queue.md +0 -23
  111. package/content/backend/redis.md +0 -26
  112. package/content/backend/router.md +0 -23
  113. package/content/backend/schedule.md +0 -23
  114. package/content/backend/sql.md +0 -27
  115. package/content/backend/sse.md +0 -26
  116. package/content/backend/ws.md +0 -26
  117. package/content/capabilities/app-node.md +0 -21
  118. package/content/capabilities/data.md +0 -21
  119. package/content/capabilities/events.md +0 -21
  120. package/content/capabilities/hooks.md +0 -22
  121. package/content/capabilities/i18n.md +0 -21
  122. package/content/capabilities/popup.md +0 -21
  123. package/content/capabilities/render-only.md +0 -23
  124. package/content/capabilities/router.md +0 -22
  125. package/content/capabilities/self-id.md +0 -21
  126. package/content/capabilities/store.md +0 -21
  127. package/content/capabilities/theme.md +0 -21
  128. package/content/capabilities/two-phase.md +0 -21
  129. package/content/components/accordion.md +0 -50
  130. package/content/components/actionsheet.md +0 -46
  131. package/content/components/affix.md +0 -51
  132. package/content/components/aichat.md +0 -57
  133. package/content/components/alert.md +0 -52
  134. package/content/components/alertgroup.md +0 -45
  135. package/content/components/anchor.md +0 -52
  136. package/content/components/app.md +0 -37
  137. package/content/components/approvalcard.md +0 -56
  138. package/content/components/appshell.md +0 -50
  139. package/content/components/aspectratio.md +0 -47
  140. package/content/components/authpage.md +0 -61
  141. package/content/components/autocomplete-v2.md +0 -40
  142. package/content/components/autocomplete.md +0 -56
  143. package/content/components/avatar.md +0 -51
  144. package/content/components/avatargroup.md +0 -47
  145. package/content/components/backtop.md +0 -53
  146. package/content/components/badge.md +0 -55
  147. package/content/components/breadcrumb.md +0 -49
  148. package/content/components/button.md +0 -63
  149. package/content/components/calendar-v2.md +0 -40
  150. package/content/components/calendar.md +0 -50
  151. package/content/components/card.md +0 -58
  152. package/content/components/carousel.md +0 -52
  153. package/content/components/cascader-v2.md +0 -39
  154. package/content/components/cascader.md +0 -54
  155. package/content/components/chart.md +0 -53
  156. package/content/components/chatinput.md +0 -63
  157. package/content/components/checkbox.md +0 -51
  158. package/content/components/checkboxgroup.md +0 -52
  159. package/content/components/citationcard.md +0 -48
  160. package/content/components/codeblock.md +0 -48
  161. package/content/components/codeeditor.md +0 -47
  162. package/content/components/collapse.md +0 -49
  163. package/content/components/colorpicker.md +0 -50
  164. package/content/components/command.md +0 -50
  165. package/content/components/confirm.md +0 -57
  166. package/content/components/contextmenu.md +0 -48
  167. package/content/components/copybutton.md +0 -51
  168. package/content/components/datepicker.md +0 -52
  169. package/content/components/descriptions-v2.md +0 -39
  170. package/content/components/descriptions.md +0 -52
  171. package/content/components/diffview-v2.md +0 -39
  172. package/content/components/diffview.md +0 -50
  173. package/content/components/divider.md +0 -48
  174. package/content/components/drawer.md +0 -55
  175. package/content/components/dropdown.md +0 -53
  176. package/content/components/editor.md +0 -49
  177. package/content/components/emptystate.md +0 -51
  178. package/content/components/exportcsv.md +0 -38
  179. package/content/components/field.md +0 -54
  180. package/content/components/filepreview-office.md +0 -37
  181. package/content/components/filepreview.md +0 -49
  182. package/content/components/filetree.md +0 -56
  183. package/content/components/fileupload-v2.md +0 -39
  184. package/content/components/fileupload.md +0 -57
  185. package/content/components/floatbutton.md +0 -52
  186. package/content/components/form-v2.md +0 -41
  187. package/content/components/form.md +0 -60
  188. package/content/components/grid.md +0 -51
  189. package/content/components/highlight-v2.md +0 -39
  190. package/content/components/highlight.md +0 -46
  191. package/content/components/hovercard.md +0 -51
  192. package/content/components/icon.md +0 -50
  193. package/content/components/imagecropper.md +0 -45
  194. package/content/components/img.md +0 -56
  195. package/content/components/infinitescroll-v2.md +0 -40
  196. package/content/components/infinitescroll.md +0 -54
  197. package/content/components/input.md +0 -65
  198. package/content/components/inputnumber.md +0 -58
  199. package/content/components/inview.md +0 -56
  200. package/content/components/jsonschemaform.md +0 -53
  201. package/content/components/jsonviewer-v2.md +0 -39
  202. package/content/components/jsonviewer.md +0 -49
  203. package/content/components/kanban.md +0 -46
  204. package/content/components/label.md +0 -48
  205. package/content/components/layout.md +0 -54
  206. package/content/components/layoutcontent.md +0 -43
  207. package/content/components/layoutheader.md +0 -43
  208. package/content/components/layoutsider.md +0 -43
  209. package/content/components/link.md +0 -52
  210. package/content/components/list.md +0 -44
  211. package/content/components/loading.md +0 -45
  212. package/content/components/logviewer-v2.md +0 -39
  213. package/content/components/logviewer.md +0 -53
  214. package/content/components/markdown.md +0 -51
  215. package/content/components/markdowneditor.md +0 -47
  216. package/content/components/math.md +0 -42
  217. package/content/components/mentions-v2.md +0 -39
  218. package/content/components/mentions.md +0 -52
  219. package/content/components/menu.md +0 -56
  220. package/content/components/menubar.md +0 -44
  221. package/content/components/messagebubble.md +0 -50
  222. package/content/components/modal.md +0 -59
  223. package/content/components/navmenu.md +0 -47
  224. package/content/components/notification.md +0 -51
  225. package/content/components/pageheader.md +0 -53
  226. package/content/components/pagination.md +0 -49
  227. package/content/components/paragraph.md +0 -45
  228. package/content/components/passwordinput.md +0 -57
  229. package/content/components/pininput-v2.md +0 -39
  230. package/content/components/pininput.md +0 -50
  231. package/content/components/pipeline.md +0 -49
  232. package/content/components/popconfirm.md +0 -58
  233. package/content/components/popover.md +0 -61
  234. package/content/components/progressbar.md +0 -50
  235. package/content/components/prompttemplate.md +0 -49
  236. package/content/components/qrcode.md +0 -50
  237. package/content/components/radiogroup.md +0 -53
  238. package/content/components/rate.md +0 -53
  239. package/content/components/reasoningblock.md +0 -47
  240. package/content/components/relationgraph.md +0 -52
  241. package/content/components/resizable.md +0 -53
  242. package/content/components/result.md +0 -49
  243. package/content/components/scrollbar.md +0 -49
  244. package/content/components/searchinput.md +0 -51
  245. package/content/components/segmentedcontrol.md +0 -59
  246. package/content/components/select-searchable.md +0 -47
  247. package/content/components/select.md +0 -66
  248. package/content/components/sessionlist.md +0 -52
  249. package/content/components/sheetgrid.md +0 -44
  250. package/content/components/skeleton.md +0 -55
  251. package/content/components/slidecanvas.md +0 -44
  252. package/content/components/slider.md +0 -62
  253. package/content/components/sortablelist.md +0 -39
  254. package/content/components/space.md +0 -52
  255. package/content/components/sparkline.md +0 -52
  256. package/content/components/statcard-countdown.md +0 -39
  257. package/content/components/statcard.md +0 -56
  258. package/content/components/steps.md +0 -48
  259. package/content/components/switch.md +0 -50
  260. package/content/components/tabbar.md +0 -45
  261. package/content/components/table-v2.md +0 -40
  262. package/content/components/table.md +0 -66
  263. package/content/components/tabs.md +0 -56
  264. package/content/components/tag.md +0 -51
  265. package/content/components/tagsinput-v2.md +0 -39
  266. package/content/components/tagsinput.md +0 -54
  267. package/content/components/text.md +0 -45
  268. package/content/components/textarea.md +0 -57
  269. package/content/components/themeswitch.md +0 -69
  270. package/content/components/timeline.md +0 -50
  271. package/content/components/title.md +0 -45
  272. package/content/components/toast.md +0 -52
  273. package/content/components/toggle-togglegroup.md +0 -52
  274. package/content/components/togglegroup.md +0 -47
  275. package/content/components/toolcallcard.md +0 -51
  276. package/content/components/tooltip.md +0 -50
  277. package/content/components/tour.md +0 -50
  278. package/content/components/transfer.md +0 -52
  279. package/content/components/tree-v2.md +0 -39
  280. package/content/components/tree.md +0 -59
  281. package/content/components/treeselect.md +0 -54
  282. package/content/components/typography.md +0 -51
  283. package/content/components/videoplayer.md +0 -52
  284. package/content/components/virtuallist.md +0 -52
  285. package/content/components/virtualtable-v2.md +0 -39
  286. package/content/components/virtualtable.md +0 -55
  287. package/content/components/watermark.md +0 -53
  288. package/content/components/wave.md +0 -43
  289. package/content/guides/ai-chat-family.md +0 -24
  290. package/content/guides/choose.md +0 -67
  291. package/content/guides/component-model.md +0 -32
  292. package/content/guides/component-standards.md +0 -123
  293. package/content/guides/components-guide.md +0 -813
  294. package/content/guides/custom-component.md +0 -321
  295. package/content/guides/data-guide.md +0 -281
  296. package/content/guides/file-preview-family.md +0 -18
  297. package/content/guides/frontend.md +0 -968
  298. package/content/guides/layout-choice.md +0 -64
  299. package/content/guides/layout-guide.md +0 -241
  300. package/content/guides/middleware.md +0 -459
  301. package/content/guides/mobile-guide.md +0 -105
  302. package/content/guides/page-building.md +0 -98
  303. package/content/guides/production.md +0 -37
  304. package/content/guides/quality.md +0 -60
  305. package/content/guides/realtime-guide.md +0 -296
  306. package/content/guides/render-only.md +0 -44
  307. package/content/guides/saas-guide.md +0 -253
  308. package/content/guides/server-guide.md +0 -359
  309. package/content/guides/start.md +0 -41
  310. package/content/guides/styling.md +0 -154
  311. package/content/guides/ui-dom-guide.md +0 -157
  312. package/content/index.json +0 -4344
  313. package/content/index.md +0 -181
  314. package/content/layout/align.md +0 -20
  315. package/content/layout/anchor.md +0 -19
  316. package/content/layout/app-shell.md +0 -23
  317. package/content/layout/border.md +0 -20
  318. package/content/layout/center.md +0 -19
  319. package/content/layout/cluster.md +0 -19
  320. package/content/layout/container.md +0 -19
  321. package/content/layout/fill.md +0 -19
  322. package/content/layout/grid.md +0 -19
  323. package/content/layout/hidden.md +0 -22
  324. package/content/layout/layer.md +0 -19
  325. package/content/layout/position.md +0 -22
  326. package/content/layout/row.md +0 -23
  327. package/content/layout/safe-area.md +0 -20
  328. package/content/layout/scroll.md +0 -21
  329. package/content/layout/spacing.md +0 -22
  330. package/content/layout/split.md +0 -19
  331. package/content/layout/stack.md +0 -19
  332. package/content/layout/surface.md +0 -23
  333. package/content/layout/text.md +0 -22
  334. package/content/patterns/app-shell.md +0 -34
  335. package/content/patterns/dashboard.md +0 -36
  336. package/content/patterns/data-screen.md +0 -30
  337. package/content/patterns/detail-page.md +0 -31
  338. package/content/patterns/docs.md +0 -34
  339. package/content/patterns/focus-task.md +0 -34
  340. package/content/patterns/landing.md +0 -35
  341. package/content/patterns/list-page.md +0 -32
  342. package/content/patterns/mobile.md +0 -31
  343. package/content/patterns/settings-page.md +0 -33
  344. package/content/patterns/workspace.md +0 -32
  345. package/dist/cli/content-sync.test.d.ts +0 -1
  346. package/dist/cli/docs-cli.test.d.ts +0 -1
  347. package/dist/cli/docs.d.ts +0 -2
  348. package/dist/cli/docs.mjs +0 -10966
  349. package/dist/client/vdom/core/build.d.ts +0 -36
  350. package/dist/client/vdom/core/diff/children.d.ts +0 -29
  351. package/dist/client/vdom/core/diff/index.d.ts +0 -22
  352. package/dist/client/vdom/core/diff/output.d.ts +0 -30
  353. package/dist/client/vdom/core/diff/same.d.ts +0 -22
  354. package/dist/client/vdom/core/serve.d.ts +0 -75
  355. package/dist/client/vdom/core/ssr/index.d.ts +0 -25
  356. package/examples/apps/admin/README.md +0 -23
  357. package/examples/apps/admin/api.ts +0 -27
  358. package/examples/apps/admin/app.tsx +0 -144
  359. package/examples/apps/admin/main.tsx +0 -8
  360. package/examples/apps/admin/server.ts +0 -37
  361. package/examples/apps/auth/README.md +0 -23
  362. package/examples/apps/auth/api.ts +0 -49
  363. package/examples/apps/auth/app.tsx +0 -137
  364. package/examples/apps/auth/main.tsx +0 -8
  365. package/examples/apps/auth/server.ts +0 -37
  366. package/examples/apps/multi/README.md +0 -23
  367. package/examples/apps/multi/app.tsx +0 -83
  368. package/examples/apps/multi/main.tsx +0 -8
  369. package/examples/apps/multi/server.ts +0 -33
  370. package/examples/apps/todo/README.md +0 -23
  371. package/examples/apps/todo/api.ts +0 -26
  372. package/examples/apps/todo/app.tsx +0 -128
  373. package/examples/apps/todo/main.tsx +0 -9
  374. package/examples/apps/todo/server.ts +0 -39
  375. package/examples/hello-world/README.md +0 -9
  376. package/examples/hello-world/client.ts +0 -14
  377. package/examples/hello-world/routes.tsx +0 -26
  378. package/examples/hello-world/server.ts +0 -46
  379. package/examples/patterns/AppShell.tsx +0 -224
  380. package/examples/patterns/Dashboard.tsx +0 -153
  381. package/examples/patterns/DataScreen.tsx +0 -104
  382. package/examples/patterns/DetailPage.tsx +0 -79
  383. package/examples/patterns/Docs.tsx +0 -105
  384. package/examples/patterns/FocusTask.tsx +0 -67
  385. package/examples/patterns/Landing.tsx +0 -90
  386. package/examples/patterns/ListPage.tsx +0 -77
  387. package/examples/patterns/Mobile.tsx +0 -154
  388. package/examples/patterns/SettingsPage.tsx +0 -98
  389. package/examples/patterns/SplitWorkspace.tsx +0 -113
package/README.md CHANGED
@@ -7,7 +7,7 @@ npm install weifuwu # 一个依赖,完整应用栈
7
7
  ```
8
8
 
9
9
  **本地开发(apps/ 下的应用)**:dev 模式经 `node_modules/weifuwu/*` 软链解析到源码
10
- (`import 'weifuwu/ui-dom'` 等——esbuild bundle 的模块解析;tsconfig paths 只管类型)。
10
+ (`import 'weifuwu/vdom'` 等——esbuild bundle 的模块解析;tsconfig paths 只管类型)。
11
11
  新环境 `npm install` 后执行 `node scripts/dev-links.mjs` 创建软链(apps 运行前提)。
12
12
  发布时才编译 dist(`node scripts/release.mjs`——dev 零构建)。
13
13
 
@@ -28,9 +28,9 @@ npm install weifuwu # 一个依赖,完整应用栈
28
28
  | 层 | 入口 | 能力 |
29
29
  |----|------|------|
30
30
  | 后端 | `weifuwu` | Trie 路由 / 中间件链 / serve / 自研 PG+Redis / SSR / GraphQL / WebSocket |
31
- | 前端 | `weifuwu/ui-dom` | **vdom3 精准事件流引擎**——createRouter(路由)+ createRoot(挂载)+ 事件流(渲染本体:`entity:action` 统一命名、DOM = fold、可回放可断言)+ SSR(事件流序列化);组件=两阶段异步组件 / ctx.params 对齐后端;**weifuwu/components 直接复用**(VNode 契约唯一来源 ui-dom,见 `content/guides/ui-dom-guide.md`) |
31
+ | 前端 | `weifuwu/vdom` | **vdom 引擎**——UIRouter(路由唯一入口:get/notFound/has/resolve——`(Request, ctx) => Response` 签名对齐后端)+ uiServe(浏览器 boot)+ uiSsr(服务端渲染——SSR SPA 首帧结构吸收)+ 命令流渲染(NDJSON 命令纯数据可回放);**weifuwu/components 直接复用**(VNode 契约唯一来源 vdom) |
32
32
  | 组件 | `weifuwu/components` | 129 个 HTML 原语组件(表单/表格/弹层/AiChat…),引用 `--wf-*` 主题变量 |
33
- | 样式 | `weifuwu/layout` | 66 布局原语 + 157 工具类 + 182 主题 Token,零自定义 CSS 文件 |
33
+ | 样式 | `weifuwu/layout` | 48 布局原语 + 90 工具类 + 183 主题 Token,零自定义 CSS 文件 |
34
34
  | SaaS 地基 | 随包内置 | rateLimit / email / userSystem / messager / queue / ai → `ctx.*` 一行接入 |
35
35
 
36
36
  > ⚠️ **注意:前后端都有 `ctx.ui`,但用途完全不同**
@@ -96,7 +96,7 @@ npm install weifuwu # 一个依赖,完整应用栈
96
96
 
97
97
  **两阶段组件模型** — 组件 = `async (initProps, ctx) => (props) => Promise<VNode>`。外层工厂只执行一次(mount,可 await 数据),内层 renderFn 每次状态/props 变化时执行(强制异步)。无 class、无 `this`、无 Hook——**位置即语义**:外层天生只跑一次,没有 hooks 规则、没有依赖数组、没有闭包陷阱(详解见[核心概念](#核心概念))。
98
98
 
99
- **render-only 确定性渲染** — 渲染唯一触发 `ctx.ui.render()`(闭包绑定组件),状态是普通对象(`let` + `render()`);跨组件共享用 `createStore` + `ctx.ui.useExternal()`。行为可静态推导,无隐式触发(详见[组件库](content/guides/components-guide.md))。
99
+ **render-only 确定性渲染** — 渲染唯一触发 `ctx.ui.render()`(闭包绑定组件),状态是普通对象(`let` + `render()`);跨组件共享用 `createStore` + `ctx.ui.useExternal()`。行为可静态推导,无隐式触发(详见组件库)。
100
100
 
101
101
  **VDOM 输出透明(写 JSX,看 DOM 即真相)** — VDOM 对用户输入零 magic:条件渲染的 false 在 DOM 里是诊断占位注释(`<!--wf-hole: false-->`),数组项 key 与组件实例 id 直接落 DOM(`data-wf-key` / `data-wf-id`)——devtools 看到的 DOM 就是引擎决策的可读输出;非法输入占位 + warn,不崩溃不静默。转化契约唯一清晰:用户写什么,vnode 就是什么,DOM 就长什么样(规则表为内部开发契约)。
102
102
 
@@ -104,15 +104,15 @@ npm install weifuwu # 一个依赖,完整应用栈
104
104
 
105
105
  **async 工厂组件** — `async (initProps, ctx) => (props) => Promise<VNode>`(weifuwu **唯一组件形态**——同步组件已不支持):工厂层声明数据(`await ctx.data.get`)、mount 初始化状态(`let` + `render()`)、render 输出视图。异步在工厂边界与 renderFn,数据经闭包注入,写数据像写同步代码。三条纪律见[核心概念 · async 组件](#核心概念)。
106
106
 
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 运行时。
107
+ **SPA/SSR/Hydration 统一透明** — 同一棵路由树(`UIRouter` 单一实现源)一个组件三场景自动适配:服务端 `uiSsr(router, url)` 输出完整 HTML(**结构吸收**——客户端首帧复用 DOM 零差异、不重建、无闪跳——失败原子回退重建),浏览器端 `uiServe(router)` 接管后续交互渲染。`ctx.data.get` 一个 API:SSR 预取 / hydration 命中(不重复请求)/ SPA 触发 fetch。服务端直接用 `.tsx`(`weifuwu/dev` Node loader),前后端同一 JSX 运行时。
108
108
 
109
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。
110
110
 
111
- **SaaS 地基随包内置** — rateLimit(限流)/ email(邮件)/ userSystem(用户认证)/ messager(消息系统)/ queue(可靠队列)以中间件形态随包提供,`app.use(...)` 一行接入(详见[SaaS 地基模块](content/guides/saas-guide.md))。互相咬合成协作基础:身份(userSystem)+ 消息(messager)的组合让「谁能跟谁说话、消息如何送达」天然对齐,不再需要第三套权限系统。
111
+ **SaaS 地基随包内置** — rateLimit(限流)/ email(邮件)/ userSystem(用户认证)/ messager(消息系统)/ queue(可靠队列)以中间件形态随包提供,`app.use(...)` 一行接入(详见SaaS 地基模块)。互相咬合成协作基础:身份(userSystem)+ 消息(messager)的组合让「谁能跟谁说话、消息如何送达」天然对齐,不再需要第三套权限系统。
112
112
 
113
113
  **机制与策略分离** — 框架管**机制**(token 怎么签、消息怎么送达、agent 循环怎么跑),开发者管**策略**(谁能建群、租户隔离 SQL、技能注册表)。这是「诚实裁剪」的积极面:**框架不越界,应用层不被绑架**——agent-platform 迁移验证了边界:多租户隔离(`WHERE tenant_id`)、技能编排、聊天产品模型留在应用层,框架守住通用能力(auth / ai / messager / UI / 数据管道)。
114
114
 
115
- **零自定义 CSS 设计系统** — 一个 CSS 文件 = 双层 Token + 布局原语 + 工具类 + 组件样式。业务页面不写 style.css:组件 + `wf-*` 原语写业务,主题改一个值(`--wf-brand-seed` 换肤 / `data-preset` 预设 / `--wf-btn-radius` 钩子),暗色自动(详见[布局系统](content/guides/layout-guide.md)与[主题配置](content/guides/styling.md))。
115
+ **零自定义 CSS 设计系统** — 一个 CSS 文件 = 双层 Token + 布局原语 + 工具类 + 组件样式。业务页面不写 style.css:组件 + `wf-*` 原语写业务,主题改一个值(`--wf-brand-seed` 换肤 / `data-preset` 预设 / `--wf-btn-radius` 钩子),暗色自动(详见布局系统与主题配置)。
116
116
 
117
117
  **自研数据层** — `ctx.sql`(PG v3 协议)与 `ctx.redis`(RESP2 协议)为**自研客户端**:确定性输出、行为可预测、统一错误模型。jsonb 自动解码、TTL 安全 API、schema 写前校验——高频痛点(双重编码/parseRow 样板/`'EX'` 参数顺序)从根上消除。
118
118
 
@@ -126,22 +126,30 @@ npm install weifuwu # 一个依赖,完整应用栈
126
126
 
127
127
  | 模式 | 适用场景 | 后端 | 客户端入口 |
128
128
  |------|---------|------|-----------|
129
- | **SPA** | 应用页(Dashboard、工具、后台) | HTML 外壳 | `createRouter(routes, root, { ctx })` |
130
- | **SSR + Hydration** | 内容页(博客、营销,需要 SEO/首屏) | `renderToEvents` `eventsToHtml` + `serializeEvents` | `deserializeEvents` + `replay`(事件流收养) |
129
+ | **SPA** | 应用页(Dashboard、工具、后台) | HTML 外壳 | `uiServe(router, { root })` |
130
+ | **SSR + Hydration** | 内容页(博客、营销,需要 SEO/首屏) | `uiSsr(router, url)`(HTML + `__DATA__` 种子) | `uiServe`(结构吸收——首帧复用服务端 DOM) |
131
131
 
132
132
  ### 先写共享部分(两种模式都一样)
133
133
 
134
134
  ```tsx
135
- // routes.tsx —— 路由声明(createRouter——vdom3 事件流引擎)
136
- import { createRouter, h } from 'weifuwu/ui-dom'
137
-
138
- // async 组件(原生):await 数据 返回视图(外层初始化,内层渲染)
139
- const Home = async (_init, ctx) => {
140
- const msg = await ctx.data.get('/api/hello') // 数据管道:一个 API 三场景
141
- return async (props) => <h1>{msg.msg}</h1>
135
+ // router.tsx —— 路由树(UIRouter——前后端同一棵树——单一实现源)
136
+ import { UIRouter, h, type UIContext } from 'weifuwu/vdom'
137
+
138
+ const Home = (_: unknown, ctx: UIContext) => {
139
+ // 数据管道:一个 API 三场景(SSR 预取 / hydration 命中 / SPA fetch)
140
+ const [getMsg] = ctx.ui.useAsyncData(() => fetch('/api/hello').then((r) => r.json()), 'hello')
141
+ const msg = getMsg()
142
+ if (!msg) return <p>加载中…</p>
143
+ return <h1>{msg.msg}</h1>
142
144
  }
143
145
 
144
- export const routes = [{ path: '/', render: () => h(Home, {}) }]
146
+ export function buildRouter(): UIRouter {
147
+ const router = new UIRouter()
148
+ router.get('/', () => h(Home))
149
+ router.get('/u/:id', (req, ctx) => h('h1', {}, `用户 ${ctx.params.id}`)) // :param 注入
150
+ router.notFound(() => h('h1', {}, '404'))
151
+ return router
152
+ }
145
153
  ```
146
154
 
147
155
  ### 模式 A:纯 SPA
@@ -169,11 +177,11 @@ serve(router, { port: 3000 })
169
177
  ```
170
178
 
171
179
  ```ts
172
- // src/client.ts —— 纯客户端渲染(vdom3 事件流引擎)
173
- import { createRouter } from 'weifuwu/ui-dom'
174
- import { routes } from './routes.tsx'
180
+ // src/client.ts —— 浏览器 boot(uiServe——UIRouter 唯一应用入口)
181
+ import { uiServe } from 'weifuwu/vdom'
182
+ import { buildRouter } from './router.tsx'
175
183
 
176
- createRouter(routes, document.querySelector('#root')!, { ctx }) // 监听 location → 匹配 → 事件流渲染
184
+ uiServe(buildRouter(), { root: '#root' }) // 监听导航 → 匹配 → 命令流渲染
177
185
  ```
178
186
 
179
187
  ### 模式 B:SSR + Hydration(内容页/SEO)
@@ -181,23 +189,22 @@ createRouter(routes, document.querySelector('#root')!, { ctx }) // 监听 loca
181
189
  同一份 `routes`、同一个组件,差异只在**后端构建事件流 → HTML + 序列化事件,客户端回放**:
182
190
 
183
191
  ```ts
184
- // server.ts —— SSR:vnode事件流 → 完整 HTML + __DATA__(DOM = fold 不变量)
192
+ // server.ts —— SSR:同一棵路由树 → 完整 HTML + __DATA__(SSR SPA 首帧)
185
193
  import { serve, Router, ui, cors } from 'weifuwu'
186
- import { renderToEvents, eventsToHtml, serializeEvents } from 'weifuwu/ui-dom'
187
- import { routes } from './routes.tsx'
194
+ import { uiSsr } from 'weifuwu/vdom'
195
+ import { buildRouter } from './router.tsx'
188
196
 
189
197
  const router = new Router()
190
198
  router.use(cors())
191
199
  router.use(ui())
192
200
 
193
- // 路由级 SSR:匹配共享 routes 构建 vnode 事件流 → HTML + __DATA__ 序列化
201
+ // SSR:路由树单源服务端渲染 → HTML + __DATA__ 种子(客户端结构吸收零闪跳)
194
202
  router.get('*', async (req, ctx) => {
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)
203
+ const html = await uiSsr(buildRouter(), new URL(req.url).pathname, {
204
+ title: '我的应用',
205
+ prefetch: async () => ({ hello: await (await fetch('http://localhost:3000/api/hello')).json() }),
206
+ })
207
+ return ctx.ui.html.unsafe(html)
201
208
  })
202
209
 
203
210
  router.get('/static/app.js', (req, ctx) => ctx.ui.js('./src/client.ts'))
@@ -207,13 +214,12 @@ serve(router, { port: 3000 })
207
214
  ```
208
215
 
209
216
  ```ts
210
- // src/client.ts —— 事件流回放收养(deserializeEvents + replay——零 DOM 猜测)
211
- import { createRouter, deserializeEvents, replay } from 'weifuwu/ui-dom'
212
- import { routes } from './routes.tsx'
217
+ // src/client.ts —— 结构吸收(uiServe 首帧复用服务端 DOM——零闪跳)
218
+ import { uiServe } from 'weifuwu/vdom'
219
+ import { buildRouter } from './router.tsx'
213
220
 
214
- const root = document.querySelector('#root')!
215
- replay(deserializeEvents((window as any).__DATA__), root) // 收养服务端 HTML(无闪跳)
216
- createRouter(routes, root) // 之后正常交互渲染
221
+ // 同一棵路由树:服务端 HTML 被吸收(焦点/输入值保持),之后正常交互渲染
222
+ uiServe(buildRouter(), { root: '#root' })
217
223
  ```
218
224
 
219
225
  ### 启动(两种模式都一样)
@@ -244,15 +250,6 @@ cd apps/agent-platform && npm run seed && npm run dev
244
250
 
245
251
  > 想**零后端、零构建**最快跑起来?直接跳到下面的「CDN 快速原型」——一个 `.html` 文件即可。
246
252
 
247
- ### 📦 安装后离线文档(content/ 随包发布)
248
-
249
- ```bash
250
- npm i weifuwu
251
- npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表/纪律/示例/验证)
252
- ```
253
-
254
- `node_modules/weifuwu/content/` 是完整文档库(逐组件文档 + 页面/应用模板 + 后端能力 + 指南 + 选型决策树 + 人类质量标准)——版本永远匹配安装的代码;`examples/` 是可复制源码(页面模式 + 应用模板 todo/auth/admin/multi)。LLM 开发路径:`read node_modules/weifuwu/content/index.md` → 目标 .md → 复制 examples/ 源码。
255
-
256
253
  ---
257
254
 
258
255
  ## CDN 快速原型(零构建、纯 HTML)
@@ -268,7 +265,7 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
268
265
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
269
266
  <title>Weifuwu CDN 示例</title>
270
267
 
271
- <!-- 组件样式(可选,如只用 weifuwu/ui-dom 则不需要) -->
268
+ <!-- 组件样式(可选,如只用 weifuwu/vdom 则不需要) -->
272
269
  <link
273
270
  rel="stylesheet"
274
271
  href="https://unpkg.com/weifuwu@latest/dist/components/style.css"
@@ -281,57 +278,35 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
281
278
  <script type="importmap">
282
279
  {
283
280
  "imports": {
284
- "weifuwu/ui-dom": "https://unpkg.com/weifuwu@latest/dist/ui-dom/index.js",
281
+ "weifuwu/vdom": "https://unpkg.com/weifuwu@latest/dist/vdom/index.js",
285
282
  "weifuwu/components": "https://unpkg.com/weifuwu@latest/dist/components/index.js"
286
283
  }
287
284
  }
288
285
  </script>
289
286
 
290
287
  <script type="module">
291
- import { createRouter, h } from 'weifuwu/ui-dom'
292
- import { Card, Button, Badge } from 'weifuwu/components'
293
-
294
- // 组件 = async (initProps, ctx) => (props) => Promise<VNode>(render-only:改状态后 ctx.ui.render())
295
- const Counter = async (_init, ctx) => {
296
- let count = 0 // mount 初始化
297
-
298
- return () =>
299
- h(Card, { variant: 'default', padding: 'lg' },
300
- h('h2', { style: { textAlign: 'center', margin: 0 } }, '⚡ Weifuwu'),
301
- h('div', { style: { fontSize: '4rem', fontWeight: 600, textAlign: 'center' } },
302
- String(count)),
303
- h('div', { style: { textAlign: 'center', marginTop: '1rem' } },
304
- h(Badge, {
305
- variant: count % 2 === 0 ? 'success' : 'warning'
306
- }, count % 2 === 0 ? '偶数' : '奇数')),
307
- h('hr', { style: { margin: '1rem 0', border: 'none', borderTop: '1px solid #eee' } }),
308
- h('div', { style: { display: 'flex', gap: '0.5rem', justifyContent: 'center' } },
309
- h(Button, { variant: 'secondary', onClick: () => { count--; ctx.ui.render() } }, '➖ 减 1'),
310
- h(Button, { variant: 'danger', onClick: () => { count = 0; ctx.ui.render() } }, '↺ 重置'),
311
- h(Button, { variant: 'primary', onClick: () => { count++; ctx.ui.render() } }, '➕ 加 1'),
312
- ),
313
- )
314
- }
288
+ import { UIRouter, uiServe, h } from 'weifuwu/vdom'
289
+
290
+ // 路由树(UIRouter——get/notFound——(req, ctx) => Response 同构后端)
291
+ const router = new UIRouter()
292
+ router.get('/', () => h('h1', {}, 'Hello weifuwu'))
293
+ router.get('/u/:id', (req, ctx) => h('h1', {}, `用户 ${ctx.params.id}`))
294
+ router.notFound(() => h('h1', {}, '404'))
315
295
 
316
- // 路由 + 渲染(vdom3 事件流引擎:createRouter——监听 location → 匹配 → 事件流落地)
317
- createRouter(
318
- [{ path: '/', render: () => h(Counter, {}) }],
319
- document.querySelector('#root')!,
320
- )
296
+ // 浏览器 boot(uiServe——监听导航 → 匹配 → 命令流渲染)
297
+ uiServe(router, { root: '#root' })
321
298
  </script>
299
+ <!-- 对照服务端:new Router().get('/', () => new Response(...))——同一签名形状 -->
322
300
  </body>
323
301
  </html>
324
302
  ```
325
303
 
326
- 将此 HTML 保存到本地用浏览器打开即可运行。
327
-
328
- ### CDN 资源地址说明
329
-
330
- | 资源 | CDN 地址 | 说明 |
304
+ | 模块 | CDN 路径 | 说明 |
331
305
  |------|---------|------|
332
- | `weifuwu/ui-dom` | `https://unpkg.com/weifuwu@latest/dist/ui-dom/index.js` | 前端运行时(createRouter, createRoot, h, 事件流, 状态管理等) |
306
+ | `weifuwu/vdom` | `https://unpkg.com/weifuwu@latest/dist/vdom/index.js` | 前端运行时(UIRouter, uiServe, h/jsx, hooks 等) |
307
+ | `weifuwu/vdom` | `https://unpkg.com/weifuwu@latest/dist/vdom/index.js` | 前端运行时(UIRouter, uiServe, h/jsx, hooks 等) |
333
308
  | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/index.js` | 129 个 UI 组件(Button, Card, Table, Modal, Icon 等) |
334
- | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/style.css` | 组件 CSS + 182 个主题 Token + 66 个布局原语 + 157 个工具类 |
309
+ | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/style.css` | 组件 CSS + 183 个主题 Token + 48 个布局原语 + 90 个工具类 |
335
310
  | 独立布局系统 | `https://unpkg.com/weifuwu@latest/dist/layout/weifuwu-layout.css` | 仅 CSS 布局,不依赖 JS |
336
311
 
337
312
 
@@ -342,6 +317,7 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
342
317
  | 导入路径 | 模块 | 用途 | 依赖 |
343
318
  |---------|------|------|------|
344
319
  | `weifuwu` | **Router** | Trie 路由 + 中间件链 + WebSocket + GraphQL | — |
320
+ | `src/shared/router` | **路由内核(前后端共享五层单源)** | trie(匹配)+ pipeline(流程骨架)+ context(URL 解析/ctx 注入)+ chain(中间件链)+ ctx-fields(ctx 扩展注册表)——**同一套机制双端复用**,serve(编解码边界)留各自域 | — |
345
321
  | `weifuwu` | **serve** | HTTP 服务器 | Router |
346
322
  | `weifuwu` | **cors** | CORS 跨域中间件 | Router |
347
323
  | `weifuwu` | **serveStatic** | 静态文件服务(ETag/304/目录索引) | Router |
@@ -349,8 +325,8 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
349
325
  | `weifuwu` | **redis** | Redis 客户端(自研 RESP2 协议)→ `ctx.redis` | Router, REDIS_URL |
350
326
  | `weifuwu/db` | **Memory 实现** | `createMemorySql()` / `MemoryRedis`——生产契约黑盒实现(开发/测试/单实例零数据库);`MemoryRedisServer`/`MemoryPostgresServer`——进程内线协议服务器(协议测试零 docker) | — |
351
327
  | `weifuwu` | **ui** | SSR 渲染 + esbuild JS/CSS 动态编译 → `ctx.ui` | Router |
352
- | `weifuwu/ui-dom` | **createRouter** | vdom3 路由:RouteDef[] + 中间件面 ctx 注入(对齐后端 `app.use`)——监听 location 匹配 事件流渲染 | — |
353
- | `weifuwu/ui-dom` | **SSR(事件流形态)** | `renderToEvents``eventsToHtml` + `serializeEvents`(DOM = fold——客户端 `replay` 收养) | Router, ui |
328
+ | `weifuwu/vdom` | **UIRouter** | 前端路由唯一入口:get/notFound/has/resolve——`(req, ctx) => Response` 签名对齐后端——params/query/route 注入(shared Trie 五层单源) | — |
329
+ | `weifuwu/vdom` | **uiServe / uiSsr** | uiServe 浏览器 boot(导航监听 命令流渲染);uiSsr 服务端渲染(HTML + `__DATA__` 种子——**结构吸收** SSR SPA 首帧) | Router, ui |
354
330
  | `weifuwu` | **rateLimit** | 限流中间件(fixed/sliding,redis 多实例原子)→ `ctx.limit` | Router, redis |
355
331
  | `weifuwu` | **email** | 邮件发送(Resend/SMTP 自研/自定义适配器)→ `ctx.email` | Router |
356
332
  | `weifuwu` | **userSystem** | 用户系统(scrypt 密码哈希 + 混合会话 + 多租户感知)→ `ctx.user` / `ctx.auth` / `ctx.tenantId` + `/api/auth/*` | Router, postgres |
@@ -364,17 +340,17 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
364
340
  | `weifuwu` | **ok / badRequest / …** | HTTP 响应辅助函数(ok/badRequest/... 等 12 个) | — |
365
341
  | `weifuwu` | **parseBody** | JSON 请求体安全解析 | — |
366
342
  | Router 方法 | **app.graphql()** | GraphQL 端点(支持 GraphiQL),Router 实例方法(无需单独 import) | Router |
367
- | `weifuwu/ui-dom` | **createRouter / createRoot** | vdom3 事件流引擎:路由(RouteDef[] + ctx 注入)/ 挂载(组件树)——渲染全链路 `entity:action` 事件流(DOM = fold——可记录/回放/断言);**事件代理**(监听 O(1)——零重绑——统一注册表);**app 节点**(多应用加载——registerApp) | — |
368
- | `weifuwu/ui-dom` | **事件流原语** | `stream` / `ev` / `evKey` / `replay` / `eventsOf` / `expectEventSequence`——渲染可观测/可断言 | — |
369
- | `weifuwu/ui-dom` | **async 组件** | async 函数即组件(与同步同签名);数据走 ctx.data 三场景(三条纪律见[核心概念](#核心概念)) | — |
370
- | `weifuwu/ui-dom` | **ctx.data** | 数据管道:SSR 预取 / hydration 命中 / SPA fetch(`ctx.data.get`) | — |
371
- | `weifuwu/ui-dom` | **api / auth / ws** | HTTP 客户端 / 认证 / WebSocket 中间件 | — |
372
- | `weifuwu/ui-dom` | **i18n** | 国际化中间件(运行时切换语言) | — |
373
- | `weifuwu/ui-dom` | **renderToEvents / eventsToHtml / serializeEvents** | 服务端渲染:vnode → 事件流 → HTML + `__DATA__` 序列化(客户端 `replay` 收养) | — |
374
- | `weifuwu/ui-dom` | **useChat / AiChat 原语** | AI 会话(流式/工具调用/HITL) | — |
375
- | `weifuwu/ui-dom` | **事件原语** | `usePopup`(统一弹窗能力层)/ `usePresence` / `useInView` / `useScrollPosition` / `useGlobalKey` / `useDrag` / `useDragDrop` / `useAnimationEnd` / `useTween` / `useReducedMotion`(浏览器事件/动画统一入口,见 [content/guides/mobile-guide.md](content/guides/mobile-guide.md)) | — |
376
- | `weifuwu/components` | **129 个组件** | Button/Table/Modal/Confirm/Toast/... + `confirm()` / `toast()` 命令式中间件 | weifuwu/ui-dom |
377
- | `weifuwu/layout` | **CSS 布局** | 66 个布局原语 + 157 个工具类 + 182 个主题 Token(也支持 `weifuwu/layout/style.css`) | — |
343
+ | `weifuwu/vdom` | **命令流渲染引擎** | 渲染 = 命令流(NDJSON 纯数据——13 命令完整自足——可回放可断言);**事件代理**(监听 O(1)——零重绑——统一注册表);三状态机 + 对账器(终态等价 fuzz 1310 对防线) | — |
344
+ | `weifuwu/vdom` | **渲染健康仪表** | `window.__wfRenderHealth`(频率/规模/复用/错误四轴——dev 模式——渲染问题出现即读数) | — |
345
+ | `weifuwu/vdom` | **组件契约** | 工厂同步(`(initProps, ctx) => RenderFn`)——异步边界全在 hooks(useAsyncData/useObservable)——渲染纯同步(见[核心概念](#核心概念)) | — |
346
+ | `weifuwu/vdom` | **useAsyncData** | 数据管道:同 key 并发合并 / 竞态取消 / 缓存保留 / SSR 种子预热(`ctx.ui.useAsyncData(fetcher, key)`) | — |
347
+ | `weifuwu/vdom` | **api / auth / ws / i18n** | HTTP 客户端 / 认证 / WebSocket / 国际化中间件(ctx 注入) | — |
348
+
349
+ | `weifuwu/vdom` | **hooks 全家** | usePopup/useControlled/useExternal/useInView/useScrollPosition/useTween/useBreakpoint/... (getter 纪律——任意位置读最新) | — |
350
+ | `weifuwu/vdom` | **useChat / AiChat 原语** | AI 会话(流式/工具调用/HITL) | — |
351
+ | `weifuwu/vdom` | **事件原语** | `usePopup`(统一弹窗能力层)/ `usePresence` / `useInView` / `useScrollPosition` / `useGlobalKey` / `useDrag` / `useDragDrop` / `useAnimationEnd` / `useTween` / `useReducedMotion`(浏览器事件/动画统一入口,见 —) | — |
352
+ | `weifuwu/components` | **129 个组件** | Button/Table/Modal/Confirm/Toast/... + `confirm()` / `toast()` 命令式中间件 | weifuwu/vdom |
353
+ | `weifuwu/layout` | **CSS 布局** | 48 个布局原语 + 90 个工具类 + 183 个主题 Token(也支持 `weifuwu/layout/style.css`) | — |
378
354
 
379
355
  ---
380
356
 
@@ -384,23 +360,23 @@ npx weifuwu docs # → http://localhost:4000 本地文档站(组件 API 表
384
360
 
385
361
  | 任务 | 用 | 位置 |
386
362
  |------|-----|------|
387
- | 起 HTTP 服务 + 路由 | `serve(app)` + `new Router()` + `app.get/post/...` | [content/guides/server-guide.md](content/guides/server-guide.md) |
388
- | 渲染页面(SPA / SSR) | `createRouter(routes, root, { ctx })`;SSR = `renderToEvents` `eventsToHtml` + `replay`(事件流形态) | [content/guides/ui-dom-guide.md](content/guides/ui-dom-guide.md) · [content/guides/frontend.md](content/guides/frontend.md) |
389
- | 数据持久化 | `postgres()` → `` ctx.sql`SELECT *` `` · `redis()` → `ctx.redis` · **`sql.query`**(Query Language AST 双后端) | [content/guides/data-guide.md](content/guides/data-guide.md) |
390
- | 零数据库开发/测试 | `createMemorySql()` / `MemoryRedis`——契约同真库、替换成本为零 | [content/guides/data-guide.md](content/guides/data-guide.md) |
391
- | 数据管道(SSR 预取/hydration/SPA) | `ctx.data.get(key)` + async 组件 | [content/guides/frontend.md](content/guides/frontend.md) |
392
- | 用户注册/登录/会话/多租户 | `userSystem()` → `ctx.auth` + `/api/auth/*` | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
393
- | 限流防爆破 | `rateLimit()` + `ctx.limit()` | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
394
- | 发邮件 | `email()` → `ctx.email`(Resend/SMTP) | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
395
- | 实时消息/聊天/通知 | `messager()` → `ctx.msg` + `app.ws` | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
396
- | 后台任务/定时 | `queue()` → `ctx.queue` · `scheduler()` → `ctx.schedule/cron` | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
397
- | AI 对话 / Agent / HITL 审批 | `ai()` → `ctx.ai` + `ctx.ui.useChat()` + `AiChat` | [content/guides/saas-guide.md](content/guides/saas-guide.md) |
398
- | GraphQL / WebSocket | `app.graphql(handler)` · `app.ws(path, handler)` | [content/guides/realtime-guide.md](content/guides/realtime-guide.md) |
399
- | 前端 UI 组件 | `weifuwu/components`(129 个:Button/Table/Modal/AiChat/...) | [content/guides/components-guide.md](content/guides/components-guide.md) |
400
- | 布局/主题/暗色 | `weifuwu/layout`(58 原语 + 157 工具类 + 182 Token) | [content/guides/layout-guide.md](content/guides/layout-guide.md) |
401
- | 样式定制(零自定义 CSS) | `--wf-*` 变量覆盖 + 组件定制钩子 | [content/guides/styling.md](content/guides/styling.md) |
402
- | 移动端适配(tap/长按/键盘/弹层) | `usePopup` / `useHoverCapable` / `useLongPress` / `useVisualViewport` | [content/guides/mobile-guide.md](content/guides/mobile-guide.md) |
403
- | 前后端类型安全中间件 | `createMiddleware`(声明注入即类型化) | [content/guides/server-guide.md](content/guides/server-guide.md) |
363
+ | 起 HTTP 服务 + 路由 | `serve(app)` + `new Router()` + `app.get/post/...` | |
364
+ | 渲染页面(SPA / SSR) | `UIRouter` + `uiServe(router, { root })`;SSR = `uiSsr(router, url)`(结构吸收——首帧零差异) | · |
365
+ | 数据持久化 | `postgres()` → `` ctx.sql`SELECT *` `` · `redis()` → `ctx.redis` · **`sql.query`**(Query Language AST 双后端) | |
366
+ | 零数据库开发/测试 | `createMemorySql()` / `MemoryRedis`——契约同真库、替换成本为零 | |
367
+ | 数据管道(SSR 预取/hydration/SPA) | `ctx.data.get(key)` + async 组件 | |
368
+ | 用户注册/登录/会话/多租户 | `userSystem()` → `ctx.auth` + `/api/auth/*` | |
369
+ | 限流防爆破 | `rateLimit()` + `ctx.limit()` | |
370
+ | 发邮件 | `email()` → `ctx.email`(Resend/SMTP) | |
371
+ | 实时消息/聊天/通知 | `messager()` → `ctx.msg` + `app.ws` | |
372
+ | 后台任务/定时 | `queue()` → `ctx.queue` · `scheduler()` → `ctx.schedule/cron` | |
373
+ | AI 对话 / Agent / HITL 审批 | `ai()` → `ctx.ai` + `ctx.ui.useChat()` + `AiChat` | |
374
+ | GraphQL / WebSocket | `app.graphql(handler)` · `app.ws(path, handler)` | |
375
+ | 前端 UI 组件 | `weifuwu/components`(129 个:Button/Table/Modal/AiChat/...) | |
376
+ | 布局/主题/暗色 | `weifuwu/layout`(49 原语 + 90 工具类 + 183 Token) | |
377
+ | 样式定制(零自定义 CSS) | `--wf-*` 变量覆盖 + 组件定制钩子 | |
378
+ | 移动端适配(tap/长按/键盘/弹层) | `usePopup` / `useHoverCapable` / `useLongPress` / `useVisualViewport` | |
379
+ | 前后端类型安全中间件 | `createMiddleware`(声明注入即类型化) | |
404
380
 
405
381
  ---
406
382
 
@@ -439,15 +415,15 @@ const Counter = async (_init, ctx) => {
439
415
  app.get('/users', (req, ctx) => { ctx.sql`SELECT *` })
440
416
  // ctx 已注入 ctx.sql
441
417
 
442
- 前端: const router = createRouter(routes, root, { ctx })
443
- router.use(api({ baseURL: '/api' }))
444
- router.use(auth())
445
- router.get('/users', async (location, ctx) => h(UsersPage, {}))
446
- // createRouter = 监听 location → 匹配 → 事件流渲染落地
447
- // ctx 已注入 ctx.api, ctx.auth
418
+ 前端: const router = new UIRouter()
419
+ router.get('/users', (req, ctx) => h(UsersPage, {}))
420
+ // UIRouter.resolve = 监听导航 → shared Trie 匹配 → params 注入 →
421
+ // handler 执行——与后端 dispatchRouter 同一内核(shared/router
422
+ // 五层单源:trie/pipeline/context/chain/ctx-fields)
423
+ // ctx 注入:params/query/route(enrichCtx)——对齐后端中间件注入
448
424
  ```
449
425
 
450
- 前端 req = `window.location`,res = `VNode`,`createRouter` = 事件流渲染落地——与后端 `Request → Response`、`serve(router)` 完全同构。
426
+ 前端 req = 标准 `Request`,res = 命令流 `Response`,`UIRouter.resolve` = `dispatchRouter` 内核——与后端 `Request → Response`、`serve(router)` **字面同构**(同一 pipeline 骨架——差异只在 verb 表与 serve 边界)。
451
427
 
452
428
  ### 状态管理
453
429
 
@@ -492,8 +468,8 @@ const UserProfile = async (_init, ctx) => {
492
468
  | | SPA | SSR + Hydration |
493
469
  |---|---|---|
494
470
  | 适用 | 应用页(后台、工具、Dashboard) | 内容页(博客、营销,需要 SEO/首屏) |
495
- | 后端 | HTML 外壳 | `renderToEvents` → `eventsToHtml` + `serializeEvents`(完整 HTML + `__DATA__`) |
496
- | 客户端 | `createRouter(routes, root, { ctx })` | `deserializeEvents` + `replay`(事件流收养——DOM = fold) |
471
+ | 后端 | HTML 外壳 | `uiSsr(router, url)`(完整 HTML + `__DATA__` 种子——同一棵路由树) |
472
+ | 客户端 | `uiServe(router, { root })` | **结构吸收**(首帧复用服务端 DOM——焦点/输入值保持——失败原子回退重建) |
497
473
 
498
474
  **怎么选**:默认 SPA;需要 SEO 或首屏即内容时用 SSR。两种模式可混合——SSR 走共享 routes,未匹配走普通 handler。
499
475
 
@@ -514,23 +490,23 @@ README 只保留入门内容(设计理念 / 快速开始 / 核心概念 / 模
514
490
 
515
491
  | 文档 | 内容 |
516
492
  |------|------|
517
- | [content/guides/server-guide.md](content/guides/server-guide.md) | HTTP 服务层:Router / serve / cors / serveStatic / HttpError / 响应辅助 / parseBody |
518
- | [content/guides/data-guide.md](content/guides/data-guide.md) | 数据层:postgres(PG v3 自研协议)/ redis(RESP2 自研协议)/ Query Language(AST 双后端)/ Memory 实现(零数据库)/ 测试零外部依赖 |
519
- | [content/guides/realtime-guide.md](content/guides/realtime-guide.md) | 实时与渲染:scheduler / ui(SSR + JS/CSS 编译)/ graphql / WebSocket |
520
- | [content/guides/saas-guide.md](content/guides/saas-guide.md) | SaaS 地基:rateLimit / email / userSystem / messager / queue / ai |
493
+ | | HTTP 服务层:Router / serve / cors / serveStatic / HttpError / 响应辅助 / parseBody |
494
+ | | 数据层:postgres(PG v3 自研协议)/ redis(RESP2 自研协议)/ Query Language(AST 双后端)/ Memory 实现(零数据库)/ 测试零外部依赖 |
495
+ | | 实时与渲染:scheduler / ui(SSR + JS/CSS 编译)/ graphql / WebSocket |
496
+ | | SaaS 地基:rateLimit / email / userSystem / messager / queue / ai |
521
497
  | [docs/ai-contract.md](docs/ai-contract.md) | AI Stream Protocol:wf: 事件(SSE 下行 + POST 上行)——流式/工具/审批 |
522
498
 
523
499
  ### 前端开发者
524
500
 
525
501
  | 文档 | 内容 |
526
502
  |------|------|
527
- | [content/guides/frontend.md](content/guides/frontend.md) | 前端核心:应用引导(createRouter/createRoot——vdom3 事件流)/ 组件模型 / 异步组件 / 状态管理 / 条件与列表 / ref / 类型(weifuwu/client 已并入 ui-dom) |
528
- | [content/guides/ui-dom-guide.md](content/guides/ui-dom-guide.md) | **ui-dom**:vdom3 精准事件流引擎(createRouter/createRoot + 事件流)——components 复用 + SSR(事件流形态)(前端唯一运行时) |
529
- | [content/guides/middleware.md](content/guides/middleware.md) | 前端中间件:router / api / auth / ws / i18n / ErrorBoundary / confirm / toast / ScrollLock / extendCtx |
530
- | [content/guides/components-guide.md](content/guides/components-guide.md) | 组件库(129 个组件 + 使用示例 + 组件列表) |
531
- | [content/guides/layout-guide.md](content/guides/layout-guide.md) | 布局系统:66 个布局原语 + 157 个工具类 + 182 个主题 Token |
503
+ | | 前端核心:应用引导(UIRouter/uiServe)/ 组件模型(工厂同步 + hooks 异步边界)/ 状态管理 / 条件与列表 / ref |
504
+ | | **vdom**:命令流渲染引擎(UIRouter + uiServe/uiSsr + hooks)——components 复用 + SSR 结构吸收(前端唯一运行时) |
505
+ | | 前端中间件:router / api / auth / ws / i18n / ErrorBoundary / confirm / toast / ScrollLock / extendCtx |
506
+ | | 组件库(129 个组件 + 使用示例 + 组件列表) |
507
+ | | 布局系统:48 个布局原语 + 90 个工具类 + 183 个主题 Token |
532
508
  | [docs/style-guide.md](docs/style-guide.md) | 样式学习路径与命名规范:三档学习(组件 → 原语 → 速查)|
533
- | [content/guides/styling.md](content/guides/styling.md) | 样式定制指南:零自定义 CSS 模式 / 暗色 / 组件级覆盖 / 作用域主题 |
509
+ | | 样式定制指南:零自定义 CSS 模式 / 暗色 / 组件级覆盖 / 作用域主题 |
534
510
 
535
511
  ### 通用
536
512
 
@@ -538,9 +514,9 @@ README 只保留入门内容(设计理念 / 快速开始 / 核心概念 / 模
538
514
  |------|------|
539
515
  | [docs/examples.md](docs/examples.md) | 组合场景示例:登录表单 / 数据列表 + 搜索 / 消息提示 |
540
516
  | [docs/environment.md](docs/environment.md) | 环境变量与开发命令 |
541
- | [content/guides/mobile-guide.md](content/guides/mobile-guide.md) | 移动端开发指南:断点 / 44px 命中区 / usePopup / 手势 / safe-area |
517
+ | | 移动端开发指南:断点 / 44px 命中区 / usePopup / 手势 / safe-area |
542
518
  | [docs/components-map.md](docs/components-map.md) | 组件速查:weifuwu ↔ antd / Element Plus / shadcn 对应 + 迁移路径 |
543
- | [content/guides/custom-component.md](content/guides/custom-component.md) | 自定义组件开发指南:usePopup / useControlled / 动画 / AI 组件 / 类型纪律 |
519
+ | | 自定义组件开发指南:usePopup / useControlled / 动画 / AI 组件 / 类型纪律 |
544
520
 
545
521
  > **贡献**:见 [CONTRIBUTING.md](CONTRIBUTING.md)——四条路径(文档/测试/demo/组件)+ 质量防线。
546
522
  >
@@ -2,7 +2,7 @@
2
2
  * weifuwu/components — ActionSheet 动作面板(移动端底部滑出)
3
3
  *
4
4
  * 命令列表 + 取消按钮(iOS 风格)——移动端 App 常用弹层(照片选择/分享/
5
- * 更多操作)。与 Modal/Drawer 同款会话级模态(usePopup presence 退场状态机 +
5
+ * 更多操作)。与 Modal/Drawer 同款会话级模态(openPopup presence 退场状态机 +
6
6
  * 焦点 trap + 滚动锁)。
7
7
  *
8
8
  * 纪律:
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AiChat — 标准 AI 对话组件(useChat 的标准展示层)
3
3
  *
4
- * 组件库第一个领域复合组件:接收 ctx.ui.useChat() 返回的 handle
4
+ * 组件库第一个领域复合组件:接收 ctx.ui.useChat() 返回的 handle // audit-exempt: 用法示例文档(组件契约 = 接收 useChat handle)
5
5
  * 渲染完整对话界面:消息气泡、工具调用卡(ToolCallCard)、HITL 审批卡
6
6
  * (ApprovalCard)、思考/工具状态、usage、错误 + 重试、输入条 + 自动滚动。
7
7
  *
@@ -14,7 +14,7 @@
14
14
  * 文案默认中文(与 ToolCallCard/ApprovalCard 一致),labels 可覆盖。
15
15
  *
16
16
  * ```tsx
17
- * const $ = ctx.ui.useChat({ url: '/api/chat', approveUrl: '/api/approve' })
17
+ * const $ = ctx.ui.useChat({ url: '/api/chat', approveUrl: '/api/approve' }) // audit-exempt: 用法示例文档(组件契约 = 接收 useChat handle)
18
18
  * return () => h(AiChat, { chat: $ })
19
19
  * ```
20
20
  */
@@ -34,8 +34,7 @@ export interface AiChatLabels {
34
34
  empty: string;
35
35
  }
36
36
  export interface AiChatProps {
37
- /** ctx.ui.useChat() 返回的会话 handle(同一 $,状态变化自动重渲染) */
38
- chat: UseChatHandle;
37
+ /** ctx.ui.useChat() 返回的会话 handle(同一 $,状态变化自动重渲染) */ chat: UseChatHandle;
39
38
  /** 消息列表最大高度(默认 '70vh') */
40
39
  maxHeight?: string;
41
40
  /** 界面文案覆盖 */
@@ -49,6 +49,11 @@ export interface ChatInputProps {
49
49
  * 回调必须 mount 层定义(稳定引用——vdom3 props 剪枝);禁止 out-param 对象(props 不可变契约——
50
50
  * 原地写 control.current 触发 vdom3 audit) */
51
51
  onControl?: (handle: ChatInputControl) => void;
52
+ /** 按键拦截(@ 菜单键盘导航——2026-08):**onKeyIntercept**(非 onKeyDown——
53
+ * 组件 props 的 on+大写被 vdom 当 DOM 事件拦截(EVENT_RE——不传组件)——
54
+ * 实测 onKeyDown 静默失效——改名避开)——返回 true = 已处理(菜单开时
55
+ * ↑↓/Enter/Esc 消费方接管)——falsy = ChatInput 默认行为 */
56
+ onKeyInterceptFn?: (e: KeyboardEvent) => boolean | void;
52
57
  }
53
58
  export interface ChatInputControl {
54
59
  /** 直接写内部输入态(不触发 onChange——程序化改写如 @ 补全) */
@@ -32,7 +32,7 @@ export interface CitationCardProps {
32
32
  maxVisible?: number;
33
33
  /** 初始展开(默认折叠) */
34
34
  defaultExpanded?: boolean;
35
- /** 点击条目回调(提供时不渲染链接,由调用方处理跳转/打开) */
35
+ /** 点击条目回调(提供时 a 不带 href——role=button + onClick——跳转/打开由调用方处理) */
36
36
  onOpen?: (citation: Citation) => void;
37
37
  }
38
38
  export declare const CitationCard: Component<CitationCardProps, UIContext>;
@@ -18,5 +18,5 @@ export interface CommandProps {
18
18
  /** 全局快捷键,如 'mod+k'(cmd/ctrl + k);null 关闭全局监听 */
19
19
  globalShortcut?: string | null;
20
20
  }
21
- /** 命令面板(对应 shadcn Command):usePopup mask 全屏遮罩 + 搜索 + 键盘流(↑↓ Enter Escape)+ Cmd+K 全局快捷键 */
21
+ /** 命令面板(对应 shadcn Command):openPopup mask 全屏遮罩 + 搜索 + 键盘流(↑↓ Enter Escape)+ Cmd+K 全局快捷键 */
22
22
  export declare const Command: Component<CommandProps>;
@@ -14,6 +14,6 @@ export interface ContextMenuProps {
14
14
  className?: string;
15
15
  }
16
16
  /** 右键菜单(对应 shadcn ContextMenu):桌面右键 / 触屏长按 在光标处弹出,点外部/Escape 关闭,方向键导航。
17
- * 实现:ctx.ui.usePopup(longpress 触发 + 外部点击/Escape 关闭 + 自由定位光标处 + portal 视口 clamp)——
17
+ * 实现:openPopup 内核(longpress 触发 + 外部点击/Escape 关闭 + 自由定位光标处 + portal 视口 clamp)——
18
18
  * 不再自建 document 监听;onTrigger 记录光标坐标,position getter 供定位。 */
19
19
  export declare const ContextMenu: Component<ContextMenuProps>;
@@ -2,7 +2,7 @@
2
2
  * weifuwu/components — DatePicker
3
3
  *
4
4
  * 四合一日期选择器,支持 mode: date | datetime | time | range
5
- * 统一 usePopup:focus 触发 + mask 遮罩 + 自由定位(left 对齐 + width 跟随 trigger)。
5
+ * 统一 openPopup 内核:focus 触发 + mask 遮罩 + 自由定位(left 对齐 + width 跟随 trigger)。
6
6
  *
7
7
  * 状态管理:闭包变量 + ctx.render()
8
8
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * weifuwu/components — Dropdown
3
3
  *
4
- * usePopup 组合器:click 触发 + 受控 open + 外部点击/Escape(document 级,
4
+ * openPopup 内核:click 触发 + 受控 open + 外部点击/Escape(document 级,
5
5
  * 弹层在 portal 中按 Escape 也能关)+ 定位/视口 clamp + portal。
6
6
  */
7
7
  import type { Component } from '../../vdom/index.ts';
@@ -8,6 +8,16 @@
8
8
  * parse 需要 DOM(DOMParser)——浏览器环境全局可用;测试注入 jsdom document。
9
9
  */
10
10
  import type { DocState } from './types.ts';
11
+ /**
12
+ * URL 协议白名单(纯函数——node 直测)。
13
+ *
14
+ * http(s)/mailto/相对/锚/query 放行;javascript:/vbscript:/data: 阻断
15
+ * (data:image/* 仅 src 场景放行——粘贴截图)。
16
+ * scheme 判定前剥空白/控制字符(`java\tscript:` 类绕过阻断)。
17
+ *
18
+ * @returns 安全 URL(原样或剥控制字符)/ null(不安全——调用方删属性或降级空 href)
19
+ */
20
+ export declare function safeUrl(url: unknown, allowDataImage?: boolean): string | null;
11
21
  /** HTML → DocState(doc 可注入——jsdom 测试;浏览器默认全局) */
12
22
  export declare function parseHtml(html: string, doc?: Document): DocState;
13
23
  /** DocState → HTML(块标签 + 对齐 + marks + embeds;相邻 ul/ol 段合并列表) */
@@ -2,8 +2,12 @@
2
2
  * ImageCropper — 图片裁剪(canvas 原生 API + 拖拽裁剪框,零依赖)
3
3
  *
4
4
  * 用法:<ImageCropper src={url} aspect={1} onCrop={dataUrl => ...} />
5
- * 实现:图片绘制到 canvas → 裁剪框(useDrag 拖动/缩放)→ onCrop 输出 dataURL。
6
- * 纪律:canvas 浏览器 API 经 ctx.browser(SSR 无害——null 检查)。
5
+ * 实现:图片绘制到 canvas → 裁剪框(pointer 事件拖动/右下柄等比缩放)→
6
+ * onCrop 输出 dataURL。
7
+ * 纪律:canvas 浏览器 API 经 ctx.browser(SSR 无害——null 检查);
8
+ * 拖拽重绘直调 draw()(canvas 内部状态——非渲染路径,不走 ctx.render——
9
+ * 避免 vdom 全量 diff——2027-09 死交互实证修复:dragging/move/resize
10
+ * 曾为死代码——注释声称拖拽但从未接线)。
7
11
  */
8
12
  import type { Component } from '../../vdom/index.ts';
9
13
  export interface ImageCropperProps {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 导航菜单(侧栏导航):分组项 + 图标 + 选中态 + 方向键导航 + Enter 激活 + 子菜单 + 折叠。
5
5
  * 用于 SaaS 应用侧边导航(替代手写 nav-item 循环)。
6
- * 裁剪(CS-05,见 design/components-cuts.md):水平菜单栏(Menubar,独立组件)、子菜单自动互斥。折叠态子菜单浮层已实现(usePopup 基座)。
6
+ * 裁剪(CS-05,见 design/components-cuts.md):水平菜单栏(Menubar,独立组件)、子菜单自动互斥。折叠态子菜单浮层已实现(openPopup 内核基座)。
7
7
  */
8
8
  import type { Component } from '../../vdom/index.ts';
9
9
  export interface MenuItem {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * <NavMenu items={[{ key, label, children: [...] }]} activeKey onSelect />
8
8
  *
9
- * 弹层纪律():子菜单/嵌套子菜单全部经 usePopup 组合器
9
+ * 弹层纪律():子菜单/嵌套子菜单全部经 openPopup 内核
10
10
  * (createPortal + fixed + 视口夹紧 + Escape + 外部点击)——不 absolute 定位。
11
11
  *
12
12
  * 裁剪(CS-05,见 design/components-cuts.md):不做 hover 延迟微调/子菜单动画曲线定制;