2 Commits

Author SHA1 Message Date
fffbe08963 Merge branch 'main' into renovate/yanhao98-composite-actions-digest
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ playwright (push) Successful in 2m52s
/ depcheck (push) Successful in 1m28s
2024-10-14 10:00:54 +08:00
2fcebab367 chore(deps): update yanhao98/composite-actions digest to 13dd5d7
All checks were successful
/ depcheck (push) Successful in 1m34s
/ playwright (push) Successful in 4m12s
/ build-and-deploy-to-vercel (push) Successful in 1m28s
2024-10-14 09:32:15 +08:00
229 changed files with 6399 additions and 327954 deletions

14
.depcheck.js Normal file
View File

@ -0,0 +1,14 @@
// https://github.com/depcheck/depcheck?tab=readme-ov-file#api
// node .depcheck.js
import depcheck from 'depcheck';
depcheck(process.cwd(), {
ignorePatterns: ['tsconfig.json'],
}).then((unused) => {
console.debug(`unused.dependencies :>> `, unused.dependencies); // an array containing the unused dependencies
console.debug(`unused.devDependencies :>> `, unused.devDependencies); // an array containing the unused devDependencies
console.debug(`unused.missing :>> `, unused.missing); // a lookup containing the dependencies missing in `package.json` and where they are used
console.debug(`unused.using :>> `, unused.using); // a lookup indicating each dependency is used by which files
console.debug(`unused.invalidFiles :>> `, unused.invalidFiles); // files that cannot access or parse
console.debug(`unused.invalidDirs :>> `, unused.invalidDirs); // directories that cannot access
});

8
.depcheckrc.yml Normal file
View File

@ -0,0 +1,8 @@
# https://github.com/depcheck/depcheck#usage
# pnpm add json -g
# pnpm exec depcheck --json | json
# pnpm exec depcheck
# ignores: ["eslint", "babel-*"]
# skip-missing: true

View File

@ -1,6 +0,0 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

2
.env
View File

@ -1,6 +1,6 @@
VITE_BASE=/
VITE_SPLIT_CHUNKS=false
VITE_SPLIT_CHUNKS=true
VITE_SOURCE_MAP=true
VITE_BUILD_TIME=
VITE_BUILD_COMMIT=

81
.gitattributes vendored
View File

@ -1,81 +0,0 @@
# 如果你先推送了一个.jpg文件然后再推送包含.gitattributes文件的更新Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效你可以通过以下步骤重新应用设置
# 1. 删除本地缓存的.jpg文件git rm --cached <file>.jpg
# 2. 重新添加该文件git add <file>.jpg
# 3. 再次提交并推送git commit -m "Apply .gitattributes changes" && git push
# 这样Git将按照.gitattributes中的新规则处理该文件。
* text=auto
# Force the following filetypes to have unix eols, so Windows does not break them
*.* text eol=lf
# Separate configuration for files without suffix
LICENSE text eol=lf
Dockerfile text eol=lf
pre-commit text eol=lf
commit-msg text eol=lf
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.ico binary
*.jpg binary
*.jpeg binary
*.png binary
*.pdf binary
*.doc binary
*.docx binary
*.ppt binary
*.pptx binary
*.xls binary
*.xlsx binary
*.exe binary
*.jar binary
*.ttf binary
*.woff binary
*.woff2 binary
*.eot binary
*.otf binary
# Add more binary...
# >>> huggingface >>>
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
# <<< huggingface <<<

15
.github/.depcheck.js vendored
View File

@ -1,15 +0,0 @@
// https://github.com/depcheck/depcheck?tab=readme-ov-file#api
// node .github/.depcheck.js
import depcheck from 'depcheck';
const unused = await depcheck(process.cwd(), {
ignorePatterns: [],
});
console.debug(`unused.dependencies :>>`, unused.dependencies); // an array containing the unused dependencies
console.debug(`unused.devDependencies :>>`, unused.devDependencies); // an array containing the unused devDependencies
console.debug(`unused.missing :>>`, unused.missing); // a lookup containing the dependencies missing in `package.json` and where they are used
console.debug(`unused.using :>>`, unused.using); // a lookup indicating each dependency is used by which files
console.debug(`unused.invalidFiles :>>`, unused.invalidFiles); // files that cannot access or parse
console.debug(`unused.invalidDirs :>>`, unused.invalidDirs); // directories that cannot access

View File

@ -1,9 +0,0 @@
# https://github.com/depcheck/depcheck#usage
# #####
# pnpm exec depcheck --config .github/.depcheckrc.yaml
# #####
# pnpm add json -g
# pnpm exec depcheck --json | json
ignores: ['@iconify-json/*']
skip-missing: true

View File

@ -1,53 +0,0 @@
# Project Conventions and Technical Guidelines
This document outlines the core technical choices, coding conventions, and configuration details for this project. Adhering to these guidelines ensures consistency and leverages the project's setup effectively.
## 🚀 Core Technologies & Conventions
* **Framework:** Vue 3
* **Language:** TypeScript
* **API Style:** **Strictly use the Composition API with `<script setup>` syntax.** The Options API and the standard `<script>` block with the `setup()` function are **not** used in this project.
* **Single File Component (SFC) Structure:** Follow this order within `.vue` files:
1. `<script setup lang="ts">` (Imports, logic, composables, etc.)
2. `<template>` (HTML structure)
3. `<style>` (Preferably `<style scoped>`)
* **Code Comments:**
* All comments within the codebase **must be written in Chinese**. (代码中的所有注释**必须使用中文**。)
* **注释应解释代码的“为什么”(设计意图、选择某方案的原因)或复杂逻辑/算法的“如何”,而不是解释“是什么”(重复代码功能)或“在哪里”(描述定义位置)。**
* **严禁添加仅说明代码被移动、重构或提取到其他文件的注释。** 例如,避免 `// XxxState 接口已移至 types.ts``// 提取为 useXYZ composable` 这样的注释。依赖代码结构、类型系统和 IDE 功能(如 Go To Definition来理解这些信息而不是通过注释。Focus comments on the *logic* itself, not its location or history.
* **同样地,严禁添加描述代码是“新增”、“修改”、“临时”或标记其开发状态的注释。** 例如,避免 `// 新增功能``// (新增)``// 临时修复``// TODO: 优化`(除非 TODO 指向具体的技术债务或未完成的逻辑,而不仅仅是标记状态)等注释。注释的目标是解释代码的内在逻辑或设计决策,而不是其变更历史或当前状态。
## 📦 UI Libraries & Components
* **UI Libraries:** This project utilizes both **Ant Design Vue** and **PrimeVue**.
* **Component Auto-Import:** Components from Ant Design Vue, PrimeVue, and those defined in `src/components/**/*.vue` (excluding filenames starting with `__`) are **automatically imported** via `unplugin-vue-components`.
* **Ant Design Vue:** Use components directly (e.g., `<a-button>`, `<a-table>`). Icons are also resolved (e.g., `<UserOutlined />`). CSS-in-JS is used; no manual style import is needed.
* **PrimeVue:** Use components directly with their standard names (e.g., `<Button>`, `<InputText>`, `<DataTable>`).
* **Custom Components:** Local components (e.g., `<MyCustomComponent />` defined in `src/components/MyCustomComponent.vue`) should be used directly in templates without explicit imports in `<script setup>`.
* **Custom Icons:** Custom SVG icons are available via `unplugin-icons` with the prefix `icon-`. Usage: `<icon-your-svg-file-name />`.
* **No Auto-Import Comments for Components:** When using auto-imported components (UI libraries, local components), **do not** add comments indicating they are auto-imported. Assume this behavior is known. Trust the tooling.
## ✨ API Auto-Import (unplugin-auto-import)
* **Automatic API Availability:** This project leverages `unplugin-auto-import` extensively. APIs from the following libraries/modules are globally available and **SHOULD NOT be explicitly imported** in `<script setup>` blocks unless necessary for specific type augmentation or rare cases:
* `vue` (e.g., `ref`, `computed`, `watch`, `onMounted`, `defineProps`, `defineEmits`, etc.)
* `pinia` (e.g., `defineStore`, `storeToRefs`)
* `vue-router/auto` (e.g., `useRouter`, `useRoute`, `useLink`) - Provided by `unplugin-vue-router` integration.
* `@vueuse/core` (Common utility functions)
* `vue-i18n` (e.g., `useI18n`)
* `consola/browser` (Available as the `consola` global for logging)
* **Auto-Imported Directories:** All exports from files within `src/stores/**` and `src/utils/**` are also auto-imported globally. Functions, variables, or stores defined and exported in these directories can be used directly without explicit import statements.
* **Template Usage:** Auto-import functionality also applies within the `<template>` block where appropriate (e.g., accessing store state or using certain utility functions directly).
* **Important:** Rely on the auto-import mechanism for the specified libraries and directories. Explicitly importing these APIs clutters the code unnecessarily.
* **No Auto-Import Explanation Comments:** When using auto-imported APIs (from libraries like Vue, Pinia, VueUse, or custom directories like `src/utils`, `src/stores`), simply use them directly. **Absolutely avoid adding comments that merely state an item is auto-imported** (e.g., DO NOT add comments like "// xxx is auto-imported from src/utils"). Focus comments on explaining complex logic or intent.
## ✅ Summary of Key Rules & Conventions
* **Always** use `<script setup lang="ts">`.
* Composition API is the **only** accepted style.
* Code comments **must be in Chinese** and explain **why/how (logic, intent)**, **strictly avoiding** comments that explain **what (code repetition), where (definition location), refactoring history, code status (e.g., 'new', 'modified'), or auto-import mechanisms.**
* Vue 3 Composition API, Pinia, Vue Router (auto), VueUse functions, `consola`, and exports from `src/stores` & `src/utils` are globally available via auto-import; **do not** import them explicitly.
* Ant Design Vue, PrimeVue, and local `src/components` components are auto-imported; use them directly in templates without explicit imports.
* **Crucially: Do not add comments solely to explain that an API or component is auto-imported OR where code has been moved/refactored OR the status of the code (e.g., 'new').** Trust the auto-import setup and IDE navigation. Focus on logic.
* Use custom SVG icons via the `<icon-name />` syntax.
* Maintain the specified SFC structure: `<script setup>`, `<template>`, `<style>`.

View File

@ -1,3 +0,0 @@
```bash
ln -s .github/copilot-instructions.md .roorules
```

71
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,71 @@
# https://cn.vitejs.dev/guide/static-deploy
on:
push:
jobs:
build-and-deploy-to-vercel:
runs-on: ubuntu-latest
steps:
- uses: yanhao98/composite-actions/setup-node-environment@main
- name: 静态代码分析
run: pnpm lint
- name: Vue 类型检查
run: npx vue-tsc --build --force
- name: 修改 .npmrc
run: |
sed -i '/use-node-version/d' .npmrc;
sed -i '/node-mirror/d' .npmrc;
# https://github.com/vercel/examples/tree/main/ci-cd/github-actions
# cname-cn.vercel.com cname-china.vercel-dns.com
- name: 拉取 Vercel 环境信息
run: pnpm exec vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: 构建项目
run: pnpm exec vercel build --target=production --token=${{ secrets.VERCEL_TOKEN }}
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 部署到 Vercel
run: pnpm exec vercel deploy --prebuilt --target=production --token=${{ secrets.VERCEL_TOKEN }}
playwright: # act --job playwright --workflows .github/workflows/ci.yaml --pull=false --dryrun
runs-on: ubuntu-latest
container: yanhao98/runner-images:pnpm-with-playwright
steps:
- uses: yanhao98/composite-actions/setup-node-environment@13dd5d7bdbf6eb3ef88afa0ae8c524dc0ccf94ba
- run: pnpm run build-only
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 部署到 Surge
id: surge_deploy
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
uses: yanhao98/composite-actions/deploy-dist-to-surge@13dd5d7bdbf6eb3ef88afa0ae8c524dc0ccf94ba
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm exec playwright test
env:
BASE_URL: ${{ steps.surge_deploy.outputs.url }}
depcheck:
runs-on: ubuntu-latest
steps:
- uses: yanhao98/composite-actions/setup-node-environment@main
- run: npx depcheck || true
- run: node .depcheck.js
# surge-preview:
# runs-on: ubuntu-latest
# permissions:
# pull-requests: write # allow surge-preview to create/update PR comments
# steps:
# - uses: yanhao98/composite-actions/setup-node-environment@main
# - uses: afc163/surge-preview@v1
# id: surge_preview_step
# with:
# dist: dist
# build: pnpm run build-only
# - name: Get the preview_url
# run: echo "url => ${{ steps.surge_preview_step.outputs.preview_url }}"

View File

@ -1,36 +0,0 @@
# https://cn.vitejs.dev/guide/static-deploy
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
jobs:
lint-build-and-check:
runs-on: ubuntu-latest
steps:
- name: 🛠️ 设置Node环境
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
- name: 🔍 静态代码分析
run: pnpm run lint
- name: 📦 构建项目
run: pnpm run build-only
- name: 📊 计算构建大小
run: |
echo "📊 构建大小统计:"
echo "----------------------------------------"
echo "🔹 人类可读格式: $(du -sh dist | cut -f1)"
echo "🔹 以MB为单位: $(du -sm dist | cut -f1) MB"
echo "🔹 以KB为单位: $(du -sk dist | cut -f1) KB"
echo "🔹 文件总数: $(find dist -type f | wc -l) 个文件"
echo "----------------------------------------"
- name: ✅ 类型检查
run: pnpm run type-check # 要先 build保证 components.d.ts 存在
- name: 🔍 检查未使用的依赖
run: npx depcheck || true
- run: node .github/.depcheck.js
- run: npx taze --help
- run: npx taze
- run: npx taze -a
# - run: npx size-limit

View File

@ -1,44 +0,0 @@
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
jobs:
surge:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.surge_deploy.outputs.url }}
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
- name: 🔨 构建项目
run: pnpm run build-only
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 🚀 部署到 Surge
id: surge_deploy
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
uses: yanhao98/composite-actions/deploy-dist-to-surge@4470aa136359d05ae3b086d37cfaa33305448a5b
playwright:
needs: surge
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.51.1-noble
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
# - name: 📥 安装 Playwright 浏览器
# run: pnpm exec playwright install --with-deps
- name: ▶️ 运行 Playwright 测试
run: npx playwright test
env:
BASE_URL: ${{ needs.surge.outputs.url }}
- name: 🧹 清理 Surge 部署
run: npx surge teardown ${{ needs.surge.outputs.url }} --token ${{ env.SURGE_TOKEN}}
env:
SURGE_TOKEN: d843de16b331c626f10771245c56ed93

View File

@ -1,33 +0,0 @@
# https://cn.vitejs.dev/guide/static-deploy
# https://github.com/vercel/examples/tree/main/ci-cd/github-actions
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
branches: [main]
jobs:
build-and-deploy-to-vercel:
runs-on: ubuntu-latest
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
- name: 📥 拉取 Vercel 环境信息
run: pnpm dlx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: 🏗️ 构建项目 # vite build
run: pnpm dlx vercel build --target=production --token=${{ secrets.VERCEL_TOKEN }}
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
__VUE_PROD_DEVTOOLS__: true
- name: 🧐 类型检查 # 要先 build保证 components.d.ts 存在
run: pnpm run type-check
# 🇨🇳: cname-cn.vercel.com cname-china.vercel-dns.com
- name: 🚀 部署到 Vercel
run: pnpm dlx vercel deploy --prebuilt --target=production --token=${{ secrets.VERCEL_TOKEN }} --confirm

3
.gitignore vendored
View File

@ -11,7 +11,6 @@ node_modules
.DS_Store
dist
dist-ssr
dist.zip
coverage
*.local
@ -27,12 +26,10 @@ coverage
*.njsproj
*.sln
*.sw?
.VSCodeCounter
*.tsbuildinfo
components.d.ts
auto-imports.d.ts
.vercel
/test-results/
/playwright-report/

View File

@ -1,20 +0,0 @@
### Husky 遇到 command not found: husky
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
```shell
ln -s $(which pnpm) $HOME/.local/bin/pnpm
```
```
# if command -v pnpm >/dev/null 2>&1; then
# # 如果 pnpm 可用,直接使用它
# pnpm exec lint-staged
# else
# # 如果 pnpm 不可用,使用 $HOME/.local/bin/pnpm
# # ln -s $(which pnpm) $HOME/.local/bin/pnpm
# echo "找不到 pnpm使用 $HOME/.local/bin/pnpm"
# "$HOME"/.local/bin/pnpm exec lint-staged
# fi
```

View File

@ -1,5 +0,0 @@
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
# 在这里添加你的 commit message 验证逻辑,例如 commitlint
# npx --no -- commitlint --edit "$1"
echo "✅ [Commit-msg] commit-msg 钩子完成!"

View File

@ -1,4 +0,0 @@
# 此钩子在 git merge 或 git pull 成功完成后运行。
echo "🔗 [Post-merge] 正在安装依赖..."
pnpm install
echo "✅ [Post-merge] 依赖安装完成!"

View File

@ -1,4 +1,11 @@
# 此钩子在执行 git commit 命令时,在创建提交之前运行。
echo "🧹 [Pre-commit] 正在运行 lint-staged..."
pnpm exec lint-staged
echo "✅ [Pre-commit] lint-staged 完成!"
echo "Running pre-commit hook..."
if command -v pnpm >/dev/null 2>&1; then
# 如果 pnpm 可用,直接使用它
pnpm exec lint-staged
else
# 如果 pnpm 不可用,使用 $HOME/.local/bin/pnpm
# ln -s $(which pnpm) $HOME/.local/bin/pnpm
echo "pnpm not found, using $HOME/.local/bin/pnpm"
"$HOME"/.local/bin/pnpm exec lint-staged
fi

10
.npmrc
View File

@ -1,12 +1,10 @@
registry=https://registry.npmmirror.com/
registry=https://mirrors.cloud.tencent.com/npm/
registry=https://mirrors.huaweicloud.com/repository/npm/
registry=https://registry.npmjs.org/
# registry=https://nexus.oo1.dev/repository/npm/
registry=https://r-npm.oo1.dev
registry=https://registry.npmmirror.com
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
use-node-version=22.14.0
node-mirror:release=https://npmmirror.com/mirrors/node/ # pnpm config set node-mirror:release=https://npmmirror.com/mirrors/node/
# use-node-version=20.17.0
node-mirror:release=https://npmmirror.com/mirrors/node/
node-mirror:rc=https://npmmirror.com/mirrors/node-rc/
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/

View File

@ -1,14 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
// https://oxc.rs/docs/guide/usage/linter/rules/unicorn/no-new-array.html
// "rules": {
// "unicorn/no-useless-spread": "off"
// },
"ignorePatterns": [
"src/shadcn/**",
"src/components/InspiraUI/**",
"public/**",
"**/**.no-lint.ts",
"**/**.nolint.ts"
]
}

View File

@ -1,5 +1,2 @@
/src/shadcn
/src/components/InspiraUI/
**/**.no-lint.ts
**/**.nolint.ts
**/**.noformat.json
/node_modules
/dist

View File

@ -1 +0,0 @@
.github/copilot-instructions.md

View File

@ -4,7 +4,6 @@
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"simonhe.common-intellisense",
"antfu.file-nesting",
"oxc.oxc-vscode"
"antfu.file-nesting"
]
}

41
.vscode/settings.json vendored
View File

@ -1,36 +1,19 @@
{
// "editor.formatOnSaveMode": "modificationsIfAvailable", // 只格式化修改的部分
"editor.formatOnSaveMode": "file", // 格式化整个文件
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"],
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.oxc": "explicit",
"source.organizeImports": "never"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "never",
"source.fixAll.stylelint": "never",
"source.fixAll.oxc": "never",
"source.organizeImports": "never",
"source.fixAll": "never"
},
"editor.formatOnSave": false,
"oxc.enable": true,
"eslint.enable": true
// https://github.com/antfu/vscode-file-nesting-config
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"tsconfig.json": "tsconfig.*.json, env.d.ts",
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
}
}

View File

@ -9,7 +9,7 @@ curl -fsSL https://get-pnpm.oo1.dev/install.sh | sh -
iwr https://get-pnpm.oo1.dev/install.ps1 -useb | iex
# 安装依赖
pnpm install --registry=https://nexus.oo1.dev/repository/npm
pnpm install --registry=https://r-npm.oo1.dev
# 启动服务
pnpm run dev
@ -17,31 +17,17 @@ pnpm run dev
## Tips
### .gitattributes
### Husky 遇到 command not found: husky
如果你先推送了一个.jpg文件然后再推送包含.gitattributes文件的更新Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效你可以通过以下步骤重新应用设置
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
```bash
1. 删除本地缓存的.jpg文件git rm --cached <file>.jpg
2. 重新添加该文件git add <file>.jpg
3. 再次提交并推送git commit -m "Apply .gitattributes changes" && git push
```shell
ln -s $(which pnpm) $HOME/.local/bin/pnpm
```
这样Git将按照.gitattributes中的新规则处理该文件。
要验证.gitattributes文件中的二进制配置是否生效可以使用以下方法
```bash
1. 推送后验证:先按照之前步骤重新添加并推送.jpg文件。
2. 查看差异diff执行 git diff检查文件是否有文本形式的改动。如果是二进制文件Git不会显示具体内容的差异。
3. Git日志验证执行 git log -p <file>.jpg 查看提交的改动记录,确认文件未受行尾或编码处理的影响。
```
如果以上测试显示该文件未发生不必要的改动,说明.gitattributes配置已生效。
## Links
- https://github.com/hyoban-template/shadcn-vue-unocss-starter$0
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
@ -57,6 +43,3 @@ pnpm run dev
- https://github.dev/antfu-collective/vitesse/
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
---
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)

318
auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,318 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createPinia: typeof import('pinia')['createPinia']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: typeof import('vue')['customRef']
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getActiveHead: typeof import('@unhead/vue')['getActiveHead']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const injectHead: typeof import('@unhead/vue')['injectHead']
const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: typeof import('@vueuse/core')['isDefined']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onLongPress: typeof import('@vueuse/core')['onLongPress']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: typeof import('vue')['provide']
const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toRaw: typeof import('vue')['toRaw']
const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCountdown: typeof import('utils4u/vue-use')['useCountdown']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useDraggable: typeof import('@vueuse/core')['useDraggable']
const useDropZone: typeof import('@vueuse/core')['useDropZone']
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
const useElementHover: typeof import('@vueuse/core')['useElementHover']
const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useHead: typeof import('@unhead/vue')['useHead']
const useHeadSafe: typeof import('@unhead/vue')['useHeadSafe']
const useId: typeof import('vue')['useId']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useModel: typeof import('vue')['useModel']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const useParallax: typeof import('@vueuse/core')['useParallax']
const useParentElement: typeof import('@vueuse/core')['useParentElement']
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSeoMeta: typeof import('@unhead/vue')['useSeoMeta']
const useServerHead: typeof import('@unhead/vue')['useServerHead']
const useServerHeadSafe: typeof import('@unhead/vue')['useServerHeadSafe']
const useServerSeoMeta: typeof import('@unhead/vue')['useServerSeoMeta']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: typeof import('@vueuse/core')['useToString']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useVueCountdown: typeof import('utils4u/vue-use')['useCountdown']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchDeep: typeof import('@vueuse/core')['watchDeep']
const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

View File

@ -1,20 +0,0 @@
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/shadcn/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/shadcn/components",
"composables": "@/shadcn/composables",
"utils": "@/shadcn/lib/utils",
"ui": "@/shadcn/components/ui",
"lib": "@/shadcn/lib"
},
"iconLibrary": "lucide"
}

43
eslint.config.mjs Normal file
View File

@ -0,0 +1,43 @@
import { includeIgnoreFile } from '@eslint/compat';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import vueTsEslintConfig from '@vue/eslint-config-typescript';
import pluginVue from 'eslint-plugin-vue';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, '.gitignore');
/**
* @type import('eslint').Linter.Config[]
*/
export default [
// --ignore-path .gitignore
includeIgnoreFile(gitignorePath),
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig({
supportedScriptLangs: {
ts: true,
tsx: true,
}
}),
skipFormatting,
{
rules: {
'vue/multi-word-component-names': 'off',
},
}
];

View File

@ -1,124 +0,0 @@
/*
MORE:
https://juejin.cn/post/7403244457263628300#heading-11
*/
import pluginVitest from '@vitest/eslint-plugin';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import { configureVueProject, defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
import { flatConfigs as eslintPluginImportX_flatConfigs } from 'eslint-plugin-import-x';
import oxlint from 'eslint-plugin-oxlint';
import perfectionist from 'eslint-plugin-perfectionist';
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
import pluginVue from 'eslint-plugin-vue';
// https://github.com/vuejs/eslint-config-typescript/#advanced-setup
configureVueProject({ scriptLangs: ['ts', 'tsx', 'js', 'jsx'] });
const _ignores = [
// >>>
// eslint-disable-next-line unicorn/no-await-expression-member
(await import('@eslint/compat')).includeIgnoreFile(
// eslint-disable-next-line unicorn/import-style, unicorn/no-await-expression-member
(await import('node:path')).default.resolve(import.meta.dirname, '.gitignore'),
),
// <<<
// >>>
// eslint-disable-next-line unicorn/no-await-expression-member
(await import('eslint/config')).globalIgnores([
'**/dist/**',
'**/dist-ssr/**',
'**/coverage/**',
'auto-imports.d.ts',
'components.d.ts',
'typed-router.d.ts',
// .oxlintrc.json > ignorePatterns >>>
'src/shadcn/**',
'src/components/InspiraUI/**',
'public/**',
'**/**.no-lint.ts',
'**/**.nolint.ts',
// <<< .oxlintrc.json
]),
// <<<
];
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
_ignores,
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
...oxlint.configs['flat/recommended'],
skipFormatting,
// region >> eslint-plugin-unicorn >>
eslintPluginUnicorn.configs.recommended,
{
rules: {
'unicorn/filename-case': 'off',
'unicorn/no-console-spaces': 'off',
'unicorn/no-null': 'off',
'unicorn/no-useless-spread': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/relative-url-style': 'off', // [plugin:vite:import-glob] Invalid glob: "imgs/*.png" (resolved: "imgs/*.png"). It must start with '/' or './'
},
},
// endregion <<< eslint-plugin-unicorn <<<
// region >>> eslint-plugin-import-x >>>
eslintPluginImportX_flatConfigs.recommended,
{
rules: {
'import-x/no-unresolved': 'off', // https://github.com/pzmosquito/eslint-import-resolver-vite/blob/67da5e259ee4c9da4c44d81b93364ae2777d00eb/index.js#L100
'import-x/newline-after-import': 'error',
'import-x/first': 'error',
'import-x/no-named-as-default': 'off',
},
},
// endregion <<< eslint-plugin-import-x <<<
// region >>> eslint-plugin-perfectionist >>>
// https://perfectionist.dev/guide/getting-started
perfectionist.configs['recommended-natural'],
{
rules: {
'perfectionist/sort-classes': 'off',
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-imports': ['error'],
'perfectionist/sort-modules': 'off',
'perfectionist/sort-object-types': 'off',
},
},
// endregion <<< eslint-plugin-perfectionist <<<
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'vue/block-order': [
'error',
{
order: ['script', 'template', 'style'],
},
],
'vue/define-macros-order': [
'error',
{
order: ['defineOptions', 'defineProps', 'defineEmits', 'defineSlots'],
},
],
'vue/multi-word-component-names': 'off',
},
},
);

View File

@ -1,26 +0,0 @@
// fake/user.fake.ts
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
let fail = !false;
export default defineFakeRoute([
{
method: 'POST',
rawResponse(req, res) {
fail = !fail;
if (fail) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Upload failed' }));
} else {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ url: 'https://picsum.photos/200/300' }));
}
},
response: () => {
return {
url: 'https://picsum.photos/200/300',
};
},
timeout: 2000,
url: '/fake/upload',
},
]);

View File

@ -1,34 +1,34 @@
import { faker } from '@faker-js/faker';
// fake/user.fake.ts
import Mock from 'mockjs';
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
import { faker } from '@faker-js/faker';
export default defineFakeRoute([
{
url: '/mock/get-user-info',
response: () => {
return Mock.mock({
avatar: '@image("200x200")',
email: '@email',
id: '@guid',
role: 'admin',
username: '@first',
email: '@email',
avatar: '@image("200x200")',
role: 'admin',
});
},
url: '/mock/get-user-info',
},
{
url: '/fake/get-user-info',
response: () => {
return {
id: faker.string.uuid(),
avatar: faker.image.avatar(),
birthday: faker.date.birthdate(),
email: faker.internet.email(),
firstName: faker.person.firstName(),
id: faker.string.uuid(),
lastName: faker.person.lastName(),
role: 'admin',
sex: faker.person.sexType(),
role: 'admin',
};
},
url: '/fake/get-user-info',
},
]);

View File

@ -2,54 +2,23 @@
<html lang="zh-CN" data-build-time="%VITE_BUILD_TIME%" data-commit="%VITE_BUILD_COMMIT%">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> -->
<link rel="icon" href="/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<title>vue-ts-example</title>
<style type="text/css">
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
}
}
#app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
@supports (min-height: 100dvh) {
#app {
min-height: 100dvh;
}
html .min-h-screen {
min-height: 100dvh;
}
}
.page-wrapper {
flex-grow: 1;
}
</style>
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/@unocss/reset/tailwind.min.css" />
</head>
<!-- ontouchstart ontouchend -->
<body>
<body ontouchstart ontouchend>
<div id="app">
<div class="page-wrapper" style="display: flex; justify-content: center; align-items: center">Loading...</div>
<div style="display: flex; justify-content: center; align-items: center; height: 100vh">Loading...</div>
</div>
<script type="module" src="/src/main.ts"></script>
<script src="https://testingcf.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script>
<!-- .min.js 是 jsDelivr 的特殊处理 -->
<!-- <script src="https://unpkg.luckincdn.com/@vant/touch-emulator@1.4.0/dist/index.js"></script> -->
<script src="https://fastly.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script>
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/nprogress/nprogress.css" />
</body>
<script>
(function (d) {
@ -79,6 +48,6 @@
} catch (e) {}
};
s.parentNode.insertBefore(tk, s);
}) /* (document) */;
})(document);
</script>
</html>

View File

@ -1,164 +1,105 @@
{
"packageManager": "pnpm@10.8.0",
"packageManager": "pnpm@9.12.1",
"name": "vue-ts-example",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 4730 --host",
"all": "run-p build-only format type-check lint",
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"lint-format": "run-p lint:oxlint lint:eslint format",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
"format": "prettier --write src/",
"type-check": "vue-tsc --build",
"lint": "run-s lint:*",
"_oxlint_cfg": "oxlint . --fix --ignore-path=.gitignore --print-config",
"__oxlint_-D": "oxlint . --fix --deny=correctness",
"lint:oxlint": "oxlint --fix",
"lint:eslint": "eslint . --fix",
"prepare": "husky",
"dev+preview": "bunx dev-and-preview@1.0.0",
"taze": "pnpx taze",
"playwright": "playwright test",
"playwright:headless": "HEADLESS=true playwright test",
"playwright:ui": "playwright test --ui",
"playwright:chromium": "playwright test --project=chromium",
"dep:dedupe": "pnpm dedupe",
"dep:update": "pnpm dlx taze major --interactive",
"sizecheck:Treemap": "pnpm dlx vite-bundle-visualizer -t treemap",
"sizecheck:Sunburst": "pnpm dlx vite-bundle-visualizer -t sunburst",
"sizecheck:Network": "pnpm dlx vite-bundle-visualizer -t network",
"knip": "pnpm dlx knip"
"depcheck": "depcheck"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,vue}": [
"src/**/*.{js,ts,vue}": [
"prettier --write",
"eslint --fix",
"oxlint --fix"
"eslint --fix"
]
},
"pnpm": {
"overrides": {
"vite": "$vite",
"vue-tsc": "$vue-tsc",
"@primevue/auto-import-resolver": "$primevue"
}
"patchedDependencies": {}
},
"dependencies": {
"@alova/adapter-axios": "^2.0.13",
"@formkit/auto-animate": "^0.8.2",
"@intlify/unplugin-vue-i18n": "^6.0.6",
"@pinia/colada": "^0.14.2",
"@primeuix/themes": "^1.0.3",
"@splinetool/runtime": "^1.9.82",
"@types/p5": "^1.7.6",
"@types/sortablejs": "^1.15.8",
"@unhead/vue": "^2.0.5",
"@alova/adapter-axios": "^2.0.7",
"@unhead/vue": "^1.11.7",
"@vant/use": "^1.6.0",
"@vueuse/core": "^13.1.0",
"alova": "^3.2.10",
"ant-design-vue": "~4.2.6",
"axios": "^1.8.4",
"cesium": "^1.128.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"consola": "^3.4.2",
"@vueuse/core": "^11.1.0",
"alova": "^3.0.20",
"axios": "^1.7.7",
"dayjs": "^1.11.13",
"deep-freeze-es6": "^4.0.0",
"jsencrypt": "^3.3.2",
"lucide-vue-next": "^0.487.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"p5": "^1.11.3",
"page-stack-vue3": "^2.5.6",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"plotly.js-dist-min": "^3.0.1",
"primeicons": "^7.0.0",
"primelocale": "^2.1.2",
"primevue": "^4.3.3",
"pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.1",
"radash": "^12.1.0",
"radix-vue": "^1.9.17",
"reka-ui": "^2.2.0",
"satellite.js": "^6.0.0",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.2.0",
"tdesign-icons-vue-next": "^0.3.5",
"three": "^0.175.0",
"taze": "^0.17.2",
"tdesign-icons-vue-next": "^0.2.6",
"tdesign-mobile-vue": "^1.4.3",
"ts-enum-util": "^4.1.0",
"utils4u": "^4.2.3",
"vant": "^4.9.18",
"vue": "^3.5.13",
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^11.1.3",
"utils4u": "^2.11.0",
"vant": "^4.9.7",
"vite-plugin-webfont-dl": "^3.9.5",
"vue": "^3.5.11",
"vue-page-stack": "^3.2.0",
"vue-router": "^4.5.0",
"vuetify": "^3.8.2"
"vue-router": "^4.4.5"
},
"devDependencies": {
"@eslint/compat": "^1.2.8",
"@faker-js/faker": "^9.6.0",
"@iconify-json/carbon": "^1.2.8",
"@iconify-json/logos": "^1.2.4",
"@iconify-json/mdi": "^1.2.3",
"@iconify/utils": "^2.3.0",
"@playwright/test": "^1.51.1",
"@primevue/auto-import-resolver": "^4.3.3",
"@tsconfig/node22": "^22.0.1",
"@types/archiver": "^6.0.3",
"@types/mockjs": "^1.0.10",
"@types/node": "^22.14.0",
"@eslint/compat": "^1.2.0",
"@faker-js/faker": "^9.0.3",
"@iconify-json/carbon": "^1.2.2",
"@iconify/utils": "^2.1.33",
"@playwright/test": "^1.48.0",
"@tsconfig/node20": "^20.1.4",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.7.5",
"@types/nprogress": "^0.2.3",
"@types/plotly.js-dist-min": "^2.3.4",
"@types/three": "^0.175.0",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vitejs/plugin-vue-jsx": "^4.1.2",
"@vitest/eslint-plugin": "^1.1.40",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0",
"@unocss/preset-attributify": "^0.63.4",
"@unocss/preset-rem-to-px": "^0.63.4",
"@unocss/reset": "^0.63.4",
"@vant/auto-import-resolver": "^1.2.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/eslint-config-prettier": "^10.0.0",
"@vue/eslint-config-typescript": "^14.0.1",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.7.0",
"archiver": "^7.0.1",
"@vue/tsconfig": "^0.5.1",
"depcheck": "^1.4.7",
"eruda": "^3.4.1",
"eslint": "^9.24.0",
"eslint-plugin-import-x": "^4.10.2",
"eslint-plugin-oxlint": "^0.16.6",
"eslint-plugin-perfectionist": "^4.11.0",
"eslint-plugin-unicorn": "^58.0.0",
"eslint-plugin-vue": "^10.0.0",
"husky": "^9.1.7",
"less": "^4.3.0",
"lint-staged": "^15.5.0",
"eruda": "^3.4.0",
"eslint": "^9.12.0",
"eslint-plugin-vue": "^9.29.0",
"husky": "^9.1.6",
"jsdom": "^25.0.1",
"less": "^4.2.0",
"lint-staged": "^15.2.10",
"mockjs": "^1.1.0",
"npm-run-all2": "^7.0.2",
"oxlint": "^0.16.6",
"prettier": "3.5.3",
"sass-embedded": "^1.86.3",
"terser": "^5.39.0",
"typescript": "~5.8.3",
"unocss": "66.1.0-beta.10",
"unocss-preset-animations": "^1.1.1",
"unocss-preset-chinese": "^0.3.3",
"unocss-preset-shadcn": "^0.5.0",
"unplugin-auto-import": "^19.1.2",
"unplugin-icons": "^22.1.0",
"unplugin-vue-components": "^28.5.0",
"unplugin-vue-macros": "^2.14.5",
"unplugin-vue-markdown": "^28.3.1",
"unplugin-vue-router": "^0.12.0",
"vite": "^6.2.6",
"vite-plugin-checker": "^0.9.1",
"vite-plugin-fake-server": "^2.2.0",
"vite-plugin-purgecss-updated-v5": "^1.2.4",
"vite-plugin-singlefile": "^2.2.0",
"vite-plugin-static-copy": "^2.3.1",
"vite-plugin-vue-devtools": "^7.7.2",
"vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-vue-meta-layouts": "^0.5.1",
"vite-plugin-vuetify": "^2.1.1",
"vite-plugin-webfont-dl": "^3.10.4",
"vue-component-type-helpers": "^2.2.8",
"vue-tsc": "^2.2.8"
"npm-run-all2": "^6.2.3",
"prettier": "^3.3.3",
"surge": "latest",
"typescript": "~5.6.0",
"unocss": "^0.63.4",
"unplugin-auto-import": "^0.18.3",
"unplugin-icons": "^0.19.3",
"unplugin-vue-components": "^0.27.4",
"unplugin-vue-macros": "^2.12.3",
"unplugin-vue-markdown": "^0.26.2",
"unplugin-vue-router": "^0.10.8",
"vercel": "latest",
"vite": "^5.4.8",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-fake-server": "^2.1.2",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@ -0,0 +1,13 @@
diff --git a/dist/vue-router.d.ts b/dist/vue-router.d.ts
index a66b6e0c627a9221a5394066666da49597891d07..19aa9d5c2d42a7221017265225d08c4467f1a69b 100644
--- a/dist/vue-router.d.ts
+++ b/dist/vue-router.d.ts
@@ -1751,7 +1751,7 @@ export { }
* NOTE: this used to be `@vue/runtime-core` but it should have been `vue` for a long time. Using both declaration at
* the same time breaks so using only one everywhere is the preferred way.
*/
-declare module 'vue' {
+declare module '@vue/runtime-core' {
export interface ComponentCustomOptions {
/**
* Guard called when the router is navigating to the route that is rendering

View File

@ -1,95 +1,79 @@
import { defineConfig, devices } from '@playwright/test';
import process from 'node:process';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
testDir: './tests/e2e',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Configure projects for major browsers */
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: {
// channel: 'chrome',
// },
// },
],
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
testDir: './tests/playwright',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL || 'https://vue-ts-example.oo1.dev',
/* Only on CI systems run the tests headless */
headless: !!process.env.CI || process.env.HEADLESS === 'true',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
// /* Run your local dev server before starting the tests */
// webServer: {
// /**
// * Use the dev server by default for faster feedback loop.
// * Use the preview server on CI for more realistic testing.
// * Playwright will re-use the local server if there is already a dev-server running.
// */
// command: process.env.CI ? 'npm run preview' : 'npm run dev',
// port: process.env.CI ? 4173 : 5173,
// reuseExistingServer: !process.env.CI,
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/',
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});

11062
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,81 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>星座图的html页面</title>
</head>
<body>
<div id="planispherewidget-container"></div>
<script src="./lib/konva.2.4.2.min.js"></script>
<script src="./lib/jquery.3.6.0.min.js"></script>
<script src="./planispherewidget.js"></script>
<script>
window.planisphereWidgetInstance = null;
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
// 强烈建议在此处添加来源验证 (event.origin) 以提高安全性
// 例如: if (event.origin !== 'YOUR_VUE_APP_ORIGIN') return;
if (!event.data) return; // 如果没有数据,则忽略
// 处理初始化消息
if (event.data.type === 'INIT_WIDGET') {
console.log('HTML 页面:收到初始化指令');
if (!window.planisphereWidgetInstance) {
// 创建 PlanisphereWidget 实例,指定容器 ID
window.planisphereWidgetInstance = new PlanisphereWidget('planispherewidget-container', {
// 从 planispherewidget.js 获取的默认值
bg_color: '#27293D', // 背景色
fore_color: '#CCCCCC', // 前景色
coor_color: '#1B1B24', // 坐标颜色
border_color: '#CCCCCC', // 边框颜色
wave_color: '#1D8CF8', // 波形颜色
margin_left: 0, // 左边距
margin_top: 0, // 上边距
// 您可以在这里覆盖上面的默认值,或者从 event.data.options 获取配置
// ...event.data.options // 如果 Vue 组件传递了配置
});
console.log('HTML 页面PlanisphereWidget 已实例化');
} else {
console.log('HTML 页面PlanisphereWidget 已存在,无需重复实例化');
}
// 可选:通知父窗口已准备好接收数据
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'WIDGET_READY' }, '*'); // 使用更具体的 targetOrigin
// }
}
// 处理数据馈送消息
else if (event.data.type === 'FEED_DATA') {
if (window.planisphereWidgetInstance) {
console.log('HTML 页面:收到来自父窗口的数据:', event.data.payload);
if (Array.isArray(event.data.payload)) {
window.planisphereWidgetInstance.feed(event.data.payload);
} else {
console.error('HTML 页面:收到的数据格式无效:', event.data.payload);
}
} else {
console.warn('HTML 页面Widget 尚未初始化,无法处理 FEED_DATA');
}
}
});
</script>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body > div {
width: 100%;
height: 100%;
}
</style>
</html>

View File

@ -1,19 +0,0 @@
## 这个代码没测试。
``javascript
// 如果需要明确设置模式(虽然默认就是星座图)
widget.setViewMode(0); // 0=星座图
widget.feed(testData);
// 如果想测试余晖效果
widget.setViewMode(3); // 3=余晖图
setInterval(() => {
const newData = [];
for (let i = 0; i < 20; i++) { // 每次添加少量新点
const x = Math.random() * 2 - 1;
const y = Math.random() * 2 - 1;
newData.push([x, y]);
}
widget.feed(newData); // 传入新数据以观察余晖
}, 500); // 每 500ms 更新一次
```

View File

@ -1,188 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>频谱图+瀑布图的html页面</title>
</head>
<body>
<div id="waterfall" style="width: 100%; height: 100%"></div>
<script src="./lib/konva.2.4.2.min.js"></script>
<script src="./lib/jquery.3.6.0.min.js"></script>
<script src="./lib/lodash@4.17.21.min.js"></script>
<script src="colormapwidget.js"></script>
<script src="waterfallwidget.js"></script>
<script>
// 定义默认配置对象
const payload = {
minFreq: 0,
maxFreq: 200000000,
rtmode: 0,
doppler: false,
adjShow: true,
adjMaxbw: 100000,
enableWfBuff: false,
unitId: 0,
coorFreqType: 0,
showFreqLable: true,
wfFreqPointLable: true,
wfFreqPointLableList: [],
showWhitelist: false,
waveSplite: 0.5,
};
const wfOption = {
wfRulerEnable: true,
wfTheme: 'default',
wfRulerGravity: true,
wfRulerLocation: -100,
wfMindB: -120,
wfMaxdB: 0,
showPeakMarker: true,
wfShowAvg: true,
wfShowMin: false,
wfShowMax: false,
wfInfoPos: 0,
wfCurLine: true,
specColor: '#00ff00',
specAvgColor: '#ffff00',
specMaxColor: '#ff0000',
specMinColor: '#0000ff',
afterGlowColor: '#00ffff',
wfAdjStepType: 0,
wfAdjStep: 1000,
};
const id = 'waterfall';
// 定义 label_manager
const pl = {
label_manager: {
labels: [
// 这里可以添加一些示例标签,或者保持为空
// { name: 'FM', id: 'id1', bg_color: '#2bffc6', fore_color: '#000', location: 80000000, bw: 100000 },
// { name: 'AM', id: 'id2', bg_color: '#2bffc6', fore_color: '#000', location: 90000000, bw: 10000 },
// { name: 'AM', id: 'id2', bg_color: '#ff0000', fore_color: '#ff0000', location: 90000000, bw: 10000 },
],
onclick: function (label) {
console.log('Label clicked:', label);
},
load: function (start, end) {
// 简单的 load 实现,可以根据需要扩展
console.log(`Loading labels for range: ${start} - ${end}`);
// return this.labels.filter(label => label.location >= start && label.location <= end);
return []; // 返回空数组以避免依赖 this.labels
},
},
};
// 全局变量,用于存储 waterfallwidget 实例
window.waterWidgetInstance = null;
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
// 强烈建议在此处添加来源验证 (event.origin) 以提高安全性
// 例如: if (event.origin !== 'YOUR_VUE_APP_ORIGIN') return;
if (!event.data) return; // 如果没有数据,则忽略
// 处理初始化消息
if (event.data.type === 'INIT_WIDGET') {
console.log('[🌐] HTML (频谱图): 收到初始化指令');
if (!window.waterWidgetInstance) {
// 合并 Vue 传递的配置(如果存在)
const mergedPayload = { ...payload, ...(event.data.config?.payload || {}) };
const mergedWfOption = { ...wfOption, ...(event.data.config?.wfOption || {}) };
// 创建 waterfallwidget 实例
window.waterWidgetInstance = new waterfallwidget(id, {
wfOption: mergedWfOption,
min_freq: mergedPayload.minFreq,
max_freq: mergedPayload.maxFreq,
min_band: 500,
max_band: 100000,
min_db: mergedWfOption.wfMindB,
max_db: mergedWfOption.wfMaxdB,
showPeakMarker: mergedWfOption.showPeakMarker,
showSpectrogramAvg: mergedWfOption.wfShowAvg,
showSpectrogramMin: mergedWfOption.wfShowMin,
showSpectrogramMax: mergedWfOption.wfShowMax,
spec_per: mergedPayload.waveSplite,
menuEnableHandle: null, // 在 iframe 中通常不需要此句柄
menuDisableText: '---',
menuDoppler: mergedPayload.doppler,
allowRunMode: mergedPayload.rtmode,
info_Position: mergedWfOption.wfInfoPos,
showCurLine: mergedWfOption.wfCurLine,
adj_maxbw: mergedPayload.adjMaxbw,
adj_show: mergedPayload.adjShow,
fucFFTBuff: mergedPayload.enableWfBuff ? this.fucFFTBuff.bind(this) : null, // 如果需要,保留 FFT 缓冲功能
unitBuffId: mergedPayload.unitId,
wave_color: mergedWfOption.specColor,
wave_color_avg: mergedWfOption.specAvgColor,
wave_color_max: mergedWfOption.specMaxColor,
wave_color_min: mergedWfOption.specMinColor,
waveAfterglow_color: mergedWfOption.afterGlowColor,
adj_step: mergedWfOption.wfAdjStepType == 1 ? 0 : mergedWfOption.wfAdjStep,
label_manager: pl.label_manager, // 保留 label_manager
coorFreqType: mergedPayload.coorFreqType,
showFreqLable: mergedPayload.showFreqLable,
wfFreqPointLable: mergedPayload.wfFreqPointLable,
wfFreqPointLableList: mergedPayload.wfFreqPointLableList,
showWhitelist: mergedPayload.showWhitelist,
});
console.log('[🌐] HTML (频谱图): waterfallwidget 已实例化');
// 监听用户框选范围变更事件 (移到初始化之后)
window.waterWidgetInstance.on('zoom-change', (e) => {
console.log('频率范围变更:', e.start, e.end);
// 可选:将此事件通知回 Vue 组件
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'ZOOM_CHANGE', payload: { start: e.start, end: e.end } }, '*');
// }
});
} else {
console.log('[🌐] HTML (频谱图): waterfallwidget 已存在,无需重复实例化');
}
// 可选:通知父窗口已准备好接收数据
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'WIDGET_READY' }, '*'); // 使用更具体的 targetOrigin
// }
}
// 处理数据馈送消息
else if (event.data.type === 'FEED_DATA') {
if (window.waterWidgetInstance) {
console.log('[🌐] HTML (频谱图): 收到来自父窗口的数据:', event.data.payload);
if (Array.isArray(event.data.payload)) {
// waterfallwidget.addData 接受可选的第二个参数 time
// 如果 Vue 组件传递了时间戳,可以在这里使用
const time = event.data.time || new Date().toLocaleTimeString(); // 如果没有提供时间,使用当前时间
window.waterWidgetInstance.addData(event.data.payload, time);
} else {
console.error('[🌐] HTML (频谱图): 收到的数据格式无效:', event.data.payload);
}
} else {
console.warn('[🌐] HTML (频谱图): Widget 尚未初始化,无法处理 FEED_DATA');
}
}
});
</script>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body > div {
width: 100%;
height: 100%;
}
</style>
</html>

View File

@ -1,153 +0,0 @@
// @ts-nocheck
colormapwidget.names = ['default', 'blue', 'gray', 'cooledit'];
colormapwidget.prototype.getGradientColors = function (name) {
if (name == 'blue') {
return ['#050525', '#000CFF', '#33FFFF', '#F9FFFF'];
} else if (name == 'gray') {
return ['#050525', '#FBFBFB'];
} else if (name == 'cooledit') {
return ['#010E19', '#720271', '#D7032C', '#FDBC5F', '#F8FFED'];
} else {
return ['#050525', '#0000CF', '#00FF20', '#EFFF00', '#FF007C'];
}
};
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
colormapwidget.prototype.colorRgb = function (sColor) {
var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
var sColor = sColor.toLowerCase();
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
var sColorNew = '#';
for (var i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = sColorNew;
}
//处理六位的颜色值
var sColorChange = [];
for (var i = 1; i < 7; i += 2) {
sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)));
}
return sColorChange;
} else {
return sColor;
}
};
// 将rgb表示方式转换为hex表示方式
colormapwidget.prototype.colorHex = function (rgb) {
var reg = /(\d{1,3}),(\d{1,3}),(\d{1,3})/;
var arr = reg.exec(rgb);
function hex(x) {
return ('0' + parseInt(x).toString(16)).slice(-2);
}
var _hex = '#' + hex(arr[1]) + hex(arr[2]) + hex(arr[3]);
return _hex.toUpperCase();
};
//计算两个颜色的step个阶梯色
colormapwidget.prototype.gradientColor = function (startColor, endColor, step) {
var startRGB = this.colorRgb(startColor); //转换为rgb数组模式
var startR = startRGB[0];
var startG = startRGB[1];
var startB = startRGB[2];
var endRGB = this.colorRgb(endColor);
var endR = endRGB[0];
var endG = endRGB[1];
var endB = endRGB[2];
var sR = (endR - startR) / step; //总差值
var sG = (endG - startG) / step;
var sB = (endB - startB) / step;
var colorArr = [];
for (var i = 0; i < step; i++) {
//计算每一步的hex值
//var hex = this.colorHex('rgb('+parseInt((sR*i+startR))+','+parseInt((sG*i+startG))+','+parseInt((sB*i+startB))+')');
//colorArr.push(hex);
colorArr.push([parseInt(sR * i + startR), parseInt(sG * i + startG), parseInt(sB * i + startB)]);
}
return colorArr;
};
colormapwidget.prototype.setDbRange = function (count) {
this.count = count;
this.mapCount = count;
this.calColors();
};
colormapwidget.prototype.getColors = function () {
if (
this.name === this.buff_name &&
this.gravity === this.buff_gravity &&
this.gravity_value === this.buff_gravity_value &&
this.count === this.buff_count
)
return this.buff_colors;
return this.calColors();
};
colormapwidget.prototype.getMapColors = function () {
return this.buff_mapCountcolors;
};
colormapwidget.prototype.calColors = function () {
this.buff_name = this.name;
this.buff_gravity = this.gravity;
this.buff_gravity_value = this.gravity_value;
this.buff_count = this.count;
this.buff_colors = this._calColorsByCount(this.count);
this.buff_mapCountcolors = this._calColorsByCount(this.mapCount);
return this.buff_colors;
};
colormapwidget.prototype._calColorsByCount = function (count) {
//console.log("_calColorsByCount " + count)
var colors = this.getGradientColors(this.name);
var arr = [];
var stepSpan = colors.length - 1;
if (!this.gravity) {
var step = Math.ceil(count / stepSpan);
for (var i = 0; i < stepSpan; i++) {
var carr = this.gradientColor(colors[i], colors[i + 1], step);
arr = arr.concat(carr);
}
} else {
var pStart = this.gravity_value - 0.2;
pStart = pStart < 0 ? 0 : pStart;
var pEnd = this.gravity_value + 0.2;
pEnd = pEnd > 1 ? 1 : pEnd;
pStart = count * pStart;
pEnd = count * pEnd;
for (var i = 0; i < pStart; i++) {
arr.push(this.colorRgb(colors[0]));
}
var step = Math.ceil((pEnd - pStart + 1) / stepSpan);
for (var i = 0; i < stepSpan; i++) {
var carr = this.gradientColor(colors[i], colors[i + 1], step);
arr = arr.concat(carr);
}
for (var i = pEnd; i < count; i++) {
arr.push(this.colorRgb(colors[colors.length - 1]));
}
}
return arr;
};
function colormapwidget(option) {
this.name = option && option.name ? option.name : 'default';
this.gravity = option && option.gravity ? option.gravity : false;
this.gravity_value = option && option.gravity_value ? option.gravity_value : 0.5;
this.count = option && option.count ? option.count : 200;
this.mapCount = option && option.mapCount ? option.mapCount : 200;
this.getColors();
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,140 +0,0 @@
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
(function(){function n(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function t(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u<i;){var o=n[u];t(e,o,r(o),n)}return e}function r(n,t){for(var r=-1,e=null==n?0:n.length;++r<e&&t(n[r],r,n)!==!1;);return n}function e(n,t){for(var r=null==n?0:n.length;r--&&t(n[r],r,n)!==!1;);return n}function u(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(!t(n[r],r,n))return!1;
return!0}function i(n,t){for(var r=-1,e=null==n?0:n.length,u=0,i=[];++r<e;){var o=n[r];t(o,r,n)&&(i[u++]=o)}return i}function o(n,t){return!!(null==n?0:n.length)&&y(n,t,0)>-1}function f(n,t,r){for(var e=-1,u=null==n?0:n.length;++e<u;)if(r(t,n[e]))return!0;return!1}function c(n,t){for(var r=-1,e=null==n?0:n.length,u=Array(e);++r<e;)u[r]=t(n[r],r,n);return u}function a(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];return n}function l(n,t,r,e){var u=-1,i=null==n?0:n.length;for(e&&i&&(r=n[++u]);++u<i;)r=t(r,n[u],u,n);
return r}function s(n,t,r,e){var u=null==n?0:n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r}function h(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(t(n[r],r,n))return!0;return!1}function p(n){return n.split("")}function _(n){return n.match($t)||[]}function v(n,t,r){var e;return r(n,function(n,r,u){if(t(n,r,u))return e=r,!1}),e}function g(n,t,r,e){for(var u=n.length,i=r+(e?1:-1);e?i--:++i<u;)if(t(n[i],i,n))return i;return-1}function y(n,t,r){return t===t?Z(n,t,r):g(n,b,r)}function d(n,t,r,e){
for(var u=r-1,i=n.length;++u<i;)if(e(n[u],t))return u;return-1}function b(n){return n!==n}function w(n,t){var r=null==n?0:n.length;return r?k(n,t)/r:Cn}function m(n){return function(t){return null==t?X:t[n]}}function x(n){return function(t){return null==n?X:n[t]}}function j(n,t,r,e,u){return u(n,function(n,u,i){r=e?(e=!1,n):t(r,n,u,i)}),r}function A(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].value;return n}function k(n,t){for(var r,e=-1,u=n.length;++e<u;){var i=t(n[e]);i!==X&&(r=r===X?i:r+i);
}return r}function O(n,t){for(var r=-1,e=Array(n);++r<n;)e[r]=t(r);return e}function I(n,t){return c(t,function(t){return[t,n[t]]})}function R(n){return n?n.slice(0,H(n)+1).replace(Lt,""):n}function z(n){return function(t){return n(t)}}function E(n,t){return c(t,function(t){return n[t]})}function S(n,t){return n.has(t)}function W(n,t){for(var r=-1,e=n.length;++r<e&&y(t,n[r],0)>-1;);return r}function L(n,t){for(var r=n.length;r--&&y(t,n[r],0)>-1;);return r}function C(n,t){for(var r=n.length,e=0;r--;)n[r]===t&&++e;
return e}function U(n){return"\\"+Yr[n]}function B(n,t){return null==n?X:n[t]}function T(n){return Nr.test(n)}function $(n){return Pr.test(n)}function D(n){for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}function M(n){var t=-1,r=Array(n.size);return n.forEach(function(n,e){r[++t]=[e,n]}),r}function F(n,t){return function(r){return n(t(r))}}function N(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){var o=n[r];o!==t&&o!==cn||(n[r]=cn,i[u++]=r)}return i}function P(n){var t=-1,r=Array(n.size);
return n.forEach(function(n){r[++t]=n}),r}function q(n){var t=-1,r=Array(n.size);return n.forEach(function(n){r[++t]=[n,n]}),r}function Z(n,t,r){for(var e=r-1,u=n.length;++e<u;)if(n[e]===t)return e;return-1}function K(n,t,r){for(var e=r+1;e--;)if(n[e]===t)return e;return e}function V(n){return T(n)?J(n):_e(n)}function G(n){return T(n)?Y(n):p(n)}function H(n){for(var t=n.length;t--&&Ct.test(n.charAt(t)););return t}function J(n){for(var t=Mr.lastIndex=0;Mr.test(n);)++t;return t}function Y(n){return n.match(Mr)||[];
}function Q(n){return n.match(Fr)||[]}var X,nn="4.17.21",tn=200,rn="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",en="Expected a function",un="Invalid `variable` option passed into `_.template`",on="__lodash_hash_undefined__",fn=500,cn="__lodash_placeholder__",an=1,ln=2,sn=4,hn=1,pn=2,_n=1,vn=2,gn=4,yn=8,dn=16,bn=32,wn=64,mn=128,xn=256,jn=512,An=30,kn="...",On=800,In=16,Rn=1,zn=2,En=3,Sn=1/0,Wn=9007199254740991,Ln=1.7976931348623157e308,Cn=NaN,Un=4294967295,Bn=Un-1,Tn=Un>>>1,$n=[["ary",mn],["bind",_n],["bindKey",vn],["curry",yn],["curryRight",dn],["flip",jn],["partial",bn],["partialRight",wn],["rearg",xn]],Dn="[object Arguments]",Mn="[object Array]",Fn="[object AsyncFunction]",Nn="[object Boolean]",Pn="[object Date]",qn="[object DOMException]",Zn="[object Error]",Kn="[object Function]",Vn="[object GeneratorFunction]",Gn="[object Map]",Hn="[object Number]",Jn="[object Null]",Yn="[object Object]",Qn="[object Promise]",Xn="[object Proxy]",nt="[object RegExp]",tt="[object Set]",rt="[object String]",et="[object Symbol]",ut="[object Undefined]",it="[object WeakMap]",ot="[object WeakSet]",ft="[object ArrayBuffer]",ct="[object DataView]",at="[object Float32Array]",lt="[object Float64Array]",st="[object Int8Array]",ht="[object Int16Array]",pt="[object Int32Array]",_t="[object Uint8Array]",vt="[object Uint8ClampedArray]",gt="[object Uint16Array]",yt="[object Uint32Array]",dt=/\b__p \+= '';/g,bt=/\b(__p \+=) '' \+/g,wt=/(__e\(.*?\)|\b__t\)) \+\n'';/g,mt=/&(?:amp|lt|gt|quot|#39);/g,xt=/[&<>"']/g,jt=RegExp(mt.source),At=RegExp(xt.source),kt=/<%-([\s\S]+?)%>/g,Ot=/<%([\s\S]+?)%>/g,It=/<%=([\s\S]+?)%>/g,Rt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,zt=/^\w*$/,Et=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,St=/[\\^$.*+?()[\]{}|]/g,Wt=RegExp(St.source),Lt=/^\s+/,Ct=/\s/,Ut=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Bt=/\{\n\/\* \[wrapped with (.+)\] \*/,Tt=/,? & /,$t=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,Dt=/[()=,{}\[\]\/\s]/,Mt=/\\(\\)?/g,Ft=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Nt=/\w*$/,Pt=/^[-+]0x[0-9a-f]+$/i,qt=/^0b[01]+$/i,Zt=/^\[object .+?Constructor\]$/,Kt=/^0o[0-7]+$/i,Vt=/^(?:0|[1-9]\d*)$/,Gt=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,Ht=/($^)/,Jt=/['\n\r\u2028\u2029\\]/g,Yt="\\ud800-\\udfff",Qt="\\u0300-\\u036f",Xt="\\ufe20-\\ufe2f",nr="\\u20d0-\\u20ff",tr=Qt+Xt+nr,rr="\\u2700-\\u27bf",er="a-z\\xdf-\\xf6\\xf8-\\xff",ur="\\xac\\xb1\\xd7\\xf7",ir="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",or="\\u2000-\\u206f",fr=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",cr="A-Z\\xc0-\\xd6\\xd8-\\xde",ar="\\ufe0e\\ufe0f",lr=ur+ir+or+fr,sr="['\u2019]",hr="["+Yt+"]",pr="["+lr+"]",_r="["+tr+"]",vr="\\d+",gr="["+rr+"]",yr="["+er+"]",dr="[^"+Yt+lr+vr+rr+er+cr+"]",br="\\ud83c[\\udffb-\\udfff]",wr="(?:"+_r+"|"+br+")",mr="[^"+Yt+"]",xr="(?:\\ud83c[\\udde6-\\uddff]){2}",jr="[\\ud800-\\udbff][\\udc00-\\udfff]",Ar="["+cr+"]",kr="\\u200d",Or="(?:"+yr+"|"+dr+")",Ir="(?:"+Ar+"|"+dr+")",Rr="(?:"+sr+"(?:d|ll|m|re|s|t|ve))?",zr="(?:"+sr+"(?:D|LL|M|RE|S|T|VE))?",Er=wr+"?",Sr="["+ar+"]?",Wr="(?:"+kr+"(?:"+[mr,xr,jr].join("|")+")"+Sr+Er+")*",Lr="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Cr="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",Ur=Sr+Er+Wr,Br="(?:"+[gr,xr,jr].join("|")+")"+Ur,Tr="(?:"+[mr+_r+"?",_r,xr,jr,hr].join("|")+")",$r=RegExp(sr,"g"),Dr=RegExp(_r,"g"),Mr=RegExp(br+"(?="+br+")|"+Tr+Ur,"g"),Fr=RegExp([Ar+"?"+yr+"+"+Rr+"(?="+[pr,Ar,"$"].join("|")+")",Ir+"+"+zr+"(?="+[pr,Ar+Or,"$"].join("|")+")",Ar+"?"+Or+"+"+Rr,Ar+"+"+zr,Cr,Lr,vr,Br].join("|"),"g"),Nr=RegExp("["+kr+Yt+tr+ar+"]"),Pr=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,qr=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Zr=-1,Kr={};
Kr[at]=Kr[lt]=Kr[st]=Kr[ht]=Kr[pt]=Kr[_t]=Kr[vt]=Kr[gt]=Kr[yt]=!0,Kr[Dn]=Kr[Mn]=Kr[ft]=Kr[Nn]=Kr[ct]=Kr[Pn]=Kr[Zn]=Kr[Kn]=Kr[Gn]=Kr[Hn]=Kr[Yn]=Kr[nt]=Kr[tt]=Kr[rt]=Kr[it]=!1;var Vr={};Vr[Dn]=Vr[Mn]=Vr[ft]=Vr[ct]=Vr[Nn]=Vr[Pn]=Vr[at]=Vr[lt]=Vr[st]=Vr[ht]=Vr[pt]=Vr[Gn]=Vr[Hn]=Vr[Yn]=Vr[nt]=Vr[tt]=Vr[rt]=Vr[et]=Vr[_t]=Vr[vt]=Vr[gt]=Vr[yt]=!0,Vr[Zn]=Vr[Kn]=Vr[it]=!1;var Gr={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a",
"\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae",
"\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g",
"\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O",
"\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w",
"\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},Hr={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},Jr={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'"},Yr={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Qr=parseFloat,Xr=parseInt,ne="object"==typeof global&&global&&global.Object===Object&&global,te="object"==typeof self&&self&&self.Object===Object&&self,re=ne||te||Function("return this")(),ee="object"==typeof exports&&exports&&!exports.nodeType&&exports,ue=ee&&"object"==typeof module&&module&&!module.nodeType&&module,ie=ue&&ue.exports===ee,oe=ie&&ne.process,fe=function(){
try{var n=ue&&ue.require&&ue.require("util").types;return n?n:oe&&oe.binding&&oe.binding("util")}catch(n){}}(),ce=fe&&fe.isArrayBuffer,ae=fe&&fe.isDate,le=fe&&fe.isMap,se=fe&&fe.isRegExp,he=fe&&fe.isSet,pe=fe&&fe.isTypedArray,_e=m("length"),ve=x(Gr),ge=x(Hr),ye=x(Jr),de=function p(x){function Z(n){if(cc(n)&&!bh(n)&&!(n instanceof Ct)){if(n instanceof Y)return n;if(bl.call(n,"__wrapped__"))return eo(n)}return new Y(n)}function J(){}function Y(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t,
this.__index__=0,this.__values__=X}function Ct(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=Un,this.__views__=[]}function $t(){var n=new Ct(this.__wrapped__);return n.__actions__=Tu(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=Tu(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=Tu(this.__views__),n}function Yt(){if(this.__filtered__){var n=new Ct(this);n.__dir__=-1,
n.__filtered__=!0}else n=this.clone(),n.__dir__*=-1;return n}function Qt(){var n=this.__wrapped__.value(),t=this.__dir__,r=bh(n),e=t<0,u=r?n.length:0,i=Oi(0,u,this.__views__),o=i.start,f=i.end,c=f-o,a=e?f:o-1,l=this.__iteratees__,s=l.length,h=0,p=Hl(c,this.__takeCount__);if(!r||!e&&u==c&&p==c)return wu(n,this.__actions__);var _=[];n:for(;c--&&h<p;){a+=t;for(var v=-1,g=n[a];++v<s;){var y=l[v],d=y.iteratee,b=y.type,w=d(g);if(b==zn)g=w;else if(!w){if(b==Rn)continue n;break n}}_[h++]=g}return _}function Xt(n){
var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function nr(){this.__data__=is?is(null):{},this.size=0}function tr(n){var t=this.has(n)&&delete this.__data__[n];return this.size-=t?1:0,t}function rr(n){var t=this.__data__;if(is){var r=t[n];return r===on?X:r}return bl.call(t,n)?t[n]:X}function er(n){var t=this.__data__;return is?t[n]!==X:bl.call(t,n)}function ur(n,t){var r=this.__data__;return this.size+=this.has(n)?0:1,r[n]=is&&t===X?on:t,this}function ir(n){
var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function or(){this.__data__=[],this.size=0}function fr(n){var t=this.__data__,r=Wr(t,n);return!(r<0)&&(r==t.length-1?t.pop():Ll.call(t,r,1),--this.size,!0)}function cr(n){var t=this.__data__,r=Wr(t,n);return r<0?X:t[r][1]}function ar(n){return Wr(this.__data__,n)>-1}function lr(n,t){var r=this.__data__,e=Wr(r,n);return e<0?(++this.size,r.push([n,t])):r[e][1]=t,this}function sr(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){
var e=n[t];this.set(e[0],e[1])}}function hr(){this.size=0,this.__data__={hash:new Xt,map:new(ts||ir),string:new Xt}}function pr(n){var t=xi(this,n).delete(n);return this.size-=t?1:0,t}function _r(n){return xi(this,n).get(n)}function vr(n){return xi(this,n).has(n)}function gr(n,t){var r=xi(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this}function yr(n){var t=-1,r=null==n?0:n.length;for(this.__data__=new sr;++t<r;)this.add(n[t])}function dr(n){return this.__data__.set(n,on),this}function br(n){
return this.__data__.has(n)}function wr(n){this.size=(this.__data__=new ir(n)).size}function mr(){this.__data__=new ir,this.size=0}function xr(n){var t=this.__data__,r=t.delete(n);return this.size=t.size,r}function jr(n){return this.__data__.get(n)}function Ar(n){return this.__data__.has(n)}function kr(n,t){var r=this.__data__;if(r instanceof ir){var e=r.__data__;if(!ts||e.length<tn-1)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new sr(e)}return r.set(n,t),this.size=r.size,this}function Or(n,t){
var r=bh(n),e=!r&&dh(n),u=!r&&!e&&mh(n),i=!r&&!e&&!u&&Oh(n),o=r||e||u||i,f=o?O(n.length,hl):[],c=f.length;for(var a in n)!t&&!bl.call(n,a)||o&&("length"==a||u&&("offset"==a||"parent"==a)||i&&("buffer"==a||"byteLength"==a||"byteOffset"==a)||Ci(a,c))||f.push(a);return f}function Ir(n){var t=n.length;return t?n[tu(0,t-1)]:X}function Rr(n,t){return Xi(Tu(n),Mr(t,0,n.length))}function zr(n){return Xi(Tu(n))}function Er(n,t,r){(r===X||Gf(n[t],r))&&(r!==X||t in n)||Br(n,t,r)}function Sr(n,t,r){var e=n[t];
bl.call(n,t)&&Gf(e,r)&&(r!==X||t in n)||Br(n,t,r)}function Wr(n,t){for(var r=n.length;r--;)if(Gf(n[r][0],t))return r;return-1}function Lr(n,t,r,e){return ys(n,function(n,u,i){t(e,n,r(n),i)}),e}function Cr(n,t){return n&&$u(t,Pc(t),n)}function Ur(n,t){return n&&$u(t,qc(t),n)}function Br(n,t,r){"__proto__"==t&&Tl?Tl(n,t,{configurable:!0,enumerable:!0,value:r,writable:!0}):n[t]=r}function Tr(n,t){for(var r=-1,e=t.length,u=il(e),i=null==n;++r<e;)u[r]=i?X:Mc(n,t[r]);return u}function Mr(n,t,r){return n===n&&(r!==X&&(n=n<=r?n:r),
t!==X&&(n=n>=t?n:t)),n}function Fr(n,t,e,u,i,o){var f,c=t&an,a=t&ln,l=t&sn;if(e&&(f=i?e(n,u,i,o):e(n)),f!==X)return f;if(!fc(n))return n;var s=bh(n);if(s){if(f=zi(n),!c)return Tu(n,f)}else{var h=zs(n),p=h==Kn||h==Vn;if(mh(n))return Iu(n,c);if(h==Yn||h==Dn||p&&!i){if(f=a||p?{}:Ei(n),!c)return a?Mu(n,Ur(f,n)):Du(n,Cr(f,n))}else{if(!Vr[h])return i?n:{};f=Si(n,h,c)}}o||(o=new wr);var _=o.get(n);if(_)return _;o.set(n,f),kh(n)?n.forEach(function(r){f.add(Fr(r,t,e,r,n,o))}):jh(n)&&n.forEach(function(r,u){
f.set(u,Fr(r,t,e,u,n,o))});var v=l?a?di:yi:a?qc:Pc,g=s?X:v(n);return r(g||n,function(r,u){g&&(u=r,r=n[u]),Sr(f,u,Fr(r,t,e,u,n,o))}),f}function Nr(n){var t=Pc(n);return function(r){return Pr(r,n,t)}}function Pr(n,t,r){var e=r.length;if(null==n)return!e;for(n=ll(n);e--;){var u=r[e],i=t[u],o=n[u];if(o===X&&!(u in n)||!i(o))return!1}return!0}function Gr(n,t,r){if("function"!=typeof n)throw new pl(en);return Ws(function(){n.apply(X,r)},t)}function Hr(n,t,r,e){var u=-1,i=o,a=!0,l=n.length,s=[],h=t.length;
if(!l)return s;r&&(t=c(t,z(r))),e?(i=f,a=!1):t.length>=tn&&(i=S,a=!1,t=new yr(t));n:for(;++u<l;){var p=n[u],_=null==r?p:r(p);if(p=e||0!==p?p:0,a&&_===_){for(var v=h;v--;)if(t[v]===_)continue n;s.push(p)}else i(t,_,e)||s.push(p)}return s}function Jr(n,t){var r=!0;return ys(n,function(n,e,u){return r=!!t(n,e,u)}),r}function Yr(n,t,r){for(var e=-1,u=n.length;++e<u;){var i=n[e],o=t(i);if(null!=o&&(f===X?o===o&&!bc(o):r(o,f)))var f=o,c=i}return c}function ne(n,t,r,e){var u=n.length;for(r=kc(r),r<0&&(r=-r>u?0:u+r),
e=e===X||e>u?u:kc(e),e<0&&(e+=u),e=r>e?0:Oc(e);r<e;)n[r++]=t;return n}function te(n,t){var r=[];return ys(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function ee(n,t,r,e,u){var i=-1,o=n.length;for(r||(r=Li),u||(u=[]);++i<o;){var f=n[i];t>0&&r(f)?t>1?ee(f,t-1,r,e,u):a(u,f):e||(u[u.length]=f)}return u}function ue(n,t){return n&&bs(n,t,Pc)}function oe(n,t){return n&&ws(n,t,Pc)}function fe(n,t){return i(t,function(t){return uc(n[t])})}function _e(n,t){t=ku(t,n);for(var r=0,e=t.length;null!=n&&r<e;)n=n[no(t[r++])];
return r&&r==e?n:X}function de(n,t,r){var e=t(n);return bh(n)?e:a(e,r(n))}function we(n){return null==n?n===X?ut:Jn:Bl&&Bl in ll(n)?ki(n):Ki(n)}function me(n,t){return n>t}function xe(n,t){return null!=n&&bl.call(n,t)}function je(n,t){return null!=n&&t in ll(n)}function Ae(n,t,r){return n>=Hl(t,r)&&n<Gl(t,r)}function ke(n,t,r){for(var e=r?f:o,u=n[0].length,i=n.length,a=i,l=il(i),s=1/0,h=[];a--;){var p=n[a];a&&t&&(p=c(p,z(t))),s=Hl(p.length,s),l[a]=!r&&(t||u>=120&&p.length>=120)?new yr(a&&p):X}p=n[0];
var _=-1,v=l[0];n:for(;++_<u&&h.length<s;){var g=p[_],y=t?t(g):g;if(g=r||0!==g?g:0,!(v?S(v,y):e(h,y,r))){for(a=i;--a;){var d=l[a];if(!(d?S(d,y):e(n[a],y,r)))continue n}v&&v.push(y),h.push(g)}}return h}function Oe(n,t,r,e){return ue(n,function(n,u,i){t(e,r(n),u,i)}),e}function Ie(t,r,e){r=ku(r,t),t=Gi(t,r);var u=null==t?t:t[no(jo(r))];return null==u?X:n(u,t,e)}function Re(n){return cc(n)&&we(n)==Dn}function ze(n){return cc(n)&&we(n)==ft}function Ee(n){return cc(n)&&we(n)==Pn}function Se(n,t,r,e,u){
return n===t||(null==n||null==t||!cc(n)&&!cc(t)?n!==n&&t!==t:We(n,t,r,e,Se,u))}function We(n,t,r,e,u,i){var o=bh(n),f=bh(t),c=o?Mn:zs(n),a=f?Mn:zs(t);c=c==Dn?Yn:c,a=a==Dn?Yn:a;var l=c==Yn,s=a==Yn,h=c==a;if(h&&mh(n)){if(!mh(t))return!1;o=!0,l=!1}if(h&&!l)return i||(i=new wr),o||Oh(n)?pi(n,t,r,e,u,i):_i(n,t,c,r,e,u,i);if(!(r&hn)){var p=l&&bl.call(n,"__wrapped__"),_=s&&bl.call(t,"__wrapped__");if(p||_){var v=p?n.value():n,g=_?t.value():t;return i||(i=new wr),u(v,g,r,e,i)}}return!!h&&(i||(i=new wr),vi(n,t,r,e,u,i));
}function Le(n){return cc(n)&&zs(n)==Gn}function Ce(n,t,r,e){var u=r.length,i=u,o=!e;if(null==n)return!i;for(n=ll(n);u--;){var f=r[u];if(o&&f[2]?f[1]!==n[f[0]]:!(f[0]in n))return!1}for(;++u<i;){f=r[u];var c=f[0],a=n[c],l=f[1];if(o&&f[2]){if(a===X&&!(c in n))return!1}else{var s=new wr;if(e)var h=e(a,l,c,n,t,s);if(!(h===X?Se(l,a,hn|pn,e,s):h))return!1}}return!0}function Ue(n){return!(!fc(n)||Di(n))&&(uc(n)?kl:Zt).test(to(n))}function Be(n){return cc(n)&&we(n)==nt}function Te(n){return cc(n)&&zs(n)==tt;
}function $e(n){return cc(n)&&oc(n.length)&&!!Kr[we(n)]}function De(n){return"function"==typeof n?n:null==n?La:"object"==typeof n?bh(n)?Ze(n[0],n[1]):qe(n):Fa(n)}function Me(n){if(!Mi(n))return Vl(n);var t=[];for(var r in ll(n))bl.call(n,r)&&"constructor"!=r&&t.push(r);return t}function Fe(n){if(!fc(n))return Zi(n);var t=Mi(n),r=[];for(var e in n)("constructor"!=e||!t&&bl.call(n,e))&&r.push(e);return r}function Ne(n,t){return n<t}function Pe(n,t){var r=-1,e=Hf(n)?il(n.length):[];return ys(n,function(n,u,i){
e[++r]=t(n,u,i)}),e}function qe(n){var t=ji(n);return 1==t.length&&t[0][2]?Ni(t[0][0],t[0][1]):function(r){return r===n||Ce(r,n,t)}}function Ze(n,t){return Bi(n)&&Fi(t)?Ni(no(n),t):function(r){var e=Mc(r,n);return e===X&&e===t?Nc(r,n):Se(t,e,hn|pn)}}function Ke(n,t,r,e,u){n!==t&&bs(t,function(i,o){if(u||(u=new wr),fc(i))Ve(n,t,o,r,Ke,e,u);else{var f=e?e(Ji(n,o),i,o+"",n,t,u):X;f===X&&(f=i),Er(n,o,f)}},qc)}function Ve(n,t,r,e,u,i,o){var f=Ji(n,r),c=Ji(t,r),a=o.get(c);if(a)return Er(n,r,a),X;var l=i?i(f,c,r+"",n,t,o):X,s=l===X;
if(s){var h=bh(c),p=!h&&mh(c),_=!h&&!p&&Oh(c);l=c,h||p||_?bh(f)?l=f:Jf(f)?l=Tu(f):p?(s=!1,l=Iu(c,!0)):_?(s=!1,l=Wu(c,!0)):l=[]:gc(c)||dh(c)?(l=f,dh(f)?l=Rc(f):fc(f)&&!uc(f)||(l=Ei(c))):s=!1}s&&(o.set(c,l),u(l,c,e,i,o),o.delete(c)),Er(n,r,l)}function Ge(n,t){var r=n.length;if(r)return t+=t<0?r:0,Ci(t,r)?n[t]:X}function He(n,t,r){t=t.length?c(t,function(n){return bh(n)?function(t){return _e(t,1===n.length?n[0]:n)}:n}):[La];var e=-1;return t=c(t,z(mi())),A(Pe(n,function(n,r,u){return{criteria:c(t,function(t){
return t(n)}),index:++e,value:n}}),function(n,t){return Cu(n,t,r)})}function Je(n,t){return Ye(n,t,function(t,r){return Nc(n,r)})}function Ye(n,t,r){for(var e=-1,u=t.length,i={};++e<u;){var o=t[e],f=_e(n,o);r(f,o)&&fu(i,ku(o,n),f)}return i}function Qe(n){return function(t){return _e(t,n)}}function Xe(n,t,r,e){var u=e?d:y,i=-1,o=t.length,f=n;for(n===t&&(t=Tu(t)),r&&(f=c(n,z(r)));++i<o;)for(var a=0,l=t[i],s=r?r(l):l;(a=u(f,s,a,e))>-1;)f!==n&&Ll.call(f,a,1),Ll.call(n,a,1);return n}function nu(n,t){for(var r=n?t.length:0,e=r-1;r--;){
var u=t[r];if(r==e||u!==i){var i=u;Ci(u)?Ll.call(n,u,1):yu(n,u)}}return n}function tu(n,t){return n+Nl(Ql()*(t-n+1))}function ru(n,t,r,e){for(var u=-1,i=Gl(Fl((t-n)/(r||1)),0),o=il(i);i--;)o[e?i:++u]=n,n+=r;return o}function eu(n,t){var r="";if(!n||t<1||t>Wn)return r;do t%2&&(r+=n),t=Nl(t/2),t&&(n+=n);while(t);return r}function uu(n,t){return Ls(Vi(n,t,La),n+"")}function iu(n){return Ir(ra(n))}function ou(n,t){var r=ra(n);return Xi(r,Mr(t,0,r.length))}function fu(n,t,r,e){if(!fc(n))return n;t=ku(t,n);
for(var u=-1,i=t.length,o=i-1,f=n;null!=f&&++u<i;){var c=no(t[u]),a=r;if("__proto__"===c||"constructor"===c||"prototype"===c)return n;if(u!=o){var l=f[c];a=e?e(l,c,f):X,a===X&&(a=fc(l)?l:Ci(t[u+1])?[]:{})}Sr(f,c,a),f=f[c]}return n}function cu(n){return Xi(ra(n))}function au(n,t,r){var e=-1,u=n.length;t<0&&(t=-t>u?0:u+t),r=r>u?u:r,r<0&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0;for(var i=il(u);++e<u;)i[e]=n[e+t];return i}function lu(n,t){var r;return ys(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function su(n,t,r){
var e=0,u=null==n?e:n.length;if("number"==typeof t&&t===t&&u<=Tn){for(;e<u;){var i=e+u>>>1,o=n[i];null!==o&&!bc(o)&&(r?o<=t:o<t)?e=i+1:u=i}return u}return hu(n,t,La,r)}function hu(n,t,r,e){var u=0,i=null==n?0:n.length;if(0===i)return 0;t=r(t);for(var o=t!==t,f=null===t,c=bc(t),a=t===X;u<i;){var l=Nl((u+i)/2),s=r(n[l]),h=s!==X,p=null===s,_=s===s,v=bc(s);if(o)var g=e||_;else g=a?_&&(e||h):f?_&&h&&(e||!p):c?_&&h&&!p&&(e||!v):!p&&!v&&(e?s<=t:s<t);g?u=l+1:i=l}return Hl(i,Bn)}function pu(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){
var o=n[r],f=t?t(o):o;if(!r||!Gf(f,c)){var c=f;i[u++]=0===o?0:o}}return i}function _u(n){return"number"==typeof n?n:bc(n)?Cn:+n}function vu(n){if("string"==typeof n)return n;if(bh(n))return c(n,vu)+"";if(bc(n))return vs?vs.call(n):"";var t=n+"";return"0"==t&&1/n==-Sn?"-0":t}function gu(n,t,r){var e=-1,u=o,i=n.length,c=!0,a=[],l=a;if(r)c=!1,u=f;else if(i>=tn){var s=t?null:ks(n);if(s)return P(s);c=!1,u=S,l=new yr}else l=t?[]:a;n:for(;++e<i;){var h=n[e],p=t?t(h):h;if(h=r||0!==h?h:0,c&&p===p){for(var _=l.length;_--;)if(l[_]===p)continue n;
t&&l.push(p),a.push(h)}else u(l,p,r)||(l!==a&&l.push(p),a.push(h))}return a}function yu(n,t){return t=ku(t,n),n=Gi(n,t),null==n||delete n[no(jo(t))]}function du(n,t,r,e){return fu(n,t,r(_e(n,t)),e)}function bu(n,t,r,e){for(var u=n.length,i=e?u:-1;(e?i--:++i<u)&&t(n[i],i,n););return r?au(n,e?0:i,e?i+1:u):au(n,e?i+1:0,e?u:i)}function wu(n,t){var r=n;return r instanceof Ct&&(r=r.value()),l(t,function(n,t){return t.func.apply(t.thisArg,a([n],t.args))},r)}function mu(n,t,r){var e=n.length;if(e<2)return e?gu(n[0]):[];
for(var u=-1,i=il(e);++u<e;)for(var o=n[u],f=-1;++f<e;)f!=u&&(i[u]=Hr(i[u]||o,n[f],t,r));return gu(ee(i,1),t,r)}function xu(n,t,r){for(var e=-1,u=n.length,i=t.length,o={};++e<u;){r(o,n[e],e<i?t[e]:X)}return o}function ju(n){return Jf(n)?n:[]}function Au(n){return"function"==typeof n?n:La}function ku(n,t){return bh(n)?n:Bi(n,t)?[n]:Cs(Ec(n))}function Ou(n,t,r){var e=n.length;return r=r===X?e:r,!t&&r>=e?n:au(n,t,r)}function Iu(n,t){if(t)return n.slice();var r=n.length,e=zl?zl(r):new n.constructor(r);
return n.copy(e),e}function Ru(n){var t=new n.constructor(n.byteLength);return new Rl(t).set(new Rl(n)),t}function zu(n,t){return new n.constructor(t?Ru(n.buffer):n.buffer,n.byteOffset,n.byteLength)}function Eu(n){var t=new n.constructor(n.source,Nt.exec(n));return t.lastIndex=n.lastIndex,t}function Su(n){return _s?ll(_s.call(n)):{}}function Wu(n,t){return new n.constructor(t?Ru(n.buffer):n.buffer,n.byteOffset,n.length)}function Lu(n,t){if(n!==t){var r=n!==X,e=null===n,u=n===n,i=bc(n),o=t!==X,f=null===t,c=t===t,a=bc(t);
if(!f&&!a&&!i&&n>t||i&&o&&c&&!f&&!a||e&&o&&c||!r&&c||!u)return 1;if(!e&&!i&&!a&&n<t||a&&r&&u&&!e&&!i||f&&r&&u||!o&&u||!c)return-1}return 0}function Cu(n,t,r){for(var e=-1,u=n.criteria,i=t.criteria,o=u.length,f=r.length;++e<o;){var c=Lu(u[e],i[e]);if(c){if(e>=f)return c;return c*("desc"==r[e]?-1:1)}}return n.index-t.index}function Uu(n,t,r,e){for(var u=-1,i=n.length,o=r.length,f=-1,c=t.length,a=Gl(i-o,0),l=il(c+a),s=!e;++f<c;)l[f]=t[f];for(;++u<o;)(s||u<i)&&(l[r[u]]=n[u]);for(;a--;)l[f++]=n[u++];return l;
}function Bu(n,t,r,e){for(var u=-1,i=n.length,o=-1,f=r.length,c=-1,a=t.length,l=Gl(i-f,0),s=il(l+a),h=!e;++u<l;)s[u]=n[u];for(var p=u;++c<a;)s[p+c]=t[c];for(;++o<f;)(h||u<i)&&(s[p+r[o]]=n[u++]);return s}function Tu(n,t){var r=-1,e=n.length;for(t||(t=il(e));++r<e;)t[r]=n[r];return t}function $u(n,t,r,e){var u=!r;r||(r={});for(var i=-1,o=t.length;++i<o;){var f=t[i],c=e?e(r[f],n[f],f,r,n):X;c===X&&(c=n[f]),u?Br(r,f,c):Sr(r,f,c)}return r}function Du(n,t){return $u(n,Is(n),t)}function Mu(n,t){return $u(n,Rs(n),t);
}function Fu(n,r){return function(e,u){var i=bh(e)?t:Lr,o=r?r():{};return i(e,n,mi(u,2),o)}}function Nu(n){return uu(function(t,r){var e=-1,u=r.length,i=u>1?r[u-1]:X,o=u>2?r[2]:X;for(i=n.length>3&&"function"==typeof i?(u--,i):X,o&&Ui(r[0],r[1],o)&&(i=u<3?X:i,u=1),t=ll(t);++e<u;){var f=r[e];f&&n(t,f,e,i)}return t})}function Pu(n,t){return function(r,e){if(null==r)return r;if(!Hf(r))return n(r,e);for(var u=r.length,i=t?u:-1,o=ll(r);(t?i--:++i<u)&&e(o[i],i,o)!==!1;);return r}}function qu(n){return function(t,r,e){
for(var u=-1,i=ll(t),o=e(t),f=o.length;f--;){var c=o[n?f:++u];if(r(i[c],c,i)===!1)break}return t}}function Zu(n,t,r){function e(){return(this&&this!==re&&this instanceof e?i:n).apply(u?r:this,arguments)}var u=t&_n,i=Gu(n);return e}function Ku(n){return function(t){t=Ec(t);var r=T(t)?G(t):X,e=r?r[0]:t.charAt(0),u=r?Ou(r,1).join(""):t.slice(1);return e[n]()+u}}function Vu(n){return function(t){return l(Ra(ca(t).replace($r,"")),n,"")}}function Gu(n){return function(){var t=arguments;switch(t.length){
case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:return new n(t[0],t[1],t[2]);case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=gs(n.prototype),e=n.apply(r,t);return fc(e)?e:r}}function Hu(t,r,e){function u(){for(var o=arguments.length,f=il(o),c=o,a=wi(u);c--;)f[c]=arguments[c];var l=o<3&&f[0]!==a&&f[o-1]!==a?[]:N(f,a);
return o-=l.length,o<e?oi(t,r,Qu,u.placeholder,X,f,l,X,X,e-o):n(this&&this!==re&&this instanceof u?i:t,this,f)}var i=Gu(t);return u}function Ju(n){return function(t,r,e){var u=ll(t);if(!Hf(t)){var i=mi(r,3);t=Pc(t),r=function(n){return i(u[n],n,u)}}var o=n(t,r,e);return o>-1?u[i?t[o]:o]:X}}function Yu(n){return gi(function(t){var r=t.length,e=r,u=Y.prototype.thru;for(n&&t.reverse();e--;){var i=t[e];if("function"!=typeof i)throw new pl(en);if(u&&!o&&"wrapper"==bi(i))var o=new Y([],!0)}for(e=o?e:r;++e<r;){
i=t[e];var f=bi(i),c="wrapper"==f?Os(i):X;o=c&&$i(c[0])&&c[1]==(mn|yn|bn|xn)&&!c[4].length&&1==c[9]?o[bi(c[0])].apply(o,c[3]):1==i.length&&$i(i)?o[f]():o.thru(i)}return function(){var n=arguments,e=n[0];if(o&&1==n.length&&bh(e))return o.plant(e).value();for(var u=0,i=r?t[u].apply(this,n):e;++u<r;)i=t[u].call(this,i);return i}})}function Qu(n,t,r,e,u,i,o,f,c,a){function l(){for(var y=arguments.length,d=il(y),b=y;b--;)d[b]=arguments[b];if(_)var w=wi(l),m=C(d,w);if(e&&(d=Uu(d,e,u,_)),i&&(d=Bu(d,i,o,_)),
y-=m,_&&y<a){return oi(n,t,Qu,l.placeholder,r,d,N(d,w),f,c,a-y)}var x=h?r:this,j=p?x[n]:n;return y=d.length,f?d=Hi(d,f):v&&y>1&&d.reverse(),s&&c<y&&(d.length=c),this&&this!==re&&this instanceof l&&(j=g||Gu(j)),j.apply(x,d)}var s=t&mn,h=t&_n,p=t&vn,_=t&(yn|dn),v=t&jn,g=p?X:Gu(n);return l}function Xu(n,t){return function(r,e){return Oe(r,n,t(e),{})}}function ni(n,t){return function(r,e){var u;if(r===X&&e===X)return t;if(r!==X&&(u=r),e!==X){if(u===X)return e;"string"==typeof r||"string"==typeof e?(r=vu(r),
e=vu(e)):(r=_u(r),e=_u(e)),u=n(r,e)}return u}}function ti(t){return gi(function(r){return r=c(r,z(mi())),uu(function(e){var u=this;return t(r,function(t){return n(t,u,e)})})})}function ri(n,t){t=t===X?" ":vu(t);var r=t.length;if(r<2)return r?eu(t,n):t;var e=eu(t,Fl(n/V(t)));return T(t)?Ou(G(e),0,n).join(""):e.slice(0,n)}function ei(t,r,e,u){function i(){for(var r=-1,c=arguments.length,a=-1,l=u.length,s=il(l+c),h=this&&this!==re&&this instanceof i?f:t;++a<l;)s[a]=u[a];for(;c--;)s[a++]=arguments[++r];
return n(h,o?e:this,s)}var o=r&_n,f=Gu(t);return i}function ui(n){return function(t,r,e){return e&&"number"!=typeof e&&Ui(t,r,e)&&(r=e=X),t=Ac(t),r===X?(r=t,t=0):r=Ac(r),e=e===X?t<r?1:-1:Ac(e),ru(t,r,e,n)}}function ii(n){return function(t,r){return"string"==typeof t&&"string"==typeof r||(t=Ic(t),r=Ic(r)),n(t,r)}}function oi(n,t,r,e,u,i,o,f,c,a){var l=t&yn,s=l?o:X,h=l?X:o,p=l?i:X,_=l?X:i;t|=l?bn:wn,t&=~(l?wn:bn),t&gn||(t&=~(_n|vn));var v=[n,t,u,p,s,_,h,f,c,a],g=r.apply(X,v);return $i(n)&&Ss(g,v),g.placeholder=e,
Yi(g,n,t)}function fi(n){var t=al[n];return function(n,r){if(n=Ic(n),r=null==r?0:Hl(kc(r),292),r&&Zl(n)){var e=(Ec(n)+"e").split("e");return e=(Ec(t(e[0]+"e"+(+e[1]+r)))+"e").split("e"),+(e[0]+"e"+(+e[1]-r))}return t(n)}}function ci(n){return function(t){var r=zs(t);return r==Gn?M(t):r==tt?q(t):I(t,n(t))}}function ai(n,t,r,e,u,i,o,f){var c=t&vn;if(!c&&"function"!=typeof n)throw new pl(en);var a=e?e.length:0;if(a||(t&=~(bn|wn),e=u=X),o=o===X?o:Gl(kc(o),0),f=f===X?f:kc(f),a-=u?u.length:0,t&wn){var l=e,s=u;
e=u=X}var h=c?X:Os(n),p=[n,t,r,e,u,l,s,i,o,f];if(h&&qi(p,h),n=p[0],t=p[1],r=p[2],e=p[3],u=p[4],f=p[9]=p[9]===X?c?0:n.length:Gl(p[9]-a,0),!f&&t&(yn|dn)&&(t&=~(yn|dn)),t&&t!=_n)_=t==yn||t==dn?Hu(n,t,f):t!=bn&&t!=(_n|bn)||u.length?Qu.apply(X,p):ei(n,t,r,e);else var _=Zu(n,t,r);return Yi((h?ms:Ss)(_,p),n,t)}function li(n,t,r,e){return n===X||Gf(n,gl[r])&&!bl.call(e,r)?t:n}function si(n,t,r,e,u,i){return fc(n)&&fc(t)&&(i.set(t,n),Ke(n,t,X,si,i),i.delete(t)),n}function hi(n){return gc(n)?X:n}function pi(n,t,r,e,u,i){
var o=r&hn,f=n.length,c=t.length;if(f!=c&&!(o&&c>f))return!1;var a=i.get(n),l=i.get(t);if(a&&l)return a==t&&l==n;var s=-1,p=!0,_=r&pn?new yr:X;for(i.set(n,t),i.set(t,n);++s<f;){var v=n[s],g=t[s];if(e)var y=o?e(g,v,s,t,n,i):e(v,g,s,n,t,i);if(y!==X){if(y)continue;p=!1;break}if(_){if(!h(t,function(n,t){if(!S(_,t)&&(v===n||u(v,n,r,e,i)))return _.push(t)})){p=!1;break}}else if(v!==g&&!u(v,g,r,e,i)){p=!1;break}}return i.delete(n),i.delete(t),p}function _i(n,t,r,e,u,i,o){switch(r){case ct:if(n.byteLength!=t.byteLength||n.byteOffset!=t.byteOffset)return!1;
n=n.buffer,t=t.buffer;case ft:return!(n.byteLength!=t.byteLength||!i(new Rl(n),new Rl(t)));case Nn:case Pn:case Hn:return Gf(+n,+t);case Zn:return n.name==t.name&&n.message==t.message;case nt:case rt:return n==t+"";case Gn:var f=M;case tt:var c=e&hn;if(f||(f=P),n.size!=t.size&&!c)return!1;var a=o.get(n);if(a)return a==t;e|=pn,o.set(n,t);var l=pi(f(n),f(t),e,u,i,o);return o.delete(n),l;case et:if(_s)return _s.call(n)==_s.call(t)}return!1}function vi(n,t,r,e,u,i){var o=r&hn,f=yi(n),c=f.length;if(c!=yi(t).length&&!o)return!1;
for(var a=c;a--;){var l=f[a];if(!(o?l in t:bl.call(t,l)))return!1}var s=i.get(n),h=i.get(t);if(s&&h)return s==t&&h==n;var p=!0;i.set(n,t),i.set(t,n);for(var _=o;++a<c;){l=f[a];var v=n[l],g=t[l];if(e)var y=o?e(g,v,l,t,n,i):e(v,g,l,n,t,i);if(!(y===X?v===g||u(v,g,r,e,i):y)){p=!1;break}_||(_="constructor"==l)}if(p&&!_){var d=n.constructor,b=t.constructor;d!=b&&"constructor"in n&&"constructor"in t&&!("function"==typeof d&&d instanceof d&&"function"==typeof b&&b instanceof b)&&(p=!1)}return i.delete(n),
i.delete(t),p}function gi(n){return Ls(Vi(n,X,_o),n+"")}function yi(n){return de(n,Pc,Is)}function di(n){return de(n,qc,Rs)}function bi(n){for(var t=n.name+"",r=fs[t],e=bl.call(fs,t)?r.length:0;e--;){var u=r[e],i=u.func;if(null==i||i==n)return u.name}return t}function wi(n){return(bl.call(Z,"placeholder")?Z:n).placeholder}function mi(){var n=Z.iteratee||Ca;return n=n===Ca?De:n,arguments.length?n(arguments[0],arguments[1]):n}function xi(n,t){var r=n.__data__;return Ti(t)?r["string"==typeof t?"string":"hash"]:r.map;
}function ji(n){for(var t=Pc(n),r=t.length;r--;){var e=t[r],u=n[e];t[r]=[e,u,Fi(u)]}return t}function Ai(n,t){var r=B(n,t);return Ue(r)?r:X}function ki(n){var t=bl.call(n,Bl),r=n[Bl];try{n[Bl]=X;var e=!0}catch(n){}var u=xl.call(n);return e&&(t?n[Bl]=r:delete n[Bl]),u}function Oi(n,t,r){for(var e=-1,u=r.length;++e<u;){var i=r[e],o=i.size;switch(i.type){case"drop":n+=o;break;case"dropRight":t-=o;break;case"take":t=Hl(t,n+o);break;case"takeRight":n=Gl(n,t-o)}}return{start:n,end:t}}function Ii(n){var t=n.match(Bt);
return t?t[1].split(Tt):[]}function Ri(n,t,r){t=ku(t,n);for(var e=-1,u=t.length,i=!1;++e<u;){var o=no(t[e]);if(!(i=null!=n&&r(n,o)))break;n=n[o]}return i||++e!=u?i:(u=null==n?0:n.length,!!u&&oc(u)&&Ci(o,u)&&(bh(n)||dh(n)))}function zi(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&bl.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function Ei(n){return"function"!=typeof n.constructor||Mi(n)?{}:gs(El(n))}function Si(n,t,r){var e=n.constructor;switch(t){case ft:return Ru(n);
case Nn:case Pn:return new e(+n);case ct:return zu(n,r);case at:case lt:case st:case ht:case pt:case _t:case vt:case gt:case yt:return Wu(n,r);case Gn:return new e;case Hn:case rt:return new e(n);case nt:return Eu(n);case tt:return new e;case et:return Su(n)}}function Wi(n,t){var r=t.length;if(!r)return n;var e=r-1;return t[e]=(r>1?"& ":"")+t[e],t=t.join(r>2?", ":" "),n.replace(Ut,"{\n/* [wrapped with "+t+"] */\n")}function Li(n){return bh(n)||dh(n)||!!(Cl&&n&&n[Cl])}function Ci(n,t){var r=typeof n;
return t=null==t?Wn:t,!!t&&("number"==r||"symbol"!=r&&Vt.test(n))&&n>-1&&n%1==0&&n<t}function Ui(n,t,r){if(!fc(r))return!1;var e=typeof t;return!!("number"==e?Hf(r)&&Ci(t,r.length):"string"==e&&t in r)&&Gf(r[t],n)}function Bi(n,t){if(bh(n))return!1;var r=typeof n;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=n&&!bc(n))||(zt.test(n)||!Rt.test(n)||null!=t&&n in ll(t))}function Ti(n){var t=typeof n;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==n:null===n}function $i(n){
var t=bi(n),r=Z[t];if("function"!=typeof r||!(t in Ct.prototype))return!1;if(n===r)return!0;var e=Os(r);return!!e&&n===e[0]}function Di(n){return!!ml&&ml in n}function Mi(n){var t=n&&n.constructor;return n===("function"==typeof t&&t.prototype||gl)}function Fi(n){return n===n&&!fc(n)}function Ni(n,t){return function(r){return null!=r&&(r[n]===t&&(t!==X||n in ll(r)))}}function Pi(n){var t=Cf(n,function(n){return r.size===fn&&r.clear(),n}),r=t.cache;return t}function qi(n,t){var r=n[1],e=t[1],u=r|e,i=u<(_n|vn|mn),o=e==mn&&r==yn||e==mn&&r==xn&&n[7].length<=t[8]||e==(mn|xn)&&t[7].length<=t[8]&&r==yn;
if(!i&&!o)return n;e&_n&&(n[2]=t[2],u|=r&_n?0:gn);var f=t[3];if(f){var c=n[3];n[3]=c?Uu(c,f,t[4]):f,n[4]=c?N(n[3],cn):t[4]}return f=t[5],f&&(c=n[5],n[5]=c?Bu(c,f,t[6]):f,n[6]=c?N(n[5],cn):t[6]),f=t[7],f&&(n[7]=f),e&mn&&(n[8]=null==n[8]?t[8]:Hl(n[8],t[8])),null==n[9]&&(n[9]=t[9]),n[0]=t[0],n[1]=u,n}function Zi(n){var t=[];if(null!=n)for(var r in ll(n))t.push(r);return t}function Ki(n){return xl.call(n)}function Vi(t,r,e){return r=Gl(r===X?t.length-1:r,0),function(){for(var u=arguments,i=-1,o=Gl(u.length-r,0),f=il(o);++i<o;)f[i]=u[r+i];
i=-1;for(var c=il(r+1);++i<r;)c[i]=u[i];return c[r]=e(f),n(t,this,c)}}function Gi(n,t){return t.length<2?n:_e(n,au(t,0,-1))}function Hi(n,t){for(var r=n.length,e=Hl(t.length,r),u=Tu(n);e--;){var i=t[e];n[e]=Ci(i,r)?u[i]:X}return n}function Ji(n,t){if(("constructor"!==t||"function"!=typeof n[t])&&"__proto__"!=t)return n[t]}function Yi(n,t,r){var e=t+"";return Ls(n,Wi(e,ro(Ii(e),r)))}function Qi(n){var t=0,r=0;return function(){var e=Jl(),u=In-(e-r);if(r=e,u>0){if(++t>=On)return arguments[0]}else t=0;
return n.apply(X,arguments)}}function Xi(n,t){var r=-1,e=n.length,u=e-1;for(t=t===X?e:t;++r<t;){var i=tu(r,u),o=n[i];n[i]=n[r],n[r]=o}return n.length=t,n}function no(n){if("string"==typeof n||bc(n))return n;var t=n+"";return"0"==t&&1/n==-Sn?"-0":t}function to(n){if(null!=n){try{return dl.call(n)}catch(n){}try{return n+""}catch(n){}}return""}function ro(n,t){return r($n,function(r){var e="_."+r[0];t&r[1]&&!o(n,e)&&n.push(e)}),n.sort()}function eo(n){if(n instanceof Ct)return n.clone();var t=new Y(n.__wrapped__,n.__chain__);
return t.__actions__=Tu(n.__actions__),t.__index__=n.__index__,t.__values__=n.__values__,t}function uo(n,t,r){t=(r?Ui(n,t,r):t===X)?1:Gl(kc(t),0);var e=null==n?0:n.length;if(!e||t<1)return[];for(var u=0,i=0,o=il(Fl(e/t));u<e;)o[i++]=au(n,u,u+=t);return o}function io(n){for(var t=-1,r=null==n?0:n.length,e=0,u=[];++t<r;){var i=n[t];i&&(u[e++]=i)}return u}function oo(){var n=arguments.length;if(!n)return[];for(var t=il(n-1),r=arguments[0],e=n;e--;)t[e-1]=arguments[e];return a(bh(r)?Tu(r):[r],ee(t,1));
}function fo(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),au(n,t<0?0:t,e)):[]}function co(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),t=e-t,au(n,0,t<0?0:t)):[]}function ao(n,t){return n&&n.length?bu(n,mi(t,3),!0,!0):[]}function lo(n,t){return n&&n.length?bu(n,mi(t,3),!0):[]}function so(n,t,r,e){var u=null==n?0:n.length;return u?(r&&"number"!=typeof r&&Ui(n,t,r)&&(r=0,e=u),ne(n,t,r,e)):[]}function ho(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:kc(r);
return u<0&&(u=Gl(e+u,0)),g(n,mi(t,3),u)}function po(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e-1;return r!==X&&(u=kc(r),u=r<0?Gl(e+u,0):Hl(u,e-1)),g(n,mi(t,3),u,!0)}function _o(n){return(null==n?0:n.length)?ee(n,1):[]}function vo(n){return(null==n?0:n.length)?ee(n,Sn):[]}function go(n,t){return(null==n?0:n.length)?(t=t===X?1:kc(t),ee(n,t)):[]}function yo(n){for(var t=-1,r=null==n?0:n.length,e={};++t<r;){var u=n[t];e[u[0]]=u[1]}return e}function bo(n){return n&&n.length?n[0]:X}function wo(n,t,r){
var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:kc(r);return u<0&&(u=Gl(e+u,0)),y(n,t,u)}function mo(n){return(null==n?0:n.length)?au(n,0,-1):[]}function xo(n,t){return null==n?"":Kl.call(n,t)}function jo(n){var t=null==n?0:n.length;return t?n[t-1]:X}function Ao(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e;return r!==X&&(u=kc(r),u=u<0?Gl(e+u,0):Hl(u,e-1)),t===t?K(n,t,u):g(n,b,u,!0)}function ko(n,t){return n&&n.length?Ge(n,kc(t)):X}function Oo(n,t){return n&&n.length&&t&&t.length?Xe(n,t):n;
}function Io(n,t,r){return n&&n.length&&t&&t.length?Xe(n,t,mi(r,2)):n}function Ro(n,t,r){return n&&n.length&&t&&t.length?Xe(n,t,X,r):n}function zo(n,t){var r=[];if(!n||!n.length)return r;var e=-1,u=[],i=n.length;for(t=mi(t,3);++e<i;){var o=n[e];t(o,e,n)&&(r.push(o),u.push(e))}return nu(n,u),r}function Eo(n){return null==n?n:Xl.call(n)}function So(n,t,r){var e=null==n?0:n.length;return e?(r&&"number"!=typeof r&&Ui(n,t,r)?(t=0,r=e):(t=null==t?0:kc(t),r=r===X?e:kc(r)),au(n,t,r)):[]}function Wo(n,t){
return su(n,t)}function Lo(n,t,r){return hu(n,t,mi(r,2))}function Co(n,t){var r=null==n?0:n.length;if(r){var e=su(n,t);if(e<r&&Gf(n[e],t))return e}return-1}function Uo(n,t){return su(n,t,!0)}function Bo(n,t,r){return hu(n,t,mi(r,2),!0)}function To(n,t){if(null==n?0:n.length){var r=su(n,t,!0)-1;if(Gf(n[r],t))return r}return-1}function $o(n){return n&&n.length?pu(n):[]}function Do(n,t){return n&&n.length?pu(n,mi(t,2)):[]}function Mo(n){var t=null==n?0:n.length;return t?au(n,1,t):[]}function Fo(n,t,r){
return n&&n.length?(t=r||t===X?1:kc(t),au(n,0,t<0?0:t)):[]}function No(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),t=e-t,au(n,t<0?0:t,e)):[]}function Po(n,t){return n&&n.length?bu(n,mi(t,3),!1,!0):[]}function qo(n,t){return n&&n.length?bu(n,mi(t,3)):[]}function Zo(n){return n&&n.length?gu(n):[]}function Ko(n,t){return n&&n.length?gu(n,mi(t,2)):[]}function Vo(n,t){return t="function"==typeof t?t:X,n&&n.length?gu(n,X,t):[]}function Go(n){if(!n||!n.length)return[];var t=0;return n=i(n,function(n){
if(Jf(n))return t=Gl(n.length,t),!0}),O(t,function(t){return c(n,m(t))})}function Ho(t,r){if(!t||!t.length)return[];var e=Go(t);return null==r?e:c(e,function(t){return n(r,X,t)})}function Jo(n,t){return xu(n||[],t||[],Sr)}function Yo(n,t){return xu(n||[],t||[],fu)}function Qo(n){var t=Z(n);return t.__chain__=!0,t}function Xo(n,t){return t(n),n}function nf(n,t){return t(n)}function tf(){return Qo(this)}function rf(){return new Y(this.value(),this.__chain__)}function ef(){this.__values__===X&&(this.__values__=jc(this.value()));
var n=this.__index__>=this.__values__.length;return{done:n,value:n?X:this.__values__[this.__index__++]}}function uf(){return this}function of(n){for(var t,r=this;r instanceof J;){var e=eo(r);e.__index__=0,e.__values__=X,t?u.__wrapped__=e:t=e;var u=e;r=r.__wrapped__}return u.__wrapped__=n,t}function ff(){var n=this.__wrapped__;if(n instanceof Ct){var t=n;return this.__actions__.length&&(t=new Ct(this)),t=t.reverse(),t.__actions__.push({func:nf,args:[Eo],thisArg:X}),new Y(t,this.__chain__)}return this.thru(Eo);
}function cf(){return wu(this.__wrapped__,this.__actions__)}function af(n,t,r){var e=bh(n)?u:Jr;return r&&Ui(n,t,r)&&(t=X),e(n,mi(t,3))}function lf(n,t){return(bh(n)?i:te)(n,mi(t,3))}function sf(n,t){return ee(yf(n,t),1)}function hf(n,t){return ee(yf(n,t),Sn)}function pf(n,t,r){return r=r===X?1:kc(r),ee(yf(n,t),r)}function _f(n,t){return(bh(n)?r:ys)(n,mi(t,3))}function vf(n,t){return(bh(n)?e:ds)(n,mi(t,3))}function gf(n,t,r,e){n=Hf(n)?n:ra(n),r=r&&!e?kc(r):0;var u=n.length;return r<0&&(r=Gl(u+r,0)),
dc(n)?r<=u&&n.indexOf(t,r)>-1:!!u&&y(n,t,r)>-1}function yf(n,t){return(bh(n)?c:Pe)(n,mi(t,3))}function df(n,t,r,e){return null==n?[]:(bh(t)||(t=null==t?[]:[t]),r=e?X:r,bh(r)||(r=null==r?[]:[r]),He(n,t,r))}function bf(n,t,r){var e=bh(n)?l:j,u=arguments.length<3;return e(n,mi(t,4),r,u,ys)}function wf(n,t,r){var e=bh(n)?s:j,u=arguments.length<3;return e(n,mi(t,4),r,u,ds)}function mf(n,t){return(bh(n)?i:te)(n,Uf(mi(t,3)))}function xf(n){return(bh(n)?Ir:iu)(n)}function jf(n,t,r){return t=(r?Ui(n,t,r):t===X)?1:kc(t),
(bh(n)?Rr:ou)(n,t)}function Af(n){return(bh(n)?zr:cu)(n)}function kf(n){if(null==n)return 0;if(Hf(n))return dc(n)?V(n):n.length;var t=zs(n);return t==Gn||t==tt?n.size:Me(n).length}function Of(n,t,r){var e=bh(n)?h:lu;return r&&Ui(n,t,r)&&(t=X),e(n,mi(t,3))}function If(n,t){if("function"!=typeof t)throw new pl(en);return n=kc(n),function(){if(--n<1)return t.apply(this,arguments)}}function Rf(n,t,r){return t=r?X:t,t=n&&null==t?n.length:t,ai(n,mn,X,X,X,X,t)}function zf(n,t){var r;if("function"!=typeof t)throw new pl(en);
return n=kc(n),function(){return--n>0&&(r=t.apply(this,arguments)),n<=1&&(t=X),r}}function Ef(n,t,r){t=r?X:t;var e=ai(n,yn,X,X,X,X,X,t);return e.placeholder=Ef.placeholder,e}function Sf(n,t,r){t=r?X:t;var e=ai(n,dn,X,X,X,X,X,t);return e.placeholder=Sf.placeholder,e}function Wf(n,t,r){function e(t){var r=h,e=p;return h=p=X,d=t,v=n.apply(e,r)}function u(n){return d=n,g=Ws(f,t),b?e(n):v}function i(n){var r=n-y,e=n-d,u=t-r;return w?Hl(u,_-e):u}function o(n){var r=n-y,e=n-d;return y===X||r>=t||r<0||w&&e>=_;
}function f(){var n=fh();return o(n)?c(n):(g=Ws(f,i(n)),X)}function c(n){return g=X,m&&h?e(n):(h=p=X,v)}function a(){g!==X&&As(g),d=0,h=y=p=g=X}function l(){return g===X?v:c(fh())}function s(){var n=fh(),r=o(n);if(h=arguments,p=this,y=n,r){if(g===X)return u(y);if(w)return As(g),g=Ws(f,t),e(y)}return g===X&&(g=Ws(f,t)),v}var h,p,_,v,g,y,d=0,b=!1,w=!1,m=!0;if("function"!=typeof n)throw new pl(en);return t=Ic(t)||0,fc(r)&&(b=!!r.leading,w="maxWait"in r,_=w?Gl(Ic(r.maxWait)||0,t):_,m="trailing"in r?!!r.trailing:m),
s.cancel=a,s.flush=l,s}function Lf(n){return ai(n,jn)}function Cf(n,t){if("function"!=typeof n||null!=t&&"function"!=typeof t)throw new pl(en);var r=function(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var o=n.apply(this,e);return r.cache=i.set(u,o)||i,o};return r.cache=new(Cf.Cache||sr),r}function Uf(n){if("function"!=typeof n)throw new pl(en);return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:
return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}function Bf(n){return zf(2,n)}function Tf(n,t){if("function"!=typeof n)throw new pl(en);return t=t===X?t:kc(t),uu(n,t)}function $f(t,r){if("function"!=typeof t)throw new pl(en);return r=null==r?0:Gl(kc(r),0),uu(function(e){var u=e[r],i=Ou(e,0,r);return u&&a(i,u),n(t,this,i)})}function Df(n,t,r){var e=!0,u=!0;if("function"!=typeof n)throw new pl(en);return fc(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),
Wf(n,t,{leading:e,maxWait:t,trailing:u})}function Mf(n){return Rf(n,1)}function Ff(n,t){return ph(Au(t),n)}function Nf(){if(!arguments.length)return[];var n=arguments[0];return bh(n)?n:[n]}function Pf(n){return Fr(n,sn)}function qf(n,t){return t="function"==typeof t?t:X,Fr(n,sn,t)}function Zf(n){return Fr(n,an|sn)}function Kf(n,t){return t="function"==typeof t?t:X,Fr(n,an|sn,t)}function Vf(n,t){return null==t||Pr(n,t,Pc(t))}function Gf(n,t){return n===t||n!==n&&t!==t}function Hf(n){return null!=n&&oc(n.length)&&!uc(n);
}function Jf(n){return cc(n)&&Hf(n)}function Yf(n){return n===!0||n===!1||cc(n)&&we(n)==Nn}function Qf(n){return cc(n)&&1===n.nodeType&&!gc(n)}function Xf(n){if(null==n)return!0;if(Hf(n)&&(bh(n)||"string"==typeof n||"function"==typeof n.splice||mh(n)||Oh(n)||dh(n)))return!n.length;var t=zs(n);if(t==Gn||t==tt)return!n.size;if(Mi(n))return!Me(n).length;for(var r in n)if(bl.call(n,r))return!1;return!0}function nc(n,t){return Se(n,t)}function tc(n,t,r){r="function"==typeof r?r:X;var e=r?r(n,t):X;return e===X?Se(n,t,X,r):!!e;
}function rc(n){if(!cc(n))return!1;var t=we(n);return t==Zn||t==qn||"string"==typeof n.message&&"string"==typeof n.name&&!gc(n)}function ec(n){return"number"==typeof n&&Zl(n)}function uc(n){if(!fc(n))return!1;var t=we(n);return t==Kn||t==Vn||t==Fn||t==Xn}function ic(n){return"number"==typeof n&&n==kc(n)}function oc(n){return"number"==typeof n&&n>-1&&n%1==0&&n<=Wn}function fc(n){var t=typeof n;return null!=n&&("object"==t||"function"==t)}function cc(n){return null!=n&&"object"==typeof n}function ac(n,t){
return n===t||Ce(n,t,ji(t))}function lc(n,t,r){return r="function"==typeof r?r:X,Ce(n,t,ji(t),r)}function sc(n){return vc(n)&&n!=+n}function hc(n){if(Es(n))throw new fl(rn);return Ue(n)}function pc(n){return null===n}function _c(n){return null==n}function vc(n){return"number"==typeof n||cc(n)&&we(n)==Hn}function gc(n){if(!cc(n)||we(n)!=Yn)return!1;var t=El(n);if(null===t)return!0;var r=bl.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&dl.call(r)==jl}function yc(n){
return ic(n)&&n>=-Wn&&n<=Wn}function dc(n){return"string"==typeof n||!bh(n)&&cc(n)&&we(n)==rt}function bc(n){return"symbol"==typeof n||cc(n)&&we(n)==et}function wc(n){return n===X}function mc(n){return cc(n)&&zs(n)==it}function xc(n){return cc(n)&&we(n)==ot}function jc(n){if(!n)return[];if(Hf(n))return dc(n)?G(n):Tu(n);if(Ul&&n[Ul])return D(n[Ul]());var t=zs(n);return(t==Gn?M:t==tt?P:ra)(n)}function Ac(n){if(!n)return 0===n?n:0;if(n=Ic(n),n===Sn||n===-Sn){return(n<0?-1:1)*Ln}return n===n?n:0}function kc(n){
var t=Ac(n),r=t%1;return t===t?r?t-r:t:0}function Oc(n){return n?Mr(kc(n),0,Un):0}function Ic(n){if("number"==typeof n)return n;if(bc(n))return Cn;if(fc(n)){var t="function"==typeof n.valueOf?n.valueOf():n;n=fc(t)?t+"":t}if("string"!=typeof n)return 0===n?n:+n;n=R(n);var r=qt.test(n);return r||Kt.test(n)?Xr(n.slice(2),r?2:8):Pt.test(n)?Cn:+n}function Rc(n){return $u(n,qc(n))}function zc(n){return n?Mr(kc(n),-Wn,Wn):0===n?n:0}function Ec(n){return null==n?"":vu(n)}function Sc(n,t){var r=gs(n);return null==t?r:Cr(r,t);
}function Wc(n,t){return v(n,mi(t,3),ue)}function Lc(n,t){return v(n,mi(t,3),oe)}function Cc(n,t){return null==n?n:bs(n,mi(t,3),qc)}function Uc(n,t){return null==n?n:ws(n,mi(t,3),qc)}function Bc(n,t){return n&&ue(n,mi(t,3))}function Tc(n,t){return n&&oe(n,mi(t,3))}function $c(n){return null==n?[]:fe(n,Pc(n))}function Dc(n){return null==n?[]:fe(n,qc(n))}function Mc(n,t,r){var e=null==n?X:_e(n,t);return e===X?r:e}function Fc(n,t){return null!=n&&Ri(n,t,xe)}function Nc(n,t){return null!=n&&Ri(n,t,je);
}function Pc(n){return Hf(n)?Or(n):Me(n)}function qc(n){return Hf(n)?Or(n,!0):Fe(n)}function Zc(n,t){var r={};return t=mi(t,3),ue(n,function(n,e,u){Br(r,t(n,e,u),n)}),r}function Kc(n,t){var r={};return t=mi(t,3),ue(n,function(n,e,u){Br(r,e,t(n,e,u))}),r}function Vc(n,t){return Gc(n,Uf(mi(t)))}function Gc(n,t){if(null==n)return{};var r=c(di(n),function(n){return[n]});return t=mi(t),Ye(n,r,function(n,r){return t(n,r[0])})}function Hc(n,t,r){t=ku(t,n);var e=-1,u=t.length;for(u||(u=1,n=X);++e<u;){var i=null==n?X:n[no(t[e])];
i===X&&(e=u,i=r),n=uc(i)?i.call(n):i}return n}function Jc(n,t,r){return null==n?n:fu(n,t,r)}function Yc(n,t,r,e){return e="function"==typeof e?e:X,null==n?n:fu(n,t,r,e)}function Qc(n,t,e){var u=bh(n),i=u||mh(n)||Oh(n);if(t=mi(t,4),null==e){var o=n&&n.constructor;e=i?u?new o:[]:fc(n)&&uc(o)?gs(El(n)):{}}return(i?r:ue)(n,function(n,r,u){return t(e,n,r,u)}),e}function Xc(n,t){return null==n||yu(n,t)}function na(n,t,r){return null==n?n:du(n,t,Au(r))}function ta(n,t,r,e){return e="function"==typeof e?e:X,
null==n?n:du(n,t,Au(r),e)}function ra(n){return null==n?[]:E(n,Pc(n))}function ea(n){return null==n?[]:E(n,qc(n))}function ua(n,t,r){return r===X&&(r=t,t=X),r!==X&&(r=Ic(r),r=r===r?r:0),t!==X&&(t=Ic(t),t=t===t?t:0),Mr(Ic(n),t,r)}function ia(n,t,r){return t=Ac(t),r===X?(r=t,t=0):r=Ac(r),n=Ic(n),Ae(n,t,r)}function oa(n,t,r){if(r&&"boolean"!=typeof r&&Ui(n,t,r)&&(t=r=X),r===X&&("boolean"==typeof t?(r=t,t=X):"boolean"==typeof n&&(r=n,n=X)),n===X&&t===X?(n=0,t=1):(n=Ac(n),t===X?(t=n,n=0):t=Ac(t)),n>t){
var e=n;n=t,t=e}if(r||n%1||t%1){var u=Ql();return Hl(n+u*(t-n+Qr("1e-"+((u+"").length-1))),t)}return tu(n,t)}function fa(n){return Qh(Ec(n).toLowerCase())}function ca(n){return n=Ec(n),n&&n.replace(Gt,ve).replace(Dr,"")}function aa(n,t,r){n=Ec(n),t=vu(t);var e=n.length;r=r===X?e:Mr(kc(r),0,e);var u=r;return r-=t.length,r>=0&&n.slice(r,u)==t}function la(n){return n=Ec(n),n&&At.test(n)?n.replace(xt,ge):n}function sa(n){return n=Ec(n),n&&Wt.test(n)?n.replace(St,"\\$&"):n}function ha(n,t,r){n=Ec(n),t=kc(t);
var e=t?V(n):0;if(!t||e>=t)return n;var u=(t-e)/2;return ri(Nl(u),r)+n+ri(Fl(u),r)}function pa(n,t,r){n=Ec(n),t=kc(t);var e=t?V(n):0;return t&&e<t?n+ri(t-e,r):n}function _a(n,t,r){n=Ec(n),t=kc(t);var e=t?V(n):0;return t&&e<t?ri(t-e,r)+n:n}function va(n,t,r){return r||null==t?t=0:t&&(t=+t),Yl(Ec(n).replace(Lt,""),t||0)}function ga(n,t,r){return t=(r?Ui(n,t,r):t===X)?1:kc(t),eu(Ec(n),t)}function ya(){var n=arguments,t=Ec(n[0]);return n.length<3?t:t.replace(n[1],n[2])}function da(n,t,r){return r&&"number"!=typeof r&&Ui(n,t,r)&&(t=r=X),
(r=r===X?Un:r>>>0)?(n=Ec(n),n&&("string"==typeof t||null!=t&&!Ah(t))&&(t=vu(t),!t&&T(n))?Ou(G(n),0,r):n.split(t,r)):[]}function ba(n,t,r){return n=Ec(n),r=null==r?0:Mr(kc(r),0,n.length),t=vu(t),n.slice(r,r+t.length)==t}function wa(n,t,r){var e=Z.templateSettings;r&&Ui(n,t,r)&&(t=X),n=Ec(n),t=Sh({},t,e,li);var u,i,o=Sh({},t.imports,e.imports,li),f=Pc(o),c=E(o,f),a=0,l=t.interpolate||Ht,s="__p += '",h=sl((t.escape||Ht).source+"|"+l.source+"|"+(l===It?Ft:Ht).source+"|"+(t.evaluate||Ht).source+"|$","g"),p="//# sourceURL="+(bl.call(t,"sourceURL")?(t.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++Zr+"]")+"\n";
n.replace(h,function(t,r,e,o,f,c){return e||(e=o),s+=n.slice(a,c).replace(Jt,U),r&&(u=!0,s+="' +\n__e("+r+") +\n'"),f&&(i=!0,s+="';\n"+f+";\n__p += '"),e&&(s+="' +\n((__t = ("+e+")) == null ? '' : __t) +\n'"),a=c+t.length,t}),s+="';\n";var _=bl.call(t,"variable")&&t.variable;if(_){if(Dt.test(_))throw new fl(un)}else s="with (obj) {\n"+s+"\n}\n";s=(i?s.replace(dt,""):s).replace(bt,"$1").replace(wt,"$1;"),s="function("+(_||"obj")+") {\n"+(_?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(u?", __e = _.escape":"")+(i?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+s+"return __p\n}";
var v=Xh(function(){return cl(f,p+"return "+s).apply(X,c)});if(v.source=s,rc(v))throw v;return v}function ma(n){return Ec(n).toLowerCase()}function xa(n){return Ec(n).toUpperCase()}function ja(n,t,r){if(n=Ec(n),n&&(r||t===X))return R(n);if(!n||!(t=vu(t)))return n;var e=G(n),u=G(t);return Ou(e,W(e,u),L(e,u)+1).join("")}function Aa(n,t,r){if(n=Ec(n),n&&(r||t===X))return n.slice(0,H(n)+1);if(!n||!(t=vu(t)))return n;var e=G(n);return Ou(e,0,L(e,G(t))+1).join("")}function ka(n,t,r){if(n=Ec(n),n&&(r||t===X))return n.replace(Lt,"");
if(!n||!(t=vu(t)))return n;var e=G(n);return Ou(e,W(e,G(t))).join("")}function Oa(n,t){var r=An,e=kn;if(fc(t)){var u="separator"in t?t.separator:u;r="length"in t?kc(t.length):r,e="omission"in t?vu(t.omission):e}n=Ec(n);var i=n.length;if(T(n)){var o=G(n);i=o.length}if(r>=i)return n;var f=r-V(e);if(f<1)return e;var c=o?Ou(o,0,f).join(""):n.slice(0,f);if(u===X)return c+e;if(o&&(f+=c.length-f),Ah(u)){if(n.slice(f).search(u)){var a,l=c;for(u.global||(u=sl(u.source,Ec(Nt.exec(u))+"g")),u.lastIndex=0;a=u.exec(l);)var s=a.index;
c=c.slice(0,s===X?f:s)}}else if(n.indexOf(vu(u),f)!=f){var h=c.lastIndexOf(u);h>-1&&(c=c.slice(0,h))}return c+e}function Ia(n){return n=Ec(n),n&&jt.test(n)?n.replace(mt,ye):n}function Ra(n,t,r){return n=Ec(n),t=r?X:t,t===X?$(n)?Q(n):_(n):n.match(t)||[]}function za(t){var r=null==t?0:t.length,e=mi();return t=r?c(t,function(n){if("function"!=typeof n[1])throw new pl(en);return[e(n[0]),n[1]]}):[],uu(function(e){for(var u=-1;++u<r;){var i=t[u];if(n(i[0],this,e))return n(i[1],this,e)}})}function Ea(n){
return Nr(Fr(n,an))}function Sa(n){return function(){return n}}function Wa(n,t){return null==n||n!==n?t:n}function La(n){return n}function Ca(n){return De("function"==typeof n?n:Fr(n,an))}function Ua(n){return qe(Fr(n,an))}function Ba(n,t){return Ze(n,Fr(t,an))}function Ta(n,t,e){var u=Pc(t),i=fe(t,u);null!=e||fc(t)&&(i.length||!u.length)||(e=t,t=n,n=this,i=fe(t,Pc(t)));var o=!(fc(e)&&"chain"in e&&!e.chain),f=uc(n);return r(i,function(r){var e=t[r];n[r]=e,f&&(n.prototype[r]=function(){var t=this.__chain__;
if(o||t){var r=n(this.__wrapped__);return(r.__actions__=Tu(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,a([this.value()],arguments))})}),n}function $a(){return re._===this&&(re._=Al),this}function Da(){}function Ma(n){return n=kc(n),uu(function(t){return Ge(t,n)})}function Fa(n){return Bi(n)?m(no(n)):Qe(n)}function Na(n){return function(t){return null==n?X:_e(n,t)}}function Pa(){return[]}function qa(){return!1}function Za(){return{}}function Ka(){return"";
}function Va(){return!0}function Ga(n,t){if(n=kc(n),n<1||n>Wn)return[];var r=Un,e=Hl(n,Un);t=mi(t),n-=Un;for(var u=O(e,t);++r<n;)t(r);return u}function Ha(n){return bh(n)?c(n,no):bc(n)?[n]:Tu(Cs(Ec(n)))}function Ja(n){var t=++wl;return Ec(n)+t}function Ya(n){return n&&n.length?Yr(n,La,me):X}function Qa(n,t){return n&&n.length?Yr(n,mi(t,2),me):X}function Xa(n){return w(n,La)}function nl(n,t){return w(n,mi(t,2))}function tl(n){return n&&n.length?Yr(n,La,Ne):X}function rl(n,t){return n&&n.length?Yr(n,mi(t,2),Ne):X;
}function el(n){return n&&n.length?k(n,La):0}function ul(n,t){return n&&n.length?k(n,mi(t,2)):0}x=null==x?re:be.defaults(re.Object(),x,be.pick(re,qr));var il=x.Array,ol=x.Date,fl=x.Error,cl=x.Function,al=x.Math,ll=x.Object,sl=x.RegExp,hl=x.String,pl=x.TypeError,_l=il.prototype,vl=cl.prototype,gl=ll.prototype,yl=x["__core-js_shared__"],dl=vl.toString,bl=gl.hasOwnProperty,wl=0,ml=function(){var n=/[^.]+$/.exec(yl&&yl.keys&&yl.keys.IE_PROTO||"");return n?"Symbol(src)_1."+n:""}(),xl=gl.toString,jl=dl.call(ll),Al=re._,kl=sl("^"+dl.call(bl).replace(St,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Ol=ie?x.Buffer:X,Il=x.Symbol,Rl=x.Uint8Array,zl=Ol?Ol.allocUnsafe:X,El=F(ll.getPrototypeOf,ll),Sl=ll.create,Wl=gl.propertyIsEnumerable,Ll=_l.splice,Cl=Il?Il.isConcatSpreadable:X,Ul=Il?Il.iterator:X,Bl=Il?Il.toStringTag:X,Tl=function(){
try{var n=Ai(ll,"defineProperty");return n({},"",{}),n}catch(n){}}(),$l=x.clearTimeout!==re.clearTimeout&&x.clearTimeout,Dl=ol&&ol.now!==re.Date.now&&ol.now,Ml=x.setTimeout!==re.setTimeout&&x.setTimeout,Fl=al.ceil,Nl=al.floor,Pl=ll.getOwnPropertySymbols,ql=Ol?Ol.isBuffer:X,Zl=x.isFinite,Kl=_l.join,Vl=F(ll.keys,ll),Gl=al.max,Hl=al.min,Jl=ol.now,Yl=x.parseInt,Ql=al.random,Xl=_l.reverse,ns=Ai(x,"DataView"),ts=Ai(x,"Map"),rs=Ai(x,"Promise"),es=Ai(x,"Set"),us=Ai(x,"WeakMap"),is=Ai(ll,"create"),os=us&&new us,fs={},cs=to(ns),as=to(ts),ls=to(rs),ss=to(es),hs=to(us),ps=Il?Il.prototype:X,_s=ps?ps.valueOf:X,vs=ps?ps.toString:X,gs=function(){
function n(){}return function(t){if(!fc(t))return{};if(Sl)return Sl(t);n.prototype=t;var r=new n;return n.prototype=X,r}}();Z.templateSettings={escape:kt,evaluate:Ot,interpolate:It,variable:"",imports:{_:Z}},Z.prototype=J.prototype,Z.prototype.constructor=Z,Y.prototype=gs(J.prototype),Y.prototype.constructor=Y,Ct.prototype=gs(J.prototype),Ct.prototype.constructor=Ct,Xt.prototype.clear=nr,Xt.prototype.delete=tr,Xt.prototype.get=rr,Xt.prototype.has=er,Xt.prototype.set=ur,ir.prototype.clear=or,ir.prototype.delete=fr,
ir.prototype.get=cr,ir.prototype.has=ar,ir.prototype.set=lr,sr.prototype.clear=hr,sr.prototype.delete=pr,sr.prototype.get=_r,sr.prototype.has=vr,sr.prototype.set=gr,yr.prototype.add=yr.prototype.push=dr,yr.prototype.has=br,wr.prototype.clear=mr,wr.prototype.delete=xr,wr.prototype.get=jr,wr.prototype.has=Ar,wr.prototype.set=kr;var ys=Pu(ue),ds=Pu(oe,!0),bs=qu(),ws=qu(!0),ms=os?function(n,t){return os.set(n,t),n}:La,xs=Tl?function(n,t){return Tl(n,"toString",{configurable:!0,enumerable:!1,value:Sa(t),
writable:!0})}:La,js=uu,As=$l||function(n){return re.clearTimeout(n)},ks=es&&1/P(new es([,-0]))[1]==Sn?function(n){return new es(n)}:Da,Os=os?function(n){return os.get(n)}:Da,Is=Pl?function(n){return null==n?[]:(n=ll(n),i(Pl(n),function(t){return Wl.call(n,t)}))}:Pa,Rs=Pl?function(n){for(var t=[];n;)a(t,Is(n)),n=El(n);return t}:Pa,zs=we;(ns&&zs(new ns(new ArrayBuffer(1)))!=ct||ts&&zs(new ts)!=Gn||rs&&zs(rs.resolve())!=Qn||es&&zs(new es)!=tt||us&&zs(new us)!=it)&&(zs=function(n){var t=we(n),r=t==Yn?n.constructor:X,e=r?to(r):"";
if(e)switch(e){case cs:return ct;case as:return Gn;case ls:return Qn;case ss:return tt;case hs:return it}return t});var Es=yl?uc:qa,Ss=Qi(ms),Ws=Ml||function(n,t){return re.setTimeout(n,t)},Ls=Qi(xs),Cs=Pi(function(n){var t=[];return 46===n.charCodeAt(0)&&t.push(""),n.replace(Et,function(n,r,e,u){t.push(e?u.replace(Mt,"$1"):r||n)}),t}),Us=uu(function(n,t){return Jf(n)?Hr(n,ee(t,1,Jf,!0)):[]}),Bs=uu(function(n,t){var r=jo(t);return Jf(r)&&(r=X),Jf(n)?Hr(n,ee(t,1,Jf,!0),mi(r,2)):[]}),Ts=uu(function(n,t){
var r=jo(t);return Jf(r)&&(r=X),Jf(n)?Hr(n,ee(t,1,Jf,!0),X,r):[]}),$s=uu(function(n){var t=c(n,ju);return t.length&&t[0]===n[0]?ke(t):[]}),Ds=uu(function(n){var t=jo(n),r=c(n,ju);return t===jo(r)?t=X:r.pop(),r.length&&r[0]===n[0]?ke(r,mi(t,2)):[]}),Ms=uu(function(n){var t=jo(n),r=c(n,ju);return t="function"==typeof t?t:X,t&&r.pop(),r.length&&r[0]===n[0]?ke(r,X,t):[]}),Fs=uu(Oo),Ns=gi(function(n,t){var r=null==n?0:n.length,e=Tr(n,t);return nu(n,c(t,function(n){return Ci(n,r)?+n:n}).sort(Lu)),e}),Ps=uu(function(n){
return gu(ee(n,1,Jf,!0))}),qs=uu(function(n){var t=jo(n);return Jf(t)&&(t=X),gu(ee(n,1,Jf,!0),mi(t,2))}),Zs=uu(function(n){var t=jo(n);return t="function"==typeof t?t:X,gu(ee(n,1,Jf,!0),X,t)}),Ks=uu(function(n,t){return Jf(n)?Hr(n,t):[]}),Vs=uu(function(n){return mu(i(n,Jf))}),Gs=uu(function(n){var t=jo(n);return Jf(t)&&(t=X),mu(i(n,Jf),mi(t,2))}),Hs=uu(function(n){var t=jo(n);return t="function"==typeof t?t:X,mu(i(n,Jf),X,t)}),Js=uu(Go),Ys=uu(function(n){var t=n.length,r=t>1?n[t-1]:X;return r="function"==typeof r?(n.pop(),
r):X,Ho(n,r)}),Qs=gi(function(n){var t=n.length,r=t?n[0]:0,e=this.__wrapped__,u=function(t){return Tr(t,n)};return!(t>1||this.__actions__.length)&&e instanceof Ct&&Ci(r)?(e=e.slice(r,+r+(t?1:0)),e.__actions__.push({func:nf,args:[u],thisArg:X}),new Y(e,this.__chain__).thru(function(n){return t&&!n.length&&n.push(X),n})):this.thru(u)}),Xs=Fu(function(n,t,r){bl.call(n,r)?++n[r]:Br(n,r,1)}),nh=Ju(ho),th=Ju(po),rh=Fu(function(n,t,r){bl.call(n,r)?n[r].push(t):Br(n,r,[t])}),eh=uu(function(t,r,e){var u=-1,i="function"==typeof r,o=Hf(t)?il(t.length):[];
return ys(t,function(t){o[++u]=i?n(r,t,e):Ie(t,r,e)}),o}),uh=Fu(function(n,t,r){Br(n,r,t)}),ih=Fu(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),oh=uu(function(n,t){if(null==n)return[];var r=t.length;return r>1&&Ui(n,t[0],t[1])?t=[]:r>2&&Ui(t[0],t[1],t[2])&&(t=[t[0]]),He(n,ee(t,1),[])}),fh=Dl||function(){return re.Date.now()},ch=uu(function(n,t,r){var e=_n;if(r.length){var u=N(r,wi(ch));e|=bn}return ai(n,e,t,r,u)}),ah=uu(function(n,t,r){var e=_n|vn;if(r.length){var u=N(r,wi(ah));e|=bn;
}return ai(t,e,n,r,u)}),lh=uu(function(n,t){return Gr(n,1,t)}),sh=uu(function(n,t,r){return Gr(n,Ic(t)||0,r)});Cf.Cache=sr;var hh=js(function(t,r){r=1==r.length&&bh(r[0])?c(r[0],z(mi())):c(ee(r,1),z(mi()));var e=r.length;return uu(function(u){for(var i=-1,o=Hl(u.length,e);++i<o;)u[i]=r[i].call(this,u[i]);return n(t,this,u)})}),ph=uu(function(n,t){return ai(n,bn,X,t,N(t,wi(ph)))}),_h=uu(function(n,t){return ai(n,wn,X,t,N(t,wi(_h)))}),vh=gi(function(n,t){return ai(n,xn,X,X,X,t)}),gh=ii(me),yh=ii(function(n,t){
return n>=t}),dh=Re(function(){return arguments}())?Re:function(n){return cc(n)&&bl.call(n,"callee")&&!Wl.call(n,"callee")},bh=il.isArray,wh=ce?z(ce):ze,mh=ql||qa,xh=ae?z(ae):Ee,jh=le?z(le):Le,Ah=se?z(se):Be,kh=he?z(he):Te,Oh=pe?z(pe):$e,Ih=ii(Ne),Rh=ii(function(n,t){return n<=t}),zh=Nu(function(n,t){if(Mi(t)||Hf(t))return $u(t,Pc(t),n),X;for(var r in t)bl.call(t,r)&&Sr(n,r,t[r])}),Eh=Nu(function(n,t){$u(t,qc(t),n)}),Sh=Nu(function(n,t,r,e){$u(t,qc(t),n,e)}),Wh=Nu(function(n,t,r,e){$u(t,Pc(t),n,e);
}),Lh=gi(Tr),Ch=uu(function(n,t){n=ll(n);var r=-1,e=t.length,u=e>2?t[2]:X;for(u&&Ui(t[0],t[1],u)&&(e=1);++r<e;)for(var i=t[r],o=qc(i),f=-1,c=o.length;++f<c;){var a=o[f],l=n[a];(l===X||Gf(l,gl[a])&&!bl.call(n,a))&&(n[a]=i[a])}return n}),Uh=uu(function(t){return t.push(X,si),n(Mh,X,t)}),Bh=Xu(function(n,t,r){null!=t&&"function"!=typeof t.toString&&(t=xl.call(t)),n[t]=r},Sa(La)),Th=Xu(function(n,t,r){null!=t&&"function"!=typeof t.toString&&(t=xl.call(t)),bl.call(n,t)?n[t].push(r):n[t]=[r]},mi),$h=uu(Ie),Dh=Nu(function(n,t,r){
Ke(n,t,r)}),Mh=Nu(function(n,t,r,e){Ke(n,t,r,e)}),Fh=gi(function(n,t){var r={};if(null==n)return r;var e=!1;t=c(t,function(t){return t=ku(t,n),e||(e=t.length>1),t}),$u(n,di(n),r),e&&(r=Fr(r,an|ln|sn,hi));for(var u=t.length;u--;)yu(r,t[u]);return r}),Nh=gi(function(n,t){return null==n?{}:Je(n,t)}),Ph=ci(Pc),qh=ci(qc),Zh=Vu(function(n,t,r){return t=t.toLowerCase(),n+(r?fa(t):t)}),Kh=Vu(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Vh=Vu(function(n,t,r){return n+(r?" ":"")+t.toLowerCase()}),Gh=Ku("toLowerCase"),Hh=Vu(function(n,t,r){
return n+(r?"_":"")+t.toLowerCase()}),Jh=Vu(function(n,t,r){return n+(r?" ":"")+Qh(t)}),Yh=Vu(function(n,t,r){return n+(r?" ":"")+t.toUpperCase()}),Qh=Ku("toUpperCase"),Xh=uu(function(t,r){try{return n(t,X,r)}catch(n){return rc(n)?n:new fl(n)}}),np=gi(function(n,t){return r(t,function(t){t=no(t),Br(n,t,ch(n[t],n))}),n}),tp=Yu(),rp=Yu(!0),ep=uu(function(n,t){return function(r){return Ie(r,n,t)}}),up=uu(function(n,t){return function(r){return Ie(n,r,t)}}),ip=ti(c),op=ti(u),fp=ti(h),cp=ui(),ap=ui(!0),lp=ni(function(n,t){
return n+t},0),sp=fi("ceil"),hp=ni(function(n,t){return n/t},1),pp=fi("floor"),_p=ni(function(n,t){return n*t},1),vp=fi("round"),gp=ni(function(n,t){return n-t},0);return Z.after=If,Z.ary=Rf,Z.assign=zh,Z.assignIn=Eh,Z.assignInWith=Sh,Z.assignWith=Wh,Z.at=Lh,Z.before=zf,Z.bind=ch,Z.bindAll=np,Z.bindKey=ah,Z.castArray=Nf,Z.chain=Qo,Z.chunk=uo,Z.compact=io,Z.concat=oo,Z.cond=za,Z.conforms=Ea,Z.constant=Sa,Z.countBy=Xs,Z.create=Sc,Z.curry=Ef,Z.curryRight=Sf,Z.debounce=Wf,Z.defaults=Ch,Z.defaultsDeep=Uh,
Z.defer=lh,Z.delay=sh,Z.difference=Us,Z.differenceBy=Bs,Z.differenceWith=Ts,Z.drop=fo,Z.dropRight=co,Z.dropRightWhile=ao,Z.dropWhile=lo,Z.fill=so,Z.filter=lf,Z.flatMap=sf,Z.flatMapDeep=hf,Z.flatMapDepth=pf,Z.flatten=_o,Z.flattenDeep=vo,Z.flattenDepth=go,Z.flip=Lf,Z.flow=tp,Z.flowRight=rp,Z.fromPairs=yo,Z.functions=$c,Z.functionsIn=Dc,Z.groupBy=rh,Z.initial=mo,Z.intersection=$s,Z.intersectionBy=Ds,Z.intersectionWith=Ms,Z.invert=Bh,Z.invertBy=Th,Z.invokeMap=eh,Z.iteratee=Ca,Z.keyBy=uh,Z.keys=Pc,Z.keysIn=qc,
Z.map=yf,Z.mapKeys=Zc,Z.mapValues=Kc,Z.matches=Ua,Z.matchesProperty=Ba,Z.memoize=Cf,Z.merge=Dh,Z.mergeWith=Mh,Z.method=ep,Z.methodOf=up,Z.mixin=Ta,Z.negate=Uf,Z.nthArg=Ma,Z.omit=Fh,Z.omitBy=Vc,Z.once=Bf,Z.orderBy=df,Z.over=ip,Z.overArgs=hh,Z.overEvery=op,Z.overSome=fp,Z.partial=ph,Z.partialRight=_h,Z.partition=ih,Z.pick=Nh,Z.pickBy=Gc,Z.property=Fa,Z.propertyOf=Na,Z.pull=Fs,Z.pullAll=Oo,Z.pullAllBy=Io,Z.pullAllWith=Ro,Z.pullAt=Ns,Z.range=cp,Z.rangeRight=ap,Z.rearg=vh,Z.reject=mf,Z.remove=zo,Z.rest=Tf,
Z.reverse=Eo,Z.sampleSize=jf,Z.set=Jc,Z.setWith=Yc,Z.shuffle=Af,Z.slice=So,Z.sortBy=oh,Z.sortedUniq=$o,Z.sortedUniqBy=Do,Z.split=da,Z.spread=$f,Z.tail=Mo,Z.take=Fo,Z.takeRight=No,Z.takeRightWhile=Po,Z.takeWhile=qo,Z.tap=Xo,Z.throttle=Df,Z.thru=nf,Z.toArray=jc,Z.toPairs=Ph,Z.toPairsIn=qh,Z.toPath=Ha,Z.toPlainObject=Rc,Z.transform=Qc,Z.unary=Mf,Z.union=Ps,Z.unionBy=qs,Z.unionWith=Zs,Z.uniq=Zo,Z.uniqBy=Ko,Z.uniqWith=Vo,Z.unset=Xc,Z.unzip=Go,Z.unzipWith=Ho,Z.update=na,Z.updateWith=ta,Z.values=ra,Z.valuesIn=ea,
Z.without=Ks,Z.words=Ra,Z.wrap=Ff,Z.xor=Vs,Z.xorBy=Gs,Z.xorWith=Hs,Z.zip=Js,Z.zipObject=Jo,Z.zipObjectDeep=Yo,Z.zipWith=Ys,Z.entries=Ph,Z.entriesIn=qh,Z.extend=Eh,Z.extendWith=Sh,Ta(Z,Z),Z.add=lp,Z.attempt=Xh,Z.camelCase=Zh,Z.capitalize=fa,Z.ceil=sp,Z.clamp=ua,Z.clone=Pf,Z.cloneDeep=Zf,Z.cloneDeepWith=Kf,Z.cloneWith=qf,Z.conformsTo=Vf,Z.deburr=ca,Z.defaultTo=Wa,Z.divide=hp,Z.endsWith=aa,Z.eq=Gf,Z.escape=la,Z.escapeRegExp=sa,Z.every=af,Z.find=nh,Z.findIndex=ho,Z.findKey=Wc,Z.findLast=th,Z.findLastIndex=po,
Z.findLastKey=Lc,Z.floor=pp,Z.forEach=_f,Z.forEachRight=vf,Z.forIn=Cc,Z.forInRight=Uc,Z.forOwn=Bc,Z.forOwnRight=Tc,Z.get=Mc,Z.gt=gh,Z.gte=yh,Z.has=Fc,Z.hasIn=Nc,Z.head=bo,Z.identity=La,Z.includes=gf,Z.indexOf=wo,Z.inRange=ia,Z.invoke=$h,Z.isArguments=dh,Z.isArray=bh,Z.isArrayBuffer=wh,Z.isArrayLike=Hf,Z.isArrayLikeObject=Jf,Z.isBoolean=Yf,Z.isBuffer=mh,Z.isDate=xh,Z.isElement=Qf,Z.isEmpty=Xf,Z.isEqual=nc,Z.isEqualWith=tc,Z.isError=rc,Z.isFinite=ec,Z.isFunction=uc,Z.isInteger=ic,Z.isLength=oc,Z.isMap=jh,
Z.isMatch=ac,Z.isMatchWith=lc,Z.isNaN=sc,Z.isNative=hc,Z.isNil=_c,Z.isNull=pc,Z.isNumber=vc,Z.isObject=fc,Z.isObjectLike=cc,Z.isPlainObject=gc,Z.isRegExp=Ah,Z.isSafeInteger=yc,Z.isSet=kh,Z.isString=dc,Z.isSymbol=bc,Z.isTypedArray=Oh,Z.isUndefined=wc,Z.isWeakMap=mc,Z.isWeakSet=xc,Z.join=xo,Z.kebabCase=Kh,Z.last=jo,Z.lastIndexOf=Ao,Z.lowerCase=Vh,Z.lowerFirst=Gh,Z.lt=Ih,Z.lte=Rh,Z.max=Ya,Z.maxBy=Qa,Z.mean=Xa,Z.meanBy=nl,Z.min=tl,Z.minBy=rl,Z.stubArray=Pa,Z.stubFalse=qa,Z.stubObject=Za,Z.stubString=Ka,
Z.stubTrue=Va,Z.multiply=_p,Z.nth=ko,Z.noConflict=$a,Z.noop=Da,Z.now=fh,Z.pad=ha,Z.padEnd=pa,Z.padStart=_a,Z.parseInt=va,Z.random=oa,Z.reduce=bf,Z.reduceRight=wf,Z.repeat=ga,Z.replace=ya,Z.result=Hc,Z.round=vp,Z.runInContext=p,Z.sample=xf,Z.size=kf,Z.snakeCase=Hh,Z.some=Of,Z.sortedIndex=Wo,Z.sortedIndexBy=Lo,Z.sortedIndexOf=Co,Z.sortedLastIndex=Uo,Z.sortedLastIndexBy=Bo,Z.sortedLastIndexOf=To,Z.startCase=Jh,Z.startsWith=ba,Z.subtract=gp,Z.sum=el,Z.sumBy=ul,Z.template=wa,Z.times=Ga,Z.toFinite=Ac,Z.toInteger=kc,
Z.toLength=Oc,Z.toLower=ma,Z.toNumber=Ic,Z.toSafeInteger=zc,Z.toString=Ec,Z.toUpper=xa,Z.trim=ja,Z.trimEnd=Aa,Z.trimStart=ka,Z.truncate=Oa,Z.unescape=Ia,Z.uniqueId=Ja,Z.upperCase=Yh,Z.upperFirst=Qh,Z.each=_f,Z.eachRight=vf,Z.first=bo,Ta(Z,function(){var n={};return ue(Z,function(t,r){bl.call(Z.prototype,r)||(n[r]=t)}),n}(),{chain:!1}),Z.VERSION=nn,r(["bind","bindKey","curry","curryRight","partial","partialRight"],function(n){Z[n].placeholder=Z}),r(["drop","take"],function(n,t){Ct.prototype[n]=function(r){
r=r===X?1:Gl(kc(r),0);var e=this.__filtered__&&!t?new Ct(this):this.clone();return e.__filtered__?e.__takeCount__=Hl(r,e.__takeCount__):e.__views__.push({size:Hl(r,Un),type:n+(e.__dir__<0?"Right":"")}),e},Ct.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),r(["filter","map","takeWhile"],function(n,t){var r=t+1,e=r==Rn||r==En;Ct.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:mi(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),r(["head","last"],function(n,t){
var r="take"+(t?"Right":"");Ct.prototype[n]=function(){return this[r](1).value()[0]}}),r(["initial","tail"],function(n,t){var r="drop"+(t?"":"Right");Ct.prototype[n]=function(){return this.__filtered__?new Ct(this):this[r](1)}}),Ct.prototype.compact=function(){return this.filter(La)},Ct.prototype.find=function(n){return this.filter(n).head()},Ct.prototype.findLast=function(n){return this.reverse().find(n)},Ct.prototype.invokeMap=uu(function(n,t){return"function"==typeof n?new Ct(this):this.map(function(r){
return Ie(r,n,t)})}),Ct.prototype.reject=function(n){return this.filter(Uf(mi(n)))},Ct.prototype.slice=function(n,t){n=kc(n);var r=this;return r.__filtered__&&(n>0||t<0)?new Ct(r):(n<0?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==X&&(t=kc(t),r=t<0?r.dropRight(-t):r.take(t-n)),r)},Ct.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},Ct.prototype.toArray=function(){return this.take(Un)},ue(Ct.prototype,function(n,t){var r=/^(?:filter|find|map|reject)|While$/.test(t),e=/^(?:head|last)$/.test(t),u=Z[e?"take"+("last"==t?"Right":""):t],i=e||/^find/.test(t);
u&&(Z.prototype[t]=function(){var t=this.__wrapped__,o=e?[1]:arguments,f=t instanceof Ct,c=o[0],l=f||bh(t),s=function(n){var t=u.apply(Z,a([n],o));return e&&h?t[0]:t};l&&r&&"function"==typeof c&&1!=c.length&&(f=l=!1);var h=this.__chain__,p=!!this.__actions__.length,_=i&&!h,v=f&&!p;if(!i&&l){t=v?t:new Ct(this);var g=n.apply(t,o);return g.__actions__.push({func:nf,args:[s],thisArg:X}),new Y(g,h)}return _&&v?n.apply(this,o):(g=this.thru(s),_?e?g.value()[0]:g.value():g)})}),r(["pop","push","shift","sort","splice","unshift"],function(n){
var t=_l[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);Z.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(bh(u)?u:[],n)}return this[r](function(r){return t.apply(bh(r)?r:[],n)})}}),ue(Ct.prototype,function(n,t){var r=Z[t];if(r){var e=r.name+"";bl.call(fs,e)||(fs[e]=[]),fs[e].push({name:t,func:r})}}),fs[Qu(X,vn).name]=[{name:"wrapper",func:X}],Ct.prototype.clone=$t,Ct.prototype.reverse=Yt,Ct.prototype.value=Qt,Z.prototype.at=Qs,
Z.prototype.chain=tf,Z.prototype.commit=rf,Z.prototype.next=ef,Z.prototype.plant=of,Z.prototype.reverse=ff,Z.prototype.toJSON=Z.prototype.valueOf=Z.prototype.value=cf,Z.prototype.first=Z.prototype.head,Ul&&(Z.prototype[Ul]=uf),Z},be=de();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(re._=be,define(function(){return be})):ue?((ue.exports=be)._=be,ee._=be):re._=be}).call(this);

View File

@ -1,428 +0,0 @@
class PlanisphereWidget {
constructor(id, options) {
console.log("PlanisphereWidget " + id);
this.options = options;
this.container = document.getElementById(id);
this.bg_color = (options && options.bg_color) ? options.bg_color : '#27293D';
this.fore_color = (options && options.bg_color) ? options.bg_color : '#CCCCCC';
this.coor_color = (options && options.coor_color) ? options.coor_color : '#1B1B24';
this.border_color = (options && options.border_color) ? options.border_color : '#CCCCCC';
this.wave_color = (options && options.wave_color) ? options.wave_color : '#1D8CF8';
this.margin_left = (options && options.margin_left) ? options.margin_left : 0;
this.margin_top = (options && options.margin_top) ? options.margin_top : 0;
this.stage = new Konva.Stage({
container: id,
width: this.container.clientWidth || 500,
height: this.container.clientHeight || 500
});
this.layer_coor = new Konva.Layer();
this.layer_coor.hitGraphEnabled(false);
this.layer_wave = new Konva.Layer();
this.layer_wave.hitGraphEnabled(false);
this.stage.add(this.layer_coor);
this.stage.add(this.layer_wave);
this.canvas = document.createElement("canvas");
this.ctx_canvas = this.canvas.getContext("2d");
this.ctx_canvas.fillStyle = this.bg_color;
this.init();
this.initResize();
this.dAlpha = false;
this.d1 = null;
this.d2 = null;
this.d3 = null;
this.d4 = null;
}
init() {
if (this.container.clientHeight === 0) return;
this.stage.setWidth(this.container.clientWidth);
this.stage.setHeight(this.container.clientWidth);
this.area_x = this.margin_left;
this.area_y = this.margin_top;
this.area_width = this.stage.getWidth() - this.margin_left;
this.area_height = this.stage.getHeight() - this.margin_top;
this.drawCoor();
this.canvas.height = this.area_height;
this.canvas.width = this.area_width;
this.ctx_canvas.strokeStyle = this.wave_color;
this.ctx_canvas.lineWidth = 1;
this.pointer = new Konva.Circle({
x: 0,
y: 0,
radius: 1,
stroke: this.wave_color,
strokeWidth: 1
});
this.pointer.cache();
}
drawCoor() {
var layer = this.layer_coor;
layer.destroyChildren();
var len = this.area_width;
var rval = this.area_width / 2;
layer.add(new Konva.Rect({
x: this.area_x,
y: this.area_y,
width: this.area_width,
height: this.area_height,
stroke: this.border_color,
strokeWidth: 1
}));
layer.add(new Konva.Line({
points: [0, 0, this.area_width, this.area_height],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [this.area_width, 0, 0, this.area_height],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval, this.area_width, rval],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval / 2, this.area_width, rval / 2],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval / 2 * 3, this.area_width, rval / 2 * 3],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
///
layer.add(new Konva.Line({
points: [rval, 0, rval, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [rval / 2, 0, rval / 2, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [rval / 2 * 3, 0, rval / 2 * 3, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Circle({
x: rval,
y: rval,
radius: rval,
stroke: this.border_color,
strokeWidth: 1
}));
layer.add(new Konva.Circle({
x: rval,
y: rval,
radius: rval / 2,
stroke: this.border_color,
strokeWidth: 1
}));
layer.batchDraw();
}
setViewMode(vm) {
if (vm == 3) {//0=星座图3=余晖图。2021.03.31李伟添加;
this.dAlpha = true;
vm = 0;
} else {
this.dAlpha = false;
}
this.viewmodel = vm;
this.clear();
}
//星座图
feed(d, di, dq) {
if (!this.viewmodel || this.viewmodel == 0) {
this.drawWave_vector(d);
// this.drawWave_planis(d);
return;
}
}
//矢量图
feedVector(d) {
if (!this.viewmodel || this.viewmodel == 0) {
this.drawWave_vector(d);
return;
}
}
//眼图
feedIQ(di, dq) {
if (this.viewmodel == 1 && di != null) {
this.drawIQ(di);
}
else if (this.viewmodel == 2 && dq != null) {
this.drawIQ(dq);
}
}
drawIQ(IQBuff) {
this.clear();
for (var j = 0; j < IQBuff.length; j++) {
this.drawWave_eye(IQBuff[j]);
}
}
clear() {
this.imgData = null;
this.drawWave_planis([]);
this.drawWave_vector([]);
}
drawWave_eye(d) {
// console.log("in drawWave_eye" + d.length);
var layer = this.layer_wave;
var width = this.area_width;
var height = this.area_height;
var rwidth = width / 2;
var rHeight = height / 2;
var ctx_canvas = layer.getContext();
ctx_canvas.strokeStyle = this.wave_color;
ctx_canvas.lineWidth = 1;
ctx_canvas.beginPath();
for (var n = 0; n < d.length; n++) {
//var x = Math.floor(width*(1-1/1.4)/2+ n * width / d.length / 1.4);
var x = Math.floor( n * width / d.length );
var y = Math.floor(rHeight + d[n] * rHeight / 1.4);
if (n == 0) {
ctx_canvas.moveTo(x, y);
}
else {
ctx_canvas.lineTo(x, y);
}
}
ctx_canvas.stroke();
}
drawWave_planis(d) {
//console.log("in plan" + d.length);
var layer = this.layer_wave;
var width = this.getDrawWHByPixelRatio(this.area_width) ;
var height = this.getDrawWHByPixelRatio(this.area_height);
var rwidth = width / 2;
var rHeight = height / 2;
//不显示的时候不报错
if (!width || !height||width == 0 || height == 0) {
return;
}
var context = layer.getContext();
context.clearRect(0, 0, width, height);
var imgData = context.createImageData(width, height);
var dlen = imgData.data.length;
//2021.03.31 李伟增加余晖效果
if (this.dAlpha && d) {
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d4, 32);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d3, 32);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d2, 64);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d1, 128);
this.d4 = this.d3;
this.d3 = this.d2;
this.d2 = this.d1;
this.d1 = d;
}
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, 255);
/*
for (var n = 0; n < d.length; n++) {
//console.log(rwidth+d[n][0] * rwidth);
var x = Math.floor(rwidth + d[n][0] * rwidth/1.4);
var y = Math.floor(rHeight+ d[n][1] * rHeight/1.4);
var p = ( y * width + x )* 4;
this.setWaveColor(imgData, p, dlen,width*4);
}
*/
context.putImageData(imgData, 0, 0);
}
drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, a) {
if (!d)
return;
for (var n = 0; n < d.length; n++) {
//console.log(rwidth+d[n][0] * rwidth);
var x = Math.floor(rwidth + d[n][0] * rwidth / 1.4);
var y = Math.floor(rHeight + d[n][1] * rHeight / 1.4);
var p = (y * width + x) * 4;
this.setWaveColor(imgData, p, dlen, width * 4, a);
}
}
drawWave_vector(d) {
var maxLine = 100;
var dlen = Math.min(maxLine, d.length);
var layer = this.layer_wave;
var width = this.area_width;
var height = this.area_height;
var rwidth = width / 2;
var rHeight = height / 2;
//不显示的时候不报错
if (!width || !height || width == 0 || height == 0) {
return;
}
var context = layer.getContext();
context.clearRect(0, 0, width, height);
var ctx_canvas = layer.getContext();
ctx_canvas.strokeStyle = this.wave_color;
ctx_canvas.lineWidth = 1;
ctx_canvas.beginPath();
for (var n = 0; n < dlen; n++) {
var x = Math.floor(rwidth + d[n][0] * rwidth / 1.2);
var y = Math.floor(rHeight + d[n][1] * rHeight / 1.2);
if (n == 0) {
ctx_canvas.moveTo(x, y);
}
else {
ctx_canvas.lineTo(x, y);
}
}
ctx_canvas.stroke();
}
/*像素单位转css单位,该函数返回当前浏览器缩放比例所影响的 x y width height 的绘图位置
仅适用putImageData getImageData等函数*/
getDrawWHByPixelRatio(xory) {
if (xory == 0) return 0;
if (!xory) return xory;
var ret = Math.ceil(window.devicePixelRatio * xory);
//console.log(`xory=${xory} ratio=${window.devicePixelRatio} ret=${ret}`);
return ret;
}
setWaveColor_thin(imgData, p, dlen, rowlen) {
var dp = p + rowlen;
if (p < 0) return;
if (dp + 11 >= dlen) return;
imgData.data[p] = 33;
imgData.data[p + 1] = 129;
imgData.data[p + 2] = 247;
imgData.data[p + 3] = 255;
}
setWaveColor(imgData, p, dlen, rowlen, a = 255) {//2021.03.31 李伟增加余晖效果 a=alpha
var dp = p + rowlen;
if (p < 0) return;
if (dp + 11 >= dlen) return;
var r = 33;
var g = 129;
var b = 247;
if (a != 255) {
r = 0;
g = 255;
b = 0;
}
//console.log(a);
imgData.data[p] = r;
imgData.data[p + 1] = g;
imgData.data[p + 2] = b;
imgData.data[p + 3] = a;//255;//2021.03.31 李伟增加余晖效果
imgData.data[p + 4] = r;
imgData.data[p + 5] = g;
imgData.data[p + 6] = b;
imgData.data[p + 7] = a;//255;
imgData.data[p + 8] = r;
imgData.data[p + 9] = g;
imgData.data[p + 10] = b;
imgData.data[p + 11] = a;//255;
imgData.data[dp] = r;
imgData.data[dp + 1] = g;
imgData.data[dp + 2] = b;
imgData.data[dp + 3] = a;//255;
imgData.data[dp + 4] = r;
imgData.data[dp + 5] = g;
imgData.data[dp + 6] = b;
imgData.data[dp + 7] = a;//255;
imgData.data[dp + 8] = r;
imgData.data[dp + 9] = g;
imgData.data[dp + 10] = b;
imgData.data[dp + 11] = a;//255;
}
destroy() {
this.imgData = null;
this.stage.destroy();
this.color_map = null;
}
dispose() {
console.log("dispose wfmin");
this.destroy();
}
initResize() {
var _self = this;
$(this.container).resize(function () {
var target = this;
if (target.resizeFlag) {
clearTimeout(target.resizeFlag);
}
target.resizeFlag = setTimeout(function () {
_self.init();
target.resizeFlag = null;
}, 100);
});
}
}

View File

@ -1,198 +0,0 @@
# PlanisphereWidget 类文档
## 概述
`PlanisphereWidget` 是一个基于 JavaScript 的类,用于在网页中创建和展示星座图、矢量图和眼图等可视化内容。该类依赖于 Konva.js 库,通过 canvas 绘制图形,提供了一种灵活的方式来呈现复杂的波形数据和坐标系图形。它适用于需要动态展示信号处理结果或科学数据的场景。
Konva.js 是一个强大的 2D 绘图库,`PlanisphereWidget` 利用其舞台Stage和层Layer机制来管理绘图元素确保图形的高效渲染和交互性。
## 构造函数
```javascript
constructor(id, options)
```
- **参数**
- `id` (String): 指定容器的 ID`PlanisphereWidget` 将在这个容器内创建 Konva 舞台并绘制图形。
- `options` (Object): 可选的配置对象,用于自定义绘图样式和布局。
- `bg_color` (String): 背景颜色,默认值为 '#27293D'。
- `fore_color` (String): 前景颜色,默认值为 '#CCCCCC'。
- `coor_color` (String): 坐标系颜色,默认值为 '#1B1B24'。
- `border_color` (String): 边框颜色,默认值为 '#CCCCCC'。
- `wave_color` (String): 波形颜色,默认值为 '#1D8CF8'。
- `margin_left` (Number): 左边距,默认值为 0。
- `margin_top` (Number): 上边距,默认值为 0。
- **功能**:初始化 `PlanisphereWidget` 实例,设置容器、颜色配置和 Konva 舞台。构造函数会创建两个 Konva 层(`layer_coor` 用于坐标系,`layer_wave` 用于波形图),并调用 `init()``initResize()` 方法进行进一步的初始化。
## 属性
- `container`: 绘图所在的 DOM 元素,通过构造函数中的 `id` 参数获取。
- `stage`: Konva 舞台对象,用于管理绘图层和渲染。
- `layer_coor`: Konva 层对象,用于绘制坐标系,不启用命中检测(`hitGraphEnabled(false)`)。
- `layer_wave`: Konva 层对象,用于绘制波形图,不启用命中检测。
- `bg_color`: 背景颜色,用于填充绘图区域。
- `fore_color`: 前景颜色,目前与 `bg_color` 绑定,实际用途待定。
- `coor_color`: 坐标系颜色,目前未直接使用。
- `border_color`: 边框和坐标线颜色。
- `wave_color`: 波形和指针颜色。
- `margin_left`: 左边距,定义绘图区域的左边界。
- `margin_top`: 上边距,定义绘图区域的上边界。
- `area_x`: 绘图区域的 X 坐标,等于 `margin_left`
- `area_y`: 绘图区域的 Y 坐标,等于 `margin_top`
- `area_width`: 绘图区域的宽度,等于舞台宽度减去 `margin_left`
- `area_height`: 绘图区域的高度,等于舞台高度减去 `margin_top`
- `canvas`: 用于离屏绘制的临时 canvas 元素。
- `ctx_canvas`: 临时 canvas 的 2D 上下文,用于绘制波形数据。
- `pointer`: Konva 圆形对象,用于表示波形中的指针。
- `viewmodel`: 当前视图模式0 表示星座图1 和 2 表示眼图的不同模式。
- `dAlpha`: 布尔值,是否启用余晖效果。
- `d1`, `d2`, `d3`, `d4`: 用于存储历史数据以实现余晖效果。
## 方法
### `init()`
- **功能**:初始化舞台和绘图区域。如果容器高度为 0则不执行初始化。设置舞台的宽度和高度计算绘图区域的尺寸并调用 `drawCoor()` 绘制坐标系。初始化离屏 canvas 和指针对象。
- **参数**:无
- **返回值**:无
### `drawCoor()`
- **功能**:绘制坐标系,包括边框、网格线和圆形参考线。坐标系包括水平和垂直的虚线网格、对角线以及两个同心圆,中心位于绘图区域的中心。
- **参数**:无
- **返回值**:无
### `setViewMode(vm)`
- **功能**:设置视图模式,决定绘制星座图还是眼图。如果 `vm` 为 3则启用余晖效果并将模式设为 0星座图。调用 `clear()` 方法清除当前绘图内容。
- **参数**
- `vm` (Number): 视图模式0 表示星座图1 和 2 表示眼图3 表示带余晖效果的星座图。
- **返回值**:无
### `feed(d, di, dq)`
- **功能**:处理星座图数据。如果当前视图模式为 0星座图则调用 `drawWave_vector(d)` 绘制矢量图。
- **参数**
- `d` (Array): 星座图数据。
- `di` (Array): 眼图 I 通道数据,未使用。
- `dq` (Array): 眼图 Q 通道数据,未使用。
- **返回值**:无
### `feedVector(d)`
- **功能**:处理矢量图数据。如果当前视图模式为 0则调用 `drawWave_vector(d)` 绘制矢量图。
- **参数**
- `d` (Array): 矢量图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `feedIQ(di, dq)`
- **功能**:处理眼图数据。如果视图模式为 1则绘制 `di` 数据;如果为 2则绘制 `dq` 数据。调用 `drawIQ()` 方法进行实际绘制。
- **参数**
- `di` (Array): 眼图 I 通道数据。
- `dq` (Array): 眼图 Q 通道数据。
- **返回值**:无
### `drawIQ(IQBuff)`
- **功能**:绘制眼图数据。清除当前绘图内容,然后遍历 `IQBuff` 数组,调用 `drawWave_eye()` 绘制每个数据段。
- **参数**
- `IQBuff` (Array): 眼图数据数组,每个元素是一段波形数据。
- **返回值**:无
### `clear()`
- **功能**:清除当前绘图内容,重置图像数据,并调用 `drawWave_planis([])``drawWave_vector([])` 清空星座图和矢量图。
- **参数**:无
- **返回值**:无
### `drawWave_eye(d)`
- **功能**:绘制眼图波形。使用 Konva 层的上下文绘制折线图,横轴为数据索引,纵轴为数据值,缩放至绘图区域的一半高度。
- **参数**
- `d` (Array): 波形数据数组,每个元素是一个数值。
- **返回值**:无
### `drawWave_planis(d)`
- **功能**:绘制星座图。清除当前绘图内容,创建图像数据对象。如果启用余晖效果(`dAlpha` 为 true则使用历史数据`d1``d4`)绘制不同透明度的点,实现余晖效果。最后绘制当前数据 `d`
- **参数**
- `d` (Array): 星座图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `drawWave_vector(d)`
- **功能**:绘制矢量图。清除当前绘图内容,使用 Konva 层的上下文绘制折线图,连接数据点,最多处理前 100 个点以提高性能。
- **参数**
- `d` (Array): 矢量图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, a)`
- **功能**:辅助方法,用于绘制星座图中的点,支持透明度设置。计算每个数据点的坐标位置,并设置图像数据中的颜色和透明度。
- **参数**
- `rwidth` (Number): 绘图区域宽度的一半。
- `rHeight` (Number): 绘图区域高度的一半。
- `width` (Number): 图像数据的宽度。
- `imgData` (ImageData): 图像数据对象。
- `dlen` (Number): 图像数据的长度。
- `d` (Array): 星座图数据,二维数组。
- `a` (Number): 透明度值,范围 0-255。
- **返回值**:无
### `getDrawWHByPixelRatio(xory)`
- **功能**:根据设备像素比计算绘图尺寸,用于处理高分辨率显示器上的绘图精度问题。
- **参数**
- `xory` (Number): 输入的宽度或高度值。
- **返回值**Number - 调整后的尺寸值。
### `setWaveColor(imgData, p, dlen, rowlen, a = 255)`
- **功能**:设置图像数据中指定位置的颜色和透明度,用于绘制星座图中的点。如果透明度不是 255则使用绿色否则使用蓝色。
- **参数**
- `imgData` (ImageData): 图像数据对象。
- `p` (Number): 像素位置索引。
- `dlen` (Number): 图像数据长度。
- `rowlen` (Number): 每行的字节数。
- `a` (Number): 透明度值,默认 255。
- **返回值**:无
### `destroy()`
- **功能**:销毁实例,清除图像数据并销毁 Konva 舞台。
- **参数**:无
- **返回值**:无
### `dispose()`
- **功能**:释放资源,调用 `destroy()` 方法。
- **参数**:无
- **返回值**:无
### `initResize()`
- **功能**:初始化窗口大小调整事件监听。使用 jQuery 的 `resize` 事件监听容器大小变化,延迟 100 毫秒后调用 `init()` 方法重新初始化绘图区域。
- **参数**:无
- **返回值**:无
## 使用示例
以下是一个简单的代码示例,展示如何创建 `PlanisphereWidget` 实例并绘制星座图:
```javascript
// HTML 中需要有一个容器元素
<div id="planisphereContainer" style="width: 500px; height: 500px;"></div>
// JavaScript 代码
const widget = new PlanisphereWidget('planisphereContainer', {
bg_color: '#27293D',
wave_color: '#1D8CF8',
border_color: '#CCCCCC',
margin_left: 10,
margin_top: 10
});
// 设置视图模式为星座图
widget.setViewMode(0);
// 提供星座图数据,格式为二维数组 [[x1, y1], [x2, y2], ...]
const data = [
[0.5, 0.5],
[-0.5, 0.5],
[-0.5, -0.5],
[0.5, -0.5]
];
widget.feed(data);
// 启用余晖效果
widget.setViewMode(3);
widget.feed(data);
```
## 注意事项
- 确保容器元素(通过 `id` 指定)在 DOM 中存在,并且具有明确的宽度和高度,否则初始化可能会失败。
- 数据格式必须符合方法的要求,例如 `feed()``feedVector()` 需要二维数组,`feedIQ()` 需要一维数组。
- 余晖效果会保存历史数据,可能会增加内存使用量,长时间使用后建议调用 `clear()` 清除历史数据。
- 绘图性能可能受数据量影响,`drawWave_vector()` 方法限制了最多绘制 100 个点以保证性能。
- 该类依赖于 Konva.js 和 jQuery 库,确保在页面中引入这些库。

File diff suppressed because it is too large Load Diff

View File

@ -1,192 +0,0 @@
# WaterfallWidget.js 文档
本文档为 `waterfallwidget.js` 文件提供详细说明,涵盖了 `waterfallwidget` 类的功能、用法和配置选项。该类用于创建频谱图和瀑布图可视化工具,广泛应用于信号处理和频谱分析领域。
## 概述
`waterfallwidget` 是一个功能强大的 JavaScript 类,用于在网页中绘制频谱图和瀑布图。它支持实时数据更新、频率缩放、用户交互、信号标注等多种功能,适用于无线电信号分析、频谱监控等场景。该类基于 Konva.js 库实现绘图功能,并提供了丰富的配置选项和事件处理机制。
## 初始化
要使用 `waterfallwidget`,需要通过构造函数创建一个实例,并传入容器 ID 和配置选项。以下是构造函数的定义:
```javascript
function waterfallwidget(id, options)
```
- **id**:字符串,指定容器的 DOM ID用于渲染瀑布图。
- **options**:对象,包含各种配置选项,用于自定义瀑布图的行为和外观。
### 配置选项
以下是常用的配置选项及其默认值:
- **min_db**:最小分贝值,默认为 -200。
- **max_db**:最大分贝值,默认为 0。
- **min_freq**:最小频率值,默认为 0。
- **max_freq**:最大频率值,默认为 40000。
- **showSpectrogramAvg**:是否显示频谱平均值,默认为 false。
- **showSpectrogramMin**:是否显示频谱最小值,默认为 false。
- **showSpectrogramMax**:是否显示频谱最大值,默认为 false。
- **showPeakMarker**:是否显示峰值标记,默认为 false。
- **allowRunMode**运行模式0 表示侦察模式1 表示实时模式,默认为 0。
- **disableZoom**:是否禁用缩放功能,默认为 false。
- **wfFreqPointLable**:是否启用频点标注,默认为 false。
- **bg_color**:背景颜色,默认为 '#37384B'。
- **fore_color**:前景颜色,默认为 '#CCCCFF'。
- **wave_color**:波形颜色,默认为 '#4BF3A7'。
更多配置选项请参见代码中的构造函数部分。
### 初始化示例
```javascript
var widget = new waterfallwidget('container', {
min_db: -150,
max_db: 0,
min_freq: 50000000,
max_freq: 100000000,
showPeakMarker: true,
allowRunMode: 1
});
widget.init();
```
## 主要方法
`waterfallwidget` 类提供了大量方法,用于数据处理、绘图和用户交互。以下是一些关键方法:
### addData(d, time)
添加新数据并更新瀑布图和频谱图。
- **d**:数组,包含信号强度数据。
- **time**:字符串,可选,数据的时间戳。
### drawWaterFall(bins)
绘制瀑布图的一行数据。
- **bins**:数组,包含信号强度数据。
### setFreqZoom(start, end)
设置频率缩放范围。
- **start**:数字,起始频率。
- **end**:数字,结束频率。
### changeDBRange(min_db, max_db, force)
更改分贝范围。
- **min_db**:数字,最小分贝值。
- **max_db**:数字,最大分贝值。
- **force**:布尔值,是否强制更新,默认为 false。
### drawAllSpectrogram(bins)
绘制完整的频谱图,包括峰值标记、最大/最小保持等。
- **bins**:数组,包含信号强度数据。
### setAdjCenter(freq, fireEvent)
设置调整中心频率。
- **freq**:数字,中心频率。
- **fireEvent**:布尔值,是否触发事件,默认为 false。
更多方法请参见代码中的详细实现。
## 属性
`waterfallwidget` 类包含大量属性,用于控制瀑布图的状态和外观。以下是一些重要属性:
- **min_db**:最小分贝值。
- **max_db**:最大分贝值。
- **freq_start**:当前显示的起始频率。
- **freq_end**:当前显示的结束频率。
- **showSpectrogramAvg**:是否显示频谱平均值。
- **showPeakMarker**:是否显示峰值标记。
- **adj_start**:调整起始频率。
- **adj_end**:调整结束频率。
- **bg_color**:背景颜色。
- **wave_color**:波形颜色。
## 事件处理
`waterfallwidget` 支持多种事件,允许开发者响应用户交互和状态变化。以下是一些常用事件:
- **zoom-change**:频率缩放变化时触发。
- **pointer-change**:指针位置变化时触发。
- **adj-change**:调整范围变化时触发。
- **label-change**:标签选择变化时触发。
- **context-click**:右键菜单点击时触发。
### 事件绑定示例
```javascript
widget.on('zoom-change', function(event) {
console.log('Zoom changed to:', event.start, event.end);
});
```
## 用户交互
`waterfallwidget` 支持丰富的用户交互功能,包括:
- **频率缩放**:通过鼠标滚轮或拖动选择区域进行缩放。
- **调整范围**:在实时模式下,可以拖动调整频率范围。
- **峰值标记**:显示信号峰值并标注频率和强度。
- **右键菜单**:提供上下文菜单,支持多种操作如侦察、数据采集等。
- **频点标注**:允许用户添加和编辑频率点标注。
## 绘图逻辑
`waterfallwidget` 将绘图区域分为频谱图和瀑布图两部分:
- **频谱图**:显示当前信号的强度分布,支持平均值、最大值、最小值等多种显示模式。
- **瀑布图**:随时间滚动显示历史信号数据,使用颜色映射表示信号强度。
绘图使用 Konva.js 库实现,通过多层 canvas 叠加实现复杂的视觉效果。
## 使用示例
以下是一个完整的示例,展示如何初始化和使用 `waterfallwidget`
```javascript
// 创建瀑布图实例
var widget = new waterfallwidget('waterfall-container', {
min_db: -150,
max_db: 0,
min_freq: 50000000,
max_freq: 100000000,
showPeakMarker: true,
showSpectrogramMax: true,
allowRunMode: 0
});
// 初始化
widget.init();
// 添加数据
setInterval(function() {
var data = Array.from({length: 2000}, () => Math.random() * (-50 + 150) - 150);
widget.addData(data, new Date().toLocaleTimeString());
}, 1000);
// 监听缩放事件
widget.on('zoom-change', function(event) {
console.log('Frequency range:', event.start, 'to', event.end);
});
```
## 注意事项
- **性能考虑**:对于大数据量和高频率更新,建议优化数据处理逻辑,避免频繁重绘。
- **浏览器兼容性**:依赖 Konva.js 库,确保在支持 HTML5 Canvas 的现代浏览器中使用。
- **配置选项**:初始化时需仔细配置频率和分贝范围,以适应具体的信号分析需求。
- **事件处理**:事件监听器应妥善管理,避免内存泄漏。
以上是 `waterfallwidget.js` 的完整文档,希望对您的使用和开发有所帮助。

View File

@ -1 +0,0 @@
- [卫星星座图绘制原理](https://juejin.cn/post/6844904142448640007)

View File

@ -1,16 +0,0 @@
```
给我说一下 语谱图spectrogram是什么
卫星领域有什么用处?
```
在卫星领域语谱图spectrogram也发挥着重要的作用主要应用于以下方面
- **信号分析与识别:**
- **信号干扰检测:** 通过观察语谱图可以快速发现是否存在对卫星信号的干扰例如人为的干扰jamming或意外的频率冲突。干扰信号通常在语谱图上会显示出异常的能量分布或特定的频率模式。
- **信号欺骗检测 (Spoofing):** 在卫星导航领域,语谱图可以帮助识别欺骗信号。欺骗信号可能会伪造卫星的定位信息,语谱图可以显示出与真实信号不同的特征。
- **雷达应用:**
- **空间目标监视 (Space Domain Awareness):** 雷达卫星或地面雷达站利用语谱图分析反射回来的信号,可以识别和跟踪空间中的物体,如废弃的卫星或碎片。通过分析信号的多普勒频移等信息,可以估计目标的速度和距离。
- 研究报告提到通过观察RHESSI卫星的观测数据生成的语谱图可以进行空间目标监视的微多普勒特征分析。
- **科学研究:**
- **粒子数据分析:** 在某些卫星任务中,例如分析太空中的粒子 flux流量可以将粒子能量随时间和空间例如 McIlwain L 参数)的变化绘制成二维彩色图,这种图也被称为语谱图。虽然这里的语谱图侧重于能量分布而非频率,但可视化的原理是相似的。

View File

@ -1,85 +0,0 @@
<!--
Plotly 需要用 iframe 嵌套在页面中的原因:
最下面那个代码,在项目里渲染不正常。
但是新建一个 vue3 项目,直接在 App.vue 里那样子写,却能正常渲染。
-->
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Plotly 图表的 HTML 页面</title>
<!-- <script src="https://cdn.plot.ly/plotly-3.0.1.js"></script> -->
<script src="./plotly-3.0.1.js"></script>
<!-- https://github.com/plotly/plotly.js/blob/master/dist/README.md#to-include-localization -->
<script src="./plotly-locale-zh-cn.js"></script>
</head>
<body>
<script>
Plotly.setPlotConfig({ locale: 'zh-CN' });
</script>
<div id="PLOTLY_CHART_spectrogram" style="width: 100%; height: 100%"></div>
<div id="PLOTLY_CHART_waterfall" style="width: 100%; height: 100%"></div>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
</html>
<!--
<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue'
import Plotly from 'plotly.js-dist-min'
const plotRef = useTemplateRef<import('plotly.js-dist-min').PlotlyHTMLElement | null>('plotRef')
// 数据
const trace1 = {
x: [1, 2, 3, 4, 5],
y: [2, 4, 1, 5, 3],
type: 'scatter',
} satisfies import('plotly.js-dist-min').Data
const data = [trace1]
// 布局
const layout = {
title: '简单折线图',
xaxis: {
title: 'X 轴',
},
yaxis: {
title: 'Y 轴',
},
} satisfies Partial<import('plotly.js-dist-min').Layout>
onMounted(async () => {
// 绘制图表
Plotly.newPlot(plotRef.value!, data, layout)
plotRef.value?.on('plotly_relayout', (eventData) => {
console.debug(`eventData :>> `, eventData)
})
})
</script>
<template>
<div ref="plotRef" style="width: 600px; height: 400px"></div>
</template>
-->

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
var locale={moduleType:"locale",name:"zh-CN",dictionary:{Autoscale:"\u81ea\u52a8\u7f29\u653e","Box Select":"\u77e9\u5f62\u6846\u9009","Click to enter Colorscale title":"\u70b9\u51fb\u8f93\u5165\u8272\u9636\u7684\u6807\u9898","Click to enter Component A title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6A\u7684\u6807\u9898","Click to enter Component B title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6B\u7684\u6807\u9898","Click to enter Component C title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6C\u7684\u6807\u9898","Click to enter Plot title":"\u70b9\u51fb\u8f93\u5165\u56fe\u8868\u7684\u6807\u9898","Click to enter X axis title":"\u70b9\u51fb\u8f93\u5165X\u8f74\u7684\u6807\u9898","Click to enter Y axis title":"\u70b9\u51fb\u8f93\u5165Y\u8f74\u7684\u6807\u9898","Compare data on hover":"\u60ac\u505c\u65f6\u6bd4\u8f83\u6570\u636e","Double-click on legend to isolate one trace":"\u53cc\u51fb\u56fe\u4f8b\u6765\u7a81\u663e\u5bf9\u5e94\u8f68\u8ff9","Double-click to zoom back out":"\u53cc\u51fb\u8fd4\u56de\u7f29\u5c0f\u663e\u793a","Download plot as a png":"\u4e0b\u8f7d\u56fe\u8868\u4e3aPNG\u683c\u5f0f","Download plot":"\u4e0b\u8f7d\u56fe\u8868","Edit in Chart Studio":"\u5728Chart Studio\u4e2d\u7f16\u8f91","IE only supports svg. Changing format to svg.":"IE\u53ea\u652f\u6301SVG\u3002\u8f6c\u6362\u683c\u5f0f\u4e3aSVG\u3002","Lasso Select":"\u5957\u7d22\u9009\u62e9","Orbital rotation":"\u8f68\u9053\u65cb\u8f6c",Pan:"\u5e73\u79fb","Produced with Plotly.js":"\u7531Plotly.js\u751f\u6210",Reset:"\u91cd\u7f6e","Reset axes":"\u91cd\u7f6e\u8f74","Reset camera to default":"\u91cd\u7f6e\u955c\u5934\u89c6\u89d2\u4e3a\u9ed8\u8ba4\u72b6\u6001","Reset camera to last save":"\u91cd\u7f6e\u955c\u5934\u89c6\u89d2\u4e3a\u4e0a\u6b21\u4fdd\u5b58\u72b6\u6001","Reset view":"\u91cd\u7f6e\u89c6\u56fe","Reset views":"\u91cd\u7f6e\u89c6\u56fe","Show closest data on hover":"\u60ac\u505c\u65f6\u663e\u793a\u6700\u8fd1\u7684\u6570\u636e","Snapshot succeeded":"\u751f\u6210\u5feb\u7167\u6210\u529f","Sorry, there was a problem downloading your snapshot!":"\u62b1\u6b49\uff0c\u4e0b\u8f7d\u5feb\u7167\u51fa\u73b0\u95ee\u9898\uff01","Taking snapshot - this may take a few seconds":"\u6b63\u5728\u751f\u6210\u5feb\u7167 - \u53ef\u80fd\u9700\u8981\u51e0\u79d2\u949f",Zoom:"\u7f29\u653e","Zoom in":"\u653e\u5927","Zoom out":"\u7f29\u5c0f","close:":"\u5173\u95ed:",trace:"\u8e2a\u8ff9:","lat:":"\u7eac\u5ea6:","lon:":"\u7ecf\u5ea6:","q1:":"\u7b2c\u4e00\u56db\u5206\u4f4d\u6570:","q3:":"\u7b2c\u4e09\u56db\u5206\u4f4d\u6570:","source:":"\u6e90:","target:":"\u76ee\u6807:","lower fence:":"\u5185\u4fa7\u680f(lower fence):","upper fence:":"\u5916\u4fa7\u680f(upper fence):","max:":"\u6700\u5927\u503c:","mean \xb1 \u03c3:":"\u5e73\u5747\u6570 \xb1 \u6807\u51c6\u5dee\u03c3:","mean:":"\u5e73\u5747\u6570:","median:":"\u4e2d\u4f4d\u6570:","min:":"\u6700\u5c0f\u503c:","Turntable rotation":"\u65cb\u8f6c\u8f6c\u76d8:","Toggle Spike Lines":"\u5207\u6362\u663e\u793a\u6570\u636e\u70b9\u8f85\u52a9\u7ebf(Spike Lines)","open:":"\u6253\u5f00:","high:":"\u9ad8:","low:":"\u4f4e:","Toggle show closest data on hover":"\u5207\u6362\u60ac\u505c\u65f6\u663e\u793a\u6700\u8fd1\u7684\u6570\u636e\u70b9","incoming flow count:":"\u6d41\u5165\u6570\u91cf:","outgoing flow count:":"\u6d41\u51fa\u6570\u91cf:","kde:":"kde:","Click to enter radial axis title":"\u70b9\u51fb\u8f93\u5165\u5f84\u5411\u8f74\u6807\u9898","new text":"\u65b0\u5efa\u6587\u672c"},format:{days:["\u661f\u671f\u65e5","\u661f\u671f\u4e00","\u661f\u671f\u4e8c","\u661f\u671f\u4e09","\u661f\u671f\u56db","\u661f\u671f\u4e94","\u661f\u671f\u516d"],shortDays:["\u5468\u65e5","\u5468\u4e00","\u5468\u4e8c","\u5468\u4e09","\u5468\u56db","\u5468\u4e94","\u5468\u516d"],months:["\u4e00\u6708","\u4e8c\u6708","\u4e09\u6708","\u56db\u6708","\u4e94\u6708","\u516d\u6708","\u4e03\u6708","\u516b\u6708","\u4e5d\u6708","\u5341\u6708","\u5341\u4e00\u6708","\u5341\u4e8c\u6708"],shortMonths:["\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u5341\u4e00","\u5341\u4e8c"],date:"%Y-%m-%d"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale);

View File

@ -1,39 +1,11 @@
<script setup lang="ts">
import type { ThemeConfig } from 'ant-design-vue/es/config-provider/context';
import { theme } from 'ant-design-vue';
import { useLayout } from './layouts/sakai-vue/composables/layout';
const { isDarkTheme } = useLayout();
const themeConfig = computed(() => {
return {
algorithm: isDarkTheme.value ? theme.darkAlgorithm : theme.defaultAlgorithm,
} satisfies ThemeConfig;
});
const VITE_BUILD_COMMIT = import.meta.env.VITE_BUILD_COMMIT;
</script>
<template>
<a-config-provider :theme="themeConfig">
<div fixed rounded-br-4 bottom-0 left-0 z-9999 px-4 py-2 bg-black text-white op-75>
commit: {{ VITE_BUILD_COMMIT }}
</div>
<div>$__DEV__: {{ $__DEV__ }}</div>
<RouterView />
</a-config-provider>
<DynamicDialog /> <ConfirmDialog /> <Toast />
</template>
<style>
.layout-wrapper {
isolation: isolate;
}
@supports (height: 100dvh) {
.h-screen {
height: 100dvh;
}
}
@supports (width: 100dvw) {
.w-screen {
width: 100dvw;
}
}
</style>

84
src/assets/base.css Normal file
View File

@ -0,0 +1,84 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
:root {
--h-base-font: lxgw-wenkai-mono-tc, alibaba-puhuiti, Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
}
:root:root {
--van-base-font: var(--h-base-font);
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family: var(--h-base-font);
font-weight: 400;
font-style: normal;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

24
src/assets/main.css Normal file
View File

@ -0,0 +1,24 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
button:not(.van-button),
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
button:not(.van-button):hover,
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}

View File

@ -2,31 +2,31 @@
// https://icon-sets.iconify.design
</script>
<script setup lang="ts">
import SomeIcon from '~icons/svg/pacman';
</script>
<template>
<div b="1px solid pink" mt-2 text-4 p-2 space-y-2>
<div b="1px solid pink">
<div b="1px solid pink" mt-8 text-16 p-8 space-y-8>
<!-- <div b="1px solid pink">
<div>@iconify-json/carbon/icons.json</div>
<div i-carbon-face-cool text-orange />
</div>
</div> -->
<div b="1px solid pink">
<div>Icons({ autoInstall: true })</div>
<icon-carbon-face-cool class="text-yellow" w-8 h-8 />
<icon-carbon-face-cool class="text-yellow" w-32 h-32 />
</div>
<div b="1px solid pink">
<!-- <div b="1px solid pink">
<div>pacman.svg</div>
<div class="icon:pacman text-(pink)" />
</div>
</div> -->
<div b="1px solid pink">
<div>pacman.svg</div>
<icon-svg:pacman text-blue w-12 h-12 />
<some-icon text-cyan w-12 h-12 />
<icon-svg-pacman text-blue w-48 h-48 />
<some-icon text-cyan w-48 h-48 />
</div>
</div>
</template>
<script setup lang="ts">
import SomeIcon from '~icons/svg/pacman';
</script>

File diff suppressed because it is too large Load Diff

View File

@ -1,316 +0,0 @@
<template>
<div
ref="bubbleParentContainer"
class="relative h-72 w-full overflow-hidden"
>
<div ref="bubbleCanvasContainer"></div>
<div
:style="{
'--bubbles-blur': `${blur}px`,
}"
class="absolute inset-0 z-[2] size-full backdrop-blur-[--bubbles-blur]"
>
<slot />
</div>
</div>
</template>
<script setup lang="ts">
import {
ShaderMaterial,
SphereGeometry,
Vector3,
Color,
MathUtils,
Mesh,
Clock,
WebGLRenderer,
Scene,
PerspectiveCamera,
} from "three";
import { ref, onMounted, onBeforeUnmount } from "vue";
defineProps({
blur: {
type: Number,
default: 0,
},
});
const bubbleParentContainer = ref<HTMLElement | null>(null);
const bubbleCanvasContainer = ref<HTMLElement | null>(null);
let renderer: WebGLRenderer;
let scene: Scene;
let camera: PerspectiveCamera;
let clock: Clock;
const spheres: Mesh[] = [];
const BG_COLOR_BOTTOM_BLUISH = rgb(170, 215, 217);
const BG_COLOR_TOP_BLUISH = rgb(57, 167, 255);
const BG_COLOR_BOTTOM_ORANGISH = rgb(255, 160, 75);
const BG_COLOR_TOP_ORANGISH = rgb(239, 172, 53);
const SPHERE_COLOR_BOTTOM_BLUISH = rgb(120, 235, 124);
const SPHERE_COLOR_TOP_BLUISH = rgb(0, 167, 255);
const SPHERE_COLOR_BOTTOM_ORANGISH = rgb(235, 170, 0);
const SPHERE_COLOR_TOP_ORANGISH = rgb(255, 120, 0);
const SPHERE_COUNT = 250;
const SPHERE_SCALE_COEFF = 3;
const ORBIT_MIN = SPHERE_SCALE_COEFF + 2;
const ORBIT_MAX = ORBIT_MIN + 10;
const RAND_SEED = 898211544;
const rand = seededRandom(RAND_SEED);
const { PI, cos, sin } = Math;
const PI2 = PI * 2;
const sizes = new Array(SPHERE_COUNT).fill(0).map(() => randRange(1) * Math.pow(randRange(), 3));
const orbitRadii = new Array(SPHERE_COUNT)
.fill(0)
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const positions: [number, number, number][] = orbitRadii.map((rad, i) => [
rad * cos(thetas[i]) * sin(phis[i]),
rad * sin(thetas[i]) * sin(phis[i]),
rad * cos(phis[i]),
]);
const sphereGeometry = new SphereGeometry(SPHERE_SCALE_COEFF);
const sphereMaterial = getGradientMaterial(
SPHERE_COLOR_BOTTOM_BLUISH,
SPHERE_COLOR_TOP_BLUISH,
SPHERE_COLOR_BOTTOM_ORANGISH,
SPHERE_COLOR_TOP_ORANGISH,
);
const bgGeometry = new SphereGeometry();
bgGeometry.scale(-1, 1, 1);
const bgMaterial = getGradientMaterial(
BG_COLOR_BOTTOM_BLUISH,
BG_COLOR_TOP_BLUISH,
BG_COLOR_BOTTOM_ORANGISH,
BG_COLOR_TOP_ORANGISH,
);
bgMaterial.uniforms.uTemperatureVariancePeriod.value = new Vector3(0, 0, 0.1);
function seededRandom(a: number) {
return function () {
a |= 0;
a = (a + 0x9e3779b9) | 0;
var t = a ^ (a >>> 16);
t = Math.imul(t, 0x21f0aaad);
t = t ^ (t >>> 15);
t = Math.imul(t, 0x735a2d97);
return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;
};
}
function randRange(n = 1) {
return rand() * n;
}
function rgb(r: number, g: number, b: number) {
return new Color(r / 255, g / 255, b / 255);
}
function getGradientMaterial(
colorBottomWarm: Color,
colorTopWarm: Color,
colorBottomCool: Color,
colorTopCool: Color,
) {
return new ShaderMaterial({
uniforms: {
colorBottomWarm: {
value: new Color().copy(colorBottomWarm),
},
colorTopWarm: {
value: new Color().copy(colorTopWarm),
},
colorBottomCool: {
value: new Color().copy(colorBottomCool),
},
colorTopCool: {
value: new Color().copy(colorTopCool),
},
uTemperature: {
value: 0.0,
},
uTemperatureVariancePeriod: {
value: new Vector3(0.08, 0.1, 0.2),
},
uElapsedTime: {
value: 0,
},
},
vertexShader: `
uniform vec4 uTemperatureVariancePeriod;
uniform float uTemperature;
uniform float uElapsedTime;
varying float topBottomMix;
varying float warmCoolMix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
topBottomMix = normal.y;
warmCoolMix = 0.6 * uTemperature +
0.4 * (sin(
(uElapsedTime + gl_Position.x) * uTemperatureVariancePeriod.x +
(uElapsedTime + gl_Position.y) * uTemperatureVariancePeriod.y +
(uElapsedTime + gl_Position.z) * uTemperatureVariancePeriod.z) * 0.5 + 0.5);
}
`,
fragmentShader: `
uniform vec3 colorBottomWarm;
uniform vec3 colorTopWarm;
uniform vec3 colorBottomCool;
uniform vec3 colorTopCool;
varying float topBottomMix;
varying float warmCoolMix;
void main() {
gl_FragColor = vec4(mix(
mix(colorTopCool, colorTopWarm, warmCoolMix),
mix(colorBottomCool, colorBottomWarm, warmCoolMix),
topBottomMix), 1.0);
}
`,
});
}
function createScene() {
const width = bubbleCanvasContainer.value?.clientWidth || 1;
const height = bubbleCanvasContainer.value?.clientHeight || 1;
// Set up the scene, camera, and renderer
scene = new Scene();
camera = new PerspectiveCamera(50, width / height, 1, 2000);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 23;
renderer = new WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(BG_COLOR_BOTTOM_BLUISH);
// Add these properties to allow overlap
sphereMaterial.depthWrite = false;
sphereMaterial.depthTest = true; // Keep this true for depth sorting
if (bubbleCanvasContainer.value) {
bubbleCanvasContainer.value.appendChild(renderer.domElement);
}
// Create the background mesh
const bgMesh = new Mesh(bgGeometry, bgMaterial);
// Position the background far behind everything
bgMesh.position.set(0, 0, -1); // Move the background far back
// Disable depth testing for the background to ensure it's always behind other objects
bgMesh.material.depthTest = false;
bgMesh.renderOrder = -1; // Ensure the background is rendered first
// Calculate the scale to ensure the background covers the full canvas
const distance = camera.position.z; // Distance from the camera
const aspect = camera.aspect;
const frustumHeight = 2 * distance * Math.tan(MathUtils.degToRad(camera.fov) / 2);
const frustumWidth = frustumHeight * aspect;
// Scale the background geometry to match the camera's frustum size
bgMesh.scale.set(
frustumWidth / bgGeometry.parameters.radius,
frustumHeight / bgGeometry.parameters.radius,
1,
);
scene.add(bgMesh); // Add the backgrou
// Create sphere meshes
const orbitRadii = new Array(SPHERE_COUNT)
.fill(0)
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const positions = orbitRadii.map((rad, i) => [
rad * cos(thetas[i]) * sin(phis[i]),
rad * sin(thetas[i]) * sin(phis[i]),
rad * cos(phis[i]),
]);
for (let i = 0; i < SPHERE_COUNT; i++) {
const sphere = new Mesh(sphereGeometry, sphereMaterial);
const [x, y, z] = positions[i];
const scaleVector = sizes[i];
sphere.scale.set(scaleVector, scaleVector, scaleVector);
sphere.position.set(x, y, z);
spheres.push(sphere);
scene.add(sphere);
}
clock = new Clock();
}
function animate() {
requestAnimationFrame(animate);
const elapsed = clock.getElapsedTime();
const temperature = sin(elapsed * 0.5) * 0.5 + 0.5;
bgMaterial.uniforms.uTemperature.value = temperature;
bgMaterial.uniforms.uElapsedTime.value = elapsed;
sphereMaterial.uniforms.uTemperature.value = temperature;
sphereMaterial.uniforms.uElapsedTime.value = elapsed;
// Floating effect for spheres
spheres.forEach((sphere, index) => {
const basePosition = positions[index];
const floatFactor = 2; // Adjust this value to control float intensity
const speed = 0.3; // Adjust this value to control float speed
const floatY = sin(elapsed * speed + index) * floatFactor;
sphere.position.y = basePosition[1] + floatY;
});
renderer.render(scene, camera);
}
function updateRendererSize() {
const width = bubbleParentContainer.value?.clientWidth || 1;
const height = bubbleParentContainer.value?.clientHeight || 1;
// Update renderer size and aspect ratio
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
// Recalculate background mesh scale
const distance = camera.position.z;
const frustumHeight = 2 * distance * Math.tan(MathUtils.degToRad(camera.fov) / 2);
const frustumWidth = frustumHeight * camera.aspect;
// Get the background mesh and update its scale
const bgMesh = scene.children.find(
(obj) => obj instanceof Mesh && obj.geometry === bgGeometry,
) as Mesh;
if (bgMesh) {
bgMesh.scale.set(
frustumWidth / bgGeometry.parameters.radius,
frustumHeight / bgGeometry.parameters.radius,
1,
);
}
}
onMounted(() => {
createScene();
updateRendererSize();
window.addEventListener("resize", updateRendererSize);
animate();
});
onBeforeUnmount(() => {
window.removeEventListener("resize", updateRendererSize); // Cleanup on component unmount
});
</script>

View File

@ -1 +0,0 @@
export { default as BubblesBg } from "./BubblesBg.vue";

View File

@ -1,166 +0,0 @@
<template>
<canvas
ref="starsCanvas"
:class="cn('absolute inset-0 w-full h-full', $props.class)"
></canvas>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { onMounted, ref } from "vue";
interface Star {
x: number;
y: number;
z: number;
speed: number;
}
const props = withDefaults(
defineProps<{
color?: string;
count?: number;
class?: string;
}>(),
{
color: "#FFF",
count: 200,
},
);
const starsCanvas = ref<HTMLCanvasElement | null>(null);
let perspective: number = 0;
let stars: Star[] = [];
let ctx: CanvasRenderingContext2D | null = null;
onMounted(() => {
const canvas = starsCanvas.value;
if (!canvas) return;
window.addEventListener("resize", resizeCanvas);
resizeCanvas(); // Call it initially to set correct size
perspective = canvas.width / 2;
stars = [];
// Initialize stars
for (let i = 0; i < props.count; i++) {
stars.push({
x: (Math.random() - 0.5) * 2 * canvas.width,
y: (Math.random() - 0.5) * 2 * canvas.height,
z: Math.random() * canvas.width,
speed: Math.random() * 5 + 2, // Speed for falling effect
});
}
animate(); // Start animation
});
function hexToRgb() {
let hex = props.color.replace(/^#/, "");
// If the hex code is 3 characters, expand it to 6 characters
if (hex.length === 3) {
hex = hex
.split("")
.map((char) => char + char)
.join("");
}
// Parse the r, g, b values from the hex string
const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255; // Extract the red component
const g = (bigint >> 8) & 255; // Extract the green component
const b = bigint & 255; // Extract the blue component
// Return the RGB values as a string separated by spaces
return {
r,
g,
b,
};
}
// Function to draw a star with a sharp line and blurred trail
function drawStar(star: Star) {
const canvas = starsCanvas.value;
if (!canvas) return;
ctx = canvas.getContext("2d");
if (!ctx) return;
const scale = perspective / (perspective + star.z); // 3D perspective scale
const x2d = canvas.width / 2 + star.x * scale;
const y2d = canvas.height / 2 + star.y * scale;
const size = Math.max(scale * 3, 0.5); // Size based on perspective
// Previous position for a trail effect
const prevScale = perspective / (perspective + star.z + star.speed * 15); // Longer trail distance
const xPrev = canvas.width / 2 + star.x * prevScale;
const yPrev = canvas.height / 2 + star.y * prevScale;
const rgb = hexToRgb();
// Draw blurred trail (longer, with low opacity)
ctx.save(); // Save current context state for restoring later
ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`;
ctx.lineWidth = size * 2.5; // Thicker trail for a blur effect
ctx.shadowBlur = 35; // Add blur to the trail
ctx.shadowColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`;
ctx.beginPath();
ctx.moveTo(x2d, y2d);
ctx.lineTo(xPrev, yPrev); // Longer trail
ctx.stroke();
ctx.restore(); // Restore context state to remove blur from the main line
// Draw sharp line (no blur)
ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.6)`;
ctx.lineWidth = size; // The line width is the same as the star's size
ctx.beginPath();
ctx.moveTo(x2d, y2d);
ctx.lineTo(xPrev, yPrev); // Sharp trail
ctx.stroke();
// Draw the actual star (dot)
ctx.fillStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1)`;
ctx.beginPath();
ctx.arc(x2d, y2d, size / 4, 0, Math.PI * 2); // Dot with size matching the width
ctx.fill();
}
// Function to animate the stars
function animate() {
const canvas = starsCanvas.value;
if (!canvas) return;
ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas for each frame
stars.forEach((star) => {
drawStar(star);
// Move star towards the screen (decrease z)
star.z -= star.speed;
// Reset star when it reaches the viewer (z = 0)
if (star.z <= 0) {
star.z = canvas.width;
star.x = (Math.random() - 0.5) * 2 * canvas.width;
star.y = (Math.random() - 0.5) * 2 * canvas.height;
}
});
requestAnimationFrame(animate); // Continue animation
}
// Set canvas to full screen
function resizeCanvas() {
const canvas = starsCanvas.value;
if (!canvas) return;
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
</script>

View File

@ -1 +0,0 @@
export { default as FallingStarsBg } from "./FallingStarsBg.vue";

View File

@ -1,65 +0,0 @@
<template>
<div
:class="
cn(
'border-beam',
'pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]',
'![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]',
'after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] animate-border-beam after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]',
props.class,
)
"
></div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { computed } from "vue";
interface BorderBeamProps {
class?: string;
size?: number;
duration?: number;
borderWidth?: number;
anchor?: number;
colorFrom?: string;
colorTo?: string;
delay?: number;
}
const props = withDefaults(defineProps<BorderBeamProps>(), {
size: 200,
duration: 15000,
anchor: 90,
borderWidth: 1.5,
colorFrom: "#ffaa40",
colorTo: "#9c40ff",
delay: 0,
});
const durationInSeconds = computed(() => `${props.duration}s`);
const delayInSeconds = computed(() => `${props.delay}s`);
</script>
<style scoped>
.border-beam {
--size: v-bind(size);
--duration: v-bind(durationInSeconds);
--anchor: v-bind(anchor);
--border-width: v-bind(borderWidth);
--color-from: v-bind(colorFrom);
--color-to: v-bind(colorTo);
--delay: v-bind(delayInSeconds);
}
.animate-border-beam::after {
content: "";
animation: border-beam-anim var(--duration) infinite linear;
}
@keyframes border-beam-anim {
to {
offset-distance: 100%;
}
}
</style>

View File

@ -1 +0,0 @@
export { default as BorderBeam } from "./BorderBeam.vue";

View File

@ -1,71 +0,0 @@
<template>
<div
:class="[
'group relative flex size-full overflow-hidden rounded-xl border bg-neutral-100 text-black dark:bg-neutral-900 dark:text-white',
$props.class,
]"
@mousemove="handleMouseMove"
@mouseleave="handleMouseLeave"
>
<div :class="cn('relative z-10', props.slotClass)">
<slot></slot>
</div>
<div
class="pointer-events-none absolute inset-0 rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
:style="{
background: backgroundStyle,
opacity: gradientOpacity,
}"
></div>
</div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { ref, computed, onMounted, type HTMLAttributes } from "vue";
const props = withDefaults(
defineProps<{
class?: HTMLAttributes["class"];
slotClass?: HTMLAttributes["class"];
gradientSize?: number;
gradientColor?: string;
gradientOpacity?: number;
}>(),
{
class: "",
slotClass: "",
gradientSize: 200,
gradientColor: "#262626",
gradientOpacity: 0.8,
},
);
const mouseX = ref(-props.gradientSize * 10);
const mouseY = ref(-props.gradientSize * 10);
function handleMouseMove(e: MouseEvent) {
const target = e.currentTarget as HTMLElement;
const rect = target.getBoundingClientRect();
mouseX.value = e.clientX - rect.left;
mouseY.value = e.clientY - rect.top;
}
function handleMouseLeave() {
mouseX.value = -props.gradientSize * 10;
mouseY.value = -props.gradientSize * 10;
}
onMounted(() => {
mouseX.value = -props.gradientSize * 10;
mouseY.value = -props.gradientSize * 10;
});
const backgroundStyle = computed(() => {
return `radial-gradient(
circle at ${mouseX.value}px ${mouseY.value}px,
${props.gradientColor} 0%,
rgba(0, 0, 0, 0) 70%
)`;
});
</script>

View File

@ -1 +0,0 @@
export { default as CardSpotlight } from "./CardSpotlight.vue";

View File

@ -1,76 +0,0 @@
<template>
<div
ref="dockRef"
:class="
cn(
'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max rounded-2xl border p-2 backdrop-blur-md transition-all gap-4',
orientation === 'vertical' && 'flex-col w-[58px] h-max',
props.class,
dockClass,
)
"
@mousemove="onMouseMove"
@mouseleave="onMouseLeave"
>
<slot />
</div>
</template>
<script setup lang="ts">
import type { DataOrientation, Direction } from "./types";
import {
MOUSE_X_INJECTION_KEY,
MOUSE_Y_INJECTION_KEY,
MAGNIFICATION_INJECTION_KEY,
DISTANCE_INJECTION_KEY,
ORIENTATION_INJECTION_KEY,
} from "./injectionKeys";
import { cn } from "@/shadcn/lib/utils";
import type { HTMLAttributes } from "vue";
interface DockProps {
class?: HTMLAttributes["class"];
magnification?: number;
distance?: number;
direction?: Direction;
orientation?: DataOrientation;
}
const props = withDefaults(defineProps<DockProps>(), {
magnification: 60,
distance: 140,
direction: "middle",
orientation: "horizontal",
});
const dockRef = ref<HTMLElement | null>(null);
const mouseX = ref(Infinity);
const mouseY = ref(Infinity);
const magnification = computed(() => props.magnification);
const distance = computed(() => props.distance);
const dockClass = computed(() => ({
"items-start": props.direction === "top",
"items-center": props.direction === "middle",
"items-end": props.direction === "bottom",
}));
function onMouseMove(e: MouseEvent) {
requestAnimationFrame(() => {
mouseX.value = e.pageX;
mouseY.value = e.pageY;
});
}
function onMouseLeave() {
requestAnimationFrame(() => {
mouseX.value = Infinity;
mouseY.value = Infinity;
});
}
provide(MOUSE_X_INJECTION_KEY, mouseX);
provide(MOUSE_Y_INJECTION_KEY, mouseY);
provide(ORIENTATION_INJECTION_KEY, props.orientation);
provide(MAGNIFICATION_INJECTION_KEY, magnification);
provide(DISTANCE_INJECTION_KEY, distance);
</script>

View File

@ -1,61 +0,0 @@
<template>
<div
ref="iconRef"
class="flex aspect-square cursor-pointer items-center justify-center rounded-full transition-all duration-200 ease-out"
:style="{
width: `${iconWidth}px`,
height: `${iconWidth}px`,
}"
:hovered="{
marginLeft: margin,
marginRight: margin,
}"
>
<slot />
</div>
</template>
<script setup lang="ts">
import {
MOUSE_X_INJECTION_KEY,
MOUSE_Y_INJECTION_KEY,
MAGNIFICATION_INJECTION_KEY,
DISTANCE_INJECTION_KEY,
ORIENTATION_INJECTION_KEY,
} from "./injectionKeys";
const iconRef = ref<HTMLDivElement | null>(null);
const mouseX = inject(MOUSE_X_INJECTION_KEY, ref(Infinity));
const mouseY = inject(MOUSE_Y_INJECTION_KEY, ref(Infinity));
const distance = inject(DISTANCE_INJECTION_KEY);
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
const magnification = inject(MAGNIFICATION_INJECTION_KEY);
const isVertical = computed(() => orientation === "vertical");
const margin = ref(0);
function calculateDistance(val: number) {
if (isVertical.value) {
const bounds = iconRef.value?.getBoundingClientRect() || {
y: 0,
height: 0,
};
return val - bounds.y - bounds.height / 2;
}
const bounds = iconRef.value?.getBoundingClientRect() || { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
}
const iconWidth = computed(() => {
const distanceCalc = isVertical.value
? calculateDistance(mouseY.value)
: calculateDistance(mouseX.value);
if (!distance?.value || !magnification?.value) return 40;
if (Math.abs(distanceCalc) < distance?.value) {
return (1 - Math.abs(distanceCalc) / distance?.value) * magnification?.value + 40;
}
return 40;
});
</script>

View File

@ -1,14 +0,0 @@
<template>
<div
:class="
cn('relative block bg-secondary', orientation === 'vertical' ? 'w-4/5 h-0.5' : 'h-4/5 w-0.5')
"
></div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { ORIENTATION_INJECTION_KEY } from "./injectionKeys";
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
</script>

View File

@ -1,5 +0,0 @@
export { default as Dock } from "./Dock.vue";
export { default as DockIcon } from "./DockIcon.vue";
export { default as DockSeparator } from "./DockSeparator.vue";
export type DataOrientation = "vertical" | "horizontal";

View File

@ -1,11 +0,0 @@
import type { Ref, InjectionKey, ComputedRef } from "vue";
import type { DataOrientation } from "./types";
export const MOUSE_X_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
export const MOUSE_Y_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
export const MAGNIFICATION_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
export const DISTANCE_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
export const ORIENTATION_INJECTION_KEY = Symbol() as InjectionKey<DataOrientation>;

View File

@ -1,2 +0,0 @@
export type DataOrientation = "vertical" | "horizontal";
export type Direction = "top" | "middle" | "bottom";

View File

@ -1,83 +0,0 @@
<template>
<button
:class="
cn(
'relative flex items-center justify-center min-w-28 min-h-10 overflow-hidden before:absolute before:-inset-[200%] animate-rainbow rainbow-btn',
props.class,
)
"
>
<span class="btn-content inline-flex size-full items-center justify-center px-4 py-2">
<slot />
</span>
</button>
</template>
<script lang="ts" setup>
import { cn } from "@/shadcn/lib/utils";
import { computed } from "vue";
interface GradientButtonProps {
borderWidth?: number;
colors?: string[];
duration?: number;
borderRadius?: number;
blur?: number;
class?: string;
bgColor?: string;
}
const props = withDefaults(defineProps<GradientButtonProps>(), {
colors: () => [
"#FF0000",
"#FFA500",
"#FFFF00",
"#008000",
"#0000FF",
"#4B0082",
"#EE82EE",
"#FF0000",
],
duration: 2500,
borderWidth: 2,
borderRadius: 8,
blur: 4,
bgColor: "#000",
});
const durationInMilliseconds = computed(() => `${props.duration}ms`);
const allColors = computed(() => props.colors.join(", "));
const borderWidthInPx = computed(() => `${props.borderWidth}px`);
const borderRadiusInPx = computed(() => `${props.borderRadius}px`);
const blurPx = computed(() => `${props.blur}px`);
</script>
<style scoped>
.animate-rainbow::before {
content: "";
background: conic-gradient(v-bind(allColors));
animation: rotate-rainbow v-bind(durationInMilliseconds) linear infinite;
filter: blur(v-bind(blurPx));
padding: v-bind(borderWidthInPx);
}
.rainbow-btn {
padding: v-bind(borderWidthInPx);
border-radius: v-bind(borderRadiusInPx);
}
.btn-content {
border-radius: v-bind(borderRadiusInPx);
background-color: v-bind(bgColor);
z-index: 0;
}
@keyframes rotate-rainbow {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -1 +0,0 @@
export { default as GradientButton } from "./GradientButton.vue";

View File

@ -1,60 +0,0 @@
<template>
<button
ref="buttonRef"
:class="
cn(
'group relative w-auto cursor-pointer overflow-hidden rounded-full border bg-background p-2 px-6 text-center font-semibold',
props.class,
)
"
>
<div class="flex items-center gap-2">
<div
class="size-2 scale-100 rounded-lg bg-primary transition-all duration-300 group-hover:scale-[100.8]"
></div>
<span
class="inline-block whitespace-nowrap transition-all duration-300 group-hover:translate-x-12 group-hover:opacity-0"
>
{{ text }}
</span>
</div>
<div
class="absolute top-0 z-10 flex size-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100"
>
<span class="whitespace-nowrap">{{ text }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-arrow-right"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
</div>
</button>
</template>
<script lang="ts" setup>
import { cn } from "@/shadcn/lib/utils";
import { ref } from "vue";
interface Props {
text?: string;
class?: string;
}
const props = withDefaults(defineProps<Props>(), {
text: "Button",
});
const buttonRef = ref<HTMLButtonElement>();
</script>
<style></style>

View File

@ -1 +0,0 @@
export { default as InteractiveHoverButton } from "./InteractiveHoverButton.vue";

View File

@ -1,138 +0,0 @@
<template>
<div
:class="
cn(patternBackgroundVariants({ variant, size }), ` ${animate ? 'move move-' + direction : ''} `, props.class)
"
>
<div
:class="
cn(
'absolute pointer-events-none inset-0 flex items-center justify-center',
patternBackgroundMaskVariants({ mask }),
)
"
></div>
<slot />
</div>
</template>
<script setup lang="ts">
import { cn } from '@/shadcn/lib/utils';
import type { BaseProps as Props } from '.';
import {
PATTERN_BACKGROUND_DIRECTION,
PATTERN_BACKGROUND_SPEED,
PATTERN_BACKGROUND_VARIANT,
patternBackgroundMaskVariants,
patternBackgroundVariants,
} from '.';
import { computed } from 'vue';
const props = withDefaults(defineProps<Props>(), {
direction: () => PATTERN_BACKGROUND_DIRECTION.Top,
variant: () => PATTERN_BACKGROUND_VARIANT.Grid,
speed: () => PATTERN_BACKGROUND_SPEED.Default,
size: undefined,
mask: undefined,
});
const durationFormSpeed = computed(() => `${props.speed}ms`);
</script>
<style scoped>
@keyframes to-top {
0% {
background-position: 0 100%;
}
100% {
background-position: 0 0;
}
}
@keyframes to-bottom {
0% {
background-position: 0 0;
}
100% {
background-position: 0 100%;
}
}
@keyframes to-right {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 0;
}
}
@keyframes to-left {
0% {
background-position: 100% 0;
}
100% {
background-position: 0 0;
}
}
@keyframes to-top-right {
0% {
background-position: 0 100%;
}
100% {
background-position: 100% 0;
}
}
@keyframes to-top-left {
0% {
background-position: 100% 100%;
}
100% {
background-position: 0 0;
}
}
@keyframes to-bottom-right {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 100%;
}
}
@keyframes to-bottom-left {
0% {
background-position: 100% 0;
}
100% {
background-position: 0 100%;
}
}
.move {
animation-duration: v-bind(durationFormSpeed);
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.move-top {
animation-name: to-top;
}
.move-bottom {
animation-name: to-bottom;
}
.move-right {
animation-name: to-right;
}
.move-left {
animation-name: to-left;
}
.move-top-right {
animation-name: to-top-right;
}
.move-top-left {
animation-name: to-top-left;
}
.move-bottom-right {
animation-name: to-bottom-right;
}
.move-bottom-left {
animation-name: to-bottom-left;
}
</style>

View File

@ -1,87 +0,0 @@
import { cva, type VariantProps } from "class-variance-authority";
import type { HTMLAttributes } from "vue";
type ObjectValues<T> = T[keyof T];
export const PATTERN_BACKGROUND_DIRECTION = {
Top: "top",
Bottom: "bottom",
Left: "left",
Right: "right",
TopLeft: "top-left",
TopRight: "top-right",
BottomLeft: "bottom-left",
BottomRight: "bottom-right",
} as const;
export type PatternBackgroundDirection = ObjectValues<typeof PATTERN_BACKGROUND_DIRECTION>;
export interface BaseProps {
class?: HTMLAttributes["class"];
animate?: boolean;
direction?: PatternBackgroundDirection;
variant?: PatternBackgroundVariants["variant"];
size?: PatternBackgroundVariants["size"];
mask?: PatternBackgroundMaskVariants["mask"];
speed?: ObjectValues<typeof PATTERN_BACKGROUND_SPEED>;
}
export const PATTERN_BACKGROUND_VARIANT = {
Grid: "grid",
Dot: "dot",
BigDot: "big-dot",
} as const;
export const PATTERN_BACKGROUND_SPEED = {
Default: 10000,
Slow: 25000,
Fast: 5000,
} as const;
export const patternBackgroundVariants = cva("relative text-clip", {
variants: {
variant: {
[PATTERN_BACKGROUND_VARIANT.Grid]:
"bg-[linear-gradient(to_right,hsl(var(--foreground)/0.3)_1px,transparent_1px),linear-gradient(to_bottom,hsl(var(--foreground)/0.3)_1px,transparent_1px)]",
[PATTERN_BACKGROUND_VARIANT.Dot]:
"bg-[radial-gradient(hsl(var(--foreground)/0.3)_1px,transparent_1px)]",
[PATTERN_BACKGROUND_VARIANT.BigDot]:
"bg-[radial-gradient(hsl(var(--foreground)/0.3)_3px,transparent_3px)]",
},
size: {
xs: "bg-[size:8px_8px]",
sm: "bg-[size:16px_16px]",
md: "bg-[size:24px_24px]",
lg: "bg-[size:32px_32px]",
},
},
defaultVariants: {
variant: "grid",
size: "md",
},
});
export type PatternBackgroundVariants = VariantProps<typeof patternBackgroundVariants>;
export const PATTERN_BACKGROUND_MASK = {
Ellipse: "ellipse",
EllipseTop: "ellipse-top",
} as const;
export const patternBackgroundMaskVariants = cva("bg-background", {
variants: {
mask: {
[PATTERN_BACKGROUND_MASK.Ellipse]:
"[mask-image:radial-gradient(ellipse_at_center,transparent,black_80%)]",
[PATTERN_BACKGROUND_MASK.EllipseTop]:
"[mask-image:radial-gradient(ellipse_at_top,transparent,black_80%)]",
},
},
defaultVariants: {
mask: "ellipse",
},
});
export type PatternBackgroundMaskVariants = VariantProps<typeof patternBackgroundMaskVariants>;
export { default as PatternBackground } from "./PatternBackground.vue";

View File

@ -1,107 +0,0 @@
<template>
<button
:class="
cn(
'group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap border border-white/10 px-6 py-3 text-white [background:var(--bg)] [border-radius:var(--radius)] dark:text-black',
'transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px',
$props.class,
)
"
:style="{
'--spread': '90deg',
'--shimmer-color': shimmerColor,
'--radius': borderRadius,
'--speed': shimmerDuration,
'--cut': shimmerSize,
'--bg': background,
}"
>
<div :class="cn('-z-30 blur-[2px]', 'absolute inset-0 overflow-visible [container-type:size]')">
<div
class="animate-shimmer-btn-shimmer-slide absolute inset-0 h-[100cqh] [aspect-ratio:1] [border-radius:0] [mask:none]"
>
<div
class="animate-shimmer-btn-spin-around absolute -inset-full w-auto rotate-0 [background:conic-gradient(from_calc(270deg-(var(--spread)*0.5)),transparent_0,var(--shimmer-color)_var(--spread),transparent_var(--spread))] [translate:0_0]"
/>
</div>
</div>
<slot />
<div
:class="
cn(
'insert-0 absolute size-full',
'rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#ffffff1f]',
// transition
'transform-gpu transition-all duration-300 ease-in-out',
// on hover
'group-hover:shadow-[inset_0_-6px_10px_#ffffff3f]',
// on click
'group-active:shadow-[inset_0_-10px_10px_#ffffff3f]',
)
"
/>
<div
class="absolute -z-20 [background:var(--bg)] [border-radius:var(--radius)] [inset:var(--cut)]"
/>
</button>
</template>
<script lang="ts" setup>
import { cn } from '@/shadcn/lib/utils';
type ShimmerButtonProps = {
shimmerColor?: string;
shimmerSize?: string;
borderRadius?: string;
shimmerDuration?: string;
background?: string;
class?: string;
};
withDefaults(defineProps<ShimmerButtonProps>(), {
shimmerColor: "#ffffff",
shimmerSize: "0.05em",
shimmerDuration: "3s",
borderRadius: "100px",
background: "rgba(0, 0, 0, 1)",
});
</script>
<style scoped>
@keyframes shimmer-btn-shimmer-slide {
to {
transform: translate(calc(100cqw - 100%), 0);
}
}
@keyframes shimmer-btn-spin-around {
0% {
transform: translateZ(0) rotate(0);
}
15%,
35% {
transform: translateZ(0) rotate(90deg);
}
65%,
85% {
transform: translateZ(0) rotate(270deg);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
.animate-shimmer-btn-shimmer-slide {
animation: shimmer-btn-shimmer-slide var(--speed) ease-in-out infinite alternate;
}
.animate-shimmer-btn-spin-around {
animation: shimmer-btn-spin-around calc(var(--speed) * 2) infinite linear;
}
</style>

View File

@ -1 +0,0 @@
export { default as ShimmerButton } from "./ShimmerButton.vue";

View File

@ -1,159 +0,0 @@
<template>
<div
ref="containerRef"
class="relative size-full overflow-hidden will-change-transform"
:style="{ background }"
>
<canvas
ref="canvasRef"
class="absolute inset-0 size-full"
/>
</div>
</template>
<script setup lang="ts">
import { useRafFn, templateRef } from "@vueuse/core";
import { ref, onMounted, onBeforeUnmount } from "vue";
interface Props {
background?: string;
particleColor?: string;
minSize?: number;
maxSize?: number;
speed?: number;
particleDensity?: number;
}
interface Particle {
x: number;
y: number;
size: number;
opacity: number;
vx: number;
vy: number;
phase: number;
phaseSpeed: number;
}
const props = withDefaults(defineProps<Props>(), {
background: "#0d47a1",
particleColor: "#ffffff",
minSize: 1,
maxSize: 3,
speed: 4,
particleDensity: 120,
});
const containerRef = templateRef<HTMLElement | null>("containerRef");
const canvasRef = templateRef<HTMLCanvasElement | null>("canvasRef");
const particles = ref<Particle[]>([]);
const ctx = ref<CanvasRenderingContext2D | null>(null);
// Adjust canvas size on mount and resize
function resizeCanvas() {
if (!canvasRef.value || !containerRef.value) return;
const dpr = window.devicePixelRatio || 1;
const rect = containerRef.value.getBoundingClientRect();
canvasRef.value.width = rect.width * dpr;
canvasRef.value.height = rect.height * dpr;
if (ctx.value) {
ctx.value.scale(dpr, dpr);
}
}
function generateParticles(): void {
const newParticles: Particle[] = [];
const count = props.particleDensity;
for (let i = 0; i < count; i++) {
const baseSpeed = 0.05;
const speedVariance = Math.random() * 0.3 + 0.7;
newParticles.push({
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * (props.maxSize - props.minSize) + props.minSize,
opacity: Math.random() * 0.5 + 0.3,
vx: (Math.random() - 0.5) * baseSpeed * speedVariance * props.speed,
vy: ((Math.random() - 0.5) * baseSpeed - baseSpeed * 0.3) * speedVariance * props.speed,
phase: Math.random() * Math.PI * 2,
phaseSpeed: 0.015,
});
}
particles.value = newParticles;
}
function updateAndDrawParticles() {
if (!ctx.value || !canvasRef.value) return;
const canvas = canvasRef.value;
ctx.value.clearRect(0, 0, canvas.width, canvas.height);
particles.value = particles.value.map((particle) => {
let newX = particle.x + particle.vx;
let newY = particle.y + particle.vy;
if (newX < -2) newX = 102;
if (newX > 102) newX = -2;
if (newY < -2) newY = 102;
if (newY > 102) newY = -2;
const newPhase = (particle.phase + particle.phaseSpeed) % (Math.PI * 2);
const opacity = 0.3 + (Math.sin(newPhase) * 0.3 + 0.3);
// Draw particle
ctx.value!.beginPath();
ctx.value!.arc(
(newX * canvas.width) / 100,
(newY * canvas.height) / 100,
particle.size,
0,
Math.PI * 2,
);
ctx.value!.fillStyle = `${props.particleColor}${Math.floor(opacity * 255)
.toString(16)
.padStart(2, "0")}`;
ctx.value!.fill();
return {
...particle,
x: newX,
y: newY,
phase: newPhase,
opacity,
};
});
}
const { pause, resume } = useRafFn(updateAndDrawParticles, { immediate: false });
// Handle window resize
let resizeObserver: ResizeObserver | undefined;
onMounted(() => {
if (!canvasRef.value) return;
ctx.value = canvasRef.value.getContext("2d");
resizeCanvas();
generateParticles();
// Set up resize observer
resizeObserver = new ResizeObserver(resizeCanvas);
if (containerRef.value) {
resizeObserver.observe(containerRef.value);
}
resume();
});
onBeforeUnmount(() => {
pause();
if (resizeObserver && containerRef.value) {
resizeObserver.unobserve(containerRef.value);
}
});
</script>

View File

@ -1 +0,0 @@
export { default as Sparkles } from "./Sparkles.vue";

View File

@ -1,80 +0,0 @@
<!-- ParentSize.vue -->
<template>
<div ref="target" :style="mergedStyles" :class="cn('w-full h-full', props.class)" v-bind="attrsWithoutClassAndStyle">
<slot />
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, useAttrs } from 'vue';
import { useDebounceFn, useResizeObserver } from '@vueuse/core';
import { cn } from '@/shadcn/lib/utils';
const props = defineProps({
class: String,
debounceTime: {
type: Number,
default: 300,
},
ignoreDimensions: {
type: [Array, String],
default: () => [],
},
parentSizeStyles: Object,
enableDebounceLeadingCall: {
type: Boolean,
default: true,
},
});
const attrs = useAttrs();
const target = ref<HTMLElement | null>(null);
const state = reactive({
width: 0,
height: 0,
top: 0,
left: 0,
});
const mergedStyles = computed(() => ({
...props.parentSizeStyles,
...(attrs.style as object),
}));
const mergedClass = computed(() => ['w-full h-full', props.class]);
const attrsWithoutClassAndStyle = computed(() => {
const { class: _, style: __, ...rest } = attrs;
return rest;
});
const normalizedIgnore = computed(() =>
Array.isArray(props.ignoreDimensions) ? props.ignoreDimensions : [props.ignoreDimensions],
);
function updateDimensions(rect: DOMRectReadOnly) {
const { width, height, top, left } = rect;
const newState = { width, height, top, left };
const hasChange = Object.keys(newState).some(
(key) => state[key as keyof typeof state] !== newState[key as keyof typeof state],
);
if (!hasChange) return;
const shouldUpdate = !Object.keys(newState).every((key) =>
normalizedIgnore.value.includes(key as keyof typeof state),
);
if (shouldUpdate) {
Object.assign(state, newState);
}
}
const debouncedUpdate = useDebounceFn(updateDimensions, props.debounceTime);
useResizeObserver(target, (entries) => {
const entry = entries[0];
if (entry) debouncedUpdate(entry.contentRect);
});
</script>

View File

@ -1,112 +0,0 @@
<!-- Spline.vue -->
<template>
<ParentSize
:parent-size-styles="parentSizeStyles"
:debounce-time="50"
v-bind="$attrs"
>
<template #default>
<canvas
ref="canvasRef"
:style="canvasStyle"
/>
<slot v-if="isLoading" />
</template>
</ParentSize>
</template>
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ref, onMounted, onUnmounted, computed } from "vue";
import { Application, type SplineEventName } from "@splinetool/runtime";
import { useDebounceFn } from "@vueuse/core";
import ParentSize from "./ParentSize.vue";
const props = defineProps({
scene: {
type: String,
required: true,
},
onLoad: Function,
renderOnDemand: {
type: Boolean,
default: true,
},
style: Object,
});
let cleanUpFns: any[] = [];
const emit = defineEmits([
"error",
...[
"spline-mouse-down",
"spline-mouse-up",
"spline-mouse-hover",
"spline-key-down",
"spline-key-up",
"spline-start",
"spline-look-at",
"spline-follow",
"spline-scroll",
],
]);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const isLoading = ref(true);
const splineApp = ref<Application | null>(null);
const parentSizeStyles = computed(() => ({
overflow: "hidden",
...props.style,
}));
const canvasStyle = computed(() => ({
display: isLoading.value ? "none" : "block",
width: "100%",
height: "100%",
}));
function eventHandler(name: SplineEventName, handler?: (e: any) => void) {
if (!handler || !splineApp.value) return;
const debouncedHandler = useDebounceFn(handler, 50, {
maxWait: 100,
});
splineApp.value.addEventListener(name, debouncedHandler);
return () => splineApp.value?.removeEventListener(name, debouncedHandler);
}
onMounted(async () => {
if (!canvasRef.value) return;
try {
splineApp.value = new Application(canvasRef.value, {
renderOnDemand: props.renderOnDemand,
});
await splineApp.value.load(props.scene);
cleanUpFns = [
eventHandler("mouseDown", (e: any) => emit("spline-mouse-down", e)),
eventHandler("mouseUp", (e: any) => emit("spline-mouse-up", e)),
eventHandler("mouseHover", (e: any) => emit("spline-mouse-hover", e)),
eventHandler("keyDown", (e: any) => emit("spline-key-down", e)),
eventHandler("keyUp", (e: any) => emit("spline-key-up", e)),
eventHandler("start", (e: any) => emit("spline-start", e)),
eventHandler("lookAt", (e: any) => emit("spline-look-at", e)),
eventHandler("follow", (e: any) => emit("spline-follow", e)),
eventHandler("scroll", (e: any) => emit("spline-scroll", e)),
].filter(Boolean);
isLoading.value = false;
props.onLoad?.(splineApp.value);
} catch (err) {
emit("error", err);
}
});
onUnmounted(() => {
cleanUpFns.forEach((fn) => fn?.());
splineApp.value?.dispose();
});
</script>

View File

@ -1,2 +0,0 @@
export { default as Spline } from "./Spline.vue";
export { default as ParentSize } from "./ParentSize.vue";

View File

@ -0,0 +1,14 @@
<template>
<TemplateFoo.define v-slot="{ msg }">
<div>Hello {{ msg.toUpperCase() }}</div>
</TemplateFoo.define>
<TemplateFoo.reuse msg="World" />
<TemplateFoo.reuse msg="Reusable" />
</template>
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core';
const TemplateFoo = createReusableTemplate<{ msg: string }>();
</script>

View File

@ -1,110 +0,0 @@
<script setup lang="ts">
import Sortable from 'sortablejs';
interface ComponentItem {
id: number | string;
name: string;
}
interface SelectorState {
组件列表: ComponentItem[];
已选流程ID: (number | string)[];
}
const props = defineProps<{
state: SelectorState;
}>();
const 流程起点列表 = ref<ComponentItem[]>([]);
// const 流程终点列表 = ref<ComponentItem[]>([]);
const 源列表引用 = useTemplateRef<HTMLDivElement | null>('源列表引用');
const 起点列表引用 = useTemplateRef<HTMLDivElement | null>('起点列表引用');
// const 终点列表引用 = useTemplateRef<HTMLDivElement | null>('终点列表引用');
const 组件列表 = computed(() => props.state.组件列表);
onMounted(() => {
if (!源列表引用.value || !起点列表引用.value /* || !终点列表引用.value */) {
consola.error('未能获取到 SortableJS 容器元素');
return;
}
// 初始化源列表
Sortable.create(源列表引用.value, {
group: {
name: 'components',
pull: 'clone', // 允许克隆
put: false, // 不允许拖入
},
sort: false, // 源列表内不允许排序
// 将 data-id 附加到拖拽数据中,供目标列表识别
setData: (dataTransfer, dragEl) => {
if (!dragEl.dataset.id) {
consola.error('拖拽元素未包含 data-id');
return;
}
dataTransfer.setData('text/plain', dragEl.dataset.id);
},
});
// 初始化流程起点列表
Sortable.create(起点列表引用.value, {
group: {
name: 'flowPoints', // 修改为新的组名,用于区分"流程点"和普通组件
pull: true, // 允许拖出
put: ['components'], // 允许接收的组
},
draggable: '.DRAG-ITEM', // 只有带 .DRAG-ITEM 的元素才能被拖动
onAdd: (evt) => {
const itemId = Number.parseInt(evt.item.dataset.id || '0', 10);
consola.info(`从 [${evt.from.dataset.name}] 拖入到 [${evt.to.dataset.name}]itemId: ${itemId}`);
},
});
});
</script>
<template>
<div class="flex gap-5 p-5 bg-[#1a1a1a] text-[#e0e0e0] font-sans min-h-100">
<!-- 组件选择区域 -->
<div class="flex-1 p-4 rounded">
<h3 class="text-center mb-5 text-white font-bold">组件选择</h3>
<div ref="源列表引用" class="flex flex-col gap-2 bg-[#2a2a2a] p-4 rounded" data-name="组件选择区域">
<div class="DRAG-ITEM" v-for="item in 组件列表" :key="item.id" :data-id="item.id">
<AButton class="min-h-50px">
{{ item.name }}
</AButton>
</div>
</div>
</div>
<!-- 构建流程区域 -->
<div class="flex-1 p-4 rounded">
<h3 class="text-center mb-5 text-white font-bold">构建流程</h3>
<div class="flex items-start mb-2.5">
<div class="w-20 text-right mr-2.5 pt-2.5 text-[#ccc]">流程起点</div>
<div ref="起点列表引用" data-name="流程起点区域" class="TARGET-AREA">
<div
v-if="流程起点列表.length === 0"
class="flex justify-center items-center h-full min-h-130px text-[#888] text-center placeholder"
>
请拖入组件构成流程起点
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.placeholder {
/* 默认隐藏的 */
display: none;
}
.TARGET-AREA {
@apply flex-1 min-h-150px rounded p-2.5 border-2 border-dashed border-[#555] bg-[#2a2a2a] mt-1.25 mb-5;
}
/* target-area 的最后一个 .placeholder 元素 */
.TARGET-AREA > .placeholder:last-child {
/* 显示 */
display: block;
}
</style>

View File

@ -1,158 +0,0 @@
<!--
* - https://github.com/vuetifyjs/vuetify/blob/05076ce61f6af069198dc4f676509f0a5e306c73/packages/vuetify/src/components/VInfiniteScroll/VInfiniteScroll.tsx
* - https://github.com/vuetifyjs/vuetify/blob/05076ce61f6af069198dc4f676509f0a5e306c73/packages/vuetifyjs/vuetify/packages/vuetify/src/composables/intersectionObserver.ts
-->
<script lang="ts">
/* https://github.com/youzan/vant/blob/be93d4990fd671338b5d1066cf8419519d668d65/packages/vant/src/list/List.tsx */
function checkIsVisible(el: Element, root: Element | null = null) {
if (!el) return false;
const elRect = el.getBoundingClientRect();
const rootRect = root
? root.getBoundingClientRect()
: { bottom: window.innerHeight, left: 0, right: window.innerWidth, top: 0 };
return (
elRect.bottom >= rootRect.top &&
elRect.top <= rootRect.bottom &&
elRect.right >= rootRect.left &&
elRect.left <= rootRect.right
);
}
</script>
<script setup lang="ts">
/**
* @example
*
* ```ts
* const list = ref<Record<string, never>[]>([]);
* const loadData = async (page: number) => { ... };
* ```
*
* ```vue
* <UseIntersectionObserverInfiniteLoading />
* ```
*/
const props = defineProps<{
complete: boolean;
error: boolean;
errorText: string;
loading: boolean;
}>();
const emit = defineEmits<{
clickError: [];
load: [];
}>();
defineSlots<{
// 加载完成(没有更多了)
complete(): unknown;
// 加载失败
error(): unknown;
// 加载完成(还有更多)
loaded(): unknown;
// 加载中
loading(): unknown;
}>();
const check = (reason?: string) => {
nextTick(() => {
if (
props.loading ||
props.complete ||
// props.disabled ||
props.error
) {
return;
}
if (checkIsVisible(target.value!)) {
emit('load');
}
});
};
const target = ref(null);
const { pause, resume } = useIntersectionObserver(
target,
([entry]) => {
if (entry?.isIntersecting) {
if (props.loading) return;
check('isIntersecting');
}
},
{
immediate: false,
root: undefined,
rootMargin: '0px',
// 数值形式单个值表示目标元素可见部分与整个目标元素的比例。例如threshold: 0.5
// 目标元素可见比例达到 50% 时触发回调。
// 数组形式多个值表示多个可见比例触发点。例如threshold: [0, 0.25, 0.5, 0.75, 1.0]。
// 在目标元素可见部分从 0% 增加到 100% 时,每达到一个阈值都会触发回调。
threshold: 0,
},
);
watchEffect(() => {
if (props.complete) {
pause();
} else {
resume();
}
});
watch(
() => [props.loading, props.complete, props.error],
() => check('watch'),
);
</script>
<template>
<div class="infinite-loading" ref="target">
<div v-if="complete" class="infinite-loading__complete">
<slot name="complete">
<span>没有更多了</span>
</slot>
</div>
<template v-else>
<template v-if="error">
<div class="infinite-loading__error" @click="emit('clickError')">
<slot name="error">
<span> {{ props.errorText || '加载失败,点击重试' }} </span>
</slot>
</div>
</template>
<template v-else>
<div v-show="loading" class="infinite-loading__loading">
<slot name="loading">
<span> 加载中... </span>
</slot>
</div>
<div v-show="!loading" class="infinite-loading__loaded" @click="check('click loaded')">
<slot name="loaded">
<span> 加载更多 </span>
</slot>
</div>
</template>
</template>
</div>
</template>
<style>
.infinite-loading__loading,
.infinite-loading__loaded,
.infinite-loading__complete,
.infinite-loading__error {
display: flex;
justify-content: center;
align-items: center;
min-height: 3rem;
color: #666;
}
.infinite-loading__loaded,
.infinite-loading__error {
cursor: pointer;
}
</style>

View File

@ -1,4 +0,0 @@
- 卫星覆盖范围: 当前实现是基于卫星高度的一个简化估算altitude \* 0.8),代码注释也指明了这一点。这种方法不够精确,仅能提供一个大致的视觉参考。精确的覆盖范围计算需要考虑卫星的视场角 (FOV) 或波束宽度等具体参数。
- TLE 数据 → 轨道 → 位置 & 速度
- 大小 → 需要外部规格数据
- 精确姿态 → 需要专门的 ADCS 数据 (TLE 不提供)

View File

@ -1,39 +0,0 @@
## 配置项目
- https://github.dev/CesiumGS/cesium-vite-example
### 其他
- https://cesium.com/blog/2024/02/13/configuring-vite-or-webpack-for-cesiumjs/
- https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/
- vite-plugin-cesium
## 参考
- [vue-cesium](https://zouyaoji.top/vue-cesium/#/zh-CN/component/controls/vc-navigation)
- https://cesium.pages.dev/
## 离线地图
- https://github.com/CesiumGS/cesium/tree/main/Documentation/OfflineGuide
- https://blog.csdn.net/lhllhllhl_/article/details/145857779
- https://blog.csdn.net/m0_54849806/article/details/126070809
- https://juejin.cn/post/6969838266182795278
- https://blog.csdn.net/CSDNqinzhike/article/details/139587028
## TLE
### 格式
- https://celestrak.org/NORAD/documentation/tle-fmt.php
- https://en.wikipedia.org/wiki/Two-line_element_set#Format
### 轨道数据
- https://celestrak.org/NORAD/elements/
- https://www.n2yo.com/satellites/?c=PRC&t=country
- https://www.space-track.org/#catalog
### 相关
- https://www.satview.org/?sat_id=63158U

View File

@ -1,101 +0,0 @@
import * as Cesium from 'cesium';
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';
import type { I卫星 } from '../managers/HCesiumManager.types'; // 保持类型定义位置
const = 2 * 60 * 60; // 2小时
const = 30;
export interface OrbitCalculationResult {
orbitPositions: Cesium.Cartesian3[]; // 用于绘制完整轨道线
sampledPositionProperty: Cesium.SampledPositionProperty;
}
export interface OrbitSample {
position: Cesium.Cartesian3;
time: Cesium.JulianDate;
}
export class SatelliteCalculator {
/**
* 解析 TLE 字符串获取卫星记录对象。
* @param tle - 包含名称和两行数据的 TLE 字符串。
* @param satelliteId - 用于错误日志的卫星 ID。
* @returns 解析成功返回 SatRec 对象,否则返回 null。
*/
parseTle(tle: string, satelliteId: string): null | SatRec {
const tleLines = tle.trim().split('\n') as [string, string, string];
if (tleLines.length < 3) {
console.error(`无效的 TLE 格式 (ID: ${satelliteId}): TLE 字符串至少需要三行`);
return null;
}
const tle1 = tleLines[1].trim();
const tle2 = tleLines[2].trim();
try {
return twoline2satrec(tle1, tle2);
} catch (error) {
console.error(`解析 TLE 失败 (ID: ${satelliteId}):`, error);
return null;
}
}
/**
* 计算卫星在给定时间段内的轨道位置。
* @param satrec - 卫星记录对象。
* @param startTime - 计算轨道的开始时间。
* @param options - 包含轨道时长、步长等选项。
* @param satelliteId - 用于日志记录的卫星 ID。
* @returns 包含 SampledPositionProperty 和轨道点数组的对象,如果计算失败则返回 null。
*/
calculateOrbit(
satrec: SatRec,
startTime: Cesium.JulianDate,
options: Pick<I卫星, 'orbitDurationSeconds' | 'showOrbit' | 'timeStepSeconds'>,
satelliteId: string,
): null | OrbitCalculationResult {
const { orbitDurationSeconds = , timeStepSeconds = , showOrbit } = options;
const sampledPositionProperty = new Cesium.SampledPositionProperty();
const orbitPositions: Cesium.Cartesian3[] = [];
let hasSamples = false; // 标记是否成功添加了至少一个样本点
for (let i = 0; i <= orbitDurationSeconds; i += timeStepSeconds) {
const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
const jsDate = Cesium.JulianDate.toDate(time);
try {
const positionAndVelocity = propagate(satrec, jsDate);
if (positionAndVelocity === null) {
console.warn(`卫星 ${satelliteId} 在时间 ${jsDate} 位置计算失败或已衰减。`);
continue; // 跳过这个时间点
}
const gmst = gstime(jsDate);
const positionEcf = eciToEcf(positionAndVelocity.position, gmst);
// 转换为 Cesium 坐标(单位:米)
const cesiumPosition = new Cesium.Cartesian3(positionEcf.x * 1000, positionEcf.y * 1000, positionEcf.z * 1000);
// 添加位置样本
sampledPositionProperty.addSample(time, cesiumPosition);
hasSamples = true; // 标记已添加样本
if (showOrbit) {
orbitPositions.push(cesiumPosition);
}
} catch (error) {
console.error(`计算卫星 ${satelliteId} 在时间 ${jsDate} 的位置时出错:`, error);
// 可以在这里决定是跳过还是中断循环
continue;
}
}
// 确保至少有一个样本点,否则 SampledPositionProperty 会有问题
if (!hasSamples) {
console.warn(`卫星 ${satelliteId} 未能计算出任何有效轨道点。`);
return null;
}
return { sampledPositionProperty, orbitPositions };
}
}

View File

@ -1,42 +0,0 @@
<script setup lang="ts">
import type { HCesiumManagerSatelliteState, HCesiumManagerStationState } from './useHCesiumManager.types';
import { useHCesiumManager } from './useHCesiumManager';
import { useHCesiumManagerSatellite } from './useHCesiumManager.卫星';
import { useHCesiumManagerStation } from './useHCesiumManager.站点';
import 'cesium/Build/Cesium/Widgets/widgets.css';
const props = defineProps<{
satelliteState: HCesiumManagerSatelliteState;
stationState: HCesiumManagerStationState;
}>();
// 1. 管理 Cesium Viewer 实例生命周期
const { hCesiumViewerManager } = useHCesiumManager('cesium-container'); // 获取新的 Manager 实例
// 2. 同步地面站实体
useHCesiumManagerStation(
() => hCesiumViewerManager.value,
() => props.stationState,
);
// 3. 同步卫星实体
useHCesiumManagerSatellite(
() => hCesiumViewerManager.value,
() => props.satelliteState,
);
</script>
<template>
<div id="cesium-container" class="relative inset-0 isolate h-full w-full">
<slot></slot>
</div>
</template>
<style scoped>
#cesium-container {
min-height: 100px;
background-color: #000;
}
</style>

View File

@ -1,20 +0,0 @@
/* prettier-ignore */
export const TOOLTIP_MAP = {
NORAD_CAT_ID_卫星编号: "5位数字如'63158'表示NORAD卫星唯一目录编号",
CLASSIFICATION_TYPE_卫星分类: "1个字母如'U',表示数据分类",
OBJECT_ID_国际标识符: "8个字符如'25045B'(年份+发射编号+部件),不足时右侧补空格",
EPOCH_历元时间: "14位数字如'25071.65907894',表示轨道数据的参考时间(年份+年积日.一天中的小数部分)",
MEAN_MOTION_DOT_平均运动一阶导数: "10位数字如'.00072212',表示平均运动变化率的一半,单位圈/天²,左侧补空格",
MEAN_MOTION_DDOT_平均运动二阶导数: "8位科学计数法如'-50502-6'表示-5.0502×10⁻⁶圈/天³,最后一位是指数",
BSTAR_BSTAR拖曳项: "BSTAR参数。8位科学计数法如'39937-3'表示0.39937×10⁻³/地球半径,表示大气拖曳影响",
EPHEMERIS_TYPE_星历类型: "通常为'0'表示使用SGP4星历模型",
ELEMENT_SET_NO_元素集编号: "4位数字如'9999'表示TLE版本号",
// CHECKSUM_校验和: "1位数字用于验证数据完整性可选项",
INCLINATION_轨道倾角: "8位数字如'19.0363'表示轨道与赤道平面的夹角范围0°-180°",
RA_OF_ASC_NODE_升交点赤经: "8位数字如'63.0294'表示轨道升交点的赤经范围0°-360°",
ECCENTRICITY_离心率: "7位数字无小数点如'7375486'表示0.7375486,小数点隐含在最前面",
ARG_OF_PERICENTER_近地点幅角: "8位数字如'181.9338'表示近地点相对升交点的角度范围0°-360°",
MEAN_ANOMALY_平近点角: "8位数字如'171.6150'表示历元时的平近点角范围0°-360°",
MEAN_MOTION_平均运动: "11位数字如'2.21786616',表示卫星每天绕地球的圈数(圈/天)",
REV_AT_EPOCH_历元时的圈数: "5位数字如'13',表示卫星在历元时完成的轨道圈数",
};

View File

@ -1,33 +0,0 @@
import type { Viewer } from 'cesium';
import * as Cesium from 'cesium';
export const VIEWER_OPTIONS_FN = (): Viewer.ConstructorOptions => {
return {
animation: true, // .cesium-viewer-animationContainer https://cesium.com/learn/ion-sdk/ref-doc/Animation.html
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.TileMapServiceImageryProvider.fromUrl(Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')),
),
baseLayerPicker: false,
fullscreenButton: !true, // 全屏按钮
geocoder: false, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false则不会创建Geocoder小部件。
// globe: false, // 地球
homeButton: true, // Home按钮
infoBox: false, // InfoBox小部件。
navigationHelpButton: false, // 是否显示导航帮助按钮
orderIndependentTranslucency: false, // 顺序无关透明度
projectionPicker: !true, // 投影选择器
requestRenderMode: !true, // 如果为真渲染帧将仅在场景内部发生变化时需要时发生。启用此功能可以减少应用程序的CPU/GPU使用率并在移动设备上节省更多电量但在此模式下需要使用{@link Scene#requestRender}显式渲染新帧。在API的其他部分对场景进行更改后在许多情况下都需要这样做。请参阅{@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|使用显式渲染提高性能}。
sceneModePicker: true, // 是否显示场景模式选择器(2D/3D切换)
selectionIndicator: true,
shadows: true, // Determines if shadows are cast by light sources.
/* animationContainer: !true, */
/* timelineContainer: true, */
/* bottomContainer: document.createElement('p'), // The DOM element or ID that will contain the bottomContainer. If not specified, the bottomContainer is added to the widget itself. */
shouldAnimate: !true,
showRenderLoopErrors: true, // 如果为真当发生渲染循环错误时此小部件将自动向用户显示包含错误的HTML面板。
timeline: true,
};
};

View File

@ -1,46 +0,0 @@
import * as Cesium from 'cesium';
export function configureCesium() {
if (document.querySelector('#hide-cesium-viewer-bottom') === null) {
document.head.append(
Object.assign(document.createElement('style'), {
id: 'hide-cesium-viewer-bottom',
innerHTML: `
.cesium-viewer-bottom {
display: none !important;
}
`.trim(),
type: 'text/css',
}),
);
}
/* 时间日期格式化 */ {
const minutes = 0 - new Date().getTimezoneOffset(); // 0 - (-480);
// Animation 的时间日期格式化
Cesium.AnimationViewModel.defaultDateFormatter = function (date) {
const dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 10);
};
Cesium.AnimationViewModel.defaultTimeFormatter = function (time) {
const dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(11, 19);
};
// Timeline 的时间日期格式化
// @ts-expect-error node_modules/@cesium/widgets/Source/Timeline/Timeline.js
Cesium.Timeline.prototype.makeLabel = function (time) {
const dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 19);
};
}
// 默认视图区域
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
75, // 西经
10, // 南纬
140, // 东经
60, // 北纬
);
}

View File

@ -1,34 +0,0 @@
import * as Cesium from 'cesium';
const provider = new Cesium.UrlTemplateImageryProvider({
maximumLevel: 18,
minimumLevel: 3,
url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
});
export function configureMapTile(viewer: Cesium.Viewer) {
if (viewer.baseLayerPicker) {
// 如果有底图选择器
const customLayerViewModel = new Cesium.ProviderViewModel({
category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等
creationFunction() {
return provider;
},
iconUrl: 'gaodeImage.png',
name: '高德地图',
tooltip: '高德地图',
});
// 设置高德地图为默认图层
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel);
const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
if (!selectedViewModel) {
console.error('未找到默认底图');
return;
}
viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel;
} else {
// 如果没有底图选择器
viewer.imageryLayers.removeAll();
viewer.imageryLayers.addImageryProvider(provider);
}
}

View File

@ -1,20 +0,0 @@
import * as Cesium from 'cesium';
const = 2 * 60 * 60; // 2小时
export function configureTimeLine(viewer: Cesium.Viewer, totalSeconds = ) {
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate());
// 设置时钟范围
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 30; // 30倍速播放
// 设置时间轴范围
viewer.timeline.zoomTo(start, stop);
viewer.clock.shouldAnimate = true;
}

View File

@ -1,148 +0,0 @@
import * as Cesium from 'cesium';
import { VIEWER_OPTIONS_FN } from '../helper/_VIEWER_OPTIONS';
import { configureCesium } from '../helper/configureCesium';
import { configureTimeLine } from '../helper/configureTimeLine';
// Cesium Ion Token 和全局配置,暂时保留在此处,也可考虑移至更全局的初始化位置
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_ION_TOKEN;
Object.assign(globalThis, { Cesium });
configureCesium();
export class HCesiumManager {
viewer: Cesium.Viewer | null = null;
/**
* 初始化 Cesium Viewer。
* @param container - 用于承载 Cesium Viewer 的 DOM 元素或其 ID。
* @returns 返回创建的 Viewer 实例,如果失败则返回 null。
*/
init(container: ConstructorParameters<typeof Cesium.Viewer>[0]): Cesium.Viewer | null {
if (this.viewer) {
console.warn('Cesium Viewer 已初始化,请先销毁再重新初始化。');
return this.viewer;
}
try {
this.viewer = new Cesium.Viewer(container, VIEWER_OPTIONS_FN());
if ($__DEV__) Object.assign(globalThis, { viewer: this.viewer });
configureTimeLine(this.viewer);
// 可以在这里添加其他 Viewer 级别的配置
this.viewer.scene.debugShowFramesPerSecond = true; // 例如 FPS 显示
console.log('Cesium Viewer 初始化成功。');
return this.viewer;
} catch (error) {
console.error('Cesium Viewer 初始化失败:', error);
this.viewer = null;
return null;
}
}
/**
* 销毁 Cesium Viewer 实例。
*/
destroy(): void {
if (this.viewer && !this.viewer.isDestroyed()) {
try {
// 在销毁 Viewer 前,确保所有通过此 Manager 添加的实体或数据源已被移除
// (或者依赖各个子 Manager 在销毁前自行清理)
// this.viewer.entities.removeAll(); // 谨慎使用,可能会移除不应移除的实体
// this.viewer.dataSources.removeAll(true); // 谨慎使用
this.viewer.destroy();
console.log('Cesium Viewer 已销毁。');
} catch (error) {
console.error('销毁 Cesium Viewer 时出错:', error);
} finally {
this.viewer = null;
}
} else {
// console.log('Cesium Viewer 未初始化或已被销毁,无需再次销毁。');
this.viewer = null; // 确保 viewer 为 null
}
}
/**
* 向 Viewer 添加一个实体。
* @param entity - 要添加的 Cesium 实体。
* @returns 返回添加的实体,如果 Viewer 未初始化则返回 null。
*/
addEntity(entity: Cesium.Entity): Cesium.Entity | null {
console.debug(`[HCesiumManager] 添加实体: ${entity.name}`);
if (!this.viewer) {
console.error('Viewer 未初始化,无法添加实体。');
return null;
}
try {
return this.viewer.entities.add(entity);
} catch (error) {
console.error('添加实体时出错:', error, entity);
return null;
}
}
/**
* 根据 ID 从 Viewer 移除一个实体。
* @param entityId - 要移除的实体的 ID。
* @returns 如果成功移除则返回 true否则返回 false。
*/
removeEntityById(entityId: string): boolean {
if (!this.viewer) {
console.error('Viewer 未初始化,无法移除实体。');
return false;
}
try {
return this.viewer.entities.removeById(entityId);
} catch (error) {
console.error(`移除 ID 为 "${entityId}" 的实体时出错:`, error);
return false;
}
}
/**
* 根据实体对象从 Viewer 移除一个实体。
* @param entity - 要移除的实体对象。
* @returns 如果成功移除则返回 true否则返回 false。
*/
removeEntity(entity: Cesium.Entity): boolean {
if (!this.viewer) {
console.error('Viewer 未初始化,无法移除实体。');
return false;
}
try {
return this.viewer.entities.remove(entity);
} catch (error) {
console.error('移除实体时出错:', error, entity);
return false;
}
}
/**
* 根据 ID 获取 Viewer 中的实体。
* @param entityId - 要获取的实体的 ID。
* @returns 返回找到的实体,如果未找到或 Viewer 未初始化则返回 undefined。
*/
getEntityById(entityId: string): Cesium.Entity | undefined {
if (!this.viewer) {
console.error('Viewer 未初始化,无法获取实体。');
return undefined;
}
try {
return this.viewer.entities.getById(entityId);
} catch (error) {
console.error(`获取 ID 为 "${entityId}" 的实体时出错:`, error);
return undefined;
}
}
/**
* 获取当前的 Cesium Viewer 实例。
* @returns 返回 Viewer 实例,如果未初始化则返回 null。
*/
getViewer(): Cesium.Viewer | null {
return this.viewer;
}
}

View File

@ -1,22 +0,0 @@
export interface I站点 {
height?: number; // 可选高度默认为0
id: string; // 站点的唯一标识符
latitude: number;
longitude: number;
name: string;
pixelSize?: number; // 点的可选像素大小
}
// 卫星选项接口
export interface I卫星 {
id: string; // 卫星的唯一标识符
orbitDurationSeconds?: number; // 轨道显示时长
showOrbit: boolean; // 是否显示完整轨道线
showCoverage: boolean; // 控制覆盖范围显示 (必填)
timeStepSeconds?: number; // 轨道计算步长(秒),默认为 30
tle: string; // 包含卫星名称和两行 TLE 数据的字符串,格式如下:
// NAME
// TLE1
// TLE2
showPath: boolean; // 控制是否显示路径/轨迹 (必填)
}

View File

@ -1,315 +0,0 @@
import * as Cesium from 'cesium';
import type { HCesiumManager } from './HCesiumManager';
import type { I卫星 } from './HCesiumManager.types';
import { type OrbitCalculationResult, SatelliteCalculator } from '../calculators/SatelliteCalculator';
interface ManagedSatelliteEntities {
coverageEntity?: Cesium.Entity;
mainEntity: Cesium.Entity;
orbitEntity?: Cesium.Entity;
}
export class HCesiumSatelliteManager {
private viewerManager: HCesiumManager;
private calculator: SatelliteCalculator;
// 用于存储当前由此管理器管理的卫星相关实体的 Map
private currentSatelliteEntities: Map<string, ManagedSatelliteEntities> = new Map();
constructor(viewerManager: HCesiumManager, calculator: SatelliteCalculator) {
this.viewerManager = viewerManager;
this.calculator = calculator;
}
/**
* 向视图中添加或更新卫星实体及其相关元素(轨道、覆盖范围)。
* 注意:当前实现仅添加,如果已存在则警告并返回现有实体。
*/
addOrUpdateSatellite(options: I卫星): undefined {
const existingEntry = this.currentSatelliteEntities.get(options.id);
const { id, tle, showOrbit, showCoverage } = options;
// --- 如果实体已存在,直接返回,不处理更新 ---
if (existingEntry) {
console.warn(`ID 为 "${options.id}" 的卫星实体已存在,跳过添加。`);
return;
}
// --- 解析 TLE 和计算轨道 ---
const satrec = this.calculator.parseTle(tle, id);
if (!satrec) {
return undefined; // 解析失败,已打印错误
}
const viewer = this.viewerManager.getViewer();
if (!viewer) {
console.error('Viewer 未初始化,无法计算轨道。');
return undefined;
}
const startTime = viewer.clock.currentTime; // 使用当前 viewer 的时间作为起点
const orbitResult: null | OrbitCalculationResult = this.calculator.calculateOrbit(satrec, startTime, options, id);
if (!orbitResult) {
console.error(`计算卫星 ${id} 的轨道失败。`);
return undefined;
}
const { sampledPositionProperty, orbitPositions } = orbitResult;
// --- 计算结束 ---
// --- 从 TLE 提取名称 ---
const tleLines = tle.trim().split('\n') as [string, string, string];
const name = tleLines[0].trim();
// --- 提取结束 ---
const randomBaseColor = Cesium.Color.fromRandom({ alpha: 1 }); // 确保 alpha 为 1
// 创建卫星实体
const satelliteEntity = this._createSatelliteEntity(id, name, sampledPositionProperty, randomBaseColor, options);
// --- 创建覆盖范围实体 (如果需要) ---
let coverageEntity: Cesium.Entity | null | undefined;
if (showCoverage) {
coverageEntity = this._createCoverageEntity(id, name, sampledPositionProperty, randomBaseColor);
}
// --- 创建结束 ---
// 添加完整轨道线(如果需要)
let orbitEntity: Cesium.Entity | undefined;
if (showOrbit && orbitPositions.length > 1) {
orbitEntity = this._createOrbitEntity(id, name, orbitPositions, randomBaseColor);
}
// --- 创建结束 ---
// --- 添加到 Viewer ---
const addedMainEntity = this.viewerManager.addEntity(satelliteEntity);
if (!addedMainEntity) {
console.error(`通过 ViewerManager 添加卫星主体 ID 为 "${id}" 的实体失败。`);
return undefined; // 主实体添加失败则中止
}
let addedCoverageEntity: Cesium.Entity | null = null;
if (coverageEntity) {
addedCoverageEntity = this.viewerManager.addEntity(coverageEntity);
if (!addedCoverageEntity) {
console.warn(`通过 ViewerManager 添加卫星覆盖范围 ID 为 "${coverageEntity.id}" 的实体失败。`);
}
}
let addedOrbitEntity: Cesium.Entity | null = null;
if (orbitEntity) {
addedOrbitEntity = this.viewerManager.addEntity(orbitEntity);
if (!addedOrbitEntity) {
console.warn(`通过 ViewerManager 添加卫星轨道 ID 为 "${orbitEntity.id}" 的实体失败。`);
}
}
// --- 添加结束 ---
// 存储实体引用
this.currentSatelliteEntities.set(id, {
mainEntity: addedMainEntity, // 存储实际添加成功的实体
orbitEntity: addedOrbitEntity ?? undefined,
coverageEntity: addedCoverageEntity ?? undefined,
});
}
/**
* 创建卫星主体实体。
*/
private _createSatelliteEntity(
id: string,
name: string,
sampledPositionProperty: Cesium.SampledPositionProperty,
randomBaseColor: Cesium.Color,
options: I卫星,
): Cesium.Entity {
// 动态轨迹路径 (Path) - 注意:这与完整轨道线 (Polyline) 不同
const path: Cesium.PathGraphics.ConstructorOptions = {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.15,
color: randomBaseColor,
}),
width: 2,
leadTime: (options.orbitDurationSeconds ?? 3600 * 2) / 2, // 默认1小时
trailTime: (options.orbitDurationSeconds ?? 3600 * 2) / 2, // 默认1小时
};
return new Cesium.Entity({
id,
name,
path: options.showPath ? path : undefined, // 根据 options.showPath 控制是否显示路径
position: sampledPositionProperty, // 使用计算好的位置属性
orientation: new Cesium.VelocityOrientationProperty(sampledPositionProperty),
point: {
pixelSize: 8,
color: randomBaseColor,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
},
label: {
text: name,
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -10),
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
},
});
}
/**
* 创建卫星覆盖范围实体。
*/
private _createCoverageEntity(
satelliteId: string,
satelliteName: string,
positionProperty: Cesium.SampledPositionProperty,
baseColor: Cesium.Color,
): Cesium.Entity | null {
// 使用 CallbackProperty 动态计算星下点位置
const subsatellitePosition = new Cesium.CallbackPositionProperty( // 使用 CallbackPositionProperty
(time, result) => {
const satelliteCartesian = positionProperty.getValue(time, result);
if (!satelliteCartesian) return;
const satelliteCartographic = Cesium.Cartographic.fromCartesian(satelliteCartesian);
if (!satelliteCartographic) return;
const subsatelliteCartographic = new Cesium.Cartographic(
satelliteCartographic.longitude,
satelliteCartographic.latitude,
0,
);
return Cesium.Cartographic.toCartesian(subsatelliteCartographic, Cesium.Ellipsoid.WGS84, result);
},
false,
Cesium.ReferenceFrame.FIXED,
);
// 使用 CallbackProperty 动态计算覆盖半径 (基于高度的简单估算)
const coverageRadius = new Cesium.CallbackProperty((time) => {
const satelliteCartesian = positionProperty.getValue(time);
if (!satelliteCartesian) return 100_000; // 默认半径
const satelliteCartographic = Cesium.Cartographic.fromCartesian(satelliteCartesian);
if (!satelliteCartographic) return 100_000;
const altitude = satelliteCartographic.height;
// 简化的估算
const calculatedRadius = altitude * 0.8;
return Math.max(calculatedRadius, 50_000); // 最小半径 50km
}, false);
return new Cesium.Entity({
id: `${satelliteId}-coverage`,
name: `${satelliteName} 覆盖范围`,
position: subsatellitePosition,
ellipse: {
semiMajorAxis: coverageRadius,
semiMinorAxis: coverageRadius,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
granularity: Cesium.Math.toRadians(1),
outlineWidth: 1,
material: baseColor.withAlpha(0.2),
outline: true,
outlineColor: baseColor.withAlpha(0.8),
},
});
}
/**
* 创建卫星轨道实体。
*/
private _createOrbitEntity(
satelliteId: string,
satelliteName: string,
orbitPositions: Cesium.Cartesian3[],
baseColor: Cesium.Color,
): Cesium.Entity {
return new Cesium.Entity({
id: `${satelliteId}-orbit`,
name: `${satelliteName} 轨道`,
polyline: {
positions: orbitPositions,
width: 1,
material: new Cesium.PolylineDashMaterialProperty({
color: baseColor.withAlpha(0.5),
dashLength: 16,
}),
clampToGround: false, // 轨道通常不贴地
},
});
}
/**
* 从视图中移除指定的卫星实体及其相关元素 (通过 ID)。
* @param entityId - 要移除的卫星实体的 ID。
* @returns 如果成功移除所有相关实体则返回 true否则返回 false。
*/
removeSatellite(entityId: string): boolean {
const satelliteData = this.currentSatelliteEntities.get(entityId);
if (!satelliteData) {
// console.warn(`未在 SatelliteManager 中找到 ID 为 "${entityId}" 的实体,无法移除。`);
return false; // 不在管理范围内
}
let allRemoved = true;
// 移除主体
if (!this.viewerManager.removeEntity(satelliteData.mainEntity)) {
console.warn(`尝试通过 ViewerManager 移除卫星主体 ID 为 "${entityId}" 的实体失败。`);
allRemoved = false;
}
// 移除轨道
if (satelliteData.orbitEntity && !this.viewerManager.removeEntity(satelliteData.orbitEntity)) {
console.warn(`尝试通过 ViewerManager 移除卫星轨道 ID 为 "${satelliteData.orbitEntity.id}" 的实体失败。`);
allRemoved = false;
}
// 移除覆盖范围
if (satelliteData.coverageEntity && !this.viewerManager.removeEntity(satelliteData.coverageEntity)) {
console.warn(`尝试通过 ViewerManager 移除卫星覆盖范围 ID 为 "${satelliteData.coverageEntity.id}" 的实体失败。`);
allRemoved = false;
}
// 从 Map 中删除,无论移除是否完全成功,以避免状态不一致
this.currentSatelliteEntities.delete(entityId);
return allRemoved;
}
/**
* 清除所有由此管理器管理的卫星实体及其相关元素。
*/
clearAllSatellites(): void {
const idsToRemove = [...this.currentSatelliteEntities.keys()];
let removalFailed = false;
for (const id of idsToRemove) {
if (!this.removeSatellite(id)) {
removalFailed = true;
}
}
if (removalFailed) {
console.warn('清除部分卫星实体时遇到问题。');
}
// 确保最终清空 Map
this.currentSatelliteEntities.clear();
}
/**
* 获取当前管理的卫星实体 Map。
* @returns 返回包含当前卫星实体及其关联实体的 Map。
*/
getCurrentSatelliteEntities(): Map<string, ManagedSatelliteEntities> {
return this.currentSatelliteEntities;
}
/**
* 销毁管理器,清理资源。
*/
destroy(): void {
this.clearAllSatellites();
// console.log('SatelliteManager 已销毁。');
}
}

Some files were not shown because too many files have changed in this diff Show More