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
249 changed files with 6431 additions and 333396 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=

82
.gitattributes vendored
View File

@ -1,82 +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
*.spline 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 <<<

View File

@ -1,21 +0,0 @@
### .gitattributes
如果你先推送了一个.jpg文件然后再推送包含.gitattributes文件的更新Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效你可以通过以下步骤重新应用设置
```bash
1. 删除本地缓存的.jpg文件git rm --cached <file>.jpg
2. 重新添加该文件git add <file>.jpg
3. 再次提交并推送git commit -m "Apply .gitattributes changes" && git push
```
这样Git将按照.gitattributes中的新规则处理该文件。
要验证.gitattributes文件中的二进制配置是否生效可以使用以下方法
```bash
1. 推送后验证:先按照之前步骤重新添加并推送.jpg文件。
2. 查看差异diff执行 git diff检查文件是否有文本形式的改动。如果是二进制文件Git不会显示具体内容的差异。
3. Git日志验证执行 git log -p <file>.jpg 查看提交的改动记录,确认文件未受行尾或编码处理的影响。
```
如果以上测试显示该文件未发生不必要的改动,说明.gitattributes配置已生效。

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,60 +0,0 @@
---
description:
globs:
alwaysApply: true
---
# GitHub Copilot Instructions
本文件定义了项目的代码生成规范GitHub Copilot 和其他 AI 助手应遵循这些指令。
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>`.

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@b4a2caa64aca72f8aeada59d0df3181a12df8268
- 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,57 +0,0 @@
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
workflow_dispatch:
inputs:
run_cleanup:
description: '是否运行 Surge 清理 Job'
required: false
type: boolean
default: true
jobs:
surge:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.surge_deploy.outputs.url }}
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
- 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@b4a2caa64aca72f8aeada59d0df3181a12df8268
playwright:
needs: surge
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.53.2-noble
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
# - name: 📥 安装 Playwright 浏览器
# run: pnpm exec playwright install --with-deps
- name: ▶️ 运行 Playwright 测试
run: npx playwright test
env:
BASE_URL: ${{ needs.surge.outputs.url }}
cleanup_surge:
runs-on: ubuntu-latest
needs: [surge, playwright]
if: github.event_name == 'push' || github.event.inputs.run_cleanup == true
steps:
- 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@b4a2caa64aca72f8aeada59d0df3181a12df8268
- 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,6 +0,0 @@
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
echo "当前提交的文件是:$1"
# npx --no -- commitlint --edit "$1"
pnpm exec 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.15.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"
]
}

40
.vscode/settings.json vendored
View File

@ -1,35 +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"
},
"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,15 +9,25 @@ 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
```
## Tips
### 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
```
## Links
- https://github.com/hyoban-template/shadcn-vue-unocss-starter
- [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/)
@ -29,12 +39,7 @@ pnpm run dev
---
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
- https://biomejs.dev/zh-cn/internals/language-support/
- https://github.dev/antfu-collective/vitesse/
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
---
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)
+ https://www.npmjs.com/package/npkill
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)

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,9 +0,0 @@
import type { UserConfig } from '@commitlint/types';
const Configuration: UserConfig = {
extends: ['@commitlint/config-conventional'],
formatter: '@commitlint/format',
};
export default Configuration;

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,128 +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',
'import-x/no-duplicates': 'off',
'import-x/named': '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',
'perfectionist/sort-enums': 'off',
'perfectionist/sort-union-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,166 +1,105 @@
{
"packageManager": "pnpm@10.12.4",
"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.16",
"@formkit/auto-animate": "^0.8.2",
"@intlify/unplugin-vue-i18n": "^6.0.8",
"@pinia/colada": "^0.17.1",
"@primeuix/themes": "^1.1.2",
"@splinetool/runtime": "^1.10.22",
"@types/p5": "^1.7.6",
"@types/sortablejs": "^1.15.8",
"@unhead/vue": "^2.0.12",
"@alova/adapter-axios": "^2.0.7",
"@unhead/vue": "^1.11.7",
"@vant/use": "^1.6.0",
"@vueuse/core": "^13.5.0",
"alova": "^3.3.4",
"ant-design-vue": "~4.2.6",
"axios": "^1.10.0",
"cesium": "^1.131.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.1",
"jsencrypt": "^3.3.2",
"lucide-vue-next": "^0.525.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"p5": "^2.0.3",
"page-stack-vue3": "^2.5.6",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"plotly.js-dist-min": "^3.0.1",
"primeicons": "^7.0.0",
"primelocale": "^2.1.4",
"primevue": "^4.3.5",
"radash": "^12.1.1",
"radix-vue": "^1.9.17",
"reka-ui": "^2.3.2",
"satellite.js": "^6.0.1",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.3.1",
"tdesign-icons-vue-next": "^0.3.6",
"three": "^0.178.0",
"pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.1",
"radash": "^12.1.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.20",
"vue": "^3.5.17",
"vue-data-ui": "^2.12.7",
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^11.1.9",
"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.1"
"vue-router": "^4.4.5"
},
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@eslint/compat": "^1.3.1",
"@faker-js/faker": "^9.9.0",
"@iconify-json/carbon": "^1.2.10",
"@iconify-json/logos": "^1.2.4",
"@iconify-json/mdi": "^1.2.3",
"@iconify/utils": "^2.3.0",
"@playwright/test": "^1.53.2",
"@primevue/auto-import-resolver": "^4.3.5",
"@tsconfig/node22": "^22.0.2",
"@types/archiver": "^6.0.3",
"@types/mockjs": "^1.0.10",
"@types/node": "^22.16.3",
"@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.178.1",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue-jsx": "^5.0.1",
"@vitest/eslint-plugin": "^1.3.4",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.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.3",
"eslint": "^9.30.1",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-oxlint": "^1.6.0",
"eslint-plugin-perfectionist": "^4.15.0",
"eslint-plugin-unicorn": "^59.0.1",
"eslint-plugin-vue": "^10.3.0",
"husky": "^9.1.7",
"less": "^4.3.0",
"lint-staged": "^16.1.2",
"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",
"naive-ui": "^2.42.0",
"npm-run-all2": "^8.0.4",
"oxlint": "^1.6.0",
"prettier": "3.6.2",
"typescript": "~5.8.3",
"unocss": "66.3.3",
"unocss-preset-animations": "^1.2.1",
"unocss-preset-chinese": "^0.3.3",
"unocss-preset-shadcn": "^0.5.0",
"unplugin-auto-import": "^19.3.0",
"unplugin-icons": "^22.1.0",
"unplugin-vue-components": "^28.8.0",
"unplugin-vue-macros": "^2.14.5",
"unplugin-vue-markdown": "^29.1.0",
"unplugin-vue-router": "^0.14.0",
"vfonts": "^0.0.3",
"vite": "^7.0.2",
"vite-plugin-checker": "^0.10.0",
"vite-plugin-fake-server": "^2.2.0",
"vite-plugin-image-tools": "^2.0.2",
"vite-plugin-purgecss-updated-v5": "^1.2.6",
"vite-plugin-singlefile": "^2.3.0",
"vite-plugin-static-copy": "^3.1.0",
"vite-plugin-vue-devtools": "^7.7.7",
"vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-vue-meta-layouts": "^0.5.1",
"vite-plugin-webfont-dl": "^3.10.5",
"vue-component-type-helpers": "^3.0.1",
"vue-tsc": "^3.0.1"
"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 */
/* 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: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* 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,
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/',
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
});

12012
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,41 +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>
<n-config-provider preflight-style-disabled>
<a-config-provider :theme="themeConfig">
<RouterView />
</a-config-provider>
</n-config-provider>
<DynamicDialog /> <ConfirmDialog /> <Toast />
<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 />
</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

@ -1,58 +0,0 @@
export enum E_选项卡菜单 {
= '幅度单位列表',
= '幅度菜单列表',
= '跨度菜单列表',
= '频率单位列表',
= '频率菜单列表',
}
export enum E_幅度菜单列表的项目 {
RefLevel = 'Ref Level',
// Attenuation = 'Attenuation', // 如果衰减也需要类似交互,则取消注释
}
export enum E_幅度单位 {
dBm = 'dBm',
mV = 'mV',
uA = 'µA', // 使用 uA 表示微安
uV = 'µV', // 使用 uV 表示微伏
}
export enum E_跨度菜单列表的项目 {
FullSpan = 'Full Span',
LastSpan = 'Last Span',
ZeroSpan = 'Zero Span',
}
/* 值是在屏幕上显示的值。 */
export enum E_选项卡菜单Freq菜单列表的项目 {
CenterFreq = 'Center',
StartFreq = 'Start',
StopFreq = 'Stop',
// CFStep = 'CFStep',
// FreqOffset = 'FreqOffset',
// SingnalTrack = 'SingnalTrack',
// ScaleType = 'ScaleType',
}
export enum E_Freq单位 {
GHz = 'GHz',
Hz = 'Hz',
kHz = 'kHz',
MHz = 'MHz',
}
export enum E_数字键盘按键 {
Dot = '.',
Num0 = 0,
Num1 = 1,
Num2 = 2,
Num3 = 3,
Num4 = 4,
Num5 = 5,
Num6 = 6,
Num7 = 7,
Num8 = 8,
Num9 = 9,
PlusMinus = '+/-',
}

View File

@ -1,13 +0,0 @@
@font-face {
font-family: 'Helvetica Custom';
font-style: normal;
font-weight: 400; /* 400 通常代表 'normal' 或 'regular' */
src: url('fonts/Helvetica-Light-05.eot');
src:
url('fonts/Helvetica-Light-05.eot?#iefix') format('embedded-opentype'),
url('fonts/Helvetica-Light-05.woff2') format('woff2'),
url('fonts/Helvetica-Light-05.woff') format('woff'),
url('fonts/Helvetica-Light-05.ttf') format('truetype'),
url('fonts/Helvetica-Light-05.svg#Helvetica') format('svg');
font-display: swap; /* 推荐 */
}

View File

@ -1,805 +0,0 @@
<!-- 频谱仪 -->
<script setup lang="ts">
import { $enum } from 'ts-enum-util';
import {
E_Freq单位,
E_幅度单位,
E_幅度菜单列表的项目,
E_数字键盘按键,
E_跨度菜单列表的项目,
E_选项卡菜单,
E_选项卡菜单Freq菜单列表的项目,
} from './CONST';
import 设备照片 from './pin-pu-yi-bg.png';
const state = reactive({
选项卡当前显示: E_选项卡菜单.频率菜单列表,
频谱仪状态: {
频率输入状态: {
选中的频率菜单列表的项目: null as E_选项卡菜单Freq菜单列表的项目 | null,
输入的值临时输入字符串: null as null | string, // 用于暂存用户输入
},
幅度输入状态: {
选中的幅度菜单列表的项目: null as E_幅度菜单列表的项目 | null,
输入的值临时输入字符串: null as null | string, // 用于暂存用户输入
refLevel: {
value: 0,
unit: E_幅度单位.dBm,
},
// attenuation: { value: 0, unit: E_幅度单位.dB }, // 如果需要衰减值
},
center: {
value: 0,
unit: E_Freq单位.Hz,
},
start: {
value: 0,
unit: E_Freq单位.Hz,
},
stop: {
value: 0,
unit: E_Freq单位.Hz,
},
span: {
currentSpan: {
start: 0,
stop: 0,
unit: E_Freq单位.Hz,
},
lastSpan: {
start: 0,
stop: 0,
unit: E_Freq单位.Hz,
},
fullSpan: {
start: 0,
stop: 3,
unit: E_Freq单位.GHz,
},
zeroSpan: false,
},
},
});
const 选项卡标题 = computed(() =>
$enum.mapValue(state.选项卡当前显示).with({
[E_选项卡菜单.频率菜单列表]: 'Freq/Channel',
[E_选项卡菜单.频率单位列表]: '',
[E_选项卡菜单.跨度菜单列表]: 'Span',
[E_选项卡菜单.幅度菜单列表]: 'Amplitude',
[E_选项卡菜单.幅度单位列表]: '',
}),
);
function 执行点击屏幕右边的按钮(按钮序号: number) {
console.group('🔘 点击屏幕右边的按钮', {
按钮序号,
选项卡当前显示: state.选项卡当前显示,
});
$enum.visitValue(state.选项卡当前显示).with({
[E_选项卡菜单.频率菜单列表]: () => {
switch (按钮序号) {
case 1: {
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.CenterFreq;
console.debug('设置 Freq 输入状态为 CenterFreq');
break;
}
case 2: {
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.StartFreq;
console.debug('设置 Freq 输入状态为 StartFreq');
break;
}
case 3: {
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.StopFreq;
console.debug('设置 Freq 输入状态为 StopFreq');
break;
}
default: {
console.debug('频率菜单列表 - 这个按键还没有实现');
break;
}
}
},
[E_选项卡菜单.频率单位列表]: () => {
let selectedUnit: E_Freq单位 | null = null;
switch (按钮序号) {
case 1: {
selectedUnit = E_Freq单位.GHz;
break;
}
case 2: {
selectedUnit = E_Freq单位.MHz;
break;
}
case 3: {
selectedUnit = E_Freq单位.kHz;
break;
}
case 4: {
selectedUnit = E_Freq单位.Hz;
break;
}
default: {
console.warn('无效的频率单位按钮序号:', 按钮序号);
console.groupEnd();
return;
}
}
const tempInputString = state.频谱仪状态.频率输入状态.输入的值临时输入字符串;
const selectedFreqItem = state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目;
// 必须有临时输入值和选中的 Freq 项目才能确认
if (!tempInputString || !selectedFreqItem) {
console.warn('频率输入未完成或未选择项目,无法确认单位。');
console.groupEnd();
return;
}
const inputValue = Number.parseFloat(tempInputString);
// 校验解析结果
if (Number.isNaN(inputValue)) {
console.warn('无法将临时输入解析为有效数字 (频率):', tempInputString);
console.groupEnd();
return;
}
console.debug('确认频率单位:', selectedUnit, '值为:', inputValue);
switch (selectedFreqItem) {
case E_选项卡菜单Freq菜单列表的项目.CenterFreq: {
state.频谱仪状态.center.value = inputValue;
state.频谱仪状态.center.unit = selectedUnit;
console.debug('更新 CenterFreq:', state.频谱仪状态.center);
break;
}
case E_选项卡菜单Freq菜单列表的项目.StartFreq: {
state.频谱仪状态.start.value = inputValue;
state.频谱仪状态.start.unit = selectedUnit;
console.debug('更新 StartFreq:', state.频谱仪状态.start);
break;
}
case E_选项卡菜单Freq菜单列表的项目.StopFreq: {
state.频谱仪状态.stop.value = inputValue;
state.频谱仪状态.stop.unit = selectedUnit;
console.debug('更新 StopFreq:', state.频谱仪状态.stop);
break;
}
}
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = null;
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 = null;
state.选项卡当前显示 = E_选项卡菜单.频率菜单列表;
console.debug('频率输入状态已重置, 切换回频率菜单列表');
},
[E_选项卡菜单.跨度菜单列表]: () => {
switch (按钮序号) {
case 1: {
// Full Span
state.频谱仪状态.span.lastSpan.start = state.频谱仪状态.start.value;
state.频谱仪状态.span.lastSpan.stop = state.频谱仪状态.stop.value;
state.频谱仪状态.span.lastSpan.unit = state.频谱仪状态.stop.unit;
state.频谱仪状态.start.value = state.频谱仪状态.span.fullSpan.start;
state.频谱仪状态.start.unit = state.频谱仪状态.span.fullSpan.unit;
state.频谱仪状态.stop.value = state.频谱仪状态.span.fullSpan.stop;
state.频谱仪状态.stop.unit = state.频谱仪状态.span.fullSpan.unit;
state.频谱仪状态.center.value = (state.频谱仪状态.start.value + state.频谱仪状态.stop.value) / 2;
state.频谱仪状态.center.unit = state.频谱仪状态.span.fullSpan.unit;
state.频谱仪状态.span.zeroSpan = false;
console.debug('设置为 Full Span');
break;
}
case 2: {
// Zero Span
state.频谱仪状态.span.lastSpan.start = state.频谱仪状态.start.value;
state.频谱仪状态.span.lastSpan.stop = state.频谱仪状态.stop.value;
state.频谱仪状态.span.lastSpan.unit = state.频谱仪状态.stop.unit;
state.频谱仪状态.start.value = state.频谱仪状态.center.value;
state.频谱仪状态.start.unit = state.频谱仪状态.center.unit;
state.频谱仪状态.stop.value = state.频谱仪状态.center.value;
state.频谱仪状态.stop.unit = state.频谱仪状态.center.unit;
state.频谱仪状态.span.zeroSpan = true;
console.debug('设置为 Zero Span');
break;
}
case 3: {
// Last Span
state.频谱仪状态.start.value = state.频谱仪状态.span.lastSpan.start;
state.频谱仪状态.start.unit = state.频谱仪状态.span.lastSpan.unit;
state.频谱仪状态.stop.value = state.频谱仪状态.span.lastSpan.stop;
state.频谱仪状态.stop.unit = state.频谱仪状态.span.lastSpan.unit;
state.频谱仪状态.center.value = (state.频谱仪状态.start.value + state.频谱仪状态.stop.value) / 2;
state.频谱仪状态.center.unit = state.频谱仪状态.span.lastSpan.unit;
state.频谱仪状态.span.zeroSpan = false;
console.debug('恢复到 Last Span');
break;
}
default: {
console.debug('跨度菜单列表 - 这个按键还没有实现');
break;
}
}
},
[E_选项卡菜单.幅度菜单列表]: () => {
switch (按钮序号) {
case 1: {
// Ref Level
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 = E_幅度菜单列表的项目.RefLevel;
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = null;
console.debug('设置 幅度输入状态为 RefLevel');
break;
}
case 2: {
// Attenuation
console.debug('点击了 幅度菜单列表 - Attenuation (功能待实现)');
break;
}
default: {
console.debug('幅度菜单列表 - 这个按键还没有实现');
break;
}
}
},
[E_选项卡菜单.幅度单位列表]: () => {
let selectedUnit: E_幅度单位 | null = null;
switch (按钮序号) {
case 1: {
selectedUnit = E_幅度单位.dBm;
break;
}
case 2: {
selectedUnit = E_幅度单位.mV;
break;
}
case 3: {
selectedUnit = E_幅度单位.uV;
break;
}
case 4: {
selectedUnit = E_幅度单位.uA;
break;
}
default: {
console.warn('无效的幅度单位按钮序号:', 按钮序号);
console.groupEnd();
return;
}
}
const tempInputString = state.频谱仪状态.幅度输入状态.输入的值临时输入字符串;
const selectedAmpItem = state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目;
if (!tempInputString || !selectedAmpItem) {
console.warn('幅度输入未完成或未选择项目,无法确认单位。');
console.groupEnd();
return;
}
const inputValue = Number.parseFloat(tempInputString);
if (Number.isNaN(inputValue)) {
console.warn('无法将临时输入解析为有效数字 (幅度):', tempInputString);
console.groupEnd();
return;
}
console.debug('确认幅度单位:', selectedUnit, '值为:', inputValue);
if (selectedAmpItem === E_幅度菜单列表的项目.RefLevel) {
state.频谱仪状态.幅度输入状态.refLevel.value = inputValue;
state.频谱仪状态.幅度输入状态.refLevel.unit = selectedUnit;
console.debug('更新 RefLevel:', state.频谱仪状态.幅度输入状态.refLevel);
}
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 = null;
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = null;
state.选项卡当前显示 = E_选项卡菜单.幅度菜单列表;
console.debug('幅度输入状态已重置, 切换回幅度菜单列表');
},
});
console.groupEnd();
}
function 执行点击数字按钮(按钮值: E_数字键盘按键) {
console.group('🔢 点击数字按钮', { 按钮值 });
const isFreqInputActive = !!state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目;
const isAmpInputActive = !!state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目;
if (!isFreqInputActive && !isAmpInputActive) {
console.warn('请先选择一个频率或幅度菜单项');
console.groupEnd();
return;
}
let 当前临时输入: string;
let 更新临时输入回调: (newVal: null | string) => void;
let 后续操作菜单: E_选项卡菜单;
if (isFreqInputActive) {
当前临时输入 = state.频谱仪状态.频率输入状态.输入的值临时输入字符串 ?? '';
更新临时输入回调 = (newVal) => {
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 = newVal;
};
后续操作菜单 = E_选项卡菜单.频率单位列表;
} else {
// isAmpInputActive
当前临时输入 = state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 ?? '';
更新临时输入回调 = (newVal) => {
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = newVal;
};
后续操作菜单 = E_选项卡菜单.幅度单位列表;
}
switch (按钮值) {
case E_数字键盘按键.Dot: {
// 只有在当前临时输入不包含小数点时才允许添加
if (当前临时输入.includes('.')) {
console.debug('已存在小数点,忽略本次输入');
} else {
// 如果当前为空,则为 '0.',否则追加 '.'
更新临时输入回调(当前临时输入 ? `${当前临时输入}.` : '0.');
console.debug(
'输入小数点,临时字符串:',
isFreqInputActive
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
);
}
break;
}
case E_数字键盘按键.Num0:
case E_数字键盘按键.Num1:
case E_数字键盘按键.Num2:
case E_数字键盘按键.Num3:
case E_数字键盘按键.Num4:
case E_数字键盘按键.Num5:
case E_数字键盘按键.Num6:
case E_数字键盘按键.Num7:
case E_数字键盘按键.Num8:
case E_数字键盘按键.Num9: {
if (当前临时输入 === '0') {
更新临时输入回调(String(按钮值));
} else if (当前临时输入 === '-0') {
更新临时输入回调(`-${String(按钮值)}`);
} else {
更新临时输入回调(当前临时输入 + String(按钮值));
}
console.debug(
'输入数字,临时字符串:',
isFreqInputActive
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
);
break;
}
case E_数字键盘按键.PlusMinus: {
if (当前临时输入.startsWith('-')) {
更新临时输入回调(当前临时输入.slice(1));
} else if (当前临时输入 && 当前临时输入 !== '0' && 当前临时输入 !== '0.') {
更新临时输入回调(`-${当前临时输入}`);
} else if (当前临时输入 === '' || 当前临时输入 === '0' || 当前临时输入 === '0.') {
更新临时输入回调('-');
} else {
console.debug('无法切换正负号');
}
console.debug(
'切换正负号,临时字符串:',
isFreqInputActive
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
);
break;
}
default: {
console.warn('未知的数字键盘按键:', 按钮值);
}
}
state.选项卡当前显示 = 后续操作菜单;
console.groupEnd();
}
const img_modules = import.meta.glob<{ default: string }>('./imgs/*.png', {
eager: true,
});
const 屏幕照片 = ref(img_modules['./imgs/屏幕_0.png']!.default);
watch(
() => state.频谱仪状态.center.value,
() => {
屏幕照片.value = img_modules['./imgs/屏幕_1.png']!.default;
},
);
watch(
() => state.频谱仪状态.start.value,
() => {
屏幕照片.value = img_modules['./imgs/屏幕_2.png']!.default;
},
);
watch(
() => state.频谱仪状态.stop.value,
() => {
屏幕照片.value = img_modules['./imgs/屏幕_3.png']!.default;
},
);
watchEffect(() => {
console.debug(`屏幕照片.value :>> `, 屏幕照片.value);
});
</script>
<template>
<!-- <pre
v-if="$__DEV__"
class="fixed bottom-10 right-10 top-10 z-[9999] overflow-auto overflow-y-auto bg-white p-[10px] text-[12px] text-black"
>{{ { 选项卡菜单7个: $enum(E_选项卡菜单).getValues(), ...state } }}</pre
> -->
<div class="wrp relative">
<img :src="设备照片" alt="频谱仪设备" />
<div
class="absolute left-[640px] top-[174px] flex w-[55px] flex-col gap-y-[4px] overflow-visible text-black"
data-box="屏幕右边的按钮"
>
<!-- 上下还有一个 ESC / RETURN -->
<ul class="flex h-[300px] flex-col justify-around gap-y-[4px] overflow-visible" data-box="屏幕右边按钮列表">
<template v-for="n in 7" :key="`按钮${n}`">
<li
class="lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-visible rounded-[2px] text-[12px]"
@click="执行点击屏幕右边的按钮(n)"
>
<button class="ppy-btn !h-full !w-full"></button>
</li>
</template>
</ul>
</div>
<div class="absolute left-[195px] top-[140px] h-[350px] w-[430px] bg-black" data-box="屏幕">
<img :src="屏幕照片" alt="频谱仪屏幕" class="absolute left-0 top-0 !h-[330px] !w-[340px]" />
<!-- -->
<!-- 屏幕左上角 Ref Level -->
<div class="absolute left-[5px] top-[20px] text-left text-[10px] text-white" data-box="屏幕左上角RefLevel">
<span>
Ref {{ state.频谱仪状态.幅度输入状态.refLevel.value }}
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
</span>
</div>
<div class="absolute bottom-0 left-0 right-[85px] flex justify-between text-white" data-box="下面左Start 右Stop">
<div class="flex-1 text-left">
<span>Start </span>
<span>{{ state.频谱仪状态.start.value }}</span>
<span>{{ state.频谱仪状态.start.unit }}</span>
</div>
<div class="flex-1 text-right">
<span>Stop </span>
<span>{{ state.频谱仪状态.stop.value }}</span>
<span>{{ state.频谱仪状态.stop.unit }}</span>
</div>
</div>
<div
v-if="
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 ||
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目
"
class="lh-[1.2] absolute left-[40px] top-[140px] flex flex-col gap-y-[0px] bg-gray-500/100 text-white"
data-box="正在输入的值"
>
<!-- 标题:显示当前选中的输入项 -->
<div v-if="state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目">
{{ state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 }}
</div>
<div v-else-if="state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目">
{{ state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 }}
</div>
<!-- 值:优先显示临时输入字符串,如果为空,则显示已确认的值 -->
<div>
<template
v-if="
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 &&
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 !== null
"
>
{{ state.频谱仪状态.频率输入状态.输入的值临时输入字符串 }}
</template>
<template
v-else-if="
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 &&
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 !== null
"
>
{{ state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 }}
</template>
<!-- 已确认的频率值 -->
<template
v-else-if="
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.CenterFreq
"
>
{{ state.频谱仪状态.center.value }}
{{ state.频谱仪状态.center.unit }}
</template>
<template
v-else-if="
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.StartFreq
"
>
{{ state.频谱仪状态.start.value }} {{ state.频谱仪状态.start.unit }}
</template>
<template
v-else-if="
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.StopFreq
"
>
{{ state.频谱仪状态.stop.value }} {{ state.频谱仪状态.stop.unit }}
</template>
<!-- 已确认的幅度值 -->
<template
v-else-if="state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 === E_幅度菜单列表的项目.RefLevel"
>
{{ state.频谱仪状态.幅度输入状态.refLevel.value }}
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
</template>
<template v-else>
0.000000000
<!-- 默认或回退显示 -->
</template>
</div>
</div>
<div
class="absolute bottom-[15px] right-0 top-0 flex w-[80px] flex-col gap-y-[4px] text-black"
data-box="屏幕右侧的选项卡"
>
<div class="menu-item lh-[30px] h-[30px] overflow-hidden bg-[#AFAFAF] text-center" data-box="菜单标题">
{{ 选项卡标题 }}
</div>
<ul
v-if="state.选项卡当前显示 === E_选项卡菜单.频率菜单列表"
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
data-box="频率菜单列表"
>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>Center Freq</span>
{{ state.频谱仪状态.center.value }}
{{ state.频谱仪状态.center.unit }}
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>Start Freq</span>
{{ state.频谱仪状态.start.value }}
{{ state.频谱仪状态.start.unit }}
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>Stop Freq</span>
{{ state.频谱仪状态.stop.value }}
{{ state.频谱仪状态.stop.unit }}
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>#CF Step</span>
<span>0.0000 hz</span>
<span>X</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>#Freq Offset</span>
<span>0.0000 Hz</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>#Singnal Track</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>#Scale Type</span>
</li>
</ul>
<ul
v-if="state.选项卡当前显示 === E_选项卡菜单.频率单位列表"
class="flex flex-1 flex-col justify-around gap-y-[4px]"
data-box="FreqChannel单位列表"
>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>GHz</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>MHz</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>KHz</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>Hz</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
</ul>
<ul
v-if="state.选项卡当前显示 === E_选项卡菜单.跨度菜单列表"
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
data-box="跨度菜单列表"
>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_跨度菜单列表的项目.FullSpan }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_跨度菜单列表的项目.ZeroSpan }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_跨度菜单列表的项目.LastSpan }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
</ul>
<ul
v-if="state.选项卡当前显示 === E_选项卡菜单.幅度菜单列表"
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
data-box="幅度菜单列表"
>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>Ref Level</span>
<span>
{{ state.频谱仪状态.幅度输入状态.refLevel.value }}
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>#Attenuation</span>
<!-- <span>TODO</span> -->
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
</ul>
<ul
v-if="state.选项卡当前显示 === E_选项卡菜单.幅度单位列表"
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
data-box="幅度单位列表"
>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_幅度单位.dBm }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_幅度单位.mV }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_幅度单位.uV }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
<span>{{ E_幅度单位.uA }}</span>
</li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
</ul>
</div>
</div>
<div class="ppy-btns absolute left-[768px] top-[120px] h-[415px] !overflow-visible" data-box="机器右边按键">
<!-- 左上角. 长的 -->
<div class="flex flex-col gap-y-[18px] !overflow-visible">
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.频率菜单列表">
FREQ<br />Channel
</button>
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.跨度菜单列表">
SPAN<br />X Scale
</button>
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.幅度菜单列表">
AMPLITUDE<br />Y Scale
</button>
<button class="ppy-btn !w-[62px] opacity-0">#</button>
<button class="ppy-btn !w-[62px] opacity-0">#</button>
</div>
<div class="mt-[20px] grid grid-cols-3 gap-[16.5px] !overflow-visible" data-box="数字键盘区域">
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num7)">7</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num8)">8</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num9)">9</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num4)">4</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num5)">5</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num6)">6</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num1)">1</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num2)">2</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num3)">3</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num0)">0</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Dot)">.</button>
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.PlusMinus)">+/-</button>
</div>
</div>
</div>
</template>
<style scoped>
@import url('./Helvetica.css');
.wrp {
font-family: 'Helvetica Custom';
}
/* .wrp * {
overflow: hidden;
} */
img[alt='频谱仪设备'] {
width: calc(2554px * 0.5);
max-width: fit-content;
height: calc(1626px * 0.5);
max-height: fit-content;
}
.ppy-btn {
display: flex; /* 使用 flex 居中文本 */
align-items: center;
justify-content: center;
width: 36px;
height: 30px;
padding-left: 2px;
overflow: hidden;
font-size: 10px;
line-height: 1.2;
color: #333; /* 深灰色文字 */
text-align: left;
text-decoration: none;
background-color: #e0e0e0; /* 按钮浅灰色背景 */
border: 1px solid green;
border: 1px solid #b0b0b0; /* 添加边框 */
border-radius: 4px;
box-shadow:
1px 1px 2px rgb(0 0 0 / 20%),
/* 轻微外阴影 */ -1px -1px 2px rgb(255 255 255 / 80%); /* 轻微外高光 */
transition: all 0.1s ease-in-out; /* 平滑过渡效果 */
/* width: 100%; */
/* 宽度占满 grid 单元格 */
/* height: 100%; */
/* 高度占满 grid 单元格 */
/* font-size: 20px; */
/* 调整字体大小 */
/* font-weight: bold; */
/* 字体加粗 */
/* border-radius: 4px; */
/* 按钮圆角 */
}
.ppy-btn:active {
background-color: #d5d5d5;
border-color: #a0a0a0;
box-shadow:
inset 1px 1px 3px rgb(0 0 0 / 30%),
inset -1px -1px 3px rgb(255 255 255 / 60%);
transform: scale(0.98);
}
.menu-item {
/* @apply bg-gray-200/50 p-[2px]; */
font-size: 12px;
/* border: 1px solid white; */
}
[alt='频谱仪设备'] {
opacity: 1;
transition: opacity 0.3s;
}
/* li,
.ppy-btns,
[data-box='屏幕'],
[data-box='菜单标题'] {
box-shadow: 0 0 0 1px rgba(255, 0, 0, 0.5);
} */
</style>

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 KiB

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