├── play ├── src │ ├── style.css │ ├── vite-env.d.ts │ ├── main.ts │ └── App.vue ├── .vscode │ └── extensions.json ├── .gitignore ├── index.html ├── README.md ├── tsconfig.app.json ├── vite.config.ts ├── package.json ├── tsconfig.json ├── tsconfig.node.json └── public │ └── vite.svg ├── .prettierignore ├── pnpm-workspace.yaml ├── src ├── version │ ├── version.ts │ ├── index.ts │ └── token-meta.json ├── type-utility.ts ├── theme │ ├── index.ts │ ├── genStyleUtils.ts │ ├── components.ts │ ├── cssinjs-utils.ts │ └── patch-antd.ts ├── _util │ ├── __tests__ │ │ └── warning.test.ts │ ├── cssinjs │ │ ├── transformers │ │ │ └── interface.ts │ │ ├── theme │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── createTheme.ts │ │ │ └── Theme.ts │ │ ├── linters │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── utils.ts │ │ │ ├── parentSelectorLinter.ts │ │ │ ├── hashedAnimationLinter.ts │ │ │ ├── contentQuotesLinter.ts │ │ │ └── legacyNotSelectorLinter.ts │ │ ├── Keyframes.ts │ │ ├── hooks │ │ │ └── useHMR.ts │ │ └── Cache.ts │ ├── cssinjs-utils │ │ ├── hooks │ │ │ ├── useCSP.ts │ │ │ ├── usePrefix.ts │ │ │ └── useToken.ts │ │ ├── util │ │ │ ├── getCompVarPrefix.ts │ │ │ ├── calc │ │ │ │ ├── index.ts │ │ │ │ ├── calculator.ts │ │ │ │ └── NumCalculator.ts │ │ │ ├── maxmin.ts │ │ │ └── getDefaultComponentToken.ts │ │ ├── interface │ │ │ ├── index.ts │ │ │ └── components.ts │ │ └── index.ts │ ├── getValue.ts │ ├── hooks │ │ ├── use-event-callback.ts │ │ ├── use-state.ts │ │ ├── use-x-component-config.ts │ │ └── useMergedState.ts │ ├── supportsPassive.js │ ├── setStyle.ts │ ├── motion.ts │ └── getScrollBarSize.ts ├── attachments │ ├── FileList │ │ ├── index.ts │ │ ├── Progress.vue │ │ ├── VideoIcon.vue │ │ └── AudioIcon.vue │ ├── index.ts │ ├── SilentUploader.vue │ └── context.ts ├── prompts │ └── index.ts ├── welcome │ ├── index.ts │ └── interface.ts ├── vc-util │ ├── Dom │ │ ├── canUseDom.ts │ │ ├── contains.ts │ │ ├── isVisible.ts │ │ ├── addEventListener.js │ │ └── class.js │ ├── devWarning.ts │ ├── get.ts │ ├── warning.ts │ ├── set.ts │ └── isEqual.ts ├── suggestion │ ├── index.ts │ ├── interface.ts │ └── style │ │ └── index.ts ├── x-stream │ └── index.ts ├── conversations │ ├── index.ts │ ├── GroupTitle.vue │ └── context.ts ├── thought-chain │ ├── index.ts │ └── context.ts ├── use-x-agent │ └── index.ts ├── use-x-chat │ ├── index.ts │ └── useSyncState.ts ├── x-provider │ ├── index.ts │ ├── hooks │ │ └── use-x-provider-context.ts │ └── index.vue ├── sender │ ├── index.ts │ ├── components │ │ ├── ClearButton.vue │ │ ├── SendButton.vue │ │ ├── LoadingButton.vue │ │ ├── ActionButton │ │ │ ├── context.ts │ │ │ └── index.vue │ │ └── SpeechButton │ │ │ └── index.vue │ ├── StopLoading.vue │ ├── context.ts │ └── style │ │ └── header.ts ├── bubble │ ├── index.ts │ ├── style │ │ ├── list.ts │ │ └── content.ts │ ├── loading.vue │ ├── hooks │ │ ├── useTypingConfig.ts │ │ ├── useListData.ts │ │ └── useDisplayData.ts │ └── context.ts ├── x-request │ └── index.ts ├── index.ts └── transition-collapse │ ├── index.ts │ └── style │ └── index.ts ├── internal └── build-utils │ └── src │ ├── index.ts │ └── path.ts ├── .husky ├── pre-commit └── commit-msg ├── netlify.toml ├── docs ├── public │ └── images │ │ ├── QQ_group_1.jpg │ │ └── wechat_public_account.jpg ├── .vitepress │ ├── theme │ │ ├── style.css │ │ ├── index.ts │ │ └── components │ │ │ └── PreferenceSwitch.vue │ ├── config │ │ └── plugins.ts │ └── vitepress │ │ ├── index.ts │ │ ├── components │ │ ├── vp-semantic.vue │ │ └── demo │ │ │ └── vp-source-code.vue │ │ ├── utils │ │ └── index.ts │ │ └── composables │ │ └── source-code.ts ├── examples-setup │ ├── prompts │ │ ├── nest.vue │ │ ├── disabled.vue │ │ ├── flex-wrap.vue │ │ ├── flex-vertical.vue │ │ ├── flex-wrap-fixed.vue │ │ └── basic.vue │ ├── sender │ │ ├── actions.vue │ │ ├── basic.vue │ │ ├── focus.vue │ │ ├── speech.vue │ │ ├── sendStyle.vue │ │ ├── headerFixed.vue │ │ ├── pasteImage.vue │ │ ├── speechCustom.vue │ │ └── submitType.vue │ ├── x-provider │ │ └── use.vue │ ├── conversations │ │ ├── basic.vue │ │ ├── group.vue │ │ ├── group-sort.vue │ │ ├── with-menu.vue │ │ └── controlled-mode.vue │ ├── playground │ │ └── independent.vue │ ├── bubble │ │ ├── basic.vue │ │ ├── loading.vue │ │ ├── shape.vue │ │ ├── header-and-footer.vue │ │ ├── typing.vue │ │ ├── markdown.vue │ │ ├── avatar-and-placement.vue │ │ └── variant.vue │ ├── welcome │ │ ├── basic.vue │ │ ├── variant.vue │ │ └── background.vue │ ├── thought-chain │ │ ├── basic.vue │ │ ├── collapsible.vue │ │ ├── size.vue │ │ └── nested.vue │ ├── suggestion │ │ ├── block.vue │ │ ├── trigger.vue │ │ └── basic.vue │ └── attachments │ │ ├── files.vue │ │ └── basic.vue ├── examples │ ├── bubble │ │ ├── basic.vue │ │ ├── loading.vue │ │ ├── header-and-footer.vue │ │ ├── shape.vue │ │ ├── typing.vue │ │ ├── avatar-and-placement.vue │ │ ├── markdown.vue │ │ └── variant.vue │ ├── sender │ │ ├── speech.vue │ │ ├── submitType.vue │ │ ├── speechCustom.vue │ │ ├── headerFixed.vue │ │ ├── basic.vue │ │ ├── focus.vue │ │ └── actions.vue │ ├── welcome │ │ ├── basic.vue │ │ ├── variant.vue │ │ └── background.vue │ ├── prompts │ │ ├── disabled.vue │ │ ├── flex-vertical.vue │ │ ├── flex-wrap-fixed.vue │ │ ├── basic.vue │ │ └── flex-wrap.vue │ ├── conversations │ │ ├── basic.vue │ │ ├── group.vue │ │ ├── controlled-mode.vue │ │ ├── group-sort.vue │ │ └── with-menu.vue │ ├── thought-chain │ │ ├── basic.vue │ │ ├── collapsible.vue │ │ ├── size.vue │ │ └── nested.vue │ ├── suggestion │ │ ├── block.vue │ │ ├── trigger.vue │ │ └── basic.vue │ └── attachments │ │ └── files.vue ├── component │ ├── overview.md │ ├── x-provider.md │ ├── x-stream.md │ ├── welcome.md │ ├── use-x-chat.md │ ├── use-x-agent.md │ └── suggestion.md ├── playground │ └── independent.md ├── index.md └── semantics │ ├── bubble.vue │ ├── welcome.vue │ ├── sender.vue │ └── attachments.vue ├── renovate.json ├── .gitignore ├── .editorconfig ├── .eslintrc.js ├── .prettierrc.js ├── vitest.config.mts ├── .github └── workflows │ └── ci.yml ├── tsconfig.json ├── tsconfig.build.json ├── vite.config.mts └── LICENSE /play/src/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | *.yaml 3 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "play" 3 | -------------------------------------------------------------------------------- /src/version/version.ts: -------------------------------------------------------------------------------- 1 | export default '1.0.3'; -------------------------------------------------------------------------------- /internal/build-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './path'; 2 | -------------------------------------------------------------------------------- /src/type-utility.ts: -------------------------------------------------------------------------------- 1 | export type AvoidValidation = T; 2 | -------------------------------------------------------------------------------- /play/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /play/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /src/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { theme } from 'ant-design-vue'; 2 | 3 | export { 4 | theme 5 | } 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /src/version/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import version from './version'; 3 | 4 | export default version; 5 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | ## history 路由模式 2 | [[redirects]] 3 | from = "/*" 4 | to = "/index.html" 5 | status = 200 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit "${1}" 5 | -------------------------------------------------------------------------------- /docs/public/images/QQ_group_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erupts/ant-design-x-vue/main/docs/public/images/QQ_group_1.jpg -------------------------------------------------------------------------------- /src/_util/__tests__/warning.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from 'vitest' 2 | 3 | describe('Test warning', () => { 4 | it('test') 5 | }) 6 | -------------------------------------------------------------------------------- /docs/public/images/wechat_public_account.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erupts/ant-design-x-vue/main/docs/public/images/wechat_public_account.jpg -------------------------------------------------------------------------------- /play/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-border-color: rgba(5, 5, 5, 6%); 3 | } 4 | 5 | .dark { 6 | --vp-border-color: rgba(253, 253, 253, 12%); 7 | } 8 | -------------------------------------------------------------------------------- /src/_util/cssinjs/transformers/interface.ts: -------------------------------------------------------------------------------- 1 | import type { CSSObject } from '..'; 2 | 3 | export interface Transformer { 4 | visit?: (cssObj: CSSObject) => CSSObject; 5 | } 6 | -------------------------------------------------------------------------------- /src/attachments/FileList/index.ts: -------------------------------------------------------------------------------- 1 | import FileList from './FileList.vue'; 2 | import FileListCard from './FileListCard.vue'; 3 | 4 | export { 5 | FileList, 6 | FileListCard 7 | } 8 | -------------------------------------------------------------------------------- /src/prompts/index.ts: -------------------------------------------------------------------------------- 1 | import Prompts from './Prompts.vue'; 2 | 3 | export type { PromptsProps } from './interface'; 4 | 5 | export default Prompts; 6 | 7 | export { 8 | Prompts, 9 | } 10 | -------------------------------------------------------------------------------- /src/welcome/index.ts: -------------------------------------------------------------------------------- 1 | import Welcome from './Welcome.vue'; 2 | 3 | export type { WelcomeProps } from './interface'; 4 | 5 | export default Welcome; 6 | 7 | export { 8 | Welcome 9 | } 10 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/nest.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/actions.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/basic.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/focus.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/speech.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/x-provider/use.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /src/vc-util/Dom/canUseDom.ts: -------------------------------------------------------------------------------- 1 | export default function canUseDom() { 2 | return !!( 3 | typeof window !== 'undefined' && 4 | window.document && 5 | window.document.createElement 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/disabled.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/flex-wrap.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/sendStyle.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/conversations/basic.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/conversations/group.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/headerFixed.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/pasteImage.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/speechCustom.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/sender/submitType.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/playground/independent.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/flex-vertical.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /src/suggestion/index.ts: -------------------------------------------------------------------------------- 1 | import Suggestion from './Suggestion.vue'; 2 | 3 | export type { SuggestionProps } from './interface'; 4 | 5 | export default Suggestion; 6 | 7 | export { 8 | Suggestion, 9 | } 10 | -------------------------------------------------------------------------------- /src/x-stream/index.ts: -------------------------------------------------------------------------------- 1 | import XStream from './x-stream'; 2 | 3 | export type { SSEFields, SSEOutput, XStreamOptions } from './x-stream'; 4 | 5 | export { 6 | XStream 7 | } 8 | 9 | export default XStream 10 | -------------------------------------------------------------------------------- /docs/examples-setup/conversations/group-sort.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/conversations/with-menu.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/flex-wrap-fixed.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | .dumi/tmp 4 | .dumi/tmp-test 5 | .dumi/tmp-production 6 | .DS_Store 7 | /coverage 8 | docs/.vitepress/cache 9 | docs/.vitepress/dist 10 | docs/.vitepress/.temp 11 | .vitepress/cache 12 | -------------------------------------------------------------------------------- /play/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /docs/examples-setup/conversations/controlled-mode.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /src/conversations/index.ts: -------------------------------------------------------------------------------- 1 | import Conversations from './Conversations.vue'; 2 | 3 | export type { ConversationsProps } from './interface'; 4 | 5 | export default Conversations; 6 | 7 | export { 8 | Conversations, 9 | } 10 | -------------------------------------------------------------------------------- /src/thought-chain/index.ts: -------------------------------------------------------------------------------- 1 | import ThoughtChain from './ThoughtChain.vue'; 2 | 3 | export type { ThoughtChainProps, ThoughtChainItem } from './interface'; 4 | 5 | export default ThoughtChain; 6 | 7 | export { 8 | ThoughtChain, 9 | } 10 | -------------------------------------------------------------------------------- /internal/build-utils/src/path.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | 3 | export const projRoot = resolve(__dirname, '..', '..', '..') 4 | 5 | // Docs 6 | export const docsDirName = 'docs' 7 | export const docRoot = resolve(projRoot, docsDirName) 8 | 9 | -------------------------------------------------------------------------------- /src/_util/cssinjs/theme/index.ts: -------------------------------------------------------------------------------- 1 | export { default as createTheme } from './createTheme'; 2 | export { default as Theme } from './Theme'; 3 | export { default as ThemeCache } from './ThemeCache'; 4 | export type { TokenType, DerivativeFunc } from './interface'; 5 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/basic.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/_util/cssinjs/theme/interface.ts: -------------------------------------------------------------------------------- 1 | export type TokenType = object; 2 | export type DerivativeFunc = ( 3 | designToken: DesignToken, 4 | derivativeToken?: DerivativeToken, 5 | ) => DerivativeToken; 6 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/interface.ts: -------------------------------------------------------------------------------- 1 | export interface LinterInfo { 2 | path?: string; 3 | hashId?: string; 4 | parentSelectors: string[]; 5 | } 6 | 7 | export interface Linter { 8 | (key: string, value: string | number, info: LinterInfo): void; 9 | } 10 | -------------------------------------------------------------------------------- /src/vc-util/devWarning.ts: -------------------------------------------------------------------------------- 1 | import devWarning, { resetWarned } from './warning'; 2 | 3 | export { resetWarned }; 4 | 5 | export default (valid: boolean, component: string, message: string): void => { 6 | devWarning(valid, `[ant-design-vue: ${component}] ${message}`); 7 | }; 8 | -------------------------------------------------------------------------------- /docs/examples/bubble/basic.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/use-x-agent/index.ts: -------------------------------------------------------------------------------- 1 | import useXAgent, { XAgent } from './use-x-agent'; 2 | export type { RequestFn, XAgentConfigPreset, XAgentConfigCustom, XAgentConfig } from './use-x-agent'; 3 | 4 | export { 5 | useXAgent, 6 | XAgent 7 | } 8 | 9 | export default useXAgent; 10 | -------------------------------------------------------------------------------- /src/version/token-meta.json: -------------------------------------------------------------------------------- 1 | {"components":{"Attachments":[{"source":"Attachments","token":"colorBgPlaceholderHover","type":"string","desc":"","descEn":"","name":"","nameEn":""}],"Bubble":[],"Conversations":[],"Prompts":[],"Sender":[],"Suggestion":[],"ThoughtChain":[],"Welcome":[]}} 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/hooks/useCSP.ts: -------------------------------------------------------------------------------- 1 | export type UseCSP = () => { 2 | nonce?: string; 3 | }; 4 | 5 | /** 6 | * Provide a default hook since not everyone needs to config this. 7 | */ 8 | const useDefaultCSP: UseCSP = () => ({}); 9 | 10 | export default useDefaultCSP; 11 | -------------------------------------------------------------------------------- /docs/.vitepress/config/plugins.ts: -------------------------------------------------------------------------------- 1 | import mdContainer from 'markdown-it-container' 2 | import createDemoContainer from '../plugins/demo' 3 | import type MarkdownIt from 'markdown-it' 4 | 5 | export const mdPlugin = (md: MarkdownIt) => { 6 | md.use(mdContainer, 'demo', createDemoContainer(md)) 7 | } 8 | -------------------------------------------------------------------------------- /docs/.vitepress/vitepress/index.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'vue' 2 | import VPDemo from './components/vp-demo.vue' 3 | import VPSemantic from './components/vp-semantic.vue' 4 | 5 | export const globals: [string, Component][] = [ 6 | ['Demo', VPDemo], 7 | ['VpSemantic', VPSemantic], 8 | ] 9 | -------------------------------------------------------------------------------- /src/vc-util/Dom/contains.ts: -------------------------------------------------------------------------------- 1 | export default function contains(root: Node | null | undefined, n?: Node) { 2 | if (!root) { 3 | return false; 4 | } 5 | 6 | // Use native if support 7 | if (root.contains) { 8 | return root.contains(n); 9 | } 10 | 11 | return false; 12 | } 13 | -------------------------------------------------------------------------------- /src/use-x-chat/index.ts: -------------------------------------------------------------------------------- 1 | import useXChat from './use-x-chat'; 2 | export type { SimpleType, MessageStatus, XChatConfig, MessageInfo, DefaultMessageInfo, RequestResultObject, RequestResult, StandardRequestResult } from './use-x-chat'; 3 | 4 | export { 5 | useXChat 6 | } 7 | 8 | export default useXChat; 9 | -------------------------------------------------------------------------------- /src/x-provider/index.ts: -------------------------------------------------------------------------------- 1 | import useXProviderContext, { 2 | defaultPrefixCls, 3 | } from './hooks/use-x-provider-context'; 4 | import XProvider from './index.vue'; 5 | 6 | export type { XProviderProps } from './context'; 7 | 8 | export { XProvider, defaultPrefixCls, useXProviderContext }; 9 | 10 | export default XProvider; 11 | -------------------------------------------------------------------------------- /docs/component/overview.md: -------------------------------------------------------------------------------- 1 | # 总览 2 | 3 | `ant-design-x-vue` 是 `@ant-design/x`的 Vue 实现,是一个专注于 Vue 生态的先进 AI 组件库,旨在简化与人工智能集成的开发过程。我们的库包括高度定制化的 AI 组件,允许开发者轻松地将对话 AI 集成到他们的应用中。除了丰富的 UI 组件,`ant-design-x-vue` 还提供了一揽子 API 解决方案,支持开发者通过令牌认证直接接入现有 AI 服务,无缝衔接与 AI 的对话和交互。无论是建立智能聊天应用、提升用户交互体验还是加快 AI 能力的集成,`ant-design-x-vue` 都是 Vue 开发者进入 AI 世界的理想伙伴。 4 | 5 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/util/getCompVarPrefix.ts: -------------------------------------------------------------------------------- 1 | const getCompVarPrefix = (component: string, prefix?: string) => 2 | `${[ 3 | prefix, 4 | component.replace(/([A-Z]+)([A-Z][a-z]+)/g, '$1-$2').replace(/([a-z])([A-Z])/g, '$1-$2'), 5 | ] 6 | .filter(Boolean) 7 | .join('-')}`; 8 | 9 | export default getCompVarPrefix; 10 | -------------------------------------------------------------------------------- /src/sender/index.ts: -------------------------------------------------------------------------------- 1 | import SenderComponent from './Sender.vue'; 2 | import SenderHeader from './SenderHeader.vue'; 3 | 4 | export type { SenderProps } from './interface'; 5 | 6 | const Sender = Object.assign(SenderComponent, { 7 | Header: SenderHeader, 8 | }); 9 | 10 | export default Sender; 11 | 12 | export { 13 | Sender, 14 | } 15 | -------------------------------------------------------------------------------- /src/vc-util/get.ts: -------------------------------------------------------------------------------- 1 | export default function get(entity: any, path: (string | number)[]) { 2 | let current = entity; 3 | 4 | for (let i = 0; i < path.length; i += 1) { 5 | if (current === null || current === undefined) { 6 | return undefined; 7 | } 8 | 9 | current = current[path[i]]; 10 | } 11 | 12 | return current; 13 | } 14 | -------------------------------------------------------------------------------- /docs/playground/independent.md: -------------------------------------------------------------------------------- 1 | --- 2 | aside: false 3 | --- 4 | 5 | 9 | 10 | # 独立式 11 | 12 | :::demo 13 | 14 | playground/independent 15 | 16 | ::: 17 | -------------------------------------------------------------------------------- /src/bubble/index.ts: -------------------------------------------------------------------------------- 1 | import BubbleComponent from './Bubble.vue'; 2 | import BubbleList from './BubbleList.vue'; 3 | 4 | export type { BubbleProps, BubbleListProps } from './interface'; 5 | 6 | const Bubble = Object.assign(BubbleComponent, { 7 | List: BubbleList, 8 | }); 9 | 10 | export default Bubble; 11 | 12 | export { 13 | Bubble, 14 | BubbleList 15 | } 16 | -------------------------------------------------------------------------------- /play/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/interface/index.ts: -------------------------------------------------------------------------------- 1 | import type { Ref, VNode } from 'vue'; 2 | 3 | export type { 4 | OverrideTokenMap, 5 | TokenMap, 6 | TokenMapKey, 7 | GlobalTokenWithComponent, 8 | ComponentToken, 9 | ComponentTokenKey, 10 | GlobalToken, 11 | } from './components'; 12 | 13 | export type UseComponentStyleResult = [(node: VNode) => VNode, Ref]; 14 | -------------------------------------------------------------------------------- /src/attachments/index.ts: -------------------------------------------------------------------------------- 1 | import AttachmentsComponent from './Attachments.vue'; 2 | import { FileListCard } from './FileList'; 3 | 4 | const Attachments = Object.assign(AttachmentsComponent, { 5 | FileCard: FileListCard, 6 | }); 7 | 8 | export type { AttachmentsProps } from './interface'; 9 | 10 | export default Attachments; 11 | 12 | export { 13 | Attachments, 14 | } 15 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['plugin:vue/vue3-recommended'], 3 | parser: 'vue-eslint-parser', 4 | parserOptions: { 5 | parser: '@typescript-eslint/parser', 6 | sourceType: 'module', 7 | ecmaVersion: 2020, 8 | ecmaFeatures: { 9 | jsx: true, 10 | }, 11 | }, 12 | rules: { 13 | 'vue/one-component-per-file': 'off' 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /src/x-request/index.ts: -------------------------------------------------------------------------------- 1 | import XFetch from './x-fetch'; 2 | import XRequest from './x-request'; 3 | 4 | export type { XFetchMiddlewares, XFetchOptions, XFetchType } from './x-fetch'; 5 | export type { XRequestBaseOptions, XRequestOptions, XRequestParams, XRequestCallbacks, XRequestFunction } from './x-request'; 6 | 7 | export { 8 | XFetch, 9 | XRequest 10 | } 11 | 12 | export default XRequest; 13 | -------------------------------------------------------------------------------- /src/_util/getValue.ts: -------------------------------------------------------------------------------- 1 | function get(entity: any, path: (string | number | symbol)[] | readonly (string | number | symbol)[]): any { 2 | var current = entity; 3 | for (var i = 0; i < path.length; i += 1) { 4 | if (current === null || current === undefined) { 5 | return undefined; 6 | } 7 | current = current[path[i]]; 8 | } 9 | return current; 10 | } 11 | 12 | export default get 13 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/hooks/usePrefix.ts: -------------------------------------------------------------------------------- 1 | export type UsePrefix = () => { 2 | /** 3 | * All the component use `@ant-design/cssinjs-utils` should have same `rootPrefixCls`. 4 | */ 5 | rootPrefixCls: string; 6 | /** 7 | * `iconPrefixCls` comes from the setting of `@ant-design/icons`. 8 | * Here maybe little coupling but everyone need use this. 9 | */ 10 | iconPrefixCls: string; 11 | }; 12 | -------------------------------------------------------------------------------- /play/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/_util/hooks/use-event-callback.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | 3 | // Saves incoming handler to the ref in order to avoid "useCallback hell" 4 | export function useEventCallback(handler?: (value: T) => void): (value: T) => void { 5 | const callbackRef = ref(handler); 6 | const fn = ref((value: T) => { 7 | callbackRef.value && callbackRef.value(value); 8 | }); 9 | callbackRef.value = handler; 10 | 11 | return fn.value; 12 | } 13 | -------------------------------------------------------------------------------- /play/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 6 | 13 | -------------------------------------------------------------------------------- /src/_util/cssinjs/Keyframes.ts: -------------------------------------------------------------------------------- 1 | import type { CSSInterpolation } from './hooks/useStyleRegister'; 2 | 3 | class Keyframe { 4 | private name: string; 5 | style: CSSInterpolation; 6 | 7 | constructor(name: string, style: CSSInterpolation) { 8 | this.name = name; 9 | this.style = style; 10 | } 11 | 12 | getName(hashId = ''): string { 13 | return hashId ? `${hashId}-${this.name}` : this.name; 14 | } 15 | 16 | _keyframe = true; 17 | } 18 | 19 | export default Keyframe; 20 | -------------------------------------------------------------------------------- /src/bubble/style/list.ts: -------------------------------------------------------------------------------- 1 | import type { GenerateStyle } from '../../theme/cssinjs-utils'; 2 | import type { BubbleToken } from '.'; 3 | 4 | const genBubbleListStyle: GenerateStyle = (token) => { 5 | const { componentCls, padding } = token; 6 | return { 7 | [`${componentCls}-list`]: { 8 | display: 'flex', 9 | flexDirection: 'column', 10 | gap: padding, 11 | overflowY: 'auto', 12 | }, 13 | }; 14 | }; 15 | 16 | export default genBubbleListStyle; 17 | -------------------------------------------------------------------------------- /src/x-provider/hooks/use-x-provider-context.ts: -------------------------------------------------------------------------------- 1 | import { useConfigContextInject } from 'ant-design-vue/es/config-provider/context'; 2 | 3 | export const defaultPrefixCls = 'ant'; 4 | 5 | function useXProviderContext() { 6 | const { getPrefixCls, direction, csp, iconPrefixCls, theme } = useConfigContextInject(); 7 | 8 | return { 9 | theme, 10 | getPrefixCls, 11 | direction, 12 | csp, 13 | iconPrefixCls, 14 | }; 15 | } 16 | 17 | export default useXProviderContext; 18 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/utils.ts: -------------------------------------------------------------------------------- 1 | import devWarning from '../../../vc-util/warning'; 2 | import type { LinterInfo } from './interface'; 3 | 4 | export function lintWarning(message: string, info: LinterInfo) { 5 | const { path, parentSelectors } = info; 6 | 7 | devWarning( 8 | false, 9 | `[Ant Design Vue CSS-in-JS] ${path ? `Error in '${path}': ` : ''}${message}${ 10 | parentSelectors.length ? ` Selector info: ${parentSelectors.join(' -> ')}` : '' 11 | }`, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/parentSelectorLinter.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from '..'; 2 | import { lintWarning } from './utils'; 3 | 4 | const linter: Linter = (_key, _value, info) => { 5 | if ( 6 | info.parentSelectors.some(selector => { 7 | const selectors = selector.split(','); 8 | return selectors.some(item => item.split('&').length > 2); 9 | }) 10 | ) { 11 | lintWarning('Should not use more than one `&` in a selector.', info); 12 | } 13 | }; 14 | 15 | export default linter; 16 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/hashedAnimationLinter.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from './interface'; 2 | import { lintWarning } from './utils'; 3 | 4 | const linter: Linter = (key, value, info) => { 5 | if (key === 'animation') { 6 | if (info.hashId && value !== 'none') { 7 | lintWarning( 8 | `You seem to be using hashed animation '${value}', in which case 'animationName' with Keyframe as value is recommended.`, 9 | info, 10 | ); 11 | } 12 | } 13 | }; 14 | 15 | export default linter; 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - run: npm i -fg corepack && corepack enable 17 | - uses: actions/setup-node@v4 18 | with: 19 | node-version: 20 20 | cache: "pnpm" 21 | - run: pnpm install 22 | - run: pnpm lint 23 | - run: pnpm build 24 | -------------------------------------------------------------------------------- /docs/.vitepress/vitepress/components/vp-semantic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /src/bubble/loading.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/sender/components/ClearButton.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /play/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import Vue from '@vitejs/plugin-vue'; 3 | import VueJsx from '@vitejs/plugin-vue-jsx'; 4 | import VueMacros from 'vue-macros/vite' 5 | import * as path from "node:path"; 6 | 7 | // https://vite.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | VueMacros({ 11 | plugins: { 12 | vue: Vue(), 13 | vueJsx: VueJsx(), 14 | }, 15 | }), 16 | ], 17 | resolve:{ 18 | alias:{ 19 | 'ant-design-x-vue':path.resolve(__dirname,'../src') 20 | } 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/_util/hooks/use-state.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import { ref } from 'vue'; 3 | 4 | export default function useState>( 5 | defaultStateValue?: T | (() => T), 6 | ): [R, (val: T) => void] { 7 | const initValue: T = 8 | typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue; 9 | 10 | const innerValue = ref(initValue) as Ref; 11 | 12 | function triggerChange(newValue: T) { 13 | innerValue.value = newValue; 14 | } 15 | 16 | return [innerValue as unknown as R, triggerChange]; 17 | } 18 | -------------------------------------------------------------------------------- /play/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "play", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc -b && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "vue": "^3.5.13", 13 | "vue-macros": "3.0.0-beta.4" 14 | }, 15 | "devDependencies": { 16 | "@vitejs/plugin-vue": "^5.2.1", 17 | "@vue/tsconfig": "^0.7.0", 18 | "ant-design-x-vue": "file:..", 19 | "typescript": "~5.7.2", 20 | "vite": "^6.2.0", 21 | "vue-tsc": "^2.2.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/examples/sender/speech.vue: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /docs/.vitepress/vitepress/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { isExternal } from 'vitepress/dist/client/shared.js' 2 | 3 | const endingSlashRE = /\/$/ 4 | export function createGitHubUrl( 5 | docsRepo: string, 6 | docsDir: string, 7 | docsBranch: string, 8 | path: string, 9 | folder = 'examples/', 10 | ext = '.vue' 11 | ) { 12 | const base = isExternal(docsRepo) 13 | ? docsRepo 14 | : `https://github.com/${docsRepo}` 15 | return `${base.replace(endingSlashRE, '')}/edit/${docsBranch}/${ 16 | docsDir ? `${docsDir.replace(endingSlashRE, '')}/` : '' 17 | }${folder || ''}${path}${ext || ''}` 18 | } -------------------------------------------------------------------------------- /docs/examples/welcome/basic.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /docs/.vitepress/vitepress/composables/source-code.ts: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue' 2 | import { useData } from 'vitepress' 3 | import { createGitHubUrl } from '../utils' 4 | 5 | import type { Ref } from 'vue' 6 | 7 | export const useSourceCode = (path: Ref) => { 8 | const { theme } = useData() 9 | 10 | const demoUrl = computed(() => { 11 | const { 12 | repo, 13 | docsDir = '', 14 | docsBranch = 'dev', 15 | docsRepo = repo, 16 | } = theme.value 17 | 18 | return createGitHubUrl(docsRepo, docsDir, docsBranch, path.value) 19 | }) 20 | 21 | return demoUrl 22 | } 23 | -------------------------------------------------------------------------------- /src/welcome/interface.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, VNode } from "vue"; 2 | 3 | export type SemanticType = 'title' | 'description' | 'icon' | 'extra'; 4 | 5 | export interface WelcomeProps { 6 | prefixCls?: string; 7 | rootClassName?: string; 8 | className?: string; 9 | style?: CSSProperties; 10 | variant?: 'filled' | 'borderless'; 11 | 12 | // Semantic 13 | classNames?: Partial>; 14 | styles?: Partial>; 15 | 16 | // Layout 17 | icon?: VNode | string; 18 | title?: VNode | string; 19 | description?: VNode | string; 20 | extra?: VNode; 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "declaration": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "jsx": "preserve", 8 | "jsxImportSource": "vue", 9 | "moduleResolution": "node", 10 | "strictNullChecks": false, 11 | "baseUrl": "./", 12 | "types": ["unplugin-vue-macros/macros-global", "node"], 13 | "paths": { 14 | "ant-design-x-vue": ["src/index.ts"], 15 | "ant-design-x-vue/*": ["src/*"], 16 | } 17 | }, 18 | "vueCompilerOptions": { 19 | "plugins": ["unplugin-vue-macros/volar"] 20 | }, 21 | "include": ["src/**/*", "docs/**/*", "internal/**/*"], 22 | } 23 | -------------------------------------------------------------------------------- /docs/examples/sender/submitType.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /play/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "declaration": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "jsx": "preserve", 8 | "jsxImportSource": "vue", 9 | "moduleResolution": "node", 10 | "strictNullChecks": false, 11 | "baseUrl": "./", 12 | "types": ["unplugin-vue-macros/macros-global", "node"], 13 | "paths": { 14 | "ant-design-x-vue": ["src/index.ts"], 15 | "ant-design-x-vue/*": ["src/*"], 16 | } 17 | }, 18 | "vueCompilerOptions": { 19 | "plugins": ["unplugin-vue-macros/volar"] 20 | }, 21 | "include": ["src/**/*", "docs/**/*", "internal/**/*"], 22 | } 23 | -------------------------------------------------------------------------------- /src/sender/components/SendButton.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /src/vc-util/Dom/isVisible.ts: -------------------------------------------------------------------------------- 1 | export default (element: HTMLElement | SVGGraphicsElement): boolean => { 2 | if (!element) { 3 | return false; 4 | } 5 | 6 | if ((element as HTMLElement).offsetParent) { 7 | return true; 8 | } 9 | 10 | if ((element as SVGGraphicsElement).getBBox) { 11 | const box = (element as SVGGraphicsElement).getBBox(); 12 | if (box.width || box.height) { 13 | return true; 14 | } 15 | } 16 | 17 | if ((element as HTMLElement).getBoundingClientRect) { 18 | const box = (element as HTMLElement).getBoundingClientRect(); 19 | if (box.width || box.height) { 20 | return true; 21 | } 22 | } 23 | 24 | return false; 25 | }; 26 | -------------------------------------------------------------------------------- /docs/examples/bubble/loading.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as genStyleUtils } from './util/genStyleUtils'; 2 | export { default as genCalc } from './util/calc'; 3 | export { default as statisticToken, merge as mergeToken, statistic } from './util/statistic'; 4 | 5 | export type { 6 | OverrideTokenMap, 7 | TokenMap, 8 | TokenMapKey, 9 | GlobalTokenWithComponent, 10 | ComponentToken, 11 | ComponentTokenKey, 12 | GlobalToken, 13 | } from './interface'; 14 | export type { default as AbstractCalculator } from './util/calc/calculator'; 15 | export type { 16 | FullToken, 17 | GetDefaultToken, 18 | GetDefaultTokenFn, 19 | GenStyleFn, 20 | TokenWithCommonCls, 21 | CSSUtil, 22 | } from './util/genStyleUtils'; 23 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/loading.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | -------------------------------------------------------------------------------- /play/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true, 22 | 23 | }, 24 | "include": ["vite.config.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /src/attachments/FileList/Progress.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/vc-util/Dom/addEventListener.js: -------------------------------------------------------------------------------- 1 | import supportsPassive from '../../_util/supportsPassive'; 2 | 3 | export default function addEventListenerWrap(target, eventType, cb, option) { 4 | if (target && target.addEventListener) { 5 | let opt = option; 6 | if ( 7 | opt === undefined && 8 | supportsPassive && 9 | (eventType === 'touchstart' || eventType === 'touchmove' || eventType === 'wheel') 10 | ) { 11 | opt = { passive: false }; 12 | } 13 | target.addEventListener(eventType, cb, opt); 14 | } 15 | return { 16 | remove: () => { 17 | if (target && target.removeEventListener) { 18 | target.removeEventListener(eventType, cb); 19 | } 20 | }, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /docs/.vitepress/vitepress/components/demo/vp-source-code.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 25 | 26 | 32 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Ant Design X Vue" 7 | text: "AI 体验新秩序" 8 | tagline: Ant Design X For Vue。Ant Design 团队匠心呈现 RICH 设计范式,打造卓越 AI 界面解决方案,引领智能新体验。 9 | actions: 10 | - theme: brand 11 | text: 开始使用 12 | link: /component/overview 13 | - theme: alt 14 | text: 设计语言 15 | link: https://x.ant.design/docs/spec/introduce-cn 16 | 17 | features: 18 | - title: AI 设计范式 - RICH 19 | details: 创造卓越 AI 产品体验 20 | - title: AI 界面解决方案 21 | details: 基于RICH,全新 AGI 混合界面(Hybrid-UI)解决方案,完美融合 GUI 和自然会话交互。 22 | - title: 组件丰富,选用自如 23 | details: Ant Design X Vue 全新 AI 组件 , 大量实用组件满足你的需求 , 灵活定制与拓展 24 | --- 25 | 26 | -------------------------------------------------------------------------------- /src/conversations/GroupTitle.vue: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /src/use-x-chat/useSyncState.ts: -------------------------------------------------------------------------------- 1 | // # copying form ant-design-vue/components/tabs/src/hooks/useSyncState.ts 2 | 3 | import type { Ref } from 'vue'; 4 | import { ref } from 'vue'; 5 | 6 | type Updater = (prev: T) => T; 7 | 8 | export default function useSyncState( 9 | defaultState: T, 10 | onChange: (newValue: T, prevValue: T) => void, 11 | ): [Ref, (updater: T | Updater) => void] { 12 | const stateRef = ref(defaultState); 13 | 14 | function setState(updater: any) { 15 | const newValue = typeof updater === 'function' ? updater(stateRef.value) : updater; 16 | if (newValue !== stateRef.value) { 17 | onChange(newValue, stateRef.value as T); 18 | } 19 | stateRef.value = newValue; 20 | } 21 | 22 | return [stateRef as Ref, setState]; 23 | } 24 | -------------------------------------------------------------------------------- /src/_util/hooks/use-x-component-config.ts: -------------------------------------------------------------------------------- 1 | import { useXProviderContextInject } from '../../x-provider/context'; 2 | 3 | import type { XComponentStyleConfig, XComponentsConfig } from '../../x-provider/context'; 4 | import { computed, Ref, unref } from 'vue'; 5 | 6 | const defaultXComponentStyleConfig: XComponentStyleConfig = { 7 | classNames: {}, 8 | styles: {}, 9 | className: '', 10 | style: {}, 11 | }; 12 | 13 | const useXComponentConfig = ( 14 | component: C, 15 | ): Ref[C] & XComponentStyleConfig> => { 16 | const xProviderContext = useXProviderContextInject(); 17 | 18 | return computed(() => ({ 19 | ...defaultXComponentStyleConfig, 20 | ...unref(xProviderContext)[component], 21 | })); 22 | }; 23 | 24 | export default useXComponentConfig; 25 | -------------------------------------------------------------------------------- /src/vc-util/Dom/class.js: -------------------------------------------------------------------------------- 1 | export function hasClass(node, className) { 2 | if (node.classList) { 3 | return node.classList.contains(className); 4 | } 5 | const originClass = node.className; 6 | return ` ${originClass} `.indexOf(` ${className} `) > -1; 7 | } 8 | 9 | export function addClass(node, className) { 10 | if (node.classList) { 11 | node.classList.add(className); 12 | } else { 13 | if (!hasClass(node, className)) { 14 | node.className = `${node.className} ${className}`; 15 | } 16 | } 17 | } 18 | 19 | export function removeClass(node, className) { 20 | if (node.classList) { 21 | node.classList.remove(className); 22 | } else { 23 | if (hasClass(node, className)) { 24 | const originClass = node.className; 25 | node.className = ` ${originClass} `.replace(` ${className} `, ' '); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "declaration": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "emitDeclarationOnly": true, 8 | "declarationDir": "./dist/typings", 9 | "lib": ["esnext", "dom"], 10 | "jsx": "preserve", 11 | "jsxImportSource": "vue", 12 | "moduleResolution": "node", 13 | "strictNullChecks": false, 14 | "baseUrl": "./", 15 | "types": ["unplugin-vue-macros/macros-global", "node"], 16 | "paths": { 17 | "ant-design-x-vue": ["src/index.ts"], 18 | "ant-design-x-vue/*": ["src/*"], 19 | } 20 | }, 21 | "vueCompilerOptions": { 22 | "plugins": ["unplugin-vue-macros/volar"] 23 | }, 24 | "include": ["src/index.ts", "src/x-provider/**/*", "src/theme/**/*", "src/bubble/**/*"], 25 | "exclude": ["src/**/__tests__/**/*"] 26 | } 27 | -------------------------------------------------------------------------------- /docs/examples/welcome/variant.vue: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /docs/examples/bubble/header-and-footer.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /src/_util/setStyle.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties } from 'vue'; 2 | 3 | /** 4 | * Easy to set element style, return previous style 5 | * IE browser compatible(IE browser doesn't merge overflow style, need to set it separately) 6 | * https://github.com/ant-design/ant-design/issues/19393 7 | * 8 | */ 9 | export interface SetStyleOptions { 10 | element?: HTMLElement; 11 | } 12 | function setStyle(style: CSSProperties, options: SetStyleOptions = {}): CSSProperties { 13 | const { element = document.body } = options; 14 | const oldStyle: CSSProperties = {}; 15 | 16 | const styleKeys = Object.keys(style); 17 | 18 | // IE browser compatible 19 | styleKeys.forEach(key => { 20 | oldStyle[key] = element.style[key]; 21 | }); 22 | 23 | styleKeys.forEach(key => { 24 | element.style[key] = style[key]; 25 | }); 26 | 27 | return oldStyle; 28 | } 29 | 30 | export default setStyle; 31 | -------------------------------------------------------------------------------- /docs/examples/prompts/disabled.vue: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/shape.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | -------------------------------------------------------------------------------- /docs/examples/bubble/shape.vue: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /src/attachments/SilentUploader.vue: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/_util/cssinjs/theme/createTheme.ts: -------------------------------------------------------------------------------- 1 | import ThemeCache from './ThemeCache'; 2 | import Theme from './Theme'; 3 | import type { DerivativeFunc, TokenType } from './interface'; 4 | 5 | const cacheThemes = new ThemeCache(); 6 | 7 | /** 8 | * Same as new Theme, but will always return same one if `derivative` not changed. 9 | */ 10 | export default function createTheme< 11 | DesignToken extends TokenType, 12 | DerivativeToken extends TokenType, 13 | >( 14 | derivatives: 15 | | DerivativeFunc[] 16 | | DerivativeFunc, 17 | ) { 18 | const derivativeArr = Array.isArray(derivatives) ? derivatives : [derivatives]; 19 | // Create new theme if not exist 20 | if (!cacheThemes.has(derivativeArr)) { 21 | cacheThemes.set(derivativeArr, new Theme(derivativeArr)); 22 | } 23 | 24 | // Get theme from cache and return 25 | return cacheThemes.get(derivativeArr)!; 26 | } 27 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/header-and-footer.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 30 | -------------------------------------------------------------------------------- /docs/examples/conversations/basic.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/util/getDefaultComponentToken.ts: -------------------------------------------------------------------------------- 1 | import type { TokenType } from '../../cssinjs'; 2 | import { merge as mergeToken } from './statistic'; 3 | import type { GetDefaultToken, GetDefaultTokenFn } from './genStyleUtils'; 4 | import type { GlobalToken, TokenMap, TokenMapKey } from '../interface'; 5 | 6 | function getDefaultComponentToken< 7 | CompTokenMap extends TokenMap, 8 | AliasToken extends TokenType, 9 | C extends TokenMapKey, 10 | >( 11 | component: C, 12 | token: GlobalToken = {} as any, 13 | getDefaultToken: GetDefaultToken, 14 | ): any { 15 | if (typeof getDefaultToken === 'function') { 16 | return (getDefaultToken as GetDefaultTokenFn)( 17 | mergeToken(token, token[component] ?? {}), 18 | ); 19 | } 20 | return getDefaultToken ?? {}; 21 | } 22 | 23 | export default getDefaultComponentToken; 24 | -------------------------------------------------------------------------------- /src/suggestion/interface.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, VNode } from "vue"; 2 | 3 | export type SuggestionItem = { 4 | label: VNode | string; 5 | value: string; 6 | 7 | icon?: VNode; 8 | 9 | children?: SuggestionItem[]; 10 | 11 | extra?: VNode | string; 12 | }; 13 | 14 | export interface RenderChildrenProps { 15 | onTrigger: (info?: T | false) => void; 16 | onKeyDown: (e: KeyboardEvent) => void; 17 | } 18 | 19 | export interface SuggestionProps { 20 | prefixCls?: string; 21 | className?: string; 22 | rootClassName?: string; 23 | style?: CSSProperties; 24 | children?: (props: RenderChildrenProps) => VNode; 25 | open?: boolean; 26 | onOpenChange?: (open: boolean) => void; 27 | items: SuggestionItem[] | ((info?: T) => SuggestionItem[]); 28 | onSelect?: (value: string) => void; 29 | block?: boolean; 30 | styles?: Partial>; 31 | classNames?: Partial>; 32 | } 33 | -------------------------------------------------------------------------------- /docs/examples/conversations/group.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /vite.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { resolve } from 'node:path'; 3 | import VueMacros from 'unplugin-vue-macros/vite' 4 | import vue from '@vitejs/plugin-vue'; 5 | import vueJsx from '@vitejs/plugin-vue-jsx'; 6 | 7 | 8 | const externals = ['vue']; 9 | 10 | export default defineConfig({ 11 | plugins: [ 12 | VueMacros({ 13 | plugins: { 14 | vue: vue(), 15 | vueJsx: vueJsx(), 16 | }, 17 | // 覆盖插件选项 18 | }), 19 | ], 20 | build: { 21 | lib: { 22 | entry: resolve(__dirname, 'src/index.ts'), 23 | name: 'index', 24 | fileName: 'index', 25 | }, 26 | rollupOptions: { 27 | external: [...externals], 28 | output: { 29 | globals: { 30 | 'vue': 'Vue', 31 | }, 32 | }, 33 | }, 34 | outDir: 'dist', 35 | }, 36 | resolve: { 37 | dedupe: ['vue'], 38 | }, 39 | optimizeDeps: { 40 | include: [...externals], 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /src/_util/cssinjs/hooks/useHMR.ts: -------------------------------------------------------------------------------- 1 | function useProdHMR() { 2 | return false; 3 | } 4 | 5 | let webpackHMR = false; 6 | 7 | function useDevHMR() { 8 | return webpackHMR; 9 | } 10 | 11 | export default process.env.NODE_ENV === 'production' ? useProdHMR : useDevHMR; 12 | 13 | // Webpack `module.hot.accept` do not support any deps update trigger 14 | // We have to hack handler to force mark as HRM 15 | if ( 16 | process.env.NODE_ENV !== 'production' && 17 | typeof module !== 'undefined' && 18 | module && 19 | (module as any).hot && 20 | typeof window !== 'undefined' 21 | ) { 22 | const win = window as any; 23 | if (typeof win.webpackHotUpdate === 'function') { 24 | const originWebpackHotUpdate = win.webpackHotUpdate; 25 | 26 | win.webpackHotUpdate = (...args: any[]) => { 27 | webpackHMR = true; 28 | setTimeout(() => { 29 | webpackHMR = false; 30 | }, 0); 31 | return originWebpackHotUpdate(...args); 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { globals } from '../vitepress' 2 | import DefaultTheme from 'vitepress/theme' 3 | import type { Theme } from 'vitepress' 4 | import './style.css' 5 | import { computed, h } from 'vue' 6 | import { theme, XProvider } from 'ant-design-x-vue' 7 | import PreferenceSwitch from './components/PreferenceSwitch.vue' 8 | import { useData } from 'vitepress' 9 | 10 | const define = (value: T): T => value 11 | 12 | export default define({ 13 | extends: DefaultTheme, 14 | Layout() { 15 | const { isDark } = useData() 16 | const algorithm = computed(() => isDark.value ? theme.darkAlgorithm : theme.defaultAlgorithm) 17 | 18 | return h(XProvider, { theme: { algorithm: algorithm.value, } }, () => h(DefaultTheme.Layout, null, { 19 | 'sidebar-nav-before': () => h(PreferenceSwitch), 20 | })); 21 | }, 22 | enhanceApp: ({ app }) => { 23 | globals.forEach(([name, Comp]) => { 24 | app.component(name, Comp) 25 | }) 26 | }, 27 | }) 28 | -------------------------------------------------------------------------------- /src/_util/cssinjs/Cache.ts: -------------------------------------------------------------------------------- 1 | export type KeyType = string | number; 2 | type ValueType = [number, any]; // [times, realValue] 3 | const SPLIT = '%'; 4 | class Entity { 5 | instanceId: string; 6 | constructor(instanceId: string) { 7 | this.instanceId = instanceId; 8 | } 9 | /** @private Internal cache map. Do not access this directly */ 10 | cache = new Map(); 11 | 12 | get(keys: KeyType[] | string): ValueType | null { 13 | return this.cache.get(Array.isArray(keys) ? keys.join(SPLIT) : keys) || null; 14 | } 15 | 16 | update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) { 17 | const path = Array.isArray(keys) ? keys.join(SPLIT) : keys; 18 | const prevValue = this.cache.get(path)!; 19 | const nextValue = valueFn(prevValue); 20 | 21 | if (nextValue === null) { 22 | this.cache.delete(path); 23 | } else { 24 | this.cache.set(path, nextValue); 25 | } 26 | } 27 | } 28 | 29 | export default Entity; 30 | -------------------------------------------------------------------------------- /src/bubble/hooks/useTypingConfig.ts: -------------------------------------------------------------------------------- 1 | import { computed, toValue, type MaybeRefOrGetter } from 'vue'; 2 | import type { BubbleProps, TypingOption } from '../interface'; 3 | 4 | function useTypingConfig(typing: MaybeRefOrGetter) { 5 | const typingEnabled = computed(() => { 6 | if (!toValue(typing)) { 7 | return false; 8 | } 9 | return true; 10 | }); 11 | const baseConfig: Required = { 12 | step: 1, 13 | interval: 50, 14 | // set default suffix is empty 15 | suffix: null, 16 | }; 17 | const config = computed(() => { 18 | const typingRaw = toValue(typing); 19 | return { 20 | ...baseConfig, 21 | ...(typeof typingRaw === 'object' ? typingRaw : {}) 22 | } 23 | }); 24 | 25 | return [ 26 | typingEnabled, 27 | computed(() => config.value.step), 28 | computed(() => config.value.interval), 29 | computed(() => config.value.suffix) 30 | ] as const; 31 | } 32 | 33 | export default useTypingConfig; 34 | -------------------------------------------------------------------------------- /docs/examples/sender/speechCustom.vue: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/typing.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | -------------------------------------------------------------------------------- /docs/examples-setup/welcome/variant.vue: -------------------------------------------------------------------------------- 1 | 8 | 31 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/util/calc/calculator.ts: -------------------------------------------------------------------------------- 1 | abstract class AbstractCalculator { 2 | /** 3 | * @descCN 计算两数的和,例如:1 + 2 4 | * @descEN Calculate the sum of two numbers, e.g. 1 + 2 5 | */ 6 | abstract add(num: number | string | AbstractCalculator): this; 7 | 8 | /** 9 | * @descCN 计算两数的差,例如:1 - 2 10 | * @descEN Calculate the difference between two numbers, e.g. 1 - 2 11 | */ 12 | abstract sub(num: number | string | AbstractCalculator): this; 13 | 14 | /** 15 | * @descCN 计算两数的积,例如:1 * 2 16 | * @descEN Calculate the product of two numbers, e.g. 1 * 2 17 | */ 18 | abstract mul(num: number | string | AbstractCalculator): this; 19 | 20 | /** 21 | * @descCN 计算两数的商,例如:1 / 2 22 | * @descEN Calculate the quotient of two numbers, e.g. 1 / 2 23 | */ 24 | abstract div(num: number | string | AbstractCalculator): this; 25 | 26 | /** 27 | * @descCN 获取计算结果 28 | * @descEN Get the calculation result 29 | */ 30 | abstract equal(options?: { unit?: boolean }): string | number; 31 | } 32 | 33 | export default AbstractCalculator; 34 | -------------------------------------------------------------------------------- /docs/examples/prompts/flex-vertical.vue: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/contentQuotesLinter.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from './interface'; 2 | import { lintWarning } from './utils'; 3 | 4 | const linter: Linter = (key, value, info) => { 5 | if (key === 'content') { 6 | // From emotion: https://github.com/emotion-js/emotion/blob/main/packages/serialize/src/index.js#L63 7 | const contentValuePattern = 8 | /(attr|counters?|url|(((repeating-)?(linear|radial))|conic)-gradient)\(|(no-)?(open|close)-quote/; 9 | const contentValues = ['normal', 'none', 'initial', 'inherit', 'unset']; 10 | if ( 11 | typeof value !== 'string' || 12 | (contentValues.indexOf(value) === -1 && 13 | !contentValuePattern.test(value) && 14 | (value.charAt(0) !== value.charAt(value.length - 1) || 15 | (value.charAt(0) !== '"' && value.charAt(0) !== "'"))) 16 | ) { 17 | lintWarning( 18 | `You seem to be using a value for 'content' without quotes, try replacing it with \`content: '"${value}"'\`.`, 19 | info, 20 | ); 21 | } 22 | } 23 | }; 24 | 25 | export default linter; 26 | -------------------------------------------------------------------------------- /src/theme/genStyleUtils.ts: -------------------------------------------------------------------------------- 1 | import { genStyleUtils } from '../_util/cssinjs-utils'; 2 | 3 | import { useXProviderContext } from '../x-provider'; 4 | import { useInternalToken } from './useToken'; 5 | 6 | import type { ComponentTokenMap } from './components'; 7 | import type { AliasToken, SeedToken } from './cssinjs-utils'; 8 | import { unref } from 'vue'; 9 | 10 | export const { genStyleHooks, genComponentStyleHook, genSubStyleComponent } = genStyleUtils< 11 | ComponentTokenMap, 12 | AliasToken, 13 | SeedToken 14 | >({ 15 | usePrefix: () => { 16 | const { getPrefixCls, iconPrefixCls } = useXProviderContext(); 17 | return { 18 | iconPrefixCls: unref(iconPrefixCls), 19 | rootPrefixCls: getPrefixCls(), 20 | }; 21 | }, 22 | useToken: () => { 23 | const [theme, realToken, hashId, token, cssVar] = useInternalToken(); 24 | return { theme, realToken, hashId, token, cssVar }; 25 | }, 26 | useCSP: () => { 27 | const { csp } = useXProviderContext(); 28 | return csp?.value ?? {}; 29 | }, 30 | layer: { 31 | name: 'antdx', 32 | dependencies: ['antd'], 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /src/_util/cssinjs/linters/legacyNotSelectorLinter.ts: -------------------------------------------------------------------------------- 1 | import type { Linter, LinterInfo } from './interface'; 2 | import { lintWarning } from './utils'; 3 | 4 | function isConcatSelector(selector: string) { 5 | const notContent = selector.match(/:not\(([^)]*)\)/)?.[1] || ''; 6 | 7 | // split selector. e.g. 8 | // `h1#a.b` => ['h1', #a', '.b'] 9 | const splitCells = notContent.split(/(\[[^[]*])|(?=[.#])/).filter(str => str); 10 | 11 | return splitCells.length > 1; 12 | } 13 | 14 | function parsePath(info: LinterInfo) { 15 | return info.parentSelectors.reduce((prev, cur) => { 16 | if (!prev) { 17 | return cur; 18 | } 19 | 20 | return cur.includes('&') ? cur.replace(/&/g, prev) : `${prev} ${cur}`; 21 | }, ''); 22 | } 23 | 24 | const linter: Linter = (_key, _value, info) => { 25 | const parentSelectorPath = parsePath(info); 26 | const notList = parentSelectorPath.match(/:not\([^)]*\)/g) || []; 27 | 28 | if (notList.length > 0 && notList.some(isConcatSelector)) { 29 | lintWarning(`Concat ':not' selector not support in legacy browsers.`, info); 30 | } 31 | }; 32 | 33 | export default linter; 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) wzc520pyfm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/examples/bubble/typing.vue: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /docs/examples/thought-chain/basic.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /src/vc-util/warning.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | let warned: Record = {}; 3 | 4 | export function warning(valid: boolean, message: string) { 5 | // Support uglify 6 | if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) { 7 | console.error(`Warning: ${message}`); 8 | } 9 | } 10 | 11 | export function note(valid: boolean, message: string) { 12 | // Support uglify 13 | if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) { 14 | console.warn(`Note: ${message}`); 15 | } 16 | } 17 | 18 | export function resetWarned() { 19 | warned = {}; 20 | } 21 | 22 | export function call( 23 | method: (valid: boolean, message: string) => void, 24 | valid: boolean, 25 | message: string, 26 | ) { 27 | if (!valid && !warned[message]) { 28 | method(false, message); 29 | warned[message] = true; 30 | } 31 | } 32 | 33 | export function warningOnce(valid: boolean, message: string) { 34 | call(warning, valid, message); 35 | } 36 | 37 | export function noteOnce(valid: boolean, message: string) { 38 | call(note, valid, message); 39 | } 40 | 41 | export default warningOnce; 42 | /* eslint-enable */ 43 | -------------------------------------------------------------------------------- /src/bubble/hooks/useListData.ts: -------------------------------------------------------------------------------- 1 | import { computed, type Ref } from 'vue'; 2 | import type { BubbleDataType, BubbleListProps } from '../interface'; 3 | import type { BubbleProps } from '../interface'; 4 | 5 | export type UnRef> = T extends Ref ? R : never; 6 | 7 | export type ListItemType = UnRef>[number]; 8 | 9 | export default function useListData( 10 | items: Ref, 11 | roles?: Ref, 12 | ) { 13 | const getRoleBubbleProps = (bubble: BubbleDataType, index: number): Partial => { 14 | if (typeof roles.value === 'function') { 15 | return roles.value(bubble, index); 16 | } 17 | 18 | if (roles) { 19 | return roles.value[bubble.role!] || {}; 20 | } 21 | 22 | return {}; 23 | } 24 | 25 | const listData = computed(() => 26 | (items.value || []).map((bubbleData, i) => { 27 | const mergedKey = bubbleData.key ?? `preset_${i}`; 28 | 29 | return { 30 | ...getRoleBubbleProps(bubbleData, i), 31 | ...bubbleData, 32 | key: mergedKey, 33 | }; 34 | })); 35 | 36 | return listData as Ref; 37 | } 38 | -------------------------------------------------------------------------------- /src/sender/components/LoadingButton.vue: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /docs/examples-setup/thought-chain/basic.vue: -------------------------------------------------------------------------------- 1 | 32 | 37 | -------------------------------------------------------------------------------- /src/sender/StopLoading.vue: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/interface/components.ts: -------------------------------------------------------------------------------- 1 | import type { TokenType } from '../../cssinjs'; 2 | 3 | export type TokenMap = Record; 4 | 5 | export type TokenMapKey = Extract; 6 | 7 | export type GlobalToken = AliasToken & 8 | CompTokenMap; 9 | 10 | export type OverrideTokenMap = { 11 | [key in keyof CompTokenMap]: Partial & Partial; 12 | }; 13 | 14 | export type GlobalTokenWithComponent< 15 | CompTokenMap extends TokenMap, 16 | AliasToken extends TokenType, 17 | C extends TokenMapKey, 18 | > = GlobalToken & CompTokenMap[C]; 19 | 20 | export type ComponentToken< 21 | CompTokenMap extends TokenMap, 22 | AliasToken extends TokenType, 23 | C extends TokenMapKey, 24 | > = Exclude[C], undefined>; 25 | 26 | export type ComponentTokenKey< 27 | CompTokenMap extends TokenMap, 28 | AliasToken extends TokenType, 29 | C extends TokenMapKey, 30 | > = keyof ComponentToken; 31 | -------------------------------------------------------------------------------- /src/theme/components.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentToken as AttachmentsToken } from '../attachments/style'; 2 | import type { ComponentToken as BubbleComponentToken } from '../bubble/style'; 3 | import type { ComponentToken as ConversationsComponentToken } from '../conversations/style'; 4 | import type { ComponentToken as PromptsComponentToken } from '../prompts/style'; 5 | import type { ComponentToken as SenderComponentToken } from '../sender/style'; 6 | import type { ComponentToken as SuggestionComponentToken } from '../suggestion/style'; 7 | import type { ComponentToken as ThoughtChainComponentToken } from '../thought-chain/style'; 8 | import type { ComponentToken as TransitionCollapseComponentToken } from '../transition-collapse/style'; 9 | import type { ComponentToken as WelcomeComponentToken } from '../welcome/style'; 10 | 11 | export interface ComponentTokenMap { 12 | TransitionCollapse?: TransitionCollapseComponentToken; 13 | Attachments?: AttachmentsToken; 14 | Bubble?: BubbleComponentToken; 15 | Conversations?: ConversationsComponentToken; 16 | Prompts?: PromptsComponentToken; 17 | Sender?: SenderComponentToken; 18 | Suggestion?: SuggestionComponentToken; 19 | ThoughtChain?: ThoughtChainComponentToken; 20 | Welcome?: WelcomeComponentToken; 21 | } 22 | -------------------------------------------------------------------------------- /docs/examples-setup/suggestion/block.vue: -------------------------------------------------------------------------------- 1 | 21 | 46 | -------------------------------------------------------------------------------- /src/_util/cssinjs/theme/Theme.ts: -------------------------------------------------------------------------------- 1 | import warning from '../../warning'; 2 | import type { DerivativeFunc, TokenType } from './interface'; 3 | 4 | let uuid = 0; 5 | 6 | /** 7 | * Theme with algorithms to derive tokens from design tokens. 8 | * Use `createTheme` first which will help to manage the theme instance cache. 9 | */ 10 | export default class Theme { 11 | private derivatives: DerivativeFunc[]; 12 | public readonly id: number; 13 | 14 | constructor( 15 | derivatives: 16 | | DerivativeFunc 17 | | DerivativeFunc[], 18 | ) { 19 | this.derivatives = Array.isArray(derivatives) ? derivatives : [derivatives]; 20 | this.id = uuid; 21 | 22 | if (derivatives.length === 0) { 23 | warning( 24 | derivatives.length > 0, 25 | '[Ant Design Vue CSS-in-JS] Theme should have at least one derivative function.', 26 | ); 27 | } 28 | 29 | uuid += 1; 30 | } 31 | 32 | getDerivativeToken(token: DesignToken): DerivativeToken { 33 | return this.derivatives.reduce( 34 | (result, derivative) => derivative(token, result), 35 | undefined as any, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/transition-collapse/style/index.ts: -------------------------------------------------------------------------------- 1 | import type { FullToken, GenerateStyle } from '../../theme/cssinjs-utils'; 2 | 3 | // biome-ignore lint/suspicious/noEmptyInterface: ComponentToken need to be empty by default 4 | export interface ComponentToken {} 5 | 6 | export interface TransitionCollapseToken 7 | extends FullToken<'TransitionCollapse'> {} 8 | 9 | export const genTransitionCollapseStyle: GenerateStyle< 10 | TransitionCollapseToken 11 | > = (token) => { 12 | const { componentCls } = token; 13 | const transitionCollapseCls = `${componentCls}-transition-collapse`; 14 | return { 15 | [componentCls]: { 16 | [transitionCollapseCls]: { 17 | '&-enter-active': { 18 | transition: ['max-height', 'padding-top', 'padding-bottom'] 19 | .map( 20 | (prop) => 21 | `${prop} ${token.motionDurationSlow} ${token.motionEaseInOut}`, 22 | ) 23 | .join(','), 24 | }, 25 | 26 | '&-leave-active': { 27 | transition: ['max-height', 'padding-top', 'padding-bottom'] 28 | .map( 29 | (prop) => 30 | `${prop} ${token.motionDurationSlow} ${token.motionEaseInOut}`, 31 | ) 32 | .join(','), 33 | }, 34 | }, 35 | }, 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/hooks/useToken.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import type { Theme, TokenType } from '../../cssinjs'; 3 | 4 | import type { OverrideTokenMap, TokenMap, GlobalToken } from '../interface'; 5 | 6 | export type TokenMapWithTheme< 7 | CompTokenMap extends TokenMap, 8 | AliasToken extends TokenType, 9 | DesignToken extends TokenType, 10 | > = { 11 | [key in keyof OverrideTokenMap]?: OverrideTokenMap< 12 | CompTokenMap, 13 | AliasToken 14 | >[key] & { 15 | theme?: Theme; 16 | }; 17 | }; 18 | 19 | export interface UseTokenReturn< 20 | CompTokenMap extends TokenMap, 21 | AliasToken extends TokenType, 22 | DesignToken extends TokenType, 23 | > { 24 | token: Ref>; 25 | realToken?: GlobalToken; 26 | theme?: Ref>; 27 | components?: TokenMapWithTheme; 28 | hashId?: Ref; 29 | hashed?: string | boolean; 30 | cssVar?: { 31 | prefix?: string; 32 | key?: string; 33 | }; 34 | } 35 | 36 | export type UseToken< 37 | CompTokenMap extends TokenMap, 38 | DesignToken extends TokenType, 39 | AliasToken extends TokenType, 40 | > = () => UseTokenReturn; 41 | -------------------------------------------------------------------------------- /src/_util/motion.ts: -------------------------------------------------------------------------------- 1 | import type { MotionEvent, CSSMotionProps, MotionEndEventHandler, MotionEventHandler } from './transition'; 2 | import { defaultPrefixCls } from '../x-provider'; 3 | 4 | // ================== Collapse Motion ================== 5 | const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 }); 6 | const getRealHeight: MotionEventHandler = (node) => { 7 | const { scrollHeight } = node; 8 | return { height: scrollHeight, opacity: 1 }; 9 | }; 10 | const getCurrentHeight: MotionEventHandler = (node) => ({ height: node ? (node as HTMLElement).offsetHeight : 0 }); 11 | const skipOpacityTransition: MotionEndEventHandler = (_, event: MotionEvent) => 12 | event?.deadline === true || (event as TransitionEvent).propertyName === 'height'; 13 | 14 | const initCollapseMotion = (rootCls = defaultPrefixCls): CSSMotionProps => ({ 15 | motionName: `${rootCls}-motion-collapse`, 16 | onAppearStart: getCollapsedHeight, 17 | onEnterStart: getCollapsedHeight, 18 | onAppearActive: getRealHeight, 19 | onEnterActive: getRealHeight, 20 | onLeaveStart: getCurrentHeight, 21 | onLeaveActive: getCollapsedHeight, 22 | onAppearEnd: skipOpacityTransition, 23 | onEnterEnd: skipOpacityTransition, 24 | onLeaveEnd: skipOpacityTransition, 25 | motionDeadline: 500, 26 | }); 27 | 28 | export default initCollapseMotion; 29 | -------------------------------------------------------------------------------- /src/bubble/context.ts: -------------------------------------------------------------------------------- 1 | import { computed, ComputedRef, defineComponent, inject, InjectionKey, provide, shallowRef, triggerRef, unref, watch } from "vue"; 2 | import { objectType } from "../_util/type"; 3 | import { BubbleContextProps } from "./interface"; 4 | 5 | const BubbleContextKey: InjectionKey> = 6 | Symbol('BubbleContext'); 7 | 8 | export const globalBubbleContextApi = shallowRef(); 9 | 10 | export const useBubbleContextProvider = (value: ComputedRef) => { 11 | provide(BubbleContextKey, value); 12 | watch( 13 | value, 14 | () => { 15 | globalBubbleContextApi.value = unref(value); 16 | triggerRef(globalBubbleContextApi); 17 | }, 18 | { immediate: true, deep: true }, 19 | ); 20 | }; 21 | 22 | export const useBubbleContextInject = () => { 23 | return inject( 24 | BubbleContextKey, 25 | computed(() => globalBubbleContextApi.value || {}), 26 | ); 27 | }; 28 | export const BubbleContextProvider = defineComponent({ 29 | props: { 30 | value: objectType(), 31 | }, 32 | setup(props, { slots }) { 33 | useBubbleContextProvider(computed(() => props.value)); 34 | return () => { 35 | return slots.default?.(); 36 | }; 37 | }, 38 | }); 39 | 40 | export default BubbleContextProvider; 41 | -------------------------------------------------------------------------------- /docs/semantics/bubble.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /docs/semantics/welcome.vue: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/markdown.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 46 | -------------------------------------------------------------------------------- /docs/component/x-provider.md: -------------------------------------------------------------------------------- 1 | # XProvider 全局化配置 2 | 3 | 为组件提供统一的全局化配置。 4 | 5 | ## 使用说明 6 | 7 | `XProvider` 继承了 `antdv` 的 `ConfigProvider`,且为 `ant-design-x-vue` 中的组件提供全局化配置。 8 | 9 | 如果您已经使用 `antdv` 的 `ConfigProvider`,请对您的代码做如下变更: 10 | 11 | ```diff 12 | - import { ConfigProvider } from 'ant-design-vue'; 13 | + import { XProvider } from 'ant-design-x-vue'; 14 | 15 | const App = () => ( 16 | - 17 | + 18 | 19 | - 20 | + 21 | ); 22 | ``` 23 | 24 | ## 代码演示 25 | 26 | ### 使用 27 | 28 | :::demo 如何使用 29 | 30 | x-provider/use 31 | 32 | ::: 33 | 34 | ## API 35 | 36 | `XProvider` 完全继承 `antdv` 的 `ConfigProvider`, 属性参考:[Antdv ConfigProvider](https://www.antdv.com/components/config-provider-cn#api) 37 | 38 | ### 组件配置 39 | 40 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 41 | | --- | --- | --- | --- | --- | 42 | | bubble | 气泡组件的全局配置 | 'classNames' \| 'styles' \| 'className' \| 'style' | - | | 43 | | conversations | 会话组件的全局配置 | 'classNames' \| 'styles' \| 'className' \| 'style' | - | | 44 | | prompts | 提示集组件的全局配置 | 'classNames' \| 'styles' \| 'className' \| 'style' | - | | 45 | | sender | 输入框组件的全局配置 | 'classNames' \| 'styles' \| 'className' \| 'style' | - | | 46 | | suggestion | 建议组件的全局配置 | 'className' \| 'style' | - | | 47 | | thoughtChain | 思维链组件的全局配置 | 'classNames' \| 'styles' \| 'className' \| 'style' | - | | 48 | -------------------------------------------------------------------------------- /docs/examples/bubble/avatar-and-placement.vue: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /docs/examples/suggestion/block.vue: -------------------------------------------------------------------------------- 1 | 50 | -------------------------------------------------------------------------------- /docs/component/x-stream.md: -------------------------------------------------------------------------------- 1 | 2 | # XStream 流 3 | 4 | 转换可读数据流。 5 | 6 | ## 何时使用 7 | 8 | * 将 SSE 协议的 `ReadableStream` 转换为 `Record` 9 | * 将任何协议的 `ReadableStream` 解码并读取 10 | 11 | ## 使用说明 12 | 13 | 常见的 `ReadableStream` 实例,如 `await fetch(...).body` 使用示例: 14 | 15 | ```js 16 | import { XStream } from '@ant-design/x'; 17 | 18 | async function request() { 19 | const response = await fetch(); 20 | // ..... 21 | 22 | for await (const chunk of XStream({ 23 | readableStream: response.body, 24 | })) { 25 | console.log(chunk); 26 | } 27 | } 28 | ``` 29 | 30 | ## 代码演示 31 | 32 | ### 默认协议 - SSE 33 | 34 | > SSE - https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events 35 | 36 | :::demo XStream 默认的 `transformStream` 是用于 SSE 协议的流转换器。`readableStream` 接收一个 `new ReadableStream(...)` 实例,常见的如 `await fetch(...).body` 37 | 38 | x-stream/default-protocol 39 | 40 | ::: 41 | 42 | ### 自定义协议 43 | 44 | > 在本示例中,我们将演示如何解析 SIP 协议, 该协议常用于 P2P 音视频会话协商。 45 | 46 | :::demo 传入 `transformStream` 流转换器,该参数需接收一个 `new TransformStream(...)` 实例。 47 | 48 | x-stream/custom-protocol 49 | 50 | ::: 51 | 52 | ## API 53 | 54 | ### XStreamOptions 55 | 56 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 57 | | --- | --- | --- | --- | --- | 58 | | readableStream | ReadableStream 实例 | ReadableStream<'Uint8Array'> | - | - | 59 | | transformStream | 自定义的 transformStream 用于转换流的处理 | TransformStream | sseTransformStream | - | 60 | -------------------------------------------------------------------------------- /docs/examples-setup/welcome/background.vue: -------------------------------------------------------------------------------- 1 | 22 | 41 | -------------------------------------------------------------------------------- /docs/examples/bubble/markdown.vue: -------------------------------------------------------------------------------- 1 | 52 | -------------------------------------------------------------------------------- /docs/examples-setup/suggestion/trigger.vue: -------------------------------------------------------------------------------- 1 | 12 | 47 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/avatar-and-placement.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | -------------------------------------------------------------------------------- /docs/semantics/sender.vue: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /src/attachments/context.ts: -------------------------------------------------------------------------------- 1 | import { computed, ComputedRef, defineComponent, inject, InjectionKey, provide, shallowRef, triggerRef, unref, watch } from "vue"; 2 | import { objectType } from "../_util/type"; 3 | import { AttachmentContextProps } from "./interface"; 4 | 5 | const AttachmentContextKey: InjectionKey> = 6 | Symbol('AttachmentContext'); 7 | 8 | export const globalAttachmentContextApi = shallowRef(); 9 | 10 | export const useAttachmentContextProvider = (value: ComputedRef) => { 11 | provide(AttachmentContextKey, value); 12 | watch( 13 | value, 14 | () => { 15 | globalAttachmentContextApi.value = unref(value); 16 | triggerRef(globalAttachmentContextApi); 17 | }, 18 | { immediate: true, deep: true }, 19 | ); 20 | }; 21 | 22 | export const useAttachmentContextInject = () => { 23 | return inject( 24 | AttachmentContextKey, 25 | computed(() => globalAttachmentContextApi.value || {}), 26 | ); 27 | }; 28 | export const AttachmentContextProvider = defineComponent({ 29 | inheritAttrs: false, 30 | props: { 31 | value: objectType(), 32 | }, 33 | setup(props, { slots }) { 34 | useAttachmentContextProvider(computed(() => props.value)); 35 | return () => { 36 | return slots.default?.(); 37 | }; 38 | }, 39 | }); 40 | 41 | export default AttachmentContextProvider; 42 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/PreferenceSwitch.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 48 | -------------------------------------------------------------------------------- /docs/examples/sender/headerFixed.vue: -------------------------------------------------------------------------------- 1 | 54 | -------------------------------------------------------------------------------- /play/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/component/welcome.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome 欢迎 3 | 4 | 清晰传达给用户可实现的意图范围和预期功能。 5 | 6 | ## 何时使用 7 | 8 | 使用合适的欢迎推荐组件,可以有效降低用户学习成本,让用户快速了解并顺利开始。 9 | 10 | ## 代码演示 11 | 12 | ### 基本 13 | 14 | :::demo 基础用法。 15 | 16 | welcome/basic 17 | 18 | ::: 19 | 20 | ### 变体 21 | 22 | :::demo 通过 `variant` 属性设置样式变体。 23 | 24 | welcome/variant 25 | 26 | ::: 27 | 28 | ### 背景定制 29 | 30 | :::demo 自定义部分样式。 31 | 32 | welcome/background 33 | 34 | ::: 35 | 36 | ## API 37 | 38 | 39 | 40 | ### WelcomeProps 41 | 42 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 43 | | --- | --- | --- | --- | --- | 44 | | classNames | 自定义样式类名,用于各个提示项的不同部分。 | Record<'icon' \| 'title' \| 'description' \| 'extra', string> | - | - | 45 | | description | 显示在提示列表中的描述。 | VNode \| string | - | - | 46 | | extra | 显示在提示列表末尾的额外操作。 | VNode | - | - | 47 | | icon | 显示在提示列表前侧的图标。 | VNode | - | - | 48 | | rootClassName | 根节点的样式类名。 | string | - | - | 49 | | styles | 自定义样式,用于各个提示项的不同部分。 | Record<'icon' \| 'title' \| 'description' \| 'extra', CSSProperties> | - | - | 50 | | title | 显示在提示列表顶部的标题。 | VNode \| string | - | - | 51 | | variant | 变体类型。 | 'filled' \| 'borderless' | 'filled' | - | 52 | 53 | ### Welcome Slots 54 | 55 | | 插槽名 | 说明 | 56 | | --- | --- | 57 | | title | 显示在提示列表顶部的标题 | 58 | | description | 显示在提示列表中的描述 | 59 | | icon | 显示在提示列表前侧的图标 | 60 | | extra | 显示在提示列表末尾的额外操作 | 61 | 62 | 63 | ## Semantic DOM 64 | 65 | 66 | 67 | ## 主题变量(Design Token) 68 | -------------------------------------------------------------------------------- /src/sender/context.ts: -------------------------------------------------------------------------------- 1 | import { computed, defineComponent, inject, provide, shallowRef, triggerRef, unref, watch } from "vue"; 2 | import type { ComputedRef, InjectionKey } from "vue"; 3 | import type { SenderHeaderContextProps } from "./interface"; 4 | import { objectType } from "../_util/type"; 5 | 6 | const SenderHeaderContextKey: InjectionKey> = 7 | Symbol('SenderHeaderContext'); 8 | 9 | export const globalSenderHeaderContextApi = shallowRef(); 10 | 11 | export const useSenderHeaderContextProvider = (value: ComputedRef) => { 12 | provide(SenderHeaderContextKey, value); 13 | watch( 14 | value, 15 | () => { 16 | globalSenderHeaderContextApi.value = unref(value); 17 | triggerRef(globalSenderHeaderContextApi); 18 | }, 19 | { immediate: true, deep: true }, 20 | ); 21 | }; 22 | 23 | export const useSenderHeaderContextInject = () => { 24 | return inject( 25 | SenderHeaderContextKey, 26 | computed(() => globalSenderHeaderContextApi.value || {}), 27 | ); 28 | }; 29 | export const SenderHeaderContextProvider = defineComponent({ 30 | props: { 31 | value: objectType(), 32 | }, 33 | setup(props, { slots }) { 34 | useSenderHeaderContextProvider(computed(() => props.value)); 35 | return () => { 36 | return slots.default?.(); 37 | }; 38 | }, 39 | }); 40 | 41 | export default SenderHeaderContextProvider; 42 | -------------------------------------------------------------------------------- /src/attachments/FileList/VideoIcon.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/suggestion/style/index.ts: -------------------------------------------------------------------------------- 1 | import { mergeToken } from '../../_util/cssinjs-utils'; 2 | import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/cssinjs-utils'; 3 | import { genStyleHooks } from '../../theme/genStyleUtils'; 4 | 5 | // biome-ignore lint/suspicious/noEmptyInterface: ComponentToken need to be empty by default 6 | export interface ComponentToken {} 7 | 8 | interface SuggestionToken extends FullToken<'Suggestion'> {} 9 | 10 | const genSuggestionStyle: GenerateStyle = (token) => { 11 | const { componentCls, antCls } = token; 12 | 13 | return { 14 | [componentCls]: { 15 | [`${antCls}-cascader-menus ${antCls}-cascader-menu`]: { 16 | height: 'auto', 17 | }, 18 | 19 | [`${componentCls}-item`]: { 20 | '&-icon': { 21 | marginInlineEnd: token.paddingXXS, 22 | }, 23 | 24 | '&-extra': { 25 | marginInlineStart: token.padding, 26 | }, 27 | }, 28 | 29 | [`&${componentCls}-block`]: { 30 | [`${componentCls}-item-extra`]: { 31 | marginInlineStart: 'auto', 32 | }, 33 | }, 34 | }, 35 | }; 36 | }; 37 | 38 | export const prepareComponentToken: GetDefaultToken<'Suggestion'> = () => ({}); 39 | 40 | export default genStyleHooks<'Suggestion'>( 41 | 'Suggestion', 42 | (token) => { 43 | const SuggestionToken = mergeToken(token, {}); 44 | return genSuggestionStyle(SuggestionToken); 45 | }, 46 | prepareComponentToken, 47 | ); 48 | -------------------------------------------------------------------------------- /src/sender/components/ActionButton/context.ts: -------------------------------------------------------------------------------- 1 | import { computed, defineComponent, inject, provide, shallowRef, triggerRef, unref, watch } from "vue"; 2 | import type { ComputedRef, InjectionKey } from "vue"; 3 | 4 | import { objectType } from "../../../_util/type"; 5 | import type { ActionButtonContextProps } from "../../interface"; 6 | 7 | const ActionButtonContextKey: InjectionKey> = 8 | Symbol('ActionButtonContext'); 9 | 10 | export const globalActionButtonContextApi = shallowRef(); 11 | 12 | export const useActionButtonContextProvider = (value: ComputedRef) => { 13 | provide(ActionButtonContextKey, value); 14 | watch( 15 | value, 16 | () => { 17 | globalActionButtonContextApi.value = unref(value); 18 | triggerRef(globalActionButtonContextApi); 19 | }, 20 | { immediate: true, deep: true }, 21 | ); 22 | }; 23 | 24 | export const useActionButtonContextInject = () => { 25 | return inject( 26 | ActionButtonContextKey, 27 | computed(() => globalActionButtonContextApi.value || {}), 28 | ); 29 | }; 30 | export const ActionButtonContextProvider = defineComponent({ 31 | props: { 32 | value: objectType(), 33 | }, 34 | setup(props, { slots }) { 35 | useActionButtonContextProvider(computed(() => props.value)); 36 | return () => { 37 | return slots.default?.(); 38 | }; 39 | }, 40 | }); 41 | 42 | export default ActionButtonContextProvider; 43 | -------------------------------------------------------------------------------- /docs/examples/bubble/variant.vue: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /src/thought-chain/context.ts: -------------------------------------------------------------------------------- 1 | import { computed, type ComputedRef, defineComponent, inject, type InjectionKey, provide, shallowRef, triggerRef, unref, watch } from "vue"; 2 | import { objectType } from "../_util/type"; 3 | import type { ThoughtChainNodeContextProps } from "./interface"; 4 | 5 | const ThoughtChainNodeContextKey: InjectionKey> = 6 | Symbol('ThoughtChainNodeContext'); 7 | 8 | export const globalThoughtChainNodeContextApi = shallowRef(); 9 | 10 | export const useThoughtChainNodeContextProvider = (value: ComputedRef) => { 11 | provide(ThoughtChainNodeContextKey, value); 12 | watch( 13 | value, 14 | () => { 15 | globalThoughtChainNodeContextApi.value = unref(value); 16 | triggerRef(globalThoughtChainNodeContextApi); 17 | }, 18 | { immediate: true, deep: true }, 19 | ); 20 | }; 21 | 22 | export const useThoughtChainNodeContextInject = () => { 23 | return inject( 24 | ThoughtChainNodeContextKey, 25 | computed(() => globalThoughtChainNodeContextApi.value || {}), 26 | ); 27 | }; 28 | export const ThoughtChainNodeContextProvider = defineComponent({ 29 | props: { 30 | value: objectType(), 31 | }, 32 | setup(props, { slots }) { 33 | useThoughtChainNodeContextProvider(computed(() => props.value)); 34 | return () => { 35 | return slots.default?.(); 36 | }; 37 | }, 38 | }); 39 | 40 | export default ThoughtChainNodeContextProvider; 41 | -------------------------------------------------------------------------------- /src/vc-util/set.ts: -------------------------------------------------------------------------------- 1 | import get from './get'; 2 | 3 | function internalSet( 4 | entity: Entity, 5 | paths: (string | number)[], 6 | value: Value, 7 | removeIfUndefined: boolean, 8 | ): Output { 9 | if (!paths.length) { 10 | return value as unknown as Output; 11 | } 12 | 13 | const [path, ...restPath] = paths; 14 | 15 | let clone: Output; 16 | if (!entity && typeof path === 'number') { 17 | clone = [] as unknown as Output; 18 | } else if (Array.isArray(entity)) { 19 | clone = [...entity] as unknown as Output; 20 | } else { 21 | clone = { ...entity } as unknown as Output; 22 | } 23 | 24 | // Delete prop if `removeIfUndefined` and value is undefined 25 | if (removeIfUndefined && value === undefined && restPath.length === 1) { 26 | delete clone[path][restPath[0]]; 27 | } else { 28 | clone[path] = internalSet(clone[path], restPath, value, removeIfUndefined); 29 | } 30 | 31 | return clone; 32 | } 33 | 34 | export default function set( 35 | entity: Entity, 36 | paths: (string | number)[], 37 | value: Value, 38 | removeIfUndefined = false, 39 | ): Output { 40 | // Do nothing if `removeIfUndefined` and parent object not exist 41 | if ( 42 | paths.length && 43 | removeIfUndefined && 44 | value === undefined && 45 | !get(entity, paths.slice(0, -1)) 46 | ) { 47 | return entity as unknown as Output; 48 | } 49 | 50 | return internalSet(entity, paths, value, removeIfUndefined); 51 | } 52 | -------------------------------------------------------------------------------- /docs/examples/welcome/background.vue: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /src/conversations/context.ts: -------------------------------------------------------------------------------- 1 | import { shallowRef, provide, watch, unref, triggerRef, inject, computed, defineComponent } from 'vue'; 2 | import type { ComputedRef, InjectionKey } from "vue"; 3 | import type { GroupTitleContextProps } from './interface' 4 | import { objectType } from '../_util/type'; 5 | 6 | const GroupTitleContextKey: InjectionKey> = 7 | Symbol('GroupTitleContext'); 8 | 9 | export const globalGroupTitleContextApi = shallowRef(); 10 | 11 | // User should not care about internal state. 12 | // Which should pass by context instead. 13 | export const useGroupTitleContextProvider = (value: ComputedRef) => { 14 | provide(GroupTitleContextKey, value); 15 | watch( 16 | value, 17 | () => { 18 | globalGroupTitleContextApi.value = unref(value); 19 | triggerRef(globalGroupTitleContextApi); 20 | }, 21 | { immediate: true, deep: true }, 22 | ); 23 | }; 24 | 25 | export const useGroupTitleContextInject = () => { 26 | return inject( 27 | GroupTitleContextKey, 28 | computed(() => globalGroupTitleContextApi.value || {}), 29 | ); 30 | }; 31 | 32 | export const GroupTitleContextProvider = defineComponent({ 33 | props: { 34 | value: objectType(), 35 | }, 36 | setup(props, { slots }) { 37 | useGroupTitleContextProvider(computed(() => props.value)); 38 | return () => { 39 | return slots.default?.(); 40 | }; 41 | }, 42 | }); 43 | 44 | export default GroupTitleContextProvider; 45 | 46 | -------------------------------------------------------------------------------- /docs/examples/sender/basic.vue: -------------------------------------------------------------------------------- 1 | 60 | -------------------------------------------------------------------------------- /docs/examples/conversations/controlled-mode.vue: -------------------------------------------------------------------------------- 1 | 54 | -------------------------------------------------------------------------------- /src/theme/cssinjs-utils.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | GlobalToken as GlobalTokenTypeUtil, 3 | OverrideTokenMap as OverrideTokenTypeUtil, 4 | FullToken as FullTokenTypeUtil, 5 | GetDefaultToken as GetDefaultTokenTypeUtil, 6 | GenStyleFn as GenStyleFnTypeUtil, 7 | TokenMapKey, 8 | } from '../_util/cssinjs-utils'; 9 | import type { CSSInterpolation } from '../_util/cssinjs'; 10 | import type { AliasToken } from 'ant-design-vue/es/theme/internal'; 11 | import type { AnyObject } from '../_util/type'; 12 | import type { ComponentTokenMap } from './components'; 13 | 14 | export type { AliasToken, SeedToken } from 'ant-design-vue/es/theme/internal'; 15 | 16 | /** Final token which contains the components level override */ 17 | export type GlobalToken = GlobalTokenTypeUtil; 18 | export type OverrideToken = OverrideTokenTypeUtil; 19 | 20 | export type OverrideComponent = TokenMapKey; 21 | 22 | export type FullToken> = FullTokenTypeUtil< 23 | ComponentTokenMap, 24 | AliasToken, 25 | C 26 | >; 27 | 28 | export type GetDefaultToken> = GetDefaultTokenTypeUtil< 29 | ComponentTokenMap, 30 | AliasToken, 31 | C 32 | >; 33 | 34 | export type GenStyleFn> = GenStyleFnTypeUtil< 35 | ComponentTokenMap, 36 | AliasToken, 37 | C 38 | >; 39 | 40 | export type GenerateStyle< 41 | ComponentToken extends AnyObject = AliasToken, 42 | ReturnType = CSSInterpolation, 43 | > = (token: ComponentToken) => ReturnType; 44 | -------------------------------------------------------------------------------- /src/_util/cssinjs-utils/util/calc/NumCalculator.ts: -------------------------------------------------------------------------------- 1 | import AbstractCalculator from './calculator'; 2 | 3 | class NumCalculator extends AbstractCalculator { 4 | result: number = 0; 5 | 6 | constructor(num: number | string | AbstractCalculator) { 7 | super(); 8 | if (num instanceof NumCalculator) { 9 | this.result = num.result; 10 | } else if (typeof num === 'number') { 11 | this.result = num; 12 | } 13 | } 14 | 15 | add(num: number | string | AbstractCalculator): this { 16 | if (num instanceof NumCalculator) { 17 | this.result += num.result; 18 | } else if (typeof num === 'number') { 19 | this.result += num; 20 | } 21 | return this; 22 | } 23 | 24 | sub(num: number | string | AbstractCalculator): this { 25 | if (num instanceof NumCalculator) { 26 | this.result -= num.result; 27 | } else if (typeof num === 'number') { 28 | this.result -= num; 29 | } 30 | return this; 31 | } 32 | 33 | mul(num: number | string | AbstractCalculator): this { 34 | if (num instanceof NumCalculator) { 35 | this.result *= num.result; 36 | } else if (typeof num === 'number') { 37 | this.result *= num; 38 | } 39 | return this; 40 | } 41 | 42 | div(num: number | string | AbstractCalculator): this { 43 | if (num instanceof NumCalculator) { 44 | this.result /= num.result; 45 | } else if (typeof num === 'number') { 46 | this.result /= num; 47 | } 48 | return this; 49 | } 50 | 51 | equal(): number { 52 | return this.result; 53 | } 54 | } 55 | 56 | export default NumCalculator; 57 | -------------------------------------------------------------------------------- /docs/examples/suggestion/trigger.vue: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /src/vc-util/isEqual.ts: -------------------------------------------------------------------------------- 1 | import warning from './warning'; 2 | 3 | /** 4 | * Deeply compares two object literals. 5 | * @param obj1 object 1 6 | * @param obj2 object 2 7 | * @param shallow shallow compare 8 | * @returns 9 | */ 10 | function isEqual(obj1: any, obj2: any, shallow = false): boolean { 11 | // https://github.com/mapbox/mapbox-gl-js/pull/5979/files#diff-fde7145050c47cc3a306856efd5f9c3016e86e859de9afbd02c879be5067e58f 12 | const refSet = new Set(); 13 | function deepEqual(a: any, b: any, level = 1): boolean { 14 | const circular = refSet.has(a); 15 | warning(!circular, 'Warning: There may be circular references'); 16 | if (circular) { 17 | return false; 18 | } 19 | if (a === b) { 20 | return true; 21 | } 22 | if (shallow && level > 1) { 23 | return false; 24 | } 25 | refSet.add(a); 26 | const newLevel = level + 1; 27 | if (Array.isArray(a)) { 28 | if (!Array.isArray(b) || a.length !== b.length) { 29 | return false; 30 | } 31 | for (let i = 0; i < a.length; i++) { 32 | if (!deepEqual(a[i], b[i], newLevel)) { 33 | return false; 34 | } 35 | } 36 | return true; 37 | } 38 | if (a && b && typeof a === 'object' && typeof b === 'object') { 39 | const keys = Object.keys(a); 40 | if (keys.length !== Object.keys(b).length) { 41 | return false; 42 | } 43 | return keys.every(key => deepEqual(a[key], b[key], newLevel)); 44 | } 45 | // other 46 | return false; 47 | } 48 | 49 | return deepEqual(obj1, obj2); 50 | } 51 | 52 | export default isEqual; 53 | -------------------------------------------------------------------------------- /src/theme/patch-antd.ts: -------------------------------------------------------------------------------- 1 | import type { MapToken } from "ant-design-vue/es/theme/interface"; 2 | import type { AliasToken, OverrideToken, SeedToken } from "./cssinjs-utils"; 3 | import type { Theme } from "ant-design-vue"; 4 | 5 | export const ignore: { 6 | [key in keyof AliasToken]?: boolean; 7 | } = { 8 | size: true, 9 | sizeSM: true, 10 | sizeLG: true, 11 | sizeMD: true, 12 | sizeXS: true, 13 | sizeXXS: true, 14 | sizeMS: true, 15 | sizeXL: true, 16 | sizeXXL: true, 17 | sizeUnit: true, 18 | sizeStep: true, 19 | motionBase: true, 20 | motionUnit: true, 21 | }; 22 | 23 | export const unitless: { 24 | [key in keyof AliasToken]?: boolean; 25 | } = { 26 | lineHeight: true, 27 | lineHeightSM: true, 28 | lineHeightLG: true, 29 | lineHeightHeading1: true, 30 | lineHeightHeading2: true, 31 | lineHeightHeading3: true, 32 | lineHeightHeading4: true, 33 | lineHeightHeading5: true, 34 | opacityLoading: true, 35 | fontWeightStrong: true, 36 | zIndexPopupBase: true, 37 | zIndexBase: true, 38 | opacityImage: true, 39 | }; 40 | 41 | export type ComponentsToken = { 42 | [key in keyof OverrideToken]?: OverrideToken[key] & { 43 | theme?: Theme; 44 | }; 45 | }; 46 | 47 | export interface DesignTokenProviderProps { 48 | token: Partial; 49 | theme?: Theme; 50 | components?: ComponentsToken; 51 | /** Just merge `token` & `override` at top to save perf */ 52 | override: { override: Partial } & ComponentsToken; 53 | hashed?: string | boolean; 54 | cssVar?: { 55 | prefix?: string; 56 | key?: string; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /docs/examples-setup/suggestion/basic.vue: -------------------------------------------------------------------------------- 1 | 38 | 62 | -------------------------------------------------------------------------------- /docs/examples-setup/attachments/files.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 76 | -------------------------------------------------------------------------------- /docs/examples/conversations/group-sort.vue: -------------------------------------------------------------------------------- 1 | 55 | -------------------------------------------------------------------------------- /src/sender/components/SpeechButton/index.vue: -------------------------------------------------------------------------------- 1 | 53 | -------------------------------------------------------------------------------- /src/_util/hooks/useMergedState.ts: -------------------------------------------------------------------------------- 1 | import type { Ref, UnwrapRef } from 'vue'; 2 | import { toRaw, watchEffect, unref, watch, ref } from 'vue'; 3 | 4 | export default function useMergedState>( 5 | defaultStateValue: T | (() => T), 6 | option?: { 7 | defaultValue?: T | (() => T); 8 | value?: Ref | Ref>; 9 | onChange?: (val: T, prevValue: T) => void; 10 | postState?: (val: T) => T; 11 | }, 12 | ): [R, (val: T) => void] { 13 | const { defaultValue, value = ref() } = option || {}; 14 | let initValue: T = 15 | typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue; 16 | if (value.value !== undefined) { 17 | initValue = unref(value as any) as T; 18 | } 19 | if (defaultValue !== undefined) { 20 | initValue = typeof defaultValue === 'function' ? (defaultValue as any)() : defaultValue; 21 | } 22 | 23 | const innerValue = ref(initValue) as Ref; 24 | const mergedValue = ref(initValue) as Ref; 25 | watchEffect(() => { 26 | let val = value.value !== undefined ? value.value : innerValue.value; 27 | if (option.postState) { 28 | val = option.postState(val as T); 29 | } 30 | mergedValue.value = val as T; 31 | }); 32 | 33 | function triggerChange(newValue: T) { 34 | const preVal = mergedValue.value; 35 | innerValue.value = newValue; 36 | if (toRaw(mergedValue.value) !== newValue && option.onChange) { 37 | option.onChange(newValue, preVal); 38 | } 39 | } 40 | 41 | // Effect of reset value to `undefined` 42 | watch(value, () => { 43 | innerValue.value = value.value as T; 44 | }); 45 | 46 | return [mergedValue as unknown as R, triggerChange]; 47 | } 48 | -------------------------------------------------------------------------------- /src/attachments/FileList/AudioIcon.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/sender/components/ActionButton/index.vue: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /docs/component/use-x-chat.md: -------------------------------------------------------------------------------- 1 | 2 | # useXChat 数据管理 3 | 4 | 配合 Agent hook 进行对话数据管理。 5 | 6 | ## 何时使用 7 | 8 | 通过 Agent 进行会话数据管理,并产出供页面渲染使用的数据。 9 | 10 | ## 代码演示 11 | 12 | ### 基本 13 | 14 | :::demo 基础用法。 15 | 16 | use-x-chat/basic 17 | 18 | ::: 19 | 20 | ### 流式输出 21 | 22 | :::demo 使用流式输出更新内容。 23 | 24 | use-x-chat/stream 25 | 26 | ::: 27 | 28 | ### 打断输出 29 | 30 | :::demo 打断正在流式输出的内容。 31 | 32 | use-x-chat/stream-cancel 33 | 34 | ::: 35 | 36 | ### 多项建议 37 | 38 | :::demo 通过定制能力,返回多个推荐内容。 39 | 40 | use-x-chat/suggestions 41 | 42 | ::: 43 | 44 | ## API 45 | 46 | ```tsx | pure 47 | type useXChat = ( 48 | config: XChatConfig, 49 | ) => XChatConfigReturnType; 50 | ``` 51 | 52 | ### XChatConfig 53 | 54 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 55 | | --- | --- | --- | --- | --- | 56 | | agent | 通过 `useXAgent` 生成的 `agent`,当使用 `onRequest` 方法时, `agent` 参数是必需的。 | XAgent | - | | 57 | | defaultMessages | 默认展示信息 | { status, message }[] | - | | 58 | | parser | 将 AgentMessage 转换成消费使用的 ParsedMessage,不设置时则直接消费 AgentMessage。支持将一条 AgentMessage 转换成多条 ParsedMessage | (message: AgentMessage) => BubbleMessage \| BubbleMessage[] | - | | 59 | | requestFallback | 请求失败的兜底信息,不提供则不会展示 | AgentMessage \| () => AgentMessage | - | | 60 | | requestPlaceholder | 请求中的占位信息,不提供则不会展示 | AgentMessage \| () => AgentMessage | - | | 61 | 62 | ### XChatConfigReturnType 63 | 64 | | 属性 | 说明 | 类型 | 版本 | 65 | | --- | --- | --- | --- | 66 | | messages | 当前管理的内容 | AgentMessages[] | | 67 | | parsedMessages | 经过 `parser` 转译过的内容 | ParsedMessages[] | | 68 | | onRequest | 添加一条 Message,并且触发请求 | (message) => void | | 69 | | setMessages | 直接修改 messages,不会触发请求 | (messages: { message, status }[]) => void | | 70 | -------------------------------------------------------------------------------- /docs/examples/prompts/flex-wrap-fixed.vue: -------------------------------------------------------------------------------- 1 | 56 | -------------------------------------------------------------------------------- /docs/examples/thought-chain/collapsible.vue: -------------------------------------------------------------------------------- 1 | 50 | -------------------------------------------------------------------------------- /docs/semantics/attachments.vue: -------------------------------------------------------------------------------- 1 | 53 | -------------------------------------------------------------------------------- /docs/examples-setup/bubble/variant.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 63 | -------------------------------------------------------------------------------- /docs/examples-setup/thought-chain/collapsible.vue: -------------------------------------------------------------------------------- 1 | 40 | 48 | -------------------------------------------------------------------------------- /docs/examples/conversations/with-menu.vue: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /docs/examples/sender/focus.vue: -------------------------------------------------------------------------------- 1 | 77 | -------------------------------------------------------------------------------- /docs/examples/suggestion/basic.vue: -------------------------------------------------------------------------------- 1 | 70 | -------------------------------------------------------------------------------- /docs/examples/thought-chain/size.vue: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /src/sender/style/header.ts: -------------------------------------------------------------------------------- 1 | import type { SenderToken } from '.'; 2 | import type { GenerateStyle } from '../../theme/cssinjs-utils'; 3 | 4 | const genSenderHeaderStyle: GenerateStyle = (token) => { 5 | const { componentCls, calc } = token; 6 | 7 | const headerCls = `${componentCls}-header`; 8 | 9 | return { 10 | [componentCls]: { 11 | [headerCls]: { 12 | borderBottomWidth: token.lineWidth, 13 | borderBottomStyle: 'solid', 14 | borderBottomColor: token.colorBorder, 15 | // ======================== Header ======================== 16 | '&-header': { 17 | background: token.colorFillAlter, 18 | fontSize: token.fontSize, 19 | lineHeight: token.lineHeight, 20 | paddingBlock: calc(token.paddingSM).sub(token.lineWidthBold).equal(), 21 | paddingInlineStart: token.padding, 22 | paddingInlineEnd: token.paddingXS, 23 | display: 'flex', 24 | 25 | [`${headerCls}-title`]: { 26 | flex: 'auto', 27 | }, 28 | }, 29 | 30 | // ======================= Content ======================== 31 | '&-content': { 32 | padding: token.padding, 33 | }, 34 | 35 | // ======================== Motion ======================== 36 | '&-motion': { 37 | transition: ['height', 'border'] 38 | .map((prop) => `${prop} ${token.motionDurationSlow}`) 39 | .join(','), 40 | overflow: 'hidden', 41 | 42 | '&-enter-start, &-leave-active': { 43 | borderBottomColor: 'transparent', 44 | }, 45 | 46 | '&-hidden': { 47 | display: 'none', 48 | }, 49 | }, 50 | }, 51 | }, 52 | }; 53 | }; 54 | 55 | export default genSenderHeaderStyle; 56 | -------------------------------------------------------------------------------- /src/bubble/hooks/useDisplayData.ts: -------------------------------------------------------------------------------- 1 | import { computed, Ref, unref, watch } from 'vue'; 2 | import { useEventCallback } from '../../_util/hooks/use-event-callback'; 3 | import useState from '../../_util/hooks/use-state'; 4 | import { ListItemType } from './useListData'; 5 | 6 | type UseDisplayDataReturn = [Ref, (value: string | number) => void]; 7 | 8 | export default function useDisplayData(items: Ref): UseDisplayDataReturn { 9 | const [displayCount, setDisplayCount] = useState(items.value.length); 10 | 11 | const displayList = computed(() => items.value.slice(0, unref(displayCount))); 12 | 13 | const displayListLastKey = computed(() => { 14 | const lastItem = unref(displayList)[unref(displayList).length - 1]; 15 | return lastItem ? lastItem.key : null; 16 | }); 17 | 18 | // When `items` changed, we replaced with latest one 19 | watch( 20 | items, 21 | () => { 22 | if (unref(displayList).length && unref(displayList).every((item, index) => item.key === items.value[index]?.key)) { 23 | return; 24 | } 25 | 26 | if (unref(displayList).length === 0) { 27 | setDisplayCount(1); 28 | } else { 29 | // Find diff index 30 | for (let i = 0; i < unref(displayList).length; i += 1) { 31 | if (unref(displayList)[i].key !== items.value[i]?.key) { 32 | setDisplayCount(i); 33 | break; 34 | } 35 | } 36 | } 37 | }, 38 | { immediate: true, deep: true }, 39 | ); 40 | 41 | // Continue to show if last one finished typing 42 | const onTypingComplete = useEventCallback((key: string | number) => { 43 | if (key === unref(displayListLastKey)) { 44 | setDisplayCount(unref(displayCount) + 1); 45 | } 46 | }); 47 | 48 | return [displayList, onTypingComplete] as const; 49 | } 50 | -------------------------------------------------------------------------------- /docs/examples/attachments/files.vue: -------------------------------------------------------------------------------- 1 | 82 | -------------------------------------------------------------------------------- /src/_util/getScrollBarSize.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | 3 | let cached: number; 4 | 5 | export default function getScrollBarSize(fresh?: boolean) { 6 | if (typeof document === 'undefined') { 7 | return 0; 8 | } 9 | 10 | if (fresh || cached === undefined) { 11 | const inner = document.createElement('div'); 12 | inner.style.width = '100%'; 13 | inner.style.height = '200px'; 14 | 15 | const outer = document.createElement('div'); 16 | const outerStyle = outer.style; 17 | 18 | outerStyle.position = 'absolute'; 19 | outerStyle.top = '0'; 20 | outerStyle.left = '0'; 21 | outerStyle.pointerEvents = 'none'; 22 | outerStyle.visibility = 'hidden'; 23 | outerStyle.width = '200px'; 24 | outerStyle.height = '150px'; 25 | outerStyle.overflow = 'hidden'; 26 | 27 | outer.appendChild(inner); 28 | 29 | document.body.appendChild(outer); 30 | 31 | const widthContained = inner.offsetWidth; 32 | outer.style.overflow = 'scroll'; 33 | let widthScroll = inner.offsetWidth; 34 | 35 | if (widthContained === widthScroll) { 36 | widthScroll = outer.clientWidth; 37 | } 38 | 39 | document.body.removeChild(outer); 40 | 41 | cached = widthContained - widthScroll; 42 | } 43 | return cached; 44 | } 45 | 46 | function ensureSize(str: string) { 47 | const match = str.match(/^(.*)px$/); 48 | const value = Number(match?.[1]); 49 | return Number.isNaN(value) ? getScrollBarSize() : value; 50 | } 51 | 52 | export function getTargetScrollBarSize(target: HTMLElement) { 53 | if (typeof document === 'undefined' || !target || !(target instanceof Element)) { 54 | return { width: 0, height: 0 }; 55 | } 56 | 57 | const { width, height } = getComputedStyle(target, '::-webkit-scrollbar'); 58 | return { 59 | width: ensureSize(width), 60 | height: ensureSize(height), 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /docs/examples-setup/attachments/basic.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 59 | -------------------------------------------------------------------------------- /src/x-provider/index.vue: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /docs/examples-setup/thought-chain/size.vue: -------------------------------------------------------------------------------- 1 | 38 | 65 | -------------------------------------------------------------------------------- /docs/examples/prompts/basic.vue: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /docs/examples-setup/prompts/basic.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 62 | -------------------------------------------------------------------------------- /docs/examples/prompts/flex-wrap.vue: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /docs/component/use-x-agent.md: -------------------------------------------------------------------------------- 1 | 2 | # useXAgent 模型调度 3 | 4 | 用于模型调度的 Agent 钩子。 5 | 6 | ## 何时使用 7 | 8 | 与后端模型进行交互,提供抽象数据流。 9 | 10 | ## 代码演示 11 | 12 | ### 预设请求 13 | 14 | :::demo 我们将 XRequest 作为预设请求,仅需配置 `baseURL`、`model` 即可 15 | 16 | use-x-agent/preset 17 | 18 | ::: 19 | 20 | ### 自定义请求 21 | 22 | :::demo 通过定制能力,返回多个推荐内容。 23 | 24 | use-x-agent/custom 25 | 26 | ::: 27 | 28 | ## API 29 | 30 | ```tsx | pure 31 | type useXAgent = ( 32 | config: XAgentConfigPreset | XAgentConfigCustom, 33 | ) => [Agent]; 34 | ``` 35 | 36 | ### XAgentConfigPreset 37 | 38 | 使用预设协议进行请求,尚未实现协议。 39 | 40 | 41 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 42 | | --- | --- | --- | --- | --- | 43 | | baseURL | 请求服务端地址 | string | - | | 44 | | key | 请求秘钥 | string | - | | 45 | | model | 协议模型 | string | - | | 46 | | dangerouslyApiKey | **注意: 🔥 `dangerouslyApiKey` 存在安全风险,对此有详细的[说明](/#)。** | string | - | - | 47 | 48 | ### XAgentConfigCustom 49 | 50 | 自定义请求协议。 51 | 52 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 53 | | ------- | ---------------------------- | --------- | ------ | ---- | 54 | | request | 配置自定义请求,支持流式更新 | RequestFn | | | 55 | 56 | #### RequestFn 57 | 58 | ```tsx | pure 59 | interface RequestFnInfo extends Partial, AnyObject { 60 | messages?: Message[]; 61 | message?: Message; 62 | } 63 | 64 | export type RequestFn = ( 65 | info: RequestFnInfo, 66 | callbacks: { 67 | onUpdate: (message: Message) => void; 68 | onSuccess: (message: Message) => void; 69 | onError: (error: Error) => void; 70 | }, 71 | ) => void; 72 | ``` 73 | 74 | ### Agent 75 | 76 | | 属性 | 说明 | 类型 | 版本 | 77 | | ------------ | --------------------------- | ------------- | ---- | 78 | | request | 调用 `useXAgent` 配置的请求 | RequestFn | | 79 | | isRequesting | 是否正在请求 | () => boolean | | 80 | -------------------------------------------------------------------------------- /docs/component/suggestion.md: -------------------------------------------------------------------------------- 1 | 2 | # Suggestion 快捷指令 3 | 4 | 用于给予用户快捷提示的组件。 5 | 6 | ## 何时使用 7 | 8 | 需要构建一个对话场景下的输入框。 9 | 10 | ## 代码演示 11 | 12 | ### 基本 13 | 14 | :::demo 基础用法,受控进行状态管理。自定义触发器。 15 | 16 | suggestion/basic 17 | 18 | ::: 19 | 20 | ### 整行宽度 21 | 22 | :::demo 通过 `block` 改为整行展示,`extra` 可用于配置额外信息。 23 | 24 | suggestion/block 25 | 26 | ::: 27 | 28 | ### 自定义 29 | 30 | :::demo 根据输入动态展示建议项的多标签示例。 31 | 32 | suggestion/trigger 33 | 34 | ::: 35 | 36 | ## API 37 | 38 | 39 | 40 | ### SuggestionsProps 41 | 42 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 43 | | --- | --- | --- | --- | --- | 44 | | block | 是否整行宽度 | boolean | false | - | 45 | | children | 自定义输入框 | ({ onTrigger, onKeyDown }) => VNode | - | - | 46 | | items | 建议项列表 | SuggestionItem[] \| ((info: T) => SuggestionItem[]) | - | - | 47 | | open | 受控打开面板 | boolean | - | - | 48 | | rootClassName | 根元素样式类名 | string | - | - | 49 | | onSelect | 选中建议项回调 | (value: string) => void | - | - | 50 | | onOpenChange | 面板打开状态变化回调 | (open: boolean) => void | - | - | 51 | 52 | #### onTrigger 53 | 54 | ```typescript | pure 55 | type onTrigger = (info: T | false) => void; 56 | ``` 57 | 58 | Suggestion 接受泛型以自定义传递给 `items` renderProps 的参数类型,当传递 `false` 时,则关闭建议面板。 59 | 60 | ### Suggestions Slots 61 | 62 | | 插槽名 | 说明 | 类型 | 63 | | --- | --- | --- | 64 | | default | 用于自定义输入框 | \{ onTrigger, onKeyDown \} | 65 | 66 | ### SuggestionItem 67 | 68 | | 属性 | 说明 | 类型 | 默认值 | 版本 | 69 | | -------- | -------------- | ---------------- | ------ | ---- | 70 | | children | 子项目 | SuggestionItem[] | - | - | 71 | | extra | 建议项额外内容 | VNode \| string | - | - | 72 | | icon | 建议项图标 | VNode | - | - | 73 | | label | 建议项显示内容 | VNode \| string | - | - | 74 | | value | 建议项值 | string | - | - | 75 | 76 | ## 主题变量(Design Token) 77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/examples-setup/thought-chain/nested.vue: -------------------------------------------------------------------------------- 1 | 52 | 60 | -------------------------------------------------------------------------------- /src/bubble/style/content.ts: -------------------------------------------------------------------------------- 1 | import { unit } from '../../_util/cssinjs'; 2 | import type { BubbleToken } from '.'; 3 | import type { GenerateStyle } from '../../theme/cssinjs-utils'; 4 | 5 | export const genVariantStyle: GenerateStyle = (token) => { 6 | const { componentCls, paddingSM, padding } = token; 7 | return { 8 | [componentCls]: { 9 | [`${componentCls}-content`]: { 10 | // Shared: filled, outlined, shadow 11 | '&-filled,&-outlined,&-shadow': { 12 | padding: `${unit(paddingSM)} ${unit(padding)}`, 13 | borderRadius: token.borderRadiusLG, 14 | }, 15 | 16 | // Filled: 17 | '&-filled': { 18 | backgroundColor: token.colorFillContent, 19 | }, 20 | 21 | // Outlined: 22 | '&-outlined': { 23 | border: `1px solid ${token.colorBorderSecondary}`, 24 | }, 25 | 26 | // Shadow: 27 | '&-shadow': { 28 | boxShadow: token.boxShadowTertiary, 29 | }, 30 | }, 31 | }, 32 | }; 33 | }; 34 | 35 | export const genShapeStyle: GenerateStyle = (token) => { 36 | const { componentCls, fontSize, lineHeight, paddingSM, padding, calc } = token; 37 | 38 | const halfRadius = calc(fontSize).mul(lineHeight).div(2).add(paddingSM).equal(); 39 | 40 | const contentCls = `${componentCls}-content`; 41 | 42 | return { 43 | [componentCls]: { 44 | [contentCls]: { 45 | // round: 46 | '&-round': { 47 | borderRadius: { 48 | _skip_check_: true, 49 | value: halfRadius, 50 | }, 51 | paddingInline: calc(padding).mul(1.25).equal(), 52 | }, 53 | }, 54 | 55 | // corner: 56 | [`&-start ${contentCls}-corner`]: { 57 | borderStartStartRadius: token.borderRadiusXS, 58 | }, 59 | [`&-end ${contentCls}-corner`]: { 60 | borderStartEndRadius: token.borderRadiusXS, 61 | }, 62 | }, 63 | }; 64 | }; 65 | -------------------------------------------------------------------------------- /docs/examples/thought-chain/nested.vue: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /docs/examples/sender/actions.vue: -------------------------------------------------------------------------------- 1 | 74 | --------------------------------------------------------------------------------