diff --git a/deploy/fba/fba-ui-src/.browserslistrc b/deploy/fba/fba-ui-src/.browserslistrc new file mode 100644 index 0000000..dc3bc09 --- /dev/null +++ b/deploy/fba/fba-ui-src/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 diff --git a/deploy/fba/fba-ui-src/.commitlintrc.js b/deploy/fba/fba-ui-src/.commitlintrc.js new file mode 100644 index 0000000..02e33fa --- /dev/null +++ b/deploy/fba/fba-ui-src/.commitlintrc.js @@ -0,0 +1 @@ +export { default } from '@vben/commitlint-config'; diff --git a/deploy/fba/fba-ui-src/.dockerignore b/deploy/fba/fba-ui-src/.dockerignore new file mode 100644 index 0000000..52b833a --- /dev/null +++ b/deploy/fba/fba-ui-src/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +.gitignore +*.md +dist +.turbo +dist.zip diff --git a/deploy/fba/fba-ui-src/.editorconfig b/deploy/fba/fba-ui-src/.editorconfig new file mode 100644 index 0000000..179aec6 --- /dev/null +++ b/deploy/fba/fba-ui-src/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=2 +max_line_length = 100 +trim_trailing_whitespace = true +quote_type = single + +[*.{yml,yaml,json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/deploy/fba/fba-ui-src/.gitattributes b/deploy/fba/fba-ui-src/.gitattributes new file mode 100644 index 0000000..d4e5bd3 --- /dev/null +++ b/deploy/fba/fba-ui-src/.gitattributes @@ -0,0 +1,11 @@ +# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Automatically normalize line endings (to LF) for all text-based files. +* text=auto eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary \ No newline at end of file diff --git a/deploy/fba/fba-ui-src/.gitconfig b/deploy/fba/fba-ui-src/.gitconfig new file mode 100644 index 0000000..4b28a69 --- /dev/null +++ b/deploy/fba/fba-ui-src/.gitconfig @@ -0,0 +1,2 @@ +[core] + ignorecase = false diff --git a/deploy/fba/fba-ui-src/.gitignore b/deploy/fba/fba-ui-src/.gitignore new file mode 100644 index 0000000..e5537be --- /dev/null +++ b/deploy/fba/fba-ui-src/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.local diff --git a/deploy/fba/fba-ui-src/.lintstagedrc.mjs b/deploy/fba/fba-ui-src/.lintstagedrc.mjs new file mode 100644 index 0000000..94b0192 --- /dev/null +++ b/deploy/fba/fba-ui-src/.lintstagedrc.mjs @@ -0,0 +1,20 @@ +export default { + '*.md': ['prettier --cache --ignore-unknown --write'], + '*.vue': [ + 'prettier --write', + 'eslint --cache --fix', + 'stylelint --fix --allow-empty-input', + ], + '*.{js,jsx,ts,tsx}': [ + 'prettier --cache --ignore-unknown --write', + 'eslint --cache --fix', + ], + '*.{scss,less,styl,html,vue,css}': [ + 'prettier --cache --ignore-unknown --write', + 'stylelint --fix --allow-empty-input', + ], + 'package.json': ['prettier --cache --write'], + '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [ + 'prettier --cache --write--parser json', + ], +}; diff --git a/deploy/fba/fba-ui-src/.node-version b/deploy/fba/fba-ui-src/.node-version new file mode 100644 index 0000000..b832e40 --- /dev/null +++ b/deploy/fba/fba-ui-src/.node-version @@ -0,0 +1 @@ +24.16.0 diff --git a/deploy/fba/fba-ui-src/.npmrc b/deploy/fba/fba-ui-src/.npmrc new file mode 100644 index 0000000..7549542 --- /dev/null +++ b/deploy/fba/fba-ui-src/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmmirror.com diff --git a/deploy/fba/fba-ui-src/.stylelintignore b/deploy/fba/fba-ui-src/.stylelintignore new file mode 100644 index 0000000..3adb33b --- /dev/null +++ b/deploy/fba/fba-ui-src/.stylelintignore @@ -0,0 +1,8 @@ +dist +public +__tests__ +coverage +.codex +.claude +.agent +.agents diff --git a/deploy/fba/fba-ui-src/Dockerfile b/deploy/fba/fba-ui-src/Dockerfile new file mode 100644 index 0000000..a630235 --- /dev/null +++ b/deploy/fba/fba-ui-src/Dockerfile @@ -0,0 +1,18 @@ +FROM guergeiro/pnpm:lts-latest-slim AS build + +WORKDIR /fba_ui + +COPY . . + +RUN pnpm install \ + && pnpm build + +FROM nginx + +COPY scripts/deploy/nginx.conf /etc/nginx/nginx.conf + +COPY --from=build /fba_ui/apps/web-antdv-next/dist /var/www/fba_ui + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/deploy/fba/fba-ui-src/LICENSE b/deploy/fba/fba-ui-src/LICENSE new file mode 100644 index 0000000..5b8539b --- /dev/null +++ b/deploy/fba/fba-ui-src/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 FastAPI Practices + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deploy/fba/fba-ui-src/README.md b/deploy/fba/fba-ui-src/README.md new file mode 100644 index 0000000..a417493 --- /dev/null +++ b/deploy/fba/fba-ui-src/README.md @@ -0,0 +1,23 @@ +# FastAPI Best Architecture UI + +Front-end Implementation of the [FastAPI Best Architecture](https://github.com/fastapi-practices/fastapi_best_architecture) + +## Help + +For more details, please check the [official documentation](https://fastapi-practices.github.io/fastapi_best_architecture_docs/frontend/summary/quick-start.html) + +## Contributors + + + + + +## Special thanks + +- [Vue.js](https://cn.vuejs.org/guide/introduction.html) +- [Vben Admin](https://www.vben.pro/) +- ... + +## License + +This project is licensed under the terms of the [MIT](https://github.com/fastapi-practices/fba_ui/blob/master/LICENSE) license diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/.env b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env new file mode 100644 index 0000000..f42dab9 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env @@ -0,0 +1,8 @@ +# 应用标题 +VITE_APP_TITLE=FBA UI + +# 应用命名空间,用于缓存、store等功能的前缀,确保隔离 +VITE_APP_NAMESPACE=fba-ui + +# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密 +VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.analyze b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.analyze new file mode 100644 index 0000000..d3df863 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.analyze @@ -0,0 +1,7 @@ +# public path +VITE_BASE=/ + +# Basic interface address SPA +VITE_GLOB_API_URL=http://localhost:8000 + +VITE_VISUALIZER=true diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.development b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.development new file mode 100644 index 0000000..7897ad3 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.development @@ -0,0 +1,16 @@ +# 端口号 +VITE_PORT=5173 + +VITE_BASE=/ + +# 接口地址 +VITE_GLOB_API_URL=http://localhost:8000 + +# 是否开启 Nitro Mock服务,true 为开启,false 为关闭 +VITE_NITRO_MOCK=false + +# 是否打开 devtools,true 为打开,false 为关闭 +VITE_DEVTOOLS=false + +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.production b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.production new file mode 100644 index 0000000..403946b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/.env.production @@ -0,0 +1,19 @@ +VITE_BASE=/ + +# 接口地址 +VITE_GLOB_API_URL=https://fba.wu-clan.site + +# 是否开启压缩,可以设置为 none, brotli, gzip +VITE_COMPRESS=gzip + +# 是否开启 PWA +VITE_PWA=false + +# vue-router 的模式 +VITE_ROUTER_HISTORY=hash + +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true + +# 打包后是否生成dist.zip +VITE_ARCHIVER=false diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/index.html b/deploy/fba/fba-ui-src/apps/web-antdv-next/index.html new file mode 100644 index 0000000..a9081c6 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/index.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + %VITE_APP_TITLE% + + + +
+ + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/package.json new file mode 100644 index 0000000..a00cda0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/package.json @@ -0,0 +1,53 @@ +{ + "name": "@vben/web-antdv-next", + "version": "5.7.0", + "homepage": "https://vben.pro", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "apps/web-antdv-next" + }, + "license": "MIT", + "author": { + "name": "vben", + "email": "ann.vben@gmail.com", + "url": "https://github.com/anncwb" + }, + "type": "module", + "scripts": { + "build": "pnpm vite build --mode production", + "build:analyze": "pnpm vite build --mode analyze", + "dev": "pnpm vite --mode development", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit --skipLibCheck" + }, + "imports": { + "#/*": "./src/*" + }, + "dependencies": { + "@vben/access": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/plugins": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/request": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/styles": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "antdv-next": "catalog:", + "croner": "catalog:", + "dayjs": "catalog:", + "mitt": "catalog:", + "pinia": "catalog:", + "socket.io-client": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/public/favicon.ico b/deploy/fba/fba-ui-src/apps/web-antdv-next/public/favicon.ico new file mode 100644 index 0000000..c2c5a4f Binary files /dev/null and b/deploy/fba/fba-ui-src/apps/web-antdv-next/public/favicon.ico differ diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/component/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/component/index.ts new file mode 100644 index 0000000..ebd6909 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/component/index.ts @@ -0,0 +1,788 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +/* eslint-disable vue/one-component-per-file */ + +import type { + AutoCompleteProps, + ButtonProps, + CascaderProps, + CheckboxGroupProps, + CheckboxProps, + DatePickerProps, + DividerProps, + InputNumberProps, + InputProps, + MentionsProps, + RadioGroupProps, + RadioProps, + RangePickerProps, + RateProps, + SelectProps, + SpaceProps, + SwitchProps, + TextAreaProps, + TimePickerProps, + TreeSelectProps, + UploadChangeParam, + UploadFile, + UploadProps, +} from 'antdv-next'; + +import type { Component, Ref } from 'vue'; + +import type { + ApiComponentSharedProps, + BaseFormComponentType, + CollapsibleParamsProps, + IconPickerProps, +} from '@vben/common-ui'; +import type { Sortable } from '@vben/hooks'; +import type { TipTapProps } from '@vben/plugins/tiptap'; +import type { Recordable } from '@vben/types'; + +import { + computed, + defineAsyncComponent, + defineComponent, + h, + nextTick, + onMounted, + onUnmounted, + ref, + render, + unref, + watch, +} from 'vue'; + +import { + ApiComponent, + globalShareState, + IconPicker, + VbenCollapsibleParams, + VCropper, +} from '@vben/common-ui'; +import { useSortable } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { $t } from '@vben/locales'; +import { VbenTiptap } from '@vben/plugins/tiptap'; +import { isEmpty } from '@vben/utils'; + +import { message, Modal, notification } from 'antdv-next'; + +import { upload_file } from '#/api'; +type AdapterUploadProps = UploadProps & { + aspectRatio?: string; + crop?: boolean; + draggable?: boolean; + handleChange?: (event: UploadChangeParam) => void; + maxSize?: number; + onDragSort?: (oldIndex: number, newIndex: number) => void; + onHandleChange?: (event: UploadChangeParam) => void; +}; + +const AutoComplete = defineAsyncComponent( + () => import('antdv-next/dist/auto-complete/index'), +); +const Button = defineAsyncComponent( + () => import('antdv-next/dist/button/index'), +); +const Checkbox = defineAsyncComponent( + () => import('antdv-next/dist/checkbox/index'), +); +const CheckboxGroup = defineAsyncComponent(() => + import('antdv-next/dist/checkbox/index').then((res) => res.CheckboxGroup), +); +const DatePicker = defineAsyncComponent( + () => import('antdv-next/dist/date-picker/index'), +); +const Divider = defineAsyncComponent( + () => import('antdv-next/dist/divider/index'), +); +const Input = defineAsyncComponent(() => import('antdv-next/dist/input/index')); +const InputNumber = defineAsyncComponent( + () => import('antdv-next/dist/input-number/index'), +); +const InputPassword = defineAsyncComponent(() => + import('antdv-next/dist/input/index').then((res) => res.InputPassword), +); +const Mentions = defineAsyncComponent( + () => import('antdv-next/dist/mentions/index'), +); +const Radio = defineAsyncComponent(() => import('antdv-next/dist/radio/index')); +const RadioGroup = defineAsyncComponent(() => + import('antdv-next/dist/radio/index').then((res) => res.RadioGroup), +); +const RangePicker = defineAsyncComponent(() => + import('antdv-next/dist/date-picker/index').then( + (res) => res.DateRangePicker, + ), +); +const Rate = defineAsyncComponent(() => import('antdv-next/dist/rate/index')); +const Select = defineAsyncComponent( + () => import('antdv-next/dist/select/index'), +); +const Space = defineAsyncComponent(() => import('antdv-next/dist/space/index')); +const Switch = defineAsyncComponent( + () => import('antdv-next/dist/switch/index'), +); +const Textarea = defineAsyncComponent( + () => import('antdv-next/dist/input/TextArea'), +); +const TimePicker = defineAsyncComponent( + () => import('antdv-next/dist/time-picker/index'), +); +const TreeSelect = defineAsyncComponent( + () => import('antdv-next/dist/tree-select/index'), +); +const Cascader = defineAsyncComponent( + () => import('antdv-next/dist/cascader/index'), +); +const Upload = defineAsyncComponent( + () => import('antdv-next/dist/upload/index'), +); +const Image = defineAsyncComponent(() => import('antdv-next/dist/image/index')); +const PreviewGroup = defineAsyncComponent(() => + import('antdv-next/dist/image/index').then((res) => res.ImagePreviewGroup), +); + +const withDefaultPlaceholder = ( + component: Component, + type: 'input' | 'select', + componentProps: Recordable = {}, +) => { + return defineComponent({ + name: component.name, + inheritAttrs: false, + setup: (props: any, { attrs, expose, slots }) => { + const placeholder = + props?.placeholder || + attrs?.placeholder || + $t(`ui.placeholder.${type}`); + // 透传组件暴露的方法 + const innerRef = ref(); + expose( + new Proxy( + {}, + { + get: (_target, key) => innerRef.value?.[key], + has: (_target, key) => key in (innerRef.value || {}), + }, + ), + ); + return () => + h( + component, + { ...componentProps, placeholder, ...props, ...attrs, ref: innerRef }, + slots, + ); + }, + }); +}; + +const IMAGE_EXTENSIONS = new Set([ + 'bmp', + 'gif', + 'jpeg', + 'jpg', + 'png', + 'svg', + 'webp', +]); + +/** + * 检查是否为图片文件 + */ +function isImageFile(file: UploadFile): boolean { + if (file.url) { + try { + const pathname = new URL(file.url, 'http://localhost').pathname; + const ext = pathname.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; + } catch { + const ext = file.url?.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; + } + } + if (!file.type) { + const ext = file.name?.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; + } + return file.type.startsWith('image/'); +} + +/** + * 创建默认的上传按钮插槽 + */ +function createDefaultUploadSlots(listType: string, placeholder: string) { + if (listType === 'picture-card') { + return { default: () => placeholder }; + } + return { + default: () => + h( + Button, + { + icon: h(IconifyIcon, { + icon: 'ant-design:upload-outlined', + class: 'mb-1 size-4', + }), + }, + () => placeholder, + ), + }; +} + +/** + * 获取文件的 Base64 + */ +function getBase64(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.addEventListener('load', () => resolve(reader.result as string)); + reader.addEventListener('error', reject); + }); +} + +/** + * 预览图片 + */ +async function previewImage( + file: UploadFile, + open: Ref, + fileList: Ref, +) { + // 非图片文件直接打开链接 + if (!isImageFile(file)) { + const url = file.url || file.preview; + if (url) { + window.open(url, '_blank'); + } else if (file.preview) { + window.open(file.preview, '_blank'); + } else { + message.error($t('ui.formRules.previewWarning')); + } + return; + } + + const [ImageComponent, PreviewGroupComponent] = await Promise.all([ + Image, + PreviewGroup, + ]); + + // 过滤图片文件并生成预览 + const imageFiles = (unref(fileList) || []).filter((f) => isImageFile(f)); + + for (const imgFile of imageFiles) { + if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) { + imgFile.preview = await getBase64(imgFile.originFileObj); + } + } + + const container = document.createElement('div'); + document.body.append(container); + let isUnmounted = false; + + const currentIndex = imageFiles.findIndex((f) => f.uid === file.uid); + + const PreviewWrapper = { + setup() { + return () => { + if (isUnmounted) return null; + return h( + PreviewGroupComponent, + { + class: 'hidden', + preview: { + open: open.value, + current: currentIndex, + onOpenChange: (value: boolean) => { + open.value = value; + if (!value) { + setTimeout(() => { + if (!isUnmounted && container) { + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + } + }, + }, + }, + () => + imageFiles.map((imgFile) => + h(ImageComponent, { + key: imgFile.uid, + src: imgFile.url || imgFile.preview, + }), + ), + ); + }; + }, + }; + + render(h(PreviewWrapper), container); +} + +/** + * 图片裁剪操作 + */ +function cropImage(file: File, aspectRatio: string | undefined) { + return new Promise((resolve, reject) => { + const container = document.createElement('div'); + document.body.append(container); + + let isUnmounted = false; + let objectUrl: null | string = null; + + const open = ref(true); + const cropperRef = ref | null>(null); + + function closeModal() { + open.value = false; + setTimeout(() => { + if (!isUnmounted && container) { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + } + + const CropperWrapper = { + setup() { + return () => { + if (isUnmounted) return null; + if (!objectUrl) { + objectUrl = URL.createObjectURL(file); + } + return h( + Modal, + { + open: open.value, + title: h('div', {}, [ + $t('ui.crop.title'), + h( + 'span', + { + class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`, + }, + $t('ui.crop.titleTip', [aspectRatio]), + ), + ]), + centered: true, + width: 548, + keyboard: false, + maskClosable: false, + closable: false, + cancelText: $t('common.cancel'), + okText: $t('ui.crop.confirm'), + destroyOnHidden: true, + onOk: async () => { + const cropper = cropperRef.value; + if (!cropper) { + reject(new Error('Cropper not found')); + closeModal(); + return; + } + try { + const dataUrl = await cropper.getCropImage(); + if (dataUrl) { + resolve(dataUrl); + } else { + reject(new Error($t('ui.crop.errorTip'))); + } + } catch { + reject(new Error($t('ui.crop.errorTip'))); + } finally { + closeModal(); + } + }, + onCancel() { + resolve(''); + closeModal(); + }, + }, + () => + h(VCropper, { + ref: (ref: any) => (cropperRef.value = ref), + img: objectUrl as string, + aspectRatio, + }), + ); + }; + }, + }; + + render(h(CropperWrapper), container); + }); +} + +/** + * 带预览功能的上传组件 + */ +function withPreviewUpload() { + return defineComponent({ + name: Upload.name, + emits: ['update:modelValue'], + setup( + props: any, + { attrs, slots, emit }: { attrs: any; emit: any; slots: any }, + ) { + const previewVisible = ref(false); + const placeholder = attrs?.placeholder || $t('ui.placeholder.upload'); + const listType = attrs?.listType || attrs?.['list-type'] || 'text'; + const fileList = ref( + attrs?.fileList || attrs?.['file-list'] || [], + ); + + const maxSize = computed(() => attrs?.maxSize ?? attrs?.['max-size']); + const aspectRatio = computed( + () => attrs?.aspectRatio ?? attrs?.['aspect-ratio'], + ); + + async function handleBeforeUpload( + file: UploadFile, + originFileList: Array, + ) { + // 文件大小限制 + if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) { + message.error($t('ui.formRules.sizeLimit', [maxSize.value])); + file.status = 'removed'; + return false; + } + + // 图片裁剪处理 + if ( + attrs.crop && + !attrs.multiple && + originFileList[0] && + isImageFile(file) + ) { + file.status = 'removed'; + const blob = await cropImage(originFileList[0], aspectRatio.value); + if (!blob) { + throw new Error($t('ui.crop.errorTip')); + } + return blob; + } + + return attrs.beforeUpload?.(file) ?? true; + } + + function handleChange(event: UploadChangeParam) { + try { + attrs.handleChange?.(event); + attrs.onHandleChange?.(event); + } catch (error) { + console.error(error); + } + fileList.value = event.fileList.filter( + (file) => file.status !== 'removed', + ); + emit( + 'update:modelValue', + event.fileList?.length ? fileList.value : undefined, + ); + } + + function handlePreview(file: UploadFile) { + previewVisible.value = true; + return previewImage(file, previewVisible, fileList); + } + + function renderUploadButton() { + if (attrs.disabled) return null; + return isEmpty(slots) + ? createDefaultUploadSlots(listType, placeholder) + : slots; + } + + // 拖拽排序 + const draggable = computed( + () => (attrs.draggable ?? false) && !attrs.disabled, + ); + const uploadId = `upload-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; + const sortableInstance = ref(null); + + const styleId = `upload-drag-style-${uploadId}`; + + function injectDragStyle() { + if (!document.querySelector(`[id="${styleId}"]`)) { + const style = document.createElement('style'); + style.id = styleId; + style.textContent = ` + [data-upload-id="${uploadId}"] .ant-upload-list-item { cursor: move; } + [data-upload-id="${uploadId}"] .ant-upload-list-item:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.15); } + `; + document.head.append(style); + } + } + + function removeDragStyle() { + document.querySelector(`[id="${styleId}"]`)?.remove(); + } + + async function initSortable(retryCount = 0) { + if (!draggable.value) return; + + injectDragStyle(); + await nextTick(); + await new Promise((resolve) => setTimeout(resolve, 100)); + + const container = document.querySelector( + `[data-upload-id="${uploadId}"] .ant-upload-list`, + ) as HTMLElement; + + if (!container) { + if (retryCount < 5) { + setTimeout(() => initSortable(retryCount + 1), 200); + } + return; + } + + const { initializeSortable } = useSortable(container, { + animation: 300, + delay: 400, + delayOnTouchOnly: true, + filter: + '.ant-upload-select, .ant-upload-list-item-error, .ant-upload-list-item-uploading', + onEnd: (evt) => { + const { oldIndex, newIndex } = evt; + if ( + oldIndex === undefined || + newIndex === undefined || + oldIndex === newIndex + ) { + return; + } + + const list = [...(fileList.value || [])]; + const [movedItem] = list.splice(oldIndex, 1); + if (movedItem) { + list.splice(newIndex, 0, movedItem); + fileList.value = list; + } + + attrs.onDragSort?.(oldIndex, newIndex); + emit('update:modelValue', fileList.value); + }, + }); + + sortableInstance.value = await initializeSortable(); + } + + // 监听表单值变化 + watch( + () => attrs.modelValue, + (res) => { + fileList.value = res; + }, + ); + + onMounted(initSortable); + onUnmounted(() => { + sortableInstance.value?.destroy(); + removeDragStyle(); + }); + + return () => + h( + 'div', + { 'data-upload-id': uploadId, class: 'w-full' }, + h( + Upload, + { + ...props, + ...attrs, + fileList: fileList.value, + beforeUpload: handleBeforeUpload, + onChange: handleChange, + onPreview: handlePreview, + }, + renderUploadButton() as any, + ), + ); + }, + }); +} + +// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 +export type ComponentType = + | 'ApiCascader' + | 'ApiSelect' + | 'ApiTreeSelect' + | 'AutoComplete' + | 'Cascader' + | 'Checkbox' + | 'CheckboxGroup' + | 'CollapsibleParams' + | 'DatePicker' + | 'DefaultButton' + | 'Divider' + | 'IconPicker' + | 'Input' + | 'InputNumber' + | 'InputPassword' + | 'Mentions' + | 'PrimaryButton' + | 'Radio' + | 'RadioGroup' + | 'RangePicker' + | 'Rate' + | 'RichEditor' + | 'Select' + | 'Space' + | 'Switch' + | 'Textarea' + | 'TimePicker' + | 'TreeSelect' + | 'Upload' + | BaseFormComponentType; + +/** + * 与 {@link ComponentType} 中注册的组件名一一对应,便于 Schema 上 `component` + `componentProps` 联动提示 + */ +export interface ComponentPropsMap { + ApiCascader: ApiComponentSharedProps & CascaderProps; + ApiSelect: ApiComponentSharedProps & SelectProps; + ApiTreeSelect: ApiComponentSharedProps & TreeSelectProps; + AutoComplete: AutoCompleteProps; + Cascader: CascaderProps; + Checkbox: CheckboxProps; + CheckboxGroup: CheckboxGroupProps; + CollapsibleParams: CollapsibleParamsProps; + DatePicker: DatePickerProps; + DefaultButton: ButtonProps; + Divider: DividerProps; + IconPicker: IconPickerProps; + Input: InputProps; + InputNumber: InputNumberProps; + InputPassword: InputProps; + Mentions: MentionsProps; + PrimaryButton: ButtonProps; + Radio: RadioProps; + RadioGroup: RadioGroupProps; + RangePicker: RangePickerProps; + Rate: RateProps; + RichEditor: TipTapProps; + Select: SelectProps; + Space: SpaceProps; + Switch: SwitchProps; + Textarea: TextAreaProps; + TimePicker: TimePickerProps; + TreeSelect: TreeSelectProps; + Upload: AdapterUploadProps; +} + +async function initComponentAdapter() { + const components: Partial> = { + // 如果你的组件体积比较大,可以使用异步加载 + // Button: () => + // import('xxx').then((res) => res.Button), + + ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', { + component: Cascader, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onOpenChange', + }), + ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', { + component: Select, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onOpenChange', + }), + ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', { + component: TreeSelect, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + optionsPropName: 'treeData', + visibleEvent: 'onOpenChange', + }), + AutoComplete, + Cascader, + Checkbox, + CheckboxGroup, + DatePicker, + // 自定义默认按钮 + DefaultButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'default' }, slots); + }, + Divider, + IconPicker: withDefaultPlaceholder(IconPicker, 'select', { + iconSlot: 'addonAfter', + inputComponent: Input, + modelValueProp: 'value', + }), + Input: withDefaultPlaceholder(Input, 'input'), + InputNumber: withDefaultPlaceholder(InputNumber, 'input', { + style: { width: '100%' }, + }), + InputPassword: withDefaultPlaceholder(InputPassword, 'input'), + Mentions: withDefaultPlaceholder(Mentions, 'input'), + // 自定义主要按钮 + PrimaryButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'primary' }, slots); + }, + Radio, + RadioGroup, + RangePicker, + Rate, + RichEditor: withDefaultPlaceholder(VbenTiptap, 'input', { + imageUpload: { + upload: (file: any, onProgress: any) => { + return new Promise((resolve, reject) => { + upload_file({ + file, + onProgress({ percent }) { + onProgress?.(percent); + }, + onSuccess(response) { + // 从响应中提取图片URL + resolve(response?.data?.url ?? response?.url ?? ''); + }, + onError() { + reject(new Error($t('ui.tiptap.upload.uploadFailed'))); + }, + }); + }); + }, + }, + }), + Select: withDefaultPlaceholder(Select, 'select'), + Space, + Switch, + Textarea: withDefaultPlaceholder(Textarea, 'input'), + TimePicker, + TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), + Upload: withPreviewUpload(), + CollapsibleParams: VbenCollapsibleParams, + }; + + // 将组件注册到全局共享状态中 + globalShareState.setComponents(components); + + // 定义全局共享状态中的消息提示 + globalShareState.defineMessage({ + // 复制成功消息提示 + copyPreferencesSuccess: (title, content) => { + notification.success({ + description: content, + title, + placement: 'bottomRight', + }); + }, + }); +} + +export { initComponentAdapter }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/form.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/form.ts new file mode 100644 index 0000000..3d06294 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/form.ts @@ -0,0 +1,48 @@ +import type { + VbenFormProps as FormProps, + VbenFormSchema as FormSchema, +} from '@vben/common-ui'; + +import type { ComponentPropsMap, ComponentType } from './component'; + +import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +async function initSetupVbenForm() { + setupVbenForm({ + config: { + // ant design vue组件库默认都是 v-model:value + baseModelPropName: 'value', + + // 一些组件是 v-model:checked 或者 v-model:fileList + modelPropNameMap: { + Checkbox: 'checked', + Radio: 'checked', + Switch: 'checked', + Upload: 'fileList', + }, + }, + defineRules: { + // 输入项目必填国际化适配 + required: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + return true; + }, + // 选择项目必填国际化适配 + selectRequired: (value, _params, ctx) => { + if (value === undefined || value === null) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, + }, + }); +} +const useVbenForm = useForm; + +export { initSetupVbenForm, useVbenForm, z }; + +export type VbenFormSchema = FormSchema; +export type VbenFormProps = FormProps; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/vxe-table.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/vxe-table.ts new file mode 100644 index 0000000..eee4d02 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/adapter/vxe-table.ts @@ -0,0 +1,318 @@ +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; +import type { Recordable } from '@vben/types'; + +import type { ComponentPropsMap, ComponentType } from './component'; + +import { h } from 'vue'; + +import { IconifyIcon } from '@vben/icons'; +import { $te } from '@vben/locales'; +import { + setupVbenVxeTable, + useVbenVxeGrid as useGrid, +} from '@vben/plugins/vxe-table'; +import { get, isFunction, isString } from '@vben/utils'; + +import { objectOmit } from '@vueuse/core'; +import { Button, Dropdown, Image, Popconfirm, Switch, Tag } from 'antdv-next'; + +import { $t } from '#/locales'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +import { useVbenForm } from './form'; + +setupVbenVxeTable({ + configVxeTable: (vxeUI) => { + vxeUI.setConfig({ + grid: { + align: 'center', + border: false, + columnConfig: { + resizable: true, + }, + minHeight: 180, + formConfig: { + // 全局禁用vxe-table的表单配置,使用formOptions + enabled: false, + }, + proxyConfig: { + autoLoad: true, + response: { + result: 'items', + total: 'total', + list: '', + }, + showActiveMsg: true, + showResponseMsg: false, + }, + round: true, + showOverflow: true, + size: 'small', + } as VxeTableGridOptions, + }); + + // 表格配置项可以用 cellRender: { name: 'CellImage' }, + vxeUI.renderer.add('CellImage', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + return h(Image, { src: row[column.field], ...props }); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellLink' }, + vxeUI.renderer.add('CellLink', { + renderTableDefault(renderOpts) { + const { props } = renderOpts; + return h( + Button, + { size: 'small', type: 'link' }, + { default: () => props?.text }, + ); + }, + }); + + // 单元格渲染:Tag + vxeUI.renderer.add('CellTag', { + renderTableDefault({ options, props }, { column, row }) { + const value = get(row, column.field); + // const tagOptions = options ?? [ + // { color: 'success', label: $t('common.enabled'), value: 1 }, + // { color: 'error', label: $t('common.disabled'), value: 0 }, + // ]; + const tagOptions = options ?? getDictOptions(DictEnum.SYS_STATUS); + const tagItem = tagOptions.find((item) => item.value === value); + return h( + Tag, + { + ...props, + ...objectOmit(tagItem ?? {}, ['label']), + }, + { default: () => tagItem?.label ?? value }, + ); + }, + }); + + // 单元格渲染器:Switch + vxeUI.renderer.add('CellSwitch', { + renderTableDefault({ attrs, props }, { column, row }) { + const loadingKey = `__loading_${column.field}`; + const finallyProps = { + checkedChildren: $t('common.enabled'), + checkedValue: props?.checkedValue || 1, + unCheckedChildren: $t('common.disabled'), + unCheckedValue: props?.unCheckedValue || 0, + ...props, + checked: row[column.field], + loading: row[loadingKey] ?? false, + 'onUpdate:checked': onChange, + }; + async function onChange(newVal: any) { + row[loadingKey] = true; + try { + const result = await attrs?.beforeChange?.(newVal, row); + if (result !== false) { + row[column.field] = newVal; + attrs?.onChange?.({ row }); + } + } finally { + row[loadingKey] = false; + } + } + return h(Switch, finallyProps); + }, + }); + + // 注册表格的操作按钮渲染器 + vxeUI.renderer.add('CellOperation', { + renderTableDefault({ attrs, options, props }, { column, row }) { + const defaultProps = { size: 'small', type: 'link', ...props }; + let align = 'end'; + if (column.align === 'center') { + align = 'center'; + } else if (column.align === 'left') { + align = 'start'; + } + const presets: Recordable> = { + delete: { + danger: true, + text: $t('common.delete'), + }, + edit: { + text: $t('common.edit'), + }, + }; + const operations: Array> = ( + options || ['edit', 'delete'] + ) + .map((opt) => { + if (isString(opt)) { + return presets[opt] + ? { code: opt, ...presets[opt], ...defaultProps } + : { + code: opt, + text: $te(`common.${opt}`) ? $t(`common.${opt}`) : opt, + ...defaultProps, + }; + } else { + return { ...defaultProps, ...presets[opt.code], ...opt }; + } + }) + .map((opt) => { + const optBtn: Recordable = {}; + Object.keys(opt).forEach((key) => { + optBtn[key] = isFunction(opt[key]) ? opt[key](row) : opt[key]; + }); + return optBtn; + }) + .filter((opt) => opt.show !== false); + + function renderBtn(opt: Recordable, listen = true) { + return h( + Button, + { + ...props, + ...opt, + icon: undefined, + onClick: listen + ? () => + attrs?.onClick?.({ + code: opt.code, + row, + }) + : undefined, + }, + { + default: () => { + const content = []; + if (opt.icon) { + content.push( + h(IconifyIcon, { class: 'size-5', icon: opt.icon }), + ); + } + content.push(opt.text); + return content; + }, + }, + ); + } + + function renderConfirm(opt: Recordable) { + return h( + Popconfirm, + { + getPopupContainer(el) { + if (el.closest('.fixed-right--wrapper')) { + return document.body; + } + return ( + el + .closest('.vxe-table--viewport-wrapper') + ?.querySelector('.vxe-table--main-wrapper') + ?.querySelector('tbody') || document.body + ); + }, + placement: 'topLeft', + title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']), + ...props, + ...opt, + icon: undefined, + onConfirm: () => { + attrs?.onClick?.({ + code: opt.code, + row, + }); + }, + }, + { + default: () => renderBtn({ ...opt }, false), + description: () => + h( + 'div', + { class: 'truncate' }, + $t('ui.actionMessage.deleteConfirm', [ + row[attrs?.nameField || 'name'], + ]), + ), + }, + ); + } + + function renderDropdown(opt: Recordable) { + const menuItems = + opt.items?.map((item: Recordable) => ({ + key: item.code || item.text, + label: item.text, + icon: item.icon ?? undefined, + disabled: item.disabled ?? undefined, + })) || []; + + return h( + Dropdown, + { + getPopupContainer(el) { + if (el.closest('.fixed-right--wrapper')) { + return document.body; + } + return ( + el + .closest('.vxe-table--viewport-wrapper') + ?.querySelector('.vxe-table--main-wrapper') + ?.querySelector('tbody') || document.body + ); + }, + placement: 'bottomLeft', + ...props, + ...opt, + menu: { + items: menuItems, + onClick: ({ key }: { key: string }) => + attrs?.onClick?.({ code: key, row }), + }, + }, + { + default: () => renderBtn({ ...opt, icon: 'tabler:dots' }, false), + }, + ); + } + + const btns = operations.map((opt) => { + if (opt.code === 'delete') { + return renderConfirm(opt); + } else if (opt.code === 'more') { + return renderDropdown(opt); + } else { + return renderBtn(opt); + } + }); + return h( + 'div', + { + class: 'flex table-operations', + style: { justifyContent: align }, + }, + btns, + ); + }, + }); + + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 + // vxeUI.formats.add + }, + useVbenForm, +}); + +export const useVbenVxeGrid = >( + ...rest: Parameters> +) => useGrid(...rest); + +export type OnActionClickParams> = { + code: string; + row: T; +}; + +export type OnActionClickFn> = ( + params: OnActionClickParams, +) => void; + +export type * from '@vben/plugins/vxe-table'; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/auth.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/auth.ts new file mode 100644 index 0000000..41607af --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/auth.ts @@ -0,0 +1,63 @@ +import { miniRequestClient, requestClient } from '#/api/request'; + +export interface CaptchaResult { + is_enabled: boolean; + expire_seconds: number; + uuid: string; + image: string; +} + +export interface LoginParams { + username: string; + password: string; + uuid: string; + captcha: string; +} + +export interface LoginResult { + access_token: string; + session_uuid: string; +} + +export type RefreshTokenResult = LoginResult; + +/** + * 登录验证码 + */ +export async function getCaptchaApi() { + return requestClient.get('/api/v1/auth/captcha'); +} + +/** + * 登录 + */ +export async function loginApi(data: LoginParams) { + return requestClient.post('/api/v1/auth/login', data); +} + +/** + * 刷新accessToken + */ +export async function refreshTokenApi() { + return miniRequestClient.post( + '/api/v1/auth/refresh', + undefined, + { + withCredentials: true, + }, + ); +} + +/** + * 退出登录 + */ +export async function logoutApi() { + return requestClient.post('/api/v1/auth/logout'); +} + +/** + * 获取用户权限码 + */ +export async function getAccessCodesApi() { + return requestClient.get('/api/v1/auth/codes'); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/index.ts new file mode 100644 index 0000000..0425686 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/index.ts @@ -0,0 +1,4 @@ +export * from './auth'; +export * from './menu'; +export * from './upload'; +export * from './user'; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/menu.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/menu.ts new file mode 100644 index 0000000..8aadf82 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/menu.ts @@ -0,0 +1,125 @@ +import type { RouteRecordStringComponent } from '@vben/types'; + +import { $t } from '@vben/locales'; + +import { requestClient } from '#/api/request'; + +export interface SysMenuResult { + id: number; + title: string; + name: string; + path: string; + sort: number; + icon?: string; + type: number; + component?: string; + perms?: string; + status: number; + display: number; + cache: number; + remark?: string; + parent_id?: number; + created_time: string; +} + +export interface SysMenuTreeResult extends SysMenuResult { + children?: SysMenuTreeResult[]; +} + +export interface SysMenuParams { + title: string; + name: string; + path?: string; + parent_id?: number; + sort?: number; + icon?: string; + type?: number; + component?: string; + perms?: string; + status?: number; + display?: number; + cache?: number; + link?: string; + remark?: string; +} + +export interface SysMenuTreeParams { + title?: string; + status: number; +} + +/** + * 获取用户所有菜单 + */ +export async function getAllMenusApi() { + return requestClient.get( + '/api/v1/sys/menus/sidebar', + ); +} + +export async function getSysMenuTreeApi(params: SysMenuTreeParams) { + const transformMenuTitles = (menuData: SysMenuTreeResult[]) => { + return menuData.map((item) => { + const transformedItem = { + ...item, + title: $t(item.title), + }; + + if (item.children && item.children.length > 0) { + transformedItem.children = transformMenuTitles(item.children); + } + + return transformedItem; + }); + }; + + const filterMenuTree = ( + menuData: SysMenuTreeResult[], + keyword: string, + ): SysMenuTreeResult[] => { + const lowerKeyword = keyword.toLowerCase(); + const result: SysMenuTreeResult[] = []; + for (const item of menuData) { + const filteredChildren = item.children?.length + ? filterMenuTree(item.children, keyword) + : []; + const isMatch = item.title.toLowerCase().includes(lowerKeyword); + if (isMatch || filteredChildren.length > 0) { + result.push({ + ...item, + children: isMatch ? item.children : filteredChildren, + }); + } + } + return result; + }; + + const { title, ...restParams } = params; + + const data = await requestClient.get( + '/api/v1/sys/menus', + { + params: restParams, + }, + ); + + const translatedData = transformMenuTitles(data); + + if (title) { + return filterMenuTree(translatedData, title); + } + + return translatedData; +} + +export async function createSysMenuApi(data: SysMenuParams) { + return requestClient.post('/api/v1/sys/menus', data); +} + +export async function updateSysMenuApi(pk: number, data: SysMenuParams) { + return requestClient.put(`/api/v1/sys/menus/${pk}`, data); +} + +export async function deleteSysMenuApi(pk: number) { + return requestClient.delete(`/api/v1/sys/menus/${pk}`); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/upload.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/upload.ts new file mode 100644 index 0000000..ebbf958 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/upload.ts @@ -0,0 +1,27 @@ +import { requestClient } from '#/api/request'; + +interface UploadFileParams { + file: File; + onError?: (error: Error) => void; + onProgress?: (progress: { percent: number }) => void; + onSuccess?: (data: any, file: File) => void; +} +export async function upload_file({ + file, + onError, + onProgress, + onSuccess, +}: UploadFileParams) { + try { + onProgress?.({ percent: 0 }); + + const data = await requestClient.upload('/api/v1/sys/files/upload', { + file, + }); + + onProgress?.({ percent: 100 }); + onSuccess?.(data, file); + } catch (error) { + onError?.(error instanceof Error ? error : new Error(String(error))); + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/user.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/user.ts new file mode 100644 index 0000000..d759e56 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/core/user.ts @@ -0,0 +1,142 @@ +import type { UserInfo } from '@vben/types'; + +import type { SysDeptResult, SysRoleResult } from '#/api'; + +import { requestClient } from '#/api/request'; + +export interface MyUserInfo extends UserInfo { + id: number; + nickname: string; + email?: string; + phone?: string; + dept?: string; + last_login_time: string; +} + +export interface SysUserResult { + id: number; + uuid: string; + dept_id?: number; + username: string; + nickname: string; + email?: string; + phone?: string; + avatar?: string; + status: number; + is_superuser: boolean; + is_staff: boolean; + is_multi_login: boolean; + join_time: string; + last_login_time: string; + dept?: SysDeptResult; + roles: SysRoleResult[]; +} + +export interface SysUserParams { + dept?: number; + username?: string; + phone?: string; + status?: number; + page?: number; + size?: number; +} + +export interface SysUpdateUserParams { + dept_id?: number; + username: string; + nickname: string; + avatar?: string; + email?: string; + phone?: string; + roles: number[]; +} + +export interface SysAddUserParams extends SysUpdateUserParams { + password: string; +} + +export interface SysUpdatePasswordParams { + old_password: string; + new_password: string; + confirm_password: string; +} + +export interface SysUpdateUserPhoneParams { + phone: string; + captcha: string; +} + +export interface SysUpdateUserEmailParams { + email: string; + captcha: string; +} + +export interface SysUpdateUserNicknameParams { + nickname: string; +} + +export interface SysUpdateUserAvatarParams { + avatar: string; +} + +export interface SysResetPasswordParams { + password: string; +} + +/** + * 获取用户信息 + */ +export async function getUserInfoApi() { + return requestClient.get('/api/v1/sys/users/me'); +} + +export async function getSysUserListApi(params: SysUserParams) { + return requestClient.get('/api/v1/sys/users', { params }); +} + +export async function createSysUserApi(data: SysAddUserParams) { + return requestClient.post('/api/v1/sys/users', data); +} + +export async function updateSysUserApi(pk: number, data: SysUpdateUserParams) { + return requestClient.put(`/api/v1/sys/users/${pk}`, data); +} + +export async function updateSysUserPermissionApi(pk: number, type: string) { + return requestClient.put(`/api/v1/sys/users/${pk}/permissions`, undefined, { + params: { type }, + paramsSerializer: 'repeat', + }); +} + +export async function updateSysUserAvatarApi(data: SysUpdateUserAvatarParams) { + return requestClient.put(`/api/v1/sys/users/me/avatar`, data); +} + +export async function updateSysUserNicknameApi( + data: SysUpdateUserNicknameParams, +) { + return requestClient.put(`/api/v1/sys/users/me/nickname`, data); +} +export async function updateSysUserPhoneApi(data: SysUpdateUserPhoneParams) { + return requestClient.put(`/api/v1/sys/users/me/phone`, data); +} + +export async function updateSysUserEmailApi(data: SysUpdateUserEmailParams) { + return requestClient.put(`/api/v1/sys/users/me/email`, data); +} + +export async function updateSysUserPasswordApi(data: SysUpdatePasswordParams) { + return requestClient.put(`/api/v1/sys/users/me/password`, data); +} + +export async function resetSysUserPasswordApi( + pk: number, + data: SysResetPasswordParams, +) { + return requestClient.put(`/api/v1/sys/users/${pk}/password`, data); +} + +export async function deleteSysUserApi(pk: number) { + return requestClient.delete(`/api/v1/sys/users/${pk}`); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/data-permission.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/data-permission.ts new file mode 100644 index 0000000..bf952d1 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/data-permission.ts @@ -0,0 +1,136 @@ +import { requestClient } from '#/api/request'; + +export interface SysDataScopeResult { + id: number; + name: string; + status: number; + created_time: string; + updated_time: string; +} + +export interface SysDataRuleResult { + id: number; + name: string; + model: string; + column: string; + operator: string; + expression: string; + value: string; + created_time: string; + updated_time: string; +} + +export interface SysDataScopeParams { + name?: string; + status?: number; + page?: number; + size?: number; +} + +export interface CreateSysDataScopeParams { + name: string; + status: number; +} + +export interface SysDataScopeRulesResult extends SysDataScopeResult { + rules: SysDataRuleResult[]; +} + +export interface SysDataRuleParams { + name?: string; +} + +export interface SysDataRuleModelColumnsResult { + key: string; + comment: string; +} + +export interface SysDataRuleTemplateVariableResult { + key: string; + comment: string; +} + +export interface CreateSysDataRuleParams { + name: string; + model: string; + column: string; + operator: string; + expression: string; + value: string; +} + +export async function getSysDataScopeListApi(params: SysDataScopeParams) { + return requestClient.get('/api/v1/sys/data-scopes', { + params, + }); +} + +export async function getSysDataScopesApi() { + return requestClient.get('/api/v1/sys/data-scopes/all'); +} + +export async function getSysDataScopeRulesApi(pk: number) { + return requestClient.get( + `/api/v1/sys/data-scopes/${pk}/rules`, + ); +} + +export async function createSysDataScope(data: CreateSysDataScopeParams) { + return requestClient.post('/api/v1/sys/data-scopes', data); +} + +export async function updateSysDataScope( + pk: number, + data: CreateSysDataScopeParams, +) { + return requestClient.put(`/api/v1/sys/data-scopes/${pk}`, data); +} + +export async function updateSysDataScopeRulesApi(pk: number, rules: number[]) { + return requestClient.put(`/api/v1/sys/data-scopes/${pk}/rules`, { rules }); +} + +export async function deleteSysDataScopeApi(pks: number[]) { + return requestClient.delete(`/api/v1/sys/data-scopes`, { data: { pks } }); +} + +export async function getSysDataRuleListApi(params: SysDataRuleParams) { + return requestClient.get('/api/v1/sys/data-rules', { + params, + }); +} + +export async function getSysDataRulesApi() { + return requestClient.get('/api/v1/sys/data-rules/all'); +} + +export async function getSysDataRuleModelsApi() { + return requestClient.get('/api/v1/sys/data-rules/models'); +} + +export async function getSysDataRuleModelColumnsApi(model: string) { + return requestClient.get( + `/api/v1/sys/data-rules/models/${model}/columns`, + ); +} + +export async function getSysDataRuleTemplateVariablesApi() { + return requestClient.get( + '/api/v1/sys/data-rules/value-template-variables', + ); +} + +export async function createSysDataRuleApi(data: CreateSysDataRuleParams) { + return requestClient.post('/api/v1/sys/data-rules', data); +} + +export async function updateSysDataRuleApi( + pk: number, + data: CreateSysDataRuleParams, +) { + return requestClient.put(`/api/v1/sys/data-rules/${pk}`, data); +} + +export async function deleteSysDataRuleApi(pks: number[]) { + return requestClient.delete(`/api/v1/sys/data-rules`, { data: { pks } }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/dept.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/dept.ts new file mode 100644 index 0000000..eea311b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/dept.ts @@ -0,0 +1,71 @@ +import { requestClient } from './request'; + +export interface SysDeptResult { + id: number; + name: string; + parent_id: number; + sort: number; + leader?: string; + phone?: string; + email?: string; + status: number; + created_time: string; +} + +export interface SysDeptTreeResult extends SysDeptResult { + children?: SysDeptTreeResult[]; +} + +export interface SysDeptParams { + name: string; + parent_id?: number; + sort?: number; + leader?: string; + phone?: string; + email?: string; + status: number; +} + +export interface SysDeptTreeParams { + name?: string; + leader?: string; + phone?: string; + status?: number; +} + +/** + * 获取部门树 + */ +export async function getSysDeptTreeApi(params: SysDeptTreeParams) { + return requestClient.get('/api/v1/sys/depts', { + params, + }); +} + +/** + * 获取部门详情 + */ +export async function getSysDeptDetailApi(pk: number) { + return requestClient.get(`/api/v1/sys/depts/${pk}`); +} + +/** + * 创建部门 + */ +export async function createSysDeptApi(data: SysDeptParams) { + return requestClient.post('/api/v1/sys/depts', data); +} + +/** + * 更新部门 + */ +export async function updateSysDeptApi(pk: number, data: SysDeptParams) { + return requestClient.put(`/api/v1/sys/depts/${pk}`, data); +} + +/** + * 删除部门 + */ +export async function deleteSysDeptApi(pk: number) { + return requestClient.delete(`/api/v1/sys/depts/${pk}`); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/index.ts new file mode 100644 index 0000000..d5f26f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/index.ts @@ -0,0 +1,8 @@ +export * from './core'; +export * from './data-permission'; +export * from './dept'; +export * from './log'; +export * from './monitor'; +export * from './plugin'; +export * from './role'; +export * from './scheduler'; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/log.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/log.ts new file mode 100644 index 0000000..4897777 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/log.ts @@ -0,0 +1,72 @@ +import type { PaginationResult } from '#/types'; + +import { requestClient } from './request'; + +export interface LoginLogParams { + username?: string; + status?: number; + ip?: string; + page?: number; + size?: number; +} + +export interface LoginLogResult { + id: number; + username: string; + status: number; + ip: string; + country?: string; + region?: string; + os?: string; + browser?: string; + device?: string; + msg: string; + login_time: string; +} + +export type OperaLogParams = LoginLogParams; + +export interface OperaLogResult { + id: number; + trace_id: string; + username?: string; + method: string; + title: string; + path: string; + ip: string; + country?: string; + region?: string; + city?: string; + user_agent: string; + os?: string; + browser?: string; + device?: string; + args?: JSON; + status: number; + code: string; + msg: string; + cost_time: number; + opera_time: string; +} + +export async function getLoginLogListApi(params: LoginLogParams) { + return requestClient.get>( + '/api/v1/logs/login', + { params }, + ); +} + +export async function deleteLoginLogApi(pks: number[]) { + return requestClient.delete('/api/v1/logs/login', { data: { pks } }); +} + +export async function getOperaLogListApi(params: OperaLogParams) { + return requestClient.get>( + '/api/v1/logs/opera', + { params }, + ); +} + +export async function deleteOperaLogApi(pks: number[]) { + return requestClient.delete('/api/v1/logs/opera', { data: { pks } }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/monitor.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/monitor.ts new file mode 100644 index 0000000..56f5115 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/monitor.ts @@ -0,0 +1,57 @@ +import { requestClient } from './request'; + +export interface ServerMonitorResult { + cpu: Record; + mem: Record; + sys: Record; + disk: Record[]; + service: Record; +} + +export interface RedisMonitorResult { + info: Record; + stats: Record[]; +} + +export interface OnlineMonitorResult { + id: number; + session_uuid: string; + username: string; + nickname: string; + ip: string; + os: string; + browser: string; + device: string; + status: number; + last_login_time: string; + expires_time: number; +} + +export interface MonitorOnlineParams { + username: string; +} + +export interface KickOutOnlineParams { + session_uuid: string; +} + +export async function getServerMonitorApi() { + return requestClient.get('/api/v1/monitors/server'); +} + +export async function getRedisMonitorApi() { + return requestClient.get('/api/v1/monitors/redis'); +} + +export async function getOnlineMonitorApi(params: MonitorOnlineParams) { + return requestClient.get('/api/v1/monitors/sessions', { + params, + }); +} + +export async function kickOutOnlineApi( + pk: number, + params: KickOutOnlineParams, +) { + return requestClient.delete(`/api/v1/monitors/sessions/${pk}`, { params }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/plugin.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/plugin.ts new file mode 100644 index 0000000..5198d8f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/plugin.ts @@ -0,0 +1,39 @@ +import { requestClient } from '#/api/request'; + +export interface PluginResult { + [key: string]: any; +} + +export async function getPluginListApi() { + return requestClient.get('/api/v1/sys/plugins'); +} + +export async function getPluginChangedApi() { + return requestClient.get('/api/v1/sys/plugins/changed'); +} + +export async function installZipPluginApi(file: File) { + return await requestClient.upload( + '/api/v1/sys/plugins', + { file }, + { params: { type: 'zip' }, timeout: 60_000 }, + ); +} + +export async function installGitPluginApi(repo_url: string) { + return await requestClient.post('/api/v1/sys/plugins', undefined, { + params: { type: 'git', repo_url }, + }); +} + +export async function updatePluginStatus(plugin: string) { + return await requestClient.put(`/api/v1/sys/plugins/${plugin}/status`); +} + +export async function downloadPluginApi(plugin: string) { + return await requestClient.download(`/api/v1/sys/plugins/${plugin}`); +} + +export async function uninstallPluginApi(plugin: string) { + return await requestClient.delete(`/api/v1/sys/plugins/${plugin}`); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/request.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/request.ts new file mode 100644 index 0000000..17fc3c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/request.ts @@ -0,0 +1,151 @@ +/** + * 该文件可自行根据业务逻辑进行调整 + */ +import type { RequestClientOptions } from '@vben/request'; + +import { useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { + authenticateResponseInterceptor, + defaultResponseInterceptor, + errorMessageResponseInterceptor, + RequestClient, +} from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +import { message } from 'antdv-next'; + +import { useAuthStore } from '#/store'; + +import { refreshTokenApi } from './core'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +function createRequestClient(baseURL: string, options?: RequestClientOptions) { + const client = new RequestClient({ + ...options, + baseURL, + }); + + /** + * 重新认证逻辑 + */ + async function doReAuthenticate() { + console.warn('Access token or refresh token is invalid or expired. '); + const accessStore = useAccessStore(); + const authStore = useAuthStore(); + accessStore.setAccessToken(null); + accessStore.setAccessSessionUuid(null); + if ( + preferences.app.loginExpiredMode === 'modal' && + accessStore.isAccessChecked + ) { + accessStore.setLoginExpired(true); + } else { + await authStore.logout(); + } + } + + /** + * 刷新token逻辑 + */ + async function doRefreshToken() { + const accessStore = useAccessStore(); + const resp = await refreshTokenApi(); + const newToken = resp.access_token; + accessStore.setAccessToken(newToken); + accessStore.setAccessSessionUuid(resp.session_uuid); + return newToken; + } + + function formatToken(token: null | string) { + return token ? `Bearer ${token}` : null; + } + + // 请求头处理 + client.addRequestInterceptor({ + fulfilled: async (config) => { + const accessStore = useAccessStore(); + + config.headers.Authorization = formatToken(accessStore.accessToken); + config.headers['Accept-Language'] = preferences.app.locale; + return config; + }, + }); + + // 处理返回的响应数据格式 + client.addResponseInterceptor( + defaultResponseInterceptor({ + codeField: 'code', + dataField: 'data', + successCode: 200, + }), + ); + + // token过期的处理 + client.addResponseInterceptor( + authenticateResponseInterceptor({ + client, + doReAuthenticate, + doRefreshToken, + enableRefreshToken: preferences.app.enableRefreshToken, + formatToken, + }), + ); + + // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 + client.addResponseInterceptor( + errorMessageResponseInterceptor((msg: string, error) => { + // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg + // 当前mock接口返回的错误字段是 error 或者 message + const responseData = error?.response?.data ?? {}; + const errorMessage = + responseData?.error ?? responseData?.msg ?? error?.msg ?? ''; + // 如果没有错误信息,则会根据状态码进行提示 + message.error(errorMessage || msg); + }), + ); + + return client; +} + +function createMiniRequestClient( + baseURL: string, + options?: RequestClientOptions, +) { + const client = new RequestClient({ + ...options, + baseURL, + }); + + // 处理返回的响应数据格式 + client.addResponseInterceptor( + defaultResponseInterceptor({ + codeField: 'code', + dataField: 'data', + successCode: 200, + }), + ); + + // 通用的错误处理 + client.addResponseInterceptor( + errorMessageResponseInterceptor((msg: string, error) => { + const responseData = error?.response?.data ?? {}; + const errorMessage = + responseData?.error ?? responseData?.msg ?? error?.msg ?? ''; + message.error(errorMessage || msg); + }), + ); + + return client; +} + +export const requestClient = createRequestClient(apiURL, { + responseReturn: 'data', +}); + +export const miniRequestClient = createMiniRequestClient(apiURL, { + responseReturn: 'data', +}); + +export const baseRequestClient = new RequestClient({ baseURL: apiURL }); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/role.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/role.ts new file mode 100644 index 0000000..10338d4 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/role.ts @@ -0,0 +1,67 @@ +import type { RouteRecordStringComponent } from '@vben/types'; + +import { requestClient } from './request'; + +export interface SysRoleParams { + name?: string; + status?: number; + page?: number; + size?: number; +} + +export interface SysRoleResult { + id: number; + name: string; + status: number; + is_filter_scopes: boolean; + remark?: string; + created_time: string; + updated_time: string; +} + +export interface CreateSysRoleParams { + name: string; + status: number; + remark?: string; +} + +/** + * 获取系统角色列表 + */ +export async function getSysRoleListApi(params: SysRoleParams) { + return requestClient.get('/api/v1/sys/roles', { params }); +} + +export async function getAllSysRoleApi() { + return requestClient.get('/api/v1/sys/roles/all'); +} + +export async function getSysRoleMenuApi(pk: number) { + return requestClient.get( + `/api/v1/sys/roles/${pk}/menus`, + ); +} + +export async function getSysRoleDataScopesApi(pk: number) { + return requestClient.get(`/api/v1/sys/roles/${pk}/scopes`); +} + +export async function createSysRoleApi(data: CreateSysRoleParams) { + return requestClient.post('/api/v1/sys/roles', data); +} + +export async function updateSysRoleApi(pk: number, data: CreateSysRoleParams) { + return requestClient.put(`/api/v1/sys/roles/${pk}`, data); +} + +export async function updateSysRoleMenuApi(pk: number, menus: number[]) { + return requestClient.put(`/api/v1/sys/roles/${pk}/menus`, { menus }); +} + +export async function updateSysRoleDataScopesApi(pk: number, scopes: number[]) { + return requestClient.put(`/api/v1/sys/roles/${pk}/scopes`, { scopes }); +} + +export async function deleteSysRoleApi(pks: number[]) { + return requestClient.delete(`/api/v1/sys/roles`, { data: { pks } }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/scheduler.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/scheduler.ts new file mode 100644 index 0000000..0d6a58f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/scheduler.ts @@ -0,0 +1,125 @@ +import type { Dayjs } from 'dayjs'; + +import type { PaginationResult } from '#/types'; + +import { requestClient } from '#/api/request'; + +export interface TaskResultParams { + name?: string; + task_id?: string; + page?: number; + size?: number; +} + +export interface TaskSchedulerParams { + name?: string; + type?: number; + page?: number; + size?: number; +} + +export interface TaskResult { + id: number; + task_id: string; + status: string; + result?: string; + date_done?: string; + traceback?: string; + name?: string; + args?: string; + kwargs?: string; + worker?: string; + retries?: number; + queue?: string; +} + +export interface CreateTaskSchedulerParams { + name: string; + task: string; + args?: string; + kwargs?: string; + queue?: string; + exchange?: string; + routing_key?: string; + start_time?: Dayjs; + expire_time?: Dayjs; + expire_seconds?: number; + type: number; + interval_every?: number; + interval_period?: string; + crontab: string; + one_off: boolean; + remark?: string; +} + +export interface TaskSchedulerResult extends CreateTaskSchedulerParams { + id: number; + enabled: boolean; + total_run_count: number; + last_run_time: string; + created_time: string; + updated_time?: string; +} + +export async function getTaskResultApi(pk: number) { + return requestClient.get(`/api/v1/task-results/${pk}`); +} + +export async function getTaskResultListApi(params?: TaskResultParams) { + return requestClient.get>( + '/api/v1/task-results', + { + params, + }, + ); +} + +export async function deleteTaskResultApi(pks: number[]) { + return requestClient.delete('/api/v1/task-results', { data: { pks } }); +} + +export async function getAllTaskSchedulerApi() { + return requestClient.get('/api/v1/schedulers/all'); +} + +export async function getTaskSchedulerListApi(params?: TaskSchedulerParams) { + return requestClient.get>( + '/api/v1/schedulers', + { params }, + ); +} + +export async function getTaskSchedulerApi(pk: number) { + return requestClient.get(`/api/v1/schedulers/${pk}`); +} + +export async function createTaskSchedulerApi(data: CreateTaskSchedulerParams) { + return requestClient.post('/api/v1/schedulers', data); +} + +export async function updateTaskSchedulerApi( + pk: number, + data: CreateTaskSchedulerParams, +) { + return requestClient.put(`/api/v1/schedulers/${pk}`, data); +} + +export async function updateTaskSchedulerStatusApi(pk: number) { + return requestClient.put(`/api/v1/schedulers/${pk}/status`); +} + +export async function deleteTaskSchedulerApi(pk: number) { + return requestClient.delete(`/api/v1/schedulers/${pk}`); +} + +export async function executeTaskSchedulerApi(pk: number) { + return requestClient.post(`/api/v1/schedulers/${pk}/execute`); +} + +export async function getTaskRegisteredApi() { + return requestClient.get('/api/v1/tasks/registered'); +} + +export async function revokeTaskSchedulerApi(task_id: string) { + return requestClient.delete(`/api/v1/tasks/${task_id}/cancel`); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/app.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/app.vue new file mode 100644 index 0000000..518f506 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/app.vue @@ -0,0 +1,47 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/bootstrap.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/bootstrap.ts new file mode 100644 index 0000000..6d72f26 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/bootstrap.ts @@ -0,0 +1,80 @@ +import { createApp, watchEffect } from 'vue'; + +import { registerAccessDirective } from '@vben/access'; +import { registerLoadingDirective } from '@vben/common-ui/es/loading'; +import { preferences } from '@vben/preferences'; +import { initStores } from '@vben/stores'; +import '@vben/styles'; +import '@vben/styles/antdv-next'; + +import { useTitle } from '@vueuse/core'; +import Antd from 'antdv-next'; + +import { $t, setupI18n } from '#/locales'; + +import { initComponentAdapter } from './adapter/component'; +import { initSetupVbenForm } from './adapter/form'; +import App from './app.vue'; +import { router } from './router'; + +async function bootstrap(namespace: string) { + // 初始化组件适配器 + await initComponentAdapter(); + + // 初始化表单组件 + await initSetupVbenForm(); + + // // 设置弹窗的默认配置 + // setDefaultModalProps({ + // fullscreenButton: false, + // }); + // // 设置抽屉的默认配置 + // setDefaultDrawerProps({ + // zIndex: 1020, + // }); + + const app = createApp(App); + + // 注册v-loading指令 + registerLoadingDirective(app, { + loading: 'loading', // 在这里可以自定义指令名称,也可以明确提供false表示不注册这个指令 + spinning: 'spinning', + }); + + // 国际化 i18n 配置 + await setupI18n(app); + + // 配置 pinia-tore + await initStores(app, { namespace }); + + // 安装权限指令 + registerAccessDirective(app); + + // 初始化 tippy + const { initTippy } = await import('@vben/common-ui/es/tippy'); + initTippy(app); + + // 全局加载 antdv + app.use(Antd); + + // 配置路由及路由守卫 + app.use(router); + + // 配置Motion插件 + const { MotionPlugin } = await import('@vben/plugins/motion'); + app.use(MotionPlugin); + + // 动态更新标题 + watchEffect(() => { + if (preferences.app.dynamicTitle) { + const routeTitle = router.currentRoute.value.meta?.title; + const pageTitle = + (routeTitle ? `${$t(routeTitle)} - ` : '') + preferences.app.name; + useTitle(pageTitle); + } + }); + + app.mount('#app'); +} + +export { bootstrap }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/auth.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/auth.vue new file mode 100644 index 0000000..8ba66e8 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/auth.vue @@ -0,0 +1,25 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/basic.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/basic.vue new file mode 100644 index 0000000..c938f09 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/basic.vue @@ -0,0 +1,256 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/index.ts new file mode 100644 index 0000000..a432078 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/layouts/index.ts @@ -0,0 +1,6 @@ +const BasicLayout = () => import('./basic.vue'); +const AuthPageLayout = () => import('./auth.vue'); + +const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView); + +export { AuthPageLayout, BasicLayout, IFrameView }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/README.md b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/README.md new file mode 100644 index 0000000..7b45103 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/README.md @@ -0,0 +1,3 @@ +# locale + +每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。 diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/index.ts new file mode 100644 index 0000000..54fb83c --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/index.ts @@ -0,0 +1,110 @@ +import type { Locale } from 'antdv-next/dist/locale/index'; + +import type { App } from 'vue'; + +import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales'; + +import { ref } from 'vue'; + +import { + $t, + setupI18n as coreSetup, + loadLocalesMapFromDir, +} from '@vben/locales'; +import { preferences } from '@vben/preferences'; + +import antdEnLocale from 'antdv-next/dist/locale/en_US'; +import antdDefaultLocale from 'antdv-next/dist/locale/zh_CN'; +import dayjs from 'dayjs'; + +const antdLocale = ref(antdDefaultLocale); + +const modules = import.meta.glob('./langs/**/*.json'); + +const pluginModules = import.meta.glob('../plugins/**/langs/**/*.json'); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); + +const pluginLocalesMap = loadLocalesMapFromDir( + /\/plugins\/[^/]+\/langs\/([^/]+)\/([^/]+)\.json$/, + pluginModules, +); +/** + * 加载应用特有的语言包 + * 这里也可以改造为从服务端获取翻译数据 + * @param lang + */ +async function loadMessages(lang: SupportedLanguagesType) { + const [appLocaleMessages, pluginLocalMessages] = await Promise.all([ + localesMap[lang]?.(), + pluginLocalesMap[lang]?.(), + loadThirdPartyMessage(lang), + ]); + return { ...appLocaleMessages?.default, ...pluginLocalMessages?.default }; +} + +/** + * 加载第三方组件库的语言包 + * @param lang + */ +async function loadThirdPartyMessage(lang: SupportedLanguagesType) { + await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]); +} + +/** + * 加载dayjs的语言包 + * @param lang + */ +async function loadDayjsLocale(lang: SupportedLanguagesType) { + let locale; + switch (lang) { + case 'en-US': { + locale = await import('dayjs/locale/en'); + break; + } + case 'zh-CN': { + locale = await import('dayjs/locale/zh-cn'); + break; + } + // 默认使用英语 + default: { + locale = await import('dayjs/locale/en'); + } + } + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } +} + +/** + * 加载antd的语言包 + * @param lang + */ +async function loadAntdLocale(lang: SupportedLanguagesType) { + switch (lang) { + case 'en-US': { + antdLocale.value = antdEnLocale; + break; + } + case 'zh-CN': { + antdLocale.value = antdDefaultLocale; + break; + } + } +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + await coreSetup(app, { + defaultLocale: preferences.app.locale, + loadMessages, + missingWarn: !import.meta.env.PROD, + ...options, + }); +} + +export { $t, antdLocale, setupI18n }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/common.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/common.json new file mode 100644 index 0000000..0684791 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/common.json @@ -0,0 +1,14 @@ +{ + "form": { + "query": "Query", + "select": "Please select", + "status": "Status" + }, + "table": { + "created_time": "Created Time", + "id": "ID", + "mark": "Mark", + "operation": "Operation", + "updated_time": "Updated time" + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/demos.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/demos.json new file mode 100644 index 0000000..551f22b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/demos.json @@ -0,0 +1,14 @@ +{ + "title": "Demos", + "antd": "Antdv Next", + "vben": { + "title": "Project", + "about": "About", + "document": "Document", + "antdv": "Ant Design Vue Version", + "antdv-next": "Antdv Next Version", + "naive-ui": "Naive UI Version", + "element-plus": "Element Plus Version", + "tdesign": "TDesign Vue Version" + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/page.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/page.json new file mode 100644 index 0000000..1538185 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/en-US/page.json @@ -0,0 +1,114 @@ +{ + "auth": { + "login": "Login", + "register": "Register", + "codeLogin": "Code Login", + "qrcodeLogin": "Qr Code Login", + "forgetPassword": "Forget Password", + "captchaPlaceholder": "Please enter the captcha", + "captchaRequired": "The captcha is required", + "profile": "Profile" + }, + "dashboard": { + "title": "Dashboard", + "analytics": "Analytics", + "workspace": "Workspace" + }, + "menu": { + "system": "System", + "scheduler": "Scheduler", + "schedulerManage": "Manage", + "schedulerRecord": "Record", + "log": "System Log", + "login": "Login", + "monitor": "System Monitor", + "opera": "Opera", + "online": "Online", + "redis": "Redis", + "server": "Server", + "sysDataPermission": "Data Permission", + "sysDataScope": "Data Scope", + "sysDataRule": "Data Rule", + "sysPlugin": "Plugin", + "sysDept": "Dept", + "sysMenu": "User", + "sysRole": "Role", + "sysUser": "User", + "profile": "Profile" + }, + "monitor": { + "redis": { + "cards": { + "commands": { + "title": "Command Statistics" + }, + "memory": { + "title": "Memory Status" + } + }, + "info": { + "title": "Server Info", + "redis_version": "Version", + "redis_mode": "Mode", + "role": "Role", + "tcp_port": "TCP Port", + "uptime": "Uptime", + "connected_clients": "Connected Clients", + "blocked_clients": "Blocked Clients", + "used_memory_human": "Used Memory", + "used_memory_rss_human": "RSS Memory", + "maxmemory_human": "Max Memory", + "mem_fragmentation_ratio": "Fragmentation Ratio", + "total_commands_processed": "Commands Processed", + "instantaneous_ops_per_sec": "Ops/Sec", + "rejected_connections": "Rejected Connections", + "keys_num": "Keys" + } + }, + "server": { + "cpu": { + "title": "CPU", + "current_freq": "Current Freq (MHz)", + "logical_num": "Logical Cores", + "physical_num": "Physical Cores", + "usage": "CPU Usage" + }, + "disk": { + "title": "Disk", + "device": "Device", + "dir": "Mount Point", + "free": "Free", + "total": "Total", + "type": "Type", + "usage": "Usage", + "used": "Used" + }, + "memory": { + "title": "Memory", + "free": "Free", + "total": "Total", + "usage": "Usage", + "used": "Used" + }, + "service": { + "title": "Service Info", + "name": "Service Name", + "version": "Version", + "home": "Home Path", + "cpu_usage": "CPU Usage", + "mem_vms": "Virtual Memory", + "mem_rss": "Physical Memory", + "mem_free": "Free Memory", + "startup": "Startup Time", + "elapsed": "Elapsed Time" + }, + "system": { + "title": "System Info", + "name": "Hostname", + "ip": "IP Address", + "os": "OS", + "arch": "Architecture" + } + } + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/common.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/common.json new file mode 100644 index 0000000..e050205 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/common.json @@ -0,0 +1,14 @@ +{ + "form": { + "query": "查询", + "select": "请选择", + "status": "状态" + }, + "table": { + "created_time": "创建时间", + "id": "序号", + "mark": "备注", + "operation": "操作", + "updated_time": "更新时间" + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/demos.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/demos.json new file mode 100644 index 0000000..ef4e43f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/demos.json @@ -0,0 +1,14 @@ +{ + "title": "演示", + "antd": "Antdv Next", + "vben": { + "title": "项目", + "about": "关于", + "document": "文档", + "antdv": "Ant Design Vue 版本", + "antdv-next": "Antdv Next 版本", + "naive-ui": "Naive UI 版本", + "element-plus": "Element Plus 版本", + "tdesign": "TDesign Vue 版本" + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/page.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/page.json new file mode 100644 index 0000000..3e32e5d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/locales/langs/zh-CN/page.json @@ -0,0 +1,114 @@ +{ + "auth": { + "login": "登录", + "register": "注册", + "codeLogin": "验证码登录", + "qrcodeLogin": "二维码登录", + "forgetPassword": "忘记密码", + "captchaPlaceholder": "请输入验证码", + "captchaRequired": "验证码是必须的", + "profile": "个人中心" + }, + "dashboard": { + "title": "概览", + "analytics": "分析页", + "workspace": "工作台" + }, + "menu": { + "system": "系统管理", + "scheduler": "任务调度", + "schedulerManage": "任务管理", + "schedulerRecord": "执行记录", + "log": "日志管理", + "login": "登录日志", + "monitor": "系统监控", + "opera": "操作日志", + "online": "在线用户", + "redis": "Redis", + "server": "服务器", + "sysDataPermission": "数据权限", + "sysDataScope": "数据范围", + "sysDataRule": "数据规则", + "sysPlugin": "插件管理", + "sysDept": "部门管理", + "sysMenu": "菜单管理", + "sysRole": "角色管理", + "sysUser": "用户管理", + "profile": "个人中心" + }, + "monitor": { + "redis": { + "cards": { + "commands": { + "title": "命令统计" + }, + "memory": { + "title": "内存使用" + } + }, + "info": { + "title": "服务器信息", + "redis_version": "版本", + "redis_mode": "运行模式", + "role": "节点角色", + "tcp_port": "监听端口", + "uptime": "运行时长", + "connected_clients": "已连接客户端数", + "blocked_clients": "阻塞客户端数", + "used_memory_human": "已使用内存", + "used_memory_rss_human": "RSS 内存", + "maxmemory_human": "最大内存限制", + "mem_fragmentation_ratio": "内存碎片率", + "total_commands_processed": "命令处理总数", + "instantaneous_ops_per_sec": "每秒操作数", + "rejected_connections": "拒绝连接数", + "keys_num": "键总数" + } + }, + "server": { + "cpu": { + "title": "CPU 信息", + "current_freq": "当前频率 (MHz)", + "logical_num": "逻辑核心数", + "physical_num": "物理核心数", + "usage": "CPU 使用率" + }, + "disk": { + "title": "磁盘信息", + "device": "设备名称", + "dir": "挂载点", + "free": "可用", + "total": "总容量", + "type": "文件系统类型", + "usage": "使用率", + "used": "已使用" + }, + "memory": { + "free": "可用", + "title": "内存信息", + "total": "总量", + "usage": "使用率", + "used": "已使用" + }, + "service": { + "title": "服务信息", + "name": "服务名称", + "version": "版本", + "home": "安装路径", + "cpu_usage": "CPU 使用率", + "mem_vms": "虚拟内存", + "mem_rss": "物理内存", + "mem_free": "可用内存", + "startup": "启动时间", + "elapsed": "运行时长" + }, + "system": { + "title": "系统信息", + "name": "主机名", + "ip": "IP 地址", + "os": "操作系统", + "arch": "系统架构" + } + } + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/main.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/main.ts new file mode 100644 index 0000000..5d728a0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/main.ts @@ -0,0 +1,31 @@ +import { initPreferences } from '@vben/preferences'; +import { unmountGlobalLoading } from '@vben/utils'; + +import { overridesPreferences } from './preferences'; + +/** + * 应用初始化完成之后再进行页面加载渲染 + */ +async function initApplication() { + // name用于指定项目唯一标识 + // 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据 + const env = import.meta.env.PROD ? 'prod' : 'dev'; + const appVersion = import.meta.env.VITE_APP_VERSION; + const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`; + + // app偏好设置初始化 + await initPreferences({ + namespace, + overrides: overridesPreferences, + }); + + // 启动应用并挂载 + // vue应用主要逻辑及视图 + const { bootstrap } = await import('./bootstrap'); + await bootstrap(namespace); + + // 移除并销毁loading + unmountGlobalLoading(); +} + +initApplication(); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/api/index.ts new file mode 100644 index 0000000..c43840e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/api/index.ts @@ -0,0 +1,12 @@ +import { requestClient } from '#/api/request'; + +interface phoneCaptchaParams { + phone: string; +} + +/** + * 发送短信验证码 + */ +export async function getPhoneCaptchaApi(data: phoneCaptchaParams) { + return requestClient.post('/api/v1/phones/captcha', data); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/package.json new file mode 100644 index 0000000..9aaac54 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/aliyun_sms/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/aliyun-sms", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/api/index.ts new file mode 100644 index 0000000..8b76bd4 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/api/index.ts @@ -0,0 +1,158 @@ +import type { Recordable } from '@vben/types'; + +import type { PaginationResult } from '#/types'; + +import { requestClient } from '#/api/request'; + +export interface QueryCodeGenBusinessParams { + table_name?: string; + page?: number; + size?: number; +} + +export interface CodeGenBusinessParams { + app_name: string; + table_name: string; + doc_comment: string; + table_comment?: string; + class_name?: string; + schema_name?: string; + filename?: string; + datetime_mixin?: boolean; + api_version?: string; + tag?: string; + gen_path?: string; + remark?: string; +} + +export interface CodeGenBusinessResult extends CodeGenBusinessParams { + id: number; + created_time: string; + updated_time: string; +} + +export interface CodeGenColumnParams { + name: string; + comment?: string; + type: string; + default?: string; + sort: number; + length: number; + is_pk: boolean; + is_nullable: boolean; + gen_business_id: number; +} + +export interface CodeGenColumnResult extends CodeGenColumnParams { + id: number; + pd_type: string; +} + +export interface CodeGenBusinessImportParams { + app: string; + table_schema: string; + table_name: string; +} + +export async function getCodeGenBusinessDetailApi(pk: number) { + return requestClient.get( + `/api/v1/code-generation/businesses/${pk}`, + ); +} + +export async function getAllCodeGenBusinessApi() { + return requestClient.get( + '/api/v1/code-generation/businesses/all', + ); +} + +export async function createCodeGenBusinessApi(data: CodeGenBusinessParams) { + return requestClient.post(`/api/v1/code-generation/businesses`, data); +} + +export async function updateCodeGenBusinessApi( + pk: number, + data: CodeGenBusinessParams, +) { + return requestClient.put(`/api/v1/code-generation/businesses/${pk}`, data); +} + +export async function deleteCodeGenBusinessApi(pk: number) { + return requestClient.delete(`/api/v1/code-generation/businesses/${pk}`); +} + +export async function getCodeGenBusinessListApi( + params: QueryCodeGenBusinessParams, +) { + return requestClient.get>( + `/api/v1/code-generation/businesses`, + { params }, + ); +} + +export async function getAllCodeGenBusinessColumnApi(pk: number) { + return requestClient.get>( + `/api/v1/code-generation/businesses/${pk}/columns`, + ); +} + +export async function getAllCodeGenColumnTypeApi() { + return requestClient.get(`/api/v1/code-generation/columns/types`); +} + +export async function getCodeGenColumnDetailApi(pk: number) { + return requestClient.get(`/api/v1/code-generation/columns/${pk}`); +} + +export async function createCodeGenColumnApi(data: CodeGenColumnParams) { + return requestClient.post(`/api/v1/code-generation/columns`, data); +} + +export async function updateCodeGenColumnApi( + pk: number, + data: CodeGenColumnParams, +) { + return requestClient.put(`/api/v1/code-generation/columns/${pk}`, data); +} + +export async function deleteCodeGenColumnApi(pk: number) { + return requestClient.delete(`/api/v1/code-generation/columns/${pk}`); +} + +export async function getCodeGenDbTableApi(params: Recordable) { + return requestClient.get( + `/api/v1/code-generation/generations/tables`, + { + params, + }, + ); +} + +export async function importCodeGenDbTableApi( + data: CodeGenBusinessImportParams, +) { + return requestClient.post( + `/api/v1/code-generation/generations/imports`, + data, + ); +} + +export async function previewCodeGenApi(pk: number) { + return requestClient.get(`/api/v1/code-generation/generations/${pk}/preview`); +} + +export async function getCodeGenPathApi(pk: number) { + return requestClient.get( + `/api/v1/code-generation/generations/${pk}/paths`, + ); +} + +export async function generateCodeApi(pk: number) { + return requestClient.post(`/api/v1/code-generation/generations/${pk}`); +} + +export async function downloadCodeApi(pk: number) { + return requestClient.download( + `/api/v1/code-generation/generations/${pk}`, + ); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/en-US/code_generator.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/en-US/code_generator.json new file mode 100644 index 0000000..15f6abe --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/en-US/code_generator.json @@ -0,0 +1,3 @@ +{ + "menu": "Code Generator" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/zh-CN/code_generator.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/zh-CN/code_generator.json new file mode 100644 index 0000000..c524130 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/langs/zh-CN/code_generator.json @@ -0,0 +1,3 @@ +{ + "menu": "代码生成" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/package.json new file mode 100644 index 0000000..e808c38 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/code-generator", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/routes/index.ts new file mode 100644 index 0000000..f1216fe --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/routes/index.ts @@ -0,0 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'PluginCodeGenerator', + path: '/plugins/code-generator', + component: () => import('../views/index.vue'), + meta: { + title: $t('code_generator.menu'), + icon: 'tabler:code', + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/column.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/column.vue new file mode 100644 index 0000000..5dc93f2 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/column.vue @@ -0,0 +1,266 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/data.ts new file mode 100644 index 0000000..23828db --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/data.ts @@ -0,0 +1,415 @@ +import type { CodeGenBusinessResult, CodeGenColumnResult } from '../api'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; + +import { ref } from 'vue'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +import { + getAllCodeGenBusinessApi, + getAllCodeGenColumnTypeApi, + getCodeGenDbTableApi, +} from '../api'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'table_name', + label: '表名称', + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'app_name', title: '应用名称' }, + { field: 'table_name', title: '表名称' }, + { field: 'table_comment', title: '表描述' }, + { + field: 'doc_comment', + title: '文档描述', + titleSuffix: { content: '用于 python 代码类、函数、参数文档' }, + }, + { + field: 'class_name', + title: '实体类名', + titleSuffix: { + content: '用于 python 代码基础类名', + }, + }, + { + field: 'schema_name', + title: 'Schema 类名', + titleSuffix: { + content: '用于 python Schema 代码基础类名', + }, + }, + { + field: 'filename', + title: '文件名', + titleSuffix: { + content: '用于 python 代码基础文件名', + }, + }, + { field: 'api_version', title: '版本' }, + { + field: 'tag', + title: '标签', + titleSuffix: { + content: '用于 API 文档分组标签', + }, + }, + { + field: 'gen_path', + title: '生成路径', + titleSuffix: { + content: '默认生成到 app 根路径,也可以自定义生成路径', + }, + align: 'left', + }, + { field: 'remark', title: $t('common.table.mark'), align: 'left' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 150, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'details', + text: '详情', + }, + 'edit', + 'delete', + ], + }, + }, + ]; +} + +const tableSchemaValue = ref(); +export const importSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'app', + label: '应用名称', + help: '将代码生成到指定应用下', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'table_schema', + label: '数据库名', + help: '当前服务所连接的数据库名', + rules: 'required', + }, + { + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getCodeGenDbTableApi, + params: { table_schema: tableSchemaValue }, + afterFetch: (data: { table_comment: string; table_name: string }[]) => { + return data.map((item: any) => ({ + label: item.table_comment || item.table_name, + value: item.table_name, + })); + }, + class: 'w-full', + }, + dependencies: { + disabled: (values) => { + return !values.table_schema; + }, + trigger(values) { + tableSchemaValue.value = values.table_schema; + }, + triggerFields: ['table_schema'], + }, + fieldName: 'table_name', + label: '数据库表名', + rules: 'required', + }, +]; + +export const editSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'app_name', + label: '应用名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'table_name', + label: '表名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'table_comment', + label: '表描述', + }, + { + component: 'Input', + fieldName: 'doc_comment', + label: '文档描述', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'class_name', + label: '实体类名', + }, + { + component: 'Input', + fieldName: 'schema_name', + label: 'Schema 类名', + }, + { + component: 'Input', + fieldName: 'filename', + label: '文件名', + }, + { + component: 'Input', + fieldName: 'api_version', + label: 'API 版本', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'tag', + label: '标签', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: true }, + // { label: $t('common.disabled'), value: false }, + // ], + + options: getDictOptions(DictEnum.SYS_CHOOSE), + optionType: 'button', + }, + fieldName: 'datetime_mixin', + label: '默认时间列', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'gen_path', + label: '生成路径', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: $t('common.table.mark'), + }, +]; + +export function useColumnColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { + field: 'name', + title: '名称', + }, + { + field: 'is_pk', + title: '是否主键', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: $t('common.enabled'), value: true }, + // { color: 'error', label: $t('common.disabled'), value: false }, + // ], + + options: getDictOptions(DictEnum.SYS_CHOOSE), + }, + }, + { + field: 'comment', + title: '描述', + }, + { + field: 'type', + title: 'SQLA 类型', + }, + { + field: 'pd_type', + title: 'Pydantic 类型', + }, + { + field: 'default', + title: '默认值', + }, + { + field: 'sort', + title: '排序', + }, + { + field: 'length', + title: '长度', + }, + { + field: 'is_nullable', + title: '选填', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: $t('common.enabled'), value: true }, + // { color: 'error', label: $t('common.disabled'), value: false }, + // ], + + options: getDictOptions(DictEnum.SYS_CHOOSE), + }, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 120, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: ['edit', 'delete'], + }, + }, + ]; +} + +export const columnSchema: VbenFormSchema[] = [ + { + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getAllCodeGenBusinessApi, + afterFetch: (data: CodeGenBusinessResult[]) => { + return data.map((item: CodeGenBusinessResult) => ({ + label: item.app_name, + value: item.id, + })); + }, + disabled: true, + class: 'w-full', + }, + fieldName: 'gen_business_id', + label: '所属业务', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'name', + label: '列名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'comment', + label: '列描述', + }, + { + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getAllCodeGenColumnTypeApi, + afterFetch: (data: string[]) => { + return data.map((item: string) => ({ + label: item, + value: item, + })); + }, + class: 'w-full', + }, + fieldName: 'type', + label: 'SQLA 列类型', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'default', + label: '列默认值', + }, + { + component: 'InputNumber', + componentProps: { + class: 'w-full', + min: 0, + }, + fieldName: 'sort', + label: '列排序', + rules: 'required', + }, + { + component: 'InputNumber', + componentProps: { + class: 'w-full', + min: 0, + }, + fieldName: 'length', + label: '列长度', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: true }, + // { label: $t('common.disabled'), value: false }, + // ], + + options: getDictOptions(DictEnum.SYS_CHOOSE), + optionType: 'button', + }, + defaultValue: false, + fieldName: 'is_pk', + label: '是否主键', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: true }, + // { label: $t('common.disabled'), value: false }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE), + optionType: 'button', + }, + defaultValue: false, + fieldName: 'is_nullable', + label: '是否选填', + rules: 'required', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/index.vue new file mode 100644 index 0000000..c8675a4 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/index.vue @@ -0,0 +1,224 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/preview.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/preview.vue new file mode 100644 index 0000000..650431a --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/code_generator/views/preview.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/api/index.ts new file mode 100644 index 0000000..f715b63 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/api/index.ts @@ -0,0 +1,28 @@ +import type { Recordable } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +export interface ConfigParams { + id: string; + name: string; + type?: string; + key: string; + value: string; + is_frontend: boolean; + remark?: string; +} + +export interface ConfigResult extends ConfigParams { + created_time: string; + updated_time?: string; +} + +export async function getAllConfigApi(params: Recordable) { + return requestClient.get('/api/v1/sys/configs/all', { + params, + }); +} + +export async function updateConfigApi(params: ConfigParams[]) { + return requestClient.put('/api/v1/sys/configs', params); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/en-US/config.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/en-US/config.json new file mode 100644 index 0000000..23f568b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/en-US/config.json @@ -0,0 +1,3 @@ +{ + "menu": "Parameter config" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/zh-CN/config.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/zh-CN/config.json new file mode 100644 index 0000000..68e4614 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/langs/zh-CN/config.json @@ -0,0 +1,3 @@ +{ + "menu": "参数配置" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/package.json new file mode 100644 index 0000000..b5e99c0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/config", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/routes/index.ts new file mode 100644 index 0000000..c6bd77f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/routes/index.ts @@ -0,0 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'PluginConfig', + path: '/plugins/config', + component: () => import('../views/index.vue'), + meta: { + title: $t('config.menu'), + icon: 'codicon:symbol-parameter', + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/data.ts new file mode 100644 index 0000000..bc0e28b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/data.ts @@ -0,0 +1,212 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { z } from '#/adapter/form'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const userSecuritySchema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { label: $t('common.enabled'), value: '1' }, + // { label: $t('common.disabled'), value: '0' }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS, { asString: true }), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'USER_SECURITY_CONFIG_STATUS', + label: '状态', + help: '默认使用本地配置,当启用时,将使用此配置', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_LOCK_THRESHOLD', + label: '密码错误锁定阈值', + description: '用户连续登录失败达到此次数后将被锁定,0 表示禁用锁定', + renderComponentContent: () => ({ + suffix: () => '次', + }), + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_LOCK_SECONDS', + label: '密码错误锁定时长(秒)', + description: '用户被锁定后自动解锁的时间', + renderComponentContent: () => ({ + suffix: () => '秒', + }), + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_PASSWORD_EXPIRY_DAYS', + label: '密码有效期(天)', + description: '密码强制修改周期,0 表示永不过期', + renderComponentContent: () => ({ + suffix: () => '天', + }), + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_PASSWORD_REMINDER_DAYS', + label: '密码到期提醒(天)', + description: '密码到期前多少天提醒用户修改密码,0 表示不提醒', + renderComponentContent: () => ({ + suffix: () => '天', + }), + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_PASSWORD_HISTORY_CHECK_COUNT', + label: '密码历史检查次数', + description: '新密码不能与最近 N 次使用的密码相同', + renderComponentContent: () => ({ + suffix: () => '次', + }), + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_PASSWORD_MIN_LENGTH', + label: '密码最小长度', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'USER_PASSWORD_MAX_LENGTH', + label: '密码最大长度', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { color: 'success', label: $t('common.enabled'), value: 'true' }, + // { color: 'error', label: $t('common.disabled'), value: 'false' }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE, { asString: true }), + optionType: 'button', + }, + defaultValue: 'false', + fieldName: 'USER_PASSWORD_REQUIRE_SPECIAL_CHAR', + label: '密码必须包含特殊字符', + labelClass: 'float-left', + rules: 'required', + }, +]; + +export const loginSchema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { label: $t('common.enabled'), value: '1' }, + // { label: $t('common.disabled'), value: '0' }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS, { asString: true }), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'LOGIN_CONFIG_STATUS', + label: '状态', + help: '默认使用本地配置,当启用时,将使用此配置', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { color: 'success', label: $t('common.enabled'), value: 'true' }, + // { color: 'error', label: $t('common.disabled'), value: 'false' }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE, { asString: true }), + optionType: 'button', + }, + defaultValue: 'true', + fieldName: 'LOGIN_CAPTCHA_ENABLED', + label: '验证码开关', + description: '是否启用登录验证码', + labelClass: 'float-left', + rules: 'required', + }, +]; + +export const emailSchema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { label: $t('common.enabled'), value: '1' }, + // { label: $t('common.disabled'), value: '0' }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS, { asString: true }), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'EMAIL_STATUS', + label: '状态', + help: '默认使用本地配置,当启用时,将使用此配置', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + options: [ + { + label: 'SMTP', + value: '0', + }, + ], + }, + defaultValue: '0', + fieldName: 'EMAIL_PROTOCOL', + label: '邮件协议', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + // options: [ + // { color: 'success', label: $t('common.enabled'), value: 'true' }, + // { color: 'error', label: $t('common.disabled'), value: 'false' }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE, { asString: true }), + optionType: 'button', + }, + defaultValue: '1', + fieldName: 'EMAIL_SSL', + label: 'SSL 加密', + labelClass: 'float-left', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'EMAIL_HOST', + label: '服务器地址', + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'EMAIL_PORT', + label: '服务器端口', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'EMAIL_USERNAME', + label: '邮箱账号', + rules: z.string().email({ message: '无效的邮箱地址' }), + }, + { + component: 'InputPassword', + fieldName: 'EMAIL_PASSWORD', + label: '邮箱密码', + help: '账号授权密码', + rules: 'required', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/email.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/email.vue new file mode 100644 index 0000000..4d949c8 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/email.vue @@ -0,0 +1,134 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/index.vue new file mode 100644 index 0000000..eb3d284 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/login.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/login.vue new file mode 100644 index 0000000..fdfbd96 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/login.vue @@ -0,0 +1,135 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/user-security.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/user-security.vue new file mode 100644 index 0000000..8811863 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/config/views/user-security.vue @@ -0,0 +1,134 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/api/index.ts new file mode 100644 index 0000000..d383312 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/api/index.ts @@ -0,0 +1,114 @@ +import type { PaginationResult } from '#/types'; + +import { requestClient } from '#/api/request'; + +export interface DictTypeParams { + name?: string; + code?: string; + status?: number; + page?: number; + size?: number; +} + +export interface DictTypeResult { + id: number; + name: string; + code: string; + status: number; + remark?: string; + created_time: string; + updated_time?: string; +} + +export interface CreateDictTypeParams { + name: string; + code: string; + status: number; + remark: string; +} + +export interface DictDataParams { + type_id?: number; + label?: string; + status?: number; +} + +export interface DictDataResult { + id: number; + type_id: number; + label: string; + value: string; + color?: string; + sort: number; + status: number; + remark: string; +} + +export interface CreateDictDataParams { + type_id: number; + label: string; + value: string; + sort: number; + status: number; + remark?: string; +} + +export async function getAllDictTypeApi() { + return await requestClient.get( + '/api/v1/sys/dict-types/all', + ); +} + +export async function getDictTypeListApi(params: DictTypeParams) { + return await requestClient.get>( + '/api/v1/sys/dict-types', + { params }, + ); +} + +export async function createDictTypeApi(data: CreateDictTypeParams) { + return await requestClient.post('/api/v1/sys/dict-types', data); +} + +export async function updateDictTypeApi( + pk: number, + data: CreateDictTypeParams, +) { + return await requestClient.put(`/api/v1/sys/dict-types/${pk}`, data); +} + +export async function deleteDictTypeApi(pks: number[]) { + return await requestClient.delete('/api/v1/sys/dict-types', { + data: { pks }, + }); +} + +export async function getDictDataDetailApi(code: string) { + return await requestClient.get( + `/api/v1/sys/dict-datas/type-codes/${code}`, + ); +} + +export async function getDictDataListApi(params: DictDataParams) { + return await requestClient.get>( + '/api/v1/sys/dict-datas', + { params }, + ); +} + +export async function createDictDataApi(data: CreateDictDataParams) { + return await requestClient.post('/api/v1/sys/dict-datas', data); +} + +export async function updateDictDataApi( + pk: number, + data: CreateDictDataParams, +) { + return await requestClient.put(`/api/v1/sys/dict-datas/${pk}`, data); +} + +export async function deleteDictDataApi(pks: number[]) { + return await requestClient.delete('/api/v1/sys/dict-datas', { + data: { pks }, + }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/en-US/dict.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/en-US/dict.json new file mode 100644 index 0000000..e2c6507 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/en-US/dict.json @@ -0,0 +1,3 @@ +{ + "menu": "Dict" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/zh-CN/dict.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/zh-CN/dict.json new file mode 100644 index 0000000..779bee0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/langs/zh-CN/dict.json @@ -0,0 +1,3 @@ +{ + "menu": "字典管理" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/package.json new file mode 100644 index 0000000..d49f804 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/dict", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/routes/index.ts new file mode 100644 index 0000000..cf1b925 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/routes/index.ts @@ -0,0 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'PluginDict', + path: '/plugins/dict', + component: () => import('../views/index.vue'), + meta: { + title: $t('dict.menu'), + icon: 'fluent-mdl2:dictionary', + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/data.ts new file mode 100644 index 0000000..5ba7ea0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/data.ts @@ -0,0 +1,243 @@ +import type { DictDataResult, DictTypeResult } from '../api'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { $t } from '@vben/locales'; + +import { Tag } from 'antdv-next'; + +import { z } from '#/adapter/form'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +import { getAllDictTypeApi } from '../api'; + +export const queryDictTypeSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '类型名称', + }, +]; + +export function useDictTypeColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'name', title: '名称' }, + { field: 'remark', title: $t('common.table.mark'), align: 'left' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 130, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: ['edit', 'delete'], + }, + }, + ]; +} + +export const dictTypeSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '类型名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'code', + label: '类型编码', + rules: z.string().regex(/^[A-Z_]+$/i, { + message: '只能包含英文字母和下划线', + }), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: '备注', + }, +]; + +export const queryDictDataSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'label', + label: '数据标签', + }, +]; + +export function useDictDataColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'label', title: '标签' }, + { field: 'value', title: '值' }, + { + field: 'color', + title: '标签样式', + slots: { + default: ({ row }: { row: any }) => { + return h(Tag, { color: row.color }, { default: () => row.label }); + }, + }, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + }, + { field: 'sort', title: '排序' }, + { field: 'remark', title: $t('common.table.mark'), align: 'left' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 130, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: ['edit', 'delete'], + }, + }, + ]; +} + +const COLOR_OPTIONS = [ + { value: 'processing', label: '主要(primary)' }, + { value: 'success', label: '成功(success)' }, + { value: 'error', label: '危险(danger)' }, + { value: 'warning', label: '警告(warning)' }, + { value: 'magenta', label: 'magenta' }, + { value: 'red', label: 'red' }, + { value: 'volcano', label: 'volcano' }, + { value: 'orange', label: 'orange' }, + { value: 'gold', label: 'gold' }, + { value: 'lime', label: 'lime' }, + { value: 'green', label: 'green' }, + { value: 'cyan', label: 'cyan' }, + { value: 'blue', label: 'blue' }, + { value: 'geekblue', label: 'geekblue' }, + { value: 'purple', label: 'purple' }, + { value: 'default', label: '默认(default)' }, + { value: 'pink', label: 'pink' }, +]; + +export const dictDataSchema: VbenFormSchema[] = [ + { + component: 'ApiSelect', + componentProps: { + api: getAllDictTypeApi, + class: 'w-full', + labelField: 'name', + valueField: 'id', + disabled: true, + }, + fieldName: 'type_id', + label: '字典类型', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'label', + label: '数据标签', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'value', + label: '数据值', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + options: COLOR_OPTIONS, + optionRender: ({ + option, + }: { + option: { data?: { label?: string; value?: string } }; + }) => { + const optionData = option?.data; + const color = optionData?.value || 'default'; + const label = optionData?.label || color; + return h(Tag, { color }, { default: () => label }); + }, + }, + fieldName: 'color', + label: '标签样式', + }, + { + component: 'InputNumber', + componentProps: { + class: 'w-full', + min: 0, + }, + fieldName: 'sort', + label: '排序', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: '备注', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-data.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-data.vue new file mode 100644 index 0000000..13b6bc5 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-data.vue @@ -0,0 +1,188 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-type.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-type.vue new file mode 100644 index 0000000..313f8d5 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/dict-type.vue @@ -0,0 +1,187 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/index.vue new file mode 100644 index 0000000..fd9f6b0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/mitt.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/mitt.ts new file mode 100644 index 0000000..020d010 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/dict/views/mitt.ts @@ -0,0 +1,7 @@ +import mitt from 'mitt'; + +type Events = { + rowClick: number; +}; + +export const emitter = mitt(); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/api/index.ts new file mode 100644 index 0000000..14b2244 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/api/index.ts @@ -0,0 +1,12 @@ +import { requestClient } from '#/api/request'; + +export interface emailCaptchaParams { + recipients: string; +} + +/** + * 获取邮箱验证码 + */ +export async function getEmailCaptchaApi(data: emailCaptchaParams) { + return requestClient.post('/api/v1/emails/captcha', data); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/package.json new file mode 100644 index 0000000..53edd71 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/email/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/email", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/api/index.ts new file mode 100644 index 0000000..4779f86 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/api/index.ts @@ -0,0 +1,56 @@ +import type { PaginationResult } from '#/types'; + +import { requestClient } from '#/api/request'; + +export interface SysNoticeParams { + title?: string; + type?: number; + status?: number; + page?: number; + size?: number; +} + +export interface SysNoticeResult { + id: number; + title: string; + type: number; + status: number; + content: string; + created_time: string; + updated_time?: string; +} + +export interface CreateSysNoticeParams { + title: string; + type: number; + status: number; + content: string; +} + +export type UpdateSysNoticeParams = CreateSysNoticeParams; + +export async function getNoticeListApi(params: SysNoticeParams) { + return await requestClient.get>( + '/api/v1/sys/notices', + { params }, + ); +} + +export async function getSysNoticeApi(pk: number) { + return requestClient.get(`/api/v1/sys/notices/${pk}`); +} + +export async function createSysNoticeApi(data: CreateSysNoticeParams) { + return requestClient.post(`/api/v1/sys/notices`, data); +} + +export async function updateSysNoticeApi( + pk: number, + data: UpdateSysNoticeParams, +) { + return requestClient.put(`/api/v1/sys/notices/${pk}`, data); +} + +export async function deleteSysNoticeApi(pks: number[]) { + return requestClient.delete('/api/v1/sys/notices', { data: { pks } }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/en-US/notice.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/en-US/notice.json new file mode 100644 index 0000000..219c050 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/en-US/notice.json @@ -0,0 +1,3 @@ +{ + "menu": "Notice" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/zh-CN/notice.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/zh-CN/notice.json new file mode 100644 index 0000000..3383a10 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/langs/zh-CN/notice.json @@ -0,0 +1,3 @@ +{ + "menu": "通知公告" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/package.json new file mode 100644 index 0000000..335b81c --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/notice", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/routes/index.ts new file mode 100644 index 0000000..1db6b0d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/routes/index.ts @@ -0,0 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'PluginNotice', + path: '/plugins/notice', + component: () => import('../views/index.vue'), + meta: { + title: $t('notice.menu'), + icon: 'fe:notice-push', + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/data.ts new file mode 100644 index 0000000..4b3aa7d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/data.ts @@ -0,0 +1,172 @@ +import type { SysNoticeResult } from '../api'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { MarkdownEditor } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'title', + label: '标题', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '通知', + // value: 0, + // }, + // { + // label: '公告', + // value: 1, + // }, + // ], + options: getDictOptions(DictEnum.NOTICE), + }, + fieldName: 'type', + label: '类型', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '已启用', + // value: 1, + // }, + // { + // label: '已停用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'title', title: '标题' }, + { + field: 'type', + title: '类型', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: '通知', value: 0 }, + // { color: 'warning', label: '公告', value: 1 }, + // ], + + options: getDictOptions(DictEnum.NOTICE), + }, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 150, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'preview', + text: '预览', + }, + 'edit', + 'delete', + ], + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + formItemClass: 'md:col-span-2', + fieldName: 'title', + label: '标题', + rules: 'required', + }, + { + component: 'RadioGroup', + formItemClass: 'col-span-1 md:col-span-1', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: '通知', value: 0 }, + // { label: '公告', value: 1 }, + // ], + + options: getDictOptions(DictEnum.NOTICE), + optionType: 'button', + }, + defaultValue: 0, + fieldName: 'type', + label: '类型', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, + { + component: h(MarkdownEditor), + modelPropName: 'value', + componentProps: { + class: 'w-full', + }, + formItemClass: 'md:col-span-2', + fieldName: 'content', + label: '内容', + rules: 'required', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/index.vue new file mode 100644 index 0000000..5bbf802 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/notice/views/index.vue @@ -0,0 +1,199 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/api/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/api/index.ts new file mode 100644 index 0000000..41e2118 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/api/index.ts @@ -0,0 +1,32 @@ +import { requestClient } from '#/api/request'; + +export interface OAuth2BindingResult { + id: number; + sid: string; + source: string; + user_id: number; +} + +export interface OAuth2BindingParams { + source: 'Github' | 'Google'; +} + +export async function getOAuth2Github() { + return requestClient.get('/api/v1/oauth2/github'); +} + +export async function getOAuth2Google() { + return requestClient.get('/api/v1/oauth2/google'); +} + +export async function getOAuth2Bindings() { + return requestClient.get('/api/v1/oauth2/me/bindings'); +} + +export async function getOAuth2BindingAuthUrl(params: OAuth2BindingParams) { + return requestClient.get('/api/v1/oauth2/me/binding', { params }); +} + +export async function deleteOAuth2Binding(params: OAuth2BindingParams) { + return requestClient.delete('/api/v1/oauth2/me/unbinding', { params }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/package.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/package.json new file mode 100644 index 0000000..3706f2c --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fba-ui/oauth2", + "version": "5.7.0", + "private": true, + "type": "module" +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/routes/index.ts new file mode 100644 index 0000000..b52e187 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/routes/index.ts @@ -0,0 +1,16 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + name: 'OAuth2Callback', + path: '/oauth2/callback', + component: () => import('../views/index.vue'), + meta: { + icon: 'mingcute:profile-line', + title: '第三方登录', + hideInMenu: true, + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/index.vue new file mode 100644 index 0000000..67b4a41 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/index.vue @@ -0,0 +1,7 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/login.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/login.vue new file mode 100644 index 0000000..a95f091 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/plugins/oauth2/views/login.vue @@ -0,0 +1,77 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/preferences.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/preferences.ts new file mode 100644 index 0000000..9300d45 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/preferences.ts @@ -0,0 +1,30 @@ +import { defineOverridesPreferences } from '@vben/preferences'; + +/** + * @description 项目配置文件 + * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置 + * !!! 更改配置后请清空缓存,否则可能不生效 + */ +export const overridesPreferences = defineOverridesPreferences({ + // overrides + app: { + accessMode: 'backend', + name: import.meta.env.VITE_APP_TITLE, + enableRefreshToken: true, + }, + footer: { + enable: false, + }, + logo: { + source: 'https://wu-clan.github.io/picx-images-hosting/logo/fba.png', + }, + shortcutKeys: { + enable: false, + }, + theme: { + mode: 'auto', + }, + widget: { + timezone: false, + }, +}); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/access.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/access.ts new file mode 100644 index 0000000..f207a09 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/access.ts @@ -0,0 +1,45 @@ +import type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, +} from '@vben/types'; + +import { generateAccessible } from '@vben/access'; +import { preferences } from '@vben/preferences'; + +import { message } from 'antdv-next'; + +import { getAllMenusApi } from '#/api'; +import { BasicLayout, IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue'); + +async function generateAccess(options: GenerateMenuAndRoutesOptions) { + const pageMap: ComponentRecordType = { + ...import.meta.glob('../views/**/*.vue'), + ...import.meta.glob('../plugins/**/*.vue'), + }; + + const layoutMap: ComponentRecordType = { + BasicLayout, + IFrameView, + }; + + return await generateAccessible(preferences.app.accessMode, { + ...options, + fetchMenuListAsync: async () => { + message.loading({ + content: `${$t('common.loadingMenu')}...`, + duration: 1.5, + }); + return await getAllMenusApi(); + }, + // 可以指定没有权限跳转403页面 + forbiddenComponent, + // 如果 route.meta.menuVisibleWithForbidden = true + layoutMap, + pageMap, + }); +} + +export { generateAccess }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/guard.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/guard.ts new file mode 100644 index 0000000..cb3ec0a --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/guard.ts @@ -0,0 +1,165 @@ +import type { Router } from 'vue-router'; + +import { LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; +import { useAccessStore, useUserStore } from '@vben/stores'; +import { startProgress, stopProgress } from '@vben/utils'; + +import { accessRoutes, coreRouteNames } from '#/router/routes'; +import { useAuthStore, useWebSocketStore } from '#/store'; + +import { generateAccess } from './access'; + +/** + * 通用守卫配置 + * @param router + */ +function setupCommonGuard(router: Router) { + // 记录已经加载的页面 + const loadedPaths = new Set(); + + router.beforeEach((to) => { + to.meta.loaded = loadedPaths.has(to.path); + + // 页面加载进度条 + if (!to.meta.loaded && preferences.transition.progress) { + startProgress(); + } + return true; + }); + + router.afterEach((to) => { + // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 + + loadedPaths.add(to.path); + + // 关闭页面加载进度条 + if (preferences.transition.progress) { + stopProgress(); + } + }); +} + +/** + * 权限访问守卫配置 + * @param router + */ +function setupAccessGuard(router: Router) { + router.beforeEach(async (to, from) => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const authStore = useAuthStore(); + + // 优先处理 OAuth2 回调 + if ( + to.name === 'OAuth2Callback' || + to.path === '/oauth2/callback' || + window.location.pathname === '/oauth2/callback' + ) { + await authStore.oauth2Login(); + // 为了兼容 vue-router hash 模式,这里直接重定向到域名 + // 再由守卫自动完成默认地址重定向 + window.location.replace(window.location.origin); + } + + // 基本路由,这些路由不需要进入权限拦截 + if (coreRouteNames.includes(to.name as string)) { + if (to.path === LOGIN_PATH && accessStore.accessToken) { + return decodeURIComponent( + (to.query?.redirect as string) || + userStore.userInfo?.homePath || + preferences.app.defaultHomePath, + ); + } + return true; + } + + // accessToken 检查 + if (!accessStore.accessToken) { + // 明确声明忽略权限访问权限,则可以访问 + if (to.meta.ignoreAccess) { + return true; + } + + // 没有访问权限,跳转登录页面 + if (to.fullPath !== LOGIN_PATH) { + return { + path: LOGIN_PATH, + // 如不需要,直接删除 query + query: + to.fullPath === preferences.app.defaultHomePath + ? {} + : { redirect: encodeURIComponent(to.fullPath) }, + // 携带当前跳转的页面,登录后重新跳转该页面 + replace: true, + }; + } + return to; + } + + // 是否已经生成过动态路由 + if (accessStore.isAccessChecked) { + return true; + } + + // 生成路由表 + // 当前登录用户拥有的角色标识列表 + const userInfo = userStore.userInfo || (await authStore.fetchUserInfo()); + const userRoles = userInfo.roles ?? []; + + // 生成菜单和路由 + const { accessibleMenus, accessibleRoutes } = await generateAccess({ + roles: userRoles, + router, + // 则会在菜单中显示,但是访问会被重定向到403 + routes: accessRoutes, + }); + + // 保存菜单信息和路由信息 + accessStore.setAccessMenus(accessibleMenus); + accessStore.setAccessRoutes(accessibleRoutes); + accessStore.setIsAccessChecked(true); + const redirectPath = (from.query.redirect ?? + (to.path === preferences.app.defaultHomePath + ? userInfo.homePath || preferences.app.defaultHomePath + : to.fullPath)) as string; + + return { + ...router.resolve(decodeURIComponent(redirectPath)), + replace: true, + }; + }); +} + +/** + * WebSocket 守卫配置 + * @param router + */ +export function setupWebSocketGuard(router: Router) { + router.beforeEach(async (_) => { + const accessStore = useAccessStore(); + const wsStore = useWebSocketStore(); + + // 检查 WebSocket 连接状态 + if (accessStore.accessToken && !wsStore.isConnected) { + wsStore.connect(); + } + + return true; + }); +} + +/** + * 项目守卫配置 + * @param router + */ +function createRouterGuard(router: Router) { + /** 通用 */ + setupCommonGuard(router); + /** 权限访问 */ + setupAccessGuard(router); + /** WebSocket */ + setupWebSocketGuard(router); +} + +export { createRouterGuard }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/index.ts new file mode 100644 index 0000000..4840230 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/index.ts @@ -0,0 +1,37 @@ +import { + createRouter, + createWebHashHistory, + createWebHistory, +} from 'vue-router'; + +import { resetStaticRoutes } from '@vben/utils'; + +import { createRouterGuard } from './guard'; +import { routes } from './routes'; + +/** + * @zh_CN 创建vue-router实例 + */ +const router = createRouter({ + history: + import.meta.env.VITE_ROUTER_HISTORY === 'hash' + ? createWebHashHistory(import.meta.env.VITE_BASE) + : createWebHistory(import.meta.env.VITE_BASE), + // 应该添加到路由的初始路由列表。 + routes, + scrollBehavior: (to, _from, savedPosition) => { + if (savedPosition) { + return savedPosition; + } + return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 }; + }, + // 是否应该禁止尾部斜杠。 + // strict: true, +}); + +const resetRoutes = () => resetStaticRoutes(router, routes); + +// 创建路由守卫 +createRouterGuard(router); + +export { resetRoutes, router }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/core.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/core.ts new file mode 100644 index 0000000..949b0b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/core.ts @@ -0,0 +1,97 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; + +import { $t } from '#/locales'; + +const BasicLayout = () => import('#/layouts/basic.vue'); +const AuthPageLayout = () => import('#/layouts/auth.vue'); +/** 全局404页面 */ +const fallbackNotFoundRoute: RouteRecordRaw = { + component: () => import('#/views/_core/fallback/not-found.vue'), + meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, + title: '404', + }, + name: 'FallbackNotFound', + path: '/:path(.*)*', +}; + +/** 基本路由,这些路由是必须存在的 */ +const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ + { + component: BasicLayout, + meta: { + hideInBreadcrumb: true, + title: 'Root', + }, + name: 'Root', + path: '/', + redirect: preferences.app.defaultHomePath, + children: [], + }, + { + component: AuthPageLayout, + meta: { + hideInTab: true, + title: 'Authentication', + }, + name: 'Authentication', + path: '/auth', + redirect: LOGIN_PATH, + children: [ + { + name: 'Login', + path: 'login', + component: () => import('#/views/_core/authentication/login.vue'), + meta: { + title: $t('page.auth.login'), + }, + }, + { + name: 'CodeLogin', + path: 'code-login', + component: () => import('#/views/_core/authentication/code-login.vue'), + meta: { + title: $t('page.auth.codeLogin'), + }, + }, + { + name: 'QrCodeLogin', + path: 'qrcode-login', + component: () => + import('#/views/_core/authentication/qrcode-login.vue'), + meta: { + title: $t('page.auth.qrcodeLogin'), + }, + }, + { + name: 'ForgetPassword', + path: 'forget-password', + component: () => + import('#/views/_core/authentication/forget-password.vue'), + meta: { + title: $t('page.auth.forgetPassword'), + }, + }, + { + name: 'Register', + path: 'register', + component: () => import('#/views/_core/authentication/register.vue'), + meta: { + title: $t('page.auth.register'), + }, + }, + ], + }, +]; + +export { coreRoutes, fallbackNotFoundRoute }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/index.ts new file mode 100644 index 0000000..931ccd8 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/index.ts @@ -0,0 +1,55 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { mergeRouteModules, traverseTreeValues } from '@vben/utils'; + +import { coreRoutes, fallbackNotFoundRoute } from './core'; + +const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', { + eager: true, +}); + +const pluginRouteFiles = import.meta.glob('../../plugins/**/routes/*.ts', { + eager: true, +}); + +// 有需要可以自行打开注释,并创建文件夹 +// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true }); +// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); + +/** 动态路由 */ +const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); + +/** 插件路由 */ +const pluginRoutes: RouteRecordRaw[] = mergeRouteModules(pluginRouteFiles); + +/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统(不会显示在菜单中) */ +// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles); +// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); +const staticRoutes: RouteRecordRaw[] = []; +const externalRoutes: RouteRecordRaw[] = []; + +/** 路由列表,由基本路由、外部路由和404兜底路由组成 + * 无需走权限验证(会一直显示在菜单中) */ +const routes: RouteRecordRaw[] = [ + ...coreRoutes, + ...externalRoutes, + fallbackNotFoundRoute, +]; + +/** 基本路由列表,这些路由不需要进入权限拦截 */ +const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name); + +/** 有权限校验的路由列表,包含动态路由和静态路由 */ +const accessRoutes = [...dynamicRoutes, ...pluginRoutes, ...staticRoutes]; + +const componentKeys: string[] = Object.keys({ + ...import.meta.glob('../../views/**/*.vue'), + ...import.meta.glob('../../plugins/**/*.vue'), +}) + .filter((item) => !item.includes('/modules/')) + .map((v) => { + const path = v.replace('../../views/', '/').replace('../../', '/'); + return path.endsWith('.vue') ? path.slice(0, -4) : path; + }); + +export { accessRoutes, componentKeys, coreRouteNames, routes }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/dashboard.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/dashboard.ts new file mode 100644 index 0000000..5254dc6 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/dashboard.ts @@ -0,0 +1,38 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + icon: 'lucide:layout-dashboard', + order: -1, + title: $t('page.dashboard.title'), + }, + name: 'Dashboard', + path: '/dashboard', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: () => import('#/views/dashboard/analytics/index.vue'), + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: $t('page.dashboard.analytics'), + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: () => import('#/views/dashboard/workspace/index.vue'), + meta: { + icon: 'carbon:workspace', + title: $t('page.dashboard.workspace'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/demos.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/demos.ts new file mode 100644 index 0000000..4605d91 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/demos.ts @@ -0,0 +1,28 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + icon: 'ic:baseline-view-in-ar', + keepAlive: true, + order: 1000, + title: $t('demos.title'), + }, + name: 'Demos', + path: '/demos', + children: [ + { + meta: { + title: $t('demos.antd'), + }, + name: 'AntDesignDemos', + path: '/demos/ant-design-next', + component: () => import('#/views/demos/antd/index.vue'), + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/log.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/log.ts new file mode 100644 index 0000000..05756c0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/log.ts @@ -0,0 +1,37 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'Log', + path: '/log', + meta: { + title: $t('page.menu.log'), + icon: 'carbon:cloud-logging', + order: 3, + }, + children: [ + { + name: 'LoginLog', + path: '/log/login', + component: () => import('#/views/log/login/index.vue'), + meta: { + title: $t('page.menu.login'), + icon: 'mdi:login', + }, + }, + { + name: 'OperaLog', + path: '/log/opera', + component: () => import('#/views/log/opera/index.vue'), + meta: { + title: $t('page.menu.opera'), + icon: 'carbon:operations-record', + }, + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/monitor.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/monitor.ts new file mode 100644 index 0000000..0f13292 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/monitor.ts @@ -0,0 +1,46 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'Monitor', + path: '/monitor', + meta: { + title: $t('page.menu.monitor'), + icon: 'mdi:monitor-eye', + order: 4, + }, + children: [ + { + name: 'Online', + path: '/monitor/online', + component: () => import('#/views/monitor/online/index.vue'), + meta: { + title: $t('page.menu.online'), + icon: 'wpf:online', + }, + }, + { + name: 'Redis', + path: '/monitor/redis', + component: () => import('#/views/monitor/redis/index.vue'), + meta: { + title: $t('page.menu.redis'), + icon: 'devicon:redis', + }, + }, + { + name: 'Server', + path: '/monitor/server', + component: () => import('#/views/monitor/server/index.vue'), + meta: { + title: $t('page.menu.server'), + icon: 'mdi:server-outline', + }, + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/scheduler.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/scheduler.ts new file mode 100644 index 0000000..8de574f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/scheduler.ts @@ -0,0 +1,36 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'Scheduler', + path: '/scheduler', + meta: { + title: $t('page.menu.scheduler'), + icon: 'ix:scheduler', + }, + children: [ + { + name: 'SchedulerManage', + path: '/scheduler/manage', + component: () => import('#/views/scheduler/manage/index.vue'), + meta: { + title: $t('page.menu.schedulerManage'), + icon: 'ix:scheduler', + }, + }, + { + name: 'SchedulerRecord', + path: '/scheduler/record', + component: () => import('#/views/scheduler/record/index.vue'), + meta: { + title: $t('page.menu.schedulerRecord'), + icon: 'ix:scheduler', + }, + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/system.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/system.ts new file mode 100644 index 0000000..dd5e07e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/system.ts @@ -0,0 +1,94 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + name: 'System', + path: '/system', + meta: { + title: $t('page.menu.system'), + icon: 'grommet-icons:system', + order: 1, + }, + children: [ + { + name: 'SysDept', + path: '/system/dept', + component: () => import('#/views/system/dept/index.vue'), + meta: { + title: $t('page.menu.sysDept'), + icon: 'mingcute:department-line', + }, + }, + { + name: 'SysUser', + path: '/system/user', + component: () => import('#/views/system/user/index.vue'), + meta: { + title: $t('page.menu.sysUser'), + icon: 'ant-design:user-outlined', + }, + }, + { + name: 'SysRole', + path: '/system/role', + component: () => import('#/views/system/role/index.vue'), + meta: { + title: $t('page.menu.sysRole'), + icon: 'carbon:user-role', + }, + }, + { + name: 'SysMenu', + path: '/system/menu', + component: () => import('#/views/system/menu/index.vue'), + meta: { + title: $t('page.menu.sysMenu'), + icon: 'material-symbols:menu', + }, + }, + { + name: 'SysDataPermission', + path: '/system/data-permission', + meta: { + title: $t('page.menu.sysDataPermission'), + icon: 'icon-park-outline:permissions', + }, + children: [ + { + name: 'SysDataScope', + path: '/system/data-scope', + component: () => + import('#/views/system/data-permission/scope/index.vue'), + meta: { + title: $t('page.menu.sysDataScope'), + icon: 'cuida:scope-outline', + }, + }, + { + name: 'SysDataRule', + path: '/system/data-rule', + component: () => + import('#/views/system/data-permission/rule/index.vue'), + meta: { + title: $t('page.menu.sysDataRule'), + icon: 'material-symbols:rule', + }, + }, + ], + }, + { + name: 'SysPlugin', + path: '/system/plugin', + component: () => import('#/views/system/plugin/index.vue'), + meta: { + title: $t('page.menu.sysPlugin'), + icon: 'clarity:plugin-line', + }, + }, + ], + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/vben.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/vben.ts new file mode 100644 index 0000000..60d3352 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/router/routes/modules/vben.ts @@ -0,0 +1,72 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + badgeType: 'dot', + icon: 'https://wu-clan.github.io/picx-images-hosting/logo/fba.png', + order: 9998, + title: '项目', + }, + name: 'VbenProject', + path: '/fba', + children: [ + { + name: 'Document', + path: '/fba/document', + component: IFrameView, + meta: { + icon: 'lucide:book-open-text', + link: 'https://fastapi-practices.github.io/fastapi_best_architecture_docs', + title: $t('demos.vben.document'), + }, + }, + { + name: 'Github', + path: '/fba/github', + component: IFrameView, + meta: { + icon: 'mdi:github', + link: 'https://github.com/fastapi-practices/fastapi_best_architecture', + title: 'Github', + }, + }, + { + name: 'Apifox', + path: '/fba/apifox', + component: IFrameView, + meta: { + icon: 'simple-icons:apifox', + iframeSrc: + 'https://apifox.com/apidoc/shared-28a93f02-730b-4f33-bb5e-4dad92058cc0', + title: 'Apifox', + }, + }, + ], + }, + { + name: 'VbenAbout', + path: '/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('demos.vben.about'), + order: 9999, + }, + }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, +]; + +export default routes; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/auth.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/auth.ts new file mode 100644 index 0000000..1a18268 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/auth.ts @@ -0,0 +1,161 @@ +import type { Recordable } from '@vben/types'; + +import type { CaptchaResult, LoginParams, MyUserInfo } from '#/api'; + +import { ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; +import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; + +import { notification } from 'antdv-next'; +import { defineStore } from 'pinia'; + +import { + getAccessCodesApi, + getCaptchaApi, + getUserInfoApi, + loginApi, + logoutApi, +} from '#/api'; +import { $t } from '#/locales'; +import { useDictStore, useWebSocketStore } from '#/store'; + +export const useAuthStore = defineStore('auth', () => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const dictStore = useDictStore(); + const router = useRouter(); + + const loginLoading = ref(false); + + /** + * 登陆验证码 + */ + async function captcha() { + const res: CaptchaResult = await getCaptchaApi(); + accessStore.setCaptchaUuid(res.uuid); + return res; + } + + /** + * 异步处理登录操作 + * Asynchronously handle the login process + * @param params 登录表单数据 + */ + async function authLogin( + params: Recordable, + onSuccess?: () => Promise | void, + ) { + // 异步处理用户登录操作并获取 accessToken + let userInfo: MyUserInfo | null = null; + try { + loginLoading.value = true; + const { access_token, session_uuid } = await loginApi( + params as LoginParams, + ); + + // 如果成功获取到 accessToken + if (access_token) { + accessStore.setAccessToken(access_token); + accessStore.setAccessSessionUuid(session_uuid); + + // 获取用户信息并存储到 accessStore 中 + const [fetchUserInfoResult, accessCodes] = await Promise.all([ + fetchUserInfo(), + getAccessCodesApi(), + ]); + + userInfo = fetchUserInfoResult; + + userStore.setUserInfo(userInfo); + accessStore.setAccessCodes(accessCodes); + + if (accessStore.loginExpired) { + accessStore.setLoginExpired(false); + } else { + onSuccess + ? await onSuccess?.() + : await router.push( + userInfo.homePath || preferences.app.defaultHomePath, + ); + } + + // 初始化WebSocket连接 + const wsStore = useWebSocketStore(); + wsStore.connect(); + + if (userInfo?.nickname) { + notification.success({ + description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.nickname}`, + duration: 3, + title: $t('authentication.loginSuccess'), + }); + } + } + } finally { + loginLoading.value = false; + } + + return { + userInfo, + }; + } + + async function oauth2Login() { + const params = new URLSearchParams(window.location.search); + const access_token = params.get('access_token'); + const session_uuid = params.get('session_uuid'); + + if (access_token && session_uuid) { + accessStore.setAccessToken(access_token); + accessStore.setAccessSessionUuid(session_uuid); + return true; + } + + console.error('Missing or invalid access_token or session_uuid'); + return false; + } + + async function logout(redirect: boolean = true) { + try { + await logoutApi(); + } catch { + // 不做任何处理 + } + resetAllStores(); + accessStore.setLoginExpired(false); + + // 回登录页带上当前路由地址 + await router.replace({ + path: LOGIN_PATH, + query: redirect + ? { + redirect: encodeURIComponent(router.currentRoute.value.fullPath), + } + : {}, + }); + } + + async function fetchUserInfo() { + const userInfo = await getUserInfoApi(); + userStore.setUserInfo(userInfo); + dictStore.resetCache(); + return userInfo; + } + + function $reset() { + loginLoading.value = false; + } + + return { + $reset, + captcha, + authLogin, + oauth2Login, + fetchUserInfo, + loginLoading, + logout, + }; +}); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/dict.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/dict.ts new file mode 100644 index 0000000..a151484 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/dict.ts @@ -0,0 +1,100 @@ +import type { DictDataResult } from '#/plugins/dict/api'; +import type { DictOptionsParams } from '#/utils/dict'; + +import { reactive } from 'vue'; + +import { $t } from '@vben/locales'; + +import { defineStore } from 'pinia'; + +import { generateDictCacheKey } from '#/utils/dict'; + +export interface DictOption { + disabled?: boolean; + label: string; + value: boolean | number | string; + color?: string; +} + +export function dictToOptions( + data: DictDataResult[], + params: DictOptionsParams, +): DictOption[] { + const { asBoolean = false, asNumber = false, asString = false } = params; + + return data.map((item) => { + let value: boolean | number | string = item.value; + if (asBoolean) { + value = item.value === 'true'; + } else if (asNumber) { + value = Number(item.value); + } else if (asString) { + // asString 时保持原样,因为 value 本身就是 string 类型 + value = item.value; + } + + return { + disabled: item.status === 0, + label: $t(item.label), + value, + color: item.color, + }; + }); +} + +export const useDictStore = defineStore('dict', () => { + const dictOptionsMap = reactive(new Map()); + const dictRequestCache = reactive( + new Map>(), + ); + + function getDictOptions( + dictName: string, + params: DictOptionsParams, + ): DictOption[] { + if (!dictName) return []; + + const cacheKey = generateDictCacheKey(dictName, params); + + if (!dictOptionsMap.has(cacheKey)) { + dictOptionsMap.set(cacheKey, []); + } + + return dictOptionsMap.get(cacheKey) || []; + } + + function setDictInfo( + dictName: string, + dictValue: DictDataResult[], + params: DictOptionsParams, + ) { + const cacheKey = generateDictCacheKey(dictName, params); + + if ( + dictOptionsMap.has(cacheKey) && + dictOptionsMap.get(cacheKey)?.length === 0 + ) { + dictOptionsMap.get(cacheKey)?.push(...dictToOptions(dictValue, params)); + } else { + dictOptionsMap.set(cacheKey, dictToOptions(dictValue, params)); + } + } + + function resetCache() { + dictOptionsMap.clear(); + dictRequestCache.clear(); + } + + function $reset() { + // doNothing + } + + return { + $reset, + dictOptionsMap, + dictRequestCache, + getDictOptions, + setDictInfo, + resetCache, + }; +}); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/index.ts new file mode 100644 index 0000000..369ff35 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/index.ts @@ -0,0 +1,3 @@ +export * from './auth'; +export * from './dict'; +export * from './websocket'; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/websocket.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/websocket.ts new file mode 100644 index 0000000..b086e46 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/store/websocket.ts @@ -0,0 +1,252 @@ +// stores/websocket.ts +import type { Socket } from 'socket.io-client'; + +import { computed, ref } from 'vue'; + +import { useAccessStore } from '@vben/stores'; + +import { defineStore } from 'pinia'; +import { io } from 'socket.io-client'; + +export const useWebSocketStore = defineStore('websocket', () => { + const socket = ref(null); + const isConnected = ref(false); + const eventCleanupFunctions = ref void>>([]); + const reconnectCallbacks = ref(new Set<() => void>()); + + // 连接配置常量 + const WS_URL = import.meta.env.VITE_GLOB_API_URL; + const WS_PATH = '/ws/socket.io'; + + // 连接配置参数 + const WS_CONFIG = { + autoConnect: true, + path: WS_PATH, + reconnection: true, + reconnectionAttempts: 3, + reconnectionDelay: 1000, + transports: ['websocket'], + }; + + /** + * 建立 WebSocket 连接 + * @returns {boolean} 连接是否成功 + */ + const connect = () => { + const accessStore = useAccessStore(); + + // 检查登录状态 + if (!accessStore.accessToken) { + console.warn('用户未登录或登录已过期,无法建立 WebSocket 连接'); + return false; + } + + // 检查是否已连接 + if (isConnected.value) { + return true; + } + + // 如果有 socket 实例但未连接,尝试连接 + if (socket.value && !isConnected.value) { + socket.value.connect(); + return true; + } + + try { + // console.log('正在初始化 WebSocket 连接...'); + // 创建 Socket 连接,携带认证信息 + socket.value = io(WS_URL, { + ...WS_CONFIG, + auth: { + session_uuid: accessStore.accessSessionUuid, + token: accessStore.accessToken, + }, + }); + + // 注册核心事件监听器 + registerCoreEvents(); + return true; + } catch (error) { + console.error('WebSocket 初始化失败:', error); + return false; + } + }; + + /** + * 注册核心事件监听器 + */ + const registerCoreEvents = () => { + if (!socket.value) return; + + const onConnect = () => { + // console.log('WebSocket 连接成功'); + isConnected.value = true; + }; + + const onConnectError = (error: Error) => { + console.error('WebSocket 连接错误:', error); + isConnected.value = false; + handleConnectionError(); + }; + + const onDisconnect = (reason: string) => { + console.warn('WebSocket 已断开:', reason); + isConnected.value = false; + }; + + socket.value.on('connect', onConnect); + socket.value.on('connect_error', onConnectError); + socket.value.on('disconnect', onDisconnect); + + // 监听 Manager 的重连事件 + socket.value.io.on('reconnect_attempt', (attempt: number) => { + console.warn( + `WebSocket 重连尝试 ${attempt}/${WS_CONFIG.reconnectionAttempts}`, + ); + }); + + socket.value.io.on('reconnect', () => { + isConnected.value = true; + reconnectCallbacks.value.forEach((callback) => callback()); + }); + + socket.value.io.on('reconnect_failed', () => { + console.error('WebSocket 重连失败,已达到最大重连次数'); + isConnected.value = false; + }); + + // 保存核心事件到清理列表 + eventCleanupFunctions.value.push( + () => socket.value?.off('connect', onConnect), + () => socket.value?.off('connect_error', onConnectError), + () => socket.value?.off('disconnect', onDisconnect), + ); + }; + + /** + * 处理连接错误 + */ + const handleConnectionError = () => { + const accessStore = useAccessStore(); + if (!accessStore.accessToken) { + disconnect(); + } + }; + + /** + * 发送消息 + * @param event 事件名称 + * @param data 发送的数据 + */ + const emit = (event: string, data?: any): boolean => { + if (!socket.value || !isConnected.value) { + console.warn('WebSocket 未连接'); + return false; + } + + try { + socket.value.emit(event, data); + return true; + } catch (error) { + console.error('发送消息失败:', error); + return false; + } + }; + + /** + * 监听事件 + * @param event 事件名称 + * @param callback 回调函数 + */ + const on = (event: string, callback: (data: any) => void) => { + if (!socket.value) { + console.warn('WebSocket 未初始化'); + return () => {}; + } + + socket.value.on(event, callback); + + // 保存清理函数 + const cleanup = () => { + socket.value?.off(event, callback); + }; + + eventCleanupFunctions.value.push(cleanup); + return cleanup; // 返回清理函数,便于手动清理 + }; + + /** + * 监听 WebSocket 重连成功事件 + * @param callback 重连成功后的回调 + */ + const onReconnect = (callback: () => void) => { + reconnectCallbacks.value.add(callback); + + return () => { + reconnectCallbacks.value.delete(callback); + }; + }; + + /** + * 移除事件监听 + * @param event 事件名称 + * @param callback 可选,特定回调函数 + */ + const off = (event: string, callback?: any) => { + if (!socket.value) { + console.warn('WebSocket 未初始化'); + return; + } + + if (callback) { + socket.value.off(event, callback); + } else { + socket.value.off(event); + } + }; + + /** + * 清理所有注册的事件监听 + */ + const cleanupEvents = () => { + eventCleanupFunctions.value.forEach((cleanup) => cleanup()); + eventCleanupFunctions.value = []; + }; + + /** + * 断开 WebSocket 连接 + */ + const disconnect = () => { + if (socket.value) { + cleanupEvents(); + socket.value.disconnect(); + socket.value = null; + isConnected.value = false; + } + }; + + // 连接状态计算属性 + const connectionStatus = computed(() => { + if (!socket.value) return 'disconnected'; + if (isConnected.value) return 'connected'; + return socket.value.connected ? 'connected' : 'connecting'; + }); + + function $reset() { + disconnect(); + } + + return { + $reset, + socket, + isConnected, + connectionStatus, + connect, + disconnect, + emit, + on, + onReconnect, + off, + cleanupEvents, + }; +}); diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/antd.d.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/antd.d.ts new file mode 100644 index 0000000..9c9fd7e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/antd.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/index.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/index.ts new file mode 100644 index 0000000..cb72765 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/index.ts @@ -0,0 +1 @@ +export * from './pagination'; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/pagination.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/pagination.ts new file mode 100644 index 0000000..fe4f4dd --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/types/pagination.ts @@ -0,0 +1,10 @@ +interface PaginationResult { + items: Array; + page: number; + size: number; + total: number; + total_pages: number; + links: any; +} + +export type { PaginationResult }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/utils/dict.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/utils/dict.ts new file mode 100644 index 0000000..4b58703 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/utils/dict.ts @@ -0,0 +1,89 @@ +import type { DictDataResult } from '#/plugins/dict/api'; + +import { getDictDataDetailApi } from '#/plugins/dict/api'; +import { useDictStore } from '#/store'; + +export enum DictEnum { + NOTICE = 'notice', + SYS_CHOOSE = 'sys_choose', + SYS_DATA_RULE_EXPRESSION = 'sys_data_rule_expression', + SYS_DATA_RULE_OPERATOR = 'sys_data_rule_operator', + SYS_FRONTEND_CONFIG = 'sys_frontend_config', + SYS_LOGIN_STATUS = 'sys_login_status', + SYS_MENU_TYPE = 'sys_menu_type', + SYS_PLUGIN_TYPE = 'sys_plugin_type', + SYS_STATUS = 'sys_status', + TASK_PERIOD_TYPE = 'task_period_type', + TASK_STRATEGY_TYPE = 'task_strategy_type', + USER_ONLINE_STATUS = 'user_online_status', +} + +export const DICT_CONFIG: Record = { + [DictEnum.SYS_STATUS]: { asNumber: true }, + [DictEnum.NOTICE]: { asNumber: true }, + [DictEnum.SYS_CHOOSE]: { asBoolean: true }, + [DictEnum.SYS_DATA_RULE_EXPRESSION]: { asNumber: true }, + [DictEnum.SYS_DATA_RULE_OPERATOR]: { asNumber: true }, + [DictEnum.SYS_FRONTEND_CONFIG]: { asBoolean: true }, + [DictEnum.SYS_LOGIN_STATUS]: { asNumber: true }, + [DictEnum.SYS_MENU_TYPE]: { asNumber: true }, + [DictEnum.SYS_PLUGIN_TYPE]: { asNumber: true }, + [DictEnum.TASK_PERIOD_TYPE]: { asString: true }, + [DictEnum.TASK_STRATEGY_TYPE]: { asNumber: true }, + [DictEnum.USER_ONLINE_STATUS]: { asNumber: true }, +}; + +export interface DictOptionsParams { + asBoolean?: boolean; + asNumber?: boolean; + asString?: boolean; +} + +export const generateDictCacheKey = ( + dictName: string, + params: DictOptionsParams = {}, +): string => { + const { asBoolean = false, asNumber = false, asString = false } = params; + return `${dictName}_${asBoolean}_${asNumber}_${asString}`; +}; + +export function getDictOptions( + dictName: string, + params: DictOptionsParams = {}, +) { + const { dictRequestCache, setDictInfo, getDictOptions } = useDictStore(); + const param = + Object.keys(params).length > 0 + ? (params ?? {}) + : DICT_CONFIG[dictName] || {}; + const cacheKey = generateDictCacheKey(dictName, param); + const dataList = getDictOptions(dictName, param); + + if (dataList.length === 0 && !dictRequestCache.has(cacheKey)) { + const requestPromise = getDictDataDetailApi(dictName) + .then((res: DictDataResult[]) => { + setDictInfo(dictName, res, param); + return res; + }) + .catch((error: any) => { + console.error(error); + return [] as DictDataResult[]; + }) + .finally(() => { + if (dataList.length > 0) { + dictRequestCache.delete(cacheKey); + } + }); + + dictRequestCache.set(cacheKey, requestPromise); + } + + return dataList; +} + +// 预加载所有字典 +export function preloadDictOptions() { + Object.keys(DICT_CONFIG).forEach((dictName) => { + getDictOptions(dictName, DICT_CONFIG[dictName]); + }); +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/README.md b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/README.md new file mode 100644 index 0000000..8248afe --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/README.md @@ -0,0 +1,3 @@ +# \_core + +此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。 diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/about/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/about/index.vue new file mode 100644 index 0000000..0ee5243 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/about/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/code-login.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/code-login.vue new file mode 100644 index 0000000..02370f7 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/code-login.vue @@ -0,0 +1,68 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue new file mode 100644 index 0000000..10444d0 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue @@ -0,0 +1,42 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/login.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/login.vue new file mode 100644 index 0000000..f47d78b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/login.vue @@ -0,0 +1,182 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/qrcode-login.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/qrcode-login.vue new file mode 100644 index 0000000..23f5f2d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/qrcode-login.vue @@ -0,0 +1,10 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/register.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/register.vue new file mode 100644 index 0000000..8c42953 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/authentication/register.vue @@ -0,0 +1,95 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/coming-soon.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/coming-soon.vue new file mode 100644 index 0000000..f394930 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/coming-soon.vue @@ -0,0 +1,7 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/forbidden.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/forbidden.vue new file mode 100644 index 0000000..8ea65fe --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/forbidden.vue @@ -0,0 +1,9 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/iframe.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/iframe.vue new file mode 100644 index 0000000..296a099 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/iframe.vue @@ -0,0 +1,7 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/internal-error.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/internal-error.vue new file mode 100644 index 0000000..819a47d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/internal-error.vue @@ -0,0 +1,9 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/not-found.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/not-found.vue new file mode 100644 index 0000000..4d178e9 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/not-found.vue @@ -0,0 +1,9 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/offline.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/offline.vue new file mode 100644 index 0000000..5de4a88 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/fallback/offline.vue @@ -0,0 +1,9 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/basic-info.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/basic-info.vue new file mode 100644 index 0000000..538ccb8 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/basic-info.vue @@ -0,0 +1,184 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/binding.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/binding.vue new file mode 100644 index 0000000..cefb0fd --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/binding.vue @@ -0,0 +1,144 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/data.ts new file mode 100644 index 0000000..d14f43b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/data.ts @@ -0,0 +1,204 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { OnlineMonitorResult } from '#/api'; + +import { ref } from 'vue'; + +import { $t } from '@vben/locales'; + +import { message } from 'antdv-next'; + +import { z } from '#/adapter/form'; +import { getPhoneCaptchaApi } from '#/plugins/aliyun_sms/api'; +import { getEmailCaptchaApi } from '#/plugins/email/api'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const avatarSchema: VbenFormSchema[] = [ + { + component: 'Textarea', + fieldName: 'avatar', + label: '头像链接', + rules: 'required', + }, +]; + +export const nicknameSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'nickname', + label: '昵称', + rules: 'required', + }, +]; + +const CODE_LENGTH = 6; +const phoneValue = ref(''); +export const phoneSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'phone', + label: '手机号', + rules: z + .string() + .min(1, { message: $t('authentication.mobileTip') }) + .refine((v) => /^\d{11}$/.test(v), { + message: $t('authentication.mobileErrortip'), + }), + }, + { + component: 'VbenPinInput', + componentProps: { + codeLength: CODE_LENGTH, + createText: (countdown: number) => { + return countdown > 0 + ? $t('authentication.sendText', [countdown]) + : $t('authentication.sendCode'); + }, + handleSendCode: async () => { + try { + await getPhoneCaptchaApi({ phone: phoneValue.value }); + message.success('短信验证码已发送,请注意查收'); + } catch (error) { + console.error(error); + } + }, + placeholder: $t('authentication.code'), + }, + dependencies: { + trigger(values) { + phoneValue.value = values.phone; + }, + triggerFields: ['phone'], + }, + fieldName: 'captcha', + label: '验证码', + rules: z.string().length(CODE_LENGTH, { + message: $t('authentication.codeTip', [CODE_LENGTH]), + }), + }, +]; + +const emailValue = ref(''); +export const emailSchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'email', + label: '邮箱', + rules: z.string().email({ message: '无效的邮箱地址' }), + }, + { + component: 'VbenPinInput', + componentProps: { + codeLength: CODE_LENGTH, + createText: (countdown: number) => { + return countdown > 0 + ? $t('authentication.sendText', [countdown]) + : $t('authentication.sendCode'); + }, + handleSendCode: async () => { + try { + await getEmailCaptchaApi({ recipients: emailValue.value }); + message.success('邮箱验证码已发送,请注意查收'); + } catch (error) { + console.error(error); + } + }, + placeholder: $t('authentication.code'), + }, + dependencies: { + trigger(values) { + emailValue.value = values.email; + }, + triggerFields: ['email'], + }, + fieldName: 'captcha', + label: '验证码', + rules: z.string().length(CODE_LENGTH, { + message: $t('authentication.codeTip', [CODE_LENGTH]), + }), + }, +]; + +export const passwordSchema: VbenFormSchema[] = [ + { + component: 'InputPassword', + fieldName: 'old_password', + label: '当前密码', + rules: z + .string({ message: '请输入当前密码' }) + .min(6, '密码长度不能少于 6 个字符') + .max(20, '密码长度不能超过 20 个字符'), + }, + { + component: 'InputPassword', + fieldName: 'new_password', + label: '新密码', + rules: z + .string({ message: '请输入新密码' }) + .min(6, '密码长度不能少于 6 个字符') + .max(20, '密码长度不能超过 20 个字符'), + }, + { + component: 'InputPassword', + fieldName: 'confirm_password', + label: '确认密码', + dependencies: { + rules(values) { + return z + .string({ message: '请输入确认密码' }) + .min(6, '密码长度不能少于 6 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value === values.new_password, + '两次密码输出不一致', + ); + }, + triggerFields: ['new_password', 'confirm_password'], + }, + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { field: 'ip', title: 'IP 地址' }, + { field: 'os', title: '操作系统' }, + { field: 'browser', title: '浏览器' }, + { field: 'device', title: '设备' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: '在线', value: 1 }, + // { color: 'warning', label: '离线', value: 0 }, + // ], + options: getDictOptions(DictEnum.USER_ONLINE_STATUS), + }, + }, + { field: 'last_login_time', title: '最后登录时间' }, + { field: 'expire_time', title: '过期时间' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 120, + cellRender: { + attrs: { + nameField: 'nickname', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'delete', + text: '强制下线', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/index.vue new file mode 100644 index 0000000..542d026 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/index.vue @@ -0,0 +1,65 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/online-device.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/online-device.vue new file mode 100644 index 0000000..dc5a613 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/online-device.vue @@ -0,0 +1,81 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/security.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/security.vue new file mode 100644 index 0000000..133e28b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/_core/profile/security.vue @@ -0,0 +1,213 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-trends.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-trends.vue new file mode 100644 index 0000000..f1f0b23 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-trends.vue @@ -0,0 +1,98 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-data.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-data.vue new file mode 100644 index 0000000..190fb41 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-data.vue @@ -0,0 +1,82 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-sales.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 0000000..6ff5208 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,46 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-source.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-source.vue new file mode 100644 index 0000000..0915c7a --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits-source.vue @@ -0,0 +1,65 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits.vue new file mode 100644 index 0000000..7e0f101 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/analytics-visits.vue @@ -0,0 +1,55 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/index.vue new file mode 100644 index 0000000..e794c99 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/analytics/index.vue @@ -0,0 +1,90 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/workspace/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/workspace/index.vue new file mode 100644 index 0000000..b95d613 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/dashboard/workspace/index.vue @@ -0,0 +1,266 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/demos/antd/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/demos/antd/index.vue new file mode 100644 index 0000000..6fb1998 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/demos/antd/index.vue @@ -0,0 +1,66 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/data.ts new file mode 100644 index 0000000..1fea5a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/data.ts @@ -0,0 +1,74 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + }, + { + component: 'Input', + fieldName: 'ip', + label: 'IP 地址', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '成功', + // value: 1, + // }, + // { + // label: '失败', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_LOGIN_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { field: 'checkbox', type: 'checkbox', align: 'left', width: 50 }, + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'username', title: '用户名' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: '成功', value: 1 }, + // { color: 'error', label: '失败', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_LOGIN_STATUS), + }, + }, + { field: 'ip', title: 'IP 地址' }, + { field: 'country', title: '国家' }, + { field: 'region', title: '地区' }, + { field: 'os', title: '操作系统' }, + { field: 'browser', title: '浏览器' }, + { field: 'device', title: '设备' }, + { field: 'msg', title: '消息', width: 150 }, + { field: 'login_time', title: '登录时间', width: 168 }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/index.vue new file mode 100644 index 0000000..fbb05dd --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/login/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/data.ts new file mode 100644 index 0000000..129dea8 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/data.ts @@ -0,0 +1,102 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { OperaLogResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + }, + { + component: 'Input', + fieldName: 'ip', + label: 'IP 地址', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '正常', + // value: 1, + // }, + // { + // label: '异常', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_LOGIN_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { field: 'checkbox', type: 'checkbox', align: 'left', width: 50 }, + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'username', title: '用户名' }, + { + field: 'method', + title: '请求方法', + cellRender: { + name: 'CellTag', + options: [ + { color: 'processing', label: 'GET', value: 'GET' }, + { color: 'success', label: 'POST', value: 'POST' }, + { color: 'cyan', label: 'PUT', value: 'PUT' }, + { color: 'error', label: 'PUT', value: 'DELETE' }, + ], + }, + }, + { field: 'title', title: '操作标题', align: 'left', width: 200 }, + { field: 'path', title: '请求路径', align: 'left', width: 250 }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: '成功', value: 1 }, + // { color: 'error', label: '失败', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_LOGIN_STATUS), + }, + }, + { field: 'cost_time', title: '耗时(ms)' }, + { field: 'opera_time', title: '操作时间' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 100, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'details', + text: '详情', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/index.vue new file mode 100644 index 0000000..b8bb96b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/log/opera/index.vue @@ -0,0 +1,228 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/data.ts new file mode 100644 index 0000000..132251b --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/data.ts @@ -0,0 +1,69 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { OnlineMonitorResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'session_uuid', title: '会话 UUID', width: 280 }, + { field: 'username', title: '用户名' }, + { field: 'nickname', title: '昵称' }, + { field: 'ip', title: 'IP 地址' }, + { field: 'os', title: '操作系统' }, + { field: 'browser', title: '浏览器' }, + { field: 'device', title: '设备' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: '在线', value: 1 }, + // { color: 'warning', label: '离线', value: 0 }, + // ], + options: getDictOptions(DictEnum.USER_ONLINE_STATUS), + }, + }, + { field: 'last_login_time', title: '最后登录时间' }, + { field: 'expire_time', title: '过期时间' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 130, + cellRender: { + attrs: { + nameField: 'nickname', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'delete', + text: '强制下线', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/index.vue new file mode 100644 index 0000000..8354792 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/online/index.vue @@ -0,0 +1,106 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/active-series.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/active-series.vue new file mode 100644 index 0000000..c82c02e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/active-series.vue @@ -0,0 +1,72 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/commands-series.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/commands-series.vue new file mode 100644 index 0000000..bb7e8a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/components/commands-series.vue @@ -0,0 +1,68 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/index.vue new file mode 100644 index 0000000..d98df01 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/redis/index.vue @@ -0,0 +1,169 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/server/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/server/index.vue new file mode 100644 index 0000000..5d7fbea --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/monitor/server/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/cron-builder.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/cron-builder.vue new file mode 100644 index 0000000..6a84130 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/cron-builder.vue @@ -0,0 +1,552 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/data.ts new file mode 100644 index 0000000..b83dfd7 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/data.ts @@ -0,0 +1,388 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { TaskSchedulerResult } from '#/api'; + +import { h } from 'vue'; + +import { $t } from '@vben/locales'; + +import { z } from '#/adapter/form'; +import { getTaskRegisteredApi } from '#/api'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const CRONTAB_PRESETS = [ + { label: '每分钟', value: '* * * * *' }, + { label: '每小时整点', value: '0 * * * *' }, + { label: '每天午夜', value: '0 0 * * *' }, + { label: '每天 9:00', value: '0 9 * * *' }, + { label: '工作日 9:00', value: '0 9 * * 1-5' }, + { label: '每15分钟', value: '*/15 * * * *' }, + { label: '每3小时', value: '0 */3 * * *' }, +]; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '任务名称', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DictEnum.TASK_STRATEGY_TYPE), + }, + fieldName: 'type', + label: '策略类型', + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { + field: 'name', + title: '任务名称', + minWidth: 120, + }, + { + field: 'task', + title: 'Celery 任务', + minWidth: 200, + showOverflow: true, + }, + { + field: 'type', + title: '策略类型', + width: 150, + cellRender: { + name: 'CellTag', + options: getDictOptions(DictEnum.TASK_STRATEGY_TYPE), + }, + }, + { + field: 'schedule', + title: '触发策略', + minWidth: 150, + slots: { default: 'schedule' }, + titleSuffix: { + icon: 'vxe-icon-question-circle-fill', + content: + 'Crontab 表达式:https://docs.celeryq.dev/en/latest/userguide/periodic-tasks.html#crontab-schedules', + }, + }, + { + field: 'enabled', + title: '状态', + width: 100, + slots: { default: 'enabled' }, + }, + { + field: 'total_run_count', + title: '执行总计', + width: 100, + slots: { default: 'total_run_count' }, + }, + { + field: 'last_run_time', + title: '最近执行', + width: 168, + }, + { + field: 'execute', + title: '手动执行', + align: 'center', + fixed: 'right', + width: 80, + slots: { default: 'execute' }, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 160, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'log', + text: '日志', + }, + 'edit', + 'delete', + ], + }, + }, + ]; +} + +export function createSchema( + onOpenCrontabBuilder: () => void, +): VbenFormSchema[] { + return [ + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: 'Interval(间隔)', value: 0 }, + // { label: 'Crontab(计划)', value: 1 }, + // ], + options: getDictOptions(DictEnum.TASK_STRATEGY_TYPE), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'type', + formItemClass: 'col-span-2 md:col-span-2', + label: '策略类型', + }, + { + component: 'Input', + fieldName: 'name', + label: '任务名称', + rules: 'required', + }, + { + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getTaskRegisteredApi, + class: 'w-full', + labelField: 'name', + valueField: 'task', + placeholder: 'Celery 任务名称或任务模块化字符串', + }, + fieldName: 'task', + label: 'Celery 任务', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'args', + label: '位置参数', + help: 'JSON 数组格式,例如:[1, "hello", true]', + rules: z + .string() + .optional() + .transform((val, ctx) => { + if (!val || val.trim() === '') return undefined; + try { + const parsed = JSON.parse(val); + if (!Array.isArray(parsed)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '必须是 JSON 数组格式,例如:[1, "hello", true]', + }); + return z.NEVER; + } + return JSON.stringify(parsed); + } catch { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '无效的 JSON 格式,请输入合法的 JSON 数组', + }); + return z.NEVER; + } + }), + }, + { + component: 'Textarea', + fieldName: 'kwargs', + label: '关键字参数', + help: 'JSON 对象格式,例如:{"key": "value", "count": 10}', + rules: z + .string() + .optional() + .transform((val, ctx) => { + if (!val || val.trim() === '') return undefined; + try { + const parsed = JSON.parse(val); + if ( + Array.isArray(parsed) || + typeof parsed !== 'object' || + parsed === null + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '必须是 JSON 对象格式,例如:{"key": "value"}', + }); + return z.NEVER; + } + return JSON.stringify(parsed); + } catch { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '无效的 JSON 格式,请输入合法的 JSON 对象', + }); + return z.NEVER; + } + }), + }, + { + component: 'Input', + fieldName: 'queue', + label: '执行队列', + help: '将任务下发到指定队列', + }, + { + component: 'Input', + fieldName: 'exchange', + label: '消息交换机', + help: '参考:https://docs.celeryq.dev/en/stable/userguide/routing.html#exchanges-queues-and-routing-keys', + }, + { + component: 'Input', + fieldName: 'routing_key', + label: '路由密钥', + help: '参考:https://docs.celeryq.dev/en/stable/userguide/routing.html#exchanges-queues-and-routing-keys', + }, + { + component: 'DatePicker', + componentProps: { + class: 'w-full', + showTime: true, + }, + fieldName: 'start_time', + label: '开始执行时间', + }, + { + component: 'DatePicker', + componentProps: { + class: 'w-full', + showTime: true, + }, + dependencies: { + disabled: (values) => { + return !!values.expire_seconds; + }, + triggerFields: ['expire_seconds'], + }, + fieldName: 'expire_time', + label: '过期时间', + help: '如果任务执行到该时间没有执行完,则取消执行', + }, + { + component: 'InputNumber', + componentProps: { + class: 'w-full', + }, + dependencies: { + disabled: (values) => { + return !!values.expire_time; + }, + triggerFields: ['expire_time'], + }, + fieldName: 'expire_seconds', + label: '过期秒数', + help: '如果任务执行超过该秒后没有执行完,则取消执行', + }, + { + component: 'InputNumber', + componentProps: { + class: 'w-full', + min: 1, + }, + dependencies: { + show: (values) => { + return values.type === 0; + }, + required: (values) => { + return values.type === 0; + }, + triggerFields: ['type'], + }, + fieldName: 'interval_every', + label: '执行周期', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + options: getDictOptions(DictEnum.TASK_PERIOD_TYPE), + }, + dependencies: { + show: (values) => { + return values.type === 0; + }, + triggerFields: ['type'], + }, + defaultValue: 'seconds', + fieldName: 'interval_period', + label: '周期类型', + }, + { + component: 'Input', + dependencies: { + show: (values) => { + return values.type === 1; + }, + required: (values) => { + return values.type === 1; + }, + triggerFields: ['type'], + }, + fieldName: 'crontab', + label: '执行计划', + rules: z + .string() + .optional() + .refine( + (val) => + !val || + val.trim() === '' || + /^(?:[\d*/,-]+\s+){4}[\d*/,-]+$/.test(val.trim()), + { + message: '无效的 Crontab 表达式', + }, + ), + help: 'Crontab 表达式:https://docs.celeryq.dev/en/latest/userguide/periodic-tasks.html#crontab-schedules', + renderComponentContent: () => ({ + suffix: () => + h( + 'span', + { + style: 'cursor: default;', + title: '可视化配置', + onMousedown: (e: Event) => e.preventDefault(), + onClick: (e: Event) => { + e.stopPropagation(); + onOpenCrontabBuilder(); + }, + }, + '⚙', + ), + }), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: true }, + // { label: $t('common.disabled'), value: false }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE), + optionType: 'button', + }, + defaultValue: false, + fieldName: 'one_off', + label: '只执行一次', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: '备注', + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/index.vue new file mode 100644 index 0000000..2f7224d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/index.vue @@ -0,0 +1,326 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/schedule.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/schedule.ts new file mode 100644 index 0000000..32b4d4e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/manage/schedule.ts @@ -0,0 +1,50 @@ +import type { TaskSchedulerResult } from '#/api'; + +import { Cron } from 'croner'; + +const periodLabelMap: Record = { + days: '天', + hours: '小时', + minutes: '分钟', + seconds: '秒', +}; + +const periodToSeconds: Record = { + days: 86_400, + hours: 3600, + minutes: 60, + seconds: 1, +}; + +function getScheduleLabel(row: TaskSchedulerResult): string { + if (row.type === 0) { + const label = + periodLabelMap[row.interval_period || 'seconds'] || row.interval_period; + return `每 ${row.interval_every || '?'} ${label}`; + } + return row.crontab || ''; +} + +function getNextRuns(row: TaskSchedulerResult): Date[] { + try { + if (row.type === 0) { + const every = row.interval_every; + const period = row.interval_period || 'seconds'; + if (!every || every <= 0) return []; + const intervalMs = every * (periodToSeconds[period] || 1) * 1000; + const baseTime = row.last_run_time + ? new Date(row.last_run_time) + : new Date(); + return Array.from( + { length: 5 }, + (_, i) => new Date(baseTime.getTime() + intervalMs * (i + 1)), + ); + } + if (!row.crontab) return []; + return new Cron(row.crontab).nextRuns(5); + } catch { + return []; + } +} + +export { getNextRuns, getScheduleLabel }; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/data.ts new file mode 100644 index 0000000..10efd17 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/data.ts @@ -0,0 +1,74 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { TaskResult } from '#/api'; + +import { $t } from '@vben/locales'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'task_id', + label: '任务 ID', + }, + { + component: 'Input', + fieldName: 'name', + label: '任务名称', + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { field: 'checkbox', type: 'checkbox', align: 'left', width: 50 }, + { + field: 'task_id', + title: '任务 ID', + minWidth: 200, + showOverflow: true, + }, + { field: 'name', title: '任务名称' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + options: [ + { color: 'success', label: 'SUCCESS', value: 'SUCCESS' }, + { color: 'error', label: 'FAILURE', value: 'FAILURE' }, + { color: 'processing', label: 'PENDING', value: 'PENDING' }, + { color: 'processing', label: 'STARTED', value: 'STARTED' }, + { color: 'warning', label: 'RETRY', value: 'RETRY' }, + { color: 'default', label: 'REVOKED', value: 'REVOKED' }, + ], + }, + }, + { + field: 'result', + title: '结果', + showOverflow: true, + }, + { field: 'retries', title: '重试次数' }, + { field: 'date_done', title: '结束时间', width: 168 }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 100, + cellRender: { + attrs: { + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'details', + text: '详情', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/index.vue new file mode 100644 index 0000000..f115164 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/scheduler/record/index.vue @@ -0,0 +1,281 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/data.ts new file mode 100644 index 0000000..55116c7 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/data.ts @@ -0,0 +1,186 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysDataRuleResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { getSysDataRuleModelColumnsApi } from '#/api'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '数据规则名称', + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'name', title: '规则名称' }, + { field: 'model', title: '模型名称' }, + { field: 'column', title: '列名称' }, + { + field: 'operator', + title: '操作符', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'cyan', label: 'OR', value: 1 }, + // { color: 'success', label: 'AND', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_DATA_RULE_OPERATOR), + }, + width: 80, + }, + { + field: 'expression', + title: '表达式', + cellRender: { + name: 'CellTag', + // options: [ + // { label: 'not in', value: 7 }, + // { label: 'in', value: 6 }, + // { label: '<=', value: 5 }, + // { label: '<', value: 4 }, + // { label: '>=', value: 3 }, + // { label: '>', value: 2 }, + // { label: '!=', value: 1 }, + // { label: '==', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_DATA_RULE_EXPRESSION), + }, + width: 80, + }, + { field: 'value', title: '规则值' }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + }, + { + field: 'updated_time', + title: $t('common.table.updated_time'), + width: 168, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 120, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '规则名称', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + options: [], + }, + fieldName: 'model', + label: '模型', + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'column', + label: '字段', + rules: 'selectRequired', + dependencies: { + componentProps: async (values) => { + if (values.model) { + const res = await getSysDataRuleModelColumnsApi(values.model); + return { + class: 'w-full', + options: res.map((item) => ({ + label: item.comment, + value: item.key, + })), + }; + } + return { class: 'w-full' }; + }, + disabled(values) { + return !values.model; + }, + triggerFields: ['model'], + }, + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: 'OR', value: 1 }, + // { label: 'AND', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_DATA_RULE_OPERATOR), + optionType: 'button', + }, + defaultValue: 0, + fieldName: 'operator', + label: '操作符', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + // options: [ + // { label: 'not in', value: 7 }, + // { label: 'in', value: 6 }, + // { label: '<=', value: 5 }, + // { label: '<', value: 4 }, + // { label: '>=', value: 3 }, + // { label: '>', value: 2 }, + // { label: '!=', value: 1 }, + // { label: '==', value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_DATA_RULE_EXPRESSION), + }, + fieldName: 'expression', + label: '表达式', + rules: 'selectRequired', + }, + { + component: 'AutoComplete', + componentProps: { + placeholder: '请输入值或选择参数', + allowClear: true, + class: 'w-full', + showSearch: { + filterOption: (input: string, option: any) => + (option?.value || '').toLowerCase().includes(input.toLowerCase()) || + (option?.label || '').toLowerCase().includes(input.toLowerCase()), + }, + options: [], + }, + fieldName: 'value', + label: '规则值', + rules: 'required', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/index.vue new file mode 100644 index 0000000..f4ee401 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/rule/index.vue @@ -0,0 +1,203 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/data.ts new file mode 100644 index 0000000..16b0bbf --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/data.ts @@ -0,0 +1,121 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysDataScopeResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '数据范围名称', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '正常', + // value: 1, + // }, + // { + // label: '停用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'name', title: '范围名称' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + width: 100, + }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + }, + { + field: 'updated_time', + title: $t('common.table.updated_time'), + width: 168, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 200, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'rule', + text: '规则设置', + }, + 'edit', + 'delete', + ], + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '数据范围名称', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, +]; + +export const drawerColumns: VxeGridProps['columns'] = [ + { + type: 'checkbox', + title: '规则名称', + align: 'left', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/index.vue new file mode 100644 index 0000000..1c1c947 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/data-permission/scope/index.vue @@ -0,0 +1,235 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/data.ts new file mode 100644 index 0000000..52eaa62 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/data.ts @@ -0,0 +1,158 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysDeptTreeResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { z } from '#/adapter/form'; +import { getSysDeptTreeApi } from '#/api'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '部门名称', + }, + { + component: 'Input', + fieldName: 'leader', + label: '负责人', + }, + { + component: 'Input', + fieldName: 'phone', + label: '手机号码', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '正常', + // value: 1, + // }, + // { + // label: '停用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { field: 'name', title: '名称', align: 'left', treeNode: true }, + { field: 'leader', title: '负责人' }, + { field: 'phone', title: '手机号码' }, + { field: 'email', title: '邮箱' }, + { field: 'sort', title: '排序' }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + formatter: 'formatDateTime', + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 200, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'add', + text: '新增下级', + }, + 'edit', + { + code: 'delete', + disabled: (row: SysDeptTreeResult) => { + return row.id === 1; + }, + }, + ], + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '部门名称', + rules: 'required', + }, + { + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: getSysDeptTreeApi, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + }, + fieldName: 'parent_id', + label: '父级部门', + }, + { + component: 'Input', + fieldName: 'leader', + label: '负责人', + }, + { + component: 'Input', + componentProps: { + allowClear: true, + }, + fieldName: 'phone', + label: '手机号码', + }, + { + component: 'Input', + componentProps: { + allowClear: true, + }, + fieldName: 'email', + label: '邮箱地址', + rules: z.string().email({ message: '无效的邮箱地址' }).optional(), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/index.vue new file mode 100644 index 0000000..0cc588d --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/dept/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/data.ts new file mode 100644 index 0000000..7078f7f --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/data.ts @@ -0,0 +1,294 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysMenuTreeResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { z } from '#/adapter/form'; +import { getSysMenuTreeApi } from '#/api'; +import { componentKeys } from '#/router/routes'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'title', + label: '菜单标题', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '正常', + // value: 1, + // }, + // { + // label: '停用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'title', + title: '标题', + align: 'left', + fixed: 'left', + slots: { default: 'title_default' }, + treeNode: true, + width: 160, + }, + { + field: 'type', + title: '类型', + width: 80, + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'orange', label: '目录', value: 0 }, + // { color: 'default', label: '菜单', value: 1 }, + // { color: 'blue', label: '按钮', value: 2 }, + // { color: 'warning', label: '内嵌', value: 3 }, + // { color: 'success', label: '外链', value: 4 }, + // ], + options: getDictOptions(DictEnum.SYS_MENU_TYPE), + }, + }, + { field: 'sort', title: '排序', width: 50 }, + { field: 'perms', title: '权限标识', align: 'left', width: 160 }, + { field: 'name', title: '菜单名称', align: 'left', width: 180 }, + { field: 'path', title: '路由地址', align: 'left', width: 180 }, + { field: 'component', title: '页面组件', align: 'left', width: 300 }, + { + field: 'status', + title: '状态', + width: 80, + cellRender: { + name: 'CellTag', + }, + }, + { field: 'remark', title: '备注' }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 200, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'add', + text: '新增子菜单', + disabled: (row: SysMenuTreeResult) => { + return [2, 3, 4].includes(row.type); + }, + }, + 'edit', + { + code: 'delete', + disabled: (row: SysMenuTreeResult) => { + return row.name === 'System' || row.name === 'Log'; + }, + }, + ], + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: '目录', value: 0 }, + // { label: '菜单', value: 1 }, + // { label: '按钮', value: 2 }, + // { label: '内嵌', value: 3 }, + // { label: '外链', value: 4 }, + // ], + options: getDictOptions(DictEnum.SYS_MENU_TYPE), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'type', + formItemClass: 'md:col-span-2', + label: '菜单类型', + }, + { + component: 'Input', + fieldName: 'title', + label: '菜单标题', + rules: 'required', + }, + { + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: getSysMenuTreeApi, + class: 'w-full', + labelField: 'title', + valueField: 'id', + childrenField: 'children', + }, + fieldName: 'parent_id', + label: '父级部门', + }, + { + component: 'Input', + fieldName: 'name', + label: '菜单名称', + rules: 'required', + }, + { + component: 'Input', + dependencies: { + show: (values) => { + return [0, 1, 3, 4].includes(values.type); + }, + triggerFields: ['type'], + }, + fieldName: 'path', + label: '路由地址', + rules: z.string().regex(/^\/.*/, { message: '必须以为 "/" 开始' }), + }, + { + component: 'InputNumber', + componentProps: { + defaultValue: 0, + min: 0, + style: { width: '100%' }, + }, + fieldName: 'sort', + label: '排序', + }, + { + component: 'IconPicker', + dependencies: { + show: (values) => { + return [0, 1, 3, 4].includes(values.type); + }, + triggerFields: ['type'], + }, + fieldName: 'icon', + label: '图标', + }, + { + component: 'AutoComplete', + componentProps: { + allowClear: true, + class: 'w-full', + filterOption(input: string, option: { value: string }) { + return option.value.toLowerCase().includes(input.toLowerCase()); + }, + options: componentKeys.map((v: any) => ({ value: v })), + }, + dependencies: { + rules: (values) => { + return values.type === 1 ? 'required' : null; + }, + show: (values) => { + return values.type === 1; + }, + triggerFields: ['type'], + }, + fieldName: 'component', + label: '组件路径', + }, + { + component: 'Input', + dependencies: { + rules: (values) => { + return values.type === 2 ? 'required' : null; + }, + show: (values) => { + return [1, 2, 3].includes(values.type); + }, + triggerFields: ['type'], + }, + fieldName: 'perms', + label: '权限标识', + }, + { + component: 'Input', + dependencies: { + show: (values) => { + return [3, 4].includes(values.type); + }, + triggerFields: ['type'], + }, + fieldName: 'link', + label: '链接地址', + rules: z.string().url($t('ui.formRules.invalidURL')), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, + { + component: 'Switch', + componentProps: { + checkedValue: 1, + unCheckedValue: 0, + }, + defaultValue: 1, + dependencies: { + show: (values) => { + return values.type !== 2; + }, + triggerFields: ['type'], + }, + fieldName: 'display', + label: '是否显示', + }, + { + component: 'Switch', + componentProps: { + checkedValue: 1, + unCheckedValue: 0, + }, + defaultValue: 1, + dependencies: { + show: (values) => { + return values.type === 1; + }, + triggerFields: ['type'], + }, + fieldName: 'cache', + label: '是否缓存', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: '备注', + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/index.vue new file mode 100644 index 0000000..e8869fa --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/menu/index.vue @@ -0,0 +1,207 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/data.ts new file mode 100644 index 0000000..35af487 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/data.ts @@ -0,0 +1,73 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { h } from 'vue'; + +import { Button } from 'antdv-next'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export function userSchema(fileList: any): VbenFormSchema[] { + return [ + { + component: 'RadioGroup', + defaultValue: 0, + componentProps: { + // options: [ + // { + // label: '压缩包', + // value: 0, + // }, + // { + // label: 'GIT', + // value: 1, + // }, + // ], + options: getDictOptions(DictEnum.SYS_PLUGIN_TYPE), + }, + fieldName: 'installType', + label: '安装方式', + }, + { + component: 'Upload', + dependencies: { + show: (values) => values && values.installType === 0, + triggerFields: ['installType'], + }, + componentProps: { + name: 'file', + accept: '.zip', + maxCount: 1, + multiple: false, + directory: false, + fileList: fileList.value, + beforeUpload: (file: any) => { + fileList.value = [file]; + return false; + }, + onRemove: () => { + fileList.value = []; + }, + }, + renderComponentContent: () => ({ + default: () => { + return h(Button, {}, { default: () => 'Upload' }); + }, + }), + fieldName: 'uploadField', + label: 'ZIP 压缩包', + rules: 'required', + help: '仅能上传一个 zip 压缩包文件,重新上传则覆盖', + }, + { + component: 'Input', + dependencies: { + show: (values) => values && values.installType === 1, + triggerFields: ['installType'], + }, + fieldName: 'repo_url', + label: 'GIT 地址', + rules: 'required', + help: '仓库内容无法实时检测,请谨慎操作,避免非插件代码植入', + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/index.vue new file mode 100644 index 0000000..87af048 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/plugin/index.vue @@ -0,0 +1,240 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data-perm.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data-perm.vue new file mode 100644 index 0000000..873d8b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data-perm.vue @@ -0,0 +1,60 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data.ts new file mode 100644 index 0000000..e61aef9 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/data.ts @@ -0,0 +1,239 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysRoleResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '角色名称', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '已启用', + // value: 1, + // }, + // { + // label: '已停用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'name', title: '角色名称' }, + { + field: 'is_filter_scopes', + title: '过滤数据权限', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'success', label: $t('common.enabled'), value: true }, + // { color: 'error', label: $t('common.disabled'), value: false }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE), + }, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + width: 100, + }, + { field: 'remark', title: $t('common.table.mark') }, + { + field: 'created_time', + title: $t('common.table.created_time'), + width: 168, + }, + { + field: 'updated_time', + title: $t('common.table.updated_time'), + width: 168, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 200, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'perm', + text: '权限设置', + }, + 'edit', + { + code: 'delete', + disabled: (row: SysRoleResult) => { + return row.id === 1; + }, + }, + ], + }, + }, + ]; +} + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'name', + label: '角色名称', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: true }, + // { label: $t('common.disabled'), value: false }, + // ], + options: getDictOptions(DictEnum.SYS_CHOOSE), + optionType: 'button', + }, + defaultValue: true, + fieldName: 'is_filter_scopes', + label: '过滤数据权限', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + // options: [ + // { label: $t('common.enabled'), value: 1 }, + // { label: $t('common.disabled'), value: 0 }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: '状态', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + label: '备注', + }, +]; + +export const drawerQuerySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'title', + label: '菜单标题', + }, +]; + +export const drawerColumns: VxeGridProps['columns'] = [ + { + type: 'checkbox', + title: '标题', + align: 'left', + fixed: 'left', + treeNode: true, + }, + { + field: 'type', + title: '类型', + cellRender: { + name: 'CellTag', + // options: [ + // { color: 'orange', label: '目录', value: 0 }, + // { color: 'default', label: '菜单', value: 1 }, + // { color: 'blue', label: '按钮', value: 2 }, + // { color: 'warning', label: '内嵌', value: 3 }, + // { color: 'success', label: '外链', value: 4 }, + // ], + options: getDictOptions(DictEnum.SYS_MENU_TYPE), + }, + }, + { field: 'perms', title: '权限标识' }, + { field: 'remark', title: '备注' }, +]; + +export function drawerDataScopeColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + return [ + { + type: 'checkbox', + title: '范围名称', + align: 'left', + fixed: 'left', + minWidth: 150, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellTag', + }, + width: 100, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 200, + cellRender: { + attrs: { + nameField: 'name', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + { + code: 'details', + text: '规则详情', + }, + ], + }, + }, + ]; +} + +export const drawerDataRuleColumns: VxeGridProps['columns'] = [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + width: 50, + }, + { field: 'name', title: '规则名称' }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/index.vue new file mode 100644 index 0000000..be73bf2 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/index.vue @@ -0,0 +1,187 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/menu-perm.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/menu-perm.vue new file mode 100644 index 0000000..f02779e --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/role/menu-perm.vue @@ -0,0 +1,256 @@ + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/data.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/data.ts new file mode 100644 index 0000000..061b841 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/data.ts @@ -0,0 +1,316 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn, VxeGridProps } from '#/adapter/vxe-table'; +import type { SysRoleResult, SysUserResult } from '#/api'; + +import { $t } from '@vben/locales'; + +import { message } from 'antdv-next'; + +import { z } from '#/adapter/form'; +import { getSysDeptTreeApi, updateSysUserPermissionApi } from '#/api'; +import { DictEnum, getDictOptions } from '#/utils/dict'; + +export const querySchema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + }, + { + component: 'Input', + fieldName: 'phone', + label: '手机号', + }, + { + component: 'Select', + componentProps: { + allowClear: true, + // options: [ + // { + // label: '正常', + // value: 1, + // }, + // { + // label: '禁用', + // value: 0, + // }, + // ], + options: getDictOptions(DictEnum.SYS_STATUS), + placeholder: $t('common.form.select'), + }, + fieldName: 'status', + label: $t('common.form.status'), + }, +]; + +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeGridProps['columns'] { + const createPermissionSwitch = ( + type: string, + props?: Record, + ) => ({ + name: 'CellSwitch', + attrs: { + async beforeChange(_checked: any, row: SysUserResult) { + await updateSysUserPermissionApi(row.id, type); + message.success($t('ui.actionMessage.operationSuccess')); + }, + }, + props, + }); + + return [ + { + field: 'seq', + title: $t('common.table.id'), + type: 'seq', + fixed: 'left', + width: 50, + }, + { field: 'username', title: '用户名', fixed: 'left', width: 100 }, + { field: 'nickname', title: '昵称', width: 100 }, + { + field: 'avatar', + title: '头像', + width: 80, + slots: { default: 'avatar' }, + }, + { + field: 'dept', + title: '部门', + width: 120, + slots: { default: 'dept' }, + }, + { + field: 'roles', + title: '角色', + width: 200, + showOverflow: 'ellipsis', + slots: { default: 'roles' }, + }, + { + field: 'phone', + title: '手机号', + width: 150, + formatter({ cellValue }) { + return cellValue || '暂无'; + }, + }, + { + field: 'email', + title: '邮箱', + width: 150, + formatter({ cellValue }) { + return cellValue || '暂无'; + }, + }, + { + field: 'status', + title: '状态', + width: 100, + cellRender: createPermissionSwitch('status'), + }, + { + field: 'is_superuser', + title: '超级管理员', + width: 100, + cellRender: createPermissionSwitch('superuser', { + checkedValue: true, + unCheckedValue: false, + }), + }, + { + field: 'is_staff', + title: '后台登录', + width: 100, + cellRender: createPermissionSwitch('staff', { + checkedValue: true, + unCheckedValue: false, + }), + }, + { + field: 'is_multi_login', + title: '多端登录', + width: 100, + cellRender: createPermissionSwitch('multi_login', { + checkedValue: true, + unCheckedValue: false, + }), + }, + { + field: 'join_time', + title: '注册时间', + width: 168, + }, + { + field: 'last_login_time', + title: '最后登录时间', + width: 168, + }, + { + field: 'operation', + title: $t('common.table.operation'), + align: 'center', + fixed: 'right', + width: 150, + cellRender: { + attrs: { + nameField: 'username', + onClick: onActionClick, + }, + name: 'CellOperation', + options: [ + 'edit', + { + code: 'delete', + disabled: (row: SysUserResult) => { + return row.username === 'admin'; + }, + }, + { + code: 'more', + items: [{ code: 'reset_password', text: '重置密码' }], + }, + ], + }, + }, + ]; +} + +export function useEditSchema(roleSelectOptions: any): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'nickname', + label: '昵称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'avatar', + label: '头像地址', + }, + { + component: 'Input', + fieldName: 'phone', + label: '手机号码', + }, + { + component: 'Input', + fieldName: 'email', + label: '邮箱', + }, + { + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: getSysDeptTreeApi, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + }, + fieldName: 'dept_id', + label: '所属部门', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + mode: 'multiple', + options: roleSelectOptions, + fieldNames: { label: 'name', value: 'id' }, + filterOption: (input: string, option: SysRoleResult) => { + return ( + option.name?.toLowerCase()?.includes(input.toLowerCase()) ?? false + ); + }, + }, + fieldName: 'roles', + label: '角色', + rules: 'selectRequired', + }, + ]; +} + +export function useAddSchema(roleSelectOptions: any): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'username', + label: '用户名', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'nickname', + label: '昵称', + }, + { + component: 'InputPassword', + fieldName: 'password', + label: '密码', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'phone', + label: '手机号码', + }, + { + component: 'Input', + fieldName: 'email', + label: '邮箱', + }, + { + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: getSysDeptTreeApi, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + }, + fieldName: 'dept_id', + label: '所属部门', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + class: 'w-full', + mode: 'multiple', + options: roleSelectOptions, + fieldNames: { label: 'name', value: 'id' }, + filterOption: (input: string, option: SysRoleResult) => { + return ( + option.name?.toLowerCase()?.includes(input.toLowerCase()) ?? false + ); + }, + }, + fieldName: 'roles', + label: '角色', + rules: 'selectRequired', + }, + ]; +} + +export const resetPwdSchema: VbenFormSchema[] = [ + { + component: 'InputPassword', + fieldName: 'password', + label: '新密码', + rules: z + .string({ message: '请输入新密码' }) + .min(6, '密码长度不能少于 6 个字符') + .max(20, '密码长度不能超过 20 个字符'), + }, +]; diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/index.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/index.vue new file mode 100644 index 0000000..4ce1aec --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/system/user/index.vue @@ -0,0 +1,379 @@ + + + diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.json new file mode 100644 index 0000000..858a0ec --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web-app.json", + "compilerOptions": { + "paths": { + "#/*": ["./src/*"] + } + }, + "references": [{ "path": "./tsconfig.node.json" }], + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.node.json b/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.node.json new file mode 100644 index 0000000..36e9fb5 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "noEmit": false + }, + "include": ["vite.config.ts"] +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/vite.config.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/vite.config.ts new file mode 100644 index 0000000..b3b1140 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/vite.config.ts @@ -0,0 +1,16 @@ +import { fileURLToPath, URL } from 'node:url'; + +import { defineConfig } from '@vben/vite-config'; + +export default defineConfig(async () => { + return { + application: {}, + vite: { + resolve: { + alias: { + '#': fileURLToPath(new URL('src', import.meta.url)), + }, + }, + }, + }; +}); diff --git a/deploy/fba/fba-ui-src/cspell.json b/deploy/fba/fba-ui-src/cspell.json new file mode 100644 index 0000000..d852439 --- /dev/null +++ b/deploy/fba/fba-ui-src/cspell.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "language": "en,en-US", + "allowCompoundWords": true, + "words": [ + "acmr", + "aliyun", + "antd", + "antdv", + "archiver", + "astro", + "axios", + "brotli", + "cascader", + "chatcmpl", + "clsx", + "cuida", + "datas", + "dedup", + "defu", + "demi", + "depts", + "dotenv", + "echart", + "echarts", + "ependencies", + "esbuild", + "esno", + "etag", + "execa", + "iconify", + "iconoir", + "indexeddb", + "intlify", + "isequal", + "jspm", + "kwargs", + "lockb", + "lucide", + "minh", + "minw", + "mkdist", + "mockjs", + "myapp", + "naiveui", + "napi", + "nocheck", + "nolebase", + "noopener", + "noreferrer", + "nprogress", + "nuxt", + "organisation", + "oxfmt", + "oxlint", + "pinia", + "prefixs", + "publint", + "pydantic", + "qrcode", + "reka", + "rollup", + "shadcn", + "sonner", + "sortablejs", + "sqla", + "styl", + "tabler", + "taze", + "tdesign", + "tsdown", + "tsgolint", + "turborepo", + "ui-kit", + "uicons", + "unplugin", + "unref", + "vben", + "vbenjs", + "vite", + "vitejs", + "vitepress", + "vitest", + "vnode", + "vueuse", + "yxxx" + ], + "ignorePaths": [ + "**/*-dist/**", + "**/*.log", + "**/*.spec.ts", + "**/*.test.ts", + "**/__tests__/**", + "**/dist/**", + "**/icons/**", + "**/node_modules/**", + "pnpm-lock.yaml" + ] +} diff --git a/deploy/fba/fba-ui-src/docker-compose.yml b/deploy/fba/fba-ui-src/docker-compose.yml new file mode 100644 index 0000000..6e8a4e3 --- /dev/null +++ b/deploy/fba/fba-ui-src/docker-compose.yml @@ -0,0 +1,42 @@ +networks: + fba_network: + # name: fba_network + # driver: bridge + external: true + +volumes: + fba_static: + external: true + fba_static_upload: + external: true + +services: + fba_ui: + build: + context: . + dockerfile: Dockerfile + image: fba_ui:latest + ports: + - '80:80' + - '443:443' + container_name: fba_ui + restart: always + command: + - nginx + - -g + - daemon off; + volumes: + # nginx https conf + # 通过 docker 进行部署时,需要打开此配置项并确保<挂载到容器内的证书文件路径>配置 + # 与 nginx conf 中的 ssl 证书文件路径配置一致,如果你直接将 ssl 证书文件 cp + # 到了 docker 容器内,则无需挂载证书文件,直接将它们注释或删除即可 + # local_ssl_pem_path:你在服务器存放 ssl pem 证书文件的路径,自行修改 + # local_ssl_key_path: 你在服务器存放 ssl key 证书文件的路径,自行修改 + # /etc/ssl/xxx.pem:挂载到容器内 ssl pem 证书文件的路径,自行修改 + # /etc/ssl/xxx.key:挂载到容器内 ssl key 证书文件的路径,自行修改 + - local_ssl_pem_path:/etc/ssl/xxx.pem + - local_ssl_key_path:/etc/ssl/xxx.key + - fba_static:/var/www/fba_server/backend/static + - fba_static_upload:/www/fba_server/backend/static/upload + networks: + - fba_network diff --git a/deploy/fba/fba-ui-src/eslint.config.mjs b/deploy/fba/fba-ui-src/eslint.config.mjs new file mode 100644 index 0000000..d9f85a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/eslint.config.mjs @@ -0,0 +1,16 @@ +import { defineConfig } from '@vben/eslint-config'; + +export default defineConfig([ + { + files: ['apps/*/src/plugins/**/*'], + rules: { + 'n/no-extraneous-import': 'off', + }, + }, + { + files: ['apps/*/src/plugins/**/package.json'], + rules: { + 'pnpm/json-enforce-catalog': 'off', + }, + }, +]); diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.d.ts b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.d.ts new file mode 100644 index 0000000..14b40f1 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.d.ts @@ -0,0 +1,6 @@ +// eslint-disable-next-line n/no-extraneous-import +import type { UserConfig } from '@commitlint/types'; + +declare const userConfig: UserConfig; + +export default userConfig; diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.mjs b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.mjs new file mode 100644 index 0000000..36c3b09 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/index.mjs @@ -0,0 +1,153 @@ +import { execSync } from 'node:child_process'; + +import { getPackagesSync } from '@vben/node-utils'; + +const { packages } = getPackagesSync(); + +const allowedScopes = [ + ...packages.map((pkg) => pkg.packageJson.name), + 'project', + 'style', + 'lint', + 'ci', + 'dev', + 'deploy', + 'other', +]; + +// precomputed scope +const scopeComplete = execSync('git status --porcelain || true') + .toString() + .trim() + .split('\n') + .find((r) => ~r.indexOf('M src')) + ?.replaceAll(/(\/)/g, '%%') + ?.match(/src%%((\w|-)*)/)?.[1] + ?.replace(/s$/, ''); + +/** + * @type {import('cz-git').UserConfig} + */ +const userConfig = { + extends: ['@commitlint/config-conventional'], + plugins: ['commitlint-plugin-function-rules'], + prompt: { + /** @use `pnpm commit :f` */ + alias: { + b: 'build: bump dependencies', + c: 'chore: update config', + f: 'docs: fix typos', + r: 'docs: update README', + s: 'style: update code format', + }, + allowCustomIssuePrefixs: false, + // scopes: [...scopes, 'mock'], + allowEmptyIssuePrefixs: false, + customScopesAlign: scopeComplete ? 'bottom' : 'top', + defaultScope: scopeComplete, + // English + typesAppend: [ + { name: 'workflow: workflow improvements', value: 'workflow' }, + { name: 'types: type definition file changes', value: 'types' }, + ], + + // 中英文对照版 + // messages: { + // type: '选择你要提交的类型 :', + // scope: '选择一个提交范围 (可选):', + // customScope: '请输入自定义的提交范围 :', + // subject: '填写简短精炼的变更描述 :\n', + // body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n', + // breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n', + // footerPrefixsSelect: '选择关联issue前缀 (可选):', + // customFooterPrefixs: '输入自定义issue前缀 :', + // footer: '列举关联issue (可选) 例如: #31, #I3244 :\n', + // confirmCommit: '是否提交或修改commit ?', + // }, + // types: [ + // { value: 'feat', name: 'feat: 新增功能' }, + // { value: 'fix', name: 'fix: 修复缺陷' }, + // { value: 'docs', name: 'docs: 文档变更' }, + // { value: 'style', name: 'style: 代码格式' }, + // { value: 'refactor', name: 'refactor: 代码重构' }, + // { value: 'perf', name: 'perf: 性能优化' }, + // { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' }, + // { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' }, + // { value: 'ci', name: 'ci: 修改 CI 配置、脚本' }, + // { value: 'revert', name: 'revert: 回滚 commit' }, + // { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' }, + // { value: 'wip', name: 'wip: 正在开发中' }, + // { value: 'workflow', name: 'workflow: 工作流程改进' }, + // { value: 'types', name: 'types: 类型定义文件修改' }, + // ], + // emptyScopesAlias: 'empty: 不填写', + // customScopesAlias: 'custom: 自定义', + }, + rules: { + /** + * type[scope]: [function] description + * + * ^^^^^^^^^^^^^^ empty line. + * - Something here + */ + 'body-leading-blank': [2, 'always'], + /** + * type[scope]: [function] description + * + * - something here + * + * ^^^^^^^^^^^^^^ + */ + 'footer-leading-blank': [1, 'always'], + /** + * type[scope]: [function] description + * ^^^^^ + */ + 'function-rules/scope-enum': [ + 2, // level: error + 'always', + (parsed) => { + if (!parsed.scope || allowedScopes.includes(parsed.scope)) { + return [true]; + } + + return [false, `scope must be one of ${allowedScopes.join(', ')}`]; + }, + ], + /** + * type[scope]: [function] description [No more than 108 characters] + * ^^^^^ + */ + 'header-max-length': [2, 'always', 108], + + 'scope-enum': [0], + 'subject-case': [0], + 'subject-empty': [2, 'never'], + 'type-empty': [2, 'never'], + /** + * type[scope]: [function] description + * ^^^^ + */ + 'type-enum': [ + 2, + 'always', + [ + 'feat', + 'fix', + 'perf', + 'style', + 'docs', + 'test', + 'refactor', + 'build', + 'ci', + 'chore', + 'revert', + 'types', + 'release', + ], + ], + }, +}; + +export default userConfig; diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/package.json b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/package.json new file mode 100644 index 0000000..5803948 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/commitlint-config/package.json @@ -0,0 +1,34 @@ +{ + "name": "@vben/commitlint-config", + "version": "5.7.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/lint-configs/commitlint-config" + }, + "license": "MIT", + "type": "module", + "files": [ + "dist" + ], + "main": "./index.mjs", + "module": "./index.mjs", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.mjs", + "default": "./index.mjs" + } + }, + "dependencies": { + "@commitlint/cli": "catalog:", + "@commitlint/config-conventional": "catalog:", + "@vben/node-utils": "workspace:*", + "commitlint-plugin-function-rules": "catalog:", + "cz-git": "catalog:", + "czg": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/package.json b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/package.json new file mode 100644 index 0000000..c59d4d4 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/package.json @@ -0,0 +1,45 @@ +{ + "name": "@vben/eslint-config", + "version": "5.7.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/lint-configs/eslint-config" + }, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm exec tsdown" + }, + "files": [ + "dist" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs" + } + }, + "dependencies": { + "@eslint/js": "catalog:", + "@typescript-eslint/parser": "catalog:", + "@vben/oxlint-config": "workspace:*", + "eslint": "catalog:", + "eslint-plugin-jsonc": "catalog:", + "eslint-plugin-n": "catalog:", + "eslint-plugin-perfectionist": "catalog:", + "eslint-plugin-pnpm": "catalog:", + "eslint-plugin-unused-imports": "catalog:", + "eslint-plugin-vue": "catalog:", + "eslint-plugin-yml": "catalog:", + "globals": "catalog:", + "vue-eslint-parser": "catalog:", + "yaml-eslint-parser": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/ignores.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/ignores.ts new file mode 100644 index 0000000..6bbb350 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/ignores.ts @@ -0,0 +1,61 @@ +import type { Linter } from 'eslint'; + +export async function ignores(): Promise { + return [ + { + ignores: [ + '**/node_modules', + '**/dist', + '**/dist-*', + '**/*-dist', + '**/.husky', + '**/.nitro', + '**/.output', + '**/Dockerfile', + '**/package-lock.json', + '**/yarn.lock', + '**/pnpm-lock.yaml', + '**/bun.lockb', + '**/output', + '**/coverage', + '**/temp', + '**/.temp', + '**/tmp', + '**/.tmp', + '**/.history', + '**/.turbo', + '**/.nuxt', + '**/.next', + '**/.vercel', + '**/.changeset', + '**/.idea', + '**/.cache', + '**/.output', + '**/.vite-inspect', + + '**/CHANGELOG*.md', + '**/*.min.*', + '**/LICENSE*', + '**/__snapshots__', + '**/*.snap', + '**/fixtures/**', + '**/.vitepress/cache/**', + '**/auto-import?(s).d.ts', + '**/components.d.ts', + '**/types/antd.d.ts', + '**/vite.config.mts.*', + '**/*.sh', + '**/*.ttf', + '**/*.woff', + '**/.github', + '**/lefthook.yml', + + '**/.agent/**', + '**/.agents/**', + '**/.codex/**', + '**/.claude/**', + '**/.cursor/**', + ], + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/index.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/index.ts new file mode 100644 index 0000000..c1c2b4c --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/index.ts @@ -0,0 +1,9 @@ +export * from './ignores'; +export * from './javascript'; +export * from './jsonc'; +export * from './node'; +export * from './perfectionist'; +export * from './pnpm'; +export * from './typescript'; +export * from './vue'; +export * from './yaml'; diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/javascript.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/javascript.ts new file mode 100644 index 0000000..bd9bd85 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/javascript.ts @@ -0,0 +1,182 @@ +import type { Linter } from 'eslint'; + +import js from '@eslint/js'; +import pluginUnusedImports from 'eslint-plugin-unused-imports'; +import globals from 'globals'; + +const rulesCoveredByOxlint = new Set([ + 'constructor-super', + 'for-direction', + 'getter-return', + 'no-async-promise-executor', + 'no-case-declarations', + 'no-class-assign', + 'no-compare-neg-zero', + 'no-cond-assign', + 'no-const-assign', + 'no-constant-binary-expression', + 'no-constant-condition', + 'no-debugger', + 'no-delete-var', + 'no-dupe-args', + 'no-dupe-class-members', + 'no-dupe-else-if', + 'no-dupe-keys', + 'no-duplicate-case', + 'no-empty', + 'no-empty-character-class', + 'no-empty-pattern', + 'no-empty-static-block', + 'no-ex-assign', + 'no-extra-boolean-cast', + 'no-fallthrough', + 'no-func-assign', + 'no-global-assign', + 'no-import-assign', + 'no-invalid-regexp', + 'no-irregular-whitespace', + 'no-loss-of-precision', + 'no-misleading-character-class', + 'no-new-native-nonconstructor', + 'no-nonoctal-decimal-escape', + 'no-obj-calls', + 'no-prototype-builtins', + 'no-redeclare', + 'no-regex-spaces', + 'no-self-assign', + 'no-setter-return', + 'no-shadow-restricted-names', + 'no-sparse-arrays', + 'no-this-before-super', + 'no-unreachable', + 'no-unsafe-finally', + 'no-unsafe-negation', + 'no-unsafe-optional-chaining', + 'no-unused-labels', + 'no-unused-private-class-members', + 'no-unused-vars', + 'no-useless-backreference', + 'no-useless-catch', + 'no-useless-escape', + 'no-with', + 'require-yield', + 'use-isnan', + 'valid-typeof', +]); + +export async function javascript(): Promise { + const recommendedRules = Object.fromEntries( + Object.entries(js.configs.recommended.rules).filter( + ([ruleName]) => !rulesCoveredByOxlint.has(ruleName), + ), + ); + + return [ + { + languageOptions: { + ecmaVersion: 'latest', + globals: { + ...globals.browser, + ...globals.es2021, + ...globals.node, + document: 'readonly', + navigator: 'readonly', + window: 'readonly', + }, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + sourceType: 'module', + }, + sourceType: 'module', + }, + linterOptions: { + reportUnusedDisableDirectives: true, + }, + plugins: { + 'unused-imports': pluginUnusedImports, + }, + rules: { + ...recommendedRules, + 'dot-notation': ['error', { allowKeywords: true }], + 'keyword-spacing': 'off', + 'no-control-regex': 'error', + 'no-empty-function': 'off', + 'no-octal': 'error', + 'no-octal-escape': 'error', + 'no-restricted-properties': [ + 'error', + { + message: + 'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.', + property: '__proto__', + }, + { + message: 'Use `Object.defineProperty` instead.', + property: '__defineGetter__', + }, + { + message: 'Use `Object.defineProperty` instead.', + property: '__defineSetter__', + }, + { + message: 'Use `Object.getOwnPropertyDescriptor` instead.', + property: '__lookupGetter__', + }, + { + message: 'Use `Object.getOwnPropertyDescriptor` instead.', + property: '__lookupSetter__', + }, + ], + 'no-restricted-syntax': [ + 'error', + 'DebuggerStatement', + 'LabeledStatement', + 'WithStatement', + 'TSEnumDeclaration[const=true]', + 'TSExportAssignment', + ], + 'no-undef-init': 'error', + 'no-undef': 'off', + 'no-unreachable-loop': 'error', + 'object-shorthand': [ + 'error', + 'always', + { + avoidQuotes: true, + ignoreConstructors: false, + }, + ], + 'one-var': ['error', { initialized: 'never' }], + 'prefer-arrow-callback': [ + 'error', + { + allowNamedFunctions: false, + allowUnboundThis: true, + }, + ], + 'prefer-regex-literals': [ + 'error', + { + disallowRedundantWrapping: true, + }, + ], + 'spaced-comment': 'error', + 'space-before-function-paren': 'off', + + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'error', + { + args: 'after-used', + argsIgnorePattern: '^_', + vars: 'all', + varsIgnorePattern: '^_', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/jsonc.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/jsonc.ts new file mode 100644 index 0000000..3f33d13 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/jsonc.ts @@ -0,0 +1,269 @@ +import type { Linter } from 'eslint'; + +import { interopDefault } from '../util'; + +export async function jsonc(): Promise { + const pluginJsonc = await interopDefault(import('eslint-plugin-jsonc')); + + return [ + { + files: ['**/*.json', '**/*.json5', '**/*.jsonc', '*.code-workspace'], + language: 'jsonc/x', + plugins: { + jsonc: pluginJsonc as any, + }, + rules: { + 'jsonc/no-bigint-literals': 'error', + 'jsonc/no-binary-expression': 'error', + 'jsonc/no-binary-numeric-literals': 'error', + 'jsonc/no-dupe-keys': 'error', + 'jsonc/no-escape-sequence-in-identifier': 'error', + 'jsonc/no-floating-decimal': 'error', + 'jsonc/no-hexadecimal-numeric-literals': 'error', + 'jsonc/no-infinity': 'error', + 'jsonc/no-multi-str': 'error', + 'jsonc/no-nan': 'error', + 'jsonc/no-number-props': 'error', + 'jsonc/no-numeric-separators': 'error', + 'jsonc/no-octal': 'error', + 'jsonc/no-octal-escape': 'error', + 'jsonc/no-octal-numeric-literals': 'error', + 'jsonc/no-parenthesized': 'error', + 'jsonc/no-plus-sign': 'error', + 'jsonc/no-regexp-literals': 'error', + 'jsonc/no-sparse-arrays': 'error', + 'jsonc/no-template-literals': 'error', + 'jsonc/no-undefined-value': 'error', + 'jsonc/no-unicode-codepoint-escapes': 'error', + 'jsonc/no-useless-escape': 'error', + 'jsonc/space-unary-ops': 'error', + 'jsonc/valid-json-number': 'error', + 'jsonc/vue-custom-block/no-parsing-error': 'error', + }, + }, + sortTsconfig(), + sortPackageJson(), + sortCspellJson(), + ]; +} + +function sortPackageJson(): Linter.Config { + return { + files: ['**/package.json'], + rules: { + 'jsonc/sort-array-values': [ + 'error', + { + order: { type: 'asc' }, + pathPattern: '^files$|^pnpm.neverBuiltDependencies$', + }, + ], + 'jsonc/sort-keys': [ + 'error', + { + order: [ + 'name', + 'version', + 'description', + 'private', + 'keywords', + 'homepage', + 'bugs', + 'repository', + 'license', + 'author', + 'contributors', + 'categories', + 'funding', + 'type', + 'scripts', + 'files', + 'sideEffects', + 'bin', + 'main', + 'module', + 'unpkg', + 'jsdelivr', + 'types', + 'typesVersions', + 'imports', + 'exports', + 'publishConfig', + 'icon', + 'activationEvents', + 'contributes', + 'peerDependencies', + 'peerDependenciesMeta', + 'dependencies', + 'optionalDependencies', + 'devDependencies', + 'engines', + 'packageManager', + 'pnpm', + 'overrides', + 'resolutions', + 'husky', + 'simple-git-hooks', + 'lint-staged', + 'eslintConfig', + ], + pathPattern: '^$', + }, + { + order: { type: 'asc' }, + pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$', + }, + { + order: { type: 'asc' }, + pathPattern: '^(?:resolutions|overrides|pnpm.overrides)$', + }, + { + order: ['types', 'import', 'require', 'default'], + pathPattern: '^exports.*$', + }, + ], + }, + }; +} + +function sortCspellJson(): Linter.Config { + return { + files: ['**/cspell.json', '**/.cspell.json'], + rules: { + 'jsonc/sort-array-values': [ + 'error', + { + order: { type: 'asc' }, + pathPattern: '^words$|^ignorePaths$', + }, + ], + }, + }; +} + +function sortTsconfig(): Linter.Config { + return { + files: [ + '**/tsconfig.json', + '**/tsconfig.*.json', + 'internal/tsconfig/*.json', + ], + rules: { + 'jsonc/sort-keys': [ + 'error', + { + order: [ + 'extends', + 'compilerOptions', + 'references', + 'files', + 'include', + 'exclude', + ], + pathPattern: '^$', + }, + { + order: [ + /* Projects */ + 'incremental', + 'composite', + 'tsBuildInfoFile', + 'disableSourceOfProjectReferenceRedirect', + 'disableSolutionSearching', + 'disableReferencedProjectLoad', + /* Language and Environment */ + 'target', + 'jsx', + 'jsxFactory', + 'jsxFragmentFactory', + 'jsxImportSource', + 'lib', + 'moduleDetection', + 'noLib', + 'reactNamespace', + 'useDefineForClassFields', + 'emitDecoratorMetadata', + 'experimentalDecorators', + /* Modules */ + 'baseUrl', + 'rootDir', + 'rootDirs', + 'customConditions', + 'module', + 'moduleResolution', + 'moduleSuffixes', + 'noResolve', + 'paths', + 'resolveJsonModule', + 'resolvePackageJsonExports', + 'resolvePackageJsonImports', + 'typeRoots', + 'types', + 'allowArbitraryExtensions', + 'allowImportingTsExtensions', + 'allowUmdGlobalAccess', + /* JavaScript Support */ + 'allowJs', + 'checkJs', + 'maxNodeModuleJsDepth', + /* Type Checking */ + 'strict', + 'strictBindCallApply', + 'strictFunctionTypes', + 'strictNullChecks', + 'strictPropertyInitialization', + 'allowUnreachableCode', + 'allowUnusedLabels', + 'alwaysStrict', + 'exactOptionalPropertyTypes', + 'noFallthroughCasesInSwitch', + 'noImplicitAny', + 'noImplicitOverride', + 'noImplicitReturns', + 'noImplicitThis', + 'noPropertyAccessFromIndexSignature', + 'noUncheckedIndexedAccess', + 'noUnusedLocals', + 'noUnusedParameters', + 'useUnknownInCatchVariables', + /* Emit */ + 'declaration', + 'declarationDir', + 'declarationMap', + 'downlevelIteration', + 'emitBOM', + 'emitDeclarationOnly', + 'importHelpers', + 'importsNotUsedAsValues', + 'inlineSourceMap', + 'inlineSources', + 'mapRoot', + 'newLine', + 'noEmit', + 'noEmitHelpers', + 'noEmitOnError', + 'outDir', + 'outFile', + 'preserveConstEnums', + 'preserveValueImports', + 'removeComments', + 'sourceMap', + 'sourceRoot', + 'stripInternal', + /* Interop Constraints */ + 'allowSyntheticDefaultImports', + 'esModuleInterop', + 'forceConsistentCasingInFileNames', + 'isolatedModules', + 'preserveSymlinks', + 'verbatimModuleSyntax', + /* Completeness */ + 'skipDefaultLibCheck', + 'skipLibCheck', + ], + pathPattern: '^compilerOptions$', + }, + ], + }, + }; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/node.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/node.ts new file mode 100644 index 0000000..a79083e --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/node.ts @@ -0,0 +1,81 @@ +import type { Linter } from 'eslint'; + +import { interopDefault } from '../util'; + +export async function node(): Promise { + const pluginNode = await interopDefault(import('eslint-plugin-n')); + + return [ + { + plugins: { + n: pluginNode, + }, + rules: { + 'n/handle-callback-err': ['error', '^(err|error)$'], + 'n/no-deprecated-api': 'error', + 'n/no-extraneous-import': [ + 'error', + { + allowModules: [ + 'tsdown', + 'unplugin-vue', + '@vben/vite-config', + 'vitest', + 'vite', + '@vue/test-utils', + '@playwright/test', + ], + }, + ], + // 'n/no-unpublished-import': 'off', + 'n/no-unsupported-features/es-syntax': [ + 'error', + { + ignores: [], + version: '>=22.18.0', + }, + ], + 'n/prefer-global/buffer': ['error', 'never'], + // 'n/no-missing-import': 'off', + 'n/prefer-global/process': ['error', 'never'], + 'n/process-exit-as-throw': 'error', + }, + }, + { + files: [ + '**/__tests__/**/*.?([cm])[jt]s?(x)', + '**/*.spec.?([cm])[jt]s?(x)', + '**/*.test.?([cm])[jt]s?(x)', + '**/*.bench.?([cm])[jt]s?(x)', + '**/*.benchmark.?([cm])[jt]s?(x)', + ], + rules: { + 'n/prefer-global/process': 'off', + }, + }, + { + files: ['apps/backend-mock/**/**', 'docs/**/**'], + rules: { + 'n/no-extraneous-import': 'off', + 'n/prefer-global/buffer': 'off', + 'n/prefer-global/process': 'off', + }, + }, + { + files: ['**/**/playwright.config.ts'], + rules: { + 'n/prefer-global/buffer': 'off', + 'n/prefer-global/process': 'off', + }, + }, + { + files: [ + 'scripts/**/*.?([cm])[jt]s?(x)', + 'internal/**/*.?([cm])[jt]s?(x)', + ], + rules: { + 'n/prefer-global/process': 'off', + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/perfectionist.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/perfectionist.ts new file mode 100644 index 0000000..cb8c98c --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/perfectionist.ts @@ -0,0 +1,105 @@ +import type { Linter } from 'eslint'; + +import { interopDefault } from '../util'; + +export async function perfectionist(): Promise { + const perfectionistPlugin = await interopDefault( + import('eslint-plugin-perfectionist'), + ); + + return [ + perfectionistPlugin.configs['recommended-natural'], + { + rules: { + 'perfectionist/sort-exports': [ + 'error', + { + order: 'asc', + type: 'natural', + }, + ], + 'perfectionist/sort-imports': [ + 'error', + { + customGroups: [ + { + selector: 'type', + groupName: 'vben-core-type', + elementNamePattern: '^@vben-core/.+', + }, + { + selector: 'type', + groupName: 'vben-type', + elementNamePattern: '^@vben/.+', + }, + { + selector: 'type', + groupName: 'vue-type', + elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'], + }, + { + groupName: 'vben', + elementNamePattern: '^@vben/.+', + }, + { + groupName: 'vben-core', + elementNamePattern: '^@vben-core/.+', + }, + { + groupName: 'vue', + elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'], + }, + ], + environment: 'node', + groups: [ + ['type-external', 'type-builtin', 'type-import'], + 'vue-type', + 'vben-type', + 'vben-core-type', + ['type-parent', 'type-sibling', 'type-index'], + ['type-internal'], + 'value-builtin', + 'vue', + 'vben', + 'vben-core', + 'value-external', + 'value-internal', + ['value-parent', 'value-sibling', 'value-index'], + 'side-effect', + 'side-effect-style', + 'style', + 'ts-equals-import', + 'unknown', + ], + internalPattern: ['^#/.+'], + newlinesBetween: 1, + order: 'asc', + type: 'natural', + }, + ], + 'perfectionist/sort-modules': 'off', + 'perfectionist/sort-named-exports': [ + 'error', + { + order: 'asc', + type: 'natural', + }, + ], + 'perfectionist/sort-objects': [ + 'off', + { + customGroups: { + items: 'items', + list: 'list', + children: 'children', + }, + groups: ['unknown', 'items', 'list', 'children'], + ignorePattern: ['children'], + order: 'asc', + type: 'natural', + }, + ], + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/pnpm.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/pnpm.ts new file mode 100644 index 0000000..5fe5afb --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/pnpm.ts @@ -0,0 +1,38 @@ +import type { Linter } from 'eslint'; + +import { interopDefault } from '../util'; + +export async function pnpm(): Promise { + const [pluginPnpm, parserPnpm] = await Promise.all([ + interopDefault(import('eslint-plugin-pnpm')), + interopDefault(import('yaml-eslint-parser')), + ] as const); + + return [ + { + files: ['package.json', '**/package.json'], + language: 'jsonc/x', + plugins: { + pnpm: pluginPnpm, + }, + rules: { + 'pnpm/json-enforce-catalog': 'error', + 'pnpm/json-prefer-workspace-settings': 'error', + 'pnpm/json-valid-catalog': 'error', + }, + }, + { + files: ['pnpm-workspace.yaml'], + languageOptions: { + parser: parserPnpm, + }, + plugins: { + pnpm: pluginPnpm, + }, + rules: { + 'pnpm/yaml-no-duplicate-catalog-item': 'error', + 'pnpm/yaml-no-unused-catalog-item': 'error', + }, + }, + ]; +} diff --git a/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/typescript.ts b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/typescript.ts new file mode 100644 index 0000000..2f1e216 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/lint-configs/eslint-config/src/configs/typescript.ts @@ -0,0 +1,44 @@ +import type { Linter } from 'eslint'; + +import { interopDefault } from '../util'; + +/** + * @typescript-eslint 的规则已迁移到 oxlint(typescript 插件)。 + * 这里仅保留 TS 解析器,供其它 eslint 插件(perfectionist、n 等)解析 TS/TSX 文件。 + * 因不再有类型感知规则,已移除 parserOptions.project,eslint 解析更快。 + * + * 注意:移除 @typescript-eslint 插件后,unused-imports/no-unused-vars 会退回核心实现, + * 无法识别 TS 类型签名里的形参(会误报)。故对 TS/TSX/Vue 统一关闭该规则, + * 未使用变量改由 oxlint 的 no-unused-vars(类型感知)负责。 + */ +export async function typescript(): Promise { + const parserTs = await interopDefault(import('@typescript-eslint/parser')); + + return [ + { + files: ['**/*.?([cm])[jt]s?(x)'], + languageOptions: { + parser: parserTs, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + extraFileExtensions: ['.vue'], + jsxPragma: 'React', + sourceType: 'module', + }, + }, + rules: { + 'unused-imports/no-unused-vars': 'off', + }, + }, + { + // Vue ` +`; + + if (!loadingHtml) { + return; + } + + return { + enforce: 'pre', + name: 'vite:inject-app-loading', + transformIndexHtml: { + handler(html) { + const re = //; + html = html.replace(re, `${injectScript}${loadingHtml}`); + return html; + }, + order: 'pre', + }, + }; +} + +/** + * 用于获取loading的html模板 + */ +async function getLoadingRawByHtmlTemplate(loadingTemplate: string) { + // 支持在app内自定义loading模板,模版参考default-loading.html即可 + let appLoadingPath = join(process.cwd(), loadingTemplate); + + if (!fs.existsSync(appLoadingPath)) { + const __dirname = fileURLToPath(new URL('.', import.meta.url)); + appLoadingPath = join(__dirname, './default-loading.html'); + } + + return await fsp.readFile(appLoadingPath, 'utf8'); +} + +export { viteInjectAppLoadingPlugin }; diff --git a/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/inject-metadata.ts b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/inject-metadata.ts new file mode 100644 index 0000000..41c4db4 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/inject-metadata.ts @@ -0,0 +1,111 @@ +import type { PluginOption } from 'vite'; + +import { + dateUtil, + findMonorepoRoot, + getPackages, + readPackageJSON, +} from '@vben/node-utils'; + +import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest'; + +function resolvePackageVersion( + pkgsMeta: Record, + name: string, + value: string, + catalog: Record, +) { + if (value.includes('catalog:')) { + return catalog[name]; + } + + if (value.includes('workspace')) { + return pkgsMeta[name]; + } + + return value; +} + +async function resolveMonorepoDependencies() { + const { packages } = await getPackages(); + const manifest = await readWorkspaceManifest(findMonorepoRoot()); + const catalog = manifest?.catalog || {}; + + const resultDevDependencies: Record = {}; + const resultDependencies: Record = {}; + const pkgsMeta: Record = {}; + + for (const { packageJson } of packages) { + pkgsMeta[packageJson.name] = packageJson.version; + } + + for (const { packageJson } of packages) { + const { dependencies = {}, devDependencies = {} } = packageJson; + for (const [key, value] of Object.entries(dependencies)) { + resultDependencies[key] = resolvePackageVersion( + pkgsMeta, + key, + value, + catalog, + ); + } + for (const [key, value] of Object.entries(devDependencies)) { + resultDevDependencies[key] = resolvePackageVersion( + pkgsMeta, + key, + value, + catalog, + ); + } + } + return { + dependencies: resultDependencies, + devDependencies: resultDevDependencies, + }; +} + +/** + * 用于注入项目信息 + */ +async function viteMetadataPlugin( + root = process.cwd(), +): Promise { + const { author, description, homepage, license, version } = + await readPackageJSON(root); + + const buildTime = dateUtil().format('YYYY-MM-DD HH:mm:ss'); + + return { + async config() { + const { dependencies, devDependencies } = + await resolveMonorepoDependencies(); + + const isAuthorObject = typeof author === 'object'; + const authorName = isAuthorObject ? author.name : author; + const authorEmail = isAuthorObject ? author.email : null; + const authorUrl = isAuthorObject ? author.url : null; + + return { + define: { + __VBEN_ADMIN_METADATA__: JSON.stringify({ + authorEmail, + authorName, + authorUrl, + buildTime, + dependencies, + description, + devDependencies, + homepage, + license, + version, + }), + 'import.meta.env.VITE_APP_VERSION': JSON.stringify(version), + }, + }; + }, + enforce: 'post', + name: 'vite:inject-metadata', + }; +} + +export { viteMetadataPlugin }; diff --git a/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/license.ts b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/license.ts new file mode 100644 index 0000000..7733288 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/license.ts @@ -0,0 +1,55 @@ +import type { PluginOption } from 'vite'; + +import { EOL } from 'node:os'; + +import { dateUtil, readPackageJSON } from '@vben/node-utils'; + +/** + * 用于注入版权信息 + * @returns + */ +async function viteLicensePlugin( + root = process.cwd(), +): Promise { + const { + description = '', + homepage = '', + version = '', + } = await readPackageJSON(root); + + return { + apply: 'build', + enforce: 'post', + generateBundle: { + handler(_options, bundle) { + const date = dateUtil().format('YYYY-MM-DD '); + const copyrightText = `/*! + * Vben Admin + * Version: ${version} + * Author: vben + * Copyright (C) 2024 Vben + * License: MIT License + * Description: ${description} + * Date Created: ${date} + * Homepage: ${homepage} + * Contact: ann.vben@gmail.com +*/ + `.trim(); + + for (const [, fileContent] of Object.entries(bundle)) { + if (fileContent.type === 'chunk' && fileContent.isEntry) { + // 插入版权信息 + const content = fileContent.code; + const updatedContent = `${copyrightText}${EOL}${content}`; + // 更新bundle + fileContent.code = updatedContent; + } + } + }, + order: 'post', + }, + name: 'vite:license', + }; +} + +export { viteLicensePlugin }; diff --git a/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/nitro-mock.ts b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/nitro-mock.ts new file mode 100644 index 0000000..60d7327 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/nitro-mock.ts @@ -0,0 +1,98 @@ +import type { PluginOption } from 'vite'; + +import type { NitroMockPluginOptions } from '../typing'; + +import { colors, consola, getPackage } from '@vben/node-utils'; + +import getPort from 'get-port'; +import { build, createDevServer, createNitro, prepare } from 'nitropack'; + +const hmrKeyRe = /^runtimeConfig\.|routeRules\./; + +export const viteNitroMockPlugin = ({ + mockServerPackage = '@vben/backend-mock', + port = 5320, + verbose = true, +}: NitroMockPluginOptions = {}): PluginOption => { + return { + async configureServer(server) { + const availablePort = await getPort({ port }); + if (availablePort !== port) { + return; + } + + const pkg = await getPackage(mockServerPackage); + if (!pkg) { + consola.log( + `Package ${mockServerPackage} not found. Skip mock server.`, + ); + return; + } + + runNitroServer(pkg.dir, port, verbose); + + const _printUrls = server.printUrls; + server.printUrls = () => { + _printUrls(); + + consola.log( + ` ${colors.green('➜')} ${colors.bold('Nitro Mock Server')}: ${colors.cyan(`http://localhost:${port}/api`)}`, + ); + }; + }, + enforce: 'pre', + name: 'vite:mock-server', + }; +}; + +async function runNitroServer(rootDir: string, port: number, verbose: boolean) { + let nitro: any; + const reload = async () => { + if (nitro) { + consola.info('Restarting dev server...'); + if ('unwatch' in nitro.options._c12) { + await nitro.options._c12.unwatch(); + } + await nitro.close(); + } + nitro = await createNitro( + { + dev: true, + preset: 'nitro-dev', + rootDir, + }, + { + c12: { + async onUpdate({ getDiff, newConfig }) { + const diff = getDiff(); + if (diff.length === 0) { + return; + } + verbose && + consola.info( + `Nitro config updated:\n${diff + .map((entry) => ` ${entry.toString()}`) + .join('\n')}`, + ); + await (diff.every((e) => hmrKeyRe.test(e.key)) + ? nitro.updateConfig(newConfig.config) + : reload()); + }, + }, + watch: true, + }, + ); + nitro.hooks.hookOnce('restart', reload); + + const server = createDevServer(nitro); + await server.listen(port, { showURL: false }); + await prepare(nitro); + await build(nitro); + + if (verbose) { + console.log(''); + consola.success(colors.bold(colors.green('Nitro Mock Server started.'))); + } + }; + return await reload(); +} diff --git a/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/print.ts b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/print.ts new file mode 100644 index 0000000..0146b8a --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/print.ts @@ -0,0 +1,28 @@ +import type { PluginOption } from 'vite'; + +import type { PrintPluginOptions } from '../typing'; + +import { colors } from '@vben/node-utils'; + +export const vitePrintPlugin = ( + options: PrintPluginOptions = {}, +): PluginOption => { + const { infoMap = {} } = options; + + return { + configureServer(server) { + const _printUrls = server.printUrls; + server.printUrls = () => { + _printUrls(); + + for (const [key, value] of Object.entries(infoMap)) { + console.log( + ` ${colors.green('➜')} ${colors.bold(key)}: ${colors.cyan(value)}`, + ); + } + }; + }, + enforce: 'pre', + name: 'vite:print-info', + }; +}; diff --git a/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/tailwind-reference.ts b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/tailwind-reference.ts new file mode 100644 index 0000000..8646049 --- /dev/null +++ b/deploy/fba/fba-ui-src/internal/vite-config/src/plugins/tailwind-reference.ts @@ -0,0 +1,40 @@ +import type { Plugin } from 'vite'; + +const REFERENCE_LINE = '@reference "@vben/tailwind-config/theme";\n'; + +/** + * Auto-inject @reference into Vue SFC `; + + // 要更新的CSS变量和它们的新值 + const updatedVariables = { + fontSize: '16px', + primaryColor: 'blue', + secondaryColor: 'green', + }; + + // 调用函数来更新CSS变量 + updateCSSVariables(updatedVariables, 'custom-styles'); + + // 获取更新后的样式内容 + const styleElement = document.querySelector('#custom-styles'); + const updatedStyleContent = styleElement ? styleElement.textContent : ''; + + // 检查更新后的样式内容是否包含正确的更新值 + expect( + updatedStyleContent?.includes('primaryColor: blue;') && + updatedStyleContent?.includes('secondaryColor: green;') && + updatedStyleContent?.includes('fontSize: 16px;'), + ).toBe(true); +}); + +it('updateCSSVariables should support a custom selector', () => { + document.head.innerHTML = ``; + + // 使用自定义选择器(如 TDesign 的 theme-mode 选择器)更新 CSS 变量 + updateCSSVariables( + { '--td-brand-color': 'rgb(0, 82, 217)' }, + 'tdesign-styles', + ":root[theme-mode='dark']", + ); + + const styleElement = document.querySelector('#tdesign-styles'); + const content = styleElement?.textContent ?? ''; + + // 选择器与变量都应正确写入 + expect(content.startsWith(":root[theme-mode='dark'] {")).toBe(true); + expect(content.includes('--td-brand-color: rgb(0, 82, 217);')).toBe(true); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/util.test.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/util.test.ts new file mode 100644 index 0000000..ccdeb96 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/util.test.ts @@ -0,0 +1,158 @@ +import { describe, expect, it } from 'vitest'; + +import { bindMethods, getNestedValue } from '../util'; + +class TestClass { + public value: string; + + constructor(value: string) { + this.value = value; + bindMethods(this); // 调用通用方法 + } + + getValue() { + return this.value; + } + + setValue(newValue: string) { + this.value = newValue; + } +} + +describe('bindMethods', () => { + it('should bind methods to the instance correctly', () => { + const instance = new TestClass('initial'); + + // 解构方法 + const { getValue } = instance; + + // 检查 getValue 是否能正确调用,并且 this 绑定了 instance + expect(getValue()).toBe('initial'); + }); + + it('should bind multiple methods', () => { + const instance = new TestClass('initial'); + + const { getValue, setValue } = instance; + + // 检查 getValue 和 setValue 方法是否正确绑定了 this + setValue('newValue'); + expect(getValue()).toBe('newValue'); + }); + + it('should not bind non-function properties', () => { + const instance = new TestClass('initial'); + + // 检查普通属性是否保持原样 + expect(instance.value).toBe('initial'); + }); + + it('should not bind constructor method', () => { + const instance = new TestClass('test'); + + // 检查 constructor 是否没有被绑定 + expect(instance.constructor.name).toBe('TestClass'); + }); + + it('should not bind getter/setter properties', () => { + class TestWithGetterSetter { + get value() { + return this._value; + } + + set value(newValue: string) { + this._value = newValue; + } + + private _value: string = 'test'; + + constructor() { + bindMethods(this); + } + } + + const instance = new TestWithGetterSetter(); + const { value } = instance; + + // Getter 和 setter 不应被绑定 + expect(value).toBe('test'); + }); +}); + +describe('getNestedValue', () => { + interface UserProfile { + age: number; + name: string; + } + + interface UserSettings { + theme: string; + } + + interface Data { + user: { + profile: UserProfile; + settings: UserSettings; + }; + } + + const data: Data = { + user: { + profile: { + age: 25, + name: 'Alice', + }, + settings: { + theme: 'dark', + }, + }, + }; + + it('should get a nested value when the path is valid', () => { + const result = getNestedValue(data, 'user.profile.name'); + expect(result).toBe('Alice'); + }); + + it('should return undefined for non-existent property', () => { + const result = getNestedValue(data, 'user.profile.gender'); + expect(result).toBeUndefined(); + }); + + it('should return undefined when accessing a non-existent deep path', () => { + const result = getNestedValue(data, 'user.nonexistent.field'); + expect(result).toBeUndefined(); + }); + + it('should return undefined if a middle level is undefined', () => { + const result = getNestedValue({ user: undefined }, 'user.profile.name'); + expect(result).toBeUndefined(); + }); + + it('should return the correct value for a nested setting', () => { + const result = getNestedValue(data, 'user.settings.theme'); + expect(result).toBe('dark'); + }); + + it('should work for a single-level path', () => { + const result = getNestedValue({ a: 1, b: 2 }, 'b'); + expect(result).toBe(2); + }); + + it('should throw if path is empty', () => { + expect(() => getNestedValue(data, '')).toThrow( + 'Path must be a non-empty string', + ); + }); + + it('should handle paths with array indexes', () => { + const complexData = { list: [{ name: 'Item1' }, { name: 'Item2' }] }; + const result = getNestedValue(complexData, 'list.1.name'); + expect(result).toBe('Item2'); + }); + + it('should return undefined when accessing an out-of-bounds array index', () => { + const complexData = { list: [{ name: 'Item1' }] }; + const result = getNestedValue(complexData, 'list.2.name'); + expect(result).toBeUndefined(); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/window.test.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/window.test.ts new file mode 100644 index 0000000..ebb04bb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/__tests__/window.test.ts @@ -0,0 +1,33 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { openWindow } from '../window'; + +describe('openWindow', () => { + // 保存原始的 window.open 函数 + let originalOpen: typeof window.open; + + beforeEach(() => { + originalOpen = window.open; + }); + + afterEach(() => { + window.open = originalOpen; + }); + + it('should call window.open with correct arguments', () => { + const url = 'https://example.com'; + const options = { noopener: true, noreferrer: true, target: '_blank' }; + + window.open = vi.fn(); + + // 调用函数 + openWindow(url, options); + + // 验证 window.open 是否被正确地调用 + expect(window.open).toHaveBeenCalledWith( + url, + options.target, + 'noopener=yes,noreferrer=yes', + ); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/cn.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/cn.ts new file mode 100644 index 0000000..3a2f977 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/cn.ts @@ -0,0 +1,10 @@ +import type { ClassValue } from 'clsx'; + +import { clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export { cn }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/date.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/date.ts new file mode 100644 index 0000000..784b977 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/date.ts @@ -0,0 +1,78 @@ +import dayjs from 'dayjs'; +import timezone from 'dayjs/plugin/timezone.js'; +import utc from 'dayjs/plugin/utc.js'; + +dayjs.extend(utc); +dayjs.extend(timezone); + +type FormatDate = Date | dayjs.Dayjs | number | string; + +type Format = + | 'HH' + | 'HH:mm' + | 'HH:mm:ss' + | 'YYYY' + | 'YYYY-MM' + | 'YYYY-MM-DD' + | 'YYYY-MM-DD HH' + | 'YYYY-MM-DD HH:mm' + | 'YYYY-MM-DD HH:mm:ss' + | (string & {}); + +export function formatDate(time?: FormatDate, format: Format = 'YYYY-MM-DD') { + if (time === undefined || time === null || time === '') { + return ''; + } + try { + const date = dayjs.isDayjs(time) ? time : dayjs(time); + if (!date.isValid()) { + throw new Error('Invalid date'); + } + return date.tz().format(format); + } catch (error) { + console.error(`Error formatting date: ${error}`); + return String(time ?? ''); + } +} + +export function formatDateTime(time?: FormatDate) { + return formatDate(time, 'YYYY-MM-DD HH:mm:ss'); +} + +export function isDate(value: any): value is Date { + return value instanceof Date; +} + +export function isDayjsObject(value: any): value is dayjs.Dayjs { + return dayjs.isDayjs(value); +} + +/** + * 获取当前时区 + * @returns 当前时区 + */ +export const getSystemTimezone = () => { + return dayjs.tz.guess(); +}; + +/** + * 自定义设置的时区 + */ +let currentTimezone = getSystemTimezone(); + +/** + * 设置默认时区 + * @param timezone + */ +export const setCurrentTimezone = (timezone?: string) => { + currentTimezone = timezone || getSystemTimezone(); + dayjs.tz.setDefault(currentTimezone); +}; + +/** + * 获取设置的时区 + * @returns 设置的时区 + */ +export const getCurrentTimezone = () => { + return currentTimezone; +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/diff.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/diff.ts new file mode 100644 index 0000000..449214d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/diff.ts @@ -0,0 +1,96 @@ +// type Diff = T; + +// 比较两个数组是否相等 + +function arraysEqual(a: T[], b: T[]): boolean { + if (a.length !== b.length) return false; + const counter = new Map(); + for (const value of a) { + counter.set(value, (counter.get(value) || 0) + 1); + } + for (const value of b) { + const count = counter.get(value); + if (count === undefined || count === 0) { + return false; + } + counter.set(value, count - 1); + } + return true; +} + +// 深度对比两个值 +// function deepEqual(oldVal: T, newVal: T): boolean { +// if ( +// typeof oldVal === 'object' && +// oldVal !== null && +// typeof newVal === 'object' && +// newVal !== null +// ) { +// return Array.isArray(oldVal) && Array.isArray(newVal) +// ? arraysEqual(oldVal, newVal) +// : diff(oldVal as any, newVal as any) === null; +// } else { +// return oldVal === newVal; +// } +// } + +// // diff 函数 +// function diff( +// oldObj: T, +// newObj: T, +// ignoreFields: (keyof T)[] = [], +// ): { [K in keyof T]?: Diff } | null { +// const difference: { [K in keyof T]?: Diff } = {}; + +// for (const key in oldObj) { +// if (ignoreFields.includes(key)) continue; +// const oldValue = oldObj[key]; +// const newValue = newObj[key]; + +// if (!deepEqual(oldValue, newValue)) { +// difference[key] = newValue; +// } +// } + +// return Object.keys(difference).length === 0 ? null : difference; +// } + +type DiffResult = Partial<{ + [K in keyof T]: T[K] extends object ? DiffResult : T[K]; +}>; + +function diff>(obj1: T, obj2: T): DiffResult { + function findDifferences(o1: any, o2: any): any { + if (Array.isArray(o1) && Array.isArray(o2)) { + if (!arraysEqual(o1, o2)) { + return o2; + } + return undefined; + } + + if ( + typeof o1 === 'object' && + typeof o2 === 'object' && + o1 !== null && + o2 !== null + ) { + const diffResult: any = {}; + + const keys = new Set([...Object.keys(o1), ...Object.keys(o2)]); + keys.forEach((key) => { + const valueDiff = findDifferences(o1[key], o2[key]); + if (valueDiff !== undefined) { + diffResult[key] = valueDiff; + } + }); + + return Object.keys(diffResult).length > 0 ? diffResult : undefined; + } + + return o1 === o2 ? undefined : o2; + } + + return findDifferences(obj1, obj2); +} + +export { arraysEqual, diff }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/dom.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/dom.ts new file mode 100644 index 0000000..35a7e5f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/dom.ts @@ -0,0 +1,107 @@ +export interface VisibleDomRect { + bottom: number; + height: number; + left: number; + right: number; + top: number; + width: number; +} + +/** + * 获取元素可见信息 + * @param element + */ +export function getElementVisibleRect( + element?: HTMLElement | null | undefined, +): VisibleDomRect { + if (!element) { + return { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + }; + } + const rect = element.getBoundingClientRect(); + const viewHeight = Math.max( + document.documentElement.clientHeight, + window.innerHeight, + ); + + const top = Math.max(rect.top, 0); + const bottom = Math.min(rect.bottom, viewHeight); + + const viewWidth = Math.max( + document.documentElement.clientWidth, + window.innerWidth, + ); + + const left = Math.max(rect.left, 0); + const right = Math.min(rect.right, viewWidth); + + // 如果元素完全不可见,则返回一个空的矩形 + if (top >= viewHeight || bottom <= 0 || left >= viewWidth || right <= 0) { + return { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + }; + } + + return { + bottom, + height: Math.max(0, bottom - top), + left, + right, + top, + width: Math.max(0, right - left), + }; +} + +export function getScrollbarWidth() { + const scrollDiv = document.createElement('div'); + + scrollDiv.style.visibility = 'hidden'; + scrollDiv.style.overflow = 'scroll'; + scrollDiv.style.position = 'absolute'; + scrollDiv.style.top = '-9999px'; + + document.body.append(scrollDiv); + + const innerDiv = document.createElement('div'); + scrollDiv.append(innerDiv); + + const scrollbarWidth = scrollDiv.offsetWidth - innerDiv.offsetWidth; + + scrollDiv.remove(); + return scrollbarWidth; +} + +export function needsScrollbar() { + const doc = document.documentElement; + const body = document.body; + + // 检查 body 的 overflow-y 样式 + const overflowY = window.getComputedStyle(body).overflowY; + + // 如果明确设置了需要滚动条的样式 + if (overflowY === 'scroll' || overflowY === 'auto') { + return doc.scrollHeight > window.innerHeight; + } + + // 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断 + return doc.scrollHeight > window.innerHeight; +} + +export function triggerWindowResize(): void { + // 创建一个新的 resize 事件 + const resizeEvent = new Event('resize'); + + // 触发 window 的 resize 事件 + window.dispatchEvent(resizeEvent); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/download.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/download.ts new file mode 100644 index 0000000..6f38ee5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/download.ts @@ -0,0 +1,157 @@ +import { openWindow } from './window'; + +interface DownloadOptions { + fileName?: string; + source: T; + target?: string; +} + +const DEFAULT_FILENAME = 'downloaded_file'; + +/** + * 通过 URL 下载文件,支持跨域 + * @throws {Error} - 当下载失败时抛出错误 + */ +export async function downloadFileFromUrl({ + fileName, + source, + target = '_blank', +}: DownloadOptions): Promise { + if (!source || typeof source !== 'string') { + throw new Error('Invalid URL.'); + } + + const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); + const isSafari = window.navigator.userAgent.toLowerCase().includes('safari'); + + if (/iP/.test(window.navigator.userAgent)) { + console.error('Your browser does not support download!'); + return; + } + + if (isChrome || isSafari) { + triggerDownload(source, resolveFileName(source, fileName)); + return; + } + if (!source.includes('?')) { + source += '?download'; + } + + openWindow(source, { target }); +} + +/** + * 通过 Base64 下载文件 + */ +export function downloadFileFromBase64({ fileName, source }: DownloadOptions) { + if (!source || typeof source !== 'string') { + throw new Error('Invalid Base64 data.'); + } + + const resolvedFileName = fileName || DEFAULT_FILENAME; + triggerDownload(source, resolvedFileName); +} + +/** + * 通过图片 URL 下载图片文件 + */ +export async function downloadFileFromImageUrl({ + fileName, + source, +}: DownloadOptions) { + const base64 = await urlToBase64(source); + downloadFileFromBase64({ fileName, source: base64 }); +} + +/** + * 通过 Blob 下载文件 + */ +export function downloadFileFromBlob({ + fileName = DEFAULT_FILENAME, + source, +}: DownloadOptions): void { + if (!(source instanceof Blob)) { + throw new TypeError('Invalid Blob data.'); + } + + const url = URL.createObjectURL(source); + triggerDownload(url, fileName); +} + +/** + * 下载文件,支持 Blob、字符串和其他 BlobPart 类型 + */ +export function downloadFileFromBlobPart({ + fileName = DEFAULT_FILENAME, + source, +}: DownloadOptions): void { + // 如果 data 不是 Blob,则转换为 Blob + const blob = + source instanceof Blob + ? source + : new Blob([source], { type: 'application/octet-stream' }); + + // 创建对象 URL 并触发下载 + const url = URL.createObjectURL(blob); + triggerDownload(url, fileName); +} + +/** + * img url to base64 + * @param url + */ +export function urlToBase64(url: string, mineType?: string): Promise { + return new Promise((resolve, reject) => { + let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null; + const ctx = canvas?.getContext('2d'); + const img = new Image(); + img.crossOrigin = ''; + img.addEventListener('load', () => { + if (!canvas || !ctx) { + return reject(new Error('Failed to create canvas.')); + } + canvas.height = img.height; + canvas.width = img.width; + ctx.drawImage(img, 0, 0); + const dataURL = canvas.toDataURL(mineType || 'image/png'); + canvas = null; + resolve(dataURL); + }); + img.src = url; + }); +} + +/** + * 通用下载触发函数 + * @param href - 文件下载的 URL + * @param fileName - 下载文件的名称,如果未提供则自动识别 + * @param revokeDelay - 清理 URL 的延迟时间 (毫秒) + */ +export function triggerDownload( + href: string, + fileName: string | undefined, + revokeDelay: number = 100, +): void { + const defaultFileName = 'downloaded_file'; + const finalFileName = fileName || defaultFileName; + + const link = document.createElement('a'); + link.href = href; + link.download = finalFileName; + link.style.display = 'none'; + + if (link.download === undefined) { + link.setAttribute('target', '_blank'); + } + + document.body.append(link); + link.click(); + link.remove(); + + // 清理临时 URL 以释放内存 + setTimeout(() => URL.revokeObjectURL(href), revokeDelay); +} + +function resolveFileName(url: string, fileName?: string): string { + return fileName || url.slice(url.lastIndexOf('/') + 1) || DEFAULT_FILENAME; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/index.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/index.ts new file mode 100644 index 0000000..991b741 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/index.ts @@ -0,0 +1,21 @@ +export * from './cn'; +export * from './date'; +export * from './diff'; +export * from './dom'; +export * from './download'; +export * from './inference'; +export * from './letter'; +export * from './merge'; +export * from './nprogress'; +export * from './resources'; +export * from './stack'; +export * from './state-handler'; +export * from './to'; +export * from './tree'; +export * from './unique'; +export * from './update-css-variables'; +export * from './util'; +export * from './window'; +export { debounce, get, isEqual, set } from 'es-toolkit/compat'; +// export { cloneDeep } from 'es-toolkit/object'; +export { default as cloneDeep } from 'lodash.clonedeep'; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/inference.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/inference.ts new file mode 100644 index 0000000..a9ace62 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/inference.ts @@ -0,0 +1,164 @@ +import { isFunction, isObject, isString } from '@vue/shared'; + +/** + * 检查传入的值是否为undefined。 + * + * @param {unknown} value 要检查的值。 + * @returns {boolean} 如果值是undefined,返回true,否则返回false。 + */ +function isUndefined(value?: unknown): value is undefined { + return value === undefined; +} + +/** + * 检查传入的值是否为boolean + * @param value + * @returns 如果值是布尔值,返回true,否则返回false。 + */ +function isBoolean(value: unknown): value is boolean { + return typeof value === 'boolean'; +} + +/** + * 检查传入的值是否为空。 + * + * 以下情况将被认为是空: + * - 值为null。 + * - 值为undefined。 + * - 值为一个空字符串。 + * - 值为一个长度为0的数组。 + * - 值为一个没有元素的Map或Set。 + * - 值为一个没有属性的对象。 + * + * @param {T} value 要检查的值。 + * @returns {boolean} 如果值为空,返回true,否则返回false。 + */ +function isEmpty(value?: T): value is T { + if (value === null || value === undefined) { + return true; + } + + if (Array.isArray(value) || isString(value)) { + return value.length === 0; + } + + if (value instanceof Map || value instanceof Set) { + return value.size === 0; + } + + if (isObject(value)) { + return Object.keys(value).length === 0; + } + + return false; +} + +/** + * 检查传入的字符串是否为有效的HTTP或HTTPS URL。 + * + * @param {string} url 要检查的字符串。 + * @return {boolean} 如果字符串是有效的HTTP或HTTPS URL,返回true,否则返回false。 + */ +function isHttpUrl(url?: string): boolean { + if (!url) { + return false; + } + // 使用正则表达式测试URL是否以http:// 或 https:// 开头 + const httpRegex = /^https?:\/\/.*$/; + return httpRegex.test(url); +} + +/** + * 检查传入的值是否为window对象。 + * + * @param {any} value 要检查的值。 + * @returns {boolean} 如果值是window对象,返回true,否则返回false。 + */ +function isWindow(value: any): value is Window { + return ( + typeof window !== 'undefined' && value !== null && value === value.window + ); +} + +/** + * 检查当前运行环境是否为Mac OS。 + * + * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。 + * 如果userAgent字符串中包含"macintosh"或"mac os x"(不区分大小写),则认为当前环境是Mac OS。 + * + * @returns {boolean} 如果当前环境是Mac OS,返回true,否则返回false。 + */ +function isMacOs(): boolean { + const macRegex = /macintosh|mac os x/i; + return macRegex.test(navigator.userAgent); +} + +/** + * 检查当前运行环境是否为Windows OS。 + * + * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。 + * 如果userAgent字符串中包含"windows"或"win32"(不区分大小写),则认为当前环境是Windows OS。 + * + * @returns {boolean} 如果当前环境是Windows OS,返回true,否则返回false。 + */ +function isWindowsOs(): boolean { + const windowsRegex = /windows|win32/i; + return windowsRegex.test(navigator.userAgent); +} + +/** + * 检查传入的值是否为数字 + * @param value + */ +function isNumber(value: any): value is number { + return typeof value === 'number' && Number.isFinite(value); +} + +/** + * Returns the first value in the provided list that is neither `null` nor `undefined`. + * + * This function iterates over the input values and returns the first one that is + * not strictly equal to `null` or `undefined`. If all values are either `null` or + * `undefined`, it returns `undefined`. + * + * @template T - The type of the input values. + * @param {...(T | null | undefined)[]} values - A list of values to evaluate. + * @returns {T | undefined} - The first value that is not `null` or `undefined`, or `undefined` if none are found. + * + * @example + * // Returns 42 because it is the first non-null, non-undefined value. + * getFirstNonNullOrUndefined(undefined, null, 42, 'hello'); // 42 + * + * @example + * // Returns 'hello' because it is the first non-null, non-undefined value. + * getFirstNonNullOrUndefined(null, undefined, 'hello', 123); // 'hello' + * + * @example + * // Returns undefined because all values are either null or undefined. + * getFirstNonNullOrUndefined(undefined, null); // undefined + */ +function getFirstNonNullOrUndefined( + ...values: (null | T | undefined)[] +): T | undefined { + for (const value of values) { + if (value !== undefined && value !== null) { + return value; + } + } + return undefined; +} + +export { + getFirstNonNullOrUndefined, + isBoolean, + isEmpty, + isFunction, + isHttpUrl, + isMacOs, + isNumber, + isObject, + isString, + isUndefined, + isWindow, + isWindowsOs, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/letter.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/letter.ts new file mode 100644 index 0000000..65a1c22 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/letter.ts @@ -0,0 +1,47 @@ +/** + * 将字符串的首字母大写 + * @param string + */ +function capitalizeFirstLetter(string: string): string { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +/** + * 将字符串的首字母转换为小写。 + * + * @param str 要转换的字符串 + * @returns 首字母小写的字符串 + */ +function toLowerCaseFirstLetter(str: string): string { + if (!str) return str; // 如果字符串为空,直接返回 + return str.charAt(0).toLowerCase() + str.slice(1); +} + +/** + * 生成驼峰命名法的键名 + * @param key + * @param parentKey + */ +function toCamelCase(key: string, parentKey: string): string { + if (!parentKey) { + return key; + } + return parentKey + key.charAt(0).toUpperCase() + key.slice(1); +} + +function kebabToCamelCase(str: string): string { + return str + .split('-') + .filter(Boolean) + .map((word, index) => + index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1), + ) + .join(''); +} + +export { + capitalizeFirstLetter, + kebabToCamelCase, + toCamelCase, + toLowerCaseFirstLetter, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/merge.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/merge.ts new file mode 100644 index 0000000..4bf79eb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/merge.ts @@ -0,0 +1,10 @@ +import { createDefu } from 'defu'; + +export { createDefu as createMerge, defu as merge } from 'defu'; + +export const mergeWithArrayOverride = createDefu((originObj, key, updates) => { + if (Array.isArray(originObj[key]) && Array.isArray(updates)) { + originObj[key] = updates; + return true; + } +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/nprogress.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/nprogress.ts new file mode 100644 index 0000000..8e8fe2e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/nprogress.ts @@ -0,0 +1,43 @@ +import type NProgress from 'nprogress'; + +// 创建一个NProgress实例的变量,初始值为null +let nProgressInstance: null | typeof NProgress = null; + +/** + * 动态加载NProgress库,并进行配置。 + * 此函数首先检查是否已经加载过NProgress库,如果已经加载过,则直接返回NProgress实例。 + * 否则,动态导入NProgress库,进行配置,然后返回NProgress实例。 + * + * @returns NProgress实例的Promise对象。 + */ +async function loadNprogress() { + if (nProgressInstance) { + return nProgressInstance; + } + nProgressInstance = await import('nprogress'); + nProgressInstance.configure({ + showSpinner: true, + speed: 300, + }); + return nProgressInstance; +} + +/** + * 开始显示进度条。 + * 此函数首先加载NProgress库,然后调用NProgress的start方法开始显示进度条。 + */ +async function startProgress() { + const nprogress = await loadNprogress(); + nprogress?.start(); +} + +/** + * 停止显示进度条,并隐藏进度条。 + * 此函数首先加载NProgress库,然后调用NProgress的done方法停止并隐藏进度条。 + */ +async function stopProgress() { + const nprogress = await loadNprogress(); + nprogress?.done(); +} + +export { startProgress, stopProgress }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/resources.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/resources.ts new file mode 100644 index 0000000..c5afa7f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/resources.ts @@ -0,0 +1,21 @@ +/** + * 加载js文件 + * @param src js文件地址 + */ +function loadScript(src: string) { + return new Promise((resolve, reject) => { + if (document.querySelector(`script[src="${src}"]`)) { + // 如果已经加载过,直接 resolve + return resolve(); + } + const script = document.createElement('script'); + script.src = src; + script.addEventListener('load', () => resolve()); + script.addEventListener('error', () => + reject(new Error(`Failed to load script: ${src}`)), + ); + document.head.append(script); + }); +} + +export { loadScript }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/stack.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/stack.ts new file mode 100644 index 0000000..d8f5d4a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/stack.ts @@ -0,0 +1,103 @@ +/** + * @zh_CN 栈数据结构 + */ +export class Stack { + /** + * @zh_CN 栈内元素数量 + */ + get size() { + return this.items.length; + } + /** + * @zh_CN 是否去重 + */ + private readonly dedup: boolean; + /** + * @zh_CN 栈内元素 + */ + private items: T[] = []; + + /** + * @zh_CN 栈的最大容量 + */ + private readonly maxSize?: number; + + constructor(dedup = true, maxSize?: number) { + this.maxSize = maxSize; + this.dedup = dedup; + } + + /** + * @zh_CN 清空栈内元素 + */ + clear() { + this.items.length = 0; + } + + /** + * @zh_CN 查看栈顶元素 + * @returns 栈顶元素 + */ + peek(): T | undefined { + return this.items[this.items.length - 1]; + } + + /** + * @zh_CN 出栈 + * @returns 栈顶元素 + */ + pop(): T | undefined { + return this.items.pop(); + } + + /** + * @zh_CN 入栈 + * @param items 要入栈的元素 + */ + push(...items: T[]) { + items.forEach((item) => { + // 去重 + if (this.dedup) { + const index = this.items.indexOf(item); + if (index !== -1) { + this.items.splice(index, 1); + } + } + this.items.push(item); + if (this.maxSize && this.items.length > this.maxSize) { + this.items.splice(0, this.items.length - this.maxSize); + } + }); + } + /** + * @zh_CN 移除栈内元素 + * @param itemList 要移除的元素列表 + */ + remove(...itemList: T[]) { + this.items = this.items.filter((i) => !itemList.includes(i)); + } + /** + * @zh_CN 保留栈内元素 + * @param itemList 要保留的元素列表 + */ + retain(itemList: T[]) { + this.items = this.items.filter((i) => itemList.includes(i)); + } + + /** + * @zh_CN 转换为数组 + * @returns 栈内元素数组 + */ + toArray(): T[] { + return [...this.items]; + } +} + +/** + * @zh_CN 创建一个栈实例 + * @param dedup 是否去重 + * @param maxSize 栈的最大容量 + * @returns 栈实例 + */ +export const createStack = (dedup = true, maxSize?: number) => + new Stack(dedup, maxSize); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/state-handler.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/state-handler.ts new file mode 100644 index 0000000..a8bbe4f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/state-handler.ts @@ -0,0 +1,50 @@ +export class StateHandler { + private condition: boolean = false; + private rejectCondition: ((reason?: Error) => void) | null = null; + private resolveCondition: (() => void) | null = null; + + isConditionTrue(): boolean { + return this.condition; + } + + reset() { + this.condition = false; + this.clearPromises(); + } + + // 触发状态为 false 时,reject + setConditionFalse() { + this.condition = false; + if (this.rejectCondition) { + this.rejectCondition(new Error('Condition was set to false')); + this.clearPromises(); + } + } + + // 触发状态为 true 时,resolve + setConditionTrue() { + this.condition = true; + if (this.resolveCondition) { + this.resolveCondition(); + this.clearPromises(); + } + } + + // 返回一个 Promise,等待 condition 变为 true + waitForCondition(): Promise { + return new Promise((resolve, reject) => { + if (this.condition) { + resolve(); // 如果 condition 已经为 true,立即 resolve + } else { + this.resolveCondition = resolve; + this.rejectCondition = reject; + } + }); + } + + // 清理 resolve/reject 函数 + private clearPromises() { + this.resolveCondition = null; + this.rejectCondition = null; + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/to.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/to.ts new file mode 100644 index 0000000..6f25405 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/to.ts @@ -0,0 +1,21 @@ +/** + * @param { Readonly } promise + * @param {object=} errorExt - Additional Information you can pass to the err object + * @return { Promise } + */ +export async function to( + promise: Readonly>, + errorExt?: object, +): Promise<[null, T] | [U, undefined]> { + try { + const data = await promise; + const result: [null, T] = [null, data]; + return result; + } catch (error) { + if (errorExt) { + const parsedError = Object.assign({}, error, errorExt); + return [parsedError as U, undefined]; + } + return [error as U, undefined]; + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/tree.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/tree.ts new file mode 100644 index 0000000..f3056dc --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/tree.ts @@ -0,0 +1,125 @@ +interface TreeConfigOptions { + // 子属性的名称,默认为'children' + childProps: string; +} + +/** + * @zh_CN 遍历树形结构,并返回所有节点中指定的值。 + * @param tree 树形结构数组 + * @param getValue 获取节点值的函数 + * @param options 作为子节点数组的可选属性名称。 + * @returns 所有节点中指定的值的数组 + */ +function traverseTreeValues( + tree: T[], + getValue: (node: T) => V, + options?: TreeConfigOptions, +): V[] { + const result: V[] = []; + const { childProps } = options || { + childProps: 'children', + }; + + const dfs = (treeNode: T) => { + const value = getValue(treeNode); + result.push(value); + const children = (treeNode as Record)?.[childProps]; + if (!children) { + return; + } + if (children.length > 0) { + for (const child of children) { + dfs(child); + } + } + }; + + for (const treeNode of tree) { + dfs(treeNode); + } + return result.filter(Boolean); +} + +/** + * 根据条件过滤给定树结构的节点,并以原有顺序返回所有匹配节点的数组。 + * @param tree 要过滤的树结构的根节点数组。 + * @param filter 用于匹配每个节点的条件。 + * @param options 作为子节点数组的可选属性名称。 + * @returns 包含所有匹配节点的数组。 + */ +function filterTree>( + tree: T[], + filter: (node: T) => boolean, + options?: TreeConfigOptions, +): T[] { + const { childProps } = options || { + childProps: 'children', + }; + + const _filterTree = (nodes: T[]): T[] => { + return nodes.filter((node: Record) => { + if (filter(node as T)) { + if (node[childProps]) { + node[childProps] = _filterTree(node[childProps]); + } + return true; + } + return false; + }); + }; + + return _filterTree(tree); +} + +/** + * 根据条件重新映射给定树结构的节 + * @param tree 要过滤的树结构的根节点数组。 + * @param mapper 用于map每个节点的条件。 + * @param options 作为子节点数组的可选属性名称。 + */ +function mapTree>( + tree: T[], + mapper: (node: T) => V, + options?: TreeConfigOptions, +): V[] { + const { childProps } = options || { + childProps: 'children', + }; + return tree.map((node) => { + const mapperNode: Record = mapper(node); + if (mapperNode[childProps]) { + mapperNode[childProps] = mapTree(mapperNode[childProps], mapper, options); + } + return mapperNode as V; + }); +} + +/** + * 对树形结构数据进行递归排序 + * @param treeData - 树形数据数组 + * @param sortFunction - 排序函数,用于定义排序规则 + * @param options - 配置选项,包括子节点属性名 + * @returns 排序后的树形数据 + */ +function sortTree>( + treeData: T[], + sortFunction: (a: T, b: T) => number, + options?: TreeConfigOptions, +): T[] { + const { childProps } = options || { + childProps: 'children', + }; + + return treeData.toSorted(sortFunction).map((item) => { + const children = item[childProps]; + if (children && Array.isArray(children) && children.length > 0) { + return { + ...item, + [childProps]: sortTree(children, sortFunction, options), + }; + } + return item; + }); +} + +export { filterTree, mapTree, sortTree, traverseTreeValues }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/unique.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/unique.ts new file mode 100644 index 0000000..e81f972 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/unique.ts @@ -0,0 +1,15 @@ +/** + * 根据指定字段对对象数组进行去重 + * @param arr 要去重的对象数组 + * @param key 去重依据的字段名 + * @returns 去重后的对象数组 + */ +function uniqueByField(arr: T[], key: keyof T): T[] { + const seen = new Map(); + return arr.filter((item) => { + const value = item[key]; + return seen.has(value) ? false : (seen.set(value, item), true); + }); +} + +export { uniqueByField }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/update-css-variables.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/update-css-variables.ts new file mode 100644 index 0000000..296e2e5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/update-css-variables.ts @@ -0,0 +1,40 @@ +/** + * 更新 CSS 变量的函数 + * @param variables 要更新的 CSS 变量与其新值的映射 + * @param id 内联样式表的 id,便于复用与覆盖 + * @param selector CSS 变量挂载的选择器,默认 `:root`。 + * 对于像 TDesign 这种将变量定义在 `:root[theme-mode='dark']` 等更高优先级选择器下的组件库, + * 需要传入相同(或更高)优先级的选择器才能正确覆盖。 + */ +function updateCSSVariables( + variables: { [key: string]: string }, + id = '__vben-styles__', + selector = ':root', +): void { + // 获取或创建内联样式表元素 + const styleElement = + document.querySelector(`#${id}`) || document.createElement('style'); + + styleElement.id = id; + + // 构建要更新的 CSS 变量的样式文本 + let cssText = `${selector} {`; + for (const key in variables) { + if (Object.prototype.hasOwnProperty.call(variables, key)) { + cssText += `${key}: ${variables[key]};`; + } + } + cssText += '}'; + + // 将样式文本赋值给内联样式表 + styleElement.textContent = cssText; + + // 将内联样式表添加到文档头部 + if (!document.querySelector(`#${id}`)) { + setTimeout(() => { + document.head.append(styleElement); + }); + } +} + +export { updateCSSVariables }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/util.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/util.ts new file mode 100644 index 0000000..885eeaa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/util.ts @@ -0,0 +1,44 @@ +export function bindMethods(instance: T): void { + const prototype = Object.getPrototypeOf(instance); + const propertyNames = Object.getOwnPropertyNames(prototype); + + propertyNames.forEach((propertyName) => { + const descriptor = Object.getOwnPropertyDescriptor(prototype, propertyName); + const propertyValue = instance[propertyName as keyof T]; + + if ( + typeof propertyValue === 'function' && + propertyName !== 'constructor' && + descriptor && + !descriptor.get && + !descriptor.set + ) { + instance[propertyName as keyof T] = propertyValue.bind(instance); + } + }); +} + +/** + * 获取嵌套对象的字段值 + * @param obj - 要查找的对象 + * @param path - 用于查找字段的路径,使用小数点分隔 + * @returns 字段值,或者未找到时返回 undefined + */ +export function getNestedValue(obj: T, path: string): any { + if (typeof path !== 'string' || path.length === 0) { + throw new Error('Path must be a non-empty string'); + } + // 把路径字符串按 "." 分割成数组 + const keys = path.split('.') as (number | string)[]; + + let current: any = obj; + + for (const key of keys) { + if (current === null || current === undefined) { + return undefined; + } + current = current[key as keyof typeof current]; + } + + return current; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/window.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/window.ts new file mode 100644 index 0000000..2d8697d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/src/utils/window.ts @@ -0,0 +1,37 @@ +interface OpenWindowOptions { + noopener?: boolean; + noreferrer?: boolean; + target?: '_blank' | '_parent' | '_self' | '_top' | string; +} + +/** + * 新窗口打开URL。 + * + * @param url - 需要打开的网址。 + * @param options - 打开窗口的选项。 + */ +function openWindow(url: string, options: OpenWindowOptions = {}): void { + // 解构并设置默认值 + const { noopener = true, noreferrer = true, target = '_blank' } = options; + + // 基于选项创建特性字符串 + const features = [noopener && 'noopener=yes', noreferrer && 'noreferrer=yes'] + .filter(Boolean) + .join(','); + + // 打开窗口 + window.open(url, target, features); +} + +/** + * 在新窗口中打开路由。 + * @param path + */ +function openRouteInNewWindow(path: string) { + const { hash, origin } = location; + const fullPath = path.startsWith('/') ? path : `/${path}`; + const url = `${origin}${hash && !fullPath.startsWith('/#') ? '/#' : ''}${fullPath}`; + openWindow(url, { target: '_blank' }); +} + +export { openRouteInNewWindow, openWindow }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/base/shared/tsconfig.json new file mode 100644 index 0000000..f6860a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/library.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/shared/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/base/shared/tsdown.config.ts new file mode 100644 index 0000000..62be779 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/shared/tsdown.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: { + 'cache/index': 'src/cache/index.ts', + 'color/index': 'src/color/index.ts', + 'constants/index': 'src/constants/index.ts', + 'global-state': 'src/global-state.ts', + store: 'src/store.ts', + 'utils/index': 'src/utils/index.ts', + }, + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/package.json b/deploy/fba/fba-ui-src/packages/@core/base/typings/package.json new file mode 100644 index 0000000..b90f3df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/package.json @@ -0,0 +1,46 @@ +{ + "name": "@vben-core/typings", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/base/typings" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown" + }, + "files": [ + "dist", + "vue-router.d.ts" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + }, + "./vue-router": { + "types": "./vue-router.d.ts" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/app.d.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/app.d.ts new file mode 100644 index 0000000..dc6081b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/app.d.ts @@ -0,0 +1,126 @@ +type LayoutType = + | 'full-content' + | 'header-mixed-nav' + | 'header-nav' + | 'header-sidebar-nav' + | 'mixed-nav' + | 'sidebar-mixed-nav' + | 'sidebar-nav'; + +type ThemeModeType = 'auto' | 'dark' | 'light'; + +/** + * 按钮位置 + * user-dropdown 用户的下拉弹出框中 + * fixed 固定在右侧 + * header 顶栏 + * auto 自动 + */ +type PreferencesButtonPositionType = + | 'auto' + | 'fixed' + | 'header' + | 'user-dropdown'; + +type BuiltinThemeType = + | 'custom' + | 'deep-blue' + | 'deep-green' + | 'default' + | 'gray' + | 'green' + | 'neutral' + | 'orange' + | 'pink' + | 'red' + | 'rose' + | 'sky-blue' + | 'slate' + | 'stone' + | 'violet' + | 'yellow' + | 'zinc' + | (Record & string); + +type ContentCompactType = 'compact' | 'wide'; + +type LayoutHeaderModeType = 'auto' | 'auto-scroll' | 'fixed' | 'static'; +type LayoutHeaderMenuAlignType = 'center' | 'end' | 'start'; + +/** + * 登录过期模式 + * modal 弹窗模式 + * page 页面模式 + */ +type LoginExpiredModeType = 'modal' | 'page'; + +/** + * 面包屑样式 + * background 背景 + * normal 默认 + */ +type BreadcrumbStyleType = 'background' | 'normal'; + +/** + * 权限模式 + * backend 后端权限模式 + * frontend 前端权限模式 + * mixed 混合权限模式 + */ +type AccessModeType = 'backend' | 'frontend' | 'mixed'; + +/** + * 导航风格 + * plain 朴素 + * rounded 圆润 + */ +type NavigationStyleType = 'plain' | 'rounded'; + +/** + * 标签栏风格 + * brisk 轻快 + * card 卡片 + * chrome 谷歌 + * plain 朴素 + */ +type TabsStyleType = 'brisk' | 'card' | 'chrome' | 'plain'; + +/** + * 页面切换动画 + */ +type PageTransitionType = 'fade' | 'fade-down' | 'fade-slide' | 'fade-up'; + +/** + * 页面切换动画 + * panel-center 居中布局 + * panel-left 居左布局 + * panel-right 居右布局 + */ +type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right'; + +/** + * 时区选项 + */ +interface TimezoneOption { + label: string; + offset: number; + timezone: string; +} + +export type { + AccessModeType, + AuthPageLayoutType, + BreadcrumbStyleType, + BuiltinThemeType, + ContentCompactType, + LayoutHeaderMenuAlignType, + LayoutHeaderModeType, + LayoutType, + LoginExpiredModeType, + NavigationStyleType, + PageTransitionType, + PreferencesButtonPositionType, + TabsStyleType, + ThemeModeType, + TimezoneOption, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/basic.d.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/basic.d.ts new file mode 100644 index 0000000..35ed709 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/basic.d.ts @@ -0,0 +1,41 @@ +interface BasicOption { + label: string; + value: string; +} + +type SelectOption = BasicOption; + +type TabOption = BasicOption; + +interface BasicUserInfo { + /** + * 头像 + */ + avatar: string; + /** + * 用户昵称 + */ + realName: string; + /** + * 用户角色 + */ + roles?: string[]; + /** + * 用户id + */ + userId: string; + /** + * 用户名 + */ + username: string; +} + +type ClassType = + | Array + | boolean + | null + | object + | string + | undefined; + +export type { BasicOption, BasicUserInfo, ClassType, SelectOption, TabOption }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/helper.d.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/helper.d.ts new file mode 100644 index 0000000..737b615 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/helper.d.ts @@ -0,0 +1,150 @@ +import type { ComputedRef, MaybeRef } from 'vue'; + +/** + * 类型级递归中增加深度计数 + */ +type Increment = [...A, unknown]; +/** + * 深层递归所有属性为可选 + */ +type DeepPartial< + T, + D extends number = 10, + C extends unknown[] = [], +> = C['length'] extends D + ? T + : T extends object + ? { + [P in keyof T]?: DeepPartial>; + } + : T; + +/** + * 深层递归所有属性为只读 + */ +type DeepReadonly< + T, + D extends number = 10, + C extends unknown[] = [], +> = C['length'] extends D + ? T + : T extends object + ? { + readonly [P in keyof T]: DeepReadonly>; + } + : T; + +/** + * 任意类型的异步函数 + */ + +type AnyPromiseFunction = ( + ...arg: T +) => PromiseLike; + +/** + * 任意类型的普通函数 + */ +type AnyNormalFunction = (...arg: T) => R; + +/** + * 任意类型的函数 + */ +type AnyFunction = + | AnyNormalFunction + | AnyPromiseFunction; + +/** + * T | null 包装 + */ +type Nullable = null | T; + +/** + * T | Not null 包装 + */ +type NonNullable = T extends null | undefined ? never : T; + +/** + * 字符串类型对象 + */ +type Recordable = Record; + +/** + * 字符串类型对象(只读) + */ +interface ReadonlyRecordable { + readonly [key: string]: T; +} + +/** + * setTimeout 返回值类型 + */ +type TimeoutHandle = ReturnType; + +/** + * setInterval 返回值类型 + */ +type IntervalHandle = ReturnType; + +/** + * 也许它是一个计算的 ref,或者一个 getter 函数 + * + */ +type MaybeReadonlyRef = (() => T) | ComputedRef; + +/** + * 也许它是一个 ref,或者一个普通值,或者一个 getter 函数 + * + */ +type MaybeComputedRef = MaybeReadonlyRef | MaybeRef; + +type Merge = { + [K in keyof O | keyof T]: K extends keyof T + ? T[K] + : K extends keyof O + ? O[K] + : never; +}; + +/** + * T = [ + * { name: string; age: number; }, + * { sex: 'male' | 'female'; age: string } + * ] + * => + * MergeAll = { + * name: string; + * sex: 'male' | 'female'; + * age: string + * } + */ +type MergeAll< + T extends object[], + R extends object = Record, +> = T extends [infer F extends object, ...infer Rest extends object[]] + ? MergeAll> + : R; + +type EmitType = (name: Name, ...args: any[]) => void; + +type MaybePromise = Promise | T; + +export type { + AnyFunction, + AnyNormalFunction, + AnyPromiseFunction, + DeepPartial, + DeepReadonly, + EmitType, + IntervalHandle, + MaybeComputedRef, + MaybePromise, + MaybeReadonlyRef, + Merge, + MergeAll, + NonNullable, + Nullable, + ReadonlyRecordable, + Recordable, + TimeoutHandle, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/index.ts new file mode 100644 index 0000000..33c3566 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/index.ts @@ -0,0 +1,6 @@ +export type * from './app'; +export type * from './basic'; +export type * from './helper'; +export type * from './menu-record'; +export type * from './tabs'; +export type * from './vue-router'; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/menu-record.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/menu-record.ts new file mode 100644 index 0000000..8c45099 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/menu-record.ts @@ -0,0 +1,82 @@ +import type { Component } from 'vue'; +import type { RouteRecordRaw } from 'vue-router'; + +import type { Recordable } from './helper'; + +/** + * 扩展路由原始对象 + */ +type ExRouteRecordRaw = RouteRecordRaw & { + parent?: string; + parents?: string[]; + path?: any; +}; + +interface MenuRecordBadgeRaw { + /** + * 徽标 + */ + badge?: string; + /** + * 徽标类型 + */ + badgeType?: 'dot' | 'normal'; + /** + * 徽标颜色 + */ + badgeVariants?: 'destructive' | 'primary' | string; +} + +/** + * 菜单原始对象 + */ +interface MenuRecordRaw extends MenuRecordBadgeRaw { + /** + * 激活时的图标名 + */ + activeIcon?: string; + /** + * 子菜单 + */ + children?: MenuRecordRaw[]; + /** + * 是否禁用菜单 + * @default false + */ + disabled?: boolean; + /** + * 图标名 + */ + icon?: Component | string; + /** + * 菜单名 + */ + name: string; + /** + * 排序号 + */ + order?: number; + /** + * 父级路径 + */ + parent?: string; + /** + * 所有父级路径 + */ + parents?: string[]; + /** + * 菜单路径,唯一,可当作key + */ + path: string; + /** + * 菜单参数 + */ + query?: Recordable; + /** + * 是否显示菜单 + * @default true + */ + show?: boolean; +} + +export type { ExRouteRecordRaw, MenuRecordBadgeRaw, MenuRecordRaw }; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/tabs.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/tabs.ts new file mode 100644 index 0000000..58f7d26 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/tabs.ts @@ -0,0 +1,8 @@ +import type { RouteLocationNormalized } from 'vue-router'; + +export interface TabDefinition extends RouteLocationNormalized { + /** + * 标签页的key + */ + key?: string; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/src/vue-router.d.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/vue-router.d.ts new file mode 100644 index 0000000..d7a80fe --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/src/vue-router.d.ts @@ -0,0 +1,157 @@ +import type { Component } from 'vue'; +import type { Router, RouteRecordRaw } from 'vue-router'; + +interface RouteMeta { + /** + * 激活图标(菜单/tab) + */ + activeIcon?: string; + /** + * 当前激活的菜单,有时候不想激活现有菜单,需要激活父级菜单时使用 + */ + activePath?: string; + /** + * 是否固定标签页 + * @default false + */ + affixTab?: boolean; + /** + * 固定标签页的顺序 + * @default 0 + */ + affixTabOrder?: number; + /** + * 需要特定的角色标识才可以访问 + * @default [] + */ + authority?: string[]; + /** + * 徽标 + */ + badge?: string; + /** + * 徽标类型 + */ + badgeType?: 'dot' | 'normal'; + /** + * 徽标颜色 + */ + badgeVariants?: + | 'default' + | 'destructive' + | 'primary' + | 'success' + | 'warning' + | string; + /** + * 路由对应dom是否缓存起来 + */ + domCached?: boolean; + /** + * 路由的完整路径作为key(默认true) + */ + fullPathKey?: boolean; + /** + * 当前路由的子级在菜单中不展现 + * @default false + */ + hideChildrenInMenu?: boolean; + /** + * 当前路由在面包屑中不展现 + * @default false + */ + hideInBreadcrumb?: boolean; + /** + * 当前路由在菜单中不展现 + * @default false + */ + hideInMenu?: boolean; + /** + * 当前路由在标签页不展现 + * @default false + */ + hideInTab?: boolean; + /** + * 图标(菜单/tab) + */ + icon?: Component | string; + /** + * iframe 地址 + */ + iframeSrc?: string; + /** + * 忽略权限,直接可以访问 + * @default false + */ + ignoreAccess?: boolean; + /** + * 开启KeepAlive缓存 + */ + keepAlive?: boolean; + /** + * 外链-跳转路径 + */ + link?: string; + /** + * 路由是否已经加载过 + */ + loaded?: boolean; + /** + * 标签页最大打开数量 + * @default -1 + */ + maxNumOfOpenTab?: number; + /** + * 菜单可以看到,但是访问会被重定向到403 + */ + menuVisibleWithForbidden?: boolean; + /** + * 不使用基础布局(仅在顶级生效) + */ + noBasicLayout?: boolean; + /** + * 在新窗口打开 + */ + openInNewWindow?: boolean; + /** + * 用于路由->菜单排序 + */ + order?: number; + /** + * 菜单所携带的参数 + */ + query?: Recordable; + /** + * 标题名称 + */ + title: string; +} + +// 定义递归类型以将 RouteRecordRaw 的 component 属性更改为 string +type RouteRecordStringComponent = Omit< + RouteRecordRaw, + 'children' | 'component' +> & { + children?: RouteRecordStringComponent[]; + component: T; +}; + +type ComponentRecordType = Record Promise>; + +interface GenerateMenuAndRoutesOptions { + fetchMenuListAsync?: () => Promise; + forbiddenComponent?: RouteRecordRaw['component']; + layoutMap?: ComponentRecordType; + pageMap?: ComponentRecordType; + roles?: string[]; + router: Router; + routes: RouteRecordRaw[]; +} + +export type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, + RouteMeta, + RouteRecordRaw, + RouteRecordStringComponent, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/base/typings/tsconfig.json new file mode 100644 index 0000000..f6860a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/library.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/tsdown.config.ts new file mode 100644 index 0000000..c4f51c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/base/typings/vue-router.d.ts b/deploy/fba/fba-ui-src/packages/@core/base/typings/vue-router.d.ts new file mode 100644 index 0000000..fc7baa3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/base/typings/vue-router.d.ts @@ -0,0 +1,8 @@ +import type { RouteMeta as IRouteMeta } from './dist/index.d.mts'; + +import 'vue-router'; + +declare module 'vue-router' { + // oxlint-disable-next-line typescript/no-empty-object-type + interface RouteMeta extends IRouteMeta {} +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/package.json b/deploy/fba/fba-ui-src/packages/@core/composables/package.json new file mode 100644 index 0000000..de58e59 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/package.json @@ -0,0 +1,48 @@ +{ + "name": "@vben-core/composables", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@core/composables" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown" + }, + "files": [ + "dist" + ], + "sideEffects": false, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*", + "@vueuse/core": "catalog:", + "reka-ui": "catalog:", + "sortablejs": "catalog:", + "vue": "catalog:" + }, + "devDependencies": { + "@types/sortablejs": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/__tests__/use-sortable.test.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/__tests__/use-sortable.test.ts new file mode 100644 index 0000000..3524143 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/__tests__/use-sortable.test.ts @@ -0,0 +1,48 @@ +import type { SortableOptions } from 'sortablejs'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { useSortable } from '../use-sortable'; + +describe('useSortable', () => { + beforeEach(() => { + vi.mock('sortablejs/modular/sortable.complete.esm.js', () => ({ + default: { + create: vi.fn(), + }, + })); + }); + it('should call Sortable.create with the correct options', async () => { + // Create a mock element + const mockElement = document.createElement('div') as HTMLDivElement; + + // Define custom options + const customOptions: SortableOptions = { + group: 'test-group', + sort: false, + }; + + // Use the useSortable function + const { initializeSortable } = useSortable(mockElement, customOptions); + + // Initialize sortable + await initializeSortable(); + + // Import sortablejs to access the mocked create function + const Sortable = + // @ts-expect-error - This is a dynamic import + await import('sortablejs/modular/sortable.complete.esm.js'); + + // Verify that Sortable.create was called with the correct parameters + expect(Sortable.default.create).toHaveBeenCalledTimes(1); + expect(Sortable.default.create).toHaveBeenCalledWith( + mockElement, + expect.objectContaining({ + animation: 300, + delay: 400, + delayOnTouchOnly: true, + ...customOptions, + }), + ); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/index.ts new file mode 100644 index 0000000..2dbd4b8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/index.ts @@ -0,0 +1,13 @@ +export * from './use-is-mobile'; +export * from './use-layout-style'; +export * from './use-namespace'; +export * from './use-priority-value'; +export * from './use-scroll-lock'; +export * from './use-simple-locale'; +export * from './use-sortable'; +export { + useEmitAsProps, + useForwardExpose, + useForwardProps, + useForwardPropsEmits, +} from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-is-mobile.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-is-mobile.ts new file mode 100644 index 0000000..e35909f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-is-mobile.ts @@ -0,0 +1,7 @@ +import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'; + +export function useIsMobile() { + const breakpoints = useBreakpoints(breakpointsTailwind); + const isMobile = breakpoints.smaller('md'); + return { isMobile }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-layout-style.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-layout-style.ts new file mode 100644 index 0000000..395e9e5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-layout-style.ts @@ -0,0 +1,87 @@ +import type { CSSProperties } from 'vue'; + +import type { VisibleDomRect } from '@vben-core/shared/utils'; + +import { computed, onMounted, onUnmounted, ref } from 'vue'; + +import { + CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, + CSS_VARIABLE_LAYOUT_CONTENT_WIDTH, + CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT, + CSS_VARIABLE_LAYOUT_HEADER_HEIGHT, +} from '@vben-core/shared/constants'; +import { getElementVisibleRect } from '@vben-core/shared/utils'; + +import { useCssVar, useDebounceFn } from '@vueuse/core'; + +/** + * @zh_CN content style + */ +export function useLayoutContentStyle() { + let resizeObserver: null | ResizeObserver = null; + const contentElement = ref(null); + const visibleDomRect = ref(null); + const contentHeight = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT); + const contentWidth = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_WIDTH); + + const overlayStyle = computed((): CSSProperties => { + const { height, left, top, width } = visibleDomRect.value ?? {}; + return { + height: `${height}px`, + left: `${left}px`, + position: 'fixed', + top: `${top}px`, + width: `${width}px`, + zIndex: 150, + }; + }); + + const debouncedCalcHeight = useDebounceFn( + (_entries: ResizeObserverEntry[]) => { + visibleDomRect.value = getElementVisibleRect(contentElement.value); + contentHeight.value = `${visibleDomRect.value.height}px`; + contentWidth.value = `${visibleDomRect.value.width}px`; + }, + 16, + ); + + onMounted(() => { + if (contentElement.value && !resizeObserver) { + resizeObserver = new ResizeObserver(debouncedCalcHeight); + resizeObserver.observe(contentElement.value); + } + }); + + onUnmounted(() => { + resizeObserver?.disconnect(); + resizeObserver = null; + }); + + return { contentElement, overlayStyle, visibleDomRect }; +} + +export function useLayoutHeaderStyle() { + const headerHeight = useCssVar(CSS_VARIABLE_LAYOUT_HEADER_HEIGHT); + + return { + getLayoutHeaderHeight: () => { + return Number.parseInt(`${headerHeight.value}`, 10); + }, + setLayoutHeaderHeight: (height: number) => { + headerHeight.value = `${height}px`; + }, + }; +} + +export function useLayoutFooterStyle() { + const footerHeight = useCssVar(CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT); + + return { + getLayoutFooterHeight: () => { + return Number.parseInt(`${footerHeight.value}`, 10); + }, + setLayoutFooterHeight: (height: number) => { + footerHeight.value = `${height}px`; + }, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-namespace.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-namespace.ts new file mode 100644 index 0000000..eb06a70 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-namespace.ts @@ -0,0 +1,106 @@ +import { DEFAULT_NAMESPACE } from '@vben-core/shared/constants'; + +/** + * @see copy https://github.com/element-plus/element-plus/blob/dev/packages/hooks/use-namespace/index.ts + */ + +const statePrefix = 'is-'; + +const _bem = ( + namespace: string, + block: string, + blockSuffix: string, + element: string, + modifier: string, +) => { + let cls = `${namespace}-${block}`; + if (blockSuffix) { + cls += `-${blockSuffix}`; + } + if (element) { + cls += `__${element}`; + } + if (modifier) { + cls += `--${modifier}`; + } + return cls; +}; + +const is: { + (name: string): string; + // oxlint-disable-next-line typescript/unified-signatures + (name: string, state: boolean | undefined): string; +} = (name: string, ...args: [] | [boolean | undefined]) => { + const state = args.length > 0 ? args[0] : true; + return name && state ? `${statePrefix}${name}` : ''; +}; + +const useNamespace = (block: string) => { + const namespace = DEFAULT_NAMESPACE; + const b = (blockSuffix = '') => _bem(namespace, block, blockSuffix, '', ''); + const e = (element?: string) => + element ? _bem(namespace, block, '', element, '') : ''; + const m = (modifier?: string) => + modifier ? _bem(namespace, block, '', '', modifier) : ''; + const be = (blockSuffix?: string, element?: string) => + blockSuffix && element + ? _bem(namespace, block, blockSuffix, element, '') + : ''; + const em = (element?: string, modifier?: string) => + element && modifier ? _bem(namespace, block, '', element, modifier) : ''; + const bm = (blockSuffix?: string, modifier?: string) => + blockSuffix && modifier + ? _bem(namespace, block, blockSuffix, '', modifier) + : ''; + const bem = (blockSuffix?: string, element?: string, modifier?: string) => + blockSuffix && element && modifier + ? _bem(namespace, block, blockSuffix, element, modifier) + : ''; + + // for css var + // --el-xxx: value; + const cssVar = (object: Record) => { + const styles: Record = {}; + for (const key in object) { + if (object[key]) { + styles[`--${namespace}-${key}`] = object[key]; + } + } + return styles; + }; + // with block + const cssVarBlock = (object: Record) => { + const styles: Record = {}; + for (const key in object) { + if (object[key]) { + styles[`--${namespace}-${block}-${key}`] = object[key]; + } + } + return styles; + }; + + const cssVarName = (name: string) => `--${namespace}-${name}`; + const cssVarBlockName = (name: string) => `--${namespace}-${block}-${name}`; + + return { + b, + be, + bem, + bm, + // css + cssVar, + cssVarBlock, + cssVarBlockName, + cssVarName, + e, + em, + is, + m, + namespace, + }; +}; + +type UseNamespaceReturn = ReturnType; + +export type { UseNamespaceReturn }; +export { useNamespace }; diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-priority-value.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-priority-value.ts new file mode 100644 index 0000000..74b5b5b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-priority-value.ts @@ -0,0 +1,94 @@ +import type { ComputedRef, Ref } from 'vue'; + +import { computed, getCurrentInstance, unref, useAttrs, useSlots } from 'vue'; + +import { + getFirstNonNullOrUndefined, + kebabToCamelCase, +} from '@vben-core/shared/utils'; + +/** + * 依次从插槽、attrs、props、state 中获取值 + * @param key + * @param props + * @param state + */ +export function usePriorityValue< + T extends Record, + S extends Record, + K extends keyof T = keyof T, +>(key: K, props: T, state: Readonly>> | undefined) { + const instance = getCurrentInstance(); + const slots = useSlots(); + const attrs = useAttrs() as T; + + const value = computed((): T[K] => { + // props不管有没有传,都会有默认值,会影响这里的顺序, + // 通过判断原始props是否有值来判断是否传入 + const rawProps = (instance?.vnode?.props || {}) as T; + + const standardRawProps = {} as T; + + for (const [key, value] of Object.entries(rawProps)) { + standardRawProps[kebabToCamelCase(key) as K] = value; + } + const propsKey = + standardRawProps?.[key] === undefined ? undefined : props[key]; + + // slot可以关闭 + return getFirstNonNullOrUndefined( + slots[key as string], + attrs[key], + propsKey, + state?.value?.[key as keyof S], + ) as T[K]; + }); + + return value; +} + +/** + * 批量获取state中的值(每个值都是ref) + * @param props + * @param state + */ +export function usePriorityValues< + T extends Record, + S extends Ref> = Readonly, NoInfer>>, +>(props: T, state: S | undefined) { + const result: { [K in keyof T]: ComputedRef } = {} as never; + + (Object.keys(props) as (keyof T)[]).forEach((key) => { + result[key] = usePriorityValue(key as keyof typeof props, props, state); + }); + + return result; +} + +/** + * 批量获取state中的值(集中在一个computed,用于透传) + * @param props + * @param state + */ +export function useForwardPriorityValues< + T extends Record, + S extends Ref> = Readonly, NoInfer>>, +>(props: T, state: S | undefined) { + const computedResult: { [K in keyof T]: ComputedRef } = {} as never; + + (Object.keys(props) as (keyof T)[]).forEach((key) => { + computedResult[key] = usePriorityValue( + key as keyof typeof props, + props, + state, + ); + }); + + return computed(() => { + const unwrapResult: Record = {}; + Object.keys(props).forEach((key) => { + unwrapResult[key] = unref(computedResult[key]); + }); + return unwrapResult as { [K in keyof T]: T[K] }; + }); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-scroll-lock.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-scroll-lock.ts new file mode 100644 index 0000000..d1c1497 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-scroll-lock.ts @@ -0,0 +1,54 @@ +import { getScrollbarWidth, needsScrollbar } from '@vben-core/shared/utils'; + +import { + useScrollLock as _useScrollLock, + tryOnBeforeUnmount, + tryOnMounted, +} from '@vueuse/core'; + +export const SCROLL_FIXED_CLASS = `_scroll__fixed_`; + +export function useScrollLock() { + const isLocked = _useScrollLock(document.body); + const scrollbarWidth = getScrollbarWidth(); + + tryOnMounted(() => { + if (!needsScrollbar()) { + return; + } + document.body.style.paddingRight = `${scrollbarWidth}px`; + + const layoutFixedNodes = document.querySelectorAll( + `.${SCROLL_FIXED_CLASS}`, + ); + const nodes = [...layoutFixedNodes]; + if (nodes.length > 0) { + nodes.forEach((node) => { + node.dataset.transition = node.style.transition; + node.style.transition = 'none'; + node.style.paddingRight = `${scrollbarWidth}px`; + }); + } + isLocked.value = true; + }); + + tryOnBeforeUnmount(() => { + if (!needsScrollbar()) { + return; + } + isLocked.value = false; + const layoutFixedNodes = document.querySelectorAll( + `.${SCROLL_FIXED_CLASS}`, + ); + const nodes = [...layoutFixedNodes]; + if (nodes.length > 0) { + nodes.forEach((node) => { + node.style.paddingRight = ''; + requestAnimationFrame(() => { + node.style.transition = node.dataset.transition || ''; + }); + }); + } + document.body.style.paddingRight = ''; + }); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/README.md b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/README.md new file mode 100644 index 0000000..c0a676d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/README.md @@ -0,0 +1,3 @@ +# Simple i18n + +Simple i18 implementation diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/index.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/index.ts new file mode 100644 index 0000000..67b8173 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/index.ts @@ -0,0 +1,27 @@ +import type { Locale } from './messages'; + +import { computed, ref } from 'vue'; + +import { createSharedComposable } from '@vueuse/core'; + +import { getMessages } from './messages'; + +export const useSimpleLocale = createSharedComposable(() => { + const currentLocale = ref('zh-CN'); + + const setSimpleLocale = (locale: Locale) => { + currentLocale.value = locale; + }; + + const $t = computed(() => { + const localeMessages = getMessages(currentLocale.value); + return (key: string) => { + return localeMessages[key] || key; + }; + }); + return { + $t, + currentLocale, + setSimpleLocale, + }; +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/messages.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/messages.ts new file mode 100644 index 0000000..671ae33 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-simple-locale/messages.ts @@ -0,0 +1,26 @@ +export type Locale = 'en-US' | 'zh-CN'; + +export const messages: Record> = { + 'en-US': { + cancel: 'Cancel', + collapse: 'Collapse', + confirm: 'Confirm', + expand: 'Expand', + prompt: 'Prompt', + reset: 'Reset', + submit: 'Submit', + confirmTitle: 'Please Confirm', + }, + 'zh-CN': { + cancel: '取消', + collapse: '收起', + confirm: '确认', + expand: '展开', + prompt: '提示', + reset: '重置', + submit: '提交', + confirmTitle: '请确认', + }, +}; + +export const getMessages = (locale: Locale) => messages[locale]; diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/src/use-sortable.ts b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-sortable.ts new file mode 100644 index 0000000..57f87a6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/src/use-sortable.ts @@ -0,0 +1,29 @@ +import type { SortableOptions } from 'sortablejs'; +import type Sortable from 'sortablejs'; + +function useSortable( + sortableContainer: T, + options: SortableOptions = {}, +) { + const initializeSortable = async () => { + const Sortable = await import( + // @ts-expect-error - This is a dynamic import + 'sortablejs/modular/sortable.complete.esm.js' + ); + const sortable = Sortable?.default?.create?.(sortableContainer, { + animation: 300, + delay: 400, + delayOnTouchOnly: true, + ...options, + }); + return sortable as Sortable; + }; + + return { + initializeSortable, + }; +} + +export { useSortable }; + +export type { Sortable }; diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/composables/tsconfig.json new file mode 100644 index 0000000..f6860a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/library.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/composables/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/composables/tsdown.config.ts new file mode 100644 index 0000000..c4f51c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/composables/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap new file mode 100644 index 0000000..2d0fca5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap @@ -0,0 +1,147 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`defaultPreferences immutability test > should not modify the config object 1`] = ` +{ + "app": { + "accessMode": "frontend", + "authPageLayout": "panel-right", + "checkUpdatesInterval": 1, + "colorGrayMode": false, + "colorWeakMode": false, + "compact": false, + "contentCompact": "wide", + "contentCompactWidth": 1200, + "contentPadding": 0, + "contentPaddingBottom": 0, + "contentPaddingLeft": 0, + "contentPaddingRight": 0, + "contentPaddingTop": 0, + "defaultAvatar": "https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp", + "defaultHomePath": "/analytics", + "dynamicTitle": true, + "enableCheckUpdates": true, + "enableCopyPreferences": true, + "enablePreferences": true, + "enableRefreshToken": false, + "enableStickyPreferencesNavigationBar": true, + "isMobile": false, + "layout": "sidebar-nav", + "locale": "zh-CN", + "loginExpiredMode": "page", + "name": "Vben Admin", + "preferencesButtonPosition": "auto", + "timezone": "Asia/Shanghai", + "watermark": false, + "watermarkContent": "", + "zIndex": 200, + }, + "breadcrumb": { + "enable": true, + "hideOnlyOne": false, + "showHome": false, + "showIcon": true, + "styleType": "normal", + }, + "copyright": { + "companyName": "Vben", + "companySiteLink": "https://www.vben.pro", + "date": "2024", + "enable": true, + "icp": "", + "icpLink": "", + "settingShow": true, + }, + "footer": { + "enable": false, + "fixed": false, + "height": 32, + }, + "header": { + "enable": true, + "height": 50, + "hidden": false, + "menuAlign": "start", + "mode": "fixed", + }, + "logo": { + "enable": true, + "fit": "contain", + "source": "https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp", + }, + "navigation": { + "accordion": true, + "split": true, + "styleType": "rounded", + }, + "shortcutKeys": { + "enable": true, + "globalEscape": false, + "globalLockScreen": true, + "globalLogout": true, + "globalPreferences": true, + "globalSearch": true, + }, + "sidebar": { + "autoActivateChild": false, + "collapseWidth": 60, + "collapsed": false, + "collapsedButton": true, + "collapsedShowTitle": false, + "draggable": true, + "enable": true, + "expandOnHover": true, + "extraCollapse": false, + "extraCollapsedWidth": 60, + "fixedButton": true, + "hidden": false, + "mixedWidth": 80, + "width": 224, + }, + "tabbar": { + "draggable": true, + "enable": true, + "height": 38, + "keepAlive": true, + "maxCount": 0, + "middleClickToClose": false, + "persist": true, + "showIcon": true, + "showMaximize": true, + "showMore": true, + "showRefresh": true, + "styleType": "chrome", + "visitHistory": true, + "wheelable": true, + }, + "theme": { + "builtinType": "default", + "colorDestructive": "hsl(348 100% 61%)", + "colorPrimary": "hsl(212 100% 45%)", + "colorSuccess": "hsl(144 57% 58%)", + "colorWarning": "hsl(42 84% 61%)", + "fontSize": 16, + "mode": "dark", + "radius": "0.5", + "semiDarkHeader": false, + "semiDarkSidebar": false, + "semiDarkSidebarSub": false, + }, + "transition": { + "enable": true, + "loading": true, + "name": "fade-slide", + "progress": true, + }, + "widget": { + "fullscreen": true, + "globalSearch": true, + "languageToggle": true, + "lockScreen": true, + "notification": true, + "refresh": true, + "sidebarToggle": true, + "themeToggle": true, + "timezone": true, + }, +} +`; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/config.test.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/config.test.ts new file mode 100644 index 0000000..f7c9bb3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/config.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest'; + +import { defaultPreferences } from '../src/config'; + +describe('defaultPreferences immutability test', () => { + // 创建快照,确保默认配置对象不被修改 + it('should not modify the config object', () => { + expect(defaultPreferences).toMatchSnapshot(); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/preferences.test.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/preferences.test.ts new file mode 100644 index 0000000..6afdb21 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/__tests__/preferences.test.ts @@ -0,0 +1,546 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { defaultPreferences } from '../src/config'; +import { isDarkTheme } from '../src/update-css-variables'; + +describe('preferences', () => { + let PreferenceManager: typeof import('../src/preferences').PreferenceManager; + let preferenceManager: InstanceType< + typeof import('../src/preferences').PreferenceManager + >; + + // 模拟 window.matchMedia 方法 + vi.stubGlobal( + 'matchMedia', + vi.fn().mockImplementation((query) => ({ + addEventListener: vi.fn(), + addListener: vi.fn(), // Deprecated + dispatchEvent: vi.fn(), + matches: query === '(prefers-color-scheme: dark)', + media: query, + onchange: null, + removeEventListener: vi.fn(), + removeListener: vi.fn(), // Deprecated + })), + ); + + vi.stubGlobal('localStorage', { + clear: vi.fn(), + getItem: vi.fn(() => null), + key: vi.fn(() => null), + length: 0, + removeItem: vi.fn(), + setItem: vi.fn(), + }); + + vi.stubGlobal('sessionStorage', { + clear: vi.fn(), + getItem: vi.fn(() => null), + key: vi.fn(() => null), + length: 0, + removeItem: vi.fn(), + setItem: vi.fn(), + }); + + beforeAll(async () => { + ({ PreferenceManager } = await import('../src/preferences')); + }); + + beforeEach(() => { + vi.mocked(localStorage.getItem).mockImplementation(() => null); + vi.mocked(localStorage.removeItem).mockReset(); + vi.mocked(localStorage.setItem).mockReset(); + vi.mocked(sessionStorage.getItem).mockImplementation(() => null); + vi.mocked(sessionStorage.removeItem).mockReset(); + vi.mocked(sessionStorage.setItem).mockReset(); + preferenceManager = new PreferenceManager(); + }); + + it('loads default preferences if no saved preferences found', () => { + const preferences = preferenceManager.getPreferences(); + expect(preferences).toEqual(defaultPreferences); + }); + + it('initializes preferences with overrides', async () => { + const overrides: any = { + app: { + locale: 'en-US', + }, + }; + await preferenceManager.initPreferences({ + namespace: 'testNamespace', + overrides, + }); + + // 等待防抖动操作完成 + // await new Promise((resolve) => setTimeout(resolve, 300)); // 等待100毫秒 + + const expected = { + ...defaultPreferences, + app: { + ...defaultPreferences.app, + ...overrides.app, + }, + }; + + expect(preferenceManager.getPreferences()).toEqual(expected); + }); + + it('updates theme mode correctly', () => { + preferenceManager.updatePreferences({ + theme: { + mode: 'light', + }, + }); + + expect(preferenceManager.getPreferences().theme.mode).toBe('light'); + }); + + it('updates color modes correctly', () => { + preferenceManager.updatePreferences({ + app: { colorGrayMode: true, colorWeakMode: true }, + }); + + expect(preferenceManager.getPreferences().app.colorGrayMode).toBe(true); + expect(preferenceManager.getPreferences().app.colorWeakMode).toBe(true); + }); + + it('resets preferences to default', async () => { + // 先更新一些偏好设置 + preferenceManager.updatePreferences({ + theme: { + mode: 'light', + }, + }); + + // 然后重置偏好设置 + await preferenceManager.resetPreferences(); + + expect(preferenceManager.getPreferences()).toEqual(defaultPreferences); + }); + + it('updates isMobile correctly', () => { + // 模拟移动端状态 + vi.stubGlobal( + 'matchMedia', + vi.fn().mockImplementation((query) => ({ + addEventListener: vi.fn(), + addListener: vi.fn(), + dispatchEvent: vi.fn(), + matches: query === '(max-width: 768px)', + media: query, + onchange: null, + removeEventListener: vi.fn(), + removeListener: vi.fn(), + })), + ); + + preferenceManager.updatePreferences({ + app: { isMobile: true }, + }); + + expect(preferenceManager.getPreferences().app.isMobile).toBe(true); + }); + + it('updates the locale preference correctly', () => { + preferenceManager.updatePreferences({ + app: { locale: 'en-US' }, + }); + + expect(preferenceManager.getPreferences().app.locale).toBe('en-US'); + }); + + it('updates the sidebar width correctly', () => { + preferenceManager.updatePreferences({ + sidebar: { width: 200 }, + }); + + expect(preferenceManager.getPreferences().sidebar.width).toBe(200); + }); + it('updates the sidebar collapse state correctly', () => { + preferenceManager.updatePreferences({ + sidebar: { collapsed: true }, + }); + + expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true); + }); + it('updates the navigation style type correctly', () => { + preferenceManager.updatePreferences({ + navigation: { styleType: 'flat' }, + } as any); + + expect(preferenceManager.getPreferences().navigation.styleType).toBe( + 'flat', + ); + }); + + it('resets preferences to default correctly', async () => { + // 先更新一些偏好设置 + preferenceManager.updatePreferences({ + app: { locale: 'en-US' }, + sidebar: { collapsed: true, width: 200 }, + theme: { + mode: 'light', + }, + }); + + // 然后重置偏好设置 + await preferenceManager.resetPreferences(); + + expect(preferenceManager.getPreferences()).toEqual(defaultPreferences); + }); + + it('does not update undefined preferences', () => { + const originalPreferences = preferenceManager.getPreferences(); + + preferenceManager.updatePreferences({ + app: { nonexistentField: 'value' }, + } as any); + + expect(preferenceManager.getPreferences()).toEqual(originalPreferences); + }); + + it('reverts to default when a preference field is deleted', () => { + preferenceManager.updatePreferences({ + app: { locale: 'en-US' }, + }); + + preferenceManager.updatePreferences({ + app: { locale: undefined }, + }); + + expect(preferenceManager.getPreferences().app.locale).toBe('en-US'); + }); + + it('ignores updates with invalid preference value types', () => { + const originalPreferences = preferenceManager.getPreferences(); + + preferenceManager.updatePreferences({ + app: { isMobile: 'true' as unknown as boolean }, // 错误类型 + }); + + expect(preferenceManager.getPreferences()).toEqual(originalPreferences); + }); + + it('merges nested preference objects correctly', () => { + preferenceManager.updatePreferences({ + app: { name: 'New App Name' }, + }); + + const expected = { + ...defaultPreferences, + app: { + ...defaultPreferences.app, + name: 'New App Name', + }, + }; + + expect(preferenceManager.getPreferences()).toEqual(expected); + }); + + it('applies updates immediately after initialization', async () => { + const overrides: any = { + app: { + locale: 'en-US', + }, + }; + + await preferenceManager.initPreferences({ + namespace: 'apply-updates', + overrides, + }); + + preferenceManager.updatePreferences({ + theme: { mode: 'light' }, + }); + + expect(preferenceManager.getPreferences().theme.mode).toBe('light'); + }); + + it('initializes custom preferences extension with default values', async () => { + const extension = { + fields: [ + { + component: 'switch', + defaultValue: true, + key: 'enableWorkbench', + label: '启用工作台', + }, + { + component: 'select', + defaultValue: 'single', + key: 'tenantMode', + label: '租户模式', + options: [ + { label: '单租户', value: 'single' }, + { label: '多租户', value: 'multi' }, + ], + }, + ], + tabLabel: '扩展', + title: '业务偏好', + } as const; + + await preferenceManager.initPreferences({ + extension, + namespace: 'custom-defaults', + }); + + expect(preferenceManager.getPreferencesExtension()).toEqual(extension); + expect(preferenceManager.getCustomPreferences()).toEqual({ + enableWorkbench: true, + tenantMode: 'single', + }); + }); + + it('does not expose mutable custom preference baselines or extension schema', async () => { + const extension = { + fields: [ + { + component: 'number', + componentProps: { + max: 10, + min: 2, + step: 2, + }, + defaultValue: 4, + key: 'pageSize', + label: '分页大小', + }, + ], + tabLabel: '扩展', + title: '业务偏好', + } as const; + + await preferenceManager.initPreferences({ + extension, + namespace: 'custom-readonly', + }); + + const initialCustomPreferences = + preferenceManager.getInitialCustomPreferences<{ + pageSize: number; + }>() as { pageSize: number }; + const preferencesExtension = preferenceManager.getPreferencesExtension<{ + pageSize: number; + }>() as { + fields: Array<{ componentProps?: { max?: number }; label: string }>; + }; + const [firstField] = preferencesExtension.fields; + + initialCustomPreferences.pageSize = 8; + expect(firstField).toBeDefined(); + expect(firstField?.componentProps).toBeDefined(); + + if (!firstField || !firstField.componentProps) { + return; + } + + firstField.label = '已修改'; + firstField.componentProps.max = 20; + + expect(preferenceManager.getInitialCustomPreferences()).toEqual({ + pageSize: 4, + }); + expect(preferenceManager.getPreferencesExtension()).toEqual(extension); + }); + + it('updates and resets custom preferences correctly', async () => { + await preferenceManager.initPreferences({ + extension: { + fields: [ + { + component: 'number', + defaultValue: 20, + key: 'pageSize', + label: '分页大小', + }, + { + component: 'input', + defaultValue: '日报', + key: 'reportTitle', + label: '报表标题', + }, + ], + tabLabel: '扩展', + }, + namespace: 'custom-reset', + }); + + preferenceManager.updateCustomPreferences({ + pageSize: 50, + reportTitle: '月报', + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 50, + reportTitle: '月报', + }); + + await preferenceManager.resetPreferences(); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 20, + reportTitle: '日报', + }); + }); + + it('ignores invalid custom preferences updates', async () => { + await preferenceManager.initPreferences({ + extension: { + fields: [ + { + component: 'switch', + defaultValue: true, + key: 'enableWorkbench', + label: '启用工作台', + }, + { + component: 'select', + defaultValue: 'single', + key: 'tenantMode', + label: '租户模式', + options: [ + { label: '单租户', value: 'single' }, + { label: '多租户', value: 'multi' }, + ], + }, + ], + tabLabel: '扩展', + }, + namespace: 'custom-invalid', + }); + + const originalCustomPreferences = preferenceManager.getCustomPreferences(); + + preferenceManager.updateCustomPreferences({ + enableWorkbench: 'true' as unknown as boolean, + tenantMode: 'unknown', + unknownField: 'value', + } as any); + + expect(preferenceManager.getCustomPreferences()).toEqual( + originalCustomPreferences, + ); + }); + + it('enforces custom number field min max and step constraints', async () => { + await preferenceManager.initPreferences({ + extension: { + fields: [ + { + component: 'number', + componentProps: { + max: 10, + min: 2, + step: 2, + }, + defaultValue: 4, + key: 'pageSize', + label: '分页大小', + }, + ], + tabLabel: '扩展', + }, + namespace: 'custom-number-constraints', + }); + + preferenceManager.updateCustomPreferences({ + pageSize: 8, + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 8, + }); + + preferenceManager.updateCustomPreferences({ + pageSize: 1, + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 8, + }); + + preferenceManager.updateCustomPreferences({ + pageSize: 12, + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 8, + }); + + preferenceManager.updateCustomPreferences({ + pageSize: 5, + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 8, + }); + }); + + it('filters cached custom number values that violate field constraints', async () => { + vi.mocked(localStorage.getItem).mockImplementation((key) => { + if (key.endsWith('cache-preferences-custom')) { + return JSON.stringify({ + value: { + pageSize: 5, + }, + }); + } + + return null; + }); + + await preferenceManager.initPreferences({ + extension: { + fields: [ + { + component: 'number', + componentProps: { + max: 10, + min: 2, + step: 2, + }, + defaultValue: 4, + key: 'pageSize', + label: '分页大小', + }, + ], + tabLabel: '扩展', + }, + namespace: 'custom-number-cache', + }); + + expect(preferenceManager.getCustomPreferences()).toEqual({ + pageSize: 4, + }); + }); +}); + +describe('isDarkTheme', () => { + it('should return true for dark theme', () => { + expect(isDarkTheme('dark')).toBe(true); + }); + + it('should return false for light theme', () => { + expect(isDarkTheme('light')).toBe(false); + }); + + it('should return system preference for auto theme', () => { + vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({ + addEventListener: vi.fn(), + addListener: vi.fn(), // Deprecated + dispatchEvent: vi.fn(), + matches: query === '(prefers-color-scheme: dark)', + media: query, + onchange: null, + removeEventListener: vi.fn(), + removeListener: vi.fn(), // Deprecated + })); + + expect(isDarkTheme('auto')).toBe(true); + expect(window.matchMedia).toHaveBeenCalledWith( + '(prefers-color-scheme: dark)', + ); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/package.json b/deploy/fba/fba-ui-src/packages/@core/preferences/package.json new file mode 100644 index 0000000..3a92f92 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/package.json @@ -0,0 +1,48 @@ +{ + "name": "@vben-core/preferences", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@core/preferences" + }, + "license": "MIT", + "type": "module", + "scripts": { + "#build": "pnpm exec tsdown" + }, + "files": [ + "dist", + "src" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/config.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/config.ts new file mode 100644 index 0000000..d776282 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/config.ts @@ -0,0 +1,149 @@ +import type { Preferences } from './types'; + +const defaultPreferences: Preferences = { + app: { + accessMode: 'frontend', + authPageLayout: 'panel-right', + checkUpdatesInterval: 1, + colorGrayMode: false, + colorWeakMode: false, + compact: false, + contentCompact: 'wide', + contentCompactWidth: 1200, + contentPadding: 0, + contentPaddingBottom: 0, + contentPaddingLeft: 0, + contentPaddingRight: 0, + contentPaddingTop: 0, + defaultAvatar: + 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp', + defaultHomePath: '/analytics', + dynamicTitle: true, + enableCheckUpdates: true, + enableCopyPreferences: true, + enablePreferences: true, + enableRefreshToken: false, + enableStickyPreferencesNavigationBar: true, + isMobile: false, + layout: 'sidebar-nav', + locale: 'zh-CN', + loginExpiredMode: 'page', + name: 'Vben Admin', + preferencesButtonPosition: 'auto', + timezone: 'Asia/Shanghai', + watermark: false, + watermarkContent: '', + zIndex: 200, + }, + breadcrumb: { + enable: true, + hideOnlyOne: false, + showHome: false, + showIcon: true, + styleType: 'normal', + }, + copyright: { + companyName: 'Vben', + companySiteLink: 'https://www.vben.pro', + date: '2024', + enable: true, + icp: '', + icpLink: '', + settingShow: true, + }, + footer: { + enable: false, + fixed: false, + height: 32, + }, + header: { + enable: true, + height: 50, + hidden: false, + menuAlign: 'start', + mode: 'fixed', + }, + + logo: { + enable: true, + fit: 'contain', + source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', + }, + navigation: { + accordion: true, + split: true, + styleType: 'rounded', + }, + shortcutKeys: { + enable: true, + globalEscape: false, + globalLockScreen: true, + globalLogout: true, + globalPreferences: true, + globalSearch: true, + }, + sidebar: { + autoActivateChild: false, + collapsed: false, + collapsedButton: true, + collapsedShowTitle: false, + collapseWidth: 60, + draggable: true, + enable: true, + expandOnHover: true, + extraCollapse: false, + extraCollapsedWidth: 60, + fixedButton: true, + hidden: false, + mixedWidth: 80, + width: 224, + }, + tabbar: { + draggable: true, + enable: true, + height: 38, + keepAlive: true, + maxCount: 0, + middleClickToClose: false, + persist: true, + showIcon: true, + showMaximize: true, + showMore: true, + showRefresh: true, + styleType: 'chrome', + visitHistory: true, + wheelable: true, + }, + theme: { + builtinType: 'default', + colorDestructive: 'hsl(348 100% 61%)', + colorPrimary: 'hsl(212 100% 45%)', + colorSuccess: 'hsl(144 57% 58%)', + colorWarning: 'hsl(42 84% 61%)', + mode: 'dark', + radius: '0.5', + fontSize: 16, + semiDarkHeader: false, + semiDarkSidebar: false, + semiDarkSidebarSub: false, + }, + transition: { + enable: true, + loading: true, + name: 'fade-slide', + progress: true, + }, + widget: { + fullscreen: true, + globalSearch: true, + languageToggle: true, + lockScreen: true, + notification: true, + refresh: true, + sidebarToggle: true, + themeToggle: true, + timezone: true, + }, +}; + +export { defaultPreferences }; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/constants.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/constants.ts new file mode 100644 index 0000000..562a7af --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/constants.ts @@ -0,0 +1,116 @@ +import type { BuiltinThemeType, TimezoneOption } from '@vben-core/typings'; + +interface BuiltinThemePreset { + color: string; + darkPrimaryColor?: string; + primaryColor?: string; + type: BuiltinThemeType; +} + +const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [ + { + color: 'hsl(212 100% 45%)', + type: 'default', + }, + { + color: 'hsl(245 82% 67%)', + type: 'violet', + }, + { + color: 'hsl(347 77% 60%)', + type: 'pink', + }, + { + color: 'hsl(42 84% 61%)', + type: 'yellow', + }, + { + color: 'hsl(231 98% 65%)', + type: 'sky-blue', + }, + { + color: 'hsl(161 90% 43%)', + type: 'green', + }, + { + color: 'hsl(240 5% 26%)', + darkPrimaryColor: 'hsl(0 0% 98%)', + primaryColor: 'hsl(240 5.9% 10%)', + type: 'zinc', + }, + { + color: 'hsl(181 84% 32%)', + type: 'deep-green', + }, + { + color: 'hsl(211 91% 39%)', + type: 'deep-blue', + }, + { + color: 'hsl(18 89% 40%)', + type: 'orange', + }, + { + color: 'hsl(0 75% 42%)', + type: 'rose', + }, + { + color: 'hsl(0 0% 25%)', + darkPrimaryColor: 'hsl(0 0% 98%)', + primaryColor: 'hsl(240 5.9% 10%)', + type: 'neutral', + }, + { + color: 'hsl(215 25% 27%)', + darkPrimaryColor: 'hsl(0 0% 98%)', + primaryColor: 'hsl(240 5.9% 10%)', + type: 'slate', + }, + { + color: 'hsl(217 19% 27%)', + darkPrimaryColor: 'hsl(0 0% 98%)', + primaryColor: 'hsl(240 5.9% 10%)', + type: 'gray', + }, + { + color: '', + type: 'custom', + }, +]; + +/** + * 时区选项 + */ +const DEFAULT_TIME_ZONE_OPTIONS: TimezoneOption[] = [ + { + offset: -5, + timezone: 'America/New_York', + label: 'America/New_York(GMT-5)', + }, + { + offset: 0, + timezone: 'Europe/London', + label: 'Europe/London(GMT0)', + }, + { + offset: 8, + timezone: 'Asia/Shanghai', + label: 'Asia/Shanghai(GMT+8)', + }, + { + offset: 9, + timezone: 'Asia/Tokyo', + label: 'Asia/Tokyo(GMT+9)', + }, + { + offset: 9, + timezone: 'Asia/Seoul', + label: 'Asia/Seoul(GMT+9)', + }, +]; + +export const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7); + +export { BUILT_IN_THEME_PRESETS, DEFAULT_TIME_ZONE_OPTIONS }; + +export type { BuiltinThemePreset }; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/index.ts new file mode 100644 index 0000000..91eb106 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/index.ts @@ -0,0 +1,24 @@ +/* oxlint-disable unicorn/prefer-export-from */ +import type { Preferences } from './types'; + +import { preferencesManager } from './preferences'; + +export const { + getPreferences, + getCustomPreferences, + getInitialCustomPreferences, + getPreferencesExtension, + updatePreferences, + updateCustomPreferences, + resetPreferences, + clearCache, + initPreferences, +} = preferencesManager; + +export const preferences: Preferences = getPreferences(); + +export { preferencesManager }; + +export * from './constants'; +export type * from './types'; +export * from './use-preferences'; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/preferences.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/preferences.ts new file mode 100644 index 0000000..c7f5caa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/preferences.ts @@ -0,0 +1,464 @@ +import type { DeepPartial } from '@vben-core/typings'; + +import type { + CustomPreferencesField, + CustomPreferencesRecord, + InitialOptions, + Preferences, + PreferencesExtension, +} from './types'; + +import { markRaw, reactive, readonly, watch } from 'vue'; + +import { StorageManager } from '@vben-core/shared/cache'; +import { isMacOs, merge } from '@vben-core/shared/utils'; + +import { + breakpointsTailwind, + useBreakpoints, + useDebounceFn, +} from '@vueuse/core'; + +import { defaultPreferences } from './config'; +import { updateCSSVariables } from './update-css-variables'; + +const STORAGE_KEYS = { + CUSTOM: 'preferences-custom', + MAIN: 'preferences', + LOCALE: 'preferences-locale', + THEME: 'preferences-theme', +} as const; + +class PreferenceManager { + private cache: StorageManager; + private customPreferencesExtension: null | PreferencesExtension = null; + private customState = reactive({}); + private debouncedSave: () => void; + private initialCustomPreferences: CustomPreferencesRecord = {}; + private initialPreferences: Preferences = defaultPreferences; + private isInitialized = false; + private state: Preferences; + + constructor() { + this.cache = new StorageManager(); + // 构造函数不再同步读取缓存,使用默认值初始化 + // 真正的缓存加载在 initPreferences 中完成(已经是 async) + this.state = reactive({ ...defaultPreferences }); + this.debouncedSave = useDebounceFn(() => this.saveToCache(), 150); + } + + /** + * 清除所有缓存的偏好设置 + */ + clearCache = async () => { + await Promise.all( + Object.values(STORAGE_KEYS).map((key) => this.cache.removeItem(key)), + ); + }; + + /** + * 获取扩展偏好设置 + */ + getCustomPreferences = < + TCustomPreferences extends object = CustomPreferencesRecord, + >() => { + return readonly(this.customState) as Readonly; + }; + + /** + * 获取初始化扩展偏好设置 + */ + getInitialCustomPreferences = < + TCustomPreferences extends object = CustomPreferencesRecord, + >() => { + return this.cloneValue( + this.initialCustomPreferences, + ) as Readonly; + }; + + /** + * 获取初始化偏好设置 + */ + getInitialPreferences = () => { + return this.initialPreferences; + }; + + /** + * 获取当前偏好设置(只读) + */ + getPreferences = () => { + return readonly(this.state); + }; + + /** + * 获取扩展偏好设置配置 + */ + getPreferencesExtension = < + TCustomPreferences extends object = CustomPreferencesRecord, + >() => { + return this.customPreferencesExtension + ? (this.cloneValue(this.customPreferencesExtension) as Readonly< + PreferencesExtension + >) + : null; + }; + + /** + * 初始化偏好设置 + * @param options - 初始化配置项 + * @param options.namespace - 命名空间,用于隔离不同应用的配置 + * @param options.overrides - 要覆盖的偏好设置 + */ + initPreferences = async < + TCustomPreferences extends object = CustomPreferencesRecord, + >({ + namespace, + overrides, + extension, + }: InitialOptions) => { + // 防止重复初始化 + if (this.isInitialized) { + return; + } + + // 使用命名空间初始化存储管理器 + this.cache = new StorageManager({ prefix: namespace }); + + // 合并初始偏好设置:前面的对象优先,后面的对象仅补齐缺失字段 + this.initialPreferences = merge({}, overrides, defaultPreferences); + this.customPreferencesExtension = extension ?? null; + this.initialCustomPreferences = this.resolveCustomPreferencesDefaults( + this.customPreferencesExtension, + ); + + // 加载缓存的偏好设置,并仅用缓存补齐初始化配置中未显式设置的字段 + const cachedPreferences = (await this.loadFromCache()) || {}; + const mergedPreference = merge( + {}, + cachedPreferences, // 用户缓存的设置优先 + this.initialPreferences, // 初始设置仅补齐缺失字段 + ); + + // 更新偏好设置 + this.updatePreferences(mergedPreference); + + const cachedCustom = (await this.loadCustomFromCache()) || {}; + this.replaceCustomPreferences( + merge( + {}, + this.sanitizeCustomPreferences(cachedCustom), + this.initialCustomPreferences, + ), + ); + await this.saveToCache(); + + // 设置监听器 + this.setupWatcher(); + + // 初始化平台标识 + this.initPlatform(); + + this.isInitialized = true; + }; + + /** + * 重置偏好设置到初始状态 + */ + resetPreferences = async () => { + // 将状态重置为初始偏好设置 + Object.assign(this.state, this.initialPreferences); + this.replaceCustomPreferences(this.initialCustomPreferences); + + // 保存偏好设置至缓存 + await this.saveToCache(); + + // 直接触发 UI 更新 + this.handleUpdates(this.state); + }; + + /** + * 更新扩展偏好设置 + * @param updates - 要更新的扩展偏好设置 + */ + updateCustomPreferences = (updates: DeepPartial) => { + if (!this.customPreferencesExtension) { + return; + } + + const sanitizedUpdates = this.sanitizeCustomPreferences( + updates as DeepPartial, + ); + + if (Object.keys(sanitizedUpdates).length === 0) { + return; + } + + this.replaceCustomPreferences( + merge({}, sanitizedUpdates, markRaw(this.customState)), + ); + this.debouncedSave(); + }; + + /** + * 更新偏好设置 + * @param updates - 要更新的偏好设置 + */ + updatePreferences = (updates: DeepPartial) => { + // 深度合并更新内容和当前状态 + const mergedState = merge({}, updates, markRaw(this.state)); + Object.assign(this.state, mergedState); + + // 根据更新的值执行更新 + this.handleUpdates(updates); + + // 保存到缓存(fire-and-forget,通过 debounce 控制频率) + this.debouncedSave(); + }; + + private cloneValue(value: T): T { + if (Array.isArray(value)) { + return value.map((item) => this.cloneValue(item)) as T; + } + + if (value && typeof value === 'object') { + return Object.fromEntries( + Object.entries(value as Record).map( + ([key, nestedValue]) => [key, this.cloneValue(nestedValue)], + ), + ) as T; + } + + return value; + } + + /** + * 处理更新 + * @param updates - 更新的偏好设置 + */ + private handleUpdates(updates: DeepPartial) { + const { theme, app } = updates; + + if ( + theme && + (Object.keys(theme).length > 0 || Reflect.has(theme, 'fontSize')) + ) { + updateCSSVariables(this.state); + } + + if ( + app && + (Reflect.has(app, 'colorGrayMode') || Reflect.has(app, 'colorWeakMode')) + ) { + this.updateColorMode(this.state); + } + } + + /** + * 初始化平台标识 + */ + private initPlatform() { + document.documentElement.dataset.platform = isMacOs() ? 'macOs' : 'window'; + } + + private isAlmostInteger(value: number, epsilon = Number.EPSILON * 10) { + return Math.abs(value - Math.round(value)) < epsilon; + } + + private isValidCustomPreferenceValue( + field: CustomPreferencesField, + value: unknown, + ) { + switch (field.component) { + case 'number': { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return false; + } + + const max = this.resolveNumericConstraint(field.componentProps?.max); + const min = this.resolveNumericConstraint(field.componentProps?.min); + const step = this.resolveNumericConstraint(field.componentProps?.step); + + if (min !== undefined && value < min) { + return false; + } + + if (max !== undefined && value > max) { + return false; + } + + if (step !== undefined) { + if (step <= 0) { + return false; + } + + const stepBase = min ?? 0; + const stepCount = (value - stepBase) / step; + + if (!this.isAlmostInteger(stepCount)) { + return false; + } + } + + return true; + } + case 'select': { + return ( + typeof value === 'string' && + field.options.some((option) => option.value === value) + ); + } + case 'switch': { + return typeof value === 'boolean'; + } + default: { + return typeof value === 'string'; + } + } + } + + /** + * 从缓存加载扩展偏好设置 + * @returns 缓存的扩展偏好设置,如果不存在则返回 null + */ + private async loadCustomFromCache(): Promise { + return this.cache.getItem(STORAGE_KEYS.CUSTOM); + } + + /** + * 从缓存加载偏好设置 + * @returns 缓存的偏好设置,如果不存在则返回 null + */ + private async loadFromCache(): Promise { + return this.cache.getItem(STORAGE_KEYS.MAIN); + } + + private replaceCustomPreferences(preferences: CustomPreferencesRecord) { + Object.keys(this.customState).forEach((key) => { + Reflect.deleteProperty(this.customState, key); + }); + Object.assign(this.customState, preferences); + } + + private resolveCustomPreferencesDefaults( + extension: null | PreferencesExtension, + ) { + if (!extension) { + return {}; + } + + const result: CustomPreferencesRecord = {}; + + for (const field of extension.fields) { + result[field.key] = field.defaultValue; + } + + return result; + } + + private resolveNumericConstraint(value: unknown) { + return typeof value === 'number' && Number.isFinite(value) + ? value + : undefined; + } + + private sanitizeCustomPreferences( + updates: DeepPartial, + ) { + if (!this.customPreferencesExtension) { + return {}; + } + + const result: CustomPreferencesRecord = {}; + + for (const field of this.customPreferencesExtension.fields) { + const value = updates[field.key]; + + if ( + value !== undefined && + this.isValidCustomPreferenceValue(field, value) + ) { + result[field.key] = value; + } + } + + return result; + } + + /** + * 保存偏好设置到缓存 + */ + private async saveToCache() { + try { + await this.cache.setItem(STORAGE_KEYS.MAIN, this.state); + await this.cache.setItem(STORAGE_KEYS.LOCALE, this.state.app.locale); + await this.cache.setItem(STORAGE_KEYS.THEME, this.state.theme.mode); + + if (this.customPreferencesExtension) { + await this.cache.setItem(STORAGE_KEYS.CUSTOM, { + ...this.customState, + }); + return; + } + + await this.cache.removeItem(STORAGE_KEYS.CUSTOM); + } catch (error) { + console.error('Failed to save preferences to cache:', error); + } + } + + /** + * 监听状态和系统偏好设置的变化 + */ + private setupWatcher() { + if (this.isInitialized) { + return; + } + + // 监听断点,判断是否移动端 + const breakpoints = useBreakpoints(breakpointsTailwind); + const isMobile = breakpoints.smaller('md'); + + watch( + () => isMobile.value, + (val) => { + this.updatePreferences({ + app: { isMobile: val }, + }); + }, + { immediate: true }, + ); + + // 监听系统主题偏好设置变化 + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', ({ matches: isDark }) => { + // 仅在自动模式下跟随系统主题 + if (this.state.theme.mode === 'auto') { + // 先应用实际的主题 + this.updatePreferences({ + theme: { mode: isDark ? 'dark' : 'light' }, + }); + // 再恢复为 auto 模式,保持跟随系统的状态 + this.updatePreferences({ + theme: { mode: 'auto' }, + }); + } + }); + } + + /** + * 更新页面颜色模式(灰色、色弱) + * @param preference - 偏好设置 + */ + private updateColorMode(preference: Preferences) { + const { colorGrayMode, colorWeakMode } = preference.app; + const dom = document.documentElement; + + dom.classList.toggle('invert-mode', colorWeakMode); + dom.classList.toggle('grayscale-mode', colorGrayMode); + } +} + +const preferencesManager = new PreferenceManager(); + +export { PreferenceManager, preferencesManager }; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/types.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/types.ts new file mode 100644 index 0000000..759d789 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/types.ts @@ -0,0 +1,446 @@ +import type { + AccessModeType, + AuthPageLayoutType, + BreadcrumbStyleType, + BuiltinThemeType, + ContentCompactType, + DeepPartial, + LayoutHeaderMenuAlignType, + LayoutHeaderModeType, + LayoutType, + LoginExpiredModeType, + NavigationStyleType, + PageTransitionType, + PreferencesButtonPositionType, + TabsStyleType, + ThemeModeType, +} from '@vben-core/typings'; + +type SupportedLanguagesType = 'en-US' | 'zh-CN'; +type CustomPreferencesValue = boolean | number | string; + +interface CustomPreferencesOption { + label: string; + value: TValue; +} + +interface BaseCustomPreferencesField< + TKey extends string = string, + TValue extends CustomPreferencesValue = CustomPreferencesValue, +> { + componentProps?: Record; + defaultValue: TValue; + disabled?: boolean; + key: TKey; + label: string; + placeholder?: string; + tip?: string; +} + +interface CustomPreferencesInputField< + TKey extends string = string, +> extends BaseCustomPreferencesField { + component: 'input'; +} + +interface CustomPreferencesNumberField< + TKey extends string = string, +> extends BaseCustomPreferencesField { + component: 'number'; +} + +interface CustomPreferencesSelectField< + TKey extends string = string, +> extends BaseCustomPreferencesField { + component: 'select'; + options: CustomPreferencesOption[]; +} + +interface CustomPreferencesSwitchField< + TKey extends string = string, +> extends BaseCustomPreferencesField { + component: 'switch'; +} + +type CustomPreferencesRecord = Record; + +type AnyCustomPreferencesField = + | CustomPreferencesInputField + | CustomPreferencesNumberField + | CustomPreferencesSelectField + | CustomPreferencesSwitchField; + +type CustomPreferencesField< + TCustomPreferences extends object = CustomPreferencesRecord, +> = + string extends Extract + ? AnyCustomPreferencesField + : { + [K in Extract< + keyof TCustomPreferences, + string + >]: TCustomPreferences[K] extends boolean + ? CustomPreferencesSwitchField + : TCustomPreferences[K] extends number + ? CustomPreferencesNumberField + : TCustomPreferences[K] extends string + ? CustomPreferencesInputField | CustomPreferencesSelectField + : never; + }[Extract]; + +interface PreferencesExtension< + TCustomPreferences extends object = CustomPreferencesRecord, +> { + fields: Array>; + tabLabel: string; + title?: string; +} + +interface AppPreferences { + /** 权限模式 */ + accessMode: AccessModeType; + /** 登录注册页面布局 */ + authPageLayout: AuthPageLayoutType; + /** 检查更新轮询时间 */ + checkUpdatesInterval: number; + /** 是否开启灰色模式 */ + colorGrayMode: boolean; + /** 是否开启色弱模式 */ + colorWeakMode: boolean; + /** 是否开启紧凑模式 */ + compact: boolean; + /** 是否开启内容紧凑模式 */ + contentCompact: ContentCompactType; + /** 内容紧凑宽度 */ + contentCompactWidth: number; + /** 内容内边距 */ + contentPadding: number; + /** 内容底部内边距 */ + contentPaddingBottom: number; + /** 内容左侧内边距 */ + contentPaddingLeft: number; + /** 内容右侧内边距 */ + contentPaddingRight: number; + /** 内容顶部内边距 */ + contentPaddingTop: number; + /** 应用默认头像 */ + defaultAvatar: string; + /** 默认首页地址 */ + defaultHomePath: string; + /** 开启动态标题 */ + dynamicTitle: boolean; + /** 是否开启检查更新 */ + enableCheckUpdates: boolean; + /** 是否显示复制偏好设置按钮 */ + enableCopyPreferences: boolean; + /** 是否显示偏好设置 */ + enablePreferences: boolean; + /** + * @zh_CN 是否开启refreshToken + */ + enableRefreshToken: boolean; + /** + * @zh_CN 是否开启首选项导航栏吸顶效果 + */ + enableStickyPreferencesNavigationBar: boolean; + /** 是否移动端 */ + isMobile: boolean; + /** 布局方式 */ + layout: LayoutType; + /** 支持的语言 */ + locale: SupportedLanguagesType; + /** 登录过期模式 */ + loginExpiredMode: LoginExpiredModeType; + /** 应用名 */ + name: string; + /** 偏好设置按钮位置 */ + preferencesButtonPosition: PreferencesButtonPositionType; + /** + * @zh_CN 应用时区 + */ + timezone: string; + /** + * @zh_CN 是否开启水印 + */ + watermark: boolean; + /** + * @zh_CN 水印文案 + */ + watermarkContent: string; + /** z-index */ + zIndex: number; +} + +interface BreadcrumbPreferences { + /** 面包屑是否启用 */ + enable: boolean; + /** 面包屑是否只有一个时隐藏 */ + hideOnlyOne: boolean; + /** 面包屑首页图标是否可见 */ + showHome: boolean; + /** 面包屑图标是否可见 */ + showIcon: boolean; + /** 面包屑风格 */ + styleType: BreadcrumbStyleType; +} + +interface CopyrightPreferences { + /** 版权公司名 */ + companyName: string; + /** 版权公司名链接 */ + companySiteLink: string; + /** 版权日期 */ + date: string; + /** 版权是否可见 */ + enable: boolean; + /** 备案号 */ + icp: string; + /** 备案号链接 */ + icpLink: string; + /** 设置面板是否显示*/ + settingShow?: boolean; +} + +interface FooterPreferences { + /** 底栏是否可见 */ + enable: boolean; + /** 底栏是否固定 */ + fixed: boolean; + /** 底栏高度 */ + height: number; +} + +interface HeaderPreferences { + /** 顶栏是否启用 */ + enable: boolean; + /** 顶栏高度 */ + height: number; + /** 顶栏是否隐藏,css-隐藏 */ + hidden: boolean; + /** 顶栏菜单位置 */ + menuAlign: LayoutHeaderMenuAlignType; + /** header显示模式 */ + mode: LayoutHeaderModeType; +} + +interface LogoPreferences { + /** logo是否可见 */ + enable: boolean; + /** logo图片适应方式 */ + fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down'; + /** logo地址 */ + source: string; + /** 暗色主题logo地址 (可选,若不设置则使用 source) */ + sourceDark?: string; +} + +interface NavigationPreferences { + /** 导航菜单手风琴模式 */ + accordion: boolean; + /** 导航菜单是否切割,只在 layout=mixed-nav 生效 */ + split: boolean; + /** 导航菜单风格 */ + styleType: NavigationStyleType; +} + +interface SidebarPreferences { + /** 点击目录时自动激活子菜单 */ + autoActivateChild: boolean; + /** 侧边栏是否折叠 */ + collapsed: boolean; + /** 侧边栏折叠按钮是否可见 */ + collapsedButton: boolean; + /** 侧边栏折叠时,是否显示title */ + collapsedShowTitle: boolean; + /** 侧边栏折叠宽度 */ + collapseWidth: number; + /** 侧边栏菜单拖拽 */ + draggable: boolean; + /** 侧边栏是否可见 */ + enable: boolean; + /** 菜单自动展开状态 */ + expandOnHover: boolean; + /** 侧边栏扩展区域是否折叠 */ + extraCollapse: boolean; + /** 侧边栏扩展区域折叠宽度 */ + extraCollapsedWidth: number; + /** 侧边栏固定按钮是否可见 */ + fixedButton: boolean; + /** 侧边栏是否隐藏 - css */ + hidden: boolean; + /** 混合侧边栏宽度 */ + mixedWidth: number; + /** 侧边栏宽度 */ + width: number; +} + +interface ShortcutKeyPreferences { + /** 是否启用快捷键-全局 */ + enable: boolean; + /** 是否启用全局关闭窗口快捷键 */ + globalEscape: boolean; + /** 是否启用全局锁屏快捷键 */ + globalLockScreen: boolean; + /** 是否启用全局注销快捷键 */ + globalLogout: boolean; + /** 是否启用全局偏好设置快捷键 */ + globalPreferences: boolean; + /** 是否启用全局搜索快捷键 */ + globalSearch: boolean; +} + +interface TabbarPreferences { + /** 是否开启多标签页拖拽 */ + draggable: boolean; + /** 是否开启多标签页 */ + enable: boolean; + /** 标签页高度 */ + height: number; + /** 开启标签页缓存功能 */ + keepAlive: boolean; + /** 限制最大数量 */ + maxCount: number; + /** 是否点击中键时关闭标签 */ + middleClickToClose: boolean; + /** 是否持久化标签 */ + persist: boolean; + /** 是否开启多标签页图标 */ + showIcon: boolean; + /** 显示最大化按钮 */ + showMaximize: boolean; + /** 显示更多按钮 */ + showMore: boolean; + /** 显示刷新按钮 */ + showRefresh: boolean; + /** 标签页风格 */ + styleType: TabsStyleType; + /** 是否开启访问历史记录 */ + visitHistory: boolean; + /** 是否开启鼠标滚轮响应 */ + wheelable: boolean; +} + +interface ThemePreferences { + /** 内置主题名 */ + builtinType: BuiltinThemeType; + /** 错误色 */ + colorDestructive: string; + /** 主题色 */ + colorPrimary: string; + /** 成功色 */ + colorSuccess: string; + /** 警告色 */ + colorWarning: string; + /** 字体大小(单位:px) */ + fontSize: number; + /** 当前主题 */ + mode: ThemeModeType; + /** 圆角 */ + radius: string; + /** 是否开启半深色header(只在theme='light'时生效) */ + semiDarkHeader: boolean; + /** 是否开启半深色菜单(只在theme='light'时生效) */ + semiDarkSidebar: boolean; + /** 是否开启半深色子菜单(只在theme='light'时生效) */ + semiDarkSidebarSub: boolean; +} + +interface TransitionPreferences { + /** 页面切换动画是否启用 */ + enable: boolean; + // /** 是否开启页面加载loading */ + loading: boolean; + /** 页面切换动画 */ + name: PageTransitionType | string; + /** 是否开启页面加载进度动画 */ + progress: boolean; +} + +interface WidgetPreferences { + /** 是否启用全屏部件 */ + fullscreen: boolean; + /** 是否启用全局搜索部件 */ + globalSearch: boolean; + /** 是否启用语言切换部件 */ + languageToggle: boolean; + /** 是否开启锁屏功能 */ + lockScreen: boolean; + /** 是否显示通知部件 */ + notification: boolean; + /** 显示刷新按钮 */ + refresh: boolean; + /** 是否显示侧边栏显示/隐藏部件 */ + sidebarToggle: boolean; + /** 是否显示主题切换部件 */ + themeToggle: boolean; + /** 是否显示时区部件 */ + timezone: boolean; +} + +interface Preferences { + /** 全局配置 */ + app: AppPreferences; + /** 顶栏配置 */ + breadcrumb: BreadcrumbPreferences; + /** 版权配置 */ + copyright: CopyrightPreferences; + /** 底栏配置 */ + footer: FooterPreferences; + /** 面包屑配置 */ + header: HeaderPreferences; + /** logo配置 */ + logo: LogoPreferences; + /** 导航配置 */ + navigation: NavigationPreferences; + /** 快捷键配置 */ + shortcutKeys: ShortcutKeyPreferences; + /** 侧边栏配置 */ + sidebar: SidebarPreferences; + /** 标签页配置 */ + tabbar: TabbarPreferences; + /** 主题配置 */ + theme: ThemePreferences; + /** 动画配置 */ + transition: TransitionPreferences; + /** 功能配置 */ + widget: WidgetPreferences; +} + +type PreferencesKeys = keyof Preferences; + +interface InitialOptions< + TCustomPreferences extends object = CustomPreferencesRecord, +> { + extension?: PreferencesExtension; + namespace: string; + overrides?: DeepPartial; +} +export type { + AnyCustomPreferencesField, + AppPreferences, + BaseCustomPreferencesField, + BreadcrumbPreferences, + CustomPreferencesField, + CustomPreferencesInputField, + CustomPreferencesNumberField, + CustomPreferencesOption, + CustomPreferencesRecord, + CustomPreferencesSelectField, + CustomPreferencesSwitchField, + CustomPreferencesValue, + FooterPreferences, + HeaderPreferences, + InitialOptions, + LogoPreferences, + NavigationPreferences, + Preferences, + PreferencesExtension, + PreferencesKeys, + ShortcutKeyPreferences, + SidebarPreferences, + SupportedLanguagesType, + TabbarPreferences, + ThemePreferences, + TransitionPreferences, + WidgetPreferences, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/update-css-variables.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/update-css-variables.ts new file mode 100644 index 0000000..6ee5f74 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/update-css-variables.ts @@ -0,0 +1,129 @@ +import type { Preferences } from './types'; + +import { generatorColorVariables } from '@vben-core/shared/color'; +import { updateCSSVariables as executeUpdateCSSVariables } from '@vben-core/shared/utils'; + +import { BUILT_IN_THEME_PRESETS } from './constants'; + +/** + * 更新主题的 CSS 变量以及其他 CSS 变量 + * @param preferences - 当前偏好设置对象,它的主题值将被用来设置文档的主题。 + */ +function updateCSSVariables(preferences: Preferences) { + // 当修改到颜色变量时,更新 css 变量 + const root = document.documentElement; + if (!root) { + return; + } + + const theme = preferences?.theme ?? {}; + + const { builtinType, mode, radius } = theme; + + // html 设置 dark 类 + if (Reflect.has(theme, 'mode')) { + const dark = isDarkTheme(mode); + root.classList.toggle('dark', dark); + } + + // html 设置 data-theme=[builtinType] + if (Reflect.has(theme, 'builtinType')) { + const rootTheme = root.dataset.theme; + if (rootTheme !== builtinType) { + root.dataset.theme = builtinType; + } + } + + // 获取当前的内置主题 + const currentBuiltType = [...BUILT_IN_THEME_PRESETS].find( + (item) => item.type === builtinType, + ); + + let builtinTypeColorPrimary: string | undefined = ''; + + if (currentBuiltType) { + const isDark = isDarkTheme(preferences.theme.mode); + // 设置不同主题的主要颜色 + const color = isDark + ? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor + : currentBuiltType.primaryColor; + builtinTypeColorPrimary = color || currentBuiltType.color; + } + + // 如果内置主题颜色和自定义颜色都不存在,则不更新主题颜色 + if ( + builtinTypeColorPrimary || + Reflect.has(theme, 'colorPrimary') || + Reflect.has(theme, 'colorDestructive') || + Reflect.has(theme, 'colorSuccess') || + Reflect.has(theme, 'colorWarning') + ) { + // preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary; + updateMainColorVariables(preferences); + } + + // 更新圆角 + if (Reflect.has(theme, 'radius')) { + document.documentElement.style.setProperty('--radius', `${radius}rem`); + } + + // 更新字体大小 + if (Reflect.has(theme, 'fontSize')) { + const fontSize = theme.fontSize; + document.documentElement.style.setProperty( + '--font-size-base', + `${fontSize}px`, + ); + document.documentElement.style.setProperty( + '--menu-font-size', + `calc(${fontSize}px * 0.875)`, + ); + } +} + +/** + * 更新主要的 CSS 变量 + * @param preference - 当前偏好设置对象,它的颜色值将被转换成 HSL 格式并设置为 CSS 变量。 + */ +function updateMainColorVariables(preference: Preferences) { + if (!preference.theme) { + return; + } + const { colorDestructive, colorPrimary, colorSuccess, colorWarning } = + preference.theme; + + const colorVariables = generatorColorVariables([ + { color: colorPrimary, name: 'primary' }, + { alias: 'warning', color: colorWarning, name: 'yellow' }, + { alias: 'success', color: colorSuccess, name: 'green' }, + { alias: 'destructive', color: colorDestructive, name: 'red' }, + ]); + + // 要设置的 CSS 变量映射 + const colorMappings = { + '--green-500': '--success', + '--primary-500': '--primary', + '--red-500': '--destructive', + '--yellow-500': '--warning', + }; + + // 统一处理颜色变量的更新 + Object.entries(colorMappings).forEach(([sourceVar, targetVar]) => { + const colorValue = colorVariables[sourceVar]; + if (colorValue) { + document.documentElement.style.setProperty(targetVar, colorValue); + } + }); + + executeUpdateCSSVariables(colorVariables); +} + +function isDarkTheme(theme: string) { + let dark = theme === 'dark'; + if (theme === 'auto') { + dark = window.matchMedia('(prefers-color-scheme: dark)').matches; + } + return dark; +} + +export { isDarkTheme, updateCSSVariables }; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/src/use-preferences.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/src/use-preferences.ts new file mode 100644 index 0000000..2a075f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/src/use-preferences.ts @@ -0,0 +1,281 @@ +import { computed } from 'vue'; + +import { diff } from '@vben-core/shared/utils'; + +import { preferencesManager } from './preferences'; +import { isDarkTheme } from './update-css-variables'; + +function usePreferences() { + const preferences = preferencesManager.getPreferences(); + const customPreferences = preferencesManager.getCustomPreferences(); + const initialPreferences = preferencesManager.getInitialPreferences(); + const initialCustomPreferences = + preferencesManager.getInitialCustomPreferences(); + const preferencesExtension = computed(() => + preferencesManager.getPreferencesExtension(), + ); + /** + * @zh_CN 计算偏好设置的变化 + */ + const diffPreference = computed(() => { + return diff(initialPreferences, preferences); + }); + + const diffCustomPreference = computed(() => { + return diff(initialCustomPreferences, customPreferences); + }); + + const appPreferences = computed(() => preferences.app); + + const shortcutKeysPreferences = computed(() => preferences.shortcutKeys); + + /** + * @zh_CN 判断是否为暗黑模式 + * @param preferences - 当前偏好设置对象,它的主题值将被用来判断是否为暗黑模式。 + * @returns 如果主题为暗黑模式,返回 true,否则返回 false。 + */ + const isDark = computed(() => { + return isDarkTheme(preferences.theme.mode); + }); + + const locale = computed(() => { + return appPreferences.value.locale; + }); + + const isMobile = computed(() => { + return appPreferences.value.isMobile; + }); + + const theme = computed(() => { + return isDark.value ? 'dark' : 'light'; + }); + + /** + * @zh_CN 布局方式 + */ + const layout = computed(() => + isMobile.value ? 'sidebar-nav' : appPreferences.value.layout, + ); + + /** + * @zh_CN 是否显示顶栏 + */ + const isShowHeaderNav = computed(() => { + return preferences.header.enable; + }); + + /** + * @zh_CN 是否全屏显示content,不需要侧边、底部、顶部、tab区域 + */ + const isFullContent = computed( + () => appPreferences.value.layout === 'full-content', + ); + + /** + * @zh_CN 是否侧边导航模式 + */ + const isSideNav = computed( + () => appPreferences.value.layout === 'sidebar-nav', + ); + + /** + * @zh_CN 是否侧边混合模式 + */ + const isSideMixedNav = computed( + () => appPreferences.value.layout === 'sidebar-mixed-nav', + ); + + /** + * @zh_CN 是否为头部导航模式 + */ + const isHeaderNav = computed( + () => appPreferences.value.layout === 'header-nav', + ); + + /** + * @zh_CN 是否为头部混合导航模式 + */ + const isHeaderMixedNav = computed( + () => appPreferences.value.layout === 'header-mixed-nav', + ); + + /** + * @zh_CN 是否为顶部通栏+侧边导航模式 + */ + const isHeaderSidebarNav = computed( + () => appPreferences.value.layout === 'header-sidebar-nav', + ); + + /** + * @zh_CN 是否为混合导航模式 + */ + const isMixedNav = computed( + () => appPreferences.value.layout === 'mixed-nav', + ); + + /** + * @zh_CN 是否包含侧边导航模式 + */ + const isSideMode = computed(() => { + return ( + isMixedNav.value || + isSideMixedNav.value || + isSideNav.value || + isHeaderMixedNav.value || + isHeaderSidebarNav.value + ); + }); + + const sidebarCollapsed = computed(() => { + return preferences.sidebar.collapsed; + }); + + /** + * @zh_CN 是否开启keep-alive + * 在tabs可见以及开启keep-alive的情况下才开启 + */ + const keepAlive = computed( + () => preferences.tabbar.enable && preferences.tabbar.keepAlive, + ); + + /** + * @zh_CN 登录注册页面布局是否为左侧 + */ + const authPanelLeft = computed(() => { + return appPreferences.value.authPageLayout === 'panel-left'; + }); + + /** + * @zh_CN 登录注册页面布局是否为右侧 + */ + const authPanelRight = computed(() => { + return appPreferences.value.authPageLayout === 'panel-right'; + }); + + /** + * @zh_CN 登录注册页面布局是否为中间 + */ + const authPanelCenter = computed(() => { + return appPreferences.value.authPageLayout === 'panel-center'; + }); + + /** + * @zh_CN 内容是否已经最大化 + * 排除 full-content模式 + */ + const contentIsMaximize = computed(() => { + const headerIsHidden = preferences.header.hidden; + const sidebarIsHidden = preferences.sidebar.hidden; + return headerIsHidden && sidebarIsHidden && !isFullContent.value; + }); + + /** + * @zh_CN 是否启用全局搜索快捷键 + */ + const globalSearchShortcutKey = computed(() => { + const { enable, globalSearch } = shortcutKeysPreferences.value; + return enable && globalSearch; + }); + + /** + * @zh_CN 是否启用全局注销快捷键 + */ + const globalLogoutShortcutKey = computed(() => { + const { enable, globalLogout } = shortcutKeysPreferences.value; + return enable && globalLogout; + }); + + /** + * @zh_CN 是否启用全局注销快捷键 + */ + const globalEscapeShortcutKey = computed(() => { + const { enable, globalEscape } = shortcutKeysPreferences.value; + return enable && globalEscape; + }); + + const globalLockScreenShortcutKey = computed(() => { + const { enable, globalLockScreen } = shortcutKeysPreferences.value; + return enable && globalLockScreen; + }); + + /** + * @zh_CN 偏好设置按钮位置 + */ + const preferencesButtonPosition = computed(() => { + const { enablePreferences, preferencesButtonPosition } = preferences.app; + // 如果没有启用偏好设置按钮 + if (!enablePreferences) { + return { + fixed: false, + header: false, + userDropdown: false, + }; + } + + const { header, sidebar } = preferences; + const headerHidden = header.hidden; + const sidebarHidden = sidebar.hidden; + + const contentIsMaximize = headerHidden && sidebarHidden; + + const isHeaderPosition = preferencesButtonPosition === 'header'; + const isUserDropdownPosition = + preferencesButtonPosition === 'user-dropdown'; + + // 如果设置了固定位置 + if (preferencesButtonPosition !== 'auto') { + return { + fixed: preferencesButtonPosition === 'fixed', + header: isHeaderPosition, + userDropdown: isUserDropdownPosition, + }; + } + + // 如果是全屏模式或者没有固定在顶部, + const fixed = + contentIsMaximize || + isFullContent.value || + isMobile.value || + !isShowHeaderNav.value; + + return { + fixed, + header: !fixed, + userDropdown: !fixed && isUserDropdownPosition, + }; + }); + + return { + authPanelCenter, + authPanelLeft, + authPanelRight, + contentIsMaximize, + customPreferences, + diffPreference, + diffCustomPreference, + globalLockScreenShortcutKey, + globalLogoutShortcutKey, + globalEscapeShortcutKey, + globalSearchShortcutKey, + isDark, + isFullContent, + isHeaderMixedNav, + isHeaderNav, + isHeaderSidebarNav, + isMixedNav, + isMobile, + isSideMixedNav, + isSideMode, + isSideNav, + keepAlive, + layout, + locale, + preferencesExtension, + preferencesButtonPosition, + sidebarCollapsed, + theme, + app: appPreferences.value, + }; +} + +export { usePreferences }; diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/preferences/tsconfig.json new file mode 100644 index 0000000..bbb8050 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src", "__tests__"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/preferences/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/preferences/tsdown.config.ts new file mode 100644 index 0000000..c4f51c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/preferences/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/README.md b/deploy/fba/fba-ui-src/packages/@core/ui-kit/README.md new file mode 100644 index 0000000..a1746f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/README.md @@ -0,0 +1,3 @@ +# ui-kit + +用于管理公共组件、不同UI组件库封装的组件 diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts new file mode 100644 index 0000000..a4c05b5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts @@ -0,0 +1,266 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FormApi } from '../src/form-api'; + +describe('formApi', () => { + let formApi: FormApi; + + beforeEach(() => { + formApi = new FormApi(); + }); + + it('should initialize with default state', () => { + expect(formApi.state).toEqual( + expect.objectContaining({ + actionWrapperClass: '', + collapsed: false, + collapsedRows: 1, + commonConfig: {}, + handleReset: undefined, + handleSubmit: undefined, + layout: 'horizontal', + resetButtonOptions: {}, + schema: [], + showCollapseButton: false, + showDefaultActions: true, + submitButtonOptions: {}, + wrapperClass: 'grid-cols-1', + }), + ); + expect(formApi.isMounted).toBe(false); + }); + + it('should mount form actions', async () => { + const formActions: any = { + meta: {}, + resetForm: vi.fn(), + setFieldValue: vi.fn(), + setValues: vi.fn(), + submitForm: vi.fn(), + validate: vi.fn(), + values: { name: 'test' }, + }; + + await formApi.mount(formActions); + expect(formApi.isMounted).toBe(true); + expect(formApi.form).toEqual(formActions); + expect(formApi.getFieldComponentRef('name')).toBeUndefined(); + }); + + it('should get values from form', async () => { + const formActions: any = { + meta: {}, + values: { name: 'test' }, + }; + + await formApi.mount(formActions, new Map()); + const values = await formApi.getValues(); + expect(values).toEqual({ name: 'test' }); + }); + + it('should format schema values when getting values', async () => { + formApi.setState({ + schema: [ + { + component: 'range-picker', + fieldName: 'filters.range', + valueFormat: (value, setValue) => { + setValue('filters.startTime', value?.[0]); + setValue('filters.endTime', value?.[1]); + }, + }, + ], + }); + + const formActions: any = { + meta: {}, + values: { + filters: { + range: [1_710_000_000_000, 1_720_000_000_000], + }, + }, + }; + const originalValuesSnapshot = structuredClone(formActions.values); + + await formApi.mount(formActions, new Map()); + + expect(formApi.getLatestSubmissionValues()).toEqual({ + filters: { + endTime: 1_720_000_000_000, + startTime: 1_710_000_000_000, + }, + }); + + const values = await formApi.getValues(); + expect(values).toEqual({ + filters: { + endTime: 1_720_000_000_000, + startTime: 1_710_000_000_000, + }, + }); + expect(formActions.values).toEqual(originalValuesSnapshot); + }); + + it('should set field value', async () => { + const setFieldValueMock = vi.fn(); + const formActions: any = { + meta: {}, + setFieldValue: setFieldValueMock, + values: { name: 'test' }, + }; + + await formApi.mount(formActions, new Map()); + await formApi.setFieldValue('name', 'new value'); + expect(setFieldValueMock).toHaveBeenCalledWith( + 'name', + 'new value', + undefined, + ); + }); + + it('should reset form', async () => { + const resetFormMock = vi.fn(); + const formActions: any = { + meta: {}, + resetForm: resetFormMock, + values: { name: 'test' }, + }; + + await formApi.mount(formActions, new Map()); + await formApi.resetForm(); + expect(resetFormMock).toHaveBeenCalled(); + }); + + it('should call handleSubmit on submit', async () => { + const handleSubmitMock = vi.fn(); + const formActions: any = { + meta: {}, + submitForm: vi.fn().mockResolvedValue(true), + values: { name: 'test' }, + }; + + const state = { + handleSubmit: handleSubmitMock, + }; + + formApi.setState(state); + await formApi.mount(formActions, new Map()); + + const result = await formApi.submitForm(); + expect(formActions.submitForm).toHaveBeenCalled(); + expect(handleSubmitMock).toHaveBeenCalledWith({ name: 'test' }); + expect(result).toEqual({ name: 'test' }); + }); + + it('should unmount form and reset state', () => { + formApi.unmount(); + expect(formApi.isMounted).toBe(false); + }); + + it('should clear component refs on unmount before mounting again', async () => { + const formActions: any = { + meta: {}, + resetForm: vi.fn(), + values: { name: 'test' }, + }; + const staleMap = new Map([ + [ + 'name', + { + $: { + type: { name: 'MockComponent' }, + }, + $el: {}, + }, + ], + ]); + + await formApi.mount(formActions, staleMap); + expect(formApi.getFieldComponentRef('name')).toEqual({ + $: { + type: { name: 'MockComponent' }, + }, + $el: {}, + }); + + formApi.unmount(); + expect(formApi.getFieldComponentRef('name')).toBeUndefined(); + + await formApi.mount(formActions); + expect(formApi.getFieldComponentRef('name')).toBeUndefined(); + }); + + it('should validate form', async () => { + const validateMock = vi.fn().mockResolvedValue(true); + const formActions: any = { + meta: {}, + validate: validateMock, + }; + + await formApi.mount(formActions, new Map()); + const isValid = await formApi.validate(); + expect(validateMock).toHaveBeenCalled(); + expect(isValid).toBe(true); + }); +}); + +describe('updateSchema', () => { + let instance: FormApi; + + beforeEach(() => { + instance = new FormApi(); + instance.state = { + schema: [ + { component: 'text', fieldName: 'name' }, + { component: 'number', fieldName: 'age', label: 'Age' }, + ], + }; + }); + + it('should update the schema correctly when fieldName matches', () => { + const newSchema = [ + { component: 'text', fieldName: 'name' }, + { component: 'number', fieldName: 'age', label: 'Age' }, + ]; + + instance.updateSchema(newSchema); + + expect(instance.state?.schema?.[0]?.component).toBe('text'); + expect(instance.state?.schema?.[1]?.label).toBe('Age'); + }); + + it('should log an error if fieldName is missing in some items', () => { + const newSchema: any[] = [ + { component: 'textarea', fieldName: 'name' }, + { component: 'number' }, + ]; + + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + instance.updateSchema(newSchema); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + 'All items in the schema array must have a valid `fieldName` property to be updated', + ); + }); + + it('should not update schema if fieldName does not match', () => { + const newSchema = [{ component: 'textarea', fieldName: 'unknown' }]; + + instance.updateSchema(newSchema); + + expect(instance.state?.schema?.[0]?.component).toBe('text'); + expect(instance.state?.schema?.[1]?.component).toBe('number'); + }); + + it('should not update schema if updatedMap is empty', () => { + const newSchema: any[] = [{ component: 'textarea' }]; + + instance.updateSchema(newSchema); + + expect(instance.state?.schema?.[0]?.component).toBe('text'); + expect(instance.state?.schema?.[1]?.component).toBe('number'); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/package.json new file mode 100644 index 0000000..8becac7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vben-core/form-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/form-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vee-validate/zod": "catalog:", + "@vueuse/core": "catalog:", + "vee-validate": "catalog:", + "vue": "catalog:", + "zod": "catalog:", + "zod-defaults": "catalog:" + }, + "devDependencies": { + "unplugin-vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-actions.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-actions.vue new file mode 100644 index 0000000..d09e1a6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-actions.vue @@ -0,0 +1,184 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-field-array.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-field-array.vue new file mode 100644 index 0000000..1d7a073 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/components/form-field-array.vue @@ -0,0 +1,177 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/config.ts new file mode 100644 index 0000000..9448d2a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/config.ts @@ -0,0 +1,90 @@ +import type { Component } from 'vue'; + +import type { + BaseFormComponentType, + FormCommonConfig, + VbenFormAdapterOptions, +} from './types'; + +import { h } from 'vue'; + +import { + VbenButton, + VbenCheckbox, + Input as VbenInput, + VbenInputPassword, + VbenPinInput, + VbenSelect, +} from '@vben-core/shadcn-ui'; +import { globalShareState } from '@vben-core/shared/global-state'; + +import { defineRule } from 'vee-validate'; + +import VbenFormFieldArray from './components/form-field-array.vue'; + +const DEFAULT_MODEL_PROP_NAME = 'modelValue'; + +export const DEFAULT_FORM_COMMON_CONFIG: FormCommonConfig = {}; + +export const COMPONENT_MAP: Record = { + DefaultButton: h(VbenButton, { size: 'sm', variant: 'outline' }), + PrimaryButton: h(VbenButton, { size: 'sm', variant: 'default' }), + VbenCheckbox, + VbenFormFieldArray, + VbenInput, + VbenInputPassword, + VbenPinInput, + VbenSelect, +}; + +export const COMPONENT_BIND_EVENT_MAP: Partial< + Record +> = { + VbenCheckbox: 'checked', +}; + +export function setupVbenForm< + T extends BaseFormComponentType = BaseFormComponentType, +>(options: VbenFormAdapterOptions) { + const { config, defineRules } = options; + + const { + disabledOnChangeListener = true, + disabledOnInputListener = true, + emptyStateValue = undefined, + } = (config || {}) as FormCommonConfig; + + Object.assign(DEFAULT_FORM_COMMON_CONFIG, { + disabledOnChangeListener, + disabledOnInputListener, + emptyStateValue, + }); + + if (defineRules) { + for (const key of Object.keys(defineRules)) { + defineRule(key, defineRules[key as never]); + } + } + + const baseModelPropName = + config?.baseModelPropName ?? DEFAULT_MODEL_PROP_NAME; + const modelPropNameMap = config?.modelPropNameMap as + | Record + | undefined; + + const components = globalShareState.getComponents(); + + for (const component of Object.keys(components)) { + const key = component as BaseFormComponentType; + COMPONENT_MAP[key] = components[component as never]; + + if (baseModelPropName !== DEFAULT_MODEL_PROP_NAME) { + COMPONENT_BIND_EVENT_MAP[key] = baseModelPropName; + } + + // 覆盖特殊组件的modelPropName + if (modelPropNameMap && modelPropNameMap[key]) { + COMPONENT_BIND_EVENT_MAP[key] = modelPropNameMap[key]; + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/field-name.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/field-name.ts new file mode 100644 index 0000000..0e2a2da --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/field-name.ts @@ -0,0 +1,14 @@ +export function resolveFieldNamePath(fieldName: string) { + if (fieldName.startsWith('[') && fieldName.endsWith(']')) { + const rawKey = fieldName.slice(1, -1); + return { + pathSegments: [rawKey], + rawKey, + }; + } + + return { + pathSegments: fieldName.match(/[^.[\]]+/g) ?? [], + rawKey: undefined, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-api.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-api.ts new file mode 100644 index 0000000..1b84246 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -0,0 +1,699 @@ +import type { + FormState, + GenericObject, + ResetFormOpts, + ValidationOptions, +} from 'vee-validate'; + +import type { ComponentPublicInstance } from 'vue'; + +import type { Recordable } from '@vben-core/typings'; + +import type { FormActions, FormSchema, VbenFormProps } from './types'; + +import { isRef, toRaw } from 'vue'; + +import { Store } from '@vben-core/shared/store'; +import { + bindMethods, + cloneDeep, + createMerge, + formatDate, + get, + isDate, + isDayjsObject, + isFunction, + isObject, + mergeWithArrayOverride, + set, + StateHandler, +} from '@vben-core/shared/utils'; + +import { resolveFieldNamePath } from './field-name'; + +function getDefaultState(): VbenFormProps { + return { + actionWrapperClass: '', + collapsed: false, + collapsedRows: 1, + collapseTriggerResize: false, + commonConfig: {}, + handleReset: undefined, + handleSubmit: undefined, + handleValuesChange: undefined, + handleCollapsedChange: undefined, + layout: 'horizontal', + resetButtonOptions: {}, + schema: [], + scrollToFirstError: false, + showCollapseButton: false, + showDefaultActions: true, + submitButtonOptions: {}, + submitOnChange: false, + submitOnEnter: false, + wrapperClass: 'grid-cols-1', + }; +} + +export class FormApi { + // private api: Pick; + public form = {} as FormActions; + isMounted = false; + + public state: null | VbenFormProps = null; + stateHandler: StateHandler; + + public store: Store; + + /** + * 组件实例映射 + */ + private componentRefMap: Map = new Map(); + + // 最后一次点击提交时的表单值 + private latestSubmissionValues: null | Recordable = null; + + private prevState: null | VbenFormProps = null; + + constructor(options: VbenFormProps = {}) { + const { ...storeState } = options; + + const defaultState = getDefaultState(); + + this.store = new Store({ + ...defaultState, + ...storeState, + }); + + this.store.subscribe((state) => { + this.prevState = this.state; + this.state = state; + this.updateState(); + }); + + this.state = this.store.state; + this.stateHandler = new StateHandler(); + bindMethods(this); + } + + /** + * 获取字段组件实例 + * @param fieldName 字段名 + * @returns 组件实例 + */ + getFieldComponentRef( + fieldName: string, + ): T | undefined { + let target = this.componentRefMap.has(fieldName) + ? (this.componentRefMap.get(fieldName) as ComponentPublicInstance) + : undefined; + if ( + target && + target.$.type.name === 'AsyncComponentWrapper' && + target.$.subTree.ref + ) { + if (Array.isArray(target.$.subTree.ref)) { + if ( + target.$.subTree.ref.length > 0 && + isRef(target.$.subTree.ref[0]?.r) + ) { + target = target.$.subTree.ref[0]?.r.value as ComponentPublicInstance; + } + } else if (isRef(target.$.subTree.ref.r)) { + target = target.$.subTree.ref.r.value as ComponentPublicInstance; + } + } + return target as T; + } + + /** + * 获取当前聚焦的字段,如果没有聚焦的字段则返回undefined + */ + getFocusedField() { + for (const fieldName of this.componentRefMap.keys()) { + const ref = this.getFieldComponentRef(fieldName); + if (ref) { + let el: HTMLElement | null = null; + if (ref instanceof HTMLElement) { + el = ref; + } else if (ref.$el instanceof HTMLElement) { + el = ref.$el; + } + if (!el) { + continue; + } + if ( + el === document.activeElement || + el.contains(document.activeElement) + ) { + return fieldName; + } + } + } + return undefined; + } + + getLatestSubmissionValues() { + return this.latestSubmissionValues || {}; + } + + getState() { + return this.state; + } + + async getValues>() { + const form = await this.getForm(); + const values = form.values + ? this.handleRangeTimeValue(cloneDeep(toRaw(form.values))) + : {}; + return this.handleValueFormat(values) as T; + } + + async isFieldValid(fieldName: string) { + const form = await this.getForm(); + return form.isFieldValid(fieldName); + } + + merge(formApi: FormApi) { + const chain = [this, formApi]; + const proxy = new Proxy(formApi, { + get(target: any, prop: any) { + if (prop === 'merge') { + return (nextFormApi: FormApi) => { + chain.push(nextFormApi); + return proxy; + }; + } + if (prop === 'submitAllForm') { + return async (needMerge: boolean = true) => { + try { + const results = await Promise.all( + chain.map(async (api) => { + const validateResult = await api.validate(); + if (!validateResult.valid) { + return; + } + const rawValues = toRaw((await api.getValues()) || {}); + return rawValues; + }), + ); + if (needMerge) { + const mergedResults = Object.assign({}, ...results); + return mergedResults; + } + return results; + } catch (error) { + console.error('Validation error:', error); + } + }; + } + return target[prop]; + }, + }); + + return proxy; + } + + mount(formActions: FormActions, componentRefMap?: Map) { + if (!this.isMounted) { + Object.assign(this.form, formActions); + this.stateHandler.setConditionTrue(); + const initialValues = this.form.values + ? this.handleRangeTimeValue(cloneDeep(toRaw(this.form.values))) + : {}; + this.setLatestSubmissionValues({ + ...this.handleValueFormat(initialValues), + }); + this.componentRefMap = + componentRefMap ?? this.componentRefMap ?? new Map(); + this.isMounted = true; + } + } + + /** + * 根据字段名移除表单项 + * @param fields + */ + async removeSchemaByFields(fields: string[]) { + const fieldSet = new Set(fields); + const schema = this.state?.schema ?? []; + + const filterSchema = schema.filter((item) => !fieldSet.has(item.fieldName)); + + this.setState({ + schema: filterSchema, + }); + } + + /** + * 重置表单 + */ + async resetForm( + state?: Partial> | undefined, + opts?: Partial, + ) { + const form = await this.getForm(); + return form.resetForm(state, opts); + } + + async resetValidate() { + const form = await this.getForm(); + const fields = Object.keys(form.errors.value); + fields.forEach((field) => { + form.setFieldError(field, undefined); + }); + } + + /** + * 滚动到第一个错误字段 + * @param errors 验证错误对象 + */ + scrollToFirstError(errors: Record | string) { + // https://github.com/logaretm/vee-validate/discussions/3835 + const firstErrorFieldName = + typeof errors === 'string' ? errors : Object.keys(errors)[0]; + + if (!firstErrorFieldName) { + return; + } + + let el = document.querySelector( + `[name="${firstErrorFieldName}"]`, + ) as HTMLElement; + + // 如果通过 name 属性找不到,尝试通过组件引用查找, 正常情况下不会走到这,怕哪天 vee-validate 改了 name 属性有个兜底的 + if (!el) { + const componentRef = this.getFieldComponentRef(firstErrorFieldName); + if (componentRef && componentRef.$el instanceof HTMLElement) { + el = componentRef.$el; + } + } + + if (el) { + // 滚动到错误字段,添加一些偏移量以确保字段完全可见 + el.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'nearest', + }); + } + } + + async setFieldValue(field: string, value: any, shouldValidate?: boolean) { + const form = await this.getForm(); + form.setFieldValue(field, value, shouldValidate); + } + + setLatestSubmissionValues(values: null | Recordable) { + this.latestSubmissionValues = { ...toRaw(values) }; + } + + setState( + stateOrFn: + | ((prev: VbenFormProps) => Partial) + | Partial, + ) { + if (isFunction(stateOrFn)) { + this.store.setState((prev) => { + return mergeWithArrayOverride(stateOrFn(prev), prev); + }); + } else { + this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev)); + } + } + + /** + * 设置表单值 + * @param fields record + * @param filterFields 过滤不在schema中定义的字段 默认为true + * @param shouldValidate + */ + async setValues( + fields: Record, + filterFields: boolean = true, + shouldValidate: boolean = false, + ) { + const form = await this.getForm(); + if (!filterFields) { + form.setValues(fields, shouldValidate); + return; + } + + /** + * 合并算法有待改进,目前的算法不支持object类型的值。 + * antd的日期时间相关组件的值类型为dayjs对象 + * element-plus的日期时间相关组件的值类型可能为Date对象 + * 以上两种类型需要排除深度合并 + */ + const fieldMergeFn = createMerge((obj, key, value) => { + if (key in obj) { + obj[key] = + !Array.isArray(obj[key]) && + isObject(obj[key]) && + !isDayjsObject(obj[key]) && + !isDate(obj[key]) + ? fieldMergeFn(value, obj[key]) + : value; + } + return true; + }); + const filteredFields = fieldMergeFn(fields, form.values); + form.setValues(filteredFields, shouldValidate); + } + + async submitForm(e?: Event) { + e?.preventDefault(); + e?.stopPropagation(); + const form = await this.getForm(); + await form.submitForm(); + const rawValues = toRaw(await this.getValues()); + await this.state?.handleSubmit?.(rawValues); + + return rawValues; + } + + unmount() { + this.form?.resetForm?.(); + // this.state = null; + this.componentRefMap = new Map(); + this.latestSubmissionValues = null; + this.isMounted = false; + this.stateHandler.reset(); + } + + updateSchema(schema: Partial[]) { + const updated: Partial[] = [...schema]; + const hasField = updated.every( + (item) => Reflect.has(item, 'fieldName') && item.fieldName, + ); + + if (!hasField) { + console.error( + 'All items in the schema array must have a valid `fieldName` property to be updated', + ); + return; + } + const currentSchema = [...(this.state?.schema ?? [])]; + + const updatedMap: Record = {}; + + updated.forEach((item) => { + if (item.fieldName) { + updatedMap[item.fieldName] = item; + } + }); + + currentSchema.forEach((schema, index) => { + const updatedData = updatedMap[schema.fieldName]; + if (updatedData) { + currentSchema[index] = mergeWithArrayOverride( + updatedData, + schema, + ) as FormSchema; + } + }); + this.setState({ schema: currentSchema }); + } + + async validate(opts?: Partial) { + const form = await this.getForm(); + + const validateResult = await form.validate(opts); + + if (Object.keys(validateResult?.errors ?? {}).length > 0) { + console.error('validate error', validateResult?.errors); + + if (this.state?.scrollToFirstError) { + this.scrollToFirstError(validateResult.errors); + } + } + return validateResult; + } + + async validateAndSubmitForm() { + const form = await this.getForm(); + const { valid, errors } = await form.validate(); + if (!valid) { + if (this.state?.scrollToFirstError) { + this.scrollToFirstError(errors); + } + return; + } + return await this.submitForm(); + } + + async validateField(fieldName: string, opts?: Partial) { + const form = await this.getForm(); + const validateResult = await form.validateField(fieldName, opts); + + if (Object.keys(validateResult?.errors ?? {}).length > 0) { + console.error('validate error', validateResult?.errors); + + if (this.state?.scrollToFirstError) { + this.scrollToFirstError(fieldName); + } + } + return validateResult; + } + + private deleteValueByFieldName( + values: Record, + fieldName: string, + ) { + const { pathSegments, rawKey } = resolveFieldNamePath(fieldName); + if (rawKey) { + Reflect.deleteProperty(values, rawKey); + return; + } + + if (!pathSegments || pathSegments.length === 0) { + Reflect.deleteProperty(values, fieldName); + return; + } + + let target: Record | undefined = values; + + for (const segment of pathSegments.slice(0, -1)) { + if (!target || !isObject(target)) { + return; + } + target = target[segment]; + } + + if (!target || !isObject(target)) { + return; + } + + const lastPathSegment = pathSegments.at(-1); + if (!lastPathSegment) { + return; + } + + Reflect.deleteProperty(target, lastPathSegment); + } + + private async getForm() { + if (!this.isMounted) { + // 等待form挂载 + await this.stateHandler.waitForCondition(); + } + if (!this.form?.meta) { + throw new Error(' is not mounted'); + } + return this.form; + } + + private handleMultiFields = (originValues: Record) => { + const arrayToStringFields = this.state?.arrayToStringFields; + if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) { + return; + } + + const processFields = (fields: string[], separator: string = ',') => { + this.processFields(fields, separator, originValues, (value, sep) => { + if (Array.isArray(value)) { + return value.join(sep); + } else if (typeof value === 'string') { + // 处理空字符串的情况 + if (value === '') { + return []; + } + // 处理复杂分隔符的情况 + const escapedSeparator = sep.replaceAll( + /[.*+?^${}()|[\]\\]/g, + String.raw`\$&`, + ); + return value.split(new RegExp(escapedSeparator)); + } else { + return value; + } + }); + }; + + // 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2'] + if (arrayToStringFields.every((item) => typeof item === 'string')) { + const lastItem = + arrayToStringFields[arrayToStringFields.length - 1] || ''; + const fields = + lastItem.length === 1 + ? arrayToStringFields.slice(0, -1) + : arrayToStringFields; + const separator = lastItem.length === 1 ? lastItem : ','; + processFields(fields, separator); + return; + } + + // 处理嵌套数组格式 [['field1'], ';'] + arrayToStringFields.forEach((fieldConfig) => { + if (Array.isArray(fieldConfig)) { + const [fields, separator = ','] = fieldConfig; + // 根据类型定义,fields 应该始终是字符串数组 + if (!Array.isArray(fields)) { + console.warn( + `Invalid field configuration: fields should be an array of strings, got ${typeof fields}`, + ); + return; + } + processFields(fields, separator); + } + }); + }; + + private handleRangeTimeValue = (originValues: Record) => { + const values = { ...originValues }; + const fieldMappingTime = this.state?.fieldMappingTime; + + this.handleMultiFields(values); + if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) { + return values; + } + + fieldMappingTime.forEach( + ([field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD']) => { + if (startTimeKey && endTimeKey && values[field] === null) { + Reflect.deleteProperty(values, startTimeKey); + Reflect.deleteProperty(values, endTimeKey); + // delete values[startTimeKey]; + // delete values[endTimeKey]; + } + + if (!values[field]) { + Reflect.deleteProperty(values, field); + // delete values[field]; + return; + } + + const [startTime, endTime] = values[field]; + if (format === null) { + values[startTimeKey] = startTime; + values[endTimeKey] = endTime; + } else if (isFunction(format)) { + values[startTimeKey] = format(startTime, startTimeKey); + values[endTimeKey] = format(endTime, endTimeKey); + } else { + const [startTimeFormat, endTimeFormat] = Array.isArray(format) + ? format + : [format, format]; + + values[startTimeKey] = startTime + ? formatDate(startTime, startTimeFormat) + : undefined; + values[endTimeKey] = endTime + ? formatDate(endTime, endTimeFormat) + : undefined; + } + // delete values[field]; + Reflect.deleteProperty(values, field); + }, + ); + return values; + }; + + private handleValueFormat = (originValues: Record) => { + const values = { ...originValues }; + const currentSchema = this.state?.schema ?? []; + + currentSchema.forEach((schema) => { + if (!schema.valueFormat) { + return; + } + + const fieldName = schema.fieldName; + const value = this.resolveValueByFieldName(values, fieldName); + + this.deleteValueByFieldName(values, fieldName); + + const formattedValue = schema.valueFormat( + value, + (key, nextValue) => { + this.setValueByFieldName(values, key, nextValue); + }, + values, + ); + + if (formattedValue !== undefined) { + this.setValueByFieldName(values, fieldName, formattedValue); + } + }); + + return values; + }; + + private processFields = ( + fields: string[], + separator: string, + originValues: Record, + transformFn: (value: any, separator: string) => any, + ) => { + fields.forEach((field) => { + const value = originValues[field]; + if (value === undefined || value === null) { + return; + } + originValues[field] = transformFn(value, separator); + }); + }; + + private resolveValueByFieldName( + values: Record, + fieldName: string, + ) { + const { rawKey } = resolveFieldNamePath(fieldName); + if (rawKey) { + return values[rawKey]; + } + + return get(values, fieldName); + } + + private setValueByFieldName( + values: Record, + fieldName: string, + value: any, + ) { + const { rawKey } = resolveFieldNamePath(fieldName); + if (rawKey) { + values[rawKey] = value; + return; + } + + set(values, fieldName, value); + } + + private updateState() { + const currentSchema = this.state?.schema ?? []; + const prevSchema = this.prevState?.schema ?? []; + // 进行了删除schema操作 + if (currentSchema.length < prevSchema.length) { + const currentFields = new Set( + currentSchema.map((item) => item.fieldName), + ); + const deletedSchema = prevSchema.filter( + (item) => !currentFields.has(item.fieldName), + ); + for (const schema of deletedSchema) { + this.form?.setFieldValue?.(schema.fieldName, undefined); + } + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/context.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/context.ts new file mode 100644 index 0000000..af4b15c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/context.ts @@ -0,0 +1,24 @@ +import type { FormRenderProps } from '../types'; + +import { computed } from 'vue'; + +import { createContext } from '@vben-core/shadcn-ui'; + +export const [injectRenderFormProps, provideFormRenderProps] = + createContext('FormRenderProps'); + +export const useFormContext = () => { + const formRenderProps = injectRenderFormProps(); + + const isVertical = computed(() => formRenderProps.layout === 'vertical'); + + const componentMap = computed(() => formRenderProps.componentMap); + const componentBindEventMap = computed( + () => formRenderProps.componentBindEventMap, + ); + return { + componentBindEventMap, + componentMap, + isVertical, + }; +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/dependencies.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/dependencies.ts new file mode 100644 index 0000000..8eb656e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/dependencies.ts @@ -0,0 +1,171 @@ +import type { + ExtendedFormApi, + FormItemDependencies, + FormSchemaRuleType, + MaybeComponentProps, +} from '../types'; + +import { computed, isRef, ref, watch } from 'vue'; + +import { get, isBoolean, isFunction } from '@vben-core/shared/utils'; + +import { useFormValues } from 'vee-validate'; + +import { resolveFieldNamePath } from '../field-name'; +import { injectFormProps } from '../use-form-context'; +import { injectRenderFormProps } from './context'; + +/** + * 解析Nested Objects对应的字段值 + * @param values 表单值 + * @param fieldName 字段名 + */ +function resolveValueByFieldName( + values: Record, + fieldName: string, +) { + // vee-validate:[] 表示禁用嵌套 + const { rawKey } = resolveFieldNamePath(fieldName); + if (rawKey) { + return values[rawKey]; + } + + return get(values, fieldName); +} +export default function useDependencies( + getDependencies: () => FormItemDependencies | undefined, +) { + const values = useFormValues(); + + const [extendApi] = injectFormProps(); + const formRenderProps = injectRenderFormProps(); + + const formApi = formRenderProps.form; + + if (!formApi) { + throw new Error('Form api is required in useDependencies'); + } + + if (!values) { + throw new Error('useDependencies should be used within '); + } + + // 在 dependencies 里提供访问extendApi的能力 + const getController = (): ExtendedFormApi => { + const controller = isRef(extendApi) + ? extendApi.value.formApi + : extendApi.formApi; + + if (!controller) { + throw new Error('formApi is required in useDependencies'); + } + + return controller; + }; + + const isIf = ref(true); + const isDisabled = ref(false); + const isShow = ref(true); + const isRequired = ref(false); + const dynamicComponentProps = ref({}); + const dynamicRules = ref(); + + const triggerFieldValues = computed(() => { + // 该字段可能会被多个字段触发 + const triggerFields = getDependencies()?.triggerFields ?? []; + return triggerFields.map((dep) => { + return resolveValueByFieldName(values.value, dep); + }); + }); + + const resetConditionState = () => { + isDisabled.value = false; + isIf.value = true; + isShow.value = true; + isRequired.value = false; + dynamicRules.value = undefined; + dynamicComponentProps.value = {}; + }; + + watch( + [triggerFieldValues, getDependencies], + async ([_values, dependencies]) => { + if (!dependencies || !dependencies?.triggerFields?.length) { + return; + } + resetConditionState(); + const { + componentProps, + disabled, + if: whenIf, + required, + rules, + show, + trigger, + } = dependencies; + + // 1. 优先判断if,如果if为false,则不渲染dom,后续判断也不再执行 + const formValues = values.value; + + if (isFunction(whenIf)) { + isIf.value = !!(await whenIf(formValues, formApi, getController())); + // 不渲染 + if (!isIf.value) return; + } else if (isBoolean(whenIf)) { + isIf.value = whenIf; + if (!isIf.value) return; + } + + // 2. 判断show,如果show为false,则隐藏 + if (isFunction(show)) { + isShow.value = !!(await show(formValues, formApi, getController())); + } else if (isBoolean(show)) { + isShow.value = show; + } + + if (isFunction(componentProps)) { + dynamicComponentProps.value = await componentProps( + formValues, + formApi, + getController(), + ); + } + + if (isFunction(rules)) { + dynamicRules.value = await rules(formValues, formApi, getController()); + } + + if (isFunction(disabled)) { + isDisabled.value = !!(await disabled( + formValues, + formApi, + getController(), + )); + } else if (isBoolean(disabled)) { + isDisabled.value = disabled; + } + + if (isFunction(required)) { + isRequired.value = !!(await required( + formValues, + formApi, + getController(), + )); + } + + if (isFunction(trigger)) { + await trigger(formValues, formApi, getController()); + } + }, + { deep: true, immediate: true }, + ); + + return { + dynamicComponentProps, + dynamicRules, + isDisabled, + isIf, + isRequired, + isShow, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/expandable.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/expandable.ts new file mode 100644 index 0000000..9383064 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/expandable.ts @@ -0,0 +1,105 @@ +import type { FormRenderProps } from '../types'; + +import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'; + +import { + breakpointsTailwind, + useBreakpoints, + useElementVisibility, +} from '@vueuse/core'; + +/** + * 动态计算行数 + */ +export function useExpandable(props: FormRenderProps) { + const wrapperRef = useTemplateRef('wrapperRef'); + const isVisible = useElementVisibility(wrapperRef); + const rowMapping = ref>({}); + // 是否已经计算过一次 + const isCalculated = ref(false); + + const breakpoints = useBreakpoints(breakpointsTailwind); + + const keepFormItemIndex = computed(() => { + const rows = props.collapsedRows ?? 1; + const mapping = rowMapping.value; + let maxItem = 0; + for (let index = 1; index <= rows; index++) { + maxItem += mapping?.[index] ?? 0; + } + // 保持一行 + return maxItem - 1 || 1; + }); + + watch( + [ + () => props.showCollapseButton, + () => breakpoints.active().value, + () => props.schema?.length, + () => isVisible.value, + ], + async ([val]) => { + if (val) { + await nextTick(); + rowMapping.value = {}; + isCalculated.value = false; + await calculateRowMapping(); + } + }, + ); + + async function calculateRowMapping() { + if (!props.showCollapseButton) { + return; + } + + await nextTick(); + if (!wrapperRef.value) { + return; + } + // 小屏幕不计算 + // if (breakpoints.smaller('sm').value) { + // // 保持一行 + // rowMapping.value = { 1: 2 }; + // return; + // } + + const formItems = [...wrapperRef.value.children]; + + const container = wrapperRef.value; + const containerStyles = window.getComputedStyle(container); + const rowHeights = containerStyles + .getPropertyValue('grid-template-rows') + .split(' '); + + const containerRect = container?.getBoundingClientRect(); + + formItems.forEach((el) => { + const itemRect = el.getBoundingClientRect(); + + // 计算元素在第几行 + const itemTop = itemRect.top - containerRect.top; + let rowStart = 0; + let cumulativeHeight = 0; + + for (const [i, rowHeight] of rowHeights.entries()) { + cumulativeHeight += Number.parseFloat(rowHeight); + if (itemTop < cumulativeHeight) { + rowStart = i + 1; + break; + } + } + if (rowStart > (props?.collapsedRows ?? 1)) { + return; + } + rowMapping.value[rowStart] = (rowMapping.value[rowStart] ?? 0) + 1; + isCalculated.value = true; + }); + } + + onMounted(() => { + calculateRowMapping(); + }); + + return { isCalculated, keepFormItemIndex, wrapperRef }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue new file mode 100644 index 0000000..89efd08 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue @@ -0,0 +1,464 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-label.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-label.vue new file mode 100644 index 0000000..71a3d8c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form-label.vue @@ -0,0 +1,32 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form.vue new file mode 100644 index 0000000..2e32cb7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/form.vue @@ -0,0 +1,192 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/helper.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/helper.ts new file mode 100644 index 0000000..cdb3999 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/helper.ts @@ -0,0 +1,60 @@ +import type { + AnyZodObject, + ZodDefault, + ZodEffects, + ZodNumber, + ZodString, + ZodTypeAny, +} from 'zod'; + +import { isObject, isString } from '@vben-core/shared/utils'; + +/** + * Get the lowest level Zod type. + * This will unpack optionals, refinements, etc. + */ +export function getBaseRules< + ChildType extends AnyZodObject | ZodTypeAny = ZodTypeAny, +>(schema: ChildType | ZodEffects): ChildType | null { + if (!schema || isString(schema)) return null; + if ('innerType' in schema._def) + return getBaseRules(schema._def.innerType as ChildType); + + if ('schema' in schema._def) + return getBaseRules(schema._def.schema as ChildType); + + return schema as ChildType; +} + +/** + * Search for a "ZodDefault" in the Zod stack and return its value. + */ +export function getDefaultValueInZodStack(schema: ZodTypeAny): any { + if (!schema || isString(schema)) { + return; + } + const typedSchema = schema as unknown as ZodDefault; + + if (typedSchema._def.typeName === 'ZodDefault') + return typedSchema._def.defaultValue(); + + if ('innerType' in typedSchema._def) { + return getDefaultValueInZodStack( + typedSchema._def.innerType as unknown as ZodTypeAny, + ); + } + if ('schema' in typedSchema._def) { + return getDefaultValueInZodStack( + (typedSchema._def as any).schema as ZodTypeAny, + ); + } + + return undefined; +} + +export function isEventObjectLike(obj: any) { + if (!obj || !isObject(obj)) { + return false; + } + return Reflect.has(obj, 'target') && Reflect.has(obj, 'stopPropagation'); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/index.ts new file mode 100644 index 0000000..f90e859 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/form-render/index.ts @@ -0,0 +1,3 @@ +export { default as FormField } from './form-field.vue'; +export { default as FormLabel } from './form-label.vue'; +export { default as Form } from './form.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/index.ts new file mode 100644 index 0000000..9000684 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/index.ts @@ -0,0 +1,14 @@ +export { setupVbenForm } from './config'; + +export type { + BaseFormComponentType, + ExtendedFormApi, + FormLayout, + VbenFormFieldArrayProps, + VbenFormProps, + FormSchema as VbenFormSchema, +} from './types'; + +export * from './use-vben-form'; +// export { default as VbenForm } from './vben-form.vue'; +export * as z from 'zod'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/types.ts new file mode 100644 index 0000000..28703b7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/types.ts @@ -0,0 +1,568 @@ +import type { FieldOptions, FormContext, GenericObject } from 'vee-validate'; +import type { ZodTypeAny } from 'zod'; + +import type { Component, HtmlHTMLAttributes, Ref } from 'vue'; + +import type { VbenButtonProps } from '@vben-core/shadcn-ui'; +import type { ClassType, MaybeComputedRef } from '@vben-core/typings'; + +export type FormLayout = 'horizontal' | 'inline' | 'vertical'; + +export type BaseFormComponentType = + | 'DefaultButton' + | 'PrimaryButton' + | 'VbenCheckbox' + | 'VbenFormFieldArray' + | 'VbenInput' + | 'VbenInputPassword' + | 'VbenPinInput' + | 'VbenSelect' + | (Record & string); + +type Breakpoints = '2xl:' | '3xl:' | '' | 'lg:' | 'md:' | 'sm:' | 'xl:'; + +type GridCols = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13; + +export type WrapperClassType = + | `${Breakpoints}grid-cols-${GridCols}` + | (Record & string); + +export type FormItemClassType = + | `${Breakpoints}cols-end-${'auto' | GridCols}` + | `${Breakpoints}cols-span-${'auto' | 'full' | GridCols}` + | `${Breakpoints}cols-start-${'auto' | GridCols}` + | (Record & string) + | WrapperClassType; + +export type FormFieldOptions = Partial< + FieldOptions & { + validateOnBlur?: boolean; + validateOnChange?: boolean; + validateOnInput?: boolean; + validateOnModelUpdate?: boolean; + } +>; + +export interface FormShape { + /** 默认值 */ + default?: any; + /** 字段名 */ + fieldName: string; + /** 是否必填 */ + required?: boolean; + rules?: ZodTypeAny; +} + +export type MaybeComponentPropKey = + | 'options' + | 'placeholder' + | 'title' + | keyof HtmlHTMLAttributes + | (Record & string); + +export type MaybeComponentProps = { [K in MaybeComponentPropKey]?: any }; + +export type FormActions = FormContext; + +export type CustomRenderType = (() => Component | string) | string; + +// 动态渲染参数 +export type CustomParamsRenderType = + | (( + value: Partial>, + actions: FormActions, + ) => Component | string) + | string; + +export type FormSchemaRuleType = + | 'required' + | 'selectRequired' + | null + | (Record & string) + | ZodTypeAny; + +type FormItemDependenciesCondition> = ( + value: Partial>, + actions: FormActions, + controller: ExtendedFormApi, // 在 dependencies 里提供访问extendApi的能力 +) => T; + +type FormItemDependenciesConditionWithRules = ( + value: Partial>, + actions: FormActions, + controller: ExtendedFormApi, // 在 dependencies 里提供访问extendApi的能力 +) => FormSchemaRuleType | PromiseLike; + +type FormItemDependenciesConditionWithProps = ( + value: Partial>, + actions: FormActions, + controller: ExtendedFormApi, // 在 dependencies 里提供访问extendApi的能力 +) => MaybeComponentProps | PromiseLike; + +export interface FormItemDependencies { + /** + * 组件参数 + * @returns 组件参数 + */ + componentProps?: FormItemDependenciesConditionWithProps; + /** + * 是否禁用 + * @returns 是否禁用 + */ + disabled?: boolean | FormItemDependenciesCondition; + /** + * 是否渲染(删除dom) + * @returns 是否渲染 + */ + if?: boolean | FormItemDependenciesCondition; + /** + * 是否必填 + * @returns 是否必填 + */ + required?: FormItemDependenciesCondition; + /** + * 字段规则 + */ + rules?: FormItemDependenciesConditionWithRules; + /** + * 是否隐藏(Css) + * @returns 是否隐藏 + */ + show?: boolean | FormItemDependenciesCondition; + /** + * 任意触发都会执行 + */ + trigger?: FormItemDependenciesCondition; + /** + * 触发字段 + */ + triggerFields: string[]; +} + +type ComponentProps = + | (( + value: Partial>, + actions: FormActions, + ) => MaybeComponentProps) + | MaybeComponentProps; + +export interface FormCommonConfig { + /** + * 是否可折叠的 + * @default false + */ + collapsible?: boolean; + /** + * 在Label后显示一个冒号 + */ + colon?: boolean; + /** + * 所有表单项的props + */ + componentProps?: ComponentProps; + /** + * 所有表单项的控件样式 + */ + controlClass?: string; + /** + * 默认折叠 + * @default false + */ + defaultCollapsed?: boolean; + /** + * 所有表单项的禁用状态 + * @default false + */ + disabled?: boolean; + /** + * 是否禁用所有表单项的change事件监听 + * @default true + */ + disabledOnChangeListener?: boolean; + /** + * 是否禁用所有表单项的input事件监听 + * @default true + */ + disabledOnInputListener?: boolean; + /** + * 所有表单项的空状态值,默认都是undefined,naive-ui的空状态值是null + */ + emptyStateValue?: null | undefined; + /** + * 所有表单项的控件样式 + * @default {} + */ + formFieldProps?: FormFieldOptions; + /** + * 所有表单项的栅格布局,支持函数形式 + * @default "" + */ + formItemClass?: (() => string) | string; + /** + * 隐藏所有表单项label + * @default false + */ + hideLabel?: boolean; + /** + * 是否隐藏必填标记 + * @default false + */ + hideRequiredMark?: boolean; + /** + * 所有表单项的label样式 + * @default "" + */ + labelClass?: string; + /** + * 所有表单项的label宽度 + */ + labelWidth?: number; + /** + * 所有表单项的model属性名 + * @default "modelValue" + */ + modelPropName?: string; + /** + * 所有表单项的wrapper样式 + */ + wrapperClass?: string; +} + +type RenderComponentContentType = ( + value: Partial>, + api: FormActions, +) => Record; + +type MappedComponentProps

= + | (( + value: Partial>, + actions: FormActions, + ) => P & Record) + | (P & Record); + +/** + * 格式化 `getValues()` 输出中的当前字段值。 + * - 返回 `undefined`:保留当前字段已被移除的状态,通常配合 `setValue(key, nextValue)` + * 把一个字段拆分写入到其他字段,例如 `startTime` / `endTime` + * - 返回其他值:会将当前字段恢复/写回为该返回值 + * - `setValue` 回调签名为 `(key, nextValue) => void` + */ +export type FormValueFormat = ( + value: any, + setValue: (fieldName: string, value: any) => void, + values: Record, +) => any; + +interface FormSchemaBody extends Omit { + /** 默认值 */ + defaultValue?: any; + /** 依赖 */ + dependencies?: FormItemDependencies; + /** 描述 */ + description?: CustomRenderType; + /** 字段名 */ + fieldName: string; + /** 帮助信息 */ + help?: CustomParamsRenderType; + /** 是否隐藏表单项 */ + hide?: boolean; + /** 表单项 */ + label?: CustomRenderType; + // 自定义组件内部渲染 + renderComponentContent?: RenderComponentContentType; + /** 字段规则 */ + rules?: FormSchemaRuleType; + /** 后缀 */ + suffix?: CustomRenderType; + /** + * 获取表单值时格式化当前字段。 + * - 返回值不为 `undefined` 时,会回写到当前 fieldName + * - 返回值为 `undefined` 时,可通过 setValue 写入一个或多个目标字段 + */ + valueFormat?: FormValueFormat; +} + +type FormSchemaDiscriminated< + T extends BaseFormComponentType, + P extends Record, +> = { + [K in Extract]: { + /** 组件 */ + component: K; + /** 组件参数 */ + componentProps?: MappedComponentProps; + } & FormSchemaBody; +}[Extract]; + +type FormSchemaFallback = { + /** 组件 */ + component: Component | T; + /** 组件参数 */ + componentProps?: ComponentProps; +} & FormSchemaBody; + +export type FormSchema< + T extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> = FormSchemaDiscriminated | FormSchemaFallback; + +/** + * 数组编辑器(VbenFormFieldArray)的组件参数 + */ +export interface VbenFormFieldArrayProps< + T extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> { + /** 操作列表头文案 */ + actionText?: string; + /** 「添加」按钮文案 */ + addButtonText?: string; + /** 新增一行时生成的默认数据;缺省时按列定义的 fieldName 生成空对象 */ + createRow?: () => Record; + disabled?: boolean; + /** 空数据文案 */ + emptyText?: string; + /** 最多行数 */ + max?: number; + /** 最少行数 */ + min?: number; + /** 列定义,每一列是一个子字段(复用 FormSchema) */ + schema: FormSchema[]; + /** 是否显示序号列 */ + showIndex?: boolean; +} + +export type HandleSubmitFn = ( + values: Record, +) => Promise | void; + +export type HandleResetFn = ( + values: Record, +) => Promise | void; + +export type FieldMappingTimeItem = [ + string, + [string, string], + ( + | ((value: any, fieldName: string) => any) + | [string, string] + | null + | string + )?, +]; + +export type FieldMappingTime = FieldMappingTimeItem[]; + +export type ArrayToStringFields = Array< + | [string[], string?] // 嵌套数组格式,可选分隔符 + | string // 单个字段,使用默认分隔符 + | string[] // 简单数组格式,最后一个元素可以是分隔符 +>; + +export interface FormFieldProps< + T extends BaseFormComponentType = BaseFormComponentType, +> extends FormSchemaBody { + /** 组件 */ + component: Component | T; + /** 组件参数 */ + componentProps?: ComponentProps; +} + +export interface FormRenderProps< + T extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> { + /** + * 表单字段数组映射字符串配置 默认使用"," + */ + arrayToStringFields?: ArrayToStringFields; + /** + * 是否折叠,在showCollapseButton=true下生效 + * true:折叠 false:展开 + */ + collapsed?: boolean; + /** + * 折叠时保持行数 + * @default 1 + */ + collapsedRows?: number; + /** + * 是否触发resize事件 + * @default false + */ + collapseTriggerResize?: boolean; + /** + * 表单项通用后备配置,当子项目没配置时使用这里的配置,子项目配置优先级高于此配置 + */ + commonConfig?: FormCommonConfig; + /** + * 紧凑模式(移除表单每一项底部为校验信息预留的空间) + */ + compact?: boolean; + /** + * 组件v-model事件绑定 + */ + componentBindEventMap?: Partial>; + /** + * 组件集合 + */ + componentMap: Record; + /** + * 表单字段映射到时间格式 + */ + fieldMappingTime?: FieldMappingTime; + /** + * 表单实例 + */ + form?: FormContext; + /** + * 表单项布局 + */ + layout?: FormLayout; + /** + * 表单定义 + */ + schema?: FormSchema[]; + + /** + * 是否显示展开/折叠 + */ + showCollapseButton?: boolean; + /** + * 格式化日期 + */ + + /** + * 表单栅格布局 + * @default "grid-cols-1" + */ + wrapperClass?: WrapperClassType; +} + +export interface ActionButtonOptions extends VbenButtonProps { + [key: string]: any; + content?: MaybeComputedRef; + show?: boolean; +} + +export interface VbenFormProps< + T extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> extends Omit< + FormRenderProps, + 'componentBindEventMap' | 'componentMap' | 'form' +> { + /** + * 操作按钮是否反转(提交按钮前置) + */ + actionButtonsReverse?: boolean; + /** + * 操作按钮组的样式 + * newLine: 在新行显示。rowEnd: 在行内显示,靠右对齐(默认)。inline: 使用grid默认样式 + */ + actionLayout?: 'inline' | 'newLine' | 'rowEnd'; + /** + * 操作按钮组显示位置,默认靠右显示 + */ + actionPosition?: 'center' | 'left' | 'right'; + /** + * 表单操作区域class + */ + actionWrapperClass?: ClassType; + /** + * 表单字段数组映射字符串配置 默认使用"," + */ + arrayToStringFields?: ArrayToStringFields; + + /** + * submitOnChange改变时防抖时间 | 默认300ms + */ + changeDebouncedTime?: number; + /** + * 表单字段映射 + */ + fieldMappingTime?: FieldMappingTime; + /** + * 表单收起展开状态变化回调 + */ + handleCollapsedChange?: (collapsed: boolean) => void; + /** + * 表单重置回调 + */ + handleReset?: HandleResetFn; + /** + * 表单提交回调 + */ + handleSubmit?: HandleSubmitFn; + /** + * 表单值变化回调 + */ + handleValuesChange?: ( + values: Record, + fieldsChanged: string[], + ) => void; + + /** + * 重置按钮参数 + */ + resetButtonOptions?: ActionButtonOptions; + + /** + * 验证失败时是否自动滚动到第一个错误字段 + * @default false + */ + scrollToFirstError?: boolean; + + /** + * 是否显示默认操作按钮 + * @default true + */ + showDefaultActions?: boolean; + + /** + * 提交按钮参数 + */ + submitButtonOptions?: ActionButtonOptions; + + /** + * 是否在字段值改变时提交表单 + * @default false + */ + submitOnChange?: boolean; + + /** + * 是否在回车时提交表单 + * @default false + */ + submitOnEnter?: boolean; +} + +export type ExtendedFormApi = import('./form-api').FormApi & { + useStore: >( + selector?: (state: NoInfer) => T, + ) => Readonly>; +}; + +export interface VbenFormAdapterOptions< + T extends BaseFormComponentType = BaseFormComponentType, +> { + config?: { + baseModelPropName?: string; + disabledOnChangeListener?: boolean; + disabledOnInputListener?: boolean; + emptyStateValue?: null | undefined; + modelPropNameMap?: Partial>; + }; + defineRules?: { + required?: ( + value: any, + params: any, + ctx: Record, + ) => boolean | string; + selectRequired?: ( + value: any, + params: any, + ctx: Record, + ) => boolean | string; + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-form-context.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-form-context.ts new file mode 100644 index 0000000..e95c87b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-form-context.ts @@ -0,0 +1,109 @@ +import type { ZodRawShape } from 'zod'; + +import type { ComputedRef } from 'vue'; + +import type { ExtendedFormApi, FormActions, VbenFormProps } from './types'; + +import { computed, unref, useSlots } from 'vue'; + +import { createContext } from '@vben-core/shadcn-ui'; +import { isString, mergeWithArrayOverride, set } from '@vben-core/shared/utils'; + +import { useForm } from 'vee-validate'; +import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod'; +import { getDefaultsForSchema } from 'zod-defaults'; + +type ExtendFormProps = VbenFormProps & { formApi?: ExtendedFormApi }; + +export const [injectFormProps, provideFormProps] = + createContext<[ComputedRef | ExtendFormProps, FormActions]>( + 'VbenFormProps', + ); + +export const [injectComponentRefMap, provideComponentRefMap] = + createContext>('ComponentRefMap'); + +export function useFormInitial( + props: ComputedRef | VbenFormProps, +) { + const slots = useSlots(); + const initialValues = generateInitialValues(); + + const form = useForm({ + ...(Object.keys(initialValues)?.length ? { initialValues } : {}), + }); + + const delegatedSlots = computed(() => { + const resultSlots: string[] = []; + + for (const key of Object.keys(slots)) { + if (key !== 'default') { + resultSlots.push(key); + } + } + return resultSlots; + }); + + function generateInitialValues() { + const initialValues: Record = {}; + + const zodObject: ZodRawShape = {}; + (unref(props).schema || []).forEach((item) => { + if (Reflect.has(item, 'defaultValue')) { + set(initialValues, item.fieldName, item.defaultValue); + } else if (item.rules && !isString(item.rules)) { + // 检查规则是否适合提取默认值 + const customDefaultValue = getCustomDefaultValue(item.rules); + zodObject[item.fieldName] = item.rules; + if (customDefaultValue !== undefined) { + initialValues[item.fieldName] = customDefaultValue; + } + } + }); + + const schemaInitialValues = getDefaultsForSchema(object(zodObject)); + + const zodDefaults: Record = {}; + for (const key in schemaInitialValues) { + set(zodDefaults, key, schemaInitialValues[key]); + } + return mergeWithArrayOverride(initialValues, zodDefaults); + } + // 自定义默认值提取逻辑 + function getCustomDefaultValue(rule: any): any { + if (rule instanceof ZodString) { + return ''; // 默认为空字符串 + } else if (rule instanceof ZodNumber) { + return null; // 默认为 null(避免显示 0) + } else if (rule instanceof ZodObject) { + // 递归提取嵌套对象的默认值 + const defaultValues: Record = {}; + for (const [key, valueSchema] of Object.entries(rule.shape)) { + defaultValues[key] = getCustomDefaultValue(valueSchema); + } + return defaultValues; + } else if (rule instanceof ZodIntersection) { + // 对于交集类型,从schema 提取默认值 + const leftDefaultValue = getCustomDefaultValue(rule._def.left); + const rightDefaultValue = getCustomDefaultValue(rule._def.right); + + // 如果左右两边都能提取默认值,合并它们 + if ( + typeof leftDefaultValue === 'object' && + typeof rightDefaultValue === 'object' + ) { + return { ...leftDefaultValue, ...rightDefaultValue }; + } + + // 否则优先使用左边的默认值 + return leftDefaultValue ?? rightDefaultValue; + } else { + return undefined; // 其他类型不提供默认值 + } + } + + return { + delegatedSlots, + form, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-vben-form.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-vben-form.ts new file mode 100644 index 0000000..da3c57d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/use-vben-form.ts @@ -0,0 +1,51 @@ +import type { + BaseFormComponentType, + ExtendedFormApi, + VbenFormProps, +} from './types'; + +import { defineComponent, h, isReactive, onBeforeUnmount, watch } from 'vue'; + +import { useSelector } from '@vben-core/shared/store'; + +import { FormApi } from './form-api'; +import VbenUseForm from './vben-use-form.vue'; + +export function useVbenForm< + T extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +>(options: VbenFormProps) { + const IS_REACTIVE = isReactive(options); + const api = new FormApi(options as unknown as VbenFormProps); + const extendedApi: ExtendedFormApi = api as never; + extendedApi.useStore = (selector) => { + return useSelector(api.store, selector); + }; + + const Form = defineComponent( + (props: VbenFormProps, { attrs, slots }) => { + onBeforeUnmount(() => { + api.unmount(); + }); + api.setState({ ...props, ...attrs }); + return () => + h(VbenUseForm, { ...props, ...attrs, formApi: extendedApi }, slots); + }, + { + name: 'VbenUseForm', + inheritAttrs: false, + }, + ); + // Add reactivity support + if (IS_REACTIVE) { + watch( + () => options.schema, + () => { + api.setState({ schema: options.schema }); + }, + { immediate: true }, + ); + } + + return [Form, extendedApi] as const; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-form.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-form.vue new file mode 100644 index 0000000..f3ba37f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-form.vue @@ -0,0 +1,79 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-use-form.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-use-form.vue new file mode 100644 index 0000000..735adb1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/src/vben-use-form.vue @@ -0,0 +1,151 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsconfig.json new file mode 100644 index 0000000..bbb8050 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src", "__tests__"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsdown.config.ts new file mode 100644 index 0000000..43e8cf1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/form-ui/tsdown.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsdown'; +import Vue from 'unplugin-vue/rolldown'; + +export default defineConfig({ + clean: true, + deps: { + skipNodeModulesBundle: true, + }, + dts: { + vue: true, + }, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + js: '.mjs', + }), + platform: 'neutral', + plugins: [Vue({ isProduction: true })], + unbundle: true, +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/package.json new file mode 100644 index 0000000..e579303 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/package.json @@ -0,0 +1,54 @@ +{ + "name": "@vben-core/layout-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/layout-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + }, + "devDependencies": { + "unplugin-vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/index.ts new file mode 100644 index 0000000..f17826d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/index.ts @@ -0,0 +1,5 @@ +export { default as LayoutContent } from './layout-content.vue'; +export { default as LayoutFooter } from './layout-footer.vue'; +export { default as LayoutHeader } from './layout-header.vue'; +export { default as LayoutSidebar } from './layout-sidebar.vue'; +export { default as LayoutTabbar } from './layout-tabbar.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue new file mode 100644 index 0000000..26fbfd3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue @@ -0,0 +1,65 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-footer.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-footer.vue new file mode 100644 index 0000000..3793f92 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-footer.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue new file mode 100644 index 0000000..fba07d4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue @@ -0,0 +1,77 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue new file mode 100644 index 0000000..11e4081 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue @@ -0,0 +1,381 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue new file mode 100644 index 0000000..148600e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue @@ -0,0 +1,30 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/index.ts new file mode 100644 index 0000000..8dfbe6c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/index.ts @@ -0,0 +1,2 @@ +export { default as SidebarCollapseButton } from './sidebar-collapse-button.vue'; +export { default as SidebarFixedButton } from './sidebar-fixed-button.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue new file mode 100644 index 0000000..9a16cbe --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue @@ -0,0 +1,19 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue new file mode 100644 index 0000000..44dec1d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue @@ -0,0 +1,19 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts new file mode 100644 index 0000000..2f2b82b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts @@ -0,0 +1,53 @@ +import type { LayoutType } from '@vben-core/typings'; + +import type { VbenLayoutProps } from '../vben-layout'; + +import { computed } from 'vue'; + +export function useLayout(props: VbenLayoutProps) { + const currentLayout = computed(() => + props.isMobile ? 'sidebar-nav' : (props.layout as LayoutType), + ); + + /** + * 是否全屏显示content,不需要侧边、底部、顶部、tab区域 + */ + const isFullContent = computed(() => currentLayout.value === 'full-content'); + + /** + * 是否侧边混合模式 + */ + const isSidebarMixedNav = computed( + () => currentLayout.value === 'sidebar-mixed-nav', + ); + + /** + * 是否为头部导航模式 + */ + const isHeaderNav = computed(() => currentLayout.value === 'header-nav'); + + /** + * 是否为混合导航模式 + */ + const isMixedNav = computed( + () => + currentLayout.value === 'mixed-nav' || + currentLayout.value === 'header-sidebar-nav', + ); + + /** + * 是否为头部混合模式 + */ + const isHeaderMixedNav = computed( + () => currentLayout.value === 'header-mixed-nav', + ); + + return { + currentLayout, + isFullContent, + isHeaderMixedNav, + isHeaderNav, + isMixedNav, + isSidebarMixedNav, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts new file mode 100644 index 0000000..2138877 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts @@ -0,0 +1,152 @@ +import { ref } from 'vue'; + +interface DragOptions { + max: number; + min: number; +} + +interface DragElements { + dragBar: HTMLElement | null; + target: HTMLElement | null; +} + +type DragCallback = (newWidth: number) => void; + +export function useSidebarDrag() { + const isDragging = ref(false); + let cleanup: (() => void) | null = null; + let dragOverlay: HTMLElement | null = null; + + const startDrag = ( + e: MouseEvent, + options: DragOptions, + elements: DragElements, + onDrag: DragCallback, + ) => { + const { min, max } = options; + const { dragBar, target } = elements; + + if (isDragging.value || !dragBar || !target) return; + + e.preventDefault(); + e.stopPropagation(); + + isDragging.value = true; + + const startX = e.clientX; + const startWidth = target.getBoundingClientRect().width; + const startLeft = dragBar.offsetLeft; + + dragBar.classList.add('bg-primary'); + dragBar.classList.remove('bg-primary/30'); + + const dragBarTransition = dragBar.style.transition; + const targetTransition = target.style.transition; + + dragBar.style.transition = 'none'; + target.style.transition = 'none'; + + dragOverlay = document.createElement('div'); + dragOverlay.style.position = 'fixed'; + dragOverlay.style.inset = '0'; + dragOverlay.style.zIndex = '9999'; + dragOverlay.style.cursor = 'col-resize'; + dragOverlay.style.userSelect = 'none'; + dragOverlay.style.outline = 'none'; + dragOverlay.tabIndex = -1; + dragOverlay.style.background = 'rgba(0,0,0,0)'; + document.body.append(dragOverlay); + + const onMouseMove = (moveEvent: MouseEvent) => { + if (!isDragging.value || !dragBar || !target) { + endDrag(); + return; + } + + const deltaX = moveEvent.clientX - startX; + let currentWidth = startWidth + deltaX; + + const isOutOfMin = currentWidth < min; + const isOutOfMax = currentWidth > max; + const isOutOfBounds = isOutOfMin || isOutOfMax; + + if (isOutOfMin) currentWidth = min; + if (isOutOfMax) currentWidth = max; + + const newLeft = startLeft + (currentWidth - startWidth); + + if (dragOverlay) + dragOverlay.style.cursor = isOutOfBounds ? 'not-allowed' : 'col-resize'; + + dragBar.style.left = `${newLeft}px`; + + if (isOutOfBounds) { + dragBar.classList.add('bg-primary/30'); + dragBar.classList.remove('bg-primary'); + } else { + dragBar.classList.add('bg-primary'); + dragBar.classList.remove('bg-primary/30'); + } + }; + + const onMouseUp = (upEvent: MouseEvent) => { + if (!isDragging.value || !dragBar || !target) { + endDrag(); + return; + } + + const deltaX = upEvent.clientX - startX; + let newWidth = startWidth + deltaX; + + newWidth = Math.min(max, Math.max(min, newWidth)); + + dragBar.classList.remove('bg-primary', 'bg-primary/30'); + + try { + onDrag?.(Math.round(newWidth)); + } finally { + endDrag(); + } + }; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + + cleanup = () => { + if (!cleanup) return; + + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + + if (dragBar) { + dragBar.style.transition = dragBarTransition; + dragBar.style.left = ''; + dragBar.classList.remove('bg-primary', 'bg-primary/30'); + } + + if (target) { + target.style.transition = targetTransition; + } + + if (dragOverlay) { + dragOverlay.remove(); + dragOverlay = null; + } + + isDragging.value = false; + cleanup = null; + }; + }; + + const endDrag = () => { + cleanup?.(); + }; + + return { + startDrag, + endDrag, + get isDragging() { + return isDragging.value; + }, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/index.ts new file mode 100644 index 0000000..ca8c8a7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/index.ts @@ -0,0 +1,2 @@ +export type * from './vben-layout'; +export { default as VbenAdminLayout } from './vben-layout.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.ts new file mode 100644 index 0000000..b6b4137 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.ts @@ -0,0 +1,180 @@ +import type { + ContentCompactType, + LayoutHeaderModeType, + LayoutType, + ThemeModeType, +} from '@vben-core/typings'; + +interface VbenLayoutProps { + /** + * 内容区域定宽 + * @default 'wide' + */ + contentCompact?: ContentCompactType; + /** + * 定宽布局宽度 + * @default 1200 + */ + contentCompactWidth?: number; + /** + * padding + * @default 16 + */ + contentPadding?: number; + /** + * paddingBottom + * @default 16 + */ + contentPaddingBottom?: number; + /** + * paddingLeft + * @default 16 + */ + contentPaddingLeft?: number; + /** + * paddingRight + * @default 16 + */ + contentPaddingRight?: number; + /** + * paddingTop + * @default 16 + */ + contentPaddingTop?: number; + /** + * footer 是否可见 + * @default false + */ + footerEnable?: boolean; + /** + * footer 是否固定 + * @default true + */ + footerFixed?: boolean; + /** + * footer 高度 + * @default 32 + */ + footerHeight?: number; + + /** + * header高度 + * @default 48 + */ + headerHeight?: number; + /** + * 顶栏是否隐藏 + * @default false + */ + headerHidden?: boolean; + /** + * header 显示模式 + * @default 'fixed' + */ + headerMode?: LayoutHeaderModeType; + /** + * header 顶栏主题 + */ + headerTheme?: ThemeModeType; + /** + * 是否显示header切换侧边栏按钮 + * @default + */ + headerToggleSidebarButton?: boolean; + /** + * header是否显示 + * @default true + */ + headerVisible?: boolean; + /** + * 是否移动端显示 + * @default false + */ + isMobile?: boolean; + /** + * 布局方式 + * sidebar-nav 侧边菜单布局 + * header-nav 顶部菜单布局 + * mixed-nav 侧边&顶部菜单布局 + * sidebar-mixed-nav 侧边混合菜单布局 + * full-content 全屏内容布局 + * @default sidebar-nav + */ + layout?: LayoutType; + /** + * 侧边菜单折叠状态 + * @default false + */ + sidebarCollapse?: boolean; + /** + * 侧边菜单折叠按钮 + * @default true + */ + sidebarCollapsedButton?: boolean; + /** + * 侧边菜单是否折叠时,是否显示title + * @default true + */ + sidebarCollapseShowTitle?: boolean; + /** + * 侧边栏是否可见 + * @default true + */ + sidebarEnable?: boolean; + /** + * 侧边菜单折叠额外宽度 + * @default 48 + */ + sidebarExtraCollapsedWidth?: number; + /** + * 侧边菜单折叠按钮是否固定 + * @default true + */ + sidebarFixedButton?: boolean; + /** + * 侧边栏是否隐藏 + * @default false + */ + sidebarHidden?: boolean; + /** + * 混合侧边栏宽度 + * @default 80 + */ + sidebarMixedWidth?: number; + /** + * 侧边栏 + * @default dark + */ + sidebarTheme?: ThemeModeType; + /** + * 侧边栏子栏 + * @default dark + */ + sidebarThemeSub?: ThemeModeType; + /** + * 侧边栏宽度 + * @default 210 + */ + sidebarWidth?: number; + /** + * 侧边菜单折叠宽度 + * @default 48 + */ + sideCollapseWidth?: number; + /** + * tab是否可见 + * @default true + */ + tabbarEnable?: boolean; + /** + * tab高度 + * @default 30 + */ + tabbarHeight?: number; + /** + * zIndex + * @default 100 + */ + zIndex?: number; +} +export type { VbenLayoutProps }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.vue new file mode 100644 index 0000000..d8a2c42 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/src/vben-layout.vue @@ -0,0 +1,634 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsdown.config.ts new file mode 100644 index 0000000..43e8cf1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/layout-ui/tsdown.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsdown'; +import Vue from 'unplugin-vue/rolldown'; + +export default defineConfig({ + clean: true, + deps: { + skipNodeModulesBundle: true, + }, + dts: { + vue: true, + }, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + js: '.mjs', + }), + platform: 'neutral', + plugins: [Vue({ isProduction: true })], + unbundle: true, +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/README.md b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/README.md new file mode 100644 index 0000000..dd77a95 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/README.md @@ -0,0 +1 @@ +# 菜单组件 diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/package.json new file mode 100644 index 0000000..5290fb1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/package.json @@ -0,0 +1,57 @@ +{ + "name": "@vben-core/menu-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/menu-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/design": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "qs": "catalog:", + "vue": "catalog:" + }, + "devDependencies": { + "@types/qs": "catalog:", + "unplugin-vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/collapse-transition.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/collapse-transition.vue new file mode 100644 index 0000000..b89c406 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/collapse-transition.vue @@ -0,0 +1,96 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/index.ts new file mode 100644 index 0000000..ce06710 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/index.ts @@ -0,0 +1,4 @@ +export { default as MenuBadge } from './menu-badge.vue'; +export { default as MenuItem } from './menu-item.vue'; +export { default as Menu } from './menu.vue'; +export { default as SubMenu } from './sub-menu.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue new file mode 100644 index 0000000..baf056a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue @@ -0,0 +1,28 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue new file mode 100644 index 0000000..9a329c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue @@ -0,0 +1,57 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue new file mode 100644 index 0000000..0170264 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue @@ -0,0 +1,138 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu.vue new file mode 100644 index 0000000..5d9f578 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/menu.vue @@ -0,0 +1,884 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/index.ts new file mode 100644 index 0000000..898ea16 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/index.ts @@ -0,0 +1,2 @@ +export type * from './normal-menu'; +export { default as NormalMenu } from './normal-menu.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.ts new file mode 100644 index 0000000..82edb4f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.ts @@ -0,0 +1,27 @@ +import type { MenuRecordRaw } from '@vben-core/typings'; + +interface NormalMenuProps { + /** + * 菜单数据 + */ + activePath?: string; + /** + * 是否折叠 + */ + collapse?: boolean; + /** + * 菜单项 + */ + menus?: MenuRecordRaw[]; + /** + * @zh_CN 是否圆润风格 + * @default true + */ + rounded?: boolean; + /** + * 主题 + */ + theme?: 'dark' | 'light'; +} + +export type { NormalMenuProps }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue new file mode 100644 index 0000000..8a1e2a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue @@ -0,0 +1,162 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue new file mode 100644 index 0000000..5622d41 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue @@ -0,0 +1,105 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue new file mode 100644 index 0000000..afc0afa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue @@ -0,0 +1,276 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/index.ts new file mode 100644 index 0000000..f9acf32 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/index.ts @@ -0,0 +1,2 @@ +export * from './use-menu'; +export * from './use-menu-context'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts new file mode 100644 index 0000000..1e3955d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts @@ -0,0 +1,55 @@ +import type { MenuProvider, SubMenuProvider } from '../types'; + +import { getCurrentInstance, inject, provide } from 'vue'; + +import { findComponentUpward } from '../utils'; + +const menuContextKey = Symbol('menuContext'); + +/** + * @zh_CN Provide menu context + */ +function createMenuContext(injectMenuData: MenuProvider) { + provide(menuContextKey, injectMenuData); +} + +/** + * @zh_CN Provide menu context + */ +function createSubMenuContext(injectSubMenuData: SubMenuProvider) { + const instance = getCurrentInstance(); + + provide(`subMenu:${instance?.uid}`, injectSubMenuData); +} + +/** + * @zh_CN Inject menu context + */ +function useMenuContext() { + const instance = getCurrentInstance(); + if (!instance) { + throw new Error('instance is required'); + } + const rootMenu = inject(menuContextKey) as MenuProvider; + return rootMenu; +} + +/** + * @zh_CN Inject menu context + */ +function useSubMenuContext() { + const instance = getCurrentInstance(); + if (!instance) { + throw new Error('instance is required'); + } + const parentMenu = findComponentUpward(instance, ['MenuUI', 'SubMenu']); + const subMenu = inject(`subMenu:${parentMenu?.uid}`) as SubMenuProvider; + return subMenu; +} + +export { + createMenuContext, + createSubMenuContext, + useMenuContext, + useSubMenuContext, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts new file mode 100644 index 0000000..e18bae7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts @@ -0,0 +1,46 @@ +import type { Ref } from 'vue'; + +import { watch } from 'vue'; + +import { useDebounceFn } from '@vueuse/core'; + +interface UseMenuScrollOptions { + delay?: number; + enable?: boolean | Ref; +} + +export function useMenuScroll( + activePath: Ref, + options: UseMenuScrollOptions = {}, +) { + const { enable = true, delay = 320 } = options; + + function scrollToActiveItem() { + const isEnabled = typeof enable === 'boolean' ? enable : enable.value; + if (!isEnabled) return; + + const activeElement = document.querySelector( + `aside a[role=menuitem].is-active`, + ); + if (activeElement) { + activeElement.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center', + }); + } + } + + const debouncedScroll = useDebounceFn(scrollToActiveItem, delay); + + watch(activePath, () => { + const isEnabled = typeof enable === 'boolean' ? enable : enable.value; + if (!isEnabled) return; + + debouncedScroll(); + }); + + return { + scrollToActiveItem, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts new file mode 100644 index 0000000..0ee93fd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts @@ -0,0 +1,48 @@ +import type { SubMenuProvider } from '../types'; + +import { computed, getCurrentInstance } from 'vue'; + +import { findComponentUpward } from '../utils'; + +function useMenu() { + const instance = getCurrentInstance(); + if (!instance) { + throw new Error('instance is required'); + } + + /** + * @zh_CN 获取所有父级菜单链路 + */ + const parentPaths = computed(() => { + let parent = instance.parent; + const paths: string[] = [instance.props.path as string]; + while (parent?.type.name !== 'MenuUI') { + if (parent?.props.path) { + paths.unshift(parent.props.path as string); + } + parent = parent?.parent ?? null; + } + + return paths; + }); + + const parentMenu = computed(() => { + return findComponentUpward(instance, ['MenuUI', 'SubMenu']); + }); + + return { + parentMenu, + parentPaths, + }; +} + +function useMenuStyle(menu?: SubMenuProvider) { + const subMenuStyle = computed(() => { + return { + '--menu-level': menu ? (menu?.level ?? 0 + 1) : 0, + }; + }); + return subMenuStyle; +} + +export { useMenu, useMenuStyle }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/index.ts new file mode 100644 index 0000000..1e3bc14 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/index.ts @@ -0,0 +1,4 @@ +export { default as MenuBadge } from './components/menu-badge.vue'; +export * from './components/normal-menu'; +export { default as Menu } from './menu.vue'; +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/menu.vue new file mode 100644 index 0000000..e9ac6d9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/menu.vue @@ -0,0 +1,32 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/sub-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/sub-menu.vue new file mode 100644 index 0000000..f84e5f8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/sub-menu.vue @@ -0,0 +1,71 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/types.ts new file mode 100644 index 0000000..4654e2f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/types.ts @@ -0,0 +1,154 @@ +import type { Component, Ref } from 'vue'; + +import type { + MenuRecordBadgeRaw, + Recordable, + ThemeModeType, +} from '@vben-core/typings'; + +interface MenuProps { + /** + * @zh_CN 是否开启手风琴模式 + * @default true + */ + accordion?: boolean; + /** + * @zh_CN 菜单是否折叠 + * @default false + */ + collapse?: boolean; + + /** + * @zh_CN 菜单折叠时是否显示菜单名称 + * @default false + */ + collapseShowTitle?: boolean; + + /** + * @zh_CN 默认激活的菜单 + */ + defaultActive?: string; + + /** + * @zh_CN 默认展开的菜单 + */ + defaultOpeneds?: string[]; + + /** + * @zh_CN 菜单模式 + * @default vertical + */ + mode?: 'horizontal' | 'vertical'; + + /** + * @zh_CN 是否圆润风格 + * @default true + */ + rounded?: boolean; + + /** + * @zh_CN 是否自动滚动到激活的菜单项 + * @default false + */ + scrollToActive?: boolean; + + /** + * @zh_CN 菜单主题 + * @default dark + */ + theme?: ThemeModeType; +} + +interface SubMenuProps extends MenuRecordBadgeRaw { + /** + * @zh_CN 激活图标 + */ + activeIcon?: string; + /** + * @zh_CN 是否禁用 + */ + disabled?: boolean; + /** + * @zh_CN 图标 + */ + icon?: Component | string; + /** + * @zh_CN submenu 名称 + */ + path: string; +} + +interface MenuItemProps extends MenuRecordBadgeRaw { + /** + * @zh_CN 图标 + */ + activeIcon?: string; + /** + * @zh_CN 是否禁用 + */ + disabled?: boolean; + /** + * @zh_CN 图标 + */ + icon?: Component | string; + /** + * @zh_CN menuitem 名称 + */ + path: string; + /** + * @zh_CN 菜单所携带的参数 + */ + query?: Recordable; +} + +interface MenuItemRegistered { + active: boolean; + parentPaths: string[]; + path: string; + query?: Recordable; +} + +interface MenuItemClicked { + parentPaths: string[]; + path: string; +} + +interface MenuProvider { + activePath?: string; + addMenuItem: (item: MenuItemRegistered) => void; + + addSubMenu: (item: MenuItemRegistered) => void; + closeMenu: (path: string, parentLinks: string[]) => void; + handleMenuItemClick: (item: MenuItemClicked) => void; + handleSubMenuClick: (subMenu: MenuItemRegistered) => void; + isMenuPopup: boolean; + items: Record; + + openedMenus: string[]; + openMenu: (path: string, parentLinks: string[]) => void; + props: MenuProps; + removeMenuItem: (item: MenuItemRegistered) => void; + + removeSubMenu: (item: MenuItemRegistered) => void; + + subMenus: Record; + theme: string; +} + +interface SubMenuProvider { + addSubMenu: (item: MenuItemRegistered) => void; + handleMouseleave?: (deepDispatch: boolean) => void; + level: number; + mouseInChild: Ref; + removeSubMenu: (item: MenuItemRegistered) => void; +} + +export type { + MenuItemClicked, + MenuItemProps, + MenuItemRegistered, + MenuProps, + MenuProvider, + SubMenuProps, + SubMenuProvider, +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/utils/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/utils/index.ts new file mode 100644 index 0000000..23a1b15 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/src/utils/index.ts @@ -0,0 +1,52 @@ +import type { + ComponentInternalInstance, + VNode, + VNodeChild, + VNodeNormalizedChildren, +} from 'vue'; + +import { isVNode } from 'vue'; + +type VNodeChildAtom = Exclude>; +type RawSlots = Exclude | null | string>; + +type FlattenVNodes = Array; + +/** + * @zh_CN Find the parent component upward + * @param instance + * @param parentNames + */ +function findComponentUpward( + instance: ComponentInternalInstance, + parentNames: string[], +) { + let parent = instance.parent; + while (parent && !parentNames.includes(parent?.type?.name ?? '')) { + parent = parent.parent; + } + return parent; +} + +const flattedChildren = ( + children: FlattenVNodes | VNode | VNodeNormalizedChildren, +): FlattenVNodes => { + const vNodes = Array.isArray(children) ? children : [children]; + const result: FlattenVNodes = []; + + vNodes.forEach((child) => { + if (Array.isArray(child)) { + result.push(...flattedChildren(child)); + } else if (isVNode(child) && Array.isArray(child.children)) { + result.push(...flattedChildren(child.children)); + } else { + result.push(child); + if (isVNode(child) && child.component?.subTree) { + result.push(...flattedChildren(child.component.subTree)); + } + } + }); + return result; +}; + +export { findComponentUpward, flattedChildren }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsdown.config.ts new file mode 100644 index 0000000..43e8cf1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/menu-ui/tsdown.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsdown'; +import Vue from 'unplugin-vue/rolldown'; + +export default defineConfig({ + clean: true, + deps: { + skipNodeModulesBundle: true, + }, + dts: { + vue: true, + }, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + js: '.mjs', + }), + platform: 'neutral', + plugins: [Vue({ isProduction: true })], + unbundle: true, +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/package.json new file mode 100644 index 0000000..0114a01 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/package.json @@ -0,0 +1,55 @@ +{ + "name": "@vben-core/popup-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/popup-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/preferences": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + }, + "devDependencies": { + "unplugin-vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts new file mode 100644 index 0000000..910a461 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts @@ -0,0 +1,246 @@ +import type { Component, VNode } from 'vue'; + +import type { Recordable } from '@vben-core/typings'; + +import type { AlertProps, BeforeCloseScope, PromptProps } from './alert'; + +import { h, nextTick, ref, render } from 'vue'; + +import { useSimpleLocale } from '@vben-core/composables'; +import { Input, VbenRenderContent } from '@vben-core/shadcn-ui'; +import { isFunction, isString } from '@vben-core/shared/utils'; + +import Alert from './alert.vue'; + +const alerts = ref>([]); + +const { $t } = useSimpleLocale(); + +export function vbenAlert(options: AlertProps): Promise; +export function vbenAlert( + message: string, + options?: Partial, +): Promise; +export function vbenAlert( + message: string, + title?: string, + options?: Partial, +): Promise; + +export function vbenAlert( + arg0: AlertProps | string, + arg1?: Partial | string, + arg2?: Partial, +): Promise { + return new Promise((resolve, reject) => { + const options: AlertProps = isString(arg0) + ? { + content: arg0, + } + : { ...arg0 }; + if (arg1) { + if (isString(arg1)) { + options.title = arg1; + } else if (!isString(arg1)) { + // 如果第二个参数是对象,则合并到选项中 + Object.assign(options, arg1); + } + } + + if (arg2 && !isString(arg2)) { + Object.assign(options, arg2); + } + // 创建容器元素 + const container = document.createElement('div'); + document.body.append(container); + + // 创建一个引用,用于在回调中访问实例 + const alertRef = { container, instance: null as any }; + + const props: AlertProps & Recordable = { + onClosed: (isConfirm: boolean) => { + // 移除组件实例以及创建的所有dom(恢复页面到打开前的状态) + // 从alerts数组中移除该实例 + alerts.value = alerts.value.filter((item) => item !== alertRef); + + // 从DOM中移除容器 + render(null, container); + if (container.parentNode) { + container.remove(); + } + + // 解析 Promise,传递用户操作结果 + if (isConfirm) { + resolve(); + } else { + reject(new Error('dialog cancelled')); + } + }, + ...options, + open: true, + title: options.title ?? $t.value('prompt'), + }; + + // 创建Alert组件的VNode + const vnode = h(Alert, props); + + // 渲染组件到容器 + render(vnode, container); + + // 保存组件实例引用 + alertRef.instance = vnode.component?.proxy as Component; + + // 将实例和容器添加到alerts数组中 + alerts.value.push(alertRef); + }); +} + +export function vbenConfirm(options: AlertProps): Promise; +export function vbenConfirm( + message: string, + options?: Partial, +): Promise; +export function vbenConfirm( + message: string, + title?: string, + options?: Partial, +): Promise; + +export function vbenConfirm( + arg0: AlertProps | string, + arg1?: Partial | string, + arg2?: Partial, +): Promise { + const defaultProps: Partial = { + showCancel: true, + }; + if (!arg1) { + return isString(arg0) + ? vbenAlert(arg0, defaultProps) + : vbenAlert({ ...defaultProps, ...arg0 }); + } else if (!arg2) { + return isString(arg1) + ? vbenAlert(arg0 as string, arg1, defaultProps) + : vbenAlert(arg0 as string, { ...defaultProps, ...arg1 }); + } + return vbenAlert(arg0 as string, arg1 as string, { + ...defaultProps, + ...arg2, + }); +} + +export async function vbenPrompt( + options: PromptProps, +): Promise { + const { + component: _component, + componentProps: _componentProps, + componentSlots, + content, + defaultValue, + modelPropName: _modelPropName, + ...delegated + } = options; + + const modelValue = ref(defaultValue); + const inputComponentRef = ref(null); + const staticContents: Component[] = [ + h(VbenRenderContent, { content, renderBr: true }), + ]; + + const modelPropName = _modelPropName || 'modelValue'; + const componentProps = { ..._componentProps }; + + // 每次渲染时都会重新计算的内容函数 + const contentRenderer = () => { + const currentProps = { + ...componentProps, + [modelPropName]: modelValue.value, + [`onUpdate:${modelPropName}`]: (val: T) => { + modelValue.value = val; + }, + }; + + // 设置当前值 + + // 设置更新处理函数 + + // 创建输入组件 + inputComponentRef.value = h( + _component || Input, + currentProps, + componentSlots, + ); + + // 返回包含静态内容和输入组件的数组 + return h( + 'div', + { class: 'flex flex-col gap-2' }, + { default: () => [...staticContents, inputComponentRef.value] }, + ); + }; + + const props: AlertProps & Recordable = { + ...delegated, + async beforeClose(scope: BeforeCloseScope) { + if (delegated.beforeClose) { + return await delegated.beforeClose({ + ...scope, + value: modelValue.value, + }); + } + }, + // 使用函数形式,每次渲染都会重新计算内容 + content: contentRenderer, + contentMasking: true, + async onOpened() { + await nextTick(); + const componentRef: null | VNode = inputComponentRef.value; + if (componentRef) { + if ( + componentRef.component?.exposed && + isFunction(componentRef.component.exposed.focus) + ) { + componentRef.component.exposed.focus(); + } else { + if (componentRef.el) { + if ( + isFunction(componentRef.el.focus) && + ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'].includes( + componentRef.el.tagName, + ) + ) { + componentRef.el.focus(); + } else if (isFunction(componentRef.el.querySelector)) { + const focusableElement = componentRef.el.querySelector( + 'input, select, textarea, button', + ); + if (focusableElement && isFunction(focusableElement.focus)) { + focusableElement.focus(); + } + } else if ( + componentRef.el.nextElementSibling && + isFunction(componentRef.el.nextElementSibling.focus) + ) { + componentRef.el.nextElementSibling.focus(); + } + } + } + } + }, + }; + + await vbenConfirm(props); + return modelValue.value; +} + +export function clearAllAlerts() { + alerts.value.forEach((alert) => { + // 从DOM中移除容器 + render(null, alert.container); + if (alert.container.parentNode) { + alert.container.remove(); + } + }); + alerts.value = []; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.ts new file mode 100644 index 0000000..ff19830 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.ts @@ -0,0 +1,101 @@ +import type { Component, VNode, VNodeArrayChildren } from 'vue'; + +import type { Recordable } from '@vben-core/typings'; + +import { createContext } from '@vben-core/shadcn-ui'; + +export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning'; + +export type BeforeCloseScope = { + isConfirm: boolean; +}; + +export type AlertProps = { + /** 关闭前的回调,如果返回false,则终止关闭 */ + beforeClose?: ( + scope: BeforeCloseScope, + ) => boolean | Promise | undefined; + /** 边框 */ + bordered?: boolean; + /** + * 按钮对齐方式 + * @default 'end' + */ + buttonAlign?: 'center' | 'end' | 'start'; + /** 取消按钮的标题 */ + cancelText?: string; + /** 是否居中显示 */ + centered?: boolean; + /** 确认按钮的标题 */ + confirmText?: string; + /** 弹窗容器的额外样式 */ + containerClass?: string; + /** 弹窗提示内容 */ + content: Component | string; + /** 弹窗内容的额外样式 */ + contentClass?: string; + /** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/ + contentMasking?: boolean; + /** 按下Esc时是否关闭弹窗 */ + escapeKeyClose?: boolean; + /** 弹窗底部内容(与按钮在同一个容器中) */ + footer?: Component | string; + /** 弹窗的图标(在标题的前面) */ + icon?: Component | IconType; + /** + * 弹窗遮罩模糊效果 + */ + overlayBlur?: number; + /** 是否显示取消按钮 */ + showCancel?: boolean; + /** 弹窗标题 */ + title?: string; +}; + +/** Prompt属性 */ +export type PromptProps = { + /** 关闭前的回调,如果返回false,则终止关闭 */ + beforeClose?: (scope: { + isConfirm: boolean; + value: T | undefined; + }) => boolean | Promise | undefined; + /** 用于接受用户输入的组件 */ + component?: Component; + /** 输入组件的属性 */ + componentProps?: Recordable; + /** 输入组件的插槽 */ + componentSlots?: + | (() => any) + | Recordable + | VNode + | VNodeArrayChildren; + /** 默认值 */ + defaultValue?: T; + /** 输入组件的值属性名 */ + modelPropName?: string; +} & Omit; + +/** + * Alert上下文 + */ +export type AlertContext = { + /** 执行取消操作 */ + doCancel: () => void; + /** 执行确认操作 */ + doConfirm: () => void; +}; + +export const [injectAlertContext, provideAlertContext] = + createContext('VbenAlertContext'); + +/** + * 获取Alert上下文 + * @returns AlertContext + */ +export function useAlertContext() { + const context = injectAlertContext(); + if (!context) { + throw new Error('useAlertContext must be used within an AlertProvider'); + } + return context; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.vue new file mode 100644 index 0000000..8e8a23f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/alert.vue @@ -0,0 +1,220 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/index.ts new file mode 100644 index 0000000..8419b5b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/alert/index.ts @@ -0,0 +1,14 @@ +export type { + AlertProps, + BeforeCloseScope, + IconType, + PromptProps, +} from './alert'; +export { useAlertContext } from './alert'; +export { default as Alert } from './alert.vue'; +export { + vbenAlert as alert, + clearAllAlerts, + vbenConfirm as confirm, + vbenPrompt as prompt, +} from './AlertBuilder'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts new file mode 100644 index 0000000..8420bb9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts @@ -0,0 +1,115 @@ +import type { DrawerState } from '../drawer'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { DrawerApi } from '../drawer-api'; + +// 模拟 Store 类 +vi.mock('@vben-core/shared/store', () => { + return { + isFunction: (fn: any) => typeof fn === 'function', + Store: class { + get state() { + return this._state; + } + private _state: DrawerState; + private subscribers: Array<(state: DrawerState) => void> = []; + + constructor(initialState: DrawerState) { + this._state = initialState; + } + + setState(fn: (prev: DrawerState) => DrawerState) { + this._state = fn(this._state); + this.subscribers.forEach((sub) => sub(this._state)); + } + + subscribe(fn: (state: DrawerState) => void) { + this.subscribers.push(fn); + return { unsubscribe: () => {} }; + } + }, + }; +}); + +describe('drawerApi', () => { + let drawerApi: DrawerApi; + let drawerState: DrawerState; + + beforeEach(() => { + drawerApi = new DrawerApi(); + drawerState = drawerApi.store.state; + }); + + it('should initialize with default state', () => { + expect(drawerState.isOpen).toBe(false); + expect(drawerState.cancelText).toBe(undefined); + expect(drawerState.confirmText).toBe(undefined); + }); + + it('should open the drawer', () => { + drawerApi.open(); + expect(drawerApi.store.state.isOpen).toBe(true); + }); + + it('should close the drawer if onBeforeClose allows it', () => { + drawerApi.close(); + expect(drawerApi.store.state.isOpen).toBe(false); + }); + + it('should not close the drawer if onBeforeClose returns false', () => { + const onBeforeClose = vi.fn(() => false); + const drawerApiWithHook = new DrawerApi({ onBeforeClose }); + drawerApiWithHook.open(); + drawerApiWithHook.close(); + expect(drawerApiWithHook.store.state.isOpen).toBe(true); + expect(onBeforeClose).toHaveBeenCalled(); + }); + + it('should trigger onCancel and keep drawer open if onCancel is provided', () => { + const onCancel = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onCancel }); + drawerApiWithHook.open(); + drawerApiWithHook.onCancel(); + expect(onCancel).toHaveBeenCalled(); + expect(drawerApiWithHook.store.state.isOpen).toBe(true); // 关闭逻辑不在 onCancel 内 + }); + + it('should update shared data correctly', () => { + const testData = { key: 'value' }; + drawerApi.setData(testData); + expect(drawerApi.getData()).toEqual(testData); + }); + + it('should set state correctly using an object', () => { + drawerApi.setState({ title: 'New Title' }); + expect(drawerApi.store.state.title).toBe('New Title'); + }); + + it('should set state correctly using a function', () => { + drawerApi.setState((prev) => ({ ...prev, confirmText: 'Yes' })); + expect(drawerApi.store.state.confirmText).toBe('Yes'); + }); + + it('should call onOpenChange when state changes', () => { + const onOpenChange = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onOpenChange }); + drawerApiWithHook.open(); + expect(onOpenChange).toHaveBeenCalledWith(true); + }); + + it('should call onClosed callback when provided', () => { + const onClosed = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onClosed }); + drawerApiWithHook.onClosed(); + expect(onClosed).toHaveBeenCalled(); + }); + + it('should call onOpened callback when provided', () => { + const onOpened = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onOpened }); + drawerApiWithHook.open(); + drawerApiWithHook.onOpened(); + expect(onOpened).toHaveBeenCalled(); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts new file mode 100644 index 0000000..f5d9f33 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts @@ -0,0 +1,178 @@ +import type { DrawerApiOptions, DrawerState } from './drawer'; + +import { Store } from '@vben-core/shared/store'; +import { bindMethods, isFunction } from '@vben-core/shared/utils'; + +export class DrawerApi { + // 共享数据 + public sharedData: Record<'payload', any> = { + payload: {}, + }; + public store: Store; + + private api: Pick< + DrawerApiOptions, + | 'onBeforeClose' + | 'onCancel' + | 'onClosed' + | 'onConfirm' + | 'onOpenChange' + | 'onOpened' + >; + + // private prevState!: DrawerState; + private state!: DrawerState; + + constructor(options: DrawerApiOptions = {}) { + const { + connectedComponent: _, + onBeforeClose, + onCancel, + onClosed, + onConfirm, + onOpenChange, + onOpened, + ...storeState + } = options; + + const defaultState: DrawerState = { + class: '', + closable: true, + closeIconPlacement: 'right', + closeOnClickModal: true, + closeOnPressEscape: true, + confirmLoading: false, + contentClass: '', + footer: true, + header: true, + isOpen: false, + loading: false, + modal: true, + openAutoFocus: false, + placement: 'right', + showCancelButton: true, + showConfirmButton: true, + submitting: false, + title: '', + }; + + this.store = new Store({ + ...defaultState, + ...storeState, + }); + + this.store.subscribe((state) => { + const prevIsOpen = this.state?.isOpen; + this.state = state; + if (state?.isOpen !== prevIsOpen) { + this.api.onOpenChange?.(!!state?.isOpen); + } + }); + this.state = this.store.state; + this.api = { + onBeforeClose, + onCancel, + onClosed, + onConfirm, + onOpenChange, + onOpened, + }; + bindMethods(this); + } + + /** + * 关闭抽屉 + * @description 关闭抽屉时会调用 onBeforeClose 钩子函数,如果 onBeforeClose 返回 false,则不关闭弹窗 + */ + async close() { + // 通过 onBeforeClose 钩子函数来判断是否允许关闭弹窗 + // 如果 onBeforeClose 返回 false,则不关闭弹窗 + const allowClose = (await this.api.onBeforeClose?.()) ?? true; + if (allowClose) { + this.store.setState((prev) => ({ + ...prev, + isOpen: false, + submitting: false, + })); + } + } + + getData>() { + return (this.sharedData?.payload ?? {}) as T; + } + + /** + * 锁定抽屉状态(用于提交过程中的等待状态) + * @description 锁定状态将禁用默认的取消按钮,使用spinner覆盖抽屉内容,隐藏关闭按钮,阻止手动关闭弹窗,将默认的提交按钮标记为loading状态 + * @param isLocked 是否锁定 + */ + lock(isLocked: boolean = true) { + return this.setState({ submitting: isLocked }); + } + + /** + * 取消操作 + */ + onCancel() { + if (this.api.onCancel) { + this.api.onCancel?.(); + } else { + this.close(); + } + } + + /** + * 弹窗关闭动画播放完毕后的回调 + */ + onClosed() { + if (!this.state.isOpen) { + this.api.onClosed?.(); + } + } + + /** + * 确认操作 + */ + onConfirm() { + this.api.onConfirm?.(); + } + + /** + * 弹窗打开动画播放完毕后的回调 + */ + onOpened() { + if (this.state.isOpen) { + this.api.onOpened?.(); + } + } + + open() { + this.store.setState((prev) => ({ ...prev, isOpen: true })); + } + + setData(payload: T) { + this.sharedData.payload = payload; + return this; + } + + setState( + stateOrFn: + | ((prev: DrawerState) => Partial) + | Partial, + ) { + if (isFunction(stateOrFn)) { + this.store.setState(stateOrFn); + } else { + this.store.setState((prev) => ({ ...prev, ...stateOrFn })); + } + return this; + } + + /** + * 解除抽屉的锁定状态 + * @description 解除由lock方法设置的锁定状态,是lock(false)的别名 + */ + unlock() { + return this.lock(false); + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts new file mode 100644 index 0000000..1b2ba4e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts @@ -0,0 +1,179 @@ +import type { Component, Ref } from 'vue'; + +import type { ClassType, MaybePromise } from '@vben-core/typings'; + +import type { DrawerApi } from './drawer-api'; + +export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top'; + +export type CloseIconPlacement = 'left' | 'right'; + +export interface DrawerProps { + /** + * 是否挂载到内容区域 + * @default false + */ + appendToMain?: boolean; + /** + * 取消按钮文字 + */ + cancelText?: string; + class?: ClassType; + /** + * 是否显示关闭按钮 + * @default true + */ + closable?: boolean; + /** + * 关闭按钮的位置 + */ + closeIconPlacement?: CloseIconPlacement; + /** + * 点击弹窗遮罩是否关闭弹窗 + * @default true + */ + closeOnClickModal?: boolean; + /** + * 按下 ESC 键是否关闭弹窗 + * @default true + */ + closeOnPressEscape?: boolean; + /** + * 确定按钮 loading + * @default false + */ + confirmLoading?: boolean; + /** + * 确定按钮文字 + */ + confirmText?: string; + contentClass?: string; + /** + * 弹窗描述 + */ + description?: string; + /** + * 在关闭时销毁抽屉 + */ + destroyOnClose?: boolean; + /** + * 是否显示底部 + * @default true + */ + footer?: boolean; + /** + * 弹窗底部样式 + */ + footerClass?: ClassType; + /** + * 是否显示顶栏 + * @default true + */ + header?: boolean; + /** + * 弹窗头部样式 + */ + headerClass?: ClassType; + /** + * 抽屉加载状态 + * @default false + */ + loading?: boolean; + /** + * 是否显示遮罩 + * @default true + */ + modal?: boolean; + + /** + * 是否自动聚焦 + */ + openAutoFocus?: boolean; + /** + * 弹窗遮罩模糊效果 + */ + overlayBlur?: number; + /** + * 抽屉位置 + * @default right + */ + placement?: DrawerPlacement; + + /** + * 是否显示取消按钮 + * @default true + */ + showCancelButton?: boolean; + /** + * 是否显示确认按钮 + * @default true + */ + showConfirmButton?: boolean; + /** + * 提交中(锁定抽屉状态) + */ + submitting?: boolean; + /** + * 弹窗标题 + */ + title?: string; + /** + * 弹窗标题提示 + */ + titleTooltip?: string; + /** + * 抽屉层级 + */ + zIndex?: number; +} + +export interface DrawerState extends DrawerProps { + /** 弹窗打开状态 */ + isOpen?: boolean; + /** + * 共享数据 + */ + sharedData?: Record; +} + +export type ExtendedDrawerApi = DrawerApi & { + useStore: >( + selector?: (state: NoInfer) => T, + ) => Readonly>; +}; + +export interface DrawerApiOptions extends DrawerState { + /** + * 独立的抽屉组件 + */ + connectedComponent?: Component; + /** + * 关闭前的回调,返回 false 可以阻止关闭 + * @returns + */ + onBeforeClose?: () => MaybePromise; + /** + * 点击取消按钮的回调 + */ + onCancel?: () => void; + /** + * 弹窗关闭动画结束的回调 + * @returns + */ + onClosed?: () => void; + /** + * 点击确定按钮的回调 + */ + onConfirm?: () => void; + /** + * 弹窗状态变化回调 + * @param isOpen + * @returns + */ + onOpenChange?: (isOpen: boolean) => void; + /** + * 弹窗打开动画结束的回调 + * @returns + */ + onOpened?: () => void; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue new file mode 100644 index 0000000..5abbf38 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -0,0 +1,338 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/index.ts new file mode 100644 index 0000000..00f87b2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/index.ts @@ -0,0 +1,3 @@ +export type * from './drawer'; +export { default as VbenDrawer } from './drawer.vue'; +export { setDefaultDrawerProps, useVbenDrawer } from './use-drawer'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts new file mode 100644 index 0000000..46ae4fd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts @@ -0,0 +1,152 @@ +import type { + DrawerApiOptions, + DrawerProps, + ExtendedDrawerApi, +} from './drawer'; + +import { + defineComponent, + h, + inject, + nextTick, + provide, + reactive, + ref, +} from 'vue'; + +import { usePreferences } from '@vben-core/preferences'; +import { useSelector } from '@vben-core/shared/store'; + +import { DrawerApi } from './drawer-api'; +import VbenDrawer from './drawer.vue'; + +const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT'); + +const { globalEscapeShortcutKey } = usePreferences(); + +/** + * 默认配置 + */ +const DEFAULT_DRAWER_PROPS: Partial = {}; + +export function setDefaultDrawerProps(props: Partial) { + Object.assign(DEFAULT_DRAWER_PROPS, props); +} + +export function useVbenDrawer< + TParentDrawerProps extends DrawerProps = DrawerProps, +>(options: DrawerApiOptions = {}) { + // Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接 + // 外部的Drawer通过provide/inject传递api + + const defaultOptions = { + closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置 + ...options, + }; + const { connectedComponent } = options; + if (connectedComponent) { + const extendedApi = reactive({}); + const isDrawerReady = ref(true); + const Drawer = defineComponent( + (props: TParentDrawerProps, { attrs, slots }) => { + provide(USER_DRAWER_INJECT_KEY, { + extendApi(api: ExtendedDrawerApi) { + // 不能直接给 reactive 赋值,会丢失响应 + // 不能用 Object.assign,会丢失 api 的原型函数 + Object.setPrototypeOf(extendedApi, api); + }, + options: defaultOptions, + async reCreateDrawer() { + isDrawerReady.value = false; + await nextTick(); + isDrawerReady.value = true; + }, + }); + checkProps(extendedApi as ExtendedDrawerApi, { + ...props, + ...attrs, + ...slots, + }); + return () => + h( + isDrawerReady.value ? connectedComponent : 'div', + { ...props, ...attrs }, + slots, + ); + }, + // eslint-disable-next-line vue/one-component-per-file + { + name: 'VbenParentDrawer', + inheritAttrs: false, + }, + ); + + return [Drawer, extendedApi as ExtendedDrawerApi] as const; + } + + const injectData = inject(USER_DRAWER_INJECT_KEY, {}); + + const mergedOptions = { + ...DEFAULT_DRAWER_PROPS, + ...injectData.options, + ...defaultOptions, + } as DrawerApiOptions; + + mergedOptions.onOpenChange = (isOpen: boolean) => { + options.onOpenChange?.(isOpen); + injectData.options?.onOpenChange?.(isOpen); + }; + + const onClosed = mergedOptions.onClosed; + mergedOptions.onClosed = () => { + onClosed?.(); + if (mergedOptions.destroyOnClose) { + injectData.reCreateDrawer?.(); + } + }; + const api = new DrawerApi(mergedOptions); + + const extendedApi: ExtendedDrawerApi = api as never; + + extendedApi.useStore = (selector) => { + return useSelector(api.store, selector); + }; + + const Drawer = defineComponent( + (props: DrawerProps, { attrs, slots }) => { + return () => + h(VbenDrawer, { ...props, ...attrs, drawerApi: extendedApi }, slots); + }, + // eslint-disable-next-line vue/one-component-per-file + { + name: 'VbenDrawer', + inheritAttrs: false, + }, + ); + injectData.extendApi?.(extendedApi); + return [Drawer, extendedApi] as const; +} + +async function checkProps(api: ExtendedDrawerApi, attrs: Record) { + if (!attrs || Object.keys(attrs).length === 0) { + return; + } + await nextTick(); + + const state = api?.store?.state; + + if (!state) { + return; + } + + const stateKeys = new Set(Object.keys(state)); + + for (const attr of Object.keys(attrs)) { + if (stateKeys.has(attr) && !['class'].includes(attr)) { + // connectedComponent存在时,不要传入Drawer的props,会造成复杂度提升,如果你需要修改Drawer的props,请使用 useVbenDrawer 或者api + console.warn( + `[Vben Drawer]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Drawer, please use useVbenDrawer or api.`, + ); + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/index.ts new file mode 100644 index 0000000..7b9c47f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './alert'; +export * from './drawer'; +export * from './modal'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts new file mode 100644 index 0000000..5067f5f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts @@ -0,0 +1,116 @@ +import type { ModalState } from '../modal'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { ModalApi } from '../modal-api'; + +vi.mock('@vben-core/shared/store', () => { + return { + isFunction: (fn: any) => typeof fn === 'function', + Store: class { + get state() { + return this._state; + } + private _state: ModalState; + private subscribers: Array<(state: ModalState) => void> = []; + + constructor(initialState: ModalState) { + this._state = initialState; + } + + setState(fn: (prev: ModalState) => ModalState) { + this._state = fn(this._state); + this.subscribers.forEach((sub) => sub(this._state)); + } + + subscribe(fn: (state: ModalState) => void) { + this.subscribers.push(fn); + return { unsubscribe: () => {} }; + } + }, + }; +}); + +describe('modalApi', () => { + let modalApi: ModalApi; + // 使用 modalState 而不是 state + let modalState: ModalState; + + beforeEach(() => { + modalApi = new ModalApi(); + // 获取 modalApi 内的 state + modalState = modalApi.store.state; + }); + + it('should initialize with default state', () => { + expect(modalState.isOpen).toBe(false); + expect(modalState.cancelText).toBe(undefined); + expect(modalState.confirmText).toBe(undefined); + }); + + it('should open the modal', () => { + modalApi.open(); + expect(modalApi.store.state.isOpen).toBe(true); + }); + + it('should close the modal if onBeforeClose allows it', () => { + modalApi.close(); + expect(modalApi.store.state.isOpen).toBe(false); + }); + + it('should not close the modal if onBeforeClose returns false', () => { + const onBeforeClose = vi.fn(() => false); + const modalApiWithHook = new ModalApi({ onBeforeClose }); + modalApiWithHook.open(); + modalApiWithHook.close(); + expect(modalApiWithHook.store.state.isOpen).toBe(true); + expect(onBeforeClose).toHaveBeenCalled(); + }); + + it('should trigger onCancel and close the modal if no onCancel hook is provided', () => { + const onCancel = vi.fn(); + const modalApiWithHook = new ModalApi({ onCancel }); + modalApiWithHook.open(); + modalApiWithHook.onCancel(); + expect(onCancel).toHaveBeenCalled(); + expect(modalApiWithHook.store.state.isOpen).toBe(true); + }); + + it('should update shared data correctly', () => { + const testData = { key: 'value' }; + modalApi.setData(testData); + expect(modalApi.getData()).toEqual(testData); + }); + + it('should set state correctly using an object', () => { + modalApi.setState({ title: 'New Title' }); + expect(modalApi.store.state.title).toBe('New Title'); + }); + + it('should set state correctly using a function', () => { + modalApi.setState((prev) => ({ ...prev, confirmText: 'Yes' })); + expect(modalApi.store.state.confirmText).toBe('Yes'); + }); + + it('should call onOpenChange when state changes', () => { + const onOpenChange = vi.fn(); + const modalApiWithHook = new ModalApi({ onOpenChange }); + modalApiWithHook.open(); + expect(onOpenChange).toHaveBeenCalledWith(true); + }); + + it('should call onClosed callback when provided', () => { + const onClosed = vi.fn(); + const modalApiWithHook = new ModalApi({ onClosed }); + modalApiWithHook.onClosed(); + expect(onClosed).toHaveBeenCalled(); + }); + + it('should call onOpened callback when provided', () => { + const onOpened = vi.fn(); + const modalApiWithHook = new ModalApi({ onOpened }); + modalApiWithHook.open(); + modalApiWithHook.onOpened(); + expect(onOpened).toHaveBeenCalled(); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/index.ts new file mode 100644 index 0000000..0cdedf2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/index.ts @@ -0,0 +1,3 @@ +export type * from './modal'; +export { default as VbenModal } from './modal.vue'; +export { setDefaultModalProps, useVbenModal } from './use-modal'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts new file mode 100644 index 0000000..9bfda4e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts @@ -0,0 +1,191 @@ +import type { ModalApiOptions, ModalState } from './modal'; + +import { Store } from '@vben-core/shared/store'; +import { bindMethods, isFunction } from '@vben-core/shared/utils'; + +export class ModalApi { + // 共享数据 + public sharedData: Record<'payload', any> = { + payload: {}, + }; + public store: Store; + + private api: Pick< + ModalApiOptions, + | 'onBeforeClose' + | 'onCancel' + | 'onClosed' + | 'onConfirm' + | 'onOpenChange' + | 'onOpened' + >; + + // private prevState!: ModalState; + private state!: ModalState; + + constructor(options: ModalApiOptions = {}) { + const { + connectedComponent: _, + onBeforeClose, + onCancel, + onClosed, + onConfirm, + onOpenChange, + onOpened, + ...storeState + } = options; + + const defaultState: ModalState = { + bordered: true, + centered: false, + class: '', + closeOnClickModal: true, + closeOnPressEscape: true, + confirmDisabled: false, + confirmLoading: false, + contentClass: '', + destroyOnClose: true, + draggable: false, + overflow: false, + footer: true, + footerClass: '', + fullscreen: false, + fullscreenButton: true, + header: true, + headerClass: '', + isOpen: false, + loading: false, + modal: true, + openAutoFocus: false, + showCancelButton: true, + showConfirmButton: true, + title: '', + animationType: 'slide', + }; + + this.store = new Store({ + ...defaultState, + ...storeState, + }); + + this.store.subscribe((state) => { + // 每次更新状态时,都会调用 onOpenChange 回调函数 + const prevIsOpen = this.state?.isOpen; + this.state = state; + if (state?.isOpen !== prevIsOpen) { + this.api.onOpenChange?.(!!state?.isOpen); + } + }); + + this.state = this.store.state; + + this.api = { + onBeforeClose, + onCancel, + onClosed, + onConfirm, + onOpenChange, + onOpened, + }; + bindMethods(this); + } + + /** + * 关闭弹窗 + * @description 关闭弹窗时会调用 onBeforeClose 钩子函数,如果 onBeforeClose 返回 false,则不关闭弹窗 + */ + async close() { + // 通过 onBeforeClose 钩子函数来判断是否允许关闭弹窗 + // 如果 onBeforeClose 返回 false,则不关闭弹窗 + const allowClose = (await this.api.onBeforeClose?.()) ?? true; + if (allowClose) { + this.store.setState((prev) => ({ + ...prev, + isOpen: false, + })); + } + } + + getData>() { + return (this.sharedData?.payload ?? {}) as T; + } + + /** + * 锁定弹窗状态(用于提交过程中的等待状态) + * @description 锁定状态将禁用默认的取消按钮,使用spinner覆盖弹窗内容,隐藏关闭按钮,阻止手动关闭弹窗,将默认的提交按钮标记为loading状态 + * @param isLocked 是否锁定 + */ + lock(isLocked = true) { + return this.setState({ submitting: isLocked }); + } + + /** + * 取消操作 + */ + onCancel() { + if (this.api.onCancel) { + this.api.onCancel?.(); + } else { + this.close(); + } + } + + /** + * 弹窗关闭动画播放完毕后的回调 + */ + onClosed() { + if (!this.state.isOpen) { + this.api.onClosed?.(); + } + } + + /** + * 确认操作 + */ + onConfirm() { + this.api.onConfirm?.(); + } + + /** + * 弹窗打开动画播放完毕后的回调 + */ + onOpened() { + if (this.state.isOpen) { + this.api.onOpened?.(); + } + } + + open() { + this.store.setState((prev) => ({ + ...prev, + isOpen: true, + submitting: false, + })); + } + + setData(payload: T) { + this.sharedData.payload = payload; + return this; + } + + setState( + stateOrFn: + | ((prev: ModalState) => Partial) + | Partial, + ) { + if (isFunction(stateOrFn)) { + this.store.setState(stateOrFn); + } else { + this.store.setState((prev) => ({ ...prev, ...stateOrFn })); + } + return this; + } + + /** + * 解除弹窗的锁定状态 + * @description 解除由lock方法设置的锁定状态,是lock(false)的别名 + */ + unlock() { + return this.lock(false); + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.ts new file mode 100644 index 0000000..5a7a502 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.ts @@ -0,0 +1,199 @@ +import type { Component, Ref } from 'vue'; + +import type { ClassType, MaybePromise } from '@vben-core/typings'; + +import type { ModalApi } from './modal-api'; + +export interface ModalProps { + /** + * 动画类型 + * @default 'slide' + */ + animationType?: 'scale' | 'slide'; + /** + * 是否要挂载到内容区域 + * @default false + */ + appendToMain?: boolean; + /** + * 是否显示边框 + * @default false + */ + bordered?: boolean; + /** + * 取消按钮文字 + */ + cancelText?: string; + /** + * 是否居中 + * @default false + */ + centered?: boolean; + + class?: ClassType; + + /** + * 是否显示右上角的关闭按钮 + * @default true + */ + closable?: boolean; + /** + * 点击弹窗遮罩是否关闭弹窗 + * @default true + */ + closeOnClickModal?: boolean; + /** + * 按下 ESC 键是否关闭弹窗 + * @default true + */ + closeOnPressEscape?: boolean; + /** + * 禁用确认按钮 + */ + confirmDisabled?: boolean; + /** + * 确定按钮 loading + * @default false + */ + confirmLoading?: boolean; + /** + * 确定按钮文字 + */ + confirmText?: string; + contentClass?: ClassType; + /** + * 弹窗描述 + */ + description?: string; + /** + * 在关闭时销毁弹窗 + */ + destroyOnClose?: boolean; + /** + * 是否可拖拽 + * @default false + */ + draggable?: boolean; + /** + * 是否显示底部 + * @default true + */ + footer?: boolean; + footerClass?: ClassType; + /** + * 是否全屏 + * @default false + */ + fullscreen?: boolean; + /** + * 是否显示全屏按钮 + * @default true + */ + fullscreenButton?: boolean; + /** + * 是否显示顶栏 + * @default true + */ + header?: boolean; + headerClass?: ClassType; + /** + * 弹窗加载状态 + * @default false + */ + loading?: boolean; + /** + * 是否显示遮罩 + * @default true + */ + modal?: boolean; + /** + * 是否自动聚焦 + */ + openAutoFocus?: boolean; + /** + * 拖动范围是否可以超出可视区 + * @default false + */ + overflow?: boolean; + /** + * 弹窗遮罩模糊效果 + */ + overlayBlur?: number; + /** + * 是否显示取消按钮 + * @default true + */ + showCancelButton?: boolean; + /** + * 是否显示确认按钮 + * @default true + */ + showConfirmButton?: boolean; + /** + * 提交中(锁定弹窗状态) + */ + submitting?: boolean; + /** + * 弹窗标题 + */ + title?: string; + /** + * 弹窗标题提示 + */ + titleTooltip?: string; + /** + * 弹窗层级 + */ + zIndex?: number; +} + +export interface ModalState extends ModalProps { + /** 弹窗打开状态 */ + isOpen?: boolean; + /** + * 共享数据 + */ + sharedData?: Record; +} + +export type ExtendedModalApi = ModalApi & { + useStore: >( + selector?: (state: NoInfer) => T, + ) => Readonly>; +}; + +export interface ModalApiOptions extends ModalState { + /** + * 独立的弹窗组件 + */ + connectedComponent?: Component; + /** + * 关闭前的回调,返回 false 可以阻止关闭 + * @returns + */ + onBeforeClose?: () => MaybePromise; + /** + * 点击取消按钮的回调 + */ + onCancel?: () => void; + /** + * 弹窗关闭动画结束的回调 + * @returns + */ + onClosed?: () => void; + /** + * 点击确定按钮的回调 + */ + onConfirm?: () => void; + /** + * 弹窗状态变化回调 + * @param isOpen + * @returns + */ + onOpenChange?: (isOpen: boolean) => void; + /** + * 弹窗打开动画结束的回调 + * @returns + */ + onOpened?: () => void; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.vue new file mode 100644 index 0000000..cc5eb62 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/modal.vue @@ -0,0 +1,376 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts new file mode 100644 index 0000000..a7bcba5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts @@ -0,0 +1,141 @@ +/** + * @copy https://github.com/element-plus/element-plus/blob/dev/packages/hooks/use-draggable/index.ts + * 调整部分细节 + */ + +import type { ComputedRef, Ref } from 'vue'; + +import { onBeforeUnmount, onMounted, reactive, ref, watchEffect } from 'vue'; + +import { unrefElement } from '@vueuse/core'; + +export function useModalDraggable( + targetRef: Ref, + dragRef: Ref, + draggable: ComputedRef, + containerSelector?: ComputedRef, + centered?: ComputedRef, + overflow?: ComputedRef, +) { + const transform = reactive({ + offsetX: 0, + offsetY: 0, + }); + + const dragging = ref(false); + + const onMousedown = (e: MouseEvent) => { + const downX = e.clientX; + const downY = e.clientY; + + if (!targetRef.value) { + return; + } + + const targetRect = targetRef.value.getBoundingClientRect(); + const { offsetX, offsetY } = transform; + const targetLeft = targetRect.left; + const targetTop = targetRect.top; + const targetWidth = targetRect.width; + const targetHeight = targetRect.height; + + let containerRect: DOMRect | null = null; + + if (containerSelector?.value) { + const container = document.querySelector(containerSelector.value); + if (container) { + containerRect = container.getBoundingClientRect(); + } + } + + let maxLeft, maxTop, minLeft, minTop; + if (containerRect) { + minLeft = containerRect.left - targetLeft + offsetX; + maxLeft = containerRect.right - targetLeft - targetWidth + offsetX; + minTop = containerRect.top - targetTop + offsetY; + maxTop = containerRect.bottom - targetTop - targetHeight + offsetY; + } else { + const docElement = document.documentElement; + const clientWidth = docElement.clientWidth; + const clientHeight = docElement.clientHeight; + minLeft = -targetLeft + offsetX; + minTop = -targetTop + offsetY; + maxLeft = clientWidth - targetLeft - targetWidth + offsetX; + maxTop = clientHeight - targetTop - targetHeight + offsetY; + } + + const onMousemove = (e: MouseEvent) => { + let moveX = offsetX + e.clientX - downX; + let moveY = offsetY + e.clientY - downY; + + if (!overflow?.value) { + moveX = Math.min(Math.max(moveX, minLeft), maxLeft); + moveY = Math.min(Math.max(moveY, minTop), maxTop); + } + + transform.offsetX = moveX; + transform.offsetY = moveY; + + if (targetRef.value) { + const isCentered = centered?.value; + targetRef.value.style.transform = isCentered + ? `translate(${moveX}px, calc(-50% + ${moveY}px))` + : `translate(${moveX}px, ${moveY}px)`; + dragging.value = true; + } + }; + + const onMouseup = () => { + dragging.value = false; + document.removeEventListener('mousemove', onMousemove); + document.removeEventListener('mouseup', onMouseup); + }; + + document.addEventListener('mousemove', onMousemove); + document.addEventListener('mouseup', onMouseup); + }; + + const onDraggable = () => { + const dragDom = unrefElement(dragRef); + if (dragDom && targetRef.value) { + dragDom.addEventListener('mousedown', onMousedown); + } + }; + + const offDraggable = () => { + const dragDom = unrefElement(dragRef); + if (dragDom && targetRef.value) { + dragDom.removeEventListener('mousedown', onMousedown); + } + }; + + const resetPosition = () => { + transform.offsetX = 0; + transform.offsetY = 0; + + const target = unrefElement(targetRef); + if (target) { + target.style.transform = ''; + } + }; + + onMounted(() => { + watchEffect(() => { + if (draggable.value) { + onDraggable(); + } else { + offDraggable(); + } + }); + }); + + onBeforeUnmount(() => { + offDraggable(); + }); + + return { + dragging, + resetPosition, + transform, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts new file mode 100644 index 0000000..87456a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts @@ -0,0 +1,168 @@ +import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal'; + +import { + defineComponent, + h, + inject, + nextTick, + provide, + reactive, + ref, +} from 'vue'; + +import { usePreferences } from '@vben-core/preferences'; +import { useSelector } from '@vben-core/shared/store'; + +import { ModalApi } from './modal-api'; +import VbenModal from './modal.vue'; + +const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT'); + +const { globalEscapeShortcutKey } = usePreferences(); +/** + * 默认配置 + */ +const DEFAULT_MODAL_PROPS: Partial = {}; + +export function setDefaultModalProps(props: Partial) { + Object.assign(DEFAULT_MODAL_PROPS, props); +} + +export function useVbenModal( + options: ModalApiOptions = {}, +) { + // Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接 + // 外部的Modal通过provide/inject传递api + + const defaultOptions = { + closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置 + ...options, + }; + const { connectedComponent } = options; + if (connectedComponent) { + const extendedApi = reactive({}); + const isModalReady = ref(true); + const Modal = defineComponent( + (props: TParentModalProps, { attrs, slots }) => { + provide(USER_MODAL_INJECT_KEY, { + extendApi(api: ExtendedModalApi) { + // 不能直接给 reactive 赋值,会丢失响应 + // 不能用 Object.assign,会丢失 api 的原型函数 + Object.setPrototypeOf(extendedApi, api); + }, + consumed: false, + options: defaultOptions, + async reCreateModal() { + isModalReady.value = false; + await nextTick(); + isModalReady.value = true; + }, + }); + checkProps(extendedApi as ExtendedModalApi, { + ...props, + ...attrs, + ...slots, + }); + return () => + h( + isModalReady.value ? connectedComponent : 'div', + { + ...props, + ...attrs, + }, + slots, + ); + }, + // eslint-disable-next-line vue/one-component-per-file + { + name: 'VbenParentModal', + inheritAttrs: false, + }, + ); + + return [Modal, extendedApi as ExtendedModalApi] as const; + } + + let injectData = inject(USER_MODAL_INJECT_KEY, {}); + // 这个数据已经被使用了,说明这个弹窗是嵌套的弹窗,不应该merge上层的配置 + if (injectData.consumed) { + injectData = {}; + } else { + injectData.consumed = true; + } + + const mergedOptions = { + ...DEFAULT_MODAL_PROPS, + ...injectData.options, + ...defaultOptions, + } as ModalApiOptions; + + mergedOptions.onOpenChange = (isOpen: boolean) => { + options.onOpenChange?.(isOpen); + injectData.options?.onOpenChange?.(isOpen); + }; + + const onClosed = mergedOptions.onClosed; + mergedOptions.onClosed = () => { + onClosed?.(); + if (mergedOptions.destroyOnClose) { + injectData.consumed = false; + injectData.reCreateModal?.(); + } + }; + + const api = new ModalApi(mergedOptions); + + const extendedApi: ExtendedModalApi = api as never; + + extendedApi.useStore = (selector) => { + return useSelector(api.store, selector); + }; + + const Modal = defineComponent( + (props: ModalProps, { attrs, slots }) => { + return () => + h( + VbenModal, + { + ...props, + ...attrs, + modalApi: extendedApi, + }, + slots, + ); + }, + // eslint-disable-next-line vue/one-component-per-file + { + name: 'VbenModal', + inheritAttrs: false, + }, + ); + injectData.extendApi?.(extendedApi); + + return [Modal, extendedApi] as const; +} + +async function checkProps(api: ExtendedModalApi, attrs: Record) { + if (!attrs || Object.keys(attrs).length === 0) { + return; + } + await nextTick(); + + const state = api?.store?.state; + + if (!state) { + return; + } + + const stateKeys = new Set(Object.keys(state)); + + for (const attr of Object.keys(attrs)) { + if (stateKeys.has(attr) && !['class'].includes(attr)) { + // connectedComponent存在时,不要传入Modal的props,会造成复杂度提升,如果你需要修改Modal的props,请使用 useModal 或者api + console.warn( + `[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useVbenModal or api.`, + ); + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsdown.config.ts new file mode 100644 index 0000000..43e8cf1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/popup-ui/tsdown.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsdown'; +import Vue from 'unplugin-vue/rolldown'; + +export default defineConfig({ + clean: true, + deps: { + skipNodeModulesBundle: true, + }, + dts: { + vue: true, + }, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + js: '.mjs', + }), + platform: 'neutral', + plugins: [Vue({ isProduction: true })], + unbundle: true, +}); diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/components.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/components.json new file mode 100644 index 0000000..dcb1940 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/components.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://shadcn-vue.com/schema.json", + "style": "new-york", + "typescript": true, + "tailwind": { + "config": "", + "css": "src/assets/index.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "@vben-core/shadcn-ui/components", + "utils": "@vben-core/shared/utils" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/package.json new file mode 100644 index 0000000..49513aa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/package.json @@ -0,0 +1,52 @@ +{ + "name": "@vben-core/shadcn-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/shadcn-ui" + }, + "license": "MIT", + "type": "module", + "files": [ + "components.json", + "src" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./src/index.ts", + "module": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "default": "./src/index.ts" + } + } + }, + "dependencies": { + "@lucide/vue": "catalog:", + "@vben-core/composables": "workspace:*", + "@vben-core/design": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "class-variance-authority": "catalog:", + "reka-ui": "catalog:", + "vee-validate": "catalog:", + "vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/assets/index.css b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/assets/index.css new file mode 100644 index 0000000..cbd9be6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/assets/index.css @@ -0,0 +1 @@ +@reference "@vben/tailwind-config/theme"; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue new file mode 100644 index 0000000..1db522b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue @@ -0,0 +1,76 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/index.ts new file mode 100644 index 0000000..8a2169c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/avatar/index.ts @@ -0,0 +1 @@ +export { default as VbenAvatar } from './avatar.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue new file mode 100644 index 0000000..4dc05e4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue @@ -0,0 +1,43 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/backtop.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/backtop.ts new file mode 100644 index 0000000..de422c0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/backtop.ts @@ -0,0 +1,38 @@ +export const backtopProps = { + /** + * @zh_CN bottom distance. + */ + bottom: { + default: 40, + type: Number, + }, + /** + * @zh_CN right distance. + */ + right: { + default: 40, + type: Number, + }, + /** + * @zh_CN the target to trigger scroll. + */ + target: { + default: '', + type: String, + }, + /** + * @zh_CN the button will not show until the scroll height reaches this value. + */ + visibilityHeight: { + default: 200, + type: Number, + }, +} as const; + +export interface BacktopProps { + bottom?: number; + isGroup?: boolean; + right?: number; + target?: string; + visibilityHeight?: number; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/index.ts new file mode 100644 index 0000000..7a6e543 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/index.ts @@ -0,0 +1 @@ +export { default as VbenBackTop } from './back-top.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/use-backtop.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/use-backtop.ts new file mode 100644 index 0000000..6a51a95 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/back-top/use-backtop.ts @@ -0,0 +1,45 @@ +import type { BacktopProps } from './backtop'; + +import { onMounted, ref, shallowRef } from 'vue'; + +import { useEventListener, useThrottleFn } from '@vueuse/core'; + +export const useBackTop = (props: BacktopProps) => { + const el = shallowRef(); + const container = shallowRef(); + const visible = ref(false); + + const handleScroll = () => { + if (el.value) { + visible.value = el.value.scrollTop >= (props?.visibilityHeight ?? 0); + } + }; + + const handleClick = () => { + el.value?.scrollTo({ behavior: 'smooth', top: 0 }); + }; + + const handleScrollThrottled = useThrottleFn(handleScroll, 300, true); + + useEventListener(container, 'scroll', handleScrollThrottled); + onMounted(() => { + container.value = document; + el.value = document.documentElement; + + if (props.target) { + el.value = document.querySelector(props.target) ?? undefined; + + if (!el.value) { + throw new Error(`target does not exist: ${props.target}`); + } + container.value = el.value; + } + // Give visible an initial value, fix #13066 + handleScroll(); + }); + + return { + handleClick, + visible, + }; +}; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue new file mode 100644 index 0000000..b8c560c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue @@ -0,0 +1,111 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-view.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-view.vue new file mode 100644 index 0000000..3e73343 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-view.vue @@ -0,0 +1,39 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue new file mode 100644 index 0000000..db2ce65 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue @@ -0,0 +1,98 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/index.ts new file mode 100644 index 0000000..9e31cb1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/index.ts @@ -0,0 +1,3 @@ +export { default as VbenBreadcrumbView } from './breadcrumb-view.vue'; + +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/types.ts new file mode 100644 index 0000000..0080f23 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/types.ts @@ -0,0 +1,17 @@ +import type { Component } from 'vue'; + +import type { BreadcrumbStyleType } from '@vben-core/typings'; + +export interface IBreadcrumb { + icon?: Component | string; + isHome?: boolean; + items?: IBreadcrumb[]; + path?: string; + title?: string; +} + +export interface BreadcrumbProps { + breadcrumbs: IBreadcrumb[]; + showIcon?: boolean; + styleType?: BreadcrumbStyleType; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button-group.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button-group.vue new file mode 100644 index 0000000..035e88c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button-group.vue @@ -0,0 +1,98 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts new file mode 100644 index 0000000..75769d3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts @@ -0,0 +1,55 @@ +import type { AsTag } from 'reka-ui'; + +import type { Component } from 'vue'; + +import type { ButtonVariants } from '../../ui'; + +export interface VbenButtonProps { + /** + * The element or component this component should render as. Can be overwrite by `asChild` + * @defaultValue "div" + */ + as?: AsTag | Component; + /** + * Change the default rendered element for the one passed as a child, merging their props and behavior. + * + * Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. + */ + asChild?: boolean; + class?: any; + disabled?: boolean; + loading?: boolean; + size?: ButtonVariants['size']; + variant?: ButtonVariants['variant']; +} + +export type CustomRenderType = (() => Component | string) | string; + +export type ValueType = boolean | number | string; + +export interface VbenButtonGroupProps extends Pick< + VbenButtonProps, + 'disabled' +> { + /** 单选模式下允许清除选中 */ + allowClear?: boolean; + /** 值改变前的回调 */ + beforeChange?: ( + value: ValueType, + isChecked: boolean, + ) => boolean | PromiseLike | undefined; + /** 按钮样式 */ + btnClass?: any; + /** 按钮间隔距离 */ + gap?: number; + /** 多选模式下限制最多选择的数量。0表示不限制 */ + maxCount?: number; + /** 是否允许多选 */ + multiple?: boolean; + /** 选项 */ + options?: { [key: string]: any; label: CustomRenderType; value: ValueType }[]; + /** 显示图标 */ + showIcon?: boolean; + /** 尺寸 */ + size?: 'large' | 'middle' | 'small'; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue new file mode 100644 index 0000000..baf6d35 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue @@ -0,0 +1,42 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue new file mode 100644 index 0000000..4172d82 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue @@ -0,0 +1,196 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue new file mode 100644 index 0000000..f9c7b0b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue @@ -0,0 +1,68 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/index.ts new file mode 100644 index 0000000..aa3d9ef --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/button/index.ts @@ -0,0 +1,5 @@ +export type * from './button'; +export { default as VbenButtonGroup } from './button-group.vue'; +export { default as VbenButton } from './button.vue'; +export { default as VbenCheckButtonGroup } from './check-button-group.vue'; +export { default as VbenIconButton } from './icon-button.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/checkbox.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/checkbox.vue new file mode 100644 index 0000000..bceaa20 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/checkbox.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/index.ts new file mode 100644 index 0000000..3df9ded --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/index.ts @@ -0,0 +1 @@ +export { default as VbenCheckbox } from './checkbox.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params-item.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params-item.vue new file mode 100644 index 0000000..85b0197 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params-item.vue @@ -0,0 +1,123 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue new file mode 100644 index 0000000..dd1cce7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue @@ -0,0 +1,263 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue new file mode 100644 index 0000000..d6e4e2c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue @@ -0,0 +1,79 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts new file mode 100644 index 0000000..8563a7e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts @@ -0,0 +1,4 @@ +export { default as VbenCollapsibleParams } from './collapsible-params.vue'; +export { default as VbenCollapsible } from './collapsible.vue'; + +export * from './type'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts new file mode 100644 index 0000000..f2fcffd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts @@ -0,0 +1,22 @@ +export interface CollapsibleParamsProps { + defaultOpen?: boolean; + maxHeight?: number | string; + params: CollapsibleParamSchema[]; + visibleCount?: number; +} + +export interface CollapsibleParamOption { + [key: string]: any; + max?: number; + min?: number; + precision?: number; + step?: number; + type?: 'exponential' | 'number' | 'select' | 'string'; +} + +export interface CollapsibleParamSchema { + defaultValue?: number | number[] | string | string[]; + description: string; + key: string; + option: CollapsibleParamOption; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue new file mode 100644 index 0000000..c36fd4c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue @@ -0,0 +1,128 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/index.ts new file mode 100644 index 0000000..53a3abe --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/index.ts @@ -0,0 +1,3 @@ +export { default as VbenContextMenu } from './context-menu.vue'; + +export type * from './interface'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/interface.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/interface.ts new file mode 100644 index 0000000..4aa4110 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/interface.ts @@ -0,0 +1,42 @@ +import type { Component } from 'vue'; + +interface IContextMenuItem { + /** + * @zh_CN 是否禁用 + */ + disabled?: boolean; + /** + * @zh_CN 点击事件处理 + * @param data + */ + handler?: (data: any) => void; + /** + * @zh_CN 是否隐藏 + */ + hidden?: boolean; + /** + * @zh_CN 图标 + */ + icon?: Component; + /** + * @zh_CN 是否显示图标 + */ + inset?: boolean; + /** + * @zh_CN 唯一标识 + */ + key: string; + /** + * @zh_CN 是否是分割线 + */ + separator?: boolean; + /** + * @zh_CN 快捷键 + */ + shortcut?: string; + /** + * @zh_CN 标题 + */ + text: string; +} +export type { IContextMenuItem }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/count-to-animator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/count-to-animator.vue new file mode 100644 index 0000000..50fece6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/count-to-animator.vue @@ -0,0 +1,128 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts new file mode 100644 index 0000000..a97bafb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts @@ -0,0 +1 @@ +export { default as VbenCountToAnimator } from './count-to-animator.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue new file mode 100644 index 0000000..a67f606 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue @@ -0,0 +1,129 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue new file mode 100644 index 0000000..677bb16 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue @@ -0,0 +1,57 @@ + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue new file mode 100644 index 0000000..645d2f3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue @@ -0,0 +1,112 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue new file mode 100644 index 0000000..a170b50 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue @@ -0,0 +1,91 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts new file mode 100644 index 0000000..1003a73 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts @@ -0,0 +1,4 @@ +export { default as VbenDescriptionsItem } from './descriptions-item.vue'; +export { default as VbenDescriptions } from './descriptions.vue'; + +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts new file mode 100644 index 0000000..0dd11c6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts @@ -0,0 +1,93 @@ +import type { Component, CSSProperties } from 'vue'; + +/** 响应式断点,与 antdv-next 保持一致 */ +export type DescriptionsBreakpoint = + | 'lg' + | 'md' + | 'sm' + | 'xl' + | 'xs' + | 'xxl' + | 'xxxl'; + +/** 当前命中的断点集合 */ +export type ScreenMap = Partial>; + +export type DescriptionsLayout = 'horizontal' | 'vertical'; + +export type DescriptionsSize = 'large' | 'middle' | 'small'; + +/** 列数,可为固定数字或按断点配置 */ +export type DescriptionsColumn = + | number + | Partial>; + +/** 单项跨列,支持固定数字、'filled'(占满当前行剩余)或按断点配置 */ +export type DescriptionsItemSpan = + | 'filled' + | number + | Partial>; + +/** 可渲染内容:字符串/数字/渲染函数/组件 */ +export type DescriptionsRenderNode = (() => any) | Component | number | string; + +export interface DescriptionsItemType { + /** 内容 */ + content?: DescriptionsRenderNode; + /** 内容样式 */ + contentStyle?: CSSProperties; + /** 唯一 key */ + key?: number | string; + /** 标签 */ + label?: DescriptionsRenderNode; + /** 标签样式 */ + labelStyle?: CSSProperties; + /** 跨列 */ + span?: DescriptionsItemSpan; +} + +export interface DescriptionsProps { + /** 是否展示边框 */ + bordered?: boolean; + class?: any; + /** 是否显示冒号(仅非 bordered 的水平布局生效) */ + colon?: boolean; + /** 一行的列数 */ + column?: DescriptionsColumn; + /** 统一的内容样式 */ + contentStyle?: CSSProperties; + /** 操作区域,位于标题右侧 */ + extra?: string; + /** 数据驱动的列表项;不传则读取默认插槽中的 VbenDescriptionsItem */ + items?: DescriptionsItemType[]; + /** 统一的标签样式 */ + labelStyle?: CSSProperties; + /** 布局方式 */ + layout?: DescriptionsLayout; + /** 尺寸 */ + size?: DescriptionsSize; + /** 标题 */ + title?: string; +} + +export interface DescriptionsItemProps { + content?: DescriptionsRenderNode; + contentStyle?: CSSProperties; + label?: DescriptionsRenderNode; + labelStyle?: CSSProperties; + span?: DescriptionsItemSpan; +} + +/** 归一化后的内部项,span 已解析为数字 */ +export interface InternalDescriptionsItem { + _index?: number; + class?: string; + content?: DescriptionsRenderNode; + contentStyle?: CSSProperties; + filled?: boolean; + key?: number | string; + label?: DescriptionsRenderNode; + labelStyle?: CSSProperties; + span?: number; + style?: CSSProperties; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts new file mode 100644 index 0000000..2c1f04f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts @@ -0,0 +1,216 @@ +import type { VNode } from 'vue'; + +import type { + DescriptionsBreakpoint, + DescriptionsColumn, + DescriptionsItemType, + InternalDescriptionsItem, + ScreenMap, +} from './types'; + +import { Comment, computed, Fragment } from 'vue'; + +import { useBreakpoints } from '@vueuse/core'; + +/** 默认列数映射 */ +export const DEFAULT_COLUMN_MAP: Record = { + lg: 3, + md: 3, + sm: 2, + xl: 3, + xs: 1, + xxl: 3, + xxxl: 4, +}; + +/** 由大到小的断点顺序,matchScreen 按此顺序取第一个命中的值 */ +const RESPONSIVE_ARRAY: DescriptionsBreakpoint[] = [ + 'xxxl', + 'xxl', + 'xl', + 'lg', + 'md', + 'sm', + 'xs', +]; + +/** 断点像素值 */ +const BREAKPOINT_PX = { + sm: 576, + md: 768, + lg: 992, + xl: 1200, + xxl: 1600, + xxxl: 2000, +}; + +/** + * 在给定的断点配置中,按由大到小的顺序取第一个命中的值 + */ +export function matchScreen( + screens: ScreenMap, + screenSizes?: Partial>, +): number | undefined { + if (!screenSizes) return undefined; + for (const breakpoint of RESPONSIVE_ARRAY) { + if (screens[breakpoint] && screenSizes[breakpoint] !== undefined) { + return screenSizes[breakpoint]; + } + } + return undefined; +} + +/** + * 监听视口宽度,返回当前命中的断点集合 + */ +export function useScreens() { + const breakpoints = useBreakpoints(BREAKPOINT_PX); + return computed(() => ({ + lg: breakpoints.lg.value, + md: breakpoints.md.value, + sm: breakpoints.sm.value, + xl: breakpoints.xl.value, + xs: !breakpoints.sm.value, + xxl: breakpoints.xxl.value, + xxxl: breakpoints.xxxl.value, + })); +} + +/** + * 计算最终列数:固定数字直接返回,否则按断点解析 + */ +export function resolveColumn( + column: DescriptionsColumn | undefined, + screens: ScreenMap, +): number { + if (typeof column === 'number') return column; + return matchScreen(screens, { ...DEFAULT_COLUMN_MAP, ...column }) ?? 3; +} + +/** + * 归一化列表项:将 span 解析为数字,'filled' 标记为 filled + */ +export function normalizeItems( + items: DescriptionsItemType[], + screens: ScreenMap, +): InternalDescriptionsItem[] { + return items.map((item, index) => { + const { span, ...rest } = item; + if (span === 'filled') { + return { ...rest, _index: index, filled: true }; + } + return { + ...rest, + _index: index, + span: typeof span === 'number' ? span : matchScreen(screens, span), + }; + }); +} + +/** + * 行装箱算法:根据列数与各项 span 将列表项拆分为多行, + * 并补齐每行最后一项以占满列数。移植自 antdv-next useRow。 + */ +export function calcRows( + items: InternalDescriptionsItem[], + column: number, +): InternalDescriptionsItem[][] { + let rows: InternalDescriptionsItem[][] = []; + let tmpRow: InternalDescriptionsItem[] = []; + let count = 0; + + items.filter(Boolean).forEach((item) => { + const { filled, ...rest } = item; + // filled:占满当前行剩余,并立即换行 + if (filled) { + tmpRow.push(rest); + rows.push(tmpRow); + tmpRow = []; + count = 0; + return; + } + + const restSpan = column - count; + count += item.span || 1; + + if (count >= column) { + // 超出列数时,将当前项 span 收敛为剩余列数,避免溢出 + tmpRow.push(count > column ? { ...rest, span: restSpan } : rest); + rows.push(tmpRow); + tmpRow = []; + count = 0; + } else { + tmpRow.push(rest); + } + }); + + if (tmpRow.length > 0) rows.push(tmpRow); + + // 补齐:若一行总 span 不足列数,扩展最后一项 + rows = rows.map((row) => { + const total = row.reduce((acc, item) => acc + (item.span || 1), 0); + if (total < column) { + const last = row[row.length - 1]; + if (last) { + last.span = column - (total - (last.span || 1)); + } + } + return row; + }); + + return rows; +} + +/** 标记组件类型为 DescriptionsItem,便于从插槽 vnode 中识别 */ +export const DESCRIPTIONS_ITEM_NAME = 'VbenDescriptionsItem'; + +function isItemVNode(node: VNode): boolean { + const type = node.type as any; + return ( + !!type && + (type.__isDescriptionsItem === true || type.name === DESCRIPTIONS_ITEM_NAME) + ); +} + +function flattenVNodes(nodes: VNode[]): VNode[] { + const result: VNode[] = []; + for (const node of nodes) { + if (node.type === Fragment && Array.isArray(node.children)) { + result.push(...flattenVNodes(node.children as VNode[])); + } else if (node.type !== Comment) { + result.push(node); + } + } + return result; +} + +/** + * 从默认插槽的 vnode 中解析出列表项,支持 + * content 写法 + */ +export function parseItemsFromSlot(nodes: VNode[]): DescriptionsItemType[] { + return flattenVNodes(nodes) + .filter((node) => isItemVNode(node)) + .map((node) => { + const props = (node.props ?? {}) as Record; + const children = (node.children ?? {}) as Record; + const labelSlot = + typeof children.label === 'function' ? children.label : undefined; + const contentDefaultSlot = + typeof children.default === 'function' ? children.default : undefined; + const contentSlot = + typeof children.content === 'function' + ? children.content + : contentDefaultSlot; + return { + class: props.class, + content: contentSlot ?? props.content, + contentStyle: props.contentStyle ?? props['content-style'], + key: node.key ?? undefined, + label: labelSlot ?? props.label, + labelStyle: props.labelStyle ?? props['label-style'], + span: props.span, + style: props.style, + } as DescriptionsItemType; + }); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-menu.vue new file mode 100644 index 0000000..1eea1e3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-menu.vue @@ -0,0 +1,49 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-radio-menu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-radio-menu.vue new file mode 100644 index 0000000..172e694 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-radio-menu.vue @@ -0,0 +1,52 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/index.ts new file mode 100644 index 0000000..bff1700 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/index.ts @@ -0,0 +1,4 @@ +export { default as VbenDropdownMenu } from './dropdown-menu.vue'; +export { default as VbenDropdownRadioMenu } from './dropdown-radio-menu.vue'; + +export type * from './interface'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/interface.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/interface.ts new file mode 100644 index 0000000..01a31a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/interface.ts @@ -0,0 +1,32 @@ +import type { Component } from 'vue'; + +interface VbenDropdownMenuItem { + disabled?: boolean; + /** + * @zh_CN 点击事件处理 + * @param data + */ + handler?: (data: any) => void; + /** + * @zh_CN 图标 + */ + icon?: Component; + /** + * @zh_CN 标题 + */ + label: string; + /** + * @zh_CN 是否是分割线 + */ + separator?: boolean; + /** + * @zh_CN 唯一标识 + */ + value: string; +} + +interface DropdownMenuProps { + menus: VbenDropdownMenuItem[]; +} + +export type { DropdownMenuProps, VbenDropdownMenuItem }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/expandable-arrow.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/expandable-arrow.vue new file mode 100644 index 0000000..5cdf619 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/expandable-arrow.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/index.ts new file mode 100644 index 0000000..a35bd9a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/index.ts @@ -0,0 +1 @@ +export { default as VbenExpandableArrow } from './expandable-arrow.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/full-screen.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/full-screen.vue new file mode 100644 index 0000000..1dcfbbb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/full-screen.vue @@ -0,0 +1,31 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/index.ts new file mode 100644 index 0000000..3997ee0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/index.ts @@ -0,0 +1 @@ +export { default as VbenFullScreen } from './full-screen.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/hover-card.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/hover-card.vue new file mode 100644 index 0000000..47ddad8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/hover-card.vue @@ -0,0 +1,55 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/index.ts new file mode 100644 index 0000000..3582521 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/index.ts @@ -0,0 +1,2 @@ +export { default as VbenHoverCard } from './hover-card.vue'; +export type { HoverCardContentProps } from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue new file mode 100644 index 0000000..80de0d7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue @@ -0,0 +1,35 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/index.ts new file mode 100644 index 0000000..8f3f204 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/icon/index.ts @@ -0,0 +1 @@ +export { default as VbenIcon } from './icon.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/index.ts new file mode 100644 index 0000000..7e90a2b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/index.ts @@ -0,0 +1,26 @@ +export * from './avatar'; +export * from './back-top'; +export * from './breadcrumb'; +export * from './button'; +export * from './checkbox'; +export * from './collapsible'; +export * from './context-menu'; +export * from './count-to-animator'; +export * from './descriptions'; +export * from './dropdown-menu'; +export * from './expandable-arrow'; +export * from './full-screen'; +export * from './hover-card'; +export * from './icon'; +export * from './input-password'; +export * from './logo'; +export * from './pin-input'; +export * from './popover'; +export * from './render-content'; +export * from './scrollbar'; +export * from './segmented'; +export * from './select'; +export * from './spine-text'; +export * from './spinner'; +export * from './table-action'; +export * from './tooltip'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/index.ts new file mode 100644 index 0000000..eeca558 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/index.ts @@ -0,0 +1 @@ +export { default as VbenInputPassword } from './input-password.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue new file mode 100644 index 0000000..c32d3f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue @@ -0,0 +1,57 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/password-strength.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/password-strength.vue new file mode 100644 index 0000000..e658642 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/input-password/password-strength.vue @@ -0,0 +1,66 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/index.ts new file mode 100644 index 0000000..d2a2f99 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/index.ts @@ -0,0 +1 @@ +export { default as VbenLogo } from './logo.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/logo.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/logo.vue new file mode 100644 index 0000000..3be3414 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/logo/logo.vue @@ -0,0 +1,92 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/index.ts new file mode 100644 index 0000000..9d6a61a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/index.ts @@ -0,0 +1,3 @@ +export { default as VbenPinInput } from './input.vue'; + +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue new file mode 100644 index 0000000..e64e655 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue @@ -0,0 +1,122 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/types.ts new file mode 100644 index 0000000..182d884 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/types.ts @@ -0,0 +1,30 @@ +interface PinInputProps { + class?: any; + /** + * 验证码长度 + */ + codeLength?: number; + /** + * 发送验证码按钮文本 + */ + createText?: (countdown: number) => string; + /** + * 是否禁用 + */ + disabled?: boolean; + /** + * 自定义验证码发送逻辑 + * @returns + */ + handleSendCode?: () => Promise; + /** + * 发送验证码按钮loading + */ + loading?: boolean; + /** + * 最大重试时间 + */ + maxTime?: number; +} + +export type { PinInputProps }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/index.ts new file mode 100644 index 0000000..bc0b64a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/index.ts @@ -0,0 +1 @@ +export { default as VbenPopover } from './popover.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/popover.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/popover.vue new file mode 100644 index 0000000..2350d17 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/popover/popover.vue @@ -0,0 +1,60 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/index.ts new file mode 100644 index 0000000..788c8af --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/index.ts @@ -0,0 +1 @@ +export { default as VbenRenderContent } from './render-content.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue new file mode 100644 index 0000000..4708662 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue @@ -0,0 +1,59 @@ + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/index.ts new file mode 100644 index 0000000..57f6f1b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/index.ts @@ -0,0 +1 @@ +export { default as VbenScrollbar } from './scrollbar.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue new file mode 100644 index 0000000..d1d394d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/index.ts new file mode 100644 index 0000000..1ec820d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/index.ts @@ -0,0 +1,3 @@ +export { default as VbenSegmented } from './segmented.vue'; + +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue new file mode 100644 index 0000000..8898556 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue @@ -0,0 +1,67 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/tabs-indicator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/tabs-indicator.vue new file mode 100644 index 0000000..8b5335b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/tabs-indicator.vue @@ -0,0 +1,37 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/types.ts new file mode 100644 index 0000000..b24f983 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/segmented/types.ts @@ -0,0 +1,6 @@ +interface SegmentedItem { + label: string; + value: string; +} + +export type { SegmentedItem }; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/index.ts new file mode 100644 index 0000000..1aa5d98 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/index.ts @@ -0,0 +1 @@ +export { default as VbenSelect } from './select.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/select.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/select.vue new file mode 100644 index 0000000..24b8ea2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/select/select.vue @@ -0,0 +1,57 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/index.ts new file mode 100644 index 0000000..a529826 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/index.ts @@ -0,0 +1 @@ +export { default as VbenSpineText } from './spine-text.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/spine-text.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/spine-text.vue new file mode 100644 index 0000000..dfc63d4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/spine-text.vue @@ -0,0 +1,49 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts new file mode 100644 index 0000000..4bba740 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts @@ -0,0 +1,2 @@ +export { default as VbenLoading } from './loading.vue'; +export { default as VbenSpinner } from './spinner.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue new file mode 100644 index 0000000..d9588c9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue new file mode 100644 index 0000000..2bc5035 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-dropdown-item.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-dropdown-item.vue new file mode 100644 index 0000000..b75ef33 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-dropdown-item.vue @@ -0,0 +1,116 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-item.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-item.vue new file mode 100644 index 0000000..4d927f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-item.vue @@ -0,0 +1,97 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/index.ts new file mode 100644 index 0000000..bf89502 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/index.ts @@ -0,0 +1,3 @@ +export { default as VbenTableAction } from './table-action.vue'; + +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/table-action.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/table-action.vue new file mode 100644 index 0000000..d5541ea --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/table-action.vue @@ -0,0 +1,216 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/types.ts new file mode 100644 index 0000000..428961c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/table-action/types.ts @@ -0,0 +1,75 @@ +import type { ButtonVariants } from '../../ui'; + +import { VbenIcon } from '../icon'; + +/** 权限码:单个或多个,配合注入的 hasPermission 判断 */ +export type TableActionAuth = string | string[]; + +/** 操作按钮提示 */ +export interface TableActionTooltip { + content: string; + side?: 'bottom' | 'left' | 'right' | 'top'; +} + +/** 气泡确认框配置 */ +export interface TableActionPopConfirm { + /** 取消按钮文案 */ + cancelText?: string; + /** 确认回调;未提供时回退到 action.onClick */ + confirm?: () => void; + /** 确认按钮文案 */ + okText?: string; + /** 提示标题 */ + title?: string; +} + +export interface ActionItem { + /** 权限码,配合注入的 hasPermission 过滤 */ + auth?: TableActionAuth; + /** 自定义类名 */ + class?: any; + /** 危险操作(红色文字) */ + danger?: boolean; + /** 是否禁用 */ + disabled?: boolean; + /** 图标组件 */ + icon?: typeof VbenIcon.icon; + /** 是否显示:布尔或返回布尔的函数 */ + ifShow?: (() => boolean) | boolean; + /** 唯一标识,点击回调可据此区分 */ + key?: number | string; + /** 加载状态 */ + loading?: boolean; + /** 点击回调 */ + onClick?: () => void; + /** 气泡确认框 */ + popConfirm?: TableActionPopConfirm; + /** 尺寸 */ + size?: ButtonVariants['size']; + /** 文本 */ + text?: string; + /** 提示:字符串或配置对象 */ + tooltip?: string | TableActionTooltip; + /** 按钮样式变体 */ + variant?: ButtonVariants['variant']; +} + +export interface TableActionProps { + /** 主操作按钮 */ + actions?: ActionItem[]; + /** 对齐方式 */ + align?: 'center' | 'end' | 'start'; + /** 自定义类名 */ + class?: any; + /** 按钮之间是否显示分割线 */ + divider?: boolean; + /** “更多”下拉中的操作 */ + dropdownActions?: ActionItem[]; + /** + * 权限判断函数,返回 false 则隐藏对应 auth 的操作。 + * 核心组件不依赖业务,由使用方注入(如 useAccess().hasAccessByCodes)。 + */ + hasPermission?: (auth?: TableActionAuth) => boolean; + /** “更多”按钮文案(提供时显示在图标右侧) */ + moreText?: string; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/help-tooltip.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/help-tooltip.vue new file mode 100644 index 0000000..3c82a1a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/help-tooltip.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/index.ts new file mode 100644 index 0000000..65bf053 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/index.ts @@ -0,0 +1,2 @@ +export { default as VbenHelpTooltip } from './help-tooltip.vue'; +export { default as VbenTooltip } from './tooltip.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/tooltip.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/tooltip.vue new file mode 100644 index 0000000..7b3d906 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/tooltip.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/index.ts new file mode 100644 index 0000000..a7c9ee4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './components'; +export * from './ui'; +export { createContext, Slot, VisuallyHidden } from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/Accordion.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/Accordion.vue new file mode 100644 index 0000000..1cc7e85 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/Accordion.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionContent.vue new file mode 100644 index 0000000..08a5c6d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionContent.vue @@ -0,0 +1,28 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionItem.vue new file mode 100644 index 0000000..2d9d087 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionItem.vue @@ -0,0 +1,29 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionTrigger.vue new file mode 100644 index 0000000..49f90df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionTrigger.vue @@ -0,0 +1,39 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/index.ts new file mode 100644 index 0000000..821d3a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/index.ts @@ -0,0 +1,4 @@ +export { default as Accordion } from './Accordion.vue'; +export { default as AccordionContent } from './AccordionContent.vue'; +export { default as AccordionItem } from './AccordionItem.vue'; +export { default as AccordionTrigger } from './AccordionTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialog.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialog.vue new file mode 100644 index 0000000..4153495 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialog.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogAction.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogAction.vue new file mode 100644 index 0000000..d5da3f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogAction.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogCancel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogCancel.vue new file mode 100644 index 0000000..2b6f62a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogCancel.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogContent.vue new file mode 100644 index 0000000..c0775f4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogContent.vue @@ -0,0 +1,101 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogDescription.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogDescription.vue new file mode 100644 index 0000000..25ffa56 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogDescription.vue @@ -0,0 +1,28 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogFooter.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogFooter.vue new file mode 100644 index 0000000..f6852cd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogFooter.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogHeader.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogHeader.vue new file mode 100644 index 0000000..3c3dea8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogHeader.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogOverlay.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogOverlay.vue new file mode 100644 index 0000000..0c721c7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogOverlay.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTitle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTitle.vue new file mode 100644 index 0000000..7b24f9e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTitle.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTrigger.vue new file mode 100644 index 0000000..fd535bf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/index.ts new file mode 100644 index 0000000..88770f1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/index.ts @@ -0,0 +1,9 @@ +export { default as AlertDialog } from './AlertDialog.vue'; +export { default as AlertDialogAction } from './AlertDialogAction.vue'; +export { default as AlertDialogCancel } from './AlertDialogCancel.vue'; +export { default as AlertDialogContent } from './AlertDialogContent.vue'; +export { default as AlertDialogDescription } from './AlertDialogDescription.vue'; +export { default as AlertDialogFooter } from './AlertDialogFooter.vue'; +export { default as AlertDialogHeader } from './AlertDialogHeader.vue'; +export { default as AlertDialogTitle } from './AlertDialogTitle.vue'; +export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue new file mode 100644 index 0000000..0f65c9c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue @@ -0,0 +1,25 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarFallback.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarFallback.vue new file mode 100644 index 0000000..1ae8336 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarFallback.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarImage.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarImage.vue new file mode 100644 index 0000000..56b49b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarImage.vue @@ -0,0 +1,17 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts new file mode 100644 index 0000000..8aae81f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts @@ -0,0 +1,22 @@ +import type { VariantProps } from 'class-variance-authority'; + +import { cva } from 'class-variance-authority'; + +export const avatarVariant = cva( + 'inline-flex items-center justify-center font-normal text-foreground select-none shrink-0 bg-secondary overflow-hidden', + { + variants: { + shape: { + circle: 'rounded-full', + square: 'rounded-md', + }, + size: { + base: 'h-16 w-16 text-2xl', + lg: 'h-32 w-32 text-5xl', + sm: 'h-10 w-10 text-xs', + }, + }, + }, +); + +export type AvatarVariants = VariantProps; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts new file mode 100644 index 0000000..16705ad --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts @@ -0,0 +1,3 @@ +export { default as Avatar } from './Avatar.vue'; +export { default as AvatarFallback } from './AvatarFallback.vue'; +export { default as AvatarImage } from './AvatarImage.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue new file mode 100644 index 0000000..f692ff2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts new file mode 100644 index 0000000..edd548b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts @@ -0,0 +1,25 @@ +import type { VariantProps } from 'class-variance-authority'; + +import { cva } from 'class-variance-authority'; + +export const badgeVariants = cva( + 'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + secondary: + 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + destructive: + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); +export type BadgeVariants = VariantProps; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts new file mode 100644 index 0000000..b37175b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts @@ -0,0 +1,3 @@ +export * from './badge'; + +export { default as Badge } from './Badge.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/Breadcrumb.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/Breadcrumb.vue new file mode 100644 index 0000000..a018dde --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/Breadcrumb.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbEllipsis.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbEllipsis.vue new file mode 100644 index 0000000..3bd314e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbEllipsis.vue @@ -0,0 +1,25 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbItem.vue new file mode 100644 index 0000000..3c5157f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbItem.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbLink.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbLink.vue new file mode 100644 index 0000000..24f8d90 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbLink.vue @@ -0,0 +1,27 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbList.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbList.vue new file mode 100644 index 0000000..4ae58b9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbList.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbPage.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbPage.vue new file mode 100644 index 0000000..d8f53f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbPage.vue @@ -0,0 +1,21 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbSeparator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbSeparator.vue new file mode 100644 index 0000000..e2c92a2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbSeparator.vue @@ -0,0 +1,24 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/index.ts new file mode 100644 index 0000000..d650f7e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/index.ts @@ -0,0 +1,7 @@ +export { default as Breadcrumb } from './Breadcrumb.vue'; +export { default as BreadcrumbEllipsis } from './BreadcrumbEllipsis.vue'; +export { default as BreadcrumbItem } from './BreadcrumbItem.vue'; +export { default as BreadcrumbLink } from './BreadcrumbLink.vue'; +export { default as BreadcrumbList } from './BreadcrumbList.vue'; +export { default as BreadcrumbPage } from './BreadcrumbPage.vue'; +export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/Button.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/Button.vue new file mode 100644 index 0000000..4c8090e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/Button.vue @@ -0,0 +1,36 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts new file mode 100644 index 0000000..1723909 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts @@ -0,0 +1,35 @@ +import { cva, type VariantProps } from 'class-variance-authority'; + +export const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', + { + defaultVariants: { + size: 'default', + variant: 'default', + }, + variants: { + size: { + default: 'h-9 px-4 py-2', + icon: 'h-8 w-8 rounded-sm px-1 text-lg', + lg: 'h-10 rounded-md px-4', + sm: 'h-8 rounded-md px-2 text-xs', + xs: 'h-8 w-8 rounded-sm px-1 text-xs', + }, + variant: { + default: + 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive-hover', + ghost: 'hover:bg-accent hover:text-accent-foreground', + heavy: 'hover:bg-heavy hover:text-heavy-foreground', + icon: 'hover:bg-accent hover:text-accent-foreground text-foreground/80', + link: 'text-primary underline-offset-4 hover:underline', + outline: + 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + }, + }, + }, +); +export type ButtonVariants = VariantProps; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts new file mode 100644 index 0000000..4b244d9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts @@ -0,0 +1,3 @@ +export * from './button'; + +export { default as Button } from './Button.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts new file mode 100644 index 0000000..cc5e174 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts @@ -0,0 +1,20 @@ +export type ButtonVariantSize = + | 'default' + | 'icon' + | 'lg' + | 'sm' + | 'xs' + | null + | undefined; + +export type ButtonVariants = + | 'default' + | 'destructive' + | 'ghost' + | 'heavy' + | 'icon' + | 'link' + | 'outline' + | 'secondary' + | null + | undefined; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/Card.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/Card.vue new file mode 100644 index 0000000..8aafcbf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/Card.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardAction.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardAction.vue new file mode 100644 index 0000000..d98e8b3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardAction.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue new file mode 100644 index 0000000..d8699b3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue @@ -0,0 +1,15 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardDescription.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardDescription.vue new file mode 100644 index 0000000..c7583ae --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardDescription.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue new file mode 100644 index 0000000..cff06ed --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue new file mode 100644 index 0000000..f335744 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue new file mode 100644 index 0000000..6a4b8fa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts new file mode 100644 index 0000000..a71b2f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts @@ -0,0 +1,7 @@ +export { default as Card } from './Card.vue'; +export { default as CardAction } from './CardAction.vue'; +export { default as CardContent } from './CardContent.vue'; +export { default as CardDescription } from './CardDescription.vue'; +export { default as CardFooter } from './CardFooter.vue'; +export { default as CardHeader } from './CardHeader.vue'; +export { default as CardTitle } from './CardTitle.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/Checkbox.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/Checkbox.vue new file mode 100644 index 0000000..87e9370 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/Checkbox.vue @@ -0,0 +1,49 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts new file mode 100644 index 0000000..7f906aa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts @@ -0,0 +1 @@ +export { default as Checkbox } from './Checkbox.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenu.vue new file mode 100644 index 0000000..e43ad00 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenu.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuCheckboxItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuCheckboxItem.vue new file mode 100644 index 0000000..b2cafcc --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuCheckboxItem.vue @@ -0,0 +1,51 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuContent.vue new file mode 100644 index 0000000..5c15b59 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuContent.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuGroup.vue new file mode 100644 index 0000000..e21d7ae --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuGroup.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuItem.vue new file mode 100644 index 0000000..d6f10c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuItem.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuLabel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuLabel.vue new file mode 100644 index 0000000..af08207 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuLabel.vue @@ -0,0 +1,32 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuPortal.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuPortal.vue new file mode 100644 index 0000000..8073687 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuPortal.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioGroup.vue new file mode 100644 index 0000000..e338694 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioGroup.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioItem.vue new file mode 100644 index 0000000..8e5577e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioItem.vue @@ -0,0 +1,51 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSeparator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSeparator.vue new file mode 100644 index 0000000..06be942 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSeparator.vue @@ -0,0 +1,24 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuShortcut.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuShortcut.vue new file mode 100644 index 0000000..8b736eb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuShortcut.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSub.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSub.vue new file mode 100644 index 0000000..f61500e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSub.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubContent.vue new file mode 100644 index 0000000..fe47648 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubContent.vue @@ -0,0 +1,37 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubTrigger.vue new file mode 100644 index 0000000..8e1fa38 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubTrigger.vue @@ -0,0 +1,39 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuTrigger.vue new file mode 100644 index 0000000..eee5052 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuTrigger.vue @@ -0,0 +1,15 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts new file mode 100644 index 0000000..c9f7d0d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts @@ -0,0 +1,14 @@ +export { default as ContextMenu } from './ContextMenu.vue'; +export { default as ContextMenuCheckboxItem } from './ContextMenuCheckboxItem.vue'; +export { default as ContextMenuContent } from './ContextMenuContent.vue'; +export { default as ContextMenuGroup } from './ContextMenuGroup.vue'; +export { default as ContextMenuItem } from './ContextMenuItem.vue'; +export { default as ContextMenuLabel } from './ContextMenuLabel.vue'; +export { default as ContextMenuRadioGroup } from './ContextMenuRadioGroup.vue'; +export { default as ContextMenuRadioItem } from './ContextMenuRadioItem.vue'; +export { default as ContextMenuSeparator } from './ContextMenuSeparator.vue'; +export { default as ContextMenuShortcut } from './ContextMenuShortcut.vue'; +export { default as ContextMenuSub } from './ContextMenuSub.vue'; +export { default as ContextMenuSubContent } from './ContextMenuSubContent.vue'; +export { default as ContextMenuSubTrigger } from './ContextMenuSubTrigger.vue'; +export { default as ContextMenuTrigger } from './ContextMenuTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue new file mode 100644 index 0000000..fe497d2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogClose.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogClose.vue new file mode 100644 index 0000000..5248172 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogClose.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue new file mode 100644 index 0000000..fbfa93c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue @@ -0,0 +1,145 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogDescription.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogDescription.vue new file mode 100644 index 0000000..3464179 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogDescription.vue @@ -0,0 +1,28 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogFooter.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogFooter.vue new file mode 100644 index 0000000..38ccfc9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogFooter.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogHeader.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogHeader.vue new file mode 100644 index 0000000..0f24e6e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogHeader.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogOverlay.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogOverlay.vue new file mode 100644 index 0000000..ac353ac --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogOverlay.vue @@ -0,0 +1,34 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogScrollContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogScrollContent.vue new file mode 100644 index 0000000..c480c75 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogScrollContent.vue @@ -0,0 +1,69 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTitle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTitle.vue new file mode 100644 index 0000000..50dc73f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTitle.vue @@ -0,0 +1,28 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTrigger.vue new file mode 100644 index 0000000..9edfd3a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts new file mode 100644 index 0000000..4f9dab8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts @@ -0,0 +1,10 @@ +export { default as Dialog } from './Dialog.vue'; +export { default as DialogClose } from './DialogClose.vue'; +export { default as DialogContent } from './DialogContent.vue'; +export { default as DialogDescription } from './DialogDescription.vue'; +export { default as DialogFooter } from './DialogFooter.vue'; +export { default as DialogHeader } from './DialogHeader.vue'; +export { default as DialogOverlay } from './DialogOverlay.vue'; +export { default as DialogScrollContent } from './DialogScrollContent.vue'; +export { default as DialogTitle } from './DialogTitle.vue'; +export { default as DialogTrigger } from './DialogTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenu.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenu.vue new file mode 100644 index 0000000..5e94fb1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenu.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuCheckboxItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuCheckboxItem.vue new file mode 100644 index 0000000..2e30c2e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuCheckboxItem.vue @@ -0,0 +1,51 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuContent.vue new file mode 100644 index 0000000..55293e7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuContent.vue @@ -0,0 +1,50 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuGroup.vue new file mode 100644 index 0000000..1d2d718 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuGroup.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuItem.vue new file mode 100644 index 0000000..a2014e4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuItem.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuLabel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuLabel.vue new file mode 100644 index 0000000..029bb84 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuLabel.vue @@ -0,0 +1,30 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioGroup.vue new file mode 100644 index 0000000..bff97bf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioGroup.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioItem.vue new file mode 100644 index 0000000..ddd9635 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioItem.vue @@ -0,0 +1,52 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSeparator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSeparator.vue new file mode 100644 index 0000000..92096a8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSeparator.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuShortcut.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuShortcut.vue new file mode 100644 index 0000000..76227c2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuShortcut.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSub.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSub.vue new file mode 100644 index 0000000..c6b1cba --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSub.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubContent.vue new file mode 100644 index 0000000..923950f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubContent.vue @@ -0,0 +1,37 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubTrigger.vue new file mode 100644 index 0000000..1cf2d91 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubTrigger.vue @@ -0,0 +1,38 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuTrigger.vue new file mode 100644 index 0000000..9eaa795 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuTrigger.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..bde907b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/index.ts @@ -0,0 +1,16 @@ +export { default as DropdownMenu } from './DropdownMenu.vue'; + +export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.vue'; +export { default as DropdownMenuContent } from './DropdownMenuContent.vue'; +export { default as DropdownMenuGroup } from './DropdownMenuGroup.vue'; +export { default as DropdownMenuItem } from './DropdownMenuItem.vue'; +export { default as DropdownMenuLabel } from './DropdownMenuLabel.vue'; +export { default as DropdownMenuRadioGroup } from './DropdownMenuRadioGroup.vue'; +export { default as DropdownMenuRadioItem } from './DropdownMenuRadioItem.vue'; +export { default as DropdownMenuSeparator } from './DropdownMenuSeparator.vue'; +export { default as DropdownMenuShortcut } from './DropdownMenuShortcut.vue'; +export { default as DropdownMenuSub } from './DropdownMenuSub.vue'; +export { default as DropdownMenuSubContent } from './DropdownMenuSubContent.vue'; +export { default as DropdownMenuSubTrigger } from './DropdownMenuSubTrigger.vue'; +export { default as DropdownMenuTrigger } from './DropdownMenuTrigger.vue'; +export { DropdownMenuPortal } from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormControl.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormControl.vue new file mode 100644 index 0000000..2d08cb1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormControl.vue @@ -0,0 +1,20 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormDescription.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormDescription.vue new file mode 100644 index 0000000..49df6a9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormDescription.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue new file mode 100644 index 0000000..e82a42e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue @@ -0,0 +1,24 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormLabel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormLabel.vue new file mode 100644 index 0000000..a875f1c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormLabel.vue @@ -0,0 +1,25 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormMessage.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormMessage.vue new file mode 100644 index 0000000..181847f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormMessage.vue @@ -0,0 +1,27 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts new file mode 100644 index 0000000..1d45b2b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts @@ -0,0 +1,11 @@ +export { default as FormControl } from './FormControl.vue'; +export { default as FormDescription } from './FormDescription.vue'; +export { default as FormItem } from './FormItem.vue'; +export { default as FormLabel } from './FormLabel.vue'; +export { default as FormMessage } from './FormMessage.vue'; +export { FORM_ITEM_INJECTION_KEY } from './injectionKeys'; +export { + Form, + Field as FormField, + FieldArray as FormFieldArray, +} from 'vee-validate'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/injectionKeys.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/injectionKeys.ts new file mode 100644 index 0000000..2338043 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/injectionKeys.ts @@ -0,0 +1,3 @@ +import type { InjectionKey } from 'vue'; + +export const FORM_ITEM_INJECTION_KEY = Symbol() as InjectionKey; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts new file mode 100644 index 0000000..6498dfc --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts @@ -0,0 +1,32 @@ +import { computed, inject } from 'vue'; + +import { FieldContextKey } from 'vee-validate'; + +import { FORM_ITEM_INJECTION_KEY } from './injectionKeys'; + +export function useFormField() { + const fieldContext = inject(FieldContextKey); + const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY); + + if (!fieldContext) + throw new Error('useFormField should be used within '); + + const { name, errorMessage: error, meta } = fieldContext; + const id = fieldItemContext; + + const fieldState = { + valid: computed(() => meta.valid), + isDirty: computed(() => meta.dirty), + isTouched: computed(() => meta.touched), + error, + }; + + return { + id, + name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCard.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCard.vue new file mode 100644 index 0000000..7435550 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCard.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardContent.vue new file mode 100644 index 0000000..6b21c61 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardContent.vue @@ -0,0 +1,42 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardTrigger.vue new file mode 100644 index 0000000..d5dd38b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/index.ts new file mode 100644 index 0000000..dd4a1ef --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/index.ts @@ -0,0 +1,3 @@ +export { default as HoverCard } from './HoverCard.vue'; +export { default as HoverCardContent } from './HoverCardContent.vue'; +export { default as HoverCardTrigger } from './HoverCardTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts new file mode 100644 index 0000000..1be71f8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts @@ -0,0 +1,31 @@ +export * from './accordion'; +export * from './alert-dialog'; +export * from './avatar'; +export * from './badge'; +export * from './breadcrumb'; +export * from './button'; +export * from './card'; +export * from './checkbox'; +export * from './dialog'; +export * from './dropdown-menu'; +export * from './form'; +export * from './hover-card'; +export * from './input'; +export * from './label'; +export * from './number-field'; +export * from './pagination'; +export * from './pin-input'; +export * from './popover'; +export * from './radio-group'; +export * from './resizable'; +export * from './scroll-area'; +export * from './select'; +export * from './separator'; +export * from './sheet'; +export * from './switch'; +export * from './tabs'; +export * from './textarea'; +export * from './toggle'; +export * from './toggle-group'; +export * from './tooltip'; +export * from './tree'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/Input.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/Input.vue new file mode 100644 index 0000000..99ea4f4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/Input.vue @@ -0,0 +1,54 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts new file mode 100644 index 0000000..c5248c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts @@ -0,0 +1 @@ +export { default as Input } from './Input.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/Label.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/Label.vue new file mode 100644 index 0000000..e1f4256 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/Label.vue @@ -0,0 +1,29 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts new file mode 100644 index 0000000..c98e59f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts @@ -0,0 +1 @@ +export { default as Label } from './Label.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberField.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberField.vue new file mode 100644 index 0000000..e8533fc --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberField.vue @@ -0,0 +1,29 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldContent.vue new file mode 100644 index 0000000..c1dd593 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldContent.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldDecrement.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldDecrement.vue new file mode 100644 index 0000000..f92888a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldDecrement.vue @@ -0,0 +1,36 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldIncrement.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldIncrement.vue new file mode 100644 index 0000000..7bb2784 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldIncrement.vue @@ -0,0 +1,36 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldInput.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldInput.vue new file mode 100644 index 0000000..ef2d167 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldInput.vue @@ -0,0 +1,23 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/index.ts new file mode 100644 index 0000000..bcdde78 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/index.ts @@ -0,0 +1,5 @@ +export { default as NumberField } from './NumberField.vue'; +export { default as NumberFieldContent } from './NumberFieldContent.vue'; +export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'; +export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'; +export { default as NumberFieldInput } from './NumberFieldInput.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/Pagination.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/Pagination.vue new file mode 100644 index 0000000..6ac4ba6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/Pagination.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationContent.vue new file mode 100644 index 0000000..e2c7b75 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationContent.vue @@ -0,0 +1,27 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationEllipsis.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationEllipsis.vue new file mode 100644 index 0000000..34e6f2d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationEllipsis.vue @@ -0,0 +1,29 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationFirst.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationFirst.vue new file mode 100644 index 0000000..8c46147 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationFirst.vue @@ -0,0 +1,50 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationItem.vue new file mode 100644 index 0000000..b7aaf61 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationItem.vue @@ -0,0 +1,47 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationLast.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationLast.vue new file mode 100644 index 0000000..b6c3512 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationLast.vue @@ -0,0 +1,50 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationNext.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationNext.vue new file mode 100644 index 0000000..781628c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationNext.vue @@ -0,0 +1,50 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrev.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrev.vue new file mode 100644 index 0000000..e95317d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrev.vue @@ -0,0 +1,35 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrevious.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrevious.vue new file mode 100644 index 0000000..e17faa8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrevious.vue @@ -0,0 +1,50 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/index.ts new file mode 100644 index 0000000..cb7efbf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/index.ts @@ -0,0 +1,8 @@ +export { default as Pagination } from './Pagination.vue'; +export { default as PaginationContent } from './PaginationContent.vue'; +export { default as PaginationEllipsis } from './PaginationEllipsis.vue'; +export { default as PaginationFirst } from './PaginationFirst.vue'; +export { default as PaginationItem } from './PaginationItem.vue'; +export { default as PaginationLast } from './PaginationLast.vue'; +export { default as PaginationNext } from './PaginationNext.vue'; +export { default as PaginationPrevious } from './PaginationPrevious.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInput.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInput.vue new file mode 100644 index 0000000..aee4f17 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInput.vue @@ -0,0 +1,38 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputGroup.vue new file mode 100644 index 0000000..c43c33f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputGroup.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue new file mode 100644 index 0000000..f037e1f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue @@ -0,0 +1,30 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSeparator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSeparator.vue new file mode 100644 index 0000000..09b8ce3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSeparator.vue @@ -0,0 +1,17 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSlot.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSlot.vue new file mode 100644 index 0000000..8802c77 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSlot.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/index.ts new file mode 100644 index 0000000..41b38f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/index.ts @@ -0,0 +1,4 @@ +export { default as PinInput } from './PinInput.vue'; +export { default as PinInputGroup } from './PinInputGroup.vue'; +export { default as PinInputSeparator } from './PinInputSeparator.vue'; +export { default as PinInputSlot } from './PinInputSlot.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/Popover.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/Popover.vue new file mode 100644 index 0000000..08612da --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/Popover.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverAnchor.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverAnchor.vue new file mode 100644 index 0000000..21eafc9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverAnchor.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverContent.vue new file mode 100644 index 0000000..5318577 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverContent.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverTrigger.vue new file mode 100644 index 0000000..ecf418d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/index.ts new file mode 100644 index 0000000..4f8d529 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/popover/index.ts @@ -0,0 +1,4 @@ +export { default as Popover } from './Popover.vue'; +export { default as PopoverAnchor } from './PopoverAnchor.vue'; +export { default as PopoverContent } from './PopoverContent.vue'; +export { default as PopoverTrigger } from './PopoverTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroup.vue new file mode 100644 index 0000000..3e33529 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroup.vue @@ -0,0 +1,30 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroupItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroupItem.vue new file mode 100644 index 0000000..9ac7e88 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroupItem.vue @@ -0,0 +1,43 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/index.ts new file mode 100644 index 0000000..7e2c65f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/index.ts @@ -0,0 +1,2 @@ +export { default as RadioGroup } from './RadioGroup.vue'; +export { default as RadioGroupItem } from './RadioGroupItem.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizableHandle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizableHandle.vue new file mode 100644 index 0000000..ec2965b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizableHandle.vue @@ -0,0 +1,48 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanel.vue new file mode 100644 index 0000000..9be8194 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanel.vue @@ -0,0 +1,32 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanelGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanelGroup.vue new file mode 100644 index 0000000..7e68f78 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanelGroup.vue @@ -0,0 +1,32 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/index.ts new file mode 100644 index 0000000..cffefef --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/index.ts @@ -0,0 +1,3 @@ +export { default as ResizableHandle } from './ResizableHandle.vue'; +export { default as ResizablePanel } from './ResizablePanel.vue'; +export { default as ResizablePanelGroup } from './ResizablePanelGroup.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollArea.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollArea.vue new file mode 100644 index 0000000..b7dc6a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollArea.vue @@ -0,0 +1,40 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollBar.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollBar.vue new file mode 100644 index 0000000..64a1ad0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollBar.vue @@ -0,0 +1,41 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/index.ts new file mode 100644 index 0000000..d6541f3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/index.ts @@ -0,0 +1,2 @@ +export { default as ScrollArea } from './ScrollArea.vue'; +export { default as ScrollBar } from './ScrollBar.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/Select.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/Select.vue new file mode 100644 index 0000000..efd46ea --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/Select.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectContent.vue new file mode 100644 index 0000000..0fd8510 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectContent.vue @@ -0,0 +1,65 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectGroup.vue new file mode 100644 index 0000000..95a63a2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectGroup.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItem.vue new file mode 100644 index 0000000..770e3df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItem.vue @@ -0,0 +1,49 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItemText.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItemText.vue new file mode 100644 index 0000000..025de87 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItemText.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectLabel.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectLabel.vue new file mode 100644 index 0000000..b583dee --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectLabel.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollDownButton.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollDownButton.vue new file mode 100644 index 0000000..a73ce5c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollDownButton.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollUpButton.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollUpButton.vue new file mode 100644 index 0000000..80c211f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollUpButton.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectSeparator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectSeparator.vue new file mode 100644 index 0000000..4a5d956 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectSeparator.vue @@ -0,0 +1,24 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectTrigger.vue new file mode 100644 index 0000000..64393de --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectTrigger.vue @@ -0,0 +1,43 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectValue.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectValue.vue new file mode 100644 index 0000000..cdf084d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectValue.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts new file mode 100644 index 0000000..d7fdf0d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts @@ -0,0 +1,11 @@ +export { default as Select } from './Select.vue'; +export { default as SelectContent } from './SelectContent.vue'; +export { default as SelectGroup } from './SelectGroup.vue'; +export { default as SelectItem } from './SelectItem.vue'; +export { default as SelectItemText } from './SelectItemText.vue'; +export { default as SelectLabel } from './SelectLabel.vue'; +export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue'; +export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue'; +export { default as SelectSeparator } from './SelectSeparator.vue'; +export { default as SelectTrigger } from './SelectTrigger.vue'; +export { default as SelectValue } from './SelectValue.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/Separator.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/Separator.vue new file mode 100644 index 0000000..f9ea347 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/Separator.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/index.ts new file mode 100644 index 0000000..3cb07b0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/separator/index.ts @@ -0,0 +1 @@ +export { default as Separator } from './Separator.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue new file mode 100644 index 0000000..07281df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetClose.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetClose.vue new file mode 100644 index 0000000..495a6d7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetClose.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetContent.vue new file mode 100644 index 0000000..49074b0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetContent.vue @@ -0,0 +1,107 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetDescription.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetDescription.vue new file mode 100644 index 0000000..a5573ab --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetDescription.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue new file mode 100644 index 0000000..00ff5a3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue new file mode 100644 index 0000000..3a6720a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetOverlay.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetOverlay.vue new file mode 100644 index 0000000..08a7c5a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetOverlay.vue @@ -0,0 +1,11 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTitle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTitle.vue new file mode 100644 index 0000000..61b4d1d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTitle.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTrigger.vue new file mode 100644 index 0000000..fb2f2ed --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts new file mode 100644 index 0000000..fd3095a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts @@ -0,0 +1,10 @@ +export * from './sheet'; +export { default as Sheet } from './Sheet.vue'; +export { default as SheetClose } from './SheetClose.vue'; +export { default as SheetContent } from './SheetContent.vue'; +export { default as SheetDescription } from './SheetDescription.vue'; +export { default as SheetFooter } from './SheetFooter.vue'; +export { default as SheetHeader } from './SheetHeader.vue'; +export { default as SheetTitle } from './SheetTitle.vue'; + +export { default as SheetTrigger } from './SheetTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts new file mode 100644 index 0000000..badf311 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts @@ -0,0 +1,24 @@ +import type { VariantProps } from 'class-variance-authority'; + +import { cva } from 'class-variance-authority'; + +export const sheetVariants = cva( + 'bg-background shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 border-border', + { + defaultVariants: { + side: 'right', + }, + variants: { + side: { + bottom: + 'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom', + left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left ', + right: + 'inset-y-0 right-0 w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right', + top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top', + }, + }, + }, +); + +export type SheetVariants = VariantProps; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/Switch.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/Switch.vue new file mode 100644 index 0000000..e3e3b95 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/Switch.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts new file mode 100644 index 0000000..924a2d1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts @@ -0,0 +1 @@ +export { default as Switch } from './Switch.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue new file mode 100644 index 0000000..f67aae3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue @@ -0,0 +1,29 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsContent.vue new file mode 100644 index 0000000..c833d64 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsContent.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue new file mode 100644 index 0000000..198b61b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsTrigger.vue new file mode 100644 index 0000000..7961625 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsTrigger.vue @@ -0,0 +1,33 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/index.ts new file mode 100644 index 0000000..7e98cfb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/index.ts @@ -0,0 +1,5 @@ +export { default as Tabs } from './Tabs.vue'; +export { default as TabsContent } from './TabsContent.vue'; +export { default as TabsList } from './TabsList.vue'; +export { default as TabsTrigger } from './TabsTrigger.vue'; +export { TabsIndicator } from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/Textarea.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/Textarea.vue new file mode 100644 index 0000000..4129895 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/Textarea.vue @@ -0,0 +1,35 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts new file mode 100644 index 0000000..e2d4773 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts @@ -0,0 +1 @@ +export { default as Textarea } from './Textarea.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue new file mode 100644 index 0000000..405f211 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue @@ -0,0 +1,64 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue new file mode 100644 index 0000000..11e4a6c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue @@ -0,0 +1,56 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts new file mode 100644 index 0000000..ac3e34b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts @@ -0,0 +1,2 @@ +export { default as ToggleGroup } from './ToggleGroup.vue'; +export { default as ToggleGroupItem } from './ToggleGroupItem.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue new file mode 100644 index 0000000..da5e1b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts new file mode 100644 index 0000000..ecc1e3f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts @@ -0,0 +1,2 @@ +export * from './toggle'; +export { default as Toggle } from './Toggle.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts new file mode 100644 index 0000000..c42477c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts @@ -0,0 +1,27 @@ +import type { VariantProps } from 'class-variance-authority'; + +import { cva } from 'class-variance-authority'; + +export const toggleVariants = cva( + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", + { + variants: { + variant: { + default: 'bg-transparent', + outline: + 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground', + }, + size: { + default: 'h-9 px-2 min-w-9', + sm: 'h-8 px-1.5 min-w-8', + lg: 'h-10 px-2.5 min-w-10', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +export type ToggleVariants = VariantProps; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue new file mode 100644 index 0000000..129d625 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue @@ -0,0 +1,16 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue new file mode 100644 index 0000000..124b5ef --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue @@ -0,0 +1,49 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue new file mode 100644 index 0000000..f56db29 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue new file mode 100644 index 0000000..b0d7d40 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts new file mode 100644 index 0000000..94e681c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts @@ -0,0 +1,4 @@ +export { default as Tooltip } from './Tooltip.vue'; +export { default as TooltipContent } from './TooltipContent.vue'; +export { default as TooltipProvider } from './TooltipProvider.vue'; +export { default as TooltipTrigger } from './TooltipTrigger.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts new file mode 100644 index 0000000..d390f63 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts @@ -0,0 +1,4 @@ +export { default as VbenTree } from './tree.vue'; +export type { TreeProps } from './types'; +export { treePropsDefaults } from './types'; +export type { FlattenedItem } from 'reka-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue new file mode 100644 index 0000000..0a6e880 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue @@ -0,0 +1,524 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts new file mode 100644 index 0000000..b962a7e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts @@ -0,0 +1,64 @@ +import type { Arrayable } from '@vueuse/core'; +import type { FlattenedItem } from 'reka-ui'; + +import type { Recordable } from '@vben-core/typings'; + +export interface TreeProps { + /** 单选时允许取消已有选项 */ + allowClear?: boolean; + /** 非关联选择时,自动选中上级节点 */ + autoCheckParent?: boolean; + /** 显示边框 */ + bordered?: boolean; + /** 取消父子关联选择 */ + checkStrictly?: boolean; + /** 子级字段名 */ + childrenField?: string; + /** 默认展开的键 */ + defaultExpandedKeys?: Array; + /** 默认展开的级别(优先级高于defaultExpandedKeys) */ + defaultExpandedLevel?: number; + /** 默认值 */ + defaultValue?: Arrayable; + /** 禁用 */ + disabled?: boolean; + /** 禁用字段名 */ + disabledField?: string; + /** 自定义节点类名 */ + getNodeClass?: (item: FlattenedItem>) => string; + iconField?: string; + /** label字段 */ + labelField?: string; + /** 是否多选 */ + multiple?: boolean; + /** 选择全部时的文字 */ + selectAllLabel?: string; + /** 显示由iconField指定的图标 */ + showIcon?: boolean; + /** 启用展开收缩动画 */ + transition?: boolean; + /** 树数据 */ + treeData: Recordable[]; + /** 值字段 */ + valueField?: string; +} + +export function treePropsDefaults() { + return { + allowClear: false, + autoCheckParent: true, + bordered: false, + checkStrictly: false, + defaultExpandedKeys: () => [], + defaultExpandedLevel: 0, + disabled: false, + disabledField: 'disabled', + iconField: 'icon', + labelField: 'label', + multiple: false, + showIcon: true, + transition: true, + valueField: 'value', + childrenField: 'children', + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/tsconfig.json new file mode 100644 index 0000000..db13af0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/shadcn-ui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "compilerOptions": { + "paths": { + "@vben-core/shadcn-ui/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/package.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/package.json new file mode 100644 index 0000000..75d0fd8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/package.json @@ -0,0 +1,54 @@ +{ + "name": "@vben-core/tabs-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/tabs-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm exec tsdown", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "production": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/design": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + }, + "devDependencies": { + "unplugin-vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/index.ts new file mode 100644 index 0000000..eafeaa6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/index.ts @@ -0,0 +1,2 @@ +export { default as TabsChrome } from './tabs-chrome/tabs.vue'; +export { default as Tabs } from './tabs/tabs.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue new file mode 100644 index 0000000..afec30a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue new file mode 100644 index 0000000..1c92189 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue @@ -0,0 +1,148 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts new file mode 100644 index 0000000..f7634ce --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts @@ -0,0 +1,3 @@ +export { default as TabsToolMore } from './tool-more.vue'; +export { default as TabsToolRefresh } from './tool-refresh.vue'; +export { default as TabsToolScreen } from './tool-screen.vue'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue new file mode 100644 index 0000000..045a670 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-refresh.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-refresh.vue new file mode 100644 index 0000000..beb88de --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-refresh.vue @@ -0,0 +1,18 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue new file mode 100644 index 0000000..acbf14e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue @@ -0,0 +1,19 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/index.ts new file mode 100644 index 0000000..6d562db --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './components/widgets'; +export { default as TabsView } from './tabs-view.vue'; +export type { IContextMenuItem } from '@vben-core/shadcn-ui'; diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue new file mode 100644 index 0000000..0f97f3b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue @@ -0,0 +1,107 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/types.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/types.ts new file mode 100644 index 0000000..19ecf03 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/types.ts @@ -0,0 +1,73 @@ +import type { IContextMenuItem } from '@vben-core/shadcn-ui'; +import type { TabDefinition, TabsStyleType } from '@vben-core/typings'; + +export type TabsEmits = { + close: [string]; + sortTabs: [number, number]; + unpin: [TabDefinition]; +}; + +export interface TabsProps { + active?: string; + /** + * @zh_CN content class + * @default tabs-chrome + */ + contentClass?: string; + /** + * @zh_CN 右键菜单 + */ + contextMenus?: (data: any) => IContextMenuItem[]; + /** + * @zh_CN 是否可以拖拽 + */ + draggable?: boolean; + /** + * @zh_CN 间隙 + * @default 7 + * 仅限 tabs-chrome + */ + gap?: number; + /** + * @zh_CN tab 最大宽度 + * 仅限 tabs-chrome + */ + maxWidth?: number; + /** + * @zh_CN 点击中键时关闭Tab + */ + middleClickToClose?: boolean; + + /** + * @zh_CN tab最小宽度 + * 仅限 tabs-chrome + */ + minWidth?: number; + + /** + * @zh_CN 是否显示图标 + */ + showIcon?: boolean; + /** + * @zh_CN 标签页风格 + */ + styleType?: TabsStyleType; + + /** + * @zh_CN 选项卡数据 + */ + tabs?: TabDefinition[]; + + /** + * @zh_CN 是否响应滚轮事件 + */ + wheelable?: boolean; +} + +export interface TabConfig extends TabDefinition { + affixTab: boolean; + closable: boolean; + icon: string; + key: string; + title: string; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts new file mode 100644 index 0000000..b9c2e8a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts @@ -0,0 +1,124 @@ +import type { Sortable } from '@vben-core/composables'; +import type { EmitType } from '@vben-core/typings'; + +import type { TabsProps } from './types'; + +import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; + +import { useIsMobile, useSortable } from '@vben-core/composables'; + +// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素 +function findParentElement(element: HTMLElement) { + const parentCls = 'group'; + return element.classList.contains(parentCls) + ? element + : element.closest(`.${parentCls}`); +} + +export function useTabsDrag(props: TabsProps, emit: EmitType) { + const sortableInstance = ref(null); + + async function initTabsSortable() { + await nextTick(); + + const el = document.querySelectorAll( + `.${props.contentClass}`, + )?.[0] as HTMLElement; + + if (!el) { + console.warn('Element not found for sortable initialization'); + return; + } + + const resetElState = async () => { + el.style.cursor = 'default'; + // el.classList.remove('dragging'); + el.querySelector('.draggable')?.classList.remove('dragging'); + }; + + const { initializeSortable } = useSortable(el, { + filter: (_evt, target: HTMLElement) => { + const parent = findParentElement(target); + const draggable = parent?.classList.contains('draggable'); + return !draggable || !props.draggable; + }, + onEnd(evt) { + const { newIndex, oldIndex } = evt; + // const fromElement = evt.item; + const { srcElement } = (evt as any).originalEvent; + + if (!srcElement) { + resetElState(); + return; + } + + const srcParent = findParentElement(srcElement); + + if (!srcParent) { + resetElState(); + return; + } + + if (!srcParent.classList.contains('draggable')) { + resetElState(); + + return; + } + + if ( + oldIndex !== undefined && + newIndex !== undefined && + !Number.isNaN(oldIndex) && + !Number.isNaN(newIndex) && + oldIndex !== newIndex + ) { + emit('sortTabs', oldIndex, newIndex); + } + resetElState(); + }, + onMove(evt) { + const parent = findParentElement(evt.related); + if (parent?.classList.contains('draggable') && props.draggable) { + const isCurrentAffix = evt.dragged.classList.contains('affix-tab'); + const isRelatedAffix = evt.related.classList.contains('affix-tab'); + // 不允许在固定的tab和非固定的tab之间互相拖拽 + return isCurrentAffix === isRelatedAffix; + } else { + return false; + } + }, + onStart: () => { + el.style.cursor = 'grabbing'; + el.querySelector('.draggable')?.classList.add('dragging'); + // el.classList.add('dragging'); + }, + }); + + sortableInstance.value = await initializeSortable(); + } + + async function init() { + const { isMobile } = useIsMobile(); + + // 移动端下tab不需要拖拽 + if (isMobile.value) { + return; + } + await nextTick(); + initTabsSortable(); + } + + onMounted(init); + + watch( + () => props.styleType, + () => { + sortableInstance.value?.destroy(); + init(); + }, + ); + + onUnmounted(() => { + sortableInstance.value?.destroy(); + }); +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts new file mode 100644 index 0000000..aef32df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts @@ -0,0 +1,202 @@ +import type { TabsProps } from './types'; + +import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; + +import { VbenScrollbar } from '@vben-core/shadcn-ui'; + +import { useDebounceFn } from '@vueuse/core'; + +type DomElement = Element | null | undefined; + +export function useTabsViewScroll(props: TabsProps) { + let resizeObserver: null | ResizeObserver = null; + let mutationObserver: MutationObserver | null = null; + let tabItemCount = 0; + const scrollbarRef = ref | null>(null); + const scrollViewportEl = ref(null); + const showScrollButton = ref(false); + const scrollIsAtLeft = ref(true); + const scrollIsAtRight = ref(false); + + function getScrollClientWidth() { + const scrollbarEl = scrollbarRef.value?.$el; + if (!scrollbarEl || !scrollViewportEl.value) return {}; + + const scrollbarWidth = scrollbarEl.clientWidth; + const scrollViewWidth = scrollViewportEl.value.clientWidth; + + return { + scrollbarWidth, + scrollViewWidth, + }; + } + + function scrollDirection( + direction: 'left' | 'right', + distance: number = 150, + ) { + const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth(); + + if (!scrollbarWidth || !scrollViewWidth) return; + + if (scrollbarWidth > scrollViewWidth) return; + + scrollViewportEl.value?.scrollBy({ + behavior: 'smooth', + left: + direction === 'left' + ? -(scrollbarWidth - distance) + : +(scrollbarWidth - distance), + }); + } + + async function initScrollbar() { + await nextTick(); + + const scrollbarEl = scrollbarRef.value?.$el; + if (!scrollbarEl) { + return; + } + + const viewportEl = scrollbarEl?.querySelector( + 'div[data-reka-scroll-area-viewport]', + ); + + scrollViewportEl.value = viewportEl; + calcShowScrollbarButton(); + + await nextTick(); + scrollToActiveIntoView(); + + // 监听大小变化 + resizeObserver?.disconnect(); + resizeObserver = new ResizeObserver( + useDebounceFn((_entries: ResizeObserverEntry[]) => { + calcShowScrollbarButton(); + scrollToActiveIntoView(); + }, 100), + ); + resizeObserver.observe(viewportEl); + + tabItemCount = props.tabs?.length || 0; + mutationObserver?.disconnect(); + // 使用 MutationObserver 仅监听子节点数量变化 + mutationObserver = new MutationObserver(() => { + const count = viewportEl.querySelectorAll( + `div[data-tab-item="true"]`, + ).length; + + if (count > tabItemCount) { + scrollToActiveIntoView(); + } + + if (count !== tabItemCount) { + calcShowScrollbarButton(); + tabItemCount = count; + } + }); + + // 配置为仅监听子节点的添加和移除 + mutationObserver.observe(viewportEl, { + attributes: false, + childList: true, + subtree: true, + }); + } + + async function scrollToActiveIntoView() { + if (!scrollViewportEl.value) { + return; + } + await nextTick(); + const viewportEl = scrollViewportEl.value; + const { scrollbarWidth } = getScrollClientWidth(); + const { scrollWidth } = viewportEl; + + if (scrollbarWidth >= scrollWidth) { + return; + } + + requestAnimationFrame(() => { + const activeItem = viewportEl?.querySelector('.is-active'); + activeItem?.scrollIntoView({ behavior: 'smooth', inline: 'start' }); + }); + } + + /** + * 计算tabs 宽度,用于判断是否显示左右滚动按钮 + */ + async function calcShowScrollbarButton() { + if (!scrollViewportEl.value) { + return; + } + + const { scrollbarWidth } = getScrollClientWidth(); + + showScrollButton.value = + scrollViewportEl.value.scrollWidth > scrollbarWidth; + } + + const handleScrollAt = useDebounceFn(({ left, right }) => { + scrollIsAtLeft.value = left; + scrollIsAtRight.value = right; + }, 100); + + function handleWheel({ deltaY }: WheelEvent) { + scrollViewportEl.value?.scrollBy({ + // behavior: 'smooth', + left: deltaY * 3, + }); + } + + watch( + () => props.active, + async () => { + // 200为了等待 tab 切换动画完成 + // setTimeout(() => { + scrollToActiveIntoView(); + // }, 300); + }, + { + flush: 'post', + }, + ); + + // watch( + // () => props.tabs?.length, + // async () => { + // await nextTick(); + // calcShowScrollbarButton(); + // }, + // { + // flush: 'post', + // }, + // ); + + watch( + () => props.styleType, + () => { + initScrollbar(); + }, + ); + + onMounted(initScrollbar); + + onUnmounted(() => { + resizeObserver?.disconnect(); + mutationObserver?.disconnect(); + resizeObserver = null; + mutationObserver = null; + }); + + return { + handleScrollAt, + handleWheel, + initScrollbar, + scrollbarRef, + scrollDirection, + scrollIsAtLeft, + scrollIsAtRight, + showScrollButton, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsdown.config.ts b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsdown.config.ts new file mode 100644 index 0000000..43e8cf1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/@core/ui-kit/tabs-ui/tsdown.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsdown'; +import Vue from 'unplugin-vue/rolldown'; + +export default defineConfig({ + clean: true, + deps: { + skipNodeModulesBundle: true, + }, + dts: { + vue: true, + }, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + js: '.mjs', + }), + platform: 'neutral', + plugins: [Vue({ isProduction: true })], + unbundle: true, +}); diff --git a/deploy/fba/fba-ui-src/packages/constants/README.md b/deploy/fba/fba-ui-src/packages/constants/README.md new file mode 100644 index 0000000..67936a6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/constants/README.md @@ -0,0 +1,19 @@ +# @vben/constants + +用于多个 `app` 公用的常量,继承了 `@vben-core/shared/constants` 的所有能力。业务上有通用常量可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/constants +``` + +### 使用 + +```ts +import { LOGIN_PATH } from '@vben/constants'; +``` diff --git a/deploy/fba/fba-ui-src/packages/constants/package.json b/deploy/fba/fba-ui-src/packages/constants/package.json new file mode 100644 index 0000000..edf5533 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/constants/package.json @@ -0,0 +1,25 @@ +{ + "name": "@vben/constants", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/constants" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*" + } +} diff --git a/deploy/fba/fba-ui-src/packages/constants/src/core.ts b/deploy/fba/fba-ui-src/packages/constants/src/core.ts new file mode 100644 index 0000000..3861466 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/constants/src/core.ts @@ -0,0 +1,23 @@ +/** + * @zh_CN 登录页面 url 地址 + */ +export const LOGIN_PATH = '/auth/login'; + +export interface LanguageOption { + label: string; + value: 'en-US' | 'zh-CN'; +} + +/** + * Supported languages + */ +export const SUPPORT_LANGUAGES: LanguageOption[] = [ + { + label: '简体中文', + value: 'zh-CN', + }, + { + label: 'English', + value: 'en-US', + }, +]; diff --git a/deploy/fba/fba-ui-src/packages/constants/src/index.ts b/deploy/fba/fba-ui-src/packages/constants/src/index.ts new file mode 100644 index 0000000..6502c6d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/constants/src/index.ts @@ -0,0 +1,2 @@ +export * from './core'; +export * from '@vben-core/shared/constants'; diff --git a/deploy/fba/fba-ui-src/packages/constants/tsconfig.json b/deploy/fba/fba-ui-src/packages/constants/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/constants/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/README.md b/deploy/fba/fba-ui-src/packages/effects/README.md new file mode 100644 index 0000000..4965b51 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/README.md @@ -0,0 +1,10 @@ +## Effects 目录 + +`effects` 目录专门用于存放与轻微耦合相关的代码和逻辑。如果你的包具有以下特点,建议将其放置在 `effects` 目录下: + +- **状态管理**:使用状态管理框架 `pinia`,并包含处理副作用(如异步操作、API 调用)的部分。 +- **用户偏好设置**:使用 `@vben-core/preferences` 处理用户偏好设置,涉及本地存储或浏览器缓存逻辑(如使用 `localStorage`)。 +- **导航和路由**:处理导航、页面跳转等场景,需要管理路由变化的逻辑。 +- **组件库依赖**:包含与特定组件库紧密耦合或依赖大型仓库的部分。 + +通过将相关代码归类到 `effects` 目录,可以使项目结构更加清晰,便于维护和扩展。 diff --git a/deploy/fba/fba-ui-src/packages/effects/access/package.json b/deploy/fba/fba-ui-src/packages/effects/access/package.json new file mode 100644 index 0000000..248a03b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vben/access", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/permissions" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "vue": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/access/src/access-control.vue b/deploy/fba/fba-ui-src/packages/effects/access/src/access-control.vue new file mode 100644 index 0000000..219608e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/src/access-control.vue @@ -0,0 +1,47 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/access/src/accessible.ts b/deploy/fba/fba-ui-src/packages/effects/access/src/accessible.ts new file mode 100644 index 0000000..fc08736 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/src/accessible.ts @@ -0,0 +1,218 @@ +import type { Component, DefineComponent } from 'vue'; + +import type { + AccessModeType, + GenerateMenuAndRoutesOptions, + RouteRecordRaw, +} from '@vben/types'; + +import { defineComponent, h } from 'vue'; + +import { + cloneDeep, + generateMenus, + generateRoutesByBackend, + generateRoutesByFrontend, + isFunction, + isString, + mapTree, +} from '@vben/utils'; + +async function generateAccessible( + mode: AccessModeType, + options: GenerateMenuAndRoutesOptions, +) { + const { router } = options; + + options.routes = cloneDeep(options.routes); + + // 生成路由 + const accessibleRoutes = await generateRoutes(mode, options); + + const root = router.getRoutes().find((item) => item.path === '/'); + + // 获取已有的路由名称列表 + const names = root?.children?.map((item) => item.name) ?? []; + + // 动态添加到router实例内 + accessibleRoutes.forEach((route) => { + if (root && !route.meta?.noBasicLayout) { + // 为了兼容之前的版本用法,如果包含子路由,则将component移除,以免出现多层BasicLayout + // 如果你的项目已经跟进了本次修改,移除了所有自定义菜单首级的BasicLayout,可以将这段if代码删除 + if (route.children && route.children.length > 0) { + delete route.component; + } + // 根据router name判断,如果路由已经存在,则不再添加 + if (names?.includes(route.name)) { + // 找到已存在的路由索引并更新,不更新会造成切换用户时,一级目录未更新,homePath 在二级目录导致的404问题 + const index = root.children?.findIndex( + (item) => item.name === route.name, + ); + if (index !== undefined && index !== -1 && root.children) { + root.children[index] = route; + } + } else { + root.children?.push(route); + } + } else { + router.addRoute(route); + } + }); + + if (root) { + if (root.name) { + router.removeRoute(root.name); + } + router.addRoute(root); + } + + // 生成菜单 + const accessibleMenus = generateMenus(accessibleRoutes, options.router); + + return { accessibleMenus, accessibleRoutes }; +} + +/** + * Generate routes + * @param mode + * @param options + */ +async function generateRoutes( + mode: AccessModeType, + options: GenerateMenuAndRoutesOptions, +) { + const { forbiddenComponent, roles, routes } = options; + + let resultRoutes: RouteRecordRaw[] = routes; + switch (mode) { + case 'backend': { + resultRoutes = await generateRoutesByBackend(options); + break; + } + case 'frontend': { + resultRoutes = await generateRoutesByFrontend( + routes, + roles || [], + forbiddenComponent, + ); + break; + } + case 'mixed': { + const [frontend_resultRoutes, backend_resultRoutes] = await Promise.all([ + generateRoutesByFrontend(routes, roles || [], forbiddenComponent), + generateRoutesByBackend(options), + ]); + resultRoutes = mergeRoutesByName( + backend_resultRoutes, + frontend_resultRoutes, + ); + break; + } + } + + /** + * 调整路由树,做以下处理: + * 1. 对未添加redirect的路由添加redirect + * 2. 将懒加载的组件名称修改为当前路由的名称(如果启用了keep-alive的话) + */ + resultRoutes = mapTree(resultRoutes, (route) => { + // 重新包装component,使用与路由名称相同的name以支持keep-alive的条件缓存。 + if ( + route.meta?.keepAlive && + isFunction(route.component) && + route.name && + isString(route.name) + ) { + const originalComponent = route.component as () => Promise<{ + default: Component | DefineComponent; + }>; + route.component = async () => { + const component = await originalComponent(); + if (!component.default) return component; + return defineComponent({ + name: route.name as string, + setup(props, { attrs, slots }) { + return () => h(component.default, { ...props, ...attrs }, slots); + }, + }); + }; + } + + // 如果有redirect或者没有子路由,则直接返回 + if (route.redirect || !route.children || route.children.length === 0) { + return route; + } + const firstChild = route.children[0]; + + // 如果子路由不是以/开头,则直接返回,这种情况需要计算全部父级的path才能得出正确的path,这里不做处理 + if (!firstChild?.path || !firstChild.path.startsWith('/')) { + return route; + } + + route.redirect = firstChild.path; + return route; + }); + + return resultRoutes; +} + +/** + * 根据 name 合并前后端路由 + * @param baseRoutes 后端路由 + * @param extraRoutes 前端路由 + */ +function mergeRoutesByName( + baseRoutes: RouteRecordRaw[], + extraRoutes: RouteRecordRaw[], +): RouteRecordRaw[] { + const result: RouteRecordRaw[] = []; + const routeMap = new Map(); + + for (const route of baseRoutes) { + const clone = { ...route } as RouteRecordRaw; + result.push(clone); + if (clone.name && isString(clone.name)) { + routeMap.set(clone.name as string, clone); + } + } + + for (const route of extraRoutes) { + if ( + route.name && + isString(route.name) && + routeMap.has(route.name as string) + ) { + const existing = routeMap.get(route.name as string); + if (!existing) { + continue; + } + const existingChildren = existing.children ?? []; + const routeChildren = route.children ?? []; + + const merged = { + ...route, + ...existing, // keep backend as base + meta: { + ...route.meta, + ...existing.meta, // backend meta wins on conflicts + }, + } as RouteRecordRaw; + + if (existingChildren.length > 0 || routeChildren.length > 0) { + merged.children = mergeRoutesByName(existingChildren, routeChildren); + } + + Object.assign(existing, merged); + } else { + const clone = { ...route } as RouteRecordRaw; + result.push(clone); + if (clone.name && isString(clone.name)) { + routeMap.set(clone.name as string, clone); + } + } + } + + return result; +} + +export { generateAccessible }; diff --git a/deploy/fba/fba-ui-src/packages/effects/access/src/directive.ts b/deploy/fba/fba-ui-src/packages/effects/access/src/directive.ts new file mode 100644 index 0000000..35d9d51 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/src/directive.ts @@ -0,0 +1,42 @@ +/** + * Global authority directive + * Used for fine-grained control of component permissions + * @Example v-access:role="[ROLE_NAME]" or v-access:role="ROLE_NAME" + * @Example v-access:code="[ROLE_CODE]" or v-access:code="ROLE_CODE" + */ +import type { App, Directive, DirectiveBinding } from 'vue'; + +import { useAccess } from './use-access'; + +function isAccessible( + el: Element, + binding: DirectiveBinding, +) { + const { accessMode, hasAccessByCodes, hasAccessByRoles } = useAccess(); + + const value = binding.value; + + if (!value) return; + const authMethod = + accessMode.value === 'frontend' && binding.arg === 'role' + ? hasAccessByRoles + : hasAccessByCodes; + + const values = Array.isArray(value) ? value : [value]; + + if (!authMethod(values)) { + el?.remove(); + } +} + +const mounted = (el: Element, binding: DirectiveBinding) => { + isAccessible(el, binding); +}; + +const authDirective: Directive = { + mounted, +}; + +export function registerAccessDirective(app: App) { + app.directive('access', authDirective); +} diff --git a/deploy/fba/fba-ui-src/packages/effects/access/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/access/src/index.ts new file mode 100644 index 0000000..392aa53 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/src/index.ts @@ -0,0 +1,4 @@ +export { default as AccessControl } from './access-control.vue'; +export * from './accessible'; +export * from './directive'; +export * from './use-access'; diff --git a/deploy/fba/fba-ui-src/packages/effects/access/src/use-access.ts b/deploy/fba/fba-ui-src/packages/effects/access/src/use-access.ts new file mode 100644 index 0000000..939cdbe --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/src/use-access.ts @@ -0,0 +1,53 @@ +import { computed } from 'vue'; + +import { preferences, updatePreferences } from '@vben/preferences'; +import { useAccessStore, useUserStore } from '@vben/stores'; + +function useAccess() { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const accessMode = computed(() => { + return preferences.app.accessMode; + }); + + /** + * 基于角色判断是否有权限 + * @description: Determine whether there is permission,The role is judged by the user's role + * @param roles + */ + function hasAccessByRoles(roles: string[]) { + const userRoleSet = new Set(userStore.userRoles); + const intersection = roles.filter((item) => userRoleSet.has(item)); + return intersection.length > 0; + } + + /** + * 基于权限码判断是否有权限 + * @description: Determine whether there is permission,The permission code is judged by the user's permission code + * @param codes + */ + function hasAccessByCodes(codes: string[]) { + const userCodesSet = new Set(accessStore.accessCodes); + + const intersection = codes.filter((item) => userCodesSet.has(item)); + return intersection.length > 0; + } + + async function toggleAccessMode() { + updatePreferences({ + app: { + accessMode: + preferences.app.accessMode === 'frontend' ? 'backend' : 'frontend', + }, + }); + } + + return { + accessMode, + hasAccessByCodes, + hasAccessByRoles, + toggleAccessMode, + }; +} + +export { useAccess }; diff --git a/deploy/fba/fba-ui-src/packages/effects/access/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/access/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/access/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/package.json b/deploy/fba/fba-ui-src/packages/effects/common-ui/package.json new file mode 100644 index 0000000..44573b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/package.json @@ -0,0 +1,62 @@ +{ + "name": "@vben/common-ui", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/common-ui" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./es/tippy": { + "types": "./src/components/tippy/index.ts", + "default": "./src/components/tippy/index.ts" + }, + "./es/loading": { + "types": "./src/components/loading/index.ts", + "default": "./src/components/loading/index.ts" + } + }, + "dependencies": { + "@codemirror/lang-python": "catalog:", + "@codemirror/lang-sql": "catalog:", + "@codemirror/theme-one-dark": "catalog:", + "@vben-core/design": "workspace:*", + "@vben-core/form-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/preferences": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/types": "workspace:*", + "@vueuse/core": "catalog:", + "@vueuse/integrations": "catalog:", + "json-bigint": "catalog:", + "md-editor-v3": "catalog:", + "qrcode": "catalog:", + "tippy.js": "catalog:", + "vue": "catalog:", + "vue-codemirror6": "catalog:", + "vue-json-pretty": "catalog:", + "vue-router": "catalog:", + "vue-tippy": "catalog:" + }, + "devDependencies": { + "@types/json-bigint": "catalog:", + "@types/qrcode": "catalog:", + "@vue/test-utils": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/api-component.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/api-component.vue new file mode 100644 index 0000000..2c6d693 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/api-component.vue @@ -0,0 +1,253 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/index.ts new file mode 100644 index 0000000..173b30d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/index.ts @@ -0,0 +1,7 @@ +export { default as ApiComponent } from './api-component.vue'; +export type { + ApiComponentLabelFn, + ApiComponentOptionsItem, + ApiComponentProps, + ApiComponentSharedProps, +} from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/types.ts new file mode 100644 index 0000000..a771f78 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/api-component/types.ts @@ -0,0 +1,72 @@ +import type { Component } from 'vue'; + +import type { AnyPromiseFunction } from '@vben/types'; + +export type ApiComponentOptionsItem = { + [name: string]: any; + children?: ApiComponentOptionsItem[]; + disabled?: boolean; + label?: string; + value?: number | string; +}; + +export type ApiComponentLabelFn = (item: ApiComponentOptionsItem) => string; + +export interface ApiComponentProps { + /** 组件 */ + component: Component; + /** 是否将value从数字转为string */ + numberToString?: boolean; + /** 获取options数据的函数 */ + api?: (arg?: any) => Promise>; + /** 传递给api的参数 */ + params?: Record; + /** 从api返回的结果中提取options数组的字段名 */ + resultField?: string; + /** label字段名 */ + labelField?: string; + /** 通过选项数据自定义label */ + labelFn?: ApiComponentLabelFn; + /** children字段名,需要层级数据的组件可用 */ + childrenField?: string; + /** value字段名 */ + valueField?: string; + /** disabled字段名 */ + disabledField?: string; + /** 组件接收options数据的属性名 */ + optionsPropName?: string; + /** 是否立即调用api */ + immediate?: boolean; + /** 每次`visibleEvent`事件发生时都重新请求数据 */ + alwaysLoad?: boolean; + /** 在api请求之前的回调函数 */ + beforeFetch?: AnyPromiseFunction; + /** 在api请求之前的判断是否允许请求的回调函数 */ + shouldFetch?: AnyPromiseFunction; + /** 在api请求之后的回调函数 */ + afterFetch?: AnyPromiseFunction; + /** 直接传入选项数据,也作为api返回空数据时的后备数据 */ + options?: ApiComponentOptionsItem[]; + /** 组件的插槽名称,用来显示一个"加载中"的图标 */ + loadingSlot?: string; + /** 触发api请求的事件名 */ + visibleEvent?: string; + /** 组件的v-model属性名,默认为modelValue。部分组件可能为value */ + modelPropName?: string; + /** + * 自动选择 + * - `first`:自动选择第一个选项 + * - `last`:自动选择最后一个选项 + * - `one`: 当请求的结果只有一个选项时,自动选择该选项 + * - 函数:自定义选择逻辑,函数的参数为请求的结果数组,返回值为选择的选项 + * - false:不自动选择(默认) + */ + autoSelect?: + | 'first' + | 'last' + | 'one' + | ((item: ApiComponentOptionsItem[]) => ApiComponentOptionsItem) + | false; +} + +export type ApiComponentSharedProps = Omit; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts new file mode 100644 index 0000000..a3f61fd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts @@ -0,0 +1,19 @@ +import type { CaptchaPoint } from '../types'; + +import { reactive } from 'vue'; + +export function useCaptchaPoints() { + const points = reactive([]); + function addPoint(point: CaptchaPoint) { + points.push(point); + } + + function clearPoints() { + points.splice(0); + } + return { + addPoint, + clearPoints, + points, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/index.ts new file mode 100644 index 0000000..13634cd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/index.ts @@ -0,0 +1,7 @@ +export { default as PointSelectionCaptcha } from './point-selection-captcha/index.vue'; +export { default as PointSelectionCaptchaCard } from './point-selection-captcha/index.vue'; + +export { default as SliderCaptcha } from './slider-captcha/index.vue'; +export { default as SliderRotateCaptcha } from './slider-rotate-captcha/index.vue'; +export { default as SliderTranslateCaptcha } from './slider-translate-captcha/index.vue'; +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue new file mode 100644 index 0000000..c20f02f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue @@ -0,0 +1,176 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue new file mode 100644 index 0000000..0593b13 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue @@ -0,0 +1,86 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue new file mode 100644 index 0000000..df80170 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue @@ -0,0 +1,254 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue new file mode 100644 index 0000000..74074a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue @@ -0,0 +1,65 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue new file mode 100644 index 0000000..05982f4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue @@ -0,0 +1,40 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue new file mode 100644 index 0000000..51bc75b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue new file mode 100644 index 0000000..1e22978 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-translate-captcha/index.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-translate-captcha/index.vue new file mode 100644 index 0000000..90c2df8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/slider-translate-captcha/index.vue @@ -0,0 +1,311 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/types.ts new file mode 100644 index 0000000..f73877e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/captcha/types.ts @@ -0,0 +1,210 @@ +import type { CSSProperties } from 'vue'; + +import type { ClassType } from '@vben/types'; + +export interface CaptchaData { + /** + * x + */ + x: number; + /** + * y + */ + y: number; + /** + * 时间戳 + */ + t: number; +} +export interface CaptchaPoint extends CaptchaData { + /** + * 数据索引 + */ + i: number; +} +export interface PointSelectionCaptchaCardProps { + /** + * 验证码图片 + */ + captchaImage: string; + /** + * 验证码图片高度 + * @default '220px' + */ + height?: number | string; + /** + * 水平内边距 + * @default '12px' + */ + paddingX?: number | string; + /** + * 垂直内边距 + * @default '16px' + */ + paddingY?: number | string; + /** + * 标题 + * @default '请按图依次点击' + */ + title?: string; + /** + * 验证码图片宽度 + * @default '300px' + */ + width?: number | string; +} + +export interface PointSelectionCaptchaProps extends PointSelectionCaptchaCardProps { + /** + * 是否展示确定按钮 + * @default false + */ + showConfirm?: boolean; + /** + * 提示图片 + * @default '' + */ + hintImage?: string; + /** + * 提示文本 + * @default '' + */ + hintText?: string; +} + +export interface SliderCaptchaProps { + class?: ClassType; + /** + * @description 滑块的样式 + * @default {} + */ + actionStyle?: CSSProperties; + + /** + * @description 滑块条的样式 + * @default {} + */ + barStyle?: CSSProperties; + + /** + * @description 内容的样式 + * @default {} + */ + contentStyle?: CSSProperties; + + /** + * @description 组件的样式 + * @default {} + */ + wrapperStyle?: CSSProperties; + + /** + * @description 是否作为插槽使用,用于联动组件,可参考旋转校验组件 + * @default false + */ + isSlot?: boolean; + + /** + * @description 验证成功的提示 + * @default '验证通过' + */ + successText?: string; + + /** + * @description 提示文字 + * @default '请按住滑块拖动' + */ + text?: string; +} + +export interface SliderRotateCaptchaProps { + /** + * @description 旋转的角度 + * @default 20 + */ + diffDegree?: number; + + /** + * @description 图片的宽度 + * @default 260 + */ + imageSize?: number; + + /** + * @description 图片的样式 + * @default {} + */ + imageWrapperStyle?: CSSProperties; + + /** + * @description 最大旋转角度 + * @default 270 + */ + maxDegree?: number; + + /** + * @description 最小旋转角度 + * @default 90 + */ + minDegree?: number; + + /** + * @description 图片的地址 + */ + src?: string; + /** + * @description 默认提示文本 + */ + defaultTip?: string; +} + +export interface SliderTranslateCaptchaProps { + /** + * @description 拼图的宽度 + * @default 420 + */ + canvasWidth?: number; + /** + * @description 拼图的高度 + * @default 280 + */ + canvasHeight?: number; + /** + * @description 切块上正方形的长度 + * @default 42 + */ + squareLength?: number; + /** + * @description 切块上圆形的半径 + * @default 10 + */ + circleRadius?: number; + /** + * @description 图片的地址 + */ + src?: string; + /** + * @description 允许的最大差距 + * @default 3 + */ + diffDistance?: number; + /** + * @description 默认提示文本 + */ + defaultTip?: string; +} + +export interface CaptchaVerifyPassingData { + isPassing: boolean; + time: number | string; +} + +export interface SliderCaptchaActionType { + resume: () => void; +} + +export interface SliderRotateVerifyPassingData { + event: MouseEvent | TouchEvent; + moveDistance: number; + moveX: number; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue new file mode 100644 index 0000000..8e226ba --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue @@ -0,0 +1,66 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/data.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/data.ts new file mode 100644 index 0000000..9d1c4c0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/data.ts @@ -0,0 +1,13 @@ +import { python } from '@codemirror/lang-python'; +import { sql } from '@codemirror/lang-sql'; + +/** + * 可自行安装依赖并按格式配置 函数形参为配置项 + * @see https://github.com/logue/vue-codemirror6?tab=readme-ov-file#supported-languages Language Support项 + */ +export const languageSupportMap = { + sql: sql(), + python: python(), +}; + +export type LanguageSupport = keyof typeof languageSupportMap; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/index.ts new file mode 100644 index 0000000..c9c7c65 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/code-mirror/index.ts @@ -0,0 +1,2 @@ +export { default as CodeMirror } from './code-mirror.vue'; +export type { LanguageSupport } from './data'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/col-page.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/col-page.vue new file mode 100644 index 0000000..73a38ca --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/col-page.vue @@ -0,0 +1,111 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/index.ts new file mode 100644 index 0000000..11b3305 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/index.ts @@ -0,0 +1,2 @@ +export { default as ColPage } from './col-page.vue'; +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/types.ts new file mode 100644 index 0000000..491d1e6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/col-page/types.ts @@ -0,0 +1,26 @@ +import type { PageProps } from '../page/types'; + +export interface ColPageProps extends PageProps { + /** + * 左侧宽度 + * @default 30 + */ + leftWidth?: number; + leftMinWidth?: number; + leftMaxWidth?: number; + leftCollapsedWidth?: number; + leftCollapsible?: boolean; + /** + * 右侧宽度 + * @default 70 + */ + rightWidth?: number; + rightMinWidth?: number; + rightCollapsedWidth?: number; + rightMaxWidth?: number; + rightCollapsible?: boolean; + + resizable?: boolean; + splitLine?: boolean; + splitHandle?: boolean; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/count-to.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/count-to.vue new file mode 100644 index 0000000..d6b0943 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/count-to.vue @@ -0,0 +1,123 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/index.ts new file mode 100644 index 0000000..d942796 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/index.ts @@ -0,0 +1,2 @@ +export { default as CountTo } from './count-to.vue'; +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/types.ts new file mode 100644 index 0000000..0b3bd82 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/count-to/types.ts @@ -0,0 +1,53 @@ +import type { CubicBezierPoints, EasingFunction } from '@vueuse/core'; + +import type { StyleValue } from 'vue'; + +import { TransitionPresets as TransitionPresetsData } from '@vueuse/core'; + +export type TransitionPresets = keyof typeof TransitionPresetsData; + +export const TransitionPresetsKeys = Object.keys( + TransitionPresetsData, +) as TransitionPresets[]; + +export interface CountToProps { + /** 初始值 */ + startVal?: number; + /** 当前值 */ + endVal: number; + /** 是否禁用动画 */ + disabled?: boolean; + /** 延迟动画开始的时间 */ + delay?: number; + /** 持续时间 */ + duration?: number; + /** 小数位数 */ + decimals?: number; + /** 小数点 */ + decimal?: string; + /** 分隔符 */ + separator?: string; + /** 前缀 */ + prefix?: string; + /** 后缀 */ + suffix?: string; + /** 过渡效果 */ + transition?: CubicBezierPoints | EasingFunction | TransitionPresets; + /** 整数部分的类名 */ + mainClass?: string; + /** 小数部分的类名 */ + decimalClass?: string; + /** 前缀部分的类名 */ + prefixClass?: string; + /** 后缀部分的类名 */ + suffixClass?: string; + + /** 整数部分的样式 */ + mainStyle?: StyleValue; + /** 小数部分的样式 */ + decimalStyle?: StyleValue; + /** 前缀部分的样式 */ + prefixStyle?: StyleValue; + /** 后缀部分的样式 */ + suffixStyle?: StyleValue; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/cropper.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/cropper.vue new file mode 100644 index 0000000..27f1ca8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/cropper.vue @@ -0,0 +1,981 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/index.ts new file mode 100644 index 0000000..58a8f0a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/cropper/index.ts @@ -0,0 +1 @@ +export { default as VCropper } from './cropper.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue new file mode 100644 index 0000000..ed9b557 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue @@ -0,0 +1,232 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/index.ts new file mode 100644 index 0000000..67a236c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/ellipsis-text/index.ts @@ -0,0 +1 @@ +export { default as EllipsisText } from './ellipsis-text.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue new file mode 100644 index 0000000..e94ed00 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue @@ -0,0 +1,300 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icons.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icons.ts new file mode 100644 index 0000000..694e904 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/icons.ts @@ -0,0 +1,56 @@ +import type { Recordable } from '@vben/types'; + +/** + * 一个缓存对象,在不刷新页面时,无需重复请求远程接口 + */ +export const ICONS_MAP: Recordable = {}; + +interface IconifyResponse { + prefix: string; + total: number; + title: string; + uncategorized?: string[]; + categories?: Recordable; + aliases?: Recordable; +} + +const PENDING_REQUESTS: Recordable> = {}; + +/** + * 通过Iconify接口获取图标集数据。 + * 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。 + * 请求结果会被缓存,刷新页面前同一个图标集不会再次请求 + * @param prefix 图标集名称 + * @returns 图标集中包含的所有图标名称 + */ +export async function fetchIconsData(prefix: string): Promise { + if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) { + return ICONS_MAP[prefix]; + } + if (Reflect.has(PENDING_REQUESTS, prefix) && PENDING_REQUESTS[prefix]) { + return PENDING_REQUESTS[prefix]; + } + PENDING_REQUESTS[prefix] = (async () => { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 1000 * 10); + const response: IconifyResponse = await fetch( + `https://api.iconify.design/collection?prefix=${prefix}`, + { signal: controller.signal }, + ).then((res) => res.json()); + clearTimeout(timeoutId); + const list = response.uncategorized || []; + if (response.categories) { + for (const category in response.categories) { + list.push(...(response.categories[category] || [])); + } + } + ICONS_MAP[prefix] = list.map((v) => `${prefix}:${v}`); + } catch (error) { + console.error(`Failed to fetch icons for prefix ${prefix}:`, error); + return [] as string[]; + } + return ICONS_MAP[prefix]; + })(); + return PENDING_REQUESTS[prefix]; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/index.ts new file mode 100644 index 0000000..8d600b8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/index.ts @@ -0,0 +1,2 @@ +export { default as IconPicker } from './icon-picker.vue'; +export type { IconPickerProps } from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/types.ts new file mode 100644 index 0000000..7695bb5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/icon-picker/types.ts @@ -0,0 +1,22 @@ +import type { VNode } from 'vue'; + +export interface IconPickerProps { + pageSize?: number; + /** 图标集的名字 */ + prefix?: string; + /** 是否自动请求API以获得图标集的数据.提供prefix时有效 */ + autoFetchApi?: boolean; + /** + * 图标列表 + */ + icons?: string[]; + /** Input组件 */ + inputComponent?: VNode; + /** 图标插槽名,预览图标将被渲染到此插槽中 */ + iconSlot?: string; + /** input组件的值属性名称 */ + modelValueProp?: string; + /** 图标样式 */ + iconClass?: string; + type?: 'icon' | 'input'; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/index.ts new file mode 100644 index 0000000..2e4a16a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/index.ts @@ -0,0 +1,53 @@ +export * from './api-component'; +export * from './captcha'; +export * from './code-mirror'; +export * from './col-page'; +export * from './count-to'; +export * from './cropper'; +export * from './ellipsis-text'; +export * from './icon-picker'; +export * from './json-viewer'; +export * from './loading'; +export * from './markdown'; +export * from './page'; +export * from './resize'; +export * from './tippy'; +export * from './tree'; +export * from '@vben-core/form-ui'; +export * from '@vben-core/popup-ui'; + +// 给文档用 +export { + VbenAvatar, + VbenButton, + VbenButtonGroup, + VbenCheckbox, + VbenCheckButtonGroup, + VbenCollapsibleParams, + VbenContextMenu, + VbenCountToAnimator, + VbenDescriptions, + VbenDescriptionsItem, + VbenFullScreen, + VbenIconButton, + VbenInputPassword, + VbenLoading, + VbenLogo, + VbenPinInput, + VbenSelect, + VbenSpinner, + VbenTableAction, +} from '@vben-core/shadcn-ui'; + +export type { + ActionItem, + CollapsibleParamSchema, + CollapsibleParamsProps, + DescriptionsColumn, + DescriptionsItemType, + DescriptionsProps, + DescriptionsSize, + FlattenedItem, + TableActionProps, +} from '@vben-core/shadcn-ui'; +export { globalShareState } from '@vben-core/shared/global-state'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.ts new file mode 100644 index 0000000..1aec78b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.ts @@ -0,0 +1,3 @@ +export { default as JsonViewer } from './index.vue'; + +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.vue new file mode 100644 index 0000000..72618f5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/index.vue @@ -0,0 +1,128 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/style.scss b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/style.scss new file mode 100644 index 0000000..ffbb184 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/style.scss @@ -0,0 +1,91 @@ +.vben-json-viewer { + font-family: Consolas, Menlo, Courier, monospace; + font-size: 14px; + color: hsl(var(--foreground)); + background: hsl(var(--background)); + + &.boxed { + padding: 10px; + border: 1px solid hsl(var(--border)); + border-radius: 4px; + } + + .vjs-key { + color: hsl(var(--heavy-foreground)); + } + + .vjs-value-null, + .vjs-value-undefined { + color: hsl(var(--secondary-foreground)); + } + + .vjs-value-boolean { + color: hsl(var(--red-400)); + } + + .vjs-value-number { + color: hsl(var(--info-foreground)); + } + + .vjs-value-string { + color: hsl(var(--primary)); + overflow-wrap: break-word; + white-space: normal; + } + + .vjs-tree-brackets { + color: hsl(var(--heavy-foreground)); + } + + .vjs-comment { + color: hsl(var(--secondary-foreground)); + } + + .vjs-tree-node { + &.is-highlight { + background-color: hsl(var(--accent)); + } + } + + .vjs-carets { + cursor: pointer; + + &:hover { + color: hsl(var(--primary)); + } + } + + .vjs-indent-unit { + &.has-line { + border-left: 1px solid hsl(var(--border)); + } + } + + .vben-json-copy-btn { + display: inline-block; + padding: 0 6px; + margin-left: 8px; + font-size: 12px; + line-height: 20px; + color: hsl(var(--primary)); + cursor: pointer; + user-select: none; + background-color: hsl(var(--secondary)); + border-radius: 3px; + opacity: 0; + transition: opacity 0.2s; + + &:hover { + background-color: hsl(var(--accent)); + } + + &.is-copied { + color: hsl(var(--success-foreground, var(--primary))); + opacity: 1; + } + } + + .vjs-tree-node:hover .vben-json-copy-btn { + opacity: 1; + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/types.ts new file mode 100644 index 0000000..8f3a206 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/json-viewer/types.ts @@ -0,0 +1,44 @@ +export interface JsonViewerProps { + /** 要展示的结构数据 */ + value: any; + /** 展开深度 */ + expandDepth?: number; + /** 是否可复制 */ + copyable?: boolean; + /** 是否排序 */ + sort?: boolean; + /** 显示边框 */ + boxed?: boolean; + /** 主题 */ + theme?: string; + /** 是否展开 */ + expanded?: boolean; + /** 时间格式化函数 */ + timeformat?: (time: Date | number | string) => string; + /** 预览模式 */ + previewMode?: boolean; + /** 显示数组索引 */ + showArrayIndex?: boolean; + /** 显示双引号 */ + showDoubleQuotes?: boolean; +} + +export interface JsonViewerAction { + action: string; + text: string; + trigger: HTMLElement; +} + +export interface JsonViewerValue { + value: any; + path: string; + depth: number; + el: HTMLElement; +} + +export interface JsonViewerToggle { + /** 鼠标事件 */ + event: MouseEvent; + /** 当前展开状态 */ + open: boolean; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/directive.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/directive.ts new file mode 100644 index 0000000..973a605 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/directive.ts @@ -0,0 +1,132 @@ +import type { App, Directive, DirectiveBinding } from 'vue'; + +import { h, render } from 'vue'; + +import { VbenLoading, VbenSpinner } from '@vben-core/shadcn-ui'; +import { isString } from '@vben-core/shared/utils'; + +const LOADING_INSTANCE_KEY = Symbol('loading'); +const SPINNER_INSTANCE_KEY = Symbol('spinner'); + +const CLASS_NAME_RELATIVE = 'spinner-parent--relative'; + +const loadingDirective: Directive = { + mounted(el, binding) { + const instance = h(VbenLoading, getOptions(binding)); + render(instance, el); + + el.classList.add(CLASS_NAME_RELATIVE); + el[LOADING_INSTANCE_KEY] = instance; + }, + unmounted(el) { + const instance = el[LOADING_INSTANCE_KEY]; + el.classList.remove(CLASS_NAME_RELATIVE); + render(null, el); + instance.el.remove(); + + el[LOADING_INSTANCE_KEY] = null; + }, + + updated(el, binding) { + const instance = el[LOADING_INSTANCE_KEY]; + const options = getOptions(binding); + if (options && instance?.component) { + try { + Object.keys(options).forEach((key) => { + instance.component.props[key] = options[key]; + }); + instance.component.update(); + } catch (error) { + console.error( + 'Failed to update loading component in directive:', + error, + ); + } + } + }, +}; + +function getOptions(binding: DirectiveBinding) { + if (binding.value === undefined) { + return { spinning: true }; + } else if (typeof binding.value === 'boolean') { + return { spinning: binding.value }; + } else { + return { ...binding.value }; + } +} + +const spinningDirective: Directive = { + mounted(el, binding) { + const instance = h(VbenSpinner, getOptions(binding)); + render(instance, el); + + el.classList.add(CLASS_NAME_RELATIVE); + el[SPINNER_INSTANCE_KEY] = instance; + }, + unmounted(el) { + const instance = el[SPINNER_INSTANCE_KEY]; + el.classList.remove(CLASS_NAME_RELATIVE); + render(null, el); + instance.el.remove(); + + el[SPINNER_INSTANCE_KEY] = null; + }, + + updated(el, binding) { + const instance = el[SPINNER_INSTANCE_KEY]; + const options = getOptions(binding); + if (options && instance?.component) { + try { + Object.keys(options).forEach((key) => { + instance.component.props[key] = options[key]; + }); + instance.component.update(); + } catch (error) { + console.error( + 'Failed to update spinner component in directive:', + error, + ); + } + } + }, +}; + +type loadingDirectiveParams = { + /** 是否注册loading指令。如果提供一个string,则将指令注册为指定的名称 */ + loading?: boolean | string; + /** 是否注册spinning指令。如果提供一个string,则将指令注册为指定的名称 */ + spinning?: boolean | string; +}; + +/** + * 注册loading指令 + * @param app + * @param params + */ +export function registerLoadingDirective( + app: App, + params?: loadingDirectiveParams, +) { + // 注入一个样式供指令使用,确保容器是相对定位 + const style = document.createElement('style'); + style.id = CLASS_NAME_RELATIVE; + style.innerHTML = ` + .${CLASS_NAME_RELATIVE} { + position: relative !important; + } + `; + document.head.append(style); + if (params?.loading !== false) { + app.directive( + isString(params?.loading) ? params.loading : 'loading', + loadingDirective, + ); + } + if (params?.spinning !== false) { + app.directive( + isString(params?.spinning) ? params.spinning : 'spinning', + spinningDirective, + ); + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/index.ts new file mode 100644 index 0000000..2fbfb04 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/index.ts @@ -0,0 +1,3 @@ +export * from './directive'; +export { default as Loading } from './loading.vue'; +export { default as Spinner } from './spinner.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/loading.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/loading.vue new file mode 100644 index 0000000..faf49e6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/loading.vue @@ -0,0 +1,39 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/spinner.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/spinner.vue new file mode 100644 index 0000000..caee2bb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/loading/spinner.vue @@ -0,0 +1,28 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/editor.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/editor.vue new file mode 100644 index 0000000..3f10a4e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/editor.vue @@ -0,0 +1,81 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/index.ts new file mode 100644 index 0000000..dfea14d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/index.ts @@ -0,0 +1,2 @@ +export { default as MarkdownEditor } from './editor.vue'; +export { default as MarkdownPreviewer } from './preview.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/preview.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/preview.vue new file mode 100644 index 0000000..89114db --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/markdown/preview.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/__tests__/page.test.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/__tests__/page.test.ts new file mode 100644 index 0000000..a89061f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/__tests__/page.test.ts @@ -0,0 +1,89 @@ +import { mount } from '@vue/test-utils'; + +import { describe, expect, it } from 'vitest'; + +import { Page } from '..'; + +describe('page.vue', () => { + it('renders title when passed', () => { + const wrapper = mount(Page, { + props: { + title: 'Test Title', + }, + }); + + expect(wrapper.text()).toContain('Test Title'); + }); + + it('renders description when passed', () => { + const wrapper = mount(Page, { + props: { + description: 'Test Description', + }, + }); + + expect(wrapper.text()).toContain('Test Description'); + }); + + it('renders default slot content', () => { + const wrapper = mount(Page, { + slots: { + default: '

Default Slot Content

', + }, + }); + + expect(wrapper.html()).toContain('

Default Slot Content

'); + }); + + it('renders footer slot when showFooter is true', () => { + const wrapper = mount(Page, { + props: { + showFooter: true, + }, + slots: { + footer: '

Footer Slot Content

', + }, + }); + + expect(wrapper.html()).toContain('

Footer Slot Content

'); + }); + + it('applies the custom contentClass', () => { + const wrapper = mount(Page, { + props: { + contentClass: 'custom-class', + }, + }); + + const contentDiv = wrapper.find('.p-4'); + expect(contentDiv.classes()).toContain('custom-class'); + }); + + it('does not render title slot if title prop is provided', () => { + const wrapper = mount(Page, { + props: { + title: 'Test Title', + }, + slots: { + title: '

Title Slot Content

', + }, + }); + + expect(wrapper.text()).toContain('Title Slot Content'); + expect(wrapper.html()).not.toContain('Test Title'); + }); + + it('does not render description slot if description prop is provided', () => { + const wrapper = mount(Page, { + props: { + description: 'Test Description', + }, + slots: { + description: '

Description Slot Content

', + }, + }); + + expect(wrapper.text()).toContain('Description Slot Content'); + expect(wrapper.html()).not.toContain('Test Description'); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/index.ts new file mode 100644 index 0000000..fd9e02f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/index.ts @@ -0,0 +1,2 @@ +export { default as Page } from './page.vue'; +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/page.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/page.vue new file mode 100644 index 0000000..55146b9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/page.vue @@ -0,0 +1,109 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/types.ts new file mode 100644 index 0000000..dfebd12 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/page/types.ts @@ -0,0 +1,23 @@ +export interface PageProps { + title?: string; + description?: string; + contentClass?: string; + /** + * 根据content可见高度自适应 + */ + autoContentHeight?: boolean; + headerClass?: string; + footerClass?: string; + /** + * Custom height offset value (in pixels) to adjust content area sizing + * when used with autoContentHeight + * @default 0 + */ + heightOffset?: number; + /** + * Whether the footer is position: fixed. + * When true, footer height is excluded from content height calculation. + * @default false + */ + footerFixed?: boolean; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/index.ts new file mode 100644 index 0000000..f937522 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/index.ts @@ -0,0 +1 @@ +export { default as VResize } from './resize.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/resize.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/resize.vue new file mode 100644 index 0000000..18b1cf6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/resize/resize.vue @@ -0,0 +1,1269 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/directive.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/directive.ts new file mode 100644 index 0000000..ed277d6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/directive.ts @@ -0,0 +1,100 @@ +import type { ComputedRef, Directive } from 'vue'; + +import { useTippy } from 'vue-tippy'; + +export default function useTippyDirective(isDark: ComputedRef) { + const directive: Directive = { + mounted(el, binding, vnode) { + const opts = + typeof binding.value === 'string' + ? { content: binding.value } + : binding.value || {}; + + const modifiers = Object.keys(binding.modifiers || {}); + const placement = modifiers.find((modifier) => modifier !== 'arrow'); + const withArrow = modifiers.includes('arrow'); + + if (placement) { + opts.placement = opts.placement || placement; + } + + if (withArrow) { + opts.arrow = opts.arrow === undefined ? true : opts.arrow; + } + + if (vnode.props && vnode.props.onTippyShow) { + opts.onShow = function (...args: any[]) { + return vnode.props?.onTippyShow(...args); + }; + } + + if (vnode.props && vnode.props.onTippyShown) { + opts.onShown = function (...args: any[]) { + return vnode.props?.onTippyShown(...args); + }; + } + + if (vnode.props && vnode.props.onTippyHidden) { + opts.onHidden = function (...args: any[]) { + return vnode.props?.onTippyHidden(...args); + }; + } + + if (vnode.props && vnode.props.onTippyHide) { + opts.onHide = function (...args: any[]) { + return vnode.props?.onTippyHide(...args); + }; + } + + if (vnode.props && vnode.props.onTippyMount) { + opts.onMount = function (...args: any[]) { + return vnode.props?.onTippyMount(...args); + }; + } + + if (el.getAttribute('title') && !opts.content) { + opts.content = el.getAttribute('title'); + el.removeAttribute('title'); + } + + if (el.getAttribute('content') && !opts.content) { + opts.content = el.getAttribute('content'); + } + + useTippy(el, opts); + }, + unmounted(el) { + if (el.$tippy) { + el.$tippy.destroy(); + } else if (el._tippy) { + el._tippy.destroy(); + } + }, + + updated(el, binding) { + const opts = + typeof binding.value === 'string' + ? { content: binding.value, theme: isDark.value ? '' : 'light' } + : Object.assign( + { theme: isDark.value ? '' : 'light' }, + binding.value, + ); + + if (el.getAttribute('title') && !opts.content) { + opts.content = el.getAttribute('title'); + el.removeAttribute('title'); + } + + if (el.getAttribute('content') && !opts.content) { + opts.content = el.getAttribute('content'); + } + + if (el.$tippy) { + el.$tippy.setProps(opts || {}); + } else if (el._tippy) { + el._tippy.setProps(opts || {}); + } + }, + }; + return directive; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/index.ts new file mode 100644 index 0000000..6080e58 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tippy/index.ts @@ -0,0 +1,67 @@ +import type { DefaultProps, Props } from 'tippy.js'; + +import type { App, SetupContext } from 'vue'; + +import { h, watchEffect } from 'vue'; +import { setDefaultProps, Tippy as TippyComponent } from 'vue-tippy'; + +import { usePreferences } from '@vben-core/preferences'; + +import useTippyDirective from './directive'; + +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/dist/backdrop.css'; +import 'tippy.js/themes/light.css'; +import 'tippy.js/animations/scale.css'; +import 'tippy.js/animations/shift-toward.css'; +import 'tippy.js/animations/shift-away.css'; +import 'tippy.js/animations/perspective.css'; + +const { isDark } = usePreferences(); +export type TippyProps = Partial< + Props & { + animation?: + | 'fade' + | 'perspective' + | 'scale' + | 'shift-away' + | 'shift-toward' + | boolean; + theme?: 'auto' | 'dark' | 'light'; + } +>; + +export function initTippy(app: App, options?: DefaultProps) { + setDefaultProps({ + allowHTML: true, + delay: [500, 200], + theme: isDark.value ? '' : 'light', + ...options, + }); + if (!options || !Reflect.has(options, 'theme') || options.theme === 'auto') { + watchEffect(() => { + setDefaultProps({ theme: isDark.value ? '' : 'light' }); + }); + } + + app.directive('tippy', useTippyDirective(isDark)); +} + +export const Tippy = (props: any, { attrs, slots }: SetupContext) => { + let theme: string = (attrs.theme as string) ?? 'auto'; + if (theme === 'auto') { + theme = isDark.value ? '' : 'light'; + } + if (theme === 'dark') { + theme = ''; + } + return h( + TippyComponent, + { + ...props, + ...attrs, + theme, + }, + slots, + ); +}; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/index.ts new file mode 100644 index 0000000..ce3bc5c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/index.ts @@ -0,0 +1 @@ +export { default as Tree } from './tree.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/tree.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/tree.vue new file mode 100644 index 0000000..4f5c8b7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/components/tree/tree.vue @@ -0,0 +1,25 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/index.ts new file mode 100644 index 0000000..27bd718 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './ui'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.ts new file mode 100644 index 0000000..c4cf957 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.ts @@ -0,0 +1,14 @@ +import type { Component } from 'vue'; + +interface AboutProps { + description?: string; + name?: string; + title?: string; +} + +interface DescriptionItem { + content: Component | string; + title: string; +} + +export type { AboutProps, DescriptionItem }; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.vue new file mode 100644 index 0000000..24fccfa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/about.vue @@ -0,0 +1,183 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/index.ts new file mode 100644 index 0000000..5860f5b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/about/index.ts @@ -0,0 +1 @@ +export { default as About } from './about.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/auth-title.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/auth-title.vue new file mode 100644 index 0000000..5b8be56 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/auth-title.vue @@ -0,0 +1,13 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/code-login.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/code-login.vue new file mode 100644 index 0000000..d7563d8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/code-login.vue @@ -0,0 +1,127 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/dingding-login.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/dingding-login.vue new file mode 100644 index 0000000..5e201d8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/dingding-login.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/forget-password.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/forget-password.vue new file mode 100644 index 0000000..9c82df4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/forget-password.vue @@ -0,0 +1,116 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/index.ts new file mode 100644 index 0000000..ac5e1e0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/index.ts @@ -0,0 +1,7 @@ +export { default as AuthenticationCodeLogin } from './code-login.vue'; +export { default as AuthenticationForgetPassword } from './forget-password.vue'; +export { default as AuthenticationLoginExpiredModal } from './login-expired-modal.vue'; +export { default as AuthenticationLogin } from './login.vue'; +export { default as AuthenticationQrCodeLogin } from './qrcode-login.vue'; +export { default as AuthenticationRegister } from './register.vue'; +export type { AuthenticationProps } from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue new file mode 100644 index 0000000..9500492 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue @@ -0,0 +1,95 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login.vue new file mode 100644 index 0000000..1606276 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/login.vue @@ -0,0 +1,186 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue new file mode 100644 index 0000000..1914875 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue @@ -0,0 +1,105 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/register.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/register.vue new file mode 100644 index 0000000..69a69d4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/register.vue @@ -0,0 +1,121 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/third-party-login.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/third-party-login.vue new file mode 100644 index 0000000..be84f0d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/third-party-login.vue @@ -0,0 +1,71 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/types.ts new file mode 100644 index 0000000..cb297e2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/authentication/types.ts @@ -0,0 +1,70 @@ +interface AuthenticationProps { + /** + * @zh_CN 验证码登录路径 + */ + codeLoginPath?: string; + /** + * @zh_CN 忘记密码路径 + */ + forgetPasswordPath?: string; + + /** + * @zh_CN 是否处于加载处理状态 + */ + loading?: boolean; + + /** + * @zh_CN 二维码登录路径 + */ + qrCodeLoginPath?: string; + + /** + * @zh_CN 注册路径 + */ + registerPath?: string; + + /** + * @zh_CN 是否显示验证码登录 + */ + showCodeLogin?: boolean; + /** + * @zh_CN 是否显示忘记密码 + */ + showForgetPassword?: boolean; + + /** + * @zh_CN 是否显示二维码登录 + */ + showQrcodeLogin?: boolean; + + /** + * @zh_CN 是否显示注册按钮 + */ + showRegister?: boolean; + + /** + * @zh_CN 是否显示记住账号 + */ + showRememberMe?: boolean; + + /** + * @zh_CN 是否显示第三方登录 + */ + showThirdPartyLogin?: boolean; + + /** + * @zh_CN 登录框子标题 + */ + subTitle?: string; + + /** + * @zh_CN 登录框标题 + */ + title?: string; + /** + * @zh_CN 提交按钮文本 + */ + submitButtonText?: string; +} + +export type { AuthenticationProps }; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue new file mode 100644 index 0000000..294d517 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue @@ -0,0 +1,24 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue new file mode 100644 index 0000000..ab264a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue @@ -0,0 +1,40 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue new file mode 100644 index 0000000..b8454ec --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue @@ -0,0 +1,55 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts new file mode 100644 index 0000000..7c67b87 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts @@ -0,0 +1,3 @@ +export { default as AnalysisChartCard } from './analysis-chart-card.vue'; +export { default as AnalysisChartsTabs } from './analysis-charts-tabs.vue'; +export { default as AnalysisOverview } from './analysis-overview.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/index.ts new file mode 100644 index 0000000..bc74424 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/index.ts @@ -0,0 +1,3 @@ +export * from './analysis'; +export type * from './typing'; +export * from './workbench'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/typing.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/typing.ts new file mode 100644 index 0000000..937e02f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/typing.ts @@ -0,0 +1,50 @@ +import type { Component } from 'vue'; + +interface AnalysisOverviewItem { + icon: Component | string; + title?: string; + totalTitle: string; + totalValue: number; + value?: number; +} + +interface WorkbenchProjectItem { + /** 临时字段 */ + [key: string]: any; + color?: string; + content: string; + date: string; + group: string; + icon: Component | string; + title: string; + url?: string; +} + +interface WorkbenchTrendItem { + avatar: string; + content: string; + date: string; + title: string; +} + +interface WorkbenchTodoItem { + completed: boolean; + content: string; + date: string; + title: string; +} + +interface WorkbenchQuickNavItem { + color?: string; + icon: Component | string; + title: string; + url?: string; +} + +export type { + AnalysisOverviewItem, + WorkbenchProjectItem, + WorkbenchQuickNavItem, + WorkbenchTodoItem, + WorkbenchTrendItem, +}; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts new file mode 100644 index 0000000..76b7af1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts @@ -0,0 +1,5 @@ +export { default as WorkbenchHeader } from './workbench-header.vue'; +export { default as WorkbenchProject } from './workbench-project.vue'; +export { default as WorkbenchQuickNav } from './workbench-quick-nav.vue'; +export { default as WorkbenchTodo } from './workbench-todo.vue'; +export { default as WorkbenchTrends } from './workbench-trends.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue new file mode 100644 index 0000000..bf17a8a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue @@ -0,0 +1,48 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue new file mode 100644 index 0000000..da4569e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue @@ -0,0 +1,73 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue new file mode 100644 index 0000000..b37e225 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue @@ -0,0 +1,56 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue new file mode 100644 index 0000000..aaa83e9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue @@ -0,0 +1,63 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue new file mode 100644 index 0000000..4cc0955 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue @@ -0,0 +1,64 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.ts new file mode 100644 index 0000000..5f8e193 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.ts @@ -0,0 +1,25 @@ +interface FallbackProps { + /** + * 描述 + */ + description?: string; + /** + * @zh_CN 首页路由地址 + * @default / + */ + homePath?: string; + /** + * @zh_CN 默认显示的图片 + * @default pageNotFoundSvg + */ + image?: string; + /** + * @zh_CN 内置类型 + */ + status?: '403' | '404' | '500' | 'coming-soon' | 'offline'; + /** + * @zh_CN 页面提示语 + */ + title?: string; +} +export type { FallbackProps }; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.vue new file mode 100644 index 0000000..153a516 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/fallback.vue @@ -0,0 +1,164 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue new file mode 100644 index 0000000..e390867 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue @@ -0,0 +1,151 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue new file mode 100644 index 0000000..385cf6a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue @@ -0,0 +1,154 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue new file mode 100644 index 0000000..97326b7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue @@ -0,0 +1,215 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue new file mode 100644 index 0000000..e511c43 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue @@ -0,0 +1,262 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue new file mode 100644 index 0000000..9e50b19 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue @@ -0,0 +1,112 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/warning.svg b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/warning.svg new file mode 100644 index 0000000..951a905 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/icons/warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/index.ts new file mode 100644 index 0000000..f56f665 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/fallback/index.ts @@ -0,0 +1,2 @@ +export type * from './fallback'; +export { default as Fallback } from './fallback.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/index.ts new file mode 100644 index 0000000..8b1db55 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/index.ts @@ -0,0 +1,5 @@ +export * from './about'; +export * from './authentication'; +export * from './dashboard'; +export * from './fallback'; +export * from './profile'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/base-setting.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/base-setting.vue new file mode 100644 index 0000000..15fc97b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/base-setting.vue @@ -0,0 +1,58 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/index.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/index.ts new file mode 100644 index 0000000..209a2e8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/index.ts @@ -0,0 +1,6 @@ +export { default as ProfileBaseSetting } from './base-setting.vue'; +export { default as ProfileNotificationSetting } from './notification-setting.vue'; +export { default as ProfilePasswordSetting } from './password-setting.vue'; +export { default as Profile } from './profile.vue'; +export { default as ProfileSecuritySetting } from './security-setting.vue'; +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/notification-setting.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/notification-setting.vue new file mode 100644 index 0000000..9a3e488 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/notification-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/password-setting.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/password-setting.vue new file mode 100644 index 0000000..06b9b3d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/password-setting.vue @@ -0,0 +1,59 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/profile.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/profile.vue new file mode 100644 index 0000000..1daa21a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/profile.vue @@ -0,0 +1,62 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/security-setting.vue b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/security-setting.vue new file mode 100644 index 0000000..9a3e488 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/security-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/types.ts b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/types.ts new file mode 100644 index 0000000..e600bde --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/src/ui/profile/types.ts @@ -0,0 +1,21 @@ +import type { BasicUserInfo } from '@vben/types'; + +export interface Props { + title?: string; + userInfo: BasicUserInfo | null; + tabs: { + label: string; + value: string; + }[]; +} + +export interface FormSchemaItem { + description: string; + fieldName: string; + label: string; + value: boolean; +} + +export interface SettingProps { + formSchema: FormSchemaItem[]; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/common-ui/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/common-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/common-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/README.md b/deploy/fba/fba-ui-src/packages/effects/hooks/README.md new file mode 100644 index 0000000..54d9ff7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/README.md @@ -0,0 +1,19 @@ +# @vben/hooks + +用于多个 `app` 公用的 hook,继承了 `@vben/hooks` 的所有能力。业务上有通用 hooks 可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/hooks +``` + +### 使用 + +```ts +import { useNamespace } from '@vben/hooks'; +``` diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/package.json b/deploy/fba/fba-ui-src/packages/effects/hooks/package.json new file mode 100644 index 0000000..e44e379 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/package.json @@ -0,0 +1,33 @@ +{ + "name": "@vben/hooks", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/hooks" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:", + "watermark-js-plus": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/index.ts new file mode 100644 index 0000000..c189fd7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/index.ts @@ -0,0 +1,9 @@ +export * from './use-app-config'; +export * from './use-content-maximize'; +export * from './use-design-tokens'; +export * from './use-hover-toggle'; +export * from './use-pagination'; +export * from './use-refresh'; +export * from './use-tabs'; +export * from './use-watermark'; +export * from '@vben-core/composables'; diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-app-config.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-app-config.ts new file mode 100644 index 0000000..41f383c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-app-config.ts @@ -0,0 +1,36 @@ +import type { + ApplicationConfig, + VbenAdminProAppConfigRaw, +} from '@vben/types/global'; + +/** + * 由 vite-inject-app-config 注入的全局配置 + */ +export function useAppConfig( + env: Record, + isProduction: boolean, +): ApplicationConfig { + // 生产环境下,直接使用 window._VBEN_ADMIN_PRO_APP_CONF_ 全局变量 + const config = isProduction + ? window._VBEN_ADMIN_PRO_APP_CONF_ + : (env as VbenAdminProAppConfigRaw); + + const { + VITE_GLOB_API_URL, + VITE_GLOB_AUTH_DINGDING_CORP_ID, + VITE_GLOB_AUTH_DINGDING_CLIENT_ID, + } = config; + + const applicationConfig: ApplicationConfig = { + apiURL: VITE_GLOB_API_URL, + auth: {}, + }; + if (VITE_GLOB_AUTH_DINGDING_CORP_ID && VITE_GLOB_AUTH_DINGDING_CLIENT_ID) { + applicationConfig.auth.dingding = { + clientId: VITE_GLOB_AUTH_DINGDING_CLIENT_ID, + corpId: VITE_GLOB_AUTH_DINGDING_CORP_ID, + }; + } + + return applicationConfig; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-content-maximize.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-content-maximize.ts new file mode 100644 index 0000000..77d1fab --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-content-maximize.ts @@ -0,0 +1,24 @@ +import { updatePreferences, usePreferences } from '@vben/preferences'; +/** + * 主体区域最大化 + */ +export function useContentMaximize() { + const { contentIsMaximize } = usePreferences(); + + function toggleMaximize() { + const isMaximize = contentIsMaximize.value; + + updatePreferences({ + header: { + hidden: !isMaximize, + }, + sidebar: { + hidden: !isMaximize, + }, + }); + } + return { + contentIsMaximize, + toggleMaximize, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-design-tokens.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-design-tokens.ts new file mode 100644 index 0000000..535efa5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-design-tokens.ts @@ -0,0 +1,421 @@ +import { reactive, watch } from 'vue'; + +import { preferences, usePreferences } from '@vben/preferences'; +import { convertToRgb, updateCSSVariables } from '@vben/utils'; + +/** + * 用于适配各个框架的设计系统 + */ + +export function useAntdDesignTokens() { + const rootStyles = getComputedStyle(document.documentElement); + + const tokens = reactive({ + borderRadius: '' as any, + colorBgBase: '', + colorBgContainer: '', + colorBgElevated: '', + colorBgLayout: '', + colorBgMask: '', + colorBorder: '', + colorBorderSecondary: '', + colorError: '', + colorInfo: '', + colorPrimary: '', + colorSuccess: '', + colorTextBase: '', + colorWarning: '', + zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡 + }); + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = rootStyles.getPropertyValue(variable); + return isColor ? `hsl(${value})` : value; + }; + + watch( + () => preferences.theme, + () => { + tokens.colorPrimary = getCssVariableValue('--primary'); + + tokens.colorInfo = getCssVariableValue('--primary'); + + tokens.colorError = getCssVariableValue('--destructive'); + + tokens.colorWarning = getCssVariableValue('--warning'); + + tokens.colorSuccess = getCssVariableValue('--success'); + + tokens.colorTextBase = getCssVariableValue('--foreground'); + + getCssVariableValue('--primary-foreground'); + + tokens.colorBorderSecondary = tokens.colorBorder = + getCssVariableValue('--border'); + + tokens.colorBgElevated = getCssVariableValue('--popover'); + + tokens.colorBgContainer = getCssVariableValue('--card'); + + tokens.colorBgBase = getCssVariableValue('--background'); + + const radius = Number.parseFloat(getCssVariableValue('--radius', false)); + // 1rem = 16px + tokens.borderRadius = radius * 16; + + tokens.colorBgLayout = getCssVariableValue('--background-deep'); + tokens.colorBgMask = getCssVariableValue('--overlay'); + }, + { immediate: true }, + ); + + return { + tokens, + }; +} + +export function useNaiveDesignTokens() { + const rootStyles = getComputedStyle(document.documentElement); + + const commonTokens = reactive({ + baseColor: '', + bodyColor: '', + borderColor: '', + borderRadius: '', + cardColor: '', + dividerColor: '', + errorColor: '', + errorColorHover: '', + errorColorPressed: '', + errorColorSuppl: '', + invertedColor: '', + modalColor: '', + popoverColor: '', + primaryColor: '', + primaryColorHover: '', + primaryColorPressed: '', + primaryColorSuppl: '', + successColor: '', + successColorHover: '', + successColorPressed: '', + successColorSuppl: '', + tableColor: '', + textColorBase: '', + warningColor: '', + warningColorHover: '', + warningColorPressed: '', + warningColorSuppl: '', + }); + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = rootStyles.getPropertyValue(variable); + return isColor ? convertToRgb(`hsl(${value})`) : value; + }; + + watch( + () => preferences.theme, + () => { + commonTokens.primaryColor = getCssVariableValue('--primary'); + commonTokens.primaryColorHover = getCssVariableValue('--primary-600'); + commonTokens.primaryColorPressed = getCssVariableValue('--primary-700'); + commonTokens.primaryColorSuppl = getCssVariableValue('--primary-800'); + + commonTokens.errorColor = getCssVariableValue('--destructive'); + commonTokens.errorColorHover = getCssVariableValue('--destructive-600'); + commonTokens.errorColorPressed = getCssVariableValue('--destructive-700'); + commonTokens.errorColorSuppl = getCssVariableValue('--destructive-800'); + + commonTokens.warningColor = getCssVariableValue('--warning'); + commonTokens.warningColorHover = getCssVariableValue('--warning-600'); + commonTokens.warningColorPressed = getCssVariableValue('--warning-700'); + commonTokens.warningColorSuppl = getCssVariableValue('--warning-800'); + + commonTokens.successColor = getCssVariableValue('--success'); + commonTokens.successColorHover = getCssVariableValue('--success-600'); + commonTokens.successColorPressed = getCssVariableValue('--success-700'); + commonTokens.successColorSuppl = getCssVariableValue('--success-800'); + + commonTokens.textColorBase = getCssVariableValue('--foreground'); + + commonTokens.baseColor = getCssVariableValue('--primary-foreground'); + + commonTokens.dividerColor = commonTokens.borderColor = + getCssVariableValue('--border'); + + commonTokens.modalColor = commonTokens.popoverColor = + getCssVariableValue('--popover'); + + commonTokens.tableColor = commonTokens.cardColor = + getCssVariableValue('--card'); + + commonTokens.bodyColor = getCssVariableValue('--background'); + commonTokens.invertedColor = getCssVariableValue('--background-deep'); + + commonTokens.borderRadius = getCssVariableValue('--radius', false); + }, + { immediate: true }, + ); + return { + commonTokens, + }; +} + +export function useElementPlusDesignTokens() { + const { isDark } = usePreferences(); + const rootStyles = getComputedStyle(document.documentElement); + + const getCssVariableValueRaw = (variable: string) => { + return rootStyles.getPropertyValue(variable); + }; + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = getCssVariableValueRaw(variable); + return isColor ? convertToRgb(`hsl(${value})`) : value; + }; + + watch( + () => preferences.theme, + () => { + const background = getCssVariableValue('--background'); + const border = getCssVariableValue('--border'); + const accent = getCssVariableValue('--accent'); + + const variables: Record = { + '--el-bg-color': background, + '--el-bg-color-overlay': getCssVariableValue('--popover'), + '--el-bg-color-page': getCssVariableValue('--background-deep'), + '--el-border-color': border, + '--el-border-color-dark': border, + '--el-border-color-extra-light': border, + '--el-border-color-hover': accent, + '--el-border-color-light': border, + '--el-border-color-lighter': border, + + '--el-border-radius-base': getCssVariableValue('--radius', false), + '--el-color-danger': getCssVariableValue('--destructive-500'), + '--el-color-danger-dark-2': isDark.value + ? getCssVariableValue('--destructive-400') + : getCssVariableValue('--destructive-600'), + '--el-color-danger-light-3': isDark.value + ? getCssVariableValue('--destructive-600') + : getCssVariableValue('--destructive-400'), + '--el-color-danger-light-5': isDark.value + ? getCssVariableValue('--destructive-700') + : getCssVariableValue('--destructive-300'), + '--el-color-danger-light-7': isDark.value + ? getCssVariableValue('--destructive-800') + : getCssVariableValue('--destructive-200'), + '--el-color-danger-light-8': isDark.value + ? getCssVariableValue('--destructive-900') + : getCssVariableValue('--destructive-100'), + '--el-color-danger-light-9': isDark.value + ? getCssVariableValue('--destructive-950') + : getCssVariableValue('--destructive-50'), + + '--el-color-error': getCssVariableValue('--destructive-500'), + '--el-color-error-dark-2': isDark.value + ? getCssVariableValue('--destructive-400') + : getCssVariableValue('--destructive-600'), + '--el-color-error-light-3': isDark.value + ? getCssVariableValue('--destructive-600') + : getCssVariableValue('--destructive-400'), + '--el-color-error-light-5': isDark.value + ? getCssVariableValue('--destructive-700') + : getCssVariableValue('--destructive-300'), + '--el-color-error-light-7': isDark.value + ? getCssVariableValue('--destructive-800') + : getCssVariableValue('--destructive-200'), + '--el-color-error-light-8': isDark.value + ? getCssVariableValue('--destructive-900') + : getCssVariableValue('--destructive-100'), + '--el-color-error-light-9': isDark.value + ? getCssVariableValue('--destructive-950') + : getCssVariableValue('--destructive-50'), + + '--el-color-info-light-5': border, + '--el-color-info-light-8': border, + '--el-color-info-light-9': getCssVariableValue('--info'), // getCssVariableValue('--secondary'), + + '--el-color-primary': getCssVariableValue('--primary-500'), + '--el-color-primary-dark-2': isDark.value + ? getCssVariableValue('--primary-400') + : getCssVariableValue('--primary-600'), + '--el-color-primary-light-3': isDark.value + ? getCssVariableValue('--primary-600') + : getCssVariableValue('--primary-400'), + '--el-color-primary-light-5': isDark.value + ? getCssVariableValue('--primary-700') + : getCssVariableValue('--primary-300'), + '--el-color-primary-light-7': isDark.value + ? getCssVariableValue('--primary-800') + : getCssVariableValue('--primary-200'), + '--el-color-primary-light-8': isDark.value + ? getCssVariableValue('--primary-900') + : getCssVariableValue('--primary-100'), + '--el-color-primary-light-9': isDark.value + ? getCssVariableValue('--primary-950') + : getCssVariableValue('--primary-50'), + + '--el-color-success': getCssVariableValue('--success-500'), + '--el-color-success-dark-2': isDark.value + ? getCssVariableValue('--success-400') + : getCssVariableValue('--success-600'), + '--el-color-success-light-3': isDark.value + ? getCssVariableValue('--success-600') + : getCssVariableValue('--success-400'), + '--el-color-success-light-5': isDark.value + ? getCssVariableValue('--success-700') + : getCssVariableValue('--success-300'), + '--el-color-success-light-7': isDark.value + ? getCssVariableValue('--success-800') + : getCssVariableValue('--success-200'), + '--el-color-success-light-8': isDark.value + ? getCssVariableValue('--success-900') + : getCssVariableValue('--success-100'), + '--el-color-success-light-9': isDark.value + ? getCssVariableValue('--success-950') + : getCssVariableValue('--success-50'), + + '--el-color-warning': getCssVariableValue('--warning-500'), + '--el-color-warning-dark-2': isDark.value + ? getCssVariableValue('--warning-400') + : getCssVariableValue('--warning-600'), + '--el-color-warning-light-3': isDark.value + ? getCssVariableValue('--warning-600') + : getCssVariableValue('--warning-400'), + '--el-color-warning-light-5': isDark.value + ? getCssVariableValue('--warning-700') + : getCssVariableValue('--warning-300'), + '--el-color-warning-light-7': isDark.value + ? getCssVariableValue('--warning-800') + : getCssVariableValue('--warning-200'), + '--el-color-warning-light-8': isDark.value + ? getCssVariableValue('--warning-900') + : getCssVariableValue('--warning-100'), + '--el-color-warning-light-9': isDark.value + ? getCssVariableValue('--warning-950') + : getCssVariableValue('--warning-50'), + + '--el-fill-color': getCssVariableValue('--accent'), + '--el-fill-color-blank': background, + '--el-fill-color-light': getCssVariableValue('--accent'), + '--el-fill-color-lighter': getCssVariableValue('--accent-lighter'), + + '--el-fill-color-dark': getCssVariableValue('--accent-dark'), + '--el-fill-color-darker': getCssVariableValue('--accent-darker'), + + // 解决ElLoading背景色问题 + '--el-mask-color': isDark.value + ? 'rgba(0,0,0,.8)' + : 'rgba(255,255,255,.9)', + + '--el-text-color-primary': getCssVariableValue('--foreground'), + + '--el-text-color-regular': getCssVariableValue('--foreground'), + }; + + updateCSSVariables(variables, `__vben_design_styles__`); + }, + { immediate: true }, + ); +} + +export function useTDesignDesignTokens() { + const { isDark } = usePreferences(); + const rootStyles = getComputedStyle(document.documentElement); + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = rootStyles.getPropertyValue(variable); + return isColor ? convertToRgb(`hsl(${value})`) : value; + }; + + /** + * 生成某个语义色对应的 TDesign 变量(语义变体 + 1~10 色阶)。 + * TDesign 的色阶在亮色模式下 1 最浅、10 最深,暗色模式下方向相反, + * 这里根据当前模式把 Vben 的 50~900 色板映射到对应方向。 + * @param tdName TDesign 颜色名,如 `brand`、`error` + * @param vbenName Vben 颜色名,如 `primary`、`destructive` + */ + const getColorTokens = (tdName: string, vbenName: string) => { + const dark = isDark.value; + const getColor = (level?: number) => + getCssVariableValue( + level === undefined ? `--${vbenName}` : `--${vbenName}-${level}`, + ); + + // 亮色模式 1 最浅、10 最深;暗色模式相反 + const lightScale = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; + const scaleLevels = dark ? [...lightScale].toReversed() : lightScale; + const scale: Record = {}; + scaleLevels.forEach((level, index) => { + scale[`--td-${tdName}-color-${index + 1}`] = getColor(level); + }); + + return { + [`--td-${tdName}-color`]: getColor(), + [`--td-${tdName}-color-active`]: getColor(dark ? 300 : 700), + [`--td-${tdName}-color-disabled`]: getColor(dark ? 800 : 200), + [`--td-${tdName}-color-focus`]: getColor(dark ? 900 : 100), + [`--td-${tdName}-color-hover`]: getColor(dark ? 400 : 600), + [`--td-${tdName}-color-light`]: getColor(dark ? 950 : 50), + [`--td-${tdName}-color-light-hover`]: getColor(dark ? 900 : 100), + ...scale, + }; + }; + + watch( + () => preferences.theme, + () => { + const variables: Record = { + // 品牌色(主题色)、功能色 + ...getColorTokens('brand', 'primary'), + ...getColorTokens('error', 'destructive'), + ...getColorTokens('warning', 'warning'), + ...getColorTokens('success', 'success'), + + // 文字颜色 + '--td-text-color-anti': getCssVariableValue('--primary-foreground'), + '--td-text-color-brand': getCssVariableValue('--primary'), + '--td-text-color-disabled': getCssVariableValue('--muted-foreground'), + '--td-text-color-link': getCssVariableValue('--primary'), + '--td-text-color-placeholder': getCssVariableValue( + '--input-placeholder', + ), + '--td-text-color-primary': getCssVariableValue('--foreground'), + '--td-text-color-secondary': getCssVariableValue('--muted-foreground'), + + // 背景颜色 + '--td-bg-color-component': getCssVariableValue('--accent'), + '--td-bg-color-component-active': + getCssVariableValue('--accent-darker'), + '--td-bg-color-component-disabled': getCssVariableValue('--muted'), + '--td-bg-color-component-hover': getCssVariableValue('--accent-hover'), + '--td-bg-color-container': getCssVariableValue('--background'), + '--td-bg-color-container-active': + getCssVariableValue('--accent-darker'), + '--td-bg-color-container-hover': getCssVariableValue('--accent'), + '--td-bg-color-container-select': getCssVariableValue('--accent'), + '--td-bg-color-page': getCssVariableValue('--background-deep'), + '--td-bg-color-secondarycontainer': getCssVariableValue('--card'), + + // 边框颜色 + '--td-border-level-1-color': getCssVariableValue('--border'), + '--td-border-level-2-color': getCssVariableValue('--border'), + '--td-component-border': getCssVariableValue('--border'), + '--td-component-stroke': getCssVariableValue('--border'), + + // 圆角 + '--td-radius-default': getCssVariableValue('--radius', false), + }; + + // TDesign 将亮/暗变量定义在 `:root[theme-mode='light'|'dark']` 下, + // 需要写入相同优先级的选择器才能覆盖默认值 + const selector = isDark.value + ? `:root[theme-mode='dark']` + : `:root[theme-mode='light']`; + + updateCSSVariables(variables, `__vben_design_tdesign_styles__`, selector); + }, + { immediate: true }, + ); +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-hover-toggle.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-hover-toggle.ts new file mode 100644 index 0000000..e0280da --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-hover-toggle.ts @@ -0,0 +1,163 @@ +import type { Arrayable, MaybeElementRef } from '@vueuse/core'; + +import type { Ref } from 'vue'; + +import { computed, effectScope, ref, unref, watch } from 'vue'; + +import { isFunction } from '@vben/utils'; + +import { tryOnScopeDispose, useElementHover } from '@vueuse/core'; + +interface HoverDelayOptions { + /** 鼠标进入延迟时间 */ + enterDelay?: (() => number) | number; + /** 鼠标离开延迟时间 */ + leaveDelay?: (() => number) | number; +} + +const DEFAULT_LEAVE_DELAY = 500; // 鼠标离开延迟时间,默认为 500ms +const DEFAULT_ENTER_DELAY = 0; // 鼠标进入延迟时间,默认为 0(立即响应) + +/** + * 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false + * @param refElement 所有需要检测的元素。支持单个元素、元素数组或响应式引用的元素数组。如果鼠标在任何一个元素内部都会返回 true + * @param delay 延迟更新状态的时间,可以是数字或包含进入/离开延迟的配置对象 + * @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用 + */ +export function useHoverToggle( + refElement: Arrayable | Ref, + delay: (() => number) | HoverDelayOptions | number = DEFAULT_LEAVE_DELAY, +) { + // 兼容旧版本API + const normalizedOptions: HoverDelayOptions = + typeof delay === 'number' || isFunction(delay) + ? { enterDelay: DEFAULT_ENTER_DELAY, leaveDelay: delay } + : { + enterDelay: DEFAULT_ENTER_DELAY, + leaveDelay: DEFAULT_LEAVE_DELAY, + ...delay, + }; + + const value = ref(false); + const enterTimer = ref | undefined>(); + const leaveTimer = ref | undefined>(); + const hoverScopes = ref[]>([]); + + // 使用计算属性包装 refElement,使其响应式变化 + const refs = computed(() => { + const raw = unref(refElement); + if (raw === null) return []; + return Array.isArray(raw) ? raw : [raw]; + }); + // 存储所有 hover 状态 + const isHovers = ref>>([]); + + // 更新 hover 监听的函数 + function updateHovers() { + // 停止并清理之前的作用域 + hoverScopes.value.forEach((scope) => scope.stop()); + hoverScopes.value = []; + + isHovers.value = refs.value.map((refEle) => { + if (!refEle) { + return ref(false); + } + const eleRef = computed(() => { + const ele = unref(refEle); + return ele instanceof Element ? ele : (ele?.$el as Element); + }); + + // 为每个元素创建独立的作用域 + const scope = effectScope(); + const hoverRef = scope.run(() => useElementHover(eleRef)) || ref(false); + hoverScopes.value.push(scope); + + return hoverRef; + }); + } + + // 监听元素数量变化,避免过度执行 + const elementsCount = computed(() => { + const raw = unref(refElement); + if (raw === null) return 0; + return Array.isArray(raw) ? raw.length : 1; + }); + + // 初始设置 + updateHovers(); + + // 只在元素数量变化时重新设置监听器 + const stopWatcher = watch(elementsCount, updateHovers, { deep: false }); + + const isOutsideAll = computed(() => isHovers.value.every((v) => !v.value)); + + function clearTimers() { + if (enterTimer.value) { + clearTimeout(enterTimer.value); + enterTimer.value = undefined; + } + if (leaveTimer.value) { + clearTimeout(leaveTimer.value); + leaveTimer.value = undefined; + } + } + + function setValueDelay(val: boolean) { + clearTimers(); + + if (val) { + // 鼠标进入 + const enterDelay = normalizedOptions.enterDelay ?? DEFAULT_ENTER_DELAY; + const delayTime = isFunction(enterDelay) ? enterDelay() : enterDelay; + + if (delayTime <= 0) { + value.value = true; + } else { + enterTimer.value = setTimeout(() => { + value.value = true; + enterTimer.value = undefined; + }, delayTime); + } + } else { + // 鼠标离开 + const leaveDelay = normalizedOptions.leaveDelay ?? DEFAULT_LEAVE_DELAY; + const delayTime = isFunction(leaveDelay) ? leaveDelay() : leaveDelay; + + if (delayTime <= 0) { + value.value = false; + } else { + leaveTimer.value = setTimeout(() => { + value.value = false; + leaveTimer.value = undefined; + }, delayTime); + } + } + } + + const hoverWatcher = watch( + isOutsideAll, + (val) => { + setValueDelay(!val); + }, + { immediate: true }, + ); + + const controller = { + enable() { + hoverWatcher.resume(); + }, + disable() { + hoverWatcher.pause(); + }, + }; + + tryOnScopeDispose(() => { + clearTimers(); + // 停止监听器 + stopWatcher(); + // 停止所有剩余的作用域 + hoverScopes.value.forEach((scope) => scope.stop()); + }); + + return [value, controller] as [typeof value, typeof controller]; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-pagination.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-pagination.ts new file mode 100644 index 0000000..0115606 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-pagination.ts @@ -0,0 +1,72 @@ +import type { Ref } from 'vue'; + +import { computed, ref, unref, watch } from 'vue'; + +/** + * Paginates an array of items + * @param list The array to paginate + * @param pageNo The current page number (1-based) + * @param pageSize Number of items per page + * @returns Paginated array slice + * @throws {Error} If pageNo or pageSize are invalid + */ +function pagination(list: T[], pageNo: number, pageSize: number): T[] { + if (pageNo < 1) throw new Error('Page number must be positive'); + if (pageSize < 1) throw new Error('Page size must be positive'); + + const offset = (pageNo - 1) * Number(pageSize); + const ret = + offset + pageSize >= list.length + ? list.slice(offset) + : list.slice(offset, offset + pageSize); + return ret; +} + +export function usePagination( + list: Ref, + pageSize: number, + totalChangeToFirstPage = true, +) { + const currentPage = ref(1); + const pageSizeRef = ref(pageSize); + + const totalPages = computed(() => + Math.ceil(unref(list).length / unref(pageSizeRef)), + ); + + const paginationList = computed(() => { + return pagination(unref(list), unref(currentPage), unref(pageSizeRef)); + }); + + const total = computed(() => { + return unref(list).length; + }); + + if (totalChangeToFirstPage) { + watch(total, () => { + setCurrentPage(1); + }); + } + + function setCurrentPage(page: number) { + if (page === 1 && unref(totalPages) === 0) { + currentPage.value = 1; + } else { + if (page < 1 || page > unref(totalPages)) { + throw new Error('Invalid page number'); + } + currentPage.value = page; + } + } + + function setPageSize(pageSize: number) { + if (pageSize < 1) { + throw new Error('Page size must be positive'); + } + pageSizeRef.value = pageSize; + // Reset to first page to prevent invalid state + currentPage.value = 1; + } + + return { setCurrentPage, total, setPageSize, paginationList, currentPage }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-refresh.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-refresh.ts new file mode 100644 index 0000000..b3a5cae --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-refresh.ts @@ -0,0 +1,16 @@ +import { useRouter } from 'vue-router'; + +import { useTabbarStore } from '@vben/stores'; + +export function useRefresh() { + const router = useRouter(); + const tabbarStore = useTabbarStore(); + + async function refresh() { + await tabbarStore.refresh(router); + } + + return { + refresh, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-tabs.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-tabs.ts new file mode 100644 index 0000000..978409e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-tabs.ts @@ -0,0 +1,133 @@ +import type { ComputedRef } from 'vue'; +import type { RouteLocationNormalized } from 'vue-router'; + +import { useRoute, useRouter } from 'vue-router'; + +import { useTabbarStore } from '@vben/stores'; + +export function useTabs() { + const router = useRouter(); + const route = useRoute(); + const tabbarStore = useTabbarStore(); + + async function closeLeftTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeLeftTabs(tab || route); + } + + async function closeAllTabs() { + await tabbarStore.closeAllTabs(router); + } + + async function closeRightTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeRightTabs(tab || route); + } + + async function closeOtherTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeOtherTabs(tab || route); + } + + async function closeCurrentTab(tab?: RouteLocationNormalized) { + await tabbarStore.closeTab(tab || route, router); + } + + async function pinTab(tab?: RouteLocationNormalized) { + await tabbarStore.pinTab(tab || route); + } + + async function unpinTab(tab?: RouteLocationNormalized) { + await tabbarStore.unpinTab(tab || route); + } + + async function toggleTabPin(tab?: RouteLocationNormalized) { + await tabbarStore.toggleTabPin(tab || route); + } + + async function refreshTab(name?: string) { + await tabbarStore.refresh(name || router); + } + + async function openTabInNewWindow(tab?: RouteLocationNormalized) { + await tabbarStore.openTabInNewWindow(tab || route, router); + } + + async function closeTabByKey(key: string) { + await tabbarStore.closeTabByKey(key, router); + } + + /** + * 设置当前标签页的标题 + * + * @description 支持设置静态标题字符串或动态计算标题 + * @description 动态标题会在每次渲染时重新计算,适用于多语言或状态相关的标题 + * + * @param title - 标题内容 + * - 静态标题: 直接传入字符串 + * - 动态标题: 传入 ComputedRef + * + * @example + * // 静态标题 + * setTabTitle('标签页') + * + * // 动态标题(多语言) + * setTabTitle(computed(() => t('page.title'))) + */ + async function setTabTitle(title: ComputedRef | string) { + tabbarStore.setUpdateTime(); + await tabbarStore.setTabTitle(route, title); + } + + async function resetTabTitle() { + tabbarStore.setUpdateTime(); + await tabbarStore.resetTabTitle(route); + } + + /** + * 获取操作是否禁用 + * @param tab + */ + function getTabDisableState(tab: RouteLocationNormalized = route) { + const tabs = tabbarStore.getTabs; + const affixTabs = tabbarStore.affixTabs; + const index = tabs.findIndex((item) => item.path === tab.path); + + const disabled = tabs.length <= 1; + + const { meta } = tab; + const affixTab = meta?.affixTab ?? false; + const isCurrentTab = route.path === tab.path; + + // 当前处于最左侧或者减去固定标签页的数量等于0 + const disabledCloseLeft = + index === 0 || index - affixTabs.length <= 0 || !isCurrentTab; + + const disabledCloseRight = !isCurrentTab || index === tabs.length - 1; + + const disabledCloseOther = + disabled || !isCurrentTab || tabs.length - affixTabs.length <= 1; + return { + disabledCloseAll: disabled, + disabledCloseCurrent: !!affixTab || disabled, + disabledCloseLeft, + disabledCloseOther, + disabledCloseRight, + disabledRefresh: !isCurrentTab, + }; + } + + return { + closeAllTabs, + closeCurrentTab, + closeLeftTabs, + closeOtherTabs, + closeRightTabs, + closeTabByKey, + getTabDisableState, + openTabInNewWindow, + pinTab, + refreshTab, + resetTabTitle, + setTabTitle, + toggleTabPin, + unpinTab, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-watermark.ts b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-watermark.ts new file mode 100644 index 0000000..8484a08 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/src/use-watermark.ts @@ -0,0 +1,84 @@ +import type { Watermark, WatermarkOptions } from 'watermark-js-plus'; + +import { nextTick, onUnmounted, readonly, ref } from 'vue'; + +const watermark = ref(); +const unmountedHooked = ref(false); +const cachedOptions = ref>({ + advancedStyle: { + colorStops: [ + { + color: 'gray', + offset: 0, + }, + { + color: 'gray', + offset: 1, + }, + ], + type: 'linear', + }, + // fontSize: '20px', + content: '', + contentType: 'multi-line-text', + globalAlpha: 0.25, + gridLayoutOptions: { + cols: 2, + gap: [20, 20], + matrix: [ + [1, 0], + [0, 1], + ], + rows: 2, + }, + height: 200, + layout: 'grid', + rotate: 30, + width: 160, +}); + +export function useWatermark() { + async function initWatermark(options: Partial) { + const { Watermark } = await import('watermark-js-plus'); + + cachedOptions.value = { + ...cachedOptions.value, + ...options, + }; + watermark.value = new Watermark(cachedOptions.value); + await watermark.value?.create(); + } + + async function updateWatermark(options: Partial) { + if (watermark.value) { + await nextTick(); + await watermark.value?.changeOptions({ + ...cachedOptions.value, + ...options, + }); + } else { + await initWatermark(options); + } + } + + function destroyWatermark() { + if (watermark.value) { + watermark.value.destroy(); + watermark.value = undefined; + } + } + + // 只在第一次调用时注册卸载钩子,防止重复注册以致于在路由切换时销毁了水印 + if (!unmountedHooked.value) { + unmountedHooked.value = true; + onUnmounted(() => { + destroyWatermark(); + }); + } + + return { + destroyWatermark, + updateWatermark, + watermark: readonly(watermark), + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/hooks/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/hooks/tsconfig.json new file mode 100644 index 0000000..b13aa7a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/hooks/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "compilerOptions": { + "types": ["vite/client", "@vben/types/global"] + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/package.json b/deploy/fba/fba-ui-src/packages/effects/layouts/package.json new file mode 100644 index 0000000..e59c33f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/package.json @@ -0,0 +1,44 @@ +{ + "name": "@vben/layouts", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/layouts" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/design": "workspace:*", + "@vben-core/form-ui": "workspace:*", + "@vben-core/layout-ui": "workspace:*", + "@vben-core/menu-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/tabs-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/authentication.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/authentication.vue new file mode 100644 index 0000000..84ae252 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/authentication.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/form.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/form.vue new file mode 100644 index 0000000..ddd756c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/form.vue @@ -0,0 +1,38 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/icons/slogan.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/icons/slogan.vue new file mode 100644 index 0000000..82cc119 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/icons/slogan.vue @@ -0,0 +1,4568 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/index.ts new file mode 100644 index 0000000..d7c1c29 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/index.ts @@ -0,0 +1,2 @@ +export { default as AuthPageLayout } from './authentication.vue'; +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/toolbar.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/toolbar.vue new file mode 100644 index 0000000..fefe374 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/toolbar.vue @@ -0,0 +1,49 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/types.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/types.ts new file mode 100644 index 0000000..c4c1c7f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/authentication/types.ts @@ -0,0 +1 @@ +export type ToolbarType = 'color' | 'language' | 'layout' | 'theme'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/README.md b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/README.md new file mode 100644 index 0000000..b1266ea --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/README.md @@ -0,0 +1,7 @@ +## layout + +### header + +- 支持N个自定义插槽,命名方式:header-right-n,header-left-n +- header-left-n ,排序方式:0-19 ,breadcrumb 21-x +- header-right-n ,排序方式:0-49,global-search,51-59,theme-toggle,61-69,language-toggle,71-79,fullscreen,81-89,notification,91-149,user-dropdown,151-x diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content-spinner.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content-spinner.vue new file mode 100644 index 0000000..e97e4b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content-spinner.vue @@ -0,0 +1,12 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content.vue new file mode 100644 index 0000000..2d6bb91 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/content.vue @@ -0,0 +1,88 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/index.ts new file mode 100644 index 0000000..8372b20 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/index.ts @@ -0,0 +1,2 @@ +export { default as LayoutContentSpinner } from './content-spinner.vue'; +export { default as LayoutContent } from './content.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/use-content-spinner.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/use-content-spinner.ts new file mode 100644 index 0000000..dfe3c53 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/content/use-content-spinner.ts @@ -0,0 +1,50 @@ +import { computed, ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { preferences } from '@vben/preferences'; + +function useContentSpinner() { + const spinning = ref(false); + const startTime = ref(0); + const router = useRouter(); + const minShowTime = 500; // 最小显示时间 + const enableLoading = computed(() => preferences.transition.loading); + + // 结束加载动画 + const onEnd = () => { + if (!enableLoading.value) { + return; + } + const processTime = performance.now() - startTime.value; + if (processTime < minShowTime) { + setTimeout(() => { + spinning.value = false; + }, minShowTime - processTime); + } else { + spinning.value = false; + } + }; + + // 路由前置守卫 + router.beforeEach((to) => { + if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { + return true; + } + startTime.value = performance.now(); + spinning.value = true; + return true; + }); + + // 路由后置守卫 + router.afterEach((to) => { + if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { + return true; + } + onEnd(); + return true; + }); + + return { spinning }; +} + +export { useContentSpinner }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/copyright.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/copyright.vue new file mode 100644 index 0000000..3004507 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/copyright.vue @@ -0,0 +1,48 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/index.ts new file mode 100644 index 0000000..e0620da --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/copyright/index.ts @@ -0,0 +1 @@ +export { default as Copyright } from './copyright.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/footer.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/footer.vue new file mode 100644 index 0000000..564c6c2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/footer.vue @@ -0,0 +1,11 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/index.ts new file mode 100644 index 0000000..7e149a2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/footer/index.ts @@ -0,0 +1 @@ +export { default as LayoutFooter } from './footer.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/header.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/header.vue new file mode 100644 index 0000000..1bc8d7f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/header.vue @@ -0,0 +1,225 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/index.ts new file mode 100644 index 0000000..44f0729 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/header/index.ts @@ -0,0 +1 @@ +export { default as LayoutHeader } from './header.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/index.ts new file mode 100644 index 0000000..b3a01cd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/index.ts @@ -0,0 +1 @@ +export { default as BasicLayout } from './layout.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/layout.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/layout.vue new file mode 100644 index 0000000..88951ea --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/layout.vue @@ -0,0 +1,441 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/extra-menu.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/extra-menu.vue new file mode 100644 index 0000000..5f194a7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/extra-menu.vue @@ -0,0 +1,41 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/index.ts new file mode 100644 index 0000000..72b87ad --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/index.ts @@ -0,0 +1,5 @@ +export { default as LayoutExtraMenu } from './extra-menu.vue'; +export { default as LayoutMenu } from './menu.vue'; +export { default as LayoutMixedMenu } from './mixed-menu.vue'; +export * from './use-extra-menu'; +export * from './use-mixed-menu'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/menu.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/menu.vue new file mode 100644 index 0000000..cbb5cfb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/menu.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/mixed-menu.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/mixed-menu.vue new file mode 100644 index 0000000..ae12cc9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/mixed-menu.vue @@ -0,0 +1,46 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-extra-menu.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-extra-menu.ts new file mode 100644 index 0000000..e2afb74 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-extra-menu.ts @@ -0,0 +1,133 @@ +import type { ComputedRef } from 'vue'; + +import type { MenuRecordRaw } from '@vben/types'; + +import { computed, ref, watch } from 'vue'; +import { useRoute } from 'vue-router'; + +import { preferences } from '@vben/preferences'; +import { useAccessStore } from '@vben/stores'; +import { findRootMenuByPath } from '@vben/utils'; + +import { useNavigation } from './use-navigation'; + +function useExtraMenu(useRootMenus?: ComputedRef) { + const accessStore = useAccessStore(); + const { navigation, willOpenedByWindow } = useNavigation(); + + const menus = computed(() => useRootMenus?.value ?? accessStore.accessMenus); + + /** 记录当前顶级菜单下哪个子菜单最后激活 */ + const defaultSubMap = new Map(); + const extraRootMenus = ref([]); + const route = useRoute(); + const extraMenus = ref([]); + const sidebarExtraVisible = ref(false); + const extraActiveMenu = ref(''); + const parentLevel = computed(() => + preferences.app.layout === 'header-mixed-nav' ? 1 : 0, + ); + + /** + * 选择混合菜单事件 + * @param menu + */ + const handleMixedMenuSelect = async (menu: MenuRecordRaw) => { + const _extraMenus = menu?.children ?? []; + const hasChildren = _extraMenus.length > 0; + + if (!willOpenedByWindow(menu.path)) { + extraMenus.value = _extraMenus ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + sidebarExtraVisible.value = hasChildren; + } + + if (!hasChildren) { + await navigation(menu.path); + } else if (preferences.sidebar.autoActivateChild) { + await navigation( + defaultSubMap.has(menu.path) + ? (defaultSubMap.get(menu.path) as string) + : menu.path, + ); + } + }; + + /** + * 选择默认菜单事件 + * @param menu + * @param rootMenu + */ + const handleDefaultSelect = async ( + menu: MenuRecordRaw, + rootMenu?: MenuRecordRaw, + ) => { + extraMenus.value = rootMenu?.children ?? extraRootMenus.value ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + + if (preferences.sidebar.expandOnHover) { + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + }; + + /** + * 侧边菜单鼠标移出事件 + */ + const handleSideMouseLeave = () => { + if (preferences.sidebar.expandOnHover) { + return; + } + + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus.value, + route.path, + ); + extraActiveMenu.value = rootMenuPath ?? findMenu?.path ?? ''; + extraMenus.value = rootMenu?.children ?? []; + }; + + const handleMenuMouseEnter = (menu: MenuRecordRaw) => { + if (!preferences.sidebar.expandOnHover) { + const { findMenu } = findRootMenuByPath(menus.value, menu.path); + extraMenus.value = findMenu?.children ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + }; + + function calcExtraMenus(path: string) { + const currentPath = route.meta?.activePath || path; + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus.value, + currentPath, + parentLevel.value, + ); + extraRootMenus.value = rootMenu?.children ?? []; + if (rootMenuPath) defaultSubMap.set(rootMenuPath, currentPath); + extraActiveMenu.value = rootMenuPath ?? findMenu?.path ?? ''; + extraMenus.value = rootMenu?.children ?? []; + if (preferences.sidebar.expandOnHover) { + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + } + + watch( + () => [route.path, preferences.app.layout], + ([path]) => { + calcExtraMenus(path || ''); + }, + { immediate: true }, + ); + + return { + extraActiveMenu, + extraMenus, + handleDefaultSelect, + handleMenuMouseEnter, + handleMixedMenuSelect, + handleSideMouseLeave, + sidebarExtraVisible, + }; +} + +export { useExtraMenu }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts new file mode 100644 index 0000000..ae217fb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts @@ -0,0 +1,173 @@ +import type { MenuRecordRaw } from '@vben/types'; + +import { computed, onBeforeMount, ref, watch } from 'vue'; +import { useRoute } from 'vue-router'; + +import { preferences, usePreferences } from '@vben/preferences'; +import { useAccessStore } from '@vben/stores'; +import { findRootMenuByPath } from '@vben/utils'; + +import { useNavigation } from './use-navigation'; + +function useMixedMenu() { + const { navigation, willOpenedByWindow } = useNavigation(); + const accessStore = useAccessStore(); + const route = useRoute(); + const splitSideMenus = ref([]); + const rootMenuPath = ref(''); + const mixedRootMenuPath = ref(''); + const mixExtraMenus = ref([]); + /** 记录当前顶级菜单下哪个子菜单最后激活 */ + const defaultSubMap = new Map(); + const { isMixedNav, isHeaderMixedNav, isMobile } = usePreferences(); + + const needSplit = computed( + () => + !isMobile.value && + ((preferences.navigation.split && isMixedNav.value) || + isHeaderMixedNav.value), + ); + + const sidebarVisible = computed(() => { + const enableSidebar = preferences.sidebar.enable; + if (needSplit.value) { + return enableSidebar && splitSideMenus.value.length > 0; + } + return enableSidebar; + }); + const menus = computed(() => accessStore.accessMenus); + + /** + * 头部菜单 + */ + const headerMenus = computed(() => { + if (!needSplit.value) { + return menus.value; + } + return menus.value.map((item) => { + return { + ...item, + children: [], + }; + }); + }); + + /** + * 侧边菜单 + */ + const sidebarMenus = computed(() => { + return needSplit.value ? splitSideMenus.value : menus.value; + }); + + const mixHeaderMenus = computed(() => { + return isHeaderMixedNav.value ? sidebarMenus.value : headerMenus.value; + }); + + /** + * 侧边菜单激活路径 + */ + const sidebarActive = computed(() => { + return (route?.meta?.activePath as string) ?? route.path; + }); + + /** + * 头部菜单激活路径 + */ + const headerActive = computed(() => { + if (!needSplit.value) { + return route.meta?.activePath ?? route.path; + } + return rootMenuPath.value; + }); + + /** + * 菜单点击事件处理 + * @param key 菜单路径 + * @param mode 菜单模式 + */ + const handleMenuSelect = (key: string, mode?: string) => { + if (!needSplit.value || mode === 'vertical') { + navigation(key); + return; + } + const rootMenu = menus.value.find((item) => item.path === key); + const _splitSideMenus = rootMenu?.children ?? []; + + if (!willOpenedByWindow(key)) { + rootMenuPath.value = rootMenu?.path ?? ''; + splitSideMenus.value = _splitSideMenus; + } + + if (_splitSideMenus.length === 0) { + navigation(key); + } else if (rootMenu && preferences.sidebar.autoActivateChild) { + navigation( + defaultSubMap.has(rootMenu.path) + ? (defaultSubMap.get(rootMenu.path) as string) + : rootMenu.path, + ); + } + }; + + /** + * 侧边菜单展开事件 + * @param key 路由路径 + * @param parentsPath 父级路径 + */ + const handleMenuOpen = (key: string, parentsPath: string[]) => { + if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) { + navigation( + defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key, + ); + } + }; + + /** + * 计算侧边菜单 + * @param path 路由路径 + */ + function calcSideMenus(path: string = route.path) { + let { rootMenu } = findRootMenuByPath(menus.value, path); + if (!rootMenu) { + rootMenu = menus.value.find((item) => item.path === path); + } + const result = findRootMenuByPath(rootMenu?.children || [], path, 1); + mixedRootMenuPath.value = result.rootMenuPath ?? ''; + mixExtraMenus.value = result.rootMenu?.children ?? []; + rootMenuPath.value = rootMenu?.path ?? ''; + splitSideMenus.value = rootMenu?.children ?? []; + } + + watch( + () => route.path, + (path) => { + const currentPath = route?.meta?.activePath ?? route?.meta?.link ?? path; + if (willOpenedByWindow(currentPath)) { + return; + } + calcSideMenus(currentPath); + if (rootMenuPath.value) + defaultSubMap.set(rootMenuPath.value, currentPath); + }, + { immediate: true }, + ); + + // 初始化计算侧边菜单 + onBeforeMount(() => { + calcSideMenus(route.meta?.activePath || route.path); + }); + + return { + handleMenuSelect, + handleMenuOpen, + headerActive, + headerMenus, + sidebarActive, + sidebarMenus, + mixHeaderMenus, + mixExtraMenus, + sidebarVisible, + }; +} + +export { useMixedMenu }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-navigation.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-navigation.ts new file mode 100644 index 0000000..daa6dc7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/menu/use-navigation.ts @@ -0,0 +1,74 @@ +import type { RouteRecordNormalized } from 'vue-router'; + +import { useRouter } from 'vue-router'; + +import { isHttpUrl, openRouteInNewWindow, openWindow } from '@vben/utils'; + +function useNavigation() { + const router = useRouter(); + const routeMetaMap = new Map(); + + // 初始化路由映射 + const initRouteMetaMap = () => { + const routes = router.getRoutes(); + routes.forEach((route) => { + routeMetaMap.set(route.path, route); + }); + }; + + initRouteMetaMap(); + + // 监听路由变化 + router.afterEach(() => { + initRouteMetaMap(); + }); + + // 检查是否应该在新窗口打开 + const shouldOpenInNewWindow = (path: string): boolean => { + if (isHttpUrl(path)) { + return true; + } + const route = routeMetaMap.get(path); + // 如果有外链或者设置了在新窗口打开,返回 true + return !!(route?.meta?.link || route?.meta?.openInNewWindow); + }; + + const resolveHref = (path: string): string => { + return router.resolve(path).href; + }; + + const navigation = async (path: string) => { + try { + const route = routeMetaMap.get(path); + const { openInNewWindow = false, query = {}, link } = route?.meta ?? {}; + + // 检查是否有外链 + if (link && typeof link === 'string') { + openWindow(link, { target: '_blank' }); + return; + } + + if (isHttpUrl(path)) { + openWindow(path, { target: '_blank' }); + } else if (openInNewWindow) { + openRouteInNewWindow(resolveHref(path)); + } else { + await router.push({ + path, + query, + }); + } + } catch (error) { + console.error('Navigation failed:', error); + throw error; + } + }; + + const willOpenedByWindow = (path: string) => { + return shouldOpenInNewWindow(path); + }; + + return { navigation, willOpenedByWindow }; +} + +export { useNavigation }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/index.ts new file mode 100644 index 0000000..5cc2479 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/index.ts @@ -0,0 +1,2 @@ +export { default as LayoutTabbar } from './tabbar.vue'; +export * from './use-tabbar'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/tabbar.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/tabbar.vue new file mode 100644 index 0000000..367604e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/tabbar.vue @@ -0,0 +1,84 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts new file mode 100644 index 0000000..0f0ef34 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts @@ -0,0 +1,227 @@ +import type { RouteLocationNormalizedGeneric } from 'vue-router'; + +import type { TabDefinition } from '@vben/types'; + +import type { IContextMenuItem } from '@vben-core/tabs-ui'; + +import { computed, ref, watch } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; + +import { useContentMaximize, useTabs } from '@vben/hooks'; +import { + ArrowLeftToLine, + ArrowRightLeft, + ArrowRightToLine, + ExternalLink, + FoldHorizontal, + Fullscreen, + Minimize2, + Pin, + PinOff, + RotateCw, + X, +} from '@vben/icons'; +import { $t, useI18n } from '@vben/locales'; +import { getTabKey, useAccessStore, useTabbarStore } from '@vben/stores'; +import { filterTree } from '@vben/utils'; + +export function useTabbar() { + const router = useRouter(); + const route = useRoute(); + const accessStore = useAccessStore(); + const tabbarStore = useTabbarStore(); + const { contentIsMaximize, toggleMaximize } = useContentMaximize(); + const { + closeAllTabs, + closeCurrentTab, + closeLeftTabs, + closeOtherTabs, + closeRightTabs, + closeTabByKey, + getTabDisableState, + openTabInNewWindow, + refreshTab, + toggleTabPin, + } = useTabs(); + + /** + * 当前路径对应的tab的key + */ + const currentActive = computed(() => { + return getTabKey(route); + }); + + const { locale } = useI18n(); + const currentTabs = ref(); + watch( + [ + () => tabbarStore.getTabs, + () => tabbarStore.updateTime, + () => locale.value, + ], + ([tabs]) => { + currentTabs.value = tabs.map((item) => wrapperTabLocale(item)); + }, + ); + + /** + * 初始化固定标签页 + */ + const initAffixTabs = () => { + const affixTabs = filterTree(router.getRoutes(), (route) => { + return !!route.meta?.affixTab; + }); + tabbarStore.setAffixTabs(affixTabs); + }; + + // 点击tab,跳转路由 + const handleClick = (key: string) => { + const { fullPath, path } = tabbarStore.getTabByKey(key); + router.push(fullPath || path); + }; + + // 关闭tab + const handleClose = async (key: string) => { + await closeTabByKey(key); + }; + + function wrapperTabLocale(tab: RouteLocationNormalizedGeneric) { + return { + ...tab, + meta: { + ...tab?.meta, + title: $t(tab?.meta?.title as string), + }, + }; + } + + watch( + () => accessStore.accessMenus, + () => { + initAffixTabs(); + }, + { immediate: true }, + ); + + watch( + () => route.fullPath, + () => { + const meta = route.matched?.[route.matched.length - 1]?.meta; + tabbarStore.addTab({ + ...route, + meta: meta || route.meta, + }); + }, + { immediate: true }, + ); + + const createContextMenus = (tab: TabDefinition) => { + const { + disabledCloseAll, + disabledCloseCurrent, + disabledCloseLeft, + disabledCloseOther, + disabledCloseRight, + disabledRefresh, + } = getTabDisableState(tab); + + const affixTab = tab?.meta?.affixTab ?? false; + + const menus: IContextMenuItem[] = [ + { + disabled: disabledCloseCurrent, + handler: async () => { + await closeCurrentTab(tab); + }, + icon: X, + key: 'close', + text: $t('preferences.tabbar.contextMenu.close'), + }, + { + handler: async () => { + await toggleTabPin(tab); + }, + icon: affixTab ? PinOff : Pin, + key: 'affix', + text: affixTab + ? $t('preferences.tabbar.contextMenu.unpin') + : $t('preferences.tabbar.contextMenu.pin'), + }, + { + handler: async () => { + if (!contentIsMaximize.value) { + await router.push(tab.fullPath); + } + toggleMaximize(); + }, + icon: contentIsMaximize.value ? Minimize2 : Fullscreen, + key: contentIsMaximize.value ? 'restore-maximize' : 'maximize', + text: contentIsMaximize.value + ? $t('preferences.tabbar.contextMenu.restoreMaximize') + : $t('preferences.tabbar.contextMenu.maximize'), + }, + { + disabled: disabledRefresh, + handler: () => refreshTab(), + icon: RotateCw, + key: 'reload', + text: $t('preferences.tabbar.contextMenu.reload'), + }, + { + handler: async () => { + await openTabInNewWindow(tab); + }, + icon: ExternalLink, + key: 'open-in-new-window', + separator: true, + text: $t('preferences.tabbar.contextMenu.openInNewWindow'), + }, + + { + disabled: disabledCloseLeft, + handler: async () => { + await closeLeftTabs(tab); + }, + icon: ArrowLeftToLine, + key: 'close-left', + text: $t('preferences.tabbar.contextMenu.closeLeft'), + }, + { + disabled: disabledCloseRight, + handler: async () => { + await closeRightTabs(tab); + }, + icon: ArrowRightToLine, + key: 'close-right', + separator: true, + text: $t('preferences.tabbar.contextMenu.closeRight'), + }, + { + disabled: disabledCloseOther, + handler: async () => { + await closeOtherTabs(tab); + }, + icon: FoldHorizontal, + key: 'close-other', + text: $t('preferences.tabbar.contextMenu.closeOther'), + }, + { + disabled: disabledCloseAll, + handler: closeAllTabs, + icon: ArrowRightLeft, + key: 'close-all', + text: $t('preferences.tabbar.contextMenu.closeAll'), + }, + ]; + + return menus.filter((item) => tabbarStore.getMenuList.includes(item.key)); + }; + + return { + createContextMenus, + currentActive, + currentTabs, + handleClick, + handleClose, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/hooks/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/hooks/index.ts new file mode 100644 index 0000000..5f825ac --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/hooks/index.ts @@ -0,0 +1,98 @@ +import type { VNode } from 'vue'; +import type { + RouteLocationNormalizedLoaded, + RouteLocationNormalizedLoadedGeneric, +} from 'vue-router'; + +import { computed } from 'vue'; + +import { preferences, usePreferences } from '@vben/preferences'; + +/** + * 转换组件,自动添加 name + * @param component + * @param route + */ +export function transformComponent( + component: VNode, + route: RouteLocationNormalizedLoadedGeneric, +) { + // 组件视图未找到,如果有设置后备视图,则返回后备视图,如果没有,则抛出错误 + if (!component) { + console.error( + 'Component view not found,please check the route configuration', + ); + return undefined; + } + + const routeName = route.name as string; + // 如果组件没有 name,则直接返回 + if (!routeName) { + return component; + } + const componentName = (component?.type as any)?.name; + + // 已经设置过 name,则直接返回 + if (componentName) { + return component; + } + + // componentName 与 routeName 一致,则直接返回 + if (componentName === routeName) { + return component; + } + + // 设置 name + component.type ||= {}; + (component.type as any).name = routeName; + + return component; +} + +/** + * Layout相关hook + */ +export function useLayoutHook() { + const { keepAlive } = usePreferences(); + /** + * 是否使用动画 + */ + const getEnabledTransition = computed(() => { + const { transition } = preferences; + const transitionName = transition.name; + return transitionName && transition.enable; + }); + + /** + * 获取路由过渡动画 + * @param _route + */ + function getTransitionName(_route: RouteLocationNormalizedLoaded) { + // 如果偏好设置未设置,则不使用动画 + const { tabbar, transition } = preferences; + const transitionName = transition.name; + if (!transitionName || !transition.enable) { + return; + } + + // 标签页未启用或者未开启缓存,则使用全局配置动画 + if (!tabbar.enable || !keepAlive) { + return transitionName; + } + + // 如果页面已经加载过,则不使用动画 + // if (route.meta.loaded) { + // return; + // } + // 已经打开且已经加载过的页面不使用动画 + // const inTabs = getCachedTabs.value.includes(route.name as string); + + // return inTabs && route.meta.loaded ? undefined : transitionName; + return transitionName; + } + + return { + getEnabledTransition, + getTransitionName, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-router-view.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-router-view.vue new file mode 100644 index 0000000..3f1f952 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-router-view.vue @@ -0,0 +1,86 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-view.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-view.vue new file mode 100644 index 0000000..7b8b46c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/iframe-view.vue @@ -0,0 +1,3 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/index.ts new file mode 100644 index 0000000..1b8c131 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/iframe/index.ts @@ -0,0 +1,2 @@ +export { default as IFrameRouterView } from './iframe-router-view.vue'; +export { default as IFrameView } from './iframe-view.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/index.ts new file mode 100644 index 0000000..124a44a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/index.ts @@ -0,0 +1,4 @@ +export * from './authentication'; +export * from './basic'; +export * from './iframe'; +export * from './widgets'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/index.ts new file mode 100644 index 0000000..daae4a0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/index.ts @@ -0,0 +1,2 @@ +export { default as RouteCachedPage } from './route-cached-page.vue'; +export { default as RouteCachedView } from './route-cached-view.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-page.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-page.vue new file mode 100644 index 0000000..63708ce --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-page.vue @@ -0,0 +1,36 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-view.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-view.vue new file mode 100644 index 0000000..96616aa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/route-cached/route-cached-view.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/breadcrumb.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/breadcrumb.vue new file mode 100644 index 0000000..c041960 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/breadcrumb.vue @@ -0,0 +1,74 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/check-updates.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/check-updates.vue new file mode 100644 index 0000000..4f3082f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/check-updates.vue @@ -0,0 +1,136 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/index.ts new file mode 100644 index 0000000..fc20c66 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/check-updates/index.ts @@ -0,0 +1 @@ +export { default as CheckUpdates } from './check-updates.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/color-toggle.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/color-toggle.vue new file mode 100644 index 0000000..82e04ff --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/color-toggle.vue @@ -0,0 +1,64 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/global-search.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/global-search.vue new file mode 100644 index 0000000..3268fae --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/global-search.vue @@ -0,0 +1,159 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/index.ts new file mode 100644 index 0000000..cd490ae --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/index.ts @@ -0,0 +1 @@ +export { default as GlobalSearch } from './global-search.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/search-panel.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/search-panel.vue new file mode 100644 index 0000000..c51f1de --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/global-search/search-panel.vue @@ -0,0 +1,299 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/index.ts new file mode 100644 index 0000000..0f9e11a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/index.ts @@ -0,0 +1,12 @@ +export { default as Breadcrumb } from './breadcrumb.vue'; +export * from './check-updates'; +export { default as AuthenticationColorToggle } from './color-toggle.vue'; +export * from './global-search'; +export { default as LanguageToggle } from './language-toggle.vue'; +export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; +export * from './lock-screen'; +export * from './notification'; +export * from './preferences'; +export * from './theme-toggle'; +export * from './timezone'; +export * from './user-dropdown'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/language-toggle.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/language-toggle.vue new file mode 100644 index 0000000..0c5f6d5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/language-toggle.vue @@ -0,0 +1,39 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/layout-toggle.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/layout-toggle.vue new file mode 100644 index 0000000..ae4eecb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/layout-toggle.vue @@ -0,0 +1,64 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/index.ts new file mode 100644 index 0000000..8ba1f16 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/index.ts @@ -0,0 +1,2 @@ +export { default as LockScreenModal } from './lock-screen-modal.vue'; +export { default as LockScreen } from './lock-screen.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue new file mode 100644 index 0000000..e5a43be --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue @@ -0,0 +1,110 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue new file mode 100644 index 0000000..76f2e5d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue @@ -0,0 +1,167 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/index.ts new file mode 100644 index 0000000..e219b71 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/index.ts @@ -0,0 +1,3 @@ +export { default as Notification } from './notification.vue'; + +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/notification.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/notification.vue new file mode 100644 index 0000000..7d0f510 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/notification.vue @@ -0,0 +1,208 @@ + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/types.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/types.ts new file mode 100644 index 0000000..4eada69 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/notification/types.ts @@ -0,0 +1,19 @@ +interface NotificationItem { + id: number | string; + avatar: string; + date: string; + isRead?: boolean; + message: string; + title: string; + /** + * 跳转链接,可以是路由路径或完整 URL + * @example '/dashboard' 或 'https://example.com' + */ + link?: string; + query?: Record; + state?: Record; + /** 业务字段 */ + [key: string]: any; +} + +export type { NotificationItem }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/block.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/block.vue new file mode 100644 index 0000000..e1941b7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/block.vue @@ -0,0 +1,22 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/checkbox-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/checkbox-item.vue new file mode 100644 index 0000000..57e662a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/checkbox-item.vue @@ -0,0 +1,63 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/custom/custom.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/custom/custom.vue new file mode 100644 index 0000000..2cfb8f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/custom/custom.vue @@ -0,0 +1,121 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue new file mode 100644 index 0000000..9bf06ad --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue @@ -0,0 +1,54 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue new file mode 100644 index 0000000..d5fd1c7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue @@ -0,0 +1,77 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/index.ts new file mode 100644 index 0000000..b1b8eac --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/index.ts @@ -0,0 +1,21 @@ +export { default as Block } from './block.vue'; +export { default as Custom } from './custom/custom.vue'; +export { default as Animation } from './general/animation.vue'; +export { default as General } from './general/general.vue'; +export { default as Breadcrumb } from './layout/breadcrumb.vue'; +export { default as Content } from './layout/content.vue'; +export { default as Copyright } from './layout/copyright.vue'; +export { default as Footer } from './layout/footer.vue'; +export { default as Header } from './layout/header.vue'; +export { default as Layout } from './layout/layout.vue'; +export { default as Navigation } from './layout/navigation.vue'; +export { default as Sidebar } from './layout/sidebar.vue'; +export { default as Tabbar } from './layout/tabbar.vue'; +export { default as Widget } from './layout/widget.vue'; +export { default as GlobalShortcutKeys } from './shortcut-keys/global.vue'; +export { default as SwitchItem } from './switch-item.vue'; +export { default as BuiltinTheme } from './theme/builtin.vue'; +export { default as ColorMode } from './theme/color-mode.vue'; +export { default as FontSize } from './theme/font-size.vue'; +export { default as Radius } from './theme/radius.vue'; +export { default as Theme } from './theme/theme.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue new file mode 100644 index 0000000..d076677 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue @@ -0,0 +1,71 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue new file mode 100644 index 0000000..51f1a40 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue @@ -0,0 +1,56 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue new file mode 100644 index 0000000..aa5428b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue @@ -0,0 +1,53 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue new file mode 100644 index 0000000..9389029 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue @@ -0,0 +1,44 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue new file mode 100644 index 0000000..8a77920 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue @@ -0,0 +1,17 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue new file mode 100644 index 0000000..ee04ee2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue @@ -0,0 +1,74 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue new file mode 100644 index 0000000..4b43530 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue @@ -0,0 +1,112 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue new file mode 100644 index 0000000..23acb1f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue @@ -0,0 +1,45 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue new file mode 100644 index 0000000..913775a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue @@ -0,0 +1,104 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue new file mode 100644 index 0000000..885c30c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue @@ -0,0 +1,102 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue new file mode 100644 index 0000000..39cd14e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue @@ -0,0 +1,79 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue new file mode 100644 index 0000000..ee2ddaa --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue @@ -0,0 +1,74 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue new file mode 100644 index 0000000..f20295b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue @@ -0,0 +1,76 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue new file mode 100644 index 0000000..baa18a9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue @@ -0,0 +1,55 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue new file mode 100644 index 0000000..d9726e6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue @@ -0,0 +1,55 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue new file mode 100644 index 0000000..c262eeb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue @@ -0,0 +1,160 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue new file mode 100644 index 0000000..9a41d4e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue @@ -0,0 +1,26 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/font-size.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/font-size.vue new file mode 100644 index 0000000..58b7b81 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/font-size.vue @@ -0,0 +1,62 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue new file mode 100644 index 0000000..0cfcd98 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue @@ -0,0 +1,38 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue new file mode 100644 index 0000000..a6423e9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue @@ -0,0 +1,114 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue new file mode 100644 index 0000000..cfa3c62 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue @@ -0,0 +1,46 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue new file mode 100644 index 0000000..a1bcefd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue @@ -0,0 +1,119 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue new file mode 100644 index 0000000..5cbcaca --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue @@ -0,0 +1,50 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue new file mode 100644 index 0000000..d5c9367 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue @@ -0,0 +1,202 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue new file mode 100644 index 0000000..18158b3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue @@ -0,0 +1,119 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue new file mode 100644 index 0000000..b44e249 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue @@ -0,0 +1,177 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/index.ts new file mode 100644 index 0000000..7eec1b4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/index.ts @@ -0,0 +1,12 @@ +import HeaderNav from './header-nav.vue'; + +export { default as ContentCompact } from './content-compact.vue'; +export { default as FullContent } from './full-content.vue'; +export { default as HeaderMixedNav } from './header-mixed-nav.vue'; +export { default as HeaderSidebarNav } from './header-sidebar-nav.vue'; +export { default as MixedNav } from './mixed-nav.vue'; +export { default as SidebarMixedNav } from './sidebar-mixed-nav.vue'; +export { default as SidebarNav } from './sidebar-nav.vue'; + +const ContentWide = HeaderNav; +export { ContentWide, HeaderNav }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue new file mode 100644 index 0000000..d194383 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue @@ -0,0 +1,161 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/setting.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/setting.vue new file mode 100644 index 0000000..d824e11 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/setting.vue @@ -0,0 +1,12 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue new file mode 100644 index 0000000..8fc0ba4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue @@ -0,0 +1,173 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue new file mode 100644 index 0000000..83ff399 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue @@ -0,0 +1,153 @@ + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/index.ts new file mode 100644 index 0000000..2c737d5 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/index.ts @@ -0,0 +1,3 @@ +export { default as PreferencesButton } from './preferences-button.vue'; +export { default as Preferences } from './preferences.vue'; +export * from './use-open-preferences'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-button.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-button.vue new file mode 100644 index 0000000..d3703b8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-button.vue @@ -0,0 +1,31 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue new file mode 100644 index 0000000..66e09a7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -0,0 +1,572 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences.vue new file mode 100644 index 0000000..ef2e41c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/preferences.vue @@ -0,0 +1,93 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts new file mode 100644 index 0000000..eb9d847 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue'; + +const openPreferences = ref(false); + +function useOpenPreferences() { + function handleOpenPreference() { + openPreferences.value = true; + } + + return { + handleOpenPreference, + openPreferences, + }; +} + +export { useOpenPreferences }; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/index.ts new file mode 100644 index 0000000..0673d21 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/index.ts @@ -0,0 +1 @@ +export { default as ThemeToggle } from './theme-toggle.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue new file mode 100644 index 0000000..178f977 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue new file mode 100644 index 0000000..19c132e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue @@ -0,0 +1,83 @@ + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/index.ts new file mode 100644 index 0000000..eef486d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/index.ts @@ -0,0 +1 @@ +export { default as TimezoneButton } from './timezone-button.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/timezone-button.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/timezone-button.vue new file mode 100644 index 0000000..15b30ba --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/timezone/timezone-button.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/index.ts b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/index.ts new file mode 100644 index 0000000..86429e9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/index.ts @@ -0,0 +1 @@ +export { default as UserDropdown } from './user-dropdown.vue'; diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue new file mode 100644 index 0000000..fb4df07 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue @@ -0,0 +1,298 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/layouts/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/layouts/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/layouts/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/README.md b/deploy/fba/fba-ui-src/packages/effects/plugins/README.md new file mode 100644 index 0000000..c394c9c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/README.md @@ -0,0 +1,28 @@ +# @vben/plugins + +该目录用于存放项目中集成的第三方库及其相关插件。每个插件都包含了可重用的逻辑、配置和组件,方便在项目中进行统一管理和调用。 + +## 注意 + +所有的第三方插件都必须以 `subpath` 形式引入,例: + +以 `echarts` 为例,引入方式如下: + +**packages.json** + +```json +"exports": { + "./echarts": { + "types": "./src/echarts/index.ts", + "default": "./src/echarts/index.ts" + } + } +``` + +**使用方式** + +```ts +import { useEcharts } from '@vben/plugins/echarts'; +``` + +这样做的好处是,应用可以自行选择是否使用插件,而不会因为插件的引入及副作用而导致打包体积增大,只引入需要的插件即可。 diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/package.json b/deploy/fba/fba-ui-src/packages/effects/plugins/package.json new file mode 100644 index 0000000..6b967d6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/package.json @@ -0,0 +1,68 @@ +{ + "name": "@vben/plugins", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/plugins" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./echarts": { + "types": "./src/echarts/index.ts", + "default": "./src/echarts/index.ts" + }, + "./tiptap": { + "types": "./src/tiptap/index.ts", + "default": "./src/tiptap/index.ts" + }, + "./vxe-table": { + "types": "./src/vxe-table/index.ts", + "default": "./src/vxe-table/index.ts" + }, + "./motion": { + "types": "./src/motion/index.ts", + "default": "./src/motion/index.ts" + } + }, + "dependencies": { + "@tiptap/core": "catalog:", + "@tiptap/extension-highlight": "catalog:", + "@tiptap/extension-image": "catalog:", + "@tiptap/extension-link": "catalog:", + "@tiptap/extension-placeholder": "catalog:", + "@tiptap/extension-text-align": "catalog:", + "@tiptap/extension-text-style": "catalog:", + "@tiptap/extension-underline": "catalog:", + "@tiptap/pm": "catalog:", + "@tiptap/starter-kit": "catalog:", + "@tiptap/vue-3": "catalog:", + "@vben-core/design": "workspace:*", + "@vben-core/form-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "@vueuse/motion": "catalog:", + "echarts": "catalog:", + "vue": "catalog:", + "vxe-pc-ui": "catalog:", + "vxe-table": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/README.md b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/README.md new file mode 100644 index 0000000..2549b68 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/README.md @@ -0,0 +1,41 @@ +# ECharts Plugin + +ECharts 图表插件,预置常用组件和图表类型。 + +## 导出 + +| 导出 | 类型 | 说明 | +| ------------ | ---- | ------------ | +| `default` | 对象 | echarts 实例 | +| `EchartsUI` | 组件 | 图表容器组件 | +| `ECOption` | 类型 | 图表配置类型 | +| `useEcharts` | 函数 | 组合式函数 | + +## 使用 + +```ts +import { EchartsUI, useEcharts, ECOption } from '@vben/plugins/echarts'; +``` + +## 类型 + +```ts +import type { ECOption } from '@vben/plugins/echarts'; +``` + +## 预置组件 + +- TitleComponent +- TooltipComponent +- GridComponent +- LegendComponent +- ToolboxComponent +- DatasetComponent +- TransformComponent + +## 预置图表 + +- BarChart +- LineChart +- PieChart +- RadarChart diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts-ui.vue b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts-ui.vue new file mode 100644 index 0000000..70d1f20 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts-ui.vue @@ -0,0 +1,15 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts.ts new file mode 100644 index 0000000..612f736 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/echarts.ts @@ -0,0 +1,45 @@ +import { + BarChart, + GaugeChart, + LineChart, + PieChart, + RadarChart, +} from 'echarts/charts'; +import { + DatasetComponent, + GridComponent, + LegendComponent, + TitleComponent, + ToolboxComponent, + TooltipComponent, + TransformComponent, +} from 'echarts/components'; +import * as echarts from 'echarts/core'; +import { + LabelLayout, + LegacyGridContainLabel, + UniversalTransition, +} from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; + +echarts.use([ + TitleComponent, + PieChart, + RadarChart, + TooltipComponent, + GridComponent, + DatasetComponent, + TransformComponent, + BarChart, + GaugeChart, + LineChart, + LabelLayout, + LegacyGridContainLabel, + UniversalTransition, + CanvasRenderer, + LegendComponent, + ToolboxComponent, +]); +export type { ECOption } from './types'; + +export default echarts; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/index.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/index.ts new file mode 100644 index 0000000..acb0f1e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/index.ts @@ -0,0 +1,4 @@ +export * from './echarts'; +export { default as EchartsUI } from './echarts-ui.vue'; +export * from './types'; +export * from './use-echarts'; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/types.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/types.ts new file mode 100644 index 0000000..b907827 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/types.ts @@ -0,0 +1,30 @@ +import type { + BarSeriesOption, + GaugeSeriesOption, + LineSeriesOption, + PieSeriesOption, + RadarSeriesOption, +} from 'echarts/charts'; +import type { + DatasetComponentOption, + GridComponentOption, + LegendComponentOption, + TitleComponentOption, + ToolboxComponentOption, + TooltipComponentOption, +} from 'echarts/components'; +import type { ComposeOption } from 'echarts/core'; + +export type ECOption = ComposeOption< + | BarSeriesOption + | DatasetComponentOption + | GaugeSeriesOption + | GridComponentOption + | LegendComponentOption + | LineSeriesOption + | PieSeriesOption + | RadarSeriesOption + | TitleComponentOption + | ToolboxComponentOption + | TooltipComponentOption +>; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/use-echarts.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/use-echarts.ts new file mode 100644 index 0000000..49ae569 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/echarts/use-echarts.ts @@ -0,0 +1,202 @@ +import type { EChartsOption } from 'echarts'; + +import type { Ref } from 'vue'; + +import type { Nullable } from '@vben/types'; + +import type EchartsUI from './echarts-ui.vue'; + +import { + computed, + nextTick, + onActivated, + onBeforeUnmount, + onDeactivated, + onMounted, + ref, + unref, + watch, +} from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { + tryOnUnmounted, + useDebounceFn, + useResizeObserver, + useTimeoutFn, + useWindowSize, +} from '@vueuse/core'; + +import echarts from './echarts'; + +type EchartsUIType = typeof EchartsUI | undefined; + +type EchartsThemeType = 'dark' | 'light' | null; + +function useEcharts(chartRef: Ref) { + let chartInstance: echarts.ECharts | null = null; + let cacheOptions: EChartsOption = {}; + // echarts是否处于激活状态 + const isActiveRef = ref(false); + + const { isDark } = usePreferences(); + const { height, width } = useWindowSize(); + const resizeHandler: () => void = useDebounceFn(resize, 200); + + const getChartEl = (): HTMLElement | null => { + const refValue = chartRef?.value as unknown; + if (!refValue) return null; + if (refValue instanceof HTMLElement) { + return refValue; + } + const maybeComponent = refValue as { $el?: HTMLElement }; + return maybeComponent.$el ?? null; + }; + + onMounted(() => (isActiveRef.value = true)); + onActivated(() => (isActiveRef.value = true)); + onDeactivated(() => (isActiveRef.value = false)); + onBeforeUnmount(() => (isActiveRef.value = false)); + + const isElHidden = (el: HTMLElement | null): boolean => { + if (!el) return true; + return el.offsetHeight === 0 || el.offsetWidth === 0; + }; + + const getOptions = computed((): EChartsOption => { + if (!isDark.value) { + return {}; + } + + return { + backgroundColor: 'transparent', + }; + }); + + const initCharts = (t?: EchartsThemeType) => { + const el = chartRef?.value?.$el; + if (!el) { + return; + } + chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null); + + return chartInstance; + }; + + const renderEcharts = ( + options: EChartsOption, + clear = true, + ): Promise> => { + if (!unref(isActiveRef)) { + return Promise.resolve(null); + } + cacheOptions = options; + const currentOptions = { + ...options, + ...getOptions.value, + }; + return new Promise((resolve) => { + if (chartRef.value?.offsetHeight === 0) { + useTimeoutFn(async () => { + resolve(await renderEcharts(currentOptions)); + }, 30); + return; + } + nextTick(() => { + const el = getChartEl(); + if (isElHidden(el)) { + useTimeoutFn(async () => { + resolve(await renderEcharts(currentOptions)); + }, 30); + return; + } + useTimeoutFn(() => { + if (!chartInstance || chartInstance?.getDom() !== el) { + chartInstance?.dispose(); + const instance = initCharts(); + if (!instance) return; + chartInstance = instance; + } + clear && chartInstance?.clear(); + chartInstance?.setOption(currentOptions); + resolve(chartInstance); + }, 30); + }); + }); + }; + + const updateData = ( + option: EChartsOption, + notMerge = false, // false = 合并(保留动画),true = 完全替换 + lazyUpdate = false, // true 时不立即重绘,适合短时间内多次调用 + ): Promise => { + return new Promise((resolve) => { + nextTick(() => { + if (!chartInstance) { + // 还没初始化 → 当作首次渲染 + renderEcharts(option).then(resolve); + return; + } + + // 合并你原有的全局配置(比如 backgroundColor) + const finalOption = { + ...option, + ...getOptions.value, + }; + + chartInstance.setOption(finalOption, { + notMerge, + lazyUpdate, + // silent: true, // 如果追求极致性能可开启(关闭所有事件) + }); + + resolve(chartInstance); + }); + }); + }; + + function resize() { + const el = getChartEl(); + if (isElHidden(el)) { + return; + } + chartInstance?.resize({ + animation: { + duration: 300, + easing: 'quadraticIn', + }, + }); + } + + watch([width, height], () => { + resizeHandler?.(); + }); + + useResizeObserver(chartRef as never, resizeHandler); + + watch([isDark, isActiveRef], () => { + if (chartInstance && unref(isActiveRef)) { + chartInstance.dispose(); + initCharts(); + renderEcharts(cacheOptions); + resize(); + } + }); + + tryOnUnmounted(() => { + // 销毁实例,释放资源 + chartInstance?.dispose(); + }); + return { + isActive: isActiveRef, + renderEcharts, + resize, + updateData, + getChartInstance: () => chartInstance, + }; +} + +export { useEcharts }; + +export type { EchartsUIType }; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/index.ts new file mode 100644 index 0000000..744be92 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/index.ts @@ -0,0 +1,2 @@ +export * from './plugins-context'; +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/README.md b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/README.md new file mode 100644 index 0000000..03c2ca1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/README.md @@ -0,0 +1,26 @@ +# Motion Plugin + +基于 @vueuse/motion 的动画插件。 + +## 导出 + +| 导出 | 类型 | 说明 | +| ----------------- | ---- | ---------- | +| `Motion` | 组件 | 动画组件 | +| `MotionGroup` | 组件 | 动画组组件 | +| `MotionDirective` | 指令 | 动画指令 | +| `MotionPlugin` | 插件 | Vue 插件 | + +## 使用 + +```ts +import { MotionPlugin, Motion, MotionDirective } from '@vben/plugins/motion'; + +app.use(MotionPlugin); +``` + +## 类型 + +```ts +import type { MotionOptions, MotionVariants } from '@vben/plugins/motion'; +``` diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/index.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/index.ts new file mode 100644 index 0000000..021465a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/index.ts @@ -0,0 +1,8 @@ +export * from './types'; + +export { + MotionComponent as Motion, + MotionDirective, + MotionGroupComponent as MotionGroup, + MotionPlugin, +} from '@vueuse/motion'; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/types.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/types.ts new file mode 100644 index 0000000..f08bf21 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/motion/types.ts @@ -0,0 +1,26 @@ +export const MotionPresets = [ + 'fade', + 'fadeVisible', + 'fadeVisibleOnce', + 'rollBottom', + 'rollLeft', + 'rollRight', + 'rollTop', + 'rollVisibleBottom', + 'rollVisibleLeft', + 'rollVisibleRight', + 'rollVisibleTop', + 'pop', + 'popVisible', + 'popVisibleOnce', + 'slideBottom', + 'slideLeft', + 'slideRight', + 'slideTop', + 'slideVisibleBottom', + 'slideVisibleLeft', + 'slideVisibleRight', + 'slideVisibleTop', +] as const; + +export type MotionPreset = (typeof MotionPresets)[number]; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/plugins-context.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/plugins-context.ts new file mode 100644 index 0000000..459c213 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/plugins-context.ts @@ -0,0 +1,39 @@ +import type { VbenPluginsOptions } from './types'; + +let globalPluginsOptions: null | VbenPluginsOptions = null; + +export function providePluginsOptions(options: VbenPluginsOptions) { + if (!globalPluginsOptions) { + globalPluginsOptions = options; + return; + } + + globalPluginsOptions = { + ...globalPluginsOptions, + ...options, + form: + globalPluginsOptions.form && options.form + ? { ...globalPluginsOptions.form, ...options.form } + : globalPluginsOptions.form || options.form, + modal: + globalPluginsOptions.modal && options.modal + ? { ...globalPluginsOptions.modal, ...options.modal } + : globalPluginsOptions.modal || options.modal, + message: + globalPluginsOptions.message && options.message + ? { ...globalPluginsOptions.message, ...options.message } + : globalPluginsOptions.message || options.message, + components: { + ...globalPluginsOptions.components, + ...options.components, + }, + }; +} + +export function injectPluginsOptions() { + return globalPluginsOptions; +} + +export function resetPluginsOptions() { + globalPluginsOptions = null; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/extensions.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/extensions.ts new file mode 100644 index 0000000..9f8aa58 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/extensions.ts @@ -0,0 +1,452 @@ +import type { Editor as CoreEditor } from '@tiptap/core'; +import type { Node as ProseMirrorNode } from '@tiptap/pm/model'; +import type { EditorView } from '@tiptap/pm/view'; +import type { Extensions } from '@tiptap/vue-3'; + +import type { ImageUploadOptions, VbenTiptapExtensionOptions } from './types'; + +import { $t } from '@vben/locales'; + +import { alert } from '@vben-core/popup-ui'; + +import Highlight from '@tiptap/extension-highlight'; +import Image from '@tiptap/extension-image'; +import Link from '@tiptap/extension-link'; +import Placeholder from '@tiptap/extension-placeholder'; +import TextAlign from '@tiptap/extension-text-align'; +import { Color, TextStyle } from '@tiptap/extension-text-style'; +import Underline from '@tiptap/extension-underline'; +import { Plugin, PluginKey } from '@tiptap/pm/state'; +import StarterKit from '@tiptap/starter-kit'; + +const DEFAULT_ACCEPT = 'image/*'; + +function validateFile( + file: File, + options: ImageUploadOptions, +): string | undefined { + if (options.maxSize !== undefined && file.size > options.maxSize) { + return $t('ui.tiptap.upload.fileTooLarge'); + } + + const accept = options.accept ?? DEFAULT_ACCEPT; + if (accept && accept !== '*/*' && accept !== 'image/*') { + const acceptedTypes = accept.split(',').map((t) => t.trim()); + const isAccepted = acceptedTypes.some((type) => { + if (type.endsWith('/*')) { + return file.type.startsWith(type.slice(0, -1)); + } + return file.type === type; + }); + if (!isAccepted) { + return $t('ui.tiptap.upload.fileTypeNotAllowed'); + } + } + + return undefined; +} + +function handleUploadError(error: unknown, options: ImageUploadOptions): void { + if (options.onUploadError) { + options.onUploadError(error); + } else { + const message = error instanceof Error ? error.message : String(error); + alert(message, $t('ui.tiptap.upload.uploadFailed')).catch(() => {}); + } +} + +function findPlaceholderPos(doc: ProseMirrorNode, blobUrl: string): number { + let found = -1; + doc.descendants((node: ProseMirrorNode, offset: number) => { + if (found !== -1) return false; + if ( + node.type.name === 'image' && + node.attrs.src === blobUrl && + node.attrs['data-uploading'] === 'true' + ) { + found = offset; + return false; + } + return true; + }); + return found; +} + +interface UploadContext { + blobUrl: string; + pos: number; +} + +function createUploadProcess( + editor: CoreEditor, + file: File, + options: ImageUploadOptions, + blobUrlTracker?: Set, + pos?: number, +): UploadContext { + const blobUrl = URL.createObjectURL(file); + blobUrlTracker?.add(blobUrl); + const insertPos = pos ?? editor.state.selection.from; + + // Insert placeholder image with blob URL + editor + .chain() + .insertContentAt(insertPos, { + attrs: { + 'data-upload-progress': 0, + 'data-uploading': 'true', + src: blobUrl, + }, + type: 'image', + }) + .run(); + + const nodePos = findPlaceholderPos(editor.state.doc, blobUrl); + + const uploadContext: UploadContext = { blobUrl, pos: nodePos }; + + options + .upload(file, (percent: number) => { + if (editor.isDestroyed) return; + + const currentPos = findPlaceholderPos(editor.state.doc, blobUrl); + if (currentPos === -1) return; + + const node = editor.state.doc.nodeAt(currentPos); + if (!node) return; + + const transaction = editor.state.tr.setNodeMarkup(currentPos, undefined, { + ...node.attrs, + 'data-upload-progress': percent, + }); + editor.view.dispatch(transaction); + }) + .then((url: string) => { + if (editor.isDestroyed) { + URL.revokeObjectURL(blobUrl); + return; + } + + const currentPos = findPlaceholderPos(editor.state.doc, blobUrl); + + if (currentPos === -1) { + blobUrlTracker?.delete(blobUrl); + URL.revokeObjectURL(blobUrl); + return; + } + + const node = editor.state.doc.nodeAt(currentPos); + if (!node) { + blobUrlTracker?.delete(blobUrl); + URL.revokeObjectURL(blobUrl); + return; + } + + const transaction = editor.state.tr.setNodeMarkup(currentPos, undefined, { + ...node.attrs, + 'data-upload-progress': null, + 'data-uploading': null, + src: url, + }); + editor.view.dispatch(transaction); + blobUrlTracker?.delete(blobUrl); + URL.revokeObjectURL(blobUrl); + }) + .catch((error: unknown) => { + if (editor.isDestroyed) { + URL.revokeObjectURL(blobUrl); + return; + } + + const currentPos = findPlaceholderPos(editor.state.doc, blobUrl); + + if (currentPos !== -1) { + const transaction = editor.state.tr.delete( + currentPos, + currentPos + (editor.state.doc.nodeAt(currentPos)?.nodeSize ?? 1), + ); + editor.view.dispatch(transaction); + } + + URL.revokeObjectURL(blobUrl); + blobUrlTracker?.delete(blobUrl); + handleUploadError(error, options); + }); + + return uploadContext; +} + +function createCustomImage( + imageUpload: ImageUploadOptions, + blobUrlTracker?: Set, +) { + return Image.extend({ + addAttributes() { + return { + ...this.parent?.(), + 'data-upload-progress': { + default: null, + parseHTML: (element) => element.dataset.uploadProgress, + renderHTML: () => { + return {}; + }, + }, + 'data-uploading': { + default: null, + parseHTML: (element) => element.dataset.uploading, + renderHTML: () => { + return {}; + }, + }, + }; + }, + + addNodeView() { + return ({ node }) => { + const isUploading = node.attrs['data-uploading'] === 'true'; + + if (!isUploading) { + return null as any; + } + + const wrapper = document.createElement('div'); + wrapper.className = 'vben-tiptap-upload-wrapper'; + + const img = document.createElement('img'); + img.src = node.attrs.src; + img.className = 'vben-tiptap__image'; + wrapper.append(img); + + const spinner = document.createElement('div'); + spinner.className = 'vben-tiptap-upload-spinner'; + wrapper.append(spinner); + + const progressBar = document.createElement('div'); + progressBar.className = 'vben-tiptap-upload-progress'; + const progressFill = document.createElement('div'); + progressFill.className = 'vben-tiptap-upload-progress-fill'; + progressBar.append(progressFill); + wrapper.append(progressBar); + + const progress = node.attrs['data-upload-progress']; + if (progress !== null && progress !== undefined && progress > 0) { + spinner.style.display = 'none'; + progressBar.style.display = ''; + progressFill.style.width = `${progress}%`; + } else { + spinner.style.display = ''; + progressBar.style.display = 'none'; + } + + return { + dom: wrapper, + update(updatedNode: ProseMirrorNode) { + if (updatedNode.attrs['data-uploading'] !== 'true') { + return false; + } + + if (updatedNode.attrs.src !== img.src) { + img.src = updatedNode.attrs.src; + } + + const newProgress = updatedNode.attrs['data-upload-progress']; + if ( + newProgress !== null && + newProgress !== undefined && + newProgress > 0 + ) { + spinner.style.display = 'none'; + progressBar.style.display = ''; + progressFill.style.width = `${newProgress}%`; + } else { + spinner.style.display = ''; + progressBar.style.display = 'none'; + } + + return true; + }, + } as any; + }; + }, + + addCommands() { + return { + ...this.parent?.(), + uploadImage: + () => + ({ editor: cmdEditor }: { editor: CoreEditor }) => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = imageUpload.accept ?? DEFAULT_ACCEPT; + input.style.display = 'none'; + + input.addEventListener('change', () => { + const file = input.files?.[0]; + if (!file) return; + + const error = validateFile(file, imageUpload); + if (error) { + handleUploadError(new Error(error), imageUpload); + return; + } + + createUploadProcess(cmdEditor, file, imageUpload, blobUrlTracker); + input.remove(); + }); + + document.body.append(input); + input.click(); + return true; + }, + }; + }, + + addProseMirrorPlugins() { + const editor = this.editor; + + return [ + new Plugin({ + key: new PluginKey('imageUploadDrop'), + props: { + handleDrop: (view: EditorView, event: DragEvent) => { + if (!event.dataTransfer?.files.length) return false; + + const imageFiles = [...event.dataTransfer.files].filter((f) => + f.type.startsWith('image/'), + ); + if (imageFiles.length === 0) return false; + + event.preventDefault(); + + // Only support single image upload + const file = imageFiles[0]; + if (!file) return false; + if (imageFiles.length > 1) { + handleUploadError( + new Error($t('ui.tiptap.upload.onlySingleImage')), + imageUpload, + ); + } + + const error = validateFile(file, imageUpload); + if (error) { + handleUploadError(new Error(error), imageUpload); + return true; + } + + const coordinates = view.posAtCoords({ + left: event.clientX, + top: event.clientY, + }); + + const pos = coordinates?.pos ?? view.state.selection.from; + + createUploadProcess( + editor, + file, + imageUpload, + blobUrlTracker, + pos, + ); + return true; + }, + }, + }), + new Plugin({ + key: new PluginKey('imageUploadPaste'), + props: { + handlePaste: (_view: EditorView, event: ClipboardEvent) => { + const items = event.clipboardData?.items; + if (!items) return false; + + const imageFiles: File[] = []; + for (const item of items) { + if (item.type.startsWith('image/')) { + const file = item.getAsFile(); + if (file) imageFiles.push(file); + } + } + + if (imageFiles.length === 0) return false; + + event.preventDefault(); + + const imageFile = imageFiles[0]; + if (!imageFile) return false; + if (imageFiles.length > 1) { + handleUploadError( + new Error($t('ui.tiptap.upload.onlySingleImage')), + imageUpload, + ); + } + + const error = validateFile(imageFile, imageUpload); + if (error) { + handleUploadError(new Error(error), imageUpload); + return true; + } + + createUploadProcess( + editor, + imageFile, + imageUpload, + blobUrlTracker, + ); + return true; + }, + }, + }), + ]; + }, + }); +} + +export function createDefaultTiptapExtensions( + options: VbenTiptapExtensionOptions = {}, +): Extensions { + return [ + StarterKit.configure({ + heading: { + levels: [1, 2, 3, 4], + }, + link: false, + }), + TextAlign.configure({ + types: ['heading', 'paragraph'], + }), + TextStyle, + Color.configure({ + types: ['textStyle'], + }), + Underline, + Highlight.configure({ + multicolor: true, + }), + Link.configure({ + autolink: true, + defaultProtocol: 'https', + enableClickSelection: true, + openOnClick: false, + protocols: ['mailto', { optionalSlashes: true, scheme: 'tel' }], + }), + options.imageUpload + ? createCustomImage( + options.imageUpload, + options._blobUrlTracker, + ).configure({ + allowBase64: true, + HTMLAttributes: { + class: 'vben-tiptap__image', + }, + }) + : Image.configure({ + allowBase64: true, + HTMLAttributes: { + class: 'vben-tiptap__image', + }, + }), + Placeholder.configure({ + placeholder: options.placeholder ?? $t('ui.tiptap.placeholder'), + }), + ]; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/index.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/index.ts new file mode 100644 index 0000000..292ead0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/index.ts @@ -0,0 +1,4 @@ +export { default as VbenTiptapPreview } from './preview.vue'; +export { default as VbenTiptap } from './tiptap.vue'; + +export * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/preview.vue b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/preview.vue new file mode 100644 index 0000000..99563d2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/preview.vue @@ -0,0 +1,34 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/style.css b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/style.css new file mode 100644 index 0000000..3f5f6cc --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/style.css @@ -0,0 +1,112 @@ +@reference "@vben/tailwind-config/theme"; + +.vben-tiptap-content h1 { + @apply text-2xl font-bold leading-[1.4]; +} + +.vben-tiptap-content h2 { + @apply text-xl font-bold leading-[1.45]; +} + +.vben-tiptap-content h3 { + @apply text-lg font-semibold leading-[1.5]; +} + +.vben-tiptap-content h4 { + @apply text-base font-semibold leading-[1.55]; +} + +.vben-tiptap-content ul { + @apply list-disc pl-6; +} + +.vben-tiptap-content ol { + @apply list-decimal pl-6; +} + +.vben-tiptap-content blockquote { + @apply border-l-4 border-primary pl-4 text-muted-foreground; +} + +.vben-tiptap-content a { + @apply text-primary underline decoration-1 underline-offset-[3px]; +} + +.vben-tiptap-content code { + @apply rounded-[0.45rem] border border-border bg-secondary px-[0.35rem] py-[0.15rem] text-[0.9em] text-primary; +} + +.vben-tiptap-content pre { + @apply overflow-x-auto rounded-[0.9rem] border border-border bg-popover p-4 text-popover-foreground; +} + +.vben-tiptap-content pre code { + @apply border-none bg-transparent p-0 text-inherit; +} + +.vben-tiptap-content img, +.vben-tiptap-content .vben-tiptap__image { + max-width: min(100%, 640px); +} + +/* Image upload states */ +.vben-tiptap-upload-wrapper { + position: relative; + display: inline-block; + max-width: min(100%, 640px); + margin: 1rem 0; +} + +.vben-tiptap-upload-wrapper img { + display: block; + margin: 0; + opacity: 0.6; +} + +.vben-tiptap-upload-spinner { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: hsl(var(--card) / 30%); + border-radius: 1rem; +} + +.vben-tiptap-upload-spinner::after { + display: block; + width: 24px; + height: 24px; + content: ''; + border: 2px solid hsl(var(--foreground) / 30%); + border-top-color: hsl(var(--foreground)); + border-radius: 50%; + animation: vben-tiptap-spin 0.8s linear infinite; +} + +.vben-tiptap-upload-progress { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 4px; + overflow: hidden; + background-color: hsl(var(--muted)); + border-radius: 0 0 1rem 1rem; +} + +.vben-tiptap-upload-progress-fill { + height: 100%; + background-color: hsl(var(--primary)); + transition: width 0.2s ease; +} + +@keyframes vben-tiptap-spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/tiptap.vue b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/tiptap.vue new file mode 100644 index 0000000..2c4da36 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/tiptap.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/toolbar.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/toolbar.ts new file mode 100644 index 0000000..2bd0eb7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/toolbar.ts @@ -0,0 +1,374 @@ +import type { Editor } from '@tiptap/vue-3'; + +import type { + ImageUploadOptions, + ToolbarAction, + ToolbarMenuItem, +} from './types'; + +import { + AlignCenter, + AlignLeft, + AlignRight, + Bold, + Highlighter, + ImagePlus, + Italic, + Link2, + List, + ListOrdered, + MessageSquareCode, + Paintbrush, + Redo2, + RemoveFormatting, + SquareCode, + Strikethrough, + TextQuote, + Underline, + Undo2, + Unlink2, +} from '@vben/icons'; +import { $t } from '@vben/locales'; +import { COLOR_PRESETS } from '@vben/preferences'; + +import { prompt } from '@vben-core/popup-ui'; + +const headingLevels = [1, 2, 3, 4] as const; +const editorColorPresets = [ + 'hsl(var(--foreground))', + 'hsl(var(--warning))', + 'hsl(var(--success))', + 'hsl(var(--destructive))', + ...COLOR_PRESETS.map((item) => item.color), +]; +const editorHighlightPresets = [ + withAlpha('hsl(var(--warning))', 0.45), + withAlpha('hsl(var(--success))', 0.35), + withAlpha('hsl(var(--primary))', 0.3), + withAlpha('hsl(var(--destructive))', 0.3), + ...COLOR_PRESETS.map((item) => withAlpha(item.color, 0.4)), +]; + +function createHeadingMenuItems(): ToolbarMenuItem[] { + return [ + { + action: (editor) => editor.chain().focus().setParagraph().run(), + can: (editor) => editor.can().chain().focus().setParagraph().run(), + isActive: (editor) => editor.isActive('paragraph'), + label: $t('ui.tiptap.toolbar.paragraph'), + shortLabel: 'P', + }, + ...headingLevels.map((level) => ({ + action: (editor: Editor) => + editor.chain().focus().toggleHeading({ level }).run(), + can: (editor: Editor) => + editor.can().chain().focus().toggleHeading({ level }).run(), + isActive: (editor: Editor) => editor.isActive('heading', { level }), + label: $t(`ui.tiptap.toolbar.heading${level}`), + shortLabel: `H${level}`, + })), + ]; +} + +function getHeadingTriggerText(editor?: Editor) { + if (editor?.isActive('paragraph')) { + return 'P'; + } + + const level = headingLevels.find((headingLevel) => + editor?.isActive('heading', { level: headingLevel }), + ); + + return level ? `H${level}` : 'H'; +} + +function normalizeLinkUrl(url: string) { + if (/^(https?:|mailto:|tel:)/i.test(url)) { + return url; + } + + return `https://${url}`; +} + +function withAlpha(color: string, alpha: number) { + const normalizedAlpha = Math.min(Math.max(alpha, 0), 1); + const hslMatch = color.match(/^hsl\((.+)\)$/); + + if (!hslMatch) { + return color; + } + + return `hsl(${hslMatch[1]} / ${normalizedAlpha})`; +} + +async function handleLinkAction(editor: Editor) { + const currentHref = editor.getAttributes('link').href as string | undefined; + + let url: string | undefined; + + try { + url = await prompt({ + componentProps: { + placeholder: 'https://example.com', + }, + content: $t('ui.tiptap.prompts.link'), + defaultValue: currentHref ?? '', + }); + } catch { + return; + } + + const nextUrl = (url ?? '').trim(); + + if (!nextUrl) { + editor.chain().focus().extendMarkRange('link').unsetLink().run(); + return; + } + + editor + .chain() + .focus() + .extendMarkRange('link') + .setLink({ + href: normalizeLinkUrl(nextUrl), + }) + .run(); +} + +async function handleImageAction(editor: Editor) { + let url: string | undefined; + + try { + url = await prompt({ + componentProps: { + placeholder: 'https://example.com/image.png', + }, + content: $t('ui.tiptap.prompts.image'), + defaultValue: '', + }); + } catch { + return; + } + + const nextUrl = (url ?? '').trim(); + + if (!nextUrl) { + return; + } + + editor.chain().focus().setImage({ src: nextUrl }).run(); +} + +export function createToolbarGroups( + imageUpload?: ImageUploadOptions, +): ToolbarAction[][] { + const headingMenuItems = createHeadingMenuItems(); + + return [ + [ + { + action: (editor) => editor.chain().focus().undo().run(), + can: (editor) => editor.can().chain().focus().undo().run(), + icon: Undo2, + label: $t('ui.tiptap.toolbar.undo'), + }, + { + action: (editor) => editor.chain().focus().redo().run(), + can: (editor) => editor.can().chain().focus().redo().run(), + icon: Redo2, + label: $t('ui.tiptap.toolbar.redo'), + }, + { + action: (editor) => + editor.chain().focus().clearNodes().unsetAllMarks().run(), + icon: RemoveFormatting, + label: $t('ui.tiptap.toolbar.clear'), + }, + ], + [ + { + action: (editor) => editor.chain().focus().toggleBold().run(), + active: { name: 'bold' }, + can: (editor) => editor.can().chain().focus().toggleBold().run(), + icon: Bold, + label: $t('ui.tiptap.toolbar.bold'), + }, + { + action: (editor) => editor.chain().focus().toggleItalic().run(), + active: { name: 'italic' }, + can: (editor) => editor.can().chain().focus().toggleItalic().run(), + icon: Italic, + label: $t('ui.tiptap.toolbar.italic'), + }, + { + action: (editor) => editor.chain().focus().toggleUnderline().run(), + active: { name: 'underline' }, + can: (editor) => editor.can().chain().focus().toggleUnderline().run(), + icon: Underline, + label: $t('ui.tiptap.toolbar.underline'), + }, + { + action: (editor) => editor.chain().focus().toggleStrike().run(), + active: { name: 'strike' }, + can: (editor) => editor.can().chain().focus().toggleStrike().run(), + icon: Strikethrough, + label: $t('ui.tiptap.toolbar.strike'), + }, + { + action: (editor) => editor.chain().focus().toggleCode().run(), + active: { name: 'code' }, + can: (editor) => editor.can().chain().focus().toggleCode().run(), + icon: SquareCode, + label: $t('ui.tiptap.toolbar.code'), + }, + ], + [ + { + action: () => {}, + can: (editor) => + headingMenuItems.some((item) => (item.can ? item.can(editor) : true)), + isActive: (editor) => + headingMenuItems.some((item) => item.isActive?.(editor)), + label: $t('ui.tiptap.toolbar.heading'), + menu: { + items: headingMenuItems, + }, + triggerText: (editor) => getHeadingTriggerText(editor), + }, + { + action: (editor) => editor.chain().focus().toggleBulletList().run(), + active: { name: 'bulletList' }, + can: (editor) => editor.can().chain().focus().toggleBulletList().run(), + icon: List, + label: $t('ui.tiptap.toolbar.bulletList'), + }, + { + action: (editor) => editor.chain().focus().toggleOrderedList().run(), + active: { name: 'orderedList' }, + can: (editor) => editor.can().chain().focus().toggleOrderedList().run(), + icon: ListOrdered, + label: $t('ui.tiptap.toolbar.orderedList'), + }, + { + action: (editor) => editor.chain().focus().toggleBlockquote().run(), + active: { name: 'blockquote' }, + can: (editor) => editor.can().chain().focus().toggleBlockquote().run(), + icon: TextQuote, + label: $t('ui.tiptap.toolbar.blockquote'), + }, + { + action: (editor) => editor.chain().focus().toggleCodeBlock().run(), + active: { name: 'codeBlock' }, + can: (editor) => editor.can().chain().focus().toggleCodeBlock().run(), + icon: MessageSquareCode, + label: $t('ui.tiptap.toolbar.codeBlock'), + }, + ], + [ + { + action: (editor) => handleLinkAction(editor), + active: { name: 'link' }, + can: (editor) => + editor.can().chain().focus().extendMarkRange('link').run(), + icon: Link2, + label: $t('ui.tiptap.toolbar.link'), + }, + { + action: (editor) => editor.chain().focus().unsetLink().run(), + can: (editor) => editor.can().chain().focus().unsetLink().run(), + icon: Unlink2, + isActive: (editor) => editor.isActive('link'), + label: $t('ui.tiptap.toolbar.unlink'), + }, + { + action: (editor) => handleImageAction(editor), + icon: ImagePlus, + label: $t('ui.tiptap.toolbar.image'), + ...(imageUpload + ? { + action: () => {}, + menu: { + items: [ + { + action: (editor) => { + if (typeof editor.commands.uploadImage === 'function') { + editor.commands.uploadImage(); + } + }, + label: $t('ui.tiptap.toolbar.imageUpload'), + shortLabel: 'UPL', + }, + { + action: (editor) => handleImageAction(editor), + label: $t('ui.tiptap.toolbar.imageUrl'), + shortLabel: 'URL', + }, + ], + }, + } + : {}), + }, + ], + [ + { + action: () => {}, + icon: Paintbrush, + indicatorColor: (editor) => + editor.getAttributes('textStyle').color as string | undefined, + isActive: (editor) => Boolean(editor.getAttributes('textStyle').color), + label: $t('ui.tiptap.toolbar.textColor'), + palette: { + apply: (editor, color) => + editor.chain().focus().setColor(color).run(), + clear: (editor) => editor.chain().focus().unsetColor().run(), + colors: editorColorPresets, + currentColor: (editor) => + editor.getAttributes('textStyle').color as string | undefined, + }, + }, + { + action: () => {}, + icon: Highlighter, + indicatorColor: (editor) => + (editor.getAttributes('highlight').color as string | undefined) ?? + '#fef08a', + isActive: (editor) => editor.isActive('highlight'), + label: $t('ui.tiptap.toolbar.highlightColor'), + palette: { + apply: (editor, color) => + editor.chain().focus().setHighlight({ color }).run(), + clear: (editor) => editor.chain().focus().unsetHighlight().run(), + colors: editorHighlightPresets, + currentColor: (editor) => + editor.getAttributes('highlight').color as string | undefined, + }, + }, + ], + [ + { + action: (editor) => editor.chain().focus().setTextAlign('left').run(), + can: (editor) => + editor.can().chain().focus().setTextAlign('left').run(), + icon: AlignLeft, + isActive: (editor) => editor.isActive({ textAlign: 'left' }), + label: $t('ui.tiptap.toolbar.alignLeft'), + }, + { + action: (editor) => editor.chain().focus().setTextAlign('center').run(), + can: (editor) => + editor.can().chain().focus().setTextAlign('center').run(), + icon: AlignCenter, + isActive: (editor) => editor.isActive({ textAlign: 'center' }), + label: $t('ui.tiptap.toolbar.alignCenter'), + }, + { + action: (editor) => editor.chain().focus().setTextAlign('right').run(), + can: (editor) => + editor.can().chain().focus().setTextAlign('right').run(), + icon: AlignRight, + isActive: (editor) => editor.isActive({ textAlign: 'right' }), + label: $t('ui.tiptap.toolbar.alignRight'), + }, + ], + ]; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/types.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/types.ts new file mode 100644 index 0000000..adf21bf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/types.ts @@ -0,0 +1,87 @@ +import type { Extensions, JSONContent } from '@tiptap/core'; +import type { Editor } from '@tiptap/vue-3'; + +import type { Component } from 'vue'; + +declare module '@tiptap/core' { + interface Commands { + imageUpload: { + uploadImage: () => ReturnType; + }; + } +} + +export interface ImageUploadOptions { + /** 允许的文件类型,默认 'image/*' */ + accept?: string; + /** 最大文件大小(字节),默认 5MB */ + maxSize?: number; + /** 上传失败回调,未提供时使用 alert 弹窗提示 */ + onUploadError?: (error: unknown) => void; + /** 上传函数,返回图片 URL,可选 onProgress 回调报告上传进度 */ + upload: ( + file: File, + onProgress?: (percent: number) => void, + ) => Promise; +} + +export interface TipTapProps { + editable?: boolean; + extensions?: Extensions; + imageUpload?: ImageUploadOptions; + minHeight?: number | string; + maxHeight?: number | string; + placeholder?: string; + previewable?: boolean; + toolbar?: boolean; +} + +export interface TipTapPreviewProps { + class?: any; + content?: string; + minHeight?: number | string; +} + +export interface VbenTiptapChangeEvent { + html: string; + json: JSONContent; + text: string; +} + +export interface VbenTiptapExtensionOptions { + imageUpload?: ImageUploadOptions; + /** 内部使用:追踪 blob URL 以便组件销毁时清理 */ + _blobUrlTracker?: Set; + placeholder?: string; +} + +export interface ToolbarAction { + action: (editor: Editor) => void; + active?: { + attrs?: Record; + name: string; + }; + can?: (editor: Editor) => boolean; + icon?: Component; + indicatorColor?: (editor: Editor) => string | undefined; + isActive?: (editor: Editor) => boolean; + label: string; + menu?: { + items: ToolbarMenuItem[]; + }; + palette?: { + apply: (editor: Editor, color: string) => void; + clear?: (editor: Editor) => void; + colors: string[]; + currentColor?: (editor: Editor) => string | undefined; + }; + triggerText?: ((editor?: Editor) => string) | string; +} + +export interface ToolbarMenuItem { + action: (editor: Editor) => void; + can?: (editor: Editor) => boolean; + isActive?: (editor: Editor) => boolean; + label: string; + shortLabel: string; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/use-tiptap-toolbar.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/use-tiptap-toolbar.ts new file mode 100644 index 0000000..25d98cb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/tiptap/use-tiptap-toolbar.ts @@ -0,0 +1,176 @@ +import type { Editor } from '@tiptap/vue-3'; + +import type { ShallowRef } from 'vue'; + +import type { ToolbarAction, ToolbarMenuItem } from './types'; + +import { cn } from '@vben-core/shared/utils'; + +interface UseTiptapToolbarOptions { + editable: () => boolean; + editor: Readonly>; +} + +export function useTiptapToolbar(options: UseTiptapToolbarOptions) { + const getEditor = () => options.editor.value; + + function getActionIndicatorColor(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor || !action.indicatorColor) { + return undefined; + } + + return action.indicatorColor(currentEditor); + } + + function getPaletteCurrentColor(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor || !action.palette?.currentColor) { + return undefined; + } + + return action.palette.currentColor(currentEditor); + } + + function canRunAction(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor || !options.editable()) { + return false; + } + + return action.can ? action.can(currentEditor) : true; + } + + function canRunMenuItem(item: ToolbarMenuItem) { + const currentEditor = getEditor(); + + if (!currentEditor || !options.editable()) { + return false; + } + + return item.can ? item.can(currentEditor) : true; + } + + function isActionActive(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor) { + return false; + } + + if (action.isActive) { + return action.isActive(currentEditor); + } + + if (!action.active) { + return false; + } + + return currentEditor.isActive(action.active.name, action.active.attrs); + } + + function isMenuItemActive(item: ToolbarMenuItem, currentEditor?: Editor) { + const targetEditor = currentEditor ?? getEditor(); + + if (!targetEditor || !item.isActive) { + return false; + } + + return item.isActive(targetEditor); + } + + function runAction(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor || !options.editable()) { + return; + } + + if (action.menu || action.palette) { + return; + } + + action.action(currentEditor); + } + + function runMenuItem(item: ToolbarMenuItem) { + const currentEditor = getEditor(); + + if (!currentEditor || !options.editable()) { + return; + } + + item.action(currentEditor); + } + + function applyPaletteColor(action: ToolbarAction, color: string) { + const currentEditor = getEditor(); + + if (!currentEditor || !action.palette) { + return; + } + + action.palette.apply(currentEditor, color); + } + + function clearPaletteColor(action: ToolbarAction) { + const currentEditor = getEditor(); + + if (!currentEditor || !action.palette?.clear) { + return; + } + + action.palette.clear(currentEditor); + } + + function getToolbarButtonClass(action: ToolbarAction) { + return cn( + 'text-muted-foreground relative rounded-[10px] border border-transparent bg-transparent shadow-none', + 'transition-[transform,color,background-color,border-color,box-shadow] duration-200 ease-out', + 'enabled:hover:border-border enabled:hover:-translate-y-px disabled:opacity-45', + 'enabled:hover:bg-accent enabled:hover:text-foreground', + isActionActive(action) && + 'bg-accent border-primary/30 shadow-primary text-primary', + ); + } + + function getPaletteSwatchClass(action: ToolbarAction, color: string) { + return cn( + 'border-border inline-flex size-8 items-center justify-center rounded-full border', + 'shadow-accent', + 'transition-[transform,box-shadow,border-color] duration-200 ease-out', + 'hover:-translate-y-px hover:scale-[1.04]', + getPaletteCurrentColor(action) === color && + 'border-primary shadow-primary', + ); + } + + function getMenuItemClass(item: ToolbarMenuItem) { + return cn( + 'flex items-center gap-2 rounded-lg p-2 text-left text-sm transition-colors', + 'disabled:cursor-not-allowed disabled:opacity-45', + isMenuItemActive(item) + ? 'bg-accent text-foreground' + : 'hover:bg-accent hover:text-foreground text-muted-foreground', + ); + } + + return { + applyPaletteColor, + canRunAction, + canRunMenuItem, + clearPaletteColor, + getActionIndicatorColor, + getMenuItemClass, + getPaletteCurrentColor, + getPaletteSwatchClass, + getToolbarButtonClass, + isActionActive, + isMenuItemActive, + runAction, + runMenuItem, + }; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/types.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/types.ts new file mode 100644 index 0000000..9a98978 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/types.ts @@ -0,0 +1,24 @@ +import type { Component } from 'vue'; + +export interface VbenPluginsFormOptions { + useVbenForm: (...args: any[]) => any; +} + +export interface VbenPluginsModalOptions { + useVbenModal?: () => any; +} + +export interface VbenPluginsMessageOptions { + useMessage?: () => any; +} + +export interface VbenPluginsComponentsOptions { + [key: string]: Component; +} + +export interface VbenPluginsOptions { + form?: VbenPluginsFormOptions; + modal?: VbenPluginsModalOptions; + message?: VbenPluginsMessageOptions; + components?: VbenPluginsComponentsOptions; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/README.md b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/README.md new file mode 100644 index 0000000..2dfd2b2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/README.md @@ -0,0 +1,50 @@ +# VXE Table Plugin + +基于 vxe-table 和 vxe-pc-ui 的表格组件插件。 + +## 导出 + +| 导出 | 类型 | 说明 | +| --------------------- | ---- | -------------- | +| `setupVbenVxeTable` | 函数 | 初始化配置函数 | +| `useVbenVxeGrid` | 函数 | 表格组合式函数 | +| `VbenVxeGrid` | 组件 | 表格组件 | +| `VxeTableGridColumns` | 类型 | 表格列类型 | +| `VxeTableGridOptions` | 类型 | 表格配置类型 | +| `VxeGridProps` | 类型 | 表格 Props | +| `VxeGridListeners` | 类型 | 表格事件类型 | + +## 使用 + +```ts +import { + setupVbenVxeTable, + useVbenVxeGrid, + VbenVxeGrid, +} from '@vben/plugins/vxe-table'; +``` + +## 初始化 + +在应用入口处调用: + +```ts +import { setupVbenVxeTable } from '@vben/plugins/vxe-table'; +import { useVbenForm } from '@vben-core/form-ui'; + +setupVbenVxeTable({ + configVxeTable: (vxeUI) => { + // 配置 VXE Table + }, + useVbenForm, +}); +``` + +## 类型 + +```ts +import type { + VxeTableGridOptions, + VxeGridProps, +} from '@vben/plugins/vxe-table'; +``` diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/api.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/api.ts new file mode 100644 index 0000000..86fe6a1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/api.ts @@ -0,0 +1,184 @@ +import type { VxeGridInstance } from 'vxe-table'; + +import type { + BaseFormComponentType, + ExtendedFormApi, +} from '@vben-core/form-ui'; + +import type { VxeGridProps } from './types'; +import type { ViewedRowHelper } from './use-viewed-row'; + +import { toRaw } from 'vue'; + +import { Store } from '@vben-core/shared/store'; +import { + bindMethods, + isBoolean, + isFunction, + mergeWithArrayOverride, + StateHandler, +} from '@vben-core/shared/utils'; + +function getDefaultState(): VxeGridProps { + return { + class: '', + gridClass: '', + gridOptions: {}, + gridEvents: {}, + formOptions: undefined, + showSearchForm: true, + }; +} + +export class VxeGridApi< + T extends Record = any, + D extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> { + public formApi = {} as ExtendedFormApi; + + // private prevState: null | VxeGridProps = null; + public grid = {} as VxeGridInstance; + public state: null | VxeGridProps = null; + + public store: Store>; + + /** + * 已读行 helper(在 mount 中初始化,业务能力全部封装在 useViewedRow 中) + */ + public viewedRowHelper: null | ViewedRowHelper = null; + + private isMounted = false; + + private stateHandler: StateHandler; + + constructor(options: VxeGridProps = {} as VxeGridProps) { + const storeState = { ...options }; + + const defaultState = getDefaultState(); + this.store = new Store>( + mergeWithArrayOverride(storeState, defaultState) as VxeGridProps, + ); + + this.store.subscribe((state) => { + // this.prevState = this.state; + this.state = state; + }); + + this.state = this.store.state; + this.stateHandler = new StateHandler(); + bindMethods(this); + } + + /** + * 清除所有已读状态 + */ + clearViewedRows() { + this.viewedRowHelper?.clearViewed(); + } + + /** + * 获取所有已读的 key 集合(返回副本,避免外部修改内部状态) + */ + getViewedKeys(): Set { + const raw = this.viewedRowHelper?.viewedSet.value; + return raw ? new Set(raw) : new Set(); + } + + /** + * 判断某行是否已读 + */ + isRowViewed(record: T): boolean { + return this.viewedRowHelper?.isViewed(record) ?? false; + } + + /** + * 批量标记行为已读 + */ + markKeysAsViewed(keys: Array) { + this.viewedRowHelper?.markKeysAsViewed(keys); + } + + /** + * 标记某行为已读 + */ + markRowAsViewed(record: T) { + this.viewedRowHelper?.markAsViewed(record); + } + + mount(instance: null | VxeGridInstance, formApi: ExtendedFormApi) { + if (!this.isMounted && instance) { + this.grid = instance; + this.formApi = formApi; + this.stateHandler.setConditionTrue(); + this.isMounted = true; + } + } + + async query(params: Record = {}) { + try { + await this.grid.commitProxy('query', toRaw(params)); + } catch (error) { + console.error('Error occurred while querying:', error); + } + } + + async reload(params: Record = {}) { + try { + await this.grid.commitProxy('reload', toRaw(params)); + } catch (error) { + console.error('Error occurred while reloading:', error); + } + } + + /** + * 移除指定 key 的已读状态 + */ + removeViewedKeys(keys: Array) { + this.viewedRowHelper?.removeKeys(keys); + } + + setGridOptions(options: Partial['gridOptions']>) { + this.setState({ + gridOptions: options, + }); + } + + setLoading(isLoading: boolean) { + this.setState({ + gridOptions: { + loading: isLoading, + }, + }); + } + + setState( + stateOrFn: + | ((prev: VxeGridProps) => Partial>) + | Partial>, + ) { + if (isFunction(stateOrFn)) { + this.store.setState((prev) => { + return mergeWithArrayOverride(stateOrFn(prev), prev); + }); + } else { + this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev)); + } + } + + toggleSearchForm(show?: boolean) { + this.setState({ + showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm, + }); + // nextTick(() => { + // this.grid.recalculate(); + // }); + return this.state?.showSearchForm; + } + + unmount() { + this.isMounted = false; + this.stateHandler.reset(); + this.viewedRowHelper = null; + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/extends.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/extends.ts new file mode 100644 index 0000000..a6cf4ca --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/extends.ts @@ -0,0 +1,81 @@ +import type { VxeGridProps, VxeUIExport } from 'vxe-table'; + +import type { Recordable } from '@vben/types'; + +import type { VxeGridApi } from './api'; + +import { formatDate, formatDateTime, isFunction } from '@vben/utils'; + +export function extendProxyOptions( + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + [ + 'query', + 'querySuccess', + 'queryError', + 'queryAll', + 'queryAllSuccess', + 'queryAllError', + ].forEach((key) => { + extendProxyOption(key, api, options, getFormValues); + }); +} + +function extendProxyOption( + key: string, + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + const { proxyConfig } = options; + const configFn = (proxyConfig?.ajax as Recordable)?.[key]; + if (!isFunction(configFn)) { + return options; + } + + const wrapperFn = async ( + params: Recordable, + customValues: Recordable, + ...args: Recordable[] + ) => { + const formValues = getFormValues(); + const data = await configFn( + params, + { + /** + * 开启toolbarConfig.refresh功能 + * 点击刷新按钮 这里的值为PointerEvent 会携带错误参数 + */ + ...(customValues instanceof PointerEvent ? {} : customValues), + ...formValues, + }, + ...args, + ); + return data; + }; + api.setState({ + gridOptions: { + proxyConfig: { + ajax: { + [key]: wrapperFn, + }, + }, + }, + }); +} + +export function extendsDefaultFormatter(vxeUI: VxeUIExport) { + vxeUI.formats.add('formatDate', { + tableCellFormatMethod({ cellValue }) { + return formatDate(cellValue); + }, + }); + + vxeUI.formats.add('formatDateTime', { + tableCellFormatMethod({ cellValue }) { + return formatDateTime(cellValue); + }, + }); +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/index.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/index.ts new file mode 100644 index 0000000..6f0efe4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/index.ts @@ -0,0 +1,10 @@ +export { setupVbenVxeTable } from './init'; +export type { VxeTableGridColumns, VxeTableGridOptions } from './types'; +export * from './use-vxe-grid'; + +export { default as VbenVxeGrid } from './use-vxe-grid.vue'; +export type { + VxeGridListeners, + VxeGridProps, + VxeGridPropTypes, +} from 'vxe-table'; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/init.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/init.ts new file mode 100644 index 0000000..61b6ca7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/init.ts @@ -0,0 +1,142 @@ +import type { SetupVxeTable } from './types'; + +import { defineComponent, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { + VxeButton, + VxeCheckbox, + VxeIcon, + VxeInput, + VxeLoading, + VxeModal, + VxeNumberInput, + VxePager, + VxeRadioGroup, + VxeSelect, + VxeTooltip, + VxeUI, + VxeUpload, +} from 'vxe-pc-ui'; +import enUS from 'vxe-pc-ui/lib/language/en-US'; // 导入默认的语言 +import zhCN from 'vxe-pc-ui/lib/language/zh-CN'; +import { + VxeColgroup, + VxeColumn, + VxeGrid, + VxeTable, + VxeToolbar, +} from 'vxe-table'; + +import { injectPluginsOptions } from '../plugins-context'; +import { extendsDefaultFormatter } from './extends'; // 是否加载过 + +// 是否加载过 +let isInit = false; + +let tableFormFactory: ((...args: any[]) => any) | undefined; + +function normalizeVxeLocale>(localeModule: T) { + return ( + localeModule && + typeof localeModule === 'object' && + 'default' in localeModule + ? localeModule.default + : localeModule + ) as T; +} + +export function useTableForm(...args: any[]) { + const pluginsOptions = injectPluginsOptions(); + const contextFormFactory = pluginsOptions?.form?.useVbenForm; + + const factory = tableFormFactory || contextFormFactory; + if (!factory) { + throw new Error( + 'useTableForm is not initialized. Please provide useVbenForm via setupVbenVxeTable() or providePluginsOptions()', + ); + } + + return factory(...args); +} + +// 部分组件,如果没注册,vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积 +const createVirtualComponent = (name = '') => { + return defineComponent({ + name, + }); +}; + +export function initVxeTable() { + if (isInit) { + return; + } + + VxeUI.component(VxeTable); + VxeUI.component(VxeColumn); + VxeUI.component(VxeColgroup); + VxeUI.component(VxeGrid); + VxeUI.component(VxeToolbar); + + VxeUI.component(VxeButton); + // VxeUI.component(VxeButtonGroup); + VxeUI.component(VxeCheckbox); + // VxeUI.component(VxeCheckboxGroup); + VxeUI.component(createVirtualComponent('VxeForm')); + // VxeUI.component(VxeFormGather); + // VxeUI.component(VxeFormItem); + VxeUI.component(VxeIcon); + VxeUI.component(VxeInput); + // VxeUI.component(VxeList); + VxeUI.component(VxeLoading); + VxeUI.component(VxeModal); + VxeUI.component(VxeNumberInput); + // VxeUI.component(VxeOptgroup); + // VxeUI.component(VxeOption); + VxeUI.component(VxePager); + // VxeUI.component(VxePulldown); + // VxeUI.component(VxeRadio); + // VxeUI.component(VxeRadioButton); + VxeUI.component(VxeRadioGroup); + VxeUI.component(VxeSelect); + // VxeUI.component(VxeSwitch); + // VxeUI.component(VxeTextarea); + VxeUI.component(VxeTooltip); + VxeUI.component(VxeUpload); + + isInit = true; +} + +export function setupVbenVxeTable(setupOptions: SetupVxeTable) { + const { configVxeTable, useVbenForm: useVbenFormFromParam } = setupOptions; + + initVxeTable(); + + // 优先使用参数传入的 useVbenForm,否则清空让 context 注入生效 + if (useVbenFormFromParam) { + tableFormFactory = useVbenFormFromParam; + } + const { isDark, locale } = usePreferences(); + + const localMap = { + 'zh-CN': normalizeVxeLocale(zhCN), + 'en-US': normalizeVxeLocale(enUS), + }; + + watch( + [() => isDark.value, () => locale.value], + ([isDarkValue, localeValue]) => { + VxeUI.setTheme(isDarkValue ? 'dark' : 'light'); + VxeUI.setI18n(localeValue, localMap[localeValue]); + VxeUI.setLanguage(localeValue); + }, + { + immediate: true, + }, + ); + + extendsDefaultFormatter(VxeUI); + + configVxeTable(VxeUI); +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/style.css b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/style.css new file mode 100644 index 0000000..89d6733 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/style.css @@ -0,0 +1,128 @@ +@reference "@vben/tailwind-config/theme"; + +:root .vxe-grid { + --vxe-ui-font-color: hsl(var(--foreground)); + --vxe-ui-font-primary-color: hsl(var(--primary)); + + /* --vxe-ui-font-lighten-color: #babdc0; + --vxe-ui-font-darken-color: #86898e; */ + --vxe-ui-font-disabled-color: hsl(var(--foreground) / 50%); + + /* base */ + --vxe-ui-base-popup-border-color: hsl(var(--border)); + --vxe-ui-input-disabled-color: hsl(var(--border) / 60%); + + /* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */ + + /* layout */ + --vxe-ui-layout-background-color: hsl(var(--background)); + --vxe-ui-table-resizable-line-color: hsl(var(--heavy)); + + /* --vxe-ui-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px hsl(var(--accent)); + --vxe-ui-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px hsl(var(--accent)); */ + + /* input */ + --vxe-ui-input-border-color: hsl(var(--border)); + + /* --vxe-ui-input-placeholder-color: #8d9095; */ + + /* --vxe-ui-input-disabled-background-color: #262727; */ + + /* loading */ + --vxe-ui-loading-background-color: hsl(var(--overlay-content)); + + /* table */ + --vxe-ui-table-header-background-color: hsl(var(--accent)); + --vxe-ui-table-border-color: hsl(var(--border)); + --vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover)); + --vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%); + --vxe-ui-table-row-hover-striped-background-color: hsl(var(--accent)); + --vxe-ui-table-row-radio-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-radio-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-checkbox-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-checkbox-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-current-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-current-background-color: hsl(var(--accent-hover)); + --vxe-ui-font-primary-tinge-color: hsl(var(--primary)); + --vxe-ui-font-primary-lighten-color: hsl(var(--primary) / 60%); + --vxe-ui-font-primary-darken-color: hsl(var(--primary)); + + height: auto !important; + + /* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ +} + +.vxe-pager { + .vxe-pager--prev-btn:not(.is--disabled):active, + .vxe-pager--next-btn:not(.is--disabled):active, + .vxe-pager--num-btn:not(.is--disabled):active, + .vxe-pager--jump-prev:not(.is--disabled):active, + .vxe-pager--jump-next:not(.is--disabled):active, + .vxe-pager--prev-btn:not(.is--disabled):focus, + .vxe-pager--next-btn:not(.is--disabled):focus, + .vxe-pager--num-btn:not(.is--disabled):focus, + .vxe-pager--jump-prev:not(.is--disabled):focus, + .vxe-pager--jump-next:not(.is--disabled):focus { + color: hsl(var(--accent-foreground)); + background-color: hsl(var(--accent)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 0 0 1px hsl(var(--border)); + } + + .vxe-pager--wrapper { + display: flex; + align-items: center; + } + + .vxe-pager--sizes { + margin-right: auto; + } +} + +.vxe-pager--wrapper { + @apply justify-center md:justify-end; +} + +.vxe-tools--operate { + margin-right: 0.25rem; + margin-left: 0.75rem; +} + +.vxe-table-custom--checkbox-option:hover { + background: none !important; +} + +.vxe-toolbar { + padding: 0; +} + +.vxe-buttons--wrapper:not(:empty), +.vxe-tools--operate:not(:empty), +.vxe-tools--wrapper:not(:empty) { + padding: 0.6em 0; +} + +.vxe-tools--operate:not(:has(button)) { + margin-left: 0; +} + +.vxe-grid--layout-header-wrapper { + overflow: visible; +} + +.vxe-grid--layout-body-content-wrapper { + overflow: hidden; +} + +/* 已读行默认样式 */ +.vxe-row--viewed { + color: hsl(var(--foreground) / 50%); + + .vxe-body--column { + opacity: 0.9; + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/types.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/types.ts new file mode 100644 index 0000000..936562f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/types.ts @@ -0,0 +1,185 @@ +import type { + VxeGridListeners, + VxeGridPropTypes, + VxeGridProps as VxeTableGridProps, + VxeTablePropTypes, + VxeUIExport, +} from 'vxe-table'; + +import type { Ref } from 'vue'; + +import type { ClassType, DeepPartial } from '@vben/types'; + +import type { BaseFormComponentType, VbenFormProps } from '@vben-core/form-ui'; + +import { useVbenForm } from '@vben-core/form-ui'; + +export interface VxePaginationInfo { + currentPage: number; + pageSize: number; + total: number; +} + +interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig { + /** 是否显示切换搜索表单的按钮 */ + search?: boolean; +} + +export type VxeTableGridColumns = VxeTableGridOptions['columns']; + +export interface VxeTableGridOptions extends VxeTableGridProps { + /** 工具栏配置 */ + toolbarConfig?: ToolbarConfigOptions; +} + +export interface SeparatorOptions { + show?: boolean; + backgroundColor?: string; +} + +/** + * 自定义存储适配器接口 + * 用户可接入任意后端(API、IndexedDB wrapper、第三方库等) + */ +export interface ViewedRowStorageAdapter { + /** 读取所有已查看的 key 列表 */ + getKeys(): Promise>; + + /** 移除所有已查看数据 */ + removeKeys(): Promise; + + /** 持久化已查看的 key 列表 */ + setKeys(keys: Array): Promise; +} + +/** + * 已读行持久化 — 公共基础字段 + */ +interface ViewedRowPersistBase { + /** 持久化数据的存活时间(毫秒) */ + ttl?: number; + /** 最大缓存数量,超出时淘汰最早标记的 key(FIFO),默认 100 */ + maxSize?: number; +} + +/** + * 已读行持久化配置(按 type 区分的联合类型) + * + * - 'memory' → 仅内存,不持久化 + * - 'localStorage' → 使用 localStorage 整体存储,key 必传 + * - 'sessionStorage' → 使用 sessionStorage 整体存储,key 必传 + * - 'indexedDB' → 使用 IndexedDB 单条存储,key 必传 + * - 'custom' → 用户自定义存储适配器,storage 必传 + */ +export type ViewedRowPersistOptions = + | ({ + /** IndexedDB 数据库名称,默认 'viewed-table-db' */ + dbName?: string; + /** IndexedDB 数据库版本,默认 1 */ + dbVersion?: number; + /** 存储 key / prefix(必传) */ + key: string; + /** IndexedDB 对象存储名称,默认 'viewed-table-row' */ + storeName?: string; + type: 'indexedDB'; + } & ViewedRowPersistBase) + | ({ + /** 存储 key(必传) */ + key: string; + type: 'localStorage' | 'sessionStorage'; + } & ViewedRowPersistBase) + | ({ + /** 自定义存储适配器(必传) */ + storage: ViewedRowStorageAdapter; + type: 'custom'; + } & ViewedRowPersistBase) + | (ViewedRowPersistBase & { + type: 'memory'; + }); + +/** + * 已查看row设置 + */ +export interface ViewedRowOptions { + /** 点击 CellOperation 中匹配的 code 时,自动将该行标记为已读 */ + actionCodes?: string | string[]; + /** 行唯一标识字段,默认取 gridOptions.rowConfig.keyField,最终兜底 'id' */ + keyField?: string; + /** 已查看的行key列表 */ + viewedKeys?: Array | Ref>; + /** + * 持久化配置 + * - 传 string:使用内置 localStorage,值为 storage key(向后兼容) + * - 传 object:高级配置 + * - 不传:不持久化(等同于 memory) + */ + persist?: string | ViewedRowPersistOptions; + rowClassName?: VxeTablePropTypes.RowClassName; + rowStyle?: VxeTablePropTypes.RowStyle; +} + +export interface VxeGridProps< + T extends Record = any, + D extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> { + /** + * 数据 + */ + tableData?: any[]; + /** + * 标题 + */ + tableTitle?: string; + /** + * 标题帮助 + */ + tableTitleHelp?: string; + /** + * 组件class + */ + class?: ClassType; + /** + * vxe-grid class + */ + gridClass?: ClassType; + /** + * vxe-grid 配置 + */ + gridOptions?: DeepPartial>; + /** + * vxe-grid 事件 + */ + gridEvents?: DeepPartial>; + /** + * 表单配置 + */ + formOptions?: VbenFormProps; + /** + * 显示搜索表单 + */ + showSearchForm?: boolean; + /** + * 搜索表单与表格主体之间的分隔条 + */ + separator?: boolean | SeparatorOptions; + /** + * 已读行功能 + */ + viewedRowOptions?: boolean | ViewedRowOptions; +} + +export type ExtendedVxeGridApi< + D extends Record = any, + F extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +> = import('./api').VxeGridApi & { + useStore: >>( + selector?: (state: NoInfer>) => S, + ) => Readonly>; +}; + +export interface SetupVxeTable { + configVxeTable: (ui: VxeUIExport) => void; + useVbenForm?: typeof useVbenForm; +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-viewed-row.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-viewed-row.ts new file mode 100644 index 0000000..71d838a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-viewed-row.ts @@ -0,0 +1,537 @@ +/* oxlint-disable unicorn/no-nested-ternary */ +import type { VxeGridProps as VxeTableGridProps } from 'vxe-table'; + +import type { + ViewedRowOptions, + ViewedRowPersistOptions, + ViewedRowStorageAdapter, +} from './types'; + +import { isRef, shallowRef, toRaw, triggerRef, watch } from 'vue'; + +import { isBoolean, isFunction } from '@vben/utils'; + +import { + IndexedDBDriver, + LocalStorageDriver, + StorageManager, +} from '@vben-core/shared/cache'; + +import { useDebounceFn } from '@vueuse/core'; + +const DEFAULT_VIEWED_CLASS = 'vxe-row--viewed'; + +// ========== 持久化策略 ========== + +/** + * localStorage / sessionStorage 适配器 + * 整体存储:key → [1, 2, 3] + */ +function createWebStorageAdapter( + storageType: 'localStorage' | 'sessionStorage', + key: string, + ttl?: number, +): ViewedRowStorageAdapter { + const manager = new StorageManager({ + driver: new LocalStorageDriver({ storageType }), + }); + + return { + async getKeys() { + const stored = await manager.getItem>(key); + return stored ?? []; + }, + async removeKeys() { + await manager.removeItem(key); + }, + async setKeys(keys) { + await manager.setItem(key, keys, ttl); + }, + }; +} + +/** + * IndexedDB 适配器 + * 单条存储:prefix:1 → { expiry, value: 1 } + */ +function createIndexedDBAdapter( + opts: Extract, +): ViewedRowStorageAdapter { + const prefix = opts.key; + const manager = new StorageManager({ + driver: new IndexedDBDriver({ + dbName: opts.dbName || 'viewed-table-db', + dbVersion: opts.dbVersion || 1, + storeName: opts.storeName || 'viewed-table-row', + }), + prefix, + }); + + return { + async getKeys() { + try { + // 通过 StorageManager 获取当前前缀下所有 key,再逐条读取(自动过滤过期) + const shortKeys = await manager.keys(); + + const results: Array = []; + for (const shortKey of shortKeys) { + const value = await manager.getItem(shortKey); + if (value !== null) { + results.push(value); + } + } + return results; + } catch (error) { + console.error('[viewedRow] indexedDB restore failed:', error); + return []; + } + }, + async removeKeys() { + try { + await manager.clear(); + } catch (error) { + console.error('[viewedRow] indexedDB clear failed:', error); + } + }, + async setKeys(keys) { + try { + const newKeySet = new Set(keys.map(String)); + // 获取已存在的 key,避免重复写入刷新过期时间 + const existingKeys = await manager.keys(); + const existingKeySet = new Set(existingKeys); + + // 只写入新增的 key,不覆盖已有记录的过期时间 + const toAdd = keys.filter((key) => !existingKeySet.has(String(key))); + if (toAdd.length > 0) { + await Promise.all( + toAdd.map((key) => manager.setItem(String(key), key, opts.ttl)), + ); + } + + // 清理不在新集合中的旧 key + const toRemove = existingKeys.filter((k) => !newKeySet.has(k)); + if (toRemove.length > 0) { + await Promise.all(toRemove.map((k) => manager.removeItem(k))); + } + } catch (error) { + console.error('[viewedRow] indexedDB persist failed:', error); + } + }, + }; +} + +/** + * 根据 persist 配置创建存储适配器 + */ +function createStorageAdapter( + persist?: string | ViewedRowPersistOptions, +): null | ViewedRowStorageAdapter { + if (!persist) return null; + + // 简写模式:string → localStorage + if (typeof persist === 'string') { + return createWebStorageAdapter('localStorage', persist); + } + + switch (persist.type) { + case 'custom': { + // 用户自定义适配器,解除 Vue 响应式代理 + return toRaw(persist.storage); + } + case 'indexedDB': { + return createIndexedDBAdapter(persist); + } + case 'localStorage': { + return createWebStorageAdapter('localStorage', persist.key, persist.ttl); + } + case 'memory': { + return null; + } + case 'sessionStorage': { + return createWebStorageAdapter( + 'sessionStorage', + persist.key, + persist.ttl, + ); + } + default: { + return null; + } + } +} + +// ========== maxSize 淘汰 ========== + +/** + * 强制执行 maxSize 限制,超出时淘汰最早插入的 key(FIFO) + */ +function enforceMaxSize(set: Set, maxSize: number): void { + if (maxSize > 0 && set.size > maxSize) { + const iterator = set.values(); + while (set.size > maxSize) { + const oldest = iterator.next().value; + if (oldest !== undefined) { + set.delete(oldest); + } + } + } +} + +// ========== 核心 composable ========== + +export function useViewedRow( + options: ViewedRowOptions & { keyField: string }, +) { + // ========== 解析持久化配置 ========== + const persistOpts: null | ViewedRowPersistOptions = options.persist + ? typeof options.persist === 'string' + ? { key: options.persist, type: 'localStorage' } + : options.persist + : null; + + const adapter = createStorageAdapter(options.persist); + const maxSize = persistOpts?.maxSize ?? 100; + + // ========== 初始化已读集合 ========== + const viewedSet = shallowRef>(new Set()); + + // ========== 持久化(防抖) ========== + function persistImmediate() { + if (!adapter) return; + adapter.setKeys([...viewedSet.value]).catch((error) => { + console.error('[viewedRow] persist failed:', error); + }); + } + + const persist = useDebounceFn(persistImmediate, 300); + + // ========== 从存储恢复 ========== + async function restoreFromStorage(): Promise { + if (!adapter) return; + + try { + const stored = await adapter.getKeys(); + if (stored && stored.length > 0) { + for (const key of stored) { + viewedSet.value.add(key); + } + if (maxSize > 0) { + enforceMaxSize(viewedSet.value, maxSize); + } + triggerRef(viewedSet); + } + } catch (error) { + console.error('[viewedRow] restore failed:', error); + } + } + + // 先恢复存储,再合并外部 viewedKeys,确保 viewedKeys 是最新插入的(最后被淘汰) + restoreFromStorage().then(() => { + if (options.viewedKeys) { + const keys = isRef(options.viewedKeys) + ? options.viewedKeys.value + : options.viewedKeys; + updateViewedSet((set) => { + let changed = false; + for (const key of keys) { + if (!set.has(key)) { + set.add(key); + changed = true; + } + } + return changed; + }); + } + }); + + // ========== 更新 viewedSet 的统一入口 ========== + function updateViewedSet(updater: (set: Set) => boolean) { + const changed = updater(viewedSet.value); + + if (changed) { + if (maxSize > 0) { + enforceMaxSize(viewedSet.value, maxSize); + } + triggerRef(viewedSet); + persist(); + } + } + + // ========== 监听外部 viewedKeys 变化(如果是 Ref) ========== + if (isRef(options.viewedKeys)) { + watch(options.viewedKeys, (newKeys) => { + updateViewedSet((set) => { + let changed = false; + for (const key of newKeys) { + if (!set.has(key)) { + set.add(key); + changed = true; + } + } + return changed; + }); + }); + } + + // ========== 标记已读 ========== + function markAsViewed(record: T) { + const key = (record as Record)[options.keyField] as + | number + | string; + if (key === null || key === undefined) return; + + updateViewedSet((set) => { + if (set.has(key)) return false; + set.add(key); + return true; + }); + } + + function markKeysAsViewed(keys: Array) { + updateViewedSet((set) => { + let changed = false; + for (const key of keys) { + if (!set.has(key)) { + set.add(key); + changed = true; + } + } + return changed; + }); + } + + // ========== 查询 ========== + function isViewed(record: T): boolean { + const key = (record as Record)[options.keyField] as + | number + | string; + return viewedSet.value.has(key); + } + + // ========== 清除 ========== + function clearViewed() { + const hadData = viewedSet.value.size > 0; + viewedSet.value.clear(); + + if (hadData) { + triggerRef(viewedSet); + } + + if (adapter) { + adapter.removeKeys().catch((error) => { + console.error('[viewedRow] clear persist failed:', error); + }); + } + } + + // ========== 移除指定 keys ========== + function removeKeys(keys: Array) { + updateViewedSet((set) => { + let changed = false; + for (const key of keys) { + if (set.has(key)) { + set.delete(key); + changed = true; + } + } + return changed; + }); + } + + // ========== rowClassName 函数 ========== + function getRowClassName(params: any): string { + if (!isViewed(params.row)) return ''; + + const { rowClassName } = options; + if (rowClassName === undefined || rowClassName === null) { + return DEFAULT_VIEWED_CLASS; + } + if (typeof rowClassName === 'string') { + return rowClassName; + } + if (isFunction(rowClassName)) { + return normalizeClassName(rowClassName(params)); + } + return DEFAULT_VIEWED_CLASS; + } + + // ========== rowStyle 函数 ========== + function getRowStyle(params: any): any { + if (!isViewed(params.row)) return undefined; + + const { rowStyle } = options; + if (rowStyle === undefined || rowStyle === null) { + return undefined; + } + if (isFunction(rowStyle)) { + return rowStyle(params); + } + return rowStyle; + } + + return { + clearViewed, + getRowClassName, + getRowStyle, + isViewed, + markAsViewed, + markKeysAsViewed, + removeKeys, + viewedSet, + }; +} + +export type ViewedRowHelper = ReturnType>; + +// ========== 工具函数 ========== + +function normalizeClassName(value: any): string { + if (!value) return ''; + if (typeof value === 'string') return value; + if (typeof value === 'object') { + return Object.entries(value) + .filter(([, v]) => v) + .map(([k]) => k) + .join(' '); + } + return ''; +} + +function mergeClassNames(...classNames: any[]): string { + return classNames + .map((c) => normalizeClassName(c)) + .filter(Boolean) + .join(' '); +} + +/** + * 包装 columns,拦截 CellOperation 的 onClick,根据 actionCodes 自动标记已读 + * 注意:columns 每次都是 cloneDeep 后的新对象,不存在重复包装问题 + */ +function wrapColumnsForViewedRow( + columns: any[], + actionCodes: string[], + markAsViewed: (record: any) => void, +): any[] { + return columns.map((column) => { + if (!column || typeof column !== 'object') return column; + + const nextColumn = { ...column }; + + if (nextColumn.cellRender?.name === 'CellOperation') { + const cellRender = { ...nextColumn.cellRender }; + const attrs = { ...cellRender.attrs }; + const originalOnClick = attrs.onClick; + + attrs.onClick = (params: { code: string; row: any }) => { + originalOnClick?.(params); + if (actionCodes.includes(params.code)) { + markAsViewed(params.row); + } + }; + + cellRender.attrs = attrs; + nextColumn.cellRender = cellRender; + } + + if (Array.isArray(nextColumn.children)) { + nextColumn.children = wrapColumnsForViewedRow( + nextColumn.children, + actionCodes, + markAsViewed, + ); + } + + return nextColumn; + }); +} + +/** + * 将 viewedRow 配置应用到 mergedOptions 上 + * 注入 rowClassName、rowStyle、columns 拦截 + */ +export function applyViewedRowOptions( + mergedOptions: VxeTableGridProps, + viewedRowConfig: boolean | ViewedRowOptions, + helper: ReturnType, +) { + // 从最新的配置中读取 rowClassName 和 rowStyle(支持运行时修改) + const viewedRowClassName = isBoolean(viewedRowConfig) + ? undefined + : viewedRowConfig.rowClassName; + const viewedRowStyle = isBoolean(viewedRowConfig) + ? undefined + : viewedRowConfig.rowStyle; + + // 注入 rowClassName + const originalRowClassName = mergedOptions.rowClassName; + mergedOptions.rowClassName = (params: any) => { + if (!helper.isViewed(params.row)) { + return normalizeClassName( + isFunction(originalRowClassName) + ? originalRowClassName(params) + : originalRowClassName, + ); + } + + let viewedClass: string; + if (viewedRowClassName === undefined || viewedRowClassName === null) { + viewedClass = DEFAULT_VIEWED_CLASS; + } else if (typeof viewedRowClassName === 'string') { + viewedClass = viewedRowClassName; + } else if (isFunction(viewedRowClassName)) { + viewedClass = normalizeClassName(viewedRowClassName(params)); + } else { + viewedClass = DEFAULT_VIEWED_CLASS; + } + + return mergeClassNames( + isFunction(originalRowClassName) + ? originalRowClassName(params) + : originalRowClassName, + viewedClass, + ); + }; + + // 注入 rowStyle + const originalRowStyle = mergedOptions.rowStyle; + mergedOptions.rowStyle = (params: any) => { + const originalStyle = isFunction(originalRowStyle) + ? originalRowStyle(params) + : originalRowStyle; + + if (!helper.isViewed(params.row)) { + return originalStyle || undefined; + } + + let viewedStyle: any; + if (viewedRowStyle === undefined || viewedRowStyle === null) { + viewedStyle = undefined; + } else if (isFunction(viewedRowStyle)) { + viewedStyle = viewedRowStyle(params); + } else { + viewedStyle = viewedRowStyle; + } + + if (!viewedStyle && !originalStyle) return undefined; + if (!originalStyle) return viewedStyle; + if (!viewedStyle) return originalStyle; + return { ...originalStyle, ...viewedStyle }; + }; + + // 拦截 CellOperation columns + const actionCodes = + !isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes + ? Array.isArray(viewedRowConfig.actionCodes) + ? viewedRowConfig.actionCodes + : [viewedRowConfig.actionCodes] + : []; + + if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) { + mergedOptions.columns = wrapColumnsForViewedRow( + mergedOptions.columns, + actionCodes, + helper.markAsViewed, + ); + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts new file mode 100644 index 0000000..219c002 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts @@ -0,0 +1,84 @@ +import type { VxeGridSlots, VxeGridSlotTypes } from 'vxe-table'; + +import type { SlotsType } from 'vue'; + +import type { BaseFormComponentType } from '@vben-core/form-ui'; + +import type { ExtendedVxeGridApi, VxeGridProps } from './types'; + +import { defineComponent, h, onBeforeUnmount } from 'vue'; + +import { useStore } from '@vben-core/shared/store'; + +import { VxeGridApi } from './api'; +import VxeGrid from './use-vxe-grid.vue'; + +type FilteredSlots = { + [K in keyof VxeGridSlots as K extends 'form' + ? never + : K]: VxeGridSlots[K]; +}; + +export function useVbenVxeGrid< + T extends Record = any, + D extends BaseFormComponentType = BaseFormComponentType, + P extends Record = Record, +>(options: VxeGridProps) { + // const IS_REACTIVE = isReactive(options); + const api = new VxeGridApi(options); + const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi< + T, + D, + P + >; + extendedApi.useStore = (selector) => { + return useStore(api.store, selector); + }; + + const Grid = defineComponent( + (props: VxeGridProps, { attrs, slots }) => { + onBeforeUnmount(() => { + api.unmount(); + }); + api.setState({ ...props, ...attrs } as Partial>); + return () => + h( + VxeGrid, + { + ...props, + ...attrs, + api: extendedApi as ExtendedVxeGridApi, + }, + slots, + ); + }, + { + name: 'VbenVxeGrid', + inheritAttrs: false, + slots: Object as SlotsType< + { + // 表格标题 + 'table-title': undefined; + // 工具栏左侧部分 + 'toolbar-actions': VxeGridSlotTypes.DefaultSlotParams; + // 工具栏右侧部分 + 'toolbar-tools': VxeGridSlotTypes.DefaultSlotParams; + } & FilteredSlots + >, + }, + ); + // Add reactivity support + // if (IS_REACTIVE) { + // watch( + // () => options, + // () => { + // api.setState(options); + // }, + // { immediate: true }, + // ); + // } + + return [Grid, extendedApi] as const; +} + +export type UseVbenVxeGrid = typeof useVbenVxeGrid; diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue new file mode 100644 index 0000000..e8ef009 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue @@ -0,0 +1,515 @@ + + + diff --git a/deploy/fba/fba-ui-src/packages/effects/plugins/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/plugins/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/plugins/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/effects/request/package.json b/deploy/fba/fba-ui-src/packages/effects/request/package.json new file mode 100644 index 0000000..e3a6073 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vben/request", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/request" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben/locales": "workspace:*", + "@vben/utils": "workspace:*", + "axios": "catalog:", + "qs": "catalog:" + }, + "devDependencies": { + "@types/qs": "catalog:", + "axios-mock-adapter": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/index.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/index.ts new file mode 100644 index 0000000..fae1b05 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/index.ts @@ -0,0 +1,2 @@ +export * from './request-client'; +export * from 'axios'; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/index.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/index.ts new file mode 100644 index 0000000..a44cd15 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/index.ts @@ -0,0 +1,3 @@ +export * from './preset-interceptors'; +export * from './request-client'; +export type * from './types'; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.test.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.test.ts new file mode 100644 index 0000000..27e8a30 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.test.ts @@ -0,0 +1,157 @@ +import type { AxiosRequestConfig } from 'axios'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FileDownloader } from './downloader'; + +describe('fileDownloader', () => { + let fileDownloader: FileDownloader; + const mockAxiosInstance = { + get: vi.fn(), + } as any; + + beforeEach(() => { + fileDownloader = new FileDownloader(mockAxiosInstance); + }); + + it('should create an instance of FileDownloader', () => { + expect(fileDownloader).toBeInstanceOf(FileDownloader); + }); + + it('should download a file and return a Blob', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should merge provided config with default config', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const customConfig: AxiosRequestConfig = { + headers: { 'Custom-Header': 'value' }, + }; + + const result = await fileDownloader.download(url, customConfig); + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + ...customConfig, + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + mockAxiosInstance.get.mockRejectedValueOnce(new Error('Network Error')); + await expect(fileDownloader.download(url)).rejects.toThrow('Network Error'); + }); + + it('should handle empty URL gracefully', async () => { + const url = ''; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); + + it('should handle null URL gracefully', async () => { + const url = null as unknown as string; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); +}); + +describe('fileDownloader use other method', () => { + let fileDownloader: FileDownloader; + + it('should call request using get', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + const mockAxiosInstance = { + request: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + mockAxiosInstance.request.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.request).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should call post', async () => { + const url = 'https://example.com/file'; + + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + const customConfig: AxiosRequestConfig = { + method: 'POST', + data: { name: 'aa' }, + }; + + await fileDownloader.download(url, customConfig); + + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + { name: 'aa' }, + { + method: 'POST', + responseType: 'blob', + responseReturn: 'body', + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + await expect(() => + fileDownloader.download(url, { method: 'postt' }), + ).rejects.toThrow( + 'RequestClient does not support method "POSTT". Please ensure the method is properly implemented in your RequestClient instance.', + ); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.ts new file mode 100644 index 0000000..6c30005 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/downloader.ts @@ -0,0 +1,60 @@ +import type { RequestClient } from '../request-client'; +import type { RequestClientConfig } from '../types'; + +type DownloadRequestConfig = { + /** + * 定义期望获得的数据类型。 + * raw: 原始的AxiosResponse,包括headers、status等。 + * body: 只返回响应数据的BODY部分(Blob) + */ + responseReturn?: 'body' | 'raw'; +} & Omit; + +class FileDownloader { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + /** + * 下载文件 + * @param url 文件的完整链接 + * @param config 配置信息,可选。 + * @returns 如果config.responseReturn为'body',则返回Blob(默认),否则返回RequestResponse + */ + public async download( + url: string, + config?: DownloadRequestConfig, + ): Promise { + const finalConfig: DownloadRequestConfig = { + responseReturn: 'body', + method: 'GET', + ...config, + responseType: 'blob', + }; + + // Prefer a generic request if available; otherwise, dispatch to method-specific calls. + const method = (finalConfig.method || 'GET').toUpperCase(); + const clientAny = this.client as any; + + if (typeof clientAny.request === 'function') { + return await clientAny.request(url, finalConfig); + } + const lower = method.toLowerCase(); + + if (typeof clientAny[lower] === 'function') { + if (['POST', 'PUT'].includes(method)) { + const { data, ...rest } = finalConfig as Record; + return await clientAny[lower](url, data, rest); + } + + return await clientAny[lower](url, finalConfig); + } + + throw new Error( + `RequestClient does not support method "${method}". Please ensure the method is properly implemented in your RequestClient instance.`, + ); + } +} + +export { FileDownloader }; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/interceptor.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/interceptor.ts new file mode 100644 index 0000000..f6d2ad8 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/interceptor.ts @@ -0,0 +1,40 @@ +import type { AxiosInstance, AxiosResponse } from 'axios'; + +import type { + RequestInterceptorConfig, + ResponseInterceptorConfig, +} from '../types'; + +const defaultRequestInterceptorConfig: RequestInterceptorConfig = { + fulfilled: (response) => response, + rejected: (error) => Promise.reject(error), +}; + +const defaultResponseInterceptorConfig: ResponseInterceptorConfig = { + fulfilled: (response: AxiosResponse) => response, + rejected: (error) => Promise.reject(error), +}; + +class InterceptorManager { + private axiosInstance: AxiosInstance; + + constructor(instance: AxiosInstance) { + this.axiosInstance = instance; + } + + addRequestInterceptor({ + fulfilled, + rejected, + }: RequestInterceptorConfig = defaultRequestInterceptorConfig) { + this.axiosInstance.interceptors.request.use(fulfilled, rejected); + } + + addResponseInterceptor({ + fulfilled, + rejected, + }: ResponseInterceptorConfig = defaultResponseInterceptorConfig) { + this.axiosInstance.interceptors.response.use(fulfilled, rejected); + } +} + +export { InterceptorManager }; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.test.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.test.ts new file mode 100644 index 0000000..4e8c6a9 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.test.ts @@ -0,0 +1,142 @@ +import type { RequestClient } from '../request-client'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { SSE } from './sse'; + +// 模拟 TextDecoder +const OriginalTextDecoder = globalThis.TextDecoder; + +beforeEach(() => { + vi.stubGlobal( + 'TextDecoder', + class { + private decoder = new OriginalTextDecoder(); + decode(value: Uint8Array, opts?: any) { + return this.decoder.decode(value, opts); + } + }, + ); +}); + +// 创建 fetch mock +const createFetchMock = (chunks: string[], ok = true) => { + const encoder = new TextEncoder(); + let index = 0; + return vi.fn().mockResolvedValue({ + ok, + status: ok ? 200 : 500, + body: { + getReader: () => ({ + read: async () => { + if (index < chunks.length) { + return { done: false, value: encoder.encode(chunks[index++]) }; + } + return { done: true, value: undefined }; + }, + }), + }, + }); +}; + +describe('sSE', () => { + let client: RequestClient; + let sse: SSE; + + beforeEach(() => { + vi.restoreAllMocks(); + client = { + getBaseUrl: () => 'http://localhost', + instance: { + interceptors: { + request: { + handlers: [], + }, + }, + }, + } as unknown as RequestClient; + sse = new SSE(client); + }); + + it('should call requestSSE when postSSE is used', async () => { + const spy = vi.spyOn(sse, 'requestSSE').mockResolvedValue(undefined); + await sse.postSSE('/test', { foo: 'bar' }, { headers: { a: '1' } }); + expect(spy).toHaveBeenCalledWith( + '/test', + { foo: 'bar' }, + { + headers: { a: '1' }, + method: 'POST', + }, + ); + }); + + it('should throw error if fetch response not ok', async () => { + vi.stubGlobal('fetch', createFetchMock([], false)); + await expect(sse.requestSSE('/bad')).rejects.toThrow( + 'HTTP error! status: 500', + ); + }); + + it('should trigger onMessage and onEnd callbacks', async () => { + const messages: string[] = []; + const onMessage = vi.fn((msg: string) => messages.push(msg)); + const onEnd = vi.fn(); + + vi.stubGlobal('fetch', createFetchMock(['hello', ' world'])); + + await sse.requestSSE('/sse', undefined, { onMessage, onEnd }); + + expect(onMessage).toHaveBeenCalledTimes(2); + expect(messages.join('')).toBe('hello world'); + // onEnd 不再带参数 + expect(onEnd).toHaveBeenCalled(); + }); + + it('should apply request interceptors', async () => { + const interceptor = vi.fn(async (config) => { + config.headers['x-test'] = 'intercepted'; + return config; + }); + (client.instance.interceptors.request as any).handlers.push({ + fulfilled: interceptor, + }); + + // 创建 fetch mock,并挂到全局 + const fetchMock = createFetchMock(['data']); + vi.stubGlobal('fetch', fetchMock); + + await sse.requestSSE('/sse', undefined, {}); + + expect(interceptor).toHaveBeenCalled(); + expect(fetchMock).toHaveBeenCalledWith( + 'http://localhost/sse', + expect.objectContaining({ + headers: expect.any(Headers), + }), + ); + + const calls = fetchMock.mock?.calls; + expect(calls).toBeDefined(); + expect(calls?.length).toBeGreaterThan(0); + + const init = calls?.[0]?.[1] as RequestInit; + expect(init).toBeDefined(); + + const headers = init?.headers as Headers; + expect(headers?.get('x-test')).toBe('intercepted'); + expect(headers?.get('accept')).toBe('text/event-stream'); + }); + + it('should throw error when no reader', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: true, + status: 200, + body: null, + }), + ); + await expect(sse.requestSSE('/sse')).rejects.toThrow('No reader'); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.ts new file mode 100644 index 0000000..aa2755c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/sse.ts @@ -0,0 +1,134 @@ +import type { AxiosRequestHeaders, InternalAxiosRequestConfig } from 'axios'; + +import type { RequestClient } from '../request-client'; +import type { SseRequestOptions } from '../types'; + +/** + * SSE模块 + */ +class SSE { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + + public async postSSE( + url: string, + data?: any, + requestOptions?: SseRequestOptions, + ) { + return this.requestSSE(url, data, { + ...requestOptions, + method: 'POST', + }); + } + + /** + * SSE请求方法 + * @param url - 请求URL + * @param data - 请求数据 + * @param requestOptions - SSE请求选项 + */ + public async requestSSE( + url: string, + data?: any, + requestOptions?: SseRequestOptions, + ) { + const baseUrl = this.client.getBaseUrl() || ''; + + let axiosConfig: InternalAxiosRequestConfig = { + url, + method: (requestOptions?.method as any) ?? 'GET', + headers: {} as AxiosRequestHeaders, + }; + const requestInterceptors = this.client.instance.interceptors + .request as any; + if ( + requestInterceptors.handlers && + requestInterceptors.handlers.length > 0 + ) { + for (const handler of requestInterceptors.handlers) { + if (typeof handler?.fulfilled === 'function') { + const next = await handler.fulfilled(axiosConfig as any); + if (next) axiosConfig = next as InternalAxiosRequestConfig; + } + } + } + + const merged = new Headers(); + Object.entries( + (axiosConfig.headers ?? {}) as Record, + ).forEach(([k, v]) => merged.set(k, String(v))); + if (requestOptions?.headers) { + new Headers(requestOptions.headers).forEach((v, k) => merged.set(k, v)); + } + if (!merged.has('accept')) { + merged.set('accept', 'text/event-stream'); + } + + let bodyInit = requestOptions?.body ?? data; + const ct = (merged.get('content-type') || '').toLowerCase(); + if ( + bodyInit && + typeof bodyInit === 'object' && + !ArrayBuffer.isView(bodyInit as any) && + !(bodyInit instanceof ArrayBuffer) && + !(bodyInit instanceof Blob) && + !(bodyInit instanceof FormData) && + ct.includes('application/json') + ) { + bodyInit = JSON.stringify(bodyInit); + } + const requestInit: RequestInit = { + ...requestOptions, + method: axiosConfig.method, + headers: merged, + body: bodyInit, + }; + + const response = await fetch(safeJoinUrl(baseUrl, url), requestInit); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + + if (!reader) { + throw new Error('No reader'); + } + while (true) { + const { done, value } = await reader.read(); + if (done) { + decoder.decode(new Uint8Array(0), { stream: false }); + requestOptions?.onEnd?.(); + reader.releaseLock?.(); + break; + } + const content = decoder.decode(value, { stream: true }); + requestOptions?.onMessage?.(content); + } + } +} + +function safeJoinUrl(baseUrl: string | undefined, url: string): string { + if (!baseUrl) { + return url; // 没有 baseUrl,直接返回 url + } + + // 如果 url 本身就是绝对地址,直接返回 + if (/^https?:\/\//i.test(url)) { + return url; + } + + // 如果 baseUrl 是完整 URL,就用 new URL + if (/^https?:\/\//i.test(baseUrl)) { + return new URL(url, baseUrl).toString(); + } + + // 否则,当作路径拼接 + return `${baseUrl.replace(/\/+$/, '')}/${url.replace(/^\/+/, '')}`; +} + +export { SSE }; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.test.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.test.ts new file mode 100644 index 0000000..8306656 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.test.ts @@ -0,0 +1,118 @@ +import type { AxiosRequestConfig, AxiosResponse } from 'axios'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FileUploader } from './uploader'; + +describe('fileUploader', () => { + let fileUploader: FileUploader; + // Mock the AxiosInstance + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + beforeEach(() => { + fileUploader = new FileUploader(mockAxiosInstance); + }); + + it('should create an instance of FileUploader', () => { + expect(fileUploader).toBeInstanceOf(FileUploader); + }); + + it('should upload a file and return the response', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + const mockResponse: AxiosResponse = { + config: {} as any, + data: { success: true }, + headers: {}, + status: 200, + statusText: 'OK', + }; + + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockResolvedValueOnce(mockResponse); + + const result = await fileUploader.upload(url, { file }); + expect(result).toEqual(mockResponse); + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + expect.any(FormData), + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }, + ); + }); + + it('should merge provided config with default config', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + const mockResponse: AxiosResponse = { + config: {} as any, + data: { success: true }, + headers: {}, + status: 200, + statusText: 'OK', + }; + + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockResolvedValueOnce(mockResponse); + + const customConfig: AxiosRequestConfig = { + headers: { 'Custom-Header': 'value' }, + }; + + const result = await fileUploader.upload(url, { file }, customConfig); + expect(result).toEqual(mockResponse); + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + expect.any(FormData), + { + headers: { + 'Content-Type': 'multipart/form-data', + 'Custom-Header': 'value', + }, + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Network Error')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Network Error', + ); + }); + + it('should handle empty URL gracefully', async () => { + const url = ''; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Request failed with status code 404')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Request failed with status code 404', + ); + }); + + it('should handle null URL gracefully', async () => { + const url = null as unknown as string; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Request failed with status code 404')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Request failed with status code 404', + ); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.ts new file mode 100644 index 0000000..1353222 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/modules/uploader.ts @@ -0,0 +1,42 @@ +import type { RequestClient } from '../request-client'; +import type { RequestClientConfig } from '../types'; + +import { isUndefined } from '@vben/utils'; + +class FileUploader { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + + public async upload( + url: string, + data: Record & { file: Blob | File }, + config?: RequestClientConfig, + ): Promise { + const formData = new FormData(); + + Object.entries(data).forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach((item, index) => { + !isUndefined(item) && formData.append(`${key}[${index}]`, item); + }); + } else { + !isUndefined(value) && formData.append(key, value); + } + }); + + const finalConfig: RequestClientConfig = { + ...config, + headers: { + 'Content-Type': 'multipart/form-data', + ...config?.headers, + }, + }; + + return this.client.post(url, formData, finalConfig); + } +} + +export { FileUploader }; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/preset-interceptors.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/preset-interceptors.ts new file mode 100644 index 0000000..c7bec37 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/preset-interceptors.ts @@ -0,0 +1,165 @@ +import type { RequestClient } from './request-client'; +import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types'; + +import { $t } from '@vben/locales'; +import { isFunction } from '@vben/utils'; + +import axios from 'axios'; + +export const defaultResponseInterceptor = ({ + codeField = 'code', + dataField = 'data', + successCode = 0, +}: { + /** 响应数据中代表访问结果的字段名 */ + codeField: string; + /** 响应数据中装载实际数据的字段名,或者提供一个函数从响应数据中解析需要返回的数据 */ + dataField: ((response: any) => any) | string; + /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */ + successCode: ((code: any) => boolean) | number | string; +}): ResponseInterceptorConfig => { + return { + fulfilled: (response) => { + const { config, data: responseData, status } = response; + + if (config.responseReturn === 'raw') { + return response; + } + + if (status >= 200 && status < 400) { + if (config.responseReturn === 'body') { + return responseData; + } else if ( + isFunction(successCode) + ? successCode(responseData[codeField]) + : responseData[codeField] === successCode + ) { + return isFunction(dataField) + ? dataField(responseData) + : responseData[dataField]; + } + } + throw Object.assign({}, response, { response }); + }, + }; +}; + +export const authenticateResponseInterceptor = ({ + client, + doReAuthenticate, + doRefreshToken, + enableRefreshToken, + formatToken, +}: { + client: RequestClient; + doReAuthenticate: () => Promise; + doRefreshToken: () => Promise; + enableRefreshToken: boolean; + formatToken: (token: string) => null | string; +}): ResponseInterceptorConfig => { + return { + rejected: async (error) => { + const { config, response } = error; + // 如果不是 401 错误,直接抛出异常 + if (response?.status !== 401) { + throw error; + } + // 判断是否启用了 refreshToken 功能 + // 如果没有启用或者已经是重试请求了,直接跳转到重新登录 + if (!enableRefreshToken || config.__isRetryRequest) { + await doReAuthenticate(); + throw error; + } + // 如果正在刷新 token,则将请求加入队列,等待刷新完成 + if (client.isRefreshing) { + return new Promise((resolve) => { + client.refreshTokenQueue.push((newToken: string) => { + config.headers.Authorization = formatToken(newToken); + resolve(client.request(config.url, { ...config })); + }); + }); + } + + // 标记开始刷新 token + client.isRefreshing = true; + // 标记当前请求为重试请求,避免无限循环 + config.__isRetryRequest = true; + + try { + const newToken = await doRefreshToken(); + + // 处理队列中的请求 + client.refreshTokenQueue.forEach((callback) => callback(newToken)); + // 清空队列 + client.refreshTokenQueue = []; + + return client.request(error.config.url, { ...error.config }); + } catch (refreshError) { + // 如果刷新 token 失败,处理错误(如强制登出或跳转登录页面) + client.refreshTokenQueue.forEach((callback) => callback('')); + client.refreshTokenQueue = []; + console.error('Refresh token failed, please login again.'); + await doReAuthenticate(); + + throw refreshError; + } finally { + client.isRefreshing = false; + } + }, + }; +}; + +export const errorMessageResponseInterceptor = ( + makeErrorMessage?: MakeErrorMessageFn, +): ResponseInterceptorConfig => { + return { + rejected: (error: any) => { + if (axios.isCancel(error)) { + return Promise.reject(error); + } + + const err: string = error?.toString?.() ?? ''; + let errMsg = ''; + if (err?.includes('Network Error')) { + errMsg = $t('ui.fallback.http.networkError'); + } else if (error?.message?.includes?.('timeout')) { + errMsg = $t('ui.fallback.http.requestTimeout'); + } + if (errMsg) { + makeErrorMessage?.(errMsg, error); + return Promise.reject(error); + } + + let errorMessage: string; + const status = error?.response?.status; + + switch (status) { + case 400: { + errorMessage = $t('ui.fallback.http.badRequest'); + break; + } + case 401: { + errorMessage = $t('ui.fallback.http.unauthorized'); + break; + } + case 403: { + errorMessage = $t('ui.fallback.http.forbidden'); + break; + } + case 404: { + errorMessage = $t('ui.fallback.http.notFound'); + break; + } + case 408: { + errorMessage = $t('ui.fallback.http.requestTimeout'); + break; + } + default: { + errorMessage = $t('ui.fallback.http.internalServerError'); + } + } + makeErrorMessage?.(errorMessage, error); + return Promise.reject(error); + }, + }; +}; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.test.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.test.ts new file mode 100644 index 0000000..6617534 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.test.ts @@ -0,0 +1,93 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { RequestClient } from './request-client'; + +describe('requestClient', () => { + let mock: MockAdapter; + let requestClient: RequestClient; + + beforeEach(() => { + mock = new MockAdapter(axios); + requestClient = new RequestClient(); + }); + + afterEach(() => { + mock.reset(); + }); + + it('should successfully make a GET request', async () => { + mock.onGet('test/url').reply(200, { data: 'response' }); + + const response = await requestClient.get('test/url'); + + expect(response.data).toEqual({ data: 'response' }); + }); + + it('should successfully make a POST request', async () => { + const postData = { key: 'value' }; + const mockData = { data: 'response' }; + mock.onPost('/test/post', postData).reply(200, mockData); + const response = await requestClient.post('/test/post', postData); + expect(response.data).toEqual(mockData); + }); + + it('should successfully make a PUT request', async () => { + const putData = { key: 'updatedValue' }; + const mockData = { data: 'updated response' }; + mock.onPut('/test/put', putData).reply(200, mockData); + const response = await requestClient.put('/test/put', putData); + expect(response.data).toEqual(mockData); + }); + + it('should successfully make a DELETE request', async () => { + const mockData = { data: 'delete response' }; + mock.onDelete('/test/delete').reply(200, mockData); + const response = await requestClient.delete('/test/delete'); + expect(response.data).toEqual(mockData); + }); + + it('should handle network errors', async () => { + mock.onGet('/test/error').networkError(); + await expect(requestClient.get('/test/error')).rejects.toMatchObject({ + isAxiosError: true, + message: 'Network Error', + }); + }); + + it('should handle timeout', async () => { + mock.onGet('/test/timeout').timeout(); + await expect(requestClient.get('/test/timeout')).rejects.toMatchObject({ + isAxiosError: true, + code: 'ECONNABORTED', + }); + }); + + it('should successfully upload a file', async () => { + const fileData = new Blob(['file contents'], { type: 'text/plain' }); + + mock.onPost('/test/upload').reply((config) => { + return config.data instanceof FormData && config.data.has('file') + ? [200, { data: 'file uploaded' }] + : [400, { error: 'Bad Request' }]; + }); + + const response = await requestClient.upload('/test/upload', { + file: fileData, + }); + expect(response.data).toEqual({ data: 'file uploaded' }); + }); + + it('should successfully download a file as a blob', async () => { + const mockFileContent = new Blob(['mock file content'], { + type: 'text/plain', + }); + + mock.onGet('/test/download').reply(200, mockFileContent); + + const res = await requestClient.download('/test/download'); + + expect(res.data).toBeInstanceOf(Blob); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.ts new file mode 100644 index 0000000..453913b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/request-client.ts @@ -0,0 +1,164 @@ +import type { AxiosInstance, AxiosResponse } from 'axios'; + +import type { RequestClientConfig, RequestClientOptions } from './types'; + +import { bindMethods, isString, merge } from '@vben/utils'; + +import axios from 'axios'; +import qs from 'qs'; + +import { FileDownloader } from './modules/downloader'; +import { InterceptorManager } from './modules/interceptor'; +import { SSE } from './modules/sse'; +import { FileUploader } from './modules/uploader'; + +function getParamsSerializer( + paramsSerializer: RequestClientOptions['paramsSerializer'], +) { + if (isString(paramsSerializer)) { + switch (paramsSerializer) { + case 'brackets': { + return (params: any) => + qs.stringify(params, { arrayFormat: 'brackets' }); + } + case 'comma': { + return (params: any) => qs.stringify(params, { arrayFormat: 'comma' }); + } + case 'indices': { + return (params: any) => + qs.stringify(params, { arrayFormat: 'indices' }); + } + case 'repeat': { + return (params: any) => qs.stringify(params, { arrayFormat: 'repeat' }); + } + } + } + return paramsSerializer; +} + +class RequestClient { + public addRequestInterceptor: InterceptorManager['addRequestInterceptor']; + + public addResponseInterceptor: InterceptorManager['addResponseInterceptor']; + public download: FileDownloader['download']; + + public readonly instance: AxiosInstance; + // 是否正在刷新token + public isRefreshing = false; + public postSSE: SSE['postSSE']; + // 刷新token队列 + public refreshTokenQueue: ((token: string) => void)[] = []; + public requestSSE: SSE['requestSSE']; + public upload: FileUploader['upload']; + + /** + * 构造函数,用于创建Axios实例 + * @param options - Axios请求配置,可选 + */ + constructor(options: RequestClientOptions = {}) { + // 合并默认配置和传入的配置 + const defaultConfig: RequestClientOptions = { + headers: { + 'Content-Type': 'application/json;charset=utf-8', + }, + responseReturn: 'raw', + // 默认超时时间 + timeout: 10_000, + }; + const { ...axiosConfig } = options; + const requestConfig = merge(axiosConfig, defaultConfig); + requestConfig.paramsSerializer = getParamsSerializer( + requestConfig.paramsSerializer, + ); + this.instance = axios.create(requestConfig); + + bindMethods(this); + + // 实例化拦截器管理器 + const interceptorManager = new InterceptorManager(this.instance); + this.addRequestInterceptor = + interceptorManager.addRequestInterceptor.bind(interceptorManager); + this.addResponseInterceptor = + interceptorManager.addResponseInterceptor.bind(interceptorManager); + + // 实例化文件上传器 + const fileUploader = new FileUploader(this); + this.upload = fileUploader.upload.bind(fileUploader); + // 实例化文件下载器 + const fileDownloader = new FileDownloader(this); + this.download = fileDownloader.download.bind(fileDownloader); + // 实例化SSE模块 + const sse = new SSE(this); + this.postSSE = sse.postSSE.bind(sse); + this.requestSSE = sse.requestSSE.bind(sse); + } + + /** + * DELETE请求方法 + */ + public delete( + url: string, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, method: 'DELETE' }); + } + + /** + * GET请求方法 + */ + public get(url: string, config?: RequestClientConfig): Promise { + return this.request(url, { ...config, method: 'GET' }); + } + + /** + * 获取基础URL + */ + public getBaseUrl() { + return this.instance.defaults.baseURL; + } + + /** + * POST请求方法 + */ + public post( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'POST' }); + } + + /** + * PUT请求方法 + */ + public put( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'PUT' }); + } + + /** + * 通用的请求方法 + */ + public async request( + url: string, + config: RequestClientConfig, + ): Promise { + try { + const response: AxiosResponse = await this.instance({ + url, + ...config, + ...(config.paramsSerializer + ? { paramsSerializer: getParamsSerializer(config.paramsSerializer) } + : {}), + }); + return response as T; + } catch (error: any) { + throw error.response ? error.response.data : error; + } + } +} + +export { RequestClient }; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/types.ts b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/types.ts new file mode 100644 index 0000000..d40ee8a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/src/request-client/types.ts @@ -0,0 +1,90 @@ +import type { + AxiosRequestConfig, + AxiosResponse, + CreateAxiosDefaults, + InternalAxiosRequestConfig, +} from 'axios'; + +type ExtendOptions = { + /** + * 参数序列化方式。预置的有 + * - brackets: ids[]=1&ids[]=2&ids[]=3 + * - comma: ids=1,2,3 + * - indices: ids[0]=1&ids[1]=2&ids[2]=3 + * - repeat: ids=1&ids=2&ids=3 + */ + paramsSerializer?: + | 'brackets' + | 'comma' + | 'indices' + | 'repeat' + | AxiosRequestConfig['paramsSerializer']; + /** + * 响应数据的返回方式。 + * - raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 + * - body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 + * - data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 + */ + responseReturn?: 'body' | 'data' | 'raw'; +}; +type RequestClientConfig = AxiosRequestConfig & ExtendOptions; + +type RequestResponse = AxiosResponse & { + config: RequestClientConfig; +}; + +type RequestContentType = + | 'application/json;charset=utf-8' + | 'application/octet-stream;charset=utf-8' + | 'application/x-www-form-urlencoded;charset=utf-8' + | 'multipart/form-data;charset=utf-8'; + +type RequestClientOptions = CreateAxiosDefaults & ExtendOptions; + +/** + * SSE 请求选项 + */ +interface SseRequestOptions extends RequestInit { + onMessage?: (message: string) => void; + onEnd?: () => void; +} + +interface RequestInterceptorConfig { + fulfilled?: ( + config: ExtendOptions & InternalAxiosRequestConfig, + ) => + | (ExtendOptions & InternalAxiosRequestConfig) + | Promise>; + rejected?: (error: any) => any; +} + +interface ResponseInterceptorConfig { + fulfilled?: ( + response: RequestResponse, + ) => Promise | RequestResponse; + rejected?: (error: any) => any; +} + +type MakeErrorMessageFn = (message: string, error: any) => void; + +interface HttpResponse { + /** + * 0 表示成功 其他表示失败 + * 0 means success, others means fail + */ + code: number; + data: T; + message: string; +} + +export type { + HttpResponse, + MakeErrorMessageFn, + RequestClientConfig, + RequestClientOptions, + RequestContentType, + RequestInterceptorConfig, + RequestResponse, + ResponseInterceptorConfig, + SseRequestOptions, +}; diff --git a/deploy/fba/fba-ui-src/packages/effects/request/tsconfig.json b/deploy/fba/fba-ui-src/packages/effects/request/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/effects/request/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/icons/README.md b/deploy/fba/fba-ui-src/packages/icons/README.md new file mode 100644 index 0000000..bc50d25 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/README.md @@ -0,0 +1,19 @@ +# @vben/icons + +用于多个 `app` 公用的图标文件,继承了 `@vben-core/icons` 的所有能力。业务上有通用图标可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/icons +``` + +### 使用 + +```ts +import { X } from '@vben/icons'; +``` diff --git a/deploy/fba/fba-ui-src/packages/icons/package.json b/deploy/fba/fba-ui-src/packages/icons/package.json new file mode 100644 index 0000000..1a5fc72 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/package.json @@ -0,0 +1,22 @@ +{ + "name": "@vben/icons", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/icons" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/icons": "workspace:*" + } +} diff --git a/deploy/fba/fba-ui-src/packages/icons/src/iconify/index.ts b/deploy/fba/fba-ui-src/packages/icons/src/iconify/index.ts new file mode 100644 index 0000000..0e778ca --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/iconify/index.ts @@ -0,0 +1,25 @@ +import { createIconifyIcon } from '@vben-core/icons'; + +export * from '@vben-core/icons'; + +export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc'); + +export const MaterialSymbolsAdd = createIconifyIcon('material-symbols:add'); + +export const MaterialSymbolsEdit = createIconifyIcon('material-symbols:edit'); + +export const MaterialSymbolsDelete = createIconifyIcon( + 'material-symbols:delete', +); + +export const MingcuteProfileLine = createIconifyIcon('mingcute:profile-line'); + +export const MaterialIconThemePython = createIconifyIcon( + 'material-icon-theme:python', +); + +export const BxsFile = createIconifyIcon('bxs:file'); + +export const FlatColorIconsFolder = createIconifyIcon( + 'flat-color-icons:folder', +); diff --git a/deploy/fba/fba-ui-src/packages/icons/src/icons/empty-icon.vue b/deploy/fba/fba-ui-src/packages/icons/src/icons/empty-icon.vue new file mode 100644 index 0000000..444a765 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/icons/empty-icon.vue @@ -0,0 +1,27 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/index.ts b/deploy/fba/fba-ui-src/packages/icons/src/index.ts new file mode 100644 index 0000000..f69f28e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/index.ts @@ -0,0 +1,3 @@ +export * from './iconify'; +export { default as EmptyIcon } from './icons/empty-icon.vue'; +export * from './svg'; diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-logo.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-logo.svg new file mode 100644 index 0000000..dbfcee7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-logo.svg @@ -0,0 +1,29 @@ + + + + Vue + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-next-logo.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-next-logo.svg new file mode 100644 index 0000000..b335be1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/antdv-next-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-1.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-1.svg new file mode 100644 index 0000000..b05970d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-1.svg @@ -0,0 +1 @@ +Asset 15 diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-2.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-2.svg new file mode 100644 index 0000000..8e17b0b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-2.svg @@ -0,0 +1 @@ +Asset 16 diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-3.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-3.svg new file mode 100644 index 0000000..ca79178 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-3.svg @@ -0,0 +1 @@ +Asset 17 diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-4.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-4.svg new file mode 100644 index 0000000..d9138df --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/avatar-4.svg @@ -0,0 +1 @@ +Asset 120 diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/bell.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/bell.svg new file mode 100644 index 0000000..90c34e1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/bell.svg @@ -0,0 +1 @@ +Asset 510 diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/cake.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/cake.svg new file mode 100644 index 0000000..43901fb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/cake.svg @@ -0,0 +1 @@ +Asset 480% diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/card.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/card.svg new file mode 100644 index 0000000..40ff3e3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/card.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/dingding.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/dingding.svg new file mode 100644 index 0000000..c2fc786 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/dingding.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/download.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/download.svg new file mode 100644 index 0000000..af9ff15 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/download.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/github.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/github.svg new file mode 100644 index 0000000..39d092d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/github.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/google.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/google.svg new file mode 100644 index 0000000..f2ad0a4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/google.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/qqchat.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/qqchat.svg new file mode 100644 index 0000000..ac9a7f0 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/qqchat.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/tdesign-logo.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/tdesign-logo.svg new file mode 100644 index 0000000..bdbba5c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/tdesign-logo.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/wechat.svg b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/wechat.svg new file mode 100644 index 0000000..346b037 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/icons/wechat.svg @@ -0,0 +1 @@ + diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/index.ts b/deploy/fba/fba-ui-src/packages/icons/src/svg/index.ts new file mode 100644 index 0000000..5044a37 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/index.ts @@ -0,0 +1,39 @@ +import { createIconifyIcon } from '@vben-core/icons'; + +import './load.js'; + +const SvgAvatar1Icon = createIconifyIcon('svg:avatar-1'); +const SvgAvatar2Icon = createIconifyIcon('svg:avatar-2'); +const SvgAvatar3Icon = createIconifyIcon('svg:avatar-3'); +const SvgAvatar4Icon = createIconifyIcon('svg:avatar-4'); +const SvgDownloadIcon = createIconifyIcon('svg:download'); +const SvgCardIcon = createIconifyIcon('svg:card'); +const SvgBellIcon = createIconifyIcon('svg:bell'); +const SvgCakeIcon = createIconifyIcon('svg:cake'); +const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo'); +const SvgGithubIcon = createIconifyIcon('svg:github'); +const SvgGoogleIcon = createIconifyIcon('svg:google'); +const SvgQQChatIcon = createIconifyIcon('svg:qqchat'); +const SvgWeChatIcon = createIconifyIcon('svg:wechat'); +const SvgDingDingIcon = createIconifyIcon('svg:dingding'); +const SvgTDesignIcon = createIconifyIcon('svg:tdesign-logo'); +const SvgAntdvNextLogoIcon = createIconifyIcon('svg:antdv-next-logo'); + +export { + SvgAntdvLogoIcon, + SvgAntdvNextLogoIcon, + SvgAvatar1Icon, + SvgAvatar2Icon, + SvgAvatar3Icon, + SvgAvatar4Icon, + SvgBellIcon, + SvgCakeIcon, + SvgCardIcon, + SvgDingDingIcon, + SvgDownloadIcon, + SvgGithubIcon, + SvgGoogleIcon, + SvgQQChatIcon, + SvgTDesignIcon, + SvgWeChatIcon, +}; diff --git a/deploy/fba/fba-ui-src/packages/icons/src/svg/load.ts b/deploy/fba/fba-ui-src/packages/icons/src/svg/load.ts new file mode 100644 index 0000000..b21869b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/src/svg/load.ts @@ -0,0 +1,83 @@ +import type { IconifyIconStructure } from '@vben-core/icons'; + +import { addIcon } from '@vben-core/icons'; + +loadSvgIcons(); + +function parseSvg(svgData: string): IconifyIconStructure { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(svgData, 'image/svg+xml'); + const svgElement = xmlDoc.documentElement; + + // 提取 SVG 根元素的关键样式属性 + const getAttrs = (el: Element, attrs: string[]) => + attrs + .map((attr) => + el.hasAttribute(attr) ? `${attr}="${el.getAttribute(attr)}"` : '', + ) + .filter(Boolean) + .join(' '); + + const rootAttrs = getAttrs(svgElement, [ + 'fill', + 'stroke', + 'fill-rule', + 'stroke-width', + ]); + + const svgContent = [...svgElement.childNodes] + .filter((node) => node.nodeType === Node.ELEMENT_NODE) + .map((node) => new XMLSerializer().serializeToString(node)) + .join(''); + // 若根有属性,用一个 g 标签包裹内容并继承属性 + const body = rootAttrs ? `${svgContent}` : svgContent; + + const viewBoxValue = svgElement.getAttribute('viewBox') || ''; + const [left, top, width, height] = viewBoxValue.split(' ').map((val) => { + const num = Number(val); + return Number.isNaN(num) ? undefined : num; + }); + + return { + body, + height, + left, + top, + width, + }; +} + +/** + * 自定义的svg图片转化为组件 + * @example ./svg/avatar.svg + * + */ +async function loadSvgIcons() { + if ( + typeof DOMParser === 'undefined' || + typeof Node === 'undefined' || + typeof XMLSerializer === 'undefined' + ) { + return; + } + + const svgEagers = import.meta.glob('./icons/**', { + eager: true, + query: '?raw', + }); + + await Promise.all( + Object.entries(svgEagers).map((svg) => { + const [key, body] = svg as [string, string | { default: string }]; + + // ./icons/xxxx.svg => xxxxxx + const start = key.lastIndexOf('/') + 1; + const end = key.lastIndexOf('.'); + const iconName = key.slice(start, end); + + return addIcon(`svg:${iconName}`, { + ...parseSvg(typeof body === 'object' ? body.default : body), + }); + }), + ); +} diff --git a/deploy/fba/fba-ui-src/packages/icons/tsconfig.json b/deploy/fba/fba-ui-src/packages/icons/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/icons/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/locales/package.json b/deploy/fba/fba-ui-src/packages/locales/package.json new file mode 100644 index 0000000..709b264 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/package.json @@ -0,0 +1,28 @@ +{ + "name": "@vben/locales", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/locales" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@intlify/core-base": "catalog:", + "@vben-core/composables": "workspace:*", + "vue": "catalog:", + "vue-i18n": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/i18n.ts b/deploy/fba/fba-ui-src/packages/locales/src/i18n.ts new file mode 100644 index 0000000..e3c75bd --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/i18n.ts @@ -0,0 +1,147 @@ +import type { App } from 'vue'; +import type { Locale } from 'vue-i18n'; + +import type { + ImportLocaleFn, + LoadMessageFn, + LocaleSetupOptions, + SupportedLanguagesType, +} from './typing'; + +import { unref } from 'vue'; +import { createI18n } from 'vue-i18n'; + +import { useSimpleLocale } from '@vben-core/composables'; + +const i18n = createI18n({ + globalInjection: true, + legacy: false, + locale: '', + messages: {}, +}); + +const modules = import.meta.glob('./langs/**/*.json'); + +const { setSimpleLocale } = useSimpleLocale(); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); +let loadMessages: LoadMessageFn; + +/** + * Load locale modules + * @param modules + */ +function loadLocalesMap(modules: Record Promise>) { + const localesMap: Record = {}; + + for (const [path, loadLocale] of Object.entries(modules)) { + const key = path.match(/([\w-]*)\.(json)/)?.[1]; + if (key) { + localesMap[key] = loadLocale as ImportLocaleFn; + } + } + return localesMap; +} + +/** + * Load locale modules with directory structure + * @param regexp - Regular expression to match language and file names + * @param modules - The modules object containing paths and import functions + * @returns A map of locales to their corresponding import functions + */ +function loadLocalesMapFromDir( + regexp: RegExp, + modules: Record Promise>, +): Record { + const localesRaw: Record Promise>> = {}; + const localesMap: Record = {}; + + // Iterate over the modules to extract language and file names + for (const path in modules) { + const match = path.match(regexp); + if (match) { + const [_, locale, fileName] = match; + if (locale && fileName) { + if (!localesRaw[locale]) { + localesRaw[locale] = {}; + } + if (modules[path]) { + localesRaw[locale][fileName] = modules[path]; + } + } + } + } + + // Convert raw locale data into async import functions + for (const [locale, files] of Object.entries(localesRaw)) { + localesMap[locale] = async () => { + const messages: Record = {}; + for (const [fileName, importFn] of Object.entries(files)) { + messages[fileName] = ((await importFn()) as any)?.default; + } + return { default: messages }; + }; + } + + return localesMap; +} + +/** + * Set i18n language + * @param locale + */ +function setI18nLanguage(locale: Locale) { + i18n.global.locale.value = locale; + + document?.querySelector('html')?.setAttribute('lang', locale); +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + const { defaultLocale = 'zh-CN' } = options; + // app可以自行扩展一些第三方库和组件库的国际化 + loadMessages = options.loadMessages || (async () => ({})); + app.use(i18n); + await loadLocaleMessages(defaultLocale); + + // 在控制台打印警告 + i18n.global.setMissingHandler((locale, key) => { + if (options.missingWarn && key.includes('.')) { + console.warn( + `[intlify] Not found '${key}' key in '${locale}' locale messages.`, + ); + } + }); +} + +/** + * Load locale messages + * @param lang + */ +async function loadLocaleMessages(lang: SupportedLanguagesType) { + if (unref(i18n.global.locale) === lang) { + return setI18nLanguage(lang); + } + setSimpleLocale(lang); + + const message = await localesMap[lang]?.(); + + if (message?.default) { + i18n.global.setLocaleMessage(lang, message.default); + } + + const mergeMessage = await loadMessages(lang); + i18n.global.mergeLocaleMessage(lang, mergeMessage); + + return setI18nLanguage(lang); +} + +export { + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +}; diff --git a/deploy/fba/fba-ui-src/packages/locales/src/index.ts b/deploy/fba/fba-ui-src/packages/locales/src/index.ts new file mode 100644 index 0000000..d4bfd81 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/index.ts @@ -0,0 +1,30 @@ +import { + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +} from './i18n'; + +const $t = i18n.global.t; +const $te = i18n.global.te; + +export { + $t, + $te, + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +}; +export { + type ImportLocaleFn, + type LocaleSetupOptions, + type SupportedLanguagesType, +} from './typing'; +export type { CompileError } from '@intlify/core-base'; + +export { useI18n } from 'vue-i18n'; + +export type { Locale } from 'vue-i18n'; diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/authentication.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/authentication.json new file mode 100644 index 0000000..7dd3462 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/authentication.json @@ -0,0 +1,65 @@ +{ + "welcomeBack": "Welcome Back", + "pageTitle": "Plug-and-play Admin system", + "pageDesc": "Efficient, versatile frontend template", + "loginSuccess": "Login Successful", + "loginSuccessDesc": "Welcome Back", + "loginSubtitle": "Enter your account details to manage your projects", + "selectAccount": "Quick Select Account", + "username": "Username", + "password": "Password", + "usernameTip": "Please enter username", + "passwordErrorTip": "Password is incorrect", + "passwordTip": "Please enter password", + "verifyRequiredTip": "Please complete the verification first", + "rememberMe": "Remember Me", + "createAnAccount": "Create an Account", + "createAccount": "Create Account", + "alreadyHaveAccount": "Already have an account?", + "accountTip": "Don't have an account?", + "signUp": "Sign Up", + "signUpSubtitle": "Make managing your applications simple and fun", + "confirmPassword": "Confirm Password", + "confirmPasswordTip": "The passwords do not match", + "agree": "I agree to", + "privacyPolicy": "Privacy-policy", + "terms": "Terms", + "agreeTip": "Please agree to the Privacy Policy and Terms", + "goToLogin": "Login instead", + "passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols", + "forgetPassword": "Forget Password?", + "forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password", + "emailTip": "Please enter email", + "emailValidErrorTip": "The email format you entered is incorrect", + "sendResetLink": "Send Reset Link", + "email": "Email", + "qrcodeSubtitle": "Scan the QR code with your phone to login", + "qrcodePrompt": "Click 'Confirm' after scanning to complete login", + "qrcodeLogin": "QR Code Login", + "wechatLogin": "Wechat Login", + "qqLogin": "QQ Login", + "githubLogin": "Github Login", + "googleLogin": "Google Login", + "dingdingLogin": "Dingding Login", + "codeSubtitle": "Enter your phone number to start managing your project", + "code": "Security code", + "codeTip": "Security code required {0} characters", + "mobile": "Mobile", + "mobileLogin": "Mobile Login", + "mobileTip": "Please enter mobile number", + "mobileErrortip": "The phone number format is incorrect", + "sendCode": "Get Security code", + "sendText": "Resend in {0}s", + "thirdPartyLogin": "Or continue with", + "weChat": "WeChat", + "qq": "QQ", + "gitHub": "GitHub", + "google": "Google", + "loginAgainTitle": "Please Log In Again", + "loginAgainSubTitle": "Your login session has expired. Please log in again to continue.", + "layout": { + "center": "Align Center", + "alignLeft": "Align Left", + "alignRight": "Align Right" + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/common.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/common.json new file mode 100644 index 0000000..538eabf --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/common.json @@ -0,0 +1,25 @@ +{ + "back": "Back", + "backToHome": "Back To Home", + "login": "Login", + "logout": "Logout", + "prompt": "Prompt", + "cancel": "Cancel", + "confirm": "Confirm", + "reset": "Reset", + "noData": "No Data", + "refresh": "Refresh", + "loadingMenu": "Loading Menu", + "query": "Search", + "search": "Search", + "enabled": "Enabled", + "disabled": "Disabled", + "edit": "Edit", + "delete": "Delete", + "detail": "Detail", + "create": "Create", + "yes": "Yes", + "no": "No", + "showSearchPanel": "Show search panel", + "hideSearchPanel": "Hide search panel" +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/preferences.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/preferences.json new file mode 100644 index 0000000..d027227 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/preferences.json @@ -0,0 +1,233 @@ +{ + "title": "Preferences", + "subtitle": "Customize Preferences & Preview in Real Time", + "enableStickyPreferencesNavigationBar": "Enable sticky preferences navigation bar", + "disableStickyPreferencesNavigationBar": "Disable sticky preferences navigation bar", + "resetTip": "Data has changed, click to reset", + "resetTitle": "Reset Preferences", + "resetSuccess": "Preferences reset successfully", + "appearance": "Appearance", + "layout": "Layout", + "content": "Content", + "other": "Other", + "wide": "Wide", + "compact": "Fixed", + "followSystem": "Follow System", + "vertical": "Vertical", + "verticalTip": "Side vertical menu mode", + "horizontal": "Horizontal", + "horizontalTip": "Horizontal menu mode, all menus displayed at the top", + "twoColumn": "Two Column", + "twoColumnTip": "Vertical Two Column Menu Mode", + "headerSidebarNav": "Header Vertical", + "headerSidebarNavTip": "Header Full Width, Sidebar Navigation Mode", + "headerTwoColumn": "Header Two Column", + "headerTwoColumnTip": "Header Navigation & Sidebar Two Column co-exists", + "mixedMenu": "Mixed Menu", + "mixedMenuTip": "Vertical & Horizontal Menu Co-exists", + "fullContent": "Full Content", + "fullContentTip": "Only display content body, hide all menus", + "normal": "Normal", + "plain": "Plain", + "rounded": "Rounded", + "copyPreferences": "Copy Preferences", + "enableCopyPreferences": "Show copy preferences button", + "copyPreferencesSuccessTitle": "Copy successful", + "copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app", + "clearAndLogout": "Clear Cache & Logout", + "mode": "Mode", + "general": "General", + "language": "Language", + "timezone": "Timezone", + "dynamicTitle": "Dynamic Title", + "watermark": "Watermark", + "watermarkContent": "Please input Watermark content", + "checkUpdates": "Periodic update check", + "position": { + "title": "Preferences Postion", + "header": "Header", + "auto": "Auto", + "fixed": "Fixed", + "userDropdown": "User Dropdown" + }, + "sidebar": { + "buttons": "Show Buttons", + "buttonFixed": "Fixed", + "buttonCollapsed": "Collapsed", + "title": "Sidebar", + "width": "Width", + "visible": "Show Sidebar", + "draggable": "Drag Sidebar Menu", + "collapsed": "Collpase Menu", + "collapsedShowTitle": "Show Menu Title", + "autoActivateChild": "Auto Activate SubMenu", + "autoActivateChildTip": "`Enabled` to automatically activate the submenu while click menu.", + "expandOnHover": "Expand On Hover", + "expandOnHoverTip": "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar." + }, + "tabbar": { + "title": "Tabbar", + "enable": "Enable Tab Bar", + "icon": "Show Tabbar Icon", + "showMore": "Show More Button", + "showMaximize": "Show Maximize Button", + "persist": "Persist Tabs", + "visitHistory": "Visit History", + "visitHistoryTip": "When enabled, the tab bar records tab visit history. \nClosing the current tab will automatically select the last opened tab.", + "maxCount": "Max Count of Tabs", + "maxCountTip": "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.", + "draggable": "Enable Draggable Sort", + "wheelable": "Support Mouse Wheel", + "middleClickClose": "Close Tab when Mouse Middle Button Click", + "wheelableTip": "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.", + "styleType": { + "title": "Tabs Style", + "chrome": "Chrome", + "card": "Card", + "plain": "Plain", + "brisk": "Brisk" + }, + "contextMenu": { + "reload": "Reload", + "close": "Close", + "pin": "Pin", + "unpin": "Unpin", + "closeLeft": "Close Left Tabs", + "closeRight": "Close Right Tabs", + "closeOther": "Close Other Tabs", + "closeAll": "Close All Tabs", + "openInNewWindow": "Open in New Window", + "maximize": "Maximize", + "restoreMaximize": "Restore" + } + }, + "navigationMenu": { + "title": "Navigation Menu", + "style": "Navigation Menu Style", + "accordion": "Sidebar Accordion Menu", + "split": "Navigation Menu Separation", + "splitTip": "When enabled, the sidebar displays the top bar's submenu" + }, + "breadcrumb": { + "title": "Breadcrumb", + "home": "Show Home Button", + "enable": "Enable Breadcrumb", + "icon": "Show Breadcrumb Icon", + "background": "background", + "style": "Breadcrumb Style", + "hideOnlyOne": "Hidden when only one" + }, + "animation": { + "title": "Animation", + "loading": "Page Loading", + "transition": "Page Transition", + "progress": "Page Progress" + }, + "theme": { + "title": "Theme", + "radius": "Radius", + "fontSize": "Font Size", + "fontSizeTip": "Adjust global font size with real-time preview", + "light": "Light", + "dark": "Dark", + "darkSidebar": "Semi Dark Sidebar", + "darkSidebarTip": "It can be enabled when the theme is light, and the layout is neither 'Horizontal' nor 'Full Content'.", + "darkSidebarSub": "Semi Dark Sidebar Sub", + "darkSidebarSubTip": "It can be enabled when the theme is light, the semi dark sidebar is enabled, and the layout uses 'Two-Column' menu mode.", + "darkHeader": "Semi Dark Header", + "weakMode": "Weak Mode", + "grayMode": "Gray Mode", + "builtin": { + "title": "Built-in", + "default": "Default", + "violet": "Violet", + "pink": "Pink", + "rose": "Rose", + "skyBlue": "Sky Blue", + "deepBlue": "Deep Blue", + "green": "Green", + "deepGreen": "Deep Green", + "orange": "Orange", + "yellow": "Yellow", + "zinc": "Zinc", + "neutral": "Neutral", + "slate": "Slate", + "gray": "Gray", + "custom": "Custom" + } + }, + "header": { + "title": "Header", + "visible": "Show Header", + "modeStatic": "Static", + "modeFixed": "Fixed", + "modeAuto": "Auto hide & Show", + "modeAutoScroll": "Scroll to Hide & Show", + "menuAlign": "Menu Align", + "menuAlignStart": "Start", + "menuAlignEnd": "End", + "menuAlignCenter": "Center" + }, + "footer": { + "title": "Footer", + "visible": "Show Footer", + "fixed": "Fixed at Bottom" + }, + "copyright": { + "title": "Copyright", + "enable": "Enable Copyright", + "companyName": "Company Name", + "companySiteLink": "Company Site Link", + "date": "Date", + "icp": "ICP License Number", + "icpLink": "ICP Site Link" + }, + "shortcutKeys": { + "title": "Shortcut Keys", + "global": "Global", + "search": "Global Search", + "logout": "Logout", + "escape": "Close Current Window", + "preferences": "Preferences" + }, + "widget": { + "title": "Widget", + "globalSearch": "Enable Global Search", + "fullscreen": "Enable Fullscreen", + "themeToggle": "Enable Theme Toggle", + "languageToggle": "Enable Language Toggle", + "notification": "Enable Notification", + "sidebarToggle": "Enable Sidebar Toggle", + "lockScreen": "Enable Lock Screen", + "refresh": "Enable Refresh", + "timezone": "Enable Timezone" + }, + "antd": { + "tabLabel": "Antd Extension", + "title": "Business Preferences", + "fields": { + "enableFormFullscreen": { + "label": "Enable Form Fullscreen Mode", + "tip": "Subprojects can read this extension configuration in their own business scenarios." + }, + "tenantMode": { + "label": "Tenant Mode", + "options": { + "single": { + "label": "Single Tenant" + }, + "multi": { + "label": "Multi Tenant" + } + } + }, + "defaultTableSize": { + "label": "Default Page Size" + }, + "reportTitle": { + "label": "Default Report Title", + "placeholder": "Please enter the default report title" + } + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/profile.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/profile.json new file mode 100644 index 0000000..1c17e2e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/profile.json @@ -0,0 +1,4 @@ +{ + "updatePassword": "Update Password", + "updateBasicProfile": "Update Basic Profile" +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/ui.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/ui.json new file mode 100644 index 0000000..b57778d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/en-US/ui.json @@ -0,0 +1,165 @@ +{ + "formRules": { + "required": "Please enter {0}", + "selectRequired": "Please select {0}", + "minLength": "{0} must be at least {1} characters", + "maxLength": "{0} can be at most {1} characters", + "length": "{0} must be {1} characters long", + "alreadyExists": "{0} `{1}` already exists", + "startWith": "{0} must start with `{1}`", + "invalidURL": "Please input a valid URL", + "sizeLimit": "The file size cannot exceed {0}MB", + "previewWarning": "Unable to open the file, there is no available URL or preview address" + }, + "actionTitle": { + "edit": "Modify {0}", + "create": "Create {0}", + "delete": "Delete {0}", + "view": "View {0}" + }, + "actionMessage": { + "deleteConfirm": "Are you sure to delete {0}?", + "deleting": "Deleting {0} ...", + "deleteSuccess": "{0} deleted successfully", + "operationSuccess": "Operation succeeded", + "operationFailed": "Operation failed" + }, + "placeholder": { + "input": "Please enter", + "select": "Please select", + "upload": "Click to upload" + }, + "captcha": { + "title": "Please complete the security verification", + "sliderSuccessText": "Passed", + "sliderDefaultText": "Slider and drag", + "alt": "Supports img tag src attribute value", + "sliderRotateDefaultTip": "Click picture to refresh", + "sliderTranslateDefaultTip": "Click picture to refresh", + "sliderRotateFailTip": "Validation failed", + "sliderRotateSuccessTip": "Validation successful, time {0} seconds", + "sliderTranslateFailTip": "Validation failed", + "sliderTranslateSuccessTip": "Validation successful, time {0} seconds", + "refreshAriaLabel": "Refresh captcha", + "confirmAriaLabel": "Confirm selection", + "confirm": "Confirm", + "pointAriaLabel": "Click point", + "clickInOrder": "Please click in order" + }, + "iconPicker": { + "placeholder": "Select an icon", + "search": "Search icon..." + }, + "jsonViewer": { + "copy": "Copy", + "copied": "Copied" + }, + "crop": { + "title": "Image Cropping", + "titleTip": "Cropping Ratio {0}", + "confirm": "Crop", + "cancel": "Cancel cropping", + "errorTip": "Cropping error" + }, + "tiptap": { + "placeholder": "Please enter content...", + "prompts": { + "image": "Enter image URL", + "link": "Enter link URL" + }, + "toolbar": { + "bold": "Bold", + "italic": "Italic", + "underline": "Underline", + "strike": "Strike", + "code": "Code", + "codeBlock": "Code Block", + "heading": "Heading", + "paragraph": "Paragraph", + "heading1": "H1", + "heading2": "H2", + "heading3": "H3", + "heading4": "H4", + "bulletList": "Bullets", + "orderedList": "Numbering", + "blockquote": "Quote", + "link": "Link", + "unlink": "Unlink", + "image": "Image", + "imageUrl": "Image URL", + "imageUpload": "Upload Image", + "textColor": "Text Color", + "highlightColor": "Highlight Color", + "alignLeft": "Left", + "alignCenter": "Center", + "alignRight": "Right", + "preview": "Preview", + "undo": "Undo", + "redo": "Redo", + "clear": "Clear" + }, + "upload": { + "fileTooLarge": "File size exceeds the limit", + "fileTypeNotAllowed": "File type is not allowed", + "onlySingleImage": "Only single image upload is supported, the first one is selected", + "uploadFailed": "Upload Failed" + } + }, + "fallback": { + "pageNotFound": "Oops! Page Not Found", + "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.", + "forbidden": "Oops! Access Denied", + "forbiddenDesc": "Sorry, but you don't have permission to access this page.", + "internalError": "Oops! Something Went Wrong", + "internalErrorDesc": "Sorry, but the server encountered an error.", + "offline": "Offline Page", + "offlineError": "Oops! Network Error", + "offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.", + "comingSoon": "Coming Soon", + "http": { + "requestTimeout": "The request timed out. Please try again later.", + "networkError": "A network error occurred. Please check your internet connection and try again.", + "badRequest": "Bad Request. Please check your input and try again.", + "unauthorized": "Unauthorized. Please log in to continue.", + "forbidden": "Forbidden. You do not have permission to access this resource.", + "notFound": "Not Found. The requested resource could not be found.", + "internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later." + } + }, + "widgets": { + "document": "Document", + "qa": "Q&A", + "setting": "Settings", + "logoutTip": "Do you want to logout?", + "viewAll": "View All Messages", + "notifications": "Notifications", + "markAllAsRead": "Make All as Read", + "clearNotifications": "Clear", + "checkUpdatesTitle": "New Version Available", + "checkUpdatesDescription": "Click to refresh and get the latest version", + "search": { + "title": "Search", + "searchNavigate": "Search Navigation", + "select": "Select", + "navigate": "Navigate", + "close": "Close", + "noResults": "No Search Results Found", + "noRecent": "No Search History", + "recent": "Search History" + }, + "lockScreen": { + "title": "Lock Screen", + "screenButton": "Unlock", + "password": "Password", + "placeholder": "Please enter password", + "unlock": "Click to unlock", + "errorPasswordTip": "Password error, please re-enter", + "backToLogin": "Back to login", + "entry": "Enter the system" + }, + "timezone": { + "setTimezone": "Set Timezone", + "setSuccess": "Timezone set successfully" + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/authentication.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/authentication.json new file mode 100644 index 0000000..27b4ce3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/authentication.json @@ -0,0 +1,65 @@ +{ + "welcomeBack": "欢迎回来", + "pageTitle": "开箱即用的大型中后台管理系统", + "pageDesc": "工程化、高性能、跨组件库的前端模版", + "loginSuccess": "登录成功", + "loginSuccessDesc": "欢迎回来", + "loginSubtitle": "请输入您的账户信息以开始管理您的项目", + "selectAccount": "快速选择账号", + "username": "账号", + "password": "密码", + "usernameTip": "请输入用户名", + "passwordTip": "请输入密码", + "verifyRequiredTip": "请先完成验证", + "passwordErrorTip": "密码错误", + "rememberMe": "记住账号", + "createAnAccount": "创建一个账号", + "createAccount": "创建账号", + "alreadyHaveAccount": "已经有账号了?", + "accountTip": "还没有账号?", + "signUp": "注册", + "signUpSubtitle": "让您的应用程序管理变得简单而有趣", + "confirmPassword": "确认密码", + "confirmPasswordTip": "两次输入的密码不一致", + "agree": "我同意", + "privacyPolicy": "隐私政策", + "terms": "条款", + "agreeTip": "请同意隐私政策和条款", + "goToLogin": "去登录", + "passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号", + "forgetPassword": "忘记密码?", + "forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接", + "emailTip": "请输入邮箱", + "emailValidErrorTip": "你输入的邮箱格式不正确", + "sendResetLink": "发送重置链接", + "email": "邮箱", + "qrcodeSubtitle": "请用手机扫描二维码登录", + "qrcodePrompt": "扫码后点击 '确认',即可完成登录", + "qrcodeLogin": "扫码登录", + "wechatLogin": "微信登录", + "qqLogin": "QQ登录", + "githubLogin": "Github登录", + "googleLogin": "Google登录", + "dingdingLogin": "钉钉登录", + "codeSubtitle": "请输入您的手机号码以开始管理您的项目", + "code": "验证码", + "codeTip": "请输入{0}位验证码", + "mobile": "手机号码", + "mobileTip": "请输入手机号", + "mobileErrortip": "手机号码格式错误", + "mobileLogin": "手机号登录", + "sendCode": "获取验证码", + "sendText": "{0}秒后重新获取", + "thirdPartyLogin": "其他登录方式", + "weChat": "微信", + "qq": "QQ", + "gitHub": "GitHub", + "google": "Google", + "loginAgainTitle": "重新登录", + "loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。", + "layout": { + "center": "居中", + "alignLeft": "居左", + "alignRight": "居右" + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/common.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/common.json new file mode 100644 index 0000000..e2d856a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/common.json @@ -0,0 +1,25 @@ +{ + "back": "返回", + "backToHome": "返回首页", + "login": "登录", + "logout": "退出登录", + "prompt": "提示", + "cancel": "取消", + "confirm": "确认", + "reset": "重置", + "noData": "暂无数据", + "refresh": "刷新", + "loadingMenu": "加载菜单中", + "query": "查询", + "search": "搜索", + "enabled": "已启用", + "disabled": "已禁用", + "edit": "修改", + "delete": "删除", + "detail": "详情", + "create": "新增", + "yes": "是", + "no": "否", + "showSearchPanel": "显示搜索面板", + "hideSearchPanel": "隐藏搜索面板" +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/preferences.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/preferences.json new file mode 100644 index 0000000..8cbc607 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/preferences.json @@ -0,0 +1,233 @@ +{ + "title": "偏好设置", + "subtitle": "自定义偏好设置 & 实时预览", + "enableStickyPreferencesNavigationBar": "开启首选项导航栏吸顶效果", + "disableStickyPreferencesNavigationBar": "关闭首选项导航栏吸顶效果", + "resetTitle": "重置偏好设置", + "resetTip": "数据有变化,点击可进行重置", + "resetSuccess": "重置偏好设置成功", + "appearance": "外观", + "layout": "布局", + "content": "内容", + "other": "其它", + "wide": "流式", + "compact": "定宽", + "followSystem": "跟随系统", + "vertical": "垂直", + "verticalTip": "侧边垂直菜单模式", + "horizontal": "水平", + "horizontalTip": "水平菜单模式,菜单全部显示在顶部", + "twoColumn": "双列菜单", + "twoColumnTip": "垂直双列菜单模式", + "headerSidebarNav": "侧边导航", + "headerSidebarNavTip": "顶部通栏,侧边导航模式", + "headerTwoColumn": "混合双列", + "headerTwoColumnTip": "双列、水平菜单共存模式", + "mixedMenu": "混合垂直", + "mixedMenuTip": "垂直水平菜单共存", + "fullContent": "内容全屏", + "fullContentTip": "不显示任何菜单,只显示内容主体", + "normal": "常规", + "plain": "朴素", + "rounded": "圆润", + "copyPreferences": "复制偏好设置", + "enableCopyPreferences": "显示复制偏好设置按钮", + "copyPreferencesSuccessTitle": "复制成功", + "copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖", + "clearAndLogout": "清空缓存 & 退出登录", + "mode": "模式", + "general": "通用", + "language": "语言", + "timezone": "时区", + "dynamicTitle": "动态标题", + "watermark": "水印", + "watermarkContent": "请输入水印文案", + "checkUpdates": "定时检查更新", + "position": { + "title": "偏好设置位置", + "header": "顶栏", + "auto": "自动", + "fixed": "固定", + "userDropdown": "用户下拉窗" + }, + "sidebar": { + "buttons": "显示按钮", + "buttonFixed": "固定按钮", + "buttonCollapsed": "折叠按钮", + "title": "侧边栏", + "width": "宽度", + "visible": "显示侧边栏", + "draggable": "侧边栏菜单拖拽", + "collapsed": "折叠菜单", + "collapsedShowTitle": "折叠显示菜单名", + "autoActivateChild": "自动激活子菜单", + "autoActivateChildTip": "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单", + "expandOnHover": "鼠标悬停展开", + "expandOnHoverTip": "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏" + }, + "tabbar": { + "title": "标签栏", + "enable": "启用标签栏", + "icon": "显示标签栏图标", + "showMore": "显示更多按钮", + "showMaximize": "显示最大化按钮", + "persist": "持久化标签页", + "visitHistory": "访问历史记录", + "visitHistoryTip": "开启后,标签栏会记录标签访问历史\n关闭当前标签,会自动选中上一个打开的标签", + "maxCount": "最大标签数", + "maxCountTip": "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", + "draggable": "启动拖拽排序", + "wheelable": "启用纵向滚轮响应", + "middleClickClose": "点击鼠标中键关闭标签页", + "wheelableTip": "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时,只能响应系统的横向滚动事件(需要按下Shift再滚动滚轮)", + "styleType": { + "title": "标签页风格", + "chrome": "谷歌", + "card": "卡片", + "plain": "朴素", + "brisk": "轻快" + }, + "contextMenu": { + "reload": "重新加载", + "close": "关闭", + "pin": "固定", + "unpin": "取消固定", + "closeLeft": "关闭左侧标签页", + "closeRight": "关闭右侧标签页", + "closeOther": "关闭其它标签页", + "closeAll": "关闭全部标签页", + "openInNewWindow": "在新窗口打开", + "maximize": "最大化", + "restoreMaximize": "还原" + } + }, + "navigationMenu": { + "title": "导航菜单", + "style": "导航菜单风格", + "accordion": "侧边导航菜单手风琴模式", + "split": "导航菜单分离", + "splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单" + }, + "breadcrumb": { + "title": "面包屑导航", + "enable": "开启面包屑导航", + "icon": "显示面包屑图标", + "home": "显示首页按钮", + "style": "面包屑风格", + "hideOnlyOne": "仅有一个时隐藏", + "background": "背景" + }, + "animation": { + "title": "动画", + "loading": "页面切换 Loading", + "transition": "页面切换动画", + "progress": "页面切换进度条" + }, + "theme": { + "title": "主题", + "radius": "圆角", + "fontSize": "字体大小", + "fontSizeTip": "调整全局字体大小,实时预览效果", + "light": "浅色", + "dark": "深色", + "darkSidebar": "深色侧边栏", + "darkSidebarTip": "当主题为浅色,布局不为水平菜单或不为内容全屏时可开启", + "darkSidebarSub": "深色侧边栏子栏", + "darkSidebarSubTip": "当主题为浅色,开启深色侧边栏且布局使用双列菜单模式时可开启", + "darkHeader": "深色顶栏", + "weakMode": "色弱模式", + "grayMode": "灰色模式", + "builtin": { + "title": "内置主题", + "default": "默认", + "violet": "紫罗兰", + "pink": "樱花粉", + "rose": "玫瑰红", + "skyBlue": "天蓝色", + "deepBlue": "深蓝色", + "green": "浅绿色", + "deepGreen": "深绿色", + "orange": "橙黄色", + "yellow": "柠檬黄", + "zinc": "锌色灰", + "neutral": "中性色", + "slate": "石板灰", + "gray": "中灰色", + "custom": "自定义" + } + }, + "header": { + "title": "顶栏", + "modeStatic": "静止", + "modeFixed": "固定", + "modeAuto": "自动隐藏和显示", + "modeAutoScroll": "滚动隐藏和显示", + "visible": "显示顶栏", + "menuAlign": "菜单位置", + "menuAlignStart": "左侧", + "menuAlignEnd": "右侧", + "menuAlignCenter": "居中" + }, + "footer": { + "title": "底栏", + "visible": "显示底栏", + "fixed": "固定在底部" + }, + "copyright": { + "title": "版权", + "enable": "启用版权", + "companyName": "公司名", + "companySiteLink": "公司主页", + "date": "日期", + "icp": "ICP 备案号", + "icpLink": "ICP 网站链接" + }, + "shortcutKeys": { + "title": "快捷键", + "global": "全局", + "search": "全局搜索", + "logout": "退出登录", + "escape": "关闭当前窗口", + "preferences": "偏好设置" + }, + "widget": { + "title": "小部件", + "globalSearch": "启用全局搜索", + "fullscreen": "启用全屏", + "themeToggle": "启用主题切换", + "languageToggle": "启用语言切换", + "notification": "启用通知", + "sidebarToggle": "启用侧边栏切换", + "lockScreen": "启用锁屏", + "refresh": "启用刷新", + "timezone": "启用时区" + }, + "antd": { + "tabLabel": "Antd 拓展", + "title": "业务偏好", + "fields": { + "enableFormFullscreen": { + "label": "启用表单全屏模式", + "tip": "子项目可在自己的业务场景中读取这个拓展配置。" + }, + "tenantMode": { + "label": "租户模式", + "options": { + "single": { + "label": "单租户" + }, + "multi": { + "label": "多租户" + } + } + }, + "defaultTableSize": { + "label": "默认分页条数" + }, + "reportTitle": { + "label": "报表默认标题", + "placeholder": "请输入报表默认标题" + } + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/profile.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/profile.json new file mode 100644 index 0000000..a38908c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/profile.json @@ -0,0 +1,4 @@ +{ + "updatePassword": "更新密码", + "updateBasicProfile": "更新基本信息" +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/ui.json b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/ui.json new file mode 100644 index 0000000..9abce19 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/langs/zh-CN/ui.json @@ -0,0 +1,165 @@ +{ + "formRules": { + "required": "请输入{0}", + "selectRequired": "请选择{0}", + "minLength": "{0}至少{1}个字符", + "maxLength": "{0}最多{1}个字符", + "length": "{0}长度必须为{1}个字符", + "alreadyExists": "{0} `{1}` 已存在", + "startWith": "{0}必须以 {1} 开头", + "invalidURL": "请输入有效的链接", + "sizeLimit": "文件大小不能超过 {0}MB", + "previewWarning": "无法打开文件,没有可用的URL或预览地址" + }, + "actionTitle": { + "edit": "修改{0}", + "create": "新增{0}", + "delete": "删除{0}", + "view": "查看{0}" + }, + "actionMessage": { + "deleteConfirm": "确定删除 {0} 吗?", + "deleting": "正在删除 {0} ...", + "deleteSuccess": "{0} 删除成功", + "operationSuccess": "操作成功", + "operationFailed": "操作失败" + }, + "placeholder": { + "input": "请输入", + "select": "请选择", + "upload": "点击上传" + }, + "captcha": { + "title": "请完成安全验证", + "sliderSuccessText": "验证通过", + "sliderDefaultText": "请按住滑块拖动", + "sliderRotateDefaultTip": "点击图片可刷新", + "sliderTranslateDefaultTip": "点击图片可刷新", + "sliderRotateFailTip": "验证失败", + "sliderRotateSuccessTip": "验证成功,耗时{0}秒", + "sliderTranslateFailTip": "验证失败", + "sliderTranslateSuccessTip": "验证成功,耗时{0}秒", + "alt": "支持img标签src属性值", + "refreshAriaLabel": "刷新验证码", + "confirmAriaLabel": "确认选择", + "confirm": "确认", + "pointAriaLabel": "点击点", + "clickInOrder": "请依次点击" + }, + "iconPicker": { + "placeholder": "选择一个图标", + "search": "搜索图标..." + }, + "jsonViewer": { + "copy": "复制", + "copied": "已复制" + }, + "crop": { + "title": "图片裁剪", + "titleTip": "裁剪比例 {0}", + "confirm": "裁剪", + "cancel": "取消裁剪", + "errorTip": "裁剪错误" + }, + "tiptap": { + "placeholder": "请输入内容...", + "prompts": { + "image": "请输入图片地址", + "link": "请输入链接地址" + }, + "toolbar": { + "bold": "加粗", + "italic": "斜体", + "underline": "下划线", + "strike": "删除线", + "code": "行内代码", + "codeBlock": "代码块", + "heading": "标题", + "paragraph": "正文", + "heading1": "标题1", + "heading2": "标题2", + "heading3": "标题3", + "heading4": "标题4", + "bulletList": "无序列表", + "orderedList": "有序列表", + "blockquote": "引用", + "link": "链接", + "unlink": "移除链接", + "image": "图片", + "imageUrl": "图片URL", + "imageUpload": "从本地上传", + "textColor": "文字颜色", + "highlightColor": "高亮颜色", + "alignLeft": "左对齐", + "alignCenter": "居中", + "alignRight": "右对齐", + "preview": "预览", + "undo": "撤销", + "redo": "重做", + "clear": "清除" + }, + "upload": { + "fileTooLarge": "文件大小超出限制", + "fileTypeNotAllowed": "不支持的文件类型", + "onlySingleImage": "仅支持单张图片上传,已选取第一张", + "uploadFailed": "上传失败" + } + }, + "fallback": { + "pageNotFound": "哎呀!未找到页面", + "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。", + "forbidden": "哎呀!访问被拒绝", + "forbiddenDesc": "抱歉,您没有权限访问此页面。", + "internalError": "哎呀!出错了", + "internalErrorDesc": "抱歉,服务器遇到错误。", + "offline": "离线页面", + "offlineError": "哎呀!网络错误", + "offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。", + "comingSoon": "即将推出", + "http": { + "requestTimeout": "请求超时,请稍后再试。", + "networkError": "网络异常,请检查您的网络连接后重试。", + "badRequest": "请求错误。请检查您的输入并重试。", + "unauthorized": "登录认证过期,请重新登录后继续。", + "forbidden": "禁止访问, 您没有权限访问此资源。", + "notFound": "未找到, 请求的资源不存在。", + "internalServerError": "内部服务器错误,请稍后再试。" + } + }, + "widgets": { + "document": "文档", + "qa": "问题 & 帮助", + "setting": "设置", + "logoutTip": "是否退出登录?", + "viewAll": "查看所有消息", + "notifications": "通知", + "markAllAsRead": "全部标记为已读", + "clearNotifications": "清空", + "checkUpdatesTitle": "新版本可用", + "checkUpdatesDescription": "点击刷新以获取最新版本", + "search": { + "title": "搜索", + "searchNavigate": "搜索导航菜单", + "select": "选择", + "navigate": "导航", + "close": "关闭", + "noResults": "未找到搜索结果", + "noRecent": "没有搜索历史", + "recent": "搜索历史" + }, + "lockScreen": { + "title": "锁定屏幕", + "screenButton": "解锁", + "password": "密码", + "placeholder": "请输入锁屏密码", + "unlock": "点击解锁", + "errorPasswordTip": "密码错误,请重新输入", + "backToLogin": "返回登录", + "entry": "进入系统" + }, + "timezone": { + "setTimezone": "设置时区", + "setSuccess": "时区设置成功" + } + } +} diff --git a/deploy/fba/fba-ui-src/packages/locales/src/typing.ts b/deploy/fba/fba-ui-src/packages/locales/src/typing.ts new file mode 100644 index 0000000..fdebac2 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/src/typing.ts @@ -0,0 +1,25 @@ +export type SupportedLanguagesType = 'en-US' | 'zh-CN'; + +export type ImportLocaleFn = () => Promise<{ default: Record }>; + +export type LoadMessageFn = ( + lang: SupportedLanguagesType, +) => Promise | undefined>; + +export interface LocaleSetupOptions { + /** + * Default language + * @default zh-CN + */ + defaultLocale?: SupportedLanguagesType; + /** + * Load message function + * @param lang + * @returns + */ + loadMessages?: LoadMessageFn; + /** + * Whether to warn when the key is not found + */ + missingWarn?: boolean; +} diff --git a/deploy/fba/fba-ui-src/packages/locales/tsconfig.json b/deploy/fba/fba-ui-src/packages/locales/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/locales/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/preferences/package.json b/deploy/fba/fba-ui-src/packages/preferences/package.json new file mode 100644 index 0000000..baac39e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/preferences/package.json @@ -0,0 +1,26 @@ +{ + "name": "@vben/preferences", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/preferences" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/typings": "workspace:*" + } +} diff --git a/deploy/fba/fba-ui-src/packages/preferences/src/index.ts b/deploy/fba/fba-ui-src/packages/preferences/src/index.ts new file mode 100644 index 0000000..2649634 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/preferences/src/index.ts @@ -0,0 +1,27 @@ +import type { + CustomPreferencesRecord, + Preferences, + PreferencesExtension, +} from '@vben-core/preferences'; +import type { DeepPartial } from '@vben-core/typings'; + +/** + * 如果你想所有的app都使用相同的默认偏好设置,你可以在这里定义 + * 而不是去修改 @vben-core/preferences 中的默认偏好设置 + * @param preferences + * @returns + */ + +function defineOverridesPreferences(preferences: DeepPartial) { + return preferences; +} + +function definePreferencesExtension< + TCustomPreferences extends object = CustomPreferencesRecord, +>(extension: PreferencesExtension) { + return extension; +} + +export { defineOverridesPreferences, definePreferencesExtension }; + +export * from '@vben-core/preferences'; diff --git a/deploy/fba/fba-ui-src/packages/preferences/tsconfig.json b/deploy/fba/fba-ui-src/packages/preferences/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/preferences/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/stores/package.json b/deploy/fba/fba-ui-src/packages/stores/package.json new file mode 100644 index 0000000..dca737e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vben/stores", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/stores" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "pinia": "catalog:", + "pinia-plugin-persistedstate": "catalog:", + "secure-ls": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/stores/shim-pinia.d.ts b/deploy/fba/fba-ui-src/packages/stores/shim-pinia.d.ts new file mode 100644 index 0000000..558d0ba --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/shim-pinia.d.ts @@ -0,0 +1,9 @@ +// https://github.com/vuejs/pinia/issues/2098 +declare module 'pinia' { + export function acceptHMRUpdate( + initialUseStore: any | StoreDefinition, + hot: any, + ): (newModule: any) => any; +} + +export { acceptHMRUpdate }; diff --git a/deploy/fba/fba-ui-src/packages/stores/src/index.ts b/deploy/fba/fba-ui-src/packages/stores/src/index.ts new file mode 100644 index 0000000..41b3662 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/index.ts @@ -0,0 +1,3 @@ +export * from './modules'; +export * from './setup'; +export { defineStore, storeToRefs } from 'pinia'; diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/access.test.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/access.test.ts new file mode 100644 index 0000000..211e256 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/access.test.ts @@ -0,0 +1,46 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; + +import { useAccessStore } from './access'; + +describe('useAccessStore', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + it('updates accessMenus state', () => { + const store = useAccessStore(); + expect(store.accessMenus).toEqual([]); + store.setAccessMenus([{ name: 'Dashboard', path: '/dashboard' }]); + expect(store.accessMenus).toEqual([ + { name: 'Dashboard', path: '/dashboard' }, + ]); + }); + + it('updates accessToken state correctly', () => { + const store = useAccessStore(); + expect(store.accessToken).toBeNull(); // 初始状态 + store.setAccessToken('abc123'); + expect(store.accessToken).toBe('abc123'); + }); + + it('returns the correct accessToken', () => { + const store = useAccessStore(); + store.setAccessToken('xyz789'); + expect(store.accessToken).toBe('xyz789'); + }); + + // 测试设置空的访问菜单列表 + it('handles empty accessMenus correctly', () => { + const store = useAccessStore(); + store.setAccessMenus([]); + expect(store.accessMenus).toEqual([]); + }); + + // 测试设置空的访问路由列表 + it('handles empty accessRoutes correctly', () => { + const store = useAccessStore(); + store.setAccessRoutes([]); + expect(store.accessRoutes).toEqual([]); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/access.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/access.ts new file mode 100644 index 0000000..a724e1b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/access.ts @@ -0,0 +1,146 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import type { MenuRecordRaw } from '@vben-core/typings'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +type AccessToken = null | string; + +interface AccessState { + /** + * 权限码 + */ + accessCodes: string[]; + /** + * 可访问的菜单列表 + */ + accessMenus: MenuRecordRaw[]; + /** + * 可访问的路由列表 + */ + accessRoutes: RouteRecordRaw[]; + /** + * 登录会话 UUID + */ + accessSessionUuid: AccessToken; + /** + * 登录 accessToken + */ + accessToken: AccessToken; + /** + * 登录验证码 UUID + */ + captchaUuid: AccessToken; + /** + * 是否已经检查过权限 + */ + isAccessChecked: boolean; + /** + * 是否锁屏状态 + */ + isLockScreen: boolean; + /** + * 锁屏密码 + */ + lockScreenPassword?: string; + /** + * 登录是否过期 + */ + loginExpired: boolean; + /** + * 登录 accessToken + */ + refreshToken: AccessToken; +} + +/** + * @zh_CN 访问权限相关 + */ +export const useAccessStore = defineStore('core-access', { + actions: { + getMenuByPath(path: string) { + function findMenu( + menus: MenuRecordRaw[], + path: string, + ): MenuRecordRaw | undefined { + for (const menu of menus) { + if (menu.path === path) { + return menu; + } + if (menu.children) { + const matched = findMenu(menu.children, path); + if (matched) { + return matched; + } + } + } + } + return findMenu(this.accessMenus, path); + }, + lockScreen(password: string) { + this.isLockScreen = true; + this.lockScreenPassword = password; + }, + setAccessCodes(codes: string[]) { + this.accessCodes = codes; + }, + setAccessMenus(menus: MenuRecordRaw[]) { + this.accessMenus = menus; + }, + setAccessRoutes(routes: RouteRecordRaw[]) { + this.accessRoutes = routes; + }, + setAccessSessionUuid(uuid: AccessToken) { + this.accessSessionUuid = uuid; + }, + setAccessToken(token: AccessToken) { + this.accessToken = token; + }, + setCaptchaUuid(uuid: AccessToken) { + this.captchaUuid = uuid; + }, + setIsAccessChecked(isAccessChecked: boolean) { + this.isAccessChecked = isAccessChecked; + }, + setLoginExpired(loginExpired: boolean) { + this.loginExpired = loginExpired; + }, + setRefreshToken(token: AccessToken) { + this.refreshToken = token; + }, + unlockScreen() { + this.isLockScreen = false; + this.lockScreenPassword = undefined; + }, + }, + persist: { + // 持久化 + pick: [ + 'accessToken', + 'accessSessionUuid', + 'refreshToken', + 'accessCodes', + 'isLockScreen', + 'lockScreenPassword', + ], + }, + state: (): AccessState => ({ + accessSessionUuid: null, + accessCodes: [], + accessMenus: [], + accessRoutes: [], + accessToken: null, + captchaUuid: null, + isAccessChecked: false, + isLockScreen: false, + lockScreenPassword: undefined, + loginExpired: false, + refreshToken: null, + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useAccessStore, hot)); +} diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/index.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/index.ts new file mode 100644 index 0000000..bb9a66f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/index.ts @@ -0,0 +1,4 @@ +export * from './access'; +export * from './tabbar'; +export * from './timezone'; +export * from './user'; diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.test.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.test.ts new file mode 100644 index 0000000..7c0fe7a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.test.ts @@ -0,0 +1,300 @@ +import { createRouter, createWebHistory } from 'vue-router'; + +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { useTabbarStore } from './tabbar'; + +describe('useAccessStore', () => { + const router = createRouter({ + history: createWebHistory(), + routes: [], + }); + router.push = vi.fn(); + router.replace = vi.fn(); + beforeEach(() => { + setActivePinia(createPinia()); + vi.clearAllMocks(); + }); + + it('adds a new tab', () => { + const store = useTabbarStore(); + const tab: any = { + fullPath: '/home', + meta: {}, + key: '/home', + name: 'Home', + path: '/home', + }; + const addNewTab = store.addTab(tab); + expect(store.tabs.length).toBe(1); + expect(store.tabs[0]).toEqual(addNewTab); + }); + + it('adds a new tab if it does not exist', () => { + const store = useTabbarStore(); + const newTab: any = { + fullPath: '/new', + meta: {}, + name: 'New', + path: '/new', + }; + const addNewTab = store.addTab(newTab); + expect(store.tabs).toContainEqual(addNewTab); + }); + + it('updates an existing tab instead of adding a new one', () => { + const store = useTabbarStore(); + const initialTab: any = { + fullPath: '/existing', + meta: { + fullPathKey: false, + }, + name: 'Existing', + path: '/existing', + query: {}, + }; + store.addTab(initialTab); + const updatedTab = { ...initialTab, query: { id: '1' } }; + store.addTab(updatedTab); + expect(store.tabs.length).toBe(1); + expect(store.tabs[0]?.query).toEqual({ id: '1' }); + }); + + it('closes all tabs', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + router.replace = vi.fn(); + + await store.closeAllTabs(router); + + expect(store.tabs.length).toBe(1); + }); + + it('closes a non-affix tab', () => { + const store = useTabbarStore(); + const tab: any = { + fullPath: '/closable', + meta: {}, + name: 'Closable', + path: '/closable', + }; + store.tabs.push(tab); + store._close(tab); + expect(store.tabs.length).toBe(0); + }); + + it('does not close an affix tab', () => { + const store = useTabbarStore(); + const affixTab: any = { + fullPath: '/affix', + meta: { affixTab: true }, + name: 'Affix', + path: '/affix', + }; + store.tabs.push(affixTab); + store._close(affixTab); + expect(store.tabs.length).toBe(1); // Affix tab should not be closed + }); + + it('returns all cache tabs', () => { + const store = useTabbarStore(); + store.cachedTabs.add('Home'); + store.cachedTabs.add('About'); + expect(store.getCachedTabs).toEqual(['Home', 'About']); + }); + + it('returns all tabs, including affix tabs', () => { + const store = useTabbarStore(); + const normalTab: any = { + fullPath: '/normal', + meta: {}, + name: 'Normal', + path: '/normal', + }; + const affixTab: any = { + fullPath: '/affix', + meta: { affixTab: true }, + name: 'Affix', + path: '/affix', + }; + store.tabs.push(normalTab); + store.affixTabs.push(affixTab); + expect(store.getTabs).toContainEqual(normalTab); + expect(store.affixTabs).toContainEqual(affixTab); + }); + + it('navigates to a specific tab', async () => { + const store = useTabbarStore(); + const tab: any = { meta: {}, name: 'Dashboard', path: '/dashboard' }; + + await store._goToTab(tab, router); + + expect(router.replace).toHaveBeenCalledWith({ + params: {}, + path: '/dashboard', + query: {}, + }); + }); + + it('closes multiple tabs by paths', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store._bulkCloseByKeys(['/home', '/contact']); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('About'); + }); + + it('closes all tabs to the left of the specified tab', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + const targetTab: any = { + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + }; + const addTargetTab = store.addTab(targetTab); + await store.closeLeftTabs(addTargetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('Contact'); + }); + + it('closes all tabs except the specified tab', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + const targetTab: any = { + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + }; + const addTargetTab = store.addTab(targetTab); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeOtherTabs(addTargetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('About'); + }); + + it('closes all tabs to the right of the specified tab', async () => { + const store = useTabbarStore(); + const targetTab: any = { + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + }; + const addTargetTab = store.addTab(targetTab); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeRightTabs(addTargetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('Home'); + }); + + it('closes the tab with the specified key', async () => { + const store = useTabbarStore(); + const keyToClose = '/about'; + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: keyToClose, + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeTabByKey(keyToClose, router); + + expect(store.tabs).toHaveLength(2); + expect( + store.tabs.find((tab) => tab.fullPath === keyToClose), + ).toBeUndefined(); + }); + + it('refreshes the current tab', async () => { + const store = useTabbarStore(); + const currentTab: any = { + fullPath: '/dashboard', + meta: { name: 'Dashboard' }, + name: 'Dashboard', + path: '/dashboard', + }; + router.currentRoute.value = currentTab; + + await store.refresh(router); + + expect(store.excludeCachedTabs.has('Dashboard')).toBe(false); + expect(store.renderRouteView).toBe(true); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.ts new file mode 100644 index 0000000..e941138 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/tabbar.ts @@ -0,0 +1,770 @@ +import type { ComputedRef, VNode } from 'vue'; +import type { + RouteLocationNormalized, + RouteLocationNormalizedLoaded, + RouteLocationNormalizedLoadedGeneric, + Router, + RouteRecordNormalized, +} from 'vue-router'; + +import type { TabDefinition } from '@vben-core/typings'; + +import { markRaw, toRaw } from 'vue'; + +import { preferences } from '@vben-core/preferences'; +import { + createStack, + openWindow, + Stack, + startProgress, + stopProgress, +} from '@vben-core/shared/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface RouteCached { + component: VNode; + key: string; + route: RouteLocationNormalizedLoadedGeneric; +} + +interface TabbarState { + cachedRoutes: Map; + /** + * @zh_CN 当前打开的标签页列表缓存 + */ + cachedTabs: Set; + /** + * @zh_CN 拖拽结束的索引 + */ + dragEndIndex: number; + /** + * @zh_CN 需要排除缓存的标签页 + */ + excludeCachedTabs: Set; + /** + * @zh_CN 标签右键菜单列表 + */ + menuList: string[]; + /** + * @zh_CN 是否刷新 + */ + renderRouteView?: boolean; + /** + * @zh_CN 当前打开的标签页列表 + */ + tabs: TabDefinition[]; + /** + * @zh_CN 更新时间,用于一些更新场景,使用watch深度监听的话,会损耗性能 + */ + updateTime?: number; + /** + * @zh_CN 上一个标签页打开的标签 + */ + visitHistory: Stack; +} + +/** + * @zh_CN 访问历史记录最大数量 + */ +const MAX_VISIT_HISTORY = 50; + +/** + * @zh_CN 访问权限相关 + */ +export const useTabbarStore = defineStore('core-tabbar', { + actions: { + /** + * Close tabs in bulk + */ + async _bulkCloseByKeys(keys: string[]) { + const keySet = new Set(keys); + this.tabs = this.tabs.filter( + (item) => !keySet.has(getTabKeyFromTab(item)), + ); + if (isVisitHistory()) { + this.visitHistory.remove(...keys); + } + + await this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭标签页 + * @param tab + */ + _close(tab: TabDefinition) { + if (isAffixTab(tab)) { + return; + } + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + index !== -1 && this.tabs.splice(index, 1); + }, + /** + * @zh_CN 跳转到默认标签页 + */ + async _goToDefaultTab(router: Router) { + if (this.getTabs.length <= 0) { + return; + } + const firstTab = this.getTabs[0]; + if (firstTab) { + await this._goToTab(firstTab, router); + } + }, + /** + * @zh_CN 跳转到标签页 + * @param tab + * @param router + */ + async _goToTab(tab: TabDefinition, router: Router) { + const { params, path, query } = tab; + const toParams = { + params: params || {}, + path, + query: query || {}, + }; + await router.replace(toParams); + }, + /** + * @zh_CN 添加标签页 + * @param routeTab + */ + addTab(routeTab: TabDefinition): TabDefinition { + let tab = cloneTab(routeTab); + if (!tab.key) { + tab.key = getTabKey(routeTab); + } + if (!isTabShown(tab)) { + return tab; + } + + const tabIndex = this.tabs.findIndex((item) => { + return equalTab(item, tab); + }); + + if (tabIndex === -1) { + const maxCount = preferences.tabbar.maxCount; + // 获取动态路由打开数,超过 0 即代表需要控制打开数 + const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? + -1) as number; + // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 + // 获取到已经打开的动态路由数, 判断是否大于某一个值 + if ( + maxNumOfOpenTab > 0 && + this.tabs.filter((tab) => tab.name === routeTab.name).length >= + maxNumOfOpenTab + ) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => item.name === routeTab.name, + ); + index !== -1 && this.tabs.splice(index, 1); + } else if (maxCount > 0 && this.tabs.length >= maxCount) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => + !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab, + ); + index !== -1 && this.tabs.splice(index, 1); + } + this.tabs.push(tab); + } else { + // 页面已经存在,不重复添加选项卡,只更新选项卡参数 + const currentTab = toRaw(this.tabs)[tabIndex]; + const mergedTab = { + ...currentTab, + ...tab, + meta: { ...currentTab?.meta, ...tab.meta }, + }; + if (currentTab) { + const curMeta = currentTab.meta; + if (Reflect.has(curMeta, 'affixTab')) { + mergedTab.meta.affixTab = curMeta.affixTab; + } + if (Reflect.has(curMeta, 'newTabTitle')) { + mergedTab.meta.newTabTitle = curMeta.newTabTitle; + } + } + tab = mergedTab; + this.tabs.splice(tabIndex, 1, mergedTab); + } + this.updateCacheTabs(); + // 添加访问历史记录 + if (isVisitHistory()) { + this.visitHistory.push(tab.key as string); + } + return tab; + }, + /** + * @zh_CN 关闭所有标签页 + */ + async closeAllTabs(router: Router) { + const newTabs = this.tabs.filter((tab) => isAffixTab(tab)); + this.tabs = newTabs.length > 0 ? newTabs : [...this.tabs].splice(0, 1); + // 设置访问历史记录 + if (isVisitHistory()) { + this.visitHistory.retain( + this.tabs.map((item) => getTabKeyFromTab(item)), + ); + } + await this._goToDefaultTab(router); + this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭左侧标签页 + * @param tab + */ + async closeLeftTabs(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + + if (index < 1) { + return; + } + + const leftTabs = this.tabs.slice(0, index); + const keys: string[] = []; + + for (const item of leftTabs) { + if (!isAffixTab(item)) { + keys.push(item.key as string); + } + } + await this._bulkCloseByKeys(keys); + }, + /** + * @zh_CN 关闭其他标签页 + * @param tab + */ + async closeOtherTabs(tab: TabDefinition) { + const closeKeys = this.tabs.map((item) => getTabKeyFromTab(item)); + + const keys: string[] = []; + + for (const key of closeKeys) { + if (key !== getTabKeyFromTab(tab)) { + const closeTab = this.tabs.find( + (item) => getTabKeyFromTab(item) === key, + ); + if (!closeTab) { + continue; + } + if (!isAffixTab(closeTab)) { + keys.push(closeTab.key as string); + } + } + } + await this._bulkCloseByKeys(keys); + }, + /** + * @zh_CN 关闭右侧标签页 + * @param tab + */ + async closeRightTabs(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + + if (index !== -1 && index < this.tabs.length - 1) { + const rightTabs = this.tabs.slice(index + 1); + + const keys: string[] = []; + for (const item of rightTabs) { + if (!isAffixTab(item)) { + keys.push(item.key as string); + } + } + await this._bulkCloseByKeys(keys); + } + }, + + /** + * @zh_CN 关闭标签页 + * @param tab + * @param router + */ + async closeTab(tab: TabDefinition, router: Router) { + const { currentRoute } = router; + const currentTabKey = getTabKey(currentRoute.value); + // 关闭不是激活选项卡 + if (currentTabKey !== getTabKeyFromTab(tab)) { + this._close(tab); + this.updateCacheTabs(); + // 移除访问历史记录 + if (isVisitHistory()) { + this.visitHistory.remove(getTabKeyFromTab(tab)); + } + return; + } + if (this.getTabs.length <= 1) { + console.error('Failed to close the tab; only one tab remains open.'); + return; + } + // 从访问历史记录中移除当前关闭的tab + if (isVisitHistory()) { + this.visitHistory.remove(currentTabKey); + this._close(tab); + + let previousTab: TabDefinition | undefined; + let previousTabKey: string | undefined; + while (true) { + previousTabKey = this.visitHistory.pop(); + if (!previousTabKey) { + break; + } + previousTab = this.getTabByKey(previousTabKey); + if (previousTab) { + break; + } + } + await (previousTab + ? this._goToTab(previousTab, router) + : this._goToDefaultTab(router)); + return; + } + // 未开启访问历史记录,直接跳转下一个或上一个tab + const index = this.getTabs.findIndex( + (item) => getTabKeyFromTab(item) === getTabKey(currentRoute.value), + ); + + const before = this.getTabs[index - 1]; + const after = this.getTabs[index + 1]; + + // 下一个tab存在,跳转到下一个 + if (after) { + this._close(tab); + await this._goToTab(after, router); + // 上一个tab存在,跳转到上一个 + } else if (before) { + this._close(tab); + await this._goToTab(before, router); + } + }, + + /** + * @zh_CN 通过key关闭标签页 + * @param key + * @param router + */ + async closeTabByKey(key: string, router: Router) { + const originKey = decodeURIComponent(key); + const index = this.tabs.findIndex( + (item) => getTabKeyFromTab(item) === originKey, + ); + if (index === -1) { + return; + } + + const tab = this.tabs[index]; + if (tab) { + await this.closeTab(tab, router); + } + }, + + /** + * 根据tab的key获取tab + * @param key + */ + getTabByKey(key: string) { + return this.getTabs.find( + (item) => getTabKeyFromTab(item) === key, + ) as TabDefinition; + }, + /** + * @zh_CN 新窗口打开标签页 + * @param tab + */ + async openTabInNewWindow(tab: TabDefinition, router: Router) { + const href = router.resolve(tab.fullPath || tab.path).href; + openWindow(new URL(href, location.href).href, { target: '_blank' }); + }, + + /** + * @zh_CN 固定标签页 + * @param tab + */ + async pinTab(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + if (index === -1) { + return; + } + const oldTab = this.tabs[index]; + tab.meta.affixTab = true; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index + const newIndex = affixTabs.findIndex((item) => equalTab(item, tab)); + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + + /** + * 刷新标签页 + */ + async refresh(router: Router | string) { + // 如果是Router路由,那么就根据当前路由刷新 + // 如果是string字符串,为路由名称,则定向刷新指定标签页,不能是当前路由名称,否则不会刷新 + if (typeof router === 'string') { + return await this.refreshByName(router); + } + + const { currentRoute } = router; + const { name } = currentRoute.value; + + this.excludeCachedTabs.add(name as string); + this.renderRouteView = false; + startProgress(); + + await new Promise((resolve) => setTimeout(resolve, 200)); + + this.excludeCachedTabs.delete(name as string); + this.renderRouteView = true; + stopProgress(); + }, + + /** + * 根据路由名称刷新指定标签页 + */ + async refreshByName(name: string) { + this.excludeCachedTabs.add(name); + await new Promise((resolve) => setTimeout(resolve, 200)); + this.excludeCachedTabs.delete(name); + }, + + /** + * @zh_CN 重置标签页标题 + */ + async resetTabTitle(tab: TabDefinition) { + if (tab?.meta?.newTabTitle) { + return; + } + const findTab = this.tabs.find((item) => equalTab(item, tab)); + if (findTab) { + findTab.meta.newTabTitle = undefined; + await this.updateCacheTabs(); + } + }, + + /** + * 设置固定标签页 + * @param tabs + */ + setAffixTabs(tabs: RouteRecordNormalized[]) { + for (const tab of tabs) { + tab.meta.affixTab = true; + this.addTab(routeToTab(tab)); + } + }, + + /** + * @zh_CN 更新菜单列表 + * @param list + */ + setMenuList(list: string[]) { + this.menuList = list; + }, + + /** + * @zh_CN 设置标签页标题 + * + * @zh_CN 支持设置静态标题字符串或计算属性作为动态标题 + * @zh_CN 当标题为计算属性时,标题会随计算属性值变化而自动更新 + * @zh_CN 适用于需要根据状态或多语言动态更新标题的场景 + * + * @param {TabDefinition} tab - 标签页对象 + * @param {ComputedRef | string} title - 标题内容,支持静态字符串或计算属性 + * + * @example + * // 设置静态标题 + * setTabTitle(tab, '新标签页'); + * + * @example + * // 设置动态标题 + * setTabTitle(tab, computed(() => t('common.dashboard'))); + */ + async setTabTitle(tab: TabDefinition, title: ComputedRef | string) { + const findTab = this.tabs.find((item) => equalTab(item, tab)); + + if (findTab) { + findTab.meta.newTabTitle = title; + + await this.updateCacheTabs(); + } + }, + setUpdateTime() { + this.updateTime = Date.now(); + }, + /** + * @zh_CN 设置标签页顺序 + * @param oldIndex + * @param newIndex + */ + async sortTabs(oldIndex: number, newIndex: number) { + const currentTab = this.tabs[oldIndex]; + if (!currentTab) { + return; + } + this.tabs.splice(oldIndex, 1); + this.tabs.splice(newIndex, 0, currentTab); + this.dragEndIndex = this.dragEndIndex + 1; + }, + + /** + * @zh_CN 切换固定标签页 + * @param tab + */ + async toggleTabPin(tab: TabDefinition) { + const affixTab = tab?.meta?.affixTab ?? false; + + await (affixTab ? this.unpinTab(tab) : this.pinTab(tab)); + }, + + /** + * @zh_CN 取消固定标签页 + * @param tab + */ + async unpinTab(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + if (index === -1) { + return; + } + const oldTab = this.tabs[index]; + tab.meta.affixTab = false; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index,使用固定tabs的下一个位置也就是活动tabs的第一个位置 + const newIndex = affixTabs.length; + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + /** + * 根据当前打开的选项卡更新缓存 + */ + async updateCacheTabs() { + const cacheMap = new Set(); + + for (const tab of this.tabs) { + // 跳过不需要持久化的标签页 + const keepAlive = tab.meta?.keepAlive; + if (!keepAlive) { + continue; + } + (tab.matched || []).forEach((t, i) => { + if (i > 0) { + cacheMap.add(t.name as string); + } + }); + + const name = tab.name as string; + cacheMap.add(name); + } + this.cachedTabs = cacheMap; + }, + /** + * 添加缓存的route + * @param component + * @param route + */ + addCachedRoute(component: VNode, route: RouteLocationNormalizedLoaded) { + const key = getTabKey(route); + if (this.cachedRoutes.has(key)) { + return; + } + this.cachedRoutes.set(key, { + key, + component: markRaw(component), + route: markRaw(route), + }); + }, + removeCachedRoute(key: string) { + this.cachedRoutes.delete(key); + }, + }, + getters: { + affixTabs(): TabDefinition[] { + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + + return affixTabs.toSorted((a, b) => { + const orderA = (a.meta?.affixTabOrder ?? 0) as number; + const orderB = (b.meta?.affixTabOrder ?? 0) as number; + return orderA - orderB; + }); + }, + getCachedTabs(): string[] { + return [...this.cachedTabs]; + }, + getExcludeCachedTabs(): string[] { + return [...this.excludeCachedTabs]; + }, + getMenuList(): string[] { + return this.menuList; + }, + getTabs(): TabDefinition[] { + const normalTabs = this.tabs.filter((tab) => !isAffixTab(tab)); + return [...this.affixTabs, ...normalTabs].filter(Boolean); + }, + getCachedRoutes(): Map { + return this.cachedRoutes; + }, + }, + persist: [ + // tabs不需要保存在localStorage + { + pick: ['tabs', 'visitHistory'], + storage: sessionStorage, + serializer: { + serialize: JSON.stringify, + deserialize(value: string) { + const parsed = JSON.parse(value); + // Stack 类实例经 JSON 序列化后会变成普通对象 {dedup, items, maxSize}, + // 丢失所有方法和 getter,需要重新构建 Stack 实例 + if (parsed.visitHistory && !(parsed.visitHistory instanceof Stack)) { + const raw = parsed.visitHistory; + const stack = createStack(true, MAX_VISIT_HISTORY); + if (Array.isArray(raw.items)) { + stack.push(...raw.items); + } + parsed.visitHistory = stack; + } + return parsed; + }, + }, + }, + ], + state: (): TabbarState => ({ + visitHistory: createStack(true, MAX_VISIT_HISTORY), + cachedRoutes: new Map(), + cachedTabs: new Set(), + dragEndIndex: 0, + excludeCachedTabs: new Set(), + menuList: [ + 'close', + 'affix', + 'maximize', + 'reload', + 'open-in-new-window', + 'close-left', + 'close-right', + 'close-other', + 'close-all', + ], + renderRouteView: true, + tabs: [], + updateTime: Date.now(), + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useTabbarStore, hot)); +} + +/** + * @zh_CN 克隆路由,防止路由被修改 + * @param route + */ +function cloneTab(route: TabDefinition): TabDefinition { + if (!route) { + return route; + } + const { matched, meta, ...opt } = route; + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path, + })) + : undefined) as RouteRecordNormalized[], + meta: { + ...meta, + newTabTitle: meta.newTabTitle, + }, + }; +} + +/** + * @zh_CN 是否是固定标签页 + * @param tab + */ +function isAffixTab(tab: TabDefinition) { + return tab?.meta?.affixTab ?? false; +} + +/** + * @zh_CN 是否显示标签 + * @param tab + */ +function isTabShown(tab: TabDefinition) { + const matched = tab?.matched ?? []; + return !tab.meta.hideInTab && matched.every((item) => !item.meta.hideInTab); +} + +/** + * 从route获取tab页的key + * @param tab + */ +function getTabKey(tab: RouteLocationNormalized | RouteRecordNormalized) { + const { + fullPath, + path, + meta: { fullPathKey } = {}, + query = {}, + } = tab as RouteLocationNormalized; + // pageKey可能是数组(查询参数重复时可能出现) + const pageKey = Array.isArray(query.pageKey) + ? query.pageKey[0] + : query.pageKey; + let rawKey; + if (pageKey) { + rawKey = pageKey; + } else { + rawKey = fullPathKey === false ? path : (fullPath ?? path); + } + try { + return decodeURIComponent(rawKey); + } catch { + return rawKey; + } +} + +/** + * @zh_CN 是否开启访问历史记录 + */ +function isVisitHistory() { + return preferences.tabbar.visitHistory; +} + +/** + * 从tab获取tab页的key + * 如果tab没有key,那么就从route获取key + * @param tab + */ +function getTabKeyFromTab(tab: TabDefinition): string { + return tab.key ?? getTabKey(tab); +} + +/** + * 比较两个tab是否相等 + * @param a + * @param b + */ +function equalTab(a: TabDefinition, b: TabDefinition) { + return getTabKeyFromTab(a) === getTabKeyFromTab(b); +} + +function routeToTab(route: RouteRecordNormalized) { + return { + meta: route.meta, + name: route.name, + path: route.path, + key: getTabKey(route), + } as TabDefinition; +} + +export { getTabKey }; diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/timezone.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/timezone.ts new file mode 100644 index 0000000..24180c6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/timezone.ts @@ -0,0 +1,132 @@ +import { ref, unref } from 'vue'; + +import { DEFAULT_TIME_ZONE_OPTIONS } from '@vben-core/preferences'; +import { + getCurrentTimezone, + setCurrentTimezone, +} from '@vben-core/shared/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface TimezoneHandler { + getTimezone?: () => Promise; + getTimezoneOptions?: () => Promise< + { + label: string; + value: string; + }[] + >; + setTimezone?: (timezone: string) => Promise; +} + +/** + * 默认时区处理模块 + * 时区存储基于pinia存储插件 + */ +const getDefaultTimezoneHandler = (): TimezoneHandler => { + return { + getTimezoneOptions: () => { + return Promise.resolve( + DEFAULT_TIME_ZONE_OPTIONS.map((item) => { + return { + label: item.label, + value: item.timezone, + }; + }), + ); + }, + }; +}; + +/** + * 自定义时区处理模块 + */ +let customTimezoneHandler: null | Partial = null; +const setTimezoneHandler = (handler: Partial) => { + customTimezoneHandler = handler; +}; + +/** + * 获取时区处理模块 + */ +const getTimezoneHandler = () => { + return { + ...getDefaultTimezoneHandler(), + ...customTimezoneHandler, + }; +}; + +/** + * timezone支持模块 + */ +const useTimezoneStore = defineStore( + 'core-timezone', + () => { + const timezoneRef = ref(getCurrentTimezone()); + + /** + * 初始化时区 + * Initialize the timezone + */ + async function initTimezone() { + const timezoneHandler = getTimezoneHandler(); + const timezone = await timezoneHandler.getTimezone?.(); + if (timezone) { + timezoneRef.value = timezone; + } + // 设置dayjs默认时区 + setCurrentTimezone(unref(timezoneRef)); + } + + /** + * 设置时区 + * Set the timezone + * @param timezone 时区字符串 + */ + async function setTimezone(timezone: string) { + const timezoneHandler = getTimezoneHandler(); + await timezoneHandler.setTimezone?.(timezone); + timezoneRef.value = timezone; + // 设置dayjs默认时区 + setCurrentTimezone(timezone); + } + + /** + * 获取时区选项 + * Get the timezone options + */ + async function getTimezoneOptions() { + const timezoneHandler = getTimezoneHandler(); + return (await timezoneHandler.getTimezoneOptions?.()) || []; + } + + initTimezone().catch((error) => { + console.error('Failed to initialize timezone during store setup:', error); + }); + + function $reset() { + timezoneRef.value = getCurrentTimezone(); + } + + return { + timezone: timezoneRef, + setTimezone, + getTimezoneOptions, + $reset, + }; + }, + { + persist: { + // 持久化 + pick: ['timezone'], + }, + }, +); + +export { setTimezoneHandler, useTimezoneStore }; + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useTimezoneStore, hot)); +} diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/user.test.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/user.test.ts new file mode 100644 index 0000000..3d8a22c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/user.test.ts @@ -0,0 +1,37 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; + +import { useUserStore } from './user'; + +describe('useUserStore', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + it('returns correct userInfo', () => { + const store = useUserStore(); + const userInfo: any = { name: 'Jane Doe', roles: [{ value: 'user' }] }; + store.setUserInfo(userInfo); + expect(store.userInfo).toEqual(userInfo); + }); + + // 测试重置用户信息时的行为 + it('clears userInfo and userRoles when setting null userInfo', () => { + const store = useUserStore(); + store.setUserInfo({ + roles: [{ roleName: 'User', value: 'user' }], + } as any); + expect(store.userInfo).not.toBeNull(); + expect(store.userRoles.length).toBeGreaterThan(0); + + store.setUserInfo(null as any); + expect(store.userInfo).toBeNull(); + expect(store.userRoles).toEqual([]); + }); + + // 测试在没有用户角色时返回空数组 + it('returns an empty array for userRoles if not set', () => { + const store = useUserStore(); + expect(store.userRoles).toEqual([]); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/stores/src/modules/user.ts b/deploy/fba/fba-ui-src/packages/stores/src/modules/user.ts new file mode 100644 index 0000000..9d37433 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/modules/user.ts @@ -0,0 +1,64 @@ +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface BasicUserInfo { + [key: string]: any; + /** + * 头像 + */ + avatar: string; + /** + * 用户昵称 + */ + realName: string; + /** + * 用户角色 + */ + roles?: string[]; + /** + * 用户id + */ + userId: string; + /** + * 用户名 + */ + username: string; +} + +interface AccessState { + /** + * 用户信息 + */ + userInfo: BasicUserInfo | null; + /** + * 用户角色 + */ + userRoles: string[]; +} + +/** + * @zh_CN 用户信息相关 + */ +export const useUserStore = defineStore('core-user', { + actions: { + setUserInfo(userInfo: BasicUserInfo | null) { + // 设置用户信息 + this.userInfo = userInfo; + // 设置角色信息 + const roles = userInfo?.roles ?? []; + this.setUserRoles(roles); + }, + setUserRoles(roles: string[]) { + this.userRoles = roles; + }, + }, + state: (): AccessState => ({ + userInfo: null, + userRoles: [], + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useUserStore, hot)); +} diff --git a/deploy/fba/fba-ui-src/packages/stores/src/setup.ts b/deploy/fba/fba-ui-src/packages/stores/src/setup.ts new file mode 100644 index 0000000..36b3a70 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/src/setup.ts @@ -0,0 +1,81 @@ +import type { Pinia } from 'pinia'; + +import type { App } from 'vue'; + +import { createPinia } from 'pinia'; +import SecureLS from 'secure-ls'; + +let pinia: Pinia; + +type SecureLSStorage = { + get(key: string): any; + set(key: string, value: unknown): void; +}; + +type SecureLSCtor = new (config?: { + encodingType?: string; + encryptionSecret?: string; + isCompression?: boolean; + metaKey?: string; +}) => SecureLSStorage; + +const secureLSModule = SecureLS as unknown as { + default?: SecureLSCtor; + SecureLS?: SecureLSCtor; +}; + +const SecureLSConstructor = + secureLSModule.default ?? + secureLSModule.SecureLS ?? + (SecureLS as unknown as SecureLSCtor); + +export interface InitStoreOptions { + /** + * @zh_CN 应用名,由于 @vben/stores 是公用的,后续可能有多个app,为了防止多个app缓存冲突,可在这里配置应用名,应用名将被用于持久化的前缀 + */ + namespace: string; +} + +/** + * @zh_CN 初始化pinia + */ +export async function initStores(app: App, options: InitStoreOptions) { + const { createPersistedState } = await import('pinia-plugin-persistedstate'); + pinia = createPinia(); + const { namespace } = options; + const ls = new SecureLSConstructor({ + encodingType: 'aes', + encryptionSecret: import.meta.env.VITE_APP_STORE_SECURE_KEY, + isCompression: true, + metaKey: `${namespace}-secure-meta`, + }); + pinia.use( + createPersistedState({ + // key $appName-$store.id + key: (storeKey) => `${namespace}-${storeKey}`, + storage: import.meta.env.DEV + ? localStorage + : { + getItem(key) { + return ls.get(key); + }, + setItem(key, value) { + ls.set(key, value); + }, + }, + }), + ); + app.use(pinia); + return pinia; +} + +export function resetAllStores() { + if (!pinia) { + console.error('Pinia is not installed'); + return; + } + const allStores = (pinia as any)._s; + for (const [_key, store] of allStores) { + store.$reset(); + } +} diff --git a/deploy/fba/fba-ui-src/packages/stores/tsconfig.json b/deploy/fba/fba-ui-src/packages/stores/tsconfig.json new file mode 100644 index 0000000..3057820 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/stores/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src", "shim-pinia.d.ts"] +} diff --git a/deploy/fba/fba-ui-src/packages/styles/README.md b/deploy/fba/fba-ui-src/packages/styles/README.md new file mode 100644 index 0000000..4826446 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/README.md @@ -0,0 +1,19 @@ +# @vben/styles + +用于多个 `app` 公用的样式文件,继承了 `@vben-core/design` 的所有能力。业务上有通用的样式文件可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/styles +``` + +### 使用 + +```ts +import '@vben/styles'; +``` diff --git a/deploy/fba/fba-ui-src/packages/styles/package.json b/deploy/fba/fba-ui-src/packages/styles/package.json new file mode 100644 index 0000000..2a3a1e1 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/package.json @@ -0,0 +1,42 @@ +{ + "name": "@vben/styles", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/styles" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./antd": { + "types": "./src/style-exports.d.ts", + "default": "./src/antd/index.css" + }, + "./antdv-next": { + "types": "./src/style-exports.d.ts", + "default": "./src/antdv-next/index.css" + }, + "./ele": { + "types": "./src/style-exports.d.ts", + "default": "./src/ele/index.css" + }, + "./naive": { + "types": "./src/style-exports.d.ts", + "default": "./src/naive/index.css" + }, + "./global": { + "types": "./src/style-exports.d.ts", + "default": "./src/global/index.scss" + } + }, + "dependencies": { + "@vben-core/design": "workspace:*" + } +} diff --git a/deploy/fba/fba-ui-src/packages/styles/src/antd/index.css b/deploy/fba/fba-ui-src/packages/styles/src/antd/index.css new file mode 100644 index 0000000..03b9e9e --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/antd/index.css @@ -0,0 +1,78 @@ +/* ant-design-vue 组件库的一些样式重置 */ + +.ant-app { + width: 100%; + height: 100%; + overscroll-behavior: none; + color: inherit; +} + +.ant-btn { + .anticon { + display: inline-flex; + } + + /* * 修复按钮添加图标时的位置问题 */ + > svg { + display: inline-block; + } + + > svg + span { + margin-inline-start: 6px; + } +} + +.ant-tag { + > svg { + display: inline-block; + } + + > svg + span { + margin-inline-start: 4px; + } +} + +.ant-message-notice-content, +.ant-notification-notice { + &:is(.dark *) { + border-color: hsl(var(--border) / 60%); + border-width: 1px; + } +} + +.form-valid-error { + /** select 选择器的样式 */ + + .ant-select:not(.valid-success) .ant-select-selector:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } + + .ant-select-focused .ant-select-selector { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%) !important; + } + + /** 数字输入框样式 */ + .ant-input-number-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + /** 密码输入框样式 */ + .ant-input-affix-wrapper:hover { + border-color: hsl(var(--destructive)); + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + .ant-input:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } +} + +/** 区间选择器下面来回切换时的样式 */ +.ant-app .form-valid-error .ant-picker-active-bar { + background-color: hsl(var(--destructive)); +} + +/** 时间选择器的样式 */ +.ant-app .form-valid-error .ant-picker-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); +} diff --git a/deploy/fba/fba-ui-src/packages/styles/src/antdv-next/index.css b/deploy/fba/fba-ui-src/packages/styles/src/antdv-next/index.css new file mode 100644 index 0000000..fd65221 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/antdv-next/index.css @@ -0,0 +1,80 @@ +/* antdv-next 组件库的一些样式重置 */ + +.ant-app { + width: 100%; + height: 100%; + overscroll-behavior: none; + color: inherit; +} + +.ant-btn { + .anticon { + display: inline-flex; + } + + /* * 修复按钮添加图标时的位置问题 */ + > .ant-btn-icon { + svg { + display: inline-block; + } + + svg + span { + margin-inline-start: 6px; + } + } +} + +.ant-tag { + > svg { + display: inline-block; + } + + > svg + span { + margin-inline-start: 4px; + } +} + +.ant-message-notice-content, +.ant-notification-notice { + &:is(.dark *) { + border-color: hsl(var(--border) / 60%); + border-width: 1px; + } +} + +.form-valid-error { + /** select 选择器的样式 */ + + .ant-select:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } + + .ant-select-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%) !important; + } + + /** 数字输入框样式 */ + .ant-input-number-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + /** 密码输入框样式 */ + .ant-input-affix-wrapper:hover { + border-color: hsl(var(--destructive)); + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + .ant-input:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } +} + +/** 区间选择器下面来回切换时的样式 */ +.ant-app .form-valid-error .ant-picker-active-bar { + background-color: hsl(var(--destructive)); +} + +/** 时间选择器的样式 */ +.ant-app .form-valid-error .ant-picker-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); +} diff --git a/deploy/fba/fba-ui-src/packages/styles/src/ele/index.css b/deploy/fba/fba-ui-src/packages/styles/src/ele/index.css new file mode 100644 index 0000000..4ee8f7b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/ele/index.css @@ -0,0 +1,44 @@ +.el-card { + --el-card-border-radius: var(--radius) !important; +} + +.form-valid-error { + /** select 选择器的样式 */ + .el-select .el-select__wrapper { + box-shadow: 0 0 0 1px var(--el-color-danger) inset; + } + + /** input 选择器的样式 */ + .el-input .el-input__wrapper { + box-shadow: 0 0 0 1px var(--el-color-danger) inset; + } + + /** radio和checkbox 选择器的样式 */ + .el-radio .el-radio__inner, + .el-checkbox .el-checkbox__inner { + border: 1px solid var(--el-color-danger); + } + + .el-checkbox-button .el-checkbox-button__inner, + .el-radio-button .el-radio-button__inner { + border: 1px solid var(--el-color-danger); + } + + .el-checkbox-button:first-child .el-checkbox-button__inner, + .el-radio-button:first-child .el-radio-button__inner { + border-left: 1px solid var(--el-color-danger); + } + + .el-checkbox-button:not(:first-child) .el-checkbox-button__inner, + .el-radio-button:not(:first-child) .el-radio-button__inner { + border-left: none; + } + + .el-textarea .el-textarea__inner { + border: 1px solid var(--el-color-danger); + } +} + +html .el-loading-mask { + z-index: 1000; +} diff --git a/deploy/fba/fba-ui-src/packages/styles/src/global/index.scss b/deploy/fba/fba-ui-src/packages/styles/src/global/index.scss new file mode 100644 index 0000000..c7af9bb --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/global/index.scss @@ -0,0 +1 @@ +@use '@vben-core/design/bem' as *; diff --git a/deploy/fba/fba-ui-src/packages/styles/src/index.ts b/deploy/fba/fba-ui-src/packages/styles/src/index.ts new file mode 100644 index 0000000..b375456 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/index.ts @@ -0,0 +1 @@ +import '@vben-core/design'; diff --git a/deploy/fba/fba-ui-src/packages/styles/src/naive/index.css b/deploy/fba/fba-ui-src/packages/styles/src/naive/index.css new file mode 100644 index 0000000..f97d55b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/naive/index.css @@ -0,0 +1,26 @@ +.form-valid-error { + .n-base-selection__state-border, + .n-input__state-border, + .n-radio-group__splitor { + border: var(--n-border-error); + } + + .n-radio-group .n-radio-button, + .n-radio-group .n-radio-group__splitor { + --n-button-border-color: rgb(255 56 96); + } + + .n-radio__dot { + --n-box-shadow: inset 0 0 0 1px rgb(255 56 96); + } + + .n-checkbox-box__border { + --n-border: 1px solid rgb(255 56 96); + } + + .n-input .n-input__input-el, + .n-input .n-input__textarea-el, + .n-input-number .n-input__input-el { + font-family: Arial, Roboto, 'Helvetica Neue', sans-serif; + } +} diff --git a/deploy/fba/fba-ui-src/packages/styles/src/style-exports.d.ts b/deploy/fba/fba-ui-src/packages/styles/src/style-exports.d.ts new file mode 100644 index 0000000..a9f6f08 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/src/style-exports.d.ts @@ -0,0 +1,3 @@ +declare const stylesheet: string; + +export default stylesheet; diff --git a/deploy/fba/fba-ui-src/packages/styles/tsconfig.json b/deploy/fba/fba-ui-src/packages/styles/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/styles/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/types/README.md b/deploy/fba/fba-ui-src/packages/types/README.md new file mode 100644 index 0000000..f796f31 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/README.md @@ -0,0 +1,20 @@ +# @vben/types + +用于多个 `app` 公用的工具类型,继承了 `@vben-core/typings` 的所有能力。业务上有通用的类型定义可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/types +``` + +### 使用 + +```ts +// 推荐加上 type +import type { SelectOption } from '@vben/types'; +``` diff --git a/deploy/fba/fba-ui-src/packages/types/global.d.ts b/deploy/fba/fba-ui-src/packages/types/global.d.ts new file mode 100644 index 0000000..77bc923 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/global.d.ts @@ -0,0 +1,32 @@ +import type { RouteMeta as IRouteMeta } from '@vben-core/typings'; + +import 'vue-router'; + +declare module 'vue-router' { + // oxlint-disable-next-line typescript/no-empty-object-type + interface RouteMeta extends IRouteMeta {} +} + +export interface VbenAdminProAppConfigRaw { + VITE_GLOB_API_URL: string; + VITE_GLOB_AUTH_DINGDING_CLIENT_ID: string; + VITE_GLOB_AUTH_DINGDING_CORP_ID: string; +} + +interface AuthConfig { + dingding?: { + clientId: string; + corpId: string; + }; +} + +export interface ApplicationConfig { + apiURL: string; + auth: AuthConfig; +} + +declare global { + interface Window { + _VBEN_ADMIN_PRO_APP_CONF_: VbenAdminProAppConfigRaw; + } +} diff --git a/deploy/fba/fba-ui-src/packages/types/package.json b/deploy/fba/fba-ui-src/packages/types/package.json new file mode 100644 index 0000000..e489048 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vben/types", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/types" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./global": { + "types": "./global.d.ts" + } + }, + "dependencies": { + "@vben-core/typings": "workspace:*", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/types/src/index.ts b/deploy/fba/fba-ui-src/packages/types/src/index.ts new file mode 100644 index 0000000..1e266c4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/src/index.ts @@ -0,0 +1,2 @@ +export type * from './user'; +export type * from '@vben-core/typings'; diff --git a/deploy/fba/fba-ui-src/packages/types/src/user.ts b/deploy/fba/fba-ui-src/packages/types/src/user.ts new file mode 100644 index 0000000..44dc5b4 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/src/user.ts @@ -0,0 +1,20 @@ +import type { BasicUserInfo } from '@vben-core/typings'; + +/** 用户信息 */ +interface UserInfo extends BasicUserInfo { + /** + * 用户描述 + */ + desc: string; + /** + * 首页地址 + */ + homePath: string; + + /** + * accessToken + */ + token: string; +} + +export type { UserInfo }; diff --git a/deploy/fba/fba-ui-src/packages/types/tsconfig.json b/deploy/fba/fba-ui-src/packages/types/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/types/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/packages/utils/README.md b/deploy/fba/fba-ui-src/packages/utils/README.md new file mode 100644 index 0000000..f06068a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/README.md @@ -0,0 +1,19 @@ +# @vben/utils + +用于多个 `app` 公用的工具包,继承了 `@vben-core/shared/utils` 的所有能力。业务上有通用的工具函数可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/utils +``` + +### 使用 + +```ts +import { isString } from '@vben/utils'; +``` diff --git a/deploy/fba/fba-ui-src/packages/utils/package.json b/deploy/fba/fba-ui-src/packages/utils/package.json new file mode 100644 index 0000000..fddacfe --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vben/utils", + "version": "5.7.0", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/utils" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "vue-router": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts new file mode 100644 index 0000000..fc0d76b --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, it } from 'vitest'; + +import { findMenuByPath, findRootMenuByPath } from '../find-menu-by-path'; + +// 示例菜单数据 +const menus: any[] = [ + { path: '/', children: [] }, + { path: '/about', children: [] }, + { + path: '/contact', + children: [ + { path: '/contact/email', children: [] }, + { path: '/contact/phone', children: [] }, + ], + }, + { + path: '/services', + children: [ + { path: '/services/design', children: [] }, + { + path: '/services/development', + children: [{ path: '/services/development/web', children: [] }], + }, + ], + }, +]; + +describe('menu Finder Tests', () => { + it('finds a top-level menu', () => { + const menu = findMenuByPath(menus, '/about'); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/about'); + }); + + it('finds a nested menu', () => { + const menu = findMenuByPath(menus, '/services/development/web'); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/services/development/web'); + }); + + it('returns null for a non-existent path', () => { + const menu = findMenuByPath(menus, '/non-existent'); + expect(menu).toBeNull(); + }); + + it('handles empty menus list', () => { + const menu = findMenuByPath([], '/about'); + expect(menu).toBeNull(); + }); + + it('handles menu items without children', () => { + const menu = findMenuByPath( + [{ path: '/only', children: undefined }] as any[], + '/only', + ); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/only'); + }); + + it('finds root menu by path', () => { + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus, + '/services/development/web', + ); + + expect(findMenu).toBeDefined(); + expect(rootMenu).toBeUndefined(); + expect(rootMenuPath).toBeUndefined(); + expect(findMenu?.path).toBe('/services/development/web'); + }); + + it('returns null for undefined or empty path', () => { + const menuUndefinedPath = findMenuByPath(menus); + const menuEmptyPath = findMenuByPath(menus, ''); + expect(menuUndefinedPath).toBeNull(); + expect(menuEmptyPath).toBeNull(); + }); + + it('checks for root menu when path does not exist', () => { + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus, + '/non-existent', + ); + expect(findMenu).toBeNull(); + expect(rootMenu).toBeUndefined(); + expect(rootMenuPath).toBeUndefined(); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-menus.test.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-menus.test.ts new file mode 100644 index 0000000..c02cc9d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-menus.test.ts @@ -0,0 +1,233 @@ +import type { Router, RouteRecordRaw } from 'vue-router'; + +import { createRouter, createWebHistory } from 'vue-router'; + +import { describe, expect, it, vi } from 'vitest'; + +import { generateMenus } from '../generate-menus'; + +// Nested route setup to test child inclusion and hideChildrenInMenu functionality + +describe('generateMenus', () => { + // 模拟路由数据 + const mockRoutes = [ + { + meta: { icon: 'home-icon', title: '首页' }, + name: 'home', + path: '/home', + }, + { + meta: { hideChildrenInMenu: true, icon: 'about-icon', title: '关于' }, + name: 'about', + path: '/about', + children: [ + { + path: 'team', + name: 'team', + meta: { icon: 'team-icon', title: '团队' }, + }, + ], + }, + ] as RouteRecordRaw[]; + + // 模拟 Vue 路由器实例 + const mockRouter = { + getRoutes: vi.fn(() => [ + { name: 'home', path: '/home' }, + { name: 'about', path: '/about' }, + { name: 'team', path: '/about/team' }, + ]), + }; + + it('the correct menu list should be generated according to the route', async () => { + const expectedMenus = [ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'home-icon', + name: '首页', + order: undefined, + parent: undefined, + parents: undefined, + path: '/home', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'about-icon', + name: '关于', + order: undefined, + parent: undefined, + parents: undefined, + path: '/about', + show: true, + children: [], + }, + ]; + + const menus = generateMenus(mockRoutes, mockRouter as any); + expect(menus).toEqual(expectedMenus); + }); + + it('includes additional meta properties in menu items', async () => { + const mockRoutesWithMeta = [ + { + meta: { icon: 'user-icon', order: 1, title: 'Profile' }, + name: 'profile', + path: '/profile', + }, + ] as RouteRecordRaw[]; + + const menus = generateMenus(mockRoutesWithMeta, mockRouter as any); + expect(menus).toEqual([ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'user-icon', + name: 'Profile', + order: 1, + parent: undefined, + parents: undefined, + path: '/profile', + show: true, + children: [], + }, + ]); + }); + + it('handles dynamic route parameters correctly', async () => { + const mockRoutesWithParams = [ + { + meta: { icon: 'details-icon', title: 'User Details' }, + name: 'userDetails', + path: '/users/:userId', + }, + ] as RouteRecordRaw[]; + + const menus = generateMenus(mockRoutesWithParams, mockRouter as any); + expect(menus).toEqual([ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'details-icon', + name: 'User Details', + order: undefined, + parent: undefined, + parents: undefined, + path: '/users/:userId', + show: true, + children: [], + }, + ]); + }); + + it('processes routes with redirects correctly', async () => { + const mockRoutesWithRedirect = [ + { + name: 'redirectedRoute', + path: '/old-path', + redirect: '/new-path', + }, + { + meta: { icon: 'path-icon', title: 'New Path' }, + name: 'newPath', + path: '/new-path', + }, + ] as RouteRecordRaw[]; + + const menus = generateMenus(mockRoutesWithRedirect, mockRouter as any); + expect(menus).toEqual([ + // Assuming your generateMenus function excludes redirect routes from the menu + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'redirectedRoute', + order: undefined, + parent: undefined, + parents: undefined, + path: '/old-path', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'path-icon', + name: 'New Path', + order: undefined, + parent: undefined, + parents: undefined, + path: '/new-path', + show: true, + children: [], + }, + ]); + }); + + const routes: any = [ + { + meta: { order: 2, title: 'Home' }, + name: 'home', + path: '/', + }, + { + meta: { order: 1, title: 'About' }, + name: 'about', + path: '/about', + }, + ]; + + const router: Router = createRouter({ + history: createWebHistory(), + routes, + }); + + it('should generate menu list with correct order', async () => { + const menus = generateMenus(routes, router); + const expectedMenus = [ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'About', + order: 1, + parent: undefined, + parents: undefined, + path: '/about', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'Home', + order: 2, + parent: undefined, + parents: undefined, + path: '/', + show: true, + children: [], + }, + ]; + + expect(menus).toEqual(expectedMenus); + }); + + it('should handle empty routes', async () => { + const emptyRoutes: any[] = []; + const menus = generateMenus(emptyRoutes, router); + expect(menus).toEqual([]); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts new file mode 100644 index 0000000..8e01853 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts @@ -0,0 +1,105 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { describe, expect, it } from 'vitest'; + +import { + generateRoutesByFrontend, + hasAuthority, +} from '../generate-routes-frontend'; + +// Mock 路由数据 +const mockRoutes = [ + { + meta: { + authority: ['admin', 'user'], + hideInMenu: false, + }, + path: '/dashboard', + children: [ + { + path: '/dashboard/overview', + meta: { authority: ['admin'], hideInMenu: false }, + }, + { + path: '/dashboard/stats', + meta: { authority: ['user'], hideInMenu: true }, + }, + ], + }, + { + meta: { authority: ['admin'], hideInMenu: false }, + path: '/settings', + }, + { + meta: { hideInMenu: false }, + path: '/profile', + }, +] as RouteRecordRaw[]; + +describe('hasAuthority', () => { + it('should return true if there is no authority defined', () => { + expect(hasAuthority(mockRoutes[2], ['admin'])).toBe(true); + }); + + it('should return true if the user has the required authority', () => { + expect(hasAuthority(mockRoutes[0], ['admin'])).toBe(true); + }); + + it('should return false if the user does not have the required authority', () => { + expect(hasAuthority(mockRoutes[1], ['user'])).toBe(false); + }); +}); + +describe('generateRoutesByFrontend', () => { + it('should handle routes without children', async () => { + const generatedRoutes = await generateRoutesByFrontend(mockRoutes, [ + 'user', + ]); + expect(generatedRoutes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + path: '/profile', // This route has no children and should be included + }), + ]), + ); + }); + + it('should handle empty roles array', async () => { + const generatedRoutes = await generateRoutesByFrontend(mockRoutes, []); + expect(generatedRoutes).toEqual( + expect.arrayContaining([ + // Only routes without authority should be included + expect.objectContaining({ + path: '/profile', + }), + ]), + ); + expect(generatedRoutes).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + path: '/dashboard', + }), + expect.objectContaining({ + path: '/settings', + }), + ]), + ); + }); + + it('should handle missing meta fields', async () => { + const routesWithMissingMeta = [ + { path: '/path1' }, // No meta + { meta: {}, path: '/path2' }, // Empty meta + { meta: { authority: ['admin'] }, path: '/path3' }, // Only authority + ]; + const generatedRoutes = await generateRoutesByFrontend( + routesWithMissingMeta as RouteRecordRaw[], + ['admin'], + ); + expect(generatedRoutes).toEqual([ + { path: '/path1' }, + { meta: {}, path: '/path2' }, + { meta: { authority: ['admin'] }, path: '/path3' }, + ]); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts new file mode 100644 index 0000000..3615556 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts @@ -0,0 +1,68 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import type { RouteModuleType } from '../merge-route-modules'; + +import { describe, expect, it } from 'vitest'; + +import { mergeRouteModules } from '../merge-route-modules'; + +describe('mergeRouteModules', () => { + it('should merge route modules correctly', () => { + const routeModules: Record = { + './dynamic-routes/about.ts': { + default: [ + { + component: () => Promise.resolve({ template: '
About
' }), + name: 'About', + path: '/about', + }, + ], + }, + './dynamic-routes/home.ts': { + default: [ + { + component: () => Promise.resolve({ template: '
Home
' }), + name: 'Home', + path: '/', + }, + ], + }, + }; + + const expectedRoutes: RouteRecordRaw[] = [ + { + component: expect.any(Function), + name: 'About', + path: '/about', + }, + { + component: expect.any(Function), + name: 'Home', + path: '/', + }, + ]; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); + + it('should handle empty modules', () => { + const routeModules: Record = {}; + const expectedRoutes: RouteRecordRaw[] = []; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); + + it('should handle modules with no default export', () => { + const routeModules: Record = { + './dynamic-routes/empty.ts': { + default: [], + }, + }; + const expectedRoutes: RouteRecordRaw[] = []; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); +}); diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/find-menu-by-path.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/find-menu-by-path.ts new file mode 100644 index 0000000..747cc7f --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/find-menu-by-path.ts @@ -0,0 +1,37 @@ +import type { MenuRecordRaw } from '@vben-core/typings'; + +function findMenuByPath( + list: MenuRecordRaw[], + path?: string, +): MenuRecordRaw | null { + for (const menu of list) { + if (menu.path === path) { + return menu; + } + const findMenu = menu.children && findMenuByPath(menu.children, path); + if (findMenu) { + return findMenu; + } + } + return null; +} + +/** + * 查找根菜单 + * @param menus + * @param path + */ +function findRootMenuByPath(menus: MenuRecordRaw[], path?: string, level = 0) { + const findMenu = findMenuByPath(menus, path); + const rootMenuPath = findMenu?.parents?.[level]; + const rootMenu = rootMenuPath + ? menus.find((item) => item.path === rootMenuPath) + : undefined; + return { + findMenu, + rootMenu, + rootMenuPath, + }; +} + +export { findMenuByPath, findRootMenuByPath }; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-menus.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-menus.ts new file mode 100644 index 0000000..9209048 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-menus.ts @@ -0,0 +1,92 @@ +import type { Router, RouteRecordRaw } from 'vue-router'; + +import type { + ExRouteRecordRaw, + MenuRecordRaw, + RouteMeta, +} from '@vben-core/typings'; + +import { filterTree, mapTree, sortTree } from '@vben-core/shared/utils'; + +/** + * 根据 routes 生成菜单列表 + * @param routes - 路由配置列表 + * @param router - Vue Router 实例 + * @returns 生成的菜单列表 + */ +function generateMenus( + routes: RouteRecordRaw[], + router: Router, +): MenuRecordRaw[] { + // 将路由列表转换为一个以 name 为键的对象映射 + const finalRoutesMap: { [key: string]: string } = Object.fromEntries( + router.getRoutes().map(({ name, path }) => [name, path]), + ); + + let menus = mapTree(routes, (route) => { + // 获取最终的路由路径 + const path = finalRoutesMap[route.name as string] ?? route.path ?? ''; + + const { + meta = {} as RouteMeta, + name: routeName, + redirect, + children = [], + } = route; + const { + activeIcon, + badge, + badgeType, + badgeVariants, + hideChildrenInMenu = false, + icon, + link, + order, + title = '', + query, + } = meta; + + // 确保菜单名称不为空 + const name = (title || routeName || '') as string; + + // 处理子菜单 + const resultChildren = hideChildrenInMenu + ? [] + : ((children as MenuRecordRaw[]) ?? []); + + // 设置子菜单的父子关系 + if (resultChildren.length > 0) { + resultChildren.forEach((child) => { + child.parents = [...(route.parents ?? []), path]; + child.parent = path; + }); + } + + // 确定最终路径 + const resultPath = hideChildrenInMenu ? redirect || path : link || path; + + return { + activeIcon, + badge, + badgeType, + badgeVariants, + icon, + name, + query, + order, + parent: route.parent, + parents: route.parents, + path: resultPath, + show: !meta.hideInMenu, + children: resultChildren, + }; + }); + + // 对菜单进行排序,避免order=0时被替换成999的问题 + menus = sortTree(menus, (a, b) => (a?.order ?? 999) - (b?.order ?? 999)); + + // 过滤掉隐藏的菜单项 + return filterTree(menus, (menu) => !!menu.show); +} + +export { generateMenus }; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-backend.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-backend.ts new file mode 100644 index 0000000..3f8693c --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-backend.ts @@ -0,0 +1,108 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, + RouteRecordStringComponent, +} from '@vben-core/typings'; + +import { mapTree } from '@vben-core/shared/utils'; + +/** + * 判断路由是否在菜单中显示但访问时展示 403(让用户知悉功能并申请权限) + */ +function menuHasVisibleWithForbidden(route: RouteRecordRaw): boolean { + return !!route.meta?.menuVisibleWithForbidden; +} + +/** + * 动态生成路由 - 后端方式 + * 对 meta.menuVisibleWithForbidden 为 true 的项直接替换为 403 组件,让用户知悉功能并申请权限。 + */ +async function generateRoutesByBackend( + options: GenerateMenuAndRoutesOptions, +): Promise { + const { + fetchMenuListAsync, + layoutMap = {}, + pageMap = {}, + forbiddenComponent, + } = options; + + try { + const menuRoutes = await fetchMenuListAsync?.(); + if (!menuRoutes) { + return []; + } + + const normalizePageMap: ComponentRecordType = {}; + + for (const [key, value] of Object.entries(pageMap)) { + normalizePageMap[normalizeViewPath(key)] = value; + } + + let routes = convertRoutes(menuRoutes, layoutMap, normalizePageMap); + + if (forbiddenComponent) { + routes = mapTree(routes, (route) => { + if (menuHasVisibleWithForbidden(route)) { + route.component = forbiddenComponent; + } + return route; + }); + } + + return routes; + } catch (error) { + console.error(error); + throw error; + } +} + +function convertRoutes( + routes: RouteRecordStringComponent[], + layoutMap: ComponentRecordType, + pageMap: ComponentRecordType, +): RouteRecordRaw[] { + return mapTree(routes, (node) => { + const route = node as unknown as RouteRecordRaw; + const { component, name } = node; + + if (!name) { + console.error('route name is required', route); + } + + // layout转换 + if (component && layoutMap[component]) { + route.component = layoutMap[component]; + // 页面组件转换 + } else if (component) { + const normalizePath = normalizeViewPath(component); + const pageKey = normalizePath.endsWith('.vue') + ? normalizePath + : `${normalizePath}.vue`; + if (pageMap[pageKey]) { + route.component = pageMap[pageKey]; + } else { + console.error(`route component is invalid: ${pageKey}`, route); + route.component = pageMap['/_core/fallback/not-found.vue']; + } + } + + return route; + }); +} + +function normalizeViewPath(path: string): string { + // 去除相对路径前缀 + const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, ''); + + // 确保路径以 '/' 开头 + const viewPath = normalizedPath.startsWith('/') + ? normalizedPath + : `/${normalizedPath}`; + + // 这里耦合了vben-admin的目录结构 + return viewPath.replace(/^\/views/, ''); +} +export { generateRoutesByBackend }; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-frontend.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-frontend.ts new file mode 100644 index 0000000..dafc8a7 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/generate-routes-frontend.ts @@ -0,0 +1,58 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { filterTree, mapTree } from '@vben-core/shared/utils'; + +/** + * 动态生成路由 - 前端方式 + */ +async function generateRoutesByFrontend( + routes: RouteRecordRaw[], + roles: string[], + forbiddenComponent?: RouteRecordRaw['component'], +): Promise { + // 根据角色标识过滤路由表,判断当前用户是否拥有指定权限 + const finalRoutes = filterTree(routes, (route) => { + return hasAuthority(route, roles); + }); + + if (!forbiddenComponent) { + return finalRoutes; + } + + // 如果有禁止访问的页面,将禁止访问的页面替换为403页面 + return mapTree(finalRoutes, (route) => { + if (menuHasVisibleWithForbidden(route)) { + route.component = forbiddenComponent; + } + return route; + }); +} + +/** + * 判断路由是否有权限访问 + * @param route + * @param access + */ +function hasAuthority(route: RouteRecordRaw, access: string[]) { + const authority = route.meta?.authority; + if (!authority) { + return true; + } + const canAccess = access.some((value) => authority.includes(value)); + + return canAccess || (!canAccess && menuHasVisibleWithForbidden(route)); +} + +/** + * 判断路由是否在菜单中显示,但是访问会被重定向到403 + * @param route + */ +function menuHasVisibleWithForbidden(route: RouteRecordRaw) { + return ( + !!route.meta?.authority && + Reflect.has(route.meta || {}, 'menuVisibleWithForbidden') && + !!route.meta?.menuVisibleWithForbidden + ); +} + +export { generateRoutesByFrontend, hasAuthority }; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/get-popup-container.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/get-popup-container.ts new file mode 100644 index 0000000..6aa84d6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/get-popup-container.ts @@ -0,0 +1,10 @@ +/** + * If the node is holding inside a form, return the form element, + * otherwise return the parent node of the given element or + * the document body if the element is not provided. + */ +export function getPopupContainer(node?: HTMLElement): HTMLElement { + return ( + node?.closest('form') ?? (node?.parentNode as HTMLElement) ?? document.body + ); +} diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/index.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/index.ts new file mode 100644 index 0000000..da2cd8d --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/index.ts @@ -0,0 +1,8 @@ +export * from './find-menu-by-path'; +export * from './generate-menus'; +export * from './generate-routes-backend'; +export * from './generate-routes-frontend'; +export * from './get-popup-container'; +export * from './merge-route-modules'; +export * from './reset-routes'; +export * from './unmount-global-loading'; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/merge-route-modules.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/merge-route-modules.ts new file mode 100644 index 0000000..53e21f3 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/merge-route-modules.ts @@ -0,0 +1,28 @@ +import type { RouteRecordRaw } from 'vue-router'; + +// 定义模块类型 +interface RouteModuleType { + default: RouteRecordRaw[]; +} + +/** + * 合并动态路由模块的默认导出 + * @param routeModules 动态导入的路由模块对象 + * @returns 合并后的路由配置数组 + */ +function mergeRouteModules( + routeModules: Record, +): RouteRecordRaw[] { + const mergedRoutes: RouteRecordRaw[] = []; + + for (const routeModule of Object.values(routeModules)) { + const moduleRoutes = (routeModule as RouteModuleType)?.default ?? []; + mergedRoutes.push(...moduleRoutes); + } + + return mergedRoutes; +} + +export { mergeRouteModules }; + +export type { RouteModuleType }; diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/reset-routes.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/reset-routes.ts new file mode 100644 index 0000000..0d53a00 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/reset-routes.ts @@ -0,0 +1,31 @@ +import type { Router, RouteRecordName, RouteRecordRaw } from 'vue-router'; + +import { traverseTreeValues } from '@vben-core/shared/utils'; + +/** + * @zh_CN 重置所有路由,如有指定白名单除外 + */ +export function resetStaticRoutes(router: Router, routes: RouteRecordRaw[]) { + // 获取静态路由所有节点包含子节点的 name,并排除不存在 name 字段的路由 + const staticRouteNames = traverseTreeValues< + RouteRecordRaw, + RouteRecordName | undefined + >(routes, (route) => { + // 这些路由需要指定 name,防止在路由重置时,不能删除没有指定 name 的路由 + if (!route.name) { + console.warn( + `The route with the path ${route.path} needs to have the field name specified.`, + ); + } + return route.name; + }); + + const { getRoutes, hasRoute, removeRoute } = router; + const allRoutes = getRoutes(); + allRoutes.forEach(({ name }) => { + // 存在于路由表且非白名单才需要删除 + if (name && !staticRouteNames.includes(name) && hasRoute(name)) { + removeRoute(name); + } + }); +} diff --git a/deploy/fba/fba-ui-src/packages/utils/src/helpers/unmount-global-loading.ts b/deploy/fba/fba-ui-src/packages/utils/src/helpers/unmount-global-loading.ts new file mode 100644 index 0000000..10b88ea --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/helpers/unmount-global-loading.ts @@ -0,0 +1,31 @@ +/** + * 移除并销毁loading + * 放在这里是而不是放在 index.html 的app标签内,是因为这样比较不会生硬,渲染过快可能会有闪烁 + * 通过先添加css动画隐藏,在动画结束后在移除loading节点来改善体验 + * 不好的地方是会增加一些代码量 + * 自定义loading可以见:https://doc.vben.pro/guide/in-depth/loading.html + */ +export function unmountGlobalLoading() { + // 查找全局 loading 元素 + const loadingElement = document.querySelector('#__app-loading__'); + + if (loadingElement) { + // 添加隐藏类,触发过渡动画 + loadingElement.classList.add('hidden'); + + // 查找所有需要移除的注入 loading 元素 + const injectLoadingElements = document.querySelectorAll( + '[data-app-loading^="inject"]', + ); + + // 当过渡动画结束时,移除 loading 元素和所有注入的 loading 元素 + loadingElement.addEventListener( + 'transitionend', + () => { + loadingElement.remove(); // 移除 loading 元素 + injectLoadingElements.forEach((el) => el.remove()); // 移除所有注入的 loading 元素 + }, + { once: true }, + ); // 确保事件只触发一次 + } +} diff --git a/deploy/fba/fba-ui-src/packages/utils/src/index.ts b/deploy/fba/fba-ui-src/packages/utils/src/index.ts new file mode 100644 index 0000000..80263b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/src/index.ts @@ -0,0 +1,4 @@ +export * from './helpers'; +export * from '@vben-core/shared/cache'; +export * from '@vben-core/shared/color'; +export * from '@vben-core/shared/utils'; diff --git a/deploy/fba/fba-ui-src/packages/utils/tsconfig.json b/deploy/fba/fba-ui-src/packages/utils/tsconfig.json new file mode 100644 index 0000000..255148a --- /dev/null +++ b/deploy/fba/fba-ui-src/packages/utils/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/library.json", + "compilerOptions": { + "types": ["@vben-core/typings/vue-router"] + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/pnpm-lock.yaml b/deploy/fba/fba-ui-src/pnpm-lock.yaml new file mode 100644 index 0000000..a1fff3b --- /dev/null +++ b/deploy/fba/fba-ui-src/pnpm-lock.yaml @@ -0,0 +1,21062 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@changesets/changelog-github': + specifier: ^0.7.0 + version: 0.7.0 + '@changesets/cli': + specifier: ^2.31.0 + version: 2.31.0 + '@changesets/git': + specifier: ^3.0.4 + version: 3.0.4 + '@clack/prompts': + specifier: ^1.5.1 + version: 1.6.0 + '@codemirror/lang-python': + specifier: ^6.2.1 + version: 6.2.1 + '@codemirror/lang-sql': + specifier: ^6.10.0 + version: 6.10.0 + '@codemirror/theme-one-dark': + specifier: ^6.1.3 + version: 6.1.3 + '@commitlint/cli': + specifier: ^21.0.2 + version: 21.0.2 + '@commitlint/config-conventional': + specifier: ^21.0.2 + version: 21.0.2 + '@eslint-community/eslint-plugin-eslint-comments': + specifier: ^4.7.2 + version: 4.7.2 + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1 + '@iconify/json': + specifier: ^2.2.487 + version: 2.2.488 + '@iconify/tailwind4': + specifier: ^1.2.3 + version: 1.2.3 + '@iconify/vue': + specifier: ^5.0.1 + version: 5.0.1 + '@intlify/core-base': + specifier: ^11.4.5 + version: 11.4.6 + '@intlify/unplugin-vue-i18n': + specifier: ^11.2.3 + version: 11.2.4 + '@jspm/generator': + specifier: ^2.16.1 + version: 2.16.1 + '@lucide/vue': + specifier: ^1.18.0 + version: 1.21.0 + '@manypkg/get-packages': + specifier: ^3.1.0 + version: 3.1.0 + '@pnpm/workspace.read-manifest': + specifier: ^1000.3.1 + version: 1000.3.1 + '@stylistic/stylelint-plugin': + specifier: ^5.2.0 + version: 5.2.0 + '@tailwindcss/typography': + specifier: ^0.5.20 + version: 0.5.20 + '@tailwindcss/vite': + specifier: ^4.3.1 + version: 4.3.1 + '@tanstack/vue-store': + specifier: ^0.11.0 + version: 0.11.0 + '@tiptap/core': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-highlight': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-image': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-link': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-placeholder': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-text-align': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-text-style': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/extension-underline': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/pm': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/starter-kit': + specifier: ^3.26.1 + version: 3.27.1 + '@tiptap/vue-3': + specifier: ^3.26.1 + version: 3.27.1 + '@tsdown/css': + specifier: ^0.22.2 + version: 0.22.3 + '@types/archiver': + specifier: ^8.0.0 + version: 8.0.0 + '@types/html-minifier-terser': + specifier: ^7.0.2 + version: 7.0.2 + '@types/json-bigint': + specifier: ^1.0.4 + version: 1.0.4 + '@types/lodash.clonedeep': + specifier: ^4.5.9 + version: 4.5.9 + '@types/node': + specifier: ^25.9.3 + version: 25.9.4 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 + '@types/qs': + specifier: ^6.15.1 + version: 6.15.1 + '@types/sortablejs': + specifier: ^1.15.9 + version: 1.15.9 + '@typescript-eslint/parser': + specifier: ^8.61.1 + version: 8.61.1 + '@vee-validate/zod': + specifier: ^4.15.1 + version: 4.15.1 + '@vitejs/plugin-vue': + specifier: ^6.0.7 + version: 6.0.7 + '@vitejs/plugin-vue-jsx': + specifier: ^5.1.5 + version: 5.1.5 + '@vue/shared': + specifier: ^3.5.38 + version: 3.5.38 + '@vue/test-utils': + specifier: ^2.4.11 + version: 2.4.11 + '@vueuse/core': + specifier: ^14.3.0 + version: 14.3.0 + '@vueuse/integrations': + specifier: ^14.3.0 + version: 14.3.0 + '@vueuse/motion': + specifier: ^3.0.3 + version: 3.0.3 + antdv-next: + specifier: ^1.3.5 + version: 1.3.6 + archiver: + specifier: ^8.0.0 + version: 8.0.0 + axios: + specifier: ^1.18.0 + version: 1.18.0 + axios-mock-adapter: + specifier: ^2.1.0 + version: 2.1.0 + cac: + specifier: ^7.0.0 + version: 7.0.0 + chalk: + specifier: ^5.6.2 + version: 5.6.2 + cheerio: + specifier: ^1.2.0 + version: 1.2.0 + circular-dependency-scanner: + specifier: ^3.0.1 + version: 3.0.1 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + commitlint-plugin-function-rules: + specifier: ^5.0.1 + version: 5.0.1 + consola: + specifier: ^3.4.2 + version: 3.4.2 + croner: + specifier: ^10.0.1 + version: 10.0.1 + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + cspell: + specifier: ^10.0.1 + version: 10.0.1 + cz-git: + specifier: ^1.13.1 + version: 1.13.1 + czg: + specifier: ^1.13.1 + version: 1.13.1 + dayjs: + specifier: ^1.11.21 + version: 1.11.21 + defu: + specifier: ^6.1.7 + version: 6.1.7 + dotenv: + specifier: ^17.4.2 + version: 17.4.2 + echarts: + specifier: ^6.1.0 + version: 6.1.0 + es-toolkit: + specifier: ^1.47.1 + version: 1.47.1 + eslint: + specifier: ^10.5.0 + version: 10.5.0 + eslint-plugin-better-tailwindcss: + specifier: ^4.6.0 + version: 4.6.0 + eslint-plugin-command: + specifier: ^3.5.2 + version: 3.5.2 + eslint-plugin-jsonc: + specifier: ^3.2.0 + version: 3.2.0 + eslint-plugin-n: + specifier: ^18.1.0 + version: 18.1.0 + eslint-plugin-perfectionist: + specifier: ^5.9.1 + version: 5.9.1 + eslint-plugin-pnpm: + specifier: ^1.6.1 + version: 1.6.1 + eslint-plugin-unused-imports: + specifier: ^4.4.1 + version: 4.4.1 + eslint-plugin-vue: + specifier: ^10.9.2 + version: 10.9.2 + eslint-plugin-yml: + specifier: ^3.4.0 + version: 3.4.0 + execa: + specifier: ^9.6.1 + version: 9.6.1 + find-up: + specifier: ^8.0.0 + version: 8.0.0 + get-port: + specifier: ^7.2.0 + version: 7.2.0 + globals: + specifier: ^17.6.0 + version: 17.6.0 + happy-dom: + specifier: ^20.10.4 + version: 20.10.6 + html-minifier-terser: + specifier: ^7.2.0 + version: 7.2.0 + is-ci: + specifier: ^4.1.0 + version: 4.1.0 + json-bigint: + specifier: ^1.0.0 + version: 1.0.0 + knip: + specifier: ^6.17.0 + version: 6.17.1 + lefthook: + specifier: ^2.1.9 + version: 2.1.9 + lodash.clonedeep: + specifier: ^4.5.0 + version: 4.5.0 + md-editor-v3: + specifier: ^6.4.2 + version: 6.5.1 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + nitropack: + specifier: ^2.13.4 + version: 2.13.4 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + ora: + specifier: ^9.4.0 + version: 9.4.0 + oxfmt: + specifier: ^0.55.0 + version: 0.55.0 + oxlint: + specifier: ^1.70.0 + version: 1.70.0 + oxlint-tsgolint: + specifier: ^0.23.0 + version: 0.23.0 + pinia-plugin-persistedstate: + specifier: ^4.7.1 + version: 4.7.1 + pkg-types: + specifier: ^2.3.1 + version: 2.3.1 + playwright: + specifier: ^1.61.0 + version: 1.61.0 + postcss: + specifier: ^8.5.15 + version: 8.5.15 + postcss-html: + specifier: ^1.8.1 + version: 1.8.1 + postcss-scss: + specifier: ^4.0.9 + version: 4.0.9 + publint: + specifier: ^0.3.21 + version: 0.3.21 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 + qs: + specifier: ^6.15.2 + version: 6.15.2 + reka-ui: + specifier: ^2.9.10 + version: 2.10.0 + resolve.exports: + specifier: ^2.0.3 + version: 2.0.3 + rimraf: + specifier: ^6.1.3 + version: 6.1.3 + rollup-plugin-visualizer: + specifier: ^7.0.1 + version: 7.0.1 + sass: + specifier: ^1.101.0 + version: 1.101.0 + sass-embedded: + specifier: ^1.100.0 + version: 1.100.0 + secure-ls: + specifier: ^2.0.0 + version: 2.0.0 + socket.io-client: + specifier: ^4.8.3 + version: 4.8.3 + sortablejs: + specifier: ^1.15.7 + version: 1.15.7 + stylelint: + specifier: ^17.13.0 + version: 17.13.0 + stylelint-config-recess-order: + specifier: ^7.7.0 + version: 7.7.0 + stylelint-config-recommended: + specifier: ^18.0.0 + version: 18.0.0 + stylelint-config-recommended-scss: + specifier: ^17.0.1 + version: 17.0.1 + stylelint-config-recommended-vue: + specifier: ^1.6.1 + version: 1.6.1 + stylelint-config-standard: + specifier: ^40.0.0 + version: 40.0.0 + stylelint-order: + specifier: ^8.1.1 + version: 8.1.1 + stylelint-scss: + specifier: ^7.2.0 + version: 7.2.0 + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + tailwindcss: + specifier: ^4.3.1 + version: 4.3.1 + theme-colors: + specifier: ^0.1.0 + version: 0.1.0 + tippy.js: + specifier: ^6.3.7 + version: 6.3.7 + tsdown: + specifier: ^0.22.2 + version: 0.22.3 + turbo: + specifier: ^2.9.18 + version: 2.9.18 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + unplugin-dts: + specifier: ^1.0.2 + version: 1.0.2 + unplugin-vue: + specifier: ^7.2.0 + version: 7.2.0 + vee-validate: + specifier: ^4.15.1 + version: 4.15.1 + vite: + specifier: 8.0.10 + version: 8.0.10 + vite-plugin-compression: + specifier: ^0.5.1 + version: 0.5.1 + vite-plugin-lazy-import: + specifier: ^1.0.7 + version: 1.0.7 + vite-plugin-pwa: + specifier: ^1.3.0 + version: 1.3.0 + vite-plugin-vue-devtools: + specifier: ^8.1.3 + version: 8.1.3 + vitest: + specifier: ^4.1.9 + version: 4.1.9 + vue-codemirror6: + specifier: ^1.5.1 + version: 1.5.2 + vue-eslint-parser: + specifier: ^10.4.1 + version: 10.4.1 + vue-i18n: + specifier: ^11.4.5 + version: 11.4.6 + vue-json-pretty: + specifier: ^2.6.0 + version: 2.6.0 + vue-router: + specifier: ^5.1.0 + version: 5.1.0 + vue-tippy: + specifier: ^6.7.1 + version: 6.7.1 + vue-tsc: + specifier: ^3.3.5 + version: 3.3.5 + vxe-pc-ui: + specifier: ^4.14.39 + version: 4.15.4 + vxe-table: + specifier: ^4.19.14 + version: 4.19.17 + watermark-js-plus: + specifier: ^1.6.3 + version: 1.6.3 + yaml-eslint-parser: + specifier: ^2.0.0 + version: 2.0.0 + zod: + specifier: ^3.25.76 + version: 3.25.76 + zod-defaults: + specifier: 0.1.3 + version: 0.1.3 + +overrides: + '@ast-grep/napi': ^0.43.0 + '@ctrl/tinycolor': ^4.2.0 + clsx: ^2.1.1 + pinia: ^3.0.4 + vue: ^3.5.38 + +importers: + + .: + devDependencies: + '@changesets/changelog-github': + specifier: 'catalog:' + version: 0.7.0 + '@changesets/cli': + specifier: 'catalog:' + version: 2.31.0(@types/node@25.9.4) + '@tsdown/css': + specifier: 'catalog:' + version: 0.22.3(jiti@2.7.0)(postcss@8.5.15)(sass-embedded@1.100.0)(sass@1.101.0)(tsdown@0.22.3)(yaml@2.9.0) + '@types/node': + specifier: 'catalog:' + version: 25.9.4 + '@vben/commitlint-config': + specifier: workspace:* + version: link:internal/lint-configs/commitlint-config + '@vben/eslint-config': + specifier: workspace:* + version: link:internal/lint-configs/eslint-config + '@vben/oxfmt-config': + specifier: workspace:* + version: link:internal/lint-configs/oxfmt-config + '@vben/oxlint-config': + specifier: workspace:* + version: link:internal/lint-configs/oxlint-config + '@vben/stylelint-config': + specifier: workspace:* + version: link:internal/lint-configs/stylelint-config + '@vben/tailwind-config': + specifier: workspace:* + version: link:internal/tailwind-config + '@vben/tsconfig': + specifier: workspace:* + version: link:internal/tsconfig + '@vben/turbo-run': + specifier: workspace:* + version: link:scripts/turbo-run + '@vben/vite-config': + specifier: workspace:* + version: link:internal/vite-config + '@vben/vsh': + specifier: workspace:* + version: link:scripts/vsh + '@vitejs/plugin-vue': + specifier: 'catalog:' + version: 6.0.7(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + '@vitejs/plugin-vue-jsx': + specifier: 'catalog:' + version: 5.1.5(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + cross-env: + specifier: 'catalog:' + version: 10.1.0 + cspell: + specifier: 'catalog:' + version: 10.0.1 + eslint: + specifier: 'catalog:' + version: 10.5.0(jiti@2.7.0) + happy-dom: + specifier: 'catalog:' + version: 20.10.6 + is-ci: + specifier: 'catalog:' + version: 4.1.0 + lefthook: + specifier: 'catalog:' + version: 2.1.9 + oxfmt: + specifier: 'catalog:' + version: 0.55.0 + oxlint: + specifier: 'catalog:' + version: 1.70.0(oxlint-tsgolint@0.23.0) + oxlint-tsgolint: + specifier: 'catalog:' + version: 0.23.0 + playwright: + specifier: 'catalog:' + version: 1.61.0 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tailwindcss: + specifier: 'catalog:' + version: 4.3.1 + tsdown: + specifier: 'catalog:' + version: 0.22.3(@tsdown/css@0.22.3)(oxc-resolver@11.21.3)(publint@0.3.21)(typescript@6.0.3)(vue-tsc@3.3.5(typescript@6.0.3)) + turbo: + specifier: 'catalog:' + version: 2.9.18 + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vitest: + specifier: 'catalog:' + version: 4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-tsc: + specifier: 'catalog:' + version: 3.3.5(typescript@6.0.3) + + apps/web-antdv-next: + dependencies: + '@vben/access': + specifier: workspace:* + version: link:../../packages/effects/access + '@vben/common-ui': + specifier: workspace:* + version: link:../../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../../packages/effects/plugins + '@vben/preferences': + specifier: workspace:* + version: link:../../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../../packages/utils + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + antdv-next: + specifier: 'catalog:' + version: 1.3.6(vue@3.5.38(typescript@6.0.3)) + croner: + specifier: 'catalog:' + version: 10.0.1 + dayjs: + specifier: 'catalog:' + version: 1.11.21 + mitt: + specifier: 'catalog:' + version: 3.0.1 + pinia: + specifier: ^3.0.4 + version: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + socket.io-client: + specifier: 'catalog:' + version: 4.8.3 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + apps/web-antdv-next/src/plugins/ai: + dependencies: + '@ag-ui/core': + specifier: ^0.0.53 + version: 0.0.53 + '@antdv-next/x': + specifier: ^1.1.1 + version: 1.1.1(antdv-next@1.3.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3)) + '@antdv-next/x-markdown': + specifier: ^0.1.0 + version: 0.1.0(vue@3.5.38(typescript@6.0.3)) + '@antdv-next/x-sdk': + specifier: ^0.0.2 + version: 0.0.2(vue@3.5.38(typescript@6.0.3)) + + apps/web-antdv-next/src/plugins/aliyun_sms: {} + + apps/web-antdv-next/src/plugins/code_generator: {} + + apps/web-antdv-next/src/plugins/config: {} + + apps/web-antdv-next/src/plugins/dict: {} + + apps/web-antdv-next/src/plugins/email: {} + + apps/web-antdv-next/src/plugins/notice: {} + + apps/web-antdv-next/src/plugins/oauth2: {} + + internal/lint-configs/commitlint-config: + dependencies: + '@commitlint/cli': + specifier: 'catalog:' + version: 21.0.2(@types/node@25.9.4)(conventional-commits-parser@6.4.0)(typescript@6.0.3) + '@commitlint/config-conventional': + specifier: 'catalog:' + version: 21.0.2 + '@vben/node-utils': + specifier: workspace:* + version: link:../../node-utils + commitlint-plugin-function-rules: + specifier: 'catalog:' + version: 5.0.1(@commitlint/lint@21.0.2) + cz-git: + specifier: 'catalog:' + version: 1.13.1 + czg: + specifier: 'catalog:' + version: 1.13.1 + + internal/lint-configs/eslint-config: + dependencies: + '@eslint/js': + specifier: 'catalog:' + version: 10.0.1(eslint@10.5.0(jiti@2.7.0)) + '@typescript-eslint/parser': + specifier: 'catalog:' + version: 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@vben/oxlint-config': + specifier: workspace:* + version: link:../oxlint-config + eslint: + specifier: 'catalog:' + version: 10.5.0(jiti@2.7.0) + eslint-plugin-jsonc: + specifier: 'catalog:' + version: 3.2.0(eslint@10.5.0(jiti@2.7.0)) + eslint-plugin-n: + specifier: 'catalog:' + version: 18.1.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint-plugin-perfectionist: + specifier: 'catalog:' + version: 5.9.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint-plugin-pnpm: + specifier: 'catalog:' + version: 1.6.1(eslint@10.5.0(jiti@2.7.0)) + eslint-plugin-unused-imports: + specifier: 'catalog:' + version: 4.4.1(eslint@10.5.0(jiti@2.7.0)) + eslint-plugin-vue: + specifier: 'catalog:' + version: 10.9.2(@typescript-eslint/parser@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.5.0(jiti@2.7.0))) + eslint-plugin-yml: + specifier: 'catalog:' + version: 3.4.0(eslint@10.5.0(jiti@2.7.0)) + globals: + specifier: 'catalog:' + version: 17.6.0 + vue-eslint-parser: + specifier: 'catalog:' + version: 10.4.1(eslint@10.5.0(jiti@2.7.0)) + yaml-eslint-parser: + specifier: 'catalog:' + version: 2.0.0 + + internal/lint-configs/oxfmt-config: + dependencies: + oxfmt: + specifier: 'catalog:' + version: 0.55.0 + + internal/lint-configs/oxlint-config: + dependencies: + '@eslint-community/eslint-plugin-eslint-comments': + specifier: 'catalog:' + version: 4.7.2(eslint@10.5.0(jiti@2.7.0)) + eslint-plugin-better-tailwindcss: + specifier: 'catalog:' + version: 4.6.0(eslint@10.5.0(jiti@2.7.0))(oxlint@1.70.0(oxlint-tsgolint@0.23.0))(tailwindcss@4.3.1)(typescript@6.0.3) + eslint-plugin-command: + specifier: 'catalog:' + version: 3.5.2(@typescript-eslint/rule-tester@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3))(@typescript-eslint/utils@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0)) + oxlint: + specifier: 'catalog:' + version: 1.70.0(oxlint-tsgolint@0.23.0) + + internal/lint-configs/stylelint-config: + dependencies: + '@stylistic/stylelint-plugin': + specifier: 'catalog:' + version: 5.2.0(stylelint@17.13.0(typescript@6.0.3)) + stylelint-config-recess-order: + specifier: 'catalog:' + version: 7.7.0(stylelint-order@8.1.1(stylelint@17.13.0(typescript@6.0.3)))(stylelint@17.13.0(typescript@6.0.3)) + stylelint-scss: + specifier: 'catalog:' + version: 7.2.0(stylelint@17.13.0(typescript@6.0.3)) + devDependencies: + postcss: + specifier: 'catalog:' + version: 8.5.15 + postcss-html: + specifier: 'catalog:' + version: 1.8.1 + postcss-scss: + specifier: 'catalog:' + version: 4.0.9(postcss@8.5.15) + stylelint: + specifier: 'catalog:' + version: 17.13.0(typescript@6.0.3) + stylelint-config-recommended: + specifier: 'catalog:' + version: 18.0.0(stylelint@17.13.0(typescript@6.0.3)) + stylelint-config-recommended-scss: + specifier: 'catalog:' + version: 17.0.1(postcss@8.5.15)(stylelint@17.13.0(typescript@6.0.3)) + stylelint-config-recommended-vue: + specifier: 'catalog:' + version: 1.6.1(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)) + stylelint-config-standard: + specifier: 'catalog:' + version: 40.0.0(stylelint@17.13.0(typescript@6.0.3)) + stylelint-order: + specifier: 'catalog:' + version: 8.1.1(stylelint@17.13.0(typescript@6.0.3)) + + internal/node-utils: + dependencies: + '@changesets/git': + specifier: 'catalog:' + version: 3.0.4 + '@manypkg/get-packages': + specifier: 'catalog:' + version: 3.1.0 + chalk: + specifier: 'catalog:' + version: 5.6.2 + consola: + specifier: 'catalog:' + version: 3.4.2 + dayjs: + specifier: 'catalog:' + version: 1.11.21 + execa: + specifier: 'catalog:' + version: 9.6.1 + find-up: + specifier: 'catalog:' + version: 8.0.0 + ora: + specifier: 'catalog:' + version: 9.4.0 + pkg-types: + specifier: 'catalog:' + version: 2.3.1 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + + internal/tailwind-config: + dependencies: + '@iconify/tailwind4': + specifier: 'catalog:' + version: 1.2.3(tailwindcss@4.3.1) + '@tailwindcss/typography': + specifier: 'catalog:' + version: 0.5.20(tailwindcss@4.3.1) + tailwindcss: + specifier: 'catalog:' + version: 4.3.1 + tw-animate-css: + specifier: 'catalog:' + version: 1.4.0 + + internal/tsconfig: + dependencies: + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + vite: + specifier: 'catalog:' + version: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + + internal/vite-config: + dependencies: + '@intlify/unplugin-vue-i18n': + specifier: 'catalog:' + version: 11.2.4(@vue/compiler-dom@3.5.38)(eslint@10.5.0(jiti@2.7.0))(rollup@4.62.2)(typescript@6.0.3)(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3)) + '@jspm/generator': + specifier: 'catalog:' + version: 2.16.1 + '@tailwindcss/vite': + specifier: 'catalog:' + version: 4.3.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + '@vben/node-utils': + specifier: workspace:* + version: link:../node-utils + archiver: + specifier: 'catalog:' + version: 8.0.0 + cheerio: + specifier: 'catalog:' + version: 1.2.0 + get-port: + specifier: 'catalog:' + version: 7.2.0 + html-minifier-terser: + specifier: 'catalog:' + version: 7.2.0 + nitropack: + specifier: 'catalog:' + version: 2.13.4(oxc-parser@0.135.0)(rolldown@1.1.2) + resolve.exports: + specifier: 'catalog:' + version: 2.0.3 + vite-plugin-pwa: + specifier: 'catalog:' + version: 1.3.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1) + vite-plugin-vue-devtools: + specifier: 'catalog:' + version: 8.1.3(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + devDependencies: + '@pnpm/workspace.read-manifest': + specifier: 'catalog:' + version: 1000.3.1 + '@types/archiver': + specifier: 'catalog:' + version: 8.0.0 + '@types/html-minifier-terser': + specifier: 'catalog:' + version: 7.0.2 + '@vitejs/plugin-vue': + specifier: 'catalog:' + version: 6.0.7(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + '@vitejs/plugin-vue-jsx': + specifier: 'catalog:' + version: 5.1.5(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + dayjs: + specifier: 'catalog:' + version: 1.11.21 + dotenv: + specifier: 'catalog:' + version: 17.4.2 + rollup-plugin-visualizer: + specifier: 'catalog:' + version: 7.0.1(rolldown@1.1.2)(rollup@4.62.2) + sass: + specifier: 'catalog:' + version: 1.101.0 + sass-embedded: + specifier: 'catalog:' + version: 1.100.0 + unplugin-dts: + specifier: 'catalog:' + version: 1.0.2(esbuild@0.28.1)(rolldown@1.1.2)(rollup@4.62.2)(typescript@6.0.3)(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + vite: + specifier: 'catalog:' + version: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-compression: + specifier: 'catalog:' + version: 0.5.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-lazy-import: + specifier: 'catalog:' + version: 1.0.7 + + packages/@core/base/design: + dependencies: + '@iconify/json': + specifier: 'catalog:' + version: 2.2.488 + + packages/@core/base/icons: + dependencies: + '@iconify/vue': + specifier: 'catalog:' + version: 5.0.1(vue@3.5.38(typescript@6.0.3)) + '@lucide/vue': + specifier: 'catalog:' + version: 1.21.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + + packages/@core/base/shared: + dependencies: + '@ctrl/tinycolor': + specifier: ^4.2.0 + version: 4.2.0 + '@tanstack/vue-store': + specifier: 'catalog:' + version: 0.11.0(vue@3.5.38(typescript@6.0.3)) + '@vue/shared': + specifier: 'catalog:' + version: 3.5.38 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + dayjs: + specifier: 'catalog:' + version: 1.11.21 + defu: + specifier: 'catalog:' + version: 6.1.7 + es-toolkit: + specifier: 'catalog:' + version: 1.47.1 + lodash.clonedeep: + specifier: 'catalog:' + version: 4.5.0 + nprogress: + specifier: 'catalog:' + version: 0.2.0 + tailwind-merge: + specifier: 'catalog:' + version: 3.6.0 + theme-colors: + specifier: 'catalog:' + version: 0.1.0 + devDependencies: + '@types/lodash.clonedeep': + specifier: 'catalog:' + version: 4.5.9 + '@types/nprogress': + specifier: 'catalog:' + version: 0.2.3 + + packages/@core/base/typings: + dependencies: + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + packages/@core/composables: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../base/shared + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + reka-ui: + specifier: 'catalog:' + version: 2.10.0(vue@3.5.38(typescript@6.0.3)) + sortablejs: + specifier: 'catalog:' + version: 1.15.7 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + devDependencies: + '@types/sortablejs': + specifier: 'catalog:' + version: 1.15.9 + + packages/@core/preferences: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + + packages/@core/ui-kit/form-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vee-validate/zod': + specifier: 'catalog:' + version: 4.15.1(vue@3.5.38(typescript@6.0.3))(zod@3.25.76) + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vee-validate: + specifier: 'catalog:' + version: 4.15.1(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + zod: + specifier: 'catalog:' + version: 3.25.76 + zod-defaults: + specifier: 'catalog:' + version: 0.1.3(zod@3.25.76) + devDependencies: + unplugin-vue: + specifier: 'catalog:' + version: 7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + + packages/@core/ui-kit/layout-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + devDependencies: + unplugin-vue: + specifier: 'catalog:' + version: 7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + + packages/@core/ui-kit/menu-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/design': + specifier: workspace:* + version: link:../../base/design + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + qs: + specifier: 'catalog:' + version: 6.15.2 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + devDependencies: + '@types/qs': + specifier: 'catalog:' + version: 6.15.1 + unplugin-vue: + specifier: 'catalog:' + version: 7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + + packages/@core/ui-kit/popup-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + devDependencies: + unplugin-vue: + specifier: 'catalog:' + version: 7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + + packages/@core/ui-kit/shadcn-ui: + dependencies: + '@lucide/vue': + specifier: 'catalog:' + version: 1.21.0(vue@3.5.38(typescript@6.0.3)) + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/design': + specifier: workspace:* + version: link:../../base/design + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + reka-ui: + specifier: 'catalog:' + version: 2.10.0(vue@3.5.38(typescript@6.0.3)) + vee-validate: + specifier: 'catalog:' + version: 4.15.1(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + + packages/@core/ui-kit/tabs-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/design': + specifier: workspace:* + version: link:../../base/design + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + devDependencies: + unplugin-vue: + specifier: 'catalog:' + version: 7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + + packages/constants: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + + packages/effects/access: + dependencies: + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + + packages/effects/common-ui: + dependencies: + '@codemirror/lang-python': + specifier: 'catalog:' + version: 6.2.1 + '@codemirror/lang-sql': + specifier: 'catalog:' + version: 6.10.0 + '@codemirror/theme-one-dark': + specifier: 'catalog:' + version: 6.1.3 + '@vben-core/design': + specifier: workspace:* + version: link:../../@core/base/design + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/popup-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/popup-ui + '@vben-core/preferences': + specifier: workspace:* + version: link:../../@core/preferences + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben/constants': + specifier: workspace:* + version: link:../../constants + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + '@vueuse/integrations': + specifier: 'catalog:' + version: 14.3.0(axios@1.18.0)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.7)(vue@3.5.38(typescript@6.0.3)) + json-bigint: + specifier: 'catalog:' + version: 1.0.0 + md-editor-v3: + specifier: 'catalog:' + version: 6.5.1(vue@3.5.38(typescript@6.0.3)) + qrcode: + specifier: 'catalog:' + version: 1.5.4 + tippy.js: + specifier: 'catalog:' + version: 6.3.7 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-codemirror6: + specifier: 'catalog:' + version: 1.5.2(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)(codemirror@6.0.2)(style-mod@4.1.3)(vue@3.5.38(typescript@6.0.3)) + vue-json-pretty: + specifier: 'catalog:' + version: 2.6.0(vue@3.5.38(typescript@6.0.3)) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + vue-tippy: + specifier: 'catalog:' + version: 6.7.1(vue@3.5.38(typescript@6.0.3)) + devDependencies: + '@types/json-bigint': + specifier: 'catalog:' + version: 1.0.4 + '@types/qrcode': + specifier: 'catalog:' + version: 1.5.6 + '@vue/test-utils': + specifier: 'catalog:' + version: 2.4.11(@vue/compiler-dom@3.5.38)(@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3)) + + packages/effects/hooks: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../@core/composables + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + watermark-js-plus: + specifier: 'catalog:' + version: 1.6.3 + + packages/effects/layouts: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../@core/composables + '@vben-core/design': + specifier: workspace:* + version: link:../../@core/base/design + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/layout-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/layout-ui + '@vben-core/menu-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/menu-ui + '@vben-core/popup-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/popup-ui + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben-core/tabs-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/tabs-ui + '@vben/constants': + specifier: workspace:* + version: link:../../constants + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + packages/effects/plugins: + dependencies: + '@tiptap/core': + specifier: 'catalog:' + version: 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-highlight': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-image': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-link': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-placeholder': + specifier: 'catalog:' + version: 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-text-align': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-text-style': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-underline': + specifier: 'catalog:' + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/pm': + specifier: 'catalog:' + version: 3.27.1 + '@tiptap/starter-kit': + specifier: 'catalog:' + version: 3.27.1 + '@tiptap/vue-3': + specifier: 'catalog:' + version: 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(vue@3.5.38(typescript@6.0.3)) + '@vben-core/design': + specifier: workspace:* + version: link:../../@core/base/design + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/popup-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/popup-ui + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 14.3.0(vue@3.5.38(typescript@6.0.3)) + '@vueuse/motion': + specifier: 'catalog:' + version: 3.0.3(magicast@0.5.3)(vue@3.5.38(typescript@6.0.3)) + echarts: + specifier: 'catalog:' + version: 6.1.0 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vxe-pc-ui: + specifier: 'catalog:' + version: 4.15.4(vue@3.5.38(typescript@6.0.3)) + vxe-table: + specifier: 'catalog:' + version: 4.19.17(vue@3.5.38(typescript@6.0.3)) + + packages/effects/request: + dependencies: + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/utils': + specifier: workspace:* + version: link:../../utils + axios: + specifier: 'catalog:' + version: 1.18.0 + qs: + specifier: 'catalog:' + version: 6.15.2 + devDependencies: + '@types/qs': + specifier: 'catalog:' + version: 6.15.1 + axios-mock-adapter: + specifier: 'catalog:' + version: 2.1.0(axios@1.18.0) + + packages/icons: + dependencies: + '@vben-core/icons': + specifier: workspace:* + version: link:../@core/base/icons + + packages/locales: + dependencies: + '@intlify/core-base': + specifier: 'catalog:' + version: 11.4.6 + '@vben-core/composables': + specifier: workspace:* + version: link:../@core/composables + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-i18n: + specifier: 'catalog:' + version: 11.4.6(vue@3.5.38(typescript@6.0.3)) + + packages/preferences: + dependencies: + '@vben-core/preferences': + specifier: workspace:* + version: link:../@core/preferences + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + + packages/stores: + dependencies: + '@vben-core/preferences': + specifier: workspace:* + version: link:../@core/preferences + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + pinia: + specifier: ^3.0.4 + version: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + pinia-plugin-persistedstate: + specifier: 'catalog:' + version: 4.7.1(@nuxt/kit@3.21.8(magicast@0.5.3))(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))) + secure-ls: + specifier: 'catalog:' + version: 2.0.0 + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + packages/styles: + dependencies: + '@vben-core/design': + specifier: workspace:* + version: link:../@core/base/design + + packages/types: + dependencies: + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + vue: + specifier: ^3.5.38 + version: 3.5.38(typescript@6.0.3) + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + packages/utils: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + vue-router: + specifier: 'catalog:' + version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + + scripts/turbo-run: + dependencies: + '@clack/prompts': + specifier: 'catalog:' + version: 1.6.0 + '@vben/node-utils': + specifier: workspace:* + version: link:../../internal/node-utils + cac: + specifier: 'catalog:' + version: 7.0.0 + + scripts/vsh: + dependencies: + '@vben/node-utils': + specifier: workspace:* + version: link:../../internal/node-utils + cac: + specifier: 'catalog:' + version: 7.0.0 + circular-dependency-scanner: + specifier: 'catalog:' + version: 3.0.1 + knip: + specifier: 'catalog:' + version: 6.17.1 + publint: + specifier: 'catalog:' + version: 0.3.21 + +packages: + + '@ag-ui/core@0.0.53': + resolution: {integrity: sha512-11UocR7fFdMWw503bWCX2IOK15vbWfxT11Mn9xOiPBVO/UVcn57ywGrlLL4UaBlPgmUTvuzr2yYR2ElSqiN2wQ==} + + '@ant-design/colors@7.2.1': + resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} + + '@ant-design/colors@8.0.1': + resolution: {integrity: sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==} + + '@ant-design/fast-color@2.0.6': + resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} + engines: {node: '>=8.x'} + + '@ant-design/fast-color@3.0.1': + resolution: {integrity: sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@antdv-next/cssinjs@1.0.6': + resolution: {integrity: sha512-8NL+AzjFZVHfG9A2l+r4rNWdRfbS5FFFozGU9jVl7WNgd7y+wEYSzl+qvRjJuqd3IryacrDfsTxIgcSQkVlr5Q==} + peerDependencies: + vue: ^3.5.38 + + '@antdv-next/icons@1.0.8': + resolution: {integrity: sha512-tA5UeJfEakn+QqnBlY1Ky5rhd6m15Jm7Fmf0B9Zw5EySlIYqE61Z7OfaHOD+RlhPmzX08Ycg9DSR6IvDObfqEg==} + peerDependencies: + vue: ^3.5.38 + + '@antdv-next/x-markdown@0.1.0': + resolution: {integrity: sha512-I+GIWlEaviWqxcLwyvYzn8XZWc3CsV0XFzCH1lGXXwqoMTskJcty8vu77e6vnnF8IPebmfPSSLQ7cdSC2wZP1Q==} + peerDependencies: + vue: ^3.5.38 + + '@antdv-next/x-sdk@0.0.2': + resolution: {integrity: sha512-oN6T49WwhMFV36UFjO8gomvu/0//2kDfcnE5nEz470kHakOp/LkJCwHTg3pa6unLUBbkBvrb8iz4PJftZFVeBg==} + peerDependencies: + vue: ^3.5.38 + + '@antdv-next/x@1.1.1': + resolution: {integrity: sha512-5rXTXIlSKMhuQwhtA0DM09qSwVdmBgPFB+gHcwOTMJ/NBCUhqRGtW/FVveVBmnD/eDqNNV3NnahD+LKVqvHU3Q==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + antdv-next: '>=1.0.0' + vue: ^3.5.38 + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@apideck/better-ajv-errors@0.3.7': + resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + + '@ast-grep/napi-darwin-arm64@0.43.0': + resolution: {integrity: sha512-JBWYRNJsQ/8yfcMDARFxcl1fVZ6+kv3sHB+ClKgSCe9DNoLvGQfqGr9UGRrNOModvXY6cX+c87iAPqPGftUUiw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@ast-grep/napi-darwin-x64@0.43.0': + resolution: {integrity: sha512-oqE/bC/uAEctnAbzqobplidExsPym4CQq7JDVD9HpkHpDHzMzPBF69whv/Qu0ZwrkXr48GCMKO5G6p+OB5n+kQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@ast-grep/napi-linux-arm64-gnu@0.43.0': + resolution: {integrity: sha512-yJSRPxwwrvVW94J2rtaatcixSAGWcSaHNbAh6soXD6HXgq6I7uMc+cyMnJstFL789yd6Pu3QIhTlpD9VY0oYhw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-arm64-musl@0.43.0': + resolution: {integrity: sha512-mknXLDsf66HvT/JEl18ZQSvR7/qWgWfqh3eHuVRqD2lE6cKBDXRnAzx9ZNZkUnL2Z5ph54Yk8dKVu09k45cegA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-linux-x64-gnu@0.43.0': + resolution: {integrity: sha512-KM6M5KKFsHG9Y7VKCKnMsWQQ1sYwj/SPdyr91SKp66AeFJ5xMtXb13WVQ3Joe9NEsi84dzzOBIJgMddz+UMvQw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-x64-musl@0.43.0': + resolution: {integrity: sha512-NfjI74m7CEEOsLi7ZkYwcxY10CfKIHPHRrr9aqMulmnrC4FGvxQMk1qpDrTqkjmEd8LdZt3PsPrmBa8AZCErew==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-win32-arm64-msvc@0.43.0': + resolution: {integrity: sha512-8qwXz3oogh834bXmNLtgsQYsuQi4ZDu4e1He/q+ENhVfjIr4IZRAUc1CoKZgWn+ZSE+B0NJJIxT8Vre2l0Q1Lg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@ast-grep/napi-win32-ia32-msvc@0.43.0': + resolution: {integrity: sha512-wUI/B9QNPzi0K7LlyO7eWvwhqFChM6+O4r5dfBfnOlkP1s/AZ04FdYaC9KAZiODE4FCWbeHbfO03RYOdWeF6eg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@ast-grep/napi-win32-x64-msvc@0.43.0': + resolution: {integrity: sha512-GsGIwYiThKiK9XPGBEguZAs4aIBgv6+DgosBs6ve2a3L390AaLqitoc+bbRz4Eu51URlFKmNP7TtvnAJbMi8QQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@ast-grep/napi@0.43.0': + resolution: {integrity: sha512-dPugC04xwgk3gJl17jJvTjVjx1IXtWI9EcEQ0szlh0jg1UfvGDdZJo9/S9Pk7gzGBNd2bW8hIL3dG6K4T8PEJA==} + engines: {node: '>= 10'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@8.0.2': + resolution: {integrity: sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@8.0.0': + resolution: {integrity: sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.7': + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.29.7': + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@babel/types@8.0.0': + resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==} + engines: {node: ^22.18.0 || >=24.11.0} + + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + + '@bufbuild/protobuf@2.12.0': + resolution: {integrity: sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA==} + + '@cacheable/memory@2.0.9': + resolution: {integrity: sha512-HdMx6DoGywB30vacDbBsITbIX4pgFqj1zsrV58jZBUw3klzkNoXhj7qOqAgledhxG7YZI5rBSJg7Zp8/VG0DuA==} + + '@cacheable/utils@2.4.1': + resolution: {integrity: sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==} + + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/changelog-github@0.7.0': + resolution: {integrity: sha512-rBsbRvc4TVn+FvFnOVM3LxlFJfTXXCp8gfVJ+0BubxWNSVnLuAzowi5j+IEraLLP52w8AAs9QfKbPS3MMiXQJA==} + + '@changesets/cli@2.31.0': + resolution: {integrity: sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==} + hasBin: true + + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + + '@changesets/get-github-info@0.8.0': + resolution: {integrity: sha512-cRnC+xdF0JIik7coko3iUP9qbnfi1iJQ3sAa6dE+Tx3+ET8bjFEm63PA4WEohgjYcmsOikPHWzPsMWWiZmntOQ==} + + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + + '@clack/core@1.4.2': + resolution: {integrity: sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.6.0': + resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==} + engines: {node: '>= 20.12.0'} + + '@cloudflare/kv-asset-handler@0.4.2': + resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} + engines: {node: '>=18.0.0'} + + '@codemirror/autocomplete@6.20.3': + resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} + + '@codemirror/commands@6.10.3': + resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==} + + '@codemirror/lang-angular@0.1.4': + resolution: {integrity: sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g==} + + '@codemirror/lang-cpp@6.0.3': + resolution: {integrity: sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==} + + '@codemirror/lang-css@6.3.1': + resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} + + '@codemirror/lang-go@6.0.1': + resolution: {integrity: sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==} + + '@codemirror/lang-html@6.4.11': + resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} + + '@codemirror/lang-java@6.0.2': + resolution: {integrity: sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==} + + '@codemirror/lang-javascript@6.2.5': + resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} + + '@codemirror/lang-jinja@6.0.1': + resolution: {integrity: sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==} + + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} + + '@codemirror/lang-less@6.0.2': + resolution: {integrity: sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==} + + '@codemirror/lang-liquid@6.3.2': + resolution: {integrity: sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw==} + + '@codemirror/lang-markdown@6.5.0': + resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==} + + '@codemirror/lang-php@6.0.2': + resolution: {integrity: sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==} + + '@codemirror/lang-python@6.2.1': + resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==} + + '@codemirror/lang-rust@6.0.2': + resolution: {integrity: sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==} + + '@codemirror/lang-sass@6.0.2': + resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==} + + '@codemirror/lang-sql@6.10.0': + resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==} + + '@codemirror/lang-vue@0.1.3': + resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==} + + '@codemirror/lang-wast@6.0.2': + resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==} + + '@codemirror/lang-xml@6.1.0': + resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==} + + '@codemirror/lang-yaml@6.1.3': + resolution: {integrity: sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==} + + '@codemirror/language-data@6.5.2': + resolution: {integrity: sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg==} + + '@codemirror/language@6.12.3': + resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==} + + '@codemirror/legacy-modes@6.5.3': + resolution: {integrity: sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==} + + '@codemirror/lint@6.9.7': + resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} + + '@codemirror/search@6.7.1': + resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==} + + '@codemirror/state@6.6.0': + resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==} + + '@codemirror/theme-one-dark@6.1.3': + resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} + + '@codemirror/view@6.43.1': + resolution: {integrity: sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==} + + '@commitlint/cli@21.0.2': + resolution: {integrity: sha512-YMmfLbqBg+ZRvvmPhc+cilSQFrh/AgzVgCT1U/OifmUZEwPbvCtA8rN//YNaF9d5eoZphxVMGYtmwA2QgQORgg==} + engines: {node: '>=22.12.0'} + hasBin: true + + '@commitlint/config-conventional@21.0.2': + resolution: {integrity: sha512-P/ZRhryQmkj0Z0dY9FOoRwe3xkwJyyAdtXwt01NT2kuZttcG2CNYp1q5Ci3u+nDT2jcbJRw2kt13Czl1qKNPfg==} + engines: {node: '>=22.12.0'} + + '@commitlint/config-validator@21.0.1': + resolution: {integrity: sha512-Zd2UFdndeMMaW2O96HK0tdfT4gOImUvidMpAd/pws2zZ4m1nrAZ/9b/v2JYuE8fs86GpXv9F7LNaIuCIWhY+pA==} + engines: {node: '>=22.12.0'} + + '@commitlint/ensure@21.0.1': + resolution: {integrity: sha512-jJ1037967wU7YN/xkv+iRlOBlmaOXPhPO5KQSqya6GyXzBlwuLzELBFao16DVg9dZyqmNrhewzwZ3SAibetHBQ==} + engines: {node: '>=22.12.0'} + + '@commitlint/execute-rule@21.0.1': + resolution: {integrity: sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==} + engines: {node: '>=22.12.0'} + + '@commitlint/format@21.0.1': + resolution: {integrity: sha512-ksmG2+cHGtuDPQQbhBbC4unwm444+6TiPw0d1bKf67hntgZqZ8E0g1MuYKUuyT5IH4IMmXZhKq22/Z3jBvtQIw==} + engines: {node: '>=22.12.0'} + + '@commitlint/is-ignored@21.0.2': + resolution: {integrity: sha512-H5z4t8PC9tUsmZ/o+EptM3Nq8sTFtskAShdcqxCoyzklW5eaVT5xbrDAET2uypzir9Vsj4ZZmBtyKjYe2XqgeQ==} + engines: {node: '>=22.12.0'} + + '@commitlint/lint@21.0.2': + resolution: {integrity: sha512-PnUmLYGeGLfW8oVatR9KpNxSHYAnJOEWlMZzfdeFOUq6WUrFx1fGQaWCWJqMoIll/xPM+GdfJV+tKHZVHhl0Fg==} + engines: {node: '>=22.12.0'} + + '@commitlint/load@21.0.2': + resolution: {integrity: sha512-lwUE70hN0/qE/ZRROhbaX65ly/FF12DrqfReLCESo37M0OQCFAf2jRS+2tSCSORq+bm4Kdju7qNDj46uc1QzTA==} + engines: {node: '>=22.12.0'} + + '@commitlint/message@21.0.2': + resolution: {integrity: sha512-5n4aqHGD/FNnom/D5L8i7cYtV+xjuXcBL832C3w9VglEsZzIsoHpJsvxzJ7cgiOsOdc/2jU4t5+7qMHh7GBX3g==} + engines: {node: '>=22.12.0'} + + '@commitlint/parse@21.0.2': + resolution: {integrity: sha512-QVZJhGHTm+oiuWyEKOCTQ0ZM3mfJ0eGWFeHuj7WzSKEth+UukcCHac9GD8pgdFlg/qGkFWOtyaNd1T8REgagaw==} + engines: {node: '>=22.12.0'} + + '@commitlint/read@21.0.2': + resolution: {integrity: sha512-BtsrnLVycSSKf4Q0gMch4giCj5NNlmcbhc8ra5vONgGtP2IjRDo33bEFtr5Pm+2N+5fXGWb2MksWPrspPfdhdw==} + engines: {node: '>=22.12.0'} + + '@commitlint/resolve-extends@21.0.1': + resolution: {integrity: sha512-0DhjYWL6uYrY16Efa032fYk3woGJDU4AGWiG1XXltT9AMUNYKyb5cIZU2ivbaMZ3+kKFqUjikD2cjh66Sbh/Sg==} + engines: {node: '>=22.12.0'} + + '@commitlint/rules@21.0.2': + resolution: {integrity: sha512-k6tQ69Td7t2qUSIbik8D3TL1q3ZJpkEbV+yLogDzCRAdOxJm4ndhtBNREsLA1/puRfWvzS9eioF2w43WT+hHgQ==} + engines: {node: '>=22.12.0'} + + '@commitlint/to-lines@21.0.1': + resolution: {integrity: sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==} + engines: {node: '>=22.12.0'} + + '@commitlint/top-level@21.0.2': + resolution: {integrity: sha512-s9KKM+e+mXgFeIh4n7KmOGAVT3mkJ3Fp1bBYHIK5pjeUwlEMzp/tZfb5u0Poa680AsQTXMEMRxZi1vQ9m2X5ug==} + engines: {node: '>=22.12.0'} + + '@commitlint/types@21.0.1': + resolution: {integrity: sha512-4u7w8jcoCUFWhjWnASYzZHAP34OqOtuFBN87nQmFvqda03YU0T6z+yB4w0gSAMpekiRqqGk5rt+qSlW+a2vSEg==} + engines: {node: '>=22.12.0'} + + '@conventional-changelog/git-client@2.7.0': + resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} + engines: {node: '>=18'} + peerDependencies: + conventional-commits-filter: ^5.0.0 + conventional-commits-parser: ^6.4.0 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + + '@cspell/cspell-bundled-dicts@10.0.1': + resolution: {integrity: sha512-WvkSDNX4Uyyj/ZgbPO6L38iFNMfK1EqsH1FteRiI2qLz6QZMXRFrIt12OqiWIplzZDDaVpBH9FCJOPJll0fjCQ==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-json-reporter@10.0.1': + resolution: {integrity: sha512-/nes1RGILec3WCBcoMOd0byNTBtnJuPaVz/+ZzqYkLtY7x58VMcBG5kyP6hPyN8cIwjRADE/SR43gwdXuqk/FA==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-performance-monitor@10.0.1': + resolution: {integrity: sha512-9tVcHXwRnbazUv4WSG0h3MqV4+LgmLNgSALAQUflPPW0EMxTf7C4Dmv9cgxJyCEQrdnVKCr58nPPaahhz9LJUg==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-pipe@10.0.1': + resolution: {integrity: sha512-HPeXMD9AZ3V/qPkvQaPcak+C7cJ2z7JTHN8smd6J8L2aThLRky2cHc2OyeaHPSHB7WA47b4z2n5u5nawZhv5VQ==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-resolver@10.0.1': + resolution: {integrity: sha512-PIzkZHD1fGUQx1XteK2d1iQ0Mzq/maYcoB4jkvAiiR6WqP3MWYNKFdI9z+R5pOq5KgMfW+5Ig1q0oSR6h8irlA==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-service-bus@10.0.1': + resolution: {integrity: sha512-y6NcIGP2IdXaBL4PVH8vxsr7K27wzz3Ech87UtUtrDSXAiVEOvXgAIknEOUVp59rTlUE8Rn4IRURC6f/hgMyfw==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-types@10.0.1': + resolution: {integrity: sha512-kLgLShnWADDVreKC63pBrWkcvxgZzFIfO34Jhx/SWfuOIA3cD8AXT+HjyuLfoGJ7mUb58hv2kUziKzEy4INb1w==} + engines: {node: '>=22.18.0'} + + '@cspell/cspell-worker@10.0.1': + resolution: {integrity: sha512-L2bJerfuYOls2wEknm8FmynLtj/G7O4UqX9I/HznRggEW6i2yZIxagDetpVDNowpyavNHJ3SJtUFiyMiZc16Sw==} + engines: {node: '>=22.18.0'} + + '@cspell/dict-ada@4.1.1': + resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} + + '@cspell/dict-al@1.1.1': + resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} + + '@cspell/dict-aws@4.0.17': + resolution: {integrity: sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==} + + '@cspell/dict-bash@4.2.3': + resolution: {integrity: sha512-ljUZoKHbDqw5Sx0qpL2qTUlmkmr+vhZH/sCNrNaBZKTbdgiswErSnIF1jRbGmEitJNxHRHWsuZyVgnTGfVO1Yw==} + + '@cspell/dict-companies@3.2.11': + resolution: {integrity: sha512-0cmafbcz2pTHXLd59eLR1gvDvN6aWAOM0+cIL4LLF9GX9yB2iKDNrKsvs4tJRqutoaTdwNFBbV0FYv+6iCtebQ==} + + '@cspell/dict-cpp@7.0.2': + resolution: {integrity: sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==} + + '@cspell/dict-cryptocurrencies@5.0.5': + resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} + + '@cspell/dict-csharp@4.0.8': + resolution: {integrity: sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==} + + '@cspell/dict-css@4.1.2': + resolution: {integrity: sha512-+ylGoKdwZ2sVOCOnU2Eq5wDZx+RaVX3HoKyNHGGsFvhSw6IidQ6tH/mAPKBDofViHJoWCPNlklE0lTr6MDG3QA==} + + '@cspell/dict-dart@2.3.2': + resolution: {integrity: sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==} + + '@cspell/dict-data-science@2.0.14': + resolution: {integrity: sha512-jl6Ds4u5u5JT+yY30pWQpAbdCHfy3lCcNkLbpL/AZKoUaLEoXbaYsps9xQtvD7DyaiXxiLZkdH2yHHXtoFtZyg==} + + '@cspell/dict-django@4.1.6': + resolution: {integrity: sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==} + + '@cspell/dict-docker@1.1.17': + resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} + + '@cspell/dict-dotnet@5.0.13': + resolution: {integrity: sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==} + + '@cspell/dict-elixir@4.0.8': + resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} + + '@cspell/dict-en-common-misspellings@2.1.12': + resolution: {integrity: sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==} + + '@cspell/dict-en-gb-mit@3.1.24': + resolution: {integrity: sha512-Oowb/Uzkh7OmDRdCcETzMc9imEb4IpLlHJXoYjX8A8DS2X/54gqSjI915JFB8hKtFjBko5OM0BLQ+6cZhFEMmQ==} + + '@cspell/dict-en_us@4.4.35': + resolution: {integrity: sha512-xWpxBCc/FzzMMo/A+0qwARVaIIhR0Ql8yhhv4rvsvg+GfQF+LG9yzg2GwTM5N2rjvzmM3nKuR9zxFZq2I6fJSg==} + + '@cspell/dict-filetypes@3.0.18': + resolution: {integrity: sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==} + + '@cspell/dict-flutter@1.1.1': + resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} + + '@cspell/dict-fonts@4.0.6': + resolution: {integrity: sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw==} + + '@cspell/dict-fsharp@1.1.1': + resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} + + '@cspell/dict-fullstack@3.2.9': + resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==} + + '@cspell/dict-gaming-terms@1.1.2': + resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} + + '@cspell/dict-git@3.1.0': + resolution: {integrity: sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==} + + '@cspell/dict-golang@6.0.26': + resolution: {integrity: sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==} + + '@cspell/dict-google@1.0.9': + resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} + + '@cspell/dict-haskell@4.0.6': + resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} + + '@cspell/dict-html-symbol-entities@4.0.5': + resolution: {integrity: sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==} + + '@cspell/dict-html@4.0.15': + resolution: {integrity: sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==} + + '@cspell/dict-java@5.0.12': + resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} + + '@cspell/dict-julia@1.1.1': + resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} + + '@cspell/dict-k8s@1.0.12': + resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} + + '@cspell/dict-kotlin@1.1.1': + resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} + + '@cspell/dict-latex@5.1.0': + resolution: {integrity: sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==} + + '@cspell/dict-lorem-ipsum@4.0.5': + resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} + + '@cspell/dict-lua@4.0.8': + resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} + + '@cspell/dict-makefile@1.0.5': + resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} + + '@cspell/dict-markdown@2.0.17': + resolution: {integrity: sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ==} + peerDependencies: + '@cspell/dict-css': ^4.1.2 + '@cspell/dict-html': ^4.0.15 + '@cspell/dict-html-symbol-entities': ^4.0.5 + '@cspell/dict-typescript': ^3.2.3 + + '@cspell/dict-monkeyc@1.0.12': + resolution: {integrity: sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==} + + '@cspell/dict-node@5.0.9': + resolution: {integrity: sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==} + + '@cspell/dict-npm@5.2.41': + resolution: {integrity: sha512-To3xsfRmMBYVXtWVEdUgV35M9a/JZ54dSuoY6m6D3uHKKL3I326Wmy4xifZ3PU8MQaWhyEH7zbIcUEtKwTQMcA==} + + '@cspell/dict-php@4.1.1': + resolution: {integrity: sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==} + + '@cspell/dict-powershell@5.0.15': + resolution: {integrity: sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==} + + '@cspell/dict-public-licenses@2.0.16': + resolution: {integrity: sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ==} + + '@cspell/dict-python@4.2.27': + resolution: {integrity: sha512-Rj6xQgYS4X6ienjgAZF+njA0GRY4oSPouJWv0vfikCTn6EWlfk0V6Dy1HP3Migj1O+IC2NmespgVq+BZNSp8OA==} + + '@cspell/dict-r@2.1.1': + resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} + + '@cspell/dict-ruby@5.1.1': + resolution: {integrity: sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA==} + + '@cspell/dict-rust@4.1.2': + resolution: {integrity: sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg==} + + '@cspell/dict-scala@5.0.9': + resolution: {integrity: sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==} + + '@cspell/dict-shell@1.2.0': + resolution: {integrity: sha512-PVctvT22lJ49niMiakO8xieY7ELCAzjSqhejWR7bAMb5AZ9F4WDEs+XdGMnoVHWeXq7K5rcepLPmEJb+37zzIw==} + + '@cspell/dict-software-terms@5.2.2': + resolution: {integrity: sha512-0CaYd6TAsKtEoA7tNswm1iptEblTzEe3UG8beG2cpSTHk7afWIVMtJLgXDv0f/Li67Lf3Z1Jf3JeXR7GsJ2TRw==} + + '@cspell/dict-sql@2.2.1': + resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} + + '@cspell/dict-svelte@1.0.7': + resolution: {integrity: sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==} + + '@cspell/dict-swift@2.0.6': + resolution: {integrity: sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==} + + '@cspell/dict-terraform@1.1.3': + resolution: {integrity: sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==} + + '@cspell/dict-typescript@3.2.3': + resolution: {integrity: sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==} + + '@cspell/dict-vue@3.0.5': + resolution: {integrity: sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==} + + '@cspell/dict-zig@1.0.0': + resolution: {integrity: sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==} + + '@cspell/dynamic-import@10.0.1': + resolution: {integrity: sha512-mP1gdq00aIcH8HxNMqnH11X6BKxLcneDtFgl/ecjIKnaGKwi44m8AndP5Kr4ODaYdl8UUw9O3dJh7KaQXnLHZQ==} + engines: {node: '>=22.18.0'} + + '@cspell/filetypes@10.0.1': + resolution: {integrity: sha512-Z5S35giU5IW49fBBq6BksUbE8PC4IYPfaKuwl5Nl9jkf/OkAKiBmCowKX45NzRUQInwK/GSqqIUifrNeI6LdLw==} + engines: {node: '>=22.18.0'} + + '@cspell/rpc@10.0.1': + resolution: {integrity: sha512-axSRKv3zEAmBm66iD/FV/MPmE4/Yf7c3PZiwTW894Yd3iEhtn3KPKeTrqQ2/tDrhB1Z2qTsap/Hue0MK4o5WXg==} + engines: {node: '>=22.18.0'} + + '@cspell/strong-weak-map@10.0.1': + resolution: {integrity: sha512-lenN1DVyPi8nJLSMSJJ670ddTjyiruLueuSZO1qLcxBqUhgxDt/mALu9N/1m6WdOVcg6m/5cLiZVg2KOo2UzRw==} + engines: {node: '>=22.18.0'} + + '@cspell/url@10.0.1': + resolution: {integrity: sha512-abYYgI29wJhWIfWTYrYuzRYDcHQUQ1N5ylnhxYn1NJnIQMqUWGLbDmt12JABtZ+R6h6UNatQrS7rhP86etvJyQ==} + engines: {node: '>=22.18.0'} + + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.5': + resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@csstools/media-query-list-parser@5.0.0': + resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/selector-resolve-nested@4.0.0': + resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/selector-specificity@6.0.0': + resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@ctrl/tinycolor@4.2.0': + resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} + engines: {node: '>=14'} + + '@cyberalien/svg-utils@1.2.15': + resolution: {integrity: sha512-ZbKU6npzW5PNocdoLVJYfKzaP+c/RpT6JUkoaKrW1DOcw6lyXub8XtcNpI3xok6FnyNjS6ZbsrrtjTnS9yeZAQ==} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/core@1.11.0': + resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@es-joy/jsdoccomment@0.84.0': + resolution: {integrity: sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-plugin-eslint-comments@4.7.2': + resolution: {integrity: sha512-LF03qURSwEWm2dz5wtdDCzNk+7Opl0X7q6I3undsaIuNsEiNvRV3BCtqu14Q/6Pzg1tBj44LcxpW2EpSLZStZw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/css-tree@4.0.4': + resolution: {integrity: sha512-nxMparyhqVWQvadx9x8dIfubfIPOE+X2b2waua8fzdnM9vdp9rgVtwEZlG0TmCwEUz/d/f40fzvO/eqBwdxz0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.2': + resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@floating-ui/vue@1.1.11': + resolution: {integrity: sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@iconify/json@2.2.488': + resolution: {integrity: sha512-W2MfGS0n1GugWhQn7SLgRKNH1JonVsKG/O9vO/htP7j3k1bGt54R1mbN9uaZlQ99uCCu65IR5UgQM66E8Cm+Fw==} + + '@iconify/tailwind4@1.2.3': + resolution: {integrity: sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA==} + peerDependencies: + tailwindcss: '>= 4.0.0' + + '@iconify/tools@5.0.12': + resolution: {integrity: sha512-aFPwSFmFphUPVjNLUkgxgUPSPVgTEEjv0mE7NgvfoQBuE5TtsMrKa4HTY65cM5oXZ2a1xKQcW/RKhiOKEiAarw==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.3': + resolution: {integrity: sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==} + + '@iconify/vue@5.0.1': + resolution: {integrity: sha512-aumwwooJlFJ5H5qYWB6ZTAyM0C8hpfcSVLB9/a3qnH1GGvIJ+FEbpEs4s/HfErYe/M5qZeLjwmESR5fFm3lXEw==} + peerDependencies: + vue: ^3.5.38 + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@internationalized/date@3.12.2': + resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==} + + '@internationalized/number@3.6.7': + resolution: {integrity: sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==} + + '@intlify/bundle-utils@11.2.4': + resolution: {integrity: sha512-eE18yR9eM9k5n8snCkHIYp2MuVTxa19aF8z9OMyxXWv0frz2HlBZDGIPFjA38pP3OJ1IlRBXC/dW5GILeLMSCQ==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.4.6': + resolution: {integrity: sha512-EOeHO95XESK9IFHgHeZXunsM/WBAoCA0DlaWODvx14vKmetAuS97t+l6Xe9hTUqntPpF93vtVSjjUDafw3wXMw==} + engines: {node: '>= 22'} + + '@intlify/devtools-types@11.4.6': + resolution: {integrity: sha512-wowQPpNem56b2d43IJmqbrzG2FeBKe5f/kUGlpNuBmXs6OSqncF8m1+1lxHuW8ISZJF0ma2RkW3iLkw0g0G4VA==} + engines: {node: '>= 22'} + + '@intlify/message-compiler@11.4.6': + resolution: {integrity: sha512-5nj3jULqeTAC1WovwMs1LQWgatTa2pM/rXN9T3XW8rdOtXW9ZF6/GLSNFTKDQmPLwclhPdgUWLJ/4w3fMeeC/Q==} + engines: {node: '>= 22'} + + '@intlify/shared@11.4.6': + resolution: {integrity: sha512-m1p1HHAMLhqSpTRH7VnXdrN0CQ4y+9vunFkpLkbD8soIuBsnQdawZXqMCgvwI2UVF9Ww7sVaw7g9tV2VO7shoA==} + engines: {node: '>= 22'} + + '@intlify/unplugin-vue-i18n@11.2.4': + resolution: {integrity: sha512-bY0ZOaVUvWTyvy4bRGCUKw4Brx5uH/ojjVKsZ1aWzY2drFKIJbeP8DpGMD2QZT8aLpZSUsHtiJlRGLQSZenrvw==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.5.38 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vite: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@vue/compiler-dom': ^3.0.0 + vue: ^3.5.38 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + '@intlify/shared': + optional: true + '@vue/compiler-dom': + optional: true + vue: + optional: true + vue-i18n: + optional: true + + '@ioredis/commands@1.10.0': + resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jspm/fetch@0.1.0': + resolution: {integrity: sha512-R749VLq+2zRvaYOrBz8JQaeahWmjOQH9tnvp6896WHhcsFwV7bJOEXiz56EMi34DxJZTLdFUFkrtjOC5Pw3zXQ==} + + '@jspm/generator@2.16.1': + resolution: {integrity: sha512-TJCi0x0xJJd+GHZvh6I2j0fklzvVCqTgoQz2yKHKabXkY5wu2AJyzISn8W5OISSuRDY3U6aopqNJE0xpKTcvrw==} + + '@jspm/import-map@1.5.0': + resolution: {integrity: sha512-wvjrSEB2MCrpicc/tA0mYEsQ1Jgusnz+4ALwbp5IqGFcnxcTamU/VaVbodmEXFFg4GaPmC2LupUl7Bks6nWOhg==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@lezer/common@1.5.2': + resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} + + '@lezer/cpp@1.1.6': + resolution: {integrity: sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==} + + '@lezer/css@1.3.3': + resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} + + '@lezer/go@1.0.1': + resolution: {integrity: sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==} + + '@lezer/highlight@1.2.3': + resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + + '@lezer/html@1.3.13': + resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} + + '@lezer/java@1.1.3': + resolution: {integrity: sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==} + + '@lezer/javascript@1.5.4': + resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} + + '@lezer/json@1.0.3': + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} + + '@lezer/lr@1.4.10': + resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} + + '@lezer/markdown@1.6.4': + resolution: {integrity: sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==} + + '@lezer/php@1.0.5': + resolution: {integrity: sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==} + + '@lezer/python@1.1.19': + resolution: {integrity: sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==} + + '@lezer/rust@1.0.2': + resolution: {integrity: sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==} + + '@lezer/sass@1.1.0': + resolution: {integrity: sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==} + + '@lezer/xml@1.0.6': + resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==} + + '@lezer/yaml@1.0.4': + resolution: {integrity: sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==} + + '@lucide/vue@1.21.0': + resolution: {integrity: sha512-eoFn3tppjKAc12ZqdnRSMFdtwQ1ZMRQFb6SV1Eub6Y8kU28ccnqKeSFdnur9hMg8gIbosU2Y3WFJr/J/xS/IlQ==} + peerDependencies: + vue: ^3.5.38 + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} + + '@mapbox/node-pre-gyp@2.0.3': + resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==} + engines: {node: '>=18'} + hasBin: true + + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@mermaid-js/parser@1.1.1': + resolution: {integrity: sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==} + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nuxt/kit@3.21.8': + resolution: {integrity: sha512-kg63DUPY5AHPn+9XM7u8rYcdWHXjzwfUscgRDuiC5YUciQ+xdLRhdwXelYFxEAx2nxJHossliiQXbMm/Fleivw==} + engines: {node: '>=18.12.0'} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@ota-meshi/ast-token-store@0.3.0': + resolution: {integrity: sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@oxc-parser/binding-android-arm-eabi@0.135.0': + resolution: {integrity: sha512-sHeZItACNcA5WRAWqF6ixriR4GkZDyY10gVgnZU7pXku1DjHFATSqnwZM809jl0gXPHxb6fKzYQCK7bNK5cACQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.135.0': + resolution: {integrity: sha512-wPte+SzgzWWFgMSF8YZDNM+tBXtJg0AXBi7+tU3yS2z1f2Af9kRLZLKuJojADmuD/cZexmnMHHC3SDItTW77Iw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.135.0': + resolution: {integrity: sha512-BmKz3lHIsqVos+9aPcdYCT9MG3APoUyM43KlEFhJMWNVDOGG8FKyiFz81Bc+mGz2o0hpuQ3PfXLfVWJrKXjo2g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.135.0': + resolution: {integrity: sha512-dM8BS+8+Br1fNvmh2QZbGiHaYttwLebRa6J4Uz9vuFzMNmvsdRYwf7993ptOaV0JTrR63AaoVLjX7nhWbijxjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.135.0': + resolution: {integrity: sha512-xlZnvvJdR9bGu2pOhvR5hMuKPHCE6Sa9owK5A484mzjHdm75VRV5nCs5w/jkmGODMMTFc+KN7EnZqEieM813kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.135.0': + resolution: {integrity: sha512-PSR8LmBK/H/PQRiN8g7RebQgZX/ntVCrdT/JBfNxE5ezdHG1s2i4rbazsRJYD83TTI1MmgTpC0MGL42PLtskQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.135.0': + resolution: {integrity: sha512-I85GJXzfUsigkkk7Ngdz95C217M4FdUi1Z2HrX5UyPmURobwQZ7m2bbUvwFkz4VGZd+lymFGKHvDZ3RQC9qOzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.135.0': + resolution: {integrity: sha512-zqEY0npz0g0aGZj/8a5BclunjVDytsBQHYtIC10Gd26HcrLwbVF6YDbqRQjunMGYdSo97u6xOBl05aTDI2diDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.135.0': + resolution: {integrity: sha512-mWAfprP819gQ2qYst1RxgTI8b/z0b29OpoKfRflIXLHde2dZLihQD4g47Onuvtpo5GPIkMYPRlX9QoeZfs/GnQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.135.0': + resolution: {integrity: sha512-gri8c2AOmJKJwOux2KTHFBfUaXoJURuVMKhmKEi/2hTF55cQteTDV2XNfTiE5oCC+Tnem1Y4/MWzcyDadtsSag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.135.0': + resolution: {integrity: sha512-Y2tkupCG5wo0SxH2rMLG4d4Kmv6DaM3sBp+GuM5lox0S8Za6VxKgQrY2Mut088QQxKkEE89n/4CCCgmw2o0e3Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.135.0': + resolution: {integrity: sha512-xDRJq6i6WTynjeP+ISbDpyH4p9BaJ0wuQcL0lCSDkt9qOXC9dmwpOu1VG/TlwmPI3KpYntmO9nJCuc3TMTsNBA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.135.0': + resolution: {integrity: sha512-V4MoUuiCRNvihxhIufRxvK+ka013V4joTSK0FAGA1KEjLuNprfH6N/Qw2uxQEVIFuNYMhD/hV6xJ/ptbzlKdHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.135.0': + resolution: {integrity: sha512-JCFZ7zM7KXOKoPAbK/ZB4wY0M1jxRECiem2UQuiXLjzGqS9+hno7mtX+qyK2F7HWK2xPhyJb+frpcOtk5DKOtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.135.0': + resolution: {integrity: sha512-9jSVS1b3hOV7sdKH4aA2DFfnTz0RgQd0v2BefR+LYbH8yIlmSM22JJZbAAjVeVXmFgUAk3zJQ1tpE/Nd+Vi2YQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.135.0': + resolution: {integrity: sha512-M857ZLBSdn1Uy/SJJz5zh0qGu67B4P9omCgXGBU2LLqTzraX6ZjVNaKq5yW1PDw/LgJXDXR/dbZfgmB310f11Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.135.0': + resolution: {integrity: sha512-2w6DVcntQZX9U5RhXtgiWb3FLWFB5EcwI1U8yr3htOCJUJjagN4BFUHz/Y/d9ZsumndZ6ByxxWEtbUZNE1bfFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.135.0': + resolution: {integrity: sha512-rX1U8+IH2Z37EJjDXKa1iifvUQAdba+vZ4Ewj1iaG5eA/QaSybzclCOwtWa0/5BuUQnnK/T2JHUEFrwhL6Ck2Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.135.0': + resolution: {integrity: sha512-9FAisBbH1QICGAjlJobiuKGd/jOuVmyqniWdQMwTa5SkCl6hhuotBCJf1n46B0flYbSOR5TzfV9HZCWSyb3c/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.135.0': + resolution: {integrity: sha512-wYF+A2AzJ2n7ul6q+Z2G/ia0S2+8cUp0AgWZzoFvF4WmUcl1P7p+o6se1Gdr5wGnWuF0iAMIkGddrjCarNr2yA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + + '@oxc-project/types@0.135.0': + resolution: {integrity: sha512-wR+xRdFkUBMvcAjBJ2q2kcZM6d+DKu2NgoOyxZgYwZdLhmiv6+rnO8PZ/P68kMiZtIKm+pW7zyEJ4kSOs0vo+Q==} + + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + + '@oxc-resolver/binding-android-arm-eabi@11.21.3': + resolution: {integrity: sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.21.3': + resolution: {integrity: sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.21.3': + resolution: {integrity: sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.21.3': + resolution: {integrity: sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.21.3': + resolution: {integrity: sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3': + resolution: {integrity: sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3': + resolution: {integrity: sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.3': + resolution: {integrity: sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@11.21.3': + resolution: {integrity: sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3': + resolution: {integrity: sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3': + resolution: {integrity: sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.3': + resolution: {integrity: sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.3': + resolution: {integrity: sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-gnu@11.21.3': + resolution: {integrity: sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@11.21.3': + resolution: {integrity: sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-openharmony-arm64@11.21.3': + resolution: {integrity: sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.21.3': + resolution: {integrity: sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.3': + resolution: {integrity: sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.21.3': + resolution: {integrity: sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==} + cpu: [x64] + os: [win32] + + '@oxfmt/binding-android-arm-eabi@0.55.0': + resolution: {integrity: sha512-+rFDOqQe5LOWgxrAJaZgLRudr6GQm0wGI6gtu7vVkrdLGjNMUSGbAlaCr8j7F2H2Er97vYQCU8WDb30onqMM1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.55.0': + resolution: {integrity: sha512-ctulLq8s3x8Zmvw6+iccB09TIKERAklRSmbJ10gk8mlAn05qZxoyo52dj3Hi9IJcmDSwF54fQaTVh2CbL6PInw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.55.0': + resolution: {integrity: sha512-xDQczLH9pw/RBk1h/GH0qcGMm8hQtmtVHBNLSH3lk1gEIR09hZ4L+mJQl4VqiVAvPK9VG9PYrWWuSQLt7xTbiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.55.0': + resolution: {integrity: sha512-JaNoFCkF2CJdGgpPSMbuO9HVyXyoNGIhMHPvp6NYAjeVKw9XEYc0HcUWJLPQa3Q69WV5wMa9m5jPMJPtbLtcRg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.55.0': + resolution: {integrity: sha512-DNbszhpg6S2MIzax5azdHFTTBIVkR5xr8yyRZuA4yoDAwOkzIp3tmldgKZM2+VlT+hJIG0xUksA+elISzMEAfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.55.0': + resolution: {integrity: sha512-2snoaoRfFFyGnbOcKUK36rREBYxe/Xgz3uHbiA5zbCB/s6R4DQj4mHqYAaWWhgizCUSDxV8cE9zAZ0XleNpKGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.55.0': + resolution: {integrity: sha512-q1aktHF/WRpSK81BX1dE/9vWrS2jGw1Nax2kb4DBLGAewubCLcoNyp4Zl/NSMgbv3vUS46Z33wIQkBVYOP3PYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.55.0': + resolution: {integrity: sha512-VD0y36aENezl/3tsclA/4G53Cc7iV+7Uoh7gz4yvcOTaEYBtJpQsE6PKDGTtUtOvGS4kv51ybfXY/nWZejO5IA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.55.0': + resolution: {integrity: sha512-r8xlKJFcsRmn0H5jZrdORae6RX9jDBrZVvOoxF+bCQtampQJClv80aZEHsv+NsLsp2KCE5ql79O7DpPVzYWpXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.55.0': + resolution: {integrity: sha512-GRKv/HXHcwIVld/WU61rF0g0R16hl5EJ+ScKdpjevT57lnLnagj/U2YUbXf2mT+2Pg1uCzWC+mvGicPV3CDdLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.55.0': + resolution: {integrity: sha512-rdv57enTiPtpSYRMKfAiEbQb0Puw5t9N7isVinDoo5qeLDScro2gznmZqSgSWbVZRzLisTeCTW8Qwgw0bOHv3A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.55.0': + resolution: {integrity: sha512-7v1nNrlD43VY6+sYQ6efYyb3lE6QY182304PD/768ZxTjOmFd/3dQa3u/nGBUAXYdGSWOQc5N3PnS0QzUXyEIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.55.0': + resolution: {integrity: sha512-f4lJLUSPOgScjFl9LiflKCTocyNRwE25JmTMbN4XQdDjoZzEHjqf3wA3VESF1/csg7i8m7+EQLbrZyYDqe10UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.55.0': + resolution: {integrity: sha512-MihqiPziJNoWy4MqNSV+jVA1g+07iQDjZiR0vaCaDoPgFEiJpCMsxamktzLV07cEeQsSJ04vQaU4CzCQwIvtDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.55.0': + resolution: {integrity: sha512-Yqghym7KYAVjP9MmSrNZiDeerMuoejNjo0r3ox5H3GDKk8eAfl8VyJm9i+pWCLDCTnAbcTUMMN2ZKjUYXH1v3g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.55.0': + resolution: {integrity: sha512-s5SDvVVSbyQl1V5UU3Yl12M+XLUQ3rl5SglNqgAA2K4PXUtQhyNSS00wivONPEnNo5W01rCou8WkDNyvI/RGHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.55.0': + resolution: {integrity: sha512-7p9FB5R32tw2KyyNX3wpQrR2WHwEHvMEiBlGXxeTCaRMCVNx3UtFMAUbaQ/pRNWIrEUZmYhJ6tcUH52uPTRYjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.55.0': + resolution: {integrity: sha512-ZYqj3fDnOT1IaVGMP5kpmkQl4F3tQIm2ZyAxvqkJYmI0xgWWak4ss4XYwv3VDfM+TWXeC9K4uQ/wW5jm/5XABA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.55.0': + resolution: {integrity: sha512-eEYT5tivGnGbPHuOHuQpi6CGLObhh0re/5jcNQHihD2GRYkTM85dyi5a19zjP8Q00t1uqAx+/QGLUGdHeqzWyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint-tsgolint/darwin-arm64@0.23.0': + resolution: {integrity: sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@0.23.0': + resolution: {integrity: sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@0.23.0': + resolution: {integrity: sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@0.23.0': + resolution: {integrity: sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@0.23.0': + resolution: {integrity: sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@0.23.0': + resolution: {integrity: sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.70.0': + resolution: {integrity: sha512-zFh0P4cswmRvw6nkyb89dr18rRanuaCPAsEXsFDoQY8WdaquI8Pt4NWFjaMJg6L23cy5NeN8J9cBnREbWzZhaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.70.0': + resolution: {integrity: sha512-qI8o4HZjeGiBrWv+pJv4lH0Yi2Gl/JSp/EumBUApezJprIKa5PS4nU0lQsQngtky8k+SplQIOjv6hwu0SSxeyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.70.0': + resolution: {integrity: sha512-8KjgVVHI5F9nVwHCRwwA78Ty7zNKP4Wd9OeN5PSv3iu/F/u1RVXoOCgLhWqust6HmwQG6xc8c+RCyaWENy24+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.70.0': + resolution: {integrity: sha512-WVydssv5PSUBXFJTdNBWlmGkbNmvPGaFt/2SUT/EZRB6bq6bEOHmMlbnupZD5jmlEvi9+mZJHi8TCw15lyfSfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.70.0': + resolution: {integrity: sha512-hJucmUf8OlinHNb1R7fI4Fw6WsAstOz7i8nmkWQfiHoZXtbufNm+MxiDTIMk1ggh2Ro4vLzgQ+bKvRY54MZoRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.70.0': + resolution: {integrity: sha512-1BnS7wbCYDSXwWzJJ+mc3NURoha6m6m6RT5c6vgAY3oz7C3OVXP+S0awo2mRq97arrJkVvO3qRQfyAHL+76xtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.70.0': + resolution: {integrity: sha512-yKy/UdbR55+M2yEcuiV5DCNC/gdQAjr/GioUy50QwBzSrKm8ueWADqyRLS9Xk+qjNeCYGg6A8FvUBds56ttfqg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.70.0': + resolution: {integrity: sha512-0A5XJ4alvmqFUFP/4oYSyaO+qLto/HrKEWTSaegiVl+HOufFngK2BjYw9x4RbwBt/du5QG6l5q1zeWiJYYG5yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.70.0': + resolution: {integrity: sha512-JiylyurlB0CLSedNtx1gzv3FvfWPF1h/2Y3BJszPLNt5XQFlBsH5ke0Jle3iJb3uqu5m2e7A/DwzpuCAHdiU+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.70.0': + resolution: {integrity: sha512-J8VPG7I3/HmgaU4u8pNU2kFx2+0U+vPLS1dXFxXOaR/2TQ0f8AC7DRz0SRGRI1bfphnX2hVYTTtLuhL4nYKL+Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.70.0': + resolution: {integrity: sha512-N2+4lV2KLN+oXTIIIwmWDhwkrnvqf5oX7Hw0zPjk+RuIVgiBQSOlJWF7uQoFx2siEYX0ZQ5cfSbEAHm+J3t7Wg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.70.0': + resolution: {integrity: sha512-1e2L7cFCvx9QDzq6NPP+0tABKb5z6nWHyddWTNKprEsjO9xNrAtPowuCGpjNXxkTdsMiZ4jc8YQ5SstZd4XK6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.70.0': + resolution: {integrity: sha512-Kwu/l/8GcYibCWA9m9N5pRXMIKVSsL/YbgpLzYkqDhWTiqdRfnNJ/+nqIKRKQiFbHWsdlHEhzMwruJK+qcEruA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.70.0': + resolution: {integrity: sha512-tap04CsHYOl0nSAQJfPNIuBxqEPB2HnhQqwaOXLg1jnp2XfRo8Fa814dA4QC4zpvTWXCjAAaCY1W5LOORkEQuQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.70.0': + resolution: {integrity: sha512-hzJa/WgvtJpbBD9rgfy0qe+MjbxOXNUT0bfR1S6EQQzfTtBFA9xg5q8KSwRrQ2QfSS+TaP4j+4mVPQrfNc6UNg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.70.0': + resolution: {integrity: sha512-xbsaNSNzVSnaJACCUYr1HQMyY/Q/Q1LkePmHG3UvZPvGCYGNxrsZp9OmtA6ick8xH47ltRRbRrPCM1YXYcyC+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.70.0': + resolution: {integrity: sha512-icAEsUI7JbW1TMRdEXV83mVAInhRVQYuuAlPpxdGwJ95chNdnCzjloRW8GglT0WvzOEZSio6fnYSk2DJ2Hv7LQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.70.0': + resolution: {integrity: sha512-FHMSWbVsPVs/f+Jcl04ws4JJ2wUnauyTzlpxWRG/lSO/8GpX08Fo2gQZqdA6CrRFI+zvkxl+N/KwJGWfUwYVZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.70.0': + resolution: {integrity: sha512-ptOlKwCz7n4AKs5VweMqG6DAg677FmKOK+vBkkL9DMNgFATIQ+upqUYBTOEwRQyRAx1ncGlPlXleV2hIcm3z4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-wasm@2.5.6': + resolution: {integrity: sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==} + engines: {node: '>= 10.0.0'} + bundledDependencies: + - napi-wasm + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/constants@1001.3.1': + resolution: {integrity: sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==} + engines: {node: '>=18.12'} + + '@pnpm/error@1000.1.0': + resolution: {integrity: sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==} + engines: {node: '>=18.12'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.3': + resolution: {integrity: sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA==} + engines: {node: '>=12'} + + '@pnpm/types@1001.3.0': + resolution: {integrity: sha512-NLTXheat/u7OEGg5M5vF6Z85zx8uKUZE0+whtX/sbFV2XL48RdnOWGPTKYuVVkv8M+launaLUTgGEXNs/ess2w==} + engines: {node: '>=18.12'} + + '@pnpm/workspace.read-manifest@1000.3.1': + resolution: {integrity: sha512-ojO1Anf4ITL7OskuUYoLI3bldQ36XNLpG2cYQZ4F6LZbBZnRY8umW6TeWzb9h/aX8UFwYp9vWbQ8R/FaMtMEeg==} + engines: {node: '>=18.12'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.7.0': + resolution: {integrity: sha512-0UTYalzk2t6S4rA2uHOz5bSSW2CHdv4vggJI6Alg90yvl0UgXs6XSXpH96OH+bRkX4J/06djv29pqXJ0lq5Kag==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + + '@publint/pack@0.1.5': + resolution: {integrity: sha512-edgyN2pP07uXiP4tJs0s8KVmU8M8i60YPbbI0/WDeok1mIJHRXz+CgD8I0nelwDkoCh3EWL/G5kGfbuHjsdbvw==} + engines: {node: '>=18'} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-android-arm64@1.1.2': + resolution: {integrity: sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-arm64@1.1.2': + resolution: {integrity: sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.2': + resolution: {integrity: sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.1.2': + resolution: {integrity: sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.2': + resolution: {integrity: sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-gnu@1.1.2': + resolution: {integrity: sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-arm64-musl@1.1.2': + resolution: {integrity: sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-ppc64-gnu@1.1.2': + resolution: {integrity: sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.2': + resolution: {integrity: sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.2': + resolution: {integrity: sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-musl@1.1.2': + resolution: {integrity: sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.1.2': + resolution: {integrity: sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-wasm32-wasi@1.1.2': + resolution: {integrity: sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-arm64-msvc@1.1.2': + resolution: {integrity: sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.2': + resolution: {integrity: sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.17': + resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/plugin-alias@6.0.0': + resolution: {integrity: sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==} + engines: {node: '>=20.19.0'} + peerDependencies: + rollup: '>=4.0.0' + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-babel@6.1.0': + resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + rollup: + optional: true + + '@rollup/plugin-commonjs@29.0.3': + resolution: {integrity: sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-terser@1.0.0': + resolution: {integrity: sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@simple-libs/child-process-utils@1.0.2': + resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} + engines: {node: '>=18'} + + '@simple-libs/stream-utils@1.2.0': + resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} + engines: {node: '>=18'} + + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@speed-highlight/core@1.2.17': + resolution: {integrity: sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@stylistic/stylelint-plugin@5.2.0': + resolution: {integrity: sha512-pfa2PMvlH87qwoOMUH4rqz7x/vuO5Kh+r/R1Pe+/5T8sukDPe/VfF6mbdUGE5gwZPlj5O1X56qw55IZrFFRF0g==} + peerDependencies: + stylelint: ^17.6.0 + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} + peerDependencies: + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' + + '@tailwindcss/vite@4.3.1': + resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/store@0.11.0': + resolution: {integrity: sha512-WlzzCt3xi0G6pCAJu1U+2jiECwabETDpQDi3hfkFZvJii9AuZqEKbOiVarX1/bWhTNjU486yQtJCCasi/0q+Cw==} + + '@tanstack/virtual-core@3.17.1': + resolution: {integrity: sha512-VZyW2Uiml5tmBZwPGrSD3Sz73OxzljQMCmzYHsUTPEuTsERf5xwa+uWb01xEzkz3ZSYTjj8NEb/mKHvgKxyZdA==} + + '@tanstack/vue-store@0.11.0': + resolution: {integrity: sha512-w1lE4D7oo6nIUhTQvL/RoTgcvoYJw+KvttWPNJd/Xu+MvNxMnjxFF/LgFp9vwmbNbiGhGCg3ZoPJptwjrfwNYw==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: ^3.5.38 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + '@tanstack/vue-virtual@3.13.29': + resolution: {integrity: sha512-MWb9tNHjpar3sP34b8+3A4I5j9akveoPXIYqqp7/ipyWd49a/kso+1S1LqEmAVR/+g/k1WWTJC4ktvdCGWgXYQ==} + peerDependencies: + vue: ^3.5.38 + + '@tiptap/core@3.27.1': + resolution: {integrity: sha512-rV6Qn4wmC6BxfF+4mu6bqGWj9vA4oXXhsrpXaJL2uhjxeHAGofjwcHof2X84VYzeyXgdlsGmqKie4TAppVXZUQ==} + peerDependencies: + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-blockquote@3.27.1': + resolution: {integrity: sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-bold@3.27.1': + resolution: {integrity: sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-bubble-menu@3.27.1': + resolution: {integrity: sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-bullet-list@3.27.1': + resolution: {integrity: sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-code-block@3.27.1': + resolution: {integrity: sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-code@3.27.1': + resolution: {integrity: sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-document@3.27.1': + resolution: {integrity: sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-dropcursor@3.27.1': + resolution: {integrity: sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==} + peerDependencies: + '@tiptap/extensions': 3.27.1 + + '@tiptap/extension-floating-menu@3.27.1': + resolution: {integrity: sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-gapcursor@3.27.1': + resolution: {integrity: sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==} + peerDependencies: + '@tiptap/extensions': 3.27.1 + + '@tiptap/extension-hard-break@3.27.1': + resolution: {integrity: sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-heading@3.27.1': + resolution: {integrity: sha512-SrC4l1kEIyv9ZXFaI/8LQqU2MyMmjczw7XXsWUQOTN4YXv0JyVgMNR3cI/wz0d2xsTfBdZ1N85Tdng+Ga1t0Sg==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-highlight@3.27.1': + resolution: {integrity: sha512-IeMIUKbW4oyRkgn92BPYQ0fifkbTwVigKwa107nGDHMokz+pHQPFHy8xG3U7gtUOra/8OUo57bFgNGuP0TaH4A==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-horizontal-rule@3.27.1': + resolution: {integrity: sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-image@3.27.1': + resolution: {integrity: sha512-+JTahgQT+NxiGjduaB3qJVyhU/wh4m3pVkht1Earioku2bm/apj5Lb8rSowa/NJYP3B+oQgV/V4YLw5dtDgBoA==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-italic@3.27.1': + resolution: {integrity: sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-link@3.27.1': + resolution: {integrity: sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-list-item@3.27.1': + resolution: {integrity: sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-list-keymap@3.27.1': + resolution: {integrity: sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-list@3.27.1': + resolution: {integrity: sha512-c2Upru7lj0/ZV/Ibww6cNz6sUS8m6Dp/9uygFhYcZOd3X8M0xBIEk42c6m6SQehkPziVA8QOgNJz7sMqsbz1OQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-ordered-list@3.27.1': + resolution: {integrity: sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-paragraph@3.27.1': + resolution: {integrity: sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-placeholder@3.27.1': + resolution: {integrity: sha512-lhcNDcczQ75yJOSywCHb58Hmtg1aPL/2TdYmeLPxVrP048D7rRs133sfONcgyyw0AvhnfmPOkHLTv3QtKSowhw==} + peerDependencies: + '@tiptap/extensions': 3.27.1 + + '@tiptap/extension-strike@3.27.1': + resolution: {integrity: sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-text-align@3.27.1': + resolution: {integrity: sha512-EXawuJBO55wd8WcTbHTMoPhv0CGQxza4yCCPB5Hqz4ZPQwahIr3ej+8yp/kimIl0xokabwZ0/Fu8STQ4AkZv5g==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-text-style@3.27.1': + resolution: {integrity: sha512-J48WIl+6YDYTFPhWXUBQk+u7+AKVUqTdvrZOQyPYCGuQMgHrYzgWrI5+HeEifUgXJ5rMIWWP3qytp7KhVVqpDQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-text@3.27.1': + resolution: {integrity: sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-underline@3.27.1': + resolution: {integrity: sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extensions@3.27.1': + resolution: {integrity: sha512-1Tdx9faw8k0/83V6X+xCDVhV8yElGt95JxeW3YMkKQJI56QdlPz0xOdJPlMiSGJKinPyVier+x9LJD/YZUZIaw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/pm@3.27.1': + resolution: {integrity: sha512-Ffjx+vimmBU7zH/KrpXzJid3+pziCe/VL2aexSTP63cyQwKQ65LkFkCKaIsSpFdQQuakVZBGWjCA5RoBV852pw==} + + '@tiptap/starter-kit@3.27.1': + resolution: {integrity: sha512-vfxRsqW8rCc0k4pzo0ilU3wobVi2wqVj88VZI2SlgZlNnUAkrDGDIAph7CTa9k9fshV+O1ivpEgPC5yC046jow==} + + '@tiptap/vue-3@3.27.1': + resolution: {integrity: sha512-o5GB6hfUnyf9sCB306rHWmaIYRL+02ROX657EkuY8tEWKHMTuMjHWl2AqHMP47wz0W9DaMOJLvcPpYdAEKq3Mw==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + vue: ^3.5.38 + + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} + engines: {node: '>=12'} + + '@tsdown/css@0.22.3': + resolution: {integrity: sha512-kmb/31lcI/Pk26WeWJxQDLy8qkvoyfBkw3n9aN6bflKkoeLqnV2BRbndzfPRJY+Ziahi796gfgDVc+ftLIgWVw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + postcss: ^8.4.0 + postcss-import: ^16.0.0 + postcss-modules: ^6.0.0 + sass: '*' + sass-embedded: '*' + tsdown: 0.22.3 + peerDependenciesMeta: + postcss: + optional: true + postcss-import: + optional: true + postcss-modules: + optional: true + sass: + optional: true + sass-embedded: + optional: true + + '@turbo/darwin-64@2.9.18': + resolution: {integrity: sha512-9f27peFu16ur8c0v9nUFUEyBnbKuuFsUTjHFWfmwGfzySBXbHwzU44QhZon6Mznz0cHsIr3984NQj/bVrnGSRw==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.9.18': + resolution: {integrity: sha512-9A6TMRq/Ib+QnbhLlgkhOm+624wO4pzSQ/yQviQfWHOlFvaYxdnIAYmu2H6TS6y7kSVL0DvzNe04NbESTOzFVQ==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.9.18': + resolution: {integrity: sha512-zCdIDtz69AnbYh913elJRRoF3QY5aa2HNnf+4rAkc7bQ+tWujiDkCNV7stazOUPggaDvhKIf2Z87qHftTeXSkw==} + cpu: [x64] + os: [linux] + + '@turbo/linux-arm64@2.9.18': + resolution: {integrity: sha512-Va1kXI04naMgYwqv/5Dfa36dTDx8015U7oaQAjrXa45ua9OoFjSV4OmvkML4EmXvUclQHCiBRbY8bvd0jV7eAg==} + cpu: [arm64] + os: [linux] + + '@turbo/windows-64@2.9.18': + resolution: {integrity: sha512-m0kDhZANxSNz9ck1ybogFscHabriAsp4eDFNrN/1H5WrgTF7b3VlcPZnhuO3v2+E2KnCbeAc+UUT10BZZHdDKw==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.9.18': + resolution: {integrity: sha512-nUdR8WqoomUys9iIQmG45TMiizJ+5BV8egSeLLZba/AWblyp3fVBcIH1kSE58OtK4g2YzbMJEth6Ttv9w5rqMA==} + cpu: [arm64] + os: [win32] + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/archiver@8.0.0': + resolution: {integrity: sha512-YpXPbEuv9+eUIPPQWUPahj3cvs9isWRuF+J4z+KbdYVDO3rWorWQFxUVHnwPu2AgKwvgpki5F2VMX0Xx+mX45A==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/html-minifier-terser@7.0.2': + resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==} + + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + + '@types/json-bigint@1.0.4': + resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/lodash.clonedeep@4.5.9': + resolution: {integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@25.9.4': + resolution: {integrity: sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==} + + '@types/node@26.0.0': + resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/sortablejs@1.15.9': + resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/rule-tester@8.61.1': + resolution: {integrity: sha512-x7xp2GZaFcrXv2tuGN5Lcdd05BcPDaL2wSPpARPSbbRE7N2N46za+9NTAtb8NX5a9FfoDLkhLYDbJjngV8xYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + + '@v-c/async-validator@1.0.1': + resolution: {integrity: sha512-2WXdbTso13119ZLiwUv1JkmdL0K4ll69id4oE3ft3LQX+YAHOoJtfx080u26lLtypgZS32PguoohgB0BVo39rg==} + + '@v-c/cascader@1.1.0': + resolution: {integrity: sha512-HXVYrm/Cj4JsMtuGx7EncsC6C9b8xWsh8jWANfWreqffpzWZX10GrVqO6fyi/Mk2d4+yenlEsx5B7p5SyXlm3A==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/checkbox@1.0.1': + resolution: {integrity: sha512-+QsE/0VfU6oeglwuHWYxRNTn3+eV08iG0uN/upDmqSGznezInzfUClh+t4acd/OxyJVtuob0WKsg/vPlT6WRWw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/collapse@1.0.0': + resolution: {integrity: sha512-y4NAl3j4mka193ZMDLHdISA8to61qoROG6/kTQ0myM2ZuEsonnEK1QWlqoEw3gveMsa6a4RdyoXLxdGdcJyp0Q==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/color-picker@1.0.6': + resolution: {integrity: sha512-XiTlEMG5p5jkCdKP7nbeo9EJrpdXnoW4uBOA2us8pVgA6m0GxrwMoCg//X+1XsuOaebG/1Swo65mjRuMBM6rZg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/dialog@1.2.0': + resolution: {integrity: sha512-I+ODJyaWTp8/ePuPQUkay70J3wVwKrzG+Eo6k+cEuO7+J93v6tXjUVT//jeY6bhmiaeeGe8BSy9EK5RuBGxTpw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/drawer@1.0.6': + resolution: {integrity: sha512-nW8rCsf7viUsTo+8SAfqcNDO4B0GC4n2VidHV4kJzbERrMvMsHVpDz1LO+UFgLs8Mn9EjpQm40JtF6d/BNkW0g==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/dropdown@1.0.2': + resolution: {integrity: sha512-D6TACf3jUiRWx4xW5h2+wVT9SMYxUasFlAHESYJr4ZMjLTLLM1Q8iBjkjhGF+vA0eYR5zqRTwlaacN0DNDZBPw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/image@1.0.12': + resolution: {integrity: sha512-SwGOHRSHPiAWlazy+vfwE3dIniIWQLXfl6R0SdlMFbUZ6mbklkym2KqJxj+ZL7psl4tpO+WsgU9IZDMBzXmFPw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/input-number@1.0.5': + resolution: {integrity: sha512-YQBpV1KnuYf0o2XrbC+OEyP6lkyqv1XhzDVh3QhHO2bs/Jr3uSD8b3cxdEGU+gjoNJUcoZx7XiIzcYhLlvMctw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/input@1.1.0': + resolution: {integrity: sha512-R9/SgqMjb5ZUB47x+4b6uRmx7DtI5uOTtDk80A03jKFSgHjF0KONm0063TChQZ2RdqiiK/5Z4MPj+EaEvbVI0w==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/mentions@1.1.0': + resolution: {integrity: sha512-sCMsUh4He0uAflcltLGqpg2YFS9wPRfZxoJaJVeCI2Z96sIoYpoaXTfZzkbHuFsISABFPH7kdHgqw/SUjKwqqQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/menu@1.2.0': + resolution: {integrity: sha512-iEvRMxit9VlxAGsvcgR1B4hLpi7gIz4dvKbxf4pnClA75OS8J4AGHSdyevtANNHplfJXoi0G3St0dKPKduETbg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/mini-decimal@1.0.1': + resolution: {integrity: sha512-76wZLdlkI017iDlaZMNOWZyDCv29YVabUJn5urQgIKtW4dnI5AkNXWtmLyhl/mu/OS7ZGisRi5ai/558QhLQxQ==} + + '@v-c/mutate-observer@1.0.2': + resolution: {integrity: sha512-btnmj0NezMcQ/vOlTlP9p47NBOa8q/CIYdDMrpM3W0JrmAHrkAUD7hiWypLXyBuiEko+SQc3U07rcm8StQ5qFA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/notification@2.0.1': + resolution: {integrity: sha512-23Vj8/eodOVSAAvXjZkgwIn2Ww0zNzX3w0wpyYi/TlDo4pOfWFEyXtJEuQ1icS9HPUlmIaBfHrBgomCWe3OxNA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/overflow@1.1.0': + resolution: {integrity: sha512-XCERIkdhdvB5Jmy6nj+g2UHHN+e9+BcK5QxvpEqo7aj6Hxbr7LoQMxW6DGjqdZQm7YJyF3hMx8Sid4OKqYg7cg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/pagination@1.0.1': + resolution: {integrity: sha512-lx8B/kjAA+ABGXSKpm92z5OjKb25UqNcDOUAc0PlYurKZYfn2JG+jTCDS2mGVHV/8PEjiIBT22Xv0XLh3blvIg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/picker@1.1.3': + resolution: {integrity: sha512-anyP/MIjoc+TwqqdOGzZQuwGLV676uT//FZXaPbsTIBREIS+inl48GhcqFkaXBoyy6b06jBNoG6pIrzLpa7Qaw==} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + vue: ^3.5.38 + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + '@v-c/portal@1.0.8': + resolution: {integrity: sha512-93elruWfHKrdtRkpFBNpi47YhQjA75tCuG8C/WvcQ9x/dp+H3i+y7h6t0iyyDjZu1w3d2U5+d43vtslmoQXBYg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/progress@1.0.1': + resolution: {integrity: sha512-ZTaXBYfAASPSk9MD/tu3eZUCnB1UhK6s0G/F0AkNwvzPiVcR2kiiDZO12YpfmdZwUjcrg8U7miAg89aSUZ/aPw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/qrcode@1.0.1': + resolution: {integrity: sha512-kPcpjgX5lEj6/vatJOqrUtqj5PGvcAYCs5CAu1kBO0B6FGnSuHM/JXlYQrmMNIOD1sI/GzgYIa9KL6mc8FCHJw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/rate@1.0.1': + resolution: {integrity: sha512-ZWWY01LeKu9S/JncdvSr2gz2Kwwum3bB/AxzzCsuhCyg+9P4BLwX7S2WZDMiJ92uYEpiYladVTCa9XdSCPaCaQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/resize-observer@1.1.2': + resolution: {integrity: sha512-yOcskQ1yG26A5l5ImPtgeuMFCwiq5QJRVnubDa9rgv3KOossTVSHUU3UkLxeqE3rdIAuyHnYcroAW43+faNdKQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/segmented@1.0.2': + resolution: {integrity: sha512-g/aWgURMJkytu05AACN3Pd9QKgka4ZQM8tl/y0AcabANldy2hmPIHek0ZH5dDdn5VmK1PhAfUTqtgDS19sLh5w==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/select@1.1.1': + resolution: {integrity: sha512-NsXZH9JfjlEsGBFahOdx8Wg3lFLM4TOZtDdcJhNZOdbhlkJvJnxRWL9iLQs2ycoD32Ufa/5QEJIyKuQKWTyohQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/slick@1.0.2': + resolution: {integrity: sha512-8BbPxJgYST+tio9jyeFQgJb/3XSFhfWZN8Am7YVYeqsYsvn3g8wu61UZNSjlkp9ArOEe+ujOgX4izfDHeXh4RA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/slider@1.1.0': + resolution: {integrity: sha512-W11+HdY7JjpsZA+2x6ZcZLK/z/cwY+o3fbqzCgguWdKY8qwhA2CWQBd6SMidFa8F+L2HU/JupNx6Yu3cVw9piQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/steps@1.0.0': + resolution: {integrity: sha512-DPL0OOb8pDLlTPZB93b8+Saxiz6V5zEpGXKaCnsbXUuOhimkc7089AuEKfpMw+8x1SrVe+gapWf5RRHWXUm2pg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/switch@1.0.1': + resolution: {integrity: sha512-dWMBfb5/8o3GFGvayyT576YWB6yPW8Aa2Ba5I9hXcYvuIH4JJ5NYI3WJqc8KpFMNA4Y4/MyVIY3NNsXyXa22bw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/table@1.1.4': + resolution: {integrity: sha512-rfJtpLR1rUfm5j4GixWZmiXIWrjSUtiZTfibl37xCvvWs7JFtfcBLeVlnFJuKbVOVp8bUSdhwOzvhwXrkEM0hg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/tabs@1.1.0': + resolution: {integrity: sha512-k91oQBDmYM2cPM1Wt/N1GdTwB2ky2NTfVBmzURRSaw2qjGkq4Gt3z8GNaXYqH+S/0hE+ciQhcCtWRdfM2oGIYA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/textarea@1.1.0': + resolution: {integrity: sha512-+E2DZogCNHb/0Jf/DJLqX5BH22/HcdTcHo9dMjmS+X/NDRCGl1sQfeEa+7mGAWnYXAmL4RIS8PS6lwEn90inbA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/tooltip@1.0.3': + resolution: {integrity: sha512-72EkTfhb67RPJvMXIW6HUYiZ+Jdrb7tBQmS3wDtFDNU7uIrS5DQLyXJDCu9qWlrPv7cQ/RHA4JfCINw88vchzw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/tour@1.1.0': + resolution: {integrity: sha512-241ToEBsqIdopZWVn9cBpajxXtFMztEuFssn+cwCSCDYikB8EdR9kEktf6cd3n9brzrgLqiRiMwigjzDsvNqEA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/tree-select@1.1.0': + resolution: {integrity: sha512-Nulw5J0Tth3+1IIMbc7aZ9m5iKWKTlCfjKCVvbXRWRN8OSc52AMDh9gyW6qPMp92Cxn1YQJtoQDgpa/IcPafuQ==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/tree@1.1.0': + resolution: {integrity: sha512-V7O0MHQqPL9eo3WGrM/+HmMoGsrca5jSPZs4GbhshTHKrQvFF38KjOs5xM4CxJBNaVSQzN2iGLUbrZYeiU/9Tw==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/trigger@1.0.16': + resolution: {integrity: sha512-u7XlKWIyESAJ53B02fHIgLtnMa+7PRlYNzqr/SxVYh8zqK64vYXKTPjEt54W0+XeFtUfa8j8zVHlqMmeH0biyA==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/upload@1.0.0': + resolution: {integrity: sha512-W92PNCD61aM/B5w8oUzHQSDHur1T8484726Ls0IoNMO5nPiF/15eEE3RuuI/t7xXQVP/fA06hNSwzXwGWdDg1w==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/util@1.0.19': + resolution: {integrity: sha512-apJGS4BVzhXbrNR6jxXF18jAiOWIn/UNmGjgSvB5r4ba9Wr/ireKCfJvhuuNsZi+scLaM0W3ghB81PbQ5vwoJg==} + peerDependencies: + vue: ^3.5.38 + + '@v-c/virtual-list@1.0.8': + resolution: {integrity: sha512-lsc0+AiOaIiG74EvzXSALq2bCwjmFlLDvKjyWXQPwz/9VA3NPkCbMvukA8HgCWvDALvmL0ejnvjc/907b8d8lA==} + peerDependencies: + vue: ^3.5.38 + + '@valibot/to-json-schema@1.7.1': + resolution: {integrity: sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==} + peerDependencies: + valibot: ^1.4.0 + + '@vavt/copy2clipboard@1.0.3': + resolution: {integrity: sha512-HtG48r2FBYp9eRvGB3QGmtRBH1zzRRAVvFbGgFstOwz4/DDaNiX0uZc3YVKPydqgOav26pibr9MtoCaWxn7aeA==} + + '@vavt/util@2.1.2': + resolution: {integrity: sha512-L3UbSJthJwr3wq0x93O5TrCepimrmVZaIl2ciZbeL18G5++gBhJXNhcH7RcVk/6rr3SavWOvwhig0mqRLoR7dw==} + + '@vee-validate/zod@4.15.1': + resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==} + peerDependencies: + zod: ^3.24.0 + + '@vercel/nft@1.10.2': + resolution: {integrity: sha512-w+WyX5Ulmj4dtTZrxaulqrjaLZHSbnPzx75SJsTNYmotKsqn1JlLnDJa+lz5hn90HJofhl/2MAtw0mCrgM3qYw==} + engines: {node: '>=20'} + hasBin: true + + '@vitejs/plugin-vue-jsx@5.1.5': + resolution: {integrity: sha512-jIAsvHOEtWpslLOI2MeElGFxH7M8pM83BU/Tor4RLyiwH0FM4nUW3xdvbw20EeU9wc5IspQwMq225K3CMnJEpA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.5.38 + + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.5.38 + + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue-macros/common@3.1.2': + resolution: {integrity: sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==} + engines: {node: '>=20.19.0'} + peerDependencies: + vue: ^3.5.38 + peerDependenciesMeta: + vue: + optional: true + + '@vue/babel-helper-vue-transform-on@1.5.0': + resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==} + + '@vue/babel-helper-vue-transform-on@2.0.1': + resolution: {integrity: sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==} + + '@vue/babel-plugin-jsx@1.5.0': + resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-jsx@2.0.1': + resolution: {integrity: sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.5.0': + resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-plugin-resolve-type@2.0.1': + resolution: {integrity: sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.38': + resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} + + '@vue/compiler-dom@3.5.38': + resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} + + '@vue/compiler-sfc@3.5.38': + resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==} + + '@vue/compiler-ssr@3.5.38': + resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + + '@vue/devtools-api@8.1.3': + resolution: {integrity: sha512-73NMCvxXh8Hyozc/jiwqTFWVcCMyi11U1zmrq4DoukQJnuo8JHt6FsNu4HdeUDa8SpIp5vb7Q22GWgIq0efsXg==} + + '@vue/devtools-core@8.1.3': + resolution: {integrity: sha512-xezkv5/CPH/o5C8PE2Len9MnTJMsctYYQbKbbUiNOJpKd+fRHj27nKDb/sbtYI8NSQduegeQhCJGKRgAiOV6Uw==} + peerDependencies: + vue: ^3.5.38 + + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + + '@vue/devtools-kit@8.1.3': + resolution: {integrity: sha512-cRn7GXiCQkMYU2Z3h3pM4YO/ndbx9FY1yLDAqIqPLcmIq4H6zAOJHein6tvZU3AfPwgrodqLiPBEF+YQaS8AxA==} + + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + + '@vue/devtools-shared@8.1.3': + resolution: {integrity: sha512-CM3uIPL+v+lrJUk33+pxspYo0MhuMWlCvf7zC9fybifvCPyM2jUbYRPwoYEJgYbwRqPikm5HozbUhp60MF2QuA==} + + '@vue/language-core@3.3.5': + resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} + + '@vue/reactivity@3.5.38': + resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} + + '@vue/runtime-core@3.5.38': + resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==} + + '@vue/runtime-dom@3.5.38': + resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==} + + '@vue/server-renderer@3.5.38': + resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==} + peerDependencies: + vue: ^3.5.38 + + '@vue/shared@3.5.38': + resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} + + '@vue/test-utils@2.4.11': + resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==} + peerDependencies: + '@vue/compiler-dom': 3.x + '@vue/server-renderer': 3.x + vue: ^3.5.38 + peerDependenciesMeta: + '@vue/server-renderer': + optional: true + + '@vueuse/core@13.9.0': + resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==} + peerDependencies: + vue: ^3.5.38 + + '@vueuse/core@14.3.0': + resolution: {integrity: sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==} + peerDependencies: + vue: ^3.5.38 + + '@vueuse/integrations@14.3.0': + resolution: {integrity: sha512-76I5FT2ESvCmCaSwapI+a/u/CFtNXmzl9f9lNp1hRtx8vKB8hfiokJr8IvQqcQG5ckGXElyXK516b54ozV3MvA==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 || ^8 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 || ^8 + vue: ^3.5.38 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@13.9.0': + resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==} + + '@vueuse/metadata@14.3.0': + resolution: {integrity: sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==} + + '@vueuse/motion@3.0.3': + resolution: {integrity: sha512-4B+ITsxCI9cojikvrpaJcLXyq0spj3sdlzXjzesWdMRd99hhtFI6OJ/1JsqwtF73YooLe0hUn/xDR6qCtmn5GQ==} + peerDependencies: + vue: ^3.5.38 + + '@vueuse/shared@13.9.0': + resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==} + peerDependencies: + vue: ^3.5.38 + + '@vueuse/shared@14.3.0': + resolution: {integrity: sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==} + peerDependencies: + vue: ^3.5.38 + + '@vxe-ui/core@4.4.14': + resolution: {integrity: sha512-bEMnvZl+R39+midxlz0vcpWS/w4WsdN9bOfjxWIfz7o95FyxOQWUjHs63IT9wNeUP09WhLkmUDKKjdl7BQYUMw==} + peerDependencies: + vue: ^3.5.38 + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + + antdv-next@1.3.6: + resolution: {integrity: sha512-ZoBTTRg+Jyb/D2tTWgwMa8vdl5IRVnQYVt5dQ1ttmesQ1zMrdkLMEOzYBGA4LGBL5D7Wcz9ZAHIEYiuZL2CPhw==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + + archiver@8.0.0: + resolution: {integrity: sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==} + engines: {node: '>=18'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-kit@2.2.0: + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} + engines: {node: '>=20.19.0'} + + ast-kit@3.0.0: + resolution: {integrity: sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==} + engines: {node: ^22.18.0 || >=24.11.0} + + ast-walker-scope@0.9.0: + resolution: {integrity: sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==} + engines: {node: '>=20.19.0'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + atomically@2.1.1: + resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios-mock-adapter@2.1.0: + resolution: {integrity: sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==} + peerDependencies: + axios: '>= 0.17.0' + + axios@1.18.0: + resolution: {integrity: sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==} + + b4a@1.8.1: + resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + bare-events@2.9.1: + resolution: {integrity: sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.7.2: + resolution: {integrity: sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.9.1: + resolution: {integrity: sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.1: + resolution: {integrity: sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==} + + bare-stream@2.13.3: + resolution: {integrity: sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.4.5: + resolution: {integrity: sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.38: + resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + c12@3.3.4: + resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} + peerDependencies: + magicast: '*' + peerDependenciesMeta: + magicast: + optional: true + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + + cacheable@2.3.5: + resolution: {integrity: sha512-EQfaKe09tl615iNvq/TBRWTFf1AKJNXYQSsMx0Z3EI0nA+pVsVPS8wJhnRlkbdacKPh1d0qVIhwTc2zsQNFEEg==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk-template@1.1.2: + resolution: {integrity: sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==} + engines: {node: '>=14.16'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} + engines: {node: '>=20.18.1'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + circular-dependency-scanner@3.0.1: + resolution: {integrity: sha512-q9Wadov30HqZGJF34ZUZZsICoR9ihKDFe0XK0jbNOuVZE5YPitHbsipLkSJg271Rj8NQzs7bvNgue2M2aILeWg==} + engines: {node: '>=20'} + hasBin: true + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.1: + resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} + engines: {node: '>=0.10.0'} + + codemirror@6.0.2: + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + colorjs.io@0.5.2: + resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + comment-json@5.0.0: + resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==} + engines: {node: '>= 6'} + + comment-parser@1.4.5: + resolution: {integrity: sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==} + engines: {node: '>= 12.0.0'} + + commitlint-plugin-function-rules@5.0.1: + resolution: {integrity: sha512-AC8fH83A+soNq9VKTlLptkbg8Zpfx88ng9hKzdiJ0N+GoYgWTWNt6yFA3zEZ0qy3ekQ4Paf1slkaH1UKWRLUvg==} + engines: {node: '>=22'} + peerDependencies: + '@commitlint/lint': '>=19 <22' + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + compatx@0.2.0: + resolution: {integrity: sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==} + + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + + compress-commons@7.0.1: + resolution: {integrity: sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==} + engines: {node: '>=18'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@7.1.0: + resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} + engines: {node: '>=18'} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + conventional-changelog-angular@8.3.1: + resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==} + engines: {node: '>=18'} + + conventional-changelog-conventionalcommits@9.3.1: + resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==} + engines: {node: '>=18'} + + conventional-commits-parser@6.4.0: + resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==} + engines: {node: '>=18'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + cookie-es@2.0.1: + resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + + cosmiconfig-typescript-loader@6.3.0: + resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + + crc32-stream@7.0.1: + resolution: {integrity: sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==} + engines: {node: '>=18'} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + croner@10.0.1: + resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==} + engines: {node: '>=18.0'} + + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + cspell-config-lib@10.0.1: + resolution: {integrity: sha512-hMpo/0j6k7pbiqrLDOLJKD2IGP9XwhjKf2miiM6p84Xeo4nyuFZaxxDCQ68R851HSYFrrdltgpoipMbj1h2Tnw==} + engines: {node: '>=22.18.0'} + + cspell-dictionary@10.0.1: + resolution: {integrity: sha512-3cZ659vgsZWkzGQJR/sNqGDVt/OnvTSieLKI76V++4t1bHJfochb9ZrrwsuMsb1VPGiyqClUP1/O6WrefF/FVg==} + engines: {node: '>=22.18.0'} + + cspell-gitignore@10.0.1: + resolution: {integrity: sha512-wN23U61Mx6qPJN3CesOmBU9vnbJ0jQm/ylK0iaVui3CcnO7Zzl5qLu5mPHUzGQGm8yso6qjyxqo16Ho7LpZGOQ==} + engines: {node: '>=22.18.0'} + hasBin: true + + cspell-glob@10.0.1: + resolution: {integrity: sha512-7bII9J3aSSpZDwhx7w+zfQXbMxHZQ3be0ilUp5bHrsjz6o07v/NqOHMGcwKdPn1sw2dxDz9sv057xE5pqXnSdw==} + engines: {node: '>=22.18.0'} + + cspell-grammar@10.0.1: + resolution: {integrity: sha512-xC9AFYmaI9wsO//a7S5tdDGKGJVD5UEEsTg+Up2fi7lPfXIryisYmV6tePNL1SEg0idYss4ja8LUZ3Mib09BjQ==} + engines: {node: '>=22.18.0'} + hasBin: true + + cspell-io@10.0.1: + resolution: {integrity: sha512-8C2ka07faxflnaqEBO3pektS21XViE/SEHT7F5ZD1ou7FyMR5u3xawTBJSczClfsxLt/WYeztBYrpmGAjmjksw==} + engines: {node: '>=22.18.0'} + + cspell-lib@10.0.1: + resolution: {integrity: sha512-RpsIPiLzc4/YMW8BMRKpyJ81x439qjYWcqgdKeXnMkbKM88J9PexzutfFf/4v97v96KzfNitEzMpbI0uj8OeUg==} + engines: {node: '>=22.18.0'} + + cspell-trie-lib@10.0.1: + resolution: {integrity: sha512-BFvhalSkRQFjKrZ//FKK7fRGrZFpifnxB5AwCkzsIsBZqicsfafcQ1xP21qpb0QqyV/IomjNgviG+tRJs+0rMw==} + engines: {node: '>=22.18.0'} + peerDependencies: + '@cspell/cspell-types': 10.0.1 + + cspell@10.0.1: + resolution: {integrity: sha512-Gg6w/flT3fKfl3la62hfTnhtNnDQ+9mU7kUhVqw/axl/Ms4oENw0oJMkWFIoj4f6nL/SDPz7KcPXd2XbkKFNmQ==} + engines: {node: '>=22.18.0'} + hasBin: true + + css-functions-list@3.3.3: + resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} + engines: {node: '>=12'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssfilter@0.0.10: + resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.34.0: + resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} + engines: {node: '>=0.10'} + + cz-git@1.13.1: + resolution: {integrity: sha512-xeMA5ci+gkBY6uKmqc6nzhG/Q4pmK1DKbVVUgByOTBgregoiYE0f3otEF0ADelyEWH4vNsO1W2o7QZYEe7Nwlw==} + engines: {node: '>=v12.20.0'} + + czg@1.13.1: + resolution: {integrity: sha512-mCfY4bCAMAdaFSlrQwtUTChzFAv59kD/y2leX5+C7X9inu0mt/VkHT7gkbzwcToh26oJaOaYPA9lcXUX92/6cw==} + engines: {node: '>=v12.20.0'} + hasBin: true + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom-zindex@1.0.7: + resolution: {integrity: sha512-cKU/h8v8IPBgdZOTPbPmq3Ib+Ac5C+kKoh9I4LbGR9BM3GwbmB16KYWKJcj5M2BavnA66EbgYzxYDLd1IytnlQ==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + dompurify@3.4.11: + resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@10.1.0: + resolution: {integrity: sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==} + engines: {node: '>=20'} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + echarts@6.1.0: + resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==} + + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + engines: {node: '>=14'} + hasBin: true + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.376: + resolution: {integrity: sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + engine.io-client@6.6.6: + resolution: {integrity: sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + + enhanced-resolve@5.24.0: + resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + env-paths@4.0.0: + resolution: {integrity: sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==} + engines: {node: '>=20'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + errx@0.1.0: + resolution: {integrity: sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.1: + resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + engines: {node: '>= 0.4'} + + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + + es-toolkit@1.47.1: + resolution: {integrity: sha512-5RAqEwf4P4E17p+W75KLOWw/nOvKZzSQpxM32IpI2KZLaVonjTrZ0Ai5ghMaVI9eKC2p8eoQgcBdkEDgzFk6+Q==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-json-compat-utils@0.2.3: + resolution: {integrity: sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==} + engines: {node: '>=12'} + peerDependencies: + '@eslint/json': '*' + eslint: '*' + jsonc-eslint-parser: ^2.4.0 || ^3.0.0 + peerDependenciesMeta: + '@eslint/json': + optional: true + + eslint-plugin-better-tailwindcss@4.6.0: + resolution: {integrity: sha512-Xeh/6KzLeays6jXSqTKx6iCdJL1qo5ant/mAabbz7dJqADYebKUUPR2zXp1xuiqqJ3HIm+MN+Ql2m4Jp0BxkMQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + oxlint: ^1.35.0 + tailwindcss: ^3.3.0 || ^4.1.17 + peerDependenciesMeta: + eslint: + optional: true + oxlint: + optional: true + + eslint-plugin-command@3.5.2: + resolution: {integrity: sha512-PA59QAkQDwvcCMEt5lYLJLI3zDGVKJeC4id/pcRY2XdRYhSGW7iyYT1VC1N3bmpuvu6Qb/9QptiS3GJMjeGTJg==} + peerDependencies: + '@typescript-eslint/rule-tester': '*' + '@typescript-eslint/typescript-estree': '*' + '@typescript-eslint/utils': '*' + eslint: '*' + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-jsonc@3.2.0: + resolution: {integrity: sha512-eQSxJypkpNycQAFE/ph/j+bDD2MiCcojxNb+7nugYzuQZvELYg4YO1Cv1y/8MbjPIEw5u3Lx0VPOTlqJJIhPPw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: '>=9.38.0' + + eslint-plugin-n@18.1.0: + resolution: {integrity: sha512-hkUm9EtnFV2h2fE16jNVUfCVUqvPzI7fGLsFdun5lFt/pbmf2kCgDx6ymi9rx+NCUSggBmurJCZOfG20JBs/kg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: '>=8.57.1' + ts-declaration-location: ^1.0.6 + typescript: '>=5.0.0' + peerDependenciesMeta: + ts-declaration-location: + optional: true + typescript: + optional: true + + eslint-plugin-perfectionist@5.9.1: + resolution: {integrity: sha512-30mHLNfEhzwaq5cquyWgnzrNXvT8AzwIwyeH5aj4U5ajhHSF2uiO6i09xpMDLv7koaZVTjLsvYF4m3gK/15tyA==} + engines: {node: ^20.0.0 || >=22.0.0} + peerDependencies: + eslint: ^8.45.0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-pnpm@1.6.1: + resolution: {integrity: sha512-pgcaJclu3YxZ/WMsiKMF58bHQasbGVARSMqCJvFaETYxSc7KcR2H74UVWV6exuGv9nNv9c0KKqn4PVHQlzMSEg==} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + + eslint-plugin-unused-imports@4.4.1: + resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 + eslint: ^10.0.0 || ^9.0.0 || ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + + eslint-plugin-vue@10.9.2: + resolution: {integrity: sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue-eslint-parser: ^10.3.0 + peerDependenciesMeta: + '@stylistic/eslint-plugin': + optional: true + '@typescript-eslint/parser': + optional: true + + eslint-plugin-yml@3.4.0: + resolution: {integrity: sha512-j6U3ESrAkidkvNb3HFN2UMxke46GNp6bsJokabXCICcgomSy3YU4oED9cjzkZ58nYxWD5qnWV1b/2YlqyWMOxA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + eslint: '>=9.38.0' + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.5.0: + resolution: {integrity: sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eta@4.6.0: + resolution: {integrity: sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==} + engines: {node: '>=20'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-equals@6.0.0: + resolution: {integrity: sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==} + engines: {node: '>=6.0.0'} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@11.1.3: + resolution: {integrity: sha512-oMbq0PD6VIiIwMF6LIa7MEwd/l9huKwmqRKXqmrkqIZv8CvRbfowL+L0ryAl8h//HfAS0zS+4SbYoRyAoA6BJA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@8.0.0: + resolution: {integrity: sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==} + engines: {node: '>=20'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@6.1.22: + resolution: {integrity: sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug==} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + formatly@0.3.0: + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} + hasBin: true + + framesync@6.1.2: + resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensequence@8.0.8: + resolution: {integrity: sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==} + engines: {node: '>=20'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + + get-port@7.2.0: + resolution: {integrity: sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==} + engines: {node: '>=16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} + + giget@3.3.0: + resolution: {integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==} + hasBin: true + + git-raw-commits@5.0.1: + resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} + engines: {node: '>=18'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + hasBin: true + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@16.2.0: + resolution: {integrity: sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gzip-size@7.0.0: + resolution: {integrity: sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + + happy-dom@20.10.6: + resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==} + engines: {node: '>=20.0.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + html-tags@5.1.0: + resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} + engines: {node: '>=20.10'} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-shutdown@1.2.2: + resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + httpxy@0.5.3: + resolution: {integrity: sha512-SMS9V6Sn7VWaS11lYhoAr0ceoaiolTWf4jYdJn0NJhCdKMu9R2H9Fh0LBDWBHQF6HRLI1PmaePYsjanSpE5PEw==} + + human-id@4.2.0: + resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} + hasBin: true + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immutable@5.1.6: + resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-fresh@4.0.0: + resolution: {integrity: sha512-Fpi660c7VPDM3fPKYovStd9IP1CPOikf6v/dGxJJMmHPcwYQIMJ4W7kO1avBYEpMqkCh+Dx3Ln6H7VYqgztLjw==} + engines: {node: '>=22.15'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + ioredis@5.11.1: + resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==} + engines: {node: '>=12.22.0'} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-ci@4.1.0: + resolution: {integrity: sha512-Ab9bQDQ11lWootZUI5qxgN2ZXwxNI5hTwnsvOc1wyxQ7zQ8OkEDw79mI0+9jI3x432NfwbVRru+3noJfXF6lSQ==} + hasBin: true + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-npm@6.1.0: + resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-safe-filename@0.1.1: + resolution: {integrity: sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==} + engines: {node: '>=20'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.8: + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + jsdoc-type-pratt-parser@7.1.1: + resolution: {integrity: sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==} + engines: {node: '>=20.0.0'} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.2: + resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonc-eslint-parser@3.1.0: + resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + knip@6.17.1: + resolution: {integrity: sha512-HcQsZSQ4Ymhuay4BVzJtM5pFZNDSomYYqcNCZOSITPQh9g18a09DqziWAxSt2G+BH9wGlG+0ZjWpEnaFlnKseQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + knitwork@1.3.0: + resolution: {integrity: sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + ky@1.14.3: + resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} + engines: {node: '>=18'} + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + lefthook-darwin-arm64@2.1.9: + resolution: {integrity: sha512-119HryNcvr4nqn0wUIrNPgpMEPn9yMQzEcW/lezRsnb56PCJriJB92+MCySPVcWDxJnZef7o0T3jdnPNiSH7Qg==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@2.1.9: + resolution: {integrity: sha512-dwo5Tke2XcQCM56DGHgFKBfRbJIL6xs2wZ0zG1TUVZgl4t4mQUt6LiZ4V/ZQfYHTZF9qywvXoIlR5N35qOaiVQ==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@2.1.9: + resolution: {integrity: sha512-+09PVap6nl6xsaHch5JLtq7WvIR++U1Q2MzA2ai0M4uB/VP3AqrvKqHw6+9hjyKnIH+HHL83uqi77EAY+LaxLA==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@2.1.9: + resolution: {integrity: sha512-8XresjKIYpkE9ARgCtBEZgJZxAU3T4MIqzj4zNy15XRT59I1Us+QdqXTNm+pkZ41Yd2X/nxs2Pkvbq3NWWlIGw==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@2.1.9: + resolution: {integrity: sha512-1oNIQfwrPe6rgU2KcDM3aF6+hpZDCKx1TmawQKpXUY5gVsbZ7MqX0Sk/1lnnWxqPm+kQQ5f6J2dpFWd+4xH8jg==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@2.1.9: + resolution: {integrity: sha512-fT+7Q+BJyGp+CslFQkNXmdFRgyVXsPHPi9NAsDX0a6QOyNnoORByAsvx6zeAKuF5rL3BBgNfho1/v2RuGxGy9w==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@2.1.9: + resolution: {integrity: sha512-4bVuafBk3dddVNo0+3hMbjcJs4mqYAstxpPMmX2ufkudSTYFNIhWoqwuGVQV/SS/xdcOKJAldW4qayAzed2ysw==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@2.1.9: + resolution: {integrity: sha512-PmPoMmLP/wQQWcQ9u2YH86bTZ3UCfBsxuEmVTEyPU2U8R1qSTp5r/Gs3G8cN5Mxo91XB9oBERtF1n+xD3W6aVA==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@2.1.9: + resolution: {integrity: sha512-KphfkBKmwBnmolyrdhIl3lrBaOyTcCgXBT2AB/9OHnEXhOLvv5uTCUkrD4YRAxXPtFKq6UvnapIeoL3GZq0bdA==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@2.1.9: + resolution: {integrity: sha512-2qlUtkJHZ3MyUxgV5XTEmcrIoNZA07iwaquoswAcqv/1MeBFXlD+O+koFRfrzWng2O5WYEbpJnd8tvaYnV8fTA==} + cpu: [x64] + os: [win32] + + lefthook@2.1.9: + resolution: {integrity: sha512-bwDaIOViTktE8kJLf9jP0p+H2/RDTlFFlc43Am2YgUsX22hI6Sq4RbzsrecwzY5y+MHTipOH7WsmWSEniePHWQ==} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + linkify-it@5.0.1: + resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + + linkifyjs@4.3.3: + resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + + listhen@1.10.0: + resolution: {integrity: sha512-kfz4C0OrC6IpaVMtYDJtf6PFjurxe9NBBoDAh/o2p587INryFOO4DQ9OetbCdDrWFt1m1CJKvYrzkGsuPHw8nQ==} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@8.0.0: + resolution: {integrity: sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==} + engines: {node: '>=20'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string-ast@1.0.3: + resolution: {integrity: sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==} + engines: {node: '>=20.19.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + markdown-it-image-figures@2.1.1: + resolution: {integrity: sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==} + engines: {node: '>=12.0.0'} + peerDependencies: + markdown-it: '*' + + markdown-it-sub@2.0.0: + resolution: {integrity: sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==} + + markdown-it-sup@2.0.0: + resolution: {integrity: sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==} + + markdown-it@14.2.0: + resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} + hasBin: true + + marked@12.0.2: + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} + engines: {node: '>= 18'} + hasBin: true + + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@4.0.0: + resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + + md-editor-v3@6.5.1: + resolution: {integrity: sha512-tdE5xgrm6FYrKDNGM6slQ2GS7eS1iZnQdQl3o+pVlfwmOKj06Lx3WtafhPN4WSxUhCwIDi8PXrr/Y9eEJWhyzA==} + peerDependencies: + vue: ^3.5.38 + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + mdn-data@2.28.1: + resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + medium-zoom@1.1.0: + resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + mermaid@11.15.0: + resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} + hasBin: true + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + modern-tar@0.7.6: + resolution: {integrity: sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==} + engines: {node: '>=18.0.0'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.14: + resolution: {integrity: sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + natural-orderby@5.0.0: + resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} + engines: {node: '>=18'} + + nitropack@2.13.4: + resolution: {integrity: sha512-tX7bT6zxNeMwkc6hxHiZeUoTOjVrcjoh1Z3cmxOlodIqjl4HISgqfGOmkWSayky3Nv9Z5+KQH52F8nmXJY5AAA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + xml2js: ^0.6.2 + peerDependenciesMeta: + xml2js: + optional: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.48: + resolution: {integrity: sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==} + engines: {node: '>=18'} + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + nprogress@0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} + engines: {node: '>=20'} + + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + oxc-parser@0.135.0: + resolution: {integrity: sha512-/DaPStu0s2zzNSRRniKyTPM6Z/o+DapOp2JYNKDL8AsgaBGPK2IdZyB87SQjVH+xeQPz+Qr9mrjglfkYgtbVRA==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.21.3: + resolution: {integrity: sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==} + + oxfmt@0.55.0: + resolution: {integrity: sha512-jSj2wCTakwgPMxkfiVZX0jf+nX+Nz6xlyAZjqNE0qXTFdCBPYlP6JAN+ODjmealw7DXBjOzYbdsqwBMAZnPZ6A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint-tsgolint@0.23.0: + resolution: {integrity: sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==} + hasBin: true + + oxlint@1.70.0: + resolution: {integrity: sha512-D6JgHtzkhRwvEC+A0Nw5AEc5bk8x5i1pHzvZIEf/a0C4hOzmAACNGtkDGPyFaxxX3ZVGxCPeig3P3rMM8XU3/g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pinia-plugin-persistedstate@4.7.1: + resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: ^3.0.4 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@3.0.4: + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} + peerDependencies: + typescript: '>=4.5.0' + vue: ^3.5.38 + peerDependenciesMeta: + typescript: + optional: true + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + + playwright-core@1.61.0: + resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.0: + resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} + engines: {node: '>=18'} + hasBin: true + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pnpm-workspace-yaml@1.6.1: + resolution: {integrity: sha512-yTeZntGWi8m9WNuhoVsP0DpFc4sC1U0+rr/qR6Zi9n2g3sxXY+JfccjXjjruNz96tM8I09yaJUA86doRnNLkbg==} + + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + + popmotion@11.0.5: + resolution: {integrity: sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-html@1.8.1: + resolution: {integrity: sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==} + engines: {node: ^12 || >=14} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} + + postcss-sorting@10.0.0: + resolution: {integrity: sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w==} + peerDependencies: + postcss: ^8.4.20 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-bytes@7.1.0: + resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} + engines: {node: '>=20'} + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + prosemirror-changeset@2.4.1: + resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.4.1: + resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-model@1.25.9: + resolution: {integrity: sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} + + prosemirror-transform@1.12.0: + resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==} + + prosemirror-view@1.41.9: + resolution: {integrity: sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + publint@0.3.21: + resolution: {integrity: sha512-OqejcnMV6E9zel2oCrUOJEiiFkGiAAni0A6ibfQNh1k9Gu5z4F+Yso8lllam7AzmV6Do0vp7u3UpZNRBwuXaHQ==} + engines: {node: '>=18'} + hasBin: true + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.3.0: + resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==} + engines: {node: '>=12.20'} + + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + engines: {node: '>=0.6'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rc9@3.0.1: + resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdir-glob@3.0.0: + resolution: {integrity: sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==} + engines: {node: '>=18'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + registry-auth-token@5.1.1: + resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.2: + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + hasBin: true + + reka-ui@2.10.0: + resolution: {integrity: sha512-HIUVfSBM/AyGkcUI7aiOxxMc4N+0UD2ZEun8dcrT0H4fveotEoeDdvzyZu97eeEvEa1H9oGHoOpApkfxlgnC7g==} + peerDependencies: + vue: ^3.5.38 + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@6.1.3: + resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} + engines: {node: 20 || >=22} + hasBin: true + + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + + rolldown-plugin-dts@0.26.0: + resolution: {integrity: sha512-e+kEPtUiDES0htk5iqkSeF4EzAV7R+vugGB44iPDuw1Kw9E+WyL1VG7PaV0IIjGHLiacztMBcMTyrr8ON9CT1Q==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20260325.1' + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0-rc.17: + resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.1.2: + resolution: {integrity: sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup-plugin-visualizer@7.0.1: + resolution: {integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==} + engines: {node: '>=22'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta || ^1.0.0-rc + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-embedded-all-unknown@1.100.0: + resolution: {integrity: sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==} + cpu: ['!arm', '!arm64', '!riscv64', '!x64'] + + sass-embedded-android-arm64@1.100.0: + resolution: {integrity: sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [android] + + sass-embedded-android-arm@1.100.0: + resolution: {integrity: sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [android] + + sass-embedded-android-riscv64@1.100.0: + resolution: {integrity: sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [android] + + sass-embedded-android-x64@1.100.0: + resolution: {integrity: sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [android] + + sass-embedded-darwin-arm64@1.100.0: + resolution: {integrity: sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [darwin] + + sass-embedded-darwin-x64@1.100.0: + resolution: {integrity: sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [darwin] + + sass-embedded-linux-arm64@1.100.0: + resolution: {integrity: sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + libc: glibc + + sass-embedded-linux-arm@1.100.0: + resolution: {integrity: sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + libc: glibc + + sass-embedded-linux-musl-arm64@1.100.0: + resolution: {integrity: sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + libc: musl + + sass-embedded-linux-musl-arm@1.100.0: + resolution: {integrity: sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + libc: musl + + sass-embedded-linux-musl-riscv64@1.100.0: + resolution: {integrity: sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + libc: musl + + sass-embedded-linux-musl-x64@1.100.0: + resolution: {integrity: sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + libc: musl + + sass-embedded-linux-riscv64@1.100.0: + resolution: {integrity: sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + libc: glibc + + sass-embedded-linux-x64@1.100.0: + resolution: {integrity: sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + libc: glibc + + sass-embedded-unknown-all@1.100.0: + resolution: {integrity: sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==} + os: ['!android', '!darwin', '!linux', '!win32'] + + sass-embedded-win32-arm64@1.100.0: + resolution: {integrity: sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [win32] + + sass-embedded-win32-x64@1.100.0: + resolution: {integrity: sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [win32] + + sass-embedded@1.100.0: + resolution: {integrity: sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==} + engines: {node: '>=16.0.0'} + hasBin: true + + sass@1.100.0: + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + engines: {node: '>=20.19.0'} + hasBin: true + + sass@1.101.0: + resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + engines: {node: '>=20.19.0'} + hasBin: true + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + secure-ls@2.0.0: + resolution: {integrity: sha512-Wgtnw0QSm0v7gVKv11nOoeyGS65EThGXnBB7jfd4IhZd2eq3B4AMPcXAL5qJ1h55+Qolun7TONTwX7H5m6e2pQ==} + engines: {node: '>=8.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serialize-javascript@7.0.6: + resolution: {integrity: sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==} + engines: {node: '>=20.0.0'} + + serve-placeholder@2.0.2: + resolution: {integrity: sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + smob@1.6.2: + resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} + engines: {node: '>=20.0.0'} + + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} + + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + engines: {node: '>=10.0.0'} + + sortablejs@1.15.7: + resolution: {integrity: sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + engines: {node: '>=18'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + streamx@2.28.0: + resolution: {integrity: sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + engines: {node: '>=20'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + + style-mod@4.1.3: + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + style-value-types@5.1.2: + resolution: {integrity: sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==} + + stylelint-config-html@1.1.0: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recess-order@7.7.0: + resolution: {integrity: sha512-TWRkg+BrwHOki4pi9y1emWgx6pFwZXOYZhNsbEObke/mzYUJVJvDEzJJQXhH7ajslQJGcrExy8ZvJqDiUbhFpA==} + peerDependencies: + stylelint: ^16.18.0 || ^17.0.0 + stylelint-order: ^7.0.0 || ^8.0.0 + + stylelint-config-recommended-scss@17.0.1: + resolution: {integrity: sha512-x5DVehzJudcwF0od3sGpgkln2PLLranFE7twwbp7dqDINCyZvwzFkMc6TLhNOvazRiVBJYATQLouJY0xPGB8WA==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^17.0.0 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-recommended-vue@1.6.1: + resolution: {integrity: sha512-lLW7hTIMBiTfjenGuDq2kyHA6fBWd/+Df7MO4/AWOxiFeXP9clbpKgg27kHfwA3H7UNMGC7aeP3mNlZB5LMmEQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recommended@18.0.0: + resolution: {integrity: sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-config-standard@40.0.0: + resolution: {integrity: sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-order@8.1.1: + resolution: {integrity: sha512-LqsEB6VggJuu5v10RtkrQsBObcdwBE7GuAOlwfc/LR3VL/w8UqKX2BOLIjhyGt0Gne/njo7gRNGiJAKhfmPMNw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^16.18.0 || ^17.0.0 + + stylelint-scss@7.2.0: + resolution: {integrity: sha512-6E79Bachv0Iz0gqRUZgdqdXCsiq26DWBWIBNHYtjTmAp3wJu6cp/I37VfW7BPntmh2puF3bY09XWl4HZGrLhzw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^16.8.2 || ^17.0.0 + + stylelint@17.13.0: + resolution: {integrity: sha512-G1WYzMerp7ihOaIe9VJCHLt12MoAD2QLf1AFerYP37+BCRBUK5UCpq8e/mN+zCIaJPKQcaxhE4WlPmqdiOx/gw==} + engines: {node: '>=20.19.0'} + hasBin: true + + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@4.5.0: + resolution: {integrity: sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==} + engines: {node: '>=20'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + sver@2.0.1: + resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + svgo@4.0.1: + resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} + engines: {node: '>=16'} + hasBin: true + + sync-child-process@1.0.2: + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + engines: {node: '>=16.0.0'} + + sync-message-port@1.2.0: + resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==} + engines: {node: '>=16.0.0'} + + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tailwind-csstree@0.3.3: + resolution: {integrity: sha512-je9J5UYRsTJqAjYrIBMMlge8T/rreRd44pJxgG5Zx/zeo4kAC/liUKqzztRZrGlYRJLvIf2Cb1DVJMTXSzEShA==} + engines: {node: '>=18.18'} + peerDependencies: + '@eslint/css': '>=1.0.0' + peerDependenciesMeta: + '@eslint/css': + optional: true + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tar-stream@3.2.0: + resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==} + + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + engines: {node: '>=18'} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + terser@5.48.0: + resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + engines: {node: '>=10'} + hasBin: true + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + + theme-colors@0.1.0: + resolution: {integrity: sha512-6gTEHQqWlQNiOEGHCSSQmU//E5SnXHJ4H7oHQOD8x77CvNYNQAmt73dqR71mzw5ULV87zaHLxK5pIBnsToFuZw==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyclip@0.1.14: + resolution: {integrity: sha512-F1oWdz8tjT17qe1d5JgDK6z03WGOhYYAN0lK3/D/fzNiy93xswLLEw7pk+3g05onhAy6Bsc6PLNUGhdgVjemMQ==} + engines: {node: ^16.14.0 || >= 17.3.0} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tsdown@0.22.3: + resolution: {integrity: sha512-louqbfA8Qf//B9jTTL0FPtXTNpjCWv1VPkbcmQMph2pTpzs+LnB1tbe4tDDRVpo2BjF5SgUXaTZe45SxB8pWHg==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.3 + '@tsdown/exe': 0.22.3 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo@2.9.18: + resolution: {integrity: sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg==} + hasBin: true + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-fest@5.7.0: + resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} + engines: {node: '>=20'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + unbash@4.0.1: + resolution: {integrity: sha512-1ajSo3813sDoVIHx4inJdUS4l5L2ic5cFiddemPiyjb/PZEoBAhFwHtbaEdRDFxbAKy7FCG7s5ww3/uCFawuIA==} + engines: {node: '>=14'} + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unctx@2.5.0: + resolution: {integrity: sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==} + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + + unimport@6.3.0: + resolution: {integrity: sha512-M+Dxk5W9WRd+8j56W9tp8lGW/dmMc7g5zj7BWQnEjKQhryBstqsi1V0izb0zHwSkEN8cSYV7K75/bykairV2tA==} + engines: {node: '>=18.12.0'} + peerDependencies: + oxc-parser: '*' + rolldown: ^1.0.0 + peerDependenciesMeta: + oxc-parser: + optional: true + rolldown: + optional: true + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unplugin-dts@1.0.2: + resolution: {integrity: sha512-VbNiMD0LMl/t6nJueGtrCp79N7ZO1nquxj/FUybJDnKwZGsnW2wjdwBSzA3QEHujoxmxZIptsG43hL7LzXE96w==} + peerDependencies: + '@microsoft/api-extractor': '>=7' + '@rspack/core': ^1 + '@vue/language-core': ~3.1.5 + esbuild: '*' + rolldown: '*' + rollup: '>=3' + typescript: '>=4' + vite: '>=3' + webpack: ^4 || ^5 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@rspack/core': + optional: true + '@vue/language-core': + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + + unplugin-utils@0.3.1: + resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} + engines: {node: '>=20.19.0'} + + unplugin-vue@7.2.0: + resolution: {integrity: sha512-6JHWcRbLO3bySOsipQSW9n2VRoPqsx9GLSWBfp7QLPCvpUr/edeAXR7pSl44EEJ+FIp9bG8Zm+xm5dnM/yir4Q==} + engines: {node: '>=20.19.0'} + peerDependencies: + vue: ^3.5.38 + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + + unplugin@3.0.0: + resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} + engines: {node: ^20.19.0 || >=22.12.0} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + untun@0.1.3: + resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} + hasBin: true + + untyped@2.0.0: + resolution: {integrity: sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==} + hasBin: true + + unwasm@0.5.3: + resolution: {integrity: sha512-keBgTSfp3r6+s9ZcSma+0chwxQdmLbB5+dAD9vjtB21UTMYuKAxHXCU1K2CbCtnP09EaWeRvACnXk0EJtUx+hw==} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} + engines: {node: '>=18'} + + uqr@0.1.3: + resolution: {integrity: sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + + valibot@1.4.1: + resolution: {integrity: sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + + vee-validate@4.15.1: + resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==} + peerDependencies: + vue: ^3.5.38 + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-dev-rpc@2.0.0: + resolution: {integrity: sha512-yKwbTwdHKSD2k/aGqyWpPHepo45OQc8lH3/6IfT4ZqeKE26ooKvi4WIEKzqWav8v+9Is8u1k8q54hvOmqASazA==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 || ^8.0.0 + + vite-hot-client@2.2.0: + resolution: {integrity: sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==} + peerDependencies: + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0 + + vite-plugin-compression@0.5.1: + resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-inspect@11.4.1: + resolution: {integrity: sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^6.0.0 || ^7.0.0-0 || ^8.0.0-0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + vite-plugin-lazy-import@1.0.7: + resolution: {integrity: sha512-mE6oAObOb4wqso4AoUGi9cLjdR+4vay1RCaKJvziBuFPlziZl7J0aw2hsqRTokLVRx3bli0a0VyjMOwsNDv58A==} + + vite-plugin-pwa@1.3.0: + resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + vite-plugin-vue-devtools@8.1.3: + resolution: {integrity: sha512-KBTUhbTXvY+GsCdShnCHG4WdijEV74KIDxhF8erfSs5g5mS13g/cPRUf4mLpD10qr5FqHYosNt0j6rP5kpiS1Q==} + engines: {node: '>=v14.21.3'} + peerDependencies: + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + + vite-plugin-vue-inspector@6.0.0: + resolution: {integrity: sha512-OpyITJLgZNibxlrik1EmRtvXHDjLRxNPsWkGFTERZs2LgMEdG4W0WoFt5GIgp3a3jRou+eJR8U1zOBk/XQgEbw==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + vite@8.0.10: + resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-codemirror6@1.5.2: + resolution: {integrity: sha512-SJRW0r8776zdqIVSZZsHuTXErmql1PKATupN+RPs4IXSTAzzKl4Sl/804BazVb9OYrd2Ym5Yv97AmwPRVD4zeg==} + engines: {node: ^20.19.0 || >=22.12.0, pnpm: '>=10.3.0'} + peerDependencies: + '@codemirror/autocomplete': ^6.0.0 + '@codemirror/commands': ^6.0.0 + '@codemirror/language': ^6.0.0 + '@codemirror/lint': ^6.0.0 + '@codemirror/search': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + codemirror: ^6.0.0 + style-mod: ^4.0.0 + vue: ^3.5.38 + + vue-component-type-helpers@3.3.5: + resolution: {integrity: sha512-Fe1jyPJoUGpJOYKOri44jduR7My4yYINOMJISuMAbmrs+L5LbIDUc8NTWZYY3EJLK0yPLuCmcd5zoCsE4k2/KA==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.5.38 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-eslint-parser@10.4.1: + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + + vue-i18n@11.4.6: + resolution: {integrity: sha512-l0gE7Rfy0phCa5ChKYkOq543Wgd39BCK6hkktfr1Ed4D99oRkgPK9ffShASZdeC8OJxGfdWmpYoAaAH6iLEuIg==} + engines: {node: '>= 22'} + peerDependencies: + vue: ^3.5.38 + + vue-json-pretty@2.6.0: + resolution: {integrity: sha512-glz1aBVS35EO8+S9agIl3WOQaW2cJZW192UVKTuGmryx01ZvOVWc4pR3t+5UcyY4jdOfBUgVHjcpRpcnjRhCAg==} + engines: {node: '>= 10.0.0', npm: '>= 5.0.0'} + peerDependencies: + vue: ^3.5.38 + + vue-router@5.1.0: + resolution: {integrity: sha512-HAbiLzLEHQwxPgvsbOJDAwtavszEgLwri6XfyrsPECIFez8+59xc9LofWVdc/HEaSRT822lJ8H9Ns38VVond5g==} + peerDependencies: + '@pinia/colada': '>=0.21.2' + '@vue/compiler-sfc': ^3.5.34 + pinia: ^3.0.4 + vite: ^7.0.0 || ^8.0.0 + vue: ^3.5.38 + peerDependenciesMeta: + '@pinia/colada': + optional: true + '@vue/compiler-sfc': + optional: true + pinia: + optional: true + vite: + optional: true + + vue-tippy@6.7.1: + resolution: {integrity: sha512-gdHbBV5/Vc8gH87hQHLA7TN1K4BlLco3MAPrTb70ZYGXxx+55rAU4a4mt0fIoP+gB3etu1khUZ6c29Br1n0CiA==} + peerDependencies: + vue: ^3.5.38 + + vue-tsc@3.3.5: + resolution: {integrity: sha512-Rzh/G2MmNlMSAMTiQEjDrsb4dgB/jbtEM47rVN2NtidF1dfb/q4w4QvpQBtW5+y3y5H27Hjh7deVwk+YB02fNg==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.38: + resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vxe-pc-ui@4.15.4: + resolution: {integrity: sha512-5mSLy8z0XNAgR7RYxqM2v/JRnGK+G1ltXindJEwu9daCQqd9rqqLPdPh12TGBeeNebLLKWn7aVa6RqVg5JWh6A==} + + vxe-table@4.19.17: + resolution: {integrity: sha512-zbuEdFbw6Pt6hsULmSW9VyJPlz/w6elGx3mmstWAI/GAiFO3zD391/s3ViJMvTD5Styyr7pzystv++elNjDPFg==} + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + + watermark-js-plus@1.6.3: + resolution: {integrity: sha512-iCLOGf70KacIwjGF9MDViYxQcRiVwOH7l42qDHLeE2HeUsQD1EQuUC9cKRG/4SErTUmdqV3yf5WnKk2dRARHPQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workbox-background-sync@7.4.1: + resolution: {integrity: sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==} + + workbox-broadcast-update@7.4.1: + resolution: {integrity: sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==} + + workbox-build@7.4.1: + resolution: {integrity: sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==} + engines: {node: '>=20.0.0'} + + workbox-cacheable-response@7.4.1: + resolution: {integrity: sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==} + + workbox-core@7.4.1: + resolution: {integrity: sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==} + + workbox-expiration@7.4.1: + resolution: {integrity: sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==} + + workbox-google-analytics@7.4.1: + resolution: {integrity: sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==} + + workbox-navigation-preload@7.4.1: + resolution: {integrity: sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==} + + workbox-precaching@7.4.1: + resolution: {integrity: sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==} + + workbox-range-requests@7.4.1: + resolution: {integrity: sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==} + + workbox-recipes@7.4.1: + resolution: {integrity: sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==} + + workbox-routing@7.4.1: + resolution: {integrity: sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==} + + workbox-strategies@7.4.1: + resolution: {integrity: sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==} + + workbox-streams@7.4.1: + resolution: {integrity: sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==} + + workbox-sw@7.4.1: + resolution: {integrity: sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==} + + workbox-window@7.4.1: + resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xe-utils@3.9.1: + resolution: {integrity: sha512-Ujk5UmoH6Iaqhgz3oGwfCXVcMdUJKlXnfvLABdnMyseMG0eHsX2mcCvLd/8sGlIXtfwsprI9bW7vgcVognLmqQ==} + + xe-utils@4.0.9: + resolution: {integrity: sha512-LLO3Otzg2i9ArUoBdsjse9Tw7cwE0XvvoP98IMjoqL8l4UELO/a8c041Mq9cAfaUdd6ksamSvGF5luZkwVArqw==} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xss@1.0.15: + resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} + engines: {node: '>= 0.10.0'} + hasBin: true + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml-eslint-parser@1.3.2: + resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml-eslint-parser@2.0.0: + resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.1: + resolution: {integrity: sha512-mxW3qiSnl+GRxXsaUMzv2Mbada1Y8CDltET9UxejDQe6DBYlSekghl5U5K0ReAikcHDi0G1vKZEmmo/NWAGKLA==} + + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + + zip-stream@7.0.5: + resolution: {integrity: sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==} + engines: {node: '>=18'} + + zod-defaults@0.1.3: + resolution: {integrity: sha512-Cp4UjuVfXqwaOx8A5LlM0IRBZs8B7AXgF+XOZWF8CjMDhsY/Jki+y7VS4adVtQj9NNSsAPwylUlJp9INuxPEnA==} + peerDependencies: + zod: ^3.23.8 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zrender@6.1.0: + resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + + zx@8.8.5: + resolution: {integrity: sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==} + engines: {node: '>= 12.17.0'} + hasBin: true + +snapshots: + + '@ag-ui/core@0.0.53': + dependencies: + zod: 3.25.76 + + '@ant-design/colors@7.2.1': + dependencies: + '@ant-design/fast-color': 2.0.6 + + '@ant-design/colors@8.0.1': + dependencies: + '@ant-design/fast-color': 3.0.1 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.29.7 + + '@ant-design/fast-color@3.0.1': {} + + '@ant-design/icons-svg@4.4.2': {} + + '@antdv-next/cssinjs@1.0.6(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + csstype: 3.2.3 + stylis: 4.4.0 + vue: 3.5.38(typescript@6.0.3) + + '@antdv-next/icons@1.0.8(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/icons-svg': 4.4.2 + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@antdv-next/x-markdown@0.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + dompurify: 3.4.11 + katex: 0.16.47 + marked: 12.0.2 + vue: 3.5.38(typescript@6.0.3) + + '@antdv-next/x-sdk@0.0.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + vue: 3.5.38(typescript@6.0.3) + + '@antdv-next/x@1.1.1(antdv-next@1.3.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@ant-design/fast-color': 3.0.1 + '@antdv-next/cssinjs': 1.0.6(vue@3.5.38(typescript@6.0.3)) + '@antdv-next/icons': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + '@vueuse/core': 14.3.0(vue@3.5.38(typescript@6.0.3)) + antdv-next: 1.3.6(vue@3.5.38(typescript@6.0.3)) + dayjs: 1.11.21 + mermaid: 11.15.0 + shiki: 3.23.0 + vue: 3.5.38(typescript@6.0.3) + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.2.4 + + '@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)': + dependencies: + ajv: 8.20.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@ast-grep/napi-darwin-arm64@0.43.0': + optional: true + + '@ast-grep/napi-darwin-x64@0.43.0': + optional: true + + '@ast-grep/napi-linux-arm64-gnu@0.43.0': + optional: true + + '@ast-grep/napi-linux-arm64-musl@0.43.0': + optional: true + + '@ast-grep/napi-linux-x64-gnu@0.43.0': + optional: true + + '@ast-grep/napi-linux-x64-musl@0.43.0': + optional: true + + '@ast-grep/napi-win32-arm64-msvc@0.43.0': + optional: true + + '@ast-grep/napi-win32-ia32-msvc@0.43.0': + optional: true + + '@ast-grep/napi-win32-x64-msvc@0.43.0': + optional: true + + '@ast-grep/napi@0.43.0': + optionalDependencies: + '@ast-grep/napi-darwin-arm64': 0.43.0 + '@ast-grep/napi-darwin-x64': 0.43.0 + '@ast-grep/napi-linux-arm64-gnu': 0.43.0 + '@ast-grep/napi-linux-arm64-musl': 0.43.0 + '@ast-grep/napi-linux-x64-gnu': 0.43.0 + '@ast-grep/napi-linux-x64-musl': 0.43.0 + '@ast-grep/napi-win32-arm64-msvc': 0.43.0 + '@ast-grep/napi-win32-ia32-msvc': 0.43.0 + '@ast-grep/napi-win32-x64-msvc': 0.43.0 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/generator@8.0.0': + dependencies: + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-string-parser@8.0.0': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-identifier@8.0.2': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/parser@8.0.0': + dependencies: + '@babel/types': 8.0.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.7 + esutils: 2.0.3 + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@babel/types@8.0.0': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + + '@braintree/sanitize-url@7.1.2': {} + + '@bufbuild/protobuf@2.12.0': {} + + '@cacheable/memory@2.0.9': + dependencies: + '@cacheable/utils': 2.4.1 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.4.1': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + '@changesets/apply-release-plan@7.1.1': + dependencies: + '@changesets/config': 3.1.4 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.8.5 + + '@changesets/assemble-release-plan@6.0.10': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.8.5 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/changelog-github@0.7.0': + dependencies: + '@changesets/get-github-info': 0.8.0 + '@changesets/types': 6.1.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding + + '@changesets/cli@2.31.0(@types/node@25.9.4)': + dependencies: + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@25.9.4) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.8.5 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.4': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.8.5 + + '@changesets/get-github-info@0.8.0': + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@changesets/get-release-plan@4.0.16': + dependencies: + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.2.0 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.2.0 + prettier: 2.8.8 + + '@chevrotain/types@11.1.2': {} + + '@clack/core@1.4.2': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.6.0': + dependencies: + '@clack/core': 1.4.2 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@cloudflare/kv-asset-handler@0.4.2': {} + + '@codemirror/autocomplete@6.20.3': + dependencies: + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + + '@codemirror/commands@6.10.3': + dependencies: + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + + '@codemirror/lang-angular@0.1.4': + dependencies: + '@codemirror/lang-html': 6.4.11 + '@codemirror/lang-javascript': 6.2.5 + '@codemirror/language': 6.12.3 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-cpp@6.0.3': + dependencies: + '@codemirror/language': 6.12.3 + '@lezer/cpp': 1.1.6 + + '@codemirror/lang-css@6.3.1': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/css': 1.3.3 + + '@codemirror/lang-go@6.0.1': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/go': 1.0.1 + + '@codemirror/lang-html@6.4.11': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-javascript': 6.2.5 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/css': 1.3.3 + '@lezer/html': 1.3.13 + + '@codemirror/lang-java@6.0.2': + dependencies: + '@codemirror/language': 6.12.3 + '@lezer/java': 1.1.3 + + '@codemirror/lang-javascript@6.2.5': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/javascript': 1.5.4 + + '@codemirror/lang-jinja@6.0.1': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-html': 6.4.11 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-json@6.0.2': + dependencies: + '@codemirror/language': 6.12.3 + '@lezer/json': 1.0.3 + + '@codemirror/lang-less@6.0.2': + dependencies: + '@codemirror/lang-css': 6.3.1 + '@codemirror/language': 6.12.3 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-liquid@6.3.2': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-html': 6.4.11 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-markdown@6.5.0': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-html': 6.4.11 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/markdown': 1.6.4 + + '@codemirror/lang-php@6.0.2': + dependencies: + '@codemirror/lang-html': 6.4.11 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/php': 1.0.5 + + '@codemirror/lang-python@6.2.1': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/python': 1.1.19 + + '@codemirror/lang-rust@6.0.2': + dependencies: + '@codemirror/language': 6.12.3 + '@lezer/rust': 1.0.2 + + '@codemirror/lang-sass@6.0.2': + dependencies: + '@codemirror/lang-css': 6.3.1 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/sass': 1.1.0 + + '@codemirror/lang-sql@6.10.0': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-vue@0.1.3': + dependencies: + '@codemirror/lang-html': 6.4.11 + '@codemirror/lang-javascript': 6.2.5 + '@codemirror/language': 6.12.3 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-wast@6.0.2': + dependencies: + '@codemirror/language': 6.12.3 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@codemirror/lang-xml@6.1.0': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/xml': 1.0.6 + + '@codemirror/lang-yaml@6.1.3': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + '@lezer/yaml': 1.0.4 + + '@codemirror/language-data@6.5.2': + dependencies: + '@codemirror/lang-angular': 0.1.4 + '@codemirror/lang-cpp': 6.0.3 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-go': 6.0.1 + '@codemirror/lang-html': 6.4.11 + '@codemirror/lang-java': 6.0.2 + '@codemirror/lang-javascript': 6.2.5 + '@codemirror/lang-jinja': 6.0.1 + '@codemirror/lang-json': 6.0.2 + '@codemirror/lang-less': 6.0.2 + '@codemirror/lang-liquid': 6.3.2 + '@codemirror/lang-markdown': 6.5.0 + '@codemirror/lang-php': 6.0.2 + '@codemirror/lang-python': 6.2.1 + '@codemirror/lang-rust': 6.0.2 + '@codemirror/lang-sass': 6.0.2 + '@codemirror/lang-sql': 6.10.0 + '@codemirror/lang-vue': 0.1.3 + '@codemirror/lang-wast': 6.0.2 + '@codemirror/lang-xml': 6.1.0 + '@codemirror/lang-yaml': 6.1.3 + '@codemirror/language': 6.12.3 + '@codemirror/legacy-modes': 6.5.3 + + '@codemirror/language@6.12.3': + dependencies: + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + style-mod: 4.1.3 + + '@codemirror/legacy-modes@6.5.3': + dependencies: + '@codemirror/language': 6.12.3 + + '@codemirror/lint@6.9.7': + dependencies: + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + crelt: 1.0.6 + + '@codemirror/search@6.7.1': + dependencies: + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + crelt: 1.0.6 + + '@codemirror/state@6.6.0': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/theme-one-dark@6.1.3': + dependencies: + '@codemirror/language': 6.12.3 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/highlight': 1.2.3 + + '@codemirror/view@6.43.1': + dependencies: + '@codemirror/state': 6.6.0 + crelt: 1.0.6 + style-mod: 4.1.3 + w3c-keyname: 2.2.8 + + '@commitlint/cli@21.0.2(@types/node@25.9.4)(conventional-commits-parser@6.4.0)(typescript@6.0.3)': + dependencies: + '@commitlint/format': 21.0.1 + '@commitlint/lint': 21.0.2 + '@commitlint/load': 21.0.2(@types/node@25.9.4)(typescript@6.0.3) + '@commitlint/read': 21.0.2(conventional-commits-parser@6.4.0) + '@commitlint/types': 21.0.1 + tinyexec: 1.2.4 + yargs: 18.0.0 + transitivePeerDependencies: + - '@types/node' + - conventional-commits-filter + - conventional-commits-parser + - typescript + + '@commitlint/config-conventional@21.0.2': + dependencies: + '@commitlint/types': 21.0.1 + conventional-changelog-conventionalcommits: 9.3.1 + + '@commitlint/config-validator@21.0.1': + dependencies: + '@commitlint/types': 21.0.1 + ajv: 8.20.0 + + '@commitlint/ensure@21.0.1': + dependencies: + '@commitlint/types': 21.0.1 + es-toolkit: 1.47.1 + + '@commitlint/execute-rule@21.0.1': {} + + '@commitlint/format@21.0.1': + dependencies: + '@commitlint/types': 21.0.1 + picocolors: 1.1.1 + + '@commitlint/is-ignored@21.0.2': + dependencies: + '@commitlint/types': 21.0.1 + semver: 7.8.5 + + '@commitlint/lint@21.0.2': + dependencies: + '@commitlint/is-ignored': 21.0.2 + '@commitlint/parse': 21.0.2 + '@commitlint/rules': 21.0.2 + '@commitlint/types': 21.0.1 + + '@commitlint/load@21.0.2(@types/node@25.9.4)(typescript@6.0.3)': + dependencies: + '@commitlint/config-validator': 21.0.1 + '@commitlint/execute-rule': 21.0.1 + '@commitlint/resolve-extends': 21.0.1 + '@commitlint/types': 21.0.1 + cosmiconfig: 9.0.2(typescript@6.0.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@25.9.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3) + es-toolkit: 1.47.1 + is-plain-obj: 4.1.0 + picocolors: 1.1.1 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@21.0.2': {} + + '@commitlint/parse@21.0.2': + dependencies: + '@commitlint/types': 21.0.1 + conventional-changelog-angular: 8.3.1 + conventional-commits-parser: 6.4.0 + + '@commitlint/read@21.0.2(conventional-commits-parser@6.4.0)': + dependencies: + '@commitlint/top-level': 21.0.2 + '@commitlint/types': 21.0.1 + git-raw-commits: 5.0.1(conventional-commits-parser@6.4.0) + tinyexec: 1.2.4 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser + + '@commitlint/resolve-extends@21.0.1': + dependencies: + '@commitlint/config-validator': 21.0.1 + '@commitlint/types': 21.0.1 + es-toolkit: 1.47.1 + global-directory: 5.0.0 + resolve-from: 5.0.0 + + '@commitlint/rules@21.0.2': + dependencies: + '@commitlint/ensure': 21.0.1 + '@commitlint/message': 21.0.2 + '@commitlint/to-lines': 21.0.1 + '@commitlint/types': 21.0.1 + + '@commitlint/to-lines@21.0.1': {} + + '@commitlint/top-level@21.0.2': + dependencies: + escalade: 3.2.0 + + '@commitlint/types@21.0.1': + dependencies: + conventional-commits-parser: 6.4.0 + picocolors: 1.1.1 + + '@conventional-changelog/git-client@2.7.0(conventional-commits-parser@6.4.0)': + dependencies: + '@simple-libs/child-process-utils': 1.0.2 + '@simple-libs/stream-utils': 1.2.0 + semver: 7.8.5 + optionalDependencies: + conventional-commits-parser: 6.4.0 + + '@cspell/cspell-bundled-dicts@10.0.1': + dependencies: + '@cspell/dict-ada': 4.1.1 + '@cspell/dict-al': 1.1.1 + '@cspell/dict-aws': 4.0.17 + '@cspell/dict-bash': 4.2.3 + '@cspell/dict-companies': 3.2.11 + '@cspell/dict-cpp': 7.0.2 + '@cspell/dict-cryptocurrencies': 5.0.5 + '@cspell/dict-csharp': 4.0.8 + '@cspell/dict-css': 4.1.2 + '@cspell/dict-dart': 2.3.2 + '@cspell/dict-data-science': 2.0.14 + '@cspell/dict-django': 4.1.6 + '@cspell/dict-docker': 1.1.17 + '@cspell/dict-dotnet': 5.0.13 + '@cspell/dict-elixir': 4.0.8 + '@cspell/dict-en-common-misspellings': 2.1.12 + '@cspell/dict-en-gb-mit': 3.1.24 + '@cspell/dict-en_us': 4.4.35 + '@cspell/dict-filetypes': 3.0.18 + '@cspell/dict-flutter': 1.1.1 + '@cspell/dict-fonts': 4.0.6 + '@cspell/dict-fsharp': 1.1.1 + '@cspell/dict-fullstack': 3.2.9 + '@cspell/dict-gaming-terms': 1.1.2 + '@cspell/dict-git': 3.1.0 + '@cspell/dict-golang': 6.0.26 + '@cspell/dict-google': 1.0.9 + '@cspell/dict-haskell': 4.0.6 + '@cspell/dict-html': 4.0.15 + '@cspell/dict-html-symbol-entities': 4.0.5 + '@cspell/dict-java': 5.0.12 + '@cspell/dict-julia': 1.1.1 + '@cspell/dict-k8s': 1.0.12 + '@cspell/dict-kotlin': 1.1.1 + '@cspell/dict-latex': 5.1.0 + '@cspell/dict-lorem-ipsum': 4.0.5 + '@cspell/dict-lua': 4.0.8 + '@cspell/dict-makefile': 1.0.5 + '@cspell/dict-markdown': 2.0.17(@cspell/dict-css@4.1.2)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.15)(@cspell/dict-typescript@3.2.3) + '@cspell/dict-monkeyc': 1.0.12 + '@cspell/dict-node': 5.0.9 + '@cspell/dict-npm': 5.2.41 + '@cspell/dict-php': 4.1.1 + '@cspell/dict-powershell': 5.0.15 + '@cspell/dict-public-licenses': 2.0.16 + '@cspell/dict-python': 4.2.27 + '@cspell/dict-r': 2.1.1 + '@cspell/dict-ruby': 5.1.1 + '@cspell/dict-rust': 4.1.2 + '@cspell/dict-scala': 5.0.9 + '@cspell/dict-shell': 1.2.0 + '@cspell/dict-software-terms': 5.2.2 + '@cspell/dict-sql': 2.2.1 + '@cspell/dict-svelte': 1.0.7 + '@cspell/dict-swift': 2.0.6 + '@cspell/dict-terraform': 1.1.3 + '@cspell/dict-typescript': 3.2.3 + '@cspell/dict-vue': 3.0.5 + '@cspell/dict-zig': 1.0.0 + + '@cspell/cspell-json-reporter@10.0.1': + dependencies: + '@cspell/cspell-types': 10.0.1 + + '@cspell/cspell-performance-monitor@10.0.1': {} + + '@cspell/cspell-pipe@10.0.1': {} + + '@cspell/cspell-resolver@10.0.1': + dependencies: + global-directory: 5.0.0 + + '@cspell/cspell-service-bus@10.0.1': {} + + '@cspell/cspell-types@10.0.1': {} + + '@cspell/cspell-worker@10.0.1': + dependencies: + cspell-lib: 10.0.1 + + '@cspell/dict-ada@4.1.1': {} + + '@cspell/dict-al@1.1.1': {} + + '@cspell/dict-aws@4.0.17': {} + + '@cspell/dict-bash@4.2.3': + dependencies: + '@cspell/dict-shell': 1.2.0 + + '@cspell/dict-companies@3.2.11': {} + + '@cspell/dict-cpp@7.0.2': {} + + '@cspell/dict-cryptocurrencies@5.0.5': {} + + '@cspell/dict-csharp@4.0.8': {} + + '@cspell/dict-css@4.1.2': {} + + '@cspell/dict-dart@2.3.2': {} + + '@cspell/dict-data-science@2.0.14': {} + + '@cspell/dict-django@4.1.6': {} + + '@cspell/dict-docker@1.1.17': {} + + '@cspell/dict-dotnet@5.0.13': {} + + '@cspell/dict-elixir@4.0.8': {} + + '@cspell/dict-en-common-misspellings@2.1.12': {} + + '@cspell/dict-en-gb-mit@3.1.24': {} + + '@cspell/dict-en_us@4.4.35': {} + + '@cspell/dict-filetypes@3.0.18': {} + + '@cspell/dict-flutter@1.1.1': {} + + '@cspell/dict-fonts@4.0.6': {} + + '@cspell/dict-fsharp@1.1.1': {} + + '@cspell/dict-fullstack@3.2.9': {} + + '@cspell/dict-gaming-terms@1.1.2': {} + + '@cspell/dict-git@3.1.0': {} + + '@cspell/dict-golang@6.0.26': {} + + '@cspell/dict-google@1.0.9': {} + + '@cspell/dict-haskell@4.0.6': {} + + '@cspell/dict-html-symbol-entities@4.0.5': {} + + '@cspell/dict-html@4.0.15': {} + + '@cspell/dict-java@5.0.12': {} + + '@cspell/dict-julia@1.1.1': {} + + '@cspell/dict-k8s@1.0.12': {} + + '@cspell/dict-kotlin@1.1.1': {} + + '@cspell/dict-latex@5.1.0': {} + + '@cspell/dict-lorem-ipsum@4.0.5': {} + + '@cspell/dict-lua@4.0.8': {} + + '@cspell/dict-makefile@1.0.5': {} + + '@cspell/dict-markdown@2.0.17(@cspell/dict-css@4.1.2)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.15)(@cspell/dict-typescript@3.2.3)': + dependencies: + '@cspell/dict-css': 4.1.2 + '@cspell/dict-html': 4.0.15 + '@cspell/dict-html-symbol-entities': 4.0.5 + '@cspell/dict-typescript': 3.2.3 + + '@cspell/dict-monkeyc@1.0.12': {} + + '@cspell/dict-node@5.0.9': {} + + '@cspell/dict-npm@5.2.41': {} + + '@cspell/dict-php@4.1.1': {} + + '@cspell/dict-powershell@5.0.15': {} + + '@cspell/dict-public-licenses@2.0.16': {} + + '@cspell/dict-python@4.2.27': + dependencies: + '@cspell/dict-data-science': 2.0.14 + + '@cspell/dict-r@2.1.1': {} + + '@cspell/dict-ruby@5.1.1': {} + + '@cspell/dict-rust@4.1.2': {} + + '@cspell/dict-scala@5.0.9': {} + + '@cspell/dict-shell@1.2.0': {} + + '@cspell/dict-software-terms@5.2.2': {} + + '@cspell/dict-sql@2.2.1': {} + + '@cspell/dict-svelte@1.0.7': {} + + '@cspell/dict-swift@2.0.6': {} + + '@cspell/dict-terraform@1.1.3': {} + + '@cspell/dict-typescript@3.2.3': {} + + '@cspell/dict-vue@3.0.5': {} + + '@cspell/dict-zig@1.0.0': {} + + '@cspell/dynamic-import@10.0.1': + dependencies: + '@cspell/url': 10.0.1 + import-meta-resolve: 4.2.0 + + '@cspell/filetypes@10.0.1': {} + + '@cspell/rpc@10.0.1': {} + + '@cspell/strong-weak-map@10.0.1': {} + + '@cspell/url@10.0.1': {} + + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/selector-resolve-nested@4.0.0(postcss-selector-parser@7.1.4)': + dependencies: + postcss-selector-parser: 7.1.4 + + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.4)': + dependencies: + postcss-selector-parser: 7.1.4 + + '@ctrl/tinycolor@4.2.0': {} + + '@cyberalien/svg-utils@1.2.15': + dependencies: + '@iconify/types': 2.0.0 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/core@1.11.0': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/hash@0.8.0': {} + + '@emotion/unitless@0.7.5': {} + + '@epic-web/invariant@1.0.0': {} + + '@es-joy/jsdoccomment@0.84.0': + dependencies: + '@types/estree': 1.0.9 + '@typescript-eslint/types': 8.61.1 + comment-parser: 1.4.5 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 7.1.1 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.5.0(jiti@2.7.0))': + dependencies: + escape-string-regexp: 4.0.0 + eslint: 10.5.0(jiti@2.7.0) + ignore: 7.0.5 + + '@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@2.7.0))': + dependencies: + eslint: 10.5.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.6.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/css-tree@4.0.4': + dependencies: + mdn-data: 2.28.1 + source-map-js: 1.2.1 + + '@eslint/js@10.0.1(eslint@10.5.0(jiti@2.7.0))': + optionalDependencies: + eslint: 10.5.0(jiti@2.7.0) + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.2': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/utils@0.2.11': {} + + '@floating-ui/vue@1.1.11(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@floating-ui/dom': 1.7.6 + '@floating-ui/utils': 0.2.11 + vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@iconify/json@2.2.488': + dependencies: + '@iconify/types': 2.0.0 + pathe: 2.0.3 + + '@iconify/tailwind4@1.2.3(tailwindcss@4.3.1)': + dependencies: + '@iconify/tools': 5.0.12 + '@iconify/types': 2.0.0 + '@iconify/utils': 3.1.3 + tailwindcss: 4.3.1 + + '@iconify/tools@5.0.12': + dependencies: + '@cyberalien/svg-utils': 1.2.15 + '@iconify/types': 2.0.0 + '@iconify/utils': 3.1.3 + fflate: 0.8.3 + modern-tar: 0.7.6 + pathe: 2.0.3 + svgo: 4.0.1 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.3': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + import-meta-resolve: 4.2.0 + + '@iconify/vue@5.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.38(typescript@6.0.3) + + '@inquirer/external-editor@1.0.3(@types/node@25.9.4)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 25.9.4 + + '@internationalized/date@3.12.2': + dependencies: + '@swc/helpers': 0.5.23 + + '@internationalized/number@3.6.7': + dependencies: + '@swc/helpers': 0.5.23 + + '@intlify/bundle-utils@11.2.4(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)))': + dependencies: + '@intlify/message-compiler': 11.4.6 + '@intlify/shared': 11.4.6 + acorn: 8.17.0 + esbuild: 0.25.12 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.2 + source-map-js: 1.2.1 + yaml-eslint-parser: 1.3.2 + optionalDependencies: + vue-i18n: 11.4.6(vue@3.5.38(typescript@6.0.3)) + + '@intlify/core-base@11.4.6': + dependencies: + '@intlify/devtools-types': 11.4.6 + '@intlify/message-compiler': 11.4.6 + '@intlify/shared': 11.4.6 + + '@intlify/devtools-types@11.4.6': + dependencies: + '@intlify/core-base': 11.4.6 + '@intlify/shared': 11.4.6 + + '@intlify/message-compiler@11.4.6': + dependencies: + '@intlify/shared': 11.4.6 + source-map-js: 1.2.1 + + '@intlify/shared@11.4.6': {} + + '@intlify/unplugin-vue-i18n@11.2.4(@vue/compiler-dom@3.5.38)(eslint@10.5.0(jiti@2.7.0))(rollup@4.62.2)(typescript@6.0.3)(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@intlify/bundle-utils': 11.2.4(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3))) + '@intlify/shared': 11.4.6 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.38)(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3)) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + debug: 4.4.3 + fast-glob: 3.3.3 + pathe: 2.0.3 + picocolors: 1.1.1 + unplugin: 2.3.11 + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue-i18n: 11.4.6(vue@3.5.38(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.4.6)(@vue/compiler-dom@3.5.38)(vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@babel/parser': 7.29.7 + optionalDependencies: + '@intlify/shared': 11.4.6 + '@vue/compiler-dom': 3.5.38 + vue: 3.5.38(typescript@6.0.3) + vue-i18n: 11.4.6(vue@3.5.38(typescript@6.0.3)) + + '@ioredis/commands@1.10.0': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/cliui@9.0.0': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jspm/fetch@0.1.0': {} + + '@jspm/generator@2.16.1': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@jspm/fetch': 0.1.0 + '@jspm/import-map': 1.5.0 + es-module-lexer: 2.1.0 + minimatch: 10.2.5 + pako: 2.1.0 + sver: 2.0.1 + tar-stream: 3.2.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + '@jspm/import-map@1.5.0': {} + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@lezer/common@1.5.2': {} + + '@lezer/cpp@1.1.6': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/css@1.3.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/go@1.0.1': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/highlight@1.2.3': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/html@1.3.13': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/java@1.1.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/javascript@1.5.4': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/json@1.0.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/lr@1.4.10': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/markdown@1.6.4': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + + '@lezer/php@1.0.5': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/python@1.1.19': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/rust@1.0.2': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/sass@1.1.0': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/xml@1.0.6': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/yaml@1.0.4': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lucide/vue@1.21.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + vue: 3.5.38(typescript@6.0.3) + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/find-root@3.1.0': + dependencies: + '@manypkg/tools': 2.1.2 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@manypkg/get-packages@3.1.0': + dependencies: + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 + + '@manypkg/tools@2.1.2': + dependencies: + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.0 + + '@mapbox/node-pre-gyp@2.0.3': + dependencies: + consola: 3.4.2 + detect-libc: 2.1.2 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.8.5 + tar: 7.5.16 + transitivePeerDependencies: + - encoding + - supports-color + + '@marijn/find-cluster-break@1.0.2': {} + + '@mermaid-js/parser@1.1.1': + dependencies: + '@chevrotain/types': 11.1.2 + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nuxt/kit@3.21.8(magicast@0.5.3)': + dependencies: + c12: 3.3.4(magicast@0.5.3) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + errx: 0.1.0 + exsolve: 1.0.8 + ignore: 7.0.5 + jiti: 2.7.0 + klona: 2.0.6 + knitwork: 1.3.0 + mlly: 1.8.2 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.1 + rc9: 3.0.1 + scule: 1.3.0 + semver: 7.8.5 + tinyglobby: 0.2.17 + ufo: 1.6.4 + unctx: 2.5.0 + untyped: 2.0.0 + transitivePeerDependencies: + - magicast + optional: true + + '@one-ini/wasm@0.1.1': {} + + '@ota-meshi/ast-token-store@0.3.0': {} + + '@oxc-parser/binding-android-arm-eabi@0.135.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.135.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.135.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.135.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.135.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.135.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.135.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.135.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.135.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.135.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.135.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.135.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.135.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.135.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.135.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.135.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.135.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.135.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.135.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.135.0': + optional: true + + '@oxc-project/types@0.127.0': {} + + '@oxc-project/types@0.133.0': {} + + '@oxc-project/types@0.135.0': {} + + '@oxc-project/types@0.137.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.21.3': + optional: true + + '@oxc-resolver/binding-android-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.21.3': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.21.3': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.3': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.21.3': + optional: true + + '@oxfmt/binding-android-arm-eabi@0.55.0': + optional: true + + '@oxfmt/binding-android-arm64@0.55.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.55.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.55.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.55.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.55.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.55.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.55.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.55.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.55.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.55.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.55.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.55.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.55.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.55.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.55.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.55.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.55.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.55.0': + optional: true + + '@oxlint-tsgolint/darwin-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/darwin-x64@0.23.0': + optional: true + + '@oxlint-tsgolint/linux-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/linux-x64@0.23.0': + optional: true + + '@oxlint-tsgolint/win32-arm64@0.23.0': + optional: true + + '@oxlint-tsgolint/win32-x64@0.23.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.70.0': + optional: true + + '@oxlint/binding-android-arm64@1.70.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.70.0': + optional: true + + '@oxlint/binding-darwin-x64@1.70.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.70.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.70.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.70.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.70.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.70.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.70.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.70.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.70.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.70.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.70.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.70.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.70.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.70.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.70.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.70.0': + optional: true + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-wasm@2.5.6': + dependencies: + is-glob: 4.0.3 + picomatch: 4.0.4 + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.4 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.3.6': {} + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/constants@1001.3.1': {} + + '@pnpm/error@1000.1.0': + dependencies: + '@pnpm/constants': 1001.3.1 + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.3': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@pnpm/types@1001.3.0': {} + + '@pnpm/workspace.read-manifest@1000.3.1': + dependencies: + '@pnpm/constants': 1001.3.1 + '@pnpm/error': 1000.1.0 + '@pnpm/types': 1001.3.0 + read-yaml-file: 2.1.0 + + '@polka/url@1.0.0-next.29': {} + + '@popperjs/core@2.11.8': {} + + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.7.0': + dependencies: + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.3': {} + + '@publint/pack@0.1.5': + dependencies: + tinyexec: 1.2.4 + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-android-arm64@1.0.3': + optional: true + + '@rolldown/binding-android-arm64@1.1.2': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.2': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-x64@1.0.3': + optional: true + + '@rolldown/binding-darwin-x64@1.1.2': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.2': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.2': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.2': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.2': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.2': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.3': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.2': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.2': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.2': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.17': {} + + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/plugin-alias@6.0.0(rollup@4.62.2)': + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(rollup@4.62.2)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + optionalDependencies: + rollup: 4.62.2 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-commonjs@29.0.3(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.4) + is-reference: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-inject@5.0.5(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-json@6.1.0(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.12 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-replace@6.0.3(rollup@4.62.2)': + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/plugin-terser@1.0.0(rollup@4.62.2)': + dependencies: + serialize-javascript: 7.0.6 + smob: 1.6.2 + terser: 5.48.0 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@sec-ant/readable-stream@0.4.1': {} + + '@shikijs/core@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@simple-libs/child-process-utils@1.0.2': + dependencies: + '@simple-libs/stream-utils': 1.2.0 + + '@simple-libs/stream-utils@1.2.0': {} + + '@sindresorhus/is@7.2.0': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@speed-highlight/core@1.2.17': {} + + '@standard-schema/spec@1.1.0': {} + + '@stylistic/stylelint-plugin@5.2.0(stylelint@17.13.0(typescript@6.0.3))': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + postcss: 8.5.15 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + style-search: 0.1.0 + stylelint: 17.13.0(typescript@6.0.3) + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + + '@tailwindcss/typography@0.5.20(tailwindcss@4.3.1)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.3.1 + + '@tailwindcss/vite@4.3.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + + '@tanstack/store@0.11.0': {} + + '@tanstack/virtual-core@3.17.1': {} + + '@tanstack/vue-store@0.11.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@tanstack/store': 0.11.0 + vue: 3.5.38(typescript@6.0.3) + vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3)) + + '@tanstack/vue-virtual@3.13.29(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@tanstack/virtual-core': 3.17.1 + vue: 3.5.38(typescript@6.0.3) + + '@tiptap/core@3.27.1(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-blockquote@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-bold@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-bubble-menu@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + optional: true + + '@tiptap/extension-bullet-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-code-block@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-code@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-document@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-dropcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-floating-menu@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + optional: true + + '@tiptap/extension-gapcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-hard-break@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-heading@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-highlight@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-horizontal-rule@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-image@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-italic@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-link@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + linkifyjs: 4.3.3 + + '@tiptap/extension-list-item@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-list-keymap@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-ordered-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-paragraph@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-placeholder@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-strike@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-text-align@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-text@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-underline@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/pm@3.27.1': + dependencies: + prosemirror-changeset: 2.4.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.4.1 + prosemirror-history: 1.5.0 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.5 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + '@tiptap/starter-kit@3.27.1': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-blockquote': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bold': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bullet-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-code': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-code-block': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-document': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-dropcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-gapcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-hard-break': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-heading': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-horizontal-rule': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-italic': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-link': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list-item': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-list-keymap': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-ordered-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-paragraph': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-strike': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-text': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-underline': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/vue-3@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@floating-ui/dom': 1.7.6 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-floating-menu': 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.30.21 + string.prototype.matchall: 4.0.12 + + '@tsdown/css@0.22.3(jiti@2.7.0)(postcss@8.5.15)(sass-embedded@1.100.0)(sass@1.101.0)(tsdown@0.22.3)(yaml@2.9.0)': + dependencies: + lightningcss: 1.32.0 + postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.15)(yaml@2.9.0) + rolldown: 1.1.2 + tsdown: 0.22.3(@tsdown/css@0.22.3)(oxc-resolver@11.21.3)(publint@0.3.21)(typescript@6.0.3)(vue-tsc@3.3.5(typescript@6.0.3)) + optionalDependencies: + postcss: 8.5.15 + sass: 1.101.0 + sass-embedded: 1.100.0 + transitivePeerDependencies: + - jiti + - tsx + - yaml + + '@turbo/darwin-64@2.9.18': + optional: true + + '@turbo/darwin-arm64@2.9.18': + optional: true + + '@turbo/linux-64@2.9.18': + optional: true + + '@turbo/linux-arm64@2.9.18': + optional: true + + '@turbo/windows-64@2.9.18': + optional: true + + '@turbo/windows-arm64@2.9.18': + optional: true + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/archiver@8.0.0': + dependencies: + '@types/node': 26.0.0 + '@types/readdir-glob': 1.1.5 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/deep-eql@4.0.2': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/geojson@7946.0.16': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/html-minifier-terser@7.0.2': {} + + '@types/jsesc@2.5.1': {} + + '@types/json-bigint@1.0.4': {} + + '@types/json-schema@7.0.15': {} + + '@types/linkify-it@5.0.0': {} + + '@types/lodash.clonedeep@4.5.9': + dependencies: + '@types/lodash': 4.17.24 + + '@types/lodash@4.17.24': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/node@12.20.55': {} + + '@types/node@25.9.4': + dependencies: + undici-types: 7.24.6 + + '@types/node@26.0.0': + dependencies: + undici-types: 8.3.0 + + '@types/nprogress@0.2.3': {} + + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 26.0.0 + + '@types/qs@6.15.1': {} + + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 26.0.0 + + '@types/resolve@1.20.2': {} + + '@types/sortablejs@1.15.9': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.9.4 + + '@typescript-eslint/parser@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/rule-tester@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + ajv: 6.15.0 + eslint: 10.5.0(jiti@2.7.0) + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + semver: 7.8.5 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/types@8.61.1': {} + + '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.1': {} + + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + '@v-c/async-validator@1.0.1': {} + + '@v-c/cascader@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/select': 1.1.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/tree': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/checkbox@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/collapse@1.0.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/color-picker@1.0.6(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@ant-design/fast-color': 3.0.1 + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/dialog@1.2.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/portal': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/drawer@1.0.6(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/portal': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/dropdown@1.0.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/image@1.0.12(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/portal': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/input-number@1.0.5(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/input': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/mini-decimal': 1.0.1 + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/input@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/mentions@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/input': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/menu': 1.2.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/textarea': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/menu@1.2.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/overflow': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/mini-decimal@1.0.1': {} + + '@v-c/mutate-observer@1.0.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/notification@2.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/overflow@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/pagination@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/picker@1.1.3(dayjs@1.11.21)(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/overflow': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + dayjs: 1.11.21 + + '@v-c/portal@1.0.8(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/progress@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/qrcode@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/rate@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/resize-observer@1.1.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + resize-observer-polyfill: 1.5.1 + vue: 3.5.38(typescript@6.0.3) + + '@v-c/segmented@1.0.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/select@1.1.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/overflow': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + '@v-c/virtual-list': 1.0.8(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/slick@1.0.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + es-toolkit: 1.45.1 + vue: 3.5.38(typescript@6.0.3) + + '@v-c/slider@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/steps@1.0.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/switch@1.0.1(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/table@1.1.4(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + '@v-c/virtual-list': 1.0.8(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/tabs@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/dropdown': 1.0.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/menu': 1.2.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/overflow': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/textarea@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/input': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/tooltip@1.0.3(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/tour@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/portal': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/tree-select@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/select': 1.1.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/tree': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/tree@1.1.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + '@v-c/virtual-list': 1.0.8(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/trigger@1.0.16(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/portal': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/upload@1.0.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@v-c/util@1.0.19(vue@3.5.38(typescript@6.0.3))': + dependencies: + vue: 3.5.38(typescript@6.0.3) + + '@v-c/virtual-list@1.0.8(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@valibot/to-json-schema@1.7.1(valibot@1.4.1(typescript@6.0.3))': + dependencies: + valibot: 1.4.1(typescript@6.0.3) + + '@vavt/copy2clipboard@1.0.3': {} + + '@vavt/util@2.1.2': {} + + '@vee-validate/zod@4.15.1(vue@3.5.38(typescript@6.0.3))(zod@3.25.76)': + dependencies: + type-fest: 4.41.0 + vee-validate: 4.15.1(vue@3.5.38(typescript@6.0.3)) + zod: 3.25.76 + transitivePeerDependencies: + - vue + + '@vercel/nft@1.10.2(rollup@4.62.2)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.3 + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + acorn: 8.17.0 + acorn-import-attributes: 1.9.5(acorn@8.17.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 13.0.6 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.4 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@vitejs/plugin-vue-jsx@5.1.5(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.1 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) + vite: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue-jsx@5.1.5(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.1 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@6.0.7(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + + '@vitejs/plugin-vue@6.0.7(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.9(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.9': {} + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue-macros/common@3.1.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/compiler-sfc': 3.5.38 + ast-kit: 2.2.0 + local-pkg: 1.2.1 + magic-string-ast: 1.0.3 + unplugin-utils: 0.3.1 + optionalDependencies: + vue: 3.5.38(typescript@6.0.3) + + '@vue/babel-helper-vue-transform-on@1.5.0': {} + + '@vue/babel-helper-vue-transform-on@2.0.1': {} + + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.29.7)': + dependencies: + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@vue/babel-helper-vue-transform-on': 1.5.0 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.29.7) + '@vue/shared': 3.5.38 + optionalDependencies: + '@babel/core': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)': + dependencies: + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@vue/babel-helper-vue-transform-on': 2.0.1 + '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7) + '@vue/shared': 3.5.38 + optionalDependencies: + '@babel/core': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.29.7)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/parser': 7.29.7 + '@vue/compiler-sfc': 3.5.38 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/parser': 7.29.7 + '@vue/compiler-sfc': 3.5.38 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.38': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.38 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.38': + dependencies: + '@vue/compiler-core': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/compiler-sfc@3.5.38': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.38 + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.15 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.38': + dependencies: + '@vue/compiler-dom': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/devtools-api@6.6.4': {} + + '@vue/devtools-api@7.7.9': + dependencies: + '@vue/devtools-kit': 7.7.9 + + '@vue/devtools-api@8.1.3': + dependencies: + '@vue/devtools-kit': 8.1.3 + + '@vue/devtools-core@8.1.3(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/devtools-kit': 8.1.3 + '@vue/devtools-shared': 8.1.3 + vue: 3.5.38(typescript@6.0.3) + + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + '@vue/devtools-kit@8.1.3': + dependencies: + '@vue/devtools-shared': 8.1.3 + birpc: 2.9.0 + hookable: 5.5.3 + perfect-debounce: 2.1.0 + + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + + '@vue/devtools-shared@8.1.3': {} + + '@vue/language-core@3.3.5': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.38 + '@vue/shared': 3.5.38 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.4 + + '@vue/reactivity@3.5.38': + dependencies: + '@vue/shared': 3.5.38 + + '@vue/runtime-core@3.5.38': + dependencies: + '@vue/reactivity': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/runtime-dom@3.5.38': + dependencies: + '@vue/reactivity': 3.5.38 + '@vue/runtime-core': 3.5.38 + '@vue/shared': 3.5.38 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + vue: 3.5.38(typescript@6.0.3) + + '@vue/shared@3.5.38': {} + + '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.38)(@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/compiler-dom': 3.5.38 + js-beautify: 1.15.4 + vue: 3.5.38(typescript@6.0.3) + vue-component-type-helpers: 3.3.5 + optionalDependencies: + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) + + '@vueuse/core@13.9.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 13.9.0 + '@vueuse/shared': 13.9.0(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@vueuse/core@14.3.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.3.0 + '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + + '@vueuse/integrations@14.3.0(axios@1.18.0)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.7)(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vueuse/core': 14.3.0(vue@3.5.38(typescript@6.0.3)) + '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + axios: 1.18.0 + nprogress: 0.2.0 + qrcode: 1.5.4 + sortablejs: 1.15.7 + + '@vueuse/metadata@13.9.0': {} + + '@vueuse/metadata@14.3.0': {} + + '@vueuse/motion@3.0.3(magicast@0.5.3)(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vueuse/core': 13.9.0(vue@3.5.38(typescript@6.0.3)) + '@vueuse/shared': 13.9.0(vue@3.5.38(typescript@6.0.3)) + defu: 6.1.7 + framesync: 6.1.2 + popmotion: 11.0.5 + style-value-types: 5.1.2 + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + '@nuxt/kit': 3.21.8(magicast@0.5.3) + transitivePeerDependencies: + - magicast + + '@vueuse/shared@13.9.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + vue: 3.5.38(typescript@6.0.3) + + '@vueuse/shared@14.3.0(vue@3.5.38(typescript@6.0.3))': + dependencies: + vue: 3.5.38(typescript@6.0.3) + + '@vxe-ui/core@4.4.14(vue@3.5.38(typescript@6.0.3))': + dependencies: + dom-zindex: 1.0.7 + vue: 3.5.38(typescript@6.0.3) + xe-utils: 4.0.9 + + abbrev@2.0.0: {} + + abbrev@3.0.1: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-import-attributes@1.9.5(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + alien-signals@3.2.1: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + ansis@4.3.1: {} + + antdv-next@1.3.6(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@ant-design/colors': 8.0.1 + '@ant-design/fast-color': 3.0.1 + '@antdv-next/cssinjs': 1.0.6(vue@3.5.38(typescript@6.0.3)) + '@antdv-next/icons': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@v-c/async-validator': 1.0.1 + '@v-c/cascader': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/checkbox': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/collapse': 1.0.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/color-picker': 1.0.6(vue@3.5.38(typescript@6.0.3)) + '@v-c/dialog': 1.2.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/drawer': 1.0.6(vue@3.5.38(typescript@6.0.3)) + '@v-c/dropdown': 1.0.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/image': 1.0.12(vue@3.5.38(typescript@6.0.3)) + '@v-c/input': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/input-number': 1.0.5(vue@3.5.38(typescript@6.0.3)) + '@v-c/mentions': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/menu': 1.2.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/mutate-observer': 1.0.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/notification': 2.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/pagination': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/picker': 1.1.3(dayjs@1.11.21)(vue@3.5.38(typescript@6.0.3)) + '@v-c/progress': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/qrcode': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/rate': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/resize-observer': 1.1.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/segmented': 1.0.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/select': 1.1.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/slick': 1.0.2(vue@3.5.38(typescript@6.0.3)) + '@v-c/slider': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/steps': 1.0.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/switch': 1.0.1(vue@3.5.38(typescript@6.0.3)) + '@v-c/table': 1.1.4(vue@3.5.38(typescript@6.0.3)) + '@v-c/tabs': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/textarea': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/tooltip': 1.0.3(vue@3.5.38(typescript@6.0.3)) + '@v-c/tour': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/tree': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/tree-select': 1.1.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/trigger': 1.0.16(vue@3.5.38(typescript@6.0.3)) + '@v-c/upload': 1.0.0(vue@3.5.38(typescript@6.0.3)) + '@v-c/util': 1.0.19(vue@3.5.38(typescript@6.0.3)) + '@v-c/virtual-list': 1.0.8(vue@3.5.38(typescript@6.0.3)) + '@vueuse/core': 14.3.0(vue@3.5.38(typescript@6.0.3)) + dayjs: 1.11.21 + es-toolkit: 1.45.1 + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + - vue + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + archiver-utils@5.0.2: + dependencies: + glob: 10.4.5 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.18.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.2.0 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + archiver@8.0.0: + dependencies: + async: 3.2.6 + buffer-crc32: 1.0.0 + is-stream: 4.0.1 + lazystream: 1.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + readdir-glob: 3.0.0 + tar-stream: 3.2.0 + zip-stream: 7.0.5 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-ify@1.0.0: {} + + array-timsort@1.0.3: {} + + array-union@2.1.0: {} + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + ast-kit@2.2.0: + dependencies: + '@babel/parser': 7.29.7 + pathe: 2.0.3 + + ast-kit@3.0.0: + dependencies: + '@babel/parser': 8.0.0 + estree-walker: 3.0.3 + pathe: 2.0.3 + + ast-walker-scope@0.9.0: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ast-kit: 2.2.0 + + astral-regex@2.0.0: {} + + async-function@1.0.0: {} + + async-sema@3.1.1: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + atomically@2.1.1: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios-mock-adapter@2.1.0(axios@1.18.0): + dependencies: + axios: 1.18.0 + fast-deep-equal: 3.1.3 + is-buffer: 2.0.5 + + axios@1.18.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + b4a@1.8.1: {} + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + bare-events@2.9.1: {} + + bare-fs@4.7.2: + dependencies: + bare-events: 2.9.1 + bare-path: 3.0.1 + bare-stream: 2.13.3(bare-events@2.9.1) + bare-url: 2.4.5 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-os@3.9.1: {} + + bare-path@3.0.1: + dependencies: + bare-os: 3.9.1 + + bare-stream@2.13.3(bare-events@2.9.1): + dependencies: + b4a: 1.8.1 + streamx: 2.28.0 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.9.1 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.4.5: + dependencies: + bare-path: 3.0.1 + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.38: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + bignumber.js@9.3.1: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + birpc@2.9.0: {} + + birpc@4.0.0: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + + brace-expansion@2.1.1: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.38 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.376 + node-releases: 2.0.48 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + buffer-crc32@1.0.0: {} + + buffer-from@1.1.2: {} + + buffer-image-size@0.6.4: + dependencies: + '@types/node': 25.9.4 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + c12@3.3.4(magicast@0.5.3): + dependencies: + chokidar: 5.0.0 + confbox: 0.2.4 + defu: 6.1.7 + dotenv: 17.4.2 + exsolve: 1.0.8 + giget: 3.3.0 + jiti: 2.7.0 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + rc9: 3.0.1 + optionalDependencies: + magicast: 0.5.3 + + cac@7.0.0: {} + + cacheable@2.3.5: + dependencies: + '@cacheable/memory': 2.0.9 + '@cacheable/utils': 2.4.1 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase@5.3.1: {} + + camelcase@8.0.0: {} + + caniuse-lite@1.0.30001799: {} + + ccount@2.0.1: {} + + chai@6.2.2: {} + + chalk-template@1.1.2: + dependencies: + chalk: 5.6.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chardet@2.2.0: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.2.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.28.0 + whatwg-mimetype: 4.0.0 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chownr@3.0.0: {} + + ci-info@4.4.0: {} + + circular-dependency-scanner@3.0.1: + dependencies: + '@ast-grep/napi': 0.43.0 + '@vue/compiler-sfc': 3.5.38 + commander: 14.0.3 + get-tsconfig: 4.14.0 + listr2: 9.0.5 + minimatch: 9.0.9 + node-cleanup: 2.1.2 + typescript: 5.9.3 + update-notifier: 7.3.1 + zx: 8.8.5 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + citty@0.2.2: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + cli-boxes@3.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@3.4.0: {} + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.1 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + clsx@2.1.1: {} + + cluster-key-slot@1.1.1: {} + + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.10.3 + '@codemirror/language': 6.12.3 + '@codemirror/lint': 6.9.7 + '@codemirror/search': 6.7.1 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + colorjs.io@0.5.2: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@10.0.1: {} + + commander@11.1.0: {} + + commander@14.0.3: {} + + commander@2.20.3: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + comment-json@5.0.0: + dependencies: + array-timsort: 1.0.3 + esprima: 4.0.1 + + comment-parser@1.4.5: {} + + commitlint-plugin-function-rules@5.0.1(@commitlint/lint@21.0.2): + dependencies: + '@commitlint/lint': 21.0.2 + + common-tags@1.8.2: {} + + commondir@1.0.1: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compare-versions@6.1.1: {} + + compatx@0.2.0: {} + + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + compress-commons@7.0.1: + dependencies: + crc-32: 1.2.2 + crc32-stream: 7.0.1 + is-stream: 4.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + compute-scroll-into-view@3.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.4: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@7.1.0: + dependencies: + atomically: 2.1.1 + dot-prop: 9.0.0 + graceful-fs: 4.2.11 + xdg-basedir: 5.1.0 + + consola@3.4.2: {} + + conventional-changelog-angular@8.3.1: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@9.3.1: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@6.4.0: + dependencies: + '@simple-libs/stream-utils': 1.2.0 + meow: 13.2.0 + + convert-source-map@2.0.0: {} + + cookie-es@1.2.3: {} + + cookie-es@2.0.1: {} + + cookie-es@3.1.1: {} + + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + core-util-is@1.0.3: {} + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + cosmiconfig-typescript-loader@6.3.0(@types/node@25.9.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3): + dependencies: + '@types/node': 25.9.4 + cosmiconfig: 9.0.2(typescript@6.0.3) + jiti: 2.6.1 + typescript: 6.0.3 + + cosmiconfig@9.0.2(typescript@6.0.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 6.0.3 + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + crc32-stream@7.0.1: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + crelt@1.0.6: {} + + croner@10.0.1: {} + + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + crypto-js@4.2.0: {} + + crypto-random-string@2.0.0: {} + + cspell-config-lib@10.0.1: + dependencies: + '@cspell/cspell-types': 10.0.1 + comment-json: 5.0.0 + smol-toml: 1.6.1 + yaml: 2.9.0 + + cspell-dictionary@10.0.1: + dependencies: + '@cspell/cspell-performance-monitor': 10.0.1 + '@cspell/cspell-pipe': 10.0.1 + '@cspell/cspell-types': 10.0.1 + cspell-trie-lib: 10.0.1(@cspell/cspell-types@10.0.1) + fast-equals: 6.0.0 + + cspell-gitignore@10.0.1: + dependencies: + '@cspell/url': 10.0.1 + cspell-glob: 10.0.1 + cspell-io: 10.0.1 + + cspell-glob@10.0.1: + dependencies: + '@cspell/url': 10.0.1 + picomatch: 4.0.4 + + cspell-grammar@10.0.1: + dependencies: + '@cspell/cspell-pipe': 10.0.1 + '@cspell/cspell-types': 10.0.1 + + cspell-io@10.0.1: + dependencies: + '@cspell/cspell-service-bus': 10.0.1 + '@cspell/url': 10.0.1 + + cspell-lib@10.0.1: + dependencies: + '@cspell/cspell-bundled-dicts': 10.0.1 + '@cspell/cspell-performance-monitor': 10.0.1 + '@cspell/cspell-pipe': 10.0.1 + '@cspell/cspell-resolver': 10.0.1 + '@cspell/cspell-types': 10.0.1 + '@cspell/dynamic-import': 10.0.1 + '@cspell/filetypes': 10.0.1 + '@cspell/rpc': 10.0.1 + '@cspell/strong-weak-map': 10.0.1 + '@cspell/url': 10.0.1 + cspell-config-lib: 10.0.1 + cspell-dictionary: 10.0.1 + cspell-glob: 10.0.1 + cspell-grammar: 10.0.1 + cspell-io: 10.0.1 + cspell-trie-lib: 10.0.1(@cspell/cspell-types@10.0.1) + env-paths: 4.0.0 + gensequence: 8.0.8 + import-fresh: 4.0.0 + resolve-from: 5.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + xdg-basedir: 5.1.0 + + cspell-trie-lib@10.0.1(@cspell/cspell-types@10.0.1): + dependencies: + '@cspell/cspell-types': 10.0.1 + + cspell@10.0.1: + dependencies: + '@cspell/cspell-json-reporter': 10.0.1 + '@cspell/cspell-performance-monitor': 10.0.1 + '@cspell/cspell-pipe': 10.0.1 + '@cspell/cspell-types': 10.0.1 + '@cspell/cspell-worker': 10.0.1 + '@cspell/dynamic-import': 10.0.1 + '@cspell/url': 10.0.1 + ansi-regex: 6.2.2 + chalk: 5.6.2 + chalk-template: 1.1.2 + commander: 14.0.3 + cspell-config-lib: 10.0.1 + cspell-dictionary: 10.0.1 + cspell-gitignore: 10.0.1 + cspell-glob: 10.0.1 + cspell-io: 10.0.1 + cspell-lib: 10.0.1 + fast-json-stable-stringify: 2.1.0 + flatted: 3.4.2 + semver: 7.8.5 + tinyglobby: 0.2.17 + + css-functions-list@3.3.3: {} + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + cssfilter@0.0.10: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.2.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.34.0 + + cytoscape-fcose@2.2.0(cytoscape@3.34.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.34.0 + + cytoscape@3.34.0: {} + + cz-git@1.13.1: {} + + czg@1.13.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dataloader@1.4.0: {} + + dayjs@1.11.21: {} + + db0@0.3.4: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@3.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.7: {} + + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + + delayed-stream@1.0.0: {} + + denque@2.1.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-indent@6.1.0: {} + + detect-libc@2.1.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff-sequences@29.6.3: {} + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom-zindex@1.0.7: {} + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + dompurify@3.4.11: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@10.1.0: + dependencies: + type-fest: 5.7.0 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dot-prop@9.0.0: + dependencies: + type-fest: 4.41.0 + + dotenv@17.4.2: {} + + dotenv@8.6.0: {} + + dts-resolver@3.0.0(oxc-resolver@11.21.3): + optionalDependencies: + oxc-resolver: 11.21.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + eastasianwidth@0.2.0: {} + + echarts@6.1.0: + dependencies: + tslib: 2.3.0 + zrender: 6.1.0 + + editorconfig@1.0.7: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.9 + semver: 7.8.5 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.376: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + empathic@2.0.1: {} + + encodeurl@2.0.0: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + engine.io-client@6.6.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.21.0 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + enhanced-resolve@5.24.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@4.5.0: {} + + entities@6.0.1: {} + + entities@7.0.1: {} + + env-paths@2.2.1: {} + + env-paths@4.0.0: + dependencies: + is-safe-filename: 0.1.1 + + environment@1.1.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser-es@1.0.5: {} + + errx@0.1.0: + optional: true + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.1 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-module-lexer@2.1.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-to-primitive@1.3.1: + dependencies: + es-abstract-get: 1.0.0 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es-toolkit@1.45.1: {} + + es-toolkit@1.47.1: {} + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escalade@3.2.0: {} + + escape-goat@4.0.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-compat-utils@0.5.1(eslint@10.5.0(jiti@2.7.0)): + dependencies: + eslint: 10.5.0(jiti@2.7.0) + semver: 7.8.5 + + eslint-json-compat-utils@0.2.3(eslint@10.5.0(jiti@2.7.0))(jsonc-eslint-parser@3.1.0): + dependencies: + eslint: 10.5.0(jiti@2.7.0) + esquery: 1.7.0 + jsonc-eslint-parser: 3.1.0 + + eslint-plugin-better-tailwindcss@4.6.0(eslint@10.5.0(jiti@2.7.0))(oxlint@1.70.0(oxlint-tsgolint@0.23.0))(tailwindcss@4.3.1)(typescript@6.0.3): + dependencies: + '@eslint/css-tree': 4.0.4 + '@valibot/to-json-schema': 1.7.1(valibot@1.4.1(typescript@6.0.3)) + enhanced-resolve: 5.24.0 + jiti: 2.7.0 + synckit: 0.11.13 + tailwind-csstree: 0.3.3 + tailwindcss: 4.3.1 + tsconfig-paths-webpack-plugin: 4.2.0 + valibot: 1.4.1(typescript@6.0.3) + optionalDependencies: + eslint: 10.5.0(jiti@2.7.0) + oxlint: 1.70.0(oxlint-tsgolint@0.23.0) + transitivePeerDependencies: + - '@eslint/css' + - typescript + + eslint-plugin-command@3.5.2(@typescript-eslint/rule-tester@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3))(@typescript-eslint/utils@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0)): + dependencies: + '@es-joy/jsdoccomment': 0.84.0 + '@typescript-eslint/rule-tester': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + + eslint-plugin-es-x@7.8.0(eslint@10.5.0(jiti@2.7.0)): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + eslint: 10.5.0(jiti@2.7.0) + eslint-compat-utils: 0.5.1(eslint@10.5.0(jiti@2.7.0)) + + eslint-plugin-jsonc@3.2.0(eslint@10.5.0(jiti@2.7.0)): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@ota-meshi/ast-token-store': 0.3.0 + diff-sequences: 29.6.3 + eslint: 10.5.0(jiti@2.7.0) + eslint-json-compat-utils: 0.2.3(eslint@10.5.0(jiti@2.7.0))(jsonc-eslint-parser@3.1.0) + jsonc-eslint-parser: 3.1.0 + natural-compare: 1.4.0 + synckit: 0.11.13 + transitivePeerDependencies: + - '@eslint/json' + + eslint-plugin-n@18.1.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + enhanced-resolve: 5.24.0 + eslint: 10.5.0(jiti@2.7.0) + eslint-plugin-es-x: 7.8.0(eslint@10.5.0(jiti@2.7.0)) + get-tsconfig: 4.14.0 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.8.5 + optionalDependencies: + typescript: 6.0.3 + + eslint-plugin-perfectionist@5.9.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + natural-orderby: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-pnpm@1.6.1(eslint@10.5.0(jiti@2.7.0)): + dependencies: + empathic: 2.0.1 + eslint: 10.5.0(jiti@2.7.0) + jsonc-eslint-parser: 3.1.0 + pathe: 2.0.3 + pnpm-workspace-yaml: 1.6.1 + tinyglobby: 0.2.17 + yaml: 2.9.0 + yaml-eslint-parser: 2.0.0 + + eslint-plugin-unused-imports@4.4.1(eslint@10.5.0(jiti@2.7.0)): + dependencies: + eslint: 10.5.0(jiti@2.7.0) + + eslint-plugin-vue@10.9.2(@typescript-eslint/parser@8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.5.0(jiti@2.7.0))): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + eslint: 10.5.0(jiti@2.7.0) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 7.1.4 + semver: 7.8.5 + vue-eslint-parser: 10.4.1(eslint@10.5.0(jiti@2.7.0)) + xml-name-validator: 4.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + + eslint-plugin-yml@3.4.0(eslint@10.5.0(jiti@2.7.0)): + dependencies: + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@ota-meshi/ast-token-store': 0.3.0 + diff-sequences: 29.6.3 + escape-string-regexp: 5.0.0 + eslint: 10.5.0(jiti@2.7.0) + natural-compare: 1.4.0 + yaml-eslint-parser: 2.0.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.5.0(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.6.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + espree@9.6.1: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + eta@4.6.0: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.4: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.9.1 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + expect-type@1.3.0: {} + + exsolve@1.0.8: {} + + extendable-error@0.1.7: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@6.0.0: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.2: {} + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fastest-levenshtein@1.0.16: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fflate@0.8.3: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@11.1.3: + dependencies: + flat-cache: 6.1.22 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-uri-to-path@1.0.0: {} + + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@8.0.0: + dependencies: + locate-path: 8.0.0 + unicorn-magic: 0.3.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flat-cache@6.1.22: + dependencies: + cacheable: 2.3.5 + flatted: 3.4.2 + hookified: 1.15.1 + + flatted@3.4.2: {} + + follow-redirects@1.16.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + formatly@0.3.0: + dependencies: + fd-package-json: 2.0.0 + + framesync@6.1.2: + dependencies: + tslib: 2.4.0 + + fresh@2.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensequence@8.0.8: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.6.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-port-please@3.2.0: {} + + get-port@7.2.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@3.3.0: {} + + git-raw-commits@5.0.1(conventional-commits-parser@6.4.0): + dependencies: + '@conventional-changelog/git-client': 2.7.0(conventional-commits-parser@6.4.0) + meow: 13.2.0 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + global-directory@5.0.0: + dependencies: + ini: 6.0.0 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@15.15.0: {} + + globals@17.6.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@16.2.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globjoin@0.1.4: {} + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + gzip-size@7.0.0: + dependencies: + duplexer: 0.1.2 + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + hachure-fill@0.5.2: {} + + happy-dom@20.10.6: + dependencies: + '@types/node': 25.9.4 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-flag@5.0.1: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hey-listen@1.0.8: {} + + hookable@5.5.3: {} + + hookable@6.1.1: {} + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.48.0 + + html-tags@5.1.0: {} + + html-void-elements@3.0.0: {} + + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-shutdown@1.2.2: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + httpxy@0.5.3: {} + + human-id@4.2.0: {} + + human-signals@8.0.1: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + idb@7.1.1: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immutable@5.1.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-fresh@4.0.0: {} + + import-meta-resolve@4.2.0: {} + + import-without-cache@0.4.0: {} + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + ini@6.0.0: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + ioredis@5.11.1: + dependencies: + '@ioredis/commands': 1.10.0 + cluster-key-slot: 1.1.1 + debug: 4.4.3 + denque: 2.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + iron-webcrypto@1.2.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@2.0.5: {} + + is-callable@1.2.7: {} + + is-ci@4.1.0: + dependencies: + ci-info: 4.4.0 + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@3.0.0: {} + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ci@1.0.0: {} + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + + is-interactive@2.0.0: {} + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-npm@6.1.0: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-obj@2.0.0: {} + + is-path-inside@4.0.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@5.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.9 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-regexp@1.0.0: {} + + is-safe-filename@0.1.1: {} + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-stream@4.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-what@5.5.0: {} + + is-windows@1.0.2: {} + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + + jiti@2.6.1: {} + + jiti@2.7.0: {} + + jju@1.4.0: {} + + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.7 + glob: 10.4.5 + js-cookie: 3.0.8 + nopt: 7.2.1 + + js-cookie@3.0.8: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@7.1.1: {} + + jsesc@3.1.0: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.2: + dependencies: + acorn: 8.17.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.8.5 + + jsonc-eslint-parser@3.1.0: + dependencies: + acorn: 8.17.0 + eslint-visitor-keys: 5.0.1 + semver: 7.8.5 + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + + katex@0.16.47: + dependencies: + commander: 8.3.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + khroma@2.1.0: {} + + kind-of@6.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} + + knip@6.17.1: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + formatly: 0.3.0 + get-tsconfig: 4.14.0 + jiti: 2.7.0 + oxc-parser: 0.135.0 + oxc-resolver: 11.21.3 + picomatch: 4.0.4 + smol-toml: 1.6.1 + strip-json-comments: 5.0.3 + tinyglobby: 0.2.17 + unbash: 4.0.1 + yaml: 2.9.0 + zod: 4.4.3 + + knitwork@1.3.0: {} + + known-css-properties@0.37.0: {} + + kolorist@1.8.0: {} + + ky@1.14.3: {} + + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + lefthook-darwin-arm64@2.1.9: + optional: true + + lefthook-darwin-x64@2.1.9: + optional: true + + lefthook-freebsd-arm64@2.1.9: + optional: true + + lefthook-freebsd-x64@2.1.9: + optional: true + + lefthook-linux-arm64@2.1.9: + optional: true + + lefthook-linux-x64@2.1.9: + optional: true + + lefthook-openbsd-arm64@2.1.9: + optional: true + + lefthook-openbsd-x64@2.1.9: + optional: true + + lefthook-windows-arm64@2.1.9: + optional: true + + lefthook-windows-x64@2.1.9: + optional: true + + lefthook@2.1.9: + optionalDependencies: + lefthook-darwin-arm64: 2.1.9 + lefthook-darwin-x64: 2.1.9 + lefthook-freebsd-arm64: 2.1.9 + lefthook-freebsd-x64: 2.1.9 + lefthook-linux-arm64: 2.1.9 + lefthook-linux-x64: 2.1.9 + lefthook-openbsd-arm64: 2.1.9 + lefthook-openbsd-x64: 2.1.9 + lefthook-windows-arm64: 2.1.9 + lefthook-windows-x64: 2.1.9 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + linkify-it@5.0.1: + dependencies: + uc.micro: 2.1.0 + + linkifyjs@4.3.3: {} + + listhen@1.10.0: + dependencies: + '@parcel/watcher': 2.5.6 + '@parcel/watcher-wasm': 2.5.6 + citty: 0.2.2 + consola: 3.4.2 + crossws: 0.3.5 + defu: 6.1.7 + get-port-please: 3.2.0 + h3: 1.15.11 + http-shutdown: 1.2.2 + jiti: 2.7.0 + mlly: 1.8.2 + node-forge: 1.4.0 + pathe: 2.0.3 + std-env: 4.1.0 + tinyclip: 0.1.14 + ufo: 1.6.4 + untun: 0.1.3 + uqr: 0.1.3 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@8.0.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.18.1: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.merge@4.6.2: {} + + lodash.sortby@4.7.0: {} + + lodash.startcase@4.4.0: {} + + lodash.truncate@4.4.2: {} + + lodash@4.18.1: {} + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + + lru-cache@11.5.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string-ast@1.0.3: + dependencies: + magic-string: 0.30.21 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + markdown-it-image-figures@2.1.1(markdown-it@14.2.0): + dependencies: + markdown-it: 14.2.0 + + markdown-it-sub@2.0.0: {} + + markdown-it-sup@2.0.0: {} + + markdown-it@14.2.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.1 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + marked@12.0.2: {} + + marked@16.4.2: {} + + math-intrinsics@1.1.0: {} + + mathml-tag-names@4.0.0: {} + + md-editor-v3@6.5.1(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.10.3 + '@codemirror/lang-markdown': 6.5.0 + '@codemirror/language': 6.12.3 + '@codemirror/language-data': 6.5.2 + '@codemirror/search': 6.7.1 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + '@lezer/highlight': 1.2.3 + '@lucide/vue': 1.21.0(vue@3.5.38(typescript@6.0.3)) + '@types/markdown-it': 14.1.2 + '@vavt/copy2clipboard': 1.0.3 + '@vavt/util': 2.1.2 + codemirror: 6.0.2 + markdown-it: 14.2.0 + markdown-it-image-figures: 2.1.1(markdown-it@14.2.0) + markdown-it-sub: 2.0.0 + markdown-it-sup: 2.0.0 + medium-zoom: 1.1.0 + vue: 3.5.38(typescript@6.0.3) + xss: 1.0.15 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdn-data@2.0.28: {} + + mdn-data@2.27.1: {} + + mdn-data@2.28.1: {} + + mdurl@2.0.0: {} + + medium-zoom@1.1.0: {} + + meow@13.2.0: {} + + meow@14.1.0: {} + + merge2@1.4.1: {} + + mermaid@11.15.0: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.3 + '@mermaid-js/parser': 1.1.1 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.34.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0) + cytoscape-fcose: 2.2.0(cytoscape@3.34.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.21 + dompurify: 3.4.11 + es-toolkit: 1.47.1 + katex: 0.16.47 + khroma: 2.1.0 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.3.0 + uuid: 14.0.1 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@4.1.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.1 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mitt@3.0.1: {} + + mlly@1.8.2: + dependencies: + acorn: 8.17.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + modern-tar@0.7.6: {} + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nanoid@3.3.14: {} + + natural-compare@1.4.0: {} + + natural-orderby@5.0.0: {} + + nitropack@2.13.4(oxc-parser@0.135.0)(rolldown@1.1.2): + dependencies: + '@cloudflare/kv-asset-handler': 0.4.2 + '@rollup/plugin-alias': 6.0.0(rollup@4.62.2) + '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.2) + '@rollup/plugin-inject': 5.0.5(rollup@4.62.2) + '@rollup/plugin-json': 6.1.0(rollup@4.62.2) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.2) + '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.2) + '@vercel/nft': 1.10.2(rollup@4.62.2) + archiver: 7.0.1 + c12: 3.3.4(magicast@0.5.3) + chokidar: 5.0.0 + citty: 0.2.2 + compatx: 0.2.0 + confbox: 0.2.4 + consola: 3.4.2 + cookie-es: 2.0.1 + croner: 10.0.1 + crossws: 0.3.5 + db0: 0.3.4 + defu: 6.1.7 + destr: 2.0.5 + dot-prop: 10.1.0 + esbuild: 0.28.1 + escape-string-regexp: 5.0.0 + etag: 1.8.1 + exsolve: 1.0.8 + globby: 16.2.0 + gzip-size: 7.0.0 + h3: 1.15.11 + hookable: 5.5.3 + httpxy: 0.5.3 + ioredis: 5.11.1 + jiti: 2.7.0 + klona: 2.0.6 + knitwork: 1.3.0 + listhen: 1.10.0 + magic-string: 0.30.21 + magicast: 0.5.3 + mime: 4.1.0 + mlly: 1.8.2 + node-fetch-native: 1.6.7 + node-mock-http: 1.0.4 + ofetch: 1.5.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + pretty-bytes: 7.1.0 + radix3: 1.1.2 + rollup: 4.62.2 + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.2)(rollup@4.62.2) + scule: 1.3.0 + semver: 7.8.5 + serve-placeholder: 2.0.2 + serve-static: 2.2.1 + source-map: 0.7.6 + std-env: 4.1.0 + ufo: 1.6.4 + ultrahtml: 1.6.0 + uncrypto: 0.1.3 + unctx: 2.5.0 + unenv: 2.0.0-rc.24 + unimport: 6.3.0(oxc-parser@0.135.0)(rolldown@1.1.2) + unplugin-utils: 0.3.1 + unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) + untyped: 2.0.0 + unwasm: 0.5.3 + youch: 4.1.1 + youch-core: 0.3.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bare-abort-controller + - bare-buffer + - better-sqlite3 + - drizzle-orm + - encoding + - idb-keyval + - mysql2 + - oxc-parser + - react-native-b4a + - rolldown + - sqlite3 + - supports-color + - uploadthing + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-addon-api@7.1.1: {} + + node-cleanup@2.1.2: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-forge@1.4.0: {} + + node-gyp-build@4.8.4: {} + + node-mock-http@1.0.4: {} + + node-releases@2.0.48: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + + normalize-path@3.0.0: {} + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + obug@2.1.3: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + ohash@2.0.11: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + open@11.0.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@9.4.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.1 + + orderedmap@2.1.1: {} + + outdent@0.5.0: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + oxc-parser@0.135.0: + dependencies: + '@oxc-project/types': 0.135.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.135.0 + '@oxc-parser/binding-android-arm64': 0.135.0 + '@oxc-parser/binding-darwin-arm64': 0.135.0 + '@oxc-parser/binding-darwin-x64': 0.135.0 + '@oxc-parser/binding-freebsd-x64': 0.135.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.135.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.135.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.135.0 + '@oxc-parser/binding-linux-arm64-musl': 0.135.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.135.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.135.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.135.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.135.0 + '@oxc-parser/binding-linux-x64-gnu': 0.135.0 + '@oxc-parser/binding-linux-x64-musl': 0.135.0 + '@oxc-parser/binding-openharmony-arm64': 0.135.0 + '@oxc-parser/binding-wasm32-wasi': 0.135.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.135.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.135.0 + '@oxc-parser/binding-win32-x64-msvc': 0.135.0 + + oxc-resolver@11.21.3: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.21.3 + '@oxc-resolver/binding-android-arm64': 11.21.3 + '@oxc-resolver/binding-darwin-arm64': 11.21.3 + '@oxc-resolver/binding-darwin-x64': 11.21.3 + '@oxc-resolver/binding-freebsd-x64': 11.21.3 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.21.3 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.21.3 + '@oxc-resolver/binding-linux-arm64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-arm64-musl': 11.21.3 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-riscv64-musl': 11.21.3 + '@oxc-resolver/binding-linux-s390x-gnu': 11.21.3 + '@oxc-resolver/binding-linux-x64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-x64-musl': 11.21.3 + '@oxc-resolver/binding-openharmony-arm64': 11.21.3 + '@oxc-resolver/binding-wasm32-wasi': 11.21.3 + '@oxc-resolver/binding-win32-arm64-msvc': 11.21.3 + '@oxc-resolver/binding-win32-x64-msvc': 11.21.3 + + oxfmt@0.55.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.55.0 + '@oxfmt/binding-android-arm64': 0.55.0 + '@oxfmt/binding-darwin-arm64': 0.55.0 + '@oxfmt/binding-darwin-x64': 0.55.0 + '@oxfmt/binding-freebsd-x64': 0.55.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.55.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.55.0 + '@oxfmt/binding-linux-arm64-gnu': 0.55.0 + '@oxfmt/binding-linux-arm64-musl': 0.55.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.55.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.55.0 + '@oxfmt/binding-linux-riscv64-musl': 0.55.0 + '@oxfmt/binding-linux-s390x-gnu': 0.55.0 + '@oxfmt/binding-linux-x64-gnu': 0.55.0 + '@oxfmt/binding-linux-x64-musl': 0.55.0 + '@oxfmt/binding-openharmony-arm64': 0.55.0 + '@oxfmt/binding-win32-arm64-msvc': 0.55.0 + '@oxfmt/binding-win32-ia32-msvc': 0.55.0 + '@oxfmt/binding-win32-x64-msvc': 0.55.0 + + oxlint-tsgolint@0.23.0: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 0.23.0 + '@oxlint-tsgolint/darwin-x64': 0.23.0 + '@oxlint-tsgolint/linux-arm64': 0.23.0 + '@oxlint-tsgolint/linux-x64': 0.23.0 + '@oxlint-tsgolint/win32-arm64': 0.23.0 + '@oxlint-tsgolint/win32-x64': 0.23.0 + + oxlint@1.70.0(oxlint-tsgolint@0.23.0): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.70.0 + '@oxlint/binding-android-arm64': 1.70.0 + '@oxlint/binding-darwin-arm64': 1.70.0 + '@oxlint/binding-darwin-x64': 1.70.0 + '@oxlint/binding-freebsd-x64': 1.70.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.70.0 + '@oxlint/binding-linux-arm-musleabihf': 1.70.0 + '@oxlint/binding-linux-arm64-gnu': 1.70.0 + '@oxlint/binding-linux-arm64-musl': 1.70.0 + '@oxlint/binding-linux-ppc64-gnu': 1.70.0 + '@oxlint/binding-linux-riscv64-gnu': 1.70.0 + '@oxlint/binding-linux-riscv64-musl': 1.70.0 + '@oxlint/binding-linux-s390x-gnu': 1.70.0 + '@oxlint/binding-linux-x64-gnu': 1.70.0 + '@oxlint/binding-linux-x64-musl': 1.70.0 + '@oxlint/binding-openharmony-arm64': 1.70.0 + '@oxlint/binding-win32-arm64-msvc': 1.70.0 + '@oxlint/binding-win32-ia32-msvc': 1.70.0 + '@oxlint/binding-win32-x64-msvc': 1.70.0 + oxlint-tsgolint: 0.23.0 + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-json@10.0.1: + dependencies: + ky: 1.14.3 + registry-auth-token: 5.1.1 + registry-url: 6.0.1 + semver: 7.8.5 + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + + package-manager-detector@1.6.0: {} + + pako@2.1.0: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-ms@4.0.0: {} + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-browserify@1.0.1: {} + + path-data-parser@0.1.0: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.1 + minipass: 7.1.3 + + path-type@4.0.0: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + perfect-debounce@2.1.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@4.0.1: {} + + pinia-plugin-persistedstate@4.7.1(@nuxt/kit@3.21.8(magicast@0.5.3))(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))): + dependencies: + defu: 6.1.7 + optionalDependencies: + '@nuxt/kit': 3.21.8(magicast@0.5.3) + pinia: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + + pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@vue/devtools-api': 7.7.9 + vue: 3.5.38(typescript@6.0.3) + optionalDependencies: + typescript: 6.0.3 + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + + playwright-core@1.61.0: {} + + playwright@1.61.0: + dependencies: + playwright-core: 1.61.0 + optionalDependencies: + fsevents: 2.3.2 + + pngjs@5.0.0: {} + + pnpm-workspace-yaml@1.6.1: + dependencies: + yaml: 2.9.0 + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + popmotion@11.0.5: + dependencies: + framesync: 6.1.2 + hey-listen: 1.0.8 + style-value-types: 5.1.2 + tslib: 2.4.0 + + possible-typed-array-names@1.1.0: {} + + postcss-html@1.8.1: + dependencies: + htmlparser2: 8.0.2 + js-tokens: 9.0.1 + postcss: 8.5.15 + postcss-safe-parser: 6.0.0(postcss@8.5.15) + + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.15)(yaml@2.9.0): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 2.7.0 + postcss: 8.5.15 + yaml: 2.9.0 + + postcss-media-query-parser@0.2.3: {} + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@6.0.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-safe-parser@7.0.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-scss@4.0.9(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sorting@10.0.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.14 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + powershell-utils@0.1.0: {} + + prelude-ls@1.2.1: {} + + prettier@2.8.8: {} + + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + pretty-bytes@7.1.0: {} + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + property-information@7.2.0: {} + + prosemirror-changeset@2.4.1: + dependencies: + prosemirror-transform: 1.12.0 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-gapcursor@1.4.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.9 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-model@1.25.9: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-tables@1.8.5: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-transform@1.12.0: + dependencies: + prosemirror-model: 1.25.9 + + prosemirror-view@1.41.9: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + proto-list@1.2.4: {} + + proxy-from-env@2.1.0: {} + + publint@0.3.21: + dependencies: + '@publint/pack': 0.1.5 + package-manager-detector: 1.6.0 + picocolors: 1.1.1 + sade: 1.8.1 + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + pupa@3.3.0: + dependencies: + escape-goat: 4.0.0 + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.15.2: + dependencies: + side-channel: 1.1.1 + + quansync@0.2.11: {} + + quansync@1.0.0: {} + + queue-microtask@1.2.3: {} + + radix3@1.1.2: {} + + range-parser@1.2.1: {} + + rc9@3.0.1: + dependencies: + defu: 6.1.7 + destr: 2.0.5 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.2 + pify: 4.0.1 + strip-bom: 3.0.0 + + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.2.0 + strip-bom: 4.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + + readdir-glob@3.0.0: + dependencies: + minimatch: 10.2.5 + + readdirp@5.0.0: {} + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.2 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + registry-auth-token@5.1.1: + dependencies: + '@pnpm/npm-conf': 3.0.3 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + regjsgen@0.8.0: {} + + regjsparser@0.13.2: + dependencies: + jsesc: 3.1.0 + + reka-ui@2.10.0(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@floating-ui/dom': 1.7.6 + '@floating-ui/vue': 1.1.11(vue@3.5.38(typescript@6.0.3)) + '@internationalized/date': 3.12.2 + '@internationalized/number': 3.6.7 + '@tanstack/vue-virtual': 3.13.29(vue@3.5.38(typescript@6.0.3)) + '@vueuse/core': 14.3.0(vue@3.5.38(typescript@6.0.3)) + '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@6.0.3)) + aria-hidden: 1.2.6 + defu: 6.1.7 + ohash: 2.0.11 + vue: 3.5.38(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' + + relateurl@0.2.7: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@6.1.3: + dependencies: + glob: 13.0.6 + package-json-from-dist: 1.0.1 + + robust-predicates@3.0.3: {} + + rolldown-plugin-dts@0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@6.0.3)(vue-tsc@3.3.5(typescript@6.0.3)): + dependencies: + '@babel/generator': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@babel/parser': 8.0.0 + ast-kit: 3.0.0 + birpc: 4.0.0 + dts-resolver: 3.0.0(oxc-resolver@11.21.3) + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.3 + rolldown: 1.1.2 + optionalDependencies: + typescript: 6.0.3 + vue-tsc: 3.3.5(typescript@6.0.3) + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.0.0-rc.17: + dependencies: + '@oxc-project/types': 0.127.0 + '@rolldown/pluginutils': 1.0.0-rc.17 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-x64': 1.0.0-rc.17 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 + + rolldown@1.0.3: + dependencies: + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 + + rolldown@1.1.2: + dependencies: + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.2 + '@rolldown/binding-darwin-arm64': 1.1.2 + '@rolldown/binding-darwin-x64': 1.1.2 + '@rolldown/binding-freebsd-x64': 1.1.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.2 + '@rolldown/binding-linux-arm64-gnu': 1.1.2 + '@rolldown/binding-linux-arm64-musl': 1.1.2 + '@rolldown/binding-linux-ppc64-gnu': 1.1.2 + '@rolldown/binding-linux-s390x-gnu': 1.1.2 + '@rolldown/binding-linux-x64-gnu': 1.1.2 + '@rolldown/binding-linux-x64-musl': 1.1.2 + '@rolldown/binding-openharmony-arm64': 1.1.2 + '@rolldown/binding-wasm32-wasi': 1.1.2 + '@rolldown/binding-win32-arm64-msvc': 1.1.2 + '@rolldown/binding-win32-x64-msvc': 1.1.2 + + rollup-plugin-visualizer@7.0.1(rolldown@1.1.2)(rollup@4.62.2): + dependencies: + open: 11.0.0 + picomatch: 4.0.4 + source-map: 0.7.6 + yargs: 18.0.0 + optionalDependencies: + rolldown: 1.1.2 + rollup: 4.62.2 + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + rope-sequence@1.3.4: {} + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rw@1.3.3: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sass-embedded-all-unknown@1.100.0: + dependencies: + sass: 1.100.0 + optional: true + + sass-embedded-android-arm64@1.100.0: + optional: true + + sass-embedded-android-arm@1.100.0: + optional: true + + sass-embedded-android-riscv64@1.100.0: + optional: true + + sass-embedded-android-x64@1.100.0: + optional: true + + sass-embedded-darwin-arm64@1.100.0: + optional: true + + sass-embedded-darwin-x64@1.100.0: + optional: true + + sass-embedded-linux-arm64@1.100.0: + optional: true + + sass-embedded-linux-arm@1.100.0: + optional: true + + sass-embedded-linux-musl-arm64@1.100.0: + optional: true + + sass-embedded-linux-musl-arm@1.100.0: + optional: true + + sass-embedded-linux-musl-riscv64@1.100.0: + optional: true + + sass-embedded-linux-musl-x64@1.100.0: + optional: true + + sass-embedded-linux-riscv64@1.100.0: + optional: true + + sass-embedded-linux-x64@1.100.0: + optional: true + + sass-embedded-unknown-all@1.100.0: + dependencies: + sass: 1.100.0 + optional: true + + sass-embedded-win32-arm64@1.100.0: + optional: true + + sass-embedded-win32-x64@1.100.0: + optional: true + + sass-embedded@1.100.0: + dependencies: + '@bufbuild/protobuf': 2.12.0 + colorjs.io: 0.5.2 + immutable: 5.1.6 + rxjs: 7.8.2 + supports-color: 8.1.1 + sync-child-process: 1.0.2 + varint: 6.0.0 + optionalDependencies: + sass-embedded-all-unknown: 1.100.0 + sass-embedded-android-arm: 1.100.0 + sass-embedded-android-arm64: 1.100.0 + sass-embedded-android-riscv64: 1.100.0 + sass-embedded-android-x64: 1.100.0 + sass-embedded-darwin-arm64: 1.100.0 + sass-embedded-darwin-x64: 1.100.0 + sass-embedded-linux-arm: 1.100.0 + sass-embedded-linux-arm64: 1.100.0 + sass-embedded-linux-musl-arm: 1.100.0 + sass-embedded-linux-musl-arm64: 1.100.0 + sass-embedded-linux-musl-riscv64: 1.100.0 + sass-embedded-linux-musl-x64: 1.100.0 + sass-embedded-linux-riscv64: 1.100.0 + sass-embedded-linux-x64: 1.100.0 + sass-embedded-unknown-all: 1.100.0 + sass-embedded-win32-arm64: 1.100.0 + sass-embedded-win32-x64: 1.100.0 + + sass@1.100.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.6 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + optional: true + + sass@1.101.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.6 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + + sax@1.6.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + scule@1.3.0: {} + + secure-ls@2.0.0: + dependencies: + crypto-js: 4.2.0 + lz-string: 1.5.0 + + semver@6.3.1: {} + + semver@7.8.5: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@7.0.6: {} + + serve-placeholder@2.0.2: + dependencies: + defu: 6.1.7 + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@3.23.0: + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smob@1.6.2: {} + + smol-toml@1.6.1: {} + + socket.io-client@4.8.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.6 + socket.io-parser: 4.2.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + sortablejs@1.15.7: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + space-separated-tokens@2.0.2: {} + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + speakingurl@14.0.1: {} + + sprintf-js@1.0.3: {} + + stackback@0.0.2: {} + + standard-as-callback@2.1.0: {} + + statuses@2.0.2: {} + + std-env@4.1.0: {} + + stdin-discarder@0.3.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + streamx@2.28.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.1: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-comments@2.0.1: {} + + strip-final-newline@4.0.0: {} + + strip-json-comments@2.0.1: {} + + strip-json-comments@5.0.3: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + + style-mod@4.1.3: {} + + style-search@0.1.0: {} + + style-value-types@5.1.2: + dependencies: + hey-listen: 1.0.8 + tslib: 2.4.0 + + stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + postcss-html: 1.8.1 + stylelint: 17.13.0(typescript@6.0.3) + + stylelint-config-recess-order@7.7.0(stylelint-order@8.1.1(stylelint@17.13.0(typescript@6.0.3)))(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + stylelint: 17.13.0(typescript@6.0.3) + stylelint-order: 8.1.1(stylelint@17.13.0(typescript@6.0.3)) + + stylelint-config-recommended-scss@17.0.1(postcss@8.5.15)(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + postcss-scss: 4.0.9(postcss@8.5.15) + stylelint: 17.13.0(typescript@6.0.3) + stylelint-config-recommended: 18.0.0(stylelint@17.13.0(typescript@6.0.3)) + stylelint-scss: 7.2.0(stylelint@17.13.0(typescript@6.0.3)) + optionalDependencies: + postcss: 8.5.15 + + stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + postcss-html: 1.8.1 + semver: 7.8.5 + stylelint: 17.13.0(typescript@6.0.3) + stylelint-config-html: 1.1.0(postcss-html@1.8.1)(stylelint@17.13.0(typescript@6.0.3)) + stylelint-config-recommended: 18.0.0(stylelint@17.13.0(typescript@6.0.3)) + + stylelint-config-recommended@18.0.0(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + stylelint: 17.13.0(typescript@6.0.3) + + stylelint-config-standard@40.0.0(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + stylelint: 17.13.0(typescript@6.0.3) + stylelint-config-recommended: 18.0.0(stylelint@17.13.0(typescript@6.0.3)) + + stylelint-order@8.1.1(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + postcss: 8.5.15 + postcss-sorting: 10.0.0(postcss@8.5.15) + stylelint: 17.13.0(typescript@6.0.3) + + stylelint-scss@7.2.0(stylelint@17.13.0(typescript@6.0.3)): + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) + '@csstools/css-tokenizer': 4.0.0 + css-tree: 3.2.1 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + stylelint: 17.13.0(typescript@6.0.3) + + stylelint@17.13.0(typescript@6.0.3): + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + colord: 2.9.3 + cosmiconfig: 9.0.2(typescript@6.0.3) + css-functions-list: 3.3.3 + css-tree: 3.2.1 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.3 + global-modules: 2.0.0 + globby: 16.2.0 + globjoin: 0.1.4 + html-tags: 5.1.0 + ignore: 7.0.5 + import-meta-resolve: 4.2.0 + mathml-tag-names: 4.0.0 + meow: 14.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-safe-parser: 7.0.1(postcss@8.5.15) + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + string-width: 8.2.1 + supports-hyperlinks: 4.5.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 7.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + stylis@4.4.0: {} + + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + + supports-color@10.2.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@4.5.0: + dependencies: + has-flag: 5.0.1 + supports-color: 10.2.2 + + supports-preserve-symlinks-flag@1.0.0: {} + + sver@2.0.1: {} + + svg-tags@1.0.0: {} + + svgo@4.0.1: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.2.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + sync-child-process@1.0.2: + dependencies: + sync-message-port: 1.2.0 + + sync-message-port@1.2.0: {} + + synckit@0.11.13: + dependencies: + '@pkgr/core': 0.3.6 + + table@6.9.0: + dependencies: + ajv: 8.20.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tagged-tag@1.0.0: {} + + tailwind-csstree@0.3.3: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.1: {} + + tapable@2.3.3: {} + + tar-stream@3.2.0: + dependencies: + b4a: 1.8.1 + bare-fs: 4.7.2 + fast-fifo: 1.3.2 + streamx: 2.28.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar@7.5.16: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + teex@1.0.1: + dependencies: + streamx: 2.28.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + term-size@2.2.1: {} + + terser@5.48.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.17.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-decoder@1.2.7: + dependencies: + b4a: 1.8.1 + transitivePeerDependencies: + - react-native-b4a + + theme-colors@0.1.0: {} + + throttle-debounce@5.0.2: {} + + tinybench@2.9.0: {} + + tinyclip@0.1.14: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@2.1.0: {} + + tinyrainbow@3.1.0: {} + + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + ts-dedent@2.3.0: {} + + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.24.0 + tapable: 2.3.3 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsdown@0.22.3(@tsdown/css@0.22.3)(oxc-resolver@11.21.3)(publint@0.3.21)(typescript@6.0.3)(vue-tsc@3.3.5(typescript@6.0.3)): + dependencies: + ansis: 4.3.1 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.3 + picomatch: 4.0.4 + rolldown: 1.1.2 + rolldown-plugin-dts: 0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@6.0.3)(vue-tsc@3.3.5(typescript@6.0.3)) + semver: 7.8.5 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + optionalDependencies: + '@tsdown/css': 0.22.3(jiti@2.7.0)(postcss@8.5.15)(sass-embedded@1.100.0)(sass@1.101.0)(tsdown@0.22.3)(yaml@2.9.0) + publint: 0.3.21 + typescript: 6.0.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - vue-tsc + + tslib@2.3.0: {} + + tslib@2.4.0: {} + + tslib@2.8.1: {} + + turbo@2.9.18: + optionalDependencies: + '@turbo/darwin-64': 2.9.18 + '@turbo/darwin-arm64': 2.9.18 + '@turbo/linux-64': 2.9.18 + '@turbo/linux-arm64': 2.9.18 + '@turbo/windows-64': 2.9.18 + '@turbo/windows-arm64': 2.9.18 + + tw-animate-css@1.4.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.16.0: {} + + type-fest@4.41.0: {} + + type-fest@5.7.0: + dependencies: + tagged-tag: 1.0.0 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + typescript@6.0.3: {} + + uc.micro@2.1.0: {} + + ufo@1.6.4: {} + + ultrahtml@1.6.0: {} + + unbash@4.0.1: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + + uncrypto@0.1.3: {} + + unctx@2.5.0: + dependencies: + acorn: 8.17.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + unplugin: 2.3.11 + + undici-types@7.24.6: {} + + undici-types@8.3.0: {} + + undici@7.28.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unicorn-magic@0.3.0: {} + + unicorn-magic@0.4.0: {} + + unimport@6.3.0(oxc-parser@0.135.0)(rolldown@1.1.2): + dependencies: + acorn: 8.17.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + pathe: 2.0.3 + picomatch: 4.0.4 + pkg-types: 2.3.1 + scule: 1.3.0 + strip-literal: 3.1.0 + tinyglobby: 0.2.17 + unplugin: 3.0.0 + unplugin-utils: 0.3.1 + optionalDependencies: + oxc-parser: 0.135.0 + rolldown: 1.1.2 + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + unplugin-dts@1.0.2(esbuild@0.28.1)(rolldown@1.1.2)(rollup@4.62.2)(typescript@6.0.3)(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + '@volar/typescript': 2.4.28 + compare-versions: 6.1.1 + debug: 4.4.3 + kolorist: 1.8.0 + local-pkg: 1.2.1 + magic-string: 0.30.21 + typescript: 6.0.3 + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.28.1 + rolldown: 1.1.2 + rollup: 4.62.2 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + + unplugin-utils@0.3.1: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.4 + + unplugin-vue@7.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0): + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@vue/reactivity': 3.5.38 + obug: 2.1.3 + unplugin: 3.0.0 + vite: 8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + transitivePeerDependencies: + - '@types/node' + - '@vitejs/devtools' + - esbuild + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.17.0 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + + unplugin@3.0.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + + unstorage@1.17.5(db0@0.3.4)(ioredis@5.11.1): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.1 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + optionalDependencies: + db0: 0.3.4 + ioredis: 5.11.1 + + untun@0.1.3: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 1.1.2 + + untyped@2.0.0: + dependencies: + citty: 0.1.6 + defu: 6.1.7 + jiti: 2.7.0 + knitwork: 1.3.0 + scule: 1.3.0 + + unwasm@0.5.3: + dependencies: + exsolve: 1.0.8 + knitwork: 1.3.0 + magic-string: 0.30.21 + mlly: 1.8.2 + pathe: 2.0.3 + pkg-types: 2.3.1 + + upath@1.2.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-notifier@7.3.1: + dependencies: + boxen: 8.0.1 + chalk: 5.6.2 + configstore: 7.1.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 + is-npm: 6.1.0 + latest-version: 9.0.0 + pupa: 3.3.0 + semver: 7.8.5 + xdg-basedir: 5.1.0 + + uqr@0.1.3: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@14.0.1: {} + + valibot@1.4.1(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + + varint@6.0.0: {} + + vee-validate@4.15.1(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@vue/devtools-api': 7.7.9 + type-fest: 4.41.0 + vue: 3.5.38(typescript@6.0.3) + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-dev-rpc@2.0.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + birpc: 4.0.0 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vite-hot-client: 2.2.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + + vite-hot-client@2.2.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + + vite-plugin-compression@0.5.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + chalk: 4.1.2 + debug: 4.4.3 + fs-extra: 10.1.0 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + + vite-plugin-inspect@11.4.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + ansis: 4.3.1 + error-stack-parser-es: 1.0.5 + obug: 2.1.3 + ohash: 2.0.11 + open: 11.0.0 + perfect-debounce: 2.1.0 + sirv: 3.0.2 + unplugin-utils: 0.3.1 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + + vite-plugin-lazy-import@1.0.7: + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + es-module-lexer: 1.7.0 + rollup: 4.62.2 + xe-utils: 3.9.1 + + vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.17 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + workbox-build: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - supports-color + + vite-plugin-vue-devtools@8.1.3(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@vue/devtools-core': 8.1.3(vue@3.5.38(typescript@6.0.3)) + '@vue/devtools-kit': 8.1.3 + '@vue/devtools-shared': 8.1.3 + sirv: 3.0.2 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-inspector: 6.0.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + transitivePeerDependencies: + - '@nuxt/kit' + - supports-color + - vue + + vite-plugin-vue-inspector@6.0.0(vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.7) + '@vue/compiler-dom': 3.5.38 + kolorist: 1.8.0 + magic-string: 0.30.21 + vite: 8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + + vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.0-rc.17 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.9.4 + esbuild: 0.28.1 + fsevents: 2.3.3 + jiti: 2.7.0 + sass: 1.101.0 + sass-embedded: 1.100.0 + terser: 5.48.0 + yaml: 2.9.0 + + vite@8.0.10(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.0-rc.17 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.0.0 + esbuild: 0.28.1 + fsevents: 2.3.3 + jiti: 2.7.0 + sass: 1.101.0 + sass-embedded: 1.100.0 + terser: 5.48.0 + yaml: 2.9.0 + + vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.0.0 + esbuild: 0.28.1 + fsevents: 2.3.3 + jiti: 2.7.0 + sass: 1.101.0 + sass-embedded: 1.100.0 + terser: 5.48.0 + yaml: 2.9.0 + + vitest@4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.0.10(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.9.4 + happy-dom: 20.10.6 + transitivePeerDependencies: + - msw + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-uri@3.1.0: {} + + vue-codemirror6@1.5.2(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.1)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)(codemirror@6.0.2)(style-mod@4.1.3)(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.10.3 + '@codemirror/language': 6.12.3 + '@codemirror/lint': 6.9.7 + '@codemirror/search': 6.7.1 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.1 + codemirror: 6.0.2 + style-mod: 4.1.3 + vue: 3.5.38(typescript@6.0.3) + vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + + vue-component-type-helpers@3.3.5: {} + + vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)): + dependencies: + vue: 3.5.38(typescript@6.0.3) + + vue-eslint-parser@10.4.1(eslint@10.5.0(jiti@2.7.0)): + dependencies: + debug: 4.4.3 + eslint: 10.5.0(jiti@2.7.0) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + vue-i18n@11.4.6(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@intlify/core-base': 11.4.6 + '@intlify/devtools-types': 11.4.6 + '@intlify/shared': 11.4.6 + '@vue/devtools-api': 6.6.4 + vue: 3.5.38(typescript@6.0.3) + + vue-json-pretty@2.6.0(vue@3.5.38(typescript@6.0.3)): + dependencies: + vue: 3.5.38(typescript@6.0.3) + + vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@babel/generator': 8.0.0 + '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@6.0.3)) + '@vue/devtools-api': 8.1.3 + ast-walker-scope: 0.9.0 + chokidar: 5.0.0 + json5: 2.2.3 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + muggle-string: 0.4.1 + pathe: 2.0.3 + picomatch: 4.0.4 + scule: 1.3.0 + tinyglobby: 0.2.17 + unplugin: 3.0.0 + unplugin-utils: 0.3.1 + vue: 3.5.38(typescript@6.0.3) + yaml: 2.9.0 + optionalDependencies: + '@vue/compiler-sfc': 3.5.38 + pinia: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + vite: 8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.48.0)(yaml@2.9.0) + + vue-tippy@6.7.1(vue@3.5.38(typescript@6.0.3)): + dependencies: + tippy.js: 6.3.7 + vue: 3.5.38(typescript@6.0.3) + + vue-tsc@3.3.5(typescript@6.0.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.5 + typescript: 6.0.3 + + vue@3.5.38(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-sfc': 3.5.38 + '@vue/runtime-dom': 3.5.38 + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) + '@vue/shared': 3.5.38 + optionalDependencies: + typescript: 6.0.3 + + vxe-pc-ui@4.15.4(vue@3.5.38(typescript@6.0.3)): + dependencies: + '@vxe-ui/core': 4.4.14(vue@3.5.38(typescript@6.0.3)) + transitivePeerDependencies: + - vue + + vxe-table@4.19.17(vue@3.5.38(typescript@6.0.3)): + dependencies: + vxe-pc-ui: 4.15.4(vue@3.5.38(typescript@6.0.3)) + transitivePeerDependencies: + - vue + + w3c-keyname@2.2.8: {} + + walk-up-path@4.0.0: {} + + watermark-js-plus@1.6.3: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@4.0.2: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@3.0.0: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + when-exit@2.1.5: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-module@2.0.1: {} + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + word-wrap@1.2.5: {} + + workbox-background-sync@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-broadcast-update@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-build@7.4.1: + dependencies: + '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) + '@babel/core': 7.29.7 + '@babel/preset-env': 7.29.7(@babel/core@7.29.7) + '@babel/runtime': 7.29.7 + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(rollup@4.62.2) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.2) + '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.2) + '@trickfilm400/rollup-plugin-off-main-thread': 3.0.0-pre1 + ajv: 8.20.0 + common-tags: 1.8.2 + eta: 4.6.0 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 11.1.0 + pretty-bytes: 5.6.0 + rollup: 4.62.2 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.4.1 + workbox-broadcast-update: 7.4.1 + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-google-analytics: 7.4.1 + workbox-navigation-preload: 7.4.1 + workbox-precaching: 7.4.1 + workbox-range-requests: 7.4.1 + workbox-recipes: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + workbox-streams: 7.4.1 + workbox-sw: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-core@7.4.1: {} + + workbox-expiration@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-google-analytics@7.4.1: + dependencies: + workbox-background-sync: 7.4.1 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-navigation-preload@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-precaching@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-range-requests@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-recipes@7.4.1: + dependencies: + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-precaching: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-routing@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-strategies@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-streams@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + + workbox-sw@7.4.1: {} + + workbox-window@7.4.1: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.4.1 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + + ws@8.21.0: {} + + wsl-utils@0.3.1: + dependencies: + is-wsl: 3.1.1 + powershell-utils: 0.1.0 + + xdg-basedir@5.1.0: {} + + xe-utils@3.9.1: {} + + xe-utils@4.0.9: {} + + xml-name-validator@4.0.0: {} + + xmlhttprequest-ssl@2.1.2: {} + + xss@1.0.15: + dependencies: + commander: 2.20.3 + cssfilter: 0.0.10 + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@5.0.0: {} + + yaml-eslint-parser@1.3.2: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.9.0 + + yaml-eslint-parser@2.0.0: + dependencies: + eslint-visitor-keys: 5.0.1 + yaml: 2.9.0 + + yaml@2.9.0: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@22.0.0: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + yoctocolors@2.1.2: {} + + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.1: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.7.0 + '@speed-highlight/core': 1.2.17 + cookie-es: 3.1.1 + youch-core: 0.3.3 + + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + + zip-stream@7.0.5: + dependencies: + compress-commons: 7.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + zod-defaults@0.1.3(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zod@4.4.3: {} + + zrender@6.1.0: + dependencies: + tslib: 2.3.0 + + zwitch@2.0.4: {} + + zx@8.8.5: {} diff --git a/deploy/fba/fba-ui-src/pnpm-workspace.yaml b/deploy/fba/fba-ui-src/pnpm-workspace.yaml new file mode 100644 index 0000000..7df1ad0 --- /dev/null +++ b/deploy/fba/fba-ui-src/pnpm-workspace.yaml @@ -0,0 +1,212 @@ +packages: + - internal/* + - internal/lint-configs/* + - packages/* + - packages/@core/base/* + - packages/@core/ui-kit/* + - packages/@core/forward/* + - packages/@core/* + - packages/effects/* + - packages/business/* + - apps/* + - apps/*/src/plugins/* + - scripts/* + - docs + - playground + +publicHoistPattern: + - lefthook + - eslint + - oxfmt + - oxlint + - stylelint + - '*postcss*' + - '@commitlint/*' + - czg + +strictPeerDependencies: false +autoInstallPeers: true +dedupePeerDependents: true +verifyDepsBeforeRun: false +minimumReleaseAge: 0 + +overrides: + '@ast-grep/napi': 'catalog:' + '@ctrl/tinycolor': 'catalog:' + clsx: 'catalog:' + pinia: 'catalog:' + vue: 'catalog:' + +catalog: + '@ast-grep/napi': ^0.43.0 + '@changesets/changelog-github': ^0.7.0 + '@changesets/cli': ^2.31.0 + '@changesets/git': ^3.0.4 + '@clack/prompts': ^1.5.1 + '@codemirror/lang-python': ^6.2.1 + '@codemirror/lang-sql': ^6.10.0 + '@codemirror/theme-one-dark': ^6.1.3 + '@commitlint/cli': ^21.0.2 + '@commitlint/config-conventional': ^21.0.2 + '@ctrl/tinycolor': ^4.2.0 + '@eslint-community/eslint-plugin-eslint-comments': ^4.7.2 + '@eslint/js': ^10.0.1 + '@iconify/json': ^2.2.487 + '@iconify/tailwind4': ^1.2.3 + '@iconify/vue': ^5.0.1 + '@intlify/core-base': ^11.4.5 + '@intlify/unplugin-vue-i18n': ^11.2.3 + '@jspm/generator': ^2.16.1 + '@lucide/vue': ^1.18.0 + '@manypkg/get-packages': ^3.1.0 + '@pnpm/workspace.read-manifest': ^1000.3.1 + '@stylistic/stylelint-plugin': ^5.2.0 + '@tailwindcss/typography': ^0.5.20 + '@tailwindcss/vite': ^4.3.1 + '@tanstack/vue-store': ^0.11.0 + '@tiptap/core': ^3.26.1 + '@tiptap/extension-highlight': ^3.26.1 + '@tiptap/extension-image': ^3.26.1 + '@tiptap/extension-link': ^3.26.1 + '@tiptap/extension-placeholder': ^3.26.1 + '@tiptap/extension-text-align': ^3.26.1 + '@tiptap/extension-text-style': ^3.26.1 + '@tiptap/extension-underline': ^3.26.1 + '@tiptap/pm': ^3.26.1 + '@tiptap/starter-kit': ^3.26.1 + '@tiptap/vue-3': ^3.26.1 + '@tsdown/css': ^0.22.2 + '@types/archiver': ^8.0.0 + '@types/html-minifier-terser': ^7.0.2 + '@types/json-bigint': ^1.0.4 + '@types/lodash.clonedeep': ^4.5.9 + '@types/node': ^25.9.3 + '@types/nprogress': ^0.2.3 + '@types/qrcode': ^1.5.6 + '@types/qs': ^6.15.1 + '@types/sortablejs': ^1.15.9 + '@typescript-eslint/parser': ^8.61.1 + '@vee-validate/zod': ^4.15.1 + '@vitejs/plugin-vue': ^6.0.7 + '@vitejs/plugin-vue-jsx': ^5.1.5 + '@vue/shared': ^3.5.38 + '@vue/test-utils': ^2.4.11 + '@vueuse/core': ^14.3.0 + '@vueuse/integrations': ^14.3.0 + '@vueuse/motion': ^3.0.3 + antdv-next: ^1.3.5 + archiver: ^8.0.0 + axios: ^1.18.0 + axios-mock-adapter: ^2.1.0 + cac: ^7.0.0 + chalk: ^5.6.2 + cheerio: ^1.2.0 + circular-dependency-scanner: ^3.0.1 + class-variance-authority: ^0.7.1 + clsx: ^2.1.1 + commitlint-plugin-function-rules: ^5.0.1 + consola: ^3.4.2 + croner: ^10.0.1 + cross-env: ^10.1.0 + cspell: ^10.0.1 + cz-git: ^1.13.1 + czg: ^1.13.1 + dayjs: ^1.11.21 + defu: ^6.1.7 + dotenv: ^17.4.2 + echarts: ^6.1.0 + es-toolkit: ^1.47.1 + eslint: ^10.5.0 + eslint-plugin-better-tailwindcss: ^4.6.0 + eslint-plugin-command: ^3.5.2 + eslint-plugin-jsonc: ^3.2.0 + eslint-plugin-n: ^18.1.0 + eslint-plugin-perfectionist: ^5.9.1 + eslint-plugin-pnpm: ^1.6.1 + eslint-plugin-unused-imports: ^4.4.1 + eslint-plugin-vue: ^10.9.2 + eslint-plugin-yml: ^3.4.0 + execa: ^9.6.1 + find-up: ^8.0.0 + get-port: ^7.2.0 + globals: ^17.6.0 + happy-dom: ^20.10.4 + html-minifier-terser: ^7.2.0 + is-ci: ^4.1.0 + json-bigint: ^1.0.0 + knip: ^6.17.0 + lefthook: ^2.1.9 + lodash.clonedeep: ^4.5.0 + md-editor-v3: ^6.4.2 + mitt: ^3.0.1 + nitropack: ^2.13.4 + nprogress: ^0.2.0 + ora: ^9.4.0 + oxfmt: ^0.55.0 + oxlint: ^1.70.0 + oxlint-tsgolint: ^0.23.0 + pinia: ^3.0.4 + pinia-plugin-persistedstate: ^4.7.1 + pkg-types: ^2.3.1 + playwright: ^1.61.0 + postcss: ^8.5.15 + postcss-html: ^1.8.1 + postcss-scss: ^4.0.9 + publint: ^0.3.21 + qrcode: ^1.5.4 + qs: ^6.15.2 + reka-ui: ^2.9.10 + resolve.exports: ^2.0.3 + rimraf: ^6.1.3 + rollup-plugin-visualizer: ^7.0.1 + sass: ^1.101.0 + sass-embedded: ^1.100.0 + secure-ls: ^2.0.0 + socket.io-client: ^4.8.3 + sortablejs: ^1.15.7 + stylelint: ^17.13.0 + stylelint-config-recess-order: ^7.7.0 + stylelint-config-recommended: ^18.0.0 + stylelint-config-recommended-scss: ^17.0.1 + stylelint-config-recommended-vue: ^1.6.1 + stylelint-config-standard: ^40.0.0 + stylelint-order: ^8.1.1 + stylelint-scss: ^7.2.0 + tailwind-merge: ^3.6.0 + tailwindcss: ^4.3.1 + theme-colors: ^0.1.0 + tippy.js: ^6.3.7 + tsdown: ^0.22.2 + turbo: ^2.9.18 + tw-animate-css: ^1.4.0 + typescript: ^6.0.3 + unplugin-dts: ^1.0.2 + unplugin-vue: ^7.2.0 + vee-validate: ^4.15.1 + vite: 8.0.10 + vite-plugin-compression: ^0.5.1 + vite-plugin-lazy-import: ^1.0.7 + vite-plugin-pwa: ^1.3.0 + vite-plugin-vue-devtools: ^8.1.3 + vitest: ^4.1.9 + vue: ^3.5.38 + vue-codemirror6: ^1.5.1 + vue-eslint-parser: ^10.4.1 + vue-i18n: ^11.4.5 + vue-json-pretty: ^2.6.0 + vue-router: ^5.1.0 + vue-tippy: ^6.7.1 + vue-tsc: ^3.3.5 + vxe-pc-ui: ^4.14.39 + vxe-table: ^4.19.14 + watermark-js-plus: ^1.6.3 + yaml-eslint-parser: ^2.0.0 + zod: ^3.25.76 + zod-defaults: 0.1.3 + +allowBuilds: + '@parcel/watcher': true + core-js: true + esbuild: true + lefthook: true + vue-demi: true diff --git a/deploy/fba/fba-ui-src/scripts/clean.mjs b/deploy/fba/fba-ui-src/scripts/clean.mjs new file mode 100644 index 0000000..2049e45 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/clean.mjs @@ -0,0 +1,141 @@ +import { promises as fs } from 'node:fs'; +import { join, normalize } from 'node:path'; + +const rootDir = process.cwd(); + +// 控制并发数量,避免创建过多的并发任务 +const CONCURRENCY_LIMIT = 10; + +// 需要跳过的目录,避免进入这些目录进行清理 +const SKIP_DIRS = new Set(['.DS_Store', '.git', '.idea', '.vscode']); + +/** + * 处理单个文件/目录项 + * @param {string} currentDir - 当前目录路径 + * @param {string} item - 文件/目录名 + * @param {string[]} targets - 要删除的目标列表 + * @param {number} _depth - 当前递归深度 + * @returns {Promise} - 是否需要进一步递归处理 + */ +async function processItem(currentDir, item, targets, _depth) { + // 跳过特殊目录 + if (SKIP_DIRS.has(item)) { + return false; + } + + try { + const itemPath = normalize(join(currentDir, item)); + + if (targets.includes(item)) { + // 匹配到目标目录或文件时直接删除 + await fs.rm(itemPath, { force: true, recursive: true }); + console.log(`✅ Deleted: ${itemPath}`); + return false; // 已删除,无需递归 + } + + // 使用 readdir 的 withFileTypes 选项,避免额外的 lstat 调用 + return true; // 可能需要递归,由调用方决定 + } catch (error) { + // 更详细的错误信息 + if (error.code === 'ENOENT') { + // 文件不存在,可能已被删除,这是正常情况 + return false; + } else if (error.code === 'EPERM' || error.code === 'EACCES') { + console.error(`❌ Permission denied: ${item} in ${currentDir}`); + } else { + console.error( + `❌ Error handling item ${item} in ${currentDir}: ${error.message}`, + ); + } + return false; + } +} + +/** + * 递归查找并删除目标目录(并发优化版本) + * @param {string} currentDir - 当前遍历的目录路径 + * @param {string[]} targets - 要删除的目标列表 + * @param {number} depth - 当前递归深度,避免过深递归 + */ +async function cleanTargetsRecursively(currentDir, targets, depth = 0) { + // 限制递归深度,避免无限递归 + if (depth > 10) { + console.warn(`Max recursion depth reached at: ${currentDir}`); + return; + } + + let dirents; + try { + // 使用 withFileTypes 选项,一次性获取文件类型信息,避免后续 lstat 调用 + dirents = await fs.readdir(currentDir, { withFileTypes: true }); + } catch (error) { + // 如果无法读取目录,可能已被删除或权限不足 + console.warn(`Cannot read directory ${currentDir}: ${error.message}`); + return; + } + + // 分批处理,控制并发数量 + for (let i = 0; i < dirents.length; i += CONCURRENCY_LIMIT) { + const batch = dirents.slice(i, i + CONCURRENCY_LIMIT); + + const tasks = batch.map(async (dirent) => { + const item = dirent.name; + const shouldRecurse = await processItem(currentDir, item, targets, depth); + + // 如果是目录且没有被删除,则递归处理 + if (shouldRecurse && dirent.isDirectory()) { + const itemPath = normalize(join(currentDir, item)); + return cleanTargetsRecursively(itemPath, targets, depth + 1); + } + + return null; + }); + + // 并发执行当前批次的任务 + const results = await Promise.allSettled(tasks); + + // 检查是否有失败的任务(可选:用于调试) + const failedTasks = results.filter( + (result) => result.status === 'rejected', + ); + if (failedTasks.length > 0) { + console.warn( + `${failedTasks.length} tasks failed in batch starting at index ${i} in directory: ${currentDir}`, + ); + } + } +} + +(async function startCleanup() { + // 要删除的目录及文件名称 + const targets = ['node_modules', 'dist', '.turbo', 'dist.zip']; + const deleteLockFile = process.argv.includes('--del-lock'); + const cleanupTargets = [...targets]; + + if (deleteLockFile) { + cleanupTargets.push('pnpm-lock.yaml'); + } + + console.log( + `🚀 Starting cleanup of targets: ${cleanupTargets.join(', ')} from root: ${rootDir}`, + ); + + const startTime = Date.now(); + + try { + // 先统计要删除的目标数量 + console.log('📊 Scanning for cleanup targets...'); + + await cleanTargetsRecursively(rootDir, cleanupTargets); + + const endTime = Date.now(); + const duration = (endTime - startTime) / 1000; + + console.log( + `✨ Cleanup process completed successfully in ${duration.toFixed(2)}s`, + ); + } catch (error) { + console.error(`💥 Unexpected error during cleanup: ${error.message}`); + process.exit(1); + } +})(); diff --git a/deploy/fba/fba-ui-src/scripts/deploy/nginx.conf b/deploy/fba/fba-ui-src/scripts/deploy/nginx.conf new file mode 100644 index 0000000..e28ba12 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/deploy/nginx.conf @@ -0,0 +1,118 @@ +worker_processes auto; +pid /run/nginx.pid; +error_log /var/log/nginx/error.log; + +# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 8192; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + client_max_body_size 5M; + client_body_buffer_size 5M; + + gzip on; + gzip_comp_level 6; + gzip_types text/plain text/css text/javascript application/javascript application/json application/xml+rss application/xml+atom image/svg+xml font/otf font/ttf font/opentype; + gzip_min_length 256; + gzip_vary on; + + keepalive_timeout 300; + + access_log /var/log/nginx/access.log; + + server { + listen 80; + listen [::]:80; + # https + # xxx.com 应该与 .env.production 中的配置保持一致 + server_name xxx.com; + rewrite ^(.*)$ https://$host$1 permanent; + } + + server { + # 非 https + # server_name 127.0.0.1; + + # https + # 与上方配置保持一致 + server_name xxx.com; + + # https + listen 443 ssl; + ssl_certificate /etc/ssl/xxx.pem; # your 证书,自行更新 + ssl_certificate_key /etc/ssl/xxx.key; # your 密钥,自行更新 + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + client_max_body_size 5M; + + root /var/www/fba_ui; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/v1/ { + proxy_pass http://fba_server:8001; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location /ws/socket.io/ { + proxy_pass http://fba_server:8001/ws/socket.io/; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + # WebSocket 支持 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + } + + location /flower/ { + proxy_pass http://fba_celery_flower:8555; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_redirect off; + + # WebSocket 支持 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location /static { + alias /var/www/fba_server/backend/static; + } + + location /static/upload { + alias /var/www/fba_server/backend/static/upload; + } + } +} diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/README.md b/deploy/fba/fba-ui-src/scripts/turbo-run/README.md new file mode 100644 index 0000000..4f90a59 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/README.md @@ -0,0 +1,59 @@ +# @vben/turbo-run + +`turbo-run` 是一个命令行工具,允许你在多个包中并行运行命令。它提供了一个交互式的界面,让你可以选择要运行命令的包。 + +## 特性 + +- 🚀 交互式选择要运行的包 +- 📦 支持 monorepo 项目结构 +- 🔍 自动检测可用的命令 +- 🎯 精确过滤目标包 + +## 安装 + +```bash +pnpm add -D @vben/turbo-run +``` + +## 使用方法 + +基本语法: + +```bash +turbo-run [script] +``` + +例如,如果你想运行 `dev` 命令: + +```bash +turbo-run dev +``` + +工具会自动检测哪些包有 `dev` 命令,并提供一个交互式界面让你选择要运行的包。 + +## 示例 + +假设你的项目中有以下包: + +- `@vben/app` +- `@vben/admin` +- `@vben/website` + +当你运行: + +```bash +turbo-run dev +``` + +工具会: + +1. 检测哪些包有 `dev` 命令 +2. 显示一个交互式选择界面 +3. 让你选择要运行命令的包 +4. 使用 `pnpm --filter` 在选定的包中运行命令 + +## 注意事项 + +- 确保你的项目使用 pnpm 作为包管理器 +- 确保目标包在 `package.json` 中定义了相应的脚本命令 +- 该工具需要在 monorepo 项目的根目录下运行 diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/bin/turbo-run.mjs b/deploy/fba/fba-ui-src/scripts/turbo-run/bin/turbo-run.mjs new file mode 100644 index 0000000..407754d --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/bin/turbo-run.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/index.mjs'); diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/package.json b/deploy/fba/fba-ui-src/scripts/turbo-run/package.json new file mode 100644 index 0000000..0ab451f --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vben/turbo-run", + "version": "5.7.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm exec tsdown" + }, + "files": [ + "dist" + ], + "bin": { + "turbo-run": "./bin/turbo-run.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@clack/prompts": "catalog:", + "@vben/node-utils": "workspace:*", + "cac": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/src/index.ts b/deploy/fba/fba-ui-src/scripts/turbo-run/src/index.ts new file mode 100644 index 0000000..62b11d0 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/src/index.ts @@ -0,0 +1,23 @@ +import { consola } from '@vben/node-utils'; + +import { cac } from 'cac'; + +import { run } from './run'; + +try { + const turboRun = cac('turbo-run'); + + turboRun + .command('[script]') + .usage(`Run turbo interactively.`) + .action(async (command: string) => { + run({ command }); + }); + + turboRun.usage('turbo-run'); + turboRun.help(); + turboRun.parse(); +} catch (error) { + consola.error(error); + process.exit(1); +} diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/src/run.ts b/deploy/fba/fba-ui-src/scripts/turbo-run/src/run.ts new file mode 100644 index 0000000..31f9f52 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/src/run.ts @@ -0,0 +1,67 @@ +import { execaCommand, getPackages } from '@vben/node-utils'; + +import { cancel, isCancel, select } from '@clack/prompts'; + +interface RunOptions { + command?: string; +} + +export async function run(options: RunOptions) { + const { command } = options; + if (!command) { + console.error('Please enter the command to run'); + process.exit(1); + } + const { packages } = await getPackages(); + // const appPkgs = await findApps(process.cwd(), packages); + // const websitePkg = packages.find( + // (item) => item.packageJson.name === '@vben/website', + // ); + + // 只显示有对应命令的包 + const selectPkgs = packages.filter((pkg) => { + return (pkg?.packageJson as Record)?.scripts?.[command]; + }); + + let selectPkg: string | symbol; + if (selectPkgs.length > 1) { + selectPkg = await select({ + message: `Select the app you need to run [${command}]:`, + options: selectPkgs.map((item) => ({ + label: item?.packageJson.name, + value: item?.packageJson.name, + })), + }); + + if (isCancel(selectPkg) || !selectPkg) { + cancel('👋 Has cancelled'); + process.exit(0); + } + } else { + selectPkg = selectPkgs[0]?.packageJson?.name ?? ''; + } + + if (!selectPkg) { + console.error('No app found'); + process.exit(1); + } + + execaCommand(`pnpm --filter=${selectPkg} run ${command}`, { + stdio: 'inherit', + }); +} + +/** + * 过滤app包 + * @param root + * @param packages + */ +// async function findApps(root: string, packages: Package[]) { +// // apps内的 +// const appPackages = packages.filter((pkg) => { +// const viteConfigExists = fs.existsSync(join(pkg.dir, 'vite.config.mts')); +// return pkg.dir.startsWith(join(root, 'apps')) && viteConfigExists; +// }); + +// return appPackages; +// } diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/tsconfig.json b/deploy/fba/fba-ui-src/scripts/turbo-run/tsconfig.json new file mode 100644 index 0000000..b2ec3b6 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/scripts/turbo-run/tsdown.config.ts b/deploy/fba/fba-ui-src/scripts/turbo-run/tsdown.config.ts new file mode 100644 index 0000000..c4f51c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/turbo-run/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/scripts/vsh/README.md b/deploy/fba/fba-ui-src/scripts/vsh/README.md new file mode 100644 index 0000000..61140d6 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/README.md @@ -0,0 +1,56 @@ +# @vben/vsh + +一个 Shell 脚本工具集合,用于 Vue Vben Admin 项目的开发和管理。 + +## 功能特性 + +- 🚀 基于 Node.js 的现代化 Shell 工具 +- 📦 支持模块化开发和按需加载 +- 🔍 提供依赖检查和分析功能 +- 🔄 支持循环依赖扫描 +- 📝 提供包发布检查功能 + +## 安装 + +```bash +# 使用 pnpm 安装 +pnpm add -D @vben/vsh + +# 或者使用 npm +npm install -D @vben/vsh + +# 或者使用 yarn +yarn add -D @vben/vsh +``` + +## 使用方法 + +### 全局安装 + +```bash +# 全局安装 +pnpm add -g @vben/vsh + +# 使用 vsh 命令 +vsh [command] +``` + +### 本地使用 + +```bash +# 在 package.json 中添加脚本 +{ + "scripts": { + "vsh": "vsh" + } +} + +# 运行命令 +pnpm vsh [command] +``` + +## 命令列表 + +- `vsh check-deps`: 检查项目依赖 +- `vsh scan-circular`: 扫描循环依赖 +- `vsh publish-check`: 检查包发布配置 diff --git a/deploy/fba/fba-ui-src/scripts/vsh/bin/vsh.mjs b/deploy/fba/fba-ui-src/scripts/vsh/bin/vsh.mjs new file mode 100644 index 0000000..407754d --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/bin/vsh.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/index.mjs'); diff --git a/deploy/fba/fba-ui-src/scripts/vsh/env.d.ts b/deploy/fba/fba-ui-src/scripts/vsh/env.d.ts new file mode 100644 index 0000000..6c21162 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/env.d.ts @@ -0,0 +1 @@ +declare module 'circular-dependency-scanner'; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/package.json b/deploy/fba/fba-ui-src/scripts/vsh/package.json new file mode 100644 index 0000000..cb74cee --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/package.json @@ -0,0 +1,31 @@ +{ + "name": "@vben/vsh", + "version": "5.7.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm exec tsdown" + }, + "files": [ + "dist" + ], + "bin": { + "vsh": "./bin/vsh.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@vben/node-utils": "workspace:*", + "cac": "catalog:", + "circular-dependency-scanner": "catalog:", + "knip": "catalog:", + "publint": "catalog:" + } +} diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/check-circular/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/check-circular/index.ts new file mode 100644 index 0000000..999f7f2 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/check-circular/index.ts @@ -0,0 +1,217 @@ +import type { CAC } from 'cac'; + +import { access, mkdtemp, readFile, rm } from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { extname, join } from 'node:path'; + +import { execa, getStagedFiles } from '@vben/node-utils'; + +const require = createRequire(import.meta.url); +const circularScannerCli = + require.resolve('circular-dependency-scanner/dist/cli.js'); + +// 默认配置 +const DEFAULT_CONFIG = { + allowedExtensions: ['.cjs', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue'], + ignoreDirs: [ + 'dist', + '.turbo', + 'output', + '.cache', + 'scripts', + 'internal', + 'packages/effects/request/src/', + 'packages/@core/ui-kit/menu-ui/src/', + 'packages/@core/ui-kit/popup-ui/src/', + ], + threshold: 0, // 循环依赖的阈值 +} as const; + +// 类型定义 +type CircularDependencyResult = string[]; + +interface CheckCircularConfig { + allowedExtensions?: string[]; + ignoreDirs?: string[]; + threshold?: number; +} + +interface CommandOptions { + config?: CheckCircularConfig; + staged: boolean; + verbose: boolean; +} + +// 缓存机制 +const cache = new Map(); + +async function detectCircularDependencies({ + cwd, + ignorePattern, + staged, +}: { + cwd: string; + ignorePattern: string; + staged: boolean; +}): Promise { + const tempDir = await mkdtemp(join(tmpdir(), 'vsh-check-circular-')); + const outputFile = join(tempDir, 'circles.json'); + + try { + const args = [circularScannerCli, cwd, '--output', outputFile]; + + if (staged) { + args.push('--absolute'); + } + + args.push('--ignore', ignorePattern); + + await execa(process.execPath, args, { + cwd, + }); + + await access(outputFile); + const output = await readFile(outputFile, 'utf8'); + return JSON.parse(output) as CircularDependencyResult[]; + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') { + return []; + } + throw error; + } finally { + await rm(tempDir, { force: true, recursive: true }); + } +} + +/** + * 格式化循环依赖的输出 + * @param circles - 循环依赖结果 + */ +function formatCircles(circles: CircularDependencyResult[]): void { + if (circles.length === 0) { + console.log('✅ No circular dependencies found'); + return; + } + + console.log('⚠️ Circular dependencies found:'); + circles.forEach((circle, index) => { + console.log(`\nCircular dependency #${index + 1}:`); + circle.forEach((file) => console.log(` → ${file}`)); + }); +} + +/** + * 检查项目中的循环依赖 + * @param options - 检查选项 + * @param options.staged - 是否只检查暂存区文件 + * @param options.verbose - 是否显示详细信息 + * @param options.config - 自定义配置 + * @returns Promise + */ +async function checkCircular({ + config = {}, + staged, + verbose, +}: CommandOptions): Promise { + try { + // 合并配置 + const finalConfig = { + ...DEFAULT_CONFIG, + ...config, + }; + + // 生成忽略模式 + const ignorePattern = `**/{${finalConfig.ignoreDirs.join(',')}}/**`; + + // 检查缓存 + const cacheKey = `${staged}-${process.cwd()}-${ignorePattern}`; + if (cache.has(cacheKey)) { + const cachedResults = cache.get(cacheKey); + if (cachedResults && verbose) { + formatCircles(cachedResults); + } + return; + } + + // 检测循环依赖 + const results = await detectCircularDependencies({ + cwd: process.cwd(), + ignorePattern, + staged, + }); + + if (staged) { + let files = await getStagedFiles(); + const allowedExtensions = new Set(finalConfig.allowedExtensions); + + // 过滤文件列表 + files = files.filter((file) => allowedExtensions.has(extname(file))); + + const circularFiles: CircularDependencyResult[] = []; + + for (const file of files) { + for (const result of results) { + const resultFiles = result.flat(); + if (resultFiles.includes(file)) { + circularFiles.push(result); + } + } + } + + // 更新缓存 + cache.set(cacheKey, circularFiles); + if (verbose) { + formatCircles(circularFiles); + } + } else { + // 更新缓存 + cache.set(cacheKey, results); + if (verbose) { + formatCircles(results); + } + } + + // 如果发现循环依赖,只输出警告信息 + if (results.length > 0) { + console.log( + '\n⚠️ Warning: Circular dependencies found, please check and fix', + ); + } + } catch (error) { + console.error( + '❌ Error checking circular dependencies:', + error instanceof Error ? error.message : error, + ); + } +} + +/** + * 定义检查循环依赖的命令 + * @param cac - CAC实例 + */ +function defineCheckCircularCommand(cac: CAC): void { + cac + .command('check-circular') + .option('--staged', 'Only check staged files') + .option('--verbose', 'Show detailed information') + .option('--threshold ', 'Threshold for circular dependencies', { + default: 0, + }) + .option('--ignore-dirs ', 'Directories to ignore, comma separated') + .usage('Analyze project circular dependencies') + .action(async ({ ignoreDirs, staged, threshold, verbose }) => { + const config: CheckCircularConfig = { + threshold: Number(threshold), + ...(ignoreDirs && { ignoreDirs: ignoreDirs.split(',') }), + }; + + await checkCircular({ + config, + staged, + verbose: verbose ?? true, + }); + }); +} + +export { type CheckCircularConfig, defineCheckCircularCommand }; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/check-dep/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/check-dep/index.ts new file mode 100644 index 0000000..15259af --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/check-dep/index.ts @@ -0,0 +1,147 @@ +import type { CAC } from 'cac'; + +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; + +import { execa } from '@vben/node-utils'; + +const require = createRequire(import.meta.url); +const knipMain = require.resolve('knip'); +const knipCli = join(dirname(knipMain), '..', 'bin', 'knip.js'); + +const DEFAULT_CONFIG = { + ignore: ['dist/**', 'docs/**', 'node_modules/**', 'public/**'], + ignoreBinaries: [] as string[], + ignoreDependencies: [ + '@iconify/json', + '@vben-core/design', + '@vben/commitlint-config', + '@vben/eslint-config', + '@vben/stylelint-config', + '@vben/tailwind-config', + '@vben/vite-config', + '@vben/oxlint-config', + 'playwright', + 'rimraf', + 'tailwindcss', + ], + ignoreWorkspaces: ['internal/lint-configs/*', 'scripts/*'], +}; + +interface KnipDependency { + col: number; + line: number; + name: string; + pos: number; +} + +interface KnipFileIssue { + dependencies: KnipDependency[]; + devDependencies: KnipDependency[]; + file: string; + optionalPeerDependencies: KnipDependency[]; +} + +interface KnipResult { + issues: KnipFileIssue[]; +} + +/** + * 格式化依赖检查结果 + * @param result - 依赖检查结果 + */ +function formatResult(result: KnipResult): void { + let hasIssues = false; + + for (const issue of result.issues) { + const hasDeps = issue.dependencies.length > 0; + const hasDevDeps = issue.devDependencies.length > 0; + + if (!hasDeps && !hasDevDeps) { + continue; + } + + hasIssues = true; + console.log(`\n📦 ${issue.file}`); + + if (hasDeps) { + console.log('⚠️ Unused dependencies:'); + for (const dep of issue.dependencies) { + console.log(` - ${dep.name}`); + } + } + + if (hasDevDeps) { + console.log('⚠️ Unused devDependencies:'); + for (const dep of issue.devDependencies) { + console.log(` - ${dep.name}`); + } + } + } + + if (!hasIssues) { + console.log('\n✅ Dependency check completed, no issues found'); + } +} + +/** + * 运行依赖检查 + */ +async function runKnipCheck(): Promise { + const cwd = process.cwd(); + const tempDir = await mkdtemp(join(tmpdir(), 'vsh-check-dep-')); + const configFile = join(tempDir, 'knip.json'); + + try { + await writeFile(configFile, JSON.stringify(DEFAULT_CONFIG)); + + const args = [ + knipCli, + '--config', + configFile, + '--include', + 'dependencies', + '--reporter', + 'json', + '--no-config-hints', + ]; + + await execa(process.execPath, args, { cwd }); + console.log('\n✅ Dependency check completed, no issues found'); + } catch (error: unknown) { + const execaError = error as { + exitCode?: number; + stdout?: string; + }; + + if (execaError.exitCode === 1 && execaError.stdout) { + const result: KnipResult = JSON.parse(execaError.stdout); + formatResult(result); + return; + } + + console.error( + '❌ Dependency check failed:', + error instanceof Error ? error.message : error, + ); + } finally { + await rm(tempDir, { force: true, recursive: true }); + } +} + +/** + * 定义依赖检查命令 + * @param cac - CAC实例 + */ +function defineCheckDepCommand(cac: CAC): void { + cac + .command('check-dep') + .usage('Analyze project dependencies using knip') + .action(async () => { + await runKnipCheck(); + }); +} + +export { defineCheckDepCommand }; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/code-workspace/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/code-workspace/index.ts new file mode 100644 index 0000000..02706db --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/code-workspace/index.ts @@ -0,0 +1,78 @@ +import type { CAC } from 'cac'; + +import { join, relative } from 'node:path'; + +import { + colors, + consola, + findMonorepoRoot, + formatFile, + getPackages, + gitAdd, + outputJSON, + toPosixPath, +} from '@vben/node-utils'; + +const CODE_WORKSPACE_FILE = join('vben-admin.code-workspace'); + +interface CodeWorkspaceCommandOptions { + autoCommit?: boolean; + spaces?: number; +} + +async function createCodeWorkspace({ + autoCommit = false, + spaces = 2, +}: CodeWorkspaceCommandOptions) { + const { packages, rootDir } = await getPackages(); + + let folders = packages.map((pkg) => { + const { dir, packageJson } = pkg; + return { + name: packageJson.name, + path: toPosixPath(relative(rootDir, dir)), + }; + }); + + folders = folders.filter(Boolean); + + const monorepoRoot = findMonorepoRoot(); + const outputPath = join(monorepoRoot, CODE_WORKSPACE_FILE); + await outputJSON(outputPath, { folders }, spaces); + + await formatFile(outputPath); + if (autoCommit) { + await gitAdd(CODE_WORKSPACE_FILE, monorepoRoot); + } +} + +async function runCodeWorkspace({ + autoCommit, + spaces, +}: CodeWorkspaceCommandOptions) { + await createCodeWorkspace({ + autoCommit, + spaces, + }); + if (autoCommit) { + return; + } + consola.log(''); + consola.success(colors.green(`${CODE_WORKSPACE_FILE} is updated!`)); + consola.log(''); +} + +function defineCodeWorkspaceCommand(cac: CAC) { + cac + .command('code-workspace') + .usage('Update the `.code-workspace` file') + .option('--spaces [number]', '.code-workspace JSON file spaces.', { + default: 2, + }) + .option('--auto-commit', 'auto commit .code-workspace JSON file.', { + default: false, + }) + .action(runCodeWorkspace); +} + +export { defineCodeWorkspaceCommand }; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/index.ts new file mode 100644 index 0000000..ed9eb6c --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/index.ts @@ -0,0 +1,77 @@ +import { colors, consola } from '@vben/node-utils'; + +import { cac } from 'cac'; + +import { version } from '../package.json'; +import { defineCheckCircularCommand } from './check-circular'; +import { defineCheckDepCommand } from './check-dep'; +import { defineCodeWorkspaceCommand } from './code-workspace'; +import { defineLintCommand } from './lint'; +import { definePubLintCommand } from './publint'; + +// 命令描述 +const COMMAND_DESCRIPTIONS = { + 'check-circular': 'Check for circular dependencies', + 'check-dep': 'Check for unused dependencies', + 'code-workspace': 'Manage VS Code workspace settings', + lint: 'Run linting on the project', + publint: 'Check package.json files for publishing standards', +} as const; + +/** + * Initialize and run the CLI + */ +async function main(): Promise { + try { + const vsh = cac('vsh'); + + // Register commands + defineLintCommand(vsh); + definePubLintCommand(vsh); + defineCodeWorkspaceCommand(vsh); + defineCheckCircularCommand(vsh); + defineCheckDepCommand(vsh); + + // Set up CLI + vsh.usage('vsh [options]'); + vsh.help(); + vsh.version(version); + + // Parse arguments without auto-running to detect unknown commands + // Note: cac v7 removed EventEmitter; use matchedCommand after parse instead + vsh.parse(undefined, { run: false }); + + if (!vsh.matchedCommand && vsh.args.length > 0) { + const unknownCmd = String(vsh.args[0]); + consola.error( + colors.red(`Invalid command: ${unknownCmd}`), + '\n', + colors.yellow('Available commands:'), + '\n', + Object.entries(COMMAND_DESCRIPTIONS) + .map(([name, desc]) => ` ${colors.cyan(name)} - ${desc}`) + .join('\n'), + ); + process.exit(1); + } + + await vsh.runMatchedCommand(); + } catch (error) { + consola.error( + colors.red('An unexpected error occurred:'), + '\n', + error instanceof Error ? error.message : error, + ); + process.exit(1); + } +} + +// Run the CLI +main().catch((error) => { + consola.error( + colors.red('Failed to start CLI:'), + '\n', + error instanceof Error ? error.message : error, + ); + process.exit(1); +}); diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/lint/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/lint/index.ts new file mode 100644 index 0000000..5c927ac --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/lint/index.ts @@ -0,0 +1,76 @@ +import type { CAC } from 'cac'; + +import { execSync } from 'node:child_process'; + +import { execaCommand } from '@vben/node-utils'; + +interface LintCommandOptions { + /** + * Format lint problem. + */ + format?: boolean; + /** + * Number of threads for oxfmt and oxlint (default: 2). + */ + threads?: number; +} + +async function runLint({ format, threads }: LintCommandOptions) { + // process.env.FORCE_COLOR = '3'; + const threadsArg = threads ? ` --threads=${threads}` : ` --threads=2`; + + if (format) { + await execaCommand(`stylelint "**/*.{vue,css,less,scss}" --cache --fix`, { + stdio: 'inherit', + }); + await execaCommand(`oxfmt${threadsArg}`, { + stdio: 'inherit', + }); + await execaCommand(`oxlint --fix --type-aware${threadsArg}`, { + stdio: 'inherit', + }); + await execaCommand(`eslint . --cache --fix`, { + stdio: 'inherit', + }); + return; + } + const subprocesses = [ + execaCommand(`oxfmt --check${threadsArg}`, { stdio: 'inherit' }), + execaCommand(`oxlint --type-aware${threadsArg}`, { stdio: 'inherit' }), + execaCommand(`eslint . --cache`, { stdio: 'inherit' }), + execaCommand(`stylelint "**/*.{vue,css,less,scss}" --cache`, { + stdio: 'inherit', + }), + ]; + + try { + await Promise.all(subprocesses); + } catch (error) { + for (const subprocess of subprocesses) { + try { + if (process.platform === 'win32' && subprocess.pid) { + execSync(`taskkill /F /T /PID ${subprocess.pid}`, { + stdio: 'ignore', + }); + } else { + subprocess.kill('SIGKILL'); + } + } catch { + // process may have already exited + } + } + await Promise.allSettled(subprocesses); + throw error; + } +} + +function defineLintCommand(cac: CAC) { + cac + .command('lint') + .usage('Batch execute project lint check.') + .option('--format', 'Format lint problem.') + .option('--threads ', 'Number of threads for oxfmt and oxlint.') + .action(runLint); +} + +export { defineLintCommand }; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/src/publint/index.ts b/deploy/fba/fba-ui-src/scripts/vsh/src/publint/index.ts new file mode 100644 index 0000000..d078673 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/src/publint/index.ts @@ -0,0 +1,185 @@ +import type { CAC } from 'cac'; +import type { Result } from 'publint'; + +import { basename, dirname, join } from 'node:path'; + +import { + colors, + consola, + ensureFile, + findMonorepoRoot, + generatorContentHash, + getPackages, + outputJSON, + readJSON, + UNICODE, +} from '@vben/node-utils'; + +import { publint } from 'publint'; +import { formatMessage } from 'publint/utils'; + +const CACHE_FILE = join( + 'node_modules', + '.cache', + 'publint', + '.pkglintcache.json', +); + +interface PubLintCommandOptions { + /** + * Only errors are checked, no program exit is performed + */ + check?: boolean; +} + +/** + * Get files that require lint + * @param files + */ +async function getLintFiles(files: string[] = []) { + const lintFiles: string[] = []; + + if (files?.length > 0) { + return files.filter((file) => basename(file) === 'package.json'); + } + + const { packages } = await getPackages(); + + for (const { dir } of packages) { + lintFiles.push(join(dir, 'package.json')); + } + return lintFiles; +} + +function getCacheFile() { + const root = findMonorepoRoot(); + return join(root, CACHE_FILE); +} + +async function readCache(cacheFile: string) { + try { + await ensureFile(cacheFile); + return await readJSON(cacheFile); + } catch { + return {}; + } +} + +async function runPublint(files: string[], { check }: PubLintCommandOptions) { + const lintFiles = await getLintFiles(files); + const cacheFile = getCacheFile(); + + const cacheData = await readCache(cacheFile); + const cache: Record = cacheData; + + const results = await Promise.all( + lintFiles.map(async (file) => { + try { + const pkgJson = await readJSON(file); + + if (pkgJson.private) { + return null; + } + + Reflect.deleteProperty(pkgJson, 'dependencies'); + Reflect.deleteProperty(pkgJson, 'devDependencies'); + Reflect.deleteProperty(pkgJson, 'peerDependencies'); + const content = JSON.stringify(pkgJson); + const hash = generatorContentHash(content); + + const publintResult: Result = + cache?.[file]?.hash === hash + ? (cache?.[file]?.result ?? []) + : await publint({ + level: 'suggestion', + pkgDir: dirname(file), + strict: true, + }); + + cache[file] = { + hash, + result: publintResult, + }; + + return { pkgJson, pkgPath: file, publintResult }; + } catch { + return null; + } + }), + ); + + await outputJSON(cacheFile, cache); + printResult(results, check); +} + +function printResult( + results: Array; + pkgPath: string; + publintResult: Result; + }>, + check?: boolean, +) { + let errorCount = 0; + let warningCount = 0; + let suggestionsCount = 0; + + for (const result of results) { + if (!result) { + continue; + } + const { pkgJson, pkgPath, publintResult } = result; + const messages = publintResult?.messages ?? []; + if (messages?.length < 1) { + continue; + } + + consola.log(''); + consola.log(pkgPath); + for (const message of messages) { + switch (message.type) { + case 'error': { + errorCount++; + + break; + } + case 'suggestion': { + suggestionsCount++; + break; + } + case 'warning': { + warningCount++; + + break; + } + // No default + } + const ruleUrl = `https://publint.dev/rules#${message.code.toLocaleLowerCase()}`; + consola.log( + ` ${formatMessage(message, pkgJson)}${colors.dim(` ${ruleUrl}`)}`, + ); + } + } + + const totalCount = warningCount + errorCount + suggestionsCount; + if (totalCount > 0) { + consola.error( + colors.red( + `${UNICODE.FAILURE} ${totalCount} problem (${errorCount} errors, ${warningCount} warnings, ${suggestionsCount} suggestions)`, + ), + ); + !check && process.exit(1); + } else { + consola.log(colors.green(`${UNICODE.SUCCESS} No problem`)); + } +} + +function definePubLintCommand(cac: CAC) { + cac + .command('publint [...files]') + .usage('Check if the monorepo package conforms to the publint standard.') + .option('--check', 'Only errors are checked, no program exit is performed.') + .action(runPublint); +} + +export { definePubLintCommand }; diff --git a/deploy/fba/fba-ui-src/scripts/vsh/tsconfig.json b/deploy/fba/fba-ui-src/scripts/vsh/tsconfig.json new file mode 100644 index 0000000..f4d397a --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "include": ["src", "env.d.ts"], + "exclude": ["node_modules"] +} diff --git a/deploy/fba/fba-ui-src/scripts/vsh/tsdown.config.ts b/deploy/fba/fba-ui-src/scripts/vsh/tsdown.config.ts new file mode 100644 index 0000000..c4f51c5 --- /dev/null +++ b/deploy/fba/fba-ui-src/scripts/vsh/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['esm'], + outExtensions: () => ({ + dts: '.d.ts', + }), +}); diff --git a/deploy/fba/fba-ui-src/stylelint.config.mjs b/deploy/fba/fba-ui-src/stylelint.config.mjs new file mode 100644 index 0000000..e380674 --- /dev/null +++ b/deploy/fba/fba-ui-src/stylelint.config.mjs @@ -0,0 +1,4 @@ +export default { + extends: ['@vben/stylelint-config'], + root: true, +}; diff --git a/deploy/fba/fba-ui-src/turbo.json b/deploy/fba/fba-ui-src/turbo.json new file mode 100644 index 0000000..3d3f373 --- /dev/null +++ b/deploy/fba/fba-ui-src/turbo.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": [ + "pnpm-lock.yaml", + "**/.env.*local", + "**/tsconfig*.json", + "internal/node-utils/*.json", + "internal/node-utils/src/**/*.ts", + "internal/vite-config/*.json", + "internal/vite-config/src/**/*.ts", + "scripts/*/src/**/*.ts", + "scripts/*/src/**/*.json" + ], + "globalEnv": ["NODE_ENV"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": [ + "dist/**", + "dist.zip", + ".vitepress/dist.zip", + ".vitepress/dist/**" + ] + }, + "preview": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "build:analyze": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "@vben/backend-mock#build": { + "dependsOn": ["^build"], + "outputs": [".nitro/**", ".output/**"] + }, + "test:e2e": {}, + "dev": { + "dependsOn": [], + "outputs": [], + "cache": false, + "persistent": true + }, + "typecheck": { + "outputs": [] + } + } +} diff --git a/deploy/fba/fba-ui-src/vitest.config.ts b/deploy/fba/fba-ui-src/vitest.config.ts new file mode 100644 index 0000000..67d6c22 --- /dev/null +++ b/deploy/fba/fba-ui-src/vitest.config.ts @@ -0,0 +1,28 @@ +import Vue from '@vitejs/plugin-vue'; +import VueJsx from '@vitejs/plugin-vue-jsx'; +import { configDefaults, defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [Vue(), VueJsx()], + test: { + environment: 'happy-dom', + environmentOptions: { + happyDOM: { + settings: { + // happy-dom v20+ disables JS evaluation by default (security fix). + // Treat disabled script loading as success to preserve test behavior. + handleDisabledFileLoadingAsSuccess: true, + }, + }, + }, + exclude: [ + ...configDefaults.exclude, + '**/e2e/**', + '**/dist/**', + '**/.{idea,git,cache,output,temp}/**', + '**/node_modules/**', + '**/{stylelint,eslint}.config.*', + '**/{oxfmt,oxlint}.config.*', + ], + }, +}); diff --git a/deploy/fba/fba-ui-src/vitest.workspace.ts b/deploy/fba/fba-ui-src/vitest.workspace.ts new file mode 100644 index 0000000..f00d6f6 --- /dev/null +++ b/deploy/fba/fba-ui-src/vitest.workspace.ts @@ -0,0 +1,3 @@ +import { defineWorkspace } from 'vitest/config'; + +export default defineWorkspace(['vitest.config.ts']);