435 Commits

Author SHA1 Message Date
9ada32765a vuetify
All checks were successful
/ lint-build-and-check (push) Successful in 5m44s
/ surge (push) Successful in 3m8s
/ playwright (push) Successful in 2m46s
2025-04-20 22:42:41 +08:00
aacca31384 feat: 更新随机图片选择逻辑,支持随机选择2或3
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m52s
/ surge (push) Successful in 2m54s
/ playwright (push) Successful in 1m24s
/ lint-build-and-check (push) Successful in 5m27s
2025-04-20 22:11:51 +08:00
5f9eeddc77 feat: 添加随机图片功能
Some checks failed
/ playwright (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ surge (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
2025-04-20 22:09:08 +08:00
f4e248a2b1 feat: vite-assets 2025-04-20 22:06:49 +08:00
b85df83f8c chore(deps): update dependency @intlify/unplugin-vue-i18n to ^6.0.6
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ surge (push) Successful in 2m59s
/ playwright (push) Successful in 1m18s
/ lint-build-and-check (push) Successful in 5m17s
2025-04-20 12:06:24 +08:00
4433f01b81 fix: 补充 Plotly 嵌套 iframe 的注释和格式
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ lint-build-and-check (push) Successful in 5m22s
/ playwright (push) Failing after 11m36s
/ surge (push) Successful in 3m8s
2025-04-18 14:04:44 +08:00
defc842edf **/**.noformat.json
Some checks failed
/ lint-build-and-check (push) Successful in 5m19s
/ build-and-deploy-to-vercel (push) Successful in 3m16s
/ playwright (push) Failing after 11m34s
/ surge (push) Successful in 2m49s
2025-04-18 13:47:58 +08:00
3daf4b2e21 feat: plotly-locale-zh-cn.js
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m50s
/ lint-build-and-check (push) Successful in 5m52s
/ surge (push) Successful in 2m50s
/ playwright (push) Failing after 11m32s
2025-04-18 12:51:30 +08:00
edcf30a4d8 Iframe-PlotlyJs-Comp.vue
Some checks failed
/ surge (push) Successful in 2m45s
/ build-and-deploy-to-vercel (push) Successful in 2m51s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-04-18 12:47:31 +08:00
1f959b1d1b Iframe-PlotlyJs-Comp.vue 2025-04-18 12:05:53 +08:00
ebed0cf4bf Iframe-PlotlyJs.page.vue
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m55s
/ surge (push) Successful in 2m39s
/ playwright (push) Successful in 1m15s
/ lint-build-and-check (push) Successful in 5m50s
2025-04-17 19:28:47 +08:00
95d5087b5e feat: add Plotly.js usage page
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 5m48s
/ lint-build-and-check (push) Successful in 7m13s
/ surge (push) Successful in 5m5s
/ playwright (push) Failing after 2m27s
2025-04-17 14:57:17 +08:00
784c5ff56d chore: 添加 'unplugin-vue-router' 到优化依赖列表 2025-04-17 13:40:58 +08:00
42094e3571 chore(deps): update dependency @primeuix/themes to ^1.0.3
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 3m18s
/ surge (push) Successful in 2m53s
/ playwright (push) Successful in 1m25s
/ lint-build-and-check (push) Successful in 4m48s
2025-04-16 21:46:04 +08:00
9f82dc6dcb Dom-Draggable.page.vue
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m18s
/ surge (push) Successful in 2m36s
/ playwright (push) Failing after 1m48s
/ lint-build-and-check (push) Successful in 4m43s
2025-04-16 19:20:22 +08:00
b9ae95bfee dd 2025-04-16 18:11:40 +08:00
182d87c773 chore: 将变量名翻译为中文以提高可读性
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m26s
/ surge (push) Successful in 2m50s
/ playwright (push) Successful in 1m56s
/ lint-build-and-check (push) Successful in 4m53s
2025-04-16 14:56:53 +08:00
5766bb828e chore: update satellite.js to version 6.0.0 and improve position validation in calculations
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m54s
/ lint-build-and-check (push) Successful in 4m37s
/ surge (push) Successful in 2m48s
/ playwright (push) Successful in 1m15s
2025-04-15 17:02:52 +08:00
22e5c31f58 SortableJS.page.vue
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 6m3s
/ lint-build-and-check (push) Successful in 7m10s
/ surge (push) Successful in 5m27s
/ playwright (push) Successful in 4m17s
2025-04-15 14:17:43 +08:00
b43b076b9d chore(deps): update lint dependencies to ^0.16.6
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m53s
/ surge (push) Successful in 2m56s
/ playwright (push) Failing after 2m12s
/ lint-build-and-check (push) Successful in 5m27s
2025-04-14 21:49:05 +08:00
d47c02f743 chore(deps): update dependency vite to ^6.2.6
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m55s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
/ surge (push) Has been cancelled
2025-04-14 21:46:01 +08:00
391e4fcb85 chore(deps): update dependency unplugin-vue-components to ^28.5.0
Some checks failed
/ playwright (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ surge (push) Has been cancelled
2025-04-14 21:45:25 +08:00
41b12e297b chore(deps): update yanhao98/composite-actions digest to 4470aa1
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ surge (push) Successful in 2m35s
/ playwright (push) Successful in 1m13s
/ lint-build-and-check (push) Has been cancelled
2025-04-14 15:16:46 +08:00
6fd22d573e chore(deps): update yanhao98/composite-actions digest to 2b8fce6
All checks were successful
/ lint-build-and-check (push) Successful in 4m48s
/ build-and-deploy-to-vercel (push) Successful in 3m8s
/ playwright (push) Successful in 1m16s
/ surge (push) Successful in 2m43s
2025-04-11 19:46:41 +08:00
e9676c120d _星座样本IQ.no-lint.ts
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m50s
/ surge (push) Successful in 2m42s
/ playwright (push) Successful in 1m52s
/ lint-build-and-check (push) Successful in 4m52s
2025-04-11 17:52:28 +08:00
09b1e481e5 waterfallwidget.js.grok-3-beta.md
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m40s
/ surge (push) Successful in 2m42s
/ playwright (push) Successful in 1m11s
/ lint-build-and-check (push) Successful in 4m46s
2025-04-11 15:46:41 +08:00
82bbcc8ac6 waterfallwidget.js.暂存.md
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m46s
/ playwright (push) Successful in 1m13s
/ surge (push) Successful in 2m40s
/ lint-build-and-check (push) Successful in 5m12s
2025-04-11 15:19:17 +08:00
a0fe6da933 playwright.yaml
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m49s
/ playwright (push) Successful in 1m24s
/ surge (push) Successful in 2m52s
/ lint-build-and-check (push) Successful in 4m51s
2025-04-11 10:06:28 +08:00
704d7d5481 playwright.yaml
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m0s
/ lint-build-and-check (push) Successful in 4m46s
/ surge (push) Successful in 2m46s
/ playwright (push) Successful in 2m29s
2025-04-11 09:56:19 +08:00
f65ef2770a playwright.yaml
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m3s
/ lint-build-and-check (push) Successful in 4m47s
/ surge (push) Successful in 2m45s
/ playwright (push) Failing after 3m42s
2025-04-11 09:48:40 +08:00
8db3b59854 playwright.yaml
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m43s
/ lint-build-and-check (push) Successful in 5m14s
/ surge (push) Successful in 5m8s
/ playwright (push) Failing after 1m22s
2025-04-11 09:34:20 +08:00
32005cd4d3 🧹 清理 Surge 部署 2025-04-11 09:14:10 +08:00
7a8a236243 增加对 .VSCodeCounter 文件的忽略
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m44s
/ lint-build-and-check (push) Successful in 4m46s
/ surge (push) Successful in 2m38s
/ playwright (push) Failing after 2m12s
2025-04-10 16:34:43 +08:00
89ebb71b9d 频谱: iframe-page-comps/IframeSpectrogram.vue
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m46s
/ surge (push) Successful in 2m50s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-04-10 16:30:32 +08:00
106eba0053 ConstellationDiagram.html.md 2025-04-10 15:46:50 +08:00
6b99afc240 整理 2025-04-10 14:57:37 +08:00
95fddc8d20 整理文件 2025-04-10 14:55:44 +08:00
1a4a169222 chore(deps): update dependency vite-plugin-static-copy to ^2.3.1
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m29s
/ surge (push) Successful in 2m56s
/ playwright (push) Successful in 1m6s
/ lint-build-and-check (push) Successful in 4m47s
2025-04-09 20:18:16 +08:00
2cec135a8f chore(deps): update all non-major dependencies
Some checks failed
renovate/stability-days Updates have not met minimum release age requirement
/ playwright (push) Has been cancelled
/ surge (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
2025-04-09 19:14:37 +08:00
2290dc2edf 优化数据发送逻辑,确保使用原始数据对象,并增加 props.data 变化的日志记录
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m19s
/ lint-build-and-check (push) Successful in 4m46s
/ playwright (push) Failing after 6m22s
/ surge (push) Successful in 2m36s
2025-04-09 18:17:00 +08:00
318fa4ea33 IframeConstellationDiagram.vue
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ lint-build-and-check (push) Successful in 4m38s
/ surge (push) Successful in 2m41s
/ playwright (push) Failing after 6m24s
2025-04-09 18:07:11 +08:00
4e56d311f1 html-page
Some checks failed
/ surge (push) Successful in 2m45s
/ build-and-deploy-to-vercel (push) Successful in 3m24s
/ lint-build-and-check (push) Successful in 4m44s
/ playwright (push) Has been cancelled
2025-04-09 18:01:33 +08:00
0dcc47519f Spectrogram.html
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 2m37s
/ lint-build-and-check (push) Failing after 2m54s
/ surge (push) Successful in 2m40s
/ playwright (push) Failing after 2m41s
2025-04-08 18:00:44 +08:00
ef48de3846 Spectrogram.html
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 2m40s
/ lint-build-and-check (push) Failing after 3m9s
/ playwright (push) Successful in 1m5s
/ surge (push) Successful in 2m42s
2025-04-08 17:51:10 +08:00
1a7831c003 升级依赖
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m55s
/ lint-build-and-check (push) Successful in 4m56s
/ surge (push) Successful in 2m59s
/ playwright (push) Failing after 2m45s
2025-04-08 15:48:43 +08:00
a6367058b5 语图page
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m51s
/ lint-build-and-check (push) Successful in 5m12s
/ surge (push) Successful in 2m43s
/ playwright (push) Failing after 2m30s
2025-04-08 15:17:03 +08:00
7abc6a8f65 fix: 添加 @ts-nocheck 注释以禁用 TypeScript 检查
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ lint-build-and-check (push) Successful in 4m44s
/ playwright (push) Failing after 4m17s
/ surge (push) Successful in 2m59s
2025-04-07 21:49:41 +08:00
371b7a71c9 星座图
Some checks failed
/ lint-build-and-check (push) Failing after 2m57s
/ build-and-deploy-to-vercel (push) Failing after 3m0s
/ surge (push) Successful in 2m39s
/ playwright (push) Successful in 1m2s
2025-04-07 16:56:04 +08:00
c06cc44cbd vite optimizeDeps
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m49s
/ playwright (push) Successful in 1m13s
/ surge (push) Successful in 2m49s
/ lint-build-and-check (push) Successful in 4m44s
2025-04-07 10:42:44 +08:00
2721b4c2f3 refactor(cesium): 重构 Cesium Viewer 状态管理和 Composable
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m53s
/ lint-build-and-check (push) Successful in 5m9s
/ surge (push) Successful in 2m46s
/ playwright (push) Failing after 3m4s
2025-04-07 10:00:30 +08:00
d7d704d120 fix: 卫星列表初始化逻辑 2025-04-07 09:11:12 +08:00
9e8affc52d chore: 更新项目配置并添加 p5.js 粒子演示
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m27s
/ surge (push) Successful in 2m54s
/ playwright (push) Successful in 1m3s
/ lint-build-and-check (push) Successful in 4m51s
2025-04-06 21:38:06 +08:00
aecfd22055 chore(husky): 更新钩子脚本注释和 emoji [no ci] 2025-04-06 19:49:41 +08:00
bf68819b4a feat: 适配 FlowbiteSidebar 页面 iPhone 横屏安全区域 2025-04-06 19:18:00 +08:00
a5dd8f3e52 Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 7797ee9' (#183) from renovate/github-actions into main
[no ci]
2025-04-06 13:17:11 +08:00
0f8ad09ecd chore(deps): update yanhao98/composite-actions digest to 7797ee9
All checks were successful
/ lint-build-and-check (push) Successful in 4m43s
/ surge (push) Successful in 2m31s
/ playwright (push) Successful in 1m5s
2025-04-06 06:08:15 +08:00
3cad491aac Merge remote-tracking branch 'origin/main'
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m30s
/ playwright (push) Successful in 53s
/ surge (push) Successful in 2m37s
/ lint-build-and-check (push) Successful in 4m39s
2025-04-06 03:03:17 +08:00
f77b63cb8c chore: 移除 @unocss/preset-rem-to-px 依赖 2025-04-06 03:03:07 +08:00
20fa8e67d1 Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 90b5130' (#182) from renovate/github-actions into main
[no ci]
2025-04-06 02:58:27 +08:00
8c556dae8f chore(deps): update yanhao98/composite-actions digest to 90b5130
Some checks failed
/ lint-build-and-check (push) Successful in 4m36s
/ surge (push) Successful in 2m50s
/ playwright (push) Failing after 3m6s
2025-04-06 02:12:19 +08:00
a42282212e chore(deps): update lint dependencies to ^0.16.4 (patch) (#175) [no ci]
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [eslint-plugin-oxlint](https://github.com/oxc-project/eslint-plugin-oxlint) | [`^0.16.3` -> `^0.16.4`](https://renovatebot.com/diffs/npm/eslint-plugin-oxlint/0.16.3/0.16.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [oxlint](https://oxc.rs) ([source](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`^0.16.3` -> `^0.16.4`](https://renovatebot.com/diffs/npm/oxlint/0.16.3/0.16.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/eslint-plugin-oxlint (eslint-plugin-oxlint)</summary>

### [`v0.16.4`](https://github.com/oxc-project/eslint-plugin-oxlint/releases/tag/v0.16.4)

[Compare Source](https://github.com/oxc-project/eslint-plugin-oxlint/compare/v0.16.3...v0.16.4)

*No significant changes*

#####     [View changes on GitHub](https://github.com/oxc-project/eslint-plugin-oxlint/compare/v0.16.3...v0.16.4)

</details>

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.16.4`](https://github.com/oxc-project/oxc/releases/tag/oxlint_v0.16.4): oxlint v0.16.4

[Compare Source](https://github.com/oxc-project/oxc/compare/oxlint_v0.16.3...oxlint_v0.16.4)

#### \[0.16.4] - 2025-04-01

-   [`da6336c`](https://github.com/oxc-project/oxc/commit/da6336c) language_server: \[**BREAKING**] Remove `enable` configuration, the client should shutdown the server instead ([#&#8203;9990](https://github.com/oxc-project/oxc/issues/9990)) (Sysix)

##### Features

-   [`06e3db9`](https://github.com/oxc-project/oxc/commit/06e3db9) linter: Support `multipleFileExtensions` option for `unicorn/filename-case` ([#&#8203;10118](https://github.com/oxc-project/oxc/issues/10118)) (shulaoda)
-   [`dbe0e46`](https://github.com/oxc-project/oxc/commit/dbe0e46) linter: Support `ignore` option for `unicorn/filename-case` ([#&#8203;10107](https://github.com/oxc-project/oxc/issues/10107)) (shulaoda)

##### Bug Fixes

-   [`0a33e27`](https://github.com/oxc-project/oxc/commit/0a33e27) editor: Update `initializationOptions` for a possible restart ([#&#8203;10121](https://github.com/oxc-project/oxc/issues/10121)) (Sysix)
-   [`ac780a2`](https://github.com/oxc-project/oxc/commit/ac780a2) editor: Repair filewatchers when no custom config provided ([#&#8203;10104](https://github.com/oxc-project/oxc/issues/10104)) (Sysix)
-   [`4303ace`](https://github.com/oxc-project/oxc/commit/4303ace) editor: Dont send `didChangeConfiguration` request to the server when it is shutdown ([#&#8203;10084](https://github.com/oxc-project/oxc/issues/10084)) (Sysix)
-   [`07f2a25`](https://github.com/oxc-project/oxc/commit/07f2a25) editor: Enable regex parsing in language server ([#&#8203;10035](https://github.com/oxc-project/oxc/issues/10035)) (camchenry)
-   [`fcf7702`](https://github.com/oxc-project/oxc/commit/fcf7702) language_server: Start from a default oxlint configuration + SafeFix for nested configuration ([#&#8203;10043](https://github.com/oxc-project/oxc/issues/10043)) (Sysix)
-   [`aba3654`](https://github.com/oxc-project/oxc/commit/aba3654) linter: Span disable directive correctly on next line ([#&#8203;10141](https://github.com/oxc-project/oxc/issues/10141)) (Ulrich Stark 🦀)
-   [`2c80858`](https://github.com/oxc-project/oxc/commit/2c80858) linter: Enable multi-file analysis for nested configs ([#&#8203;10089](https://github.com/oxc-project/oxc/issues/10089)) (camchenry)

##### Performance

-   [`566be59`](https://github.com/oxc-project/oxc/commit/566be59) linter: Replace `phf_set` with `array` in `eslint/func-names` ([#&#8203;10119](https://github.com/oxc-project/oxc/issues/10119)) (shulaoda)
-   [`5e14fe9`](https://github.com/oxc-project/oxc/commit/5e14fe9) linter: Inline `PRE_DEFINE_VAR` and use `array` format ([#&#8203;10079](https://github.com/oxc-project/oxc/issues/10079)) (shulaoda)

##### Refactor

-   [`327be53`](https://github.com/oxc-project/oxc/commit/327be53) editor: `LanguageClient` can be undefined ([#&#8203;10112](https://github.com/oxc-project/oxc/issues/10112)) (Sysix)
-   [`5ec477c`](https://github.com/oxc-project/oxc/commit/5ec477c) editor: Make `onConfigChange` async ([#&#8203;10110](https://github.com/oxc-project/oxc/issues/10110)) (Sysix)
-   [`a278d73`](https://github.com/oxc-project/oxc/commit/a278d73) editor: Use warning background when the plugin is deactived ([#&#8203;10085](https://github.com/oxc-project/oxc/issues/10085)) (Sysix)
-   [`c0e5251`](https://github.com/oxc-project/oxc/commit/c0e5251) language_server: Set `null` as a default value for `configPath` ([#&#8203;10047](https://github.com/oxc-project/oxc/issues/10047)) (Sysix)
-   [`09c0ac6`](https://github.com/oxc-project/oxc/commit/09c0ac6) linter: Improve `unicorn/filename-case` ([#&#8203;10117](https://github.com/oxc-project/oxc/issues/10117)) (shulaoda)
-   [`d8e49a1`](https://github.com/oxc-project/oxc/commit/d8e49a1) linter: Compute lintable extensions at compile time ([#&#8203;10090](https://github.com/oxc-project/oxc/issues/10090)) (camchenry)
-   [`b3ec235`](https://github.com/oxc-project/oxc/commit/b3ec235) linter: Use items of `oxc_ast::ast` module directly ([#&#8203;10100](https://github.com/oxc-project/oxc/issues/10100)) (Ulrich Stark 🦀)
-   [`93e6c0b`](https://github.com/oxc-project/oxc/commit/93e6c0b) linter: Use `FormalParameter::has_modifier` to detect parameter properties ([#&#8203;10097](https://github.com/oxc-project/oxc/issues/10097)) (Ulrich Stark 🦀)

##### Testing

-   [`410b8d6`](https://github.com/oxc-project/oxc/commit/410b8d6) editor: Use debug build of the language server ([#&#8203;10083](https://github.com/oxc-project/oxc/issues/10083)) (Sysix)
-   [`500add0`](https://github.com/oxc-project/oxc/commit/500add0) editor: Add test for `oxc.fixAll` command ([#&#8203;10045](https://github.com/oxc-project/oxc/issues/10045)) (Sysix)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzAuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Reviewed-on: https://git.1-h.cc/examples/vue-ts-example/pulls/175
Co-authored-by: Renovate Bot <renovatebot@oo1.dev>
Co-committed-by: Renovate Bot <renovatebot@oo1.dev>
2025-04-06 01:44:29 +08:00
0344b51b40 chore(deps): update dependency typescript to ~5.8.3
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 2m39s
/ playwright (push) Successful in 46s
/ surge (push) Successful in 2m32s
/ lint-build-and-check (push) Successful in 4m30s
2025-04-05 21:44:17 +08:00
5e652561a1 feat: gitattributes [no ci] 2025-04-05 21:18:02 +08:00
4ee2048ae4 fix: 在 lint-staged 中添加 oxlint 修复命令
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ surge (push) Successful in 2m23s
/ playwright (push) Successful in 48s
/ lint-build-and-check (push) Successful in 4m27s
2025-04-05 16:31:42 +08:00
f0c6290e89 chore: 添加 Vercel 工作流文档链接 [no ci] 2025-04-05 15:43:22 +08:00
42a367e83b chore(deps): update yanhao98/composite-actions digest to 2a7a660
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m38s
/ lint-build-and-check (push) Successful in 4m18s
/ surge (push) Successful in 5m1s
/ playwright (push) Successful in 1m5s
2025-04-04 22:22:08 +08:00
bd4dd95c2a chore(deps): update dependency @primeuix/themes to ^1.0.1
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 4m29s
/ lint-build-and-check (push) Successful in 5m58s
/ playwright (push) Successful in 3m15s
/ surge (push) Successful in 4m21s
2025-04-04 00:21:10 +08:00
cc3567d022 fix: 更新 Vercel 工作流 [no ci] 2025-04-03 23:53:29 +08:00
5b28d93122 fix: 在 Vercel 工作流中启用 Vue 生产环境开发工具
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m49s
/ lint-build-and-check (push) Successful in 4m35s
/ playwright (push) Failing after 3m56s
/ surge (push) Successful in 2m43s
2025-04-03 22:37:28 +08:00
ecd82d30a4 fix: 更新 lint-staged 配置,支持 .tsx 文件格式
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m47s
/ lint-build-and-check (push) Successful in 4m36s
/ surge (push) Successful in 2m44s
/ playwright (push) Failing after 11m36s
2025-04-03 15:26:27 +08:00
5a69629f35 feat: 添加地面站实体创建逻辑,优化管理器功能
Some checks failed
/ surge (push) Successful in 2m46s
/ build-and-deploy-to-vercel (push) Successful in 2m51s
/ lint-build-and-check (push) Successful in 4m36s
/ playwright (push) Has been cancelled
2025-04-03 15:12:11 +08:00
aacd168450 refactor: 更新地面站和卫星状态接口,优化类型命名和属性结构 2025-04-03 13:32:12 +08:00
b742597f61 fix: 更新卫星和地面站管理器中的选项解构,确保 showOrbit 属性的正确性 2025-04-03 13:29:25 +08:00
61e6b21d90 refactor: 重命名卫星和地面站管理器函数,优化代码可读性 2025-04-03 13:22:35 +08:00
64e82fbb7f refactor: 重命名管理器类并更新相关类型导入,优化代码结构 2025-04-03 13:17:34 +08:00
29678d8f61 fix: 修复卫星和地面站管理器中的多余括号和注释,优化代码可读性 2025-04-03 12:47:15 +08:00
8198fefe91 Refactor: 重构 HCesiumManager 2025-04-03 12:35:24 +08:00
b1c8597936 chore(deps): update yanhao98/composite-actions digest to 2d4be1a
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m38s
/ surge (push) Successful in 2m48s
/ playwright (push) Successful in 1m1s
/ lint-build-and-check (push) Successful in 5m1s
2025-04-03 01:54:34 +08:00
fbff6e0542 Merge remote-tracking branch 'origin/main'
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m42s
/ lint-build-and-check (push) Successful in 4m57s
/ playwright (push) Failing after 11m30s
/ surge (push) Successful in 3m39s
# Conflicts:
#	.github/workflows/playwright.yaml
#	.github/workflows/vercel.yaml
2025-04-03 00:47:24 +08:00
fb71cf676c Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 2d4be1a' (#173) from renovate/github-actions into main
[no ci]
2025-04-03 00:39:53 +08:00
3d20ff6e83 feat: 优化 GitHub Actions 工作流中的步骤名称以增强可读性 [skip ci] 2025-04-03 00:39:18 +08:00
bcec165c2f feat: 为 GitHub Actions 步骤添加名称以增强可读性 2025-04-03 00:27:03 +08:00
7261a45cab feat: 更新地面站和卫星状态管理为使用 Set
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m13s
/ lint-build-and-check (push) Successful in 4m38s
/ playwright (push) Failing after 12m17s
/ surge (push) Successful in 2m47s
2025-04-02 19:09:00 +08:00
02ce0fa9a0 h-cesium-viewer
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ lint-build-and-check (push) Successful in 4m32s
/ playwright (push) Failing after 12m4s
/ surge (push) Successful in 2m34s
2025-04-02 16:39:15 +08:00
098a769dbd feat: 为卫星实体和覆盖范围添加随机基色 2025-04-02 15:33:33 +08:00
db9d394171 feat: 添加卫星覆盖范围计算功能 2025-04-02 15:17:47 +08:00
175ed3450d chore(deps): update yanhao98/composite-actions digest to 2d4be1a
Some checks failed
/ lint-build-and-check (push) Successful in 5m13s
/ surge (push) Successful in 3m32s
/ playwright (push) Failing after 11m40s
2025-04-02 15:08:11 +08:00
751b890a80 fix: 添加 import-x/no-unresolved 规则的注释,提供更多上下文信息 [no ci] 2025-04-02 11:38:14 +08:00
1fecb5cb92 refactor: 重新组织 ESLint 插件配置,添加区域注释以提高可读性
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m37s
/ lint-build-and-check (push) Successful in 4m35s
/ surge (push) Successful in 2m48s
/ playwright (push) Failing after 12m51s
2025-04-02 11:31:45 +08:00
2bc762a89b feat: 更新 ESLint 配置
Some checks failed
/ surge (push) Successful in 2m34s
/ build-and-deploy-to-vercel (push) Successful in 2m46s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-04-02 11:27:09 +08:00
9ed4c0bc6b chore(deps): update dependency primelocale to ^2.1.2
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m35s
/ lint-build-and-check (push) Successful in 4m44s
/ surge (push) Successful in 2m42s
/ playwright (push) Failing after 11m51s
2025-04-02 10:01:34 +08:00
9daafb9157 chore(deps): update yanhao98/composite-actions digest to 502d85b
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m40s
/ surge (push) Successful in 2m52s
/ lint-build-and-check (push) Successful in 4m22s
/ playwright (push) Has been cancelled
2025-04-02 09:54:08 +08:00
162f9a9a13 feat: 添加 vite-plugin-checker 插件,配置 ESLint 和 Vue 类型检查
Some checks failed
/ playwright (push) Has been cancelled
/ surge (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
2025-04-02 09:51:35 +08:00
15980dd551 feat: 添加 husky 钩子,支持 commit-msg 和 post-merge 自动提示,更新 pre-commit 钩子中文提示
Some checks failed
/ surge (push) Successful in 2m30s
/ build-and-deploy-to-vercel (push) Successful in 2m43s
/ lint-build-and-check (push) Successful in 4m53s
/ playwright (push) Failing after 19m31s
2025-04-01 19:18:31 +08:00
da515f4dfc feat: 添加卫星管理功能,新增 SatelliteOptions 接口,更新相关组件以支持卫星实体的添加和移除
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ lint-build-and-check (push) Successful in 4m28s
/ surge (push) Successful in 2m46s
/ playwright (push) Failing after 12m11s
2025-04-01 18:00:25 +08:00
13681003fb fix: 更新代码注释规范,强调注释应解释逻辑和设计意图,禁止描述代码状态或历史 2025-04-01 18:00:18 +08:00
21660cd9fa fix: 整理代码 2025-04-01 16:34:05 +08:00
99b5aeb042 feat: 重构地面站管理逻辑,新增 GroundStationState 接口,优化组件间数据传递 2025-04-01 16:20:22 +08:00
c829eaaf71 fix: 更新代码注释规范,强调注释应解释复杂逻辑和设计意图,而非重复代码或描述位置 2025-04-01 16:15:39 +08:00
f32c52867f fix: 更新代码注释规范,强调注释应解释复杂逻辑而非重复代码 2025-04-01 15:28:35 +08:00
5c92482122 fix: 修正移除站点逻辑,确保从选中项中正确移除已删除的站点
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m34s
/ lint-build-and-check (push) Successful in 4m26s
/ playwright (push) Failing after 11m51s
/ surge (push) Successful in 2m32s
2025-04-01 14:32:32 +08:00
302151eea8 fix: 修正页面加载路径,确保测试正确访问首页
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m30s
/ lint-build-and-check (push) Successful in 4m28s
/ surge (push) Successful in 2m43s
/ playwright (push) Failing after 14m27s
2025-04-01 12:14:45 +08:00
9166bca070 fix: 调整类型检查步骤的位置,确保构建后进行类型检查
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m6s
/ lint-build-and-check (push) Successful in 4m8s
/ playwright (push) Has been cancelled
/ surge (push) Has been cancelled
2025-04-01 11:58:55 +08:00
8f2a77702b feat: InspiraUI
Some checks failed
/ surge (push) Successful in 2m41s
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-04-01 11:55:16 +08:00
db2c210a09 fix: message.not-used [no ci] 2025-04-01 09:23:16 +08:00
7072f171e0 refactor: 重构 Cesium 相关代码
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m53s
/ lint-build-and-check (push) Successful in 4m23s
/ surge (push) Successful in 3m14s
/ playwright (push) Successful in 5m51s
2025-04-01 01:51:02 +08:00
2c6a4287d2 feat: 整理 HCesiumViewerCls 相关
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m10s
/ lint-build-and-check (push) Successful in 4m25s
/ surge (push) Successful in 2m42s
/ playwright (push) Successful in 5m15s
2025-03-31 20:43:13 +08:00
4636f9fde4 feat: 添加 Cesium Viewer 组件及相关功能,优化项目结构和配置
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ lint-build-and-check (push) Successful in 4m30s
/ surge (push) Successful in 2m57s
/ playwright (push) Successful in 6m56s
2025-03-31 19:43:56 +08:00
32bc83e16e chore(deps): update dependency vite to ^6.2.4
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 2m31s
/ lint-build-and-check (push) Successful in 4m20s
/ surge (push) Successful in 2m30s
/ playwright (push) Successful in 5m16s
2025-03-31 18:35:50 +08:00
12d5ee979f feat: 优化响应式布局,使用 CSS 支持的单位调整屏幕高度和宽度
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m42s
/ lint-build-and-check (push) Successful in 4m25s
/ surge (push) Successful in 2m18s
/ playwright (push) Failing after 5m2s
2025-03-31 15:30:45 +08:00
3edb7392ce feat: 全局 FluidCursor
Some checks failed
/ surge (push) Successful in 2m20s
/ build-and-deploy-to-vercel (push) Successful in 2m42s
/ lint-build-and-check (push) Successful in 4m24s
/ playwright (push) Has been cancelled
2025-03-31 15:25:40 +08:00
b16c038be9 feat: 删除 OrbPro 相关文件和依赖,清理代码库
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ lint-build-and-check (push) Successful in 4m5s
/ surge (push) Successful in 2m34s
/ playwright (push) Failing after 5m1s
2025-03-31 15:15:54 +08:00
aea90d1f0d feat: 优化首页模板,移除不必要的条件渲染,调整样式以适应全屏
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m8s
/ lint-build-and-check (push) Successful in 5m25s
/ surge (push) Successful in 3m24s
/ playwright (push) Successful in 4m33s
2025-03-31 14:47:13 +08:00
334b2485f5 feat: 添加 InspiraUI 组件和模式背景功能,更新依赖项
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m0s
/ lint-build-and-check (push) Successful in 4m44s
/ playwright (push) Successful in 3m18s
/ surge (push) Successful in 3m1s
2025-03-31 14:11:30 +08:00
6e18e0f737 feat: 添加 headless 测试支持和修复接口请求页面的路径
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m18s
/ lint-build-and-check (push) Successful in 4m16s
/ surge (push) Successful in 2m46s
/ playwright (push) Successful in 5m34s
2025-03-31 12:36:39 +08:00
5ba87212b9 feat: 添加 oxlint 配置文件并更新 lint 脚本
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m53s
/ lint-build-and-check (push) Successful in 4m15s
/ playwright (push) Failing after 6m44s
/ surge (push) Successful in 2m53s
2025-03-31 01:41:17 +08:00
3adafb58d5 chore: 移除不必要的 oxlint lint 脚本
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m2s
/ lint-build-and-check (push) Successful in 4m13s
/ surge (push) Successful in 2m58s
/ playwright (push) Failing after 5m16s
2025-03-31 00:57:16 +08:00
2fd7f2fcbc feat: 更新配置和依赖项,启用 oxc 插件,移除 cdnImport 插件
Some checks failed
/ lint-build-and-check (push) Failing after 36s
/ playwright (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ surge (push) Has been cancelled
2025-03-31 00:55:50 +08:00
0cc1730b2c feat: 更新依赖
Some checks failed
/ lint-build-and-check (push) Failing after 38s
/ build-and-deploy-to-vercel (push) Successful in 3m4s
/ playwright (push) Failing after 5m32s
/ surge (push) Successful in 2m18s
2025-03-31 00:38:50 +08:00
8d0ed93ee0 feat: 添加 InspiraUI 组件
Some checks failed
/ lint-build-and-check (push) Failing after 37s
/ playwright (push) Failing after 4m50s
/ surge (push) Successful in 2m32s
2025-03-30 23:45:01 +08:00
05d6a71da8 feat: <Isolate />
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 3m2s
/ lint-build-and-check (push) Successful in 4m31s
/ surge (push) Successful in 2m27s
/ playwright (push) Failing after 5m21s
2025-03-30 22:58:32 +08:00
4b109a0829 chore(deps): 更新多个依赖项版本
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m3s
/ lint-build-and-check (push) Successful in 4m31s
/ surge (push) Successful in 2m46s
/ playwright (push) Successful in 4m31s
2025-03-28 09:58:16 +08:00
0cf585be90 手动修复
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m10s
/ lint-build-and-check (push) Successful in 4m20s
/ surge (push) Successful in 2m59s
/ playwright (push) Successful in 5m50s
2025-03-28 00:43:59 +08:00
ba91e845d5 帮我把 @unhead/vue 的版本升级到 v2.0.2
Some checks failed
/ surge (push) Failing after 1m1s
/ playwright (push) Has been skipped
/ lint-build-and-check (push) Failing after 1m31s
---
title: Migrate Your TypeScript App To Unhead v2
description: Learn about how to migrate to TypeScript Unhead v2 from v1.
navigation:
  title: Upgrade Guide
---

## Introduction

With the release of Unhead v2, we now have first-class support for other frameworks. However, this guide will focus on
the changes that affec TypeScript users.

The high-level of Unhead v2 was to remove deprecations and remove the implicit context implementation.

### Legacy Support

Unhead v2 is mostly fully backwards compatible with Unhead v1.

While not recommended, if upgrading is not possible for you, you can change your imports to the following:

```diff [TypeScript]
-import { createServerHead, useHead } from 'unhead'
+import { createServerHead, useHead } from 'unhead/legacy'
```

This will be removed in a future minor version, so you should lock your dependencies to the version that works for you.

## Client / Server Subpath Exports

🚦 Impact Level: Critical

**⚠️ Breaking Changes:**

- `createServerHead()`{lang="ts"} and `createHead()`{lang="ts"} exports from `unhead` are removed

The path where you import `createHead` from has been updated to be a subpath export.

Please follow the updated installation instructions or simply update the import to use the subpath.

**Client bundle:**

```diff
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/client'

// avoids bundling server plugins
createHead()
```

**Server bundle:**

```diff
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/server'

// avoids bundling server plugins
-createServerHead()
+createHead()
```

## Removed Implicit Context

🚦 Impact Level: Critical

**⚠️ Breaking Changes:**

- `getActiveHead()`{lang="ts"}, `activeHead`{lang="ts"} exports are removed

The implicit context implementation kept a global instance of Unhead available so that you could use the `useHead()`{lang="ts"} composables
anywhere in your application.

```ts
useHead({
  title: 'This just worked!'
})
```

While safe client side, this was a leaky abstraction server side and led to memory leaks in some cases.

In v2, the core composables no longer have access to the Unhead instance. Instead, you must pass the Unhead instance to the composables.

::note
Passing the instance is only relevant if you're importing from `unhead`. In JavaScript frameworks we tie the context to the framework itself so you
don't need to worry about this.
::

::code-group

```ts [TypeScript v2]
import { useHead } from 'unhead'

// example of getting the instance
const unheadInstance = useMyApp().unhead
useHead(unheadInstance, {
  title: 'Looks good'
})
```

```ts [TypeScript v1]
import { useHead } from 'unhead'

useHead({
  title: 'Just worked! But with SSR issues'
})
```

::

## Removed `vmid`, `hid`, `children`, `body`

🚦 Impact Level: High

For legacy support with Vue Meta we allowed end users to provide deprecated properties:  `vmid`, `hid`, `children` and `body`.

You must either update these properties to the appropriate replacement, remove them, or you can use the `DeprecationsPlugin`.

**Meta tags with `vmid`, `hid`**

These are already deduped magically so you can safely remove them there.

```diff
useHead({
  meta: [{
    name: 'description',
-   vmid: 'description'
-   hid: 'description'
  }]
})
```

**Other Tags with `vmid`, `hid`**

Use `key` if you need the deduplication feature. This is useful for tags that may change from server to client
rendering.

```diff
useHead({
  script: [{
-   vmid: 'my-key'
-   hid: 'my-key'
+   key: 'my-key',
  }]
})
```

**Using `children`**

The `children` key is a direct replacement of `innerHTML` which you should use instead.

::Caution
When migrating your code ensure that you're not dynamically setting `innerHTML` as this can lead to XSS vulnerabilities.
::

```diff
useHead({
  script: [
      {
-        children: '..'
+        innerHTML: '..'
      }
   ]
})
```

**Using `body`**

The `body` key should be updated to use the Tag Position feature.

```diff
useHead({
  script: [
      {
-        body: true
+        tagPosition: 'bodyClose'
      }
   ]
})
```

**Use Deprecations Plugin**

```ts
import { createHead } from 'unhead'
import { DeprecationsPlugin } from 'unhead/plugins'

const unhead = createHead({
  plugins: [DeprecationsPlugin]
})
```

## Opt-in Template Params & Tag Alias Sorting

🚦 Impact Level: High

To reduce the bundle size and improve performance, we've moved the template params and tag alias sorting to optional plugins.

If you'd like to continue using these, please opt-in to the plugins.

```ts
import { AliasSortingPlugin, TemplateParamsPlugin } from 'unhead/plugins'

createHead({
  plugins: [TemplateParamsPlugin, AliasSortingPlugin]
})
```

## Vue 2 Support

🚦 Impact Level: Critical

Unhead v2 no longer supports Vue v2. If you're using Vue v2, you will need to lock your dependencies to the latest v1 version of Unhead.

## Promise Input Support

🚦 Impact Level: Medium

If you have promises as input they will no longer be resolved, either await the promise before passing it along or register the optional promises plugin.

**Option 1: Await Promise**

```diff
useHead({
  link: [
    {
-     href: import('~/assets/MyFont.css?url'),
+     href: await import('~/assets/MyFont.css?url'),
      rel: 'stylesheet',
      type: 'text/css'
    }
  ]
})
```

**Option 2: Promise Plugin**

```ts
import { PromisePlugin } from 'unhead/plugins'

const unhead = createHead({
  plugins: [PromisePlugin]
})
```

## Updated `useScript()`{lang="ts"}

🚦 Impact Level: High

**⚠️ Breaking Changes:**

- Script instance is no longer augmented as a proxy and promise
- `script.proxy`{lang="ts"} is rewritten for simpler, more stable behavior
- `stub()`{lang="ts"} and runtime hook `script:instance-fn` are removed

**Replacing promise usage**

If you're using the script as a promise you should instead opt to use the `onLoaded()` functions.

```diff
const script = useScript()

-script.then(() => console.log('loaded')
+script.onLoaded(() => console.log('loaded'))
```

**Replacing proxy usage**

If you're accessing the underlying API directly from the script instance, you will now need to only access it from the `.proxy`.

```diff
const script = useScript('..', {
  use() { return { foo: [] } }
})

-script.foo.push('bar')
+script.proxy.foo.push('bar')
```

**Replacing `stub()`**

If you were using stub for anything you should replace this with either custom `use()` behavior.

```diff
const script = useScript('...', {
-  stub() { return { foo: import.meta.server ? [] : undefined } }
})

+script.proxy = {} // your own implementation
```

## Tag Sorting Updated

🚦 Impact Level: :UBadge{color="success" variant="subtle" size="sm" label="Low"}

An optional [Capo.js](https://rviscomi.github.io/capo.js/) plugin was added to Unhead, in v2 we make this the default sorting behavior.

::warning
As all head tags may be re-ordered this will break any snapshot tests that you have in place and in some rare cases may lead to performance regressions.
::

You can opt-out of Capo.js sorting by providing the option.

```ts
createHead({
  disableCapoSorting: true,
})
```

## Default SSR Tags

🚦 Impact Level: Low

When SSR Unhead will now insert important default tags for you:
- `<meta charset="utf-8">`
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
- `<html lang="en">`

If you were previously relying on these being left empty, you may need to either disable them by using `disableDefaultTags` or insert tags
to override them.

```ts
import { createHead, useHead } from 'unhead/server'

// disable when creating the head instance
const head = createHead({
  disableDefaults: true,
})

useHead(head, {
  htmlAttrs: {
    lang: 'fr'
  }
})
```

## CJS Exports Removed

🚦 Impact Level: Low

CommonJS exports have been removed in favor of ESM only.

```diff
-const { createHead } = require('unhead/client')
+import { createHead } from 'unhead/client'
```

## Deprecated `@unhead/schema`

🚦 Impact Level: Low

The `@unhead/schema` package is now deprecated and will be removed in a future version. You should instead import
the schema from `unhead/types` or `unhead`.

```diff
-import { HeadTag } from '@unhead/schema'
+import { HeadTag } from 'unhead/types'
```
2025-03-28 00:06:28 +08:00
ea50699dc1 feat: 移动 depcheck 工作流到 lint.yaml,删除旧的 depcheck.yaml 文件
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 3m55s
/ lint-build-and-check (push) Successful in 5m39s
/ surge (push) Successful in 3m32s
/ playwright (push) Successful in 4m13s
2025-03-27 22:55:36 +08:00
dcbdba3a55 docs: 添加 Vuetify 相关链接到 UseIntersectionObserverInfiniteLoading 组件
All checks were successful
/ depcheck (push) Successful in 2m30s
/ lint-build-and-check (push) Successful in 2m39s
/ build-and-deploy-to-vercel (push) Successful in 3m4s
/ playwright (push) Successful in 4m48s
/ surge (push) Successful in 2m38s
2025-03-27 11:24:16 +08:00
a6389aee36 chore(deps): update dependency eslint-plugin-oxlint to ^0.15.15
All checks were successful
/ depcheck (push) Successful in 2m24s
/ lint-build-and-check (push) Successful in 2m33s
/ build-and-deploy-to-vercel (push) Successful in 3m19s
/ surge (push) Successful in 2m27s
/ playwright (push) Successful in 4m43s
2025-03-26 20:32:09 +08:00
a05ca276ca chore(deps): update dependency eslint-plugin-unicorn to v58
Some checks failed
/ depcheck (push) Successful in 2m22s
/ lint-build-and-check (push) Successful in 2m43s
/ playwright (push) Has been cancelled
/ surge (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2025-03-26 20:29:15 +08:00
6dc43a1812 chore(deps): update yanhao98/composite-actions digest to 33959a3
All checks were successful
/ lint-build-and-check (push) Successful in 2m45s
/ depcheck (push) Successful in 2m59s
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ surge (push) Successful in 2m30s
/ playwright (push) Successful in 4m57s
2025-03-26 01:51:21 +08:00
fa8d02f08d feat: 移除 size-limit 依赖及相关工作流配置
All checks were successful
/ depcheck (push) Successful in 2m39s
/ lint-build-and-check (push) Successful in 3m1s
/ build-and-deploy-to-vercel (push) Successful in 3m4s
/ playwright (push) Successful in 4m41s
/ surge (push) Successful in 2m27s
2025-03-25 11:42:13 +08:00
68406f5681 feat: 添加 size-limit 依赖
Some checks failed
/ lint-build-and-check (push) Successful in 2m31s
/ depcheck (push) Failing after 2m46s
/ build-and-deploy-to-vercel (push) Successful in 2m53s
/ surge (push) Successful in 2m32s
/ playwright (push) Successful in 3m50s
2025-03-25 10:54:13 +08:00
fb095ea5be fix: createUtils4uAutoImports
Some checks failed
/ depcheck (push) Failing after 2m39s
/ lint-build-and-check (push) Successful in 2m48s
/ surge (push) Successful in 2m53s
/ build-and-deploy-to-vercel (push) Successful in 2m57s
/ playwright (push) Has been cancelled
2025-03-25 10:49:06 +08:00
b475a7bbdd feat: 添加构建大小统计到 depcheck 工作流,并移除 playwright 工作流中的重复统计
Some checks failed
/ lint-build-and-check (push) Failing after 52s
/ build-and-deploy-to-vercel (push) Failing after 1m3s
/ surge (push) Failing after 1m6s
/ playwright (push) Has been skipped
/ depcheck (push) Failing after 2m41s
2025-03-25 10:39:18 +08:00
2df09cf33f feat: 整理目录结构
Some checks failed
/ playwright (push) Has been skipped
/ surge (push) Failing after 36s
/ lint-build-and-check (push) Failing after 55s
/ build-and-deploy-to-vercel (push) Failing after 1m13s
/ depcheck (push) Has been cancelled
2025-03-25 10:36:19 +08:00
2482608a2e feat: 添加 FlowbiteSidebar 组件及路由定义
All checks were successful
/ depcheck (push) Successful in 2m19s
/ lint-build-and-check (push) Successful in 2m52s
/ build-and-deploy-to-vercel (push) Successful in 3m0s
/ surge (push) Successful in 2m43s
/ playwright (push) Successful in 3m26s
2025-03-25 01:03:03 +08:00
4ac0e0c138 fix: 修复导入名称以匹配 vite-plugin-webfont-dl 的更新
All checks were successful
/ lint-build-and-check (push) Successful in 2m52s
/ depcheck (push) Successful in 2m52s
/ build-and-deploy-to-vercel (push) Successful in 3m9s
/ playwright (push) Successful in 3m51s
/ surge (push) Successful in 2m22s
2025-03-24 10:36:14 +08:00
a8a06c589c chore(deps): update unplugin packages
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ lint-build-and-check (push) Successful in 2m36s
/ depcheck (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 3m35s
/ surge (push) Successful in 2m41s
/ playwright (push) Successful in 5m31s
2025-03-24 03:14:23 +08:00
295f2d0c95 feat: 添加主题配置支持并优化 UI 组件页面的暗黑模式样式
All checks were successful
/ lint-build-and-check (push) Successful in 2m38s
/ depcheck (push) Successful in 3m7s
/ build-and-deploy-to-vercel (push) Successful in 3m22s
/ playwright (push) Successful in 4m49s
/ surge (push) Successful in 2m36s
2025-03-24 01:19:00 +08:00
cf95ef7e8e feat: 添加输入组件并更新 UI 组件页面布局
All checks were successful
/ depcheck (push) Successful in 2m31s
/ lint-build-and-check (push) Successful in 2m34s
/ build-and-deploy-to-vercel (push) Successful in 3m37s
/ surge (push) Successful in 2m54s
/ playwright (push) Successful in 5m22s
2025-03-24 00:54:43 +08:00
22d3eedea0 feat: UIComponentsComponents
All checks were successful
/ depcheck (push) Successful in 2m49s
/ lint-build-and-check (push) Successful in 2m38s
/ build-and-deploy-to-vercel (push) Successful in 3m22s
/ surge (push) Successful in 3m7s
/ playwright (push) Successful in 5m9s
2025-03-24 00:22:24 +08:00
e42241ddc5 feat: unocss-preset-shadcn
All checks were successful
/ lint-build-and-check (push) Successful in 2m28s
/ depcheck (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 2m51s
/ playwright (push) Successful in 4m3s
/ surge (push) Successful in 2m31s
2025-03-23 22:10:38 +08:00
e66dc097f7 chore: 更新 Vercel 命令为 pnpm dlx 以提高执行效率
All checks were successful
/ depcheck (push) Successful in 2m21s
/ build-and-deploy-to-vercel (push) Successful in 2m48s
/ lint-build-and-check (push) Successful in 2m49s
/ surge (push) Successful in 2m25s
/ playwright (push) Successful in 1m50s
2025-03-23 02:19:13 +08:00
7abce90d75 chore(deps): update dependency eslint-plugin-vue to v10
All checks were successful
/ depcheck (push) Successful in 2m15s
/ lint-build-and-check (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 2m57s
/ surge (push) Successful in 2m33s
/ playwright (push) Successful in 3m26s
2025-03-23 02:08:50 +08:00
cc17419836 chore(deps): update yanhao98/composite-actions digest to ae93019
Some checks failed
/ playwright (push) Blocked by required conditions
/ lint-build-and-check (push) Has been cancelled
/ surge (push) Has been cancelled
/ depcheck (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2025-03-23 02:06:25 +08:00
e83f6c9247 docs(vite.config.plugin.archiver): 添加相关参考链接
All checks were successful
/ depcheck (push) Successful in 2m24s
/ lint-build-and-check (push) Successful in 3m6s
/ build-and-deploy-to-vercel (push) Successful in 3m20s
/ surge (push) Successful in 2m29s
/ playwright (push) Successful in 5m9s
在vite.config.plugin.archiver.ts文件中添加了两个参考链接,以便开发者更好地理解插件的实现背景和用途。
2025-03-23 00:55:18 +08:00
051244dde6 refactor(archiver): 优化打包插件逻辑并支持更多压缩格式
Some checks failed
/ surge (push) Successful in 2m32s
/ depcheck (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 2m51s
/ lint-build-and-check (push) Successful in 3m5s
/ playwright (push) Has been cancelled
重构打包插件以支持多种压缩格式(zip, tar, tgz),并增加时间戳选项。同时,将插件调用移至VS Code终端检测逻辑中,确保仅在VS Code环境中执行
2025-03-23 00:46:56 +08:00
e4f2ad3110 feat: 添加构建后自动打包dist目录为zip文件的功能
All checks were successful
/ depcheck (push) Successful in 2m18s
/ build-and-deploy-to-vercel (push) Successful in 2m44s
/ lint-build-and-check (push) Successful in 2m51s
/ playwright (push) Successful in 3m21s
/ surge (push) Successful in 2m53s
引入archiver依赖,新增vite插件viteArchiverPlugin,用于在构建完成后自动将dist目录打包成zip文件。同时更新.gitignore和package.json以支持该功能。
2025-03-23 00:29:19 +08:00
6883cd2dfe feat: 禁用 vite-plugin-purgecss
All checks were successful
/ lint-build-and-check (push) Successful in 2m36s
/ depcheck (push) Successful in 2m44s
/ build-and-deploy-to-vercel (push) Successful in 3m9s
/ surge (push) Successful in 3m1s
/ playwright (push) Successful in 4m31s
2025-03-22 23:58:21 +08:00
bee73670b7 feat: 更新 Playwright 工作流,增加以KB为单位的构建大小统计
Some checks failed
/ depcheck (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 2m54s
/ lint-build-and-check (push) Successful in 3m1s
/ surge (push) Successful in 3m4s
/ playwright (push) Has been cancelled
2025-03-22 23:54:24 +08:00
d23fcd72c7 feat: 更新 Playwright 工作流以计算构建大小;添加 vite-plugin-purgecss-updated-v5 插件
Some checks failed
/ surge (push) Successful in 2m24s
/ lint-build-and-check (push) Successful in 2m37s
/ depcheck (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 2m47s
/ playwright (push) Has been cancelled
2025-03-22 23:50:24 +08:00
9e6d526ada feat: 添加构建大小计算步骤;更新依赖项,新增 vite-plugin-singlefile 插件
All checks were successful
/ lint-build-and-check (push) Successful in 2m25s
/ depcheck (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 3m13s
/ playwright (push) Successful in 3m11s
/ surge (push) Successful in 2m27s
2025-03-22 23:44:06 +08:00
974f05719e feat: 添加 StyleLayer 组件,展示 @layer 优先级规则及样式示例;新增 style.page.vue 页面
All checks were successful
/ lint-build-and-check (push) Successful in 2m58s
/ depcheck (push) Successful in 3m14s
/ build-and-deploy-to-vercel (push) Successful in 3m36s
/ surge (push) Successful in 3m45s
/ playwright (push) Successful in 7m43s
2025-03-22 18:29:23 +08:00
4db3a84f61 chore(deps): update dependency prettier to v3.5.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ lint-build-and-check (push) Successful in 2m42s
/ depcheck (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 3m16s
/ surge (push) Successful in 2m41s
/ playwright (push) Successful in 4m17s
2025-03-17 13:09:49 +08:00
c66070a30f feat: 更新 cesium-helper 的 README.md,优化链接和格式
[skip ci]
2025-03-17 10:51:30 +08:00
01adfcd908 chore: 更新 package.json,优化依赖命令并添加 knip 工具
All checks were successful
/ lint-build-and-check (push) Successful in 2m32s
/ depcheck (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 3m4s
/ surge (push) Successful in 2m50s
/ playwright (push) Successful in 4m35s
2025-03-16 19:36:49 +08:00
eafe01fcb3 feat: 更新 cesium-helper 的 README.md,添加 vue-vite-cesium-demo 链接
[skip ci]
2025-03-16 17:36:59 +08:00
a5be55dcf7 chore(deps): update dependency @types/node to ^22.13.10
All checks were successful
/ depcheck (push) Successful in 2m8s
/ lint-build-and-check (push) Successful in 2m49s
/ build-and-deploy-to-vercel (push) Successful in 3m16s
/ surge (push) Successful in 2m37s
/ playwright (push) Successful in 4m36s
2025-03-16 17:28:13 +08:00
0f04b268b6 chore(deps): update yanhao98/composite-actions digest to a7c66fb
Some checks failed
/ playwright (push) Blocked by required conditions
/ depcheck (push) Successful in 2m9s
/ lint-build-and-check (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ surge (push) Has been cancelled
2025-03-16 17:25:25 +08:00
74631308f6 feat: 更新 cesium-helper 的 README.md,添加新的链接
[skip ci]
2025-03-16 17:24:56 +08:00
ffdc49ec2a chore(deps): update dependency typescript to ~5.8.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 2m8s
/ lint-build-and-check (push) Successful in 2m52s
/ build-and-deploy-to-vercel (push) Successful in 3m7s
/ surge (push) Successful in 2m39s
/ playwright (push) Successful in 4m15s
2025-03-14 20:24:25 +08:00
29c9eee458 feat: 更新 README.md,添加卫星相关链接
All checks were successful
/ depcheck (push) Successful in 2m21s
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ lint-build-and-check (push) Successful in 3m8s
/ playwright (push) Successful in 5m56s
/ surge (push) Successful in 2m35s
2025-03-14 17:58:09 +08:00
f7137cb6e5 feat: 更新工具提示,添加 REV_AT_EPOCH 的校验和说明
All checks were successful
/ depcheck (push) Successful in 2m21s
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ lint-build-and-check (push) Successful in 3m8s
/ surge (push) Successful in 2m42s
/ playwright (push) Successful in 5m50s
2025-03-14 15:28:56 +08:00
9f0bed4513 demo_03_卫星加站点
All checks were successful
/ depcheck (push) Successful in 2m26s
/ lint-build-and-check (push) Successful in 3m6s
/ build-and-deploy-to-vercel (push) Successful in 3m2s
/ surge (push) Successful in 2m46s
/ playwright (push) Successful in 5m26s
2025-03-14 14:42:06 +08:00
8963fe99ff feat: 更新 utils4u 依赖至 4.2.1 版本
All checks were successful
/ depcheck (push) Successful in 2m6s
/ lint-build-and-check (push) Successful in 2m44s
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ playwright (push) Successful in 4m16s
/ surge (push) Successful in 2m53s
2025-03-14 14:04:26 +08:00
31909c906d feat: 添加 eslint-plugin-unicorn 依赖,更新 ESLint 配置以支持新规则
All checks were successful
/ depcheck (push) Successful in 2m3s
/ build-and-deploy-to-vercel (push) Successful in 2m52s
/ lint-build-and-check (push) Successful in 2m57s
/ surge (push) Successful in 2m32s
/ playwright (push) Successful in 3m10s
2025-03-14 12:50:54 +08:00
8b12d3e3dd feat: 更新 vue-component-type-helpers 依赖至 2.2.8 版本
All checks were successful
/ depcheck (push) Successful in 2m4s
/ lint-build-and-check (push) Successful in 3m0s
/ build-and-deploy-to-vercel (push) Successful in 3m31s
/ surge (push) Successful in 2m26s
/ playwright (push) Successful in 3m32s
2025-03-14 11:43:21 +08:00
e528005e7c docs: 更新 README 文件,修改项目配置和 TLE 数据链接
All checks were successful
/ depcheck (push) Successful in 2m23s
/ lint-build-and-check (push) Successful in 2m39s
/ build-and-deploy-to-vercel (push) Successful in 3m14s
/ playwright (push) Successful in 5m16s
/ surge (push) Successful in 2m27s
2025-03-13 16:52:03 +08:00
7d98cb0074 feat: 添加卫星数量动态选择按钮,优化布局和交互
All checks were successful
/ depcheck (push) Successful in 2m7s
/ lint-build-and-check (push) Successful in 3m6s
/ build-and-deploy-to-vercel (push) Successful in 3m11s
/ surge (push) Successful in 2m26s
/ playwright (push) Successful in 3m47s
2025-03-12 19:15:46 +08:00
091b3d1e29 feat: 添加卫星数据加载按钮,优化初始加载逻辑
Some checks failed
/ lint-build-and-check (push) Waiting to run
/ surge (push) Waiting to run
/ playwright (push) Blocked by required conditions
/ build-and-deploy-to-vercel (push) Waiting to run
/ depcheck (push) Has been cancelled
2025-03-12 19:12:40 +08:00
8b702826b3 feat: 更新 TLE 数据处理和示例,完善卫星轨道参数解析逻辑
Some checks are pending
/ lint-build-and-check (push) Successful in 2m35s
/ depcheck (push) Successful in 2m38s
/ playwright (push) Waiting to run
/ surge (push) Successful in 2m33s
/ build-and-deploy-to-vercel (push) Successful in 3m10s
2025-03-12 19:02:11 +08:00
447907aa7c feat: 优化卫星页面加载和状态管理,引入加载动画和响应式数据处理
All checks were successful
/ depcheck (push) Successful in 2m11s
/ lint-build-and-check (push) Successful in 3m11s
/ build-and-deploy-to-vercel (push) Successful in 3m22s
/ surge (push) Successful in 2m30s
/ playwright (push) Successful in 5m13s
2025-03-12 18:33:26 +08:00
38f35328b3 refactor: 重构卫星选择器组件,优化布局和交互细节
All checks were successful
/ lint-build-and-check (push) Successful in 2m35s
/ depcheck (push) Successful in 2m39s
/ build-and-deploy-to-vercel (push) Successful in 3m10s
/ surge (push) Successful in 2m40s
/ playwright (push) Successful in 6m7s
2025-03-12 14:34:20 +08:00
4fba5091be fix: 修复卫星名称提取逻辑,增强类型安全性
Some checks failed
/ playwright (push) Blocked by required conditions
/ depcheck (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
/ surge (push) Has been cancelled
2025-03-12 14:32:16 +08:00
92d795ba88 refactor: 重构卫星选择器为独立组件,优化代码结构和性能
Some checks failed
/ playwright (push) Blocked by required conditions
/ depcheck (push) Successful in 2m10s
/ build-and-deploy-to-vercel (push) Has been cancelled
/ surge (push) Has been cancelled
/ lint-build-and-check (push) Has been cancelled
2025-03-12 14:29:38 +08:00
ff46fdd637 feat: 优化卫星选择器标题显示,增强用户交互体验
All checks were successful
/ depcheck (push) Successful in 2m8s
/ lint-build-and-check (push) Successful in 2m34s
/ build-and-deploy-to-vercel (push) Successful in 3m14s
/ surge (push) Successful in 3m6s
/ playwright (push) Successful in 5m3s
2025-03-12 14:06:19 +08:00
ab38d2a3f6 feat: 优化卫星选择器并完善 Cesium 页面交互
All checks were successful
/ depcheck (push) Successful in 2m9s
/ lint-build-and-check (push) Successful in 2m49s
/ build-and-deploy-to-vercel (push) Successful in 3m6s
/ surge (push) Successful in 3m6s
/ playwright (push) Successful in 5m2s
2025-03-12 12:12:41 +08:00
80fb2f66ee chore(deps): update dependency @vueuse/core to v13
All checks were successful
/ depcheck (push) Successful in 2m4s
/ lint-build-and-check (push) Successful in 2m54s
/ build-and-deploy-to-vercel (push) Successful in 2m56s
/ surge (push) Successful in 2m38s
/ playwright (push) Successful in 3m22s
2025-03-12 07:59:00 +08:00
203ab3da7d feat: 优化卫星实体类,更新 TLE 处理逻辑并增强类型定义
All checks were successful
/ depcheck (push) Successful in 2m7s
/ lint-build-and-check (push) Successful in 2m49s
/ build-and-deploy-to-vercel (push) Successful in 3m29s
/ surge (push) Successful in 2m31s
/ playwright (push) Successful in 4m17s
2025-03-11 18:21:40 +08:00
a2f07c7d76 feat: 添加卫星实体类及相关功能,优化 Cesium 初始化和时间线设置
All checks were successful
/ depcheck (push) Successful in 2m5s
/ lint-build-and-check (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 3m1s
/ surge (push) Successful in 3m3s
/ playwright (push) Successful in 5m52s
2025-03-11 16:01:55 +08:00
ec49e340ea fix: 更新接口请求页面的 URL 路径
All checks were successful
/ depcheck (push) Successful in 2m4s
/ lint-build-and-check (push) Successful in 2m39s
/ surge (push) Successful in 2m33s
/ playwright (push) Successful in 2m41s
/ build-and-deploy-to-vercel (push) Successful in 6m59s
2025-03-11 14:09:38 +08:00
4542944f52 整理
Some checks failed
/ depcheck (push) Successful in 2m24s
/ lint-build-and-check (push) Successful in 2m36s
/ build-and-deploy-to-vercel (push) Successful in 3m13s
/ surge (push) Successful in 2m40s
/ playwright (push) Failing after 8m16s
2025-03-10 13:15:38 +08:00
a7b10809c1 feat: 添加假 API 接口以获取用户信息,并更新 Cesium 示例代码 2025-03-10 12:00:45 +08:00
ca1521328c chore: 更新 setup-node-environment 版本并添加静态代码分析工作流
All checks were successful
/ depcheck (push) Successful in 2m24s
/ lint-build-and-check (push) Successful in 2m35s
/ build-and-deploy-to-vercel (push) Successful in 3m4s
/ surge (push) Successful in 15m42s
/ playwright (push) Successful in 3m11s
2025-03-10 11:02:45 +08:00
8733c3e42e chore(deps): 升级 oxlint
All checks were successful
/ depcheck (push) Successful in 2m3s
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ surge (push) Successful in 3m27s
/ playwright (push) Successful in 2m48s
2025-03-09 23:30:29 +08:00
35a658bfe4 feat: /cesium
All checks were successful
/ depcheck (push) Successful in 2m19s
/ build-and-deploy-to-vercel (push) Successful in 3m9s
/ surge (push) Successful in 2m45s
/ playwright (push) Successful in 4m5s
2025-03-07 15:27:21 +08:00
c8f2fe83fa feat: 添加额外的离线地图链接以丰富文档内容
All checks were successful
/ depcheck (push) Successful in 2m10s
/ build-and-deploy-to-vercel (push) Successful in 3m0s
/ surge (push) Successful in 2m40s
/ playwright (push) Successful in 4m49s
2025-03-06 17:36:22 +08:00
9eaf7f5791 feat: 添加 Cesium 配置文件和离线地图支持,优化初始化选项
All checks were successful
/ depcheck (push) Successful in 2m9s
/ build-and-deploy-to-vercel (push) Successful in 2m59s
/ playwright (push) Successful in 4m31s
/ surge (push) Successful in 2m58s
2025-03-06 16:33:33 +08:00
1d251b4e1c feat: 添加离线地图链接并优化 Cesium 初始化配置
All checks were successful
/ depcheck (push) Successful in 2m9s
/ build-and-deploy-to-vercel (push) Successful in 2m58s
/ surge (push) Successful in 2m58s
/ playwright (push) Successful in 4m36s
2025-03-06 15:50:02 +08:00
3acea1577b refactor: 重命名变量以提高代码可读性
All checks were successful
/ depcheck (push) Successful in 2m22s
/ build-and-deploy-to-vercel (push) Successful in 3m10s
/ surge (push) Successful in 2m39s
/ playwright (push) Successful in 4m3s
2025-03-05 12:50:26 +08:00
8c1b8987c5 feat: 更新轨道演示功能,添加未来位置注释并优化相机跟踪设置
Some checks failed
/ depcheck (push) Successful in 2m9s
/ build-and-deploy-to-vercel (push) Failing after 45s
/ surge (push) Successful in 2m46s
/ playwright (push) Successful in 4m43s
2025-03-05 12:30:42 +08:00
4fd1e1b992 feat: 添加轨道生成演示功能
Some checks failed
/ depcheck (push) Successful in 2m14s
/ build-and-deploy-to-vercel (push) Failing after 48s
/ surge (push) Successful in 2m44s
/ playwright (push) Successful in 3m58s
2025-03-05 11:40:55 +08:00
2b7186ef69 chore: eslint 配置
All checks were successful
/ depcheck (push) Successful in 2m19s
/ playwright (push) Successful in 1m45s
/ surge (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 3m10s
2025-03-05 00:57:59 +08:00
feb7659b75 feat: 在轨道生成演示中添加确认提示,增强用户交互体验
Some checks failed
/ depcheck (push) Successful in 2m17s
/ build-and-deploy-to-vercel (push) Failing after 49s
/ playwright (push) Successful in 1m48s
/ surge (push) Successful in 3m13s
2025-03-05 00:48:02 +08:00
dc318c04e3 feat: 添加 Cesium 相关功能和配置,更新样式及文档
Some checks failed
/ depcheck (push) Successful in 2m14s
/ build-and-deploy-to-vercel (push) Failing after 51s
/ surge (push) Successful in 2m31s
/ playwright (push) Successful in 1m21s
2025-03-05 00:42:30 +08:00
49a7ef0dae fix: ERR_PNPM_LOCKFILE_CONFIG_MISMATCH
All checks were successful
/ depcheck (push) Successful in 2m7s
/ build-and-deploy-to-vercel (push) Successful in 2m31s
/ surge (push) Successful in 1m53s
/ playwright (push) Successful in 3m21s
2025-03-02 16:48:49 +08:00
41a036e60f chore: 更新依赖项版本
Some checks failed
/ depcheck (push) Failing after 5s
/ playwright (push) Has been skipped
/ surge (push) Failing after 13s
/ build-and-deploy-to-vercel (push) Failing after 10s
2025-03-01 18:30:11 +08:00
c7047146e4 chore: eslint.config.ts
All checks were successful
/ depcheck (push) Successful in 2m12s
/ build-and-deploy-to-vercel (push) Successful in 2m22s
/ surge (push) Successful in 1m53s
/ playwright (push) Successful in 3m18s
2025-03-01 18:20:11 +08:00
94b71c5c47 feat: 添加 HAPopconfirm 组件并更新 index.page.vue 以集成确认功能
Some checks failed
/ depcheck (push) Successful in 2m2s
/ surge (push) Successful in 2m2s
/ playwright (push) Successful in 4m1s
/ build-and-deploy-to-vercel (push) Failing after 44s
2025-02-28 14:47:10 +08:00
8c855b5b3d feat: 更新 ESLint 配置,添加宏定义顺序和导入后换行规则
Some checks failed
/ depcheck (push) Successful in 2m13s
/ build-and-deploy-to-vercel (push) Failing after 39s
/ surge (push) Successful in 2m1s
/ playwright (push) Successful in 4m31s
2025-02-28 11:56:18 +08:00
4a4118c2dc 重命名 APopconfirmButton 为 HAPopconfirmButton 并更新相关引用
All checks were successful
/ depcheck (push) Successful in 2m12s
/ build-and-deploy-to-vercel (push) Successful in 2m19s
/ surge (push) Successful in 2m1s
/ playwright (push) Successful in 4m55s
2025-02-28 11:41:02 +08:00
7375b66360 更新 AntdV 组件页面布局
All checks were successful
/ depcheck (push) Successful in 2m14s
/ build-and-deploy-to-vercel (push) Successful in 2m36s
/ surge (push) Successful in 2m11s
/ playwright (push) Successful in 3m44s
2025-02-28 09:54:06 +08:00
5ada043ee9 feat: 添加 APopconfirmButton 组件并更新 index.page.vue 以集成确认删除功能
All checks were successful
/ depcheck (push) Successful in 2m15s
/ build-and-deploy-to-vercel (push) Successful in 2m34s
/ surge (push) Successful in 2m21s
/ playwright (push) Successful in 3m46s
2025-02-28 00:35:13 +08:00
ff4718c54b chore(deps): pin dependency prettier to v3.4.2
All checks were successful
/ depcheck (push) Successful in 2m14s
/ build-and-deploy-to-vercel (push) Successful in 2m31s
/ surge (push) Successful in 2m16s
/ playwright (push) Successful in 3m43s
2025-02-27 13:12:20 +08:00
1f2911c096 feat: update ESLint configuration and package dependencies, add Ant Design Vue
All checks were successful
/ depcheck (push) Successful in 2m38s
/ build-and-deploy-to-vercel (push) Successful in 2m21s
/ surge (push) Successful in 2m33s
/ playwright (push) Successful in 3m21s
2025-02-27 12:28:58 +08:00
8aef352614 chore(deps): update dependency pinia to v3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m36s
/ build-and-deploy-to-vercel (push) Successful in 2m5s
/ surge (push) Successful in 8m2s
/ playwright (push) Successful in 3m40s
2025-02-27 05:46:06 +08:00
97431f932f feat: add unocss-preset-chinese and update related configurations
All checks were successful
/ depcheck (push) Successful in 1m39s
/ build-and-deploy-to-vercel (push) Successful in 2m7s
/ surge (push) Successful in 1m47s
/ playwright (push) Successful in 3m23s
2025-02-25 00:27:05 +08:00
4552bbca34 chore: update Node.js version and dependencies to latest releases
All checks were successful
/ depcheck (push) Successful in 1m13s
/ build-and-deploy-to-vercel (push) Successful in 1m46s
/ surge (push) Successful in 1m59s
/ playwright (push) Successful in 5m46s
2025-02-21 10:03:18 +08:00
055b5d2a9e chore(deps): update unocss monorepo to v66
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ depcheck (push) Successful in 1m36s
/ build-and-deploy-to-vercel (push) Successful in 2m2s
/ surge (push) Successful in 1m19s
/ playwright (push) Successful in 2m24s
2025-02-18 19:36:22 +08:00
a70e33f209 fix: remove duplicate entry for utils4u in package.json
All checks were successful
/ depcheck (push) Successful in 1m17s
/ build-and-deploy-to-vercel (push) Successful in 1m43s
/ surge (push) Successful in 1m42s
/ playwright (push) Successful in 2m57s
2025-02-13 19:13:50 +08:00
3b22aba719 fix: pnpm-lock.yaml is not up to date with <ROOT>/package.json
All checks were successful
/ depcheck (push) Successful in 1m38s
/ build-and-deploy-to-vercel (push) Successful in 1m42s
/ surge (push) Successful in 2m9s
/ playwright (push) Successful in 2m3s
2025-02-12 14:27:37 +08:00
cb6cf464ee Merge branch 'sat'
Some checks failed
/ depcheck (push) Failing after 3s
/ surge (push) Failing after 21s
/ playwright (push) Has been skipped
/ build-and-deploy-to-vercel (push) Failing after 2s
2025-02-11 19:28:55 +08:00
d394592a6b fix(SAT): update ISS data source assignment and correct CESIUM_BASE_URL to HTTPS
All checks were successful
/ depcheck (push) Successful in 1m37s
/ build-and-deploy-to-vercel (push) Successful in 1m45s
/ surge (push) Successful in 1m17s
/ playwright (push) Successful in 2m55s
2025-02-11 18:58:13 +08:00
d3548f1105 chore: update dev script to allow Vite to bind to all network interfaces
All checks were successful
/ depcheck (push) Successful in 1m17s
/ build-and-deploy-to-vercel (push) Successful in 1m41s
/ surge (push) Successful in 6m16s
/ playwright (push) Successful in 2m39s
2025-02-11 18:36:10 +08:00
6a3560a7f8 chore(deps): update orbpro to version 1.126.0; set CESIUM_BASE_URL in SAT page
All checks were successful
/ depcheck (push) Successful in 1m55s
/ surge (push) Successful in 1m38s
/ playwright (push) Successful in 1m10s
/ build-and-deploy-to-vercel (push) Successful in 2m13s
2025-02-11 18:22:06 +08:00
e14c897b2c chore: update Vercel workflow to include 'sat' branch for deployment
All checks were successful
/ depcheck (push) Successful in 1m11s
/ surge (push) Successful in 36s
/ playwright (push) Successful in 2m23s
2025-02-10 17:35:06 +08:00
19e46190f1 feat(SAT): add CesiumViewer component and utility functions; update dependencies
All checks were successful
/ depcheck (push) Successful in 1m58s
/ build-and-deploy-to-vercel (push) Successful in 2m4s
/ surge (push) Successful in 1m44s
/ playwright (push) Successful in 2m4s
2025-02-10 17:34:24 +08:00
64fa5bbc93 chore(deps): update dependency vercel to v41
All checks were successful
/ depcheck (push) Successful in 1m35s
/ build-and-deploy-to-vercel (push) Successful in 1m4s
/ surge (push) Successful in 45s
/ playwright (push) Successful in 2m6s
2025-02-09 07:23:37 +08:00
8962d65ed3 chore(deps): update dependency utils4u to v4
Some checks failed
/ playwright (push) Blocked by required conditions
/ build-and-deploy-to-vercel (push) Waiting to run
/ surge (push) Has been cancelled
/ depcheck (push) Has been cancelled
2025-02-09 07:22:28 +08:00
f26342ddfc chore(ci): update Vercel workflow to trigger on main and sat branches
All checks were successful
/ depcheck (push) Successful in 1m16s
/ build-and-deploy-to-vercel (push) Successful in 1m44s
/ surge (push) Successful in 1m40s
/ playwright (push) Successful in 1m37s
2025-02-08 13:45:38 +08:00
503d1b27ef chore(deps): update vite packages
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ depcheck (push) Successful in 1m31s
/ build-and-deploy-to-vercel (push) Successful in 1m6s
/ surge (push) Successful in 39s
/ playwright (push) Successful in 4m35s
2025-02-08 11:47:41 +08:00
c465fd6fec chore: refactor countdown logic and update utils4u auto imports
Some checks failed
/ build-and-deploy-to-vercel (push) Has been cancelled
/ depcheck (push) Successful in 1m42s
/ surge (push) Successful in 39s
/ playwright (push) Successful in 2m34s
2025-02-08 10:39:36 +08:00
d533738e0a chore(deps): update vercel to ^41.0.2
All checks were successful
/ depcheck (push) Successful in 1m18s
/ surge (push) Successful in 1m46s
/ playwright (push) Successful in 2m3s
/ build-and-deploy-to-vercel (push) Successful in 1m43s
2025-02-07 18:27:58 +08:00
d4b8585143 feat(SAT): add SAT page and viewer component; update dependencies
Some checks failed
/ playwright (push) Blocked by required conditions
/ surge (push) Has been cancelled
/ depcheck (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2025-02-07 18:26:25 +08:00
1d5a387ae0 chore(deps): update pnpm to v10
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m4s
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 2m28s
/ surge (push) Successful in 58s
/ playwright (push) Successful in 2m40s
2025-02-06 07:46:22 +08:00
63d21602fc chore(deps): update all non-major dependencies
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m12s
/ depcheck (push) Successful in 1m32s
/ surge (push) Successful in 2m13s
/ playwright (push) Successful in 2m4s
2025-02-05 21:00:52 +08:00
74452b748a chore(deps): update dependency unplugin-vue-router to ^0.11.1
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m18s
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ playwright (push) Successful in 1m48s
/ surge (push) Successful in 1m2s
2025-02-05 13:43:58 +08:00
72c638894f chore(ci): enable Playwright browser installation in workflow
All checks were successful
/ depcheck (push) Successful in 1m16s
/ build-and-deploy-to-vercel (push) Successful in 2m48s
/ surge (push) Successful in 1m2s
/ playwright (push) Successful in 2m4s
2025-02-05 12:14:07 +08:00
f98225aebf chore(deps): update node.js to v22.13.1
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m13s
/ build-and-deploy-to-vercel (push) Successful in 1m29s
/ surge (push) Successful in 1m4s
/ playwright (push) Failing after 11m39s
2025-02-04 08:19:02 +08:00
581dd237ca chore(deps): update dependency unplugin-vue-markdown to ^28.1.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m17s
/ build-and-deploy-to-vercel (push) Successful in 1m13s
/ surge (push) Successful in 2m20s
/ playwright (push) Successful in 1m48s
2025-02-01 21:00:52 +08:00
1fe225cf05 chore(deps): update all non-major dependencies to ^65.4.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m18s
/ depcheck (push) Successful in 2m25s
/ surge (push) Successful in 1m5s
/ playwright (push) Successful in 1m59s
2025-01-30 19:13:16 +08:00
f3b4ee26fe chore(deps): update dependency @types/node to ^22.10.7
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m18s
/ build-and-deploy-to-vercel (push) Successful in 1m31s
/ surge (push) Successful in 2m10s
/ playwright (push) Successful in 1m53s
2025-01-30 10:47:42 +08:00
e03064cddd chore(deps): update dependency taze to ^18.2.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m28s
/ depcheck (push) Successful in 2m20s
/ surge (push) Successful in 46s
/ playwright (push) Successful in 1m3s
2025-01-29 13:55:07 +08:00
c34dbceb2c chore(deps): update v0.x to v65
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m25s
/ build-and-deploy-to-vercel (push) Successful in 2m39s
/ playwright (push) Successful in 1m48s
/ surge (push) Successful in 1m12s
2025-01-28 05:46:15 +08:00
01525ddbc2 chore(deps): update dependency @types/node to ^22.10.6
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m19s
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ surge (push) Successful in 2m16s
/ playwright (push) Successful in 1m50s
2025-01-28 03:16:57 +08:00
525fb84811 更新 package.json
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ depcheck (push) Successful in 2m20s
/ surge (push) Successful in 52s
/ playwright (push) Successful in 1m25s
2025-01-24 14:33:53 +08:00
7239da28b1 chore(deps): update v0.x
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m30s
/ playwright (push) Successful in 1m6s
/ surge (push) Successful in 52s
/ depcheck (push) Successful in 2m27s
2025-01-24 09:09:35 +08:00
14fdb7ef1f chore(ci): reorganize CI workflows and add depcheck and playwright jobs
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m39s
/ depcheck (push) Successful in 2m20s
/ playwright (push) Successful in 1m18s
/ surge (push) Successful in 52s
2025-01-24 00:28:13 +08:00
a7312f402f chore(deps): update dependency typescript to ~5.7.3
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m30s
/ depcheck (push) Successful in 1m25s
/ playwright (push) Failing after 6m23s
2025-01-23 03:15:31 +08:00
f6414e5ce0 chore(deps): update vite packages
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m23s
/ depcheck (push) Successful in 1m37s
/ playwright (push) Failing after 8m31s
2025-01-22 21:18:15 +08:00
af32fe53a8 chore(deps): update v0.x
Some checks failed
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Failing after 6m16s
/ depcheck (push) Successful in 2m42s
/ playwright (push) Failing after 2m30s
2025-01-22 21:14:40 +08:00
c8ae17353e chore(deps): update v0.x to ^0.65.4
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m21s
/ depcheck (push) Successful in 1m23s
/ playwright (push) Failing after 2m24s
2025-01-22 19:00:06 +08:00
2d59658f1d chore(deps): update dependency vite-plugin-webfont-dl to ^3.10.4
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Failing after 6m29s
/ depcheck (push) Successful in 2m34s
/ playwright (push) Failing after 2m37s
2025-01-22 18:56:55 +08:00
536f879930 feat: 移除不再使用的 ESLint 插件和类型定义,优化依赖项
All checks were successful
/ playwright (push) Successful in 1m24s
/ depcheck (push) Successful in 6m29s
/ build-and-deploy-to-vercel (push) Successful in 1m56s
2025-01-22 12:01:38 +08:00
53188ff9cf feat: 更新 ESLint 配置,支持更多语言,调整 npm 镜像源
Some checks failed
/ depcheck (push) Successful in 1m32s
/ playwright (push) Successful in 1m41s
/ build-and-deploy-to-vercel (push) Failing after 6m28s
2025-01-22 11:54:35 +08:00
b0cd8b7c61 feat: 添加 eslint-plugin-perfectionist 插件,优化导入排序规则
All checks were successful
/ playwright (push) Successful in 1m27s
/ depcheck (push) Successful in 1m38s
/ build-and-deploy-to-vercel (push) Successful in 2m44s
2025-01-21 23:05:11 +08:00
a7b6d6ea75 chore(deps): update all non-major dependencies
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ depcheck (push) Successful in 1m27s
/ build-and-deploy-to-vercel (push) Successful in 1m32s
/ playwright (push) Successful in 2m49s
2025-01-20 18:36:08 +08:00
cc914c9a80 chore(deps): update node.js to v22.13.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 9m30s
/ build-and-deploy-to-vercel (push) Successful in 2m19s
/ playwright (push) Successful in 2m15s
2025-01-20 08:34:01 +08:00
83117bbf4b feat: 优化菜单项,添加路由名称和标题支持,更新首页元信息
All checks were successful
/ playwright (push) Successful in 1m21s
/ build-and-deploy-to-vercel (push) Successful in 1m24s
/ depcheck (push) Successful in 1m2s
2025-01-08 17:41:36 +08:00
943eac62df feat: 添加日期选择器和分页器组件,优化页面布局和用户交互
Some checks failed
/ depcheck (push) Successful in 1m28s
/ playwright (push) Failing after 2m44s
/ build-and-deploy-to-vercel (push) Successful in 52s
2025-01-08 17:23:15 +08:00
f86afbdb2c feat: 更新确认对话框的拒绝按钮属性,优化用户交互体验 [no ci] 2025-01-08 15:25:45 +08:00
7258853b57 feat: 添加路由元信息支持,优化无限加载组件的状态管理和错误处理
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 55s
/ depcheck (push) Successful in 1m24s
/ playwright (push) Failing after 2m50s
2025-01-08 14:48:01 +08:00
d4b34a4f1f feat: 更新 utils4u、vercel 和 vite-plugin-vue-devtools 依赖至最新版本
All checks were successful
/ playwright (push) Successful in 58s
/ depcheck (push) Successful in 1m35s
/ build-and-deploy-to-vercel (push) Successful in 1m30s
2025-01-08 10:57:55 +08:00
df0ccdaed1 feat: 添加 terser 作为构建工具的压缩选项
All checks were successful
/ playwright (push) Successful in 56s
/ depcheck (push) Successful in 1m46s
/ build-and-deploy-to-vercel (push) Successful in 1m39s
2025-01-08 10:25:05 +08:00
3292800f0d feat: 优化插件设置逻辑,支持自动安装模块并增强调试信息
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m20s
/ depcheck (push) Successful in 58s
/ playwright (push) Successful in 1m51s
2025-01-08 10:17:07 +08:00
b70da8aad4 feat: 重构插件初始化逻辑,简化应用启动流程并添加调试工具支持
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m19s
/ depcheck (push) Successful in 1m31s
/ playwright (push) Successful in 1m14s
2025-01-08 09:35:21 +08:00
be321165c8 feat: 更新依赖版本,包括 @unhead/vue、tdesign-icons-vue-next 和 utils4u,调整 VueMacros 路由路径
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m40s
/ depcheck (push) Successful in 1m21s
/ playwright (push) Successful in 1m57s
2025-01-07 18:45:13 +08:00
cfd71c8364 feat: 更新 pnpm 依赖至 9.15.3 版本 [no ci] 2025-01-07 13:59:57 +08:00
f76bb5d0e2 feat: 更新 utils4u 依赖至 3.1.0 版本
All checks were successful
/ depcheck (push) Successful in 1m45s
/ playwright (push) Successful in 58s
/ build-and-deploy-to-vercel (push) Successful in 1m31s
2025-01-07 13:59:14 +08:00
1a0688bf54 feat: 添加字体页面,展示相关链接和信息
Some checks failed
/ depcheck (push) Successful in 1m8s
/ build-and-deploy-to-vercel (push) Successful in 1m38s
/ playwright (push) Failing after 2m29s
2025-01-06 10:44:38 +08:00
72a51b7fc7 feat: 更新 CI 工作流,添加构建项目的注释
Some checks failed
/ depcheck (push) Successful in 1m50s
/ build-and-deploy-to-vercel (push) Successful in 12m10s
/ playwright (push) Failing after 10m49s
2025-01-05 23:03:31 +08:00
b638926537 feat: 更新 CI 工作流,调整构建和类型检查顺序
Some checks failed
/ playwright (push) Has been cancelled
/ depcheck (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2025-01-05 23:02:24 +08:00
6309f1a7d7 feat: 更新触摸模拟器脚本链接,替换为 unpkg 源
Some checks failed
/ depcheck (push) Successful in 1m23s
/ build-and-deploy-to-vercel (push) Successful in 12m53s
/ playwright (push) Has been cancelled
2025-01-05 22:16:04 +08:00
1713315c08 feat: 优化样式和简化 DatePicker 组件的代码
All checks were successful
/ depcheck (push) Successful in 1m5s
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ playwright (push) Successful in 1m5s
2025-01-03 18:26:42 +08:00
a1cf330f9a feat: 更新 unocss 配置,添加新的颜色变量和文本颜色定义
Some checks failed
/ build-and-deploy-to-vercel (push) Has been cancelled
/ depcheck (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-01-03 18:25:37 +08:00
4750d0dd43 feat: 删除不必要的页面和组件,更新路由类型定义,添加新链接到文档
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m30s
/ depcheck (push) Successful in 1m27s
/ playwright (push) Successful in 1m5s
2025-01-03 17:12:06 +08:00
181213526e feat: 添加 JSPage 页面,支持 JavaScript 文件,更新 ESLint 和 TypeScript 配置
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ depcheck (push) Successful in 1m7s
/ playwright (push) Failing after 2m35s
2025-01-03 17:06:47 +08:00
f5c0cb8665 feat: 重构页面结构,删除不必要的页面,添加新页面,优化暗黑主题样式
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ depcheck (push) Successful in 1m7s
/ playwright (push) Failing after 2m28s
2025-01-03 16:47:30 +08:00
263f3f6dfa feat: 更新 DatePicker 组件,添加手动输入和输入 ID,优化标签关联
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m35s
/ playwright (push) Failing after 2m26s
/ depcheck (push) Successful in 1m5s
2025-01-03 16:00:40 +08:00
6f35ac8105 feat: 更新依赖项版本,添加 vue-tsc 作为覆盖项,修复 primevue 版本号
All checks were successful
/ depcheck (push) Successful in 1m39s
/ playwright (push) Successful in 1m46s
/ build-and-deploy-to-vercel (push) Successful in 1m18s
2025-01-03 14:03:12 +08:00
44b55323a6 feat: 更新加载更多提示文本,升级依赖项以修复潜在问题
Some checks failed
/ depcheck (push) Failing after 4s
/ playwright (push) Failing after 10s
/ build-and-deploy-to-vercel (push) Failing after 3s
2025-01-03 11:29:51 +08:00
09d2779f7c feat: 更新 TypeScript 配置文件,添加引用链接以便于维护和参考
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m32s
/ playwright (push) Successful in 1m3s
/ depcheck (push) Successful in 1m28s
2025-01-02 23:12:58 +08:00
47e2c5d813 feat: 更新 Vite 配置,添加手动分块注释以优化构建输出
All checks were successful
/ depcheck (push) Successful in 1m34s
/ playwright (push) Successful in 1m54s
/ build-and-deploy-to-vercel (push) Successful in 1m36s
2025-01-02 23:09:12 +08:00
78ec619f23 feat: 添加 .editorconfig 文件,更新 ESLint 配置,修改 TypeScript 配置以支持 Node 22,优化依赖项版本
All checks were successful
/ depcheck (push) Successful in 1m31s
/ build-and-deploy-to-vercel (push) Successful in 2m6s
/ playwright (push) Successful in 2m27s
2025-01-02 22:22:51 +08:00
d132ae7484 feat: 修复无限加载页面的错误处理逻辑,优化数据加载和缓存更新
All checks were successful
/ depcheck (push) Successful in 1m32s
/ build-and-deploy-to-vercel (push) Successful in 1m16s
/ playwright (push) Successful in 2m0s
2025-01-02 17:12:08 +08:00
24966a3b7d feat: 重构缓存逻辑,优化数据更新和清空缓存的实现 2025-01-02 16:52:56 +08:00
d30163351a feat: 添加深冻结库,重构无限加载页面,优化缓存逻辑并新增详情页
All checks were successful
/ depcheck (push) Successful in 1m16s
/ build-and-deploy-to-vercel (push) Successful in 1m47s
/ playwright (push) Successful in 1m55s
2025-01-02 16:18:37 +08:00
49da2ee1d8 feat: 修改用户数据返回类型,优化用户信息展示逻辑
Some checks failed
/ depcheck (push) Successful in 1m32s
/ build-and-deploy-to-vercel (push) Successful in 1m10s
/ playwright (push) Failing after 2m21s
2025-01-02 14:46:17 +08:00
087c593b58 feat: 移除未使用的提示服务功能,优化按钮逻辑以支持不同的提示类型
Some checks failed
/ depcheck (push) Successful in 1m21s
/ build-and-deploy-to-vercel (push) Successful in 2m5s
/ playwright (push) Failing after 2m48s
2025-01-02 11:49:44 +08:00
ce16720368 feat: 更新样式和组件,修复文件上传相关问题,升级依赖版本 2025-01-02 11:49:44 +08:00
a440113044 feat: 在发送验证码成功后添加提示消息 2025-01-02 11:49:44 +08:00
ed9e505f0c feat: 优化样式,修复文件上传组件的布局问题并增强消息文本的换行处理
All checks were successful
/ depcheck (push) Successful in 1m36s
/ playwright (push) Successful in 2m52s
/ build-and-deploy-to-vercel (push) Successful in 2m6s
2025-01-01 23:43:31 +08:00
b46edb4aeb feat: 优化发送验证码按钮,增加加载状态并调整倒计时逻辑
All checks were successful
/ depcheck (push) Successful in 1m28s
/ playwright (push) Successful in 1m6s
/ build-and-deploy-to-vercel (push) Successful in 1m39s
2024-12-30 14:39:54 +08:00
0b09c3dbb6 feat: 修复 flex 布局子元素宽度超出父元素的问题
Some checks failed
/ depcheck (push) Successful in 1m28s
/ build-and-deploy-to-vercel (push) Successful in 1m15s
/ playwright (push) Failing after 2m24s
2024-12-30 14:29:43 +08:00
ee5db6d91c feat: 添加新的页面组件 src/pages/a/b/index.page.vue
Some checks failed
/ playwright (push) Failing after 2m20s
/ build-and-deploy-to-vercel (push) Successful in 1m36s
/ depcheck (push) Successful in 1m10s
2024-12-30 12:58:01 +08:00
7df5552b05 feat: 删除不再使用的 Vue 组件,清理代码库
All checks were successful
/ depcheck (push) Successful in 1m30s
/ build-and-deploy-to-vercel (push) Successful in 1m41s
/ playwright (push) Successful in 1m8s
2024-12-30 12:15:38 +08:00
1c53703092 feat: 更新 openToast 函数,优化消息摘要格式以提高可读性
All checks were successful
/ depcheck (push) Successful in 1m23s
/ playwright (push) Successful in 1m7s
/ build-and-deploy-to-vercel (push) Successful in 1m41s
2024-12-30 10:59:50 +08:00
6918d26da2 feat: 优化 ToastService 消息添加逻辑,支持多种消息类型
Some checks failed
/ depcheck (push) Successful in 1m4s
/ build-and-deploy-to-vercel (push) Successful in 1m35s
/ playwright (push) Failing after 2m48s
2024-12-30 10:56:51 +08:00
15d7bd3806 feat: 优化 ToastService 消息格式,调整参数顺序以提升可读性
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m42s
/ depcheck (push) Successful in 1m52s
/ playwright (push) Successful in 1m57s
2024-12-30 10:54:06 +08:00
3c2af936cb feat: 添加链接到相关项目和资源,丰富页面内容
All checks were successful
/ depcheck (push) Successful in 2m25s
/ playwright (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 2m11s
2024-12-29 21:17:33 +08:00
8ea637d7a0 feat: 添加对 100dvh 支持的样式调整,优化布局适应性
All checks were successful
/ depcheck (push) Successful in 2m48s
/ build-and-deploy-to-vercel (push) Successful in 1m40s
/ playwright (push) Successful in 1m54s
2024-12-29 02:01:39 +08:00
a09fb23df0 feat: 更新页面模板,修复模板内容显示问题
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m41s
/ playwright (push) Successful in 1m56s
/ depcheck (push) Successful in 1m34s
2024-12-29 01:32:17 +08:00
eff30bd6c0 feat: 更新组件样式和结构,优化函数式组件展示
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m14s
/ depcheck (push) Successful in 1m10s
/ playwright (push) Failing after 2m57s
2024-12-27 15:02:29 +08:00
6b6e313195 feat: 重构菜单项生成逻辑,优化路由处理并支持分组结构
All checks were successful
/ depcheck (push) Successful in 1m30s
/ build-and-deploy-to-vercel (push) Successful in 1m42s
/ playwright (push) Successful in 1m56s
2024-12-27 14:56:49 +08:00
c6594a1254 feat: 重构菜单项生成逻辑,支持分组和树形结构
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m42s
/ playwright (push) Successful in 1m46s
/ depcheck (push) Successful in 1m32s
2024-12-27 14:49:49 +08:00
865ecf9156 暂存
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m16s
/ playwright (push) Failing after 3m0s
/ depcheck (push) Successful in 1m10s
2024-12-27 13:09:01 +08:00
56791aa06c chore: 注释掉不再使用的 createSplitChunkOutput 导入
Some checks failed
/ depcheck (push) Successful in 1m7s
/ build-and-deploy-to-vercel (push) Successful in 1m15s
/ playwright (push) Failing after 2m59s
2024-12-27 00:04:41 +08:00
b6a51c1245 feat: 更新路由插件,重构路由获取逻辑并添加新的路由页面
Some checks failed
/ depcheck (push) Successful in 1m35s
/ playwright (push) Successful in 1m56s
/ build-and-deploy-to-vercel (push) Failing after 30s
2024-12-27 00:02:11 +08:00
bc8fc96580 feat: 更新 package.json 和 pnpm-lock.yaml,添加新的大小检查命令和依赖项版本更新
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m49s
/ playwright (push) Successful in 2m22s
/ depcheck (push) Successful in 1m9s
2024-12-26 22:54:37 +08:00
8d048038ee feat: 添加第二布局页面,更新路由配置以支持新布局
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m14s
/ playwright (push) Successful in 2m45s
/ depcheck (push) Successful in 1m14s
2024-12-26 18:51:16 +08:00
f3e6dbcc0e feat: 添加 @formkit/auto-animate 插件
All checks were successful
/ depcheck (push) Successful in 1m12s
/ playwright (push) Successful in 1m19s
/ build-and-deploy-to-vercel (push) Successful in 2m32s
2024-12-26 18:38:35 +08:00
c2d942554d chore: 清理 CI 配置,移除不必要的 .npmrc 修改步骤
All checks were successful
/ depcheck (push) Successful in 1m21s
/ playwright (push) Successful in 2m39s
/ build-and-deploy-to-vercel (push) Successful in 1m15s
2024-12-26 17:39:19 +08:00
97f6575f49 fix: 修测试用例
Some checks failed
/ depcheck (push) Waiting to run
/ build-and-deploy-to-vercel (push) Has been cancelled
/ playwright (push) Has been cancelled
2024-12-26 17:37:47 +08:00
1dac41ce88 feat: 更新 VSCode 设置,更新 package.json 脚本,添加新功能页面和重构组件
Some checks failed
/ playwright (push) Failing after 2m49s
/ depcheck (push) Successful in 1m54s
/ build-and-deploy-to-vercel (push) Successful in 1m37s
2024-12-26 16:25:39 +08:00
4e3633d7ea feat: 添加 vite-plugin-vue-meta-layouts 插件,优化路由菜单项生成逻辑
Some checks failed
/ playwright (push) Waiting to run
/ depcheck (push) Waiting to run
/ build-and-deploy-to-vercel (push) Has been cancelled
2024-12-26 15:53:53 +08:00
d18799e8cb feat: 更新 touch-emulator 脚本链接至新的 CDN 地址
Some checks failed
/ playwright (push) Failing after 2m42s
/ depcheck (push) Successful in 1m34s
/ build-and-deploy-to-vercel (push) Successful in 1m33s
2024-12-26 15:04:24 +08:00
02048f1266 feat: 更新 pnpm-lock.yaml 中的依赖版本,升级 ms 至 2.1.3 和 @vue/language-core 至 2.2.0
Some checks failed
/ playwright (push) Failing after 2m14s
/ build-and-deploy-to-vercel (push) Successful in 1m36s
/ depcheck (push) Successful in 1m29s
2024-12-26 13:11:28 +08:00
83d7f6b72e feat: 更新 Vite 版本至 6.0.6
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m30s
/ depcheck (push) Successful in 1m7s
/ playwright (push) Failing after 3m28s
2024-12-26 13:05:14 +08:00
1856bd4fc8 feat: 重构 AppMenu 组件以使用 PanelMenu,优化菜单项生成逻辑,更新无限加载页面按钮样式,调整 Vite 插件配置
Some checks failed
/ playwright (push) Failing after 2m51s
/ depcheck (push) Successful in 1m40s
/ build-and-deploy-to-vercel (push) Successful in 1m34s
2024-12-26 00:20:33 +08:00
49e18facda feat: 更新 VSCode 配置,添加 oxc 插件,调整 eslint 配置,优化构建和类型检查命令
Some checks failed
/ playwright (push) Failing after 2m19s
/ depcheck (push) Successful in 1m30s
/ build-and-deploy-to-vercel (push) Successful in 1m29s
2024-12-25 23:44:18 +08:00
eed0f4f3f4 feat: 重构 AppMenu 组件以动态生成菜单项,更新路由和样式,添加返回按钮
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m33s
/ playwright (push) Failing after 2m43s
/ depcheck (push) Successful in 1m35s
2024-12-25 19:19:21 +08:00
78fe5b0c37 feat: 在 unocss 配置中添加主题使用说明注释
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m8s
/ depcheck (push) Successful in 1m11s
/ playwright (push) Failing after 3m46s
2024-12-25 14:39:46 +08:00
cf87a60357 feat: 更新样式导入,调整 nprogress 和 unocss 重置样式的引用
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m7s
/ playwright (push) Has been cancelled
/ depcheck (push) Has been cancelled
2024-12-25 14:37:52 +08:00
82d89363b1 feat: 更新 AppConfigurator 组件,添加新的 Sakai 链接,调整页面模板和路由配置,更新 unocss 配置以添加颜色
Some checks failed
/ depcheck (push) Waiting to run
/ playwright (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2024-12-25 14:36:51 +08:00
e91582db23 feat: 添加新的 Sakai Vue 布局和样式,重构基础样式,更新 README,调整插件配置
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m41s
/ playwright (push) Failing after 3m26s
/ depcheck (push) Successful in 1m10s
2024-12-25 14:14:55 +08:00
99c8e8d892 feat: 更新路由名称映射,调整插件配置 2024-12-25 11:12:29 +08:00
7a682b1ea6 feat: 更新页面模板内容,调整 VSCode 设置,更新依赖管理器版本并添加新脚本
All checks were successful
/ playwright (push) Successful in 1m56s
/ depcheck (push) Successful in 1m27s
/ build-and-deploy-to-vercel (push) Successful in 1m43s
2024-12-25 11:00:19 +08:00
03d22e09a0 feat: 更新 README 和样式文件,调整依赖安装路径,重构基础样式并添加重置样式
All checks were successful
/ depcheck (push) Successful in 1m10s
/ build-and-deploy-to-vercel (push) Successful in 1m9s
/ playwright (push) Successful in 2m23s
2024-12-25 09:49:29 +08:00
ea2eddb4c3 feat: 重构样式文件,删除不必要的样式并更新路径配置
All checks were successful
/ playwright (push) Successful in 1m49s
/ depcheck (push) Successful in 1m24s
/ build-and-deploy-to-vercel (push) Successful in 1m35s
2024-12-25 09:38:29 +08:00
1b62a08c4d feat: 更新 Layouts 配置,添加 pagesDirs 选项以指定页面目录
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m10s
/ playwright (push) Successful in 2m1s
/ depcheck (push) Successful in 1m6s
2024-12-25 00:35:16 +08:00
cce5d94017 feat: 添加默认布局和第二布局,更新路由配置以支持新布局
Some checks failed
/ depcheck (push) Waiting to run
/ playwright (push) Has been cancelled
/ build-and-deploy-to-vercel (push) Has been cancelled
2024-12-25 00:33:35 +08:00
bfbeb326bc feat: 添加 vite-plugin-vue-layouts 插件并更新 vite 版本至 6.0.5 2024-12-25 00:06:46 +08:00
400de0f222 chore: 更新依赖项版本以保持兼容性和稳定性
Some checks failed
/ playwright (push) Failing after 22s
/ depcheck (push) Failing after 6s
/ build-and-deploy-to-vercel (push) Failing after 6s
2024-12-24 23:20:26 +08:00
9d421c5bfe feat: 重构插件安装方式,统一使用 install 方法并移除不必要的 setup 函数
All checks were successful
/ playwright (push) Successful in 2m19s
/ build-and-deploy-to-vercel (push) Successful in 1m9s
/ depcheck (push) Successful in 1m5s
2024-12-24 23:13:09 +08:00
e9f5aa56a6 feat: 更新 ReusableTemplate 组件,增加对默认插槽的支持和修复插槽属性传递
All checks were successful
/ playwright (push) Successful in 2m33s
/ depcheck (push) Successful in 1m11s
/ build-and-deploy-to-vercel (push) Successful in 1m8s
2024-12-24 18:14:17 +08:00
b3e670ee50 feat: 为 Cascadeselect 组件添加最大高度和垂直滚动支持
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m35s
/ depcheck (push) Successful in 1m32s
/ playwright (push) Successful in 2m23s
2024-12-23 18:58:47 +08:00
01d619a857 fix: 更新类型声明,将模块名称从 '@vue/runtime-dom' 更改为 'vue'
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m34s
/ depcheck (push) Successful in 1m28s
/ playwright (push) Failing after 3m35s
2024-12-23 12:19:01 +08:00
68e28f7f30 feat: 添加滚动行为和优化路由插件注册顺序
Some checks failed
/ depcheck (push) Successful in 1m26s
/ playwright (push) Failing after 3m24s
/ build-and-deploy-to-vercel (push) Successful in 1m33s
2024-12-23 10:56:41 +08:00
f74f76750d fix: 修复 ProgressSpinner 组件的类名,添加强制应用样式
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m34s
/ depcheck (push) Successful in 1m27s
/ playwright (push) Failing after 3m24s
2024-12-23 10:52:21 +08:00
de9260dcf5 feat: 更新 index.html 和样式文件,优化样式导入和结构
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m34s
/ depcheck (push) Successful in 1m28s
/ playwright (push) Failing after 3m25s
2024-12-23 10:44:47 +08:00
4037060842 feat: 将主样式文件从 CSS 更改为 LESS 以支持更灵活的样式管理
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m32s
/ depcheck (push) Successful in 1m27s
/ playwright (push) Failing after 3m14s
2024-12-23 10:24:03 +08:00
32dd090d31 fix: 禁用 VITE_SPLIT_CHUNKS 以优化构建性能
Some checks failed
/ depcheck (push) Successful in 1m26s
/ playwright (push) Failing after 3m36s
/ build-and-deploy-to-vercel (push) Successful in 1m35s
2024-12-23 10:16:32 +08:00
20fadd2cfd feat: 重构样式导入,优化样式文件结构
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m36s
/ depcheck (push) Successful in 1m27s
/ playwright (push) Failing after 3m35s
2024-12-23 10:09:08 +08:00
b7e09dd383 fix: 测试样式导入顺序问题
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 39s
/ playwright (push) Failing after 1m15s
/ depcheck (push) Successful in 1m27s
2024-12-20 18:21:31 +08:00
c48340d45e feat: 添加 PrimeVue 相关样式导入以支持主题和重置样式
Some checks failed
/ playwright (push) Failing after 1m20s
/ build-and-deploy-to-vercel (push) Failing after 24s
/ depcheck (push) Successful in 1m9s
2024-12-20 18:18:53 +08:00
76aa6eac79 feat: 在可重用模板中添加标题以增强可读性
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m37s
/ playwright (push) Successful in 1m49s
/ depcheck (push) Successful in 1m27s
2024-12-20 16:33:59 +08:00
49fc15b1b6 fix: 修改用户数据加载延迟时间和新鲜度设置
All checks were successful
/ playwright (push) Successful in 1m58s
/ build-and-deploy-to-vercel (push) Successful in 1m32s
/ depcheck (push) Successful in 1m26s
2024-12-20 15:33:55 +08:00
50e0c5ebc6 feat: 添加暗色模式支持,重构用户数据加载逻辑
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m34s
/ playwright (push) Successful in 1m47s
/ depcheck (push) Successful in 1m27s
2024-12-20 15:28:24 +08:00
89eae5bcbb defineColadaLoader
All checks were successful
/ depcheck (push) Successful in 1m12s
/ build-and-deploy-to-vercel (push) Successful in 2m2s
/ playwright (push) Successful in 1m25s
2024-12-20 14:53:31 +08:00
a8ce2f39e2 fix: 注释掉被墙的 Typekit 脚本以避免加载错误
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m32s
/ depcheck (push) Successful in 1m26s
/ playwright (push) Successful in 1m57s
2024-12-20 14:16:47 +08:00
487b6bd315 feat: 添加 Pinia 插件支持并重构主应用初始化逻辑
All checks were successful
/ depcheck (push) Successful in 1m26s
/ playwright (push) Successful in 3m25s
/ build-and-deploy-to-vercel (push) Successful in 3m56s
2024-12-19 23:09:17 +08:00
5f81c647fa chore: 添加 auto-imports.d.ts 到 .gitignore
All checks were successful
/ depcheck (push) Successful in 1m11s
/ build-and-deploy-to-vercel (push) Successful in 1m14s
/ playwright (push) Successful in 3m26s
2024-12-19 22:58:40 +08:00
cb779c0234 refactor: 移除不再使用的 utils4u 导入,更新为直接使用 useCountdown
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m16s
/ depcheck (push) Successful in 1m14s
/ playwright (push) Successful in 3m27s
2024-12-19 22:54:25 +08:00
319ec789f4 chore(deps): 更新 PrimeVue 版本至 4.2.6,删除不再使用的补丁文件
All checks were successful
/ depcheck (push) Successful in 1m9s
/ build-and-deploy-to-vercel (push) Successful in 1m42s
/ playwright (push) Successful in 1m51s
2024-12-19 18:07:24 +08:00
c9bde0b7da feat: 更新 utils4u 依赖至 2.18.1,并添加 PrimeVue 服务的全局类型声明
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m11s
/ depcheck (push) Successful in 1m8s
/ playwright (push) Successful in 3m4s
2024-12-19 13:11:42 +08:00
6cd44c4b34 chore(deps): 更新 utils4u 和 vant 依赖至最新版本并调整导入方式
Some checks failed
/ depcheck (push) Successful in 1m8s
/ build-and-deploy-to-vercel (push) Failing after 28s
/ playwright (push) Failing after 1m3s
2024-12-19 12:34:07 +08:00
b578c18e43 fix: 修正 utils4u 导入路径
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m12s
/ depcheck (push) Successful in 1m9s
/ playwright (push) Successful in 2m10s
2024-12-18 19:02:11 +08:00
1cec4cdb2f chore(deps): 更新 utils4u 依赖至 2.16.0 2024-12-18 19:01:23 +08:00
1d2d19df65 fix: type
All checks were successful
/ playwright (push) Successful in 1m39s
/ depcheck (push) Successful in 1m33s
/ build-and-deploy-to-vercel (push) Successful in 1m36s
2024-12-18 18:42:06 +08:00
aa84274f0a feat: 添加热模块替换支持到计数器状态管理
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m22s
/ depcheck (push) Successful in 1m14s
/ playwright (push) Successful in 3m27s
2024-12-18 18:08:22 +08:00
41ecc9ff56 chore(deps): 更新 npm registry 配置并升级部分依赖版本
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m44s
/ playwright (push) Successful in 3m8s
/ depcheck (push) Successful in 1m18s
2024-12-18 12:17:26 +08:00
a5c1cf38f6 feat: 添加全局错误处理逻辑,优化 PrimeVue 插件导入和路由设置
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m37s
/ playwright (push) Successful in 2m1s
/ depcheck (push) Successful in 1m45s
2024-12-17 09:21:11 +08:00
1dc0230a3e chore: 更新 npm registry 配置并升级部分依赖版本
All checks were successful
/ playwright (push) Successful in 3m8s
/ build-and-deploy-to-vercel (push) Successful in 2m13s
/ depcheck (push) Successful in 1m31s
2024-12-17 00:32:07 +08:00
5f561d3a5f chore(deps): update node.js to v22.12.0
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m18s
/ depcheck (push) Successful in 1m17s
/ playwright (push) Failing after 4m30s
2024-12-16 10:26:56 +08:00
86943c1e58 feat: 添加文件上传数量限制,优化禁用状态逻辑并显示上传文件信息
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m44s
/ playwright (push) Successful in 1m56s
/ depcheck (push) Successful in 1m35s
2024-12-16 01:09:34 +08:00
511f07f778 feat: 优化假数据上传逻辑,简化响应处理并添加调试信息
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m39s
/ depcheck (push) Successful in 1m36s
/ playwright (push) Successful in 1m59s
2024-12-16 00:54:28 +08:00
5f0eaa2579 feat: 更新上传假数据处理逻辑,添加原始响应方法并支持选择禁用选项
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m19s
/ depcheck (push) Successful in 1m19s
/ playwright (push) Successful in 3m28s
2024-12-16 00:38:02 +08:00
abe0262130 feat: upload-demo.vue
All checks were successful
/ playwright (push) Successful in 1m57s
/ build-and-deploy-to-vercel (push) Successful in 1m38s
/ depcheck (push) Successful in 1m36s
2024-12-16 00:21:03 +08:00
86d0e5622d feat: 重构 PrimeVue 相关插件,优化对话框和通知服务的使用,更新样式和组件结构
All checks were successful
/ depcheck (push) Successful in 1m37s
/ build-and-deploy-to-vercel (push) Successful in 1m37s
/ playwright (push) Successful in 1m56s
2024-12-15 18:45:27 +08:00
0be091c32f fix: 更新 CI 工作流,修改构建命令以确保类型定义文件存在
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m35s
/ playwright (push) Successful in 1m49s
/ depcheck (push) Successful in 1m32s
2024-12-15 00:51:09 +08:00
efc52343f2 chore: 更新依赖项版本,提升项目稳定性
Some checks failed
/ playwright (push) Successful in 1m27s
/ depcheck (push) Successful in 1m36s
/ build-and-deploy-to-vercel (push) Failing after 46s
2024-12-15 00:41:52 +08:00
baa100d6ed fix: 修正 CI 工作流中的 Vue 类型检查命令格式
Some checks are pending
/ playwright (push) Successful in 2m1s
/ depcheck (push) Successful in 1m19s
/ build-and-deploy-to-vercel (push) Waiting to run
2024-12-15 00:36:00 +08:00
b8ac17b600 feat: 更新 App.vue 模板,合并对话框和提示组件的渲染
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 31s
/ depcheck (push) Successful in 1m19s
/ playwright (push) Successful in 3m21s
2024-12-14 21:55:53 +08:00
553d3be256 feat: 添加 useDialogRef 函数,优化对话框引用管理,更新对话框组件逻辑
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 34s
/ depcheck (push) Has been cancelled
/ playwright (push) Has been cancelled
2024-12-14 21:54:51 +08:00
b9ad180d4c feat: 优化确认对话框的属性设置,简化代码结构
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 31s
/ depcheck (push) Successful in 1m19s
/ playwright (push) Successful in 3m25s
2024-12-14 20:49:49 +08:00
b1c0efb853 feat: 添加确认对话框组件,优化提示和对话框服务的交互逻辑
Some checks failed
/ playwright (push) Successful in 1m30s
/ depcheck (push) Successful in 1m19s
/ build-and-deploy-to-vercel (push) Failing after 1m50s
2024-12-14 20:01:38 +08:00
9ed80478e6 fix: 修正 DynamicDialogOptions.data 的注释格式 [no ci] 2024-12-13 10:15:45 +08:00
722f880f81 feat: 重构 DynamicDialog 组件,优化数据注入和事件处理逻辑
Some checks failed
/ playwright (push) Successful in 2m1s
/ depcheck (push) Successful in 1m38s
/ build-and-deploy-to-vercel (push) Failing after 39s
2024-12-13 09:40:29 +08:00
6ceb3f0766 feat: 添加 DynamicDialog 和 Toast 组件,重构 PrimeVue 相关逻辑和样式
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 24s
/ depcheck (push) Successful in 1m21s
/ playwright (push) Successful in 3m16s
2024-12-12 23:34:20 +08:00
22ff97d003 feat: 添加 Toast 和 Dialog 服务,优化组件交互和样式
Some checks failed
/ depcheck (push) Successful in 1m35s
/ build-and-deploy-to-vercel (push) Failing after 40s
/ playwright (push) Successful in 2m4s
2024-12-12 19:20:49 +08:00
dbcc28fb42 feat: 优化 UseIntersectionObserverInfiniteLoading 组件的加载逻辑和模板结构
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 39s
/ playwright (push) Successful in 2m1s
/ depcheck (push) Successful in 1m30s
2024-12-11 15:48:35 +08:00
aa902ed159 Revert "feat: 使用 radash 的 throttle 包装 load 函数,优化加载频率并重构 refresh 方法"
This reverts commit 40bf214da7.
2024-12-11 15:44:43 +08:00
40bf214da7 feat: 使用 radash 的 throttle 包装 load 函数,优化加载频率并重构 refresh 方法
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 40s
/ playwright (push) Successful in 2m10s
/ depcheck (push) Successful in 1m39s
2024-12-11 15:42:49 +08:00
3a9b19d64f feat: 添加 UseIntersectionObserverInfiniteLoading 组件的使用示例,优化模板结构 [no-ci]
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 27s
/ depcheck (push) Successful in 1m24s
/ playwright (push) Successful in 3m38s
2024-12-11 15:31:03 +08:00
294c73df09 feat: 调整 Toast 组件样式,优化消息文本的上边距
Some checks failed
/ depcheck (push) Failing after 0s
/ playwright (push) Failing after 53s
/ build-and-deploy-to-vercel (push) Failing after 0s
2024-12-11 15:00:25 +08:00
b166cc9d60 feat: 更新 CI 工作流,修改 Surge 部署步骤以使用主分支
Some checks failed
/ playwright (push) Failing after -1s
/ build-and-deploy-to-vercel (push) Failing after 10s
/ depcheck (push) Failing after 1s
2024-12-10 09:54:34 +08:00
f314b9c023 feat: 调整组件样式,优化图标和布局的尺寸,更新无限加载组件的刷新方法
Some checks failed
/ playwright (push) Failing after 0s
/ depcheck (push) Failing after 3s
/ build-and-deploy-to-vercel (push) Failing after 10s
2024-12-08 18:00:21 +08:00
1685a34bb2 feat: 重构 ToastService,添加消息管理功能并更新按钮事件处理
Some checks failed
/ playwright (push) Failing after 9s
/ depcheck (push) Failing after 3s
/ build-and-deploy-to-vercel (push) Failing after 2s
2024-12-08 17:43:40 +08:00
b18bab4d7c feat: 更新无限加载组件,添加刷新功能和错误处理,优化加载状态管理
Some checks failed
/ playwright (push) Failing after 1s
/ depcheck (push) Failing after 2s
/ build-and-deploy-to-vercel (push) Failing after 11s
2024-12-08 16:53:50 +08:00
cd1cd72002 feat: 更新无限加载组件,支持分页加载和错误处理
All checks were successful
/ depcheck (push) Successful in 1m25s
/ playwright (push) Successful in 3m19s
/ build-and-deploy-to-vercel (push) Successful in 1m16s
2024-12-08 00:23:28 +08:00
5f98fe12ba feat: 更新无限加载组件
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m24s
/ playwright (push) Successful in 2m6s
/ depcheck (push) Successful in 1m38s
2024-12-08 00:04:31 +08:00
2574e38d9a feat: 添加无限加载组件和示例页面
All checks were successful
/ playwright (push) Successful in 3m16s
/ depcheck (push) Successful in 1m25s
/ build-and-deploy-to-vercel (push) Successful in 1m14s
2024-12-07 23:11:08 +08:00
12a02eb193 feat: 添加 useIntersectionObserverList 页面,支持懒加载和可见性检测
All checks were successful
/ depcheck (push) Successful in 1m26s
/ build-and-deploy-to-vercel (push) Successful in 1m9s
/ playwright (push) Successful in 2m57s
2024-12-07 19:28:37 +08:00
b309260e8f 00
Some checks failed
/ playwright (push) Successful in 3m16s
/ depcheck (push) Successful in 1m26s
/ build-and-deploy-to-vercel (push) Failing after 37s
2024-12-07 02:37:21 +08:00
34b55ce71a chore(deps): update dependency typescript to ~5.7.2
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Failing after 38s
/ depcheck (push) Successful in 1m38s
/ playwright (push) Successful in 2m10s
2024-12-07 01:45:37 +08:00
190b5159bc feat: 更新README.md,添加primevue-scopedtokens链接 [no ci] 2024-12-04 18:39:02 +08:00
71c2e6408a feat: 更新依赖项版本和CI配置
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m28s
/ depcheck (push) Successful in 1m40s
/ playwright (push) Successful in 1m42s
2024-12-04 14:36:30 +08:00
ce2f1f6a27 feat: 更新图标组件和Vite配置以支持自定义类名
All checks were successful
/ playwright (push) Successful in 2m5s
/ depcheck (push) Successful in 1m54s
/ build-and-deploy-to-vercel (push) Successful in 1m25s
2024-12-04 14:00:24 +08:00
2479c80cd1 feat: 更新样式和脚本以改善应用布局和响应式设计
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m22s
/ playwright (push) Successful in 1m58s
/ depcheck (push) Successful in 1m57s
2024-12-04 12:01:21 +08:00
32902909c2 [no ci] 2024-12-03 10:16:41 +08:00
3676127dc9 feat: 更新依赖项版本以提升项目稳定性和功能
All checks were successful
/ depcheck (push) Successful in 1m42s
/ build-and-deploy-to-vercel (push) Successful in 1m41s
/ playwright (push) Successful in 3m12s
2024-11-27 17:34:03 +08:00
081ae50e09 chore(deps): update yanhao98/composite-actions digest to 6af4afe
All checks were successful
/ depcheck (push) Successful in 1m48s
/ build-and-deploy-to-vercel (push) Successful in 1m17s
/ playwright (push) Successful in 3m18s
2024-11-26 23:30:38 +08:00
adf5fd2b4b feat: 更新依赖项版本以保持项目的最新状态
All checks were successful
/ depcheck (push) Successful in 1m46s
/ build-and-deploy-to-vercel (push) Successful in 1m39s
/ playwright (push) Successful in 3m7s
2024-11-26 23:08:39 +08:00
fdcc4aa18c feat: 在 primevue.vue 中添加类型忽略注释以处理缺失的类型
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m21s
/ playwright (push) Successful in 2m0s
/ depcheck (push) Successful in 1m46s
2024-11-26 12:49:36 +08:00
7e06011c05 feat: 在 typed-router.d.ts 中添加 '$Path' 路由记录信息
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 45s
/ playwright (push) Successful in 3m14s
/ depcheck (push) Successful in 1m51s
2024-11-26 12:39:37 +08:00
fea7e3d20e feat: 添加 404 页面组件,显示路径不存在的消息
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m21s
/ depcheck (push) Successful in 1m46s
/ playwright (push) Successful in 3m13s
2024-11-26 11:03:41 +08:00
8d765091d8 feat: 更新 tsconfig 和 Vite 插件配置,添加类型定义文件以增强类型支持
All checks were successful
/ depcheck (push) Successful in 1m46s
/ build-and-deploy-to-vercel (push) Successful in 1m20s
/ playwright (push) Successful in 3m13s
2024-11-25 17:01:57 +08:00
b6854ae632 feat: 更新 PrimeVue 配置以支持多语言,移除不必要的代码
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m20s
/ depcheck (push) Successful in 1m49s
/ playwright (push) Successful in 1m59s
2024-11-25 16:53:41 +08:00
3883fad98d feat: 重构 Vite 配置,提取插件配置到单独文件并简化 tsconfig 配置
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m25s
/ depcheck (push) Successful in 1m49s
/ playwright (push) Successful in 3m47s
2024-11-25 16:44:36 +08:00
bb27e0107d fix: 更新 nprogress 样式表链接以使用新的 CDN
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m20s
/ playwright (push) Successful in 2m5s
/ depcheck (push) Successful in 1m48s
2024-11-25 16:27:44 +08:00
84003f22d3 chore(deps): 更新依赖项
All checks were successful
/ playwright (push) Successful in 2m51s
/ build-and-deploy-to-vercel (push) Successful in 2m1s
/ depcheck (push) Successful in 1m47s
2024-11-25 15:57:36 +08:00
1473a58e06 fix: 修正组件类名并更新按钮文本以提高可读性
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m22s
/ playwright (push) Successful in 1m59s
/ depcheck (push) Successful in 2m3s
2024-11-22 16:13:56 +08:00
f708970fa4 feat: 添加 primelocale 依赖并更新 PrimeVue 组件以支持多语言选择
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 1m54s
/ depcheck (push) Has been cancelled
/ playwright (push) Has been cancelled
2024-11-22 16:11:41 +08:00
e39fea979d feat: 更新 PrimeVue 组件,添加城市选择下拉框并优化按钮布局
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m39s
/ depcheck (push) Successful in 1m36s
/ playwright (push) Successful in 3m58s
2024-11-22 12:45:08 +08:00
2bbe9c7a59 feat: 添加 PrimeVue 组件并更新样式,增强按钮交互体验 2024-11-22 12:34:43 +08:00
30721736a4 chore(deps): 更新依赖项 @vueuse/core、alova、@types/node 和 typescript 到最新版本
All checks were successful
/ playwright (push) Successful in 1m46s
/ depcheck (push) Successful in 1m59s
/ build-and-deploy-to-vercel (push) Successful in 2m24s
2024-11-22 11:55:12 +08:00
8cbb8deceb chore(deps): update all non-major dependencies
Some checks failed
renovate/stability-days Updates have not met minimum release age requirement
/ depcheck (push) Successful in 1m52s
/ build-and-deploy-to-vercel (push) Failing after 29s
/ playwright (push) Successful in 2m21s
2024-11-20 21:20:37 +08:00
6239cfd928 chore(deps): update yanhao98/composite-actions digest to fdab136
Some checks failed
/ depcheck (push) Successful in 1m50s
/ build-and-deploy-to-vercel (push) Failing after 41s
/ playwright (push) Successful in 1m28s
2024-11-20 20:03:04 +08:00
0e991ee39e chore(deps): update all non-major dependencies
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ playwright (push) Successful in 1m27s
/ depcheck (push) Successful in 1m43s
/ build-and-deploy-to-vercel (push) Failing after 27s
2024-11-20 19:03:22 +08:00
26d9cbaaff chore(deps): update all non-major dependencies
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Failing after 27s
/ depcheck (push) Successful in 1m44s
/ playwright (push) Successful in 1m31s
2024-11-20 16:15:09 +08:00
b597d71eac feat(vscode): 更新设置以格式化修改部分并添加i18n-ally语言路径
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m44s
/ playwright (push) Successful in 2m36s
/ depcheck (push) Successful in 1m37s
2024-11-15 11:35:04 +08:00
6b5d34d183 feat(i18n): 添加注释以说明国际化组件的类型安全示例
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m14s
/ depcheck (push) Successful in 1m40s
/ playwright (push) Successful in 2m53s
2024-11-07 18:45:38 +08:00
6b657f3f2b feat(i18n): 更新国际化组件,优化语言文件和结构,增强类型支持
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m16s
/ depcheck (push) Successful in 1m36s
/ playwright (push) Successful in 2m49s
2024-11-07 15:26:33 +08:00
3eed9a44df feat(i18n): 重构国际化组件,添加新的语言支持和功能
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m16s
/ depcheck (push) Successful in 1m38s
/ playwright (push) Successful in 2m46s
2024-11-07 12:36:37 +08:00
a4cc30f816 feat(i18n): 移除国际化
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m8s
/ playwright (push) Successful in 2m26s
/ depcheck (push) Successful in 1m36s
2024-11-07 10:38:42 +08:00
4dd414e4bf feat(i18n): 添加国际化消息支持,更新首页组件以显示重要消息
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m15s
/ depcheck (push) Successful in 1m39s
/ playwright (push) Successful in 2m52s
2024-11-06 19:01:12 +08:00
b717b6252c feat(i18n): 更新语言文件导入逻辑,增强类型支持
All checks were successful
/ playwright (push) Successful in 2m47s
/ depcheck (push) Successful in 1m36s
/ build-and-deploy-to-vercel (push) Successful in 1m17s
2024-11-06 18:45:37 +08:00
ec1d932250 feat(i18n): 添加中文和英文语言文件,集成国际化支持
Some checks failed
/ build-and-deploy-to-vercel (push) Failing after 1m10s
/ playwright (push) Successful in 2m34s
/ depcheck (push) Successful in 1m39s
2024-11-06 18:28:53 +08:00
40752e555f feat(i18n): 添加国际化支持,更新语言切换功能
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m20s
/ depcheck (push) Successful in 2m27s
/ playwright (push) Successful in 2m1s
2024-11-06 17:44:32 +08:00
e2f60c32a3 chore(npmrc): 更新 node-mirror:release 配置注释
All checks were successful
/ playwright (push) Successful in 1m52s
/ build-and-deploy-to-vercel (push) Successful in 1m15s
/ depcheck (push) Successful in 1m41s
2024-11-05 16:19:02 +08:00
9f5e3703e1 chore(npmrc): 添加多个 npm 镜像源
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m15s
/ depcheck (push) Successful in 1m39s
/ playwright (push) Successful in 3m15s
2024-11-05 15:07:38 +08:00
2cc4d30046 chore(deps): update all non-major dependencies
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 2m2s
/ playwright (push) Successful in 2m29s
/ depcheck (push) Successful in 1m51s
2024-11-01 13:50:11 +08:00
3f16456c95 chore(deps): update dependency npm-run-all2 to ^6.2.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m39s
/ depcheck (push) Successful in 1m32s
/ playwright (push) Successful in 3m41s
2024-11-01 12:44:52 +08:00
ffb2646375 chore(deps): update dependency @types/node to ^22.8.4
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m54s
/ playwright (push) Successful in 2m22s
/ depcheck (push) Successful in 1m52s
2024-10-31 09:32:53 +08:00
b1dac39295 chore(deps): update dependency @types/node to ^22.7.6
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ depcheck (push) Successful in 1m45s
/ build-and-deploy-to-vercel (push) Successful in 1m53s
/ playwright (push) Successful in 2m26s
2024-10-31 08:27:34 +08:00
99d9ae9cd2 更新 README.md
All checks were successful
/ playwright (push) Successful in 3m24s
/ depcheck (push) Successful in 1m49s
/ build-and-deploy-to-vercel (push) Successful in 1m46s
2024-10-29 15:18:09 +08:00
35bd3ff9c1 更新 README.md
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m13s
/ depcheck (push) Successful in 1m52s
/ playwright (push) Successful in 4m17s
2024-10-27 23:38:13 +08:00
bd78a7b598 chore: 更新 CI 流程,优化依赖检查和类型检查步骤
All checks were successful
/ depcheck (push) Successful in 2m15s
/ playwright (push) Successful in 3m8s
/ build-and-deploy-to-vercel (push) Successful in 2m9s
2024-10-19 16:34:06 +08:00
f302a56d2c chore: 更新 pre-commit 钩子,添加运行提示图标
Some checks failed
/ depcheck (push) Failing after 37s
/ playwright (push) Failing after 1m43s
/ build-and-deploy-to-vercel (push) Failing after 1m5s
2024-10-19 15:44:24 +08:00
3d03311ed7 chore: 更新 pre-commit 钩子,添加 TypeScript 格式化设置并清理 VSCode 配置 2024-10-19 15:41:38 +08:00
3ffb874049 chore: 在 CI 流程中添加 taze 依赖检查步骤
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 2m6s
/ playwright (push) Successful in 2m55s
/ depcheck (push) Successful in 1m53s
2024-10-18 17:06:19 +08:00
107b0a2d98 chore(deps): update all non-major dependencies
All checks were successful
/ depcheck (push) Successful in 1m46s
/ playwright (push) Successful in 2m47s
/ build-and-deploy-to-vercel (push) Successful in 1m39s
2024-10-17 12:45:29 +08:00
b3fc1e8999 chore: 简化 CI 流程中的 Vue 类型检查步骤
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m34s
/ depcheck (push) Successful in 1m35s
/ playwright (push) Successful in 3m24s
2024-10-17 12:41:49 +08:00
823f767fc9 docs: 更新 README.md,添加 .gitattributes 使用说明
All checks were successful
/ depcheck (push) Successful in 1m37s
/ playwright (push) Successful in 3m27s
/ build-and-deploy-to-vercel (push) Successful in 1m37s
2024-10-17 12:01:34 +08:00
a5ec941074 chore: 添加 .gitattributes 文件以配置文本和二进制文件处理
All checks were successful
/ depcheck (push) Successful in 1m33s
/ build-and-deploy-to-vercel (push) Successful in 1m57s
/ playwright (push) Successful in 2m52s
2024-10-17 09:14:56 +08:00
23fbb10a3c chore(deps): update dependency node to v20.18.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
/ build-and-deploy-to-vercel (push) Successful in 1m40s
/ playwright (push) Successful in 2m51s
/ depcheck (push) Successful in 1m50s
2024-10-17 08:14:54 +08:00
229 changed files with 327916 additions and 6361 deletions

View File

@ -1,14 +0,0 @@
// 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
});

View File

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

6
.editorconfig Normal file
View File

@ -0,0 +1,6 @@
[*.{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=true
VITE_SPLIT_CHUNKS=false
VITE_SOURCE_MAP=true
VITE_BUILD_TIME=
VITE_BUILD_COMMIT=

81
.gitattributes vendored Normal file
View File

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

15
.github/.depcheck.js vendored Normal file
View File

@ -0,0 +1,15 @@
// 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

9
.github/.depcheckrc.yaml vendored Normal file
View File

@ -0,0 +1,9 @@
# 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

53
.github/copilot-instructions.md vendored Normal file
View File

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

3
.github/copilot-instructions_MD vendored Normal file
View File

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

View File

@ -1,71 +0,0 @@
# 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@e7fd5cc26bbf735f90a7236b9c34cf8e1a3de85f
- 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@e7fd5cc26bbf735f90a7236b9c34cf8e1a3de85f
- 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 }}"

36
.github/workflows/lint.yaml vendored Normal file
View File

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

44
.github/workflows/playwright.yaml vendored Normal file
View File

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

33
.github/workflows/vercel.yaml vendored Normal file
View File

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

3
.gitignore vendored
View File

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

20
.husky/README.md Normal file
View File

@ -0,0 +1,20 @@
### 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
```

5
.husky/commit-msg Normal file
View File

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

4
.husky/post-merge Normal file
View File

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

View File

@ -1,11 +1,4 @@
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
# 此钩子在执行 git commit 命令时,在创建提交之前运行。
echo "🧹 [Pre-commit] 正在运行 lint-staged..."
pnpm exec lint-staged
echo "✅ [Pre-commit] lint-staged 完成!"

10
.npmrc
View File

@ -1,10 +1,12 @@
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://r-npm.oo1.dev
registry=https://registry.npmmirror.com
# registry=https://nexus.oo1.dev/repository/npm/
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
# use-node-version=20.17.0
node-mirror:release=https://npmmirror.com/mirrors/node/
use-node-version=22.14.0
node-mirror:release=https://npmmirror.com/mirrors/node/ # pnpm config set node-mirror:release=https://npmmirror.com/mirrors/node/
node-mirror:rc=https://npmmirror.com/mirrors/node-rc/
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/

14
.oxlintrc.json Normal file
View File

@ -0,0 +1,14 @@
{
"$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,2 +1,5 @@
/node_modules
/dist
/src/shadcn
/src/components/InspiraUI/
**/**.no-lint.ts
**/**.nolint.ts
**/**.noformat.json

1
.roorules Symbolic link
View File

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

View File

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

41
.vscode/settings.json vendored
View File

@ -1,19 +1,36 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// "editor.formatOnSaveMode": "modificationsIfAvailable", // 只格式化修改的部分
"editor.formatOnSaveMode": "file", // 格式化整个文件
"[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$"],
// 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"
}
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.oxc": "explicit",
"source.organizeImports": "never"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "never",
"source.fixAll.stylelint": "never",
"source.fixAll.oxc": "never",
"source.organizeImports": "never",
"source.fixAll": "never"
},
"editor.formatOnSave": false,
"oxc.enable": true,
"eslint.enable": true
}

View File

@ -9,7 +9,7 @@ curl -fsSL https://get-pnpm.oo1.dev/install.sh | sh -
iwr https://get-pnpm.oo1.dev/install.ps1 -useb | iex
# 安装依赖
pnpm install --registry=https://r-npm.oo1.dev
pnpm install --registry=https://nexus.oo1.dev/repository/npm
# 启动服务
pnpm run dev
@ -17,17 +17,31 @@ pnpm run dev
## Tips
### Husky 遇到 command not found: husky
### .gitattributes
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
如果你先推送了一个.jpg文件然后再推送包含.gitattributes文件的更新Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效你可以通过以下步骤重新应用设置
```shell
ln -s $(which pnpm) $HOME/.local/bin/pnpm
```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配置已生效。
## Links
- https://github.com/hyoban-template/shadcn-vue-unocss-starter$0
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
@ -43,3 +57,6 @@ ln -s $(which pnpm) $HOME/.local/bin/pnpm
- https://github.dev/antfu-collective/vitesse/
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
---
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)

318
auto-imports.d.ts vendored
View File

@ -1,318 +0,0 @@
/* 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')
}

20
components.json Normal file
View File

@ -0,0 +1,20 @@
{
"$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"
}

View File

@ -1,43 +0,0 @@
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',
},
}
];

124
eslint.config.ts Normal file
View File

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

26
fake/upload.fake.ts Normal file
View File

@ -0,0 +1,26 @@
// 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({
id: '@guid',
username: '@first',
email: '@email',
avatar: '@image("200x200")',
email: '@email',
id: '@guid',
role: 'admin',
username: '@first',
});
},
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(),
sex: faker.person.sexType(),
role: 'admin',
sex: faker.person.sexType(),
};
},
url: '/fake/get-user-info',
},
]);

View File

@ -2,23 +2,54 @@
<html lang="zh-CN" data-build-time="%VITE_BUILD_TIME%" data-commit="%VITE_BUILD_COMMIT%">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<!-- <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> -->
<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>
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/@unocss/reset/tailwind.min.css" />
<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>
</head>
<body ontouchstart ontouchend>
<!-- ontouchstart ontouchend -->
<body>
<div id="app">
<div style="display: flex; justify-content: center; align-items: center; height: 100vh">Loading...</div>
<div class="page-wrapper" style="display: flex; justify-content: center; align-items: center">Loading...</div>
</div>
<script type="module" src="/src/main.ts"></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" />
<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> -->
</body>
<script>
(function (d) {
@ -48,6 +79,6 @@
} catch (e) {}
};
s.parentNode.insertBefore(tk, s);
})(document);
}) /* (document) */;
</script>
</html>

View File

@ -1,105 +1,164 @@
{
"packageManager": "pnpm@9.12.1",
"packageManager": "pnpm@10.8.0",
"name": "vue-ts-example",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --port 4730 --host",
"all": "run-p build-only format type-check lint",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
"lint-format": "run-p lint:oxlint lint:eslint format",
"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",
"depcheck": "depcheck"
"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"
},
"lint-staged": {
"src/**/*.{js,ts,vue}": [
"src/**/*.{js,jsx,ts,tsx,vue}": [
"prettier --write",
"eslint --fix"
"eslint --fix",
"oxlint --fix"
]
},
"pnpm": {
"patchedDependencies": {}
"overrides": {
"vite": "$vite",
"vue-tsc": "$vue-tsc",
"@primevue/auto-import-resolver": "$primevue"
}
},
"dependencies": {
"@alova/adapter-axios": "^2.0.7",
"@unhead/vue": "^1.11.7",
"@alova/adapter-axios": "^2.0.13",
"@formkit/auto-animate": "^0.8.2",
"@intlify/unplugin-vue-i18n": "^6.0.6",
"@pinia/colada": "^0.14.2",
"@primeuix/themes": "^1.0.3",
"@splinetool/runtime": "^1.9.82",
"@types/p5": "^1.7.6",
"@types/sortablejs": "^1.15.8",
"@unhead/vue": "^2.0.5",
"@vant/use": "^1.6.0",
"@vueuse/core": "^11.1.0",
"alova": "^3.0.20",
"axios": "^1.7.7",
"@vueuse/core": "^13.1.0",
"alova": "^3.2.10",
"ant-design-vue": "~4.2.6",
"axios": "^1.8.4",
"cesium": "^1.128.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"consola": "^3.4.2",
"dayjs": "^1.11.13",
"deep-freeze-es6": "^4.0.0",
"jsencrypt": "^3.3.2",
"lucide-vue-next": "^0.487.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"p5": "^1.11.3",
"page-stack-vue3": "^2.5.6",
"pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.1",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"plotly.js-dist-min": "^3.0.1",
"primeicons": "^7.0.0",
"primelocale": "^2.1.2",
"primevue": "^4.3.3",
"radash": "^12.1.0",
"taze": "^0.17.2",
"tdesign-icons-vue-next": "^0.2.6",
"tdesign-mobile-vue": "^1.4.3",
"radix-vue": "^1.9.17",
"reka-ui": "^2.2.0",
"satellite.js": "^6.0.0",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.2.0",
"tdesign-icons-vue-next": "^0.3.5",
"three": "^0.175.0",
"ts-enum-util": "^4.1.0",
"utils4u": "^2.11.0",
"vant": "^4.9.7",
"vite-plugin-webfont-dl": "^3.9.5",
"vue": "^3.5.11",
"utils4u": "^4.2.3",
"vant": "^4.9.18",
"vue": "^3.5.13",
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^11.1.3",
"vue-page-stack": "^3.2.0",
"vue-router": "^4.4.5"
"vue-router": "^4.5.0",
"vuetify": "^3.8.2"
},
"devDependencies": {
"@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",
"@eslint/compat": "^1.2.8",
"@faker-js/faker": "^9.6.0",
"@iconify-json/carbon": "^1.2.8",
"@iconify-json/logos": "^1.2.4",
"@iconify-json/mdi": "^1.2.3",
"@iconify/utils": "^2.3.0",
"@playwright/test": "^1.51.1",
"@primevue/auto-import-resolver": "^4.3.3",
"@tsconfig/node22": "^22.0.1",
"@types/archiver": "^6.0.3",
"@types/mockjs": "^1.0.10",
"@types/node": "^22.14.0",
"@types/nprogress": "^0.2.3",
"@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",
"@types/plotly.js-dist-min": "^2.3.4",
"@types/three": "^0.175.0",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vitejs/plugin-vue-jsx": "^4.1.2",
"@vitest/eslint-plugin": "^1.1.40",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.5.1",
"@vue/tsconfig": "^0.7.0",
"archiver": "^7.0.1",
"depcheck": "^1.4.7",
"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",
"eruda": "^3.4.1",
"eslint": "^9.24.0",
"eslint-plugin-import-x": "^4.10.2",
"eslint-plugin-oxlint": "^0.16.6",
"eslint-plugin-perfectionist": "^4.11.0",
"eslint-plugin-unicorn": "^58.0.0",
"eslint-plugin-vue": "^10.0.0",
"husky": "^9.1.7",
"less": "^4.3.0",
"lint-staged": "^15.5.0",
"mockjs": "^1.1.0",
"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"
"npm-run-all2": "^7.0.2",
"oxlint": "^0.16.6",
"prettier": "3.5.3",
"sass-embedded": "^1.86.3",
"terser": "^5.39.0",
"typescript": "~5.8.3",
"unocss": "66.1.0-beta.10",
"unocss-preset-animations": "^1.1.1",
"unocss-preset-chinese": "^0.3.3",
"unocss-preset-shadcn": "^0.5.0",
"unplugin-auto-import": "^19.1.2",
"unplugin-icons": "^22.1.0",
"unplugin-vue-components": "^28.5.0",
"unplugin-vue-macros": "^2.14.5",
"unplugin-vue-markdown": "^28.3.1",
"unplugin-vue-router": "^0.12.0",
"vite": "^6.2.6",
"vite-plugin-checker": "^0.9.1",
"vite-plugin-fake-server": "^2.2.0",
"vite-plugin-purgecss-updated-v5": "^1.2.4",
"vite-plugin-singlefile": "^2.2.0",
"vite-plugin-static-copy": "^2.3.1",
"vite-plugin-vue-devtools": "^7.7.2",
"vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-vue-meta-layouts": "^0.5.1",
"vite-plugin-vuetify": "^2.1.1",
"vite-plugin-webfont-dl": "^3.10.4",
"vue-component-type-helpers": "^2.2.8",
"vue-tsc": "^2.2.8"
}
}

View File

@ -1,13 +0,0 @@
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,79 +1,95 @@
import { defineConfig, devices } from '@playwright/test';
import process from 'node:process';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
/* Run tests in files in parallel */
fullyParallel: true,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* 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: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL || 'https://vue-ts-example.oo1.dev',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{ 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'] },
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: '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,
/* 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',
/* Run your local dev server before starting the tests */
/* Only on CI systems run the tests headless */
headless: !!process.env.CI || process.env.HEADLESS === 'true',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
// /* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// /**
// * Use the dev server by default for faster feedback loop.
// * Use the preview server on CI for more realistic testing.
// * Playwright will re-use the local server if there is already a dev-server running.
// */
// command: process.env.CI ? 'npm run preview' : 'npm run dev',
// port: process.env.CI ? 4173 : 5173,
// reuseExistingServer: !process.env.CI,
// },
/* 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,
});

10972
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

@ -0,0 +1,81 @@
<!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

@ -0,0 +1,19 @@
## 这个代码没测试。
``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

@ -0,0 +1,188 @@
<!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

@ -0,0 +1,153 @@
// @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

7380
public/html-page/lib/konva.2.4.2.min.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
/**
* @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

@ -0,0 +1,428 @@
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

@ -0,0 +1,198 @@
# 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

@ -0,0 +1,192 @@
# 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

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

View File

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

View File

@ -0,0 +1,85 @@
<!--
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>
-->

257645
public/iframe/plotly-3.0.1.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
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,11 +1,39 @@
<script setup lang="ts">
const VITE_BUILD_COMMIT = import.meta.env.VITE_BUILD_COMMIT;
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;
});
</script>
<template>
<div fixed rounded-br-4 bottom-0 left-0 z-9999 px-4 py-2 bg-black text-white op-75>
commit: {{ VITE_BUILD_COMMIT }}
</div>
<div>$__DEV__: {{ $__DEV__ }}</div>
<RouterView />
<a-config-provider :theme="themeConfig">
<RouterView />
</a-config-provider>
<DynamicDialog /> <ConfirmDialog /> <Toast />
</template>
<style>
.layout-wrapper {
isolation: isolate;
}
@supports (height: 100dvh) {
.h-screen {
height: 100dvh;
}
}
@supports (width: 100dvw) {
.w-screen {
width: 100dvw;
}
}
</style>

View File

@ -1,84 +0,0 @@
/* 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;
}

View File

@ -1,24 +0,0 @@
@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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,316 @@
<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

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

View File

@ -0,0 +1,166 @@
<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

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

View File

@ -0,0 +1,65 @@
<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

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

View File

@ -0,0 +1,71 @@
<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

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

View File

@ -0,0 +1,76 @@
<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

@ -0,0 +1,61 @@
<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

@ -0,0 +1,14 @@
<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

@ -0,0 +1,5 @@
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

@ -0,0 +1,11 @@
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

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

View File

@ -0,0 +1,83 @@
<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

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

View File

@ -0,0 +1,60 @@
<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

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

View File

@ -0,0 +1,138 @@
<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

@ -0,0 +1,87 @@
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

@ -0,0 +1,107 @@
<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

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

View File

@ -0,0 +1,159 @@
<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

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

View File

@ -0,0 +1,80 @@
<!-- 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

@ -0,0 +1,112 @@
<!-- 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,127 @@
import * as Cesium from 'cesium';
import type { HCesiumManager } from './HCesiumManager';
import type { I站点 } from './HCesiumManager.types';
export class HCesiumStationManager {
private viewerManager: HCesiumManager;
// 用于存储当前由此管理器管理的地面站实体的 Map
private currentStationEntities: Map<string, Cesium.Entity> = new Map();
constructor(viewerManager: HCesiumManager) {
this.viewerManager = viewerManager;
}
/**
* 向视图中添加或更新地面站实体。
* 注意:当前实现仅添加,如果已存在则警告并返回现有实体。
*/
addOrUpdateStation(options: I站点): undefined {
const existingEntity = this.currentStationEntities.get(options.id);
if (existingEntity) {
console.warn(`ID 为 "${options.id}" 的地面站实体已由管理器追踪,跳过添加。`);
return;
}
// 解构赋值获取站点信息
const { height = 0, id, latitude, longitude, name, pixelSize = 10 } = options;
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
const stationEntity = this._createStationEntity(id, name, position, pixelSize);
const addedEntity = this.viewerManager.addEntity(stationEntity);
if (addedEntity) {
this.currentStationEntities.set(id, addedEntity); // 添加成功后,将其存入 Map
} else {
console.error(`通过 ViewerManager 添加 ID 为 "${id}" 的地面站实体失败。`);
}
}
/**
* 创建地面站点的 Cesium 实体对象。
* 这是一个内部辅助方法,封装了实体创建的细节。
*/
private _createStationEntity(
id: string,
name: string,
position: Cesium.Cartesian3,
pixelSize: number,
): Cesium.Entity {
return new Cesium.Entity({
id, // 使用传入的 id 作为实体的唯一标识符
name,
position,
point: {
pixelSize,
color: Cesium.Color.fromRandom({ alpha: 1 }), // 确保 alpha 为 1
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
},
label: {
text: name,
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -12), // 标签略微偏移到点的上方
},
});
}
/**
* 从视图中移除指定的地面站实体 (通过 ID)。
*/
removeStation(entityId: string): boolean {
const entityToRemove = this.currentStationEntities.get(entityId);
if (!entityToRemove) {
// console.warn(`未在 GroundStationManager 中找到 ID 为 "${entityId}" 的实体,无法移除。`);
return false; // 不在管理范围内
}
const removed = this.viewerManager.removeEntity(entityToRemove); // 优先使用实体对象移除
if (removed) {
// 移除成功后,从 Map 中删除
this.currentStationEntities.delete(entityId);
return true;
} else {
console.warn(`尝试通过 ViewerManager 移除 ID 为 "${entityId}" 的地面站实体失败。`);
// 即使 ViewerManager 移除失败,也尝试从内部 Map 中移除,以保持一致性?
// 或者保留在 Map 中以供调试?暂时选择移除。
this.currentStationEntities.delete(entityId);
return false;
}
}
/**
* 清除所有由此管理器管理的地面站实体。
*/
clearAllStations(): void {
// 迭代 Map 的 ID 进行移除,避免在迭代 Map 值时修改 Map
const idsToRemove = [...this.currentStationEntities.keys()];
let removalFailed = false;
for (const id of idsToRemove) {
if (!this.removeStation(id)) {
removalFailed = true; // 记录是否有移除失败的情况
}
}
if (removalFailed) {
console.warn('清除部分地面站实体时遇到问题。');
}
// 确保最终清空 Map
this.currentStationEntities.clear();
}
/**
* 获取当前管理的地面站实体 Map。
* @returns 返回包含当前地面站实体的 Map。
*/
getCurrentStationEntities(): Map<string, Cesium.Entity> {
return this.currentStationEntities;
}
/**
* 销毁管理器,清理资源。
*/
destroy(): void {
this.clearAllStations();
}
}

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