weifuwu 0.86.0 → 0.87.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 -45
- package/content/apps/admin.md +55 -0
- package/content/apps/agent-platform.md +35 -0
- package/content/apps/auth.md +49 -0
- package/content/apps/multi.md +44 -0
- package/content/apps/todo.md +51 -0
- package/content/backend/ai.md +28 -0
- package/content/backend/auth.md +20 -0
- package/content/backend/email.md +23 -0
- package/content/backend/graphql.md +23 -0
- package/content/backend/http.md +23 -0
- package/content/backend/limit.md +23 -0
- package/content/backend/middleware.md +17 -0
- package/content/backend/queue.md +23 -0
- package/content/backend/redis.md +26 -0
- package/content/backend/router.md +23 -0
- package/content/backend/schedule.md +23 -0
- package/content/backend/sql.md +27 -0
- package/content/backend/sse.md +26 -0
- package/content/backend/ws.md +26 -0
- package/content/capabilities/app-node.md +21 -0
- package/content/capabilities/data.md +21 -0
- package/content/capabilities/events.md +21 -0
- package/content/capabilities/hooks.md +22 -0
- package/content/capabilities/i18n.md +21 -0
- package/content/capabilities/popup.md +21 -0
- package/content/capabilities/render-only.md +23 -0
- package/content/capabilities/router.md +22 -0
- package/content/capabilities/self-id.md +21 -0
- package/content/capabilities/store.md +21 -0
- package/content/capabilities/theme.md +21 -0
- package/content/capabilities/two-phase.md +21 -0
- package/content/components/accordion.md +50 -0
- package/content/components/actionsheet.md +46 -0
- package/content/components/affix.md +51 -0
- package/content/components/aichat.md +57 -0
- package/content/components/alert.md +52 -0
- package/content/components/alertgroup.md +45 -0
- package/content/components/anchor.md +52 -0
- package/content/components/app.md +37 -0
- package/content/components/approvalcard.md +56 -0
- package/content/components/appshell.md +50 -0
- package/content/components/aspectratio.md +47 -0
- package/content/components/authpage.md +61 -0
- package/content/components/autocomplete-v2.md +40 -0
- package/content/components/autocomplete.md +56 -0
- package/content/components/avatar.md +51 -0
- package/content/components/avatargroup.md +47 -0
- package/content/components/backtop.md +53 -0
- package/content/components/badge.md +55 -0
- package/content/components/breadcrumb.md +49 -0
- package/content/components/button.md +63 -0
- package/content/components/calendar-v2.md +40 -0
- package/content/components/calendar.md +50 -0
- package/content/components/card.md +58 -0
- package/content/components/carousel.md +52 -0
- package/content/components/cascader-v2.md +39 -0
- package/content/components/cascader.md +54 -0
- package/content/components/chart.md +53 -0
- package/content/components/chatinput.md +63 -0
- package/content/components/checkbox.md +51 -0
- package/content/components/checkboxgroup.md +52 -0
- package/content/components/citationcard.md +48 -0
- package/content/components/codeblock.md +48 -0
- package/content/components/codeeditor.md +47 -0
- package/content/components/collapse.md +49 -0
- package/content/components/colorpicker.md +50 -0
- package/content/components/command.md +50 -0
- package/content/components/confirm.md +57 -0
- package/content/components/contextmenu.md +48 -0
- package/content/components/copybutton.md +51 -0
- package/content/components/datepicker.md +52 -0
- package/content/components/descriptions-v2.md +39 -0
- package/content/components/descriptions.md +52 -0
- package/content/components/diffview-v2.md +39 -0
- package/content/components/diffview.md +50 -0
- package/content/components/divider.md +48 -0
- package/content/components/drawer.md +55 -0
- package/content/components/dropdown.md +53 -0
- package/content/components/editor.md +49 -0
- package/content/components/emptystate.md +51 -0
- package/content/components/exportcsv.md +38 -0
- package/content/components/field.md +54 -0
- package/content/components/filepreview-office.md +37 -0
- package/content/components/filepreview.md +49 -0
- package/content/components/filetree.md +56 -0
- package/content/components/fileupload-v2.md +39 -0
- package/content/components/fileupload.md +57 -0
- package/content/components/floatbutton.md +52 -0
- package/content/components/form-v2.md +41 -0
- package/content/components/form.md +60 -0
- package/content/components/grid.md +51 -0
- package/content/components/highlight-v2.md +39 -0
- package/content/components/highlight.md +46 -0
- package/content/components/hovercard.md +51 -0
- package/content/components/icon.md +50 -0
- package/content/components/imagecropper.md +45 -0
- package/content/components/img.md +56 -0
- package/content/components/infinitescroll-v2.md +40 -0
- package/content/components/infinitescroll.md +54 -0
- package/content/components/input.md +65 -0
- package/content/components/inputnumber.md +58 -0
- package/content/components/inview.md +56 -0
- package/content/components/jsonschemaform.md +53 -0
- package/content/components/jsonviewer-v2.md +39 -0
- package/content/components/jsonviewer.md +49 -0
- package/content/components/kanban.md +46 -0
- package/content/components/label.md +48 -0
- package/content/components/layout.md +54 -0
- package/content/components/layoutcontent.md +43 -0
- package/content/components/layoutheader.md +43 -0
- package/content/components/layoutsider.md +43 -0
- package/content/components/link.md +52 -0
- package/content/components/list.md +44 -0
- package/content/components/loading.md +45 -0
- package/content/components/logviewer-v2.md +39 -0
- package/content/components/logviewer.md +53 -0
- package/content/components/markdown.md +51 -0
- package/content/components/markdowneditor.md +47 -0
- package/content/components/math.md +42 -0
- package/content/components/mentions-v2.md +39 -0
- package/content/components/mentions.md +52 -0
- package/content/components/menu.md +56 -0
- package/content/components/menubar.md +44 -0
- package/content/components/messagebubble.md +50 -0
- package/content/components/modal.md +59 -0
- package/content/components/navmenu.md +47 -0
- package/content/components/notification.md +51 -0
- package/content/components/pageheader.md +53 -0
- package/content/components/pagination.md +49 -0
- package/content/components/paragraph.md +45 -0
- package/content/components/passwordinput.md +57 -0
- package/content/components/pininput-v2.md +39 -0
- package/content/components/pininput.md +50 -0
- package/content/components/pipeline.md +49 -0
- package/content/components/popconfirm.md +58 -0
- package/content/components/popover.md +61 -0
- package/content/components/progressbar.md +50 -0
- package/content/components/prompttemplate.md +49 -0
- package/content/components/qrcode.md +50 -0
- package/content/components/radiogroup.md +53 -0
- package/content/components/rate.md +53 -0
- package/content/components/reasoningblock.md +47 -0
- package/content/components/relationgraph.md +52 -0
- package/content/components/resizable.md +53 -0
- package/content/components/result.md +49 -0
- package/content/components/scrollbar.md +49 -0
- package/content/components/searchinput.md +51 -0
- package/content/components/segmentedcontrol.md +59 -0
- package/content/components/select-searchable.md +47 -0
- package/content/components/select.md +66 -0
- package/content/components/sessionlist.md +52 -0
- package/content/components/sheetgrid.md +44 -0
- package/content/components/skeleton.md +55 -0
- package/content/components/slidecanvas.md +44 -0
- package/content/components/slider.md +62 -0
- package/content/components/sortablelist.md +39 -0
- package/content/components/space.md +52 -0
- package/content/components/sparkline.md +52 -0
- package/content/components/statcard-countdown.md +39 -0
- package/content/components/statcard.md +56 -0
- package/content/components/steps.md +48 -0
- package/content/components/switch.md +50 -0
- package/content/components/tabbar.md +45 -0
- package/content/components/table-v2.md +40 -0
- package/content/components/table.md +66 -0
- package/content/components/tabs.md +56 -0
- package/content/components/tag.md +51 -0
- package/content/components/tagsinput-v2.md +39 -0
- package/content/components/tagsinput.md +54 -0
- package/content/components/text.md +45 -0
- package/content/components/textarea.md +57 -0
- package/content/components/themeswitch.md +69 -0
- package/content/components/timeline.md +50 -0
- package/content/components/title.md +45 -0
- package/content/components/toast.md +52 -0
- package/content/components/toggle-togglegroup.md +52 -0
- package/content/components/togglegroup.md +47 -0
- package/content/components/toolcallcard.md +51 -0
- package/content/components/tooltip.md +50 -0
- package/content/components/tour.md +50 -0
- package/content/components/transfer.md +52 -0
- package/content/components/tree-v2.md +39 -0
- package/content/components/tree.md +59 -0
- package/content/components/treeselect.md +54 -0
- package/content/components/typography.md +51 -0
- package/content/components/videoplayer.md +52 -0
- package/content/components/virtuallist.md +52 -0
- package/content/components/virtualtable-v2.md +39 -0
- package/content/components/virtualtable.md +55 -0
- package/content/components/watermark.md +53 -0
- package/content/components/wave.md +43 -0
- package/content/guides/ai-chat-family.md +24 -0
- package/content/guides/choose.md +67 -0
- package/content/guides/component-model.md +32 -0
- package/content/guides/component-standards.md +123 -0
- package/{docs/components.md → content/guides/components-guide.md} +31 -7
- package/{docs/custom-components.md → content/guides/custom-component.md} +70 -55
- package/{docs/data.md → content/guides/data-guide.md} +5 -2
- package/content/guides/file-preview-family.md +18 -0
- package/{docs → content/guides}/frontend.md +122 -52
- package/content/guides/layout-choice.md +64 -0
- package/{docs/layout.md → content/guides/layout-guide.md} +12 -8
- package/{docs/frontend-middleware.md → content/guides/middleware.md} +34 -3
- package/{docs/mobile.md → content/guides/mobile-guide.md} +20 -24
- package/content/guides/page-building.md +98 -0
- package/content/guides/production.md +37 -0
- package/content/guides/quality.md +60 -0
- package/{docs/realtime.md → content/guides/realtime-guide.md} +6 -3
- package/content/guides/render-only.md +44 -0
- package/{docs/saas.md → content/guides/saas-guide.md} +8 -5
- package/{docs/server.md → content/guides/server-guide.md} +5 -2
- package/content/guides/start.md +41 -0
- package/{docs → content/guides}/styling.md +5 -1
- package/{docs/frontend-ui-dom.md → content/guides/ui-dom-guide.md} +7 -1
- package/content/index.json +4344 -0
- package/content/index.md +181 -0
- package/content/layout/align.md +20 -0
- package/content/layout/anchor.md +19 -0
- package/content/layout/app-shell.md +23 -0
- package/content/layout/border.md +20 -0
- package/content/layout/center.md +19 -0
- package/content/layout/cluster.md +19 -0
- package/content/layout/container.md +19 -0
- package/content/layout/fill.md +19 -0
- package/content/layout/grid.md +19 -0
- package/content/layout/hidden.md +22 -0
- package/content/layout/layer.md +19 -0
- package/content/layout/position.md +22 -0
- package/content/layout/row.md +23 -0
- package/content/layout/safe-area.md +20 -0
- package/content/layout/scroll.md +21 -0
- package/content/layout/spacing.md +22 -0
- package/content/layout/split.md +19 -0
- package/content/layout/stack.md +19 -0
- package/content/layout/surface.md +23 -0
- package/content/layout/text.md +22 -0
- package/content/patterns/app-shell.md +34 -0
- package/content/patterns/dashboard.md +36 -0
- package/content/patterns/data-screen.md +30 -0
- package/content/patterns/detail-page.md +31 -0
- package/content/patterns/docs.md +34 -0
- package/content/patterns/focus-task.md +34 -0
- package/content/patterns/landing.md +35 -0
- package/content/patterns/list-page.md +32 -0
- package/content/patterns/mobile.md +31 -0
- package/content/patterns/settings-page.md +33 -0
- package/content/patterns/workspace.md +32 -0
- package/dist/cli/apps-typecheck.test.d.ts +11 -0
- package/dist/cli/docs.d.ts +2 -0
- package/dist/cli/docs.mjs +10961 -0
- package/dist/cli/home-flash.test.d.ts +1 -0
- package/dist/{components → client/components}/Accordion/Accordion.d.ts +1 -1
- package/dist/client/components/ActionSheet/ActionSheet.d.ts +37 -0
- package/dist/{components → client/components}/Affix/Affix.d.ts +1 -1
- package/dist/{components → client/components}/AiChat/AiChat.d.ts +3 -3
- package/dist/{components → client/components}/Alert/Alert.d.ts +1 -1
- package/dist/{components → client/components}/AlertGroup/AlertGroup.d.ts +1 -1
- package/dist/{components → client/components}/Anchor/Anchor.d.ts +1 -1
- package/dist/client/components/AppShell/AppShell.d.ts +55 -0
- package/dist/{components → client/components}/ApprovalCard/ApprovalCard.d.ts +2 -2
- package/dist/{components → client/components}/AspectRatio/AspectRatio.d.ts +1 -1
- package/dist/{components → client/components}/AuthPage/AuthPage.d.ts +2 -2
- package/dist/{components → client/components}/AutoComplete/AutoComplete.d.ts +1 -1
- package/dist/{components → client/components}/Avatar/Avatar.d.ts +1 -1
- package/dist/{components → client/components}/AvatarGroup/AvatarGroup.d.ts +1 -1
- package/dist/{components → client/components}/BackTop/BackTop.d.ts +1 -1
- package/dist/{components → client/components}/Badge/Badge.d.ts +1 -1
- package/dist/{components → client/components}/Breadcrumb/Breadcrumb.d.ts +1 -1
- package/dist/{components → client/components}/Button/Button.d.ts +1 -1
- package/dist/{components → client/components}/Calendar/Calendar.d.ts +1 -1
- package/dist/{components → client/components}/Card/Card.d.ts +1 -1
- package/dist/{components → client/components}/Carousel/Carousel.d.ts +1 -1
- package/dist/{components → client/components}/Cascader/Cascader.d.ts +1 -1
- package/dist/{components → client/components}/Chart/Chart.d.ts +1 -1
- package/dist/{components → client/components}/Chart/chart-utils.d.ts +5 -1
- package/dist/{components → client/components}/ChatInput/ChatInput.d.ts +7 -7
- package/dist/{components → client/components}/Checkbox/Checkbox.d.ts +1 -1
- package/dist/{components → client/components}/CheckboxGroup/CheckboxGroup.d.ts +1 -1
- package/dist/{components → client/components}/CitationCard/CitationCard.d.ts +3 -3
- package/dist/{components → client/components}/CodeBlock/CodeBlock.d.ts +1 -1
- package/dist/client/components/CodeEditor/CodeEditor.d.ts +18 -0
- package/dist/{components → client/components}/Collapse/Collapse.d.ts +1 -1
- package/dist/{components → client/components}/ColorPicker/ColorPicker.d.ts +1 -1
- package/dist/{components → client/components}/Command/Command.d.ts +1 -1
- package/dist/{components → client/components}/Confirm/Confirm.d.ts +2 -7
- package/dist/{components → client/components}/ContextMenu/ContextMenu.d.ts +1 -1
- package/dist/{components → client/components}/CopyButton/CopyButton.d.ts +1 -1
- package/dist/{components → client/components}/DatePicker/DatePicker.d.ts +2 -2
- package/dist/{components → client/components}/Descriptions/Descriptions.d.ts +1 -1
- package/dist/{components → client/components}/DiffView/DiffView.d.ts +1 -1
- package/dist/{components → client/components}/Divider/Divider.d.ts +1 -1
- package/dist/{components → client/components}/Drawer/Drawer.d.ts +1 -1
- package/dist/{components → client/components}/Dropdown/Dropdown.d.ts +1 -1
- package/dist/{components → client/components}/Editor/Editor.d.ts +1 -1
- package/dist/{components → client/components}/Editor/tools/toolbar.d.ts +1 -1
- package/dist/{components → client/components}/EmptyState/EmptyState.d.ts +1 -1
- package/dist/client/components/ExportCSV/ExportCSV.d.ts +30 -0
- package/dist/{components → client/components}/Field/Field.d.ts +1 -1
- package/dist/{components → client/components}/FilePreview/FilePreview.d.ts +1 -1
- package/dist/client/components/FileTree/FileTree.d.ts +57 -0
- package/dist/{components → client/components}/FileUpload/FileUpload.d.ts +1 -1
- package/dist/{components → client/components}/FloatButton/FloatButton.d.ts +1 -1
- package/dist/{components → client/components}/Form/Form.d.ts +1 -1
- package/dist/{components → client/components}/Grid/Grid.d.ts +1 -1
- package/dist/{components → client/components}/Highlight/Highlight.d.ts +1 -1
- package/dist/{components → client/components}/HoverCard/HoverCard.d.ts +5 -4
- package/dist/{components → client/components}/Icon/Icon.d.ts +2 -2
- package/dist/client/components/ImageCropper/ImageCropper.d.ts +17 -0
- package/dist/{components → client/components}/Img/Img.d.ts +1 -1
- package/dist/{components → client/components}/InView/InView.d.ts +1 -1
- package/dist/{components → client/components}/InfiniteScroll/InfiniteScroll.d.ts +1 -1
- package/dist/{components → client/components}/Input/Input.d.ts +1 -1
- package/dist/{components → client/components}/InputNumber/InputNumber.d.ts +1 -1
- package/dist/{components → client/components}/JSONViewer/JSONViewer.d.ts +1 -1
- package/dist/{components → client/components}/JsonSchemaForm/JsonSchemaForm.d.ts +3 -3
- package/dist/{components → client/components}/Kanban/Kanban.d.ts +1 -1
- package/dist/{components → client/components}/Label/Label.d.ts +1 -1
- package/dist/{components → client/components}/Layout/Layout.d.ts +1 -1
- package/dist/{components → client/components}/Link/Link.d.ts +1 -1
- package/dist/{components → client/components}/List/List.d.ts +1 -1
- package/dist/{components → client/components}/Loading/Loading.d.ts +1 -1
- package/dist/{components → client/components}/LogViewer/LogViewer.d.ts +1 -1
- package/dist/{components → client/components}/Markdown/Markdown.d.ts +1 -1
- package/dist/client/components/MarkdownEditor/MarkdownEditor.d.ts +18 -0
- package/dist/client/components/Math/Math.d.ts +15 -0
- package/dist/{components → client/components}/Mentions/Mentions.d.ts +1 -1
- package/dist/{components → client/components}/Menu/Menu.d.ts +1 -1
- package/dist/{components → client/components}/Menubar/Menubar.d.ts +1 -1
- package/dist/{components → client/components}/MessageBubble/MessageBubble.d.ts +1 -1
- package/dist/{components → client/components}/Modal/Modal.d.ts +1 -1
- package/dist/{components → client/components}/NavMenu/NavMenu.d.ts +2 -2
- package/dist/{components → client/components}/Notification/Notification.d.ts +7 -2
- package/dist/{components → client/components}/PageHeader/PageHeader.d.ts +1 -1
- package/dist/{components → client/components}/Pagination/Pagination.d.ts +1 -1
- package/dist/{components → client/components}/PasswordInput/PasswordInput.d.ts +1 -1
- package/dist/{components → client/components}/PinInput/PinInput.d.ts +1 -1
- package/dist/{components → client/components}/Pipeline/Pipeline.d.ts +1 -1
- package/dist/{components → client/components}/Popconfirm/Popconfirm.d.ts +2 -2
- package/dist/{components → client/components}/Popover/Popover.d.ts +5 -4
- package/dist/{components → client/components}/ProgressBar/ProgressBar.d.ts +1 -1
- package/dist/client/components/PromptTemplate/PromptTemplate.d.ts +37 -0
- package/dist/{components → client/components}/QRCode/QRCode.d.ts +1 -1
- package/dist/{components → client/components}/RadioGroup/RadioGroup.d.ts +1 -1
- package/dist/{components → client/components}/Rate/Rate.d.ts +1 -1
- package/dist/{components → client/components}/ReasoningBlock/ReasoningBlock.d.ts +3 -3
- package/dist/client/components/RelationGraph/RelationGraph.d.ts +59 -0
- package/dist/{components → client/components}/Resizable/Resizable.d.ts +1 -1
- package/dist/{components → client/components}/Result/Result.d.ts +1 -1
- package/dist/{components → client/components}/Scrollbar/Scrollbar.d.ts +1 -1
- package/dist/{components → client/components}/SearchInput/SearchInput.d.ts +1 -1
- package/dist/{components → client/components}/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/{components → client/components}/Select/Select.d.ts +1 -1
- package/dist/{components → client/components}/SessionList/SessionList.d.ts +3 -3
- package/dist/{components → client/components}/SheetGrid/SheetGrid.d.ts +1 -1
- package/dist/{components → client/components}/Skeleton/Skeleton.d.ts +1 -1
- package/dist/{components → client/components}/SlideCanvas/SlideCanvas.d.ts +1 -1
- package/dist/client/components/Slider/Slider.d.ts +27 -0
- package/dist/client/components/SortableList/SortableList.d.ts +23 -0
- package/dist/{components → client/components}/Space/Space.d.ts +1 -1
- package/dist/{components → client/components}/Sparkline/Sparkline.d.ts +1 -1
- package/dist/{components → client/components}/StatCard/StatCard.d.ts +1 -1
- package/dist/{components → client/components}/Steps/Steps.d.ts +1 -1
- package/dist/{components → client/components}/Switch/Switch.d.ts +1 -1
- package/dist/client/components/TabBar/TabBar.d.ts +35 -0
- package/dist/{components → client/components}/Table/Table.d.ts +7 -1
- package/dist/client/components/Tabs/Tabs.d.ts +22 -0
- package/dist/{components → client/components}/Tag/Tag.d.ts +1 -1
- package/dist/{components → client/components}/TagsInput/TagsInput.d.ts +1 -1
- package/dist/{components → client/components}/Textarea/Textarea.d.ts +1 -1
- package/dist/{components → client/components}/ThemeSwitch/ThemeSwitch.d.ts +1 -1
- package/dist/{components → client/components}/Timeline/Timeline.d.ts +1 -1
- package/dist/{components → client/components}/Toast/Toast.d.ts +1 -1
- package/dist/{components → client/components}/ToggleGroup/ToggleGroup.d.ts +1 -1
- package/dist/{components → client/components}/ToolCallCard/ToolCallCard.d.ts +2 -2
- package/dist/{components → client/components}/Tooltip/Tooltip.d.ts +4 -3
- package/dist/{components → client/components}/Tour/Tour.d.ts +1 -1
- package/dist/{components → client/components}/Transfer/Transfer.d.ts +1 -1
- package/dist/{components → client/components}/Tree/Tree.d.ts +5 -1
- package/dist/{components → client/components}/TreeSelect/TreeSelect.d.ts +5 -1
- package/dist/{components → client/components}/Typography/Typography.d.ts +1 -1
- package/dist/client/components/VideoPlayer/VideoPlayer.d.ts +25 -0
- package/dist/{components → client/components}/VirtualList/VirtualList.d.ts +1 -1
- package/dist/{components → client/components}/VirtualTable/VirtualTable.d.ts +1 -1
- package/dist/{components → client/components}/Watermark/Watermark.d.ts +1 -1
- package/dist/client/components/Wave/Wave.d.ts +15 -0
- package/dist/{components → client/components}/index.d.ts +44 -5
- package/dist/client/components/index.js +57 -0
- package/dist/{components → client/components}/style.css +1081 -98
- package/dist/{layout → client/layout}/weifuwu-layout.css +60 -6
- package/dist/{office → client/office}/xml-serialize.d.ts +1 -1
- package/dist/client/vdom/browser/Browser.d.ts +73 -0
- package/dist/client/vdom/browser/create-client-browser.d.ts +11 -0
- package/dist/client/vdom/commands.d.ts +19 -0
- package/dist/client/vdom/context/UIContext.d.ts +61 -0
- package/dist/client/vdom/context/data.d.ts +17 -0
- package/dist/client/vdom/core/async-guard.d.ts +20 -0
- package/dist/client/vdom/core/build.d.ts +36 -0
- package/dist/client/vdom/core/command/create.d.ts +25 -0
- package/dist/client/vdom/core/command/index.d.ts +15 -0
- package/dist/client/vdom/core/command/insert.d.ts +32 -0
- package/dist/client/vdom/core/command/lifecycle.d.ts +47 -0
- package/dist/client/vdom/core/command/props.d.ts +22 -0
- package/dist/client/vdom/core/diff/attrs.d.ts +10 -0
- package/dist/client/vdom/core/diff/children.d.ts +29 -0
- package/dist/client/vdom/core/diff/cleanup.d.ts +44 -0
- package/dist/client/vdom/core/diff/index.d.ts +22 -0
- package/dist/client/vdom/core/diff/output.d.ts +30 -0
- package/dist/client/vdom/core/diff/same.d.ts +22 -0
- package/dist/client/vdom/core/field/attributes.d.ts +15 -0
- package/dist/client/vdom/core/field/events.d.ts +54 -0
- package/dist/client/vdom/core/field/index.d.ts +19 -0
- package/dist/client/vdom/core/field/key.d.ts +16 -0
- package/dist/client/vdom/core/field/props.d.ts +15 -0
- package/dist/client/vdom/core/field/ref.d.ts +38 -0
- package/dist/client/vdom/core/field/style.d.ts +17 -0
- package/dist/client/vdom/core/node/children.d.ts +20 -0
- package/dist/client/vdom/core/node/component.d.ts +93 -0
- package/dist/client/vdom/core/node/fragment.d.ts +14 -0
- package/dist/client/vdom/core/node/hole.d.ts +22 -0
- package/dist/client/vdom/core/node/index.d.ts +35 -0
- package/dist/client/vdom/core/node/keyed.d.ts +64 -0
- package/dist/client/vdom/core/node/native.d.ts +20 -0
- package/dist/client/vdom/core/node/portal.d.ts +11 -0
- package/dist/client/vdom/core/patch/fields.d.ts +15 -0
- package/dist/client/vdom/core/patch/index.d.ts +51 -0
- package/dist/client/vdom/core/patch/processors.d.ts +75 -0
- package/dist/client/vdom/core/patch/state-machine.d.ts +38 -0
- package/dist/client/vdom/core/patch/verify.d.ts +17 -0
- package/dist/client/vdom/core/router.d.ts +31 -0
- package/dist/client/vdom/core/serve.d.ts +75 -0
- package/dist/client/vdom/core/ssr/absorb.d.ts +53 -0
- package/dist/client/vdom/core/ssr/html.d.ts +32 -0
- package/dist/client/vdom/core/ssr/index.d.ts +25 -0
- package/dist/client/vdom/core/transform/component.d.ts +23 -0
- package/dist/client/vdom/core/transform/element.d.ts +21 -0
- package/dist/client/vdom/core/transform/fragment.d.ts +21 -0
- package/dist/client/vdom/core/transform/hole.d.ts +13 -0
- package/dist/client/vdom/core/transform/index.d.ts +46 -0
- package/dist/client/vdom/core/transform/states.d.ts +22 -0
- package/dist/client/vdom/core/transform/table.d.ts +28 -0
- package/dist/client/vdom/core/transform/text.d.ts +10 -0
- package/dist/client/vdom/core/vnode.d.ts +45 -0
- package/dist/client/vdom/dev/effect-guard.d.ts +8 -0
- package/dist/client/vdom/hooks/ai-stream.d.ts +46 -0
- package/dist/client/vdom/hooks/basic.d.ts +42 -0
- package/dist/client/vdom/hooks/chat.d.ts +91 -0
- package/dist/client/vdom/hooks/controlled.d.ts +27 -0
- package/dist/client/vdom/hooks/drag-media.d.ts +52 -0
- package/dist/client/vdom/hooks/env.d.ts +119 -0
- package/dist/client/vdom/hooks/input.d.ts +43 -0
- package/dist/client/vdom/hooks/observe.d.ts +60 -0
- package/dist/client/vdom/hooks/popup-manager.d.ts +92 -0
- package/dist/client/vdom/hooks/popup.d.ts +52 -0
- package/dist/client/vdom/hooks/stable.d.ts +45 -0
- package/dist/client/vdom/index.d.ts +67 -0
- package/dist/client/vdom/index.js +8 -0
- package/dist/client/vdom/jsx-runtime.d.ts +28 -0
- package/dist/client/vdom/jsx-runtime.js +1 -0
- package/dist/client/vdom/middlewares/api.d.ts +38 -0
- package/dist/client/vdom/middlewares/auth-i18n.d.ts +66 -0
- package/dist/client/vdom/middlewares/ws.d.ts +37 -0
- package/dist/client/vdom/store.d.ts +49 -0
- package/dist/client/vdom/testing.d.ts +46 -0
- package/dist/client/vdom/testing.js +2 -0
- package/dist/dev/index.d.ts +12 -0
- package/dist/dev/index.js +4 -4
- package/dist/{ai → server/ai}/contracts.d.ts +1 -1
- package/dist/{ai → server/ai}/index.d.ts +2 -2
- package/dist/{ai → server/ai}/types.d.ts +1 -1
- package/dist/{core → server/core}/router.d.ts +2 -4
- package/dist/{db → server/db}/contracts.d.ts +1 -1
- package/dist/server/db/postgres-server.d.ts +37 -0
- package/dist/server/db/redis-server.d.ts +32 -0
- package/dist/server/db/server.d.ts +30 -0
- package/dist/server/db/test-servers.d.ts +21 -0
- package/dist/{email → server/email}/contracts.d.ts +1 -1
- package/dist/{index.js → server/index.js} +3145 -2487
- package/dist/{ui → server/ui}/index.d.ts +2 -2
- package/dist/{user → server/user}/token.d.ts +1 -1
- package/dist/shared/router/trie.d.ts +61 -0
- package/docs/components-map.md +5 -5
- package/docs/style-guide.md +14 -2
- package/examples/apps/admin/README.md +23 -0
- package/examples/apps/admin/api.ts +27 -0
- package/examples/apps/admin/app.tsx +144 -0
- package/examples/apps/admin/main.tsx +8 -0
- package/examples/apps/admin/server.ts +37 -0
- package/examples/apps/auth/README.md +23 -0
- package/examples/apps/auth/api.ts +49 -0
- package/examples/apps/auth/app.tsx +137 -0
- package/examples/apps/auth/main.tsx +8 -0
- package/examples/apps/auth/server.ts +37 -0
- package/examples/apps/multi/README.md +23 -0
- package/examples/apps/multi/app.tsx +83 -0
- package/examples/apps/multi/main.tsx +8 -0
- package/examples/apps/multi/server.ts +33 -0
- package/examples/apps/todo/README.md +23 -0
- package/examples/apps/todo/api.ts +26 -0
- package/examples/apps/todo/app.tsx +128 -0
- package/examples/apps/todo/main.tsx +9 -0
- package/examples/apps/todo/server.ts +39 -0
- package/examples/hello-world/README.md +9 -0
- package/examples/hello-world/client.ts +14 -0
- package/examples/hello-world/routes.tsx +26 -0
- package/examples/hello-world/server.ts +46 -0
- package/examples/patterns/AppShell.tsx +224 -0
- package/examples/patterns/Dashboard.tsx +153 -0
- package/examples/patterns/DataScreen.tsx +104 -0
- package/examples/patterns/DetailPage.tsx +79 -0
- package/examples/patterns/Docs.tsx +105 -0
- package/examples/patterns/FocusTask.tsx +67 -0
- package/examples/patterns/Landing.tsx +90 -0
- package/examples/patterns/ListPage.tsx +77 -0
- package/examples/patterns/Mobile.tsx +154 -0
- package/examples/patterns/SettingsPage.tsx +98 -0
- package/examples/patterns/SplitWorkspace.tsx +113 -0
- package/package.json +61 -29
- package/dist/components/Slider/Slider.d.ts +0 -19
- package/dist/components/Tabs/Tabs.d.ts +0 -12
- package/dist/components/index.js +0 -55
- package/dist/ui-dom/Confirm.d.ts +0 -8
- package/dist/ui-dom/Notification.d.ts +0 -8
- package/dist/ui-dom/Toast.d.ts +0 -8
- package/dist/ui-dom/ai.d.ts +0 -59
- package/dist/ui-dom/browser.d.ts +0 -10
- package/dist/ui-dom/debug.d.ts +0 -21
- package/dist/ui-dom/hooks/chat.d.ts +0 -11
- package/dist/ui-dom/hooks/events.d.ts +0 -30
- package/dist/ui-dom/hooks/external.d.ts +0 -20
- package/dist/ui-dom/hooks/index.d.ts +0 -29
- package/dist/ui-dom/hooks/input.d.ts +0 -33
- package/dist/ui-dom/hooks/media.d.ts +0 -17
- package/dist/ui-dom/hooks/popup.d.ts +0 -30
- package/dist/ui-dom/hooks/stable.d.ts +0 -35
- package/dist/ui-dom/hooks/types.d.ts +0 -77
- package/dist/ui-dom/i18n.d.ts +0 -31
- package/dist/ui-dom/index.d.ts +0 -47
- package/dist/ui-dom/index.js +0 -41
- package/dist/ui-dom/jsx-runtime.d.ts +0 -4
- package/dist/ui-dom/jsx-runtime.js +0 -1
- package/dist/ui-dom/locale/en_US.d.ts +0 -69
- package/dist/ui-dom/locale/zh_CN.d.ts +0 -69
- package/dist/ui-dom/middleware/api.d.ts +0 -91
- package/dist/ui-dom/middleware/auth.d.ts +0 -30
- package/dist/ui-dom/middleware/ws.d.ts +0 -33
- package/dist/ui-dom/motion.d.ts +0 -8
- package/dist/ui-dom/popup-tracker.d.ts +0 -36
- package/dist/ui-dom/popup.d.ts +0 -42
- package/dist/ui-dom/store.d.ts +0 -24
- package/dist/ui-dom/testing.d.ts +0 -68
- package/dist/ui-dom/testing.js +0 -7
- package/dist/ui-dom/types.d.ts +0 -634
- package/dist/ui-dom/use-chat.d.ts +0 -95
- package/dist/ui-dom/vdom3/README.d.ts +0 -32
- package/dist/ui-dom/vdom3/app.d.ts +0 -20
- package/dist/ui-dom/vdom3/audit.d.ts +0 -29
- package/dist/ui-dom/vdom3/build.d.ts +0 -20
- package/dist/ui-dom/vdom3/commands.d.ts +0 -21
- package/dist/ui-dom/vdom3/comp-index.d.ts +0 -18
- package/dist/ui-dom/vdom3/delegate.d.ts +0 -53
- package/dist/ui-dom/vdom3/events.d.ts +0 -17
- package/dist/ui-dom/vdom3/index.d.ts +0 -42
- package/dist/ui-dom/vdom3/jsx.d.ts +0 -32
- package/dist/ui-dom/vdom3/record.d.ts +0 -16
- package/dist/ui-dom/vdom3/registry.d.ts +0 -30
- package/dist/ui-dom/vdom3/render.d.ts +0 -39
- package/dist/ui-dom/vdom3/replay.d.ts +0 -28
- package/dist/ui-dom/vdom3/root.d.ts +0 -24
- package/dist/ui-dom/vdom3/router.d.ts +0 -31
- package/dist/ui-dom/vdom3/scheduler.d.ts +0 -23
- package/dist/ui-dom/vdom3/ssr.d.ts +0 -27
- package/dist/ui-dom/vdom3/sync.d.ts +0 -22
- package/dist/ui-dom/vdom3/types.d.ts +0 -173
- package/dist/ui-dom/vdom3/ui.d.ts +0 -16
- package/dist/ui-dom/vdom3.js +0 -41
- package/dist/ui-dom/vnode.d.ts +0 -13
- /package/dist/{make-executable-schema.test.d.ts → cli/content-sync.test.d.ts} +0 -0
- /package/dist/{ui-dom/testing.test.d.ts → cli/docs-cli.test.d.ts} +0 -0
- /package/dist/{components → client/components}/CodeBlock/highlight.d.ts +0 -0
- /package/dist/{components → client/components}/DatePicker/calendar-utils.d.ts +0 -0
- /package/dist/{components → client/components}/DiffView/diff-utils.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/edit-events.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/apply.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/diff.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/dom.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/history.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/html.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/inverse.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/model/types.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/tools/table.d.ts +0 -0
- /package/dist/{components → client/components}/Editor/tools/types.d.ts +0 -0
- /package/dist/{components → client/components}/FilePreview/markdown.d.ts +0 -0
- /package/dist/{components → client/components}/Markdown/parser.d.ts +0 -0
- /package/dist/{components → client/components}/OfficeEditor/ai/ai-bridge.d.ts +0 -0
- /package/dist/{components → client/components}/OfficeEditor/model/apply.d.ts +0 -0
- /package/dist/{components → client/components}/OfficeEditor/model/types.d.ts +0 -0
- /package/dist/{components → client/components}/Pipeline/dag-utils.d.ts +0 -0
- /package/dist/{components → client/components}/QRCode/qr.d.ts +0 -0
- /package/dist/{components → client/components}/Sparkline/sparkline-utils.d.ts +0 -0
- /package/dist/{office → client/office}/docx.d.ts +0 -0
- /package/dist/{office → client/office}/pptx.d.ts +0 -0
- /package/dist/{office → client/office}/xlsx.d.ts +0 -0
- /package/dist/{office → client/office}/xml.d.ts +0 -0
- /package/dist/{office → client/office}/zip.d.ts +0 -0
- /package/dist/{ai → server/ai}/agent.d.ts +0 -0
- /package/dist/{ai → server/ai}/client.d.ts +0 -0
- /package/dist/{ai → server/ai}/sse.d.ts +0 -0
- /package/dist/{core → server/core}/serve.d.ts +0 -0
- /package/dist/{core → server/core}/ws.d.ts +0 -0
- /package/dist/{db → server/db}/errors.d.ts +0 -0
- /package/dist/{db → server/db}/memory-redis.d.ts +0 -0
- /package/dist/{db → server/db}/memory-sql.d.ts +0 -0
- /package/dist/{db → server/db}/postgres/connection.d.ts +0 -0
- /package/dist/{db → server/db}/postgres/pool.d.ts +0 -0
- /package/dist/{db → server/db}/postgres/protocol.d.ts +0 -0
- /package/dist/{db → server/db}/postgres/schema.d.ts +0 -0
- /package/dist/{db → server/db}/query-builder.d.ts +0 -0
- /package/dist/{db → server/db}/query.d.ts +0 -0
- /package/dist/{db → server/db}/redis/client.d.ts +0 -0
- /package/dist/{db → server/db}/redis/commands.d.ts +0 -0
- /package/dist/{db → server/db}/redis/connection.d.ts +0 -0
- /package/dist/{db → server/db}/redis/pipeline.d.ts +0 -0
- /package/dist/{db → server/db}/redis/pool.d.ts +0 -0
- /package/dist/{db → server/db}/redis/resp.d.ts +0 -0
- /package/dist/{db → server/db}/redis/subscriber.d.ts +0 -0
- /package/dist/{db → server/db}/sql-parser.d.ts +0 -0
- /package/dist/{email → server/email}/index.d.ts +0 -0
- /package/dist/{email → server/email}/smtp.d.ts +0 -0
- /package/dist/{graphql.d.ts → server/graphql.d.ts} +0 -0
- /package/dist/{index.d.ts → server/index.d.ts} +0 -0
- /package/dist/{make-executable-schema.d.ts → server/make-executable-schema.d.ts} +0 -0
- /package/dist/{messager → server/messager}/index.d.ts +0 -0
- /package/dist/{middleware → server/middleware}/cors.d.ts +0 -0
- /package/dist/{middleware → server/middleware}/rate-limit.d.ts +0 -0
- /package/dist/{middleware → server/middleware}/static.d.ts +0 -0
- /package/dist/{postgres → server/postgres}/client.d.ts +0 -0
- /package/dist/{postgres → server/postgres}/index.d.ts +0 -0
- /package/dist/{postgres → server/postgres}/types.d.ts +0 -0
- /package/dist/{queue → server/queue}/index.d.ts +0 -0
- /package/dist/{redis → server/redis}/client.d.ts +0 -0
- /package/dist/{redis → server/redis}/index.d.ts +0 -0
- /package/dist/{redis → server/redis}/types.d.ts +0 -0
- /package/dist/{request.d.ts → server/request.d.ts} +0 -0
- /package/dist/{response.d.ts → server/response.d.ts} +0 -0
- /package/dist/{scheduler → server/scheduler}/cron.d.ts +0 -0
- /package/dist/{scheduler → server/scheduler}/index.d.ts +0 -0
- /package/dist/{types.d.ts → server/types.d.ts} +0 -0
- /package/dist/{ui → server/ui}/html-safe.d.ts +0 -0
- /package/dist/{user → server/user}/index.d.ts +0 -0
- /package/dist/{user → server/user}/password.d.ts +0 -0
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
import type { Middleware } from '../types.ts';
|
|
29
|
-
import type { Component } from '
|
|
30
|
-
declare module '
|
|
29
|
+
import type { Component } from '../../client/vdom/index.ts';
|
|
30
|
+
declare module '../../server/types.ts' {
|
|
31
31
|
interface Context {
|
|
32
32
|
ui: {
|
|
33
33
|
/** Tagged template → HTML Response */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 会话令牌(零依赖,node:crypto)
|
|
3
3
|
*
|
|
4
4
|
* access token:HMAC-SHA256 签名的 JWT 形态(base64url),
|
|
5
|
-
* 与 weifuwu/
|
|
5
|
+
* 与 weifuwu/vdom 的 auth() 兼容(客户端解码 payload 检查 exp)。
|
|
6
6
|
* refresh token:不透明随机串(256-bit),DB 只存 SHA-256 哈希(可撤销)。
|
|
7
7
|
*/
|
|
8
8
|
/** 签发 JWT access token(HS256) */
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu shared — Trie 路径匹配核心(前后端共用——零 HTTP 依赖)
|
|
3
|
+
*
|
|
4
|
+
* 来源:后端 Router(src/server/core/router.ts)的 Trie 匹配逻辑抽离——
|
|
5
|
+
* 真实事故语义保留:
|
|
6
|
+
* - **静态段优先** → :param → * 通配(O(path_segments) 匹配——确定性——
|
|
7
|
+
* 注册序优先静态)
|
|
8
|
+
* - **param 冲突检查**:同一位置 :id 与 :name 并存抛错(不静默)
|
|
9
|
+
* - **通配独立槽**:精确 value 与通配 value 互不覆盖(showcase SSR 事故——
|
|
10
|
+
* '/' + '/*' 并存时通配曾抢先精确 handler)
|
|
11
|
+
* - **根路径 '/'**:splitPath 过滤空段返回 []——value 绑 root 节点
|
|
12
|
+
*
|
|
13
|
+
* 泛型 value:后端 = method 表({ handlers, middlewares });前端 = 页面
|
|
14
|
+
* handler。匹配返回 { value, params }——params 注入由调用方决定
|
|
15
|
+
* (后端 ctx/前端 req)。
|
|
16
|
+
*/
|
|
17
|
+
/** 泛型 Trie 节点 */
|
|
18
|
+
export interface TrieNode<T> {
|
|
19
|
+
children: Map<string, TrieNode<T>>;
|
|
20
|
+
/** 参数段名(:id → 'id'——该子节点是参数槽) */
|
|
21
|
+
param?: string;
|
|
22
|
+
/** 通配段(*——匹配剩余任意段) */
|
|
23
|
+
wildcard?: boolean;
|
|
24
|
+
/** 精确路径 value(路径完全匹配) */
|
|
25
|
+
value?: T;
|
|
26
|
+
/** 通配路径 value(独立槽——不被精确覆盖;反之亦然) */
|
|
27
|
+
wildcardValue?: T;
|
|
28
|
+
}
|
|
29
|
+
export declare function createTrie<T>(): TrieNode<T>;
|
|
30
|
+
/** 路径分段('/a/b' → ['a','b']——空段过滤——根路径 '/' → []) */
|
|
31
|
+
export declare function splitPath(path: string): string[];
|
|
32
|
+
/**
|
|
33
|
+
* 查找路径终点节点(不含通配语义——用于冲突检查/多方法合并)——
|
|
34
|
+
* 遇 '*' 段返回当前节点(通配槽);未注册路径返回 null。
|
|
35
|
+
*
|
|
36
|
+
* **精确匹配纪律(真实事故——agent-platform 沙盒路由)**:静态段只匹配
|
|
37
|
+
* 静态子节点——参数段(:xxx)只匹配参数槽。此前静态段用 `children.get(':')`
|
|
38
|
+
* 兜底——`:id/debug` 会命中 `:id/:action` 参数槽(children key ':')——
|
|
39
|
+
* 冲突检查返回 POST action 节点——value.handlers.set('GET', debugHandler)
|
|
40
|
+
* **污染共享 value**(GET 写进 POST action 的 handlers)——后续
|
|
41
|
+
* `:id/<静态段>`(processes/stats)注册时再次命中 action 槽——误报
|
|
42
|
+
* `route conflict already registered`。
|
|
43
|
+
*/
|
|
44
|
+
export declare function trieFind<T>(root: TrieNode<T>, path: string): TrieNode<T> | null;
|
|
45
|
+
/**
|
|
46
|
+
* 注册(path → value)——分段插入:
|
|
47
|
+
* '/'(空段)→ value 绑 root;'*' 结尾 → wildcardValue(独立槽);
|
|
48
|
+
* 其余逐段 getOrCreateChild——终点绑 value。返回终点节点(冲突检查)。
|
|
49
|
+
*/
|
|
50
|
+
export declare function trieRegister<T>(root: TrieNode<T>, path: string, value: T, isWildcardValue?: boolean): TrieNode<T>;
|
|
51
|
+
/**
|
|
52
|
+
* 匹配(segments → { value, params, wildcard } | null):
|
|
53
|
+
* 精确优先(静态 → :param 逐段)——段失败或终端无 value → 通配兜底
|
|
54
|
+
* (从 root 沿路径检查任意前缀深度的通配注册——params['*'] = 剩余段);
|
|
55
|
+
* 精确命中且节点有通配槽('/' + '/*' 并存)→ params['*'] = ''(精确优先标记)。
|
|
56
|
+
*/
|
|
57
|
+
export declare function trieMatch<T>(root: TrieNode<T>, segments: string[]): {
|
|
58
|
+
value: T;
|
|
59
|
+
params: Record<string, string>;
|
|
60
|
+
wildcard: boolean;
|
|
61
|
+
} | null;
|
package/docs/components-map.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
| 标签页 | `<Tabs items active onChange>`(方向键 + 移动端横向滚动) | Tabs | Tabs | Tabs | 等价 |
|
|
26
26
|
| 分页 | `<Pagination total page onChange>`(页码折叠 + 移动端 44px) | Pagination | Pagination | Pagination | 等价 |
|
|
27
27
|
| 步骤条 | `<Steps items current>` | Steps | Steps | — | 等价 |
|
|
28
|
-
| 下拉菜单 | `<Dropdown items>`(danger variant) | Dropdown | Dropdown | DropdownMenu | 等价(
|
|
28
|
+
| 下拉菜单 | `<Dropdown items>`(danger variant) | Dropdown | Dropdown | DropdownMenu | 等价(openPopup:外部点击/Escape 关闭) |
|
|
29
29
|
| 水平菜单栏 | `<Menubar menus>`(←→↓ 键盘 + 触屏) | Menu 水平 | Menu | Menubar | 等价 |
|
|
30
30
|
| 页面标题 | `<PageHeader title sub>` | PageHeader(v5 移除) | PageHeader | — | weifuwu 保留独立组件 |
|
|
31
31
|
| 回到顶部 | `<BackTop threshold>` | FloatButton / BackTop | Backtop | — | 滚动超阈值显示 |
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
| 模态框 | `<Modal open title onClose width>` | Modal | Dialog | Dialog | `open/onClose`(antd v5 同);焦点 trap + 归还;移动端 bottom-sheet |
|
|
90
90
|
| 确认框 | `<Confirm>` + `await confirm()` 命令式 | Popconfirm / Modal.confirm | Popconfirm / MessageBox | AlertDialog | 声明式 + 命令式双模式 |
|
|
91
91
|
| 抽屉 | `<Drawer position="left\|right\|top\|bottom">` | Drawer | Drawer | Sheet / Drawer | **四方向覆盖 shadcn Sheet + Drawer**;移动端全宽 |
|
|
92
|
-
| 气泡 | `<Popover position>`(视口夹紧) | Popover | Popover | Popover | 等价(
|
|
93
|
-
| 悬浮提示 | `<Tooltip content position>`(string) | Tooltip | Tooltip | Tooltip |
|
|
92
|
+
| 气泡 | `<Popover position>`(视口夹紧) | Popover | Popover | Popover | 等价(openPopup:外部点击/Escape/tap 降级) |
|
|
93
|
+
| 悬浮提示 | `<Tooltip content position>`(string) | Tooltip | Tooltip | Tooltip | openPopup:桌面 hover / 触屏 tap |
|
|
94
94
|
| 悬停卡 | `<HoverCard content>`(富内容 + 延迟) | — | — | HoverCard | shadcn 对齐;触屏 tap |
|
|
95
95
|
| 右键菜单 | `<ContextMenu items>` | Dropdown context | — | ContextMenu | 桌面右键 + **触屏长按**双通道 |
|
|
96
96
|
| 消息 | `<Toast>` + `toast()` 命令式 | message | Message | Sonner | 命令式 `toast('已保存','success')` |
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
## 第十批:文件预览与 Office 编辑(2026-12——ODES 事件流)
|
|
132
132
|
|
|
133
133
|
> 文档 = fold(事件流):docx/xlsx/pptx ↔ ODES 模型(DocState/WorkbookState/DeckState)
|
|
134
|
-
> 前端零依赖转换(自研 ZIP/XML + DecompressionStream)——详见
|
|
134
|
+
> 前端零依赖转换(自研 ZIP/XML + DecompressionStream)——详见 content/guides/components-guide.md。
|
|
135
135
|
|
|
136
136
|
| 组件 | 说明 |
|
|
137
137
|
|------|------|
|
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
| 组件 | 类型 | 三库等价 | 要点 |
|
|
213
213
|
|------|------|---------|------|
|
|
214
214
|
| **Layout**(+LayoutHeader/Sider/Content/Footer) | 新增 | antd Layout / EP Container / shadcn Sidebar | 复合子组件双模式;含 Sider → row 布局;Sider 折叠受控/非受控 + trigger |
|
|
215
|
-
| **Popconfirm** | 新增 | antd / EP Popconfirm |
|
|
215
|
+
| **Popconfirm** | 新增 | antd / EP Popconfirm | openPopup 命令式弹窗;danger 危险色;确认后自动关闭 |
|
|
216
216
|
| **AutoComplete** | 新增 | antd+EP Autocomplete / shadcn Combobox | 自由输入联想;包含过滤(纯函数);键盘 ↓↑/Enter/Escape;选中回填;error 错误态 |
|
|
217
217
|
| **Link** | 新增 | EP Link / antd Typography.Link | 语义色/下划线/disabled/new window/icon |
|
|
218
218
|
| **FloatButton** | 新增 | antd FloatButton(特有) | fixed 定位 + badge + 组展开状态机 |
|
package/docs/style-guide.md
CHANGED
|
@@ -15,7 +15,7 @@ weifuwu 的设计语言与框架哲学同源(确定性、诚实裁剪、render
|
|
|
15
15
|
| 4 | **专业** Instrumental | 面向 AI 应用/管理后台/数据工具:键盘可达、数据密度、三态完整 | 键盘焦点全局;compact 预设;加载/空/错误三态规范 |
|
|
16
16
|
| 5 | **中文原生** CJK-Native | 字阶/行高/断行针对中文;表头不做 uppercase;数字防抖 | `--wf-heading-case: none`;`wf-nums`(tabular-nums) |
|
|
17
17
|
|
|
18
|
-
主题配置三档(详见 [
|
|
18
|
+
主题配置三档(详见 [content/guides/styling.md](styling.md)):**① 改 `--wf-brand-seed` 一个值换肤**(色阶 color-mix 自动派生,暗色自动跟随)→ **② `<html data-preset="minimal|compact|rounded">` 预设**(与 data-theme 正交)→ **③ 深度定制**(组件钩子 + @layer 覆盖)。
|
|
19
19
|
|
|
20
20
|
## 统一语法:`wf-<域>-<名字>`
|
|
21
21
|
|
|
@@ -157,6 +157,18 @@ wf-rounded-md 圆角
|
|
|
157
157
|
|
|
158
158
|
完整钩子清单:`--wf-btn-*` `--wf-card-*` `--wf-field-*` `--wf-modal-*` `--wf-drawer-width` `--wf-toast-*` `--wf-alert-radius` `--wf-badge-radius` `--wf-tag-radius` `--wf-switch-radius` `--wf-popover-*` `--wf-tooltip-radius` `--wf-dropdown-min-width` `--wf-datepicker-*`。
|
|
159
159
|
|
|
160
|
+
### 表面语言(微流明·边界即结构)
|
|
161
|
+
|
|
162
|
+
静态卡片 = **1px 细边框**(`--wf-color-border`)——阴影仅用于抬升语义(hover/浮层/拖拽中);卡片 hover 抬升用 `wf-elevate`。旧阴影观感可覆盖 `--wf-card-shadow` 恢复。浮层按层级用圆角矩阵:
|
|
163
|
+
|
|
164
|
+
| 表面 | 圆角 | 底 | 说明 |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| Card / StatCard 默认 | --wf-radius-md (8) | bg | 细边框;阴影仅 hover(wf-elevate) |
|
|
167
|
+
| Modal | **--wf-radius-lg (12)** | bg-elevated | 抬升语义 |
|
|
168
|
+
| 面板族(dropdown/select/datepicker/popover) | --wf-field-radius | bg-elevated | 弹出面板 |
|
|
169
|
+
| Tooltip / 小浮层 | --wf-radius-sm (4) | bg-elevated | 摘要层 |
|
|
170
|
+
| Drawer | 0(贴边) | bg-elevated | 全幅侧拉 |
|
|
171
|
+
|
|
160
172
|
### 动效定制 — 时长/缓动/位移
|
|
161
173
|
|
|
162
174
|
```html
|
|
@@ -177,7 +189,7 @@ wf-rounded-md 圆角
|
|
|
177
189
|
用户 @layer utilities 可精准盖过 weifuwu 的 utilities
|
|
178
190
|
```
|
|
179
191
|
|
|
180
|
-
## 主题 Token(
|
|
192
|
+
## 主题 Token(182 个,双层)
|
|
181
193
|
|
|
182
194
|
- **原始层**(`--wf-brand-*` `--wf-slate-*` `--wf-dark-*`):色值只定义一次,品牌/暗色调校改这里
|
|
183
195
|
- **语义层**(`--wf-color-*` `--wf-space-*` `--wf-radius-*` …):组件消费,主题切换覆盖这里
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# admin 应用模板(复制即用)
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
# 1. 安装依赖(weifuwu 包——含 content/ 离线文档 + examples/ 模板源码)
|
|
5
|
+
npm i weifuwu
|
|
6
|
+
|
|
7
|
+
# 2. 复制本目录到你的项目(或直接在 examples/apps/admin 内开发)
|
|
8
|
+
# 3. 启动
|
|
9
|
+
node server.ts # → http://localhost:admin/3300 3301 3302 3303
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 改造指南
|
|
13
|
+
|
|
14
|
+
见 `node_modules/weifuwu/content/apps/admin.md`(目录结构 + 逐步改造步骤)。
|
|
15
|
+
|
|
16
|
+
## 结构
|
|
17
|
+
|
|
18
|
+
| 文件 | 职责 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| app.tsx | 前端:路由表 + 页面组件 + 状态 |
|
|
21
|
+
| api.ts | 后端:API 注册函数(独立/嵌入共享) |
|
|
22
|
+
| server.ts | 独立入口(MemorySql——生产换 postgres() 一行) |
|
|
23
|
+
| main.tsx | 独立前端入口 |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 后端 API——订单表(注册函数:独立 server 与 showcase 嵌入共享)
|
|
3
|
+
*/
|
|
4
|
+
import type { Router } from 'weifuwu'
|
|
5
|
+
|
|
6
|
+
export function registerAdminApi(app: Router, sql: any): void {
|
|
7
|
+
app.get('/api/admin/orders', async () => {
|
|
8
|
+
const rows = await sql`SELECT * FROM admin_orders ORDER BY id DESC LIMIT 50`
|
|
9
|
+
return Response.json({ rows })
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** 建表 + 种子数据(独立/嵌入共用) */
|
|
14
|
+
export async function ensureAdminTables(sql: any): Promise<void> {
|
|
15
|
+
await sql.unsafe('CREATE TABLE IF NOT EXISTS admin_orders (id serial PRIMARY KEY, customer text, amount numeric, status text, date text)')
|
|
16
|
+
const rows = await sql`SELECT COUNT(*) AS n FROM admin_orders`
|
|
17
|
+
if (Number(rows[0]?.n) > 0) return
|
|
18
|
+
const seed = [
|
|
19
|
+
['张伟', 1280, 'paid', '2026-08-10'], ['李娜', 560, 'pending', '2026-08-11'],
|
|
20
|
+
['王强', 3200, 'shipped', '2026-08-12'], ['赵敏', 890, 'paid', '2026-08-13'],
|
|
21
|
+
['陈杰', 2100, 'pending', '2026-08-14'], ['刘洋', 450, 'paid', '2026-08-15'],
|
|
22
|
+
['孙丽', 1750, 'shipped', '2026-08-16'], ['周涛', 980, 'paid', '2026-08-17'],
|
|
23
|
+
]
|
|
24
|
+
for (const [customer, amount, status, date] of seed) {
|
|
25
|
+
await sql`INSERT INTO admin_orders (customer, amount, status, date) VALUES (${customer}, ${amount}, ${status}, ${date})`
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 应用模板——管理后台(AppShell + 多页 + 表格,完整全栈复制即用)
|
|
3
|
+
*
|
|
4
|
+
* 演示能力:
|
|
5
|
+
* - Layout 组件骨架(Sider 折叠 + Header + Content)+ 路由 layout 包裹复用
|
|
6
|
+
* - Table 排序 + StatCard KPI + 菜单导航(源于 agent-platform 架构提炼)
|
|
7
|
+
* - 后端:MemorySql 订单表(api.ts 注册函数——独立/嵌入共享)
|
|
8
|
+
*/
|
|
9
|
+
import { UIRouter, uiServe, h, createStore, createClientBrowser } from 'weifuwu/vdom'
|
|
10
|
+
import type { Component } from 'weifuwu/vdom'
|
|
11
|
+
import { Layout, LayoutHeader, LayoutSider, LayoutContent, Menu, StatCard, Table, Badge, Tag, ThemeSwitch, PageHeader, EmptyState } from 'weifuwu/components'
|
|
12
|
+
|
|
13
|
+
export interface Order { id: string; customer: string; amount: number; status: 'pending' | 'paid' | 'shipped'; date: string }
|
|
14
|
+
|
|
15
|
+
export const adminStore = createStore<{ orders: Order[]; loading: boolean }>({ orders: [], loading: true })
|
|
16
|
+
|
|
17
|
+
export async function loadOrders(): Promise<void> {
|
|
18
|
+
const res = await fetch('/api/admin/orders')
|
|
19
|
+
const data = await res.json()
|
|
20
|
+
adminStore.update((s) => { s.orders = data.rows; s.loading = false })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const statusTag = (s: Order['status']) =>
|
|
24
|
+
s === 'paid' ? <Tag variant="success">已支付</Tag> : s === 'shipped' ? <Tag variant="primary">已发货</Tag> : <Tag>待支付</Tag>
|
|
25
|
+
|
|
26
|
+
/** 仪表盘页 */
|
|
27
|
+
export const DashboardPage: Component = async (_init: any, ctx: any) => {
|
|
28
|
+
void loadOrders()
|
|
29
|
+
const state = ctx.ui.useExternal(adminStore)
|
|
30
|
+
const paid = state.orders.filter((o: Order) => o.status === 'paid' || o.status === 'shipped')
|
|
31
|
+
const total = state.orders.reduce((a: number, o: Order) => a + o.amount, 0)
|
|
32
|
+
return async (_p: any) => (
|
|
33
|
+
<div class="wf-stack wf-gap-md">
|
|
34
|
+
<PageHeader title="仪表盘" sub="KPI + 订单概览(数据来自 MemorySql)" />
|
|
35
|
+
<div class="wf-grid" style="--wf-cols:repeat(auto-fit,minmax(180px,1fr));--wf-gap:12px">
|
|
36
|
+
<StatCard label="订单总数" value={state.orders.length} />
|
|
37
|
+
<StatCard label="已支付/发货" value={paid.length} />
|
|
38
|
+
<StatCard label="销售总额" value={`¥${total.toLocaleString()}`} />
|
|
39
|
+
<StatCard label="待处理" value={state.orders.filter((o: Order) => o.status === 'pending').length} />
|
|
40
|
+
</div>
|
|
41
|
+
{state.loading ? <div class="wf-text-secondary">加载中…</div> : (
|
|
42
|
+
<Table
|
|
43
|
+
columns={[
|
|
44
|
+
{ key: 'id', label: '订单号' },
|
|
45
|
+
{ key: 'customer', label: '客户' },
|
|
46
|
+
{ key: 'amount', label: '金额', sortable: true, render: (v: any) => `¥${Number(v).toLocaleString()}` },
|
|
47
|
+
{ key: 'date', label: '日期' },
|
|
48
|
+
{ key: 'status', label: '状态', render: (v: any) => statusTag(v) },
|
|
49
|
+
]}
|
|
50
|
+
data={state.orders}
|
|
51
|
+
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 订单页(全量表格 + 筛选) */
|
|
59
|
+
export const OrdersPage: Component = async (_init: any, ctx: any) => {
|
|
60
|
+
void loadOrders()
|
|
61
|
+
const state = ctx.ui.useExternal(adminStore)
|
|
62
|
+
let q = ''
|
|
63
|
+
return async (_p: any) => {
|
|
64
|
+
const list = state.orders.filter((o: Order) => !q || o.customer.toLowerCase().includes(q.toLowerCase()) || o.id.includes(q))
|
|
65
|
+
return (
|
|
66
|
+
<div class="wf-stack wf-gap-md">
|
|
67
|
+
<PageHeader title="订单管理" sub={`共 ${list.length} 条`}>
|
|
68
|
+
<input class="wf-input" style="max-width:200px" placeholder="搜索客户/订单号…" value={q}
|
|
69
|
+
onInput={(e: any) => { q = (e.target as HTMLInputElement).value; ctx.render() }} />
|
|
70
|
+
</PageHeader>
|
|
71
|
+
{list.length === 0 && !state.loading ? <EmptyState text="无匹配订单" /> : (
|
|
72
|
+
<Table
|
|
73
|
+
columns={[
|
|
74
|
+
{ key: 'id', label: '订单号' },
|
|
75
|
+
{ key: 'customer', label: '客户', sortable: true },
|
|
76
|
+
{ key: 'amount', label: '金额', sortable: true, render: (v: any) => `¥${Number(v).toLocaleString()}` },
|
|
77
|
+
{ key: 'date', label: '日期' },
|
|
78
|
+
{ key: 'status', label: '状态', render: (v: any) => statusTag(v) },
|
|
79
|
+
]}
|
|
80
|
+
data={list}
|
|
81
|
+
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── 路由(layout 包裹 = AppShell 复用——跨页保持侧栏/折叠状态) ──
|
|
90
|
+
export const adminRoutes = [
|
|
91
|
+
{ path: '/', render: () => h(DashboardPage, {}) },
|
|
92
|
+
{ path: '/orders', render: () => h(OrdersPage, {}) },
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
const AppShell: Component = async (_init: any, ctx: any) => {
|
|
96
|
+
let collapsed = false
|
|
97
|
+
const current = () => location.hash.replace('#/', '') || '/'
|
|
98
|
+
return async (props: any) => (
|
|
99
|
+
<Layout style="min-height:420px;border:1px solid var(--wf-color-border);border-radius:8px;overflow:hidden">
|
|
100
|
+
<LayoutSider collapsed={collapsed} collapsible onCollapse={(c) => { collapsed = c; ctx.render() }} width={200}>
|
|
101
|
+
<div style={{ background: "var(--wf-color-bg-secondary)", height: "100%" }}>
|
|
102
|
+
<div class="wf-p-sm wf-text-bold" style="color:var(--wf-color-text)">⚙️ 管理后台</div>
|
|
103
|
+
<Menu
|
|
104
|
+
items={[
|
|
105
|
+
{ key: '/', label: '仪表盘' },
|
|
106
|
+
{ key: '/orders', label: '订单管理' },
|
|
107
|
+
]}
|
|
108
|
+
activeKey={current()}
|
|
109
|
+
collapsible
|
|
110
|
+
collapsed={collapsed}
|
|
111
|
+
onCollapseChange={(c) => { collapsed = c; ctx.render() }}
|
|
112
|
+
onSelect={(k: string) => { location.hash = `#/${k === '/' ? '' : k}` }}
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
</LayoutSider>
|
|
116
|
+
<Layout>
|
|
117
|
+
<LayoutHeader style="display:flex;align-items:center;justify-content:flex-end;gap:8px">
|
|
118
|
+
<Badge variant="success">admin 模板</Badge>
|
|
119
|
+
<ThemeSwitch />
|
|
120
|
+
</LayoutHeader>
|
|
121
|
+
<LayoutContent style="padding:16px">{props.page}</LayoutContent>
|
|
122
|
+
</Layout>
|
|
123
|
+
</Layout>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const adminAppShell = AppShell
|
|
128
|
+
|
|
129
|
+
export function createAdminApp(root: HTMLElement, options?: { history?: boolean }): ReturnType<typeof uiServe> {
|
|
130
|
+
// vdom 规范面:UIRouter(Trie 匹配)+ uiServe(渲染落地)——布局共享
|
|
131
|
+
// (root 稳定——AppShell 包裹——精准路由切换)
|
|
132
|
+
const router = new UIRouter()
|
|
133
|
+
for (const r of adminRoutes) {
|
|
134
|
+
router.get(r.path, (req: Request, ctx: any) =>
|
|
135
|
+
(ctx as { stream: (v: unknown) => Response }).stream(h(AppShell, { page: r.render() })))
|
|
136
|
+
}
|
|
137
|
+
return uiServe(router, {
|
|
138
|
+
root,
|
|
139
|
+
browser: createClientBrowser()!,
|
|
140
|
+
...(options?.history === false ? {} : {}),
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const pathFromHash = (): string => location.hash.replace(/^#/, '') || '/'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 独立入口——复制本目录即可运行:
|
|
3
|
+
* cd examples/apps/admin && node server.ts → http://localhost:3302
|
|
4
|
+
*/
|
|
5
|
+
import { createAdminApp, pathFromHash } from './app.tsx'
|
|
6
|
+
|
|
7
|
+
const sub = createAdminApp(document.querySelector('#root') as HTMLElement)
|
|
8
|
+
window.addEventListener('hashchange', () => sub.navigate(pathFromHash()))
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin 独立 server——MemorySql 订单表(生产换 postgres())
|
|
3
|
+
* cd examples/apps/admin && node server.ts → http://localhost:3302
|
|
4
|
+
*/
|
|
5
|
+
import { serve, Router, ui, createMemorySql } from 'weifuwu'
|
|
6
|
+
import { resolve, dirname } from 'node:path'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
import { registerAdminApi, ensureAdminTables } from './api.ts'
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
11
|
+
const sql = createMemorySql()
|
|
12
|
+
await ensureAdminTables(sql)
|
|
13
|
+
|
|
14
|
+
const app = new Router()
|
|
15
|
+
app.use(ui())
|
|
16
|
+
app.get('/app.js', (req, ctx) => ctx.ui.js(resolve(__dirname, 'main.tsx')))
|
|
17
|
+
registerAdminApi(app, sql)
|
|
18
|
+
|
|
19
|
+
app.get('/*', async (req, ctx) => ctx.ui.html`
|
|
20
|
+
<!DOCTYPE html>
|
|
21
|
+
<html lang="zh-CN">
|
|
22
|
+
<head>
|
|
23
|
+
<meta charset="UTF-8">
|
|
24
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
25
|
+
<link rel="stylesheet" href="/components.css">
|
|
26
|
+
<title>admin 应用模板</title>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div id="root"></div>
|
|
30
|
+
<script src="/app.js"></script>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
`)
|
|
34
|
+
app.get('/components.css', (req, ctx) => ctx.ui.css('weifuwu/components/style.css'))
|
|
35
|
+
|
|
36
|
+
serve(app, { port: 3302 })
|
|
37
|
+
console.log('admin 模板 → http://localhost:3302')
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# auth 应用模板(复制即用)
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
# 1. 安装依赖(weifuwu 包——含 content/ 离线文档 + examples/ 模板源码)
|
|
5
|
+
npm i weifuwu
|
|
6
|
+
|
|
7
|
+
# 2. 复制本目录到你的项目(或直接在 examples/apps/auth 内开发)
|
|
8
|
+
# 3. 启动
|
|
9
|
+
node server.ts # → http://localhost:auth/3300 3301 3302 3303
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 改造指南
|
|
13
|
+
|
|
14
|
+
见 `node_modules/weifuwu/content/apps/auth.md`(目录结构 + 逐步改造步骤)。
|
|
15
|
+
|
|
16
|
+
## 结构
|
|
17
|
+
|
|
18
|
+
| 文件 | 职责 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| app.tsx | 前端:路由表 + 页面组件 + 状态 |
|
|
21
|
+
| api.ts | 后端:API 注册函数(独立/嵌入共享) |
|
|
22
|
+
| server.ts | 独立入口(MemorySql——生产换 postgres() 一行) |
|
|
23
|
+
| main.tsx | 独立前端入口 |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth 后端 API——内存用户表 + 会话(注册函数:独立 server 与 showcase 嵌入共享)
|
|
3
|
+
*/
|
|
4
|
+
import type { Router } from 'weifuwu'
|
|
5
|
+
import { createMemorySql } from 'weifuwu'
|
|
6
|
+
|
|
7
|
+
export interface AuthUser { id: string; email: string; name: string }
|
|
8
|
+
|
|
9
|
+
export function registerAuthApi(app: Router, sql: any): void {
|
|
10
|
+
// 内存会话表(token → userId)——演示用;生产换 ctx.auth/userSystem
|
|
11
|
+
const sessions = new Map<string, string>()
|
|
12
|
+
let seq = 0
|
|
13
|
+
|
|
14
|
+
app.post('/api/auth/register', async (req: Request) => {
|
|
15
|
+
const { email, password, name } = await req.json()
|
|
16
|
+
if (!String(email ?? '').includes('@')) return Response.json({ ok: false, error: '邮箱格式无效' })
|
|
17
|
+
if (String(password ?? '').length < 6) return Response.json({ ok: false, error: '密码至少 6 位' })
|
|
18
|
+
const rows = await sql`INSERT INTO auth_users (email, password, name) VALUES (${String(email)}, ${String(password)}, ${String(name ?? email.split('@')[0])}) RETURNING id, email, name`
|
|
19
|
+
const user = rows[0]
|
|
20
|
+
const token = `tok_${++seq}_${user.id}`
|
|
21
|
+
sessions.set(token, String(user.id))
|
|
22
|
+
return Response.json({ ok: true, user, token }, { status: 201 })
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
app.post('/api/auth/login', async (req: Request) => {
|
|
26
|
+
const { email, password } = await req.json()
|
|
27
|
+
const rows = await sql`SELECT * FROM auth_users WHERE email = ${String(email)} AND password = ${String(password)}`
|
|
28
|
+
if (!rows.length) return Response.json({ ok: false, error: '邮箱或密码错误' }, { status: 401 })
|
|
29
|
+
const user = rows[0]
|
|
30
|
+
const token = `tok_${++seq}_${user.id}`
|
|
31
|
+
sessions.set(token, String(user.id))
|
|
32
|
+
return Response.json({ ok: true, user, token })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
app.get('/api/auth/me', async (req: Request) => {
|
|
36
|
+
const auth = req.headers.get('authorization') ?? ''
|
|
37
|
+
const token = auth.replace('Bearer ', '')
|
|
38
|
+
const userId = sessions.get(token)
|
|
39
|
+
if (!userId) return Response.json({ ok: false }, { status: 401 })
|
|
40
|
+
const rows = await sql`SELECT id, email, name FROM auth_users WHERE id = ${userId}`
|
|
41
|
+
if (!rows.length) return Response.json({ ok: false }, { status: 401 })
|
|
42
|
+
return Response.json({ ok: true, user: rows[0] })
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 建表(独立/嵌入共用) */
|
|
47
|
+
export async function ensureAuthTables(sql: any): Promise<void> {
|
|
48
|
+
await sql.unsafe('CREATE TABLE IF NOT EXISTS auth_users (id serial PRIMARY KEY, email text UNIQUE, password text, name text)')
|
|
49
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth 应用模板——登录/注册 → 受保护页 → 登出(完整全栈,复制即用)
|
|
3
|
+
*
|
|
4
|
+
* 演示能力:
|
|
5
|
+
* - 应用级状态(createStore user)+ 路由守卫(未登录 → 登录页)
|
|
6
|
+
* - 表单校验 + 提交 loading + 错误条(AuthPage 组件)
|
|
7
|
+
* - 后端:内存用户表 + 会话(api.ts 注册函数——独立/嵌入共享)
|
|
8
|
+
* - 会话持久化:ctx.browser.storage(浏览器纪律)
|
|
9
|
+
*/
|
|
10
|
+
import { UIRouter, uiServe, h, createStore, createClientBrowser } from 'weifuwu/vdom'
|
|
11
|
+
import type { Component } from 'weifuwu/vdom'
|
|
12
|
+
import { AuthPage, Input, Button, Alert, PageHeader, Avatar, Space } from 'weifuwu/components'
|
|
13
|
+
|
|
14
|
+
export interface User { id: string; email: string; name: string }
|
|
15
|
+
|
|
16
|
+
export const authStore = createStore<{ user: User | null; error: string | null }>({ user: null, error: null })
|
|
17
|
+
|
|
18
|
+
async function api(browser: any, path: string, body: Record<string, string>): Promise<{ ok: boolean; user?: User; error?: string }> {
|
|
19
|
+
const token = browser.storageGet('auth:token')
|
|
20
|
+
const res = await fetch(path, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
|
|
23
|
+
body: JSON.stringify(body),
|
|
24
|
+
})
|
|
25
|
+
return res.json()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 登录/注册页(AuthPage 组件——同一骨架两用) */
|
|
29
|
+
const AuthFormPage = (mode: 'login' | 'register'): Component =>
|
|
30
|
+
async (_init: any, ctx: any) => {
|
|
31
|
+
const browser = ctx.browser
|
|
32
|
+
let email = ''
|
|
33
|
+
let password = ''
|
|
34
|
+
let name = ''
|
|
35
|
+
let error = ''
|
|
36
|
+
let loading = false
|
|
37
|
+
const submit = async () => {
|
|
38
|
+
error = ''
|
|
39
|
+
if (!email.includes('@')) { error = '请输入有效邮箱'; ctx.render(); return }
|
|
40
|
+
if (password.length < 6) { error = '密码至少 6 位'; ctx.render(); return }
|
|
41
|
+
loading = true; ctx.render()
|
|
42
|
+
const res = await api(browser, `/api/auth/${mode}`, { email, password, name })
|
|
43
|
+
loading = false
|
|
44
|
+
if (res.ok && res.user) {
|
|
45
|
+
authStore.update((s) => { s.user = res.user!; s.error = null })
|
|
46
|
+
browser.storageSet('auth:token', (res as any).token ?? '')
|
|
47
|
+
location.hash = '#/'
|
|
48
|
+
} else {
|
|
49
|
+
error = res.error ?? '请求失败'
|
|
50
|
+
}
|
|
51
|
+
ctx.render()
|
|
52
|
+
}
|
|
53
|
+
return async (_p: any) => (
|
|
54
|
+
<AuthPage
|
|
55
|
+
title={mode === 'login' ? '登录' : '注册'}
|
|
56
|
+
subtitle="auth 应用模板——内存用户 + 会话(演示)"
|
|
57
|
+
logo={<Avatar name="wf" />}
|
|
58
|
+
submitLabel={mode === 'login' ? '登录' : '注册'}
|
|
59
|
+
loading={loading}
|
|
60
|
+
error={error || null}
|
|
61
|
+
onSubmit={() => void submit()}
|
|
62
|
+
footer={<a href={mode === 'login' ? '#/register' : '#/login'} class="wf-link" style="cursor:pointer">{mode === 'login' ? '没有账号?注册' : '已有账号?登录'}</a>}
|
|
63
|
+
>
|
|
64
|
+
{mode === 'register' && (
|
|
65
|
+
<Input label="昵称" value={name} onInput={(e: any) => { name = (e.target as HTMLInputElement).value; ctx.render() }} placeholder="如何称呼你" />
|
|
66
|
+
)}
|
|
67
|
+
<Input label="邮箱" type="email" value={email} onInput={(e: any) => { email = (e.target as HTMLInputElement).value; ctx.render() }} placeholder="name@example.com" required />
|
|
68
|
+
<Input label="密码" type="password" value={password} onInput={(e: any) => { password = (e.target as HTMLInputElement).value; ctx.render() }} placeholder="至少 6 位" />
|
|
69
|
+
</AuthPage>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const LoginPage = AuthFormPage('login')
|
|
74
|
+
export const RegisterPage = AuthFormPage('register')
|
|
75
|
+
|
|
76
|
+
/** 受保护页(未登录 → 登录页——路由守卫) */
|
|
77
|
+
export const DashboardPage: Component = async (_init: any, ctx: any) => {
|
|
78
|
+
const browser = ctx.browser
|
|
79
|
+
// 会话恢复(mount——ctx.browser.storage 安全适配)
|
|
80
|
+
void (async () => {
|
|
81
|
+
try {
|
|
82
|
+
const token = browser.storageGet('auth:token')
|
|
83
|
+
if (!token) return
|
|
84
|
+
const res = await fetch('/api/auth/me', { headers: { Authorization: `Bearer ${token}` } })
|
|
85
|
+
if (res.ok) {
|
|
86
|
+
const { user } = await res.json()
|
|
87
|
+
authStore.update((s) => { s.user = user })
|
|
88
|
+
}
|
|
89
|
+
} catch { /* 无会话 */ }
|
|
90
|
+
})()
|
|
91
|
+
const state = ctx.ui.useExternal(authStore)
|
|
92
|
+
const logout = () => {
|
|
93
|
+
browser.storageRemove('auth:token')
|
|
94
|
+
authStore.update((s) => { s.user = null })
|
|
95
|
+
location.hash = '#/login'
|
|
96
|
+
}
|
|
97
|
+
return async (_p: any) => {
|
|
98
|
+
if (!state.user) return h(LoginPage, {})
|
|
99
|
+
const u = state.user
|
|
100
|
+
return (
|
|
101
|
+
<div class="wf-stack wf-gap-md" style="max-width:560px">
|
|
102
|
+
<PageHeader
|
|
103
|
+
title={`欢迎,${u.name ?? u.email}`}
|
|
104
|
+
sub="登录态由 createStore 持有——路由守卫读它决定渲染"
|
|
105
|
+
>
|
|
106
|
+
<Button variant="ghost" onClick={logout}>登出</Button>
|
|
107
|
+
</PageHeader>
|
|
108
|
+
<div class="wf-surface wf-border wf-rounded-md wf-p-md wf-stack wf-gap-sm">
|
|
109
|
+
<Space align="center" size="md"><Avatar name={u.name ?? u.email} size="lg" /><b>{u.email}</b></Space>
|
|
110
|
+
<Alert variant="success">✓ 已登录——受保护内容可见。刷新页面会话自动恢复(ctx.browser.storage)</Alert>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 路由守卫:包装页面——未登录跳登录(守卫逻辑在页面组件内读 store) */
|
|
118
|
+
export const guard = (page: Component): Component => page
|
|
119
|
+
|
|
120
|
+
// ── 路由表 ──
|
|
121
|
+
export const authRoutes = [
|
|
122
|
+
{ path: '/', render: () => h(DashboardPage, {}) },
|
|
123
|
+
{ path: '/login', render: () => h(LoginPage, {}) },
|
|
124
|
+
{ path: '/register', render: () => h(RegisterPage, {}) },
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
export const pathFromHash = (): string => location.hash.replace(/^#/, '') || '/'
|
|
128
|
+
|
|
129
|
+
export function createAuthApp(root: HTMLElement, _options?: { history?: boolean }): ReturnType<typeof uiServe> {
|
|
130
|
+
// vdom 规范面:UIRouter + uiServe——布局共享精准路由
|
|
131
|
+
const router = new UIRouter()
|
|
132
|
+
for (const r of authRoutes) {
|
|
133
|
+
router.get(r.path, (req: Request, ctx: any) =>
|
|
134
|
+
(ctx as { stream: (v: unknown) => Response }).stream(r.render()))
|
|
135
|
+
}
|
|
136
|
+
return uiServe(router, { root, browser: createClientBrowser()! })
|
|
137
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth 独立入口——复制本目录即可运行:
|
|
3
|
+
* cd examples/apps/auth && node server.ts → http://localhost:3301
|
|
4
|
+
*/
|
|
5
|
+
import { createAuthApp, pathFromHash } from './app.tsx'
|
|
6
|
+
|
|
7
|
+
const sub = createAuthApp(document.querySelector('#root') as HTMLElement)
|
|
8
|
+
window.addEventListener('hashchange', () => sub.navigate(pathFromHash()))
|